@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
package/lib/ecosystems/golem.js
CHANGED
|
@@ -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(
|
|
1349
|
+
function normalizedGolemCallGraphMode(options = {}) {
|
|
1346
1350
|
let callgraphMode = String(options.golemCallgraph || "static").toLowerCase();
|
|
1347
1351
|
if (!GOLEM_CALLGRAPH_MODES.has(callgraphMode)) {
|
|
1348
|
-
|
|
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(
|
|
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
|
-
|
|
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:
|
|
1682
|
+
cwd: analysisDir,
|
|
1670
1683
|
shell: false,
|
|
1671
1684
|
});
|
|
1672
1685
|
if (result?.status !== 0 || result?.error || !safeExistsSync(outputFile)) {
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
226
|
-
|
|
346
|
+
}
|
|
347
|
+
let rootProjectName;
|
|
348
|
+
for (const chunk of taskChunks) {
|
|
349
|
+
if (chunk.projectPath) {
|
|
227
350
|
continue;
|
|
228
351
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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 (
|
|
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 =
|
|
1732
|
+
catalog = parseToml(readFileSync(catalogFile, "utf-8"));
|
|
1414
1733
|
} catch (error) {
|
|
1415
1734
|
console.warn(`Failed to parse ${catalogFile}: ${error.message}`);
|
|
1416
1735
|
return [];
|