From d82b68274c75c14d258b4898a34edc25d9712b87 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Mon, 24 Aug 2026 23:46:45 -0300 Subject: [PATCH] fix(sse): exact-domain cookie match and origin-equality URL assertions (#11452) Clears CodeQL js/incomplete-url-substring-sanitization alerts #860-#865: - volcengineConsoleAutoLogin: cookie domain filter now uses an exact/ dot-suffix helper (isVolcengineCookieDomain) instead of substring includes(), rejecting look-alike hosts like volcengine.com.evil.test - security-s1-s2-s4 tests: agent-card/agent.json URL assertions compare parsed origin equality instead of startsWith prefix Co-authored-by: Markus Hartung --- open-sse/services/volcengineConsoleAutoLogin.ts | 11 +++++++++-- tests/unit/security-s1-s2-s4.test.ts | 16 ++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/open-sse/services/volcengineConsoleAutoLogin.ts b/open-sse/services/volcengineConsoleAutoLogin.ts index 6048cbb63f..c0e8762144 100644 --- a/open-sse/services/volcengineConsoleAutoLogin.ts +++ b/open-sse/services/volcengineConsoleAutoLogin.ts @@ -96,6 +96,13 @@ const ARK_CONSOLE_URL = /** Cookie names required for a valid console session (mirrors tokenExtractionConfig) */ const REQUIRED_COOKIES = ["digest", "AccountID", "csrfToken", "userInfo"] as const; +/** Exact-domain match for session cookies — substring checks would also accept + * look-alike hosts (e.g. `volcengine.com.evil.test`). Playwright may report the + * domain with or without a leading dot. */ +function isVolcengineCookieDomain(domain: string): boolean { + return domain === "volcengine.com" || domain.endsWith(".volcengine.com"); +} + const DEFAULT_SESSION_TIMEOUT = 300_000; const SUBMIT_COOKIE_TIMEOUT = 90_000; const CAPTURE_POLL_INTERVAL = 1_000; @@ -618,7 +625,7 @@ export class VolcengineConsoleAutoLoginService { for (const cookie of cookies as Array<{ name: string; domain: string; value: string }>) { if ( REQUIRED_COOKIES.includes(cookie.name as (typeof REQUIRED_COOKIES)[number]) && - cookie.domain.includes("volcengine.com") + isVolcengineCookieDomain(cookie.domain) ) { credentials[cookie.name] = cookie.value; } @@ -763,7 +770,7 @@ export class VolcengineConsoleAutoLoginService { domain: string; }>; const present = REQUIRED_COOKIES.filter((name) => - cookies.some((c) => c.name === name && c.domain.includes("volcengine.com")) + cookies.some((c) => c.name === name && isVolcengineCookieDomain(c.domain)) ); parts.push( `cookies=[${present.join(",") || "none of digest/AccountID/csrfToken/userInfo"}]` diff --git a/tests/unit/security-s1-s2-s4.test.ts b/tests/unit/security-s1-s2-s4.test.ts index 74dc6152af..e060bb5bb4 100644 --- a/tests/unit/security-s1-s2-s4.test.ts +++ b/tests/unit/security-s1-s2-s4.test.ts @@ -41,11 +41,13 @@ describe("S2 — agent-card topology sanitisation", () => { assert.equal(res.status, 200); const card = (await res.json()) as { url?: string; supportedInterfaces?: { url?: string }[] }; assert.ok(card.url, "card must have a url"); - assert.ok(card.url.startsWith("https://gateway.example.com"), `expected gateway.example.com, got ${card.url}`); + assert.equal(new URL(card.url).origin, "https://gateway.example.com", `expected gateway.example.com origin, got ${card.url}`); if (card.supportedInterfaces && card.supportedInterfaces.length > 0) { - assert.ok( - card.supportedInterfaces[0].url?.startsWith("https://gateway.example.com"), - `interface URL should use dynamic origin, got ${card.supportedInterfaces[0].url}` + const ifaceUrl = card.supportedInterfaces[0].url; + assert.equal( + ifaceUrl ? new URL(ifaceUrl).origin : undefined, + "https://gateway.example.com", + `interface URL should use dynamic origin, got ${ifaceUrl}` ); } }); @@ -62,7 +64,8 @@ describe("S2 — agent-card topology sanitisation", () => { const res = await mod.GET(request); assert.equal(res.status, 200); const card = (await res.json()) as { url?: string }; - assert.ok(card.url?.startsWith("https://custom.example.com"), `expected custom.example.com, got ${card.url}`); + assert.ok(card.url, "card must have a url"); + assert.equal(new URL(card.url).origin, "https://custom.example.com", `expected custom.example.com origin, got ${card.url}`); }); it("agent.json derives URL from request.nextUrl.origin when OMNIROUTE_BASE_URL is unset", async () => { @@ -76,7 +79,8 @@ describe("S2 — agent-card topology sanitisation", () => { const res = await mod.GET(request); assert.equal(res.status, 200); const card = (await res.json()) as { url?: string }; - assert.ok(card.url?.startsWith("https://gateway.example.com"), `expected gateway.example.com, got ${card.url}`); + assert.ok(card.url, "card must have a url"); + assert.equal(new URL(card.url).origin, "https://gateway.example.com", `expected gateway.example.com origin, got ${card.url}`); }); });