@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
@@ -4,19 +4,26 @@ import path, {
4
4
  sep as _sep,
5
5
  basename,
6
6
  dirname,
7
+ isAbsolute,
7
8
  join,
8
9
  relative,
9
10
  resolve,
11
+ win32,
10
12
  } from "node:path";
11
13
 
12
14
  import { build, Purl } from "@cdxgen/cdx-purl";
13
15
  import ssri from "ssri";
14
16
  import { parse as _load, parseAllDocuments } from "yaml";
15
17
 
16
- import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
18
+ import {
19
+ DEBUG_MODE,
20
+ isSecureMode,
21
+ readEnvironmentVariable,
22
+ } from "../core/activity.js";
17
23
  import { recordDegradation } from "../core/buildLedger.js";
18
24
  import { shouldFetchPackageMetadata } from "../core/env.js";
19
25
  import { safeExistsSync } from "../core/fs.js";
26
+ import { isPathInsideProject } from "../core/lockPaths.js";
20
27
  import { analyzeSuspiciousJsFile } from "../inventory/analyzer.js";
21
28
  import { npmPurl, parsePurlLoose } from "../inventory/purl.js";
22
29
  import Arborist from "../third-party/arborist/lib/index.js";
@@ -864,6 +871,95 @@ function undoPackageExtensions(tree) {
864
871
  }
865
872
  }
866
873
 
874
+ /**
875
+ * True for a home-relative path such as `~/pkg`; a `~1.2.3` range is not one.
876
+ *
877
+ * @param {string} value Path or specifier
878
+ *
879
+ * @returns {Boolean} True when the value starts with `~/` or `~\`, or is `~`
880
+ */
881
+ function isHomeRelativePath(value) {
882
+ return value === "~" || value.startsWith("~/") || value.startsWith("~\\");
883
+ }
884
+
885
+ /**
886
+ * Extract the filesystem path a lockfile `version` or `resolved` value names,
887
+ * if any. `file:` and `link:` specifiers and bare relative or absolute paths
888
+ * are paths; registry and git URLs, other scheme-bearing specifiers such as
889
+ * `npm:` aliases, and plain versions are not.
890
+ *
891
+ * @param {string} value Lockfile `version` or `resolved` value
892
+ *
893
+ * @returns {string|undefined} The path, or undefined when the value is not one
894
+ */
895
+ function lockfileValuePath(value) {
896
+ if (typeof value !== "string" || !value) {
897
+ return undefined;
898
+ }
899
+ if (value.startsWith("file:") || value.startsWith("link:")) {
900
+ return value.slice(5);
901
+ }
902
+ if (
903
+ value.startsWith(".") ||
904
+ value.startsWith("/") ||
905
+ value.startsWith("\\") ||
906
+ isHomeRelativePath(value) ||
907
+ value.includes("\0") ||
908
+ win32.isAbsolute(value)
909
+ ) {
910
+ return value;
911
+ }
912
+ return undefined;
913
+ }
914
+
915
+ /**
916
+ * Identify a lockfile node whose `version`, `resolved`, or link target names a
917
+ * path outside the project root (issue #3816). A crafted package-lock.json
918
+ * can use such values to point at arbitrary directories and to put traversal
919
+ * strings into purls; a legitimate one uses them for sibling `file:` and
920
+ * `npm link` dependencies.
921
+ *
922
+ * Arborist reports a link's `resolved` relative to the folder holding the
923
+ * link, so links are judged by their absolute `realpath`. Other nodes carry
924
+ * `resolved` already absolutised against the lockfile, and a path-valued
925
+ * `version` is written relative to the lockfile, so the lockfile directory is
926
+ * their only base.
927
+ *
928
+ * @param {Object} node Arborist node from the virtual tree
929
+ * @param {string} projectRoot Directory the lockfile paths are relative to
930
+ *
931
+ * @returns {string|undefined} The offending path, relative to projectRoot when
932
+ * it was absolute, or undefined when the node stays inside.
933
+ */
934
+ function findOutsideNpmNodePath(node, projectRoot) {
935
+ const pathValues = [];
936
+ if (node?.isLink) {
937
+ if (typeof node.realpath === "string" && node.realpath) {
938
+ pathValues.push(node.realpath);
939
+ }
940
+ } else {
941
+ pathValues.push(lockfileValuePath(node?.resolved));
942
+ }
943
+ pathValues.push(lockfileValuePath(node?.version));
944
+ for (const pathValue of pathValues) {
945
+ if (pathValue === undefined) {
946
+ continue;
947
+ }
948
+ // `~` expands to the home directory, which never sits inside the project,
949
+ // and a Windows drive or UNC path read on POSIX names a foreign root.
950
+ if (
951
+ isHomeRelativePath(pathValue) ||
952
+ (win32.isAbsolute(pathValue) && !isAbsolute(pathValue)) ||
953
+ !isPathInsideProject(pathValue, projectRoot)
954
+ ) {
955
+ return isAbsolute(pathValue)
956
+ ? relative(projectRoot, pathValue)
957
+ : pathValue;
958
+ }
959
+ }
960
+ return undefined;
961
+ }
962
+
867
963
  /**
868
964
  * Parse nodejs package lock file
869
965
  *
@@ -880,6 +976,33 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
880
976
  options.projectRoot || dirname(pkgLockFile),
881
977
  );
882
978
  const pkgSpecVersionCache = {};
979
+ // Nodes whose lockfile paths point outside the project (issue #3816),
980
+ // reported once per lockfile after the walk instead of once per entry. In
981
+ // secure mode they are omitted together with the edges into them; otherwise
982
+ // they are kept, since sibling `file:` and `npm link` dependencies are
983
+ // legitimate.
984
+ const outsidePathEntries = [];
985
+ const outsidePathByNode = new Map();
986
+ const projectRoot = dirname(resolve(pkgLockFile));
987
+ const outsideNpmNodePath = (node, rootNode) => {
988
+ if (!node || node === rootNode) {
989
+ return undefined;
990
+ }
991
+ if (!outsidePathByNode.has(node)) {
992
+ const value = findOutsideNpmNodePath(node, projectRoot);
993
+ outsidePathByNode.set(node, value);
994
+ if (value) {
995
+ outsidePathEntries.push({
996
+ name: node.packageName || node.name || node.location,
997
+ value,
998
+ });
999
+ }
1000
+ }
1001
+ return outsidePathByNode.get(node);
1002
+ };
1003
+ // Every node is checked, so the default mode can still report what it kept.
1004
+ const isOmittedNpmNode = (node, rootNode) =>
1005
+ Boolean(outsideNpmNodePath(node, rootNode)) && isSecureMode;
883
1006
  if (!safeExistsSync(pkgLockFile)) {
884
1007
  return {
885
1008
  pkgList,
@@ -899,6 +1022,11 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
899
1022
  return { pkgList: [], dependenciesList: [] };
900
1023
  }
901
1024
  visited.add(node);
1025
+ // A dependency whose lockfile paths point outside the project is omitted
1026
+ // in secure mode (issue #3816).
1027
+ if (isOmittedNpmNode(node, rootNode)) {
1028
+ return { pkgList: [], dependenciesList: [] };
1029
+ }
902
1030
  let pkgList = [];
903
1031
  let dependenciesList = [];
904
1032
 
@@ -1315,6 +1443,9 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
1315
1443
  const workspaceDependsOn = [];
1316
1444
  if (node.fsChildren && node.fsChildren.size > 0) {
1317
1445
  for (const workspaceNode of node.fsChildren) {
1446
+ if (isOmittedNpmNode(workspaceNode, rootNode)) {
1447
+ continue;
1448
+ }
1318
1449
  const {
1319
1450
  pkgList: childPkgList,
1320
1451
  dependenciesList: childDependenciesList,
@@ -1366,6 +1497,9 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
1366
1497
  if (node !== rootNode && !node.edgesOut.size) {
1367
1498
  for (const child of node.children) {
1368
1499
  const childNode = child[1];
1500
+ if (isOmittedNpmNode(childNode, rootNode)) {
1501
+ continue;
1502
+ }
1369
1503
  const {
1370
1504
  pkgList: childPkgList,
1371
1505
  dependenciesList: childDependenciesList,
@@ -1398,6 +1532,9 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
1398
1532
  // this handles the case when a node has a ["requires"] key
1399
1533
  const pkgDependsOn = [];
1400
1534
  for (const edge of node.edgesOut.values()) {
1535
+ if (isOmittedNpmNode(edge.to, rootNode)) {
1536
+ continue;
1537
+ }
1401
1538
  let targetVersion;
1402
1539
  let targetName;
1403
1540
  let foundMatch = false;
@@ -1666,6 +1803,26 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
1666
1803
  options,
1667
1804
  ));
1668
1805
 
1806
+ if (outsidePathEntries.length) {
1807
+ const first = outsidePathEntries[0];
1808
+ if (isSecureMode) {
1809
+ console.warn(
1810
+ `WARNING: Omitted ${outsidePathEntries.length} entries of ${pkgLockFile} whose file or link paths point outside the project root; first entry "${first.name}" (${first.value}).`,
1811
+ );
1812
+ recordDegradation("js.lockfile-unsafe-path", {
1813
+ ecosystem: "npm",
1814
+ tool: "npm",
1815
+ impact: "transitive-deps",
1816
+ path: pkgLockFile,
1817
+ detail: `The lockfile carries ${outsidePathEntries.length} entries with file or link paths outside the project root; secure mode omits them.`,
1818
+ });
1819
+ } else {
1820
+ console.warn(
1821
+ `WARNING: ${pkgLockFile} has ${outsidePathEntries.length} entries whose file or link paths point outside the project root; first entry "${first.name}" (${first.value}). They are kept; secure mode omits them.`,
1822
+ );
1823
+ }
1824
+ }
1825
+
1669
1826
  if (shouldFetchPackageMetadata() && pkgList?.length) {
1670
1827
  if (DEBUG_MODE) {
1671
1828
  console.log(
@@ -1721,12 +1878,23 @@ export function yarnLockToIdentMap(lockData) {
1721
1878
  // - npm:^6.0.0
1722
1879
  // - npm:@types/ioredis@^4.28.10
1723
1880
  // - npm:strip-ansi@^6.0.1
1881
+ // - npm:@example/left-lib
1724
1882
  // See test cases with yarn3.lock and yarn6.lock
1725
1883
  if (range?.startsWith("npm:")) {
1726
- if (range.includes("@")) {
1727
- range = range.split("@").slice(-1)[0];
1884
+ // The target of an npm: alias is `name[@range]`, so split at the
1885
+ // last "@" beyond index 0 and a scope is never mistaken for a
1886
+ // version separator. A bare scoped target names a package with
1887
+ // no version; "*" is the placeholder the version lookup uses for
1888
+ // such targets. A bare unscoped target is a tag or range of the
1889
+ // declared package ("npm:latest"), never an alias.
1890
+ const spec = range.slice("npm:".length);
1891
+ const sepIdx = spec.lastIndexOf("@");
1892
+ if (sepIdx > 0) {
1893
+ range = spec.slice(sepIdx + 1);
1894
+ } else if (spec.startsWith("@")) {
1895
+ range = "*";
1728
1896
  } else {
1729
- range = range.replace("npm:", "");
1897
+ range = spec;
1730
1898
  }
1731
1899
  if (
1732
1900
  !l.includes(" ") &&
@@ -1796,6 +1964,13 @@ function _parseYarnLine(l) {
1796
1964
  if (aliasSepIdx > -1) {
1797
1965
  // Aliased dependency: use the real package name from the target.
1798
1966
  fullName = target.slice(0, aliasSepIdx);
1967
+ } else if (target.startsWith("@")) {
1968
+ // A bare scoped target names a package with no version, so the alias
1969
+ // points at the target itself; a key line closes with ":".
1970
+ const bareName = target.endsWith(":") ? target.slice(0, -1) : target;
1971
+ if (bareName) {
1972
+ fullName = bareName;
1973
+ }
1799
1974
  }
1800
1975
  }
1801
1976
  }
@@ -1823,13 +1998,22 @@ function _resolveYarnDependencyBomRef(
1823
1998
  let packageNameToUse = packageName;
1824
1999
  let versionRangeToUse = versionRange;
1825
2000
  if (versionRange.startsWith("npm:")) {
1826
- if (versionRange.includes("@")) {
1827
- versionRangeToUse = versionRange.split("@").splice(-1)[0];
1828
- packageNameToUse = versionRange
1829
- .replace("npm:", "")
1830
- .replace(`@${versionRangeToUse}`, "");
2001
+ // The target of an npm: alias is `name[@range]`, so split at the last "@"
2002
+ // beyond index 0 and a scope is never mistaken for a version separator.
2003
+ const spec = versionRange.slice("npm:".length);
2004
+ const sepIdx = spec.lastIndexOf("@");
2005
+ if (sepIdx > 0) {
2006
+ packageNameToUse = spec.slice(0, sepIdx);
2007
+ versionRangeToUse = spec.slice(sepIdx + 1);
2008
+ } else if (spec.startsWith("@")) {
2009
+ // A bare scoped target names a package with no version; the version is
2010
+ // looked up from identMap below.
2011
+ packageNameToUse = spec;
2012
+ versionRangeToUse = "*";
1831
2013
  } else {
1832
- versionRangeToUse = versionRange.replace("npm:", "");
2014
+ // A bare unscoped target is a tag or range of the declared package
2015
+ // ("npm:latest"), never an alias.
2016
+ versionRangeToUse = spec;
1833
2017
  }
1834
2018
  }
1835
2019
  let resolvedVersion =
@@ -3178,6 +3362,126 @@ export function parsePnpmAliasRef(value) {
3178
3362
  };
3179
3363
  }
3180
3364
 
3365
+ /**
3366
+ * Read the patch file paths that pnpm applies, keyed by name@version.
3367
+ *
3368
+ * The lockfile records only the hash of each patch. The paths live in the
3369
+ * `patchedDependencies` of pnpm-workspace.yaml, or in the `pnpm` field of
3370
+ * package.json for projects that predate pnpm-workspace.yaml settings.
3371
+ *
3372
+ * @param {string} projectDir Directory holding the lockfile
3373
+ *
3374
+ * @returns {Object} Patch file path by name@version
3375
+ */
3376
+ function readPnpmPatchFiles(projectDir) {
3377
+ const patchFiles = {};
3378
+ const sources = [
3379
+ ["pnpm-workspace.yaml", (data) => _load(data)?.patchedDependencies],
3380
+ ["package.json", (data) => JSON.parse(data)?.pnpm?.patchedDependencies],
3381
+ ];
3382
+ for (const [fileName, extract] of sources) {
3383
+ const sourceFile = join(projectDir, fileName);
3384
+ if (!safeExistsSync(sourceFile)) {
3385
+ continue;
3386
+ }
3387
+ try {
3388
+ for (const [patchKey, patchValue] of Object.entries(
3389
+ extract(readFileSync(sourceFile, "utf-8")) || {},
3390
+ )) {
3391
+ if (
3392
+ typeof patchValue === "string" &&
3393
+ patchValue &&
3394
+ !patchFiles[patchKey]
3395
+ ) {
3396
+ patchFiles[patchKey] = patchValue;
3397
+ }
3398
+ }
3399
+ } catch (_e) {
3400
+ // A malformed settings file must not break the lockfile parse
3401
+ }
3402
+ }
3403
+ return patchFiles;
3404
+ }
3405
+
3406
+ /**
3407
+ * Decide whether a parsed pnpm lockfile document is a boot document.
3408
+ *
3409
+ * Since pnpm 12 a lockfile can hold two YAML documents. The first, the boot
3410
+ * document, records the project's config dependencies and the resolved pnpm
3411
+ * version under its importers, and the last is the project lockfile. The
3412
+ * boot document is recognised by these keys rather than by its position
3413
+ * alone, and tolerates either key missing.
3414
+ *
3415
+ * See: https://pnpm.io/lockfile
3416
+ *
3417
+ * @param {Object} docObj Parsed YAML document
3418
+ *
3419
+ * @returns {boolean} true when the document looks like a boot document
3420
+ */
3421
+ export function isPnpmBootDocument(docObj) {
3422
+ const importers = docObj?.importers;
3423
+ if (!importers || typeof importers !== "object") {
3424
+ return false;
3425
+ }
3426
+ for (const importer of Object.values(importers)) {
3427
+ if (
3428
+ importer &&
3429
+ typeof importer === "object" &&
3430
+ ("configDependencies" in importer ||
3431
+ "packageManagerDependencies" in importer)
3432
+ ) {
3433
+ return true;
3434
+ }
3435
+ }
3436
+ return false;
3437
+ }
3438
+
3439
+ /**
3440
+ * Find which pnpm catalog pinned each dependency of the workspace.
3441
+ *
3442
+ * A lockfile importer records the specifier next to the resolved version,
3443
+ * and a specifier of `catalog:` or `catalog:<name>` means the version came
3444
+ * from the default or the named catalog of pnpm-workspace.yaml. The lockfile
3445
+ * is enough, so the workspace file is not read.
3446
+ *
3447
+ * @param {Object} importers The importers section of a pnpm lockfile
3448
+ *
3449
+ * @returns {Object} Sorted catalog names, keyed by name@version
3450
+ */
3451
+ function collectPnpmCatalogUsage(importers) {
3452
+ const usage = {};
3453
+ if (!importers || typeof importers !== "object") {
3454
+ return {};
3455
+ }
3456
+ for (const importer of Object.values(importers)) {
3457
+ for (const section of [
3458
+ "dependencies",
3459
+ "devDependencies",
3460
+ "optionalDependencies",
3461
+ ]) {
3462
+ for (const [depName, depEntry] of Object.entries(
3463
+ importer?.[section] || {},
3464
+ )) {
3465
+ const specifier = depEntry?.specifier;
3466
+ if (
3467
+ typeof specifier !== "string" ||
3468
+ !specifier.startsWith("catalog:") ||
3469
+ typeof depEntry.version !== "string"
3470
+ ) {
3471
+ continue;
3472
+ }
3473
+ const catalogName = specifier.slice("catalog:".length).trim();
3474
+ const key = `${depName}@${depEntry.version.split("(")[0]}`;
3475
+ usage[key] = usage[key] || new Set();
3476
+ usage[key].add(catalogName || "default");
3477
+ }
3478
+ }
3479
+ }
3480
+ return Object.fromEntries(
3481
+ Object.entries(usage).map(([key, names]) => [key, [...names].sort()]),
3482
+ );
3483
+ }
3484
+
3181
3485
  /**
3182
3486
  * Parse nodejs pnpm lock file
3183
3487
  *
@@ -3185,7 +3489,6 @@ export function parsePnpmAliasRef(value) {
3185
3489
  * @param {Object} parentComponent parent component
3186
3490
  * @param {Array[String]} workspacePackages Workspace packages
3187
3491
  * @param {Object} workspaceSrcFiles Workspace package.json files
3188
- * @param {Object} _workspaceCatalogs Workspace catalogs
3189
3492
  * @param {Object} _workspaceDirectDeps Direct dependencies of each workspace
3190
3493
  * @param {Object} depsWorkspaceRefs Workspace references for each dependency
3191
3494
  * @param {string} projectRoot Root path used to relativize pnpm-lock evidence paths
@@ -3195,13 +3498,13 @@ export async function parsePnpmLock(
3195
3498
  parentComponent = null,
3196
3499
  workspacePackages = [],
3197
3500
  workspaceSrcFiles = {},
3198
- _workspaceCatalogs = {},
3199
3501
  _workspaceDirectDeps = {},
3200
3502
  depsWorkspaceRefs = {},
3201
3503
  projectRoot = undefined,
3202
3504
  ) {
3203
3505
  let pkgList = [];
3204
3506
  const dependenciesList = [];
3507
+ const formulationList = [];
3205
3508
  const pnpmLockDir = dirname(pnpmLock);
3206
3509
  const pnpmEvidenceRoot = projectRoot ? resolve(projectRoot) : pnpmLockDir;
3207
3510
  const npmrcConfig = loadNpmrcConfig(projectRoot || pnpmLockDir);
@@ -3245,13 +3548,15 @@ export async function parsePnpmLock(
3245
3548
  }
3246
3549
  if (safeExistsSync(pnpmLock)) {
3247
3550
  const lockData = readFileSync(pnpmLock, "utf8");
3248
- let yamlObj = parseAllDocuments(lockData);
3249
- if (!yamlObj) {
3551
+ const yamlDocs = parseAllDocuments(lockData);
3552
+ if (!yamlDocs) {
3250
3553
  return {};
3251
3554
  }
3252
- if (Array.isArray(yamlObj)) {
3555
+ let yamlObj = yamlDocs;
3556
+ if (Array.isArray(yamlDocs)) {
3253
3557
  try {
3254
- yamlObj = yamlObj[yamlObj.length - 1].toJS();
3558
+ // The project lockfile is always the last document
3559
+ yamlObj = yamlDocs[yamlDocs.length - 1].toJS();
3255
3560
  } catch (_e) {
3256
3561
  recordDegradation("js.pnpm-lock-unparseable", {
3257
3562
  ecosystem: "npm",
@@ -3265,12 +3570,44 @@ export async function parsePnpmLock(
3265
3570
  return {};
3266
3571
  }
3267
3572
  }
3573
+ // Earlier documents can hold a boot document with the project's config
3574
+ // dependencies and the resolved pnpm version. It is detected by shape,
3575
+ // and a malformed document must not break the project parse.
3576
+ let bootDocObj = null;
3577
+ if (Array.isArray(yamlDocs) && yamlDocs.length > 1) {
3578
+ for (const yamlDoc of yamlDocs.slice(0, -1)) {
3579
+ let docObj;
3580
+ try {
3581
+ docObj = yamlDoc.toJS();
3582
+ } catch (_e) {
3583
+ continue;
3584
+ }
3585
+ if (isPnpmBootDocument(docObj)) {
3586
+ bootDocObj = docObj;
3587
+ break;
3588
+ }
3589
+ }
3590
+ }
3268
3591
  lockfileVersion = yamlObj.lockfileVersion;
3269
3592
  try {
3270
3593
  lockfileVersion = Number.parseFloat(lockfileVersion, 10);
3271
3594
  } catch (_e) {
3272
3595
  // ignore parse errors
3273
3596
  }
3597
+ // The lockfile's patchedDependencies maps name@version to a hash of the
3598
+ // patch file; the patch file path itself lives in pnpm-workspace.yaml.
3599
+ const patchedDependencies = {};
3600
+ for (const [patchKey, patchValue] of Object.entries(
3601
+ yamlObj.patchedDependencies || {},
3602
+ )) {
3603
+ if (typeof patchValue === "string" && patchValue) {
3604
+ patchedDependencies[patchKey] = patchValue;
3605
+ }
3606
+ }
3607
+ const patchFiles = Object.keys(patchedDependencies).length
3608
+ ? readPnpmPatchFiles(pnpmLockDir)
3609
+ : {};
3610
+ const catalogUsage = collectPnpmCatalogUsage(yamlObj.importers);
3274
3611
  Object.assign(
3275
3612
  gitPkgRefs,
3276
3613
  buildPnpmGitPkgRefs(
@@ -3346,6 +3683,11 @@ export async function parsePnpmLock(
3346
3683
  githubServerHost,
3347
3684
  npmrcConfig,
3348
3685
  );
3686
+ // Dev dependencies are still edges from the root: a monorepo root often
3687
+ // declares only devDependencies (the build tooling), and without this
3688
+ // edge the root component under-reports what the install pulls in.
3689
+ // See #2020.
3690
+ ddeplist.add(dpurl);
3349
3691
  possibleDevelopmentDeps[dpurl] = true;
3350
3692
  }
3351
3693
  // Find the root optional and peer dependencies
@@ -3414,6 +3756,15 @@ export async function parsePnpmLock(
3414
3756
  {};
3415
3757
  const componentPeerDeps =
3416
3758
  yamlObj?.importers[importedComponentName]["peerDependencies"] || {};
3759
+ if (
3760
+ DEBUG_MODE &&
3761
+ !Object.keys(componentDeps).length &&
3762
+ Object.keys(componentDevDeps).length
3763
+ ) {
3764
+ console.log(
3765
+ `The workspace ${importedComponentName} declares only devDependencies, so its dependency tree is scoped as development. --required-only will exclude these components.`,
3766
+ );
3767
+ }
3417
3768
  let compPurl;
3418
3769
  let pkgSrcFile;
3419
3770
  let fallbackMode = true;
@@ -3915,6 +4266,35 @@ export async function parsePnpmLock(
3915
4266
  value: deprecatedMessage,
3916
4267
  });
3917
4268
  }
4269
+ const patchKey = `${group ? `${group}/${name}` : name}@${version}`;
4270
+ const patchHash = patchedDependencies[patchKey];
4271
+ if (patchHash) {
4272
+ // The component keeps its upstream purl, so vulnerability matching
4273
+ // sees the published version the patch was applied to. See #803.
4274
+ properties.push({
4275
+ name: "cdx:pnpm:patched",
4276
+ value: "true",
4277
+ });
4278
+ properties.push({
4279
+ name: "cdx:pnpm:patchHash",
4280
+ value: patchHash,
4281
+ });
4282
+ if (patchFiles[patchKey]) {
4283
+ properties.push({
4284
+ name: "cdx:pnpm:patchFile",
4285
+ value: patchFiles[patchKey],
4286
+ });
4287
+ }
4288
+ }
4289
+ const catalogNames = catalogUsage[patchKey];
4290
+ if (catalogNames?.length) {
4291
+ // The version is pinned by a catalog entry in pnpm-workspace.yaml
4292
+ // and not by the importer's own package.json. See #4611.
4293
+ properties.push({
4294
+ name: "cdx:pnpm:catalog",
4295
+ value: catalogNames.join(","),
4296
+ });
4297
+ }
3918
4298
  const binary_metadata = { os, cpu, libc };
3919
4299
  Object.entries(binary_metadata).forEach(([key, value]) => {
3920
4300
  if (!value) return;
@@ -4045,6 +4425,108 @@ export async function parsePnpmLock(
4045
4425
  }
4046
4426
  }
4047
4427
  }
4428
+ // Config dependencies and the resolved pnpm version live in the boot
4429
+ // document. pnpm installs config dependencies before anything else and
4430
+ // they may run hooks, so they are reported as components even though
4431
+ // they are not part of the project dependency graph.
4432
+ if (bootDocObj) {
4433
+ const bootImporter = bootDocObj.importers?.["."];
4434
+ const configDeps = bootImporter?.configDependencies;
4435
+ if (configDeps && typeof configDeps === "object") {
4436
+ for (const [depName, depEntry] of Object.entries(configDeps)) {
4437
+ const version =
4438
+ typeof depEntry === "string" ? depEntry : depEntry?.version;
4439
+ if (!depName || !version || protoLockKey.test(depName)) {
4440
+ continue;
4441
+ }
4442
+ let group = "";
4443
+ let name = depName;
4444
+ const slashIdx = name.indexOf("/");
4445
+ if (name.startsWith("@") && slashIdx > 0) {
4446
+ group = name.slice(0, slashIdx);
4447
+ name = name.slice(slashIdx + 1);
4448
+ }
4449
+ const purlString = build({
4450
+ type: "npm",
4451
+ namespace: group || null,
4452
+ name,
4453
+ version,
4454
+ });
4455
+ const bomRef = decodeURIComponent(purlString);
4456
+ const bootLockKey = `${depName}@${version}`;
4457
+ const bootEntry =
4458
+ bootDocObj.packages?.[bootLockKey] ||
4459
+ bootDocObj.snapshots?.[bootLockKey];
4460
+ const existingPkg = pkgRefMap[bomRef];
4461
+ if (existingPkg) {
4462
+ // The package is also a runtime dependency of the project, so
4463
+ // keep that component and record the additional config role.
4464
+ existingPkg.properties = existingPkg.properties || [];
4465
+ if (
4466
+ !existingPkg.properties.some(
4467
+ (p) => p.name === "cdx:pnpm:configDependency",
4468
+ )
4469
+ ) {
4470
+ existingPkg.properties.push({
4471
+ name: "cdx:pnpm:configDependency",
4472
+ value: "true",
4473
+ });
4474
+ }
4475
+ } else {
4476
+ const thePkg = {
4477
+ group,
4478
+ name,
4479
+ version,
4480
+ purl: purlString,
4481
+ "bom-ref": bomRef,
4482
+ type: "library",
4483
+ _integrity: bootEntry?.resolution?.integrity,
4484
+ properties: [
4485
+ { name: "internal:SrcFile", value: pnpmLockSrcFile },
4486
+ { name: "cdx:pnpm:configDependency", value: "true" },
4487
+ ],
4488
+ evidence: {
4489
+ identity: {
4490
+ field: "purl",
4491
+ confidence: 1,
4492
+ methods: [
4493
+ {
4494
+ technique: "manifest-analysis",
4495
+ confidence: 1,
4496
+ value: pnpmLockSrcFile,
4497
+ },
4498
+ ],
4499
+ },
4500
+ },
4501
+ };
4502
+ pkgList.push(thePkg);
4503
+ pkgRefMap[bomRef] = thePkg;
4504
+ }
4505
+ }
4506
+ }
4507
+ const pnpmVersion =
4508
+ bootImporter?.packageManagerDependencies?.pnpm?.version;
4509
+ if (pnpmVersion) {
4510
+ // The pnpm version resolved for this project belongs next to the
4511
+ // other build tools in the formulation section. Like the toolchain
4512
+ // components there, it carries no bom-ref so that it cannot collide
4513
+ // with the document's bom-ref namespace.
4514
+ formulationList.push({
4515
+ type: "application",
4516
+ name: "pnpm",
4517
+ version: pnpmVersion,
4518
+ purl: build({
4519
+ type: "npm",
4520
+ namespace: null,
4521
+ name: "pnpm",
4522
+ version: pnpmVersion,
4523
+ }),
4524
+ description:
4525
+ "pnpm version resolved for this project, read from the lockfile boot document.",
4526
+ properties: [{ name: "internal:SrcFile", value: pnpmLockSrcFile }],
4527
+ });
4528
+ }
4529
+ }
4048
4530
  }
4049
4531
  // We need to repeat optional packages detection
4050
4532
  if (Object.keys(possibleOptionalDeps).length) {
@@ -4192,17 +4674,18 @@ export async function parsePnpmLock(
4192
4674
  );
4193
4675
  }
4194
4676
  pkgList = await getNpmMetadata(pkgList, undefined, { npmrcConfig });
4195
- return {
4196
- pkgList,
4197
- dependenciesList,
4198
- parentSubComponents,
4199
- };
4200
4677
  }
4201
- return {
4678
+ const result = {
4202
4679
  pkgList,
4203
4680
  dependenciesList,
4204
4681
  parentSubComponents,
4205
4682
  };
4683
+ // Reported only when the boot document contributed, so that lockfiles
4684
+ // without one keep producing the result shape they always did.
4685
+ if (formulationList.length) {
4686
+ result.formulationList = formulationList;
4687
+ }
4688
+ return result;
4206
4689
  }
4207
4690
 
4208
4691
  /**