@cdxgen/cdxgen 13.2.0 → 13.4.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 (311) hide show
  1. package/README.md +33 -14
  2. package/bin/cdxgen.js +310 -138
  3. package/bin/evinse.js +22 -0
  4. package/bin/repl.js +86 -0
  5. package/bin/sign.js +119 -15
  6. package/bin/tracebom.js +69 -5
  7. package/bin/validate.js +107 -19
  8. package/bin/verify.js +172 -63
  9. package/data/helpers/init.gradle +46 -21
  10. package/data/remediations.json +85 -0
  11. package/data/rules/dependency-sources.yaml +67 -0
  12. package/data/rules/scala-evidence.yaml +115 -0
  13. package/lib/audit/index.js +38 -6
  14. package/lib/cli/bomAssembly.js +105 -15
  15. package/lib/cli/cliOptions.js +93 -1
  16. package/lib/cli/index.js +156 -58
  17. package/lib/cli/jsBom.js +99 -53
  18. package/lib/cli/jvmBom.js +708 -147
  19. package/lib/cli/managedBom.js +415 -224
  20. package/lib/cli/nativeBom.js +264 -44
  21. package/lib/core/activity.js +12 -0
  22. package/lib/core/env.js +22 -15
  23. package/lib/core/fs.js +176 -7
  24. package/lib/core/httpClient.js +159 -24
  25. package/lib/core/metadataProperties.js +97 -0
  26. package/lib/core/pluginRun.js +184 -0
  27. package/lib/core/processTree.js +100 -0
  28. package/lib/core/projectConfig.js +31 -4
  29. package/lib/core/runState.js +39 -0
  30. package/lib/core/toml.js +72 -0
  31. package/lib/ecosystems/asarutils.js +4 -2
  32. package/lib/ecosystems/bunutils.js +274 -108
  33. package/lib/ecosystems/cargoCache.js +231 -8
  34. package/lib/ecosystems/caxa.js +434 -2
  35. package/lib/ecosystems/cmakeResolver.js +76 -21
  36. package/lib/ecosystems/cppBuildContext.js +631 -0
  37. package/lib/ecosystems/cppEvidence.js +399 -51
  38. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  39. package/lib/ecosystems/denoutils.js +93 -66
  40. package/lib/ecosystems/ecosystems.js +1983 -793
  41. package/lib/ecosystems/golem.js +28 -12
  42. package/lib/ecosystems/gradleutils.js +356 -37
  43. package/lib/ecosystems/jsEvidence.js +104 -44
  44. package/lib/ecosystems/kosi.js +31 -7
  45. package/lib/ecosystems/npmutils.js +174 -9
  46. package/lib/ecosystems/parsers-dotnet.js +616 -159
  47. package/lib/ecosystems/parsers-elm.js +46 -28
  48. package/lib/ecosystems/parsers-gleam.js +3 -4
  49. package/lib/ecosystems/parsers-go.js +371 -70
  50. package/lib/ecosystems/parsers-js.js +263 -76
  51. package/lib/ecosystems/parsers-julia.js +2 -3
  52. package/lib/ecosystems/parsers-jvm.js +204 -12
  53. package/lib/ecosystems/parsers-misc.js +929 -216
  54. package/lib/ecosystems/parsers-python.js +167 -24
  55. package/lib/ecosystems/parsers-rust.js +28 -22
  56. package/lib/ecosystems/parsers-terraform.js +1573 -19
  57. package/lib/ecosystems/pythonutils.js +102 -6
  58. package/lib/ecosystems/remote/huggingface.js +3 -0
  59. package/lib/ecosystems/rubyutils.js +1492 -182
  60. package/lib/ecosystems/rusi.js +54 -10
  61. package/lib/ecosystems/sbtutils.js +243 -187
  62. package/lib/ecosystems/scalacliutils.js +342 -0
  63. package/lib/ecosystems/scalasem.js +2618 -0
  64. package/lib/ecosystems/terraformRegistry.js +288 -0
  65. package/lib/ecosystems/utils.js +2 -0
  66. package/lib/ecosystems/vendoredCode.js +156 -0
  67. package/lib/evinser/evinser.js +1008 -191
  68. package/lib/evinser/swiftsem.js +1914 -302
  69. package/lib/helpers/agenticToolutils.js +2 -2
  70. package/lib/helpers/bomSigner.js +1101 -222
  71. package/lib/inventory/aiCollector.js +4 -1
  72. package/lib/inventory/analyzer.js +110 -127
  73. package/lib/inventory/atomUtils.js +703 -43
  74. package/lib/inventory/auditCategories.js +7 -0
  75. package/lib/inventory/buildToolRateLimit.js +144 -0
  76. package/lib/inventory/cacheCommands.js +2 -1
  77. package/lib/inventory/cdxrs.js +103 -12
  78. package/lib/inventory/cmakeBuildDirs.js +254 -0
  79. package/lib/inventory/deps.js +501 -124
  80. package/lib/inventory/depsUtils.js +183 -11
  81. package/lib/inventory/display.js +4 -5
  82. package/lib/inventory/dosai.js +798 -57
  83. package/lib/inventory/dosaiParsers.js +310 -15
  84. package/lib/inventory/dynamic.js +90 -6
  85. package/lib/inventory/evidenceUtils.js +166 -113
  86. package/lib/inventory/fetchBatch.js +690 -55
  87. package/lib/inventory/fetchDiskCache.js +597 -0
  88. package/lib/inventory/forgeEnricher.js +14 -5
  89. package/lib/inventory/jvmLocalRepos.js +917 -0
  90. package/lib/inventory/mcpConfigParser.js +2 -3
  91. package/lib/inventory/parentSelection.js +146 -0
  92. package/lib/inventory/protobom.js +8 -1
  93. package/lib/inventory/purl.js +152 -3
  94. package/lib/inventory/rustFormulationParser.js +3 -4
  95. package/lib/inventory/sbomDocument.js +2 -70
  96. package/lib/inventory/scalaCoords.js +153 -0
  97. package/lib/inventory/source.js +45 -27
  98. package/lib/inventory/spdx.js +101 -0
  99. package/lib/inventory/traceRunner.js +295 -25
  100. package/lib/inventory/tracedCrypto.js +294 -0
  101. package/lib/managers/binary.js +115 -24
  102. package/lib/managers/docker.js +467 -46
  103. package/lib/managers/oci.js +63 -3
  104. package/lib/parsers/cmakeCache.js +69 -0
  105. package/lib/parsers/cmakeLists.js +466 -0
  106. package/lib/parsers/cmakePresets.js +400 -0
  107. package/lib/parsers/compileCommands.js +557 -0
  108. package/lib/parsers/largeJson.js +454 -0
  109. package/lib/parsers/npmrc.js +19 -1
  110. package/lib/parsers/strictJson.js +322 -0
  111. package/lib/parsers/terraformConfig.js +1073 -0
  112. package/lib/parsers/terraformSource.js +652 -0
  113. package/lib/server/server.js +23 -0
  114. package/lib/stages/postgen/introspection/markers.js +1 -1
  115. package/lib/stages/postgen/postgen.js +83 -16
  116. package/lib/stages/postgen/specVersionCompat.js +32 -0
  117. package/lib/validator/bomValidator.js +6 -16
  118. package/lib/validator/index.js +64 -11
  119. package/lib/validator/reporters/console.js +3 -0
  120. package/lib/validator/reporters/json.js +2 -0
  121. package/lib/validator/reporters/sarif.js +7 -0
  122. package/package.json +23 -23
  123. package/types/lib/audit/index.d.ts.map +1 -1
  124. package/types/lib/cli/bomAssembly.d.ts +2 -1
  125. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  126. package/types/lib/cli/cliOptions.d.ts +33 -1
  127. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  128. package/types/lib/cli/index.d.ts.map +1 -1
  129. package/types/lib/cli/jsBom.d.ts.map +1 -1
  130. package/types/lib/cli/jvmBom.d.ts +24 -1
  131. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  132. package/types/lib/cli/managedBom.d.ts +7 -4
  133. package/types/lib/cli/managedBom.d.ts.map +1 -1
  134. package/types/lib/cli/nativeBom.d.ts +3 -1
  135. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  136. package/types/lib/core/activity.d.ts.map +1 -1
  137. package/types/lib/core/env.d.ts +9 -0
  138. package/types/lib/core/env.d.ts.map +1 -1
  139. package/types/lib/core/fs.d.ts +55 -0
  140. package/types/lib/core/fs.d.ts.map +1 -1
  141. package/types/lib/core/httpClient.d.ts +13 -8
  142. package/types/lib/core/httpClient.d.ts.map +1 -1
  143. package/types/lib/core/metadataProperties.d.ts +39 -0
  144. package/types/lib/core/metadataProperties.d.ts.map +1 -0
  145. package/types/lib/core/pluginRun.d.ts +97 -0
  146. package/types/lib/core/pluginRun.d.ts.map +1 -0
  147. package/types/lib/core/processTree.d.ts +36 -0
  148. package/types/lib/core/processTree.d.ts.map +1 -0
  149. package/types/lib/core/projectConfig.d.ts +6 -2
  150. package/types/lib/core/projectConfig.d.ts.map +1 -1
  151. package/types/lib/core/runState.d.ts +29 -0
  152. package/types/lib/core/runState.d.ts.map +1 -0
  153. package/types/lib/core/toml.d.ts +20 -0
  154. package/types/lib/core/toml.d.ts.map +1 -0
  155. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  156. package/types/lib/ecosystems/bunutils.d.ts +12 -5
  157. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  158. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  159. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  160. package/types/lib/ecosystems/caxa.d.ts +14 -0
  161. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  162. package/types/lib/ecosystems/cmakeResolver.d.ts +28 -3
  163. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  164. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  165. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  166. package/types/lib/ecosystems/cppEvidence.d.ts +17 -1
  167. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  168. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  169. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  170. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  171. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  172. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  173. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  174. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  175. package/types/lib/ecosystems/gradleutils.d.ts +70 -4
  176. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  177. package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
  178. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  179. package/types/lib/ecosystems/kosi.d.ts +13 -0
  180. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  181. package/types/lib/ecosystems/npmutils.d.ts +57 -0
  182. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  183. package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
  184. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  187. package/types/lib/ecosystems/parsers-go.d.ts +54 -2
  188. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  189. package/types/lib/ecosystems/parsers-js.d.ts +16 -5
  190. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  191. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +70 -4
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +53 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  199. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  200. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  201. package/types/lib/ecosystems/pythonutils.d.ts +27 -2
  202. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  203. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  204. package/types/lib/ecosystems/rubyutils.d.ts +164 -3
  205. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/rusi.d.ts +13 -0
  207. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  208. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  209. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  210. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  211. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  212. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  213. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  214. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  215. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  216. package/types/lib/ecosystems/utils.d.ts +2 -2
  217. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  218. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  219. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  220. package/types/lib/evinser/evinser.d.ts +132 -15
  221. package/types/lib/evinser/evinser.d.ts.map +1 -1
  222. package/types/lib/evinser/swiftsem.d.ts +280 -15
  223. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  224. package/types/lib/helpers/bomSigner.d.ts +182 -13
  225. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  226. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  227. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  228. package/types/lib/inventory/atomUtils.d.ts +219 -5
  229. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  230. package/types/lib/inventory/auditCategories.d.ts +3 -0
  231. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  232. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  233. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  234. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  235. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  236. package/types/lib/inventory/cdxrs.d.ts +8 -3
  237. package/types/lib/inventory/cdxrs.d.ts.map +1 -1
  238. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  239. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  240. package/types/lib/inventory/deps.d.ts +59 -1
  241. package/types/lib/inventory/deps.d.ts.map +1 -1
  242. package/types/lib/inventory/depsUtils.d.ts +15 -0
  243. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  244. package/types/lib/inventory/display.d.ts.map +1 -1
  245. package/types/lib/inventory/dosai.d.ts +121 -9
  246. package/types/lib/inventory/dosai.d.ts.map +1 -1
  247. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  248. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  249. package/types/lib/inventory/dynamic.d.ts +10 -0
  250. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  251. package/types/lib/inventory/evidenceUtils.d.ts +19 -4
  252. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  253. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  254. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  255. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  256. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  257. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  258. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  259. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  260. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/parentSelection.d.ts +34 -0
  262. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  263. package/types/lib/inventory/plugins.d.ts.map +1 -1
  264. package/types/lib/inventory/protobom.d.ts.map +1 -1
  265. package/types/lib/inventory/purl.d.ts +71 -2
  266. package/types/lib/inventory/purl.d.ts.map +1 -1
  267. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  268. package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
  269. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  270. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  271. package/types/lib/inventory/source.d.ts.map +1 -1
  272. package/types/lib/inventory/spdx.d.ts +9 -0
  273. package/types/lib/inventory/spdx.d.ts.map +1 -1
  274. package/types/lib/inventory/traceRunner.d.ts +59 -1
  275. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  276. package/types/lib/inventory/tracedCrypto.d.ts +42 -0
  277. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  278. package/types/lib/managers/binary.d.ts +30 -1
  279. package/types/lib/managers/binary.d.ts.map +1 -1
  280. package/types/lib/managers/docker.d.ts +29 -0
  281. package/types/lib/managers/docker.d.ts.map +1 -1
  282. package/types/lib/managers/oci.d.ts +17 -1
  283. package/types/lib/managers/oci.d.ts.map +1 -1
  284. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  285. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  286. package/types/lib/parsers/cmakeLists.d.ts +77 -0
  287. package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
  288. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  289. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  290. package/types/lib/parsers/compileCommands.d.ts +115 -0
  291. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  292. package/types/lib/parsers/largeJson.d.ts +52 -0
  293. package/types/lib/parsers/largeJson.d.ts.map +1 -0
  294. package/types/lib/parsers/npmrc.d.ts +5 -0
  295. package/types/lib/parsers/npmrc.d.ts.map +1 -1
  296. package/types/lib/parsers/strictJson.d.ts +46 -0
  297. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  298. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  299. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  300. package/types/lib/parsers/terraformSource.d.ts +66 -0
  301. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  302. package/types/lib/server/server.d.ts.map +1 -1
  303. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  304. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  306. package/types/lib/validator/index.d.ts +18 -1
  307. package/types/lib/validator/index.d.ts.map +1 -1
  308. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  309. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  310. package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
  311. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -11,6 +11,10 @@ import {
11
11
  safeSpawnSync,
12
12
  } from "../core/fs.js";
13
13
  import { dirNameStr } from "../core/paths.js";
14
+ import {
15
+ reportPluginRunFailure,
16
+ resolvePluginSourceDir,
17
+ } from "../core/pluginRun.js";
14
18
  import { resolvePluginBinary } from "../inventory/plugins.js";
15
19
 
16
20
  const GO_LANGUAGES = new Set(["go", "golang"]);
@@ -1342,10 +1346,13 @@ function normalizedGolemDataFlowMode(options = {}) {
1342
1346
  return dataFlowMode;
1343
1347
  }
1344
1348
 
1345
- function normalizedGolemCallGraphMode(dataFlowMode, options = {}) {
1349
+ function normalizedGolemCallGraphMode(options = {}) {
1346
1350
  let callgraphMode = String(options.golemCallgraph || "static").toLowerCase();
1347
1351
  if (!GOLEM_CALLGRAPH_MODES.has(callgraphMode)) {
1348
- callgraphMode = dataFlowMode || "static";
1352
+ // An unrecognised mode falls back to the default. It used to fall back to
1353
+ // the data-flow mode ("security", "all"), which is not a call-graph mode
1354
+ // and made golem reject the run.
1355
+ callgraphMode = "static";
1349
1356
  }
1350
1357
  return callgraphMode;
1351
1358
  }
@@ -1637,11 +1644,17 @@ export function runGolemAnalysis(src, outputFile, options = {}) {
1637
1644
  return false;
1638
1645
  }
1639
1646
  const dataFlowMode = normalizedGolemDataFlowMode(options);
1640
- const callgraphMode = normalizedGolemCallGraphMode(dataFlowMode, options);
1647
+ const callgraphMode = normalizedGolemCallGraphMode(options);
1648
+ // cdxgen may run with its working directory already inside the project
1649
+ // while the source argument is relative to a repository root; a plain
1650
+ // resolve would join the two into a directory that does not exist. The
1651
+ // helper checks the result and falls back to the working directory when it
1652
+ // is itself the Go module under analysis.
1653
+ const analysisDir = resolvePluginSourceDir(src, ["go.mod", "go.work"]);
1641
1654
  const args = [
1642
1655
  "analyze",
1643
1656
  "--dir",
1644
- resolve(src),
1657
+ analysisDir,
1645
1658
  "--format",
1646
1659
  "json",
1647
1660
  "--callgraph",
@@ -1666,17 +1679,20 @@ export function runGolemAnalysis(src, outputFile, options = {}) {
1666
1679
  console.log("Executing", executable, args.join(" "));
1667
1680
  }
1668
1681
  const result = safeSpawnSync(executable, args, {
1669
- cwd: resolve(src),
1682
+ cwd: analysisDir,
1670
1683
  shell: false,
1671
1684
  });
1672
1685
  if (result?.status !== 0 || result?.error || !safeExistsSync(outputFile)) {
1673
- if (DEBUG_MODE) {
1674
- if (result?.stdout || result?.stderr) {
1675
- console.error(result.stdout, result.stderr);
1676
- } else {
1677
- console.log("Check if the golem plugin was installed successfully.");
1678
- }
1679
- }
1686
+ reportPluginRunFailure({
1687
+ tool: "golem",
1688
+ ecosystem: "go",
1689
+ executable,
1690
+ args,
1691
+ dir: analysisDir,
1692
+ result,
1693
+ outputFile,
1694
+ options,
1695
+ });
1680
1696
  return false;
1681
1697
  }
1682
1698
  return true;
@@ -1,10 +1,11 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { chmodSync, readFileSync } from "node:fs";
2
+ import { chmodSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { homedir, platform } from "node:os";
4
4
  import { basename, join, resolve } from "node:path";
5
+ import process from "node:process";
5
6
 
6
7
  import { build } from "@cdxgen/cdx-purl";
7
- import * as toml from "smol-toml";
8
+ import { valid } from "semver";
8
9
 
9
10
  import {
10
11
  DEBUG_MODE,
@@ -22,6 +23,11 @@ import {
22
23
  import { getAllFiles, safeExistsSync, safeSpawnSync } from "../core/fs.js";
23
24
  import { thoughtLog } from "../core/logger.js";
24
25
  import { isWin } from "../core/paths.js";
26
+ import { parseToml } from "../core/toml.js";
27
+ import {
28
+ isBuildToolRateLimited,
29
+ noteBuildToolRateLimit,
30
+ } from "../inventory/buildToolRateLimit.js";
25
31
  import { collectJarNS } from "../inventory/deps.js";
26
32
  import { minimumGradleVersionForJava } from "../inventory/jvmToolEnv.js";
27
33
  import { applyPurl, tryBuildPurl } from "../inventory/purl.js";
@@ -41,19 +47,33 @@ function isJvmToolPinned() {
41
47
  /**
42
48
  * Method to return the gradle command to use.
43
49
  *
50
+ * Project-local wrapper scripts (`gradlew`) are attacker-controlled files in
51
+ * the scanned project, so they are only used when dependency tooling is
52
+ * allowed (`options.installDeps !== false`); otherwise the system Gradle is
53
+ * used.
54
+ *
44
55
  * @param {string} srcPath Path to look for gradlew wrapper
45
56
  * @param {string|null} rootPath Root directory to look for gradlew wrapper
57
+ * @param {Object} [options] CLI options (`installDeps` gates wrapper use)
46
58
  */
47
- export function getGradleCommand(srcPath, rootPath) {
59
+ export function getGradleCommand(srcPath, rootPath, options = {}) {
48
60
  let gradleCmd = "gradle";
49
61
  const pinnedToolSelected = isJvmToolPinned();
62
+ // Introspection mode exists to observe the project's own toolchain, so it
63
+ // keeps using wrappers even when dependency installation is disabled.
64
+ const allowProjectWrappers =
65
+ options.installDeps !== false || options.introspect === true;
50
66
 
51
67
  let findGradleFile = "gradlew";
52
68
  if (platform() === "win32") {
53
69
  findGradleFile = "gradlew.bat";
54
70
  }
55
71
 
56
- if (!pinnedToolSelected && safeExistsSync(join(srcPath, findGradleFile))) {
72
+ if (
73
+ allowProjectWrappers &&
74
+ !pinnedToolSelected &&
75
+ safeExistsSync(join(srcPath, findGradleFile))
76
+ ) {
57
77
  // Use local gradle wrapper if available
58
78
  // Enable execute permission
59
79
  try {
@@ -72,6 +92,7 @@ export function getGradleCommand(srcPath, rootPath) {
72
92
  reason: `Selected project-local Gradle wrapper ${gradleCmd}.`,
73
93
  });
74
94
  } else if (
95
+ allowProjectWrappers &&
75
96
  !pinnedToolSelected &&
76
97
  rootPath &&
77
98
  safeExistsSync(join(rootPath, findGradleFile))
@@ -137,6 +158,65 @@ export function getGradleCommand(srcPath, rootPath) {
137
158
  return gradleCmd;
138
159
  }
139
160
 
161
+ /**
162
+ * System properties that force the evaluation model cdxgen's Gradle
163
+ * introspection depends on.
164
+ *
165
+ * The bundled init script registers `afterEvaluate` on every project and a
166
+ * `taskGraph.whenReady` listener, so it cannot run under isolated projects
167
+ * (which forbid reaching into another project's model and fail the build at
168
+ * configuration time, see issue #4444) nor under the configuration cache (whose
169
+ * reuse skips init scripts entirely, dropping the `<CDXGEN:repository>` and
170
+ * `<CDXGEN:includedBuild>` markers even when the first run succeeded). Both
171
+ * features are commonly enabled from `gradle.properties` (e.g. okhttp), which
172
+ * command-line `-D` flags override, so every cdxgen invocation turns them
173
+ * off: these are ephemeral introspection builds, not the user's build. The
174
+ * isolated-projects property is spelled `org.gradle.unsafe.isolated-projects`
175
+ * from Gradle 8.6 until Gradle 9.7 renamed it, so both spellings are forced
176
+ * off to cover every Gradle in between. The configuration cache likewise
177
+ * keeps its pre-8.1 `org.gradle.unsafe.configuration-cache` spelling.
178
+ *
179
+ * Each feature is handled as one group. Gradle disables a feature when any of
180
+ * its spellings is explicitly `false`, so if the user already chose a value
181
+ * via `GRADLE_ARGS`, either as a `-D` property under any spelling or as the
182
+ * `--[no-]isolated-projects` / `--[no-]configuration-cache` option (which
183
+ * beats `-D` properties), cdxgen adds no override for that feature at all.
184
+ *
185
+ * @param {string[]} gradleArguments User-supplied general gradle arguments.
186
+ * @returns {string[]} The `-D` flags to inject before the user's arguments.
187
+ */
188
+ function gradleEvaluationOverrides(gradleArguments) {
189
+ const features = [
190
+ {
191
+ option: "isolated-projects",
192
+ properties: [
193
+ "org.gradle.isolated-projects",
194
+ "org.gradle.unsafe.isolated-projects",
195
+ ],
196
+ },
197
+ {
198
+ option: "configuration-cache",
199
+ properties: [
200
+ "org.gradle.configuration-cache",
201
+ "org.gradle.unsafe.configuration-cache",
202
+ ],
203
+ },
204
+ ];
205
+ const overrides = [];
206
+ for (const { option, properties } of features) {
207
+ const userChose = gradleArguments.some(
208
+ (arg) =>
209
+ arg === `--${option}` ||
210
+ arg === `--no-${option}` ||
211
+ properties.some((property) => arg.startsWith(`-D${property}=`)),
212
+ );
213
+ if (!userChose) {
214
+ overrides.push(...properties.map((property) => `-D${property}=false`));
215
+ }
216
+ }
217
+ return overrides;
218
+ }
219
+
140
220
  /**
141
221
  * Method to combine the general gradle arguments, the sub-commands and the sub-commands' arguments in the correct way
142
222
  *
@@ -160,6 +240,7 @@ export function buildGradleCommandArguments(
160
240
  "--no-parallel",
161
241
  ]
162
242
  .concat(getGradleDaemonParameter())
243
+ .concat(gradleEvaluationOverrides(gradleArguments))
163
244
  .concat(gradleArguments);
164
245
  const maxCliArgsLength = isWin
165
246
  ? 7500 - gradleCommandLength - mainGradleArguments.join(" ").length - 2
@@ -202,47 +283,87 @@ function getGradleDaemonParameter() {
202
283
  }
203
284
  }
204
285
 
286
+ /**
287
+ * Parse a Gradle task header line such as `> Task :app:dependencies FAILED`
288
+ * into the path of the project that owns the task and the task's name.
289
+ *
290
+ * @param {string} line A line of Gradle output
291
+ * @returns {{projectPath: string, taskName: string}|undefined} The owning
292
+ * project's path (`""` for a root task such as `:dependencies`) and the task
293
+ * name, or undefined when the line is not a task header
294
+ */
295
+ function parseGradleTaskHeader(line) {
296
+ if (!line.startsWith("> Task :")) {
297
+ return undefined;
298
+ }
299
+ // The path is the first token: a status such as UP-TO-DATE or FAILED may
300
+ // follow it, and a CRLF line keeps its carriage return.
301
+ const taskPath = line.slice("> Task ".length).trim().split(/\s+/)[0];
302
+ const lastColon = taskPath.lastIndexOf(":");
303
+ return {
304
+ projectPath: taskPath.slice(0, lastColon),
305
+ taskName: taskPath.slice(lastColon + 1),
306
+ };
307
+ }
308
+
205
309
  /**
206
310
  * Method to split the output produced by Gradle using parallel processing by project
207
311
  *
312
+ * Gradle groups console output by task and prints a `> Task :path` header
313
+ * before each group, repeating it whenever a task's output resumes after
314
+ * another task's. Each group is therefore keyed by the project in its own
315
+ * header (`> Task :app:dependencies` belongs to `:app`), never by whatever
316
+ * report header happened to precede it, and the groups of one project are
317
+ * concatenated in order, so reordering or interrupting the task reports cannot
318
+ * move a report to another project or overwrite one (issue 4465). The groups
319
+ * of a root task (`:dependencies`) all take the root project's name from the
320
+ * `Root project '…'` header that only a root task prints, so a continuation
321
+ * group, which carries no report header, stays with the rest of the root
322
+ * report. The groups of other tasks are dropped along with their headers.
323
+ *
208
324
  * @param {string} rawOutput Full output produced by Gradle using parallel processing
209
325
  * @param {string[]} relevantTasks The list of gradle tasks whose output need to be considered.
210
326
  * @returns {map} Map with subProject names as keys and corresponding dependency task outputs as values.
211
327
  */
212
328
  export function splitOutputByGradleProjects(rawOutput, relevantTasks) {
213
329
  const outputSplitBySubprojects = new Map();
214
- let subProjectOut = "";
215
- const outSplitByLine = rawOutput.split("\n");
216
- let currentProjectName = "root";
217
- const regexPatternForRelevantTasks = `.*:(${relevantTasks.join("|")})(?=s|\r|$)`;
218
- const regexForRelevantTasks = new RegExp(regexPatternForRelevantTasks);
219
- for (const [i, line] of outSplitByLine.entries()) {
220
- //filter out everything before first task output
221
- if (!line.startsWith("> Task :") && subProjectOut === "") {
222
- continue;
330
+ const relevantTaskNames = new Set(relevantTasks);
331
+ const taskChunks = [];
332
+ // Lines before the first task header (build logs) belong to no task.
333
+ let currentChunk;
334
+ for (const line of rawOutput.split("\n")) {
335
+ const taskHeader = parseGradleTaskHeader(line);
336
+ if (taskHeader) {
337
+ currentChunk = relevantTaskNames.has(taskHeader.taskName)
338
+ ? { projectPath: taskHeader.projectPath, lines: [line] }
339
+ : undefined;
340
+ if (currentChunk) {
341
+ taskChunks.push(currentChunk);
342
+ }
343
+ } else if (currentChunk) {
344
+ currentChunk.lines.push(line);
223
345
  }
224
-
225
- //ignore output of irrelevant tasks
226
- if (line.startsWith("> Task :") && !regexForRelevantTasks.test(line)) {
346
+ }
347
+ let rootProjectName;
348
+ for (const chunk of taskChunks) {
349
+ if (chunk.projectPath) {
227
350
  continue;
228
351
  }
229
-
230
- if (line.startsWith("Root project '") || line.startsWith("Project ':")) {
231
- currentProjectName = line.split("'")[1];
232
- outputSplitBySubprojects.set(currentProjectName, "");
233
- }
234
- // if previous subProject has ended, push to array and reset subProject string
235
- if (line.startsWith("> Task :") && subProjectOut !== "") {
236
- outputSplitBySubprojects.set(currentProjectName, subProjectOut);
237
- subProjectOut = "";
238
- }
239
- //if in subproject block, keep appending to string
240
- subProjectOut += `${line}\n`;
241
- //if end of last dependencies output block, push to array
242
- if (i === outSplitByLine.length - 1) {
243
- outputSplitBySubprojects.set(currentProjectName, subProjectOut);
352
+ const rootHeader = chunk.lines.find((line) =>
353
+ line.startsWith("Root project '"),
354
+ );
355
+ if (rootHeader) {
356
+ rootProjectName = rootHeader.split("'")[1];
357
+ break;
244
358
  }
245
359
  }
360
+ for (const chunk of taskChunks) {
361
+ const projectName = chunk.projectPath || rootProjectName || "root";
362
+ outputSplitBySubprojects.set(
363
+ projectName,
364
+ `${outputSplitBySubprojects.get(projectName) || ""}${chunk.lines.join("\n")}\n`,
365
+ );
366
+ }
246
367
  return outputSplitBySubprojects;
247
368
  }
248
369
 
@@ -380,8 +501,14 @@ export function executeParallelGradleProperties(
380
501
  dir,
381
502
  allProjectsStr,
382
503
  extraArgs = [],
504
+ options = {},
383
505
  ) {
384
- const gradleCmd = getGradleCommand(dir, null);
506
+ // This runs once per properties round, so a rate limit an earlier round met
507
+ // ends the later rounds too.
508
+ if (isBuildToolRateLimited("gradle")) {
509
+ return "";
510
+ }
511
+ const gradleCmd = getGradleCommand(dir, null, options);
385
512
  const gradleArgs = buildGradleCommandArguments(
386
513
  extraArgs.concat(
387
514
  readEnvironmentVariable("GRADLE_ARGS")
@@ -407,6 +534,22 @@ export function executeParallelGradleProperties(
407
534
  cwd: dir,
408
535
  shell: isWin,
409
536
  });
537
+ // A properties invocation needs the same repository the build does, so a
538
+ // 429 answer here ends every further gradle invocation in this scan. The
539
+ // dependencies task can even exit 0 under a rate limit, so the output is
540
+ // read whatever the status is. The properties this invocation did print
541
+ // are kept.
542
+ if (
543
+ noteBuildToolRateLimit("gradle", commandOutputText(result), {
544
+ command: `${basename(gradleCmd)} ${gradleArg.join(" ")}`,
545
+ exitCode: typeof result.status === "number" ? result.status : undefined,
546
+ })
547
+ ) {
548
+ if (result.stdout) {
549
+ allOutputs.push(result.stdout);
550
+ }
551
+ break;
552
+ }
410
553
  if (result.status !== 0 || result.error) {
411
554
  recordGradleInvocationFailure(result, {
412
555
  command: `${basename(gradleCmd)} ${gradleArg.join(" ")}`,
@@ -1205,11 +1348,16 @@ export async function collectGradleDependencies(
1205
1348
  /**
1206
1349
  * Method to return the mill command to use.
1207
1350
  *
1351
+ * The project-local `mill` script is an attacker-controlled file in the
1352
+ * scanned project, so it is only used when dependency tooling is allowed
1353
+ * (`options.installDeps !== false`).
1354
+ *
1208
1355
  * @param {string} srcPath Path to look for mill wrapper
1356
+ * @param {Object} [options] CLI options (`installDeps` gates wrapper use)
1209
1357
  */
1210
- export function getMillCommand(srcPath) {
1358
+ export function getMillCommand(srcPath, options = {}) {
1211
1359
  let millCmd = `mill${platform() === "win32" ? ".bat" : ""}`;
1212
- if (safeExistsSync(join(srcPath, millCmd))) {
1360
+ if (options.installDeps !== false && safeExistsSync(join(srcPath, millCmd))) {
1213
1361
  // Use local mill wrapper if available
1214
1362
  // Enable execute permission
1215
1363
  try {
@@ -1223,18 +1371,184 @@ export function getMillCommand(srcPath) {
1223
1371
  return millCmd;
1224
1372
  }
1225
1373
 
1374
+ /**
1375
+ * Determine the Mill version of a build.
1376
+ *
1377
+ * The version is read from the `.mill-version` file, from the
1378
+ * `//| mill-version:` header of the build file, or from the launcher itself.
1379
+ *
1380
+ * @param {string} millRootPath Root of the Mill build
1381
+ * @param {string} millCmd Mill command to use for the fallback probe
1382
+ * @returns {string|null} The Mill version, or null when it cannot be determined
1383
+ */
1384
+ export function getMillVersion(millRootPath, millCmd) {
1385
+ const millVersionFile = join(millRootPath, ".mill-version");
1386
+ if (safeExistsSync(millVersionFile)) {
1387
+ const version = (readFileSync(millVersionFile, "utf-8") || "").trim();
1388
+ if (valid(version)) {
1389
+ return version;
1390
+ }
1391
+ }
1392
+ for (const buildFile of ["build.mill", "build.sc"]) {
1393
+ const buildFilePath = join(millRootPath, buildFile);
1394
+ if (!safeExistsSync(buildFilePath)) {
1395
+ continue;
1396
+ }
1397
+ const headerMatch = /^\/\/[|\s]\s*mill-version:\s*(\S+)/m.exec(
1398
+ readFileSync(buildFilePath, "utf-8"),
1399
+ );
1400
+ if (headerMatch?.[1] && valid(headerMatch[1])) {
1401
+ return headerMatch[1];
1402
+ }
1403
+ }
1404
+ const versionResult = safeSpawnSync(millCmd, ["--no-server", "--version"], {
1405
+ cwd: millRootPath,
1406
+ shell: isWin,
1407
+ });
1408
+ const versionMatch = /version\s+(\S+)/.exec(`${versionResult.stdout || ""}`);
1409
+ return versionMatch?.[1] && valid(versionMatch[1]) ? versionMatch[1] : null;
1410
+ }
1411
+
1412
+ /**
1413
+ * Find the Mill daemons running under a tree.
1414
+ *
1415
+ * Daemons record their process id in `out/mill-daemon*` (Mill 1.x) or
1416
+ * `out/mill-server*` directories, directly or one level below (Mill 0.12
1417
+ * keeps one subdirectory per server). Only processes that are still alive
1418
+ * are returned.
1419
+ *
1420
+ * @param {string} rootPath Tree to search
1421
+ * @returns {Map<string, Set<number>>} Live daemon process ids by the root of
1422
+ * the build they belong to
1423
+ */
1424
+ export function findMillDaemons(rootPath) {
1425
+ const daemons = new Map();
1426
+ const readPid = (dir) => {
1427
+ for (const candidate of [dir, ...safeSubdirs(dir)]) {
1428
+ try {
1429
+ const pid = Number.parseInt(
1430
+ readFileSync(join(candidate, "processId"), "utf-8").trim(),
1431
+ 10,
1432
+ );
1433
+ if (pid > 0 && isProcessAlive(pid)) {
1434
+ return pid;
1435
+ }
1436
+ } catch (_err) {
1437
+ // no process id recorded in this directory
1438
+ }
1439
+ }
1440
+ return undefined;
1441
+ };
1442
+ const visit = (dir, depth) => {
1443
+ if (depth > 4) {
1444
+ return;
1445
+ }
1446
+ let entries;
1447
+ try {
1448
+ entries = readdirSync(dir, { withFileTypes: true });
1449
+ } catch (_err) {
1450
+ return;
1451
+ }
1452
+ for (const entry of entries) {
1453
+ if (!entry.isDirectory()) {
1454
+ continue;
1455
+ }
1456
+ if (entry.name === "out") {
1457
+ for (const name of safeSubdirs(join(dir, entry.name))) {
1458
+ const base = basename(name);
1459
+ if (
1460
+ !base.startsWith("mill-daemon") &&
1461
+ !base.startsWith("mill-server")
1462
+ ) {
1463
+ continue;
1464
+ }
1465
+ const pid = readPid(name);
1466
+ if (pid) {
1467
+ if (!daemons.has(dir)) {
1468
+ daemons.set(dir, new Set());
1469
+ }
1470
+ daemons.get(dir).add(pid);
1471
+ }
1472
+ }
1473
+ } else if (!["target", "node_modules", ".git"].includes(entry.name)) {
1474
+ visit(join(dir, entry.name), depth + 1);
1475
+ }
1476
+ }
1477
+ };
1478
+ visit(rootPath, 0);
1479
+ return daemons;
1480
+ }
1481
+
1482
+ function safeSubdirs(dir) {
1483
+ try {
1484
+ return readdirSync(dir, { withFileTypes: true })
1485
+ .filter((entry) => entry.isDirectory())
1486
+ .map((entry) => join(dir, entry.name));
1487
+ } catch (_err) {
1488
+ return [];
1489
+ }
1490
+ }
1491
+
1492
+ function isProcessAlive(pid) {
1493
+ try {
1494
+ process.kill(pid, 0);
1495
+ return true;
1496
+ } catch (err) {
1497
+ return err?.code === "EPERM";
1498
+ }
1499
+ }
1500
+
1501
+ /**
1502
+ * Stop the Mill daemons that started under the scanned tree during the scan.
1503
+ *
1504
+ * cdxgen's own Mill calls run with --no-server, but a project build cdxgen
1505
+ * runs can shell out to Mill without it and leave a daemon behind. Daemons
1506
+ * that were already running when the scan started belong to the user, for
1507
+ * example to an IDE, and are left alone; every other live daemon is shut
1508
+ * down through the launcher of the build it belongs to.
1509
+ *
1510
+ * @param {string} rootPath Scanned tree
1511
+ * @param {Map<string, Set<number>>} [runningBefore] Result of
1512
+ * {@link findMillDaemons} taken before the scan
1513
+ */
1514
+ export function stopMillDaemons(rootPath, runningBefore = new Map()) {
1515
+ for (const [buildRoot, pids] of findMillDaemons(rootPath)) {
1516
+ const before = runningBefore.get(buildRoot) || new Set();
1517
+ if ([...pids].every((pid) => before.has(pid))) {
1518
+ continue;
1519
+ }
1520
+ const launcher = safeExistsSync(join(buildRoot, "mill"))
1521
+ ? join(buildRoot, "mill")
1522
+ : "mill";
1523
+ console.log("Stopping the Mill daemon the scan started in", buildRoot);
1524
+ safeSpawnSync(launcher, ["shutdown"], {
1525
+ cwd: buildRoot,
1526
+ shell: isWin,
1527
+ });
1528
+ }
1529
+ }
1530
+
1226
1531
  /**
1227
1532
  * Method to return the maven command to use.
1228
1533
  *
1534
+ * Project-local wrapper scripts (`mvnw`) are attacker-controlled files in the
1535
+ * scanned project, so they are only used when dependency tooling is allowed
1536
+ * (`options.installDeps !== false`); otherwise the installed Maven is used.
1537
+ *
1229
1538
  * @param {string} srcPath Path to look for maven wrapper
1230
1539
  * @param {string} rootPath Root directory to look for maven wrapper
1540
+ * @param {Object} [options] CLI options (`installDeps` gates wrapper use)
1231
1541
  */
1232
- export function getMavenCommand(srcPath, rootPath) {
1542
+ export function getMavenCommand(srcPath, rootPath, options = {}) {
1233
1543
  let mavenCmd = "mvn";
1234
1544
  // Check if the wrapper script is both available and functional
1235
1545
  let isWrapperReady = false;
1236
1546
  let isWrapperFound = false;
1237
1547
  const pinnedToolSelected = isJvmToolPinned();
1548
+ // Introspection mode exists to observe the project's own toolchain, so it
1549
+ // keeps using wrappers even when dependency installation is disabled.
1550
+ const allowProjectWrappers =
1551
+ options.installDeps !== false || options.introspect === true;
1238
1552
  let findMavenFile = "mvnw";
1239
1553
  let mavenWrapperCmd = null;
1240
1554
  if (platform() === "win32") {
@@ -1247,7 +1561,11 @@ export function getMavenCommand(srcPath, rootPath) {
1247
1561
  }
1248
1562
  }
1249
1563
 
1250
- if (!pinnedToolSelected && safeExistsSync(join(srcPath, findMavenFile))) {
1564
+ if (
1565
+ allowProjectWrappers &&
1566
+ !pinnedToolSelected &&
1567
+ safeExistsSync(join(srcPath, findMavenFile))
1568
+ ) {
1251
1569
  // Use local maven wrapper if available
1252
1570
  // Enable execute permission
1253
1571
  try {
@@ -1267,6 +1585,7 @@ export function getMavenCommand(srcPath, rootPath) {
1267
1585
  reason: `Found Maven wrapper candidate ${mavenWrapperCmd}.`,
1268
1586
  });
1269
1587
  } else if (
1588
+ allowProjectWrappers &&
1270
1589
  !pinnedToolSelected &&
1271
1590
  rootPath &&
1272
1591
  safeExistsSync(join(rootPath, findMavenFile))
@@ -1410,7 +1729,7 @@ export function getMavenCommand(srcPath, rootPath) {
1410
1729
  export function parseGradleVersionCatalog(catalogFile) {
1411
1730
  let catalog;
1412
1731
  try {
1413
- catalog = toml.parse(readFileSync(catalogFile, "utf-8"));
1732
+ catalog = parseToml(readFileSync(catalogFile, "utf-8"));
1414
1733
  } catch (error) {
1415
1734
  console.warn(`Failed to parse ${catalogFile}: ${error.message}`);
1416
1735
  return [];