@cyclonedx/cdxgen
Version:
Creates CycloneDX Software Bill of Materials (SBOM) from source or container image
146 lines (129 loc) • 4.96 kB
JavaScript
import { strict as assert } from "node:assert";
import { describe, test } from "poku";
import { parseBunDescriptor, parseBunLock } from "./bunutils.js";
const FIXTURE = "./test/data/bun/bun.lock";
describe("parseBunDescriptor", async () => {
await test("parses an unscoped descriptor", () => {
assert.deepStrictEqual(parseBunDescriptor("left-pad@1.3.0"), {
group: "",
name: "left-pad",
version: "1.3.0",
});
});
await test("parses a scoped descriptor", () => {
assert.deepStrictEqual(parseBunDescriptor("@babel/parser@7.29.7"), {
group: "@babel",
name: "parser",
version: "7.29.7",
});
});
await test("preserves git specifiers as the version", () => {
const parsed = parseBunDescriptor(
"foo@git+https://github.com/foo/bar#abcdef",
);
assert.deepStrictEqual(parsed.group, "");
assert.deepStrictEqual(parsed.name, "foo");
assert.deepStrictEqual(
parsed.version,
"git+https://github.com/foo/bar#abcdef",
);
});
});
describe("parseBunLock", async () => {
await test("returns empty lists for a missing file", async () => {
const { pkgList, dependenciesList } = await parseBunLock(
"./test/data/bun/does-not-exist.lock",
);
assert.deepStrictEqual(pkgList.length, 0);
assert.deepStrictEqual(dependenciesList.length, 0);
});
await test("parses the fixture lockfile", async () => {
const { pkgList, dependenciesList } = await parseBunLock(FIXTURE);
// 5 packages in the fixture.
assert.deepStrictEqual(pkgList.length, 5);
assert.deepStrictEqual(dependenciesList.length, 5);
const byName = Object.fromEntries(pkgList.map((p) => [p.name, p]));
// Scoped registry package: purl + bom-ref + integrity + distribution ref.
const parser = byName.parser;
assert.deepStrictEqual(parser.group, "@babel");
assert.deepStrictEqual(parser.version, "7.29.7");
assert.deepStrictEqual(parser.purl, "pkg:npm/%40babel/parser@7.29.7");
assert.deepStrictEqual(parser["bom-ref"], "pkg:npm/@babel/parser@7.29.7");
assert.ok(parser._integrity.startsWith("sha512-"));
assert.ok(
parser.externalReferences.some(
(ref) =>
ref.type === "distribution" &&
ref.url ===
"https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
),
);
// Has a binary declared in metadata.
assert.ok(
parser.properties.some(
(prop) => prop.name === "cdx:npm:has_binary" && prop.value === "true",
),
);
// left-pad is a plain production dependency (no optional scope).
assert.deepStrictEqual(byName["left-pad"].scope, undefined);
// typescript is a devDependency: scoped optional + development property.
const ts = byName.typescript;
assert.deepStrictEqual(ts.scope, "optional");
assert.ok(
ts.properties.some((prop) => prop.name === "cdx:npm:package:development"),
);
// fsevents is an optionalDependency: optional scope + optional property + os.
const fsevents = byName.fsevents;
assert.deepStrictEqual(fsevents.scope, "optional");
assert.ok(
fsevents.properties.some(
(prop) => prop.name === "cdx:npm:package:optional",
),
);
assert.ok(
fsevents.properties.some(
(prop) => prop.name === "cdx:npm:os" && prop.value === "darwin",
),
);
// Dependency graph: @babel/parser depends on @babel/types.
const parserDeps = dependenciesList.find(
(d) => d.ref === "pkg:npm/@babel/parser@7.29.7",
);
assert.deepStrictEqual(parserDeps.dependsOn, [
"pkg:npm/@babel/types@7.29.7",
]);
// Every component carries the SrcFile property and manifest-analysis
// evidence pointing at the lockfile.
for (const pkg of pkgList) {
assert.ok(
pkg.properties.some(
(prop) => prop.name === "SrcFile" && prop.value === FIXTURE,
),
);
assert.deepStrictEqual(
pkg.evidence.identity.methods[0].technique,
"manifest-analysis",
);
}
});
await test("adds the root dependency entry when a parent component is given", async () => {
const parentComponent = {
name: "bun-fixture",
version: "1.0.0",
"bom-ref": "pkg:npm/bun-fixture@1.0.0",
};
const { dependenciesList } = await parseBunLock(FIXTURE, {
parentComponent,
});
const rootDeps = dependenciesList.find(
(d) => d.ref === "pkg:npm/bun-fixture@1.0.0",
);
assert.ok(rootDeps);
// Root prod deps: left-pad, @babel/parser and the optional fsevents.
assert.ok(rootDeps.dependsOn.includes("pkg:npm/left-pad@1.3.0"));
assert.ok(rootDeps.dependsOn.includes("pkg:npm/@babel/parser@7.29.7"));
assert.ok(rootDeps.dependsOn.includes("pkg:npm/fsevents@2.3.3"));
// typescript is dev-only and must NOT be a production root dependency.
assert.ok(!rootDeps.dependsOn.includes("pkg:npm/typescript@6.0.3"));
});
});