@cdxgen/cdxgen 13.3.0 → 13.5.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 (372) hide show
  1. package/README.md +12 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +115 -31
  4. package/bin/evinse.js +23 -1
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +115 -0
  7. package/bin/sign.js +45 -15
  8. package/bin/tracebom.js +5 -1
  9. package/bin/validate.js +84 -18
  10. package/bin/verify.js +155 -86
  11. package/data/cbomosdb-queries.json +13 -1
  12. package/data/component-tags.json +8 -0
  13. package/data/helpers/init.gradle +46 -21
  14. package/data/queries-darwin.json +25 -1
  15. package/data/queries-win.json +19 -1
  16. package/data/queries.json +41 -0
  17. package/data/remediations.json +344 -1
  18. package/data/rules/cbom-security.yaml +235 -4
  19. package/data/rules/dependency-sources.yaml +67 -0
  20. package/data/rules/go-binaries.yaml +46 -0
  21. package/data/rules/golem-go.yaml +80 -0
  22. package/data/rules/hbom-compliance.yaml +40 -22
  23. package/data/rules/hbom-performance.yaml +7 -1
  24. package/data/rules/host-topology.yaml +1 -1
  25. package/data/rules/obom-runtime.yaml +31 -2
  26. package/data/rules/php-runtime.yaml +143 -0
  27. package/data/rules/scala-evidence.yaml +115 -0
  28. package/lib/audit/index.js +52 -6
  29. package/lib/cli/bomAssembly.js +28 -3
  30. package/lib/cli/cliOptions.js +110 -3
  31. package/lib/cli/index.js +186 -58
  32. package/lib/cli/jsBom.js +50 -29
  33. package/lib/cli/jvmBom.js +779 -146
  34. package/lib/cli/managedBom.js +1021 -261
  35. package/lib/cli/nativeBom.js +364 -58
  36. package/lib/core/activity.js +12 -0
  37. package/lib/core/env.js +47 -17
  38. package/lib/core/fs.js +31 -21
  39. package/lib/core/httpClient.js +233 -69
  40. package/lib/core/pluginRun.js +184 -0
  41. package/lib/core/processTree.js +100 -0
  42. package/lib/core/projectConfig.js +1 -0
  43. package/lib/core/projectTooling.js +97 -0
  44. package/lib/core/propertySanitizer.js +72 -0
  45. package/lib/core/runState.js +39 -0
  46. package/lib/core/toml.js +72 -0
  47. package/lib/ecosystems/asarutils.js +4 -2
  48. package/lib/ecosystems/bunutils.js +5 -7
  49. package/lib/ecosystems/cargoCache.js +231 -8
  50. package/lib/ecosystems/cmakeResolver.js +10 -21
  51. package/lib/ecosystems/cppBuildContext.js +631 -0
  52. package/lib/ecosystems/cppEvidence.js +337 -22
  53. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  54. package/lib/ecosystems/denoutils.js +93 -66
  55. package/lib/ecosystems/ecosystems.js +1967 -788
  56. package/lib/ecosystems/golem.js +578 -18
  57. package/lib/ecosystems/gradleutils.js +340 -46
  58. package/lib/ecosystems/jsEvidence.js +85 -24
  59. package/lib/ecosystems/kosi.js +21 -4
  60. package/lib/ecosystems/npmutils.js +64 -0
  61. package/lib/ecosystems/parsers-dotnet.js +668 -153
  62. package/lib/ecosystems/parsers-elm.js +46 -28
  63. package/lib/ecosystems/parsers-gleam.js +3 -4
  64. package/lib/ecosystems/parsers-go.js +1283 -297
  65. package/lib/ecosystems/parsers-js.js +92 -26
  66. package/lib/ecosystems/parsers-julia.js +2 -3
  67. package/lib/ecosystems/parsers-jvm.js +463 -29
  68. package/lib/ecosystems/parsers-misc.js +405 -93
  69. package/lib/ecosystems/parsers-python.js +152 -20
  70. package/lib/ecosystems/parsers-rust.js +4 -4
  71. package/lib/ecosystems/parsers-terraform.js +1573 -19
  72. package/lib/ecosystems/pythonutils.js +102 -6
  73. package/lib/ecosystems/remote/huggingface.js +3 -0
  74. package/lib/ecosystems/rubyutils.js +73 -20
  75. package/lib/ecosystems/rusi.js +32 -9
  76. package/lib/ecosystems/sbtutils.js +243 -187
  77. package/lib/ecosystems/scalacliutils.js +343 -0
  78. package/lib/ecosystems/scalasem.js +2620 -0
  79. package/lib/ecosystems/terraformRegistry.js +288 -0
  80. package/lib/ecosystems/utils.js +2 -0
  81. package/lib/ecosystems/vendoredCode.js +156 -0
  82. package/lib/evinser/evinser.js +695 -78
  83. package/lib/evinser/swiftsem.js +68 -5
  84. package/lib/helpers/agenticToolutils.js +2 -2
  85. package/lib/helpers/bomSigner.js +576 -128
  86. package/lib/inventory/agentSkillSpec.js +17 -0
  87. package/lib/inventory/aiCollector.js +4 -1
  88. package/lib/inventory/analyzer.js +110 -127
  89. package/lib/inventory/apkInstalledDb.js +243 -0
  90. package/lib/inventory/atomUtils.js +204 -48
  91. package/lib/inventory/auditCategories.js +10 -3
  92. package/lib/inventory/buildToolRateLimit.js +144 -0
  93. package/lib/inventory/cacheCommands.js +2 -1
  94. package/lib/inventory/cacheDir.js +52 -9
  95. package/lib/inventory/certificates.js +2371 -0
  96. package/lib/inventory/cmakeBuildDirs.js +254 -0
  97. package/lib/inventory/communityAiConfigParser.js +4 -9
  98. package/lib/inventory/deps.js +507 -128
  99. package/lib/inventory/display.js +200 -3
  100. package/lib/inventory/dosai.js +747 -101
  101. package/lib/inventory/dosaiParsers.js +310 -15
  102. package/lib/inventory/dynamic.js +96 -8
  103. package/lib/inventory/envcontext.js +168 -5
  104. package/lib/inventory/evidenceUtils.js +590 -132
  105. package/lib/inventory/fetchBatch.js +574 -38
  106. package/lib/inventory/fetchDiskCache.js +597 -0
  107. package/lib/inventory/forgeEnricher.js +14 -5
  108. package/lib/inventory/formulationParsers.js +3 -2
  109. package/lib/inventory/goBuildInfo.js +140 -0
  110. package/lib/inventory/hbom.js +67 -7
  111. package/lib/inventory/hbomAnalysis.js +16 -0
  112. package/lib/inventory/hostTopology.js +21 -0
  113. package/lib/inventory/jvmLocalRepos.js +917 -0
  114. package/lib/inventory/mcpConfigParser.js +2 -3
  115. package/lib/inventory/osPackagePurl.js +244 -0
  116. package/lib/inventory/osPackageResolver.js +89 -87
  117. package/lib/inventory/osinfo.js +79 -31
  118. package/lib/inventory/osqueryTransform.js +114 -5
  119. package/lib/inventory/parentSelection.js +146 -0
  120. package/lib/inventory/phpBuildInfo.js +487 -0
  121. package/lib/inventory/phpHostProbe.js +641 -0
  122. package/lib/inventory/rootfsCerts.js +426 -0
  123. package/lib/inventory/rustFormulationParser.js +3 -4
  124. package/lib/inventory/scalaCoords.js +153 -0
  125. package/lib/inventory/source.js +215 -11
  126. package/lib/inventory/spdx.js +245 -0
  127. package/lib/inventory/traceRunner.js +93 -8
  128. package/lib/inventory/tracedCrypto.js +423 -0
  129. package/lib/managers/binary.js +529 -183
  130. package/lib/managers/docker.js +199 -44
  131. package/lib/managers/oci.js +24 -2
  132. package/lib/parsers/cmakeCache.js +69 -0
  133. package/lib/parsers/cmakeLists.js +16 -2
  134. package/lib/parsers/cmakePresets.js +400 -0
  135. package/lib/parsers/compileCommands.js +557 -0
  136. package/lib/parsers/email.js +277 -0
  137. package/lib/parsers/largeJson.js +34 -2
  138. package/lib/parsers/strictJson.js +322 -0
  139. package/lib/parsers/terraformConfig.js +1073 -0
  140. package/lib/parsers/terraformSource.js +652 -0
  141. package/lib/server/server.js +209 -1
  142. package/lib/stages/postgen/annotator.js +17 -1
  143. package/lib/stages/postgen/introspection/markers.js +1 -1
  144. package/lib/stages/postgen/introspection/reflect.js +4 -1
  145. package/lib/stages/postgen/postgen.js +20 -6
  146. package/lib/stages/postgen/ruleEngine.js +58 -1
  147. package/lib/stages/postgen/specVersionCompat.js +62 -0
  148. package/lib/stages/pregen/pregen.js +117 -5
  149. package/lib/validator/bomValidator.js +26 -5
  150. package/lib/validator/generated/runtime.mjs +13 -0
  151. package/lib/validator/generated/validate-1.6.mjs +26 -7
  152. package/lib/validator/generated/validate-1.7.mjs +31 -12
  153. package/lib/validator/generated/validate-2.0.mjs +30 -11
  154. package/lib/validator/index.js +25 -10
  155. package/lib/validator/reporters/json.js +1 -0
  156. package/package.json +23 -23
  157. package/types/bin/cdxgen-secure.d.ts +3 -0
  158. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  159. package/types/bin/repl.d.ts.map +1 -1
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +9 -2
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +43 -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.map +1 -1
  167. package/types/lib/cli/jvmBom.d.ts +44 -1
  168. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  169. package/types/lib/cli/managedBom.d.ts +7 -4
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts +3 -1
  172. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  173. package/types/lib/core/activity.d.ts.map +1 -1
  174. package/types/lib/core/env.d.ts +27 -2
  175. package/types/lib/core/env.d.ts.map +1 -1
  176. package/types/lib/core/fs.d.ts.map +1 -1
  177. package/types/lib/core/httpClient.d.ts +13 -8
  178. package/types/lib/core/httpClient.d.ts.map +1 -1
  179. package/types/lib/core/pluginRun.d.ts +97 -0
  180. package/types/lib/core/pluginRun.d.ts.map +1 -0
  181. package/types/lib/core/processTree.d.ts +36 -0
  182. package/types/lib/core/processTree.d.ts.map +1 -0
  183. package/types/lib/core/projectConfig.d.ts.map +1 -1
  184. package/types/lib/core/projectTooling.d.ts +41 -0
  185. package/types/lib/core/projectTooling.d.ts.map +1 -0
  186. package/types/lib/core/propertySanitizer.d.ts +26 -0
  187. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  188. package/types/lib/core/runState.d.ts +29 -0
  189. package/types/lib/core/runState.d.ts.map +1 -0
  190. package/types/lib/core/toml.d.ts +20 -0
  191. package/types/lib/core/toml.d.ts.map +1 -0
  192. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  193. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  194. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  195. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
  197. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  199. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  200. package/types/lib/ecosystems/cppEvidence.d.ts +19 -3
  201. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  202. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  203. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  204. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  205. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  207. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  208. package/types/lib/ecosystems/golem.d.ts +81 -1
  209. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  210. package/types/lib/ecosystems/gradleutils.d.ts +59 -7
  211. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  212. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  213. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  214. package/types/lib/ecosystems/npmutils.d.ts +15 -0
  215. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-dotnet.d.ts +155 -5
  217. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  218. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  219. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  220. package/types/lib/ecosystems/parsers-go.d.ts +119 -5
  221. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  222. package/types/lib/ecosystems/parsers-js.d.ts +6 -1
  223. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  224. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  225. package/types/lib/ecosystems/parsers-jvm.d.ts +74 -3
  226. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  227. package/types/lib/ecosystems/parsers-misc.d.ts +21 -59
  228. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  229. package/types/lib/ecosystems/parsers-python.d.ts +57 -6
  230. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  231. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  232. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  233. package/types/lib/ecosystems/pythonutils.d.ts +26 -1
  234. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  235. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  236. package/types/lib/ecosystems/rubyutils.d.ts +21 -1
  237. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  238. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  239. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  240. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  241. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  242. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  243. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  244. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  245. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  246. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  247. package/types/lib/ecosystems/utils.d.ts +2 -2
  248. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  249. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  250. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  251. package/types/lib/evinser/evinser.d.ts +100 -8
  252. package/types/lib/evinser/evinser.d.ts.map +1 -1
  253. package/types/lib/evinser/swiftsem.d.ts +10 -0
  254. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  255. package/types/lib/helpers/bomSigner.d.ts +80 -4
  256. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  257. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  258. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  259. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  260. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  261. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  262. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  263. package/types/lib/inventory/atomUtils.d.ts +96 -10
  264. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  265. package/types/lib/inventory/auditCategories.d.ts +3 -0
  266. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  267. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  268. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  269. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  270. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  271. package/types/lib/inventory/cacheDir.d.ts +12 -4
  272. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  273. package/types/lib/inventory/certificates.d.ts +205 -0
  274. package/types/lib/inventory/certificates.d.ts.map +1 -0
  275. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  276. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  277. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  278. package/types/lib/inventory/deps.d.ts +61 -2
  279. package/types/lib/inventory/deps.d.ts.map +1 -1
  280. package/types/lib/inventory/display.d.ts +18 -0
  281. package/types/lib/inventory/display.d.ts.map +1 -1
  282. package/types/lib/inventory/dosai.d.ts +202 -14
  283. package/types/lib/inventory/dosai.d.ts.map +1 -1
  284. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  285. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  286. package/types/lib/inventory/dynamic.d.ts +16 -1
  287. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  288. package/types/lib/inventory/envcontext.d.ts +40 -2
  289. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  290. package/types/lib/inventory/evidenceUtils.d.ts +48 -14
  291. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  292. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  293. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  294. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  295. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  296. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  297. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  298. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  299. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  300. package/types/lib/inventory/hbom.d.ts.map +1 -1
  301. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  302. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  303. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  304. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  305. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  306. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  307. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  308. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  309. package/types/lib/inventory/osPackageResolver.d.ts +6 -2
  310. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  311. package/types/lib/inventory/osinfo.d.ts +31 -6
  312. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  313. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  314. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  315. package/types/lib/inventory/parentSelection.d.ts +34 -0
  316. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  317. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  318. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  319. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  320. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  321. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  322. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  323. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  324. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  325. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  326. package/types/lib/inventory/source.d.ts +28 -0
  327. package/types/lib/inventory/source.d.ts.map +1 -1
  328. package/types/lib/inventory/spdx.d.ts +24 -0
  329. package/types/lib/inventory/spdx.d.ts.map +1 -1
  330. package/types/lib/inventory/traceRunner.d.ts +16 -1
  331. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  332. package/types/lib/inventory/tracedCrypto.d.ts +64 -0
  333. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  334. package/types/lib/managers/binary.d.ts +10 -0
  335. package/types/lib/managers/binary.d.ts.map +1 -1
  336. package/types/lib/managers/docker.d.ts +12 -0
  337. package/types/lib/managers/docker.d.ts.map +1 -1
  338. package/types/lib/managers/oci.d.ts +3 -1
  339. package/types/lib/managers/oci.d.ts.map +1 -1
  340. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  341. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  342. package/types/lib/parsers/cmakeLists.d.ts +2 -1
  343. package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
  344. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  345. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  346. package/types/lib/parsers/compileCommands.d.ts +115 -0
  347. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  348. package/types/lib/parsers/email.d.ts +19 -0
  349. package/types/lib/parsers/email.d.ts.map +1 -0
  350. package/types/lib/parsers/largeJson.d.ts +13 -0
  351. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  352. package/types/lib/parsers/strictJson.d.ts +46 -0
  353. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  354. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  355. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  356. package/types/lib/parsers/terraformSource.d.ts +66 -0
  357. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  358. package/types/lib/server/server.d.ts +8 -0
  359. package/types/lib/server/server.d.ts.map +1 -1
  360. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  361. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  362. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  363. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  364. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  365. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  366. package/types/lib/stages/pregen/pregen.d.ts +14 -1
  367. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  368. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  369. package/types/lib/validator/index.d.ts +8 -0
  370. package/types/lib/validator/index.d.ts.map +1 -1
  371. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  372. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -6,7 +6,13 @@ import {
6
6
  statSync,
7
7
  } from "node:fs";
8
8
  import { tmpdir, totalmem } from "node:os";
9
- import { delimiter as _delimiter, dirname, join, resolve } from "node:path";
9
+ import {
10
+ delimiter as _delimiter,
11
+ basename,
12
+ dirname,
13
+ join,
14
+ resolve,
15
+ } from "node:path";
10
16
  import process from "node:process";
11
17
 
12
18
  import {
@@ -23,7 +29,10 @@ import {
23
29
  } from "../core/fs.js";
24
30
  import { TRACE_MODE } from "../core/logger.js";
25
31
  import { dirNameStr, isWin } from "../core/paths.js";
32
+ import { formatDuration, reapProcessTree } from "../core/processTree.js";
33
+ import { isProjectToolingAllowed } from "../core/projectTooling.js";
26
34
  import { readJsonFile } from "../parsers/largeJson.js";
35
+ import { cmakeBuildDirCandidates } from "./cmakeBuildDirs.js";
27
36
 
28
37
  const ASTGEN_DEFAULT_IGNORE_DIRS = [
29
38
  "venv",
@@ -83,9 +92,6 @@ const ATOM_C_LANGUAGES = new Set([
83
92
 
84
93
  const COMPILE_COMMANDS_FILE = "compile_commands.json";
85
94
 
86
- /** Build directories, under the scan root, searched for a compilation database. */
87
- const COMPILE_COMMANDS_BUILD_DIRS = ["build", "out", "builddir"];
88
-
89
95
  /**
90
96
  * Characters a Windows shell command line would interpret: `&`, `|`, `<` and
91
97
  * `>` chain or redirect commands, `%` expands variables even inside quotes,
@@ -117,9 +123,12 @@ function compileCommandsIn(dir) {
117
123
  * language its build uses.
118
124
  *
119
125
  * An explicit `options.compileCommands` (a file, or a directory holding one
120
- * directly or under `build/`) wins. Otherwise the scan root, `build/`, `out/`,
121
- * `builddir/`, `cmake-build-*` and the directory of `options.cmakeCache` are
122
- * searched. A database can come with the code it describes, and atom asks the
126
+ * directly or under `build/`) wins. Otherwise the scan root and the project's
127
+ * build directories are searched: the directory of `options.cmakeCache`, the
128
+ * build directories of its CMake presets, and the conventional `build`,
129
+ * `out`, `builddir` and `cmake-build-` directories (see
130
+ * `cmakeBuildDirCandidates`). A database can come with the code it describes,
131
+ * and atom asks the
123
132
  * GCC or Clang driver it names (from the PATH, or an absolute path outside
124
133
  * the project) for its predefined macros, so in secure mode only an explicit
125
134
  * database is used.
@@ -159,20 +168,7 @@ export function findCompileCommands(src, options = {}) {
159
168
  if (!rootIsDir) {
160
169
  return undefined;
161
170
  }
162
- const dirs = [root, ...COMPILE_COMMANDS_BUILD_DIRS.map((d) => join(root, d))];
163
- try {
164
- dirs.push(
165
- ...readdirSync(root, { withFileTypes: true })
166
- .filter((e) => e.isDirectory() && e.name.startsWith("cmake-build-"))
167
- .map((e) => join(root, e.name))
168
- .sort(),
169
- );
170
- } catch (_err) {
171
- // an unreadable root has no build directories to offer
172
- }
173
- if (options.cmakeCache) {
174
- dirs.push(dirname(resolve(`${options.cmakeCache}`)));
175
- }
171
+ const dirs = [root, ...cmakeBuildDirCandidates(root, options)];
176
172
  for (const dir of dirs) {
177
173
  const candidate = join(dir, COMPILE_COMMANDS_FILE);
178
174
  try {
@@ -319,6 +315,38 @@ export function atomCompileCommandsArgs(src, language, options = {}) {
319
315
  return ["--frontend-args", `compile-commands=${database}`];
320
316
  }
321
317
 
318
+ /**
319
+ * The arguments that stop atom's Java frontend from running the scanned
320
+ * project's build. In its default delombok mode, atom runs
321
+ * `mvn dependency:build-classpath` inside a Maven project, which loads the
322
+ * project's `.mvn` configuration and build extensions, so delombok and
323
+ * dependency fetching are turned off whenever the project's own tooling may
324
+ * not run.
325
+ *
326
+ * @param {string} language atom `-l` value
327
+ * @param {Object} options CLI options
328
+ * @returns {string[]|undefined} Arguments to add to the atom command, or
329
+ * undefined when the installed atom cannot be told to leave the build alone
330
+ */
331
+ export function atomBuildToolArgs(language, options = {}) {
332
+ if (
333
+ `${language}`.toLowerCase() !== "java" ||
334
+ isProjectToolingAllowed(options)
335
+ ) {
336
+ return [];
337
+ }
338
+ const keys = atomFrontendArgKeys(language);
339
+ if (!keys.has("delombok-mode")) {
340
+ return undefined;
341
+ }
342
+ return [
343
+ "--frontend-args",
344
+ keys.has("fetch-dependencies")
345
+ ? "delombok-mode=no-delombok,fetch-dependencies=false"
346
+ : "delombok-mode=no-delombok",
347
+ ];
348
+ }
349
+
322
350
  // Absolute ceiling on atom's heap. Past this point a larger heap buys a
323
351
  // collector that runs less often rather than an analysis that succeeds where it
324
352
  // otherwise would not, while the reservation itself is what pushes a build
@@ -751,18 +779,32 @@ export function buildAtomCommandEnv(options = {}, language = "") {
751
779
  // that does not exist on native platforms, and atom 3.0.x crashes parsing
752
780
  // that bogus value. Forward the resolved php-parse location through the env
753
781
  // so executeAtom can bypass the dispatcher for PHP (see
754
- // resolveDirectAtomBinaryPath). Computed independently of exclude patterns.
782
+ // resolveDirectAtomBinaryPath). PHP_ASTGEN_BIN additionally names the
783
+ // batch-capable generator (phpastgen.js): chen parses the whole tree, vendor
784
+ // included, in one generator run instead of one php interpreter per file,
785
+ // which on windows is the difference between minutes and hours. Computed
786
+ // independently of exclude patterns.
755
787
  const phpParseBin = ATOM_PHP_LANGUAGES.has(normalizedLanguage)
756
788
  ? resolvePhpParseBin()
757
789
  : undefined;
790
+ const phpAstgenBin = ATOM_PHP_LANGUAGES.has(normalizedLanguage)
791
+ ? resolvePhpAstgenBin()
792
+ : undefined;
793
+ const phpEnv = {};
794
+ if (phpParseBin) {
795
+ phpEnv.PHP_PARSER_BIN = phpParseBin;
796
+ }
797
+ if (phpAstgenBin) {
798
+ phpEnv.PHP_ASTGEN_BIN = phpAstgenBin;
799
+ }
758
800
  if (!excludePatterns.length) {
759
- return phpParseBin ? { PHP_PARSER_BIN: phpParseBin } : {};
801
+ return phpEnv;
760
802
  }
761
803
  const chenIgnoreDirs = mergeCsvValues(
762
804
  readEnvironmentVariable("CHEN_IGNORE_DIRS"),
763
805
  extractIgnoreDirsFromExcludePatterns(excludePatterns, true),
764
806
  );
765
- const env = {};
807
+ const env = { ...phpEnv };
766
808
  if (chenIgnoreDirs) {
767
809
  env.CHEN_IGNORE_DIRS = chenIgnoreDirs;
768
810
  }
@@ -788,9 +830,6 @@ export function buildAtomCommandEnv(options = {}, language = "") {
788
830
  env.ASTGEN_IGNORE_FILE_PATTERN = astgenIgnoreFilePattern;
789
831
  }
790
832
  }
791
- if (phpParseBin) {
792
- env.PHP_PARSER_BIN = phpParseBin;
793
- }
794
833
  return env;
795
834
  }
796
835
 
@@ -880,15 +919,19 @@ export function resolveAtomProvider(opts = {}) {
880
919
  * ceiling differently, and a native-image runtime option on a jar run reaches
881
920
  * atom as an unknown argument that fails every slice. So the kind follows what
882
921
  * is installed where cdxgen can see it: the native binary means native, the jar
883
- * package without it means jar. With neither in view (a custom `ATOM_CMD`, an
884
- * install elsewhere) the platform's kind stands.
922
+ * package without it means jar. An `ATOM_CMD` is classified by what it names
923
+ * (see `atomCommandKind`). With nothing in view (an install elsewhere) the
924
+ * platform's kind stands.
885
925
  *
886
926
  * Also used to gate Java/JDK advice, so users on the five native platforms are
887
927
  * not told to install a JDK for a failure that has nothing to do with Java.
888
928
  */
889
929
  export function atomProviderKind() {
890
930
  const { kind } = resolveAtomProvider();
891
- if (kind !== "native" || readEnvironmentVariable("ATOM_CMD")) {
931
+ if (readEnvironmentVariable("ATOM_CMD")) {
932
+ return atomCommandKind(readEnvironmentVariable("ATOM_CMD"), kind);
933
+ }
934
+ if (kind !== "native") {
892
935
  return kind;
893
936
  }
894
937
  const atomHome = readEnvironmentVariable("ATOM_HOME");
@@ -906,13 +949,64 @@ export function atomProviderKind() {
906
949
  return jarPlugins ? "jar" : kind;
907
950
  }
908
951
 
952
+ /**
953
+ * Whether `ATOM_CMD` launches a jar or follows the platform.
954
+ *
955
+ * The jar launchers (`plugins/bin/atom`, `plugins/bin/atom.bat`) sit next to a
956
+ * `plugins/lib` directory holding atom's jars, so a launcher with that sibling,
957
+ * or any `.bat` (only the jar payload ships one), runs on the JVM whatever the
958
+ * platform is. Everything else, including the npm dispatcher (`node
959
+ * .../index.js`, its `atom`/`atom.cmd` bin shims) and a native binary, follows
960
+ * the platform's kind. A GraalVM `-XX:` heap argument handed to a jar launcher
961
+ * is rejected by atom's option parser before any analysis starts, and a
962
+ * `JAVA_TOOL_OPTIONS` ceiling handed to a native image is silently ignored, so
963
+ * the distinction decides whether the heap is bounded at all.
964
+ *
965
+ * @param {string} command The `ATOM_CMD` value
966
+ * @param {"native"|"jar"} platformKind Kind the platform prefers
967
+ * @returns {"native"|"jar"}
968
+ */
969
+ export function atomCommandKind(command, platformKind) {
970
+ const [bin, leadingArg] = splitAtomCommand(command);
971
+ const runsScript =
972
+ leadingArg !== undefined && /^node(\.exe)?$/i.test(basename(bin));
973
+ let launcher = runsScript ? leadingArg : bin;
974
+ if (/\.bat$/i.test(launcher)) {
975
+ return "jar";
976
+ }
977
+ try {
978
+ // npm bin entries are symlinks into the package (`/usr/local/bin/atom` ->
979
+ // `.../@appthreat/atom/index.js`), and `/usr/local` has a `lib` of its own.
980
+ launcher = realpathSync(launcher);
981
+ } catch {
982
+ return platformKind;
983
+ }
984
+ const binDir = dirname(launcher);
985
+ if (basename(binDir) !== "bin") {
986
+ return platformKind;
987
+ }
988
+ try {
989
+ const libDir = join(dirname(binDir), "lib");
990
+ if (
991
+ readdirSync(libDir).some(
992
+ (file) => file.startsWith("io.appthreat.atom") && file.endsWith(".jar"),
993
+ )
994
+ ) {
995
+ return "jar";
996
+ }
997
+ } catch {
998
+ // no lib directory: not a jar layout
999
+ }
1000
+ return platformKind;
1001
+ }
1002
+
909
1003
  /**
910
1004
  * Locate the `php-parse` binary that the PHP frontend needs.
911
1005
  *
912
1006
  * atom 3's dispatcher unconditionally sets `PHP_PARSER_BIN=<ATOM_HOME>/bin/php-parse`,
913
1007
  * which for a native sub-package does not exist and also clobbers a caller-set
914
- * value. cdxgen therefore resolves the real location and forwards it through
915
- * the child env (see `buildAtomCommandEnv`); `executeAtom` then spawns the
1008
+ * value. cdxgen therefore resolves the real location and forwards it through the
1009
+ * child env (see `buildAtomCommandEnv`); `executeAtom` then spawns the
916
1010
  * native binary directly for PHP so the dispatcher cannot clobber it (see
917
1011
  * `resolveDirectAtomBinaryPath`). Resolution order:
918
1012
  * 1. explicit `PHP_PARSER_BIN` env var (operators / container images)
@@ -929,6 +1023,44 @@ export function resolvePhpParseBin() {
929
1023
  if (readEnvironmentVariable("PHP_PARSER_BIN")) {
930
1024
  return readEnvironmentVariable("PHP_PARSER_BIN");
931
1025
  }
1026
+ return parsetoolsFile("plugins", "bin", "php-parse");
1027
+ }
1028
+
1029
+ /**
1030
+ * Locate the `phpastgen` generator that lets the PHP frontend parse whole
1031
+ * directories in one batch.
1032
+ *
1033
+ * `php-parse` (forwarded as `PHP_PARSER_BIN`) is the per-file parser: atom
1034
+ * starts a php interpreter for every `.php` file, which on windows turns a
1035
+ * vendored PHP project (thousands of files under `vendor/`) into an hours-long
1036
+ * run that no timeout can rescue. `phpastgen` answers the `--parser-info`
1037
+ * capability probe, so atom's batch path engages, and atom-parsetools 1.9 and
1038
+ * later parse the whole tree, vendor included, with a few interpreters that
1039
+ * each take a chunk of files. It is a Node script, so the frontend launches it
1040
+ * with `node` rather than `php`; an atom that does not know `PHP_ASTGEN_BIN`
1041
+ * simply ignores it and keeps today's per-file behaviour. Resolution order:
1042
+ * 1. explicit `PHP_ASTGEN_BIN` env var (operators / container images)
1043
+ * 2. `@appthreat/atom-parsetools/phpastgen.js` under cdxgen's own
1044
+ * node_modules, then under `GLOBAL_NODE_MODULES_PATH` for global installs
1045
+ *
1046
+ * @returns {string|undefined}
1047
+ */
1048
+ export function resolvePhpAstgenBin() {
1049
+ if (readEnvironmentVariable("PHP_ASTGEN_BIN")) {
1050
+ return readEnvironmentVariable("PHP_ASTGEN_BIN");
1051
+ }
1052
+ return parsetoolsFile("phpastgen.js");
1053
+ }
1054
+
1055
+ /**
1056
+ * Resolve a file inside the installed `@appthreat/atom-parsetools` package,
1057
+ * searching cdxgen's own node_modules and `GLOBAL_NODE_MODULES_PATH` for global
1058
+ * installs.
1059
+ *
1060
+ * @param {...string} segments Path segments below the package root
1061
+ * @returns {string|undefined}
1062
+ */
1063
+ export function parsetoolsFile(...segments) {
932
1064
  const roots = [dirNameStr];
933
1065
  if (readEnvironmentVariable("GLOBAL_NODE_MODULES_PATH")) {
934
1066
  roots.push(dirname(readEnvironmentVariable("GLOBAL_NODE_MODULES_PATH")));
@@ -939,9 +1071,7 @@ export function resolvePhpParseBin() {
939
1071
  "node_modules",
940
1072
  "@appthreat",
941
1073
  "atom-parsetools",
942
- "plugins",
943
- "bin",
944
- "php-parse",
1074
+ ...segments,
945
1075
  );
946
1076
  if (safeExistsSync(candidate)) {
947
1077
  return candidate;
@@ -1213,25 +1343,41 @@ export function atomTimeouts() {
1213
1343
  };
1214
1344
  }
1215
1345
 
1216
- function formatDuration(ms) {
1217
- if (ms >= 60_000) {
1218
- const minutes = Math.round(ms / 6_000) / 10;
1219
- return `${minutes} minute${minutes === 1 ? "" : "s"}`;
1220
- }
1221
- return `${Math.round(ms / 100) / 10} seconds`;
1222
- }
1223
-
1224
1346
  // The status the atom dispatcher exits with when ATOM_TIMEOUT stops atom.
1225
1347
  const ATOM_TIMEOUT_EXIT_CODE = 124;
1226
1348
 
1349
+ /**
1350
+ * Reap everything a timed-out atom run left behind on Windows.
1351
+ *
1352
+ * cdxgen starts atom through a shell there (`shell: isWin`), so the spawn
1353
+ * timeout terminates only that `cmd.exe`, and the runtime and every parser
1354
+ * worker under it (one `php.exe` per file for the per-file PHP frontend) keep
1355
+ * running against the next slice of the same run. See
1356
+ * {@link reapProcessTree} for how they are found. A chain broken by an
1357
+ * intermediate that already exited (the npm dispatcher stopping its runtime
1358
+ * itself) cannot be followed, and atom 4 stops its helpers once
1359
+ * `ATOM_PARENT_PID` is gone.
1360
+ *
1361
+ * @param {number|undefined} rootPid pid of the process cdxgen spawned
1362
+ * @param {number} startedAt Epoch milliseconds at which the run started
1363
+ * @param {number} [endedAt] Epoch milliseconds at which the spawned process ended
1364
+ * @returns {number[]} The pids that were stopped
1365
+ */
1366
+ export function reapAtomProcessTree(rootPid, startedAt, endedAt = Date.now()) {
1367
+ return reapProcessTree(rootPid, startedAt, endedAt, "atom");
1368
+ }
1369
+
1227
1370
  /**
1228
1371
  * Whether an atom run ended because it ran out of time.
1229
1372
  *
1230
1373
  * The dispatcher reports its own limit with status 124. cdxgen's spawn timeout
1231
1374
  * shows up as `ETIMEDOUT`. A dispatcher older than the status code stops atom
1232
- * with SIGTERM and exits 1, so status 1 at or past the limit counts as well,
1233
- * unless atom's own output shows it failed for another reason first (an
1234
- * exhausted heap, a crash), which deserves its own diagnosis.
1375
+ * with SIGTERM and passes on how the runtime ended: 1, or the runtime's own
1376
+ * exit on SIGTERM (143) or SIGKILL (137), which is what atom 3.1 reports. Those
1377
+ * statuses at or past the limit count as well, unless atom's own output shows
1378
+ * it failed for another reason first (an exhausted heap, a crash), which
1379
+ * deserves its own diagnosis. Missing one leaves the half-written atom behind,
1380
+ * and the next slice of the run fails to load it.
1235
1381
  *
1236
1382
  * @param {Object} result spawnSync result
1237
1383
  * @param {number} elapsedMs How long the run took
@@ -1245,13 +1391,21 @@ export function atomRunTimedOut(result, elapsedMs, atomTimeoutMs) {
1245
1391
  if (result?.error?.code === "ETIMEDOUT") {
1246
1392
  return true;
1247
1393
  }
1248
- if (result?.status !== 1 || elapsedMs < atomTimeoutMs) {
1394
+ if (
1395
+ !ATOM_STOPPED_STATUSES.includes(result?.status) ||
1396
+ elapsedMs < atomTimeoutMs
1397
+ ) {
1249
1398
  return false;
1250
1399
  }
1251
1400
  const output = `${result.stdout || ""}\n${result.stderr || ""}`;
1252
1401
  return !ATOM_FAILURE_MARKERS.some((marker) => output.includes(marker));
1253
1402
  }
1254
1403
 
1404
+ // How a runtime stopped by an older dispatcher at ATOM_TIMEOUT exits: the
1405
+ // dispatcher's own 1, or the runtime's exit on SIGTERM (128 + 15) or SIGKILL
1406
+ // (128 + 9) passed through.
1407
+ const ATOM_STOPPED_STATUSES = [1, 137, 143];
1408
+
1255
1409
  // Output that means atom failed by itself, whatever the clock says.
1256
1410
  const ATOM_FAILURE_MARKERS = [
1257
1411
  "OutOfMemoryError",
@@ -1350,10 +1504,12 @@ export function executeAtom(src, args, extra_env = {}) {
1350
1504
  env,
1351
1505
  timeout: spawnTimeoutMs,
1352
1506
  });
1353
- if (atomRunTimedOut(result, Date.now() - startedAt, atomTimeoutMs)) {
1507
+ const endedAt = Date.now();
1508
+ if (atomRunTimedOut(result, endedAt - startedAt, atomTimeoutMs)) {
1354
1509
  console.warn(
1355
1510
  `WARN: atom did not finish within ${formatDuration(atomTimeoutMs)} and was stopped, so its results are missing or incomplete. Allow more time with ATOM_TIMEOUT (milliseconds, atom only) or CDXGEN_TIMEOUT_MS (every external command).`,
1356
1511
  );
1512
+ reapAtomProcessTree(result.pid, startedAt, endedAt);
1357
1513
  // A stopped atom leaves its graph half written, and the next slice of the
1358
1514
  // same run would try to reuse it and fail to load it.
1359
1515
  const atomFileIndex = args.indexOf("-o");
@@ -1,4 +1,4 @@
1
- import { shouldRunPredictiveBomAudit } from "../core/env.js";
1
+ import { getCommandName, shouldRunPredictiveBomAudit } from "../core/env.js";
2
2
 
3
3
  /** Frozen list of hardware-bill-of-materials (HBOM) audit categories. */
4
4
  export const HBOM_AUDIT_CATEGORIES = Object.freeze([
@@ -23,6 +23,12 @@ export const GOLEM_AUDIT_CATEGORIES = Object.freeze([
23
23
  "golem-compliance",
24
24
  ]);
25
25
 
26
+ /** Frozen list of Scala Evinse (scalasem) audit categories. */
27
+ export const SCALA_AUDIT_CATEGORIES = Object.freeze([
28
+ "scala-security",
29
+ "scala-compliance",
30
+ ]);
31
+
26
32
  /** Frozen list of Cargo (Rust) audit categories. */
27
33
  export const CARGO_AUDIT_CATEGORIES = Object.freeze([
28
34
  "cargo-security",
@@ -64,6 +70,7 @@ export const BOM_AUDIT_CATEGORY_ALIASES = Object.freeze({
64
70
  golem: [...GOLEM_AUDIT_CATEGORIES],
65
71
  hbom: [...HBOM_AUDIT_CATEGORIES],
66
72
  rust: [...CARGO_AUDIT_CATEGORIES],
73
+ scala: [...SCALA_AUDIT_CATEGORIES],
67
74
  host: [...HBOM_AUDIT_CATEGORIES, ...HOST_TOPOLOGY_AUDIT_CATEGORIES],
68
75
  });
69
76
 
@@ -179,11 +186,11 @@ export function validateBomAuditCategories(categories, rules) {
179
186
  * @returns {string | undefined} Default category string, if any
180
187
  */
181
188
  export function getDefaultBomAuditCategories(options, commandPath) {
182
- const normalizedCommandPath = `${commandPath || ""}`.toLowerCase();
189
+ const commandName = getCommandName(commandPath);
183
190
  const defaultHbomCategories = options?.includeRuntime
184
191
  ? `${DEFAULT_HBOM_AUDIT_CATEGORIES},host-topology`
185
192
  : DEFAULT_HBOM_AUDIT_CATEGORIES;
186
- if (normalizedCommandPath.includes("hbom")) {
193
+ if (commandName.startsWith("hbom")) {
187
194
  return defaultHbomCategories;
188
195
  }
189
196
  const projectTypes = Array.isArray(options?.projectType)
@@ -0,0 +1,144 @@
1
+ import { recordDegradation } from "../core/buildLedger.js";
2
+ import { registerRunReset } from "../core/runState.js";
3
+
4
+ /**
5
+ * Text a build tool prints when its Maven repository answers HTTP 429, as
6
+ * recorded from each tool against a repository that rate limits. One small
7
+ * matcher per tool, plain substring tests on the combined output: the tools
8
+ * reword their messages between versions, so the matched fragment is the one
9
+ * every observed phrasing keeps (the status number in the tool's own wording).
10
+ *
11
+ * sbt, Mill and scala-cli resolve through Coursier, so they share the
12
+ * coursier matcher. The Coursier inside sbt 1.x reports the JDK's
13
+ * `Server returned HTTP response code: 429`; the one in sbt 2, Mill and
14
+ * scala-cli reports `retryable HTTP error: <url> (HTTP 429)`, with a
15
+ * `Retry-After` inside the parentheses when the server sent one.
16
+ *
17
+ * @type {Object<string, string[]>}
18
+ */
19
+ const RATE_LIMIT_MARKERS = {
20
+ maven: ["status code: 429", "Too Many Requests"],
21
+ gradle: ["Received status code 429", "[HTTP 429: http"],
22
+ coursier: ["response code: 429", "(HTTP 429"],
23
+ };
24
+
25
+ /**
26
+ * The matcher family a tool belongs to.
27
+ *
28
+ * @type {Object<string, string>}
29
+ */
30
+ const TOOL_MATCHER = {
31
+ maven: "maven",
32
+ gradle: "gradle",
33
+ sbt: "coursier",
34
+ mill: "coursier",
35
+ "scala-cli": "coursier",
36
+ };
37
+
38
+ /**
39
+ * How each tool is moved to a mirror, named in one hint per tool and scan.
40
+ * sbt reads its own repositories file; Mill and scala-cli read Coursier's
41
+ * mirror.properties, whose entry spells the semicolon-separated format
42
+ * because a comma list is silently ignored.
43
+ *
44
+ * @type {Object<string, string>}
45
+ */
46
+ const MIRROR_SETTINGS = {
47
+ maven:
48
+ "Point Maven at a mirror with a <mirror> entry in the Maven settings.xml, or set MVNW_REPOURL for the Maven wrapper.",
49
+ gradle:
50
+ "Point Gradle at a mirror by declaring the repository in a Gradle init script.",
51
+ sbt: "Point sbt at a mirror by listing it in ~/.sbt/repositories and passing -Dsbt.override.build.repos=true in SBT_OPTS or .sbtopts.",
52
+ coursier:
53
+ "Point Coursier at a mirror with mirror.properties, setting central.from and central.to (separate several central.from URLs with semicolons, not commas).",
54
+ };
55
+
56
+ /**
57
+ * Tools whose later invocations or retries cdxgen skips once one met a rate
58
+ * limit. Mill and scala-cli resolve in a single invocation.
59
+ *
60
+ * @type {Set<string>}
61
+ */
62
+ const STOPPED_TOOLS = new Set(["maven", "gradle", "sbt"]);
63
+
64
+ /**
65
+ * Tools whose repository answered HTTP 429 during the current scan. Further
66
+ * invocations of that tool would only pay the same back-off for the same
67
+ * empty answer.
68
+ *
69
+ * @type {Set<string>}
70
+ */
71
+ const rateLimitedTools = new Set();
72
+
73
+ registerRunReset("inventory.buildToolRateLimit", () => {
74
+ rateLimitedTools.clear();
75
+ });
76
+
77
+ /**
78
+ * Whether a tool's combined output names a rate limit from its repository.
79
+ * The answer text is treated as untrusted: only fixed substrings are looked
80
+ * for, nothing from the output is kept.
81
+ *
82
+ * @param {string} output Combined stdout and stderr of one invocation.
83
+ * @param {string} tool One of maven, gradle, sbt, mill, scala-cli.
84
+ * @returns {boolean} True when the output names an HTTP 429 answer.
85
+ */
86
+ export function detectBuildToolRateLimit(output, tool) {
87
+ const markers = RATE_LIMIT_MARKERS[TOOL_MATCHER[tool]];
88
+ if (!markers || typeof output !== "string" || !output) {
89
+ return false;
90
+ }
91
+ return markers.some((marker) => output.includes(marker));
92
+ }
93
+
94
+ /**
95
+ * Whether a tool was already found rate limited in this scan, so the caller
96
+ * can skip its remaining invocations.
97
+ *
98
+ * @param {string} tool One of maven, gradle, sbt, mill, scala-cli.
99
+ * @returns {boolean}
100
+ */
101
+ export function isBuildToolRateLimited(tool) {
102
+ return rateLimitedTools.has(tool);
103
+ }
104
+
105
+ /**
106
+ * Read a completed invocation's output for a rate limit. When one is found,
107
+ * the tool is remembered for the scan, one hint naming the tool's mirror
108
+ * setting is printed, and the `build.rate-limited` degradation is recorded.
109
+ * The first invocation that shows the rate limit is the one recorded; later
110
+ * ones add nothing, because the same answer ends them all.
111
+ *
112
+ * @param {string} tool One of maven, gradle, sbt, mill, scala-cli.
113
+ * @param {string|undefined} output Combined output, as commandOutputText
114
+ * returns it for a spawn result.
115
+ * @param {Object} [context] Call-site facts for the degradation event.
116
+ * @param {string} [context.command] The redacted command that was attempted.
117
+ * @param {number} [context.exitCode] The invocation's exit status.
118
+ * @returns {boolean} True when this output names a rate limit.
119
+ */
120
+ export function noteBuildToolRateLimit(tool, output, context = {}) {
121
+ if (!detectBuildToolRateLimit(output, tool)) {
122
+ return false;
123
+ }
124
+ if (isBuildToolRateLimited(tool)) {
125
+ return true;
126
+ }
127
+ rateLimitedTools.add(tool);
128
+ const stopped = STOPPED_TOOLS.has(tool)
129
+ ? `, so cdxgen runs no further ${tool} resolution in this scan and`
130
+ : ", so";
131
+ console.warn(
132
+ `The repository ${tool} downloads from is rate limiting this machine (HTTP 429)${stopped} its dependency tree is incomplete. ${MIRROR_SETTINGS[tool] || MIRROR_SETTINGS[TOOL_MATCHER[tool]]} cdxgen's own lookups can be moved with MAVEN_CENTRAL_URL.`,
133
+ );
134
+ recordDegradation("build.rate-limited", {
135
+ ecosystem: "java",
136
+ tool,
137
+ impact: "transitive-deps",
138
+ command: context.command,
139
+ exitCode: context.exitCode,
140
+ detail: `The repository ${tool} downloads from answered HTTP 429, so ${tool} resolved no dependencies.`,
141
+ outputExcerpt: output,
142
+ });
143
+ return true;
144
+ }
@@ -1,5 +1,6 @@
1
1
  /**
2
- * `cdxgen cache` subcommand — inspect and purge the metadata fetch cache.
2
+ * `cdxgen cache` subcommand — inspect and purge the metadata fetch cache that
3
+ * cdxrs and the JS batch pool share.
3
4
  *
4
5
  * This module operates directly on the filesystem because JS is the authority
5
6
  * for the cache directory. The cache layout is:
@@ -28,13 +28,21 @@ import { isMac, isWin } from "../core/paths.js";
28
28
  * rather than reading `isWin`/`isMac` here, or the Windows and Linux branches
29
29
  * become untestable on a macOS developer machine.
30
30
  *
31
+ * Every environment-derived root must be absolute by the *target* platform's
32
+ * rules, never the host's (`path.isAbsolute` would accept `\cache` on a
33
+ * Windows host): a relative value is ignored, so it cannot point inside the
34
+ * working directory. XDG requires this of `XDG_CACHE_HOME`; the home and
35
+ * `LOCALAPPDATA` roots get the same treatment for the same reason.
36
+ *
31
37
  * @param {Object} inputs Resolution inputs.
32
38
  * @param {"linux"|"macos"|"windows"} inputs.platform Which convention to apply.
33
- * @param {string} [inputs.cdxgenCacheDir] `CDXGEN_CACHE_DIR`.
39
+ * @param {string} [inputs.cdxgenCacheDir] `CDXGEN_CACHE_DIR`, an explicit
40
+ * override taken as given.
34
41
  * @param {string} [inputs.xdgCacheHome] `XDG_CACHE_HOME`. Ignored unless
35
- * absolute, per the XDG spec, so it cannot point inside the working directory.
36
- * @param {string} [inputs.home] Home directory.
37
- * @param {string} [inputs.localAppData] `LOCALAPPDATA`.
42
+ * `/`-rooted.
43
+ * @param {string} [inputs.home] Home directory. Ignored unless absolute.
44
+ * @param {string} [inputs.localAppData] `LOCALAPPDATA`. Ignored unless drive-
45
+ * or UNC-rooted; the home profile is the fallback.
38
46
  * @returns {string|null} Absolute path, or null when nothing resolves.
39
47
  */
40
48
  export function resolveCacheDirFor({
@@ -48,20 +56,55 @@ export function resolveCacheDirFor({
48
56
  return path.resolve(cdxgenCacheDir);
49
57
  }
50
58
  if (platform === "windows") {
51
- if (localAppData?.trim()) {
59
+ if (isWindowsAbsolute(localAppData)) {
52
60
  return path.join(localAppData, "cdxgen", "cache");
53
61
  }
54
- return home?.trim()
62
+ return isWindowsAbsolute(home)
55
63
  ? path.join(home, "AppData", "Local", "cdxgen", "cache")
56
64
  : null;
57
65
  }
58
66
  if (platform === "macos") {
59
- return home?.trim() ? path.join(home, "Library", "Caches", "cdxgen") : null;
67
+ return isPosixAbsolute(home)
68
+ ? path.join(home, "Library", "Caches", "cdxgen")
69
+ : null;
60
70
  }
61
- if (xdgCacheHome?.trim() && path.isAbsolute(xdgCacheHome)) {
71
+ if (isPosixAbsolute(xdgCacheHome)) {
62
72
  return path.join(xdgCacheHome, "cdxgen");
63
73
  }
64
- return home?.trim() ? path.join(home, ".cache", "cdxgen") : null;
74
+ return isPosixAbsolute(home) ? path.join(home, ".cache", "cdxgen") : null;
75
+ }
76
+
77
+ /**
78
+ * Absolute by POSIX rules: `/`-rooted.
79
+ *
80
+ * @param {string} [value] Candidate path
81
+ * @returns {boolean} Whether the path is absolute
82
+ */
83
+ function isPosixAbsolute(value) {
84
+ return typeof value === "string" && value.startsWith("/");
85
+ }
86
+
87
+ /**
88
+ * Absolute by Windows rules: a drive letter followed by a separator (`C:\` or
89
+ * `C:/`), or a UNC path (`\\server\share`). `C:cache` (relative to the drive's
90
+ * current directory) and `\cache` (relative to the current drive) are not.
91
+ * Mirrors `is_windows_absolute` in cdxrs.
92
+ *
93
+ * @param {string} [value] Candidate path
94
+ * @returns {boolean} Whether the path is absolute
95
+ */
96
+ function isWindowsAbsolute(value) {
97
+ if (typeof value !== "string" || value.length < 3) {
98
+ return false;
99
+ }
100
+ const isSeparator = (char) => char === "\\" || char === "/";
101
+ const driveRooted = /^[a-zA-Z]$/.test(value[0]) && value[1] === ":";
102
+ if (driveRooted) {
103
+ return isSeparator(value[2]);
104
+ }
105
+ return (
106
+ isSeparator(value[0]) && isSeparator(value[1]) && !isSeparator(value[2])
107
+ );
65
108
  }
66
109
 
67
110
  /**