fix: make security fixtures fail closed

This commit is contained in:
DongHyeonka
2026-08-02 05:40:58 +09:00
parent 76d0ab0f62
commit 100a3bb6ba
8 changed files with 374 additions and 83 deletions
+102
View File
@@ -12,6 +12,11 @@ import {
} from "../../scripts/lib/supply-chain.ts";
import { digestReleaseInputFiles } from "../../scripts/lib/release-input-evidence.ts";
import { findSecretMatches } from "../../scripts/lib/secret-scan.ts";
import {
parseSecretScanIncludedPaths,
selectIncludedInventoryFiles,
} from "../../scripts/lib/secret-scan-policy.ts";
import { checkSecurityFixtures } from "../../scripts/lib/security-fixture-check.ts";
const integrity = `sha512-${Buffer.alloc(64, 7).toString("base64")}`;
const dependency = {
@@ -26,6 +31,103 @@ const dependency = {
};
describe("supply-chain policy", () => {
it.each([
["empty", []],
["empty entry", [""]],
["blank entry", [" "]],
["absolute", ["/src"]],
["backslash", ["src\\file.ts"]],
["dot", ["."]],
["dotdot", [".."]],
["traversal", ["src/../docs"]],
["trailing slash", ["src/"]],
["mixed", ["src", 42]],
["duplicate", ["src", "src"]],
])("rejects %s secret-scan include paths", (_name, includedPaths) => {
expect(() => parseSecretScanIncludedPaths(includedPaths)).toThrow();
});
it("requires every configured include path to match the inventory", () => {
expect(
selectIncludedInventoryFiles(
["README.md", "src/app.ts"],
["src"],
),
).toEqual(["src/app.ts"]);
expect(() =>
selectIncludedInventoryFiles(
["README.md", "src/app.ts"],
["misspelled"],
),
).toThrow(/misspelled/u);
expect(
selectIncludedInventoryFiles(
["README.md", "src/app.ts"],
null,
),
).toEqual(["README.md", "src/app.ts"]);
});
it("rejects a crashed fixture scan and cannot reuse a stale repository artifact", async () => {
const cleaned: string[] = [];
await expect(
checkSecurityFixtures({
createTempDirectory: async () => "/tmp/fresh-security-fixture",
runScan: () => ({
status: 1,
signal: null,
stdout: "",
stderr: "Security scan found 3 blocking result(s).\n",
}),
readArtifact: async (artifactPath) => {
expect(artifactPath).toBe(
"/tmp/fresh-security-fixture/scan-fixture.sarif",
);
throw Object.assign(new Error("fresh artifact missing"), {
code: "ENOENT",
});
},
cleanup: async (directory) => {
cleaned.push(directory);
},
}),
).rejects.toThrow(/fresh artifact missing/u);
expect(cleaned).toEqual(["/tmp/fresh-security-fixture"]);
await expect(
checkSecurityFixtures({
createTempDirectory: async () => "/tmp/fresh-security-fixture",
runScan: () => ({
status: null,
signal: "SIGTERM",
stdout: "",
stderr: "Security scan found 3 blocking result(s).\n",
}),
readArtifact: async () => "{}",
cleanup: async () => undefined,
}),
).rejects.toThrow(/did not fail exactly/u);
});
it("wires the exact security fixture checker as a passing CI gate", async () => {
const gates = JSON.parse(await readFile("config/ci/gates.json", "utf8")) as {
gates: Record<string, { steps: unknown[]; evidence: string[] }>;
};
const securityGate = gates.gates["FE-GATE-013"]!;
expect(securityGate.steps).toContainEqual({
script: "check:security:fixtures",
expect: "pass",
});
expect(securityGate.steps).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ script: "scan:security:fixture" }),
]),
);
expect(securityGate.evidence).not.toContain(
"artifacts/security/scan-fixture.sarif",
);
});
it("uses one fail-closed repository inventory for provenance and secret scanning", async () => {
const [provenanceSource, securitySource] = await Promise.all([
readFile("scripts/generate-supply-chain.ts", "utf8"),