diff --git a/server/__tests__/codex-discovery.test.js b/server/__tests__/codex-discovery.test.js index b7cf0a0b..6e325bff 100644 --- a/server/__tests__/codex-discovery.test.js +++ b/server/__tests__/codex-discovery.test.js @@ -98,6 +98,16 @@ describe("codex-discovery", () => { assert.equal(meta.description, 'say \\"hi\\"'); }); + it("recognises a quote preceded by an escaped backslash as a real terminator", () => { + const meta = cx.readTomlMeta('path = "path\\\\" # c\n'); + assert.equal(meta.path, "path\\"); + }); + + it("keeps counting backslash pairs correctly with four of them before the terminator", () => { + const meta = cx.readTomlMeta('key = "a\\\\\\\\" # c\n'); + assert.equal(meta.key, "a\\\\"); + }); + it("reads a rule file's preview", () => { write( path.join(TMP, "rules", "no-force-push.rules"), diff --git a/server/lib/codex-discovery.js b/server/lib/codex-discovery.js index 82aed0a8..d026dca0 100644 --- a/server/lib/codex-discovery.js +++ b/server/lib/codex-discovery.js @@ -119,25 +119,26 @@ function readTomlMeta(text) { if (!value.slice(3).includes('"""')) inTripleQuote = true; continue; } - if ( - (value.startsWith('"') && value.endsWith('"')) || - (value.startsWith("'") && value.endsWith("'")) - ) { - value = value.slice(1, -1); - } else if (value.startsWith('"') || value.startsWith("'")) { - // A quoted value with a trailing `# comment` outside the string — - // e.g. model = "gpt-5.4" # default. Find the real closing quote - // (skipping a `\"` escaped one) and strip everything after it. + if (value.startsWith('"') || value.startsWith("'")) { + // Find the real closing quote — one not preceded by an ODD run of + // backslashes (an even run, including zero, means it isn't escaped) — + // so a trailing `# comment` outside the string is stripped without + // being fooled by an escaped quote or an escaped backslash inside it. const quote = value[0]; let closeIdx = -1; for (let i = 1; i < value.length; i++) { - if (value[i] === quote && value[i - 1] !== "\\") { + if (value[i] !== quote) continue; + let backslashes = 0; + while (value[i - 1 - backslashes] === "\\") backslashes++; + if (backslashes % 2 === 0) { closeIdx = i; break; } } - if (closeIdx > 0) value = value.slice(1, closeIdx); - else continue; + if (closeIdx < 0) continue; + // \\ decodes to one backslash; other escapes (e.g. \") are left + // literal — this is a best-effort reader, not a full TOML unescaper. + value = value.slice(1, closeIdx).replace(/\\\\/g, "\\"); } else if (/^(true|false|-?\d+(\.\d+)?)\s*(#.*)?$/.test(value)) { value = value.replace(/\s*#.*$/, ""); value = value === "true" ? true : value === "false" ? false : Number(value);