@cyclonedx/cdxgen 12.3.3 → 12.4.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 (157) hide show
  1. package/README.md +64 -22
  2. package/bin/audit.js +21 -7
  3. package/bin/cdxgen.js +238 -116
  4. package/bin/convert.js +28 -13
  5. package/bin/hbom.js +490 -0
  6. package/bin/repl.js +580 -29
  7. package/bin/validate.js +34 -4
  8. package/bin/verify.js +40 -5
  9. package/data/README.md +298 -25
  10. package/data/component-tags.json +6 -0
  11. package/data/crypto-oid.json +16 -0
  12. package/data/predictive-audit-allowlist.json +11 -0
  13. package/data/queries-darwin.json +12 -1
  14. package/data/queries-win.json +7 -1
  15. package/data/queries.json +39 -2
  16. package/data/rules/ai-agent-governance.yaml +16 -0
  17. package/data/rules/asar-archives.yaml +150 -0
  18. package/data/rules/chrome-extensions.yaml +8 -0
  19. package/data/rules/ci-permissions.yaml +42 -18
  20. package/data/rules/container-risk.yaml +14 -7
  21. package/data/rules/dependency-sources.yaml +11 -0
  22. package/data/rules/hbom-compliance.yaml +325 -0
  23. package/data/rules/hbom-performance.yaml +307 -0
  24. package/data/rules/hbom-security.yaml +248 -0
  25. package/data/rules/host-topology.yaml +165 -0
  26. package/data/rules/mcp-servers.yaml +18 -3
  27. package/data/rules/obom-runtime.yaml +907 -22
  28. package/data/rules/package-integrity.yaml +14 -0
  29. package/data/rules/rootfs-hardening.yaml +179 -0
  30. package/data/rules/vscode-extensions.yaml +9 -0
  31. package/lib/audit/index.js +209 -8
  32. package/lib/audit/index.poku.js +332 -0
  33. package/lib/audit/reporters.js +222 -0
  34. package/lib/audit/targets.js +146 -1
  35. package/lib/audit/targets.poku.js +186 -0
  36. package/lib/cli/asar.poku.js +328 -0
  37. package/lib/cli/index.js +506 -88
  38. package/lib/cli/index.poku.js +1352 -212
  39. package/lib/evinser/evinser.js +14 -9
  40. package/lib/helpers/analyzer.js +1406 -29
  41. package/lib/helpers/analyzer.poku.js +342 -0
  42. package/lib/helpers/analyzerScope.js +712 -0
  43. package/lib/helpers/asarutils.js +1556 -0
  44. package/lib/helpers/asarutils.poku.js +443 -0
  45. package/lib/helpers/auditCategories.js +12 -0
  46. package/lib/helpers/auditCategories.poku.js +32 -0
  47. package/lib/helpers/cbomutils.js +271 -1
  48. package/lib/helpers/cbomutils.poku.js +248 -5
  49. package/lib/helpers/display.js +291 -1
  50. package/lib/helpers/display.poku.js +149 -0
  51. package/lib/helpers/evidenceUtils.js +58 -0
  52. package/lib/helpers/evidenceUtils.poku.js +54 -0
  53. package/lib/helpers/exportUtils.js +9 -0
  54. package/lib/helpers/gtfobins.js +142 -8
  55. package/lib/helpers/gtfobins.poku.js +24 -1
  56. package/lib/helpers/hbom.js +710 -0
  57. package/lib/helpers/hbom.poku.js +496 -0
  58. package/lib/helpers/hbomAnalysis.js +268 -0
  59. package/lib/helpers/hbomAnalysis.poku.js +249 -0
  60. package/lib/helpers/hbomLoader.js +35 -0
  61. package/lib/helpers/hostTopology.js +803 -0
  62. package/lib/helpers/hostTopology.poku.js +363 -0
  63. package/lib/helpers/inventoryStats.js +69 -0
  64. package/lib/helpers/inventoryStats.poku.js +86 -0
  65. package/lib/helpers/lolbas.js +19 -1
  66. package/lib/helpers/lolbas.poku.js +23 -0
  67. package/lib/helpers/osqueryTransform.js +47 -0
  68. package/lib/helpers/osqueryTransform.poku.js +47 -0
  69. package/lib/helpers/plugins.js +349 -0
  70. package/lib/helpers/plugins.poku.js +57 -0
  71. package/lib/helpers/protobom.js +156 -45
  72. package/lib/helpers/protobom.poku.js +140 -5
  73. package/lib/helpers/remote/dependency-track.js +36 -3
  74. package/lib/helpers/remote/dependency-track.poku.js +44 -0
  75. package/lib/helpers/source.js +24 -0
  76. package/lib/helpers/source.poku.js +32 -0
  77. package/lib/helpers/utils.js +1438 -93
  78. package/lib/helpers/utils.poku.js +846 -4
  79. package/lib/managers/binary.e2e.poku.js +367 -0
  80. package/lib/managers/binary.js +2293 -353
  81. package/lib/managers/binary.poku.js +1699 -1
  82. package/lib/managers/docker.js +201 -79
  83. package/lib/managers/docker.poku.js +337 -12
  84. package/lib/server/server.js +2 -27
  85. package/lib/stages/postgen/annotator.js +38 -0
  86. package/lib/stages/postgen/annotator.poku.js +107 -1
  87. package/lib/stages/postgen/auditBom.js +121 -18
  88. package/lib/stages/postgen/auditBom.poku.js +1366 -31
  89. package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
  90. package/lib/stages/postgen/postgen.js +192 -1
  91. package/lib/stages/postgen/postgen.poku.js +321 -0
  92. package/lib/stages/postgen/ruleEngine.js +116 -0
  93. package/lib/stages/pregen/envAudit.js +14 -3
  94. package/package.json +23 -21
  95. package/types/bin/hbom.d.ts +3 -0
  96. package/types/bin/hbom.d.ts.map +1 -0
  97. package/types/bin/repl.d.ts.map +1 -1
  98. package/types/lib/audit/index.d.ts +44 -0
  99. package/types/lib/audit/index.d.ts.map +1 -1
  100. package/types/lib/audit/reporters.d.ts +16 -0
  101. package/types/lib/audit/reporters.d.ts.map +1 -1
  102. package/types/lib/audit/targets.d.ts.map +1 -1
  103. package/types/lib/cli/index.d.ts +16 -0
  104. package/types/lib/cli/index.d.ts.map +1 -1
  105. package/types/lib/evinser/evinser.d.ts +4 -0
  106. package/types/lib/evinser/evinser.d.ts.map +1 -1
  107. package/types/lib/helpers/analyzer.d.ts +33 -0
  108. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  109. package/types/lib/helpers/analyzerScope.d.ts +11 -0
  110. package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
  111. package/types/lib/helpers/asarutils.d.ts +34 -0
  112. package/types/lib/helpers/asarutils.d.ts.map +1 -0
  113. package/types/lib/helpers/auditCategories.d.ts +5 -0
  114. package/types/lib/helpers/auditCategories.d.ts.map +1 -1
  115. package/types/lib/helpers/cbomutils.d.ts +3 -2
  116. package/types/lib/helpers/cbomutils.d.ts.map +1 -1
  117. package/types/lib/helpers/display.d.ts.map +1 -1
  118. package/types/lib/helpers/evidenceUtils.d.ts +8 -0
  119. package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
  120. package/types/lib/helpers/exportUtils.d.ts.map +1 -1
  121. package/types/lib/helpers/gtfobins.d.ts +8 -0
  122. package/types/lib/helpers/gtfobins.d.ts.map +1 -1
  123. package/types/lib/helpers/hbom.d.ts +49 -0
  124. package/types/lib/helpers/hbom.d.ts.map +1 -0
  125. package/types/lib/helpers/hbomAnalysis.d.ts +62 -0
  126. package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
  127. package/types/lib/helpers/hbomLoader.d.ts +7 -0
  128. package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
  129. package/types/lib/helpers/hostTopology.d.ts +12 -0
  130. package/types/lib/helpers/hostTopology.d.ts.map +1 -0
  131. package/types/lib/helpers/inventoryStats.d.ts +11 -0
  132. package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
  133. package/types/lib/helpers/lolbas.d.ts.map +1 -1
  134. package/types/lib/helpers/osqueryTransform.d.ts +3 -0
  135. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
  136. package/types/lib/helpers/plugins.d.ts +58 -0
  137. package/types/lib/helpers/plugins.d.ts.map +1 -0
  138. package/types/lib/helpers/protobom.d.ts +3 -4
  139. package/types/lib/helpers/protobom.d.ts.map +1 -1
  140. package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
  141. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  142. package/types/lib/helpers/source.d.ts.map +1 -1
  143. package/types/lib/helpers/utils.d.ts +45 -8
  144. package/types/lib/helpers/utils.d.ts.map +1 -1
  145. package/types/lib/managers/binary.d.ts +5 -0
  146. package/types/lib/managers/binary.d.ts.map +1 -1
  147. package/types/lib/managers/docker.d.ts.map +1 -1
  148. package/types/lib/server/server.d.ts +2 -1
  149. package/types/lib/server/server.d.ts.map +1 -1
  150. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  151. package/types/lib/stages/postgen/auditBom.d.ts +26 -1
  152. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  153. package/types/lib/stages/postgen/postgen.d.ts +2 -1
  154. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  155. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  156. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  157. package/data/spdx-model-v3.0.1.jsonld +0 -15999
package/lib/cli/index.js CHANGED
@@ -11,7 +11,6 @@ import { platform as _platform, arch, homedir } from "node:os";
11
11
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
12
12
  import process from "node:process";
13
13
 
14
- import got from "got";
15
14
  import { PackageURL } from "packageurl-js";
16
15
  import { gte, lte } from "semver";
17
16
  import { parse } from "ssri";
@@ -35,8 +34,15 @@ import {
35
34
  detectPythonMcpInventory,
36
35
  findJSImportsExports,
37
36
  } from "../helpers/analyzer.js";
37
+ import {
38
+ cleanupAsarTempDir,
39
+ extractAsarToTempDir,
40
+ parseAsarArchive,
41
+ rewriteExtractedArchivePaths,
42
+ } from "../helpers/asarutils.js";
38
43
  import { expandBomAuditCategories } from "../helpers/auditCategories.js";
39
44
  import { parseCaxaMetadata } from "../helpers/caxa.js";
45
+ import { collectSourceCryptoComponents } from "../helpers/cbomutils.js";
40
46
  import {
41
47
  CHROME_EXTENSION_PURL_TYPE,
42
48
  collectChromeExtensionsFromPath,
@@ -49,15 +55,19 @@ import {
49
55
  trimComponents,
50
56
  } from "../helpers/depsUtils.js";
51
57
  import { GIT_COMMAND } from "../helpers/envcontext.js";
52
- import { thoughtLog } from "../helpers/logger.js";
53
58
  import {
54
- classifyMcpReference,
55
- enrichComponentWithMcpMetadata,
56
- } from "../helpers/mcp.js";
59
+ createHbomDocument,
60
+ ensureHbomRuntimeSupport,
61
+ ensureNoMixedHbomProjectTypes,
62
+ hasHbomProjectType,
63
+ } from "../helpers/hbom.js";
64
+ import { mergeHostInventoryBoms } from "../helpers/hostTopology.js";
65
+ import { thoughtLog } from "../helpers/logger.js";
66
+ import { enrichComponentWithMcpMetadata } from "../helpers/mcp.js";
57
67
  import { isPyLockFile } from "../helpers/pylockutils.js";
58
68
  import {
59
69
  buildDependencyTrackBomPayload,
60
- getDependencyTrackBomUrl,
70
+ getDependencyTrackBomApiUrl,
61
71
  } from "../helpers/remote/dependency-track.js";
62
72
  import { table } from "../helpers/table.js";
63
73
  import {
@@ -71,6 +81,7 @@ import {
71
81
  CARGO_CMD,
72
82
  CDXGEN_VERSION,
73
83
  CLJ_CMD,
84
+ cdxgenAgent,
74
85
  checksumFile,
75
86
  cleanupPlugin,
76
87
  collectGemModuleNames,
@@ -111,6 +122,7 @@ import {
111
122
  getTmpDir,
112
123
  hasAnyProjectType,
113
124
  includeMavenTestScope,
125
+ isAllowedHttpHost,
114
126
  isDryRun,
115
127
  isFeatureEnabled,
116
128
  isMac,
@@ -201,6 +213,7 @@ import {
201
213
  readZipEntry,
202
214
  recomputeScope,
203
215
  recordActivity,
216
+ recordSensitiveFileRead,
204
217
  resetActivityContext,
205
218
  SWIFT_CMD,
206
219
  safeExistsSync,
@@ -226,10 +239,12 @@ import {
226
239
  VSCODE_EXTENSION_PURL_TYPE,
227
240
  } from "../helpers/vsixutils.js";
228
241
  import {
242
+ enrichOSComponentsWithTrustData,
229
243
  executeOsQuery,
230
244
  getBinaryBom,
231
245
  getDotnetSlices,
232
246
  getOSPackages,
247
+ getPluginToolComponents,
233
248
  } from "../managers/binary.js";
234
249
  import {
235
250
  addSkippedSrcFiles,
@@ -369,6 +384,18 @@ const shouldIncludeNodeModulesDir = (options = {}, baseProjectTypes = []) => {
369
384
  );
370
385
  };
371
386
 
387
+ const hasExplicitProjectTypeSelection = (options, baseProjectType) => {
388
+ options = options || {};
389
+ const projectTypes = Array.isArray(options.projectType)
390
+ ? options.projectType
391
+ : options.projectType
392
+ ? [options.projectType]
393
+ : [];
394
+ return projectTypes.some((selectedProjectType) =>
395
+ PROJECT_TYPE_ALIASES[baseProjectType]?.includes(selectedProjectType),
396
+ );
397
+ };
398
+
372
399
  const determineParentComponent = (options) => {
373
400
  let parentComponent;
374
401
  if (options.parentComponent && Object.keys(options.parentComponent).length) {
@@ -840,6 +867,18 @@ function addMetadata(parentComponent = {}, options = {}, context = {}) {
840
867
  value: options.allOSComponentTypes.sort().join("\\n"),
841
868
  });
842
869
  }
870
+ if (Number.isInteger(options?.unpackagedExecutableCount)) {
871
+ mproperties.push({
872
+ name: "cdx:container:unpackagedExecutableCount",
873
+ value: String(options.unpackagedExecutableCount),
874
+ });
875
+ }
876
+ if (Number.isInteger(options?.unpackagedSharedLibraryCount)) {
877
+ mproperties.push({
878
+ name: "cdx:container:unpackagedSharedLibraryCount",
879
+ value: String(options.unpackagedSharedLibraryCount),
880
+ });
881
+ }
843
882
  // Should we move these to formulation?
844
883
  if (options?.bundledSdks?.length) {
845
884
  for (const sdk of options.bundledSdks) {
@@ -943,6 +982,9 @@ export function listComponents(options, allImports, pkg, ptype = "npm") {
943
982
  return Object.keys(compMap).map((k) => compMap[k]);
944
983
  }
945
984
 
985
+ // These component types do not have PURLs
986
+ const NON_PURL_TYPES = ["cryptographic-asset", "file", "data"];
987
+
946
988
  /**
947
989
  * Given the specified package, create a CycloneDX component and add it to the list.
948
990
  */
@@ -979,9 +1021,9 @@ function addComponent(
979
1021
  }
980
1022
  const version = pkg.version || "";
981
1023
  const licenses = pkg.licenses || getLicenses(pkg);
982
- let purl =
983
- pkg.purl ||
984
- new PackageURL(
1024
+ let purl = pkg.purl;
1025
+ if (!purl && ptype) {
1026
+ purl = new PackageURL(
985
1027
  ptype,
986
1028
  encodeForPurl(group),
987
1029
  encodeForPurl(name),
@@ -989,9 +1031,9 @@ function addComponent(
989
1031
  pkg.qualifiers,
990
1032
  encodeForPurl(pkg.subpath),
991
1033
  );
992
- let purlString = purl.toString();
993
- // There is no purl for cryptographic-asset
994
- if (ptype === "cryptographic-asset") {
1034
+ }
1035
+ let purlString = purl?.toString();
1036
+ if (NON_PURL_TYPES.includes(ptype) || NON_PURL_TYPES.includes(pkg.type)) {
995
1037
  purl = undefined;
996
1038
  purlString = undefined;
997
1039
  }
@@ -1102,7 +1144,7 @@ function addComponent(
1102
1144
  if (pkg.properties?.length) {
1103
1145
  component.properties = pkg.properties;
1104
1146
  }
1105
- if (pkg.cryptoProperties?.length) {
1147
+ if (pkg.cryptoProperties && typeof pkg.cryptoProperties === "object") {
1106
1148
  component.cryptoProperties = pkg.cryptoProperties;
1107
1149
  }
1108
1150
  // Retain nested components
@@ -2769,11 +2811,10 @@ export async function createJavaBom(path, options) {
2769
2811
  }
2770
2812
 
2771
2813
  /**
2772
- * Function to create bom string for Node.js projects
2814
+ * Identify the requested AI inventory project types.
2773
2815
  *
2774
- * @param {string} path to the project
2775
2816
  * @param {Object} options Parse options from the cli
2776
- * @returns {Promise<Object>} Promise resolving to BOM object
2817
+ * @returns {string[]} Requested AI inventory types
2777
2818
  */
2778
2819
  function getRequestedAiInventoryTypes(options) {
2779
2820
  return AI_INVENTORY_PROJECT_TYPES.filter((type) =>
@@ -2787,6 +2828,48 @@ function getExcludedAiInventoryTypes(options) {
2787
2828
  );
2788
2829
  }
2789
2830
 
2831
+ function filterIncludedAiInventoryTypes(
2832
+ includedAiInventoryTypes,
2833
+ excludedAiInventoryTypes,
2834
+ ) {
2835
+ return [...new Set(includedAiInventoryTypes)].filter(
2836
+ (type) => !excludedAiInventoryTypes.includes(type),
2837
+ );
2838
+ }
2839
+
2840
+ /**
2841
+ * Determine which AI inventory types should be collected for a scan.
2842
+ *
2843
+ * This combines explicit project-type opt-ins with BOM audit category-driven
2844
+ * opt-ins, then removes any explicitly excluded inventory types.
2845
+ *
2846
+ * @param {Object} options Parse options from the CLI
2847
+ * @returns {string[]} AI inventory types to collect
2848
+ */
2849
+ function getIncludedAiInventoryTypes(options) {
2850
+ const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
2851
+ const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
2852
+ const exactAiInventoryType = getExactAiInventoryType(options);
2853
+ if (exactAiInventoryType) {
2854
+ return filterIncludedAiInventoryTypes(
2855
+ requestedAiInventoryTypes,
2856
+ excludedAiInventoryTypes,
2857
+ );
2858
+ }
2859
+ const auditCategories = expandBomAuditCategories(options?.bomAuditCategories);
2860
+ const includedAiInventoryTypes = [...requestedAiInventoryTypes];
2861
+ if (auditCategories.includes("ai-agent")) {
2862
+ includedAiInventoryTypes.push("ai-skill");
2863
+ }
2864
+ if (auditCategories.includes("mcp-server")) {
2865
+ includedAiInventoryTypes.push("mcp");
2866
+ }
2867
+ return filterIncludedAiInventoryTypes(
2868
+ includedAiInventoryTypes,
2869
+ excludedAiInventoryTypes,
2870
+ );
2871
+ }
2872
+
2790
2873
  function getExactAiInventoryType(options) {
2791
2874
  const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
2792
2875
  return requestedAiInventoryTypes.length === 1 &&
@@ -2796,22 +2879,14 @@ function getExactAiInventoryType(options) {
2796
2879
  : undefined;
2797
2880
  }
2798
2881
 
2799
- function shouldDetectMcpInventory(options, allImports = {}) {
2800
- if (hasAnyProjectType(["mcp"], options, false)) {
2801
- return true;
2802
- }
2803
- const auditCategories = expandBomAuditCategories(options?.bomAuditCategories);
2804
- if (
2805
- auditCategories.some((category) =>
2806
- ["mcp-server", "ai-agent"].includes(category),
2807
- )
2808
- ) {
2809
- return true;
2810
- }
2811
- return Object.keys(allImports).some((importName) => {
2812
- const classification = classifyMcpReference(importName);
2813
- return classification.isMcp;
2814
- });
2882
+ /**
2883
+ * Determine whether MCP source-code analysis should run for the current scan.
2884
+ *
2885
+ * @param {string[]} includedAiInventoryTypes AI inventory types selected for collection
2886
+ * @returns {boolean} True when MCP inventory collection is enabled
2887
+ */
2888
+ function shouldDetectMcpInventory(includedAiInventoryTypes) {
2889
+ return includedAiInventoryTypes.includes("mcp");
2815
2890
  }
2816
2891
 
2817
2892
  function summarizeAiInventoryNames(subjects, discoveryPath, kindSet) {
@@ -2906,14 +2981,8 @@ export async function createNodejsBom(path, options) {
2906
2981
  let parentComponent = {};
2907
2982
  const parentSubComponents = [];
2908
2983
  let ppurl = "";
2909
- const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
2910
- const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
2911
2984
  const exactAiInventoryType = getExactAiInventoryType(options);
2912
- const includedAiInventoryTypes = exactAiInventoryType
2913
- ? requestedAiInventoryTypes
2914
- : AI_INVENTORY_PROJECT_TYPES.filter(
2915
- (type) => !excludedAiInventoryTypes.includes(type),
2916
- );
2985
+ const includedAiInventoryTypes = getIncludedAiInventoryTypes(options);
2917
2986
  let aiInventory = { components: [], dependencies: [], services: [] };
2918
2987
  // Docker mode requires special handling
2919
2988
  if (hasAnyProjectType(["docker", "oci", "container", "os"], options, false)) {
@@ -2965,16 +3034,13 @@ export async function createNodejsBom(path, options) {
2965
3034
  const retData = await findJSImportsExports(path, options.deep);
2966
3035
  allImports = retData.allImports;
2967
3036
  allExports = retData.allExports;
2968
- if (shouldDetectMcpInventory(options, allImports)) {
3037
+ if (shouldDetectMcpInventory(includedAiInventoryTypes)) {
2969
3038
  mcpInventory = detectMcpInventory(path, options.deep);
2970
3039
  }
2971
3040
  }
2972
3041
  if (includedAiInventoryTypes.length) {
2973
3042
  aiInventory = collectAiInventory(path, options, includedAiInventoryTypes);
2974
3043
  }
2975
- if (excludedAiInventoryTypes.includes("mcp")) {
2976
- mcpInventory = { components: [], dependencies: [], services: [] };
2977
- }
2978
3044
  const aiInventorySummary = summarizeAiInventory(aiInventory);
2979
3045
  if (!exactAiInventoryType) {
2980
3046
  if (aiInventorySummary.instructionCount || aiInventorySummary.skillCount) {
@@ -2987,7 +3053,6 @@ export async function createNodejsBom(path, options) {
2987
3053
  `I found ${aiInventorySummary.mcpConfigCount} MCP config component(s). Use '--exclude-type mcp' to drop them, or '--bom-audit --bom-audit-categories ai-inventory --tlp-classification AMBER' to keep and flag them.`,
2988
3054
  );
2989
3055
  }
2990
- emitAiInventorySummary(aiInventory, path);
2991
3056
  }
2992
3057
  if (exactAiInventoryType === "ai-skill") {
2993
3058
  const exactComponents = trimComponents([...(aiInventory.components || [])]);
@@ -3167,11 +3232,15 @@ export async function createNodejsBom(path, options) {
3167
3232
  }
3168
3233
  const basePath = dirname(apkgJson);
3169
3234
  let npmrcData;
3170
- if (safeExistsSync(join(basePath, ".npmrc"))) {
3235
+ const npmrcFile = join(basePath, ".npmrc");
3236
+ if (safeExistsSync(npmrcFile)) {
3171
3237
  thoughtLog(
3172
3238
  "Wait, there is a .npmrc file here! I'm going to check if it has anything malicious.",
3173
3239
  );
3174
- npmrcData = readFileSync(join(basePath, ".npmrc"), "utf-8");
3240
+ npmrcData = readFileSync(npmrcFile, "utf-8");
3241
+ recordSensitiveFileRead(npmrcFile, {
3242
+ label: "npm registry configuration",
3243
+ });
3175
3244
  const npmrcObj = parseNpmrc(npmrcData);
3176
3245
  for (const [key, value] of Object.entries(npmrcObj)) {
3177
3246
  const baseKey = key.replace(/^(?:\/\/[^/]+\/|@[^:]+:)/, "");
@@ -4102,14 +4171,7 @@ export async function createPythonBom(path, options) {
4102
4171
  let dependencies = [];
4103
4172
  let pkgList = [];
4104
4173
  let formulationList = [];
4105
- const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
4106
- const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
4107
- const includedAiInventoryTypes = AI_INVENTORY_PROJECT_TYPES.filter(
4108
- (type) =>
4109
- (!requestedAiInventoryTypes.length ||
4110
- requestedAiInventoryTypes.includes(type)) &&
4111
- !excludedAiInventoryTypes.includes(type),
4112
- );
4174
+ const includedAiInventoryTypes = getIncludedAiInventoryTypes(options);
4113
4175
  let aiInventory = { components: [], dependencies: [], services: [] };
4114
4176
  let mcpInventory = { components: [], dependencies: [], services: [] };
4115
4177
  const tempDir = safeMkdtempSync(join(getTmpDir(), "cdxgen-venv-"));
@@ -5955,6 +6017,7 @@ export function createOSBom(_path, options) {
5955
6017
  let pkgList = [];
5956
6018
  let bomData = {};
5957
6019
  let parentComponent = {};
6020
+ let externalTools = getPluginToolComponents(["osquery"]);
5958
6021
  for (const queryCategory of Object.keys(osQueries)) {
5959
6022
  const queryObj = osQueries[queryCategory];
5960
6023
  const results = executeOsQuery(queryObj.query);
@@ -5973,6 +6036,20 @@ export function createOSBom(_path, options) {
5973
6036
  );
5974
6037
  }
5975
6038
  } // for
6039
+ const hostTrustInventory = enrichOSComponentsWithTrustData(pkgList);
6040
+ if (hostTrustInventory?.components?.length) {
6041
+ pkgList = hostTrustInventory.components;
6042
+ }
6043
+ if (hostTrustInventory?.tools?.length) {
6044
+ externalTools = externalTools.concat(hostTrustInventory.tools);
6045
+ }
6046
+ if (externalTools.length) {
6047
+ options.tools = Array.from(
6048
+ new Map(
6049
+ externalTools.map((tool) => [tool["bom-ref"] || tool.name, tool]),
6050
+ ).values(),
6051
+ );
6052
+ }
5976
6053
  if (pkgList.length) {
5977
6054
  bomData = buildBomNSData(options, pkgList, "", {
5978
6055
  src: "",
@@ -7776,6 +7853,10 @@ export async function createVscodeExtensionBom(path, options) {
7776
7853
  let pkgList = [];
7777
7854
  let dependencies = [];
7778
7855
  const tempDirs = [];
7856
+ const shouldDiscoverInstalledIdeExtensions = hasExplicitProjectTypeSelection(
7857
+ options,
7858
+ "vscode-extension",
7859
+ );
7779
7860
 
7780
7861
  // Mode 1: Scan for .vsix files in the given directory, or treat the input
7781
7862
  // path as a single .vsix file.
@@ -7821,7 +7902,7 @@ export async function createVscodeExtensionBom(path, options) {
7821
7902
  }
7822
7903
 
7823
7904
  // Mode 2: Auto-discover extensions from known IDE locations
7824
- if (options.deep || options.projectType?.includes("ide-extensions")) {
7905
+ if (shouldDiscoverInstalledIdeExtensions) {
7825
7906
  const ideDirs = discoverIdeExtensionDirs();
7826
7907
  if (ideDirs.length) {
7827
7908
  if (DEBUG_MODE) {
@@ -7863,6 +7944,198 @@ export async function createVscodeExtensionBom(path, options) {
7863
7944
  });
7864
7945
  }
7865
7946
 
7947
+ /**
7948
+ * Function to create BOM for Electron ASAR archives.
7949
+ *
7950
+ * @param {string} path to a single archive or a directory to scan
7951
+ * @param {Object} options Parse options from the cli
7952
+ * @returns {Promise<Object>} Promise resolving to BOM object
7953
+ */
7954
+ export async function createAsarBom(path, options) {
7955
+ let pkgList = [];
7956
+ let dependencies = [];
7957
+ let parentComponent = {};
7958
+ const tempDirs = [];
7959
+ const processedArchives = new Set();
7960
+ const maxNestedAsarDepth = 4;
7961
+ const explicitAsarPath = path.endsWith(".asar") ? resolve(path) : undefined;
7962
+ const asarFiles = explicitAsarPath
7963
+ ? [explicitAsarPath]
7964
+ : getAllFiles(path, `${options.multiProject ? "**/" : ""}*.asar`, options);
7965
+
7966
+ const aggregateArchiveResults = (
7967
+ archiveAnalysis,
7968
+ isPrimaryArchive = false,
7969
+ ) => {
7970
+ if (
7971
+ archiveAnalysis.parentComponent &&
7972
+ Object.keys(archiveAnalysis.parentComponent).length
7973
+ ) {
7974
+ if (isPrimaryArchive && !Object.keys(parentComponent).length) {
7975
+ parentComponent = archiveAnalysis.parentComponent;
7976
+ } else {
7977
+ pkgList.push(archiveAnalysis.parentComponent);
7978
+ }
7979
+ }
7980
+ if (archiveAnalysis.components?.length) {
7981
+ pkgList = pkgList.concat(archiveAnalysis.components);
7982
+ }
7983
+ if (archiveAnalysis.dependencies?.length) {
7984
+ dependencies = mergeDependencies(
7985
+ dependencies,
7986
+ archiveAnalysis.dependencies,
7987
+ parentComponent,
7988
+ );
7989
+ }
7990
+ };
7991
+
7992
+ const analyzeExtractedArchive = async (
7993
+ extractedDir,
7994
+ archiveAnalysis,
7995
+ archiveIdentityPath,
7996
+ ) => {
7997
+ if (!archiveAnalysis.packageManifestPaths?.length) {
7998
+ return undefined;
7999
+ }
8000
+ const nodeBomOptions = {
8001
+ ...options,
8002
+ installDeps: false,
8003
+ multiProject: true,
8004
+ noBabel: false,
8005
+ path: extractedDir,
8006
+ projectType: ["js"],
8007
+ };
8008
+ const nodeBomData = await createNodejsBom(extractedDir, nodeBomOptions);
8009
+ if (nodeBomData?.bomJson?.components?.length) {
8010
+ rewriteExtractedArchivePaths(
8011
+ nodeBomData.bomJson.components,
8012
+ extractedDir,
8013
+ archiveIdentityPath,
8014
+ );
8015
+ pkgList = pkgList.concat(nodeBomData.bomJson.components);
8016
+ }
8017
+ if (nodeBomData?.bomJson?.dependencies?.length) {
8018
+ dependencies = mergeDependencies(
8019
+ dependencies,
8020
+ nodeBomData.bomJson.dependencies,
8021
+ );
8022
+ }
8023
+ if (
8024
+ archiveAnalysis.parentComponent?.["bom-ref"] &&
8025
+ nodeBomData?.parentComponent?.["bom-ref"] &&
8026
+ archiveAnalysis.parentComponent["bom-ref"] !==
8027
+ nodeBomData.parentComponent["bom-ref"]
8028
+ ) {
8029
+ rewriteExtractedArchivePaths(
8030
+ nodeBomData.parentComponent,
8031
+ extractedDir,
8032
+ archiveIdentityPath,
8033
+ );
8034
+ dependencies = mergeDependencies(dependencies, [
8035
+ {
8036
+ ref: archiveAnalysis.parentComponent["bom-ref"],
8037
+ dependsOn: [nodeBomData.parentComponent["bom-ref"]],
8038
+ },
8039
+ ]);
8040
+ }
8041
+ return nodeBomData;
8042
+ };
8043
+
8044
+ const processAsarArchive = async (
8045
+ archivePath,
8046
+ archiveIdentityPath,
8047
+ isPrimaryArchive = false,
8048
+ depth = 0,
8049
+ ) => {
8050
+ const processedKey = archiveIdentityPath;
8051
+ if (processedArchives.has(processedKey)) {
8052
+ return undefined;
8053
+ }
8054
+ processedArchives.add(processedKey);
8055
+ const archiveAnalysis = await parseAsarArchive(archivePath, {
8056
+ ...options,
8057
+ asarVirtualPath: archiveIdentityPath,
8058
+ });
8059
+ aggregateArchiveResults(archiveAnalysis, isPrimaryArchive);
8060
+ const shouldExtract =
8061
+ archiveAnalysis.packageManifestPaths?.length ||
8062
+ (depth < maxNestedAsarDepth &&
8063
+ archiveAnalysis.summary?.nestedArchiveCount > 0);
8064
+ if (!shouldExtract) {
8065
+ return archiveAnalysis.parentComponent?.["bom-ref"];
8066
+ }
8067
+ const extractedDir = await extractAsarToTempDir(archivePath);
8068
+ if (!extractedDir) {
8069
+ return archiveAnalysis.parentComponent?.["bom-ref"];
8070
+ }
8071
+ tempDirs.push(extractedDir);
8072
+ await analyzeExtractedArchive(
8073
+ extractedDir,
8074
+ archiveAnalysis,
8075
+ archiveIdentityPath,
8076
+ );
8077
+ if (depth < maxNestedAsarDepth) {
8078
+ const nestedAsarFiles = getAllFiles(extractedDir, "**/*.asar", options);
8079
+ for (const nestedArchivePath of nestedAsarFiles) {
8080
+ const relativeNestedArchivePath = relative(
8081
+ extractedDir,
8082
+ nestedArchivePath,
8083
+ ).replaceAll("\\", "/");
8084
+ if (
8085
+ !relativeNestedArchivePath ||
8086
+ relativeNestedArchivePath.startsWith("..")
8087
+ ) {
8088
+ continue;
8089
+ }
8090
+ const nestedArchiveIdentityPath = `${archiveIdentityPath}#/${relativeNestedArchivePath}`;
8091
+ const nestedParentRef = await processAsarArchive(
8092
+ nestedArchivePath,
8093
+ nestedArchiveIdentityPath,
8094
+ false,
8095
+ depth + 1,
8096
+ );
8097
+ if (
8098
+ archiveAnalysis.parentComponent?.["bom-ref"] &&
8099
+ nestedParentRef &&
8100
+ archiveAnalysis.parentComponent["bom-ref"] !== nestedParentRef
8101
+ ) {
8102
+ dependencies = mergeDependencies(dependencies, [
8103
+ {
8104
+ ref: archiveAnalysis.parentComponent["bom-ref"],
8105
+ dependsOn: [nestedParentRef],
8106
+ },
8107
+ ]);
8108
+ }
8109
+ }
8110
+ }
8111
+ return archiveAnalysis.parentComponent?.["bom-ref"];
8112
+ };
8113
+ try {
8114
+ for (const archivePath of asarFiles) {
8115
+ const isPrimaryArchive =
8116
+ explicitAsarPath && resolve(archivePath) === explicitAsarPath;
8117
+ await processAsarArchive(
8118
+ archivePath,
8119
+ resolve(archivePath),
8120
+ isPrimaryArchive,
8121
+ 0,
8122
+ );
8123
+ }
8124
+ } finally {
8125
+ for (const tempDir of tempDirs) {
8126
+ cleanupAsarTempDir(tempDir);
8127
+ }
8128
+ }
8129
+ pkgList = trimComponents(pkgList);
8130
+ return buildBomNSData(options, pkgList, "asar", {
8131
+ src: path,
8132
+ filename: asarFiles.join(", "),
8133
+ nsMapping: {},
8134
+ dependencies,
8135
+ parentComponent,
8136
+ });
8137
+ }
8138
+
7866
8139
  /**
7867
8140
  * Function to create BOM for installed Chrome and Chromium-based browser extensions.
7868
8141
  *
@@ -7874,7 +8147,8 @@ export async function createChromeExtensionBom(path, options) {
7874
8147
  let dependencies = [];
7875
8148
  let sourcePaths = [];
7876
8149
  const directResult = collectChromeExtensionsFromPath(path);
7877
- const chromeDirs = discoverChromiumExtensionDirs();
8150
+ const shouldDiscoverInstalledChromeExtensions =
8151
+ hasExplicitProjectTypeSelection(options, "chrome-extension");
7878
8152
  let pkgList = directResult.components || [];
7879
8153
  if (directResult.extensionDirs?.length) {
7880
8154
  sourcePaths = directResult.extensionDirs.slice();
@@ -7893,20 +8167,23 @@ export async function createChromeExtensionBom(path, options) {
7893
8167
  `Found ${pkgList.length} component(s) from direct Chrome extension path scan`,
7894
8168
  );
7895
8169
  }
7896
- if (chromeDirs.length) {
7897
- if (DEBUG_MODE) {
7898
- thoughtLog(
7899
- `Discovered Chromium extension directories: ${chromeDirs.map((d) => `${d.browser} (${d.channel}): ${d.dir}`).join(", ")}`,
7900
- );
7901
- }
7902
- if (!pkgList.length) {
7903
- pkgList = collectInstalledChromeExtensions(chromeDirs);
7904
- sourcePaths = chromeDirs.map((d) => d.dir);
7905
- }
7906
- if (DEBUG_MODE && pkgList.length && !directResult.components?.length) {
7907
- thoughtLog(
7908
- `Found ${pkgList.length} Chrome/Chromium extension(s) from ${chromeDirs.length} browser location(s)`,
7909
- );
8170
+ if (shouldDiscoverInstalledChromeExtensions) {
8171
+ const chromeDirs = discoverChromiumExtensionDirs();
8172
+ if (chromeDirs.length) {
8173
+ if (DEBUG_MODE) {
8174
+ thoughtLog(
8175
+ `Discovered Chromium extension directories: ${chromeDirs.map((d) => `${d.browser} (${d.channel}): ${d.dir}`).join(", ")}`,
8176
+ );
8177
+ }
8178
+ if (!pkgList.length) {
8179
+ pkgList = collectInstalledChromeExtensions(chromeDirs);
8180
+ sourcePaths = chromeDirs.map((d) => d.dir);
8181
+ }
8182
+ if (DEBUG_MODE && pkgList.length && !directResult.components?.length) {
8183
+ thoughtLog(
8184
+ `Found ${pkgList.length} Chrome/Chromium extension(s) from ${chromeDirs.length} browser location(s)`,
8185
+ );
8186
+ }
7910
8187
  }
7911
8188
  }
7912
8189
  pkgList = trimComponents(pkgList);
@@ -8026,6 +8303,26 @@ export async function createCryptoCertsBom(path, options) {
8026
8303
  for (const f of certFiles) {
8027
8304
  const name = basename(f);
8028
8305
  const fileHash = await checksumFile("sha256", f);
8306
+ let evidence;
8307
+ if (options.evidence) {
8308
+ const identityEvidence = {
8309
+ field: "name",
8310
+ confidence: 1,
8311
+ concludedValue: name,
8312
+ methods: [
8313
+ {
8314
+ technique: "filename",
8315
+ confidence: 1,
8316
+ value: f,
8317
+ },
8318
+ ],
8319
+ };
8320
+ evidence = {
8321
+ identity:
8322
+ options.specVersion >= 1.6 ? [identityEvidence] : identityEvidence,
8323
+ occurrences: [{ location: f }],
8324
+ };
8325
+ }
8029
8326
  const apkg = {
8030
8327
  name,
8031
8328
  type: "cryptographic-asset",
@@ -8038,10 +8335,18 @@ export async function createCryptoCertsBom(path, options) {
8038
8335
  implementationPlatform: "unknown",
8039
8336
  },
8040
8337
  },
8338
+ ...(evidence ? { evidence } : {}),
8041
8339
  properties: [{ name: "SrcFile", value: f }],
8042
8340
  };
8043
8341
  pkgList.push(apkg);
8044
8342
  }
8343
+ const sourceCryptoComponents = await collectSourceCryptoComponents(
8344
+ path,
8345
+ options,
8346
+ );
8347
+ if (sourceCryptoComponents.length) {
8348
+ pkgList.push(...sourceCryptoComponents);
8349
+ }
8045
8350
  return {
8046
8351
  bomJson: {
8047
8352
  components: pkgList,
@@ -8132,6 +8437,7 @@ export async function createMultiXBom(pathList, options) {
8132
8437
  ) {
8133
8438
  const {
8134
8439
  osPackages,
8440
+ osPackageFiles,
8135
8441
  dependenciesList,
8136
8442
  allTypes,
8137
8443
  bundledSdks,
@@ -8139,6 +8445,7 @@ export async function createMultiXBom(pathList, options) {
8139
8445
  binPaths,
8140
8446
  executables,
8141
8447
  sharedLibs,
8448
+ services,
8142
8449
  tools,
8143
8450
  } = await getOSPackages(
8144
8451
  options.allLayersExplodedDir,
@@ -8149,14 +8456,16 @@ export async function createMultiXBom(pathList, options) {
8149
8456
  options.bundledSdks = bundledSdks;
8150
8457
  options.bundledRuntimes = bundledRuntimes;
8151
8458
  options.binPaths = binPaths;
8459
+ options.unpackagedExecutableCount = executables?.length || 0;
8460
+ options.unpackagedSharedLibraryCount = sharedLibs?.length || 0;
8152
8461
  if (DEBUG_MODE) {
8153
8462
  console.log(
8154
- `**OS**: Found ${osPackages.length} OS packages, ${executables?.length} executables, and ${sharedLibs.length} shared libraries at ${options.allLayersExplodedDir}`,
8463
+ `**OS**: Found ${osPackages.length} OS packages, ${osPackageFiles?.length} package-owned files, ${executables?.length} unpackaged executables, ${sharedLibs.length} unpackaged shared libraries, and ${services?.length || 0} packaged services at ${options.allLayersExplodedDir}`,
8155
8464
  );
8156
8465
  }
8157
8466
  if (osPackages.length) {
8158
8467
  thoughtLog(
8159
- `I found ${osPackages.length} OS packages and ${executables?.length} executables at ${options.allLayersExplodedDir}`,
8468
+ `I found ${osPackages.length} OS packages, ${osPackageFiles?.length || 0} package-owned files, and ${services?.length || 0} packaged services at ${options.allLayersExplodedDir}`,
8160
8469
  );
8161
8470
  } else if (executables?.length || sharedLibs?.length) {
8162
8471
  thoughtLog(
@@ -8178,14 +8487,20 @@ export async function createMultiXBom(pathList, options) {
8178
8487
  ).concat(tools);
8179
8488
  }
8180
8489
  components = components.concat(osPackages);
8490
+ components = components.concat(osPackageFiles || []);
8181
8491
  components = components.concat(executables);
8182
8492
  components = components.concat(sharedLibs);
8183
8493
  if (dependenciesList?.length) {
8184
8494
  dependencies = mergeDependencies(dependencies, dependenciesList);
8185
8495
  }
8496
+ if (services?.length) {
8497
+ options.services = mergeServices(options.services || [], services);
8498
+ }
8186
8499
  if (parentComponent && Object.keys(parentComponent).length) {
8187
8500
  // Make the parent oci image depend on all os components
8188
- const parentDependsOn = new Set(osPackages.map((p) => p["bom-ref"]));
8501
+ const parentDependsOn = new Set(
8502
+ osPackages.concat(osPackageFiles || []).map((p) => p["bom-ref"]),
8503
+ );
8189
8504
  dependencies.splice(0, 0, {
8190
8505
  ref: parentComponent["bom-ref"],
8191
8506
  dependsOn: Array.from(parentDependsOn).sort(),
@@ -8887,6 +9202,28 @@ export async function createMultiXBom(pathList, options) {
8887
9202
  }
8888
9203
  }
8889
9204
  }
9205
+ if (hasAnyProjectType(["asar"], options)) {
9206
+ setProjectTypeActivityContext("asar", path);
9207
+ bomData = await createAsarBom(path, options);
9208
+ if (bomData?.bomJson?.components?.length) {
9209
+ if (DEBUG_MODE) {
9210
+ console.log(
9211
+ `Found ${bomData.bomJson.components.length} ASAR component(s) at ${path}`,
9212
+ );
9213
+ }
9214
+ components = components.concat(bomData.bomJson.components);
9215
+ dependencies = mergeDependencies(
9216
+ dependencies,
9217
+ bomData.bomJson.dependencies,
9218
+ );
9219
+ if (
9220
+ bomData.parentComponent &&
9221
+ Object.keys(bomData.parentComponent).length
9222
+ ) {
9223
+ parentSubComponents.push(bomData.parentComponent);
9224
+ }
9225
+ }
9226
+ }
8890
9227
  if (hasAnyProjectType(["vscode-extension"], options)) {
8891
9228
  setProjectTypeActivityContext("vscode-extension", path);
8892
9229
  bomData = await createVscodeExtensionBom(path, options);
@@ -9326,6 +9663,16 @@ export async function createXBom(path, options) {
9326
9663
  return await createVscodeExtensionBom(path, options);
9327
9664
  }
9328
9665
 
9666
+ // Electron ASAR archives
9667
+ const asarFiles = getAllFiles(
9668
+ path,
9669
+ `${options.multiProject ? "**/" : ""}*.asar`,
9670
+ options,
9671
+ );
9672
+ if (asarFiles.length) {
9673
+ return await createAsarBom(path, options);
9674
+ }
9675
+
9329
9676
  // Helm charts
9330
9677
  const chartFiles = getAllFiles(
9331
9678
  path,
@@ -9428,6 +9775,35 @@ export async function createXBom(path, options) {
9428
9775
  }
9429
9776
  }
9430
9777
 
9778
+ /**
9779
+ * Function to create a hardware BOM for the current host.
9780
+ *
9781
+ * @param {string} _path Source path (unused for live host HBOM generation)
9782
+ * @param {Object} options Parse options from the cli
9783
+ * @returns {Promise<Object>} Promise resolving to BOM object
9784
+ */
9785
+ export async function createHBom(_path, options) {
9786
+ ensureHbomRuntimeSupport(options, options.commandName || "hbom");
9787
+ let bomJson = await createHbomDocument(options);
9788
+ if (options.includeRuntime) {
9789
+ const runtimeOptions = {
9790
+ ...options,
9791
+ includeRuntime: false,
9792
+ multiProject: false,
9793
+ projectType: ["os"],
9794
+ };
9795
+ const obomData = await createOSBom(_path, runtimeOptions);
9796
+ bomJson = mergeHostInventoryBoms(bomJson, obomData);
9797
+ } else {
9798
+ bomJson = mergeHostInventoryBoms(bomJson);
9799
+ }
9800
+ return {
9801
+ bomJson,
9802
+ dependencies: bomJson.dependencies || [],
9803
+ parentComponent: bomJson.metadata?.component,
9804
+ };
9805
+ }
9806
+
9431
9807
  /**
9432
9808
  * Function to create bom string for various languages
9433
9809
  *
@@ -9440,6 +9816,21 @@ export async function createBom(path, options) {
9440
9816
  if (!projectType) {
9441
9817
  projectType = [];
9442
9818
  }
9819
+ ensureNoMixedHbomProjectTypes(projectType);
9820
+ if (hasHbomProjectType(projectType)) {
9821
+ const selectedHbomProjectType = Array.isArray(projectType)
9822
+ ? projectType[0]
9823
+ : `${projectType}`.split(",")[0];
9824
+ options.projectType = [selectedHbomProjectType];
9825
+ setActivityContext({
9826
+ projectType: selectedHbomProjectType,
9827
+ sourcePath: path,
9828
+ });
9829
+ thoughtLog(
9830
+ "The user wants a Hardware Bill-of-Materials (HBOM) for the current host. Let's use the dedicated hardware collector.",
9831
+ );
9832
+ return await createHBom(path, options);
9833
+ }
9443
9834
  let exportData;
9444
9835
  let isContainerMode = false;
9445
9836
  const isLocalDirectoryInput =
@@ -9574,6 +9965,9 @@ export async function createBom(path, options) {
9574
9965
  if (path.endsWith(".war")) {
9575
9966
  projectType = ["java"];
9576
9967
  }
9968
+ if (!projectType.length && path.endsWith(".asar")) {
9969
+ projectType = ["asar"];
9970
+ }
9577
9971
  if (projectType.length > 1) {
9578
9972
  setActivityContext({
9579
9973
  projectType: projectType.join(","),
@@ -9697,6 +10091,9 @@ export async function createBom(path, options) {
9697
10091
  if (PROJECT_TYPE_ALIASES["caxa"].includes(projectType[0])) {
9698
10092
  return await createCaxaBom(path, options);
9699
10093
  }
10094
+ if (PROJECT_TYPE_ALIASES["asar"].includes(projectType[0])) {
10095
+ return await createAsarBom(path, options);
10096
+ }
9700
10097
  if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
9701
10098
  return await createVscodeExtensionBom(path, options);
9702
10099
  }
@@ -9741,17 +10138,33 @@ export async function createBom(path, options) {
9741
10138
  * @throws {Error} if the request fails
9742
10139
  */
9743
10140
  export async function submitBom(args, bomContents) {
10141
+ const dependencyTrackApiUrl = getDependencyTrackBomApiUrl(args.serverUrl);
10142
+ const serverUrl = dependencyTrackApiUrl?.toString();
10143
+ if (!dependencyTrackApiUrl || !serverUrl) {
10144
+ console.log(
10145
+ "Invalid Dependency-Track server URL. Provide an absolute http(s) URL without dangerous characters.",
10146
+ );
10147
+ args.failOnError && process.exit(1);
10148
+ return undefined;
10149
+ }
10150
+ const serverHost = dependencyTrackApiUrl.hostname;
10151
+ if (!isAllowedHttpHost(serverHost)) {
10152
+ console.log(
10153
+ `Dependency-Track server host '${serverHost}' is not allowed by CDXGEN_ALLOWED_HOSTS.`,
10154
+ );
10155
+ args.failOnError && process.exit(1);
10156
+ return undefined;
10157
+ }
9744
10158
  if (isDryRun) {
9745
10159
  recordActivity({
9746
10160
  kind: "network",
9747
10161
  reason:
9748
10162
  "Dry run mode blocks Dependency-Track submission and reports the request instead.",
9749
10163
  status: "blocked",
9750
- target: getDependencyTrackBomUrl(args.serverUrl),
10164
+ target: serverUrl,
9751
10165
  });
9752
10166
  return undefined;
9753
10167
  }
9754
- const serverUrl = getDependencyTrackBomUrl(args.serverUrl);
9755
10168
  const bomPayload = buildDependencyTrackBomPayload(args, bomContents);
9756
10169
  if (!bomPayload) {
9757
10170
  console.log(
@@ -9770,15 +10183,7 @@ export async function submitBom(args, bomContents) {
9770
10183
  );
9771
10184
  }
9772
10185
  try {
9773
- if (DEBUG_MODE && args.skipDtTlsCheck) {
9774
- console.log(
9775
- "Calling ",
9776
- serverUrl,
9777
- "with --skip-dt-tls-check argument: Skip DT TLS check.",
9778
- );
9779
- }
9780
- // See issue #1963 regarding CRLF hardening
9781
- return await got(serverUrl, {
10186
+ const requestOptions = {
9782
10187
  method: "PUT",
9783
10188
  followRedirect: false,
9784
10189
  https: {
@@ -9787,11 +10192,22 @@ export async function submitBom(args, bomContents) {
9787
10192
  headers: {
9788
10193
  "X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
9789
10194
  "Content-Type": "application/json",
9790
- "user-agent": `@CycloneDX/cdxgen ${CDXGEN_VERSION}`,
9791
10195
  },
9792
10196
  json: bomPayload,
9793
10197
  responseType: "json",
9794
- }).json();
10198
+ context: {
10199
+ activityIntent: "bom-submit",
10200
+ },
10201
+ };
10202
+ if (DEBUG_MODE && args.skipDtTlsCheck) {
10203
+ console.log(
10204
+ "Calling ",
10205
+ serverUrl,
10206
+ "with --skip-dt-tls-check argument: Skip DT TLS check.",
10207
+ );
10208
+ }
10209
+ // See issue #1963 regarding CRLF hardening
10210
+ return await cdxgenAgent(dependencyTrackApiUrl, requestOptions).json();
9795
10211
  } catch (error) {
9796
10212
  if (error.response && error.response.statusCode === 401) {
9797
10213
  // Unauthorized
@@ -9804,7 +10220,7 @@ export async function submitBom(args, bomContents) {
9804
10220
  );
9805
10221
  // Method not allowed errors
9806
10222
  try {
9807
- return await got(serverUrl, {
10223
+ return await cdxgenAgent(dependencyTrackApiUrl, {
9808
10224
  method: "POST",
9809
10225
  followRedirect: false,
9810
10226
  https: {
@@ -9813,10 +10229,12 @@ export async function submitBom(args, bomContents) {
9813
10229
  headers: {
9814
10230
  "X-Api-Key": (args.apiKey || "").replace(/[\r\n]/g, ""),
9815
10231
  "Content-Type": "application/json",
9816
- "user-agent": `@CycloneDX/cdxgen ${CDXGEN_VERSION}`,
9817
10232
  },
9818
10233
  json: bomPayload,
9819
10234
  responseType: "json",
10235
+ context: {
10236
+ activityIntent: "bom-submit",
10237
+ },
9820
10238
  }).json();
9821
10239
  } catch (error) {
9822
10240
  if (DEBUG_MODE) {