@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
@@ -0,0 +1,686 @@
1
+ // Bounded, read-only probe for a runtime no package manager owns on a live
2
+ // host, for `-t os` / obom. osquery's package tables list what dpkg, rpm and
3
+ // the like installed, so a runtime built from source or unpacked from a
4
+ // tarball (a Docker Official image, a /usr/local or /opt build, a version
5
+ // manager's tree) is invisible to them, and with it the version an advisory is
6
+ // read against.
7
+ //
8
+ // A runtime spec names the directories to look in, a few fixed ones and never
9
+ // a walk of the file system, and how to read a build from a file. A candidate
10
+ // is opened only after the package databases have said they do not own it: a
11
+ // package manager's file is identified by its package, and distros backport
12
+ // fixes under old version numbers. Only files nothing owns are read, for their
13
+ // bytes; nothing is executed.
14
+ //
15
+ // A package database that exists but cannot be read leaves ownership unknown.
16
+ // Unknown is not unpackaged: it emits nothing and records a degradation.
17
+ //
18
+ // Logging, activity and degradation calls go through the spec's hooks, so the
19
+ // caller's module owns them and its tests can stub them.
20
+
21
+ import { readdirSync, realpathSync, statSync } from "node:fs";
22
+ import {
23
+ basename,
24
+ isAbsolute,
25
+ join,
26
+ posix,
27
+ relative,
28
+ resolve,
29
+ sep,
30
+ win32,
31
+ } from "node:path";
32
+ import process from "node:process";
33
+
34
+ import { safeExistsSync, safeReadFileSync } from "../core/fs.js";
35
+ import { thoughtLog } from "../core/logger.js";
36
+ import {
37
+ APK_INSTALLED_DB_PATHS,
38
+ readApkInstalledDb,
39
+ } from "./apkInstalledDb.js";
40
+ import { packageStoreOf } from "./phpBuildInfo.js";
41
+ import { tryBuildPurl } from "./purl.js";
42
+
43
+ // One dpkg list, and every dpkg list together. A database past these caps
44
+ // counts as unreadable instead of being read in part; the apk database has the
45
+ // same cap in its reader.
46
+ const MAX_DATABASE_FILE_BYTES = 64 * 1024 * 1024;
47
+ const MAX_DATABASE_TOTAL_BYTES = 256 * 1024 * 1024;
48
+ const MAX_DPKG_LISTS = 100000;
49
+ // Paths per rpm_package_files query.
50
+ const RPM_QUERY_CHUNK = 200;
51
+
52
+ export function listDirectory(directory) {
53
+ try {
54
+ return readdirSync(directory).sort();
55
+ } catch {
56
+ return [];
57
+ }
58
+ }
59
+
60
+ export function isDirectory(path) {
61
+ try {
62
+ return statSync(path).isDirectory();
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
67
+
68
+ function realPathOf(path) {
69
+ try {
70
+ return realpathSync(path);
71
+ } catch {
72
+ return undefined;
73
+ }
74
+ }
75
+
76
+ function quoteForOsquery(value) {
77
+ return `'${value.replaceAll("'", "''")}'`;
78
+ }
79
+
80
+ function byBomRef(a, b) {
81
+ const left = a["bom-ref"];
82
+ const right = b["bom-ref"];
83
+ return left < right ? -1 : left > right ? 1 : 0;
84
+ }
85
+
86
+ function readBounded(file, maxBytes) {
87
+ try {
88
+ const stats = statSync(file);
89
+ if (!stats.isFile() || stats.size > maxBytes) {
90
+ return undefined;
91
+ }
92
+ } catch {
93
+ return undefined;
94
+ }
95
+ const text = safeReadFileSync(file, "utf-8");
96
+ return typeof text === "string" ? text : undefined;
97
+ }
98
+
99
+ // A package database: its name and path, `absent` when the host has none,
100
+ // `unreadable` when it has one that could not be read in full, and the paths of
101
+ // the candidates it lists.
102
+ function database(name, path, state = "ok") {
103
+ return { name, path, state, owned: new Set(), ownerOf: new Map() };
104
+ }
105
+
106
+ // dpkg keeps one <package>.list per package, naming each path as it is on the
107
+ // system.
108
+ function scanDpkg(infoDirectory, wanted) {
109
+ const result = database("dpkg", infoDirectory);
110
+ if (!safeExistsSync(infoDirectory)) {
111
+ result.state = "absent";
112
+ return result;
113
+ }
114
+ const lists = listDirectory(infoDirectory).filter((name) =>
115
+ name.endsWith(".list"),
116
+ );
117
+ if (!isDirectory(infoDirectory) || lists.length > MAX_DPKG_LISTS) {
118
+ result.state = "unreadable";
119
+ }
120
+ let total = 0;
121
+ for (const name of lists.slice(0, MAX_DPKG_LISTS)) {
122
+ const text = readBounded(
123
+ join(infoDirectory, name),
124
+ Math.min(MAX_DATABASE_FILE_BYTES, MAX_DATABASE_TOTAL_BYTES - total),
125
+ );
126
+ if (text === undefined) {
127
+ result.state = "unreadable";
128
+ continue;
129
+ }
130
+ total += text.length;
131
+ // libruby3.2:amd64.list names the package libruby3.2
132
+ const packageName = name.slice(0, -".list".length).split(":")[0];
133
+ for (const line of text.split("\n")) {
134
+ if (wanted.has(line)) {
135
+ result.owned.add(line);
136
+ result.ownerOf.set(line, packageName);
137
+ }
138
+ }
139
+ }
140
+ return result;
141
+ }
142
+
143
+ // apk's installed database is read by the same reader the OS inventory uses, so
144
+ // the packages an OBOM lists and the files this probe treats as owned follow one
145
+ // parser. A dry run never gets here: probeHostRuntime returns before it.
146
+ function scanApk(context, wanted) {
147
+ const { apkInstalledFile } = context.databases;
148
+ const apk = readApkInstalledDb({
149
+ rootPath: context.rootPath,
150
+ databaseFile: apkInstalledFile,
151
+ wantedFiles: wanted,
152
+ platform: context.platform,
153
+ });
154
+ const result = database(
155
+ "apk",
156
+ apk.path ||
157
+ apkInstalledFile ||
158
+ join(context.rootPath, APK_INSTALLED_DB_PATHS[0]),
159
+ );
160
+ result.state = { ok: "ok", unreadable: "unreadable" }[apk.state] || "absent";
161
+ for (const pkg of apk.packages) {
162
+ for (const file of pkg.files) {
163
+ result.owned.add(file);
164
+ if (pkg.name) {
165
+ result.ownerOf.set(file, pkg.name);
166
+ }
167
+ }
168
+ }
169
+ return result;
170
+ }
171
+
172
+ // osquery's rpm_package_files answers for a path without being told the
173
+ // package.
174
+ function queryRpm(databaseDirectories, wanted, executeOsQuery) {
175
+ const result = database("rpm", databaseDirectories[0]);
176
+ if (!databaseDirectories.some((directory) => safeExistsSync(directory))) {
177
+ result.state = "absent";
178
+ return result;
179
+ }
180
+ if (!wanted.size) {
181
+ return result;
182
+ }
183
+ if (typeof executeOsQuery !== "function") {
184
+ result.state = "unreadable";
185
+ return result;
186
+ }
187
+ // The query travels as one command-line argument, which Linux caps at
188
+ // 128 KiB, so a long list of paths is asked in chunks.
189
+ const paths = [...wanted];
190
+ const rows = [];
191
+ for (let start = 0; start < paths.length; start += RPM_QUERY_CHUNK) {
192
+ const chunk = executeOsQuery(
193
+ `SELECT package, path FROM rpm_package_files WHERE path IN (${paths
194
+ .slice(start, start + RPM_QUERY_CHUNK)
195
+ .map(quoteForOsquery)
196
+ .join(", ")})`,
197
+ );
198
+ if (!Array.isArray(chunk)) {
199
+ result.state = "unreadable";
200
+ return result;
201
+ }
202
+ rows.push(...chunk);
203
+ }
204
+ for (const row of rows) {
205
+ if (typeof row?.path === "string") {
206
+ result.owned.add(row.path);
207
+ if (typeof row.package === "string" && row.package) {
208
+ result.ownerOf.set(row.path, row.package);
209
+ }
210
+ }
211
+ }
212
+ return result;
213
+ }
214
+
215
+ // Chocolatey's database is the list of packages the OS scan has already made.
216
+ // The client is itself a package, so a Chocolatey with none listed was not
217
+ // listed: the query failed.
218
+ function readChocolatey(context) {
219
+ const configured =
220
+ context.readEnv("ChocolateyInstall") ||
221
+ join(context.rootPath, "ProgramData", "chocolatey");
222
+ const result = database("chocolatey", configured);
223
+ if (!safeExistsSync(configured)) {
224
+ result.state = "absent";
225
+ return result;
226
+ }
227
+ const packages = (context.packageComponents || []).filter((component) =>
228
+ component?.purl?.startsWith("pkg:chocolatey/"),
229
+ );
230
+ if (!packages.length) {
231
+ result.state = "unreadable";
232
+ return result;
233
+ }
234
+ result.directory = realPathOf(configured) || resolve(configured);
235
+ result.packageNames = new Set(
236
+ packages.map((component) => `${component.name}`.toLowerCase()),
237
+ );
238
+ return result;
239
+ }
240
+
241
+ // A Chocolatey package can unpack outside the package's own folder: php under
242
+ // C:\tools, nodejs.install through the Node.js MSI into Program Files. What
243
+ // makes a file Chocolatey's is the runtime's package being installed and the
244
+ // file being where Chocolatey keeps it or where the spec says the package puts
245
+ // it.
246
+ function chocolateyOwns(chocolatey, context, candidate) {
247
+ const names = context.spec.chocolateyPackages || [];
248
+ if (!names.some((name) => chocolatey.packageNames?.has(name))) {
249
+ return false;
250
+ }
251
+ const directories = [
252
+ chocolatey.directory,
253
+ ...(context.spec.chocolateyDirectories?.(context) || []).map(
254
+ (directory) => realPathOf(directory) || resolve(directory),
255
+ ),
256
+ ];
257
+ const real = candidate.real.toLowerCase();
258
+ return directories.some((directory) =>
259
+ real.startsWith(`${directory.toLowerCase().replace(/[\\/]+$/, "")}${sep}`),
260
+ );
261
+ }
262
+
263
+ // Folders that hold many products. An installer that names one of them as its
264
+ // own install location would otherwise claim every file below it.
265
+ const SHARED_WINDOWS_FOLDERS = new Set([
266
+ "program files",
267
+ "program files (x86)",
268
+ "programdata",
269
+ "users",
270
+ "windows",
271
+ ]);
272
+
273
+ // Windows Installer records each product's install folder; the OBOM's
274
+ // windows_programs rows carry it as install_location. A host always has some
275
+ // installed products, so with no rows at all the table was not read, and the
276
+ // installer cannot be asked.
277
+ function readWindowsInstaller(context) {
278
+ const result = database("windows installer", "programs");
279
+ const programs = (context.packageComponents || []).filter((component) =>
280
+ component?.properties?.some(
281
+ (property) =>
282
+ property?.name === "cdx:osquery:category" &&
283
+ property.value === "windows_programs",
284
+ ),
285
+ );
286
+ if (!programs.length) {
287
+ result.state = "absent";
288
+ return result;
289
+ }
290
+ result.locations = [];
291
+ for (const component of programs) {
292
+ const location = component.properties.find(
293
+ (property) => property?.name === "install_location",
294
+ )?.value;
295
+ if (typeof location !== "string" || !win32.isAbsolute(location)) {
296
+ continue;
297
+ }
298
+ const normalized = win32
299
+ .normalize(location)
300
+ .replace(/\\+$/, "")
301
+ .toLowerCase();
302
+ const parts = normalized.split("\\").slice(1);
303
+ if (
304
+ !parts.length ||
305
+ (parts.length === 1 && SHARED_WINDOWS_FOLDERS.has(parts[0]))
306
+ ) {
307
+ continue;
308
+ }
309
+ result.locations.push(`${normalized}\\`);
310
+ }
311
+ return result;
312
+ }
313
+
314
+ function windowsInstallerOwns(installer, candidate) {
315
+ return [candidate.logical, candidate.listedLogical].some(
316
+ (path) =>
317
+ typeof path === "string" &&
318
+ installer.locations.some((location) =>
319
+ path.toLowerCase().startsWith(location),
320
+ ),
321
+ );
322
+ }
323
+
324
+ // A path as the system being described names it, and as a purl subpath. `base`
325
+ // is the directory the path hangs off, so that a symlinked root, as a temporary
326
+ // directory often is, does not matter.
327
+ function locate(context, base, absolutePath) {
328
+ const parts = relative(base, absolutePath).split(sep);
329
+ return {
330
+ logical: context.pathApi.join(context.logicalRoot, ...parts),
331
+ subpath: parts.join("/"),
332
+ };
333
+ }
334
+
335
+ /**
336
+ * The distinct runtime files of the spec's directories, at most `maxProbes`.
337
+ * A symlink is read through to the file it names, which must itself be a
338
+ * runtime file under the root, and two names for one file are one candidate.
339
+ *
340
+ * @param {Object} context probe context
341
+ * @returns {Object[]} candidates with their real path, kind and locations
342
+ */
343
+ function collectCandidates(context) {
344
+ const { realRoot, platform, maxProbes, spec } = context;
345
+ const classify = (name) => spec.classify(name, platform);
346
+ const candidates = [];
347
+ const seen = new Set();
348
+ for (const { directory, only } of spec.directories(context)) {
349
+ for (const name of listDirectory(directory)) {
350
+ if (!classify(name)) {
351
+ continue;
352
+ }
353
+ const listed = join(directory, name);
354
+ const real = realPathOf(listed);
355
+ if (!real || seen.has(real)) {
356
+ continue;
357
+ }
358
+ const kind = classify(basename(real));
359
+ const inside = relative(realRoot, real);
360
+ if (
361
+ !kind ||
362
+ (only && kind !== only) ||
363
+ !inside ||
364
+ inside.startsWith("..") ||
365
+ isAbsolute(inside)
366
+ ) {
367
+ continue;
368
+ }
369
+ try {
370
+ if (!statSync(real).isFile()) {
371
+ continue;
372
+ }
373
+ } catch {
374
+ continue;
375
+ }
376
+ if (candidates.length >= maxProbes) {
377
+ thoughtLog(
378
+ `The ${spec.label} host probe looked at ${maxProbes} files and left any others unread.`,
379
+ );
380
+ return candidates;
381
+ }
382
+ seen.add(real);
383
+ candidates.push({
384
+ real,
385
+ kind,
386
+ name: basename(real),
387
+ ...locate(context, realRoot, real),
388
+ listedLogical: locate(context, context.rootPath, listed).logical,
389
+ });
390
+ }
391
+ }
392
+ return candidates;
393
+ }
394
+
395
+ function recordUnreadable(unreadable, recorded, context) {
396
+ if (recorded.has(unreadable.name)) {
397
+ return;
398
+ }
399
+ recorded.add(unreadable.name);
400
+ const { spec } = context;
401
+ spec.hooks.recordDegradation(spec.remediationId, {
402
+ ecosystem: spec.ecosystem,
403
+ tool: unreadable.name,
404
+ impact: "components",
405
+ path: unreadable.path,
406
+ detail: `The ${unreadable.name} package database could not be read in full, so ${spec.label} files it may own were not identified as unpackaged ${spec.label}.`,
407
+ });
408
+ }
409
+
410
+ /**
411
+ * Sort the candidates into those a package owns, those whose ownership could
412
+ * not be settled, and the unpackaged rest.
413
+ *
414
+ * @param {Object[]} candidates candidates from collectCandidates
415
+ * @param {Object} context probe context
416
+ * @returns {{unpackaged: Object[], owned: number, unknown: number}}
417
+ */
418
+ function classifyOwnership(candidates, context) {
419
+ const unpackaged = [];
420
+ const recorded = new Set();
421
+ let owned = 0;
422
+ let unknown = 0;
423
+ const pending = candidates.filter((candidate) => {
424
+ if (packageStoreOf(candidate.logical)) {
425
+ owned++;
426
+ return false;
427
+ }
428
+ return true;
429
+ });
430
+ if (!pending.length) {
431
+ return { unpackaged, owned, unknown };
432
+ }
433
+ const wanted = new Set(
434
+ pending.flatMap((candidate) => [
435
+ candidate.logical,
436
+ candidate.listedLogical,
437
+ ]),
438
+ );
439
+ const databases = [];
440
+ if (context.platform === "win32") {
441
+ databases.push(readChocolatey(context), readWindowsInstaller(context));
442
+ } else {
443
+ const { dpkgInfoDirectory, rpmDatabaseDirectories } = context.databases;
444
+ databases.push(
445
+ scanDpkg(dpkgInfoDirectory, wanted),
446
+ scanApk(context, wanted),
447
+ );
448
+ // Only what dpkg and apk did not claim is asked of osquery.
449
+ const claimed = new Set(databases.flatMap((db) => [...db.owned]));
450
+ databases.push(
451
+ queryRpm(
452
+ rpmDatabaseDirectories,
453
+ new Set([...wanted].filter((path) => !claimed.has(path))),
454
+ context.executeOsQuery,
455
+ ),
456
+ );
457
+ }
458
+ for (const candidate of pending) {
459
+ const paths = [candidate.logical, candidate.listedLogical];
460
+ const isOwned = databases.some(
461
+ (db) =>
462
+ db.state === "ok" &&
463
+ (db.name === "chocolatey"
464
+ ? chocolateyOwns(db, context, candidate)
465
+ : db.name === "windows installer"
466
+ ? windowsInstallerOwns(db, candidate)
467
+ : paths.some((path) => db.owned.has(path))),
468
+ );
469
+ if (isOwned) {
470
+ owned++;
471
+ continue;
472
+ }
473
+ const unreadable = databases.filter((db) => db.state === "unreadable");
474
+ if (!unreadable.length) {
475
+ unpackaged.push(candidate);
476
+ continue;
477
+ }
478
+ unknown++;
479
+ for (const db of unreadable) {
480
+ recordUnreadable(db, recorded, context);
481
+ }
482
+ }
483
+ return { unpackaged, owned, unknown };
484
+ }
485
+
486
+ // Like a container's unpackaged files, but named by where they are: a host can
487
+ // hold several binaries of one name.
488
+ function fileComponent(candidate) {
489
+ const purl = tryBuildPurl({
490
+ type: "generic",
491
+ name: candidate.name,
492
+ subpath: candidate.subpath,
493
+ });
494
+ return {
495
+ name: candidate.name,
496
+ type: "file",
497
+ ...(purl ? { purl } : {}),
498
+ "bom-ref": purl ? decodeURIComponent(purl) : `file:${candidate.logical}`,
499
+ properties: [{ name: "internal:SrcFile", value: candidate.logical }],
500
+ evidence: {
501
+ identity: [
502
+ {
503
+ field: "purl",
504
+ confidence: 0,
505
+ methods: [
506
+ { technique: "filename", confidence: 0, value: candidate.logical },
507
+ ],
508
+ concludedValue: candidate.logical,
509
+ },
510
+ ],
511
+ },
512
+ };
513
+ }
514
+
515
+ function createContext(options, platform, readEnv, logicalRoot, spec) {
516
+ const rootPath = options.rootPath || logicalRoot;
517
+ const realRoot = realPathOf(rootPath);
518
+ if (!realRoot) {
519
+ return undefined;
520
+ }
521
+ const databases = options.packageDatabases || {};
522
+ return {
523
+ platform,
524
+ readEnv,
525
+ rootPath,
526
+ realRoot,
527
+ logicalRoot,
528
+ pathApi: platform === "win32" ? win32 : posix,
529
+ maxProbes: options.maxProbes || spec.maxProbes,
530
+ spec,
531
+ executeOsQuery: options.executeOsQuery,
532
+ packageComponents: options.packageComponents,
533
+ databases: {
534
+ dpkgInfoDirectory:
535
+ databases.dpkgInfoDirectory || join(rootPath, "var/lib/dpkg/info"),
536
+ // Unset, the reader looks where apk keeps the database under the root.
537
+ apkInstalledFile: databases.apkInstalledFile,
538
+ rpmDatabaseDirectories: databases.rpmDatabaseDirectories || [
539
+ join(rootPath, "var/lib/rpm"),
540
+ join(rootPath, "usr/lib/sysimage/rpm"),
541
+ ],
542
+ },
543
+ };
544
+ }
545
+
546
+ /**
547
+ * Look for a runtime no package manager owns, in the directories a spec names.
548
+ * Components come back only for the files nothing owns: a `file` component for
549
+ * each runtime binary or library the spec could read a build from, the
550
+ * components the spec's inventory builds from them, and the edges between them.
551
+ *
552
+ * @param {Object} options probe options, as probeHostPhp documents them
553
+ * @param {Object} spec the runtime: `label`, `ecosystem`, `capability`,
554
+ * `remediationId` (a remediation catalog id, spelled out by the caller),
555
+ * `maxProbes`, `directories(context)`, `classify(name, platform)`,
556
+ * `readBuild(context, candidate)`, `createInventory()`,
557
+ * `attach(inventory, component, build, candidate)`, `finish(inventory)`,
558
+ * optional `chocolateyPackages` and `chocolateyDirectories(context)` (where
559
+ * those packages unpack outside Chocolatey's folder), and `hooks` carrying
560
+ * the caller's
561
+ * `isDryRun`, `readEnvironmentVariable`, `recordActivity` and
562
+ * `recordDegradation`
563
+ * @returns {{components: Object[], dependencies: Object[]}}
564
+ */
565
+ export function probeHostRuntime(options, spec) {
566
+ const empty = { components: [], dependencies: [] };
567
+ const { hooks } = spec;
568
+ const platform = options.platform || process.platform;
569
+ const readEnv = (name) =>
570
+ options.env ? options.env[name] : hooks.readEnvironmentVariable(name);
571
+ const logicalRoot =
572
+ platform === "win32" ? `${readEnv("SystemDrive") || "C:"}\\` : "/";
573
+ if (hooks.isDryRun) {
574
+ hooks.recordActivity({
575
+ capability: spec.capability,
576
+ kind: "read",
577
+ reason: `Dry run mode blocks the ${spec.label} host probe, which opens executables and package databases outside the project.`,
578
+ status: "blocked",
579
+ target: options.rootPath || logicalRoot,
580
+ });
581
+ return empty;
582
+ }
583
+ const context = createContext(options, platform, readEnv, logicalRoot, spec);
584
+ if (!context) {
585
+ return empty;
586
+ }
587
+ const candidates = collectCandidates(context);
588
+ if (!candidates.length) {
589
+ return empty;
590
+ }
591
+ const { unpackaged, owned, unknown } = classifyOwnership(candidates, context);
592
+ const inventory = spec.createInventory();
593
+ const files = [];
594
+ for (const candidate of unpackaged) {
595
+ const build = spec.readBuild(context, candidate);
596
+ if (build) {
597
+ const component = fileComponent(candidate);
598
+ spec.attach(inventory, component, build, candidate);
599
+ files.push(component);
600
+ }
601
+ }
602
+ hooks.recordActivity({
603
+ capability: spec.capability,
604
+ kind: "read",
605
+ reason: `Looked at ${candidates.length} ${spec.label} file(s) in the fixed directories: ${owned} owned by a package, ${unknown} of unknown ownership, ${files.length} unpackaged ${spec.label} identified.`,
606
+ status: "completed",
607
+ target: context.rootPath,
608
+ });
609
+ const finished = spec.finish(inventory);
610
+ if (files.length) {
611
+ thoughtLog(
612
+ `I found ${files.length} unpackaged ${spec.label} file(s) on this host.`,
613
+ );
614
+ }
615
+ return {
616
+ components: [
617
+ ...files.sort(byBomRef),
618
+ ...finished.components.sort(byBomRef),
619
+ ],
620
+ dependencies: finished.dependencies,
621
+ };
622
+ }
623
+
624
+ /**
625
+ * Ask the package databases of a POSIX host which package owns each path.
626
+ * The paths are named as the host names them (`/usr/lib/ruby/...`). A
627
+ * database that exists but cannot be read is reported, so the caller can tell
628
+ * "no owner" from "unknown".
629
+ *
630
+ * @param {Object} options probe options, as probeHostRuntime takes them
631
+ * @param {string[]} paths logical paths to look up
632
+ * @returns {{owners: Map<string, {database: string, packageName: string}>,
633
+ * unreadable: string[]}}
634
+ */
635
+ export function findHostPathOwners(options, paths) {
636
+ const owners = new Map();
637
+ const platform = options.platform || process.platform;
638
+ const rootPath = options.rootPath || "/";
639
+ if (platform === "win32" || !paths?.length || !realPathOf(rootPath)) {
640
+ return { owners, unreadable: [] };
641
+ }
642
+ const configured = options.packageDatabases || {};
643
+ const context = {
644
+ platform,
645
+ rootPath,
646
+ executeOsQuery: options.executeOsQuery,
647
+ databases: {
648
+ apkInstalledFile: configured.apkInstalledFile,
649
+ },
650
+ };
651
+ const wanted = new Set(paths);
652
+ const databases = [
653
+ scanDpkg(
654
+ configured.dpkgInfoDirectory || join(rootPath, "var/lib/dpkg/info"),
655
+ wanted,
656
+ ),
657
+ scanApk(context, wanted),
658
+ ];
659
+ const claimed = new Set(databases.flatMap((db) => [...db.owned]));
660
+ databases.push(
661
+ queryRpm(
662
+ configured.rpmDatabaseDirectories || [
663
+ join(rootPath, "var/lib/rpm"),
664
+ join(rootPath, "usr/lib/sysimage/rpm"),
665
+ ],
666
+ new Set([...wanted].filter((path) => !claimed.has(path))),
667
+ options.executeOsQuery,
668
+ ),
669
+ );
670
+ for (const db of databases) {
671
+ if (db.state !== "ok") {
672
+ continue;
673
+ }
674
+ for (const [path, packageName] of db.ownerOf) {
675
+ if (!owners.has(path)) {
676
+ owners.set(path, { database: db.name, packageName });
677
+ }
678
+ }
679
+ }
680
+ return {
681
+ owners,
682
+ unreadable: databases
683
+ .filter((db) => db.state === "unreadable")
684
+ .map((db) => db.name),
685
+ };
686
+ }
@@ -423,7 +423,11 @@ export function coursierPrefixToUrl(parts) {
423
423
  }
424
424
 
425
425
  /**
426
- * Locate a Maven coordinate's directory in the Coursier cache.
426
+ * Locate a Maven coordinate's directory in the Coursier cache. Coursier
427
+ * leaves an empty version directory behind in every repository it asked and
428
+ * missed, so a directory counts only when it holds the artifact's jar or POM;
429
+ * otherwise a project's own module would be credited to whichever repository
430
+ * the build happened to probe first.
427
431
  *
428
432
  * @param {string} group Maven groupId.
429
433
  * @param {string} name Maven artifactId, including any Scala suffix.
@@ -449,7 +453,10 @@ export function locateInCoursierCache(group, name, version) {
449
453
  let result = null;
450
454
  for (let i = 0; i < prefixes.length; i++) {
451
455
  const dir = join(cacheRoot, ...prefixes[i], ...groupParts, name, version);
452
- if (safeExistsSync(dir)) {
456
+ if (
457
+ safeExistsSync(join(dir, artifactFileName(name, version, "", "jar"))) ||
458
+ safeExistsSync(join(dir, artifactFileName(name, version, "", "pom")))
459
+ ) {
453
460
  const repoUrl = coursierPrefixToUrl(prefixes[i]);
454
461
  if (repoUrl) {
455
462
  result = { repoUrl, dir };