Skip to content

Commit ccfe13a

Browse files
fix(oauth): bound anthropic OAuth token request response reads at 16 MiB
1 parent 2f851ec commit ccfe13a

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

src/llm/utils/oauth/anthropic.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,39 @@ describe("Anthropic OAuth token responses", () => {
7979
"Anthropic token refresh returned invalid token fields.",
8080
);
8181
});
82+
83+
it("caps oversized Anthropic OAuth responses at 16 MiB instead of buffering the full body", async () => {
84+
// 18 MiB body in 1 MiB chunks exceeds the shared 16 MiB cap on
85+
// readProviderTextResponse. The bounded reader must surface the cap
86+
// with the per-surface label so logs can attribute the rejection to
87+
// this call site, not github-copilot or chutes.
88+
const CHUNK = 1024 * 1024;
89+
const CHUNK_COUNT = 18;
90+
let pulls = 0;
91+
const encoder = new TextEncoder();
92+
const stream = new ReadableStream<Uint8Array>({
93+
pull(controller) {
94+
if (pulls >= CHUNK_COUNT) {
95+
controller.close();
96+
return;
97+
}
98+
pulls += 1;
99+
controller.enqueue(encoder.encode("{".repeat(CHUNK)));
100+
},
101+
});
102+
vi.stubGlobal(
103+
"fetch",
104+
vi.fn(
105+
async () =>
106+
new Response(stream, {
107+
status: 200,
108+
headers: { "content-type": "application/json" },
109+
}),
110+
),
111+
);
112+
113+
await expect(refreshAnthropicToken("old-refresh-token")).rejects.toThrow(
114+
"Anthropic OAuth token request: text response exceeds 16777216 bytes",
115+
);
116+
});
82117
});

src/llm/utils/oauth/anthropic.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import type { Server } from "node:http";
9+
import { readProviderTextResponse } from "../../../agents/provider-http-errors.js";
910
import { toErrorObject } from "../../../infra/errors.js";
1011
import {
1112
generateOAuthState,
@@ -233,7 +234,9 @@ async function postJson(
233234
signal: buildOAuthRequestSignal({ signal: options.signal, timeoutMs }),
234235
});
235236

236-
const responseBody = await response.text();
237+
// 16 MiB cap. A hostile or broken Anthropic OAuth endpoint cannot force
238+
// the runtime to buffer an unbounded token-exchange body before parsing.
239+
const responseBody = await readProviderTextResponse(response, "Anthropic OAuth token request");
237240

238241
if (!response.ok) {
239242
throw new Error(

0 commit comments

Comments
 (0)