fix: make security fixtures fail closed
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"trackedRoots": [
|
||||
"tests/fixtures/security/secret-detection/forbidden"
|
||||
],
|
||||
"generatedRoots": [],
|
||||
"includedPaths": [
|
||||
"tests/fixtures/security/secret-detection/misspelled"
|
||||
],
|
||||
"excludedPaths": [],
|
||||
"allowlist": []
|
||||
}
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user