* Build a `fetch` bound to a DNS-validated, pinned IP so that calls to * user-supplied agent URLs cannot be rebound to internal hosts (SSRF). * * Redirects are not followed: an authenticated agent call must not have its * `X-API-Key` carried to a redirected host. A per-hop `timeout` and an optio
(
resolvedIP: string,
config: { timeout: number; signal?: AbortSignal }
)
| 94 | * propagate to the outbound connection. |
| 95 | */ |
| 96 | function createPinnedFetch( |
| 97 | resolvedIP: string, |
| 98 | config: { timeout: number; signal?: AbortSignal } |
| 99 | ): typeof fetch { |
| 100 | return async (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => { |
| 101 | const url = input instanceof Request ? input.url : input.toString() |
| 102 | const method = init?.method ?? (input instanceof Request ? input.method : undefined) |
| 103 | |
| 104 | const rawHeaders = init?.headers ?? (input instanceof Request ? input.headers : undefined) |
| 105 | const headers = |
| 106 | rawHeaders instanceof Headers |
| 107 | ? Object.fromEntries(rawHeaders.entries()) |
| 108 | : Array.isArray(rawHeaders) |
| 109 | ? Object.fromEntries(rawHeaders as string[][]) |
| 110 | : (rawHeaders as Record<string, string> | undefined) |
| 111 | |
| 112 | let body: string | Uint8Array | undefined |
| 113 | if (init?.body != null) { |
| 114 | if (typeof init.body === 'string' || init.body instanceof Uint8Array) { |
| 115 | body = init.body |
| 116 | } else if (init.body instanceof ArrayBuffer) { |
| 117 | body = new Uint8Array(init.body) |
| 118 | } else { |
| 119 | const text = await new Response(init.body as BodyInit).text() |
| 120 | if (text) body = text |
| 121 | } |
| 122 | } else if (input instanceof Request && !input.bodyUsed) { |
| 123 | const text = await input.text() |
| 124 | if (text) body = text |
| 125 | } |
| 126 | |
| 127 | const callSignal = |
| 128 | config.signal ?? |
| 129 | (init?.signal instanceof AbortSignal |
| 130 | ? init.signal |
| 131 | : input instanceof Request && input.signal instanceof AbortSignal |
| 132 | ? input.signal |
| 133 | : undefined) |
| 134 | |
| 135 | const res = await secureFetchWithPinnedIP(url, resolvedIP, { |
| 136 | method, |
| 137 | headers, |
| 138 | body, |
| 139 | signal: callSignal, |
| 140 | timeout: config.timeout, |
| 141 | maxRedirects: 0, |
| 142 | maxResponseBytes: A2A_MAX_RESPONSE_BYTES, |
| 143 | }) |
| 144 | return new Response(res.body, { |
| 145 | status: res.status, |
| 146 | statusText: res.statusText, |
| 147 | headers: new Headers(res.headers.toRecord()), |
| 148 | }) |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Resolve (and cache) an agent card. The card is resolved by trying the SDK |
no test coverage detected