From 87eaac4010dd2c341d1bd54b1103ceb7a7941d00 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 3 Jun 2026 13:15:46 +0200 Subject: [PATCH] fix(e2e): bound image auth mock bodies --- CHANGELOG.md | 1 + .../e2e/openai-image-auth-docker-client.ts | 36 ++++++++++--------- .../openai-image-auth-docker-client.test.ts | 33 +++++++++++++++++ 3 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 test/scripts/openai-image-auth-docker-client.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d154c11e9b7..456d5694fc03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ Docs: https://docs.openclaw.ai - Release/CI/E2E: stop tracked gateway and mock service process groups so descendant helpers do not survive E2E cleanup. - Release/CI/E2E: exit Telegram credential proof wrappers promptly after forwarded shutdown signals while keeping the descendant force-kill guard armed. - Release/CI/E2E: reject oversized ClickClack fixture request bodies before release journey smokes can accumulate unbounded payloads. +- Release/CI/E2E: reject oversized OpenAI image-auth mock request bodies before Docker proof runs can accumulate unbounded payloads. - Release/CI/E2E: fail secret-provider proof runs when temporary state cleanup still fails after retries instead of hiding the cleanup error. - Release/CI/E2E: fail package-candidate ref proofs when temporary source worktree cleanup fails instead of leaving stale worktrees behind. - Release/CI/E2E: remove package tarball extract directories when tar extraction fails before validation can continue. diff --git a/scripts/e2e/openai-image-auth-docker-client.ts b/scripts/e2e/openai-image-auth-docker-client.ts index e6e5bd42b76e..e5865313c098 100644 --- a/scripts/e2e/openai-image-auth-docker-client.ts +++ b/scripts/e2e/openai-image-auth-docker-client.ts @@ -1,12 +1,15 @@ import http from "node:http"; import type { AddressInfo } from "node:net"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { isRequestBodyTooLargeError, readBody } from "./lib/mock-openai-http.mjs"; const DIRECT_IMAGE_BYTES = Buffer.from("docker-direct-image"); const CODEX_IMAGE_BYTES = Buffer.from("docker-codex-image"); const DIRECT_TOKEN = "sk-openclaw-image-auth-e2e"; const CODEX_TOKEN = "docker-codex-oauth-token"; -type RequestRecord = { +export type RequestRecord = { method?: string; url?: string; authorization?: string; @@ -21,18 +24,6 @@ function assert(condition: unknown, message: string): asserts condition { } } -function readBody(req: http.IncomingMessage): Promise { - return new Promise((resolve, reject) => { - let body = ""; - req.setEncoding("utf8"); - req.on("data", (chunk) => { - body += chunk; - }); - req.on("end", () => resolve(body)); - req.on("error", reject); - }); -} - function writeJson(res: http.ServerResponse, status: number, body: unknown): void { res.writeHead(status, { "content-type": "application/json" }); res.end(JSON.stringify(body)); @@ -63,14 +54,23 @@ function writeCodexSse(res: http.ServerResponse): void { res.end("data: [DONE]\n\n"); } -async function startMockServer(records: RequestRecord[]): Promise<{ +export async function startMockServer(records: RequestRecord[]): Promise<{ baseUrl: string; close: () => Promise; }> { const server = http.createServer((req, res) => { void (async () => { try { - const body = await readBody(req); + let body: string; + try { + body = await readBody(req); + } catch (error) { + if (isRequestBodyTooLargeError(error)) { + writeJson(res, 413, { error: { message: error.message } }); + return; + } + throw error; + } records.push({ method: req.method, url: req.url, @@ -164,7 +164,7 @@ function createCodexOAuthStore() { } as const; } -async function main() { +export async function main() { assert( process.env.OPENAI_API_KEY === DIRECT_TOKEN, "Docker lane must expose the direct OpenAI API key", @@ -246,4 +246,6 @@ async function main() { } } -await main(); +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + await main(); +} diff --git a/test/scripts/openai-image-auth-docker-client.test.ts b/test/scripts/openai-image-auth-docker-client.test.ts new file mode 100644 index 000000000000..6e85ff58eefc --- /dev/null +++ b/test/scripts/openai-image-auth-docker-client.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "vitest"; +import { + startMockServer, + type RequestRecord, +} from "../../scripts/e2e/openai-image-auth-docker-client.ts"; + +describe("OpenAI image auth Docker client mock server", () => { + it("rejects oversized request bodies before recording them", async () => { + const previousLimit = process.env.OPENCLAW_MOCK_OPENAI_REQUEST_MAX_BYTES; + process.env.OPENCLAW_MOCK_OPENAI_REQUEST_MAX_BYTES = "4"; + const records: RequestRecord[] = []; + const server = await startMockServer(records); + try { + const response = await fetch(`${server.baseUrl}/v1/images/generations`, { + method: "POST", + body: "too large", + }); + + await expect(response.json()).resolves.toEqual({ + error: { message: "mock OpenAI request body exceeded 4 bytes" }, + }); + expect(response.status).toBe(413); + expect(records).toEqual([]); + } finally { + await server.close(); + if (previousLimit === undefined) { + delete process.env.OPENCLAW_MOCK_OPENAI_REQUEST_MAX_BYTES; + } else { + process.env.OPENCLAW_MOCK_OPENAI_REQUEST_MAX_BYTES = previousLimit; + } + } + }); +});