chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,640 @@
|
||||
import { createHash, generateKeyPairSync, sign } from "node:crypto";
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
diffDependencyInventories,
|
||||
isValidSha512Integrity,
|
||||
parsePnpmLockfilePackages,
|
||||
supplyChainDigest,
|
||||
validateDependencyReview,
|
||||
validateLicensePolicy,
|
||||
} 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";
|
||||
import {
|
||||
evaluatePromotionEvidence,
|
||||
providerEvidenceSignaturePayload,
|
||||
providerPublicKeyFingerprint,
|
||||
} from "../../scripts/lib/provider-evidence.ts";
|
||||
import {
|
||||
createReleaseCandidateManifest,
|
||||
RELEASE_CANDIDATE_EVIDENCE_PATHS,
|
||||
verifyReleaseCandidate,
|
||||
} from "../../scripts/lib/release-candidate.ts";
|
||||
import { deterministicSupplyChainGeneratedAt } from "../../scripts/lib/supply-chain-time.ts";
|
||||
import {
|
||||
indexCiGateContract,
|
||||
loadCiGateContract,
|
||||
} from "../../scripts/contracts/ci-gates.ts";
|
||||
|
||||
const integrity = `sha512-${Buffer.alloc(64, 7).toString("base64")}`;
|
||||
const dependency = {
|
||||
name: "fixture",
|
||||
version: "1.0.0",
|
||||
direct: true,
|
||||
scope: "production",
|
||||
optional: false,
|
||||
license: "MIT",
|
||||
integrity,
|
||||
dependencies: [],
|
||||
};
|
||||
|
||||
const candidateDistSha256 = "1".repeat(64);
|
||||
const lockfileSha256 = "2".repeat(64);
|
||||
const NOW = Date.parse("2026-08-02T01:00:00.000Z");
|
||||
const sourceIdentity = Object.freeze({
|
||||
revision: "a".repeat(40),
|
||||
sourceSetSha256: "b".repeat(64),
|
||||
});
|
||||
const expectedProviderContext = Object.freeze({
|
||||
run: Object.freeze({ id: "fixture-run", attempt: 1 }),
|
||||
source: sourceIdentity,
|
||||
candidate: Object.freeze({
|
||||
archiveSha256: "3".repeat(64),
|
||||
bundleSha256: "4".repeat(64),
|
||||
distSha256: candidateDistSha256,
|
||||
lockfileSha256,
|
||||
}),
|
||||
vulnerabilityInvocationNonce: "5".repeat(64),
|
||||
provenanceInvocationNonce: "6".repeat(64),
|
||||
secretScanAttestation: Object.freeze({
|
||||
status: "PASS" as const,
|
||||
localEvidenceAssessmentSha256: "7".repeat(64),
|
||||
sourceSetSha256: sourceIdentity.sourceSetSha256,
|
||||
policySha256: "8".repeat(64),
|
||||
sarifSha256: "9".repeat(64),
|
||||
scanInputSha256: "a".repeat(64),
|
||||
}),
|
||||
});
|
||||
|
||||
function signedProviderEvidence(
|
||||
value: Record<string, unknown>,
|
||||
keyId: string,
|
||||
privateKey: ReturnType<typeof generateKeyPairSync>["privateKey"],
|
||||
publicKeyFingerprint: string,
|
||||
) {
|
||||
return {
|
||||
...value,
|
||||
signature: {
|
||||
algorithm: "Ed25519",
|
||||
keyId,
|
||||
publicKeyFingerprint,
|
||||
value: sign(
|
||||
null,
|
||||
providerEvidenceSignaturePayload(value),
|
||||
privateKey,
|
||||
).toString("base64"),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function providerPair(input: Readonly<{
|
||||
vulnerabilityKeys: ReturnType<typeof generateKeyPairSync>;
|
||||
provenanceKeys: ReturnType<typeof generateKeyPairSync>;
|
||||
candidate?: typeof expectedProviderContext.candidate;
|
||||
vulnerabilityFingerprint?: string;
|
||||
provenanceFingerprint?: string;
|
||||
}>) {
|
||||
const candidate = input.candidate ?? expectedProviderContext.candidate;
|
||||
const vulnerabilityFingerprint = input.vulnerabilityFingerprint ??
|
||||
providerPublicKeyFingerprint(input.vulnerabilityKeys.publicKey);
|
||||
const provenanceFingerprint = input.provenanceFingerprint ??
|
||||
providerPublicKeyFingerprint(input.provenanceKeys.publicKey);
|
||||
return {
|
||||
vulnerabilityReport: signedProviderEvidence({
|
||||
schemaVersion: 2,
|
||||
evidenceType: "vulnerability-report",
|
||||
provider: "fixture-vulnerability-provider",
|
||||
issuedAt: "2026-08-02T01:00:00.000Z",
|
||||
expiresAt: "2026-08-02T02:00:00.000Z",
|
||||
run: { ...expectedProviderContext.run, invocationNonce: expectedProviderContext.vulnerabilityInvocationNonce },
|
||||
source: expectedProviderContext.source,
|
||||
candidate,
|
||||
secretScanAttestation: expectedProviderContext.secretScanAttestation,
|
||||
findings: [],
|
||||
}, "fixture-vulnerability-key", input.vulnerabilityKeys.privateKey, vulnerabilityFingerprint),
|
||||
provenanceAttestation: signedProviderEvidence({
|
||||
schemaVersion: 2,
|
||||
evidenceType: "provenance-attestation",
|
||||
provider: "fixture-provenance-provider",
|
||||
issuedAt: "2026-08-02T01:00:00.000Z",
|
||||
expiresAt: "2026-08-02T02:00:00.000Z",
|
||||
run: { ...expectedProviderContext.run, invocationNonce: expectedProviderContext.provenanceInvocationNonce },
|
||||
source: expectedProviderContext.source,
|
||||
candidate,
|
||||
signer: "fixture-workload-identity",
|
||||
subject: { name: "dist", digest: { sha256: candidate.distSha256 } },
|
||||
}, "fixture-provenance-key", input.provenanceKeys.privateKey, provenanceFingerprint),
|
||||
};
|
||||
}
|
||||
|
||||
function providerTrust(
|
||||
keyId: string,
|
||||
publicKey: ReturnType<typeof generateKeyPairSync>["publicKey"],
|
||||
publicKeyFingerprint = providerPublicKeyFingerprint(publicKey),
|
||||
) {
|
||||
return { keyId, publicKey, publicKeyFingerprint };
|
||||
}
|
||||
|
||||
describe("supply-chain policy", () => {
|
||||
it("derives a stable supply-chain timestamp from the immutable build epoch", () => {
|
||||
const input = {
|
||||
generatedAt: "2026-08-01T00:00:00.000Z",
|
||||
sourceDateEpoch: "1785542400",
|
||||
};
|
||||
expect(deterministicSupplyChainGeneratedAt(input)).toBe(
|
||||
"2026-08-01T00:00:00.000Z",
|
||||
);
|
||||
expect(deterministicSupplyChainGeneratedAt(input)).toBe(
|
||||
deterministicSupplyChainGeneratedAt({ ...input }),
|
||||
);
|
||||
expect(() =>
|
||||
deterministicSupplyChainGeneratedAt({
|
||||
generatedAt: "not-a-time",
|
||||
sourceDateEpoch: "1785542400",
|
||||
}),
|
||||
).toThrow(/generatedAt/u);
|
||||
expect(() =>
|
||||
deterministicSupplyChainGeneratedAt({
|
||||
generatedAt: "2026-08-01T00:00:00.000Z",
|
||||
sourceDateEpoch: "1785542401",
|
||||
}),
|
||||
).toThrow(/SOURCE_DATE_EPOCH/u);
|
||||
});
|
||||
|
||||
it("rejects release candidate dist bytes changed after manifest creation", async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "release-candidate-"));
|
||||
try {
|
||||
const rawLockfile = "lockfileVersion: '9.0'\n";
|
||||
const rawLockfileSha256 = createHash("sha256")
|
||||
.update(rawLockfile)
|
||||
.digest("hex");
|
||||
await mkdir(path.join(root, "dist/.vite"), { recursive: true });
|
||||
await writeFile(path.join(root, "dist/app.js"), "immutable\n");
|
||||
await writeFile(path.join(root, "dist/.vite/metadata.json"), "{}\n");
|
||||
await writeFile(path.join(root, "pnpm-lock.yaml"), rawLockfile);
|
||||
for (const file of RELEASE_CANDIDATE_EVIDENCE_PATHS) {
|
||||
if (file === "pnpm-lock.yaml") continue;
|
||||
await mkdir(path.dirname(path.join(root, file)), { recursive: true });
|
||||
await writeFile(
|
||||
path.join(root, file),
|
||||
file === "artifacts/release/dependency-inventory.json"
|
||||
? `${JSON.stringify({ lockfileSha256: rawLockfileSha256 })}\n`
|
||||
: `${file}\n`,
|
||||
);
|
||||
}
|
||||
const manifest = await createReleaseCandidateManifest(root);
|
||||
expect(manifest.lockfileSha256).toBe(rawLockfileSha256);
|
||||
expect(manifest.files).toContainEqual(
|
||||
expect.objectContaining({
|
||||
path: "pnpm-lock.yaml",
|
||||
sha256: rawLockfileSha256,
|
||||
}),
|
||||
);
|
||||
expect(await createReleaseCandidateManifest(root)).toEqual(manifest);
|
||||
expect((await verifyReleaseCandidate(manifest, root)).failures).toEqual(
|
||||
[],
|
||||
);
|
||||
|
||||
await writeFile(path.join(root, "dist/app.js"), "mutated\n");
|
||||
expect(
|
||||
(await verifyReleaseCandidate(manifest, root)).failures,
|
||||
).toEqual(
|
||||
expect.arrayContaining([
|
||||
"release candidate dist digest mismatch",
|
||||
"release candidate bundle digest mismatch",
|
||||
"release candidate file set or file digest mismatch",
|
||||
]),
|
||||
);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a dependency inventory digest that differs from raw pnpm-lock bytes", async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "release-lockfile-"));
|
||||
try {
|
||||
await mkdir(path.join(root, "dist"), { recursive: true });
|
||||
await writeFile(path.join(root, "dist/app.js"), "immutable\n");
|
||||
await writeFile(path.join(root, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n");
|
||||
for (const file of RELEASE_CANDIDATE_EVIDENCE_PATHS) {
|
||||
if (file === "pnpm-lock.yaml") continue;
|
||||
await mkdir(path.dirname(path.join(root, file)), { recursive: true });
|
||||
await writeFile(
|
||||
path.join(root, file),
|
||||
file === "artifacts/release/dependency-inventory.json"
|
||||
? `${JSON.stringify({ lockfileSha256 })}\n`
|
||||
: `${file}\n`,
|
||||
);
|
||||
}
|
||||
await expect(createReleaseCandidateManifest(root)).rejects.toThrow(
|
||||
/raw pnpm-lock digest mismatch/u,
|
||||
);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("fails promotion when external provider evidence is absent", () => {
|
||||
const result = evaluatePromotionEvidence({
|
||||
expected: expectedProviderContext,
|
||||
localStatus: "PASS",
|
||||
vulnerabilityReport: null,
|
||||
provenanceAttestation: null,
|
||||
vulnerabilityTrust: null,
|
||||
provenanceTrust: null,
|
||||
nowEpochMs: () => NOW,
|
||||
});
|
||||
|
||||
expect(result.status).toBe("FAIL_UNVERIFIED");
|
||||
});
|
||||
|
||||
it("passes only signed provider evidence for the exact immutable candidate", () => {
|
||||
const vulnerabilityKeys = generateKeyPairSync("ed25519");
|
||||
const provenanceKeys = generateKeyPairSync("ed25519");
|
||||
const { vulnerabilityReport, provenanceAttestation } = providerPair({
|
||||
vulnerabilityKeys,
|
||||
provenanceKeys,
|
||||
});
|
||||
|
||||
const result = evaluatePromotionEvidence({
|
||||
expected: expectedProviderContext,
|
||||
localStatus: "PASS",
|
||||
vulnerabilityReport,
|
||||
provenanceAttestation,
|
||||
vulnerabilityTrust: providerTrust(
|
||||
"fixture-vulnerability-key",
|
||||
vulnerabilityKeys.publicKey,
|
||||
),
|
||||
provenanceTrust: providerTrust(
|
||||
"fixture-provenance-key",
|
||||
provenanceKeys.publicKey,
|
||||
),
|
||||
nowEpochMs: () => NOW,
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
status: "PASS",
|
||||
vulnerabilityStatus: "PASS",
|
||||
provenanceAttestationStatus: "PASS",
|
||||
failures: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects correctly signed provider evidence for a different digest", () => {
|
||||
const vulnerabilityKeys = generateKeyPairSync("ed25519");
|
||||
const provenanceKeys = generateKeyPairSync("ed25519");
|
||||
const wrongDistSha256 = "3".repeat(64);
|
||||
const { vulnerabilityReport, provenanceAttestation } = providerPair({
|
||||
vulnerabilityKeys,
|
||||
provenanceKeys,
|
||||
candidate: { ...expectedProviderContext.candidate, distSha256: wrongDistSha256 },
|
||||
});
|
||||
|
||||
const result = evaluatePromotionEvidence({
|
||||
expected: expectedProviderContext,
|
||||
localStatus: "PASS",
|
||||
vulnerabilityReport,
|
||||
provenanceAttestation,
|
||||
vulnerabilityTrust: providerTrust("fixture-vulnerability-key", vulnerabilityKeys.publicKey),
|
||||
provenanceTrust: providerTrust("fixture-provenance-key", provenanceKeys.publicKey),
|
||||
nowEpochMs: () => NOW,
|
||||
});
|
||||
|
||||
expect(result.status).toBe("FAIL_UNVERIFIED");
|
||||
expect(result.failures).toEqual(
|
||||
expect.arrayContaining([
|
||||
"vulnerability report candidate identity mismatch",
|
||||
"provenance attestation candidate identity mismatch",
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects candidate bytes changed after provider attestation", () => {
|
||||
const vulnerabilityKeys = generateKeyPairSync("ed25519");
|
||||
const provenanceKeys = generateKeyPairSync("ed25519");
|
||||
const { vulnerabilityReport, provenanceAttestation } = providerPair({
|
||||
vulnerabilityKeys,
|
||||
provenanceKeys,
|
||||
});
|
||||
|
||||
const result = evaluatePromotionEvidence({
|
||||
expected: {
|
||||
...expectedProviderContext,
|
||||
candidate: { ...expectedProviderContext.candidate, distSha256: "4".repeat(64) },
|
||||
},
|
||||
localStatus: "PASS",
|
||||
vulnerabilityReport,
|
||||
provenanceAttestation,
|
||||
vulnerabilityTrust: providerTrust("fixture-vulnerability-key", vulnerabilityKeys.publicKey),
|
||||
provenanceTrust: providerTrust("fixture-provenance-key", provenanceKeys.publicKey),
|
||||
nowEpochMs: () => NOW,
|
||||
});
|
||||
|
||||
expect(result.status).toBe("FAIL_UNVERIFIED");
|
||||
expect(result.failures).toContain("vulnerability report candidate identity mismatch");
|
||||
});
|
||||
|
||||
it("rejects Ed448 keys mislabeled as Ed25519 evidence", () => {
|
||||
const vulnerabilityKeys = generateKeyPairSync("ed448");
|
||||
const provenanceKeys = generateKeyPairSync("ed448");
|
||||
const fakeFingerprint = `sha256:${"7".repeat(64)}`;
|
||||
const { vulnerabilityReport, provenanceAttestation } = providerPair({
|
||||
vulnerabilityKeys,
|
||||
provenanceKeys,
|
||||
vulnerabilityFingerprint: fakeFingerprint,
|
||||
provenanceFingerprint: fakeFingerprint,
|
||||
});
|
||||
|
||||
expect(
|
||||
evaluatePromotionEvidence({
|
||||
expected: expectedProviderContext,
|
||||
localStatus: "PASS",
|
||||
vulnerabilityReport,
|
||||
provenanceAttestation,
|
||||
vulnerabilityTrust: {
|
||||
keyId: "fixture-vulnerability-key",
|
||||
publicKey: vulnerabilityKeys.publicKey,
|
||||
publicKeyFingerprint: fakeFingerprint,
|
||||
},
|
||||
provenanceTrust: {
|
||||
keyId: "fixture-provenance-key",
|
||||
publicKey: provenanceKeys.publicKey,
|
||||
publicKeyFingerprint: fakeFingerprint,
|
||||
},
|
||||
nowEpochMs: () => NOW,
|
||||
}).status,
|
||||
).toBe("FAIL_UNVERIFIED");
|
||||
});
|
||||
|
||||
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 contract = await loadCiGateContract(process.cwd(), {
|
||||
mode: process.env.CI_CONTRACT_MODE === "removal-fixture"
|
||||
? "removal-fixture"
|
||||
: "canonical",
|
||||
});
|
||||
const index = indexCiGateContract(contract);
|
||||
const securityGate = index.gates.get("FE-GATE-013");
|
||||
expect(securityGate).toBeDefined();
|
||||
const commands = securityGate!.commandIds.map((commandId) =>
|
||||
index.commands.get(commandId),
|
||||
);
|
||||
expect(commands).toContainEqual(
|
||||
expect.objectContaining({
|
||||
script: "check:security:fixtures",
|
||||
expect: "pass",
|
||||
}),
|
||||
);
|
||||
expect(commands).not.toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ script: "scan:security:fixture" }),
|
||||
]),
|
||||
);
|
||||
const evidence = securityGate!.evidenceArtifactIds.map(
|
||||
(artifactId) => index.artifacts.get(artifactId)?.path,
|
||||
);
|
||||
expect(evidence).not.toContain(
|
||||
"artifacts/security/scan-fixture.sarif",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses one fail-closed repository inventory for provenance and secret scanning", async () => {
|
||||
const [provenanceSource, securityCliSource, securityEvaluatorSource] =
|
||||
await Promise.all([
|
||||
readFile("scripts/generate-supply-chain.ts", "utf8"),
|
||||
readFile("scripts/security-scan.ts", "utf8"),
|
||||
readFile("scripts/lib/secret-scan-evaluator.ts", "utf8"),
|
||||
]);
|
||||
expect(securityCliSource).toContain("evaluateRepositorySecretScan");
|
||||
for (const source of [provenanceSource, securityEvaluatorSource]) {
|
||||
expect(source).toContain("buildRepositoryFileInventory");
|
||||
expect(source).not.toContain("async function filesWithin");
|
||||
}
|
||||
});
|
||||
|
||||
it("binds provenance digest behavior to tracked files outside policy roots", async () => {
|
||||
const contents = new Map([
|
||||
["src/app.ts", Buffer.from("app\n")],
|
||||
["README.md", Buffer.from("one\n")],
|
||||
]);
|
||||
const first = await digestReleaseInputFiles(
|
||||
["README.md", "src/app.ts"],
|
||||
async (file) => contents.get(file)!,
|
||||
);
|
||||
contents.set("README.md", Buffer.from("two\n"));
|
||||
const second = await digestReleaseInputFiles(
|
||||
["README.md", "src/app.ts"],
|
||||
async (file) => contents.get(file)!,
|
||||
);
|
||||
expect(second).not.toBe(first);
|
||||
});
|
||||
|
||||
it("detects every forbidden secret fixture, including quoted JSON keys", async () => {
|
||||
const fixtureRoot = "tests/fixtures/security/secret-detection/forbidden";
|
||||
const findings = (
|
||||
await Promise.all(
|
||||
["source.ts", "dist.ts", "config.json"].map(async (file) =>
|
||||
findSecretMatches(
|
||||
`${fixtureRoot}/${file}`,
|
||||
await readFile(`${fixtureRoot}/${file}`, "utf8"),
|
||||
),
|
||||
),
|
||||
)
|
||||
).flat();
|
||||
expect(findings.map((finding) => [finding.file, finding.ruleId])).toEqual([
|
||||
[`${fixtureRoot}/source.ts`, "aws-access-key"],
|
||||
[`${fixtureRoot}/dist.ts`, "assigned-secret"],
|
||||
[`${fixtureRoot}/config.json`, "assigned-secret"],
|
||||
]);
|
||||
});
|
||||
|
||||
it("covers every mandatory release input in the secret scan policy", async () => {
|
||||
const policy = JSON.parse(
|
||||
await readFile("config/security/secret-scan-policy.json", "utf8"),
|
||||
) as { trackedRoots: string[] };
|
||||
expect(policy.trackedRoots).toEqual(
|
||||
expect.arrayContaining([
|
||||
"index.html",
|
||||
".dependency-cruiser.json",
|
||||
".nvmrc",
|
||||
".npmrc",
|
||||
"eslint.config.ts",
|
||||
"package.json",
|
||||
"pnpm-lock.yaml",
|
||||
"pnpm-workspace.yaml",
|
||||
"scripts",
|
||||
"schemas",
|
||||
"config",
|
||||
".gitea/workflows/quality-gates.yml",
|
||||
"vite.config.ts",
|
||||
"vite.service-worker.config.ts",
|
||||
"vitest.config.ts",
|
||||
"playwright.config.ts",
|
||||
"playwright.capabilities.config.ts",
|
||||
"playwright.dev.config.ts",
|
||||
"playwright.storybook.config.ts",
|
||||
"playwright.visual.config.ts",
|
||||
"tsconfig.json",
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.base.json",
|
||||
"tsconfig.node.json",
|
||||
"tsconfig.recipes.json",
|
||||
"tsconfig.service-worker.json",
|
||||
"tsconfig.test.json",
|
||||
"tsconfig.web-worker.json",
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("parses every top-level lockfile package and validates SRI", () => {
|
||||
const parsed = parsePnpmLockfilePackages(`
|
||||
packages:
|
||||
|
||||
'@scope/one@1.0.0':
|
||||
resolution: {integrity: ${integrity}}
|
||||
|
||||
two@2.0.0:
|
||||
resolution: {integrity: ${integrity}}
|
||||
|
||||
snapshots:
|
||||
`);
|
||||
expect(parsed).toEqual([
|
||||
{ name: "@scope/one", version: "1.0.0", integrity },
|
||||
{ name: "two", version: "2.0.0", integrity },
|
||||
]);
|
||||
expect(parsed.every((entry) => isValidSha512Integrity(entry.integrity))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps inventory digests stable when dependency ordering changes", () => {
|
||||
const other = { ...dependency, name: "other" };
|
||||
expect(supplyChainDigest([dependency, other])).toBe(
|
||||
supplyChainDigest([other, dependency]),
|
||||
);
|
||||
});
|
||||
|
||||
it("calculates actual additions and requires independent high-risk review", () => {
|
||||
const before = { dependencies: [] };
|
||||
const after = { dependencies: [dependency] };
|
||||
const diff = diffDependencyInventories(before, after);
|
||||
expect(diff.added).toEqual(["fixture@1.0.0"]);
|
||||
expect(
|
||||
validateDependencyReview(diff, after, {
|
||||
changes: [
|
||||
{
|
||||
changeId: "add:fixture@1.0.0",
|
||||
owner: "one",
|
||||
reviewer: "one",
|
||||
reason: "fixture",
|
||||
rollback: "remove",
|
||||
},
|
||||
],
|
||||
}).passed,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("allows explicit policy licenses and rejects denied licenses", () => {
|
||||
expect(
|
||||
validateLicensePolicy(
|
||||
{ dependencies: [dependency] },
|
||||
{ allowedLicenses: ["MIT"], deniedLicensePatterns: ["AGPL"] },
|
||||
).passed,
|
||||
).toBe(true);
|
||||
expect(
|
||||
validateLicensePolicy(
|
||||
{
|
||||
dependencies: [{ ...dependency, license: "AGPL-3.0" }],
|
||||
},
|
||||
{ allowedLicenses: ["MIT"], deniedLicensePatterns: ["AGPL"] },
|
||||
).passed,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user