@cdxgen/cdxgen 13.3.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 (255) hide show
  1. package/README.md +9 -6
  2. package/bin/cdxgen.js +90 -26
  3. package/bin/evinse.js +18 -1
  4. package/bin/repl.js +86 -0
  5. package/bin/sign.js +45 -15
  6. package/bin/tracebom.js +5 -1
  7. package/bin/validate.js +84 -18
  8. package/bin/verify.js +155 -86
  9. package/data/helpers/init.gradle +46 -21
  10. package/data/remediations.json +61 -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/index.js +113 -54
  15. package/lib/cli/jsBom.js +50 -29
  16. package/lib/cli/jvmBom.js +665 -138
  17. package/lib/cli/managedBom.js +320 -200
  18. package/lib/cli/nativeBom.js +139 -30
  19. package/lib/core/activity.js +12 -0
  20. package/lib/core/env.js +17 -9
  21. package/lib/core/fs.js +3 -2
  22. package/lib/core/httpClient.js +159 -24
  23. package/lib/core/pluginRun.js +184 -0
  24. package/lib/core/processTree.js +100 -0
  25. package/lib/core/runState.js +39 -0
  26. package/lib/core/toml.js +72 -0
  27. package/lib/ecosystems/asarutils.js +4 -2
  28. package/lib/ecosystems/bunutils.js +5 -7
  29. package/lib/ecosystems/cargoCache.js +231 -8
  30. package/lib/ecosystems/cmakeResolver.js +10 -21
  31. package/lib/ecosystems/cppBuildContext.js +631 -0
  32. package/lib/ecosystems/cppEvidence.js +337 -22
  33. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  34. package/lib/ecosystems/denoutils.js +93 -66
  35. package/lib/ecosystems/ecosystems.js +1967 -788
  36. package/lib/ecosystems/golem.js +28 -12
  37. package/lib/ecosystems/gradleutils.js +314 -30
  38. package/lib/ecosystems/jsEvidence.js +85 -24
  39. package/lib/ecosystems/kosi.js +21 -4
  40. package/lib/ecosystems/npmutils.js +64 -0
  41. package/lib/ecosystems/parsers-dotnet.js +567 -151
  42. package/lib/ecosystems/parsers-elm.js +46 -28
  43. package/lib/ecosystems/parsers-gleam.js +3 -4
  44. package/lib/ecosystems/parsers-go.js +324 -61
  45. package/lib/ecosystems/parsers-js.js +92 -26
  46. package/lib/ecosystems/parsers-julia.js +2 -3
  47. package/lib/ecosystems/parsers-jvm.js +204 -12
  48. package/lib/ecosystems/parsers-misc.js +182 -55
  49. package/lib/ecosystems/parsers-python.js +141 -10
  50. package/lib/ecosystems/parsers-rust.js +4 -4
  51. package/lib/ecosystems/parsers-terraform.js +1573 -19
  52. package/lib/ecosystems/pythonutils.js +102 -6
  53. package/lib/ecosystems/remote/huggingface.js +3 -0
  54. package/lib/ecosystems/rubyutils.js +67 -17
  55. package/lib/ecosystems/rusi.js +22 -9
  56. package/lib/ecosystems/sbtutils.js +243 -187
  57. package/lib/ecosystems/scalacliutils.js +342 -0
  58. package/lib/ecosystems/scalasem.js +2618 -0
  59. package/lib/ecosystems/terraformRegistry.js +288 -0
  60. package/lib/ecosystems/utils.js +2 -0
  61. package/lib/ecosystems/vendoredCode.js +156 -0
  62. package/lib/evinser/evinser.js +578 -69
  63. package/lib/helpers/agenticToolutils.js +2 -2
  64. package/lib/helpers/bomSigner.js +576 -128
  65. package/lib/inventory/aiCollector.js +4 -1
  66. package/lib/inventory/analyzer.js +110 -127
  67. package/lib/inventory/atomUtils.js +171 -48
  68. package/lib/inventory/auditCategories.js +7 -0
  69. package/lib/inventory/buildToolRateLimit.js +144 -0
  70. package/lib/inventory/cacheCommands.js +2 -1
  71. package/lib/inventory/cmakeBuildDirs.js +254 -0
  72. package/lib/inventory/deps.js +501 -124
  73. package/lib/inventory/dosai.js +248 -44
  74. package/lib/inventory/dosaiParsers.js +310 -15
  75. package/lib/inventory/dynamic.js +52 -6
  76. package/lib/inventory/evidenceUtils.js +154 -109
  77. package/lib/inventory/fetchBatch.js +574 -38
  78. package/lib/inventory/fetchDiskCache.js +597 -0
  79. package/lib/inventory/forgeEnricher.js +14 -5
  80. package/lib/inventory/jvmLocalRepos.js +917 -0
  81. package/lib/inventory/mcpConfigParser.js +2 -3
  82. package/lib/inventory/parentSelection.js +146 -0
  83. package/lib/inventory/rustFormulationParser.js +3 -4
  84. package/lib/inventory/scalaCoords.js +153 -0
  85. package/lib/inventory/source.js +8 -8
  86. package/lib/inventory/spdx.js +101 -0
  87. package/lib/inventory/traceRunner.js +88 -7
  88. package/lib/inventory/tracedCrypto.js +294 -0
  89. package/lib/managers/docker.js +33 -32
  90. package/lib/managers/oci.js +24 -2
  91. package/lib/parsers/cmakeCache.js +69 -0
  92. package/lib/parsers/cmakeLists.js +16 -2
  93. package/lib/parsers/cmakePresets.js +400 -0
  94. package/lib/parsers/compileCommands.js +557 -0
  95. package/lib/parsers/strictJson.js +322 -0
  96. package/lib/parsers/terraformConfig.js +1073 -0
  97. package/lib/parsers/terraformSource.js +652 -0
  98. package/lib/server/server.js +23 -0
  99. package/lib/stages/postgen/introspection/markers.js +1 -1
  100. package/lib/stages/postgen/postgen.js +15 -1
  101. package/lib/stages/postgen/specVersionCompat.js +32 -0
  102. package/lib/validator/index.js +25 -10
  103. package/lib/validator/reporters/json.js +1 -0
  104. package/package.json +21 -21
  105. package/types/lib/audit/index.d.ts.map +1 -1
  106. package/types/lib/cli/index.d.ts.map +1 -1
  107. package/types/lib/cli/jsBom.d.ts.map +1 -1
  108. package/types/lib/cli/jvmBom.d.ts +24 -1
  109. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  110. package/types/lib/cli/managedBom.d.ts +7 -4
  111. package/types/lib/cli/managedBom.d.ts.map +1 -1
  112. package/types/lib/cli/nativeBom.d.ts +3 -1
  113. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  114. package/types/lib/core/activity.d.ts.map +1 -1
  115. package/types/lib/core/env.d.ts +9 -0
  116. package/types/lib/core/env.d.ts.map +1 -1
  117. package/types/lib/core/fs.d.ts.map +1 -1
  118. package/types/lib/core/httpClient.d.ts +13 -8
  119. package/types/lib/core/httpClient.d.ts.map +1 -1
  120. package/types/lib/core/pluginRun.d.ts +97 -0
  121. package/types/lib/core/pluginRun.d.ts.map +1 -0
  122. package/types/lib/core/processTree.d.ts +36 -0
  123. package/types/lib/core/processTree.d.ts.map +1 -0
  124. package/types/lib/core/runState.d.ts +29 -0
  125. package/types/lib/core/runState.d.ts.map +1 -0
  126. package/types/lib/core/toml.d.ts +20 -0
  127. package/types/lib/core/toml.d.ts.map +1 -0
  128. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  129. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  130. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  131. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  132. package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
  133. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  134. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  135. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  136. package/types/lib/ecosystems/cppEvidence.d.ts +18 -2
  137. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  138. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  139. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  140. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  141. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  142. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  143. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  144. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  145. package/types/lib/ecosystems/gradleutils.d.ts +50 -0
  146. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  147. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  148. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  149. package/types/lib/ecosystems/npmutils.d.ts +15 -0
  150. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  151. package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
  152. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  153. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  154. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  155. package/types/lib/ecosystems/parsers-go.d.ts +44 -2
  156. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  157. package/types/lib/ecosystems/parsers-js.d.ts +6 -1
  158. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  159. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  160. package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
  161. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  162. package/types/lib/ecosystems/parsers-misc.d.ts +3 -1
  163. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  164. package/types/lib/ecosystems/parsers-python.d.ts +53 -3
  165. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  166. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  167. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  168. package/types/lib/ecosystems/pythonutils.d.ts +27 -2
  169. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  170. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  171. package/types/lib/ecosystems/rubyutils.d.ts +21 -1
  172. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  173. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  174. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  175. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  176. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  177. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  178. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  179. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  180. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  181. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  182. package/types/lib/ecosystems/utils.d.ts +2 -2
  183. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  184. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  185. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  186. package/types/lib/evinser/evinser.d.ts +75 -8
  187. package/types/lib/evinser/evinser.d.ts.map +1 -1
  188. package/types/lib/helpers/bomSigner.d.ts +80 -4
  189. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  190. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  191. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  192. package/types/lib/inventory/atomUtils.d.ts +82 -10
  193. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  194. package/types/lib/inventory/auditCategories.d.ts +3 -0
  195. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  196. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  197. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  198. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  199. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  200. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  201. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  202. package/types/lib/inventory/deps.d.ts +59 -1
  203. package/types/lib/inventory/deps.d.ts.map +1 -1
  204. package/types/lib/inventory/dosai.d.ts +57 -8
  205. package/types/lib/inventory/dosai.d.ts.map +1 -1
  206. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  207. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  208. package/types/lib/inventory/dynamic.d.ts +10 -0
  209. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  210. package/types/lib/inventory/evidenceUtils.d.ts +19 -4
  211. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  212. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  213. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  214. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  215. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  216. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  217. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  218. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  219. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  220. package/types/lib/inventory/parentSelection.d.ts +34 -0
  221. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  222. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  223. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  224. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  225. package/types/lib/inventory/source.d.ts.map +1 -1
  226. package/types/lib/inventory/spdx.d.ts +9 -0
  227. package/types/lib/inventory/spdx.d.ts.map +1 -1
  228. package/types/lib/inventory/traceRunner.d.ts +12 -0
  229. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  230. package/types/lib/inventory/tracedCrypto.d.ts +42 -0
  231. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  232. package/types/lib/managers/docker.d.ts.map +1 -1
  233. package/types/lib/managers/oci.d.ts +3 -1
  234. package/types/lib/managers/oci.d.ts.map +1 -1
  235. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  236. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  237. package/types/lib/parsers/cmakeLists.d.ts +2 -1
  238. package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
  239. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  240. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  241. package/types/lib/parsers/compileCommands.d.ts +115 -0
  242. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  243. package/types/lib/parsers/strictJson.d.ts +46 -0
  244. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  245. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  246. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  247. package/types/lib/parsers/terraformSource.d.ts +66 -0
  248. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  249. package/types/lib/server/server.d.ts.map +1 -1
  250. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  251. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  252. package/types/lib/validator/index.d.ts +8 -0
  253. package/types/lib/validator/index.d.ts.map +1 -1
  254. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  255. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -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:
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Where a C/C++ project's build trees are: the build directories its CMake
3
+ * presets configure, and the conventional ones (`build`, `build-<name>`,
4
+ * `out`, `builddir`, `cmake-build-<name>`). Shared by the lookups for
5
+ * `CMakeCache.txt` and for the compilation database, so both search the same
6
+ * places in the same order.
7
+ */
8
+
9
+ import { readdirSync, readFileSync, statSync } from "node:fs";
10
+ import { platform } from "node:os";
11
+ import {
12
+ basename,
13
+ delimiter,
14
+ dirname,
15
+ isAbsolute,
16
+ join,
17
+ relative,
18
+ resolve,
19
+ } from "node:path";
20
+
21
+ import { isSecureMode, readEnvironmentVariable } from "../core/activity.js";
22
+ import {
23
+ expandPresetMacros,
24
+ parseCmakePresets,
25
+ resolveConfigurePresets,
26
+ } from "../parsers/cmakePresets.js";
27
+
28
+ /** Presets files CMake reads from the source directory, in order. */
29
+ const PRESETS_FILES = ["CMakePresets.json", "CMakeUserPresets.json"];
30
+
31
+ /** A presets file larger than this is not read. */
32
+ const MAX_PRESETS_FILE_BYTES = 4 * 1024 * 1024;
33
+
34
+ /** At most this many presets files are read, includes counted. */
35
+ const MAX_PRESETS_FILES = 64;
36
+
37
+ /** At most this many build directories are proposed. */
38
+ const MAX_BUILD_DIRS = 128;
39
+
40
+ const HOST_SYSTEM_NAMES = {
41
+ darwin: "Darwin",
42
+ linux: "Linux",
43
+ win32: "Windows",
44
+ freebsd: "FreeBSD",
45
+ openbsd: "OpenBSD",
46
+ netbsd: "NetBSD",
47
+ sunos: "SunOS",
48
+ aix: "AIX",
49
+ };
50
+
51
+ /**
52
+ * Whether `child` is `parent` or inside it.
53
+ *
54
+ * @param {string} child Path to test
55
+ * @param {string} parent Directory
56
+ * @returns {boolean}
57
+ */
58
+ export function isInsideDir(child, parent) {
59
+ const rel = relative(resolve(parent), resolve(child));
60
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
61
+ }
62
+
63
+ function isDirectory(p) {
64
+ try {
65
+ return statSync(p).isDirectory();
66
+ } catch (_err) {
67
+ return false;
68
+ }
69
+ }
70
+
71
+ function readSmallFile(p) {
72
+ try {
73
+ const stat = statSync(p);
74
+ if (!stat.isFile() || stat.size > MAX_PRESETS_FILE_BYTES) {
75
+ return undefined;
76
+ }
77
+ return readFileSync(p, "utf-8");
78
+ } catch (_err) {
79
+ return undefined;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * CMake's `${hostSystemName}` for this host.
85
+ *
86
+ * @returns {string}
87
+ */
88
+ export function hostSystemName() {
89
+ return HOST_SYSTEM_NAMES[platform()] || platform();
90
+ }
91
+
92
+ /**
93
+ * Read a project's presets documents: `CMakePresets.json` and
94
+ * `CMakeUserPresets.json`, each followed by the files it includes (schema
95
+ * version 4 and later). In secure mode an include outside the project is not
96
+ * read.
97
+ *
98
+ * @param {string} root Project source directory
99
+ * @returns {Array<{file: string, fileDir: string, document: Object}>}
100
+ */
101
+ export function readCmakePresetDocuments(root) {
102
+ const sourceDir = resolve(root);
103
+ const documents = [];
104
+ const seen = new Set();
105
+ const visit = (file) => {
106
+ const abs = resolve(file);
107
+ if (seen.has(abs) || seen.size >= MAX_PRESETS_FILES) {
108
+ return;
109
+ }
110
+ seen.add(abs);
111
+ if (isSecureMode && !isInsideDir(abs, sourceDir)) {
112
+ return;
113
+ }
114
+ const document = parseCmakePresets(readSmallFile(abs));
115
+ if (!document) {
116
+ return;
117
+ }
118
+ const fileDir = dirname(abs);
119
+ documents.push({ file: abs, fileDir, document });
120
+ for (const include of document.include) {
121
+ const expanded = expandPresetMacros(include, {
122
+ sourceDir,
123
+ sourceParentDir: dirname(sourceDir),
124
+ sourceDirName: basename(sourceDir),
125
+ fileDir,
126
+ pathListSep: delimiter,
127
+ hostSystemName: hostSystemName(),
128
+ penv: readEnvironmentVariable,
129
+ });
130
+ visit(isAbsolute(expanded) ? expanded : join(fileDir, expanded));
131
+ }
132
+ };
133
+ for (const name of PRESETS_FILES) {
134
+ visit(join(sourceDir, name));
135
+ }
136
+ return documents;
137
+ }
138
+
139
+ /**
140
+ * The visible configure presets of a project, resolved and with their macros
141
+ * expanded for this host.
142
+ *
143
+ * @param {string} root Project source directory
144
+ * @returns {Object[]} See `resolveConfigurePresets`
145
+ */
146
+ export function readCmakeConfigurePresets(root) {
147
+ const sourceDir = resolve(root);
148
+ const documents = readCmakePresetDocuments(sourceDir);
149
+ if (!documents.length) {
150
+ return [];
151
+ }
152
+ return resolveConfigurePresets(documents, {
153
+ sourceDir,
154
+ sourceParentDir: dirname(sourceDir),
155
+ sourceDirName: basename(sourceDir),
156
+ hostSystemName: hostSystemName(),
157
+ pathListSep: delimiter,
158
+ penv: readEnvironmentVariable,
159
+ });
160
+ }
161
+
162
+ function childDirs(dir, accept = () => true) {
163
+ try {
164
+ return readdirSync(dir, { withFileTypes: true })
165
+ .filter((e) => e.isDirectory() && accept(e.name))
166
+ .map((e) => join(dir, e.name))
167
+ .sort();
168
+ } catch (_err) {
169
+ return [];
170
+ }
171
+ }
172
+
173
+ /** The order conventional build directories are searched in. */
174
+ function conventionalRank(dir) {
175
+ const name = basename(dir);
176
+ if (name === "build") {
177
+ return 0;
178
+ }
179
+ if (name === "out") {
180
+ return 2;
181
+ }
182
+ if (name === "builddir") {
183
+ return 3;
184
+ }
185
+ if (name.startsWith("cmake-build-")) {
186
+ return 4;
187
+ }
188
+ return 1;
189
+ }
190
+
191
+ /**
192
+ * The directories a project's build trees may be in, most specific first:
193
+ * the directory of an explicit `--cmake-cache`, the build directories of its
194
+ * configure presets, then the `build`, `build-<name>`, `out`, `builddir` and
195
+ * `cmake-build-<name>` directories at the project root, and the directories
196
+ * one level below `build`, `out` and `out/build` (where Visual Studio puts
197
+ * preset builds). Only existing directories are listed; in secure mode, only
198
+ * those inside the project.
199
+ *
200
+ * @param {string} root Project source directory
201
+ * @param {Object} [options] CLI options (`cmakeCache`)
202
+ * @returns {string[]} Absolute directories, without duplicates
203
+ */
204
+ export function cmakeBuildDirCandidates(root, options = {}) {
205
+ const sourceDir = resolve(root);
206
+ const dirs = [];
207
+ const add = (d) => {
208
+ if (!d || dirs.length >= MAX_BUILD_DIRS) {
209
+ return;
210
+ }
211
+ const abs = resolve(d);
212
+ if (dirs.includes(abs) || abs === sourceDir) {
213
+ return;
214
+ }
215
+ if (isSecureMode && !isInsideDir(abs, sourceDir)) {
216
+ return;
217
+ }
218
+ if (isDirectory(abs)) {
219
+ dirs.push(abs);
220
+ }
221
+ };
222
+ if (options.cmakeCache) {
223
+ add(dirname(resolve(`${options.cmakeCache}`)));
224
+ }
225
+ if (!isDirectory(sourceDir)) {
226
+ return dirs;
227
+ }
228
+ for (const preset of readCmakeConfigurePresets(sourceDir)) {
229
+ add(preset.binaryDir);
230
+ }
231
+ const rootBuildDirs = childDirs(
232
+ sourceDir,
233
+ (name) =>
234
+ name.startsWith("build") ||
235
+ name === "out" ||
236
+ name.startsWith("cmake-build-"),
237
+ );
238
+ rootBuildDirs.sort(
239
+ (a, b) => conventionalRank(a) - conventionalRank(b) || a.localeCompare(b),
240
+ );
241
+ for (const d of rootBuildDirs) {
242
+ add(d);
243
+ }
244
+ for (const parent of [
245
+ join(sourceDir, "build"),
246
+ join(sourceDir, "out"),
247
+ join(sourceDir, "out", "build"),
248
+ ]) {
249
+ for (const d of childDirs(parent)) {
250
+ add(d);
251
+ }
252
+ }
253
+ return dirs;
254
+ }