( requestId: string, url: string, options?: IsolatedFetchOptions )
| 289 | } |
| 290 | |
| 291 | async function secureFetch( |
| 292 | requestId: string, |
| 293 | url: string, |
| 294 | options?: IsolatedFetchOptions |
| 295 | ): Promise<string> { |
| 296 | if (url.length > MAX_FETCH_URL_LENGTH) { |
| 297 | return JSON.stringify({ |
| 298 | error: `Security Error: fetch URL exceeds maximum length (${MAX_FETCH_URL_LENGTH})`, |
| 299 | }) |
| 300 | } |
| 301 | |
| 302 | try { |
| 303 | const response = await secureFetchWithValidation( |
| 304 | url, |
| 305 | normalizeFetchOptions(options), |
| 306 | 'fetchUrl' |
| 307 | ) |
| 308 | const bodyResult = truncateString(await response.text(), MAX_FETCH_RESPONSE_CHARS) |
| 309 | const headers: Record<string, string> = {} |
| 310 | for (const [key, value] of response.headers) { |
| 311 | headers[key] = value |
| 312 | } |
| 313 | return JSON.stringify({ |
| 314 | ok: response.ok, |
| 315 | status: response.status, |
| 316 | statusText: response.statusText, |
| 317 | body: bodyResult.value, |
| 318 | bodyTruncated: bodyResult.truncated, |
| 319 | headers, |
| 320 | }) |
| 321 | } catch (error: unknown) { |
| 322 | logger.warn(`[${requestId}] Isolated fetch failed`, { |
| 323 | url: sanitizeUrlForLog(url), |
| 324 | error: toError(error).message, |
| 325 | }) |
| 326 | return JSON.stringify({ error: getErrorMessage(error, 'Unknown fetch error') }) |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | function normalizeOwnerKey(ownerKey?: string): string { |
| 331 | if (!ownerKey) return 'anonymous' |
no test coverage detected