@cdxgen/cdxgen 13.1.0 → 13.2.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 (186) hide show
  1. package/README.md +8 -7
  2. package/bin/cdxgen.js +16 -3
  3. package/bin/evinse.js +2 -0
  4. package/data/crypto-oid.json +88 -12
  5. package/data/remediations.json +23 -0
  6. package/data/rules/ai-bom.yaml +78 -0
  7. package/data/rules/cargo-rust.yaml +132 -0
  8. package/data/rules/mcp-servers.yaml +122 -0
  9. package/lib/audit/targets.js +6 -1
  10. package/lib/cli/bomAssembly.js +12 -0
  11. package/lib/cli/index.js +192 -1
  12. package/lib/cli/jsBom.js +59 -2
  13. package/lib/cli/jvmBom.js +22 -0
  14. package/lib/cli/managedBom.js +152 -0
  15. package/lib/cli/nativeBom.js +798 -1
  16. package/lib/core/activity.js +120 -2
  17. package/lib/core/env.js +39 -1
  18. package/lib/core/userAgent.js +151 -0
  19. package/lib/ecosystems/cargoCache.js +364 -0
  20. package/lib/ecosystems/caxa.js +13 -5
  21. package/lib/ecosystems/cppEvidence.js +40 -2
  22. package/lib/ecosystems/ecosystems.js +241 -91
  23. package/lib/ecosystems/golem.js +11 -1
  24. package/lib/ecosystems/gradleutils.js +143 -2
  25. package/lib/ecosystems/kosi.js +1256 -0
  26. package/lib/ecosystems/parsers-crystal.js +179 -0
  27. package/lib/ecosystems/parsers-dotnet.js +69 -39
  28. package/lib/ecosystems/parsers-elm.js +816 -0
  29. package/lib/ecosystems/parsers-gleam.js +1 -1
  30. package/lib/ecosystems/parsers-julia.js +268 -0
  31. package/lib/ecosystems/parsers-lua.js +218 -0
  32. package/lib/ecosystems/parsers-misc.js +172 -0
  33. package/lib/ecosystems/parsers-nim.js +221 -0
  34. package/lib/ecosystems/parsers-opam.js +180 -0
  35. package/lib/ecosystems/parsers-perl.js +277 -0
  36. package/lib/ecosystems/parsers-python.js +1 -1
  37. package/lib/ecosystems/parsers-r.js +148 -0
  38. package/lib/ecosystems/parsers-rebar.js +411 -0
  39. package/lib/ecosystems/parsers-rust.js +932 -17
  40. package/lib/ecosystems/parsers-spack.js +204 -0
  41. package/lib/ecosystems/parsers-stack.js +264 -0
  42. package/lib/ecosystems/parsers-terraform.js +194 -0
  43. package/lib/ecosystems/rusi.js +126 -6
  44. package/lib/evinser/evinser.js +111 -1
  45. package/lib/helpers/agenticToolutils.js +925 -0
  46. package/lib/inventory/agentFormulationParser.js +57 -4
  47. package/lib/inventory/aiCollector.js +504 -10
  48. package/lib/inventory/aiInventory.js +12 -0
  49. package/lib/inventory/analyzer.js +488 -30
  50. package/lib/inventory/astWorker.js +6 -0
  51. package/lib/inventory/auditCategories.js +8 -0
  52. package/lib/inventory/cbomutils.js +30 -0
  53. package/lib/inventory/cdxrs.js +7 -2
  54. package/lib/inventory/communityAiConfigParser.js +215 -2
  55. package/lib/inventory/dosai.js +77 -0
  56. package/lib/inventory/envcontext.js +9 -6
  57. package/lib/inventory/evidenceUtils.js +13 -0
  58. package/lib/inventory/fetchBatch.js +74 -2
  59. package/lib/inventory/fetchRate.js +74 -11
  60. package/lib/inventory/forgeEnricher.js +3 -2
  61. package/lib/inventory/jvmToolEnv.js +6 -5
  62. package/lib/inventory/mcpConfigParser.js +166 -2
  63. package/lib/inventory/mcpDiscovery.js +11 -0
  64. package/lib/inventory/mcpServerManifest.js +389 -0
  65. package/lib/inventory/mcpbBundle.js +419 -0
  66. package/lib/inventory/plugins.js +3 -0
  67. package/lib/inventory/protobom.js +54 -88
  68. package/lib/inventory/protobomLoader.js +4 -0
  69. package/lib/inventory/rustFormulationParser.js +1 -1
  70. package/lib/inventory/source.js +10 -4
  71. package/lib/inventory/stylePackageRefs.js +41 -0
  72. package/lib/inventory/svelteUtils.js +524 -0
  73. package/lib/parsers/huggingfaceUtils.js +15 -7
  74. package/lib/parsers/onnx.js +294 -0
  75. package/lib/parsers/safetensors.js +114 -0
  76. package/lib/stages/postgen/introspection/markers.js +29 -6
  77. package/lib/stages/postgen/postgen.js +169 -1
  78. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
  79. package/package.json +25 -25
  80. package/types/lib/audit/targets.d.ts.map +1 -1
  81. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  82. package/types/lib/cli/index.d.ts +2 -2
  83. package/types/lib/cli/index.d.ts.map +1 -1
  84. package/types/lib/cli/jsBom.d.ts +23 -0
  85. package/types/lib/cli/jsBom.d.ts.map +1 -1
  86. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  87. package/types/lib/cli/managedBom.d.ts +33 -0
  88. package/types/lib/cli/managedBom.d.ts.map +1 -1
  89. package/types/lib/cli/nativeBom.d.ts +88 -8
  90. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  91. package/types/lib/core/activity.d.ts.map +1 -1
  92. package/types/lib/core/env.d.ts +21 -0
  93. package/types/lib/core/env.d.ts.map +1 -1
  94. package/types/lib/core/userAgent.d.ts +56 -0
  95. package/types/lib/core/userAgent.d.ts.map +1 -0
  96. package/types/lib/ecosystems/cargoCache.d.ts +75 -0
  97. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
  98. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  99. package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
  100. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  101. package/types/lib/ecosystems/ecosystems.d.ts +15 -1
  102. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  103. package/types/lib/ecosystems/gradleutils.d.ts +20 -0
  104. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  105. package/types/lib/ecosystems/kosi.d.ts +73 -0
  106. package/types/lib/ecosystems/kosi.d.ts.map +1 -0
  107. package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
  108. package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
  109. package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
  110. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  111. package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
  112. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
  113. package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
  114. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
  115. package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
  116. package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
  117. package/types/lib/ecosystems/parsers-misc.d.ts +23 -0
  118. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  119. package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
  120. package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
  121. package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
  122. package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
  123. package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
  124. package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
  125. package/types/lib/ecosystems/parsers-r.d.ts +35 -0
  126. package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
  127. package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
  128. package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
  129. package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
  130. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  131. package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
  132. package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
  133. package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
  134. package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
  135. package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
  136. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
  137. package/types/lib/ecosystems/pythonutils.d.ts +1 -1
  138. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  139. package/types/lib/evinser/evinser.d.ts +29 -0
  140. package/types/lib/evinser/evinser.d.ts.map +1 -1
  141. package/types/lib/helpers/agenticToolutils.d.ts +87 -0
  142. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
  143. package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
  144. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  145. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  146. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  147. package/types/lib/inventory/auditCategories.d.ts +4 -0
  148. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  149. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  150. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  151. package/types/lib/inventory/dosai.d.ts +23 -0
  152. package/types/lib/inventory/dosai.d.ts.map +1 -1
  153. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  154. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  155. package/types/lib/inventory/fetchBatch.d.ts +34 -0
  156. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  157. package/types/lib/inventory/fetchRate.d.ts +9 -6
  158. package/types/lib/inventory/fetchRate.d.ts.map +1 -1
  159. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  160. package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
  161. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  162. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  163. package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
  164. package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
  165. package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
  166. package/types/lib/inventory/mcpbBundle.d.ts +19 -0
  167. package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
  168. package/types/lib/inventory/plugins.d.ts.map +1 -1
  169. package/types/lib/inventory/protobom.d.ts +18 -13
  170. package/types/lib/inventory/protobom.d.ts.map +1 -1
  171. package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
  172. package/types/lib/inventory/source.d.ts.map +1 -1
  173. package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
  174. package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
  175. package/types/lib/inventory/svelteUtils.d.ts +124 -0
  176. package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
  177. package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
  178. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  179. package/types/lib/parsers/onnx.d.ts +28 -0
  180. package/types/lib/parsers/onnx.d.ts.map +1 -0
  181. package/types/lib/parsers/safetensors.d.ts +22 -0
  182. package/types/lib/parsers/safetensors.d.ts.map +1 -0
  183. package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
  184. package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
  185. package/types/lib/stages/postgen/postgen.d.ts +33 -0
  186. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
@@ -1097,7 +1097,19 @@ export function addComponent(
1097
1097
  component.modelCard = pkg.modelCard || undefined;
1098
1098
  component.data = pkg.data || undefined;
1099
1099
  }
1100
+ if (options.specVersion >= 1.7) {
1101
+ // An external component is provided by the deployment environment rather
1102
+ // than bundled with the product. The schema defaults it to false, so it
1103
+ // is only worth emitting when true.
1104
+ component.isExternal = pkg.isExternal ? true : undefined;
1105
+ }
1100
1106
  component["type"] = determinePackageType(pkg);
1107
+ // `version` is optional in every spec version cdxgen emits. An empty string
1108
+ // is not a version, and a consumer reading one has to guess whether it means
1109
+ // "unknown" or "the empty release"; leaving the field out says it plainly.
1110
+ if (!component.version) {
1111
+ delete component.version;
1112
+ }
1101
1113
  if (pkg["bom-ref"]) {
1102
1114
  component["bom-ref"] = pkg["bom-ref"];
1103
1115
  } else if (purlString) {
package/lib/cli/index.js CHANGED
@@ -32,6 +32,11 @@ import { thoughtLog } from "../core/logger.js";
32
32
  import { mapWithConcurrency } from "../core/parallel.js";
33
33
  import { dirNameStr } from "../core/paths.js";
34
34
  import { ui } from "../core/ui.js";
35
+ import { isElmProjectFile } from "../ecosystems/parsers-elm.js";
36
+ import {
37
+ isJuliaManifestFile,
38
+ isJuliaProjectFile,
39
+ } from "../ecosystems/parsers-julia.js";
35
40
  import {
36
41
  parseBitbucketPipelinesFile,
37
42
  parseContainerFile,
@@ -90,6 +95,7 @@ import {
90
95
  determineParentComponent,
91
96
  } from "./bomAssembly.js";
92
97
  import {
98
+ createAgenticBom,
93
99
  createAsarBom,
94
100
  createCaxaBom,
95
101
  createChromeExtensionBom,
@@ -126,24 +132,36 @@ import {
126
132
  createGitHubBom,
127
133
  createHelmBom,
128
134
  createJenkinsBom,
135
+ createPerlBom,
129
136
  createPHPBom,
130
137
  createPythonBom,
138
+ createRBom,
131
139
  createRubyBom,
140
+ createTerraformBom,
132
141
  } from "./managedBom.js";
133
142
  import {
134
143
  createCargoCacheBom,
135
144
  createClojureBom,
136
145
  createCocoaBom,
137
146
  createCppBom,
147
+ createCrystalBom,
138
148
  createDartBom,
139
149
  createElixirBom,
150
+ createElmBom,
151
+ createErlangBom,
140
152
  createGleamBom,
141
153
  createGoBom,
142
154
  createHaskellBom,
155
+ createJuliaBom,
156
+ createLuaBom,
157
+ createNimBom,
143
158
  createNixBom,
159
+ createOCamlBom,
144
160
  createRustBom,
161
+ createSpackBom,
145
162
  createSwiftBom,
146
163
  createZigBom,
164
+ findStackLockFiles,
147
165
  getCargoCacheDir,
148
166
  } from "./nativeBom.js";
149
167
 
@@ -154,22 +172,33 @@ export {
154
172
  createGitHubBom,
155
173
  createHelmBom,
156
174
  createJenkinsBom,
175
+ createPerlBom,
157
176
  createPHPBom,
158
177
  createPixiBom,
159
178
  createPythonBom,
179
+ createRBom,
160
180
  createRubyBom,
181
+ createTerraformBom,
161
182
  } from "./managedBom.js";
162
183
  export {
163
184
  createClojureBom,
164
185
  createCocoaBom,
165
186
  createCppBom,
187
+ createCrystalBom,
166
188
  createDartBom,
167
189
  createElixirBom,
190
+ createElmBom,
191
+ createErlangBom,
168
192
  createGleamBom,
169
193
  createGoBom,
170
194
  createHaskellBom,
195
+ createJuliaBom,
196
+ createLuaBom,
197
+ createNimBom,
171
198
  createNixBom,
199
+ createOCamlBom,
172
200
  createRustBom,
201
+ createSpackBom,
173
202
  createSwiftBom,
174
203
  createZigBom,
175
204
  } from "./nativeBom.js";
@@ -2060,7 +2089,8 @@ export async function createXBom(path, options) {
2060
2089
  `${options.multiProject ? "**/" : ""}cabal.project.freeze`,
2061
2090
  options,
2062
2091
  );
2063
- if (hackageFiles.length) {
2092
+ const stackLockDetectFiles = findStackLockFiles(path, options);
2093
+ if (hackageFiles.length || stackLockDetectFiles.length) {
2064
2094
  return createHaskellBom(path, options);
2065
2095
  }
2066
2096
 
@@ -2285,6 +2315,131 @@ export async function createXBom(path, options) {
2285
2315
  if (gleamTomlFiles.length) {
2286
2316
  return await createGleamBom(path, options);
2287
2317
  }
2318
+
2319
+ // Julia
2320
+ const juliaProjectFiles = getAllFiles(
2321
+ path,
2322
+ `${options.multiProject ? "**/" : ""}Project.toml`,
2323
+ options,
2324
+ );
2325
+ const juliaManifestFiles = getAllFiles(
2326
+ path,
2327
+ `${options.multiProject ? "**/" : ""}Manifest.toml`,
2328
+ options,
2329
+ );
2330
+ // `Project.toml` is not a name Julia owns — Cloud Native Buildpacks use it
2331
+ // too — so dispatch only when a file actually declares a Julia environment.
2332
+ if (
2333
+ juliaManifestFiles.some(isJuliaManifestFile) ||
2334
+ juliaProjectFiles.some(isJuliaProjectFile)
2335
+ ) {
2336
+ return await createJuliaBom(path, options);
2337
+ }
2338
+
2339
+ // Erlang with rebar3
2340
+ const rebarLockFiles = getAllFiles(
2341
+ path,
2342
+ `${options.multiProject ? "**/" : ""}rebar.lock`,
2343
+ options,
2344
+ );
2345
+ if (rebarLockFiles.length) {
2346
+ return await createErlangBom(path, options);
2347
+ }
2348
+
2349
+ // OCaml with opam
2350
+ const opamLockedFiles = getAllFiles(
2351
+ path,
2352
+ `${options.multiProject ? "**/" : ""}*.opam.locked`,
2353
+ options,
2354
+ );
2355
+ if (opamLockedFiles.length) {
2356
+ return await createOCamlBom(path, options);
2357
+ }
2358
+
2359
+ // Elm with elm.json (0.19) or elm-package.json (0.18)
2360
+ const elmJsonFiles = getAllFiles(
2361
+ path,
2362
+ `${options.multiProject ? "**/" : ""}elm.json`,
2363
+ options,
2364
+ ).filter(isElmProjectFile);
2365
+ const legacyElmFiles = getAllFiles(
2366
+ path,
2367
+ `${options.multiProject ? "**/" : ""}elm-package.json`,
2368
+ options,
2369
+ );
2370
+ if (elmJsonFiles.length || legacyElmFiles.length) {
2371
+ return await createElmBom(path, options);
2372
+ }
2373
+
2374
+ // Crystal with shards
2375
+ const shardYmlFiles = getAllFiles(
2376
+ path,
2377
+ `${options.multiProject ? "**/" : ""}shard.yml`,
2378
+ options,
2379
+ );
2380
+ if (shardYmlFiles.length) {
2381
+ return await createCrystalBom(path, options);
2382
+ }
2383
+
2384
+ // Nim with nimble
2385
+ const nimbleFiles = getAllFiles(
2386
+ path,
2387
+ `${options.multiProject ? "**/" : ""}*.nimble`,
2388
+ options,
2389
+ );
2390
+ if (nimbleFiles.length) {
2391
+ return await createNimBom(path, options);
2392
+ }
2393
+
2394
+ // Lua with LuaRocks
2395
+ const rockspecFiles = getAllFiles(
2396
+ path,
2397
+ `${options.multiProject ? "**/" : ""}*.rockspec`,
2398
+ options,
2399
+ );
2400
+ if (rockspecFiles.length) {
2401
+ return await createLuaBom(path, options);
2402
+ }
2403
+
2404
+ // Spack environments
2405
+ const spackLockFiles = getAllFiles(
2406
+ path,
2407
+ `${options.multiProject ? "**/" : ""}spack.lock`,
2408
+ options,
2409
+ );
2410
+ if (spackLockFiles.length) {
2411
+ return await createSpackBom(path, options);
2412
+ }
2413
+
2414
+ // R with renv
2415
+ const renvLockFiles = getAllFiles(
2416
+ path,
2417
+ `${options.multiProject ? "**/" : ""}renv.lock`,
2418
+ options,
2419
+ );
2420
+ if (renvLockFiles.length) {
2421
+ return createRBom(path, options);
2422
+ }
2423
+
2424
+ // Perl with Carton
2425
+ const cpanSnapshotFiles = getAllFiles(
2426
+ path,
2427
+ `${options.multiProject ? "**/" : ""}cpanfile.snapshot`,
2428
+ options,
2429
+ );
2430
+ if (cpanSnapshotFiles.length) {
2431
+ return createPerlBom(path, options);
2432
+ }
2433
+
2434
+ // Terraform and OpenTofu providers
2435
+ const tfLockFiles = getAllFiles(
2436
+ path,
2437
+ `${options.multiProject ? "**/" : ""}.terraform.lock.hcl`,
2438
+ options,
2439
+ );
2440
+ if (tfLockFiles.length) {
2441
+ return createTerraformBom(path, options);
2442
+ }
2288
2443
  }
2289
2444
 
2290
2445
  /**
@@ -2636,6 +2791,39 @@ async function createBomForPath(path, options) {
2636
2791
  if (PROJECT_TYPE_ALIASES["gleam"].includes(projectType[0])) {
2637
2792
  return await createGleamBom(path, options);
2638
2793
  }
2794
+ if (PROJECT_TYPE_ALIASES["julia"].includes(projectType[0])) {
2795
+ return await createJuliaBom(path, options);
2796
+ }
2797
+ if (PROJECT_TYPE_ALIASES["erlang"].includes(projectType[0])) {
2798
+ return await createErlangBom(path, options);
2799
+ }
2800
+ if (PROJECT_TYPE_ALIASES["ocaml"].includes(projectType[0])) {
2801
+ return await createOCamlBom(path, options);
2802
+ }
2803
+ if (PROJECT_TYPE_ALIASES["elm"].includes(projectType[0])) {
2804
+ return await createElmBom(path, options);
2805
+ }
2806
+ if (PROJECT_TYPE_ALIASES["crystal"].includes(projectType[0])) {
2807
+ return await createCrystalBom(path, options);
2808
+ }
2809
+ if (PROJECT_TYPE_ALIASES["nim"].includes(projectType[0])) {
2810
+ return await createNimBom(path, options);
2811
+ }
2812
+ if (PROJECT_TYPE_ALIASES["lua"].includes(projectType[0])) {
2813
+ return await createLuaBom(path, options);
2814
+ }
2815
+ if (PROJECT_TYPE_ALIASES["spack"].includes(projectType[0])) {
2816
+ return await createSpackBom(path, options);
2817
+ }
2818
+ if (PROJECT_TYPE_ALIASES["r"].includes(projectType[0])) {
2819
+ return createRBom(path, options);
2820
+ }
2821
+ if (PROJECT_TYPE_ALIASES["perl"].includes(projectType[0])) {
2822
+ return createPerlBom(path, options);
2823
+ }
2824
+ if (PROJECT_TYPE_ALIASES["terraform"].includes(projectType[0])) {
2825
+ return createTerraformBom(path, options);
2826
+ }
2639
2827
  if (PROJECT_TYPE_ALIASES["caxa"].includes(projectType[0])) {
2640
2828
  return await createCaxaBom(path, options);
2641
2829
  }
@@ -2645,6 +2833,9 @@ async function createBomForPath(path, options) {
2645
2833
  if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
2646
2834
  return await createVscodeExtensionBom(path, options);
2647
2835
  }
2836
+ if (PROJECT_TYPE_ALIASES["agentic"].includes(projectType[0])) {
2837
+ return await createAgenticBom(path, options);
2838
+ }
2648
2839
  if (PROJECT_TYPE_ALIASES["chrome-extension"].includes(projectType[0])) {
2649
2840
  return await createChromeExtensionBom(path, options);
2650
2841
  }
package/lib/cli/jsBom.js CHANGED
@@ -53,6 +53,10 @@ import {
53
53
  fetchHuggingFaceAssetInventory,
54
54
  normalizeHuggingFaceReference,
55
55
  } from "../ecosystems/remote/huggingface.js";
56
+ import {
57
+ AGENTIC_TOOL_PURL_TYPE,
58
+ collectAgenticTools,
59
+ } from "../helpers/agenticToolutils.js";
56
60
  import {
57
61
  cleanupTempDir,
58
62
  collectInstalledExtensions,
@@ -100,6 +104,13 @@ const isWin = _platform() === "win32";
100
104
  * local Modelfile/`.gguf` file input. Returns the explicit selection from
101
105
  * `getExactAiInventoryType` when one is present.
102
106
  *
107
+ * Direct HuggingFace interpretation of the source path is opt-in: only
108
+ * unambiguous references (`pkg:huggingface/...` purls and huggingface.co
109
+ * URLs) are recognized on their own. Bare `owner/repo`-shaped values are
110
+ * ordinary relative paths (eg: `repotests/sveltejs-realworld`) unless an AI
111
+ * inventory type such as `-t ai` is explicitly selected, in which case
112
+ * `getExactAiInventoryType` already returned it above.
113
+ *
103
114
  * @param {string} path Project or file path
104
115
  * @param {object} options CLI options
105
116
  * @returns {string|undefined} The detected AI inventory type (`"ai"`) or undefined
@@ -109,7 +120,7 @@ export function getDirectAiInventoryType(path, options) {
109
120
  if (exactAiInventoryType) {
110
121
  return exactAiInventoryType;
111
122
  }
112
- if (normalizeHuggingFaceReference(path)) {
123
+ if (normalizeHuggingFaceReference(path, { explicitOnly: true })) {
113
124
  return "ai";
114
125
  }
115
126
  try {
@@ -157,8 +168,10 @@ export async function createNodejsBom(path, options) {
157
168
  excludedAiInventoryTypes,
158
169
  );
159
170
  let aiInventory = { components: [], dependencies: [], services: [] };
171
+ // A value that exists on the local filesystem is always a project path -
172
+ // never a remote model id - so existing inputs skip remote resolution.
160
173
  const directHuggingFaceSource =
161
- exactAiInventoryType === "ai"
174
+ exactAiInventoryType === "ai" && !safeExistsSync(path)
162
175
  ? normalizeHuggingFaceReference(path)
163
176
  : undefined;
164
177
  if (directHuggingFaceSource?.repoId) {
@@ -1748,6 +1761,50 @@ export async function createVscodeExtensionBom(path, options) {
1748
1761
  });
1749
1762
  }
1750
1763
 
1764
+ /**
1765
+ * Create a BOM that catalogs agentic CLI tools installed for the current user
1766
+ * (for example Kiro CLI, zcode, opencode, and Claude Code), along with the
1767
+ * agents, plugins, and providers they define.
1768
+ *
1769
+ * The scan is read-only and value-redacting: it records tool versions where a
1770
+ * declarative source exists, plus structural counts, but never reads
1771
+ * credentials, transcripts, session logs, or telemetry.
1772
+ *
1773
+ * @param {string} path Positional input. A host scan does not read a project
1774
+ * tree, so an explicit path other than the current directory is rejected
1775
+ * rather than silently ignored.
1776
+ * @param {Object} options Parse options from the CLI
1777
+ * @returns {Promise<Object>} Promise resolving to BOM data
1778
+ */
1779
+ export async function createAgenticBom(path, options) {
1780
+ // Guard against a surprising host-wide scan masquerading as a project scan.
1781
+ // cdxgen always supplies a positional path (defaulting to "."), so only an
1782
+ // explicit path that resolves somewhere other than the current directory is
1783
+ // treated as a mistake.
1784
+ const resolvedPath = path ? resolve(path) : process.cwd();
1785
+ if (resolvedPath !== process.cwd()) {
1786
+ throw new Error(
1787
+ "The 'agentic' project type performs a host-wide scan of installed " +
1788
+ "agentic CLI tool directories and does not accept a path argument. " +
1789
+ `Re-run without the path (received '${path}').`,
1790
+ );
1791
+ }
1792
+ // Must not go to stdout: every entry point keeps stdout reserved for the BOM
1793
+ // payload so `cdxgen -t agentic -o -` stays pipeable.
1794
+ thoughtLog(
1795
+ "Scanning the current user's home directory for installed agentic CLI tools.",
1796
+ );
1797
+ const inventory = collectAgenticTools(options);
1798
+ const pkgList = trimComponents(inventory.components || []);
1799
+ return buildBomNSData(options, pkgList, AGENTIC_TOOL_PURL_TYPE, {
1800
+ src: "~",
1801
+ filename: "agentic-cli-tools",
1802
+ nsMapping: {},
1803
+ dependencies: inventory.dependencies || [],
1804
+ services: inventory.services || [],
1805
+ });
1806
+ }
1807
+
1751
1808
  /**
1752
1809
  * Function to create BOM for Electron ASAR archives.
1753
1810
  *
package/lib/cli/jvmBom.js CHANGED
@@ -49,6 +49,7 @@ import {
49
49
  parseGradleInfoLogsForUrls,
50
50
  parseGradleProperties,
51
51
  parseGradleResolvedDistributions,
52
+ parseGradleVersionCatalog,
52
53
  recordGradleInvocationFailure,
53
54
  splitOutputByGradleProjects,
54
55
  } from "../ecosystems/gradleutils.js";
@@ -1372,6 +1373,27 @@ export async function createJavaBom(path, options) {
1372
1373
  jarNSMapping = { ...jarNSMapping, ...tmpjarNSMapping };
1373
1374
  }
1374
1375
  }
1376
+
1377
+ // A version catalog declares coordinates without resolving them, so it
1378
+ // is used only when Gradle produced no dependency information at all.
1379
+ // The emitted components carry cdx:gradle:catalog=true to mark them as
1380
+ // declared rather than resolved.
1381
+ if (!pkgList.length) {
1382
+ const catalogFiles = getAllFiles(
1383
+ path,
1384
+ `${options.multiProject ? "**/" : ""}gradle/libs.versions.toml`,
1385
+ options,
1386
+ );
1387
+ for (const catalogFile of catalogFiles) {
1388
+ if (DEBUG_MODE) {
1389
+ console.log(`Parsing ${catalogFile}`);
1390
+ }
1391
+ const dlist = parseGradleVersionCatalog(catalogFile);
1392
+ if (dlist?.length) {
1393
+ pkgList = pkgList.concat(dlist);
1394
+ }
1395
+ }
1396
+ }
1375
1397
  }
1376
1398
 
1377
1399
  // Bazel
@@ -50,6 +50,7 @@ import {
50
50
  parseGitHubWorkflowData,
51
51
  parseHelmYamlData,
52
52
  } from "../ecosystems/parsers-misc.js";
53
+ import { parseCpanSnapshot } from "../ecosystems/parsers-perl.js";
53
54
  import {
54
55
  generatePixiLockFile,
55
56
  parseMojoProject,
@@ -61,6 +62,8 @@ import {
61
62
  parseReqFile,
62
63
  parseSetupPyFile,
63
64
  } from "../ecosystems/parsers-python.js";
65
+ import { parseRenvLock } from "../ecosystems/parsers-r.js";
66
+ import { parseTerraformLockFile } from "../ecosystems/parsers-terraform.js";
64
67
  import { collectEmbeddedSboms } from "../ecosystems/pep770.js";
65
68
  import { isPyLockFile } from "../ecosystems/pylockutils.js";
66
69
  import {
@@ -2150,3 +2153,152 @@ export async function createCryptoCertsBom(path, options) {
2150
2153
  },
2151
2154
  };
2152
2155
  }
2156
+
2157
+ /**
2158
+ * Function to create bom string for R projects using renv.
2159
+ *
2160
+ * The `renv.lock` file carries the resolved package set for the project
2161
+ * library, including the R version it was produced under.
2162
+ *
2163
+ * @param {string} path to the project
2164
+ * @param {Object} options Parse options from the cli
2165
+ * @returns {Object} BOM object
2166
+ */
2167
+ export function createRBom(path, options) {
2168
+ let pkgList = [];
2169
+ let dependencies = [];
2170
+ let rootInputs = [];
2171
+ let rVersion;
2172
+ const renvLockFiles = getAllFiles(
2173
+ path,
2174
+ `${options.multiProject ? "**/" : ""}renv.lock`,
2175
+ options,
2176
+ );
2177
+ for (const renvLockFile of renvLockFiles) {
2178
+ if (DEBUG_MODE) {
2179
+ console.log(`Parsing ${renvLockFile}`);
2180
+ }
2181
+ const result = parseRenvLock(renvLockFile);
2182
+ if (result.pkgList?.length) {
2183
+ pkgList = pkgList.concat(result.pkgList);
2184
+ }
2185
+ if (result.dependencies?.length) {
2186
+ dependencies = dependencies.concat(result.dependencies);
2187
+ }
2188
+ if (result.rootInputs?.length) {
2189
+ rootInputs = rootInputs.concat(result.rootInputs);
2190
+ }
2191
+ if (!rVersion) {
2192
+ rVersion = result.rVersion;
2193
+ }
2194
+ }
2195
+ if (!pkgList.length) {
2196
+ return {};
2197
+ }
2198
+ // renv locks the library, not the project: they carry no project name, so
2199
+ // the directory is the name the project is already known by. The R version
2200
+ // the library was resolved under is a property of that project.
2201
+ const projectName = basename(resolve(path));
2202
+ const parentComponent = {
2203
+ type: "application",
2204
+ name: projectName,
2205
+ "bom-ref": `application:${projectName}:latest`,
2206
+ description: `R project: ${projectName}`,
2207
+ properties: [
2208
+ { name: "internal:SrcFile", value: renvLockFiles.join(", ") },
2209
+ ...(rVersion ? [{ name: "cdx:renv:rVersion", value: rVersion }] : []),
2210
+ ],
2211
+ };
2212
+ if (rootInputs.length) {
2213
+ dependencies.push({
2214
+ ref: parentComponent["bom-ref"],
2215
+ dependsOn: [...new Set(rootInputs)].sort(),
2216
+ });
2217
+ }
2218
+ return buildBomNSData(options, pkgList, "cran", {
2219
+ src: path,
2220
+ filename: renvLockFiles.join(", "),
2221
+ dependencies,
2222
+ parentComponent,
2223
+ });
2224
+ }
2225
+
2226
+ /**
2227
+ * Function to create bom string for Perl projects using Carton.
2228
+ *
2229
+ * The `cpanfile.snapshot` carries the resolved install set; the declared
2230
+ * `cpanfile` is consulted to distinguish direct dependencies.
2231
+ *
2232
+ * @param {string} path to the project
2233
+ * @param {Object} options Parse options from the cli
2234
+ * @returns {Object} BOM object
2235
+ */
2236
+ export function createPerlBom(path, options) {
2237
+ let pkgList = [];
2238
+ let dependencies = [];
2239
+ const snapshotFiles = getAllFiles(
2240
+ path,
2241
+ `${options.multiProject ? "**/" : ""}cpanfile.snapshot`,
2242
+ options,
2243
+ );
2244
+ for (const snapshotFile of snapshotFiles) {
2245
+ if (DEBUG_MODE) {
2246
+ console.log(`Parsing ${snapshotFile}`);
2247
+ }
2248
+ const baseDir = dirname(snapshotFile);
2249
+ const cpanfile = join(baseDir, "cpanfile");
2250
+ const result = parseCpanSnapshot(
2251
+ snapshotFile,
2252
+ safeExistsSync(cpanfile) ? cpanfile : undefined,
2253
+ );
2254
+ if (result.pkgList?.length) {
2255
+ pkgList = pkgList.concat(result.pkgList);
2256
+ }
2257
+ if (result.dependencies?.length) {
2258
+ dependencies = dependencies.concat(result.dependencies);
2259
+ }
2260
+ }
2261
+ if (pkgList.length) {
2262
+ return buildBomNSData(options, pkgList, "cpan", {
2263
+ src: path,
2264
+ filename: snapshotFiles.join(", "),
2265
+ dependencies,
2266
+ });
2267
+ }
2268
+ return {};
2269
+ }
2270
+
2271
+ /**
2272
+ * Function to create bom string for Terraform and OpenTofu projects.
2273
+ *
2274
+ * The `.terraform.lock.hcl` file pins the providers the configuration
2275
+ * resolves to, with content hashes suitable for the CycloneDX hashes array.
2276
+ *
2277
+ * @param {string} path to the project
2278
+ * @param {Object} options Parse options from the cli
2279
+ * @returns {Object} BOM object
2280
+ */
2281
+ export function createTerraformBom(path, options) {
2282
+ let pkgList = [];
2283
+ const lockFiles = getAllFiles(
2284
+ path,
2285
+ `${options.multiProject ? "**/" : ""}.terraform.lock.hcl`,
2286
+ options,
2287
+ );
2288
+ for (const lockFile of lockFiles) {
2289
+ if (DEBUG_MODE) {
2290
+ console.log(`Parsing ${lockFile}`);
2291
+ }
2292
+ const result = parseTerraformLockFile(lockFile);
2293
+ if (result.pkgList?.length) {
2294
+ pkgList = pkgList.concat(result.pkgList);
2295
+ }
2296
+ }
2297
+ if (pkgList.length) {
2298
+ return buildBomNSData(options, pkgList, "terraform", {
2299
+ src: path,
2300
+ filename: lockFiles.join(", "),
2301
+ });
2302
+ }
2303
+ return {};
2304
+ }