mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
fix(anthropic-oauth): bound OAuth token endpoint response reads (#96644)
postJson reads the Anthropic OAuth token endpoint response body with an unbounded await response.text(). A compromised or hijacked OAuth endpoint can stream an arbitrarily large body and force the runtime to buffer the entire payload before parsing — an OOM/DoS vector. Replace with readResponseWithLimit at 16 MiB cap + TextDecoder decode to match the sibling bounded-read pattern (provider-http-errors.ts:308). Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -79,4 +79,26 @@ describe("Anthropic OAuth token responses", () => {
|
||||
"Anthropic token refresh returned invalid token fields.",
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects an oversized Anthropic token refresh response", async () => {
|
||||
let pullCount = 0;
|
||||
const cancel = vi.fn(async () => undefined);
|
||||
const oversizedStream = new ReadableStream<Uint8Array>({
|
||||
pull(controller) {
|
||||
pullCount += 1;
|
||||
controller.enqueue(new Uint8Array(pullCount === 1 ? 16 * 1024 * 1024 + 1 : 1));
|
||||
},
|
||||
cancel,
|
||||
});
|
||||
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => new Response(oversizedStream, { status: 200 })),
|
||||
);
|
||||
|
||||
await expect(refreshAnthropicToken("old-refresh-token")).rejects.toThrow("too large");
|
||||
|
||||
expect(pullCount).toBeLessThanOrEqual(2);
|
||||
expect(cancel).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import type { Server } from "node:http";
|
||||
import { readResponseWithLimit } from "@openclaw/media-core/read-response-with-limit";
|
||||
import { toErrorObject } from "../../../infra/errors.js";
|
||||
import {
|
||||
generateOAuthState,
|
||||
@@ -52,6 +53,10 @@ const CALLBACK_PATH = "/callback";
|
||||
const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
||||
const SCOPES =
|
||||
"org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
|
||||
|
||||
/** Max response body bytes for Anthropic OAuth token endpoint (16 MiB). */
|
||||
const OAUTH_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
|
||||
|
||||
async function getNodeApis(): Promise<NodeApis> {
|
||||
if (nodeApis) {
|
||||
return nodeApis;
|
||||
@@ -233,7 +238,10 @@ async function postJson(
|
||||
signal: buildOAuthRequestSignal({ signal: options.signal, timeoutMs }),
|
||||
});
|
||||
|
||||
const responseBody = await response.text();
|
||||
const buffer = await readResponseWithLimit(response, OAUTH_RESPONSE_MAX_BYTES, {
|
||||
onOverflow: ({ size }) => new Error(`Anthropic OAuth response too large: ${size} bytes`),
|
||||
});
|
||||
const responseBody = new TextDecoder().decode(buffer);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
|
||||
Reference in New Issue
Block a user