@cdxgen/cdxgen 13.0.1 → 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 (286) hide show
  1. package/README.md +30 -16
  2. package/bin/audit.js +6 -0
  3. package/bin/cdxgen.js +141 -7
  4. package/bin/evinse.js +3 -1
  5. package/data/component-tags.json +8 -1
  6. package/data/crypto-oid.json +88 -12
  7. package/data/frameworks-list.json +86 -1
  8. package/data/remediations.json +1167 -0
  9. package/data/rules/ai-bom.yaml +78 -0
  10. package/data/rules/asar-archives.yaml +1 -1
  11. package/data/rules/build-fidelity.yaml +782 -0
  12. package/data/rules/cargo-rust.yaml +132 -0
  13. package/data/rules/mcp-servers.yaml +122 -0
  14. package/data/rules/package-integrity.yaml +13 -13
  15. package/lib/audit/index.js +48 -4
  16. package/lib/audit/scoring.js +2 -8
  17. package/lib/audit/targets.js +6 -1
  18. package/lib/cli/bomAssembly.js +32 -1
  19. package/lib/cli/cliOptions.js +94 -7
  20. package/lib/cli/index.js +268 -49
  21. package/lib/cli/jsBom.js +91 -5
  22. package/lib/cli/jvmBom.js +1515 -1314
  23. package/lib/cli/managedBom.js +249 -28
  24. package/lib/cli/nativeBom.js +986 -10
  25. package/lib/core/activity.js +120 -2
  26. package/lib/core/buildLedger.js +799 -0
  27. package/lib/core/deferredExit.js +141 -0
  28. package/lib/core/env.js +108 -2
  29. package/lib/core/fs.js +60 -0
  30. package/lib/core/logger.js +12 -99
  31. package/lib/core/severity.js +16 -0
  32. package/lib/core/syncFileWriter.js +106 -0
  33. package/lib/core/userAgent.js +151 -0
  34. package/lib/ecosystems/cargoCache.js +364 -0
  35. package/lib/ecosystems/caxa.js +13 -5
  36. package/lib/ecosystems/cppEvidence.js +40 -2
  37. package/lib/ecosystems/ecosystems.js +250 -91
  38. package/lib/ecosystems/golem.js +11 -1
  39. package/lib/ecosystems/gradleutils.js +498 -17
  40. package/lib/ecosystems/kosi.js +1256 -0
  41. package/lib/ecosystems/parsers-crystal.js +179 -0
  42. package/lib/ecosystems/parsers-dotnet.js +69 -39
  43. package/lib/ecosystems/parsers-elm.js +816 -0
  44. package/lib/ecosystems/parsers-gleam.js +1 -1
  45. package/lib/ecosystems/parsers-js.js +26 -0
  46. package/lib/ecosystems/parsers-julia.js +268 -0
  47. package/lib/ecosystems/parsers-jvm.js +9 -0
  48. package/lib/ecosystems/parsers-lua.js +218 -0
  49. package/lib/ecosystems/parsers-misc.js +188 -2
  50. package/lib/ecosystems/parsers-nim.js +221 -0
  51. package/lib/ecosystems/parsers-opam.js +180 -0
  52. package/lib/ecosystems/parsers-perl.js +277 -0
  53. package/lib/ecosystems/parsers-python.js +172 -36
  54. package/lib/ecosystems/parsers-r.js +148 -0
  55. package/lib/ecosystems/parsers-rebar.js +411 -0
  56. package/lib/ecosystems/parsers-rust.js +944 -19
  57. package/lib/ecosystems/parsers-spack.js +204 -0
  58. package/lib/ecosystems/parsers-stack.js +264 -0
  59. package/lib/ecosystems/parsers-terraform.js +194 -0
  60. package/lib/ecosystems/pypiClassifiers.js +326 -0
  61. package/lib/ecosystems/pythonutils.js +49 -1
  62. package/lib/ecosystems/rubyutils.js +13 -0
  63. package/lib/ecosystems/rusi.js +126 -6
  64. package/lib/evinser/evinser.js +141 -13
  65. package/lib/helpers/agenticToolutils.js +925 -0
  66. package/lib/helpers/remote/dependency-track.js +81 -14
  67. package/lib/inventory/agentFormulationParser.js +57 -4
  68. package/lib/inventory/aiCollector.js +504 -10
  69. package/lib/inventory/aiInventory.js +12 -0
  70. package/lib/inventory/analyzer.js +488 -30
  71. package/lib/inventory/astWorker.js +6 -0
  72. package/lib/inventory/auditCategories.js +17 -0
  73. package/lib/inventory/cbomutils.js +30 -0
  74. package/lib/inventory/cdxrs.js +7 -2
  75. package/lib/inventory/communityAiConfigParser.js +215 -2
  76. package/lib/inventory/depsUtils.js +72 -0
  77. package/lib/inventory/dosai.js +508 -22
  78. package/lib/inventory/envcontext.js +215 -24
  79. package/lib/inventory/evidenceUtils.js +13 -0
  80. package/lib/inventory/fetchBatch.js +131 -2
  81. package/lib/inventory/fetchRate.js +74 -11
  82. package/lib/inventory/forgeEnricher.js +3 -2
  83. package/lib/inventory/jvmToolEnv.js +594 -0
  84. package/lib/inventory/mcpConfigParser.js +166 -2
  85. package/lib/inventory/mcpDiscovery.js +11 -0
  86. package/lib/inventory/mcpServerManifest.js +389 -0
  87. package/lib/inventory/mcpbBundle.js +419 -0
  88. package/lib/inventory/osinfo.js +1 -0
  89. package/lib/inventory/plugins.js +3 -0
  90. package/lib/inventory/protobom.js +54 -88
  91. package/lib/inventory/protobomLoader.js +4 -0
  92. package/lib/inventory/rustFormulationParser.js +1 -1
  93. package/lib/inventory/source.js +10 -4
  94. package/lib/inventory/stylePackageRefs.js +41 -0
  95. package/lib/inventory/svelteUtils.js +524 -0
  96. package/lib/inventory/toolRequirements.js +903 -0
  97. package/lib/managers/binary.js +24 -0
  98. package/lib/managers/docker.js +26 -5
  99. package/lib/parsers/huggingfaceUtils.js +15 -7
  100. package/lib/parsers/onnx.js +294 -0
  101. package/lib/parsers/safetensors.js +114 -0
  102. package/lib/server/server.js +1 -1
  103. package/lib/stages/postgen/annotationBuilder.js +97 -0
  104. package/lib/stages/postgen/auditBom.js +76 -79
  105. package/lib/stages/postgen/introspection/annotate.js +320 -0
  106. package/lib/stages/postgen/introspection/commandFacts.js +293 -0
  107. package/lib/stages/postgen/introspection/emit.js +192 -0
  108. package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
  109. package/lib/stages/postgen/introspection/markers.js +250 -0
  110. package/lib/stages/postgen/introspection/reflect.js +1214 -0
  111. package/lib/stages/postgen/introspection/report.js +1208 -0
  112. package/lib/stages/postgen/introspection/score.js +1067 -0
  113. package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
  114. package/lib/stages/postgen/parentEdge.js +156 -0
  115. package/lib/stages/postgen/postgen.js +284 -3
  116. package/lib/stages/postgen/ruleEngine.js +17 -0
  117. package/lib/stages/pregen/envAudit.js +42 -2
  118. package/lib/stages/pregen/pregen.js +1171 -6
  119. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
  120. package/lib/validator/bomValidator.js +11 -2
  121. package/package.json +26 -26
  122. package/types/lib/audit/index.d.ts.map +1 -1
  123. package/types/lib/audit/scoring.d.ts +2 -8
  124. package/types/lib/audit/scoring.d.ts.map +1 -1
  125. package/types/lib/audit/targets.d.ts.map +1 -1
  126. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  127. package/types/lib/cli/cliOptions.d.ts +2 -1
  128. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  129. package/types/lib/cli/index.d.ts +10 -2
  130. package/types/lib/cli/index.d.ts.map +1 -1
  131. package/types/lib/cli/jsBom.d.ts +23 -0
  132. package/types/lib/cli/jsBom.d.ts.map +1 -1
  133. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  134. package/types/lib/cli/managedBom.d.ts +33 -0
  135. package/types/lib/cli/managedBom.d.ts.map +1 -1
  136. package/types/lib/cli/nativeBom.d.ts +88 -8
  137. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  138. package/types/lib/core/activity.d.ts.map +1 -1
  139. package/types/lib/core/buildLedger.d.ts +272 -0
  140. package/types/lib/core/buildLedger.d.ts.map +1 -0
  141. package/types/lib/core/deferredExit.d.ts +84 -0
  142. package/types/lib/core/deferredExit.d.ts.map +1 -0
  143. package/types/lib/core/env.d.ts +43 -1
  144. package/types/lib/core/env.d.ts.map +1 -1
  145. package/types/lib/core/fs.d.ts +21 -0
  146. package/types/lib/core/fs.d.ts.map +1 -1
  147. package/types/lib/core/logger.d.ts +2 -11
  148. package/types/lib/core/logger.d.ts.map +1 -1
  149. package/types/lib/core/severity.d.ts +11 -0
  150. package/types/lib/core/severity.d.ts.map +1 -0
  151. package/types/lib/core/syncFileWriter.d.ts +33 -0
  152. package/types/lib/core/syncFileWriter.d.ts.map +1 -0
  153. package/types/lib/core/userAgent.d.ts +56 -0
  154. package/types/lib/core/userAgent.d.ts.map +1 -0
  155. package/types/lib/ecosystems/cargoCache.d.ts +75 -0
  156. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
  157. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  158. package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
  159. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  160. package/types/lib/ecosystems/ecosystems.d.ts +15 -1
  161. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  162. package/types/lib/ecosystems/gradleutils.d.ts +53 -0
  163. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  164. package/types/lib/ecosystems/kosi.d.ts +73 -0
  165. package/types/lib/ecosystems/kosi.d.ts.map +1 -0
  166. package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
  167. package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
  168. package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
  169. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  170. package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
  171. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
  172. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  173. package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
  174. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
  175. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  176. package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
  177. package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
  178. package/types/lib/ecosystems/parsers-misc.d.ts +23 -0
  179. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  180. package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
  181. package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
  182. package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
  183. package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
  184. package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
  185. package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
  186. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  187. package/types/lib/ecosystems/parsers-r.d.ts +35 -0
  188. package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
  189. package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
  190. package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
  191. package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
  192. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  193. package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
  194. package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
  195. package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
  196. package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
  197. package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
  198. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
  199. package/types/lib/ecosystems/pypiClassifiers.d.ts +80 -0
  200. package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
  201. package/types/lib/ecosystems/pythonutils.d.ts +21 -1
  202. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  203. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  205. package/types/lib/evinser/evinser.d.ts +34 -1
  206. package/types/lib/evinser/evinser.d.ts.map +1 -1
  207. package/types/lib/helpers/agenticToolutils.d.ts +87 -0
  208. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
  209. package/types/lib/helpers/remote/dependency-track.d.ts +45 -4
  210. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  211. package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
  212. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  213. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  214. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  215. package/types/lib/inventory/auditCategories.d.ts +12 -0
  216. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  217. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  218. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  219. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  220. package/types/lib/inventory/dosai.d.ts +43 -0
  221. package/types/lib/inventory/dosai.d.ts.map +1 -1
  222. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  223. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  224. package/types/lib/inventory/fetchBatch.d.ts +43 -0
  225. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  226. package/types/lib/inventory/fetchRate.d.ts +9 -6
  227. package/types/lib/inventory/fetchRate.d.ts.map +1 -1
  228. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  229. package/types/lib/inventory/jvmToolEnv.d.ts +190 -0
  230. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
  231. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  232. package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
  233. package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
  234. package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
  235. package/types/lib/inventory/mcpbBundle.d.ts +19 -0
  236. package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
  237. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  238. package/types/lib/inventory/plugins.d.ts.map +1 -1
  239. package/types/lib/inventory/protobom.d.ts +18 -13
  240. package/types/lib/inventory/protobom.d.ts.map +1 -1
  241. package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
  242. package/types/lib/inventory/source.d.ts.map +1 -1
  243. package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
  244. package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
  245. package/types/lib/inventory/svelteUtils.d.ts +124 -0
  246. package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
  247. package/types/lib/inventory/toolRequirements.d.ts +216 -0
  248. package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
  249. package/types/lib/managers/binary.d.ts.map +1 -1
  250. package/types/lib/managers/docker.d.ts.map +1 -1
  251. package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
  252. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  253. package/types/lib/parsers/onnx.d.ts +28 -0
  254. package/types/lib/parsers/onnx.d.ts.map +1 -0
  255. package/types/lib/parsers/safetensors.d.ts +22 -0
  256. package/types/lib/parsers/safetensors.d.ts.map +1 -0
  257. package/types/lib/stages/postgen/annotationBuilder.d.ts +53 -0
  258. package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
  259. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  260. package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
  261. package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
  262. package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
  263. package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
  264. package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
  265. package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
  266. package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
  267. package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
  268. package/types/lib/stages/postgen/introspection/markers.d.ts +58 -0
  269. package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
  270. package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
  271. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
  272. package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
  273. package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
  274. package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
  275. package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
  276. package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
  277. package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
  278. package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
  279. package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
  280. package/types/lib/stages/postgen/postgen.d.ts +35 -2
  281. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  282. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  283. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  284. package/types/lib/stages/pregen/pregen.d.ts +131 -0
  285. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  286. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -6,6 +6,8 @@ import process from "node:process";
6
6
  import { build } from "@cdxgen/cdx-purl";
7
7
 
8
8
  import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
9
+ import { recordDegradation } from "../core/buildLedger.js";
10
+ import { deferFailOnError } from "../core/deferredExit.js";
9
11
  import {
10
12
  DOTNET_CMD,
11
13
  hasAnyProjectType,
@@ -48,6 +50,7 @@ import {
48
50
  parseGitHubWorkflowData,
49
51
  parseHelmYamlData,
50
52
  } from "../ecosystems/parsers-misc.js";
53
+ import { parseCpanSnapshot } from "../ecosystems/parsers-perl.js";
51
54
  import {
52
55
  generatePixiLockFile,
53
56
  parseMojoProject,
@@ -59,6 +62,8 @@ import {
59
62
  parseReqFile,
60
63
  parseSetupPyFile,
61
64
  } from "../ecosystems/parsers-python.js";
65
+ import { parseRenvLock } from "../ecosystems/parsers-r.js";
66
+ import { parseTerraformLockFile } from "../ecosystems/parsers-terraform.js";
62
67
  import { collectEmbeddedSboms } from "../ecosystems/pep770.js";
63
68
  import { isPyLockFile } from "../ecosystems/pylockutils.js";
64
69
  import {
@@ -94,6 +99,7 @@ import {
94
99
  trimComponents,
95
100
  } from "../inventory/depsUtils.js";
96
101
  import {
102
+ collectDosaiServiceComponents,
97
103
  collectDosaiServicesFromMethods,
98
104
  createDosaiMethodsSlice,
99
105
  isDosaiDotnetLanguage,
@@ -102,6 +108,7 @@ import {
102
108
  } from "../inventory/dosai.js";
103
109
  import { addEvidenceForDotnet } from "../inventory/evidenceUtils.js";
104
110
  import { nugetPurl, pypiBomRef } from "../inventory/purl.js";
111
+ import { classifyProbeResult } from "../inventory/toolRequirements.js";
105
112
  import { getPluginToolComponents } from "../managers/binary.js";
106
113
  import { getTreeWithPlugin } from "../managers/piptree.js";
107
114
  import {
@@ -408,7 +415,11 @@ export async function createPythonBom(path, options) {
408
415
  }
409
416
  let retMap = await parsePyLockData(lockData, f);
410
417
  if (retMap?.workspaceWarningShown) {
411
- options.failOnError && process.exit(1);
418
+ deferFailOnError(options, {
419
+ ecosystem: "python",
420
+ tool: "python",
421
+ detail: "the python lock file could not be parsed",
422
+ });
412
423
  }
413
424
  if (retMap?.pyLockProperties?.length) {
414
425
  parentComponent.properties = parentComponent.properties || [];
@@ -458,15 +469,6 @@ export async function createPythonBom(path, options) {
458
469
  retMap.dependenciesList,
459
470
  parentComponent,
460
471
  );
461
- if (retMap.rootList) {
462
- const parentDependsOn = new Set();
463
- for (const p of retMap.rootList)
464
- parentDependsOn.add(pypiBomRef(p.name, p.version));
465
- dependencies.splice(0, 0, {
466
- ref: parentComponent["bom-ref"],
467
- dependsOn: [...parentDependsOn].sort(),
468
- });
469
- }
470
472
  } else {
471
473
  let exportedReqs = "";
472
474
  if (f.endsWith("poetry.lock")) {
@@ -493,6 +495,11 @@ export async function createPythonBom(path, options) {
493
495
  exportedReqs = expCmd.stdout.toString();
494
496
  }
495
497
  if (exportedReqs) {
498
+ // The export contains the resolved closure, not just the first
499
+ // level, so it contributes components only. The root edge comes
500
+ // from parsePyLockData (seeded from the manifest); for locks
501
+ // without a neighbouring pyproject.toml the generic postgen
502
+ // parent-edge repair keeps the tree reachable.
496
503
  const tmpReqFile = join(
497
504
  tempDir,
498
505
  `exported-${basename(basePath)}-reqs.txt`,
@@ -501,13 +508,6 @@ export async function createPythonBom(path, options) {
501
508
  const dlist = await parseReqFile(tmpReqFile, false);
502
509
  if (dlist?.length) {
503
510
  pkgList = pkgList.concat(dlist);
504
- const parentDependsOn = new Set();
505
- for (const p of dlist)
506
- parentDependsOn.add(pypiBomRef(p.name, p.version));
507
- dependencies.splice(0, 0, {
508
- ref: parentComponent["bom-ref"],
509
- dependsOn: [...parentDependsOn].sort(),
510
- });
511
511
  }
512
512
  }
513
513
  }
@@ -577,14 +577,30 @@ export async function createPythonBom(path, options) {
577
577
  safeSpawnSync("pipenv", ["install"], { cwd: path, shell: isWin });
578
578
  const piplockFile = join(path, "Pipfile.lock");
579
579
  if (safeExistsSync(piplockFile)) {
580
- const lockData = JSON.parse(readFileSync(piplockFile));
581
- const dlist = await parsePiplockData(lockData);
580
+ let dlist;
581
+ try {
582
+ const lockData = JSON.parse(readFileSync(piplockFile));
583
+ dlist = await parsePiplockData(lockData);
584
+ } catch (err) {
585
+ recordDegradation("python.lockfile-unparseable.pipenv", {
586
+ ecosystem: "python",
587
+ impact: "transitive-deps",
588
+ path: piplockFile,
589
+ detail:
590
+ "The Pipfile.lock could not be parsed, so no locked versions were captured from it.",
591
+ });
592
+ console.log(`Error while parsing the lock file ${piplockFile}.`, err);
593
+ }
582
594
  if (dlist?.length) {
583
595
  pkgList = pkgList.concat(dlist);
584
596
  }
585
597
  } else {
586
598
  console.error("Pipfile.lock not found at", path);
587
- options.failOnError && process.exit(1);
599
+ deferFailOnError(options, {
600
+ ecosystem: "python",
601
+ tool: "pipenv",
602
+ detail: "Pipfile.lock was not found",
603
+ });
588
604
  }
589
605
  } else if (reqDirFiles?.length) {
590
606
  for (const j in reqDirFiles) {
@@ -1087,13 +1103,33 @@ export function createPHPBom(path, options) {
1087
1103
  shell: isWin,
1088
1104
  });
1089
1105
  if (versionResult.status !== 0 || versionResult.error) {
1106
+ // A composer the runtime or a policy denied is not an unavailable
1107
+ // composer; the version probes report that as degraded evidence.
1108
+ if (classifyProbeResult(versionResult) === "missing") {
1109
+ recordDegradation("php.composer.missing", {
1110
+ ecosystem: "php",
1111
+ tool: "composer",
1112
+ impact: "components",
1113
+ command: "composer --version",
1114
+ exitCode:
1115
+ typeof versionResult.status === "number"
1116
+ ? versionResult.status
1117
+ : undefined,
1118
+ detail:
1119
+ "The composer executable could not be run, so the composer.lock generation could not be attempted.",
1120
+ });
1121
+ }
1090
1122
  console.error(
1091
1123
  "No composer version found. Check if composer is installed and available in PATH.",
1092
1124
  );
1093
1125
  if (DEBUG_MODE) {
1094
1126
  console.log(versionResult.error, versionResult.stderr);
1095
1127
  }
1096
- options.failOnError && process.exit(1);
1128
+ deferFailOnError(options, {
1129
+ ecosystem: "php",
1130
+ tool: "composer",
1131
+ detail: "the composer version command failed",
1132
+ });
1097
1133
  }
1098
1134
  let composerVersion;
1099
1135
  if (DEBUG_MODE) {
@@ -1129,7 +1165,11 @@ export function createPHPBom(path, options) {
1129
1165
  console.log(result.stderr);
1130
1166
  }
1131
1167
  console.log(result.error);
1132
- options.failOnError && process.exit(1);
1168
+ deferFailOnError(options, {
1169
+ ecosystem: "php",
1170
+ tool: "composer",
1171
+ detail: "the composer install command failed",
1172
+ });
1133
1173
  }
1134
1174
  }
1135
1175
  }
@@ -1206,6 +1246,15 @@ export function createPHPBom(path, options) {
1206
1246
  parentComponent,
1207
1247
  });
1208
1248
  }
1249
+ if (composerJsonMode && !composerLockMode) {
1250
+ recordDegradation("php.no-lockfile", {
1251
+ ecosystem: "php",
1252
+ tool: "composer",
1253
+ impact: "components",
1254
+ detail:
1255
+ "No composer.lock was found or generated, and the lock file is the source the Composer parser reads.",
1256
+ });
1257
+ }
1209
1258
  return {};
1210
1259
  }
1211
1260
 
@@ -1319,7 +1368,11 @@ export async function createRubyBom(path, options) {
1319
1368
  "Bundle install has failed. Check if bundle is installed and available in PATH.",
1320
1369
  );
1321
1370
  console.log(result.error, result.stderr);
1322
- options.failOnError && process.exit(1);
1371
+ deferFailOnError(options, {
1372
+ ecosystem: "ruby",
1373
+ tool: "bundler",
1374
+ detail: "the bundler command failed",
1375
+ });
1323
1376
  }
1324
1377
  }
1325
1378
  }
@@ -1361,7 +1414,11 @@ export async function createRubyBom(path, options) {
1361
1414
  console.log(
1362
1415
  "SBOM generation using only gemspec files is imprecise and results in an inaccurate dependency tree.",
1363
1416
  );
1364
- options.failOnError && process.exit(1);
1417
+ deferFailOnError(options, {
1418
+ ecosystem: "ruby",
1419
+ tool: "bundler",
1420
+ detail: "only gemspec files were available to parse",
1421
+ });
1365
1422
  }
1366
1423
  for (const f of gemspecFiles) {
1367
1424
  const gemspecData = readFileSync(f, { encoding: "utf-8" });
@@ -1634,7 +1691,11 @@ export async function createCsharpBom(path, options) {
1634
1691
  "TIP: msbuild is not available for Linux. Try using a Windows build agent to generate an SBOM for this project.",
1635
1692
  );
1636
1693
  }
1637
- options.failOnError && process.exit(1);
1694
+ deferFailOnError(options, {
1695
+ ecosystem: "csharp",
1696
+ tool: "dotnet",
1697
+ detail: "the dotnet command failed",
1698
+ });
1638
1699
  }
1639
1700
  if (result?.stderr?.includes("To install missing framework")) {
1640
1701
  console.log(
@@ -1683,7 +1744,11 @@ export async function createCsharpBom(path, options) {
1683
1744
  console.log(result.stdout);
1684
1745
  }
1685
1746
  console.log("---------");
1686
- options.failOnError && process.exit(1);
1747
+ deferFailOnError(options, {
1748
+ ecosystem: "csharp",
1749
+ tool: "dotnet",
1750
+ detail: "the dotnet restore did not produce a usable assets file",
1751
+ });
1687
1752
  }
1688
1753
  }
1689
1754
  // Collect the assets, lock, and nupkg files generated from restore
@@ -1786,7 +1851,11 @@ export async function createCsharpBom(path, options) {
1786
1851
  console.log(
1787
1852
  "Alternatively, try using the custom `ghcr.io/cdxgen/cdxgen-dotnet:v13` container image, which bundles a range of dotnet SDKs.",
1788
1853
  );
1789
- options.failOnError && process.exit(1);
1854
+ deferFailOnError(options, {
1855
+ ecosystem: "csharp",
1856
+ tool: "dotnet",
1857
+ detail: "no dotnet dependency evidence could be collected",
1858
+ });
1790
1859
  }
1791
1860
  }
1792
1861
  // packages.lock.json parsing for any project without a project.assets.json
@@ -1960,7 +2029,10 @@ export async function createCsharpBom(path, options) {
1960
2029
  }
1961
2030
  pkgList = addEvidenceForDotnet(pkgList, slicesFile);
1962
2031
  const methodsSlice = readDosaiJsonFile(slicesFile);
1963
- const servicesMap = collectDosaiServicesFromMethods(methodsSlice, {});
2032
+ // Provider-backed Services[] first so bom-refs, trust zones, and data
2033
+ // classifications survive; ApiEndpoints derivation fills in the rest.
2034
+ const servicesMap = collectDosaiServiceComponents(methodsSlice, {});
2035
+ collectDosaiServicesFromMethods(methodsSlice, servicesMap);
1964
2036
  services = normalizeDosaiServiceMap(servicesMap);
1965
2037
  }
1966
2038
  }
@@ -2081,3 +2153,152 @@ export async function createCryptoCertsBom(path, options) {
2081
2153
  },
2082
2154
  };
2083
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
+ }