@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.
- package/README.md +33 -14
- package/bin/cdxgen.js +310 -138
- package/bin/evinse.js +22 -0
- package/bin/repl.js +86 -0
- package/bin/sign.js +119 -15
- package/bin/tracebom.js +69 -5
- package/bin/validate.js +107 -19
- package/bin/verify.js +172 -63
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +85 -0
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +38 -6
- package/lib/cli/bomAssembly.js +105 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +156 -58
- package/lib/cli/jsBom.js +99 -53
- package/lib/cli/jvmBom.js +708 -147
- package/lib/cli/managedBom.js +415 -224
- package/lib/cli/nativeBom.js +264 -44
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +22 -15
- package/lib/core/fs.js +176 -7
- package/lib/core/httpClient.js +159 -24
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +274 -108
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/caxa.js +434 -2
- package/lib/ecosystems/cmakeResolver.js +76 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +399 -51
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1983 -793
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +356 -37
- package/lib/ecosystems/jsEvidence.js +104 -44
- package/lib/ecosystems/kosi.js +31 -7
- package/lib/ecosystems/npmutils.js +174 -9
- package/lib/ecosystems/parsers-dotnet.js +616 -159
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +371 -70
- package/lib/ecosystems/parsers-js.js +263 -76
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +929 -216
- package/lib/ecosystems/parsers-python.js +167 -24
- package/lib/ecosystems/parsers-rust.js +28 -22
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +1492 -182
- package/lib/ecosystems/rusi.js +54 -10
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +342 -0
- package/lib/ecosystems/scalasem.js +2618 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +1008 -191
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +1101 -222
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +703 -43
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cdxrs.js +103 -12
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +798 -57
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +90 -6
- package/lib/inventory/evidenceUtils.js +166 -113
- package/lib/inventory/fetchBatch.js +690 -55
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/protobom.js +8 -1
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +45 -27
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +295 -25
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +467 -46
- package/lib/managers/oci.js +63 -3
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +466 -0
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +23 -0
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/postgen.js +83 -16
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +64 -11
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +2 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +23 -23
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +24 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +9 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +55 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- package/types/lib/core/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- package/types/lib/ecosystems/bunutils.d.ts +12 -5
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- package/types/lib/ecosystems/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +28 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +17 -1
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +70 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +13 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +57 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +54 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +16 -5
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +70 -4
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +53 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +27 -2
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +164 -3
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +132 -15
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +182 -13
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +219 -5
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
- package/types/lib/inventory/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +59 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +121 -9
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +10 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +19 -4
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +9 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +59 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +42 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +17 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +77 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- package/types/lib/parsers/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +18 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
|
@@ -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
|
|
|
@@ -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:
|
package/lib/inventory/cdxrs.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module is the ONLY place that spawns the `cdxrs` binary. It enforces
|
|
5
5
|
* the governing invariant: **no failure mode may abort SBOM generation.**
|
|
6
|
-
* Every failure (missing binary, non-zero exit, timeout,
|
|
7
|
-
* version-major mismatch, CDXGEN_RS_DISABLE) logs once at
|
|
8
|
-
* a sentinel that makes the caller take the JS path.
|
|
6
|
+
* Every failure (missing binary, non-zero exit, timeout, oversized stdout,
|
|
7
|
+
* malformed stdout, version-major mismatch, CDXGEN_RS_DISABLE) logs once at
|
|
8
|
+
* `warn` and returns a sentinel that makes the caller take the JS path.
|
|
9
9
|
*
|
|
10
10
|
* Protocol:
|
|
11
11
|
* cdxrs <subcommand> --input <file|-> --output <file|-> --format json
|
|
@@ -42,6 +42,45 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
42
42
|
*/
|
|
43
43
|
const MAX_STDIN_BYTES = 32 * 1024 * 1024;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* V8's maximum string length on 64-bit builds (2^29 − 24). Node exposes no
|
|
47
|
+
* API for it, so it is stated exactly as the runtime's own ERR_STRING_TOO_LONG
|
|
48
|
+
* message does. `Buffer#toString()` throws above it — the crash from issue
|
|
49
|
+
* 4393, where a pnpm workspace's registry fetch crossed the ceiling.
|
|
50
|
+
*/
|
|
51
|
+
const V8_MAX_STRING_CHARS = 0x1fffffe8;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Ceiling on the stdout bytes collected from a cdxrs child. stdout must end up
|
|
55
|
+
* as a single JS string, so a payload above V8's string limit can never be
|
|
56
|
+
* delivered no matter how long the child runs: buffering it only burns memory
|
|
57
|
+
* until the concat throws. Past the ceiling the child is killed and the caller
|
|
58
|
+
* takes the JS path. Counting bytes (never smaller than the decoded character
|
|
59
|
+
* count) keeps the boundary safe for multibyte output.
|
|
60
|
+
*/
|
|
61
|
+
const DEFAULT_MAX_STDOUT_BYTES = V8_MAX_STRING_CHARS;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the stdout ceiling for a run. CDXGEN_RS_MAX_STDOUT_BYTES lowers (or
|
|
65
|
+
* raises) it — used by tests and by hosts that want the failover earlier.
|
|
66
|
+
*
|
|
67
|
+
* @returns {number} Maximum stdout bytes to collect before failing over.
|
|
68
|
+
*/
|
|
69
|
+
function maxStdoutBytes() {
|
|
70
|
+
const raw = readEnvironmentVariable("CDXGEN_RS_MAX_STDOUT_BYTES");
|
|
71
|
+
if (!raw) {
|
|
72
|
+
return DEFAULT_MAX_STDOUT_BYTES;
|
|
73
|
+
}
|
|
74
|
+
const parsed = Number.parseInt(raw, 10);
|
|
75
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
console.warn(
|
|
79
|
+
`cdxrs: ignoring malformed CDXGEN_RS_MAX_STDOUT_BYTES "${raw}"; using the default ${DEFAULT_MAX_STDOUT_BYTES} bytes.`,
|
|
80
|
+
);
|
|
81
|
+
return DEFAULT_MAX_STDOUT_BYTES;
|
|
82
|
+
}
|
|
83
|
+
|
|
45
84
|
/** Sentinel returned when the Rust path is unavailable. Callers take JS path. */
|
|
46
85
|
export const CDXRS_FALLBACK = Object.freeze({
|
|
47
86
|
ok: false,
|
|
@@ -164,6 +203,11 @@ function probeVersion(binPath) {
|
|
|
164
203
|
*
|
|
165
204
|
* Passing `content` is the normal case, since cdxgen holds BOMs in memory.
|
|
166
205
|
*
|
|
206
|
+
* Collected stdout is bounded: it must become a single JS string, so a child
|
|
207
|
+
* that would exceed V8's string limit (by default; see
|
|
208
|
+
* CDXGEN_RS_MAX_STDOUT_BYTES) is killed and reported as a fallback instead of
|
|
209
|
+
* crashing the process at the concat.
|
|
210
|
+
*
|
|
167
211
|
* @param {string} subcommand The subcommand to run (e.g. "info").
|
|
168
212
|
* @param {Object} opts
|
|
169
213
|
* @param {string|Buffer} [opts.content] BOM data to feed over stdin.
|
|
@@ -284,6 +328,9 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
284
328
|
let timedOut = false;
|
|
285
329
|
const stdoutChunks = [];
|
|
286
330
|
const stderrChunks = [];
|
|
331
|
+
const stdoutLimit = maxStdoutBytes();
|
|
332
|
+
let stdoutBytes = 0;
|
|
333
|
+
let stdoutOverflow = false;
|
|
287
334
|
|
|
288
335
|
const pluginRuntime = getDefaultPluginRuntime();
|
|
289
336
|
const env = { ...process.env };
|
|
@@ -313,6 +360,17 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
313
360
|
child.stdin.end(stdinContent);
|
|
314
361
|
}
|
|
315
362
|
|
|
363
|
+
// Kill the process group (-pid) so any child processes of cdxrs die too.
|
|
364
|
+
// Best effort: the process may have already exited, in which case the
|
|
365
|
+
// close/error handlers take over.
|
|
366
|
+
const killProcessGroup = () => {
|
|
367
|
+
try {
|
|
368
|
+
process.kill(-child.pid, "SIGKILL");
|
|
369
|
+
} catch {
|
|
370
|
+
child.kill("SIGKILL");
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
316
374
|
const timer = setTimeout(() => {
|
|
317
375
|
timedOut = true;
|
|
318
376
|
if (DEBUG_MODE) {
|
|
@@ -320,16 +378,21 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
320
378
|
`cdxrs: timeout after ${timeoutMs}ms, killing process group.`,
|
|
321
379
|
);
|
|
322
380
|
}
|
|
323
|
-
|
|
324
|
-
// Kill the process group (-pid) so any child processes of cdxrs die too.
|
|
325
|
-
process.kill(-child.pid, "SIGKILL");
|
|
326
|
-
} catch {
|
|
327
|
-
// Process may have already exited; fall through to error handling.
|
|
328
|
-
child.kill("SIGKILL");
|
|
329
|
-
}
|
|
381
|
+
killProcessGroup();
|
|
330
382
|
}, timeoutMs);
|
|
331
383
|
|
|
332
384
|
child.stdout?.on("data", (chunk) => {
|
|
385
|
+
if (stdoutOverflow) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
stdoutBytes += chunk.length;
|
|
389
|
+
if (stdoutBytes > stdoutLimit) {
|
|
390
|
+
// The payload can never become a deliverable string, so stop
|
|
391
|
+
// buffering, kill the child, and let the close handler fall back.
|
|
392
|
+
stdoutOverflow = true;
|
|
393
|
+
killProcessGroup();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
333
396
|
stdoutChunks.push(chunk);
|
|
334
397
|
});
|
|
335
398
|
|
|
@@ -365,8 +428,36 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
365
428
|
return;
|
|
366
429
|
}
|
|
367
430
|
|
|
368
|
-
|
|
369
|
-
|
|
431
|
+
// Checked before the exit code: the child was killed mid-stream for
|
|
432
|
+
// producing more stdout than can ever be delivered as a string, so even
|
|
433
|
+
// an exit-0 result is an undeliverable truncation (issue 4393).
|
|
434
|
+
if (stdoutOverflow) {
|
|
435
|
+
console.warn(
|
|
436
|
+
`cdxrs: stdout passed ${stdoutLimit} bytes (a string cannot exceed ${V8_MAX_STRING_CHARS} characters), so the child was stopped.`,
|
|
437
|
+
);
|
|
438
|
+
resolve({ ...CDXRS_FALLBACK, reason: "stdout-too-large" });
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
let stdout;
|
|
443
|
+
try {
|
|
444
|
+
stdout = Buffer.concat(stdoutChunks).toString();
|
|
445
|
+
} catch (err) {
|
|
446
|
+
// The byte cap above should make this unreachable; it is kept so that
|
|
447
|
+
// no failure inside this handler can abort SBOM generation.
|
|
448
|
+
console.warn(
|
|
449
|
+
`cdxrs: could not collect child stdout (${err.message}), falling back to JS path.`,
|
|
450
|
+
);
|
|
451
|
+
resolve({ ...CDXRS_FALLBACK, reason: "stdout-collect-failed" });
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
let stderr = "";
|
|
455
|
+
try {
|
|
456
|
+
stderr = Buffer.concat(stderrChunks).toString();
|
|
457
|
+
} catch {
|
|
458
|
+
// stderr carries NDJSON logs, already forwarded as they arrived;
|
|
459
|
+
// dropping the raw tail must not fail a run with valid stdout.
|
|
460
|
+
}
|
|
370
461
|
|
|
371
462
|
// For the validate subcommand, exit code 3 means "validation failure
|
|
372
463
|
// with findings on stdout" — the operation succeeded, the BOM just has
|
|
@@ -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
|
+
}
|