From ab36b35035d302f69d579a060d69f04adbdaa223 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Fri, 14 Aug 2026 10:07:07 -0300 Subject: [PATCH] fix(security): sanitize test regex and annotate CodeQL hash false-positives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests/unit/early-sse-route-intent.test.ts built a RegExp from a hardcoded string but only escaped `?`/`.`, missing `\` — js/incomplete-sanitization (#816). Not exploitable (fixed literal input) but the escaping was genuinely incomplete; now escapes backslash too. reasoningCache.ts::buildAssistantMessageCacheKey and codexIdentity.ts's two UUID derivation helpers hash a cache-scope/account-seed with SHA-256 to produce a lookup key / deterministic ID — not a stored, verified password. CodeQL's js/insufficient-password-hash overfires on any hash of a secret-like variable, the same false-positive class already annotated at src/lib/db/apiKeys.ts:624. Added matching lgtm/nosemgrep annotations and inline rationale so the intent is clear to reviewers and future scans. Refs #815 #816 #817 #818 --- open-sse/config/codexIdentity.ts | 19 +++++++++++++++---- open-sse/services/reasoningCache.ts | 6 +++++- tests/unit/early-sse-route-intent.test.ts | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/open-sse/config/codexIdentity.ts b/open-sse/config/codexIdentity.ts index bc45fa1cf6..d743d81649 100644 --- a/open-sse/config/codexIdentity.ts +++ b/open-sse/config/codexIdentity.ts @@ -37,15 +37,26 @@ function nonEmptyString(value: unknown): string | null { return normalized || null; } -/** Keep the historical installation-id layout so existing accounts stay stable. */ +/** + * Keep the historical installation-id layout so existing accounts stay stable. + * CodeQL: not password hashing — this derives a deterministic installation UUID + * from an account seed, never verified against a stored credential. Same + * false-positive class as src/lib/db/apiKeys.ts::hashKey. + * lgtm[js/insufficient-password-hash] + */ function uuidFromLegacyInstallationValue(value: string): string { - const hash = createHash("sha256").update(value).digest("hex"); + const hash = createHash("sha256").update(value).digest("hex"); // nosemgrep: insufficient-password-hash return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`; } -/** RFC4122 v4 from SHA-256. Same seed → same UUID. */ +/** + * RFC4122 v4 from SHA-256. Same seed → same UUID. + * CodeQL: not password hashing — deterministic ID derivation from an account + * seed, never verified against a stored credential. + * lgtm[js/insufficient-password-hash] + */ export function deriveStableUUIDv4(seed: string): string { - const digest = createHash("sha256").update(seed).digest(); + const digest = createHash("sha256").update(seed).digest(); // nosemgrep: insufficient-password-hash const bytes = Buffer.from(digest.subarray(0, 16)); bytes[6] = (bytes[6] & 0x0f) | 0x40; bytes[8] = (bytes[8] & 0x3f) | 0x80; diff --git a/open-sse/services/reasoningCache.ts b/open-sse/services/reasoningCache.ts index bdf870e3fb..f4dddbfeba 100644 --- a/open-sse/services/reasoningCache.ts +++ b/open-sse/services/reasoningCache.ts @@ -302,7 +302,11 @@ export function buildAssistantMessageCacheKey( if (!message || message.role !== "assistant") return ""; const transcript = messages.slice(0, messageIndex + 1).map(canonicalizeHistoryMessage); - const digest = createHash("sha256") + // CodeQL: not password hashing — this derives a cache-lookup key from the + // conversation transcript, never verified against a stored credential. + // Same false-positive class as src/lib/db/apiKeys.ts::hashKey. + // lgtm[js/insufficient-password-hash] + const digest = createHash("sha256") // nosemgrep: insufficient-password-hash .update(normalizedScope) .update("\x1f") .update(JSON.stringify(transcript)) diff --git a/tests/unit/early-sse-route-intent.test.ts b/tests/unit/early-sse-route-intent.test.ts index b8b48918fc..486b176831 100644 --- a/tests/unit/early-sse-route-intent.test.ts +++ b/tests/unit/early-sse-route-intent.test.ts @@ -22,7 +22,7 @@ const ROUTES = [ for (const route of ROUTES) { test(`${route.name} early-heartbeat gate uses the real stream resolver`, () => { - const escapedBodyExpression = route.bodyExpression.replace(/[?.]/g, "\\$&"); + const escapedBodyExpression = route.bodyExpression.replace(/[.?\\]/g, "\\$&"); assert.match( route.source, new RegExp(