Compare commits

..

3 Commits

Author SHA1 Message Date
adevwithpurpose
af6813ca12 fix(security): keep only the regex sanitization; drop non-functional CodeQL annotations
The lgtm[]/nosemgrep: comments in codexIdentity.ts and reasoningCache.ts use
formats GitHub Actions CodeQL does not honor, and shifting those sha256 lines
re-attributed the already-dismissed base alerts to this PR as two new CodeQL
findings. Revert those two annotation-only files to base so the existing
dismissals apply; retain the real fix (escaping backslash in the test regex),
which resolves the open js/incomplete-sanitization alert.
2026-08-15 10:08:14 -03:00
adevwithpurpose
864e817eda Merge remote-tracking branch 'origin/release/v3.8.50' into fix/codeql-0814-hash-fp-and-sanitize 2026-08-15 09:04:23 -03:00
Xiangzhe
ab36b35035 fix(security): sanitize test regex and annotate CodeQL hash false-positives
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
2026-08-14 10:07:07 -03:00
4 changed files with 1 additions and 84 deletions

View File

@@ -1 +0,0 @@
- fix(providers): strip uniqueItems from Gemini tool schemas (Gemini rejects it with 400 'Unknown name uniqueItems') (#9617)

View File

@@ -58,11 +58,6 @@ export const GEMINI_UNSUPPORTED_SCHEMA_KEYS = new Set([
"contains",
"minContains",
"maxContains",
// #9617: array uniqueness keyword — agentic-CLI tool schemas (JSON-Schema
// generators) set this routinely and Gemini's schema parser has no field for
// it, rejecting the whole request with "Unknown name \"uniqueItems\"".
// Upstream 9router already strips it alongside `contains` for the same error.
"uniqueItems",
// Complex schema keywords (handled by flattenAnyOfOneOf/mergeAllOf)
"anyOf",
"oneOf",

View File

@@ -1,77 +0,0 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { buildGeminiTools } from "../../open-sse/translator/helpers/geminiToolsSanitizer.ts";
// Issue #9617: Gemini rejects `uniqueItems` in function_declarations parameter schemas
// with HTTP 400 "Unknown name \"uniqueItems\" ... Cannot find field" (Gemini's protobuf-JSON
// schema parser only accepts a subset of JSON Schema/OpenAPI 3.0 — the same class of error
// already fixed for `multipleOf`, `minItems`, `maxItems`, `strict`, `encrypted` in
// GEMINI_UNSUPPORTED_SCHEMA_KEYS, open-sse/translator/helpers/geminiHelper.ts).
test("buildGeminiTools strips uniqueItems from array schemas (issue #9617)", () => {
const tools = [
{
type: "function",
function: {
name: "exit_worktree",
description: "test tool with an array-of-objects parameter",
parameters: {
type: "object",
properties: {
items: {
type: "array",
uniqueItems: true,
items: {
type: "object",
properties: {
name: { type: "string" },
action: { type: "string" },
},
required: ["name", "action"],
},
},
},
required: ["items"],
},
},
},
];
const geminiTools = buildGeminiTools(tools);
const serialized = JSON.stringify(geminiTools);
assert.ok(geminiTools, "expected buildGeminiTools to return a tools array");
assert.equal(
serialized.includes("uniqueItems"),
false,
`uniqueItems leaked into the Gemini payload (would trigger upstream 400 "Unknown name \\"uniqueItems\\""): ${serialized}`
);
});
// Companion: a top-level (non-nested) array property with uniqueItems is also stripped —
// matches the reporter's deeply-nested case with extra path coverage.
test("buildGeminiTools strips uniqueItems from a top-level array parameter schema (issue #9617)", () => {
const tools = [
{
type: "function",
function: {
name: "list_worktrees",
description: "test tool with a top-level array parameter",
parameters: {
type: "object",
properties: {
paths: {
type: "array",
uniqueItems: true,
items: { type: "string" },
},
},
required: ["paths"],
},
},
},
];
const serialized = JSON.stringify(buildGeminiTools(tools));
assert.equal(serialized.includes("uniqueItems"), false);
});

View File

@@ -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(