@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
@@ -23,6 +23,21 @@ export const GOLEM_AUDIT_CATEGORIES = Object.freeze([
23
23
  "golem-compliance",
24
24
  ]);
25
25
 
26
+ /** Frozen list of Cargo (Rust) audit categories. */
27
+ export const CARGO_AUDIT_CATEGORIES = Object.freeze([
28
+ "cargo-security",
29
+ "cargo-compliance",
30
+ ]);
31
+
32
+ /**
33
+ * Frozen list of audit categories that never activate on their own. These
34
+ * rule packs serve a specific feature (`--introspect` for build-fidelity) and
35
+ * stay inactive unless the caller explicitly requests the category, so a
36
+ * default `--bom-audit` run - which evaluates every loaded rule - cannot pick
37
+ * them up.
38
+ */
39
+ export const OPT_IN_BOM_AUDIT_CATEGORIES = Object.freeze(["build-fidelity"]);
40
+
26
41
  /** Frozen list of AI-bill-of-materials audit categories. */
27
42
  export const AI_BOM_AUDIT_CATEGORIES = Object.freeze([
28
43
  "ai-governance",
@@ -43,10 +58,12 @@ export const BOM_AUDIT_CATEGORY_ALIASES = Object.freeze({
43
58
  "ai-bom": [...AI_BOM_AUDIT_CATEGORIES, "ai-agent", "mcp-server"],
44
59
  "ai-provenance": ["ai-provenance", "ai-oversight"],
45
60
  "ai-oversight": ["ai-oversight"],
61
+ cargo: [...CARGO_AUDIT_CATEGORIES],
46
62
  cbom: [...CBOM_AUDIT_CATEGORIES],
47
63
  "crypto-bom": [...CBOM_AUDIT_CATEGORIES],
48
64
  golem: [...GOLEM_AUDIT_CATEGORIES],
49
65
  hbom: [...HBOM_AUDIT_CATEGORIES],
66
+ rust: [...CARGO_AUDIT_CATEGORIES],
50
67
  host: [...HBOM_AUDIT_CATEGORIES, ...HOST_TOPOLOGY_AUDIT_CATEGORIES],
51
68
  });
52
69
 
@@ -345,6 +345,36 @@ function normalizeDosaiCryptoNames(cryptoObject) {
345
345
  names.add("sha-512");
346
346
  }
347
347
  }
348
+ // dosai reports the post-quantum families under their generic names and no
349
+ // generic OID exists for any of them, so the parameter set has to come from
350
+ // the call-site context (dosai's Code field carries the source line, e.g.
351
+ // "MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65)"). Without a parameter set the
352
+ // asset stays unmapped and is skipped rather than emitted with a wrong OID.
353
+ if (/^ML-?DSA$/i.test(cleanName)) {
354
+ const parameterSet = context.match(/(?:MLDsa|ML-DSA-)(44|65|87)\b/i);
355
+ if (parameterSet) {
356
+ names.add(`ml-dsa-${parameterSet[1]}`);
357
+ }
358
+ }
359
+ if (/^ML-?KEM$/i.test(cleanName)) {
360
+ const parameterSet = context.match(/(?:MLKem|ML-KEM-)(512|768|1024)\b/i);
361
+ if (parameterSet) {
362
+ names.add(`ml-kem-${parameterSet[1]}`);
363
+ }
364
+ }
365
+ if (/^SLH-?DSA$/i.test(cleanName)) {
366
+ // .NET spells it SlhDsaSha2_128s; NIST SLH-DSA-SHA2-128s. The hash family
367
+ // is required: SHA2 and SHAKE have distinct OIDs, so a context naming only
368
+ // the size, such as SLH-DSA-192f, resolves to no single parameter set.
369
+ const parameterSet = context.match(
370
+ /(?:SlhDsa|SLH-DSA-)(SHA2|SHAKE)[-_]?(128|192|256)(s|f)\b/i,
371
+ );
372
+ if (parameterSet) {
373
+ names.add(
374
+ `slh-dsa-${parameterSet[1].toLowerCase()}-${parameterSet[2]}${parameterSet[3].toLowerCase()}`,
375
+ );
376
+ }
377
+ }
348
378
  return Array.from(names);
349
379
  }
350
380
 
@@ -25,8 +25,13 @@ import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
25
25
  import { safeExistsSync } from "../core/fs.js";
26
26
  import { getDefaultPluginRuntime, resolvePluginBinary } from "./plugins.js";
27
27
 
28
- /** Major version this bridge expects from the cdxrs binary. */
29
- const CDXRS_VERSION_MAJOR = "3";
28
+ /**
29
+ * Major version this bridge expects from the cdxrs binary. cdxgen-plugins-bin
30
+ * versions every tool in lockstep, so this moves with
31
+ * `@cdxgen/cdxgen-plugins-bin` in package.json even when cdxrs itself carries
32
+ * no breaking change (4.0.0 is 3.2.0 renumbered by the release).
33
+ */
34
+ const CDXRS_VERSION_MAJOR = "4";
30
35
 
31
36
  /** Default timeout for a cdxrs invocation. */
32
37
  const DEFAULT_TIMEOUT_MS = 30_000;
@@ -1,8 +1,9 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { basename, dirname, extname } from "node:path";
2
+ import { basename, dirname, extname, join } from "node:path";
3
3
 
4
4
  import { parse as loadYaml } from "yaml";
5
5
 
6
+ import { safeExistsSync } from "../core/fs.js";
6
7
  import { sanitizeBomPropertyValue } from "../core/propertySanitizer.js";
7
8
  import { parseJsonLike } from "./jsonLike.js";
8
9
  import {
@@ -28,6 +29,18 @@ const COMMUNITY_AI_PATTERNS = [
28
29
  "**/.claude/skills/*/SKILL.md",
29
30
  ".agents/skills/*/SKILL.md",
30
31
  "**/.agents/skills/*/SKILL.md",
32
+ ".codex/skills/*/SKILL.md",
33
+ "**/.codex/skills/*/SKILL.md",
34
+ "skills/*/SKILL.md",
35
+ "**/skills/*/SKILL.md",
36
+ ".claude-plugin/plugin.json",
37
+ "**/.claude-plugin/plugin.json",
38
+ ".claude-plugin/marketplace.json",
39
+ "**/.claude-plugin/marketplace.json",
40
+ ".claude/agents/*.md",
41
+ "**/.claude/agents/*.md",
42
+ ".claude/commands/**/*.md",
43
+ "**/.claude/commands/**/*.md",
31
44
  ".cursor/rules/*.{md,mdc}",
32
45
  "**/.cursor/rules/*.{md,mdc}",
33
46
  ".windsurf/rules/**/*.md",
@@ -145,9 +158,24 @@ function frameworkForFile(filePath, raw = "") {
145
158
  if (lowerPath.includes("/.claude/skills/")) {
146
159
  return "claude-compatible";
147
160
  }
161
+ if (lowerPath.includes("/.claude-plugin/")) {
162
+ return "claude-plugin";
163
+ }
164
+ if (lowerPath.includes("/.claude/agents/")) {
165
+ return "claude-subagent";
166
+ }
167
+ if (lowerPath.includes("/.claude/commands/")) {
168
+ return "claude-command";
169
+ }
170
+ if (lowerPath.includes("/.codex/skills/")) {
171
+ return "codex";
172
+ }
148
173
  if (lowerPath.includes("/.agents/skills/")) {
149
174
  return "agent-compatible";
150
175
  }
176
+ if (/(?:^|\/)skills\/[^/]+\/skill\.md$/u.test(lowerPath)) {
177
+ return "agent-skills";
178
+ }
151
179
  return "community-ai";
152
180
  }
153
181
 
@@ -250,6 +278,173 @@ function parseSkillFile(filePath, raw) {
250
278
  metadata.metadata,
251
279
  );
252
280
  }
281
+ // Record whether the frontmatter satisfies the open Agent Skills constraints
282
+ // (name: 1-64 lowercase-alnum-hyphen; description: 1-1024 chars) as a single
283
+ // reviewer-friendly boolean rather than copying the raw values twice.
284
+ const specValid =
285
+ typeof metadata?.name === "string" &&
286
+ /^[a-z0-9-]{1,64}$/u.test(metadata.name) &&
287
+ typeof metadata?.description === "string" &&
288
+ metadata.description.length >= 1 &&
289
+ metadata.description.length <= 1024;
290
+ addUniqueProperty(
291
+ component.properties,
292
+ "cdx:skill:specValid",
293
+ specValid ? "true" : "false",
294
+ );
295
+ const skillDir = dirname(filePath);
296
+ for (const [subdir, property] of [
297
+ ["scripts", "cdx:skill:hasScripts"],
298
+ ["references", "cdx:skill:hasReferences"],
299
+ ["assets", "cdx:skill:hasAssets"],
300
+ ]) {
301
+ if (safeExistsSync(join(skillDir, subdir))) {
302
+ addUniqueProperty(component.properties, property, "true");
303
+ }
304
+ }
305
+ maybeAddFileSignals(component.properties, filePath, raw);
306
+ return [component];
307
+ }
308
+
309
+ /**
310
+ * Parse a Claude Code plugin manifest (`.claude-plugin/plugin.json`) into a
311
+ * single application component describing the plugin.
312
+ *
313
+ * @param {string} filePath Manifest path
314
+ * @param {string} raw File contents
315
+ * @returns {Object[]} Component list (one plugin component or empty)
316
+ */
317
+ function parsePluginManifest(filePath, raw) {
318
+ let manifest;
319
+ try {
320
+ manifest = parseJsonLike(raw);
321
+ } catch {
322
+ return [];
323
+ }
324
+ if (!manifest?.name) {
325
+ return [];
326
+ }
327
+ const component = createComponent(
328
+ filePath,
329
+ "claude-plugin",
330
+ "agent-plugin",
331
+ manifest.name,
332
+ );
333
+ if (manifest.version) {
334
+ component.version = String(manifest.version);
335
+ }
336
+ addUniqueProperty(
337
+ component.properties,
338
+ "cdx:plugin:description",
339
+ manifest.description,
340
+ );
341
+ addUniqueProperty(
342
+ component.properties,
343
+ "cdx:plugin:author",
344
+ typeof manifest.author === "string"
345
+ ? manifest.author
346
+ : manifest.author?.name,
347
+ );
348
+ for (const [key, property] of [
349
+ ["commands", "cdx:plugin:commandCount"],
350
+ ["agents", "cdx:plugin:agentCount"],
351
+ ["skills", "cdx:plugin:skillCount"],
352
+ ["hooks", "cdx:plugin:hookCount"],
353
+ ["mcpServers", "cdx:plugin:mcpServerCount"],
354
+ ]) {
355
+ const value = manifest[key];
356
+ if (Array.isArray(value)) {
357
+ addUniqueProperty(component.properties, property, String(value.length));
358
+ } else if (value && typeof value === "object") {
359
+ addUniqueProperty(
360
+ component.properties,
361
+ property,
362
+ String(Object.keys(value).length),
363
+ );
364
+ }
365
+ }
366
+ maybeAddFileSignals(component.properties, filePath, raw);
367
+ return [component];
368
+ }
369
+
370
+ /**
371
+ * Parse a Claude Code plugin marketplace file
372
+ * (`.claude-plugin/marketplace.json`) into one component per listed plugin.
373
+ *
374
+ * @param {string} filePath Marketplace path
375
+ * @param {string} raw File contents
376
+ * @returns {Object[]} Component list
377
+ */
378
+ function parsePluginMarketplace(filePath, raw) {
379
+ let marketplace;
380
+ try {
381
+ marketplace = parseJsonLike(raw);
382
+ } catch {
383
+ return [];
384
+ }
385
+ const plugins = Array.isArray(marketplace?.plugins)
386
+ ? marketplace.plugins
387
+ : [];
388
+ const components = [];
389
+ for (const plugin of plugins) {
390
+ const name = plugin?.name || plugin?.source;
391
+ if (!name) {
392
+ continue;
393
+ }
394
+ const component = createComponent(
395
+ filePath,
396
+ "claude-plugin",
397
+ "agent-plugin-listing",
398
+ String(name),
399
+ );
400
+ addUniqueProperty(
401
+ component.properties,
402
+ "cdx:plugin:description",
403
+ plugin.description,
404
+ );
405
+ addUniqueProperty(
406
+ component.properties,
407
+ "cdx:plugin:source",
408
+ typeof plugin.source === "string" ? plugin.source : undefined,
409
+ );
410
+ components.push(component);
411
+ }
412
+ return components;
413
+ }
414
+
415
+ /**
416
+ * Parse a Claude Code subagent definition (`.claude/agents/*.md`) into an
417
+ * agent-definition component using its markdown frontmatter.
418
+ *
419
+ * @param {string} filePath Subagent markdown path
420
+ * @param {string} raw File contents
421
+ * @returns {Object[]} Component list
422
+ */
423
+ function parseClaudeSubagent(filePath, raw) {
424
+ const { metadata } = parseFrontmatter(raw);
425
+ const name = metadata?.name || basename(filePath, extname(filePath));
426
+ const component = createComponent(
427
+ filePath,
428
+ "claude-subagent",
429
+ "agent-definition",
430
+ name,
431
+ );
432
+ addUniqueProperty(component.properties, "cdx:agent:role", "claude-subagent");
433
+ addUniqueProperty(
434
+ component.properties,
435
+ "cdx:agent:description",
436
+ metadata?.description,
437
+ );
438
+ addUniqueProperty(component.properties, "cdx:agent:model", metadata?.model);
439
+ if (Array.isArray(metadata?.tools)) {
440
+ addUniqueProperty(
441
+ component.properties,
442
+ "cdx:agent:tools",
443
+ metadata.tools.join(","),
444
+ );
445
+ } else if (typeof metadata?.tools === "string") {
446
+ addUniqueProperty(component.properties, "cdx:agent:tools", metadata.tools);
447
+ }
253
448
  maybeAddFileSignals(component.properties, filePath, raw);
254
449
  return [component];
255
450
  }
@@ -708,10 +903,28 @@ export const communityAiConfigParser = {
708
903
  components.push(...parseAgentMarkdown(filePath, raw));
709
904
  continue;
710
905
  }
906
+ if (lowerPath.endsWith("/.claude-plugin/plugin.json")) {
907
+ components.push(...parsePluginManifest(filePath, raw));
908
+ continue;
909
+ }
910
+ if (lowerPath.endsWith("/.claude-plugin/marketplace.json")) {
911
+ components.push(...parsePluginMarketplace(filePath, raw));
912
+ continue;
913
+ }
914
+ if (lowerPath.includes("/.claude/agents/")) {
915
+ components.push(...parseClaudeSubagent(filePath, raw));
916
+ continue;
917
+ }
918
+ if (lowerPath.includes("/.claude/commands/")) {
919
+ components.push(...parseInstructionMarkdown(filePath, raw));
920
+ continue;
921
+ }
711
922
  if (
712
923
  lowerPath.includes("/.opencode/skills/") ||
713
924
  lowerPath.includes("/.claude/skills/") ||
714
- lowerPath.includes("/.agents/skills/")
925
+ lowerPath.includes("/.agents/skills/") ||
926
+ lowerPath.includes("/.codex/skills/") ||
927
+ lowerPath.endsWith("/skill.md")
715
928
  ) {
716
929
  components.push(...parseSkillFile(filePath, raw));
717
930
  continue;
@@ -222,6 +222,28 @@ function serviceIdentityKey(service) {
222
222
  return `${service?.group || ""}:${service?.name || ""}:${service?.version || ""}`.toLowerCase();
223
223
  }
224
224
 
225
+ /**
226
+ * Union two services[].data[] lists, keyed by flow + classification + name so that repeated
227
+ * classifications of the same payload do not accumulate duplicates.
228
+ *
229
+ * @param {Array} existingData Data entries already on the service
230
+ * @param {Array} newData Data entries being merged in
231
+ * @returns {Array} Merged data entries
232
+ */
233
+ function mergeServiceData(existingData = [], newData = []) {
234
+ const seen = new Map();
235
+ for (const entry of [...existingData, ...newData]) {
236
+ if (!entry?.flow || !entry?.classification) {
237
+ continue;
238
+ }
239
+ const key = `${entry.flow}|${entry.classification}|${entry.name || ""}`;
240
+ if (!seen.has(key)) {
241
+ seen.set(key, entry);
242
+ }
243
+ }
244
+ return Array.from(seen.values());
245
+ }
246
+
225
247
  function mergeServiceProperties(existingProps = [], newProps = []) {
226
248
  const merged = [...existingProps];
227
249
  for (const newProp of newProps) {
@@ -249,6 +271,38 @@ function normalizeServiceEndpoints(endpoints) {
249
271
  return [];
250
272
  }
251
273
 
274
+ function evidenceOccurrenceKey(occurrence) {
275
+ const location = occurrence?.location || {};
276
+ return `${location.path || ""}#${location.line ?? ""}#${location.column ?? ""}`;
277
+ }
278
+
279
+ /**
280
+ * Union two service evidence objects by their occurrences so a collision keeps
281
+ * every call site instead of whichever side arrived first.
282
+ *
283
+ * @param {Object} [existingEvidence] Evidence already on the service
284
+ * @param {Object} [newEvidence] Evidence being merged in
285
+ * @returns {Object} Evidence covering both inputs
286
+ */
287
+ function mergeServiceEvidence(existingEvidence, newEvidence) {
288
+ if (!existingEvidence) {
289
+ return newEvidence;
290
+ }
291
+ const occurrences = [...(existingEvidence.occurrences || [])];
292
+ const seen = new Set(occurrences.map(evidenceOccurrenceKey));
293
+ for (const occurrence of newEvidence?.occurrences || []) {
294
+ const key = evidenceOccurrenceKey(occurrence);
295
+ if (!seen.has(key)) {
296
+ seen.add(key);
297
+ occurrences.push(occurrence);
298
+ }
299
+ }
300
+ if (!occurrences.length) {
301
+ return existingEvidence;
302
+ }
303
+ return { ...existingEvidence, occurrences };
304
+ }
305
+
252
306
  /**
253
307
  * Merge CycloneDX services using bom-ref or group/name/version identity.
254
308
  *
@@ -313,6 +367,24 @@ export function mergeServices(services, newServices) {
313
367
  existing.properties || [],
314
368
  service.properties || [],
315
369
  );
370
+ // Carry the richer fields through a collision too. Without these, a dosai service that collided
371
+ // with one derived from endpoints lost precisely what schema 4.0.0 adds: its bom-ref, its data
372
+ // classifications, and its evidence.
373
+ existing["bom-ref"] = existing["bom-ref"] || service["bom-ref"];
374
+ if (Array.isArray(service.data) && service.data.length) {
375
+ existing.data = mergeServiceData(existing.data || [], service.data);
376
+ }
377
+ if (service.evidence) {
378
+ existing.evidence = mergeServiceEvidence(
379
+ existing.evidence,
380
+ service.evidence,
381
+ );
382
+ }
383
+ if (Array.isArray(service.tags) && service.tags.length) {
384
+ existing.tags = Array.from(
385
+ new Set([...(existing.tags || []), ...service.tags]),
386
+ );
387
+ }
316
388
  if (Array.isArray(service.services) && service.services.length) {
317
389
  existing.services = mergeServices(
318
390
  existing.services || [],