@cyclonedx/cdxgen
Version:
Creates CycloneDX Software Bill of Materials (SBOM) from source or container image
159 lines (144 loc) • 5.03 kB
JavaScript
import { assert, describe, it } from "poku";
import { shouldFail, validateBomAdvanced } from "./index.js";
function richBom() {
return {
bomFormat: "CycloneDX",
specVersion: "1.6",
serialNumber: "urn:uuid:1b671687-395b-41f5-a30f-a58921a69b79",
version: 1,
metadata: {
timestamp: "2024-02-02T00:00:00Z",
tools: {
components: [
{ type: "application", name: "cdxgen", version: "12.0.0" },
],
},
component: {
name: "demo",
version: "1.0.0",
type: "application",
"bom-ref": "pkg:generic/demo@1.0.0",
},
supplier: {
name: "Acme",
contact: [{ email: "psirt@example.com" }],
},
},
components: [
{
type: "library",
name: "lodash",
version: "4.17.21",
purl: "pkg:npm/lodash@4.17.21",
"bom-ref": "pkg:npm/lodash@4.17.21",
licenses: [{ license: { id: "MIT" } }],
hashes: [{ alg: "SHA-256", content: "x" }],
copyright: "© OpenJS",
},
],
dependencies: [
{ ref: "pkg:generic/demo@1.0.0", dependsOn: ["pkg:npm/lodash@4.17.21"] },
{ ref: "pkg:npm/lodash@4.17.21", dependsOn: [] },
],
};
}
describe("validateBomAdvanced", () => {
it("accepts CycloneDX 2.0-dev root fields in compliance evaluation", () => {
const bom20 = richBom();
delete bom20.bomFormat;
bom20.specFormat = "CycloneDX";
bom20.specVersion = "2.0";
const report = validateBomAdvanced(bom20, { schema: false });
assert.strictEqual(
report.allFindings.some((finding) =>
/bomFormat\/specVersion missing/.test(finding.message),
),
false,
);
});
it("returns structural, compliance, and benchmark data", () => {
const report = validateBomAdvanced(richBom(), { schema: false });
assert.strictEqual(typeof report.schemaValid, "boolean");
assert.strictEqual(typeof report.deepValid, "boolean");
assert.ok(Array.isArray(report.findings));
assert.ok(Array.isArray(report.allFindings));
assert.ok(Array.isArray(report.benchmarks));
assert.ok(report.summary);
assert.strictEqual(report.summary.schemaValid, report.schemaValid);
assert.strictEqual(report.summary.deepValid, report.deepValid);
});
it("hides pass findings by default but includes manual", () => {
const report = validateBomAdvanced(richBom(), { schema: false });
for (const f of report.findings) {
assert.notStrictEqual(f.status, "pass");
}
assert.ok(report.findings.some((f) => f.status === "manual"));
});
it("respects minSeverity filter", () => {
const report = validateBomAdvanced(richBom(), {
schema: false,
minSeverity: "high",
includeManual: true,
});
for (const f of report.findings) {
assert.ok(["high", "critical"].includes(f.severity));
}
});
it("marks signatureVerified=false when public key given but BOM unsigned", () => {
const report = validateBomAdvanced(richBom(), {
schema: false,
publicKey: "-----BEGIN PUBLIC KEY-----\nfake\n-----END PUBLIC KEY-----",
});
assert.strictEqual(report.signatureVerified, false);
assert.ok(report.signatureDetails?.error);
});
it("returns signatureVerified=null when no public key supplied", () => {
const report = validateBomAdvanced(richBom(), { schema: false });
assert.strictEqual(report.signatureVerified, null);
});
});
describe("shouldFail", () => {
const fakeReport = (opts) => ({
schemaValid: opts.schemaValid ?? true,
deepValid: opts.deepValid ?? true,
signatureVerified: opts.signatureVerified ?? null,
allFindings: opts.findings || [],
});
it("fails on any finding at or above fail-severity", () => {
const r = fakeReport({
findings: [{ status: "fail", severity: "high", ruleId: "X" }],
});
const { shouldFail: f } = shouldFail(r, { failSeverity: "high" });
assert.strictEqual(f, true);
});
it("does not fail when severity is below threshold", () => {
const r = fakeReport({
findings: [{ status: "fail", severity: "low", ruleId: "X" }],
});
const { shouldFail: f } = shouldFail(r, { failSeverity: "high" });
assert.strictEqual(f, false);
});
it("fails in strict mode on schema invalid", () => {
const r = fakeReport({ schemaValid: false });
assert.strictEqual(
shouldFail(r, { strict: true, failSeverity: "critical" }).shouldFail,
true,
);
});
it("fails when signature required but verification failed", () => {
const r = fakeReport({ signatureVerified: false });
const { shouldFail: f, reason } = shouldFail(r, {
requireSignature: true,
failSeverity: "critical",
});
assert.strictEqual(f, true);
assert.match(reason, /Signature/);
});
it("ignores signature when not required", () => {
const r = fakeReport({ signatureVerified: false });
assert.strictEqual(
shouldFail(r, { failSeverity: "critical" }).shouldFail,
false,
);
});
});