Files
dependency-guard/test/verify.test.js
T

156 lines
7.1 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { DEFAULT_POLICY } from "../lib/constants.js";
import { verifyDirectPackage } from "../lib/verify.js";
import { jsonResponse, packageDocument } from "./helpers.js";
const item = { name: "alpha", version: "1.2.3", dev: false };
const now = () => Date.parse("2025-01-01T00:00:00Z");
test("verifies SLSA source repository, tag ref, and existing commit", async () => {
const commit = "abcdef1234567890abcdef1234567890abcdef12";
const statement = {
predicate: {
buildDefinition: {
externalParameters: { workflow: { repository: "https://github.com/example/alpha", ref: "refs/tags/v1.2.3" } },
resolvedDependencies: [{ uri: "git+https://github.com/example/alpha@refs/tags/v1.2.3", digest: { gitCommit: commit } }],
},
},
};
const document = packageDocument({ version: { dist: {
signatures: [{ keyid: "key", sig: "sig" }],
attestations: {
url: "https://registry.npmjs.org/-/npm/v1/attestations/alpha@1.2.3",
provenance: { predicateType: "https://slsa.dev/provenance/v1" },
},
} } });
const fetch = async (url) => {
if (url.includes("attestations")) return jsonResponse({ attestations: [{ predicateType: "https://slsa.dev/provenance/v1", bundle: { dsseEnvelope: { payload: Buffer.from(JSON.stringify(statement)).toString("base64") } } }] });
throw new Error(`unexpected URL ${url}`);
};
const result = await verifyDirectPackage(item, document, structuredClone(DEFAULT_POLICY), {
fetchImpl: fetch,
remoteRefs: async () => [{ sha: commit, ref: "refs/tags/v1.2.3" }],
now,
timeoutMs: 100,
});
assert.deepEqual(result.failures, []);
assert.equal(result.provenance, "slsa");
});
test("accepts branch-built provenance only when a version tag pins its commit", async () => {
const commit = "abcdef1234567890abcdef1234567890abcdef12";
const statement = {
predicate: {
buildDefinition: {
externalParameters: { workflow: { repository: "https://github.com/example/alpha", ref: "refs/heads/main" } },
resolvedDependencies: [{ uri: "git+https://github.com/example/alpha@refs/heads/main", digest: { gitCommit: commit } }],
},
},
};
const document = packageDocument({ version: { dist: {
signatures: [{ keyid: "key", sig: "sig" }],
attestations: {
url: "https://registry.npmjs.org/-/npm/v1/attestations/alpha@1.2.3",
provenance: { predicateType: "https://slsa.dev/provenance/v1" },
},
} } });
const result = await verifyDirectPackage(item, document, structuredClone(DEFAULT_POLICY), {
fetchImpl: async () => jsonResponse({ attestations: [{ predicateType: "https://slsa.dev/provenance/v1", bundle: { dsseEnvelope: { payload: Buffer.from(JSON.stringify(statement)).toString("base64") } } }] }),
remoteRefs: async () => [{ sha: commit, ref: "refs/tags/v1.2.3" }],
now,
timeoutMs: 100,
});
assert.deepEqual(result.failures, []);
});
test("rejects branch-built provenance when no version tag pins its commit", async () => {
const commit = "abcdef1234567890abcdef1234567890abcdef12";
const statement = {
predicate: {
buildDefinition: {
externalParameters: { workflow: { repository: "https://github.com/example/alpha", ref: "refs/heads/main" } },
resolvedDependencies: [{ uri: "git+https://github.com/example/alpha@refs/heads/main", digest: { gitCommit: commit } }],
},
},
};
const document = packageDocument({ version: { dist: {
signatures: [{ keyid: "key", sig: "sig" }],
attestations: {
url: "https://registry.npmjs.org/-/npm/v1/attestations/alpha@1.2.3",
provenance: { predicateType: "https://slsa.dev/provenance/v1" },
},
} } });
const result = await verifyDirectPackage(item, document, structuredClone(DEFAULT_POLICY), {
fetchImpl: async () => jsonResponse({ attestations: [{ predicateType: "https://slsa.dev/provenance/v1", bundle: { dsseEnvelope: { payload: Buffer.from(JSON.stringify(statement)).toString("base64") } } }] }),
remoteRefs: async () => [],
now,
timeoutMs: 100,
});
assert.ok(result.failures.some((message) => message.includes("no Git tag containing 1.2.3")));
});
test("fallback accepts an annotated version tag dereferenced to gitHead", async () => {
const document = packageDocument();
const gitHead = document.versions["1.2.3"].gitHead;
const result = await verifyDirectPackage(item, document, structuredClone(DEFAULT_POLICY), {
remoteRefs: async () => [{ sha: gitHead, ref: "refs/tags/v1.2.3^{}" }],
now,
timeoutMs: 100,
});
assert.deepEqual(result.failures, []);
assert.equal(result.provenance, "gitHead-tag");
});
test("rejects direct lifecycle scripts and untrusted maintainers", async () => {
const document = packageDocument({ version: { scripts: { postinstall: "node setup.js" }, maintainers: [{ name: "mallory" }] } });
const policy = structuredClone(DEFAULT_POLICY);
policy.trustedMaintainers.alpha = ["alice"];
const result = await verifyDirectPackage(item, document, policy, {
remoteRefs: async () => [{ sha: document.versions["1.2.3"].gitHead, ref: "refs/tags/alpha-1.2.3" }],
now,
timeoutMs: 100,
});
assert.ok(result.failures.some((message) => message.includes("lifecycle hook")));
assert.ok(result.failures.some((message) => message.includes("registry maintainer set changed")));
});
test("rejects a missing previously trusted maintainer", async () => {
const document = packageDocument({ version: { maintainers: [{ name: "alice" }] } });
const policy = structuredClone(DEFAULT_POLICY);
policy.trustedMaintainers.alpha = ["alice", "bob"];
const gitHead = document.versions["1.2.3"].gitHead;
const result = await verifyDirectPackage(item, document, policy, {
remoteRefs: async () => [{ sha: gitHead, ref: "refs/tags/v1.2.3" }],
now,
timeoutMs: 100,
});
assert.ok(result.failures.some((message) => message.includes("registry maintainer set changed")));
});
test("accepts only a version-pinned unexpired source evidence exception", async () => {
const document = packageDocument({ version: { gitHead: null } });
const policy = structuredClone(DEFAULT_POLICY);
policy.sourceEvidenceExceptions.alpha = {
version: "1.2.3",
reason: "Upstream does not publish version tags for this package.",
expiresAt: "2025-02-01T00:00:00Z",
};
const result = await verifyDirectPackage(item, document, policy, { now, timeoutMs: 100 });
assert.deepEqual(result.failures, []);
assert.equal(result.provenance, "policy-exception");
assert.ok(result.warnings.some((message) => message.includes("reviewed sourceEvidenceExceptions")));
});
test("rejects an expired source evidence exception", async () => {
const document = packageDocument({ version: { gitHead: null } });
const policy = structuredClone(DEFAULT_POLICY);
policy.sourceEvidenceExceptions.alpha = {
version: "1.2.3",
reason: "Upstream does not publish version tags for this package.",
expiresAt: "2024-12-31T00:00:00Z",
};
const result = await verifyDirectPackage(item, document, policy, { now, timeoutMs: 100 });
assert.ok(result.failures.some((message) => message.includes("gitHead is missing")));
});