@cdxgen/cdxgen 13.4.0 → 13.6.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 (305) hide show
  1. package/README.md +9 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +29 -5
  4. package/bin/evinse.js +8 -0
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +29 -0
  7. package/bin/validate.js +6 -0
  8. package/data/cbomosdb-queries.json +13 -1
  9. package/data/component-tags.json +8 -0
  10. package/data/license-aliases.json +24 -2
  11. package/data/queries-darwin.json +25 -1
  12. package/data/queries-win.json +19 -1
  13. package/data/queries.json +41 -0
  14. package/data/remediations.json +407 -1
  15. package/data/rules/ai-bom.yaml +1 -1
  16. package/data/rules/brotli-decoder.yaml +107 -0
  17. package/data/rules/cbom-security.yaml +235 -4
  18. package/data/rules/go-binaries.yaml +46 -0
  19. package/data/rules/golem-go.yaml +80 -0
  20. package/data/rules/hbom-compliance.yaml +40 -22
  21. package/data/rules/hbom-performance.yaml +7 -1
  22. package/data/rules/host-topology.yaml +1 -1
  23. package/data/rules/obom-runtime.yaml +31 -2
  24. package/data/rules/php-runtime.yaml +143 -0
  25. package/data/rules/vscode-extensions.yaml +75 -0
  26. package/data/spdx-license-list.json +121 -7
  27. package/data/spdx-licenses.json +69 -0
  28. package/data/spdx.schema.json +845 -2
  29. package/lib/audit/index.js +14 -0
  30. package/lib/cli/bomAssembly.js +120 -6
  31. package/lib/cli/cliOptions.js +110 -3
  32. package/lib/cli/index.js +182 -6
  33. package/lib/cli/jsBom.js +153 -64
  34. package/lib/cli/jvmBom.js +363 -66
  35. package/lib/cli/managedBom.js +853 -87
  36. package/lib/cli/nativeBom.js +267 -33
  37. package/lib/core/boundedFile.js +344 -0
  38. package/lib/core/brotliProbe.js +182 -0
  39. package/lib/core/env.js +38 -8
  40. package/lib/core/fs.js +40 -19
  41. package/lib/core/httpClient.js +172 -46
  42. package/lib/core/lockPaths.js +38 -0
  43. package/lib/core/paths.js +24 -1
  44. package/lib/core/processTree.js +5 -0
  45. package/lib/core/projectConfig.js +1 -0
  46. package/lib/core/projectTooling.js +97 -0
  47. package/lib/core/propertySanitizer.js +72 -0
  48. package/lib/core/toolRegistry.js +170 -0
  49. package/lib/ecosystems/cppEvidence.js +263 -106
  50. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  51. package/lib/ecosystems/ecosystems.js +25 -10
  52. package/lib/ecosystems/gemHostProbe.js +301 -0
  53. package/lib/ecosystems/golem.js +550 -6
  54. package/lib/ecosystems/gradleutils.js +858 -244
  55. package/lib/ecosystems/parsers-bazel.js +107 -6
  56. package/lib/ecosystems/parsers-dotnet.js +117 -18
  57. package/lib/ecosystems/parsers-go.js +1090 -367
  58. package/lib/ecosystems/parsers-js.js +505 -22
  59. package/lib/ecosystems/parsers-jvm.js +259 -17
  60. package/lib/ecosystems/parsers-misc.js +438 -64
  61. package/lib/ecosystems/parsers-python.js +100 -12
  62. package/lib/ecosystems/pythonutils.js +128 -2
  63. package/lib/ecosystems/rubyutils.js +544 -34
  64. package/lib/ecosystems/rusi.js +10 -0
  65. package/lib/ecosystems/scalacliutils.js +4 -1
  66. package/lib/ecosystems/scalasem.js +798 -151
  67. package/lib/ecosystems/vendoredCode.js +237 -3
  68. package/lib/ecosystems/venvProbe.js +360 -0
  69. package/lib/evinser/evinser.js +472 -61
  70. package/lib/evinser/swiftsem.js +68 -5
  71. package/lib/helpers/agenticToolutils.js +413 -93
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentSkillSpec.js +17 -0
  74. package/lib/inventory/apkInstalledDb.js +243 -0
  75. package/lib/inventory/atomUtils.js +110 -0
  76. package/lib/inventory/auditCategories.js +3 -3
  77. package/lib/inventory/brotliDecoder.js +142 -0
  78. package/lib/inventory/buildToolRateLimit.js +1 -1
  79. package/lib/inventory/cacheDir.js +52 -9
  80. package/lib/inventory/cbomutils.js +56 -7
  81. package/lib/inventory/certificates.js +2371 -0
  82. package/lib/inventory/communityAiConfigParser.js +4 -9
  83. package/lib/inventory/deps.js +41 -4
  84. package/lib/inventory/display.js +200 -3
  85. package/lib/inventory/dosai.js +639 -63
  86. package/lib/inventory/dotnetRuntime.js +217 -0
  87. package/lib/inventory/dynamic.js +45 -3
  88. package/lib/inventory/envcontext.js +475 -18
  89. package/lib/inventory/evidenceUtils.js +447 -23
  90. package/lib/inventory/fetchBatch.js +1 -1
  91. package/lib/inventory/formulationParsers.js +32 -3
  92. package/lib/inventory/goBuildInfo.js +140 -0
  93. package/lib/inventory/gtfobins.js +13 -0
  94. package/lib/inventory/hbom.js +67 -7
  95. package/lib/inventory/hbomAnalysis.js +16 -0
  96. package/lib/inventory/hostRuntimeProbe.js +686 -0
  97. package/lib/inventory/hostTopology.js +21 -0
  98. package/lib/inventory/jvmLocalRepos.js +9 -2
  99. package/lib/inventory/jvmToolEnv.js +29 -0
  100. package/lib/inventory/licenseEnhancer.js +12 -0
  101. package/lib/inventory/mavenCentralMirror.js +534 -0
  102. package/lib/inventory/nodeBuildInfo.js +218 -0
  103. package/lib/inventory/nodeHostProbe.js +202 -0
  104. package/lib/inventory/osPackagePurl.js +244 -0
  105. package/lib/inventory/osPackageResolver.js +271 -87
  106. package/lib/inventory/osinfo.js +79 -31
  107. package/lib/inventory/osqueryTransform.js +114 -5
  108. package/lib/inventory/phpBuildInfo.js +438 -0
  109. package/lib/inventory/phpHostProbe.js +185 -0
  110. package/lib/inventory/rootfsCerts.js +426 -0
  111. package/lib/inventory/rubyBuildInfo.js +972 -0
  112. package/lib/inventory/rubyHostProbe.js +218 -0
  113. package/lib/inventory/scalaCoords.js +4 -3
  114. package/lib/inventory/scalaDedupe.js +440 -0
  115. package/lib/inventory/source.js +256 -5
  116. package/lib/inventory/spdx.js +144 -0
  117. package/lib/inventory/traceRunner.js +5 -1
  118. package/lib/inventory/tracedCrypto.js +129 -0
  119. package/lib/managers/binary.js +866 -186
  120. package/lib/managers/docker.js +245 -12
  121. package/lib/parsers/email.js +277 -0
  122. package/lib/parsers/largeJson.js +62 -13
  123. package/lib/parsers/vscodeExtensionId.js +183 -0
  124. package/lib/server/server.js +198 -2
  125. package/lib/stages/postgen/annotator.js +17 -1
  126. package/lib/stages/postgen/introspection/reflect.js +4 -1
  127. package/lib/stages/postgen/postgen.js +5 -5
  128. package/lib/stages/postgen/ruleEngine.js +71 -6
  129. package/lib/stages/postgen/specVersionCompat.js +30 -0
  130. package/lib/stages/pregen/envAudit.js +1 -0
  131. package/lib/stages/pregen/pregen.js +231 -62
  132. package/lib/validator/bomValidator.js +32 -6
  133. package/lib/validator/generated/runtime.mjs +13 -0
  134. package/lib/validator/generated/validate-1.6.mjs +27 -8
  135. package/lib/validator/generated/validate-1.7.mjs +32 -13
  136. package/lib/validator/generated/validate-2.0.mjs +31 -12
  137. package/lib/validator/index.js +1 -1
  138. package/package.json +19 -18
  139. package/types/bin/cdxgen-secure.d.ts +3 -0
  140. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  141. package/types/bin/repl.d.ts.map +1 -1
  142. package/types/lib/audit/index.d.ts.map +1 -1
  143. package/types/lib/cli/bomAssembly.d.ts +22 -3
  144. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  145. package/types/lib/cli/cliOptions.d.ts +43 -0
  146. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  147. package/types/lib/cli/index.d.ts.map +1 -1
  148. package/types/lib/cli/jsBom.d.ts +1 -17
  149. package/types/lib/cli/jsBom.d.ts.map +1 -1
  150. package/types/lib/cli/jvmBom.d.ts +20 -0
  151. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  152. package/types/lib/cli/managedBom.d.ts.map +1 -1
  153. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  154. package/types/lib/core/boundedFile.d.ts +70 -0
  155. package/types/lib/core/boundedFile.d.ts.map +1 -0
  156. package/types/lib/core/brotliProbe.d.ts +37 -0
  157. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  158. package/types/lib/core/env.d.ts +22 -2
  159. package/types/lib/core/env.d.ts.map +1 -1
  160. package/types/lib/core/fs.d.ts.map +1 -1
  161. package/types/lib/core/httpClient.d.ts +24 -0
  162. package/types/lib/core/httpClient.d.ts.map +1 -1
  163. package/types/lib/core/lockPaths.d.ts +19 -0
  164. package/types/lib/core/lockPaths.d.ts.map +1 -0
  165. package/types/lib/core/processTree.d.ts +2 -0
  166. package/types/lib/core/processTree.d.ts.map +1 -1
  167. package/types/lib/core/projectConfig.d.ts.map +1 -1
  168. package/types/lib/core/projectTooling.d.ts +41 -0
  169. package/types/lib/core/projectTooling.d.ts.map +1 -0
  170. package/types/lib/core/propertySanitizer.d.ts +26 -0
  171. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  172. package/types/lib/core/toolRegistry.d.ts +36 -0
  173. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  174. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  175. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  176. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  177. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  178. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  179. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  180. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  181. package/types/lib/ecosystems/golem.d.ts +81 -1
  182. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  183. package/types/lib/ecosystems/gradleutils.d.ts +57 -21
  184. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
  187. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  188. package/types/lib/ecosystems/parsers-go.d.ts +85 -13
  189. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  190. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  191. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +37 -58
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +14 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  199. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  200. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  201. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  202. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  203. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  205. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  206. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  207. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  208. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  209. package/types/lib/evinser/evinser.d.ts +88 -8
  210. package/types/lib/evinser/evinser.d.ts.map +1 -1
  211. package/types/lib/evinser/swiftsem.d.ts +10 -0
  212. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  213. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  214. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  215. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  216. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  217. package/types/lib/inventory/atomUtils.d.ts +33 -0
  218. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  219. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  220. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  221. package/types/lib/inventory/cacheDir.d.ts +12 -4
  222. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  223. package/types/lib/inventory/cbomutils.d.ts +17 -0
  224. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  225. package/types/lib/inventory/certificates.d.ts +205 -0
  226. package/types/lib/inventory/certificates.d.ts.map +1 -0
  227. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  228. package/types/lib/inventory/deps.d.ts +2 -1
  229. package/types/lib/inventory/deps.d.ts.map +1 -1
  230. package/types/lib/inventory/display.d.ts +18 -0
  231. package/types/lib/inventory/display.d.ts.map +1 -1
  232. package/types/lib/inventory/dosai.d.ts +162 -7
  233. package/types/lib/inventory/dosai.d.ts.map +1 -1
  234. package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
  235. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  236. package/types/lib/inventory/dynamic.d.ts +6 -1
  237. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  238. package/types/lib/inventory/envcontext.d.ts +92 -8
  239. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  240. package/types/lib/inventory/evidenceUtils.d.ts +29 -10
  241. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  242. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  243. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  244. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  245. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  246. package/types/lib/inventory/hbom.d.ts.map +1 -1
  247. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  248. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  249. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  250. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  251. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  252. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  253. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  254. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  255. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  256. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  257. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  258. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  259. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  260. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  261. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  262. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  263. package/types/lib/inventory/osPackageResolver.d.ts +44 -2
  264. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  265. package/types/lib/inventory/osinfo.d.ts +31 -6
  266. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  267. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  268. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  269. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  270. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  271. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  272. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  273. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  274. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  275. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  276. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  277. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  278. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  279. package/types/lib/inventory/source.d.ts +38 -0
  280. package/types/lib/inventory/source.d.ts.map +1 -1
  281. package/types/lib/inventory/spdx.d.ts +15 -0
  282. package/types/lib/inventory/spdx.d.ts.map +1 -1
  283. package/types/lib/inventory/traceRunner.d.ts +4 -1
  284. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  285. package/types/lib/inventory/tracedCrypto.d.ts +22 -0
  286. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
  287. package/types/lib/managers/binary.d.ts +10 -0
  288. package/types/lib/managers/binary.d.ts.map +1 -1
  289. package/types/lib/managers/docker.d.ts +24 -0
  290. package/types/lib/managers/docker.d.ts.map +1 -1
  291. package/types/lib/parsers/email.d.ts +19 -0
  292. package/types/lib/parsers/email.d.ts.map +1 -0
  293. package/types/lib/parsers/largeJson.d.ts +17 -3
  294. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  295. package/types/lib/server/server.d.ts +8 -0
  296. package/types/lib/server/server.d.ts.map +1 -1
  297. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  298. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  299. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  300. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  301. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  302. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  303. package/types/lib/stages/pregen/pregen.d.ts +26 -9
  304. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -47,6 +47,7 @@ import {
47
47
  resolveGitUrlFromPurl,
48
48
  resolvePurlSourceDirectory,
49
49
  sanitizeRemoteUrlForLogs,
50
+ validateAndRejectGitSource,
50
51
  } from "../inventory/source.js";
51
52
  import { executeOsQuery } from "../managers/binary.js";
52
53
  import { getBomWithOras } from "../managers/oci.js";
@@ -1307,6 +1308,17 @@ export function buildTargetContextFindings(target) {
1307
1308
  * @returns {void}
1308
1309
  */
1309
1310
  function cloneRepositoryToDir(repoUrl, cloneDir, gitRef) {
1311
+ // The repository URL comes from registry metadata, so it gets the same
1312
+ // protocol and host checks as a URL sent to the server.
1313
+ const validationError = validateAndRejectGitSource(repoUrl);
1314
+ if (validationError) {
1315
+ const error = new Error(
1316
+ `${validationError.error}: ${validationError.details}`,
1317
+ );
1318
+ error.errorType = "clone";
1319
+ error.retryable = false;
1320
+ throw error;
1321
+ }
1310
1322
  const gitArgs = [
1311
1323
  "-c",
1312
1324
  "alias.clone=",
@@ -1316,6 +1328,8 @@ function cloneRepositoryToDir(repoUrl, cloneDir, gitRef) {
1316
1328
  "safe.bareRepository=explicit",
1317
1329
  "-c",
1318
1330
  "core.hooksPath=/dev/null",
1331
+ "-c",
1332
+ "core.symlinks=false",
1319
1333
  "clone",
1320
1334
  "--template=",
1321
1335
  "--depth",
@@ -25,6 +25,7 @@ import {
25
25
  CDXGEN_VERSION,
26
26
  frameworksList,
27
27
  } from "../core/state.js";
28
+ import { getExecutedTools } from "../core/toolRegistry.js";
28
29
  import {
29
30
  isPkgTypeOnlyImport,
30
31
  setNpmCleanedVersionProperty,
@@ -45,6 +46,7 @@ import {
45
46
  setCycloneDxFormat,
46
47
  toCycloneDxSpecVersionString,
47
48
  } from "../inventory/bomUtils.js";
49
+ import { mergeDuplicateCryptoComponents } from "../inventory/certificates.js";
48
50
  import { mergeServices, trimComponents } from "../inventory/depsUtils.js";
49
51
  import {
50
52
  attachIdentityTools,
@@ -59,7 +61,7 @@ import {
59
61
  } from "../inventory/purl.js";
60
62
  import { repairPurl } from "../inventory/sbomDocument.js";
61
63
  import { getLicenses } from "../inventory/spdx.js";
62
- import { getBinaryBom } from "../managers/binary.js";
64
+ import { getBinaryBom, getPluginToolComponents } from "../managers/binary.js";
63
65
  import { isValidIriReference } from "../parsers/iri.js";
64
66
 
65
67
  /**
@@ -292,10 +294,85 @@ function anchorDetectedParent(
292
294
  return anchored;
293
295
  }
294
296
 
297
+ /**
298
+ * Builds the tool components for the external helpers and build tools this
299
+ * run executed, from the run-scoped registry (issue #598). Bundled plugin
300
+ * helpers are enriched with the manifest component of the plugin package that
301
+ * ships them, so the plugin package's version and identity travel with the
302
+ * helper. Empty when the run spawned nothing external, which keeps such BOMs
303
+ * byte-identical to the previous behaviour.
304
+ *
305
+ * @returns {object[]} Sorted CycloneDX tool components
306
+ */
307
+ export const buildExternalToolComponents = () => {
308
+ const recordedTools = getExecutedTools();
309
+ if (!recordedTools.length) {
310
+ return [];
311
+ }
312
+ const recordedNames = recordedTools.map((tool) => tool.name);
313
+ const pluginComponents = getPluginToolComponents(recordedNames);
314
+ const pluginByName = new Map(
315
+ pluginComponents.map((component) => [component.name, component]),
316
+ );
317
+ const components = [];
318
+ for (const tool of recordedTools) {
319
+ const pluginComponent = pluginByName.get(tool.name);
320
+ if (pluginComponent?.["bom-ref"]) {
321
+ components.push(pluginComponent);
322
+ continue;
323
+ }
324
+ const component = {
325
+ type: "application",
326
+ name: tool.name,
327
+ };
328
+ if (tool.version) {
329
+ component.version = tool.version;
330
+ }
331
+ components.push(component);
332
+ }
333
+ return components;
334
+ };
335
+
336
+ /**
337
+ * Drops the executed tools that are already present: a tool whose bom-ref or
338
+ * name matches an explicitly passed context tool, or whose bom-ref an earlier
339
+ * executed tool already carries (several bundled helpers can share one plugin
340
+ * package component). The context tools themselves are never touched.
341
+ *
342
+ * @param {object[]} contextTools Tool components passed in by the caller
343
+ * @param {object[]} executedTools Tool components from the run's registry
344
+ * @returns {object[]} The executed tools to append
345
+ */
346
+ const newExecutedTools = (contextTools, executedTools) => {
347
+ const seenRefs = new Set();
348
+ const seenNames = new Set();
349
+ for (const component of contextTools) {
350
+ if (component?.["bom-ref"]) {
351
+ seenRefs.add(component["bom-ref"]);
352
+ }
353
+ if (component?.name) {
354
+ seenNames.add(component.name);
355
+ }
356
+ }
357
+ const unique = [];
358
+ for (const component of executedTools) {
359
+ const ref = component["bom-ref"];
360
+ if ((ref && seenRefs.has(ref)) || seenNames.has(component.name)) {
361
+ continue;
362
+ }
363
+ if (ref) {
364
+ seenRefs.add(ref);
365
+ }
366
+ unique.push(component);
367
+ }
368
+ return unique;
369
+ };
370
+
295
371
  /**
296
372
  * Assembles the `metadata.tools` block, including the cdxgen tool entry and,
297
373
  * for spec versions prior to 1.5, the legacy tools array. For spec version
298
- * 1.6+ the author field is converted to the authors array.
374
+ * 1.6+ the author field is converted to the authors array. The tools this run
375
+ * executed follow the context tools, sorted by name, and cdxgen comes last.
299
376
  *
300
377
  * @param {object} options CLI options
301
378
  * @param {object} [context={}] Additional context carrying existing components
@@ -303,6 +380,7 @@ function anchorDetectedParent(
303
380
  * tools array (1.4) or a components array with tool components (1.5+)
304
381
  */
305
382
  export const addToolsSection = (options, context = {}) => {
383
+ const externalTools = buildExternalToolComponents();
306
384
  if (options.specVersion === 1.4) {
307
385
  return [
308
386
  {
@@ -310,6 +388,11 @@ export const addToolsSection = (options, context = {}) => {
310
388
  name: "cdxgen",
311
389
  version: CDXGEN_VERSION,
312
390
  },
391
+ ...newExecutedTools([], externalTools).map((tool) =>
392
+ tool.version
393
+ ? { name: tool.name, version: tool.version }
394
+ : { name: tool.name },
395
+ ),
313
396
  ];
314
397
  }
315
398
  let components = [];
@@ -330,6 +413,7 @@ export const addToolsSection = (options, context = {}) => {
330
413
  } else if (tools && Object.keys(tools).length && tools.components) {
331
414
  components = components.concat(tools.components);
332
415
  }
416
+ components = components.concat(newExecutedTools(components, externalTools));
333
417
  const cdxToolComponent = {
334
418
  group: CDXGEN_TOOL_GROUP,
335
419
  name: CDXGEN_TOOL_NAME,
@@ -457,6 +541,11 @@ export const addLifecyclesSection = (options) => {
457
541
  /**
458
542
  * Function to create metadata block
459
543
  *
544
+ * @param {Object} [parentComponent] Component the BOM describes
545
+ * @param {Object} [options] CLI options
546
+ * @param {Object} [context] Generator context. `context.metadataProperties`
547
+ * holds facts of a manifest that has no component to carry them, such as the
548
+ * platform data of a composer.lock whose composer.json names no project.
460
549
  */
461
550
  export function addMetadata(parentComponent = {}, options = {}, context = {}) {
462
551
  // DO NOT fork this project to just change the vendor or author's name
@@ -518,7 +607,13 @@ export function addMetadata(parentComponent = {}, options = {}, context = {}) {
518
607
  // We cannot use purl or bom-ref here since they would not match
519
608
  // purl - could have application on one side and a different type
520
609
  // bom-ref could have qualifiers on one side
521
- if (fullName !== parentFullName) {
610
+ // A purl subpath marks a separate project that declares the
611
+ // parent's coordinates, such as a second Maven project or a Gradle
612
+ // subproject with the same group, name and version.
613
+ const separateProject =
614
+ comp.purl?.includes("#") &&
615
+ comp.purl.split("#")[1] !== parentComponent.purl?.split("#")[1];
616
+ if (fullName !== parentFullName || separateProject) {
522
617
  if (!comp["bom-ref"]) {
523
618
  comp["bom-ref"] = `pkg:${comp.type}/${decodeURIComponent(
524
619
  fullName,
@@ -752,7 +847,7 @@ export function addMetadata(parentComponent = {}, options = {}, context = {}) {
752
847
  });
753
848
  let appLanguage;
754
849
  // TODO: Move these lists to a config file.
755
- for (const lang in [
850
+ for (const lang of [
756
851
  "java",
757
852
  "node",
758
853
  "dotnet",
@@ -828,6 +923,9 @@ export function addMetadata(parentComponent = {}, options = {}, context = {}) {
828
923
  // AI provenance/oversight detection (`-t ai-provenance`) is injected into the
829
924
  // BOM document root (bomJson.properties) after post-processing, not into
830
925
  // metadata.properties. See ensureAiProvenanceProperties/ensureAiOversightProperties.
926
+ if (context?.metadataProperties?.length) {
927
+ mproperties.push(...context.metadataProperties);
928
+ }
831
929
  if (mproperties.length) {
832
930
  metadata.properties = mproperties;
833
931
  }
@@ -1602,6 +1700,10 @@ export const buildBomNSData = (options, pkgInfo, ptype, context) => {
1602
1700
  if (context?.formulationList?.length) {
1603
1701
  bomNSData.formulationList = context.formulationList;
1604
1702
  }
1703
+ // createMultiXBom rebuilds the metadata block, so these are handed on.
1704
+ if (context?.metadataProperties?.length) {
1705
+ bomNSData.metadataProperties = context.metadataProperties;
1706
+ }
1605
1707
  // Carry language-specific provenance citations (e.g. PEP 770 embedded
1606
1708
  // SBOMs) so postProcess can attach them in the single root-level citations
1607
1709
  // section it builds.
@@ -1933,16 +2035,28 @@ export function emitAiInventorySummary(aiInventory, discoveryPath) {
1933
2035
  * @param {Array} components Components
1934
2036
  * @param {Object} parentComponent Parent component
1935
2037
  * @param {Array} dependencies Dependencies
2038
+ * @param {Array} [metadataProperties] Properties for the metadata block, from
2039
+ * generators whose manifest has no component to carry them
1936
2040
  *
1937
2041
  * @returns {Object} Object including BOM Json
1938
2042
  */
1939
- export function dedupeBom(options, components, parentComponent, dependencies) {
2043
+ export function dedupeBom(
2044
+ options,
2045
+ components,
2046
+ parentComponent,
2047
+ dependencies,
2048
+ metadataProperties = [],
2049
+ ) {
1940
2050
  if (!components) {
1941
2051
  return {};
1942
2052
  }
1943
2053
  if (!dependencies) {
1944
2054
  dependencies = [];
1945
2055
  }
2056
+ // Certificate components from different producers (trustinspector rootfs,
2057
+ // the source-directory scan, observed chains) share a bom-ref; merge their
2058
+ // occurrences and properties before the generic trim.
2059
+ components = mergeDuplicateCryptoComponents(components);
1946
2060
  components = trimComponents(components);
1947
2061
  // Let's apply some common tweaks
1948
2062
  // Convert evidence.identity section to an object for 1.5
@@ -1971,7 +2085,7 @@ export function dedupeBom(options, components, parentComponent, dependencies) {
1971
2085
  ),
1972
2086
  serialNumber: serialNum,
1973
2087
  version: 1,
1974
- metadata: addMetadata(parentComponent, options, {}),
2088
+ metadata: addMetadata(parentComponent, options, { metadataProperties }),
1975
2089
  components,
1976
2090
  services: options.services || [],
1977
2091
  dependencies,
@@ -19,6 +19,7 @@ import {
19
19
  METADATA_PROPERTY_CONFIG_KEYS,
20
20
  parseMetadataProperties,
21
21
  } from "../core/metadataProperties.js";
22
+ import { toCamel } from "../core/paths.js";
22
23
  import {
23
24
  DEFAULT_CDX_SPEC_VERSION,
24
25
  normalizeCycloneDxComponentTypeFilter,
@@ -124,6 +125,106 @@ export function isUserProvided(value, yaDefault) {
124
125
  return value !== undefined && value !== yaDefault;
125
126
  }
126
127
 
128
+ /**
129
+ * The value the operator gave an option on the command line or through its
130
+ * CDXGEN_* environment variable. yargs also fills options in from defaults
131
+ * and from a config file, which cannot be told apart in its output.
132
+ *
133
+ * @param {string} option kebab-case option name
134
+ * @param {object} [context]
135
+ * @param {string[]} [context.argv] Raw command-line arguments
136
+ * @param {object} [context.env] Environment variables
137
+ * @param {boolean} [context.boolean] Read the option as a boolean flag
138
+ * @returns {string|boolean|undefined} The operator's value, or undefined
139
+ */
140
+ export function operatorOptionChoice(
141
+ option,
142
+ { argv = process.argv.slice(2), env = process.env, boolean = false } = {},
143
+ ) {
144
+ const spellings = [...new Set([option, toCamel(option)])];
145
+ const toBoolean = (value) =>
146
+ ["true", "1"].includes(`${value}`.trim().toLowerCase());
147
+ let choice;
148
+ for (let i = 0; i < argv.length; i++) {
149
+ const arg = `${argv[i]}`;
150
+ if (arg === "--") {
151
+ break;
152
+ }
153
+ for (const name of spellings) {
154
+ if (boolean && arg === `--no-${name}`) {
155
+ choice = false;
156
+ } else if (arg === `--${name}`) {
157
+ const next = argv[i + 1] === undefined ? "" : `${argv[i + 1]}`;
158
+ if (!boolean) {
159
+ choice = next;
160
+ i++;
161
+ } else if (["true", "false"].includes(next.toLowerCase())) {
162
+ choice = toBoolean(next);
163
+ i++;
164
+ } else {
165
+ choice = true;
166
+ }
167
+ } else if (arg.startsWith(`--${name}=`)) {
168
+ const value = arg.slice(name.length + 3);
169
+ choice = boolean ? toBoolean(value) : value;
170
+ }
171
+ }
172
+ }
173
+ if (choice !== undefined) {
174
+ return choice;
175
+ }
176
+ const envValue =
177
+ env[`CDXGEN_${option.toUpperCase().replaceAll("-", "_")}`] ?? "";
178
+ if (`${envValue}`.trim() === "") {
179
+ return undefined;
180
+ }
181
+ return boolean ? toBoolean(envValue) : `${envValue}`;
182
+ }
183
+
184
+ /**
185
+ * Make the operator's choice about running the scanned project's own code
186
+ * final. A config file is read from the working directory, which is usually
187
+ * the scanned project, so it carries that project's trust. Neither the config
188
+ * file nor a profile or lifecycle may turn installs on when the operator
189
+ * disabled them, and secure mode keeps installs off unless the operator
190
+ * passes --install-deps. Introspection keeps the project's build tools
191
+ * running with installs off, which is likewise the operator's decision, so
192
+ * introspection asked for only by the config file does not do that.
193
+ *
194
+ * **Mutates `options` in place.**
195
+ *
196
+ * @param {object} options The options object, after applyAdvancedOptions
197
+ * @param {object} [context]
198
+ * @param {object} [context.projectConfig] Sanitized config file contents
199
+ * @param {boolean} [context.isSecureMode] Whether cdxgen runs in secure mode
200
+ * @param {string[]} [context.argv] Raw command-line arguments
201
+ * @param {object} [context.env] Environment variables
202
+ * @returns {void}
203
+ */
204
+ export function applyOperatorToolingChoice(options, context = {}) {
205
+ const { projectConfig, argv, env } = context;
206
+ const secureMode = context.isSecureMode ?? isSecureMode;
207
+ const installChoice = operatorOptionChoice("install-deps", {
208
+ argv,
209
+ env,
210
+ boolean: true,
211
+ });
212
+ if (installChoice === false || (secureMode && installChoice !== true)) {
213
+ options.installDeps = false;
214
+ }
215
+ const configAsksIntrospect =
216
+ ["true", "1"].includes(`${projectConfig?.introspect}`.toLowerCase()) ||
217
+ projectConfig?.profile === "introspect";
218
+ if (
219
+ options.introspect === true &&
220
+ configAsksIntrospect &&
221
+ operatorOptionChoice("introspect", { argv, env, boolean: true }) !== true &&
222
+ operatorOptionChoice("profile", { argv, env }) !== "introspect"
223
+ ) {
224
+ options.introspectFromConfigFile = true;
225
+ }
226
+ }
227
+
127
228
  /**
128
229
  * Combine the custom metadata properties of every source (discussion 4391):
129
230
  * the `--metadata-property` flag, the CDXGEN_METADATA_PROPERTY and
@@ -588,7 +689,9 @@ export function applyAdvancedOptions(options, context = {}) {
588
689
  options.deep = true;
589
690
  options.evidence = false;
590
691
  options.includeCrypto = false;
591
- options.installDeps = !secureMode;
692
+ // A profile may skip installs but never turn them back on once they
693
+ // were disabled, which would run the scanned project's code.
694
+ options.installDeps = !secureMode && options.installDeps !== false;
592
695
  break;
593
696
  case "deep-learning":
594
697
  case "ml-deep":
@@ -596,7 +699,7 @@ export function applyAdvancedOptions(options, context = {}) {
596
699
  options.deep = true;
597
700
  options.evidence = true;
598
701
  options.includeCrypto = true;
599
- options.installDeps = !secureMode;
702
+ options.installDeps = !secureMode && options.installDeps !== false;
600
703
  options.bomAudit = true;
601
704
  break;
602
705
  default:
@@ -619,7 +722,11 @@ export function applyAdvancedOptions(options, context = {}) {
619
722
  "PREVIEW: post-build lifecycle SBOM generation is supported only for limited project types.",
620
723
  });
621
724
  }
622
- options.installDeps = true;
725
+ // The project is already built, so installs are on only where they
726
+ // would have been anyway: a disabled install stays disabled.
727
+ if (options.installDeps !== false) {
728
+ options.installDeps = true;
729
+ }
623
730
  break;
624
731
  default:
625
732
  break;