@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
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { readFileSync, statSync } from "node:fs";
10
- import { basename, dirname, relative, resolve, sep } from "node:path";
10
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
11
11
 
12
12
  import { getAllFiles } from "../core/fs.js";
13
13
  import { isInsideDir } from "../inventory/cmakeBuildDirs.js";
@@ -69,6 +69,222 @@ function licenseOf(file) {
69
69
  }
70
70
  }
71
71
 
72
+ /** A version header or CMakeLists.txt larger than this is not read. */
73
+ const MAX_VERSION_FILE_BYTES = 256 * 1024;
74
+
75
+ function readSmall(file) {
76
+ try {
77
+ const stat = statSync(file);
78
+ if (!stat.isFile() || stat.size > MAX_VERSION_FILE_BYTES) {
79
+ return undefined;
80
+ }
81
+ return readFileSync(file, "utf-8");
82
+ } catch (_err) {
83
+ return undefined;
84
+ }
85
+ }
86
+
87
+ function defineValue(text, name) {
88
+ for (const line of text.split(/\r?\n/)) {
89
+ const trimmed = line.trim();
90
+ if (!trimmed.startsWith("#")) {
91
+ continue;
92
+ }
93
+ const parts = trimmed.replace(/^#\s*/, "").split(/\s+/);
94
+ if (parts[0] === "define" && parts[1] === name && parts.length >= 3) {
95
+ return parts.slice(2).join(" ").replace(/^"|"$/g, "");
96
+ }
97
+ }
98
+ return undefined;
99
+ }
100
+
101
+ function numericTriple(text, names) {
102
+ const values = names.map((name) => defineValue(text, name));
103
+ return values.every((value) => /^\d+$/.test(value || ""))
104
+ ? values.join(".")
105
+ : undefined;
106
+ }
107
+
108
+ function packedBrotliVersion(value) {
109
+ if (!/^0x[0-9a-fA-F]{1,8}$/.test(value || "")) {
110
+ return undefined;
111
+ }
112
+ const packed = Number.parseInt(value, 16);
113
+ return `${packed >>> 24}.${(packed >>> 12) & 0xfff}.${packed & 0xfff}`;
114
+ }
115
+
116
+ /**
117
+ * Libraries that are often copied into C and C++ trees, the header each keeps
118
+ * its version in, and the CPE NVD names it by. The header is the library's
119
+ * own, so finding it at that path is what identifies the copy.
120
+ */
121
+ const VENDORED_VERSION_HEADERS = [
122
+ {
123
+ file: "c/common/version.h",
124
+ // 1.1 and later define MAJOR/MINOR/PATCH; 1.0.x only the packed
125
+ // BROTLI_VERSION 0xMMMnnnppp (1.0.9 is 0x1000009).
126
+ read: (text) =>
127
+ numericTriple(text, [
128
+ "BROTLI_VERSION_MAJOR",
129
+ "BROTLI_VERSION_MINOR",
130
+ "BROTLI_VERSION_PATCH",
131
+ ]) || packedBrotliVersion(defineValue(text, "BROTLI_VERSION")),
132
+ cpe: "google:brotli",
133
+ // google/brotli 0ed4268 (CVE-2026-111497) added this error code.
134
+ fixMarker: {
135
+ file: "c/include/brotli/decode.h",
136
+ text: "BLOCK_SWITCH",
137
+ property: "cdx:brotli:c:blockSwitchFix",
138
+ },
139
+ },
140
+ {
141
+ file: "zlib.h",
142
+ read: (text) => defineValue(text, "ZLIB_VERSION"),
143
+ cpe: "zlib:zlib",
144
+ },
145
+ {
146
+ file: "lib/zstd.h",
147
+ read: (text) =>
148
+ numericTriple(text, [
149
+ "ZSTD_VERSION_MAJOR",
150
+ "ZSTD_VERSION_MINOR",
151
+ "ZSTD_VERSION_RELEASE",
152
+ ]),
153
+ cpe: "facebook:zstandard",
154
+ },
155
+ {
156
+ file: "lib/lz4.h",
157
+ read: (text) =>
158
+ numericTriple(text, [
159
+ "LZ4_VERSION_MAJOR",
160
+ "LZ4_VERSION_MINOR",
161
+ "LZ4_VERSION_RELEASE",
162
+ ]),
163
+ cpe: "lz4_project:lz4",
164
+ },
165
+ {
166
+ file: "png.h",
167
+ read: (text) => defineValue(text, "PNG_LIBPNG_VER_STRING"),
168
+ cpe: "libpng:libpng",
169
+ },
170
+ {
171
+ file: "sqlite3.h",
172
+ read: (text) => defineValue(text, "SQLITE_VERSION"),
173
+ cpe: "sqlite:sqlite",
174
+ },
175
+ {
176
+ file: "lib/expat.h",
177
+ read: (text) =>
178
+ numericTriple(text, [
179
+ "XML_MAJOR_VERSION",
180
+ "XML_MINOR_VERSION",
181
+ "XML_MICRO_VERSION",
182
+ ]),
183
+ cpe: "libexpat_project:libexpat",
184
+ },
185
+ ];
186
+
187
+ const PLAIN_VERSION_RE = /^\d+(\.\d+){1,3}$/;
188
+
189
+ // `project(brotli VERSION 1.2.0 LANGUAGES C)`, on one line.
190
+ function cmakeProjectVersion(dir) {
191
+ const text = readSmall(join(dir, "CMakeLists.txt"));
192
+ if (!text) {
193
+ return undefined;
194
+ }
195
+ for (const line of text.split(/\r?\n/)) {
196
+ const trimmed = line.trim();
197
+ if (!trimmed.toLowerCase().startsWith("project(")) {
198
+ continue;
199
+ }
200
+ const args = trimmed.slice("project(".length).split(/[\s)]+/);
201
+ const index = args.findIndex((arg) => arg.toUpperCase() === "VERSION");
202
+ if (index !== -1 && PLAIN_VERSION_RE.test(args[index + 1] || "")) {
203
+ return args[index + 1];
204
+ }
205
+ }
206
+ return undefined;
207
+ }
208
+
209
+ /**
210
+ * Read the version of a vendored directory from the library's own version
211
+ * header, else from its CMake project() call.
212
+ *
213
+ * @param {string} dir Absolute path of the vendored directory
214
+ * @returns {{version?: string, source?: string, cpe?: string, properties: Object[]}|undefined}
215
+ * undefined when nothing is known, and without a version when only the fix
216
+ * marker could be read
217
+ */
218
+ export function vendoredVersionOf(dir) {
219
+ for (const entry of VENDORED_VERSION_HEADERS) {
220
+ const text = readSmall(join(dir, entry.file));
221
+ if (text === undefined) {
222
+ continue;
223
+ }
224
+ // The fix marker is read whether or not a version could be: a tree whose
225
+ // version header changed shape still shows whether its decoder is fixed.
226
+ const properties = [];
227
+ if (entry.fixMarker) {
228
+ const markerText = readSmall(join(dir, entry.fixMarker.file));
229
+ if (markerText !== undefined) {
230
+ properties.push({
231
+ name: entry.fixMarker.property,
232
+ value: markerText.includes(entry.fixMarker.text) ? "true" : "false",
233
+ });
234
+ }
235
+ }
236
+ const version = entry.read(text);
237
+ if (version && PLAIN_VERSION_RE.test(version)) {
238
+ return {
239
+ version,
240
+ source: entry.file,
241
+ cpe: `cpe:2.3:a:${entry.cpe}:${version}:*:*:*:*:*:*:*`,
242
+ properties,
243
+ };
244
+ }
245
+ if (properties.length) {
246
+ return { properties };
247
+ }
248
+ }
249
+ const version = cmakeProjectVersion(dir);
250
+ return version
251
+ ? { version, source: "CMakeLists.txt", properties: [] }
252
+ : undefined;
253
+ }
254
+
255
+ /**
256
+ * Directory names under which a project keeps other projects' code.
257
+ */
258
+ const VENDORED_DIR_NAMES = new Set([
259
+ "third_party",
260
+ "third-party",
261
+ "thirdparty",
262
+ "vendor",
263
+ "vendored",
264
+ "external",
265
+ ]);
266
+
267
+ /**
268
+ * Whether a file lies under a directory conventionally holding another
269
+ * project's code (`third_party/`, `vendor/`, `external/`) below the scan root.
270
+ * Build files there belong to the copied project: building them runs that
271
+ * project's build, and their dependencies are its build tooling.
272
+ *
273
+ * @param {string} root Project scan root
274
+ * @param {string} file File path
275
+ * @returns {boolean}
276
+ */
277
+ export function isInVendoredDir(root, file) {
278
+ const rel = relative(resolve(root), resolve(file));
279
+ if (!rel || rel.startsWith("..")) {
280
+ return false;
281
+ }
282
+ return rel
283
+ .split(/[\\/]+/)
284
+ .slice(0, -1)
285
+ .some((segment) => VENDORED_DIR_NAMES.has(segment.toLowerCase()));
286
+ }
287
+
72
288
  function licenseChoice(id) {
73
289
  return id.includes(" ") ? { expression: id } : { license: { id } };
74
290
  }
@@ -120,17 +336,32 @@ export function findVendoredCode(root, options = {}, excludeDirs = []) {
120
336
  }
121
337
  const path = relDir(file);
122
338
  const name = basename(dir);
123
- const purl = tryBuildPurl({ type: "generic", name, subpath: path });
339
+ const versionInfo = vendoredVersionOf(dir);
340
+ const purl = tryBuildPurl({
341
+ type: "generic",
342
+ name,
343
+ version: versionInfo?.version || null,
344
+ subpath: path,
345
+ });
124
346
  const licenseFile = relative(absRoot, file).split(sep).join("/");
125
347
  const component = {
126
348
  type: "library",
127
349
  name,
128
- version: "",
350
+ version: versionInfo?.version || "",
129
351
  licenses: [licenseChoice(id)],
130
352
  properties: [
131
353
  { name: "cdx:vendored", value: "true" },
132
354
  { name: "cdx:vendored:path", value: path },
133
355
  { name: "cdx:vendored:licenseFile", value: licenseFile },
356
+ ...(versionInfo?.version
357
+ ? [
358
+ {
359
+ name: "cdx:vendored:versionSource",
360
+ value: `${path}/${versionInfo.source}`,
361
+ },
362
+ ]
363
+ : []),
364
+ ...(versionInfo?.properties || []),
134
365
  ],
135
366
  evidence: {
136
367
  identity: {
@@ -143,6 +374,9 @@ export function findVendoredCode(root, options = {}, excludeDirs = []) {
143
374
  licenses: [licenseChoice(id)],
144
375
  },
145
376
  };
377
+ if (versionInfo?.cpe) {
378
+ component.cpe = versionInfo.cpe;
379
+ }
146
380
  if (purl) {
147
381
  component.purl = purl;
148
382
  component["bom-ref"] = decodeURIComponent(purl);
@@ -0,0 +1,360 @@
1
+ // Bounded, read-only probe for the Python packages of virtual environments and
2
+ // pyenv installs on a live host, for `-t os` / obom.
3
+ //
4
+ // osquery's python_packages table reads the interpreter's own site-packages
5
+ // directories, so a package installed into a virtual environment (an app's
6
+ // /opt/app/venv, a Poetry or pipenv environment under a home directory) or into
7
+ // a pyenv-built Python is missing from an OBOM. Brotli and brotlicffi are
8
+ // usually installed exactly there, pulled in by requests, aiohttp or httpx.
9
+ //
10
+ // Environments are found in fixed places, one wildcard level at a time: a
11
+ // directory holding pyvenv.cfg is a virtual environment, and a pyenv
12
+ // `versions/*` directory is an interpreter install. Only `*.dist-info/METADATA`
13
+ // files are read; nothing is executed.
14
+
15
+ import { readdirSync, statSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import process from "node:process";
18
+
19
+ import { isDryRun, recordActivity } from "../core/activity.js";
20
+ import { safeExistsSync, safeReadFileSync } from "../core/fs.js";
21
+ import { thoughtLog } from "../core/logger.js";
22
+ import { applyPurl, tryBuildPurl } from "../inventory/purl.js";
23
+ import { parseBdistMetadata } from "./ecosystems.js";
24
+
25
+ const MAX_ENVIRONMENTS = 64;
26
+ const MAX_DIRECTORIES = 512;
27
+ const MAX_PACKAGES = 8192;
28
+ const MAX_METADATA_BYTES = 4 * 1024 * 1024;
29
+
30
+ // Relative to the root.
31
+ const ROOT_PATTERNS = [
32
+ "opt/*",
33
+ "opt/*/*",
34
+ "srv/*",
35
+ "srv/*/*",
36
+ "app",
37
+ "app/*",
38
+ "usr/local/*",
39
+ ];
40
+ // Relative to a home directory.
41
+ const HOME_PATTERNS = [
42
+ "*",
43
+ "*/*",
44
+ ".virtualenvs/*",
45
+ ".local/share/virtualenvs/*",
46
+ ".cache/pypoetry/virtualenvs/*",
47
+ "Library/Caches/pypoetry/virtualenvs/*",
48
+ ".pyenv/versions/*",
49
+ ];
50
+ const HOME_PARENTS = { linux: ["home"], darwin: ["Users"] };
51
+ // Folders of a macOS home that privacy protection (TCC) guards. Looking inside
52
+ // one makes macOS ask the user to grant the terminal access, so a wildcard
53
+ // never enters them; Library is skipped too, for the Mail, Messages and cloud
54
+ // storage folders under it. Patterns that name a folder below Library, such as
55
+ // Poetry's cache, still reach it.
56
+ const MACOS_PROTECTED_HOME_FOLDERS = new Set([
57
+ "Desktop",
58
+ "Documents",
59
+ "Downloads",
60
+ "Library",
61
+ "Movies",
62
+ "Music",
63
+ "Pictures",
64
+ ]);
65
+
66
+ function listDirectory(directory) {
67
+ try {
68
+ return readdirSync(directory).sort();
69
+ } catch {
70
+ return [];
71
+ }
72
+ }
73
+
74
+ function isDirectory(path) {
75
+ try {
76
+ return statSync(path).isDirectory();
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ function isFile(path) {
83
+ try {
84
+ return statSync(path).isFile();
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ // `skipFirst` names folders a wildcard in the pattern's first segment never
91
+ // enters.
92
+ function expand(base, pattern, skipFirst) {
93
+ let current = [base];
94
+ for (const [depth, segment] of pattern.split("/").entries()) {
95
+ const next = [];
96
+ for (const directory of current) {
97
+ if (segment !== "*") {
98
+ next.push(join(directory, segment));
99
+ continue;
100
+ }
101
+ for (const name of listDirectory(directory)) {
102
+ // Hidden directories are only entered when a pattern names them.
103
+ if (name.startsWith(".") || (depth === 0 && skipFirst?.has(name))) {
104
+ continue;
105
+ }
106
+ next.push(join(directory, name));
107
+ if (next.length >= MAX_DIRECTORIES) {
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ current = next.filter(isDirectory).slice(0, MAX_DIRECTORIES);
113
+ }
114
+ return current;
115
+ }
116
+
117
+ /**
118
+ * Find the virtual environments and pyenv interpreters under the fixed
119
+ * places of a root.
120
+ *
121
+ * @param {string} rootPath root directory
122
+ * @param {string} platform `linux` or `darwin`
123
+ * @returns {{path: string, kind: string}[]} environments, at most MAX_ENVIRONMENTS
124
+ */
125
+ export function findPythonEnvironments(rootPath, platform) {
126
+ const bases = [{ base: rootPath, patterns: ROOT_PATTERNS }];
127
+ const homes = platform === "linux" ? [join(rootPath, "root")] : [];
128
+ for (const parent of HOME_PARENTS[platform] || []) {
129
+ for (const name of listDirectory(join(rootPath, parent))) {
130
+ homes.push(join(rootPath, parent, name));
131
+ }
132
+ }
133
+ const skipFirst =
134
+ platform === "darwin" ? MACOS_PROTECTED_HOME_FOLDERS : undefined;
135
+ for (const home of homes.filter(isDirectory)) {
136
+ bases.push({ base: home, patterns: HOME_PATTERNS, skipFirst });
137
+ }
138
+ const environments = [];
139
+ const seen = new Set();
140
+ for (const { base, patterns, skipFirst } of bases) {
141
+ for (const pattern of patterns) {
142
+ for (const candidate of expand(base, pattern, skipFirst)) {
143
+ if (seen.has(candidate)) {
144
+ continue;
145
+ }
146
+ let kind;
147
+ if (isFile(join(candidate, "pyvenv.cfg"))) {
148
+ kind = "virtualenv";
149
+ } else if (pattern.endsWith(".pyenv/versions/*")) {
150
+ kind = "pyenv";
151
+ }
152
+ // A directory with a nested .venv is the project, not the environment.
153
+ if (!kind && isFile(join(candidate, ".venv", "pyvenv.cfg"))) {
154
+ const nested = join(candidate, ".venv");
155
+ if (!seen.has(nested)) {
156
+ seen.add(nested);
157
+ environments.push({ path: nested, kind: "virtualenv" });
158
+ }
159
+ continue;
160
+ }
161
+ if (!kind) {
162
+ continue;
163
+ }
164
+ seen.add(candidate);
165
+ environments.push({ path: candidate, kind });
166
+ if (environments.length >= MAX_ENVIRONMENTS) {
167
+ return environments;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ return environments;
173
+ }
174
+
175
+ function sitePackagesOf(environment) {
176
+ const lib = join(environment, "lib");
177
+ const directories = [];
178
+ for (const name of listDirectory(lib)) {
179
+ if (!name.startsWith("python")) {
180
+ continue;
181
+ }
182
+ const sitePackages = join(lib, name, "site-packages");
183
+ if (isDirectory(sitePackages)) {
184
+ directories.push(sitePackages);
185
+ }
186
+ }
187
+ return directories;
188
+ }
189
+
190
+ function readMetadata(file) {
191
+ try {
192
+ const stats = statSync(file);
193
+ if (!stats.isFile() || stats.size > MAX_METADATA_BYTES) {
194
+ return undefined;
195
+ }
196
+ } catch {
197
+ return undefined;
198
+ }
199
+ const text = safeReadFileSync(file, "utf-8");
200
+ return typeof text === "string" ? text : undefined;
201
+ }
202
+
203
+ /**
204
+ * Inventory the packages installed in the virtual environments and pyenv
205
+ * interpreters of a host. One component per distinct name and version, with
206
+ * an occurrence for each dist-info directory it was found in, so no host path
207
+ * reaches a purl.
208
+ *
209
+ * @param {Object} [options] options
210
+ * @param {string} [options.rootPath="/"] root directory; tests point it at a built tree
211
+ * @param {string} [options.platform] `linux` or `darwin`
212
+ * @returns {Object[]} pypi components
213
+ */
214
+ export function probeHostPythonEnvironments(options = {}) {
215
+ const rootPath = options.rootPath || "/";
216
+ const platform = options.platform || process.platform;
217
+ if (!HOME_PARENTS[platform]) {
218
+ return [];
219
+ }
220
+ if (isDryRun) {
221
+ recordActivity({
222
+ capability: "python-env-probe",
223
+ kind: "read",
224
+ reason:
225
+ "Dry run mode blocks the Python environment probe, which reads package metadata outside the project.",
226
+ status: "blocked",
227
+ target: rootPath,
228
+ });
229
+ return [];
230
+ }
231
+ if (!safeExistsSync(rootPath)) {
232
+ return [];
233
+ }
234
+ const environments = findPythonEnvironments(rootPath, platform);
235
+ const byRef = new Map();
236
+ let packages = 0;
237
+ for (const environment of environments) {
238
+ for (const sitePackages of sitePackagesOf(environment.path)) {
239
+ for (const name of listDirectory(sitePackages)) {
240
+ if (!name.endsWith(".dist-info") || packages >= MAX_PACKAGES) {
241
+ continue;
242
+ }
243
+ const distInfo = join(sitePackages, name);
244
+ const metadataFile = join(distInfo, "METADATA");
245
+ const raw = readMetadata(metadataFile);
246
+ if (raw === undefined) {
247
+ continue;
248
+ }
249
+ const [pkg] = parseBdistMetadata(metadataFile, raw);
250
+ if (!pkg?.name || !pkg.version) {
251
+ continue;
252
+ }
253
+ packages += 1;
254
+ const purl = tryBuildPurl({
255
+ type: "pypi",
256
+ name: pkg.name,
257
+ version: pkg.version,
258
+ });
259
+ if (!purl) {
260
+ continue;
261
+ }
262
+ const ref = decodeURIComponent(purl);
263
+ let component = byRef.get(ref);
264
+ if (!component) {
265
+ component = applyPurl(
266
+ {
267
+ type: "library",
268
+ name: pkg.name,
269
+ version: pkg.version,
270
+ ...(pkg.description ? { description: pkg.description } : {}),
271
+ properties: [
272
+ { name: "cdx:python:environment", value: environment.kind },
273
+ ],
274
+ evidence: { occurrences: [] },
275
+ },
276
+ purl,
277
+ );
278
+ byRef.set(ref, component);
279
+ } else if (
280
+ !component.properties.some(
281
+ (prop) =>
282
+ prop.name === "cdx:python:environment" &&
283
+ prop.value === environment.kind,
284
+ )
285
+ ) {
286
+ component.properties.push({
287
+ name: "cdx:python:environment",
288
+ value: environment.kind,
289
+ });
290
+ }
291
+ component.evidence.occurrences.push({ location: distInfo });
292
+ }
293
+ }
294
+ }
295
+ recordActivity({
296
+ capability: "python-env-probe",
297
+ kind: "read",
298
+ reason: `Read ${packages} package(s) from ${environments.length} Python environment(s) in the fixed places.`,
299
+ status: "completed",
300
+ target: rootPath,
301
+ });
302
+ if (byRef.size) {
303
+ thoughtLog(
304
+ `I found ${byRef.size} Python package(s) in ${environments.length} virtual environment(s) or pyenv install(s).`,
305
+ );
306
+ }
307
+ return Array.from(byRef.values()).sort((a, b) =>
308
+ a["bom-ref"] < b["bom-ref"] ? -1 : a["bom-ref"] > b["bom-ref"] ? 1 : 0,
309
+ );
310
+ }
311
+
312
+ /**
313
+ * Add the packages of the host's Python environments to an OBOM's component
314
+ * list. A package osquery already listed from the interpreter's own
315
+ * site-packages keeps its component; the environment's dist-info becomes one
316
+ * more occurrence of it, and its cdx:python:environment property says where
317
+ * else it is installed.
318
+ *
319
+ * @param {Object[]} components OBOM components
320
+ * @param {Object[]} envPackages result of probeHostPythonEnvironments
321
+ * @returns {Object[]} the components, with the environment packages merged in
322
+ */
323
+ export function mergeHostPythonEnvironmentPackages(components, envPackages) {
324
+ if (!envPackages?.length) {
325
+ return components;
326
+ }
327
+ const byPurl = new Map();
328
+ for (const component of components) {
329
+ if (typeof component?.purl === "string") {
330
+ byPurl.set(decodeURIComponent(component.purl), component);
331
+ }
332
+ }
333
+ const added = [];
334
+ for (const pkg of envPackages) {
335
+ const existing = byPurl.get(decodeURIComponent(pkg.purl));
336
+ if (!existing) {
337
+ added.push(pkg);
338
+ continue;
339
+ }
340
+ existing.evidence = existing.evidence || {};
341
+ const occurrences = existing.evidence.occurrences || [];
342
+ for (const occurrence of pkg.evidence?.occurrences || []) {
343
+ if (!occurrences.some((o) => o.location === occurrence.location)) {
344
+ occurrences.push(occurrence);
345
+ }
346
+ }
347
+ existing.evidence.occurrences = occurrences;
348
+ existing.properties = existing.properties || [];
349
+ for (const prop of pkg.properties || []) {
350
+ if (
351
+ !existing.properties.some(
352
+ (p) => p.name === prop.name && p.value === prop.value,
353
+ )
354
+ ) {
355
+ existing.properties.push(prop);
356
+ }
357
+ }
358
+ }
359
+ return added.length ? components.concat(added) : components;
360
+ }