@cyclonedx/cdxgen 12.4.2 → 12.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +6 -0
  2. package/bin/audit.js +7 -0
  3. package/bin/cdxgen.js +48 -2
  4. package/bin/evinse.js +7 -0
  5. package/lib/audit/index.js +165 -2
  6. package/lib/audit/index.poku.js +462 -0
  7. package/lib/cli/index.js +320 -172
  8. package/lib/cli/index.poku.js +81 -0
  9. package/lib/evinser/evinser.js +31 -9
  10. package/lib/helpers/analyzer.js +890 -0
  11. package/lib/helpers/analyzer.poku.js +341 -0
  12. package/lib/helpers/atomUtils.js +445 -0
  13. package/lib/helpers/atomUtils.poku.js +137 -0
  14. package/lib/helpers/bomUtils.js +71 -0
  15. package/lib/helpers/bomUtils.poku.js +45 -0
  16. package/lib/helpers/depsUtils.js +146 -0
  17. package/lib/helpers/depsUtils.poku.js +183 -0
  18. package/lib/helpers/display.js +12 -6
  19. package/lib/helpers/display.poku.js +38 -0
  20. package/lib/helpers/utils.js +653 -191
  21. package/lib/helpers/utils.poku.js +414 -4
  22. package/lib/managers/binary.js +18 -9
  23. package/lib/stages/postgen/postgen.js +215 -0
  24. package/lib/stages/postgen/postgen.poku.js +218 -3
  25. package/lib/validator/bomValidator.js +11 -2
  26. package/package.json +8 -8
  27. package/types/lib/audit/index.d.ts.map +1 -1
  28. package/types/lib/cli/index.d.ts.map +1 -1
  29. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  30. package/types/lib/helpers/atomUtils.d.ts +18 -0
  31. package/types/lib/helpers/atomUtils.d.ts.map +1 -0
  32. package/types/lib/helpers/bomUtils.d.ts +10 -0
  33. package/types/lib/helpers/bomUtils.d.ts.map +1 -1
  34. package/types/lib/helpers/depsUtils.d.ts +9 -0
  35. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  36. package/types/lib/helpers/display.d.ts.map +1 -1
  37. package/types/lib/helpers/dosaiParsers.d.ts.map +1 -1
  38. package/types/lib/helpers/utils.d.ts +19 -0
  39. package/types/lib/helpers/utils.d.ts.map +1 -1
  40. package/types/lib/managers/binary.d.ts +2 -1
  41. package/types/lib/managers/binary.d.ts.map +1 -1
  42. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  43. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -106,7 +106,9 @@ import {
106
106
  parseLeinDep,
107
107
  parseLeiningenData,
108
108
  parseMakeDFile,
109
+ parseMavenArgs,
109
110
  parseMavenTree,
111
+ parseMavenTreeJson,
110
112
  parseMillDependency,
111
113
  parseMinJs,
112
114
  parseMixLockData,
@@ -339,6 +341,32 @@ it("safeSpawnSync() does not classify non-probe -v commands as version checks",
339
341
  }
340
342
  });
341
343
 
344
+ it("safeSpawnSync() blocks shell metacharacters with shell execution", () => {
345
+ const markerFile = path.join(tmpdir(), "cdxgen-safe-spawn-shell-marker");
346
+ const originalConsoleWarn = console.warn;
347
+ const warnings = [];
348
+ rmSync(markerFile, { force: true });
349
+ resetRecordedActivities();
350
+ console.warn = (message) => {
351
+ warnings.push(message);
352
+ };
353
+
354
+ try {
355
+ const result = safeSpawnSync("printf", ["blocked", ">", markerFile], {
356
+ shell: true,
357
+ });
358
+ assert.strictEqual(result.status, 1);
359
+ assert.ok(result.error);
360
+ assert.match(result.error.message, /shell metacharacters/);
361
+ assert.strictEqual(existsSync(markerFile), false);
362
+ assert.ok(warnings.some((warning) => /Security Alert/.test(warning)));
363
+ } finally {
364
+ console.warn = originalConsoleWarn;
365
+ resetRecordedActivities();
366
+ rmSync(markerFile, { force: true });
367
+ }
368
+ });
369
+
342
370
  it("safeSpawnSync() reads CDXGEN_ALLOWED_COMMANDS once per invocation", () => {
343
371
  const originalAllowedCommands = process.env.CDXGEN_ALLOWED_COMMANDS;
344
372
  process.env.CDXGEN_ALLOWED_COMMANDS = "echo-cdxgen-test";
@@ -598,6 +626,37 @@ it("records recursive file discovery activity in dry-run mode", () => {
598
626
  }
599
627
  });
600
628
 
629
+ it("records suspicious discovered paths with shell metacharacters in dry-run mode", () => {
630
+ const tmpRoot = mkdtempSync(
631
+ path.join(tmpdir(), "cdxgen-dry-run-shell-path-"),
632
+ );
633
+ const shellIfs = "$" + "{IFS}";
634
+ const maliciousDirName =
635
+ platform() === "win32"
636
+ ? "evil&echo%CDXGEN_GITURL_E2E_MARKER%&rem"
637
+ : `evil;cd${shellIfs}..;printf${shellIfs}marker>CDXGEN_GITURL_E2E_MARKER;#`;
638
+ const maliciousDir = path.join(tmpRoot, maliciousDirName);
639
+ const pomFile = path.join(maliciousDir, "pom.xml");
640
+ mkdirSync(maliciousDir, { recursive: true });
641
+ writeFileSync(pomFile, "<project />");
642
+ setDryRunMode(true);
643
+ resetRecordedActivities();
644
+ try {
645
+ assert.deepStrictEqual(getAllFiles(tmpRoot, "**/pom.xml"), [pomFile]);
646
+ const suspiciousActivity = getRecordedActivities().find(
647
+ (activity) => activity.classification === "suspicious-path",
648
+ );
649
+ assert.ok(suspiciousActivity);
650
+ assert.strictEqual(suspiciousActivity.risk, "shell-metacharacters");
651
+ assert.strictEqual(suspiciousActivity.target, pomFile);
652
+ assert.match(suspiciousActivity.reason, /shell metacharacters/);
653
+ } finally {
654
+ setDryRunMode(false);
655
+ resetRecordedActivities();
656
+ rmSync(tmpRoot, { force: true, recursive: true });
657
+ }
658
+ });
659
+
601
660
  it("records updated discovery activity when a repeated glob match count changes", () => {
602
661
  const tmpRoot = mkdtempSync(path.join(tmpdir(), "cdxgen-dry-run-glob-"));
603
662
  const packageJsonFile = path.join(tmpRoot, "package.json");
@@ -1508,7 +1567,7 @@ it("parse maven tree", () => {
1508
1567
  name: "com.ibm.icu",
1509
1568
  version: "67.1.0.v20200706-1749",
1510
1569
  qualifiers: { type: "eclipse-plugin" },
1511
- scope: undefined,
1570
+ scope: "excluded",
1512
1571
  properties: [],
1513
1572
  });
1514
1573
  assert.deepStrictEqual(parsedList.dependenciesList.length, 79);
@@ -1550,12 +1609,12 @@ it("parse maven tree", () => {
1550
1609
  encoding: "utf-8",
1551
1610
  }),
1552
1611
  );
1553
- assert.deepStrictEqual(parsedList.pkgList.length, 90);
1612
+ assert.deepStrictEqual(parsedList.pkgList.length, 102);
1554
1613
  assert.deepStrictEqual(
1555
1614
  parsedList.parentComponent["bom-ref"],
1556
1615
  "pkg:maven/org.apache.dubbo/dubbo-spring-boot-starter@3.3.0?type=jar",
1557
1616
  );
1558
- assert.deepStrictEqual(parsedList.dependenciesList.length, 90);
1617
+ assert.deepStrictEqual(parsedList.dependenciesList.length, 102);
1559
1618
  assert.deepStrictEqual(parsedList.dependenciesList[0], {
1560
1619
  ref: "pkg:maven/org.apache.dubbo/dubbo-spring-boot-starter@3.3.0?type=jar",
1561
1620
  dependsOn: [
@@ -1571,11 +1630,109 @@ it("parse maven tree", () => {
1571
1630
  "pkg:maven/org.junit.vintage/junit-vintage-engine@5.8.2?type=jar",
1572
1631
  "pkg:maven/org.mockito/mockito-core@4.11.0?type=jar",
1573
1632
  "pkg:maven/org.mockito/mockito-inline@4.11.0?type=jar",
1574
- "pkg:maven/org.yaml/snakeyaml@1.30?type=jar",
1633
+ "pkg:maven/org.springframework.boot/spring-boot-starter@2.7.18?type=jar",
1575
1634
  ],
1576
1635
  });
1577
1636
  });
1578
1637
 
1638
+ it("parse maven tree optional and repeated dependency edges", () => {
1639
+ const parsedOptional = parseMavenTree(`example:optional-sample:jar:1.0.0
1640
+ \\- org.apache.maven:maven-artifact:jar:3.9.9:compile (optional)
1641
+ \\- org.codehaus.plexus:plexus-utils:jar:3.5.1:compile (optional)
1642
+ `);
1643
+ assert.strictEqual(parsedOptional.pkgList.length, 3);
1644
+ assert.strictEqual(parsedOptional.pkgList[1].scope, "optional");
1645
+ assert.deepStrictEqual(parsedOptional.pkgList[1].properties, []);
1646
+ assert.deepStrictEqual(parsedOptional.dependenciesList[0], {
1647
+ ref: "pkg:maven/example/optional-sample@1.0.0?type=jar",
1648
+ dependsOn: ["pkg:maven/org.apache.maven/maven-artifact@3.9.9?type=jar"],
1649
+ });
1650
+ const parsedDuplicate = parseMavenTree(`example:dup-root:jar:1.0.0
1651
+ +- g:a:jar:1:compile
1652
+ | \\- g:c:jar:1:compile
1653
+ \\- g:b:jar:1:compile
1654
+ \\- g:c:jar:1:compile
1655
+ `);
1656
+ const bDependency = parsedDuplicate.dependenciesList.find(
1657
+ (dep) => dep.ref === "pkg:maven/g/b@1?type=jar",
1658
+ );
1659
+ assert.deepStrictEqual(bDependency.dependsOn, ["pkg:maven/g/c@1?type=jar"]);
1660
+ });
1661
+
1662
+ it("parse maven tree json", () => {
1663
+ const parsedList = parseMavenTreeJson(
1664
+ JSON.stringify({
1665
+ groupId: "example",
1666
+ artifactId: "json-root",
1667
+ version: "1.0.0",
1668
+ type: "jar",
1669
+ scope: "",
1670
+ classifier: "",
1671
+ optional: "false",
1672
+ children: [
1673
+ {
1674
+ groupId: "org.apache.maven",
1675
+ artifactId: "maven-artifact",
1676
+ version: "3.9.9",
1677
+ type: "jar",
1678
+ scope: "compile",
1679
+ classifier: "",
1680
+ optional: "true",
1681
+ children: [
1682
+ {
1683
+ groupId: "org.codehaus.plexus",
1684
+ artifactId: "plexus-utils",
1685
+ version: "3.5.1",
1686
+ type: "jar",
1687
+ scope: "compile",
1688
+ classifier: "",
1689
+ optional: "true",
1690
+ },
1691
+ ],
1692
+ },
1693
+ ],
1694
+ }),
1695
+ );
1696
+ assert.strictEqual(parsedList.pkgList.length, 3);
1697
+ assert.strictEqual(parsedList.pkgList[1].scope, "optional");
1698
+ assert.deepStrictEqual(parsedList.dependenciesList[0], {
1699
+ ref: "pkg:maven/example/json-root@1.0.0?type=jar",
1700
+ dependsOn: ["pkg:maven/org.apache.maven/maven-artifact@3.9.9?type=jar"],
1701
+ });
1702
+ assert.deepStrictEqual(parsedList.dependenciesList[1], {
1703
+ ref: "pkg:maven/org.apache.maven/maven-artifact@3.9.9?type=jar",
1704
+ dependsOn: ["pkg:maven/org.codehaus.plexus/plexus-utils@3.5.1?type=jar"],
1705
+ });
1706
+ assert.deepStrictEqual(parseMavenTreeJson("{not-json"), {});
1707
+ assert.deepStrictEqual(parseMavenTree("{not-json"), {});
1708
+ });
1709
+
1710
+ it("parse maven args", () => {
1711
+ assert.deepStrictEqual(
1712
+ parseMavenArgs(
1713
+ '--settings "/tmp/path with spaces/settings.xml" -P dev,test -DskipTests',
1714
+ ),
1715
+ [
1716
+ "--settings",
1717
+ "/tmp/path with spaces/settings.xml",
1718
+ "-P",
1719
+ "dev,test",
1720
+ "-DskipTests",
1721
+ ],
1722
+ );
1723
+ assert.deepStrictEqual(
1724
+ parseMavenArgs(String.raw`-s C:\Users\me\settings.xml -Dpath=C:\repo\demo`),
1725
+ [
1726
+ "-s",
1727
+ String.raw`C:\Users\me\settings.xml`,
1728
+ String.raw`-Dpath=C:\repo\demo`,
1729
+ ],
1730
+ );
1731
+ assert.deepStrictEqual(parseMavenArgs(String.raw`-Dname=hello\ world`), [
1732
+ "-Dname=hello world",
1733
+ ]);
1734
+ });
1735
+
1579
1736
  // Slow test
1580
1737
  /*
1581
1738
  it("get maven metadata", async () => {
@@ -5147,6 +5304,41 @@ it("parsePomFile", () => {
5147
5304
  assert.deepStrictEqual(data.isQuarkus, false);
5148
5305
  });
5149
5306
 
5307
+ it("parsePomFile maven 4 model", () => {
5308
+ const tempDir = mkdtempSync(path.join(tmpdir(), "cdxgen-maven4-pom-"));
5309
+ const pomFile = path.join(tempDir, "pom.xml");
5310
+ writeFileSync(
5311
+ pomFile,
5312
+ `<project xmlns="http://maven.apache.org/POM/4.1.0" root="true" preserve.model.version="true">
5313
+ <modelVersion>4.1.0</modelVersion>
5314
+ <groupId>example</groupId>
5315
+ <artifactId>maven4-root</artifactId>
5316
+ <version>1.0.0</version>
5317
+ <packaging>pom</packaging>
5318
+ <subprojects><subproject>app</subproject></subprojects>
5319
+ <dependencies>
5320
+ <dependency>
5321
+ <groupId>org.example</groupId>
5322
+ <artifactId>demo</artifactId>
5323
+ <version>1.2.3</version>
5324
+ <type>test-jar</type>
5325
+ <classifier>tests</classifier>
5326
+ <optional>true</optional>
5327
+ </dependency>
5328
+ </dependencies>
5329
+ </project>`,
5330
+ );
5331
+ const data = parsePom(pomFile);
5332
+ assert.deepStrictEqual(data.modules, ["app"]);
5333
+ assert.strictEqual(data.properties.modelVersion, "4.1.0");
5334
+ assert.strictEqual(data.properties.mavenRoot, "true");
5335
+ assert.strictEqual(data.properties.preserveModelVersion, "true");
5336
+ assert.strictEqual(data.dependencies[0].qualifiers.type, "test-jar");
5337
+ assert.strictEqual(data.dependencies[0].qualifiers.classifier, "tests");
5338
+ assert.strictEqual(data.dependencies[0].scope, "optional");
5339
+ rmSync(tempDir, { recursive: true, force: true });
5340
+ });
5341
+
5150
5342
  it("parsePomMetadata", async () => {
5151
5343
  const deps = parsePom("./test/pom.xml");
5152
5344
  const data = await getMvnMetadata(deps.dependencies);
@@ -6111,6 +6303,73 @@ it("parsePnpmWorkspace", async () => {
6111
6303
  assert.deepStrictEqual(Object.keys(wobj.catalogs).length, 217);
6112
6304
  });
6113
6305
 
6306
+ it("parsePnpmWorkspace handles scalar package fixtures", async () => {
6307
+ const projectDir = mkdtempSync(path.join(tmpdir(), "cdxgen-pnpm-workspace-"));
6308
+ try {
6309
+ const workspaceFile = path.join(projectDir, "pnpm-workspace.yaml");
6310
+ writeFileSync(workspaceFile, 'packages: "pkg"\n');
6311
+
6312
+ const wobj = parsePnpmWorkspace(workspaceFile);
6313
+
6314
+ assert.deepStrictEqual(wobj.packages, ["pkg"]);
6315
+ assert.deepStrictEqual(wobj.packagePatterns, ["pkg"]);
6316
+ } finally {
6317
+ rmSync(projectDir, { recursive: true, force: true });
6318
+ }
6319
+ });
6320
+
6321
+ it("parsePnpmWorkspace preserves negated package patterns", async () => {
6322
+ const projectDir = mkdtempSync(path.join(tmpdir(), "cdxgen-pnpm-workspace-"));
6323
+ try {
6324
+ const workspaceFile = path.join(projectDir, "pnpm-workspace.yaml");
6325
+ writeFileSync(workspaceFile, 'packages:\n - "pkg"\n - "!**/test/**"\n');
6326
+
6327
+ const wobj = parsePnpmWorkspace(workspaceFile);
6328
+
6329
+ assert.deepStrictEqual(wobj.packages, ["pkg"]);
6330
+ assert.deepStrictEqual(wobj.packagePatterns, ["pkg"]);
6331
+ assert.deepStrictEqual(wobj.excludePackages, ["**/test/**"]);
6332
+ } finally {
6333
+ rmSync(projectDir, { recursive: true, force: true });
6334
+ }
6335
+ });
6336
+
6337
+ it("parsePnpmLock relativizes pnpm evidence paths from project root", async () => {
6338
+ const projectDir = mkdtempSync(path.join(tmpdir(), "cdxgen-pnpm-lock-"));
6339
+ try {
6340
+ const nestedDir = path.join(projectDir, "packages", "app");
6341
+ mkdirSync(nestedDir, { recursive: true });
6342
+ const lockFile = path.join(nestedDir, "pnpm-lock.yaml");
6343
+ writeFileSync(
6344
+ lockFile,
6345
+ readFileSync("./test/data/pnpm-lock6.yaml", "utf-8"),
6346
+ );
6347
+
6348
+ const parsedList = await parsePnpmLock(
6349
+ lockFile,
6350
+ null,
6351
+ [],
6352
+ {},
6353
+ {},
6354
+ {},
6355
+ {},
6356
+ projectDir,
6357
+ );
6358
+ const firstPkg = parsedList.pkgList[0];
6359
+
6360
+ assert.strictEqual(
6361
+ firstPkg.properties.find((p) => p.name === "SrcFile")?.value,
6362
+ path.join("packages", "app", "pnpm-lock.yaml"),
6363
+ );
6364
+ assert.strictEqual(
6365
+ firstPkg.evidence.identity.methods[0].value,
6366
+ path.join("packages", "app", "pnpm-lock.yaml"),
6367
+ );
6368
+ } finally {
6369
+ rmSync(projectDir, { recursive: true, force: true });
6370
+ }
6371
+ });
6372
+
6114
6373
  it("parsePnpmLock", async () => {
6115
6374
  let parsedList = await parsePnpmLock("./test/pnpm-lock.yaml");
6116
6375
  assert.deepStrictEqual(parsedList.pkgList.length, 1706);
@@ -6599,6 +6858,55 @@ it("pnpmMetadata with scoped packages", async () => {
6599
6858
  assert.deepStrictEqual(babelPkg.name, "@babel/core");
6600
6859
  });
6601
6860
 
6861
+ it("pnpmMetadata enriches split scoped package metadata from pnpm virtual store", async () => {
6862
+ const projectDir = mkdtempSync(path.join(tmpdir(), "cdxgen-pnpm-scope-"));
6863
+ try {
6864
+ const scopedPackageDir = path.join(
6865
+ projectDir,
6866
+ "node_modules",
6867
+ ".pnpm",
6868
+ "@example+scoped-lib@1.2.3",
6869
+ "node_modules",
6870
+ "@example",
6871
+ "scoped-lib",
6872
+ );
6873
+ mkdirSync(scopedPackageDir, { recursive: true });
6874
+ writeFileSync(
6875
+ path.join(scopedPackageDir, "package.json"),
6876
+ JSON.stringify({
6877
+ name: "@example/scoped-lib",
6878
+ version: "1.2.3",
6879
+ description: "Scoped pnpm metadata fixture",
6880
+ license: "MIT",
6881
+ }),
6882
+ );
6883
+
6884
+ const result = await pnpmMetadata(
6885
+ [
6886
+ {
6887
+ group: "@example",
6888
+ name: "scoped-lib",
6889
+ version: "1.2.3",
6890
+ properties: [],
6891
+ },
6892
+ ],
6893
+ path.join(projectDir, "pnpm-lock.yaml"),
6894
+ );
6895
+
6896
+ assert.strictEqual(result[0].description, "Scoped pnpm metadata fixture");
6897
+ assert.strictEqual(result[0].license, "MIT");
6898
+ assert.ok(
6899
+ result[0].properties.some(
6900
+ (p) =>
6901
+ p.name === "LocalNodeModulesPath" &&
6902
+ p.value.includes("@example+scoped-lib@1.2.3"),
6903
+ ),
6904
+ );
6905
+ } finally {
6906
+ rmSync(projectDir, { recursive: true, force: true });
6907
+ }
6908
+ });
6909
+
6602
6910
  it("pnpmMetadata integration with parsePnpmLock", async () => {
6603
6911
  // Test that the integration works by parsing a real pnpm lock file
6604
6912
  const parsedList = await parsePnpmLock("./pnpm-lock.yaml");
@@ -7034,6 +7342,97 @@ it("parseYarnLock", async () => {
7034
7342
  });
7035
7343
  });
7036
7344
 
7345
+ it("parseYarnLock marks root dev, optional, peer and dependency-only closures", async () => {
7346
+ const projectDir = mkdtempSync(path.join(tmpdir(), "cdxgen-yarn-scope-"));
7347
+ try {
7348
+ writeFileSync(
7349
+ path.join(projectDir, "package.json"),
7350
+ JSON.stringify({
7351
+ dependencies: {
7352
+ "runtime-a": "^1.0.0",
7353
+ },
7354
+ devDependencies: {
7355
+ "dev-tool": "^1.0.0",
7356
+ },
7357
+ optionalDependencies: {
7358
+ "optional-root": "^1.0.0",
7359
+ },
7360
+ peerDependencies: {
7361
+ "peer-root": "^1.0.0",
7362
+ },
7363
+ }),
7364
+ );
7365
+ writeFileSync(
7366
+ path.join(projectDir, "yarn.lock"),
7367
+ [
7368
+ "runtime-a@^1.0.0:",
7369
+ ' version "1.0.0"',
7370
+ " dependencies:",
7371
+ ' runtime-child "^1.0.0"',
7372
+ " optionalDependencies:",
7373
+ ' optional-lock-child "^1.0.0"',
7374
+ "",
7375
+ "runtime-child@^1.0.0:",
7376
+ ' version "1.0.0"',
7377
+ "",
7378
+ "optional-lock-child@^1.0.0:",
7379
+ ' version "1.0.0"',
7380
+ "",
7381
+ "dev-tool@^1.0.0:",
7382
+ ' version "1.0.0"',
7383
+ " dependencies:",
7384
+ ' dev-child "^1.0.0"',
7385
+ "",
7386
+ "dev-child@^1.0.0:",
7387
+ ' version "1.0.0"',
7388
+ "",
7389
+ "optional-root@^1.0.0:",
7390
+ ' version "1.0.0"',
7391
+ " dependencies:",
7392
+ ' optional-child "^1.0.0"',
7393
+ "",
7394
+ "optional-child@^1.0.0:",
7395
+ ' version "1.0.0"',
7396
+ "",
7397
+ "peer-root@^1.0.0:",
7398
+ ' version "1.0.0"',
7399
+ " dependencies:",
7400
+ ' peer-child "^1.0.0"',
7401
+ "",
7402
+ "peer-child@^1.0.0:",
7403
+ ' version "1.0.0"',
7404
+ "",
7405
+ ].join("\n"),
7406
+ );
7407
+
7408
+ const parsedList = await parseYarnLock(path.join(projectDir, "yarn.lock"));
7409
+ const byName = Object.fromEntries(
7410
+ parsedList.pkgList.map((pkg) => [pkg.name, pkg]),
7411
+ );
7412
+ const hasProperty = (pkg, name) =>
7413
+ pkg.properties?.some(
7414
+ (property) => property.name === name && property.value === "true",
7415
+ );
7416
+
7417
+ assert.equal(byName["runtime-a"].scope, undefined);
7418
+ assert.equal(byName["runtime-child"].scope, undefined);
7419
+ assert.equal(byName["dev-tool"].scope, "optional");
7420
+ assert.equal(byName["dev-child"].scope, "optional");
7421
+ assert.equal(byName["optional-root"].scope, "optional");
7422
+ assert.equal(byName["optional-child"].scope, "optional");
7423
+ assert.equal(byName["optional-lock-child"].scope, "optional");
7424
+ assert.equal(byName["peer-root"].scope, "optional");
7425
+ assert.equal(byName["peer-child"].scope, "optional");
7426
+ assert.ok(hasProperty(byName["dev-child"], "cdx:npm:package:development"));
7427
+ assert.ok(
7428
+ hasProperty(byName["optional-child"], "cdx:npm:package:optional"),
7429
+ );
7430
+ assert.ok(hasProperty(byName["peer-child"], "cdx:npm:package:peer"));
7431
+ } finally {
7432
+ rmSync(projectDir, { recursive: true, force: true });
7433
+ }
7434
+ });
7435
+
7037
7436
  describe("yarn workspace functionality", () => {
7038
7437
  it("should parse yarn workspace lock file with workspace packages", async () => {
7039
7438
  const mockWorkspacePackages = [
@@ -10295,6 +10694,17 @@ it("jar manifest inference and pom properties parsing tests", () => {
10295
10694
  groupId: "org.apache.commons",
10296
10695
  version: "3.6.1",
10297
10696
  });
10697
+ assert.deepStrictEqual(parsePomProperties("artifactId=demo\ncustom=a=b=c"), {
10698
+ artifactId: "demo",
10699
+ custom: "a=b=c",
10700
+ });
10701
+ assert.deepStrictEqual(
10702
+ parsePomProperties("artifactId=demo\r\r\ncustom=a\r=b\r=c"),
10703
+ {
10704
+ artifactId: "demo",
10705
+ custom: "a=b=c",
10706
+ },
10707
+ );
10298
10708
  });
10299
10709
 
10300
10710
  it("jar group suffix trimming tests", () => {
@@ -11,6 +11,7 @@ import process from "node:process";
11
11
 
12
12
  import { PackageURL } from "packageurl-js";
13
13
 
14
+ import { isCycloneDxComponentTypeEnabled } from "../helpers/bomUtils.js";
14
15
  import { createContainerRiskProperties } from "../helpers/containerRisk.js";
15
16
  import { createGtfoBinsProperties } from "../helpers/gtfobins.js";
16
17
  import {
@@ -608,10 +609,11 @@ export function executeSourcekitten(args) {
608
609
  *
609
610
  * @param src {String} Source directory containing the extracted filesystem.
610
611
  * @param imageConfig {Object} Image configuration containing environment variables, command, entrypoints etc
612
+ * @param options CLI options controlling inventory generation
611
613
  *
612
614
  * @returns {Object} Metadata containing packages, dependencies, etc
613
615
  */
614
- export async function getOSPackages(src, imageConfig) {
616
+ export async function getOSPackages(src, imageConfig, options = {}) {
615
617
  if (isDryRun) {
616
618
  recordActivity({
617
619
  kind: "container",
@@ -1101,7 +1103,10 @@ export async function getOSPackages(src, imageConfig) {
1101
1103
  }
1102
1104
  }
1103
1105
  }
1104
- const rootfsRepositoryInventory = await collectRootfsRepositoryInventory(src);
1106
+ const rootfsRepositoryInventory = await collectRootfsRepositoryInventory(
1107
+ src,
1108
+ options,
1109
+ );
1105
1110
  if (rootfsRepositoryInventory.components.length) {
1106
1111
  pkgList.push(...rootfsRepositoryInventory.components);
1107
1112
  }
@@ -1393,13 +1398,17 @@ function promoteTrivyOsPackageIdentity(component, trivyMetadata) {
1393
1398
  return fallbackProperties;
1394
1399
  }
1395
1400
 
1396
- async function collectRootfsRepositoryInventory(basePath) {
1397
- let { components: trustedKeyComponents, refsByPath } =
1398
- await collectTrustedKeyComponents(basePath);
1399
- trustedKeyComponents = applyTrustMaterialEnhancements(
1400
- trustedKeyComponents,
1401
- collectTrustInspectorRootfsInventory(basePath),
1402
- );
1401
+ async function collectRootfsRepositoryInventory(basePath, options = {}) {
1402
+ let trustedKeyComponents = [];
1403
+ let refsByPath = new Map();
1404
+ if (isCycloneDxComponentTypeEnabled("cryptographic-asset", options)) {
1405
+ ({ components: trustedKeyComponents, refsByPath } =
1406
+ await collectTrustedKeyComponents(basePath));
1407
+ trustedKeyComponents = applyTrustMaterialEnhancements(
1408
+ trustedKeyComponents,
1409
+ collectTrustInspectorRootfsInventory(basePath),
1410
+ );
1411
+ }
1403
1412
  refsByPath = new Map(
1404
1413
  trustedKeyComponents
1405
1414
  .map((component) => {