@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
package/lib/core/fs.js CHANGED
@@ -43,6 +43,7 @@ import {
43
43
  } from "./activity.js";
44
44
  import { thoughtLog, traceLog } from "./logger.js";
45
45
  import { isWin } from "./paths.js";
46
+ import { recordExecutedTool } from "./toolRegistry.js";
46
47
 
47
48
  function getArchiveSourceByteSize(sourcePath) {
48
49
  if (!sourcePath || !safeExistsSync(sourcePath)) {
@@ -139,10 +140,126 @@ export function safeIsExecutableSync(filePath) {
139
140
  }
140
141
  }
141
142
 
143
+ // writeFileSync flags that create or replace a file, which the no-follow
144
+ // write below reproduces with open(2) flags.
145
+ const NO_FOLLOW_WRITE_FLAGS = new Set([
146
+ "a",
147
+ "a+",
148
+ "as",
149
+ "as+",
150
+ "ax",
151
+ "ax+",
152
+ "w",
153
+ "w+",
154
+ "wx",
155
+ "wx+",
156
+ ]);
157
+
158
+ /**
159
+ * Determine whether a path names a device or a process file, such as
160
+ * /dev/stdout, /dev/null or the /dev/fd entry of a shell process
161
+ * substitution. These are symbolic links an operator names on purpose, so
162
+ * they are written through as given.
163
+ *
164
+ * @param {string} filePath File path to check.
165
+ * @returns {boolean} True for paths under /dev or /proc on POSIX systems.
166
+ */
167
+ function isDeviceOrProcessPath(filePath) {
168
+ if (isWin) {
169
+ return false;
170
+ }
171
+ const absolutePath = resolve(filePath);
172
+ return absolutePath.startsWith("/dev/") || absolutePath.startsWith("/proc/");
173
+ }
174
+
175
+ /**
176
+ * Convert a writeFileSync flag string into open(2) flags.
177
+ *
178
+ * @param {string} flag Flag such as "w", "a" or "wx".
179
+ * @returns {number} Numeric open flags.
180
+ */
181
+ function writeOpenFlags(flag) {
182
+ let flags =
183
+ fsConstants.O_CREAT |
184
+ (flag.includes("+") ? fsConstants.O_RDWR : fsConstants.O_WRONLY) |
185
+ (flag.startsWith("a") ? fsConstants.O_APPEND : fsConstants.O_TRUNC);
186
+ if (flag.includes("x")) {
187
+ flags |= fsConstants.O_EXCL;
188
+ }
189
+ return flags;
190
+ }
191
+
192
+ /**
193
+ * Write a file without following a symbolic link in the last component of
194
+ * its path. A link found there is removed and a regular file is created in
195
+ * its place, so a link committed to a scanned repository under a name cdxgen
196
+ * writes, such as bom.json, cannot send the write to the file it points at.
197
+ * The open uses O_NOFOLLOW where the platform has it, so a link that appears
198
+ * after the check makes the write fail rather than follow it. Links in the
199
+ * parent directories are followed as usual.
200
+ *
201
+ * @param {string} filePath File path to write.
202
+ * @param {string|Buffer|Uint8Array} data Data to write.
203
+ * @param {Object|string} [options] writeFileSync options or encoding.
204
+ * @returns {boolean} True when a symbolic link was replaced.
205
+ */
206
+ function writeFileWithoutFollowingLink(filePath, data, options) {
207
+ const flag =
208
+ (typeof options === "object" && options?.flag
209
+ ? String(options.flag)
210
+ : undefined) || "w";
211
+ if (
212
+ typeof filePath !== "string" ||
213
+ !NO_FOLLOW_WRITE_FLAGS.has(flag) ||
214
+ isDeviceOrProcessPath(filePath)
215
+ ) {
216
+ writeFileSync(filePath, data, options);
217
+ return false;
218
+ }
219
+ let replacedLink = false;
220
+ let pathStats;
221
+ try {
222
+ pathStats = lstatSync(filePath);
223
+ } catch {
224
+ pathStats = undefined;
225
+ }
226
+ if (pathStats?.isSymbolicLink()) {
227
+ // rmSync removes the link itself, including a directory link on Windows.
228
+ rmSync(filePath, { force: true });
229
+ replacedLink = true;
230
+ }
231
+ const openFlags =
232
+ writeOpenFlags(flag) |
233
+ (fsConstants.O_NOFOLLOW || 0) |
234
+ (replacedLink ? fsConstants.O_EXCL : 0);
235
+ const mode =
236
+ typeof options === "object" && options?.mode !== undefined
237
+ ? options.mode
238
+ : 0o666;
239
+ const fd = openSync(filePath, openFlags, mode);
240
+ try {
241
+ writeFileSync(fd, data, {
242
+ encoding: typeof options === "string" ? options : options?.encoding,
243
+ });
244
+ } finally {
245
+ closeSync(fd);
246
+ }
247
+ if (replacedLink) {
248
+ console.warn(
249
+ `'${filePath}' was a symbolic link. cdxgen replaced the link with a regular file instead of writing to the file it pointed at.`,
250
+ );
251
+ }
252
+ return replacedLink;
253
+ }
254
+
142
255
  /**
143
256
  * Permission- and dry-run-aware wrapper around writeFileSync. Records the
144
257
  * activity and returns undefined when blocked.
145
258
  *
259
+ * The last component of the path is never followed when it is a symbolic
260
+ * link: the link is replaced with a regular file. Paths under /dev and /proc,
261
+ * such as /dev/stdout, are written through as given.
262
+ *
146
263
  * @param {string} filePath File path to write.
147
264
  * @param {string|Buffer} data Data to write.
148
265
  * @param {Object} [options] writeFileSync options (encoding, mode, flag).
@@ -152,8 +269,15 @@ export function safeWriteSync(filePath, data, options) {
152
269
  if (isWriteBlocked(filePath)) {
153
270
  return undefined;
154
271
  }
155
- writeFileSync(filePath, data, options);
156
- recordFilesystemActivity("write", filePath, "completed");
272
+ const replacedLink = writeFileWithoutFollowingLink(filePath, data, options);
273
+ recordFilesystemActivity(
274
+ "write",
275
+ filePath,
276
+ "completed",
277
+ replacedLink
278
+ ? "Replaced a symbolic link with a regular file instead of following it."
279
+ : undefined,
280
+ );
157
281
  }
158
282
 
159
283
  /**
@@ -172,7 +296,7 @@ export function safeWriteChunksSync(filePath, chunks) {
172
296
  return undefined;
173
297
  }
174
298
  const tempPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
175
- const fd = openSync(tempPath, "w");
299
+ const fd = openSync(tempPath, "wx");
176
300
  try {
177
301
  for (const chunk of chunks) {
178
302
  const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
@@ -991,6 +1115,8 @@ export function safeSpawnSync(command, args, options) {
991
1115
  }
992
1116
  traceLog("spawn", { command, args, ...options });
993
1117
  commandsExecuted.add(command);
1118
+ // The tool as invoked, before a shell invocation folds the arguments in.
1119
+ const executedCommand = command;
994
1120
  // Fix for DEP0190 warning
995
1121
  if (options?.shell === true) {
996
1122
  const joined = joinShellCommand(command, args, rawShellArgs);
@@ -1000,6 +1126,15 @@ export function safeSpawnSync(command, args, options) {
1000
1126
  }
1001
1127
  }
1002
1128
  const result = spawnSync(command, args, options);
1129
+ // The run-scoped tool registry feeds metadata.tools (issue #598); every
1130
+ // external execution funnels through this wrapper. A spawn that failed to
1131
+ // start, such as a missing binary, ran nothing and is not recorded; through
1132
+ // a shell, a missing command shows as exit code 127, or 9009 on cmd.exe.
1133
+ const shellCommandNotFound =
1134
+ options?.shell === true && [127, 9009].includes(result.status);
1135
+ if (!result.error && !shellCommandNotFound) {
1136
+ recordExecutedTool(executedCommand);
1137
+ }
1003
1138
  recordActivity({
1004
1139
  kind: activityDescriptor.kind,
1005
1140
  ...activityDescriptor.metadata,
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Longest exclude or include pattern cdxgen accepts from an untrusted source,
3
+ * such as a server request or a config file in the scanned directory.
4
+ */
5
+ export const UNTRUSTED_GLOB_PATTERN_MAX_LENGTH = 512;
6
+
7
+ /**
8
+ * Most patterns a list of untrusted exclude or include patterns may stand
9
+ * for once brace expressions are expanded. Every expanded pattern is matched
10
+ * against every file the scan walks, so the total, not the length of the
11
+ * list as written, is what decides the cost.
12
+ */
13
+ export const UNTRUSTED_GLOB_EXPANSION_BUDGET = 128;
14
+
15
+ function isIntegerText(text) {
16
+ const digits = text.startsWith("-") ? text.slice(1) : text;
17
+ return (
18
+ digits.length > 0 && [...digits].every((char) => char >= "0" && char <= "9")
19
+ );
20
+ }
21
+
22
+ function isLetter(text) {
23
+ return (
24
+ text.length === 1 &&
25
+ ((text >= "a" && text <= "z") || (text >= "A" && text <= "Z"))
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Count the members of a brace range such as `1..10`, `a..z` or `0..100..5`.
31
+ *
32
+ * @param {string} body text between the braces
33
+ * @param {number} limit value returned for ranges at or beyond the limit
34
+ * @returns {number|undefined} member count, or undefined when the body is
35
+ * not a range
36
+ */
37
+ function braceRangeSize(body, limit) {
38
+ const parts = body.split("..");
39
+ if (parts.length !== 2 && parts.length !== 3) {
40
+ return undefined;
41
+ }
42
+ const [start, end, step = "1"] = parts;
43
+ if (!isIntegerText(step)) {
44
+ return undefined;
45
+ }
46
+ const increment = Math.abs(Number(step)) || 1;
47
+ let distance;
48
+ if (isIntegerText(start) && isIntegerText(end)) {
49
+ distance = Math.abs(Number(end) - Number(start));
50
+ } else if (isLetter(start) && isLetter(end)) {
51
+ distance = Math.abs(end.charCodeAt(0) - start.charCodeAt(0));
52
+ } else {
53
+ return undefined;
54
+ }
55
+ const size = Math.floor(distance / increment) + 1;
56
+ return Number.isFinite(size) && size < limit ? size : limit;
57
+ }
58
+
59
+ function newBraceGroup(start) {
60
+ return {
61
+ current: 1,
62
+ hasAnyComma: false,
63
+ hasComma: false,
64
+ hasDots: false,
65
+ hasGroup: false,
66
+ start,
67
+ sum: 0,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Count, from above, how many patterns a glob's brace expressions expand to,
73
+ * following the brace-expansion package that minimatch uses:
74
+ *
75
+ * - alternatives in `{a,b}` add up, and groups side by side or inside one
76
+ * another multiply;
77
+ * - a range such as `{1..9}`, `{a..z}` or `{0..100..5}` counts its members;
78
+ * - a group with no comma of its own but a list inside it, such as
79
+ * `{{a,b}c}`, is expanded and each result is braced and expanded again,
80
+ * so it counts as its contents, unless its text holds `..` and a result
81
+ * could become a range of any size.
82
+ *
83
+ * brace-expansion reads braces that form neither a list nor a range in ways
84
+ * that depend on the rest of the pattern, so a group that is neither, a
85
+ * closing brace with no group open, a group left open, and the `..` case
86
+ * above all count as more than `cap`. Patterns that list alternatives or
87
+ * ranges, such as `{test,tests}` or `*.{js,ts}`, get the exact count, or one
88
+ * slightly above it where a letter range such as `{Z..c}` spans the `\` that
89
+ * brace-expansion leaves out. `\` escapes the next character. The count stops growing at
90
+ * `cap + 1` and each character is read once, so a pattern that would expand
91
+ * to millions costs no more to count than its length.
92
+ *
93
+ * @param {string} pattern glob pattern
94
+ * @param {number} [cap] largest count of interest
95
+ * @returns {number} upper bound on the number of expanded patterns, at most
96
+ * `cap + 1`
97
+ */
98
+ export function globBraceExpansionCount(
99
+ pattern,
100
+ cap = Number.MAX_SAFE_INTEGER - 1,
101
+ ) {
102
+ const limit = cap + 1;
103
+ const clamp = (value) => Math.min(value, limit);
104
+ const groups = [newBraceGroup(0)];
105
+ const text = String(pattern);
106
+ for (let index = 0; index < text.length; index += 1) {
107
+ const character = text[index];
108
+ if (character === "\\") {
109
+ index += 1;
110
+ continue;
111
+ }
112
+ const group = groups.at(-1);
113
+ if (character === "{") {
114
+ group.hasGroup = true;
115
+ groups.push(newBraceGroup(index + 1));
116
+ } else if (character === "." && text[index + 1] === ".") {
117
+ group.hasDots = true;
118
+ } else if (character === "," && groups.length > 1) {
119
+ group.sum = clamp(group.sum + group.current);
120
+ group.current = 1;
121
+ group.hasComma = true;
122
+ group.hasAnyComma = true;
123
+ } else if (character === "}") {
124
+ if (groups.length === 1) {
125
+ return limit;
126
+ }
127
+ let count;
128
+ if (group.hasComma) {
129
+ count = clamp(group.sum + group.current);
130
+ } else if (group.hasAnyComma) {
131
+ count = group.hasDots ? limit : group.current;
132
+ } else if (!group.hasGroup) {
133
+ count = braceRangeSize(text.slice(group.start, index), limit);
134
+ }
135
+ if (count === undefined) {
136
+ return limit;
137
+ }
138
+ groups.pop();
139
+ const parent = groups.at(-1);
140
+ parent.current = clamp(parent.current * count);
141
+ parent.hasAnyComma ||= group.hasAnyComma;
142
+ parent.hasDots ||= group.hasDots;
143
+ }
144
+ }
145
+ return groups.length === 1 ? groups[0].current : limit;
146
+ }
147
+
148
+ /**
149
+ * Split exclude or include patterns from an untrusted source into the ones
150
+ * cdxgen uses and the ones it refuses. Patterns are taken in order while
151
+ * they fit the expansion budget, so a list cannot multiply the work of a
152
+ * scan by brace expansion or sheer number.
153
+ *
154
+ * @param {unknown} patterns a pattern, a list of patterns, or nothing
155
+ * @param {Object} [options] bound options
156
+ * @param {boolean} [options.allowBraces=true] refuse every pattern with a
157
+ * brace when false
158
+ * @param {number} [options.budget] expanded patterns allowed in all
159
+ * @param {number} [options.used=0] expanded patterns earlier lists have
160
+ * already taken from the budget
161
+ * @returns {{accepted: string[], refused: {pattern: string, reason: string}[], expansions: number}}
162
+ * the patterns to use, the ones refused with the reason, and the expanded
163
+ * patterns the accepted ones use
164
+ */
165
+ export function boundUntrustedGlobPatterns(
166
+ patterns,
167
+ {
168
+ allowBraces = true,
169
+ budget = UNTRUSTED_GLOB_EXPANSION_BUDGET,
170
+ used = 0,
171
+ } = {},
172
+ ) {
173
+ const entries =
174
+ patterns === undefined || patterns === null
175
+ ? []
176
+ : Array.isArray(patterns)
177
+ ? patterns
178
+ : [patterns];
179
+ const accepted = [];
180
+ const refused = [];
181
+ let expansions = 0;
182
+ for (const entry of entries) {
183
+ if (typeof entry !== "string" && typeof entry !== "number") {
184
+ refused.push({ pattern: String(entry), reason: "is not a string" });
185
+ continue;
186
+ }
187
+ const pattern = String(entry);
188
+ if (pattern.length > UNTRUSTED_GLOB_PATTERN_MAX_LENGTH) {
189
+ refused.push({
190
+ pattern,
191
+ reason: `is longer than ${UNTRUSTED_GLOB_PATTERN_MAX_LENGTH} characters`,
192
+ });
193
+ continue;
194
+ }
195
+ if (!allowBraces && pattern.includes("{")) {
196
+ refused.push({ pattern, reason: "uses braces" });
197
+ continue;
198
+ }
199
+ const count = globBraceExpansionCount(pattern, budget);
200
+ if (count > budget) {
201
+ refused.push({
202
+ pattern,
203
+ reason: `expands to more than ${budget} patterns`,
204
+ });
205
+ continue;
206
+ }
207
+ if (used + expansions + count > budget) {
208
+ refused.push({
209
+ pattern,
210
+ reason: `would bring the patterns past ${budget} after brace expansion`,
211
+ });
212
+ continue;
213
+ }
214
+ expansions += count;
215
+ accepted.push(pattern);
216
+ }
217
+ return { accepted, expansions, refused };
218
+ }
@@ -5,6 +5,7 @@ import { brotliDecompressSync, gunzipSync, inflateSync } from "node:zlib";
5
5
 
6
6
  import { Agent, EnvHttpProxyAgent, request as undiciRequest } from "undici";
7
7
 
8
+ import { readBrotliDecoder } from "./brotliProbe.js";
8
9
  import { registerRunReset } from "./runState.js";
9
10
 
10
11
  // Environment variables here are read with raw `process.env` rather than
@@ -208,6 +209,75 @@ export function resolveTimeout(timeout) {
208
209
  return undefined;
209
210
  }
210
211
 
212
+ let runtimeBrotliVerdict;
213
+
214
+ /**
215
+ * Set or clear the cached runtime Brotli verdict. Exported for unit tests only.
216
+ *
217
+ * @param {boolean|undefined} verdict verdict to use, or undefined to re-check
218
+ */
219
+ export function _setRuntimeBrotliVerdict(verdict) {
220
+ runtimeBrotliVerdict = verdict;
221
+ }
222
+
223
+ /**
224
+ * Whether the Brotli decoder of the runtime cdxgen runs on carries the
225
+ * CVE-2026-111497 fix, read once from the decoder itself: the node binary, or
226
+ * the shared libbrotlidec a `--shared-brotli` build (Homebrew, distro
227
+ * packages) loads; under Bun, the bun binary, which carries its own copy.
228
+ * process.versions.brotli cannot answer this, since Node.js backports the fix
229
+ * without changing it. Deno decodes Brotli in Rust, which was never affected.
230
+ *
231
+ * On Node.js cdxgen never asks for `br`, so a Brotli response comes only from
232
+ * a server that sends it unasked. When the decoder cannot be shown to be
233
+ * patched, such a body is left undecoded rather than handed to a decoder an
234
+ * attacker can write past. Bun decodes in its transport, so there the client
235
+ * stops asking for `br` instead.
236
+ *
237
+ * @param {Object} [runtime] the runtime to judge, for tests
238
+ * @returns {boolean} true when Brotli bodies may be decoded
239
+ */
240
+ export function runtimeBrotliDecoderIsPatched(runtime = undefined) {
241
+ if (!runtime && runtimeBrotliVerdict !== undefined) {
242
+ return runtimeBrotliVerdict;
243
+ }
244
+ const subject = runtime || {
245
+ isDeno: globalThis.Deno?.version?.deno !== undefined,
246
+ execPath: process.execPath,
247
+ sharedBrotli: [true, "true"].includes(
248
+ process.config?.variables?.node_shared_brotli,
249
+ ),
250
+ sharedObjects: () => process.report?.getReport?.()?.sharedObjects || [],
251
+ };
252
+ let verdict = false;
253
+ if (subject.isDeno) {
254
+ verdict = true;
255
+ } else {
256
+ const decoderFile = subject.sharedBrotli
257
+ ? subject
258
+ .sharedObjects()
259
+ .find((path) =>
260
+ `${path}`.split(/[\\/]/).pop().startsWith("libbrotlidec"),
261
+ )
262
+ : subject.execPath;
263
+ verdict = Boolean(
264
+ decoderFile &&
265
+ readBrotliDecoder(decoderFile)?.c?.blockSwitchFix === "true",
266
+ );
267
+ }
268
+ if (!runtime) {
269
+ runtimeBrotliVerdict = verdict;
270
+ if (!verdict) {
271
+ console.warn(
272
+ isBun
273
+ ? "The Brotli decoder in this Bun is not patched for CVE-2026-111497, or could not be checked, so cdxgen no longer asks servers for Brotli-encoded responses. Bun still decodes one a server sends unasked; upgrade Bun to a release with a patched Brotli."
274
+ : "The Brotli decoder of this runtime is not patched for CVE-2026-111497, or could not be checked, so Brotli-encoded responses are left undecoded. Upgrade Node.js to 22.23.4, 24.22.1 or 26.12.1 or later (and the shared brotli library on Homebrew or distro builds).",
275
+ );
276
+ }
277
+ }
278
+ return verdict;
279
+ }
280
+
211
281
  /**
212
282
  * Decompress a response body buffer according to its `Content-Encoding`. undici
213
283
  * performs no automatic decompression, so we replicate the behaviour `got` gave
@@ -235,6 +305,9 @@ function decompressBody(rawBody, contentEncoding) {
235
305
  } else if (encoding === "deflate") {
236
306
  buffer = inflateSync(buffer);
237
307
  } else if (encoding === "br") {
308
+ if (!runtimeBrotliDecoderIsPatched()) {
309
+ return buffer;
310
+ }
238
311
  buffer = brotliDecompressSync(buffer);
239
312
  } else if (encoding === "identity") {
240
313
  // no-op
@@ -296,6 +369,30 @@ async function readBody(undiciResponse, responseType) {
296
369
  return { body, rawBody };
297
370
  }
298
371
 
372
+ /**
373
+ * The headers to put on the wire. Bun's bundled undici asks for br by default
374
+ * and decodes it with Bun's own decoder before this client sees the body, so
375
+ * the check in decompressBody never runs there. Where that decoder is not
376
+ * patched, ask only for the encodings this client decodes itself. A server
377
+ * that sends br unasked is still decoded by Bun; only upgrading Bun closes
378
+ * that. The cache key is built from the caller's headers, not these.
379
+ *
380
+ * @param {Object} headers Request headers.
381
+ * @returns {Object} Headers to send.
382
+ */
383
+ function transportHeaders(headers) {
384
+ if (
385
+ !isBun ||
386
+ Object.keys(headers).some(
387
+ (key) => key.toLowerCase() === "accept-encoding",
388
+ ) ||
389
+ runtimeBrotliDecoderIsPatched()
390
+ ) {
391
+ return headers;
392
+ }
393
+ return { ...headers, "accept-encoding": "gzip, deflate" };
394
+ }
395
+
299
396
  /**
300
397
  * Convert a body into the `[body, headers]` pair to hand to undici. When the
301
398
  * `json` option is present it is serialized and a JSON `content-type` is added
@@ -752,7 +849,7 @@ async function doRequest(mergedOptions) {
752
849
  const insecure = options.https?.rejectUnauthorized === false;
753
850
  const requestOptions = {
754
851
  method,
755
- headers,
852
+ headers: transportHeaders(headers),
756
853
  body: method === "GET" || method === "HEAD" ? undefined : body,
757
854
  dispatcher: selectDispatcher(insecure),
758
855
  signal,
@@ -0,0 +1,38 @@
1
+ import { isAbsolute, relative, resolve, win32 } from "node:path";
2
+
3
+ /**
4
+ * Containment check for a path taken from a lockfile (issue #3816). A crafted
5
+ * npm lockfile can name `file:`/`link:` specifiers or link targets built from
6
+ * `..` chains or absolute paths, so a parser that is about to list such an
7
+ * entry checks the resolved path against the project root first.
8
+ *
9
+ * The candidate is resolved against `projectRoot`, so both relative values
10
+ * and the absolute paths arborist already normalised are accepted. The root
11
+ * itself counts as inside: a lockfile may legitimately link the project to
12
+ * its own directory.
13
+ *
14
+ * @param {string} candidate Path to check, absolute or relative to projectRoot
15
+ * @param {string} projectRoot Project root the path must stay inside; a falsy
16
+ * root means the current working directory, mirroring `node:path`
17
+ *
18
+ * @returns {Boolean} True when the path is inside projectRoot
19
+ */
20
+ export function isPathInsideProject(candidate, projectRoot) {
21
+ if (typeof candidate !== "string" || !candidate) {
22
+ return false;
23
+ }
24
+ if (candidate.includes("\0")) {
25
+ return false;
26
+ }
27
+ const rootAbs = resolve(projectRoot || process.cwd());
28
+ const rel = relative(rootAbs, resolve(rootAbs, candidate));
29
+ if (!rel) {
30
+ return true;
31
+ }
32
+ // relative() returns an absolute path when the two sit on different
33
+ // Windows drives.
34
+ if (isAbsolute(rel) || win32.isAbsolute(rel)) {
35
+ return false;
36
+ }
37
+ return rel !== ".." && !rel.startsWith("../") && !rel.startsWith("..\\");
38
+ }
package/lib/core/paths.js CHANGED
@@ -14,7 +14,7 @@
14
14
  * `https://` URL, and `fileURLToPath` throws `ERR_INVALID_URL_SCHEME` on those.
15
15
  */
16
16
 
17
- import { platform } from "node:os";
17
+ import { homedir, platform } from "node:os";
18
18
  import { dirname } from "node:path";
19
19
  import { fileURLToPath } from "node:url";
20
20
 
@@ -68,3 +68,26 @@ export function isValidDriveRoot(root) {
68
68
  export function toCamel(str) {
69
69
  return str.replace(/-([a-z])/g, (_, g) => g.toUpperCase());
70
70
  }
71
+
72
+ /**
73
+ * Rewrite an absolute path under the user's home directory to a `~`-relative
74
+ * form so the emitted BOM does not carry the local username.
75
+ *
76
+ * @param {string} filePath Absolute path
77
+ * @param {string} [home] Home directory, defaulting to the current user's
78
+ * @returns {string} Home-relative path when applicable, else the input
79
+ */
80
+ export function redactHomePath(filePath, home = homedir()) {
81
+ const value = String(filePath || "");
82
+ if (!home || !value.startsWith(home)) {
83
+ return value;
84
+ }
85
+ // Require a path boundary so a sibling home such as /Users/alice-backup is
86
+ // not rewritten as if it sat under /Users/alice.
87
+ const remainder = value.slice(home.length);
88
+ if (remainder && !/^[/\\]/u.test(remainder)) {
89
+ return value;
90
+ }
91
+ const rest = remainder.replace(/^[/\\]/u, "");
92
+ return rest ? `~/${rest.replaceAll("\\", "/")}` : "~";
93
+ }