Compare commits

...

1 Commits

Author SHA1 Message Date
diegosouzapw
514d3c39d7 test(ci): pin the openapi-security-tiers two-arm contract with an executing gate test (#12581)
The ALWAYS_PROTECTED two-arm read itself landed in #12605; what was still
missing is a regression guard. This runs the real gate and asserts it exits 0
with no "NOT covered" line, so the LOCAL_ONLY-arm defect (#12350) cannot
silently reappear on the ALWAYS_PROTECTED arm. Also fails the parse guard when
ALWAYS_PROTECTED_API_PATTERNS comes back empty, instead of reporting every
regex-covered route as an annotation mismatch.
2026-09-05 02:55:46 -03:00
2 changed files with 42 additions and 2 deletions

View File

@@ -115,12 +115,14 @@ const ALWAYS_PROTECTED_PATTERNS = parsePatterns("ALWAYS_PROTECTED_API_PATTERNS")
if (
LOCAL_ONLY_PREFIXES.length === 0 ||
LOCAL_ONLY_PATTERNS.length === 0 ||
ALWAYS_PROTECTED_PATHS.length === 0
ALWAYS_PROTECTED_PATHS.length === 0 ||
ALWAYS_PROTECTED_PATTERNS.length === 0
) {
console.error(
`[openapi-security-tiers] FAIL — could not parse routeGuard.ts constants ` +
`(prefixes=${LOCAL_ONLY_PREFIXES.length}, patterns=${LOCAL_ONLY_PATTERNS.length}, ` +
`alwaysProtected=${ALWAYS_PROTECTED_PATHS.length})`
`alwaysProtected=${ALWAYS_PROTECTED_PATHS.length}, ` +
`alwaysProtectedPatterns=${ALWAYS_PROTECTED_PATTERNS.length})`
);
process.exit(1);
}

View File

@@ -0,0 +1,38 @@
import test from "node:test";
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
const GATE = join(ROOT, "scripts", "check", "check-openapi-security-tiers.mjs");
function runGate(): { code: number; out: string } {
try {
const out = execFileSync(process.execPath, [GATE], {
cwd: ROOT,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
return { code: 0, out };
} catch (err) {
const e = err as { status?: number; stdout?: string; stderr?: string };
return { code: e.status ?? 1, out: `${e.stdout ?? ""}${e.stderr ?? ""}` };
}
}
// routeGuard protects a path when EITHER list matches — `isAlwaysProtectedPath`
// ORs ALWAYS_PROTECTED_API_PATHS with ALWAYS_PROTECTED_API_PATTERNS. The gate
// used to read only the prefix array, so every regex-covered route was reported
// as an annotation mismatch: the four `{claude,codex}-auth/{export,apply-local}`
// routes turned release/v3.8.51 red while being correctly protected at runtime.
// Same defect class the LOCAL_ONLY arm already had (#12350).
test("openapi-security-tiers accepts routes covered only by ALWAYS_PROTECTED_API_PATTERNS", () => {
const { code, out } = runGate();
assert.ok(
!/has x-always-protected but is NOT/.test(out),
`gate reported an always-protected route as uncovered:\n${out}`
);
assert.equal(code, 0, `gate must pass on a clean tree, got exit ${code}:\n${out}`);
});