diff --git a/src/lib/cloudAgent/baseAgent.ts b/src/lib/cloudAgent/baseAgent.ts index 9f5689b90b..0acf3cb475 100644 --- a/src/lib/cloudAgent/baseAgent.ts +++ b/src/lib/cloudAgent/baseAgent.ts @@ -1,3 +1,4 @@ +import { randomBytes } from "node:crypto"; import type { CloudAgentTask, CloudAgentStatus, @@ -86,10 +87,12 @@ export abstract class CloudAgentBase { } protected generateTaskId(): string { - return `task_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; + // Cryptographically secure suffix (not Math.random) — task IDs flow into + // session/external identifiers, so they must be unpredictable (CodeQL js/insecure-randomness). + return `task_${Date.now()}_${randomBytes(8).toString("hex")}`; } protected generateActivityId(): string { - return `act_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; + return `act_${Date.now()}_${randomBytes(8).toString("hex")}`; } } diff --git a/tests/unit/antigravity-discovery-bootstrap.test.ts b/tests/unit/antigravity-discovery-bootstrap.test.ts index 3d0aa82f98..11f8c43097 100644 --- a/tests/unit/antigravity-discovery-bootstrap.test.ts +++ b/tests/unit/antigravity-discovery-bootstrap.test.ts @@ -152,7 +152,10 @@ describe("ensureAntigravityProjectAssigned", () => { const mockFetch = async (url: string, _init?: RequestInit): Promise => { hitUrls.push(url); - if (url.includes("daily-cloudcode-pa.googleapis.com")) { + // Exact hostname match (not substring .includes) so the check can't be fooled by a + // look-alike host like daily-cloudcode-pa.googleapis.com.evil.com (CodeQL + // js/incomplete-url-substring-sanitization). + if (new URL(url).hostname === "daily-cloudcode-pa.googleapis.com") { // First URL fails return new Response("not found", { status: 404 }); }