@cdxgen/cdxgen 13.5.0 → 13.7.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 (380) hide show
  1. package/README.md +17 -10
  2. package/bin/audit.js +7 -4
  3. package/bin/cdxgen.js +260 -37
  4. package/bin/convert.js +146 -22
  5. package/bin/evinse.js +28 -3
  6. package/bin/repl.js +47 -3
  7. package/bin/sign.js +211 -34
  8. package/bin/tracebom.js +18 -6
  9. package/bin/validate.js +24 -3
  10. package/bin/verify.js +113 -5
  11. package/data/behavior-taxonomy.schema.json +1491 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
  13. package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
  14. package/data/license-aliases.json +24 -2
  15. package/data/pqc-migration.json +239 -0
  16. package/data/remediations.json +176 -0
  17. package/data/rules/ai-bom.yaml +1 -1
  18. package/data/rules/brotli-decoder.yaml +107 -0
  19. package/data/rules/cbom-security.yaml +4 -3
  20. package/data/rules/package-integrity.yaml +28 -0
  21. package/data/rules/vscode-extensions.yaml +75 -0
  22. package/data/spdx-license-list.json +121 -7
  23. package/data/spdx-licenses.json +69 -0
  24. package/data/spdx.schema.json +845 -2
  25. package/lib/audit/index.js +25 -3
  26. package/lib/cli/bomAssembly.js +172 -4
  27. package/lib/cli/cliOptions.js +121 -17
  28. package/lib/cli/index.js +720 -902
  29. package/lib/cli/jsBom.js +156 -64
  30. package/lib/cli/jvmBom.js +602 -86
  31. package/lib/cli/managedBom.js +161 -19
  32. package/lib/cli/nativeBom.js +52 -5
  33. package/lib/core/boundedFile.js +344 -0
  34. package/lib/core/brotliProbe.js +182 -0
  35. package/lib/core/env.js +34 -0
  36. package/lib/core/fs.js +138 -3
  37. package/lib/core/globPatterns.js +218 -0
  38. package/lib/core/httpClient.js +98 -1
  39. package/lib/core/lockPaths.js +38 -0
  40. package/lib/core/paths.js +24 -1
  41. package/lib/core/processTree.js +100 -8
  42. package/lib/core/projectConfig.js +332 -25
  43. package/lib/core/toolRegistry.js +184 -0
  44. package/lib/ecosystems/cppEvidence.js +263 -106
  45. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  46. package/lib/ecosystems/denoutils.js +2 -2
  47. package/lib/ecosystems/ecosystems.js +25 -10
  48. package/lib/ecosystems/gemHostProbe.js +301 -0
  49. package/lib/ecosystems/golem.js +3 -8
  50. package/lib/ecosystems/gradleutils.js +1909 -229
  51. package/lib/ecosystems/jsEvidence.js +90 -38
  52. package/lib/ecosystems/kosi.js +3 -5
  53. package/lib/ecosystems/npmVersionResolution.js +376 -0
  54. package/lib/ecosystems/parsers-bazel.js +107 -6
  55. package/lib/ecosystems/parsers-js.js +505 -22
  56. package/lib/ecosystems/parsers-misc.js +209 -20
  57. package/lib/ecosystems/parsers-python.js +178 -30
  58. package/lib/ecosystems/parsers-rust.js +117 -15
  59. package/lib/ecosystems/pythonutils.js +128 -2
  60. package/lib/ecosystems/remote/huggingface.js +22 -0
  61. package/lib/ecosystems/rubyutils.js +538 -31
  62. package/lib/ecosystems/rusi.js +2 -4
  63. package/lib/ecosystems/scalacliutils.js +2 -0
  64. package/lib/ecosystems/scalasem.js +798 -152
  65. package/lib/ecosystems/vendoredCode.js +237 -3
  66. package/lib/ecosystems/venvProbe.js +360 -0
  67. package/lib/evinser/evinser.js +536 -71
  68. package/lib/evinser/scalasem.js +5 -1
  69. package/lib/helpers/agenticToolutils.js +525 -94
  70. package/lib/helpers/bomSigner.js +43 -9
  71. package/lib/helpers/jssSigner.js +1276 -0
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentBlueprint.js +193 -0
  74. package/lib/inventory/agentToolNames.js +145 -0
  75. package/lib/inventory/aiCollector.js +153 -7
  76. package/lib/inventory/aiInventory.js +44 -8
  77. package/lib/inventory/aiProviders.js +324 -0
  78. package/lib/inventory/atomUtils.js +77 -0
  79. package/lib/inventory/bomUtils.js +8 -1
  80. package/lib/inventory/brotliDecoder.js +142 -0
  81. package/lib/inventory/buildToolRateLimit.js +1 -1
  82. package/lib/inventory/cbomutils.js +56 -7
  83. package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
  84. package/lib/inventory/citations.js +31 -2
  85. package/lib/inventory/communityAiConfigParser.js +978 -80
  86. package/lib/inventory/cyclonedx20.js +152 -0
  87. package/lib/inventory/cyclonedx20Common.js +286 -0
  88. package/lib/inventory/cyclonedx20Derived.js +159 -0
  89. package/lib/inventory/cyclonedx20Parties.js +552 -0
  90. package/lib/inventory/cyclonedx20Read.js +768 -0
  91. package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
  92. package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
  93. package/lib/inventory/cyclonedx20ReadContext.js +380 -0
  94. package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
  95. package/lib/inventory/cyclonedx20ReadSections.js +599 -0
  96. package/lib/inventory/cyclonedx20Render.js +766 -0
  97. package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
  98. package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
  99. package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
  100. package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
  101. package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
  102. package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
  103. package/lib/inventory/cyclonedx20RenderSections.js +499 -0
  104. package/lib/inventory/cyclonedx20Tables.js +594 -0
  105. package/lib/inventory/deps.js +141 -13
  106. package/lib/inventory/dosai.js +375 -6
  107. package/lib/inventory/dosaiParsers.js +3 -6
  108. package/lib/inventory/dotnetRuntime.js +367 -0
  109. package/lib/inventory/ecosystemFormulationParsers.js +979 -0
  110. package/lib/inventory/envcontext.js +436 -48
  111. package/lib/inventory/evidenceUtils.js +13 -1
  112. package/lib/inventory/fetchBatch.js +1 -1
  113. package/lib/inventory/formulationParsers.js +124 -14
  114. package/lib/inventory/gtfobins.js +13 -0
  115. package/lib/inventory/hbom.js +45 -6
  116. package/lib/inventory/hbomLoader.js +166 -0
  117. package/lib/inventory/hostRuntimeProbe.js +686 -0
  118. package/lib/inventory/jvmLocalRepos.js +9 -2
  119. package/lib/inventory/jvmToolEnv.js +29 -0
  120. package/lib/inventory/licenseEnhancer.js +12 -0
  121. package/lib/inventory/mavenCentralMirror.js +534 -0
  122. package/lib/inventory/mcpConfigParser.js +5 -0
  123. package/lib/inventory/nodeBuildInfo.js +218 -0
  124. package/lib/inventory/nodeHostProbe.js +202 -0
  125. package/lib/inventory/occurrenceKey.js +90 -0
  126. package/lib/inventory/osPackageResolver.js +182 -0
  127. package/lib/inventory/phpBuildInfo.js +10 -59
  128. package/lib/inventory/phpHostProbe.js +32 -488
  129. package/lib/inventory/pqcMigration.js +426 -0
  130. package/lib/inventory/rubyBuildInfo.js +972 -0
  131. package/lib/inventory/rubyHostProbe.js +218 -0
  132. package/lib/inventory/rustFormulationParser.js +21 -10
  133. package/lib/inventory/scalaCoords.js +4 -3
  134. package/lib/inventory/scalaDedupe.js +440 -0
  135. package/lib/inventory/source.js +114 -8
  136. package/lib/managers/binary.js +337 -3
  137. package/lib/managers/docker.js +79 -0
  138. package/lib/parsers/huggingfaceManifest.js +9 -1
  139. package/lib/parsers/huggingfaceUtils.js +18 -0
  140. package/lib/parsers/kiroPermissions.js +166 -0
  141. package/lib/parsers/largeJson.js +28 -11
  142. package/lib/parsers/vscodeExtensionId.js +183 -0
  143. package/lib/server/server.js +48 -35
  144. package/lib/stages/postgen/postgen.js +41 -1
  145. package/lib/stages/postgen/ruleEngine.js +13 -5
  146. package/lib/stages/postgen/specVersionCompat.js +76 -0
  147. package/lib/stages/pregen/envAudit.js +1 -0
  148. package/lib/stages/pregen/pregen.js +114 -57
  149. package/lib/validator/bomValidator.js +14 -2
  150. package/lib/validator/complianceEngine.js +30 -3
  151. package/lib/validator/complianceRules.js +1416 -4
  152. package/lib/validator/generated/validate-1.6.mjs +1 -1
  153. package/lib/validator/generated/validate-1.7.mjs +1 -1
  154. package/lib/validator/generated/validate-2.0.mjs +57554 -13851
  155. package/lib/validator/index.js +49 -8
  156. package/lib/validator/reporters/json.js +6 -0
  157. package/package.json +11 -10
  158. package/types/bin/repl.d.ts.map +1 -1
  159. package/types/lib/audit/index.d.ts +6 -2
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +29 -1
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +6 -0
  164. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  165. package/types/lib/cli/index.d.ts.map +1 -1
  166. package/types/lib/cli/jsBom.d.ts +3 -18
  167. package/types/lib/cli/jsBom.d.ts.map +1 -1
  168. package/types/lib/cli/jvmBom.d.ts +15 -0
  169. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  172. package/types/lib/core/boundedFile.d.ts +100 -0
  173. package/types/lib/core/boundedFile.d.ts.map +1 -0
  174. package/types/lib/core/brotliProbe.d.ts +37 -0
  175. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  176. package/types/lib/core/env.d.ts +14 -0
  177. package/types/lib/core/env.d.ts.map +1 -1
  178. package/types/lib/core/fs.d.ts +4 -0
  179. package/types/lib/core/fs.d.ts.map +1 -1
  180. package/types/lib/core/globPatterns.d.ts +70 -0
  181. package/types/lib/core/globPatterns.d.ts.map +1 -0
  182. package/types/lib/core/httpClient.d.ts +24 -0
  183. package/types/lib/core/httpClient.d.ts.map +1 -1
  184. package/types/lib/core/lockPaths.d.ts +19 -0
  185. package/types/lib/core/lockPaths.d.ts.map +1 -0
  186. package/types/lib/core/paths.d.ts +9 -0
  187. package/types/lib/core/paths.d.ts.map +1 -1
  188. package/types/lib/core/processTree.d.ts +17 -4
  189. package/types/lib/core/processTree.d.ts.map +1 -1
  190. package/types/lib/core/projectConfig.d.ts +47 -8
  191. package/types/lib/core/projectConfig.d.ts.map +1 -1
  192. package/types/lib/core/toolRegistry.d.ts +36 -0
  193. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  194. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  195. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  197. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  199. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  200. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  201. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  202. package/types/lib/ecosystems/gradleutils.d.ts +294 -20
  203. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
  205. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  206. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  207. package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
  208. package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
  209. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  210. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  211. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  212. package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
  213. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  214. package/types/lib/ecosystems/parsers-python.d.ts +10 -0
  215. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  217. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  218. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  219. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  220. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  221. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  222. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  223. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  224. package/types/lib/ecosystems/scalasem.d.ts +58 -16
  225. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  226. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  227. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  228. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  229. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  230. package/types/lib/evinser/evinser.d.ts +120 -13
  231. package/types/lib/evinser/evinser.d.ts.map +1 -1
  232. package/types/lib/evinser/scalasem.d.ts.map +1 -1
  233. package/types/lib/helpers/agenticToolutils.d.ts +53 -19
  234. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
  235. package/types/lib/helpers/bomSigner.d.ts +20 -3
  236. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  237. package/types/lib/helpers/jssSigner.d.ts +288 -0
  238. package/types/lib/helpers/jssSigner.d.ts.map +1 -0
  239. package/types/lib/helpers/vsixutils.d.ts +303 -19
  240. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  241. package/types/lib/inventory/agentBlueprint.d.ts +70 -0
  242. package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
  243. package/types/lib/inventory/agentToolNames.d.ts +51 -0
  244. package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
  245. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  246. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  247. package/types/lib/inventory/aiProviders.d.ts +85 -0
  248. package/types/lib/inventory/aiProviders.d.ts.map +1 -0
  249. package/types/lib/inventory/atomUtils.d.ts +19 -0
  250. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  251. package/types/lib/inventory/bomUtils.d.ts.map +1 -1
  252. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  253. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  254. package/types/lib/inventory/cbomutils.d.ts +17 -0
  255. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  256. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
  257. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
  258. package/types/lib/inventory/citations.d.ts +5 -0
  259. package/types/lib/inventory/citations.d.ts.map +1 -1
  260. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/cyclonedx20.d.ts +99 -0
  262. package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
  263. package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
  264. package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
  265. package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
  266. package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
  267. package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
  268. package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
  269. package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
  270. package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
  271. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
  272. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
  273. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
  274. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
  275. package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
  276. package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
  277. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
  278. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
  279. package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
  280. package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
  281. package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
  282. package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
  283. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
  284. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
  285. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
  286. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
  287. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
  288. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
  289. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
  290. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
  291. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
  292. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
  293. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
  294. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
  295. package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
  296. package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
  297. package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
  298. package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
  299. package/types/lib/inventory/deps.d.ts +35 -2
  300. package/types/lib/inventory/deps.d.ts.map +1 -1
  301. package/types/lib/inventory/dosai.d.ts +36 -1
  302. package/types/lib/inventory/dosai.d.ts.map +1 -1
  303. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  304. package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
  305. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  306. package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
  307. package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
  308. package/types/lib/inventory/envcontext.d.ts +74 -12
  309. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  310. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  311. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  312. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  313. package/types/lib/inventory/hbom.d.ts +11 -0
  314. package/types/lib/inventory/hbom.d.ts.map +1 -1
  315. package/types/lib/inventory/hbomLoader.d.ts +54 -0
  316. package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
  317. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  318. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  319. package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
  320. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
  321. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  322. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  323. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  324. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  325. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  326. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  327. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  328. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  329. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  330. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  331. package/types/lib/inventory/occurrenceKey.d.ts +52 -0
  332. package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
  333. package/types/lib/inventory/osPackageResolver.d.ts +38 -0
  334. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  335. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
  336. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
  337. package/types/lib/inventory/pqcMigration.d.ts +84 -0
  338. package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
  339. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  340. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  341. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  342. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  343. package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
  344. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  345. package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
  346. package/types/lib/inventory/scalaDedupe.d.ts +43 -0
  347. package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
  348. package/types/lib/inventory/source.d.ts +25 -0
  349. package/types/lib/inventory/source.d.ts.map +1 -1
  350. package/types/lib/managers/binary.d.ts.map +1 -1
  351. package/types/lib/managers/docker.d.ts +12 -0
  352. package/types/lib/managers/docker.d.ts.map +1 -1
  353. package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
  354. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
  355. package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
  356. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  357. package/types/lib/parsers/kiroPermissions.d.ts +62 -0
  358. package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
  359. package/types/lib/parsers/largeJson.d.ts +4 -3
  360. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  361. package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
  362. package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
  363. package/types/lib/server/server.d.ts.map +1 -1
  364. package/types/lib/stages/postgen/postgen.d.ts +7 -0
  365. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  366. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  367. package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
  368. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  369. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  370. package/types/lib/stages/pregen/pregen.d.ts +12 -8
  371. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  372. package/types/lib/validator/bomValidator.d.ts +5 -1
  373. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  374. package/types/lib/validator/complianceEngine.d.ts +4 -4
  375. package/types/lib/validator/complianceEngine.d.ts.map +1 -1
  376. package/types/lib/validator/complianceRules.d.ts +14 -2
  377. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  378. package/types/lib/validator/index.d.ts +10 -0
  379. package/types/lib/validator/index.d.ts.map +1 -1
  380. package/types/lib/validator/reporters/json.d.ts.map +1 -1
@@ -1,14 +1,22 @@
1
- import { readdirSync, readFileSync } from "node:fs";
1
+ import { readdirSync, realpathSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { join } from "node:path";
3
+ import { basename, dirname, isAbsolute, join } from "node:path";
4
4
 
5
5
  import { build } from "@cdxgen/cdx-purl";
6
6
  import { parse as loadYaml } from "yaml";
7
7
 
8
8
  import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
9
+ import { readUserFileText } from "../core/boundedFile.js";
9
10
  import { safeExistsSync } from "../core/fs.js";
10
- import { isMac, isWin } from "../core/paths.js";
11
+ import { isMac, isWin, redactHomePath } from "../core/paths.js";
11
12
  import { parseToml } from "../core/toml.js";
13
+ import {
14
+ kiroPermissionSummaryEntries,
15
+ summarizeKiroPermissions,
16
+ } from "../parsers/kiroPermissions.js";
17
+ import { findIdeProductInfo, IDE_PRODUCTS } from "./vsixutils.js";
18
+
19
+ export { redactHomePath };
12
20
 
13
21
  /**
14
22
  * The purl type used for installed agentic CLI tools. These tools ship as
@@ -45,29 +53,6 @@ function buildGenericPurl(name, version) {
45
53
  }
46
54
  }
47
55
 
48
- /**
49
- * Rewrite an absolute path under the user's home directory to a `~`-relative
50
- * form so the emitted BOM does not carry the local username.
51
- *
52
- * @param {string} filePath Absolute path
53
- * @returns {string} Home-relative path when applicable, else the input
54
- */
55
- export function redactHomePath(filePath) {
56
- const home = homedir();
57
- const value = String(filePath || "");
58
- if (!home || !value.startsWith(home)) {
59
- return value;
60
- }
61
- // Require a path boundary so a sibling home such as /Users/alice-backup is
62
- // not rewritten as if it sat under /Users/alice.
63
- const remainder = value.slice(home.length);
64
- if (remainder && !/^[/\\]/u.test(remainder)) {
65
- return value;
66
- }
67
- const rest = remainder.replace(/^[/\\]/u, "");
68
- return rest ? `~/${rest.replaceAll("\\", "/")}` : "~";
69
- }
70
-
71
56
  // Directory or file names inside a tool home that must never be read for their
72
57
  // values. They hold credentials, transcripts, or telemetry. The scanner may
73
58
  // count entries under some of these, but it does not open their contents.
@@ -95,23 +80,40 @@ const SENSITIVE_ENTRY_NAMES = new Set([
95
80
  * extension locations). Only the current platform's directories plus
96
81
  * always-present dot-directories are included.
97
82
  *
98
- * @returns {Array<{tool: string, name: string, dirs: string[], supportDirs?: string[]}>}
83
+ * An entry may also name `mcpConfigs`, the files and keys where the tool
84
+ * lists its MCP servers, and `ide`, the IDE_PRODUCTS id of the editor the tool
85
+ * ships in, whose installed version and extensions folder it then reports.
86
+ *
87
+ * @param {string} [home] Home directory to resolve the folders against
88
+ * @returns {Array<{tool: string, name: string, dirs: string[], supportDirs?: string[], mcpConfigs?: Array<{file: string, key: string}>, ide?: string}>}
99
89
  * One entry per known agentic CLI tool.
100
90
  */
101
- export function getAgenticToolDirs() {
102
- const home = homedir();
91
+ export function getAgenticToolDirs(home = homedir()) {
92
+ // These variables describe the current user, so another home, such as a
93
+ // fixture or another user's home, uses the defaults.
94
+ const ownHome = home === homedir();
103
95
  const appData =
104
- readEnvironmentVariable("APPDATA") || join(home, "AppData", "Roaming");
96
+ (ownHome && readEnvironmentVariable("APPDATA")) ||
97
+ join(home, "AppData", "Roaming");
105
98
  const xdgConfigHome =
106
- readEnvironmentVariable("XDG_CONFIG_HOME") || join(home, ".config");
99
+ (ownHome && readEnvironmentVariable("XDG_CONFIG_HOME")) ||
100
+ join(home, ".config");
107
101
  const xdgDataHome =
108
- readEnvironmentVariable("XDG_DATA_HOME") || join(home, ".local", "share");
102
+ (ownHome && readEnvironmentVariable("XDG_DATA_HOME")) ||
103
+ join(home, ".local", "share");
109
104
 
110
105
  return [
111
106
  {
112
107
  tool: "kiro-cli",
113
108
  name: "Kiro CLI",
114
- dirs: [join(home, ".kiro")],
109
+ // The Kiro editor also creates ~/.kiro, so the CLI is recognised by the
110
+ // files only the CLI writes there.
111
+ dirs: [
112
+ join(home, ".kiro", "settings", "cli.json"),
113
+ join(home, ".kiro", "sessions", "cli"),
114
+ ],
115
+ homeDir: join(home, ".kiro"),
116
+ mcpConfigs: [{ file: join("settings", "mcp.json"), key: "mcpServers" }],
115
117
  supportDirs: isWin
116
118
  ? [join(appData, "kiro-cli")]
117
119
  : isMac
@@ -136,6 +138,8 @@ export function getAgenticToolDirs() {
136
138
  tool: "claude-code",
137
139
  name: "Claude Code",
138
140
  dirs: [join(home, ".claude")],
141
+ // User-scope MCP servers live in ~/.claude.json, beside the folder.
142
+ mcpConfigs: [{ file: join(home, ".claude.json"), key: "mcpServers" }],
139
143
  },
140
144
  {
141
145
  tool: "codex",
@@ -161,11 +165,111 @@ export function getAgenticToolDirs() {
161
165
  tool: "cursor",
162
166
  name: "Cursor",
163
167
  dirs: [join(home, ".cursor")],
168
+ mcpConfigs: [{ file: "mcp.json", key: "mcpServers" }],
169
+ ide: "cursor",
164
170
  },
165
171
  {
166
172
  tool: "windsurf",
167
173
  name: "Windsurf",
168
174
  dirs: [join(home, ".codeium", "windsurf")],
175
+ mcpConfigs: [{ file: "mcp_config.json", key: "mcpServers" }],
176
+ ide: "windsurf",
177
+ },
178
+ {
179
+ tool: "devin-desktop",
180
+ name: "Devin Desktop",
181
+ dirs: [join(home, ".devin")],
182
+ mcpConfigs: [
183
+ {
184
+ file: join(xdgConfigHome, "devin", "config.json"),
185
+ key: "mcpServers",
186
+ },
187
+ ],
188
+ ide: "devin-desktop",
189
+ },
190
+ {
191
+ tool: "kiro",
192
+ name: "Kiro",
193
+ // Kiro and Kiro CLI share ~/.kiro; the IDE is told apart by its
194
+ // extensions folder.
195
+ dirs: [join(home, ".kiro", "extensions")],
196
+ homeDir: join(home, ".kiro"),
197
+ mcpConfigs: [{ file: join("settings", "mcp.json"), key: "mcpServers" }],
198
+ ide: "kiro",
199
+ },
200
+ {
201
+ tool: "trae",
202
+ name: "Trae",
203
+ dirs: [join(home, ".trae")],
204
+ ide: "trae",
205
+ },
206
+ {
207
+ tool: "antigravity",
208
+ name: "Antigravity",
209
+ dirs: [join(home, ".antigravity")],
210
+ ide: "antigravity",
211
+ },
212
+ {
213
+ tool: "antigravity-ide",
214
+ name: "Antigravity IDE",
215
+ dirs: [join(home, ".antigravity-ide")],
216
+ ide: "antigravity-ide",
217
+ },
218
+ {
219
+ tool: "qoder",
220
+ name: "Qoder",
221
+ dirs: [join(home, ".qoder")],
222
+ ide: "qoder",
223
+ },
224
+ {
225
+ tool: "void",
226
+ name: "Void",
227
+ dirs: [join(home, ".void-editor")],
228
+ ide: "void",
229
+ },
230
+ {
231
+ tool: "qwen-code",
232
+ name: "Qwen Code",
233
+ dirs: [join(home, ".qwen")],
234
+ mcpConfigs: [{ file: "settings.json", key: "mcpServers" }],
235
+ },
236
+ {
237
+ tool: "factory-droid",
238
+ name: "Factory Droid",
239
+ dirs: [join(home, ".factory")],
240
+ mcpConfigs: [{ file: "mcp.json", key: "mcpServers" }],
241
+ },
242
+ {
243
+ tool: "auggie",
244
+ name: "Augment Auggie CLI",
245
+ dirs: [join(home, ".augment")],
246
+ mcpConfigs: [{ file: "settings.json", key: "mcpServers" }],
247
+ },
248
+ {
249
+ tool: "continue",
250
+ name: "Continue",
251
+ dirs: [join(home, ".continue")],
252
+ },
253
+ {
254
+ tool: "cline-cli",
255
+ name: "Cline CLI",
256
+ dirs: [join(home, ".cline")],
257
+ mcpConfigs: [
258
+ {
259
+ file: join("data", "settings", "cline_mcp_settings.json"),
260
+ key: "mcpServers",
261
+ },
262
+ ],
263
+ },
264
+ {
265
+ tool: "claude-desktop",
266
+ name: "Claude Desktop",
267
+ dirs: isWin
268
+ ? [join(appData, "Claude")]
269
+ : isMac
270
+ ? [join(home, "Library", "Application Support", "Claude")]
271
+ : [join(xdgConfigHome, "Claude")],
272
+ mcpConfigs: [{ file: "claude_desktop_config.json", key: "mcpServers" }],
169
273
  },
170
274
  {
171
275
  tool: "goose",
@@ -184,11 +288,16 @@ export function getAgenticToolDirs() {
184
288
  join(home, ".config", "amp"),
185
289
  ...(isWin ? [join(appData, "amp")] : []),
186
290
  ],
291
+ mcpConfigs: [
292
+ { file: "settings.json", key: "amp.mcpServers" },
293
+ { file: "settings.jsonc", key: "amp.mcpServers" },
294
+ ],
187
295
  },
188
296
  {
189
297
  tool: "copilot-cli",
190
298
  name: "GitHub Copilot CLI",
191
299
  dirs: [join(home, ".copilot-cli"), join(home, ".copilot")],
300
+ mcpConfigs: [{ file: "mcp-config.json", key: "mcpServers" }],
192
301
  },
193
302
  {
194
303
  tool: "crush",
@@ -206,27 +315,74 @@ export function getAgenticToolDirs() {
206
315
  * Discover which agentic CLI tools are installed for the current user by
207
316
  * checking their known home directories.
208
317
  *
209
- * @returns {Array<{tool: string, name: string, dir: string, supportDir?: string}>}
318
+ * @param {string} [home] Home directory to resolve the folders against
319
+ * @returns {Array<{tool: string, name: string, dir: string, supportDir?: string, mcpConfigs?: Object[], ide?: string}>}
210
320
  * One entry per discovered tool, using the first existing directory.
211
321
  */
212
- export function discoverAgenticTools() {
322
+ export function discoverAgenticTools(home = homedir()) {
213
323
  const found = [];
214
- for (const entry of getAgenticToolDirs()) {
215
- const dir = entry.dirs.find((candidate) => safeExistsSync(candidate));
216
- if (!dir) {
324
+ for (const entry of getAgenticToolDirs(home)) {
325
+ const marker = entry.dirs.find((candidate) => safeExistsSync(candidate));
326
+ if (!marker) {
217
327
  continue;
218
328
  }
219
329
  const supportDir = (entry.supportDirs || []).find((candidate) =>
220
330
  safeExistsSync(candidate),
221
331
  );
222
- found.push({ tool: entry.tool, name: entry.name, dir, supportDir });
332
+ found.push({
333
+ tool: entry.tool,
334
+ name: entry.name,
335
+ dir: entry.homeDir || marker,
336
+ supportDir,
337
+ mcpConfigs: entry.mcpConfigs,
338
+ ide: entry.ide,
339
+ });
223
340
  }
224
341
  return found;
225
342
  }
226
343
 
344
+ // Largest tool configuration file read. ~/.claude.json keeps per-project state
345
+ // and grows with use, so the cap is generous; it only has to stop a FIFO, a
346
+ // device or a runaway file from stalling the scan or exhausting memory.
347
+ const MAX_CONFIG_FILE_BYTES = 64 * 1024 * 1024;
348
+
349
+ /**
350
+ * Read a tool configuration file only when it is a regular file of bounded
351
+ * size. A FIFO or a device reads as undefined, and so does a symlink when the
352
+ * scan runs as root.
353
+ *
354
+ * @param {string} filePath Absolute path
355
+ * @returns {string|undefined} File text, or undefined
356
+ */
357
+ function readConfigText(filePath) {
358
+ return readUserFileText(filePath, MAX_CONFIG_FILE_BYTES);
359
+ }
360
+
227
361
  function parseJsonFile(filePath) {
228
362
  try {
229
- return parseJsonLikeText(readFileSync(filePath, "utf-8"));
363
+ return parseJsonLikeText(readConfigText(filePath));
364
+ } catch {
365
+ return undefined;
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Read a small YAML settings file, such as a Kiro permissions document,
371
+ * through the bounded user-file reader. A file that exists but holds no
372
+ * document, such as an empty or comment-only permissions file, reads as an
373
+ * empty object so its presence still counts.
374
+ *
375
+ * @param {string} filePath Absolute file path
376
+ * @returns {Object|undefined} Parsed document, or undefined when the file is
377
+ * missing, unreadable, or malformed
378
+ */
379
+ function parseYamlFile(filePath) {
380
+ try {
381
+ const raw = readConfigText(filePath);
382
+ if (typeof raw !== "string") {
383
+ return undefined;
384
+ }
385
+ return loadYaml(raw) || {};
230
386
  } catch {
231
387
  return undefined;
232
388
  }
@@ -335,7 +491,52 @@ function countDirEntries(dirPath, filterFn) {
335
491
  * @param {string} [supportDir] Optional platform support directory
336
492
  * @returns {string|undefined} Version string when found
337
493
  */
494
+ /**
495
+ * Read a semver-like version from the start of a release folder name such as
496
+ * `2.1.278` or `0.137.0-aarch64-apple-darwin`.
497
+ *
498
+ * @param {string} name Folder name
499
+ * @returns {string|undefined} The version, or undefined
500
+ */
501
+ function versionFromReleaseName(name) {
502
+ const match = /^v?(\d+\.\d+\.\d+)/u.exec(`${name || ""}`);
503
+ return match ? match[1] : undefined;
504
+ }
505
+
506
+ /**
507
+ * Follow a launcher symlink that points into a per-version folder, as the
508
+ * native Claude Code and Codex installers do, and read the version from the
509
+ * folder name.
510
+ *
511
+ * @param {string} linkPath Launcher or `current` symlink
512
+ * @returns {string|undefined} The version, or undefined
513
+ */
514
+ function versionFromReleaseLink(linkPath) {
515
+ try {
516
+ return versionFromReleaseName(basename(realpathSync(linkPath)));
517
+ } catch {
518
+ return undefined;
519
+ }
520
+ }
521
+
338
522
  function readToolVersion(tool, dir, supportDir) {
523
+ if (tool === "claude-code") {
524
+ const home = dirname(dir);
525
+ const version =
526
+ versionFromReleaseLink(join(home, ".local", "bin", "claude")) ||
527
+ versionFromReleaseLink(join(home, ".claude", "local", "claude"));
528
+ if (version) {
529
+ return version;
530
+ }
531
+ }
532
+ if (tool === "codex") {
533
+ const version = versionFromReleaseLink(
534
+ join(dir, "packages", "standalone", "current"),
535
+ );
536
+ if (version) {
537
+ return version;
538
+ }
539
+ }
339
540
  const candidates = [];
340
541
  if (tool === "opencode") {
341
542
  // ~/.opencode/package.json pins the installed opencode plugin runtime,
@@ -370,7 +571,7 @@ function readToolVersion(tool, dir, supportDir) {
370
571
  }
371
572
  } else {
372
573
  try {
373
- const raw = readFileSync(candidate, "utf-8").trim();
574
+ const raw = (readConfigText(candidate) || "").trim();
374
575
  if (/^v?\d+\.\d+/.test(raw)) {
375
576
  return raw.replace(/^v/, "");
376
577
  }
@@ -382,6 +583,58 @@ function readToolVersion(tool, dir, supportDir) {
382
583
  return undefined;
383
584
  }
384
585
 
586
+ /**
587
+ * Collect the workspace-scope permission counts under
588
+ * `~/.kiro/workspace-roots/<hash>/permissions.yaml`. Only counts and booleans
589
+ * are returned; the hash-named directories, the file paths, and the rule
590
+ * match patterns stay out of the result.
591
+ *
592
+ * @param {string} dir Kiro home directory
593
+ * @returns {{workspacePermissionFileCount?: number, trustedWorkspaceCount?: number, workspaceWithAllowRuleCount?: number}}
594
+ * Aggregate workspace posture counts
595
+ */
596
+ function countKiroWorkspacePermissions(dir) {
597
+ const rootsDir = join(dir, "workspace-roots");
598
+ if (!safeExistsSync(rootsDir)) {
599
+ return {};
600
+ }
601
+ let entries = [];
602
+ try {
603
+ entries = readdirSync(rootsDir, { withFileTypes: true });
604
+ } catch {
605
+ return {};
606
+ }
607
+ let fileCount = 0;
608
+ let trustedCount = 0;
609
+ let withAllowRuleCount = 0;
610
+ for (const entry of entries) {
611
+ if (!entry.isDirectory()) {
612
+ continue;
613
+ }
614
+ const permissionsPath = join(rootsDir, entry.name, "permissions.yaml");
615
+ const doc = parseYamlFile(permissionsPath);
616
+ if (doc === undefined) {
617
+ continue;
618
+ }
619
+ fileCount += 1;
620
+ const summary = summarizeKiroPermissions(doc);
621
+ if (summary.workspaceTrusted === true) {
622
+ trustedCount += 1;
623
+ }
624
+ if (summary.allowRuleCount > 0) {
625
+ withAllowRuleCount += 1;
626
+ }
627
+ }
628
+ if (!fileCount) {
629
+ return {};
630
+ }
631
+ return {
632
+ trustedWorkspaceCount: trustedCount,
633
+ workspacePermissionFileCount: fileCount,
634
+ workspaceWithAllowRuleCount: withAllowRuleCount,
635
+ };
636
+ }
637
+
385
638
  function countKiroSignals(dir, supportDir) {
386
639
  const signals = {};
387
640
  const agentsDir = join(dir, "agents");
@@ -404,6 +657,17 @@ function countKiroSignals(dir, supportDir) {
404
657
  if (settings && typeof settings["chat.defaultAgent"] === "string") {
405
658
  signals.defaultAgent = settings["chat.defaultAgent"];
406
659
  }
660
+ // The V3 user-scope permission posture from ~/.kiro/settings/permissions.yaml.
661
+ const userPermissions = parseYamlFile(
662
+ join(dir, "settings", "permissions.yaml"),
663
+ );
664
+ if (userPermissions !== undefined) {
665
+ Object.assign(
666
+ signals,
667
+ kiroPermissionSummaryEntries(summarizeKiroPermissions(userPermissions)),
668
+ );
669
+ }
670
+ Object.assign(signals, countKiroWorkspacePermissions(dir));
407
671
  return signals;
408
672
  }
409
673
 
@@ -476,7 +740,7 @@ function countCodexSignals(dir) {
476
740
  return signals;
477
741
  }
478
742
  try {
479
- const config = parseToml(readFileSync(configPath, "utf-8"));
743
+ const config = parseToml(readConfigText(configPath) || "");
480
744
  if (config?.mcp_servers && typeof config.mcp_servers === "object") {
481
745
  signals.mcpServerCount = Object.keys(config.mcp_servers).length;
482
746
  }
@@ -488,24 +752,35 @@ function countCodexSignals(dir) {
488
752
 
489
753
  function countCursorSignals(dir) {
490
754
  const signals = {};
491
- const mcpConfig = parseJsonFile(join(dir, "mcp.json"));
492
- if (mcpConfig?.mcpServers && typeof mcpConfig.mcpServers === "object") {
493
- signals.mcpServerCount = Object.keys(mcpConfig.mcpServers).length;
494
- }
495
755
  const agentsDir = join(dir, "agents");
496
756
  if (safeExistsSync(agentsDir)) {
497
757
  signals.agentCount = countDirEntries(agentsDir, (name) =>
498
758
  name.endsWith(".md"),
499
759
  );
500
760
  }
501
- return signals;
502
- }
503
-
504
- function countWindsurfSignals(dir) {
505
- const signals = {};
506
- const mcpConfig = parseJsonFile(join(dir, "mcp_config.json"));
507
- if (mcpConfig?.mcpServers && typeof mcpConfig.mcpServers === "object") {
508
- signals.mcpServerCount = Object.keys(mcpConfig.mcpServers).length;
761
+ const rulesDir = join(dir, "rules");
762
+ if (safeExistsSync(rulesDir)) {
763
+ signals.ruleCount = countDirEntries(rulesDir, (name) =>
764
+ /\.(md|mdc)$/u.test(name),
765
+ );
766
+ }
767
+ for (const [skillsDir, key] of [
768
+ [join(dir, "skills"), "skillCount"],
769
+ [join(dir, "skills-cursor"), "bundledSkillCount"],
770
+ ]) {
771
+ if (safeExistsSync(skillsDir)) {
772
+ signals[key] = countDirEntries(skillsDir);
773
+ }
774
+ }
775
+ // The Cursor CLI keeps its tool permissions in cli-config.json. Only the
776
+ // number of rules is recorded, not the commands or paths they name.
777
+ const cliConfig = parseJsonFile(join(dir, "cli-config.json"));
778
+ const permissions = cliConfig?.permissions;
779
+ if (Array.isArray(permissions?.allow)) {
780
+ signals.cliAllowRuleCount = permissions.allow.length;
781
+ }
782
+ if (Array.isArray(permissions?.deny)) {
783
+ signals.cliDenyRuleCount = permissions.deny.length;
509
784
  }
510
785
  return signals;
511
786
  }
@@ -514,7 +789,7 @@ function countGooseSignals(dir) {
514
789
  const signals = {};
515
790
  // Goose keeps its resolved extension set in config.yaml.
516
791
  try {
517
- const config = loadYaml(readFileSync(join(dir, "config.yaml"), "utf-8"));
792
+ const config = loadYaml(readConfigText(join(dir, "config.yaml")) || "");
518
793
  if (Array.isArray(config?.extensions)) {
519
794
  signals.extensionCount = config.extensions.length;
520
795
  }
@@ -546,6 +821,81 @@ function countGeminiSignals(dir) {
546
821
  return signals;
547
822
  }
548
823
 
824
+ function countClaudeDesktopSignals(dir) {
825
+ const signals = {};
826
+ // Desktop extensions (.mcpb/.dxt) each bundle an MCP server.
827
+ const extensionsDir = join(dir, "Claude Extensions");
828
+ if (safeExistsSync(extensionsDir)) {
829
+ signals.extensionCount = countDirEntries(extensionsDir);
830
+ }
831
+ return signals;
832
+ }
833
+
834
+ /**
835
+ * Count the MCP servers a tool lists across its configuration files. A server
836
+ * named in two files is counted once.
837
+ *
838
+ * @param {string} dir Tool home directory, which relative files resolve against
839
+ * @param {Array<{file: string, key: string}>} [mcpConfigs] Files and the key
840
+ * holding the server map. A key is looked up as written first, so a dotted
841
+ * key such as `amp.mcpServers` matches a literal property.
842
+ * @returns {number|undefined} Number of servers, or undefined when no file
843
+ * defines any
844
+ */
845
+ export function countConfiguredMcpServers(dir, mcpConfigs) {
846
+ const names = new Set();
847
+ let sawConfig = false;
848
+ for (const { file, key } of mcpConfigs || []) {
849
+ const config = parseJsonFile(isAbsolute(file) ? file : join(dir, file));
850
+ if (!config || typeof config !== "object") {
851
+ continue;
852
+ }
853
+ let servers = config[key];
854
+ if (servers === undefined && key.includes(".")) {
855
+ servers = key.split(".").reduce((value, part) => value?.[part], config);
856
+ }
857
+ if (servers && typeof servers === "object" && !Array.isArray(servers)) {
858
+ sawConfig = true;
859
+ for (const name of Object.keys(servers)) {
860
+ names.add(name);
861
+ }
862
+ }
863
+ }
864
+ return sawConfig ? names.size : undefined;
865
+ }
866
+
867
+ /**
868
+ * Describe the editor an agentic tool ships in: its installed version and
869
+ * how many extensions are in its extensions folder.
870
+ *
871
+ * @param {string} ideId Entry id from IDE_PRODUCTS
872
+ * @param {string} home Home directory
873
+ * @returns {{version?: string, installDir?: string, extensionCount?: number}}
874
+ * What could be read
875
+ */
876
+ function describeAgenticIde(ideId, home) {
877
+ const product = IDE_PRODUCTS.find((p) => p.id === ideId);
878
+ if (!product) {
879
+ return {};
880
+ }
881
+ const info = findIdeProductInfo({ id: ideId, kind: "desktop" }, home);
882
+ const result = {};
883
+ if (info?.version) {
884
+ result.version = info.version;
885
+ }
886
+ if (info?.appDir) {
887
+ result.installDir = info.appDir;
888
+ }
889
+ const extensionsDir = join(home, product.dataFolderName, "extensions");
890
+ if (safeExistsSync(extensionsDir)) {
891
+ result.extensionCount = countDirEntries(
892
+ extensionsDir,
893
+ (name) => !name.startsWith(".") && name !== "extensions.json",
894
+ );
895
+ }
896
+ return result;
897
+ }
898
+
549
899
  /**
550
900
  * Compute safe, redacted signal counts for a discovered tool. Only structural
551
901
  * counts and small enumerated values are returned; no credential, transcript,
@@ -554,9 +904,34 @@ function countGeminiSignals(dir) {
554
904
  * @param {string} tool Tool key
555
905
  * @param {string} dir Discovered tool home directory
556
906
  * @param {string} [supportDir] Optional platform support directory
907
+ * @param {Array<{file: string, key: string}>} [mcpConfigs] Files that list
908
+ * the tool's MCP servers, used when the tool has no reader of its own
909
+ * @param {string} [home] Home directory the tool was found in
557
910
  * @returns {Object} Map of signal name to count/boolean/small-string value
558
911
  */
559
- export function collectToolSignals(tool, dir, supportDir) {
912
+ export function collectToolSignals(
913
+ tool,
914
+ dir,
915
+ supportDir,
916
+ mcpConfigs,
917
+ home = homedir(),
918
+ ) {
919
+ const signals = toolSpecificSignals(tool, dir, supportDir);
920
+ if (signals.mcpServerCount === undefined) {
921
+ const mcpServerCount = countConfiguredMcpServers(
922
+ dir,
923
+ mcpConfigs ||
924
+ getAgenticToolDirs(home).find((entry) => entry.tool === tool)
925
+ ?.mcpConfigs || [{ file: ".mcp.json", key: "mcpServers" }],
926
+ );
927
+ if (mcpServerCount !== undefined) {
928
+ signals.mcpServerCount = mcpServerCount;
929
+ }
930
+ }
931
+ return signals;
932
+ }
933
+
934
+ function toolSpecificSignals(tool, dir, supportDir) {
560
935
  switch (tool) {
561
936
  case "kiro-cli":
562
937
  return countKiroSignals(dir, supportDir);
@@ -572,48 +947,60 @@ export function collectToolSignals(tool, dir, supportDir) {
572
947
  return countGeminiSignals(dir);
573
948
  case "cursor":
574
949
  return countCursorSignals(dir);
575
- case "windsurf":
576
- return countWindsurfSignals(dir);
950
+ case "claude-desktop":
951
+ return countClaudeDesktopSignals(dir);
577
952
  case "goose":
578
953
  return countGooseSignals(dir);
579
954
  case "crush":
580
955
  return countCrushSignals(dir);
581
- default: {
582
- // Generic fallback: count top-level, non-sensitive config entries.
583
- const signals = {};
584
- const mcpConfig = parseJsonFile(join(dir, ".mcp.json"));
585
- if (mcpConfig?.mcpServers) {
586
- signals.mcpServerCount = Object.keys(mcpConfig.mcpServers).length;
587
- }
588
- return signals;
589
- }
956
+ default:
957
+ return {};
590
958
  }
591
959
  }
592
960
 
593
961
  /**
594
962
  * Build a CycloneDX application component for one discovered agentic tool.
595
963
  *
596
- * @param {{tool: string, name: string, dir: string, supportDir?: string}} entry
964
+ * @param {{tool: string, name: string, dir: string, supportDir?: string, mcpConfigs?: Object[], ide?: string}} entry
597
965
  * Discovered tool entry
966
+ * @param {string} [home] Home directory, used to find the tool's editor
598
967
  * @returns {Object} CycloneDX component
599
968
  */
600
- export function componentForAgenticTool(entry) {
601
- const { tool, name, dir, supportDir } = entry;
602
- const version = readToolVersion(tool, dir, supportDir);
969
+ export function componentForAgenticTool(entry, home = homedir()) {
970
+ const { tool, name, dir, supportDir, mcpConfigs, ide } = entry;
971
+ const ideInfo = ide ? describeAgenticIde(ide, home) : {};
972
+ const version = readToolVersion(tool, dir, supportDir) || ideInfo.version;
603
973
  const purl = buildGenericPurl(tool, version);
604
974
  const properties = [
605
- { name: "internal:SrcFile", value: redactHomePath(dir) },
975
+ { name: "internal:SrcFile", value: redactHomePath(dir, home) },
606
976
  { name: "cdx:agentic:tool", value: tool },
607
- { name: "cdx:agentic:home", value: redactHomePath(dir) },
977
+ { name: "cdx:agentic:home", value: redactHomePath(dir, home) },
608
978
  { name: "cdx:agentic:inventorySource", value: "host-scan" },
609
979
  ];
610
980
  if (supportDir) {
611
981
  properties.push({
612
982
  name: "cdx:agentic:supportDir",
613
- value: redactHomePath(supportDir),
983
+ value: redactHomePath(supportDir, home),
984
+ });
985
+ }
986
+ // ~/.cursor or ~/.codeium/windsurf can exist with only a CLI or a plugin
987
+ // installed, so the editor is named only when it was found.
988
+ const ideFound =
989
+ Boolean(ide) &&
990
+ (ideInfo.installDir !== undefined || ideInfo.extensionCount !== undefined);
991
+ if (ideFound) {
992
+ properties.push({ name: "cdx:agentic:ide", value: ide });
993
+ }
994
+ if (ideInfo.installDir) {
995
+ properties.push({
996
+ name: "cdx:agentic:ideInstallDir",
997
+ value: redactHomePath(ideInfo.installDir, home),
614
998
  });
615
999
  }
616
- const signals = collectToolSignals(tool, dir, supportDir);
1000
+ const signals = collectToolSignals(tool, dir, supportDir, mcpConfigs, home);
1001
+ if (ideInfo.extensionCount !== undefined) {
1002
+ signals.extensionCount = ideInfo.extensionCount;
1003
+ }
617
1004
  for (const [key, value] of Object.entries(signals)) {
618
1005
  if (value === undefined || value === null || value === "") {
619
1006
  continue;
@@ -639,7 +1026,12 @@ export function componentForAgenticTool(entry) {
639
1026
  if (purl) {
640
1027
  component.purl = purl;
641
1028
  }
642
- component.description = `${name} agentic CLI tool discovered on the host`;
1029
+ component.description = ideFound
1030
+ ? `${name} AI editor discovered on the host`
1031
+ : `${name} agentic CLI tool discovered on the host`;
1032
+ if (ideFound) {
1033
+ component.tags.push("ai-ide");
1034
+ }
643
1035
  return component;
644
1036
  }
645
1037
 
@@ -652,13 +1044,17 @@ function childBomRef(parentRef, kind, id) {
652
1044
  * Parse Kiro agent definitions (`~/.kiro/agents/*.json`) into components.
653
1045
  *
654
1046
  * Each agent contributes its name, model, and redacted counts of MCP servers,
655
- * tools, and allowed tools. Prompts and resource values are never copied.
1047
+ * tools, and allowed tools, plus the permission posture of its V3
1048
+ * `permissions` object and the integer `kiroUpgrade` revision. Prompts,
1049
+ * resource values, match patterns, and the upgrade fingerprint or source are
1050
+ * never copied.
656
1051
  *
657
1052
  * @param {string} dir Kiro home directory
658
1053
  * @param {string} parentRef Parent tool bom-ref
1054
+ * @param {string} [home] Home directory the tool was found in, for redaction
659
1055
  * @returns {Object[]} Child components
660
1056
  */
661
- function parseKiroAgents(dir, parentRef) {
1057
+ function parseKiroAgents(dir, parentRef, home = homedir()) {
662
1058
  const agentsDir = join(dir, "agents");
663
1059
  if (!safeExistsSync(agentsDir)) {
664
1060
  return [];
@@ -681,7 +1077,7 @@ function parseKiroAgents(dir, parentRef) {
681
1077
  const properties = [
682
1078
  {
683
1079
  name: "internal:SrcFile",
684
- value: redactHomePath(join(agentsDir, file)),
1080
+ value: redactHomePath(join(agentsDir, file), home),
685
1081
  },
686
1082
  { name: "cdx:agentic:tool", value: "kiro-cli" },
687
1083
  { name: "cdx:agentic:kind", value: "agent-definition" },
@@ -707,6 +1103,24 @@ function parseKiroAgents(dir, parentRef) {
707
1103
  value: String(agent.allowedTools.length),
708
1104
  });
709
1105
  }
1106
+ if (agent.permissions && typeof agent.permissions === "object") {
1107
+ for (const [suffix, value] of Object.entries(
1108
+ kiroPermissionSummaryEntries(
1109
+ summarizeKiroPermissions(agent.permissions),
1110
+ ),
1111
+ )) {
1112
+ properties.push({
1113
+ name: `cdx:agentic:${suffix}`,
1114
+ value: String(value),
1115
+ });
1116
+ }
1117
+ }
1118
+ if (Number.isInteger(agent.kiroUpgrade?.revision)) {
1119
+ properties.push({
1120
+ name: "cdx:agentic:upgradeRevision",
1121
+ value: String(agent.kiroUpgrade.revision),
1122
+ });
1123
+ }
710
1124
  components.push({
711
1125
  "bom-ref": childBomRef(parentRef, "agent", agent.name),
712
1126
  type: "application",
@@ -725,9 +1139,10 @@ function parseKiroAgents(dir, parentRef) {
725
1139
  *
726
1140
  * @param {string} dir zcode home directory
727
1141
  * @param {string} parentRef Parent tool bom-ref
1142
+ * @param {string} [home] Home directory the tool was found in, for redaction
728
1143
  * @returns {Object[]} Child components
729
1144
  */
730
- function parseZcodePlugins(dir, parentRef) {
1145
+ function parseZcodePlugins(dir, parentRef, home = homedir()) {
731
1146
  const cacheDir = join(dir, "cli", "plugins", "cache");
732
1147
  if (!safeExistsSync(cacheDir)) {
733
1148
  return [];
@@ -763,7 +1178,7 @@ function parseZcodePlugins(dir, parentRef) {
763
1178
  typeof plugin.version === "string" ? plugin.version : undefined,
764
1179
  );
765
1180
  const properties = [
766
- { name: "internal:SrcFile", value: redactHomePath(full) },
1181
+ { name: "internal:SrcFile", value: redactHomePath(full, home) },
767
1182
  { name: "cdx:agentic:tool", value: "zcode" },
768
1183
  { name: "cdx:agentic:kind", value: "plugin" },
769
1184
  ];
@@ -813,6 +1228,7 @@ function parseZcodePlugins(dir, parentRef) {
813
1228
  *
814
1229
  * @param {string} dir opencode home directory
815
1230
  * @param {string} parentRef Parent tool bom-ref
1231
+ * @param {string} [home] Home directory the tool was found in, for redaction
816
1232
  * @returns {Object[]} Child components
817
1233
  */
818
1234
  /**
@@ -823,9 +1239,10 @@ function parseZcodePlugins(dir, parentRef) {
823
1239
  *
824
1240
  * @param {string} dir opencode home directory
825
1241
  * @param {string} parentRef Parent tool bom-ref
1242
+ * @param {string} [home] Home directory the tool was found in, for redaction
826
1243
  * @returns {Object[]} Child provider services
827
1244
  */
828
- function parseOpencodeProviders(dir, parentRef) {
1245
+ function parseOpencodeProviders(dir, parentRef, home = homedir()) {
829
1246
  const jsonPath = join(dir, "opencode.json");
830
1247
  const jsoncPath = join(dir, "opencode.jsonc");
831
1248
  const usedPath = safeExistsSync(jsonPath) ? jsonPath : jsoncPath;
@@ -840,7 +1257,7 @@ function parseOpencodeProviders(dir, parentRef) {
840
1257
  group: providerName,
841
1258
  name: providerName,
842
1259
  properties: [
843
- { name: "internal:SrcFile", value: redactHomePath(usedPath) },
1260
+ { name: "internal:SrcFile", value: redactHomePath(usedPath, home) },
844
1261
  { name: "cdx:agentic:tool", value: "opencode" },
845
1262
  { name: "cdx:agentic:kind", value: "provider" },
846
1263
  { name: "cdx:ai:kind", value: "inference-service" },
@@ -857,18 +1274,25 @@ function parseOpencodeProviders(dir, parentRef) {
857
1274
  * @param {string} tool Tool key
858
1275
  * @param {string} dir Tool home directory
859
1276
  * @param {string} parentRef Parent tool bom-ref
1277
+ * @param {string} [home] Home directory the tool was found in, for redaction
860
1278
  * @returns {{components: Object[], services: Object[]}} Child inventory
861
1279
  */
862
- function collectToolChildren(tool, dir, parentRef) {
1280
+ function collectToolChildren(tool, dir, parentRef, home = homedir()) {
863
1281
  switch (tool) {
864
1282
  case "kiro-cli":
865
- return { components: parseKiroAgents(dir, parentRef), services: [] };
1283
+ return {
1284
+ components: parseKiroAgents(dir, parentRef, home),
1285
+ services: [],
1286
+ };
866
1287
  case "zcode":
867
- return { components: parseZcodePlugins(dir, parentRef), services: [] };
1288
+ return {
1289
+ components: parseZcodePlugins(dir, parentRef, home),
1290
+ services: [],
1291
+ };
868
1292
  case "opencode":
869
1293
  return {
870
1294
  components: [],
871
- services: parseOpencodeProviders(dir, parentRef),
1295
+ services: parseOpencodeProviders(dir, parentRef, home),
872
1296
  };
873
1297
  default:
874
1298
  return { components: [], services: [] };
@@ -882,22 +1306,29 @@ function collectToolChildren(tool, dir, parentRef) {
882
1306
  *
883
1307
  * This host scan is unconditional: it does not consult scan filters such as
884
1308
  * `--exclude` or profile/technique options, because it inventories fixed tool
885
- * home directories rather than a project tree. The `options` argument is
886
- * accepted for signature parity and is intentionally not used to alter results.
1309
+ * home directories rather than a project tree.
887
1310
  *
888
- * @param {Object} [options={}] Collection options (accepted but not applied)
1311
+ * @param {Object} [options={}] Collection options
1312
+ * @param {string} [options.home] Home directory to scan, defaulting to the
1313
+ * current user's
889
1314
  * @returns {{components: Object[], services: Object[], dependencies: Object[]}} Discovered inventory
890
1315
  */
891
- export function collectAgenticTools(_options = {}) {
1316
+ export function collectAgenticTools(options = {}) {
892
1317
  const components = [];
893
1318
  const services = [];
894
1319
  const dependencies = [];
895
- for (const entry of discoverAgenticTools()) {
1320
+ const home = options.home || homedir();
1321
+ for (const entry of discoverAgenticTools(home)) {
896
1322
  try {
897
- const toolComponent = componentForAgenticTool(entry);
1323
+ const toolComponent = componentForAgenticTool(entry, home);
898
1324
  components.push(toolComponent);
899
1325
  const parentRef = toolComponent["bom-ref"];
900
- const children = collectToolChildren(entry.tool, entry.dir, parentRef);
1326
+ const children = collectToolChildren(
1327
+ entry.tool,
1328
+ entry.dir,
1329
+ parentRef,
1330
+ home,
1331
+ );
901
1332
  const childRefs = [];
902
1333
  if (children.components.length) {
903
1334
  components.push(...children.components);