@cdxgen/cdxgen 13.0.0 → 13.1.0

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 (170) hide show
  1. package/README.md +26 -13
  2. package/bin/audit.js +6 -0
  3. package/bin/cdxgen.js +140 -8
  4. package/bin/convert.js +7 -0
  5. package/bin/evinse.js +1 -1
  6. package/bin/hbom.js +13 -4
  7. package/bin/tracebom.js +9 -1
  8. package/data/component-tags.json +8 -1
  9. package/data/frameworks-list.json +86 -1
  10. package/data/remediations.json +1144 -0
  11. package/data/rules/asar-archives.yaml +1 -1
  12. package/data/rules/build-fidelity.yaml +782 -0
  13. package/data/rules/package-integrity.yaml +13 -13
  14. package/lib/audit/index.js +48 -4
  15. package/lib/audit/scoring.js +2 -8
  16. package/lib/cli/bomAssembly.js +83 -15
  17. package/lib/cli/cliOptions.js +124 -7
  18. package/lib/cli/index.js +76 -48
  19. package/lib/cli/jsBom.js +32 -3
  20. package/lib/cli/jvmBom.js +1494 -1315
  21. package/lib/cli/managedBom.js +97 -28
  22. package/lib/cli/nativeBom.js +189 -10
  23. package/lib/core/buildLedger.js +799 -0
  24. package/lib/core/deferredExit.js +141 -0
  25. package/lib/core/env.js +69 -1
  26. package/lib/core/fs.js +60 -0
  27. package/lib/core/logger.js +76 -25
  28. package/lib/core/severity.js +16 -0
  29. package/lib/core/syncFileWriter.js +106 -0
  30. package/lib/core/ui.js +46 -2
  31. package/lib/ecosystems/ecosystems.js +9 -0
  32. package/lib/ecosystems/gradleutils.js +355 -15
  33. package/lib/ecosystems/parsers-js.js +26 -0
  34. package/lib/ecosystems/parsers-jvm.js +9 -0
  35. package/lib/ecosystems/parsers-misc.js +16 -2
  36. package/lib/ecosystems/parsers-python.js +171 -35
  37. package/lib/ecosystems/parsers-rust.js +12 -2
  38. package/lib/ecosystems/pypiClassifiers.js +326 -0
  39. package/lib/ecosystems/pythonutils.js +49 -1
  40. package/lib/ecosystems/rubyutils.js +13 -0
  41. package/lib/ecosystems/utils.js +9 -1
  42. package/lib/evinser/evinser.js +30 -12
  43. package/lib/helpers/remote/dependency-track.js +81 -14
  44. package/lib/inventory/auditCategories.js +9 -0
  45. package/lib/inventory/depsUtils.js +72 -0
  46. package/lib/inventory/dosai.js +431 -22
  47. package/lib/inventory/envcontext.js +211 -19
  48. package/lib/inventory/fetchBatch.js +57 -0
  49. package/lib/inventory/jvmToolEnv.js +593 -0
  50. package/lib/inventory/osinfo.js +123 -16
  51. package/lib/inventory/table.js +9 -5
  52. package/lib/inventory/toolRequirements.js +903 -0
  53. package/lib/managers/binary.js +216 -40
  54. package/lib/managers/docker.js +26 -5
  55. package/lib/server/server.js +1 -1
  56. package/lib/stages/postgen/annotationBuilder.js +97 -0
  57. package/lib/stages/postgen/auditBom.js +76 -79
  58. package/lib/stages/postgen/introspection/annotate.js +320 -0
  59. package/lib/stages/postgen/introspection/commandFacts.js +293 -0
  60. package/lib/stages/postgen/introspection/emit.js +192 -0
  61. package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
  62. package/lib/stages/postgen/introspection/markers.js +227 -0
  63. package/lib/stages/postgen/introspection/reflect.js +1214 -0
  64. package/lib/stages/postgen/introspection/report.js +1208 -0
  65. package/lib/stages/postgen/introspection/score.js +1067 -0
  66. package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
  67. package/lib/stages/postgen/parentEdge.js +156 -0
  68. package/lib/stages/postgen/postgen.js +115 -2
  69. package/lib/stages/postgen/ruleEngine.js +17 -0
  70. package/lib/stages/pregen/envAudit.js +42 -2
  71. package/lib/stages/pregen/pregen.js +1171 -6
  72. package/lib/validator/bomValidator.js +11 -2
  73. package/package.json +14 -14
  74. package/types/lib/audit/index.d.ts.map +1 -1
  75. package/types/lib/audit/scoring.d.ts +2 -8
  76. package/types/lib/audit/scoring.d.ts.map +1 -1
  77. package/types/lib/cli/bomAssembly.d.ts +14 -0
  78. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  79. package/types/lib/cli/cliOptions.d.ts +2 -1
  80. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  81. package/types/lib/cli/index.d.ts +8 -0
  82. package/types/lib/cli/index.d.ts.map +1 -1
  83. package/types/lib/cli/jsBom.d.ts.map +1 -1
  84. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  85. package/types/lib/cli/managedBom.d.ts.map +1 -1
  86. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  87. package/types/lib/core/buildLedger.d.ts +272 -0
  88. package/types/lib/core/buildLedger.d.ts.map +1 -0
  89. package/types/lib/core/deferredExit.d.ts +84 -0
  90. package/types/lib/core/deferredExit.d.ts.map +1 -0
  91. package/types/lib/core/env.d.ts +22 -1
  92. package/types/lib/core/env.d.ts.map +1 -1
  93. package/types/lib/core/fs.d.ts +21 -0
  94. package/types/lib/core/fs.d.ts.map +1 -1
  95. package/types/lib/core/logger.d.ts +14 -1
  96. package/types/lib/core/logger.d.ts.map +1 -1
  97. package/types/lib/core/severity.d.ts +11 -0
  98. package/types/lib/core/severity.d.ts.map +1 -0
  99. package/types/lib/core/syncFileWriter.d.ts +33 -0
  100. package/types/lib/core/syncFileWriter.d.ts.map +1 -0
  101. package/types/lib/core/ui.d.ts +1 -1
  102. package/types/lib/core/ui.d.ts.map +1 -1
  103. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  104. package/types/lib/ecosystems/gradleutils.d.ts +33 -0
  105. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  106. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  107. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  108. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  109. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  110. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  111. package/types/lib/ecosystems/pypiClassifiers.d.ts +80 -0
  112. package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
  113. package/types/lib/ecosystems/pythonutils.d.ts +20 -0
  114. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  115. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  116. package/types/lib/ecosystems/utils.d.ts +4 -4
  117. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  118. package/types/lib/evinser/evinser.d.ts +5 -1
  119. package/types/lib/evinser/evinser.d.ts.map +1 -1
  120. package/types/lib/helpers/remote/dependency-track.d.ts +45 -4
  121. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  122. package/types/lib/inventory/auditCategories.d.ts +8 -0
  123. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  124. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  125. package/types/lib/inventory/dosai.d.ts +20 -0
  126. package/types/lib/inventory/dosai.d.ts.map +1 -1
  127. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  128. package/types/lib/inventory/fetchBatch.d.ts +9 -0
  129. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  130. package/types/lib/inventory/jvmToolEnv.d.ts +189 -0
  131. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
  132. package/types/lib/inventory/osinfo.d.ts +83 -2
  133. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  134. package/types/lib/inventory/table.d.ts +4 -1
  135. package/types/lib/inventory/table.d.ts.map +1 -1
  136. package/types/lib/inventory/toolRequirements.d.ts +216 -0
  137. package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
  138. package/types/lib/managers/binary.d.ts +71 -0
  139. package/types/lib/managers/binary.d.ts.map +1 -1
  140. package/types/lib/managers/docker.d.ts.map +1 -1
  141. package/types/lib/stages/postgen/annotationBuilder.d.ts +53 -0
  142. package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
  143. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  144. package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
  145. package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
  146. package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
  147. package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
  148. package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
  149. package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
  150. package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
  151. package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
  152. package/types/lib/stages/postgen/introspection/markers.d.ts +57 -0
  153. package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
  154. package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
  155. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
  156. package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
  157. package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
  158. package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
  159. package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
  160. package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
  161. package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
  162. package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
  163. package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
  164. package/types/lib/stages/postgen/postgen.d.ts +2 -2
  165. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  166. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  167. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  168. package/types/lib/stages/pregen/pregen.d.ts +131 -0
  169. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  170. package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/cli/index.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  recordActivity,
19
19
  setActivityContext,
20
20
  } from "../core/activity.js";
21
+ import { recordDegradation } from "../core/buildLedger.js";
21
22
  import { hasAnyProjectType, PROJECT_TYPE_ALIASES } from "../core/env.js";
22
23
  import {
23
24
  clearFileDiscoveryCache,
@@ -40,7 +41,7 @@ import {
40
41
  } from "../ecosystems/parsers-misc.js";
41
42
  import { isPyLockFile } from "../ecosystems/pylockutils.js";
42
43
  import {
43
- buildDependencyTrackBomPayload,
44
+ buildDependencyTrackBomRequest,
44
45
  getDependencyTrackBomApiUrl,
45
46
  } from "../helpers/remote/dependency-track.js";
46
47
  import { optionIncludesAiInventoryProjectType } from "../inventory/aiInventory.js";
@@ -173,6 +174,11 @@ export {
173
174
  createZigBom,
174
175
  } from "./nativeBom.js";
175
176
 
177
+ import {
178
+ deferFailOnError,
179
+ isDeferredFailOnError,
180
+ } from "../core/deferredExit.js";
181
+
176
182
  const dirName = dirNameStr;
177
183
 
178
184
  const isWin = _platform() === "win32";
@@ -816,6 +822,23 @@ export async function createMultiXBom(pathList, options) {
816
822
  // from here on, so repeated walks of them can share one view of the tree.
817
823
  setDirWalkCacheRoot(options.allLayersExplodedDir);
818
824
  const scanOnePath = async (path) => {
825
+ try {
826
+ return await scanOnePathInner(path);
827
+ } catch (err) {
828
+ if (!isDeferredFailOnError(err)) {
829
+ throw err;
830
+ }
831
+ // The failing path contributes nothing, and the remaining paths still
832
+ // do: fail-on-error scopes the loss to the leg that failed.
833
+ return {
834
+ components: [],
835
+ dependencies: [],
836
+ options: { ...sharedOptions },
837
+ parentSubComponents: [],
838
+ };
839
+ }
840
+ };
841
+ const scanOnePathInner = async (path) => {
819
842
  // Each path accumulates into its own lists and its own view of options, so
820
843
  // that legs which record a parent component or a tool cannot overwrite
821
844
  // another path's. The lists are merged below in pathList order.
@@ -1433,6 +1456,14 @@ export async function createMultiXBom(pathList, options) {
1433
1456
  );
1434
1457
  }
1435
1458
  setProjectTypeActivityContext("jar", path);
1459
+ recordDegradation("jvm.jar.heuristic", {
1460
+ ecosystem: "java",
1461
+ tool: "jar",
1462
+ impact: "versions",
1463
+ path,
1464
+ detail:
1465
+ "Components are being inferred by scanning jar/war/ear files from disk.",
1466
+ });
1436
1467
  bomData = await createJarBom(path, options);
1437
1468
  if (bomData?.bomJson?.components?.length) {
1438
1469
  thoughtLog(
@@ -2292,7 +2323,28 @@ export async function createHBom(_path, options) {
2292
2323
  * @param {Object} options Parse options from the cli
2293
2324
  * @returns {Promise<Object>} Promise resolving to BOM object
2294
2325
  */
2326
+ /**
2327
+ * Generate a BOM for one path, letting a deferred fail-on-error abort return
2328
+ * the empty BOM the flag promises rather than unwinding past the reports.
2329
+ *
2330
+ * @param {string} path Source path.
2331
+ * @param {Object} options CLI options.
2332
+ * @returns {Promise<Object>} BOM NS data.
2333
+ */
2295
2334
  export async function createBom(path, options) {
2335
+ try {
2336
+ return await createBomForPath(path, options);
2337
+ } catch (err) {
2338
+ if (!isDeferredFailOnError(err)) {
2339
+ throw err;
2340
+ }
2341
+ // fail-on-error forbids an incomplete result, so the extractor kept
2342
+ // nothing; the CLI still writes the reports and exits non-zero.
2343
+ return buildBomNSData(options, [], "", { src: path, filename: "" });
2344
+ }
2345
+ }
2346
+
2347
+ async function createBomForPath(path, options) {
2296
2348
  clearFileDiscoveryCache();
2297
2349
  let { projectType } = options;
2298
2350
  if (!projectType) {
@@ -2327,7 +2379,11 @@ export async function createBom(path, options) {
2327
2379
  console.log(
2328
2380
  `OS BOM generation has failed due to problems with exporting the image ${path}`,
2329
2381
  );
2330
- options.failOnError && process.exit(1);
2382
+ deferFailOnError(options, {
2383
+ ecosystem: "container",
2384
+ tool: "docker",
2385
+ detail: "exporting the image for the OS BOM failed",
2386
+ });
2331
2387
  return {};
2332
2388
  }
2333
2389
  isContainerMode = true;
@@ -2367,7 +2423,11 @@ export async function createBom(path, options) {
2367
2423
  console.log(
2368
2424
  `OCI BOM generation has failed due to problems with exporting the image ${path}.`,
2369
2425
  );
2370
- options.failOnError && process.exit(1);
2426
+ deferFailOnError(options, {
2427
+ ecosystem: "container",
2428
+ tool: "docker",
2429
+ detail: "exporting the image for the OCI BOM failed",
2430
+ });
2371
2431
  return {};
2372
2432
  }
2373
2433
  if (DEBUG_MODE) {
@@ -2660,8 +2720,8 @@ export async function submitBom(args, bomContents) {
2660
2720
  });
2661
2721
  return undefined;
2662
2722
  }
2663
- const bomPayload = buildDependencyTrackBomPayload(args, bomContents);
2664
- if (!bomPayload) {
2723
+ const bomRequest = buildDependencyTrackBomRequest(args, bomContents);
2724
+ if (!bomRequest) {
2665
2725
  console.log(
2666
2726
  "Invalid Dependency-Track submission arguments. Provide projectId or projectName (projectVersion defaults to main) and specify parent project either by UUID or by parent project name + version.",
2667
2727
  );
@@ -2674,21 +2734,21 @@ export async function submitBom(args, bomContents) {
2674
2734
  serverUrl,
2675
2735
  "params",
2676
2736
  args.projectName,
2677
- bomPayload.projectVersion,
2737
+ args.projectVersion || "main",
2678
2738
  );
2679
2739
  }
2680
2740
  try {
2681
2741
  const requestOptions = {
2682
- method: "PUT",
2742
+ method: "POST",
2683
2743
  followRedirect: false,
2684
2744
  https: {
2685
2745
  rejectUnauthorized: !args.skipDtTlsCheck,
2686
2746
  },
2687
2747
  headers: {
2688
2748
  "X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
2689
- "Content-Type": "application/json",
2749
+ "Content-Type": bomRequest.contentType,
2690
2750
  },
2691
- json: bomPayload,
2751
+ body: bomRequest.body,
2692
2752
  responseType: "json",
2693
2753
  context: {
2694
2754
  activityIntent: "bom-submit",
@@ -2711,47 +2771,15 @@ export async function submitBom(args, bomContents) {
2711
2771
  );
2712
2772
  } else if (error.response && error.response.statusCode === 405) {
2713
2773
  console.log(
2714
- "Method PUT not allowed on Dependency-Track server. Trying with POST ...",
2774
+ `The server at ${serverUrl} does not accept a multipart/form-data POST on /api/v1/bom. Check that the URL points to a Dependency-Track 4.x or later API server.`,
2775
+ );
2776
+ } else if (DEBUG_MODE) {
2777
+ console.log(
2778
+ "Unable to submit the SBOM to the Dependency-Track server",
2779
+ error,
2715
2780
  );
2716
- // Method not allowed errors
2717
- try {
2718
- return await cdxgenAgent(dependencyTrackApiUrl, {
2719
- method: "POST",
2720
- followRedirect: false,
2721
- https: {
2722
- rejectUnauthorized: !args.skipDtTlsCheck,
2723
- },
2724
- headers: {
2725
- "X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
2726
- "Content-Type": "application/json",
2727
- },
2728
- json: bomPayload,
2729
- responseType: "json",
2730
- context: {
2731
- activityIntent: "bom-submit",
2732
- },
2733
- }).json();
2734
- } catch (error) {
2735
- if (DEBUG_MODE) {
2736
- console.log(
2737
- "Unable to submit the SBOM to the Dependency-Track server using POST method",
2738
- error,
2739
- );
2740
- } else {
2741
- console.log(
2742
- "Unable to submit the SBOM to the Dependency-Track server using POST method",
2743
- );
2744
- }
2745
- }
2746
2781
  } else {
2747
- if (DEBUG_MODE) {
2748
- console.log(
2749
- "Unable to submit the SBOM to the Dependency-Track server using POST method",
2750
- error,
2751
- );
2752
- } else {
2753
- console.log("Unable to submit the SBOM to the Dependency-Track server");
2754
- }
2782
+ console.log("Unable to submit the SBOM to the Dependency-Track server");
2755
2783
  }
2756
2784
  // rethrow error as function is async and we should try to catch it in the caller
2757
2785
  throw error;
package/lib/cli/jsBom.js CHANGED
@@ -11,6 +11,8 @@ import {
11
11
  readEnvironmentVariable,
12
12
  recordSensitiveFileRead,
13
13
  } from "../core/activity.js";
14
+ import { recordDegradation } from "../core/buildLedger.js";
15
+ import { deferFailOnError } from "../core/deferredExit.js";
14
16
  import { hasAnyProjectType, isPackageManagerAllowed } from "../core/env.js";
15
17
  import { getAllFiles, safeExistsSync, safeSpawnSync } from "../core/fs.js";
16
18
  import { thoughtLog } from "../core/logger.js";
@@ -529,6 +531,14 @@ export async function createNodejsBom(path, options) {
529
531
  isSecureMode &&
530
532
  !installArgs.join(" ").includes("--allow-git")
531
533
  ) {
534
+ recordDegradation("js.git-dependency", {
535
+ ecosystem: "npm",
536
+ tool: pkgMgr,
537
+ impact: "integrity",
538
+ command: `${pkgMgr} ${installArgs.join(" ")}`,
539
+ detail:
540
+ "Secure mode npm install may still fetch and install git dependencies.",
541
+ });
532
542
  console.log(
533
543
  "Consider passing '--allow-git=none' via the environment variable NPM_INSTALL_ARGS to prevent any git dependencies from being fetched and installed via npm.",
534
544
  );
@@ -580,6 +590,13 @@ export async function createNodejsBom(path, options) {
580
590
  `**PACKAGE MANAGER**: Let's run the '${pkgMgr}' command with the arguments '${installArgs.join(" ")}' to generate the needed lock files.`,
581
591
  );
582
592
  }
593
+ recordDegradation("js.no-lockfile", {
594
+ ecosystem: "npm",
595
+ tool: pkgMgr,
596
+ impact: "versions",
597
+ detail:
598
+ "No lockfile was found for this project, so the SBOM is generated without pinned dependency versions.",
599
+ });
583
600
  console.warn(
584
601
  "\x1b[1;35mNotice: Generating an SBOM without a lockfile is risky and non-deterministic. Consider generating and committing the lockfile to your repository to ensure reproducible builds and SBOMs.\x1b[0m",
585
602
  );
@@ -640,7 +657,11 @@ export async function createNodejsBom(path, options) {
640
657
  console.log("---------------");
641
658
  console.log(result.stderr);
642
659
  }
643
- options.failOnError && process.exit(1);
660
+ deferFailOnError(options, {
661
+ ecosystem: "npm",
662
+ tool: "npm",
663
+ detail: "the npm command failed",
664
+ });
644
665
  } else {
645
666
  anyInstallSuccess = true;
646
667
  }
@@ -936,7 +957,11 @@ export async function createNodejsBom(path, options) {
936
957
  );
937
958
  if (result.status === 1 || result.error) {
938
959
  console.error(result.stdout, result.stderr);
939
- options.failOnError && process.exit(1);
960
+ deferFailOnError(options, {
961
+ ecosystem: "npm",
962
+ tool: "npm",
963
+ detail: "the yarn or pnpm command failed",
964
+ });
940
965
  }
941
966
  }
942
967
  // Look for shrinkwrap file
@@ -1013,7 +1038,11 @@ export async function createNodejsBom(path, options) {
1013
1038
  pnpmLock,
1014
1039
  "was found!",
1015
1040
  );
1016
- options.failOnError && process.exit(1);
1041
+ deferFailOnError(options, {
1042
+ ecosystem: "npm",
1043
+ tool: "npm",
1044
+ detail: "no JavaScript dependency evidence could be collected",
1045
+ });
1017
1046
  }
1018
1047
  if (
1019
1048
  yarnLockFiles?.length &&