diff --git a/scripts/one-way-doors.ts b/scripts/one-way-doors.ts index 6735c386d..35f0f69cd 100644 --- a/scripts/one-way-doors.ts +++ b/scripts/one-way-doors.ts @@ -62,9 +62,12 @@ const DESTRUCTIVE_PATTERNS: RegExp[] = [ /\bterraform\s+destroy\b/i, /\brollback\b/i, - // Credentials / auth — allow filler words ("the", "my") between verb and noun - /\brevoke\s+[\w\s]*\b(api key|token|credential|access key|password)\b/i, - /\breset\s+[\w\s]*\b(api key|token|password|credential)\b/i, + // Credentials / auth — allow filler words ("the", "my") between verb and noun. + // Keep the noun alternation identical across revoke/reset/rotate so the three + // verbs stay parallel; a noun in one but not the others is a false-negative + // safety hole (e.g. "reset my secret" must be one-way just like "rotate my secret"). + /\brevoke\s+[\w\s]*\b(api key|token|secret|credential|access key|password)\b/i, + /\breset\s+[\w\s]*\b(api key|token|secret|credential|access key|password)\b/i, /\brotate\s+[\w\s]*\b(api key|token|secret|credential|access key|password)\b/i, // Scope / architecture forks (reversible with effort — still deserve confirmation) diff --git a/test/one-way-doors.test.ts b/test/one-way-doors.test.ts index 382200408..dcd1a5b38 100644 --- a/test/one-way-doors.test.ts +++ b/test/one-way-doors.test.ts @@ -29,4 +29,17 @@ describe("one-way-door credential keyword net (#1839)", () => { expect(classifyQuestion({ summary: `rotate my ${noun}` }).oneWay).toBe(true); } }); + + // revoke/reset/rotate must all share the same credential noun list. Previously + // "secret" was only in rotate (missing from revoke and reset) and "access key" + // was missing from reset, so "revoke my secret" / "reset my secret" / + // "reset my access key" leaked through as two-way (auto-decidable). + test("revoke/reset/rotate are all parallel for every credential noun", () => { + for (const verb of ["revoke", "reset", "rotate"]) { + for (const noun of ["api key", "token", "secret", "credential", "access key", "password"]) { + const r = classifyQuestion({ summary: `${verb} my ${noun}` }); + expect(r.oneWay, `${verb} my ${noun} should be one-way`).toBe(true); + } + } + }); });