@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,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { readFileSync } from "node:fs";
2
+ import { readdirSync, readFileSync } from "node:fs";
3
3
  import {
4
4
  sep as _sep,
5
5
  basename,
@@ -18,6 +18,7 @@ import {
18
18
  hasDangerousUnicode,
19
19
  readEnvironmentVariable,
20
20
  } from "../core/activity.js";
21
+ import { readRegularTextFile } from "../core/boundedFile.js";
21
22
  import { recordDegradation } from "../core/buildLedger.js";
22
23
  import { deferFailOnError } from "../core/deferredExit.js";
23
24
  import { shouldFetchLicense } from "../core/env.js";
@@ -52,7 +53,12 @@ import {
52
53
  tryBuildPurl,
53
54
  } from "../inventory/purl.js";
54
55
  import { getLicenses, spdxLicenses } from "../inventory/spdx.js";
55
- import { cmakeFetchDependencies } from "../parsers/cmakeLists.js";
56
+ import {
57
+ cmakeFetchDependencies,
58
+ cmakeSetVariables,
59
+ expandCmakeVariables,
60
+ parseCmakeCommands,
61
+ } from "../parsers/cmakeLists.js";
56
62
  import { isValidIdnEmail } from "../parsers/email.js";
57
63
  import { submodulePurlCoordinates } from "../parsers/gitmodules.js";
58
64
  import { getDartMetadata } from "./ecosystems.js";
@@ -2784,6 +2790,130 @@ export function cmakeFetchDependencyComponent(dep, srcFile) {
2784
2790
  return component;
2785
2791
  }
2786
2792
 
2793
+ const MAX_MESON_WRAPS = 256;
2794
+ const MAX_MESON_WRAP_BYTES = 64 * 1024;
2795
+
2796
+ function upstreamVersionOf(text) {
2797
+ if (typeof text !== "string") {
2798
+ return undefined;
2799
+ }
2800
+ const value = text.trim().replace(/^v(?=\d)/, "");
2801
+ return /^\d+(\.\d+)*([-+._~][0-9A-Za-z.]+)?$/.test(value) ? value : undefined;
2802
+ }
2803
+
2804
+ // `brotli-1.1.0`, `brotli-1.1.0.tar.gz`: the version after the last `-` that
2805
+ // starts a number.
2806
+ function versionFromArchiveName(name) {
2807
+ if (typeof name !== "string") {
2808
+ return undefined;
2809
+ }
2810
+ const stem = name.replace(/\.(tar\.gz|tar\.xz|tar\.bz2|tgz|zip|tar)$/i, "");
2811
+ const dash = stem.search(/-\d[^-]*$/);
2812
+ return dash === -1 ? undefined : upstreamVersionOf(stem.slice(dash + 1));
2813
+ }
2814
+
2815
+ /**
2816
+ * Read the wrap files of a meson project's subprojects directory: which
2817
+ * dependency names each wrap provides, and the version it pins.
2818
+ * https://mesonbuild.com/Wrap-dependency-system-manual.html
2819
+ *
2820
+ * A WrapDB wrap records `wrapdb_version = <upstream>-<wrap revision>`; any wrap
2821
+ * names its unpacked `directory` and its archive, which carry the upstream
2822
+ * version; a git wrap pins a `revision` that is often a version tag.
2823
+ *
2824
+ * @param {string} subprojectsDir path of the subprojects directory
2825
+ * @returns {Map<string, {wrap: string, version: string, source: string, revision?: string}>}
2826
+ * by provided dependency name, lower-case
2827
+ */
2828
+ export function readMesonWraps(subprojectsDir) {
2829
+ const provided = new Map();
2830
+ let wrapFiles = [];
2831
+ try {
2832
+ wrapFiles = readdirSync(subprojectsDir)
2833
+ .filter((name) => name.endsWith(".wrap"))
2834
+ .sort()
2835
+ .slice(0, MAX_MESON_WRAPS);
2836
+ } catch {
2837
+ return provided;
2838
+ }
2839
+ for (const wrapFile of wrapFiles) {
2840
+ // A regular file only: a FIFO named *.wrap would block a plain read.
2841
+ const text = readRegularTextFile(
2842
+ join(subprojectsDir, wrapFile),
2843
+ MAX_MESON_WRAP_BYTES,
2844
+ );
2845
+ if (text === undefined) {
2846
+ continue;
2847
+ }
2848
+ const sections = {};
2849
+ let section = "";
2850
+ for (const rawLine of text.split(/\r?\n/)) {
2851
+ const line = rawLine.trim();
2852
+ if (!line || line.startsWith("#") || line.startsWith(";")) {
2853
+ continue;
2854
+ }
2855
+ if (line.startsWith("[") && line.endsWith("]")) {
2856
+ section = line.slice(1, -1).trim();
2857
+ sections[section] = sections[section] || {};
2858
+ continue;
2859
+ }
2860
+ const equals = line.indexOf("=");
2861
+ if (equals <= 0 || !section) {
2862
+ continue;
2863
+ }
2864
+ sections[section][line.slice(0, equals).trim()] = line
2865
+ .slice(equals + 1)
2866
+ .trim();
2867
+ }
2868
+ const body = sections["wrap-file"] || sections["wrap-git"] || {};
2869
+ let version;
2870
+ let source;
2871
+ let revision;
2872
+ if (body.wrapdb_version) {
2873
+ const dash = body.wrapdb_version.lastIndexOf("-");
2874
+ version = upstreamVersionOf(
2875
+ dash > 0 ? body.wrapdb_version.slice(0, dash) : body.wrapdb_version,
2876
+ );
2877
+ revision = dash > 0 ? body.wrapdb_version.slice(dash + 1) : undefined;
2878
+ source = "wrapdb_version";
2879
+ }
2880
+ if (!version && sections["wrap-file"]) {
2881
+ version =
2882
+ versionFromArchiveName(body.directory) ||
2883
+ versionFromArchiveName(body.source_filename);
2884
+ source = version ? "directory" : undefined;
2885
+ }
2886
+ if (!version && sections["wrap-git"]) {
2887
+ version = upstreamVersionOf(body.revision);
2888
+ source = version ? "revision" : undefined;
2889
+ }
2890
+ if (!version) {
2891
+ continue;
2892
+ }
2893
+ const wrap = wrapFile.slice(0, -".wrap".length);
2894
+ const entry = { wrap, version, source, ...(revision ? { revision } : {}) };
2895
+ const names = new Set([wrap.toLowerCase()]);
2896
+ const provide = sections.provide || {};
2897
+ for (const [key, value] of Object.entries(provide)) {
2898
+ if (key === "dependency_names" || key === "program_names") {
2899
+ for (const name of value.split(",")) {
2900
+ if (name.trim()) {
2901
+ names.add(name.trim().toLowerCase());
2902
+ }
2903
+ }
2904
+ } else {
2905
+ names.add(key.toLowerCase());
2906
+ }
2907
+ }
2908
+ for (const name of names) {
2909
+ if (!provided.has(name)) {
2910
+ provided.set(name, entry);
2911
+ }
2912
+ }
2913
+ }
2914
+ return provided;
2915
+ }
2916
+
2787
2917
  /**
2788
2918
  * Parse a CMake-like build file (CMakeLists.txt, meson.build, etc.) and extract the
2789
2919
  * parent component and list of dependency packages.
@@ -2801,6 +2931,11 @@ export function cmakeFetchDependencyComponent(dep, srcFile) {
2801
2931
  */
2802
2932
  export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
2803
2933
  const rawData = readFileSync(cmakeListFile, { encoding: "utf-8" });
2934
+ // The wraps a meson project ships pin the versions of the subprojects its
2935
+ // dependency() calls fall back to.
2936
+ const mesonWraps = basename(cmakeListFile).startsWith("meson")
2937
+ ? readMesonWraps(join(dirname(cmakeListFile), "subprojects"))
2938
+ : new Map();
2804
2939
  let cmakeListData = rawData;
2805
2940
  const pkgList = [];
2806
2941
  const pkgAddedMap = {};
@@ -2808,6 +2943,25 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
2808
2943
  const versionsMap = {};
2809
2944
  let parentComponent = {};
2810
2945
  const templateValues = {};
2946
+ // The parent component is built the same way whether its name and version
2947
+ // came from the parsed project() command or from the line loop below.
2948
+ const toParentComponent = (parentName, parentVersion) => {
2949
+ const component = {
2950
+ group: options.projectGroup || "",
2951
+ name: parentName,
2952
+ version: parentVersion || options.projectVersion || "",
2953
+ type: "application",
2954
+ };
2955
+ component["purl"] = build({
2956
+ type: pkgType,
2957
+ namespace: component.group || null,
2958
+ name: component.name,
2959
+ version: component.version || null,
2960
+ subpath: null,
2961
+ });
2962
+ component["bom-ref"] = decodeURIComponent(component["purl"]);
2963
+ return component;
2964
+ };
2811
2965
  // A dependency fetched at configure time is the same package a find_package()
2812
2966
  // of the same name asks for: the fetched component, with its source and pin,
2813
2967
  // stands for both.
@@ -2823,6 +2977,38 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
2823
2977
  fetchedNames.add(dep.name.toLowerCase());
2824
2978
  pkgList.push(component);
2825
2979
  }
2980
+ // A project() call may span several lines, which the line loop below
2981
+ // cannot rejoin, so the parent name and version are read from the parsed
2982
+ // commands instead. The first argument is the project name and the
2983
+ // VERSION keyword is followed by the version.
2984
+ const commands = parseCmakeCommands(rawData);
2985
+ const variables = cmakeSetVariables(commands);
2986
+ const projectCommand = commands.find(
2987
+ (command) => command.name === "project",
2988
+ );
2989
+ if (projectCommand?.args.length) {
2990
+ const args = projectCommand.args.map((arg) =>
2991
+ expandCmakeVariables(arg, variables),
2992
+ );
2993
+ const parentName = args[0].replace(":", "").trim();
2994
+ let parentVersion;
2995
+ const versionIndex = args.findIndex(
2996
+ (arg) => arg.toLowerCase() === "version",
2997
+ );
2998
+ if (versionIndex > -1 && args.length > versionIndex + 1) {
2999
+ parentVersion = args[versionIndex + 1];
3000
+ }
3001
+ // A `${VAR}` that no `set()` in this file defines stays literal. It is
3002
+ // not a version, and percent-encoding it into a purl publishes the
3003
+ // variable name as the version, so the field is left empty instead.
3004
+ // CMakeCache.txt carries the evaluated value when a build tree exists.
3005
+ if (parentVersion?.includes("$")) {
3006
+ parentVersion = undefined;
3007
+ }
3008
+ if (parentName.length && !parentName.includes("$")) {
3009
+ parentComponent = toParentComponent(parentName, parentVersion);
3010
+ }
3011
+ }
2826
3012
  }
2827
3013
  cmakeListData = cmakeListData
2828
3014
  .replace(/^ {2}/g, "")
@@ -2883,23 +3069,7 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
2883
3069
  parentVersion = undefined;
2884
3070
  }
2885
3071
  if (parentName?.length && !parentName.includes("$")) {
2886
- parentComponent = {
2887
- group: options.projectGroup || "",
2888
- name: parentName,
2889
- version: parentVersion || options.projectVersion || "",
2890
- type: "application",
2891
- };
2892
- const safePath = path ? path.replace(/^\/+/, "") : null;
2893
- parentComponent["purl"] = build({
2894
- type: pkgType,
2895
- namespace: parentComponent.group || null,
2896
- name: parentComponent.name,
2897
- version: parentComponent.version || null,
2898
- subpath: safePath,
2899
- });
2900
- parentComponent["bom-ref"] = decodeURIComponent(
2901
- parentComponent["purl"],
2902
- );
3072
+ parentComponent = toParentComponent(parentName, parentVersion);
2903
3073
  }
2904
3074
  } else if (CMAKE_FIND_COMMANDS.has(command)) {
2905
3075
  let tmpB = commandArgs.split(")")[0].split(" ");
@@ -3041,7 +3211,26 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
3041
3211
  // wrap match has since replaced, so the scraped name is the fallback.
3042
3212
  const versionSpecifier =
3043
3213
  versionSpecifiersMap[n] || versionSpecifiersMap[scrapedName];
3044
- const version = versionsMap[n] || versionsMap[scrapedName] || "";
3214
+ let version = versionsMap[n] || versionsMap[scrapedName] || "";
3215
+ const localWrap =
3216
+ mesonWraps.get(scrapedName.toLowerCase()) ||
3217
+ mesonWraps.get(n.toLowerCase());
3218
+ if (localWrap && !version) {
3219
+ version = localWrap.version;
3220
+ props.push({ name: "cdx:meson:wrap", value: localWrap.wrap });
3221
+ props.push({
3222
+ name: "cdx:meson:wrap:versionSource",
3223
+ value: localWrap.source,
3224
+ });
3225
+ if (localWrap.revision) {
3226
+ props.push({
3227
+ name: "cdx:meson:wrap:revision",
3228
+ value: localWrap.revision,
3229
+ });
3230
+ }
3231
+ // A pinned wrap is a stronger identity than a name match alone.
3232
+ confidence = Math.max(confidence, 0.7);
3233
+ }
3045
3234
  if (versionSpecifier) {
3046
3235
  props.push({
3047
3236
  name: "cdx:build:versionSpecifiers",
@@ -8,6 +8,7 @@ import { parse as _load } from "yaml";
8
8
  import { DEBUG_MODE } from "../core/activity.js";
9
9
  import { recordDegradation } from "../core/buildLedger.js";
10
10
  import { getAllFiles, safeExistsSync, safeSpawnSync } from "../core/fs.js";
11
+ import { sanitizeBomUrl } from "../core/propertySanitizer.js";
11
12
  import { PYTHON_STD_MODULES } from "../core/state.js";
12
13
  import { parseToml } from "../core/toml.js";
13
14
  import { tryBuildPurl } from "../inventory/purl.js";
@@ -888,6 +889,61 @@ export function enrichFromInstalledMetadata(pkgList, installedMetadata) {
888
889
  return enriched;
889
890
  }
890
891
 
892
+ // The interpreter cdxgen assumes for a lock file's environment. A marker that
893
+ // tests the implementation against it is decidable without a target machine.
894
+ const IMPLEMENTATION_CLAUSE_RE =
895
+ /^(platform_python_implementation|implementation_name)\s*(==|!=)\s*(['"])([A-Za-z]+)\3$/;
896
+
897
+ function implementationClauseIsFalse(clause) {
898
+ const match = IMPLEMENTATION_CLAUSE_RE.exec(clause.trim());
899
+ if (!match) {
900
+ return false;
901
+ }
902
+ const expected = match[1] === "implementation_name" ? "cpython" : "CPython";
903
+ const equal = match[4] === expected;
904
+ return match[2] === "==" ? !equal : equal;
905
+ }
906
+
907
+ /**
908
+ * Whether an environment marker is false for every CPython interpreter. Only
909
+ * `platform_python_implementation` and `implementation_name` clauses are
910
+ * decided; anything else, or a marker with parentheses, is treated as possibly
911
+ * true, so the answer errs towards keeping a package.
912
+ *
913
+ * @param {string} marker PEP 508 environment marker
914
+ * @returns {boolean} true when no CPython environment satisfies the marker
915
+ */
916
+ export function markerExcludesCPython(marker) {
917
+ if (typeof marker !== "string" || !marker.trim() || /[()]/.test(marker)) {
918
+ return false;
919
+ }
920
+ return marker
921
+ .split(/\s+or\s+/)
922
+ .every((disjunct) =>
923
+ disjunct.split(/\s+and\s+/).some(implementationClauseIsFalse),
924
+ );
925
+ }
926
+
927
+ function recordMarkerEdge(markerEdges, depKey, apkgDep) {
928
+ if (!depKey) {
929
+ return;
930
+ }
931
+ const edge = markerEdges.get(depKey) || {
932
+ total: 0,
933
+ cpythonExcluded: 0,
934
+ markers: new Set(),
935
+ };
936
+ edge.total += 1;
937
+ const marker = typeof apkgDep === "object" ? apkgDep?.marker : undefined;
938
+ if (typeof marker === "string" && marker.trim()) {
939
+ edge.markers.add(marker.trim());
940
+ if (markerExcludesCPython(marker)) {
941
+ edge.cpythonExcluded += 1;
942
+ }
943
+ }
944
+ markerEdges.set(depKey, edge);
945
+ }
946
+
891
947
  /**
892
948
  * Method to parse python lock files such as poetry.lock, pdm.lock, uv.lock, and pylock.toml.
893
949
  *
@@ -911,6 +967,8 @@ export async function parsePyLockData(
911
967
  const depsMap = {};
912
968
  const existingPkgMap = {};
913
969
  const pkgBomRefMap = {};
970
+ // Incoming edges per dependency, with the environment markers they carry.
971
+ const markerEdges = new Map();
914
972
  let directDepsKeys = {};
915
973
  let groupDepsKeys = {};
916
974
  let dependencySourceMap = {};
@@ -1080,10 +1138,22 @@ export async function parsePyLockData(
1080
1138
  hasWorkspaces = true;
1081
1139
  }
1082
1140
  for (const amember of workspaceMembers) {
1083
- if (amember === parentComponent.name) {
1141
+ if (amember === parentComponent?.name) {
1142
+ continue;
1143
+ }
1144
+ // a lock file read without its pyproject.toml has no parent to hold
1145
+ // the members, which is said once for the whole lock file
1146
+ if (!parentComponent) {
1147
+ if (!workspaceWarningShown) {
1148
+ console.log(
1149
+ `The workspace members in ${lockFile || "the lock file"} are read without a pyproject.toml, so there is no project to list them under.`,
1150
+ );
1151
+ workspaceWarningShown = true;
1152
+ }
1084
1153
  continue;
1085
1154
  }
1086
1155
  if (workspaceComponentMap[amember]) {
1156
+ parentComponent.components = parentComponent.components || [];
1087
1157
  parentComponent.components.push(workspaceComponentMap[amember]);
1088
1158
  } else {
1089
1159
  if (!workspaceWarningShown) {
@@ -1334,9 +1404,16 @@ export async function parsePyLockData(
1334
1404
  devDependencies.length ||
1335
1405
  optionalDependencies.length
1336
1406
  ) {
1337
- if (Array.isArray(apkg.dependencies)) {
1407
+ // A package whose only dependencies are optional (urllib3 has no
1408
+ // required ones, only the `brotli`, `socks` and `zstd` extras) has no
1409
+ // `dependencies` key in uv.lock, but its extras are still edges.
1410
+ if (
1411
+ Array.isArray(apkg.dependencies) ||
1412
+ (!apkg.dependencies &&
1413
+ (devDependencies.length || optionalDependencies.length))
1414
+ ) {
1338
1415
  // pdm.lock files
1339
- let allDeps = apkg.dependencies;
1416
+ let allDeps = apkg.dependencies || [];
1340
1417
  allDeps = allDeps.concat(devDependencies);
1341
1418
  allDeps = allDeps.concat(optionalDependencies);
1342
1419
  for (const apkgDep of allDeps) {
@@ -1348,6 +1425,7 @@ export async function parsePyLockData(
1348
1425
  const depPkgRef =
1349
1426
  existingPkgMap[nameLower] || existingPkgMap[nameStr];
1350
1427
  depsMap[pkg["bom-ref"]].add(depPkgRef || nameStr);
1428
+ recordMarkerEdge(markerEdges, depPkgRef || nameLower, apkgDep);
1351
1429
  // Propagate the workspace properties to the child components
1352
1430
  if (depPkgRef && pkgBomRefMap[depPkgRef]) {
1353
1431
  const dependentPkg = pkgBomRefMap[depPkgRef];
@@ -1421,6 +1499,28 @@ export async function parsePyLockData(
1421
1499
  }
1422
1500
  }
1423
1501
  }
1502
+ // A package reached only through edges whose environment marker excludes
1503
+ // CPython (brotlicffi is `platform_python_implementation != 'CPython'`) is
1504
+ // not installed by a CPython environment built from this lock, so it is
1505
+ // optional rather than part of the CPython closure. A direct dependency of
1506
+ // the project is left alone: the manifest asked for it.
1507
+ for (const [depRef, edge] of markerEdges) {
1508
+ const dependentPkg = pkgBomRefMap[depRef];
1509
+ if (
1510
+ !dependentPkg ||
1511
+ !edge.total ||
1512
+ edge.cpythonExcluded !== edge.total ||
1513
+ directDepsKeys[normalizePythonDependencyKey(dependentPkg.name)]
1514
+ ) {
1515
+ continue;
1516
+ }
1517
+ dependentPkg.scope = "optional";
1518
+ dependentPkg.properties = dependentPkg.properties || [];
1519
+ dependentPkg.properties.push({
1520
+ name: "cdx:pip:markers",
1521
+ value: Array.from(edge.markers).sort().join(" or "),
1522
+ });
1523
+ }
1424
1524
  // Seed the parent component's first-level edges from the manifest. Lock
1425
1525
  // files describe the resolved closure but do not say which of the locked
1426
1526
  // packages the root project directly depends on; parsePyProjectTomlFile
@@ -1542,14 +1642,40 @@ export async function parsePyLockData(
1542
1642
  }
1543
1643
 
1544
1644
  /**
1545
- * First version clause of a PEP 508 requirement: an operator and the version
1546
- * that follows it.
1645
+ * An exact PEP 440 version clause: `==` or `===` and a version with no
1646
+ * wildcard.
1547
1647
  *
1548
- * `===` (arbitrary equality) is tried before `==`, whitespace may separate the
1549
- * operator from the version, and a version may carry a PEP 440 epoch (`1!2.0`)
1550
- * or a local version label (`2.1.0+cpu`).
1648
+ * Whitespace may separate the operator from the version, and a version may
1649
+ * carry a PEP 440 epoch (`1!2.0`) or a local version label (`2.1.0+cpu`).
1551
1650
  */
1552
- const PEP440_VERSION_CLAUSE = /(===|==|!=|<=|>=|~=|<|>)\s*([0-9A-Za-z.*_+!-]*)/;
1651
+ const PEP440_EXACT_CLAUSE = /^(===?)\s*([0-9A-Za-z._+!-]+)$/;
1652
+
1653
+ /**
1654
+ * Version a PEP 508 requirement pins: the version of its only exact clause.
1655
+ *
1656
+ * A requirement with no exact clause, a wildcard (`==1.2.*`) or more than one
1657
+ * exact clause pins no version, so a range such as `>=1.0,<2` gives null
1658
+ * rather than one of its bounds. Per-requirement options such as
1659
+ * `--config-settings` are not clauses.
1660
+ *
1661
+ * @param {String} versionSpecifiers Version specifiers of the requirement
1662
+ * @returns {String|null} The pinned version, or null
1663
+ */
1664
+ function pinnedRequirementVersion(versionSpecifiers) {
1665
+ const specifiers = versionSpecifiers || "";
1666
+ const optionsIndex = specifiers.indexOf(" --");
1667
+ const clausesSource =
1668
+ optionsIndex === -1 ? specifiers : specifiers.substring(0, optionsIndex);
1669
+ let trimmed = clausesSource.trim();
1670
+ if (trimmed.startsWith("(") && trimmed.endsWith(")")) {
1671
+ trimmed = trimmed.substring(1, trimmed.length - 1);
1672
+ }
1673
+ const exactClauses = trimmed
1674
+ .split(",")
1675
+ .map((clause) => clause.trim().match(PEP440_EXACT_CLAUSE))
1676
+ .filter(Boolean);
1677
+ return exactClauses.length === 1 ? exactClauses[0][2] : null;
1678
+ }
1553
1679
 
1554
1680
  /**
1555
1681
  * Method to parse requirements.txt file. This must be replaced with atom parsedeps.
@@ -1738,7 +1864,7 @@ async function parseReqData(
1738
1864
 
1739
1865
  // Handle extras (e.g., package[extra1,extra2])
1740
1866
  let extras = null;
1741
- const extrasMatch = l.match(/^([a-zA-Z0-9_\-.]+)(\[([^\]]+)])?(.*)$/);
1867
+ const extrasMatch = l.match(/^([a-zA-Z0-9_\-.]+)(\s*\[([^\]]+)])?(.*)$/);
1742
1868
  if (extrasMatch) {
1743
1869
  const [, packageName, , extrasStr, versionSpecifiers] = extrasMatch;
1744
1870
  const name = packageName;
@@ -1749,14 +1875,7 @@ async function parseReqData(
1749
1875
  if (PYTHON_STD_MODULES.includes(name)) {
1750
1876
  continue;
1751
1877
  }
1752
- const versionMatch = versionSpecifiers.match(PEP440_VERSION_CLAUSE);
1753
- let version = null;
1754
- if (versionMatch) {
1755
- version = versionMatch[2].replaceAll("*", "0") || null;
1756
- if (version === "0") {
1757
- version = null;
1758
- }
1759
- }
1878
+ const version = pinnedRequirementVersion(versionSpecifiers);
1760
1879
  const apkg = {
1761
1880
  name,
1762
1881
  version,
@@ -1776,7 +1895,10 @@ async function parseReqData(
1776
1895
  value: extras.join(","),
1777
1896
  });
1778
1897
  }
1779
- if (versionSpecifiers && !versionSpecifiers.trim().startsWith("==")) {
1898
+ if (
1899
+ versionSpecifiers?.trim() &&
1900
+ (!version || !versionSpecifiers.trim().startsWith("=="))
1901
+ ) {
1780
1902
  properties.push({
1781
1903
  name: "cdx:pypi:versionSpecifiers",
1782
1904
  value: versionSpecifiers.trim(),
@@ -1813,12 +1935,7 @@ async function parseReqData(
1813
1935
  if (PYTHON_STD_MODULES.includes(name)) {
1814
1936
  continue;
1815
1937
  }
1816
- const versionMatch = versionSpecifiers.match(PEP440_VERSION_CLAUSE);
1817
- let version = null;
1818
- if (versionMatch) {
1819
- version = versionMatch[2].replaceAll("*", "0") || null;
1820
- if (version === "0") version = null;
1821
- }
1938
+ const version = pinnedRequirementVersion(versionSpecifiers);
1822
1939
  const apkg = {
1823
1940
  name,
1824
1941
  version,
@@ -1829,7 +1946,10 @@ async function parseReqData(
1829
1946
  if (licenses) {
1830
1947
  apkg.licenses = licenses;
1831
1948
  }
1832
- if (versionSpecifiers && !versionSpecifiers.trim().startsWith("==")) {
1949
+ if (
1950
+ versionSpecifiers?.trim() &&
1951
+ (!version || !versionSpecifiers.trim().startsWith("=="))
1952
+ ) {
1833
1953
  properties.push({
1834
1954
  name: "cdx:pypi:versionSpecifiers",
1835
1955
  value: versionSpecifiers.trim(),
@@ -1977,6 +2097,33 @@ function createPixiBomRef(packageData) {
1977
2097
  return `library:${packageData["name"]}:${packageData["version"] || ""}`;
1978
2098
  }
1979
2099
 
2100
+ /**
2101
+ * Return the download url of a pixi.lock package, without credentials, query
2102
+ * or fragment. A private conda channel can carry its access token in a
2103
+ * `/t/<token>/` path segment, so such a url is left out.
2104
+ *
2105
+ * @param {unknown} url Package url from pixi.lock
2106
+ * @returns {string|undefined} Distribution url
2107
+ */
2108
+ function pixiDistributionUrl(url) {
2109
+ if (typeof url !== "string" || !url.trim()) {
2110
+ return undefined;
2111
+ }
2112
+ const sanitized = sanitizeBomUrl(url);
2113
+ try {
2114
+ const parsed = new URL(sanitized);
2115
+ if (
2116
+ !["http:", "https:"].includes(parsed.protocol) ||
2117
+ parsed.pathname.split("/").includes("t")
2118
+ ) {
2119
+ return undefined;
2120
+ }
2121
+ } catch {
2122
+ return undefined;
2123
+ }
2124
+ return sanitized;
2125
+ }
2126
+
1980
2127
  /**
1981
2128
  * Method to parse pixi.lock data
1982
2129
  *
@@ -2066,10 +2213,13 @@ export function parsePixiLockFile(pixiLockFileName, path) {
2066
2213
  *
2067
2214
  */
2068
2215
  const purlTemplate = createPurlTemplate(packageData);
2216
+ const distributionUrl = pixiDistributionUrl(packageData["url"]);
2069
2217
  return {
2070
2218
  name: packageData["name"],
2071
2219
  version: packageData["version"],
2072
2220
  ...(purlTemplate ? { purl: purlTemplate } : {}),
2221
+ // The package download url becomes a distribution external reference.
2222
+ ...(distributionUrl ? { distribution: { url: distributionUrl } } : {}),
2073
2223
  type: "library",
2074
2224
  "bom-ref": createPixiBomRef(packageData),
2075
2225
  // "licenses": [
@@ -2077,10 +2227,8 @@ export function parsePixiLockFile(pixiLockFileName, path) {
2077
2227
  // "id": packageData["license"]
2078
2228
  // }]
2079
2229
  // ],
2080
- supplier: {
2081
- name: packageData["build"],
2082
- url: packageData["url"],
2083
- },
2230
+ // The conda build string is not a supplier, so it stays in
2231
+ // cdx:pixi:build below.
2084
2232
  // "hashes": [
2085
2233
  // {"md5": packageData["md5"]},
2086
2234
  // {"sha256": packageData["sha256"]}