@cdxgen/cdxgen 13.3.0 → 13.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +115 -31
- package/bin/evinse.js +23 -1
- package/bin/hbom.js +15 -7
- package/bin/repl.js +115 -0
- package/bin/sign.js +45 -15
- package/bin/tracebom.js +5 -1
- package/bin/validate.js +84 -18
- package/bin/verify.js +155 -86
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/helpers/init.gradle +46 -21
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +344 -1
- package/data/rules/cbom-security.yaml +235 -4
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/go-binaries.yaml +46 -0
- package/data/rules/golem-go.yaml +80 -0
- package/data/rules/hbom-compliance.yaml +40 -22
- package/data/rules/hbom-performance.yaml +7 -1
- package/data/rules/host-topology.yaml +1 -1
- package/data/rules/obom-runtime.yaml +31 -2
- package/data/rules/php-runtime.yaml +143 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +52 -6
- package/lib/cli/bomAssembly.js +28 -3
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +186 -58
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +779 -146
- package/lib/cli/managedBom.js +1021 -261
- package/lib/cli/nativeBom.js +364 -58
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +47 -17
- package/lib/core/fs.js +31 -21
- package/lib/core/httpClient.js +233 -69
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- 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 +5 -7
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/cmakeResolver.js +10 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +337 -22
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1967 -788
- package/lib/ecosystems/golem.js +578 -18
- package/lib/ecosystems/gradleutils.js +340 -46
- package/lib/ecosystems/jsEvidence.js +85 -24
- package/lib/ecosystems/kosi.js +21 -4
- package/lib/ecosystems/npmutils.js +64 -0
- package/lib/ecosystems/parsers-dotnet.js +668 -153
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +1283 -297
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +463 -29
- package/lib/ecosystems/parsers-misc.js +405 -93
- package/lib/ecosystems/parsers-python.js +152 -20
- package/lib/ecosystems/parsers-rust.js +4 -4
- 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 +73 -20
- package/lib/ecosystems/rusi.js +32 -9
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +343 -0
- package/lib/ecosystems/scalasem.js +2620 -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 +695 -78
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +204 -48
- package/lib/inventory/auditCategories.js +10 -3
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +507 -128
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +747 -101
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +96 -8
- package/lib/inventory/envcontext.js +168 -5
- package/lib/inventory/evidenceUtils.js +590 -132
- package/lib/inventory/fetchBatch.js +574 -38
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/formulationParsers.js +3 -2
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +89 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/phpBuildInfo.js +487 -0
- package/lib/inventory/phpHostProbe.js +641 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +215 -11
- package/lib/inventory/spdx.js +245 -0
- package/lib/inventory/traceRunner.js +93 -8
- package/lib/inventory/tracedCrypto.js +423 -0
- package/lib/managers/binary.js +529 -183
- package/lib/managers/docker.js +199 -44
- package/lib/managers/oci.js +24 -2
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +16 -2
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +34 -2
- 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 +209 -1
- package/lib/stages/postgen/annotator.js +17 -1
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/introspection/reflect.js +4 -1
- package/lib/stages/postgen/postgen.js +20 -6
- package/lib/stages/postgen/ruleEngine.js +58 -1
- package/lib/stages/postgen/specVersionCompat.js +62 -0
- package/lib/stages/pregen/pregen.js +117 -5
- package/lib/validator/bomValidator.js +26 -5
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +26 -7
- package/lib/validator/generated/validate-1.7.mjs +31 -12
- package/lib/validator/generated/validate-2.0.mjs +30 -11
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +23 -23
- package/types/bin/cdxgen-secure.d.ts +3 -0
- package/types/bin/cdxgen-secure.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +9 -2
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +43 -0
- 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 +44 -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 +27 -2
- package/types/lib/core/env.d.ts.map +1 -1
- 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/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.map +1 -1
- package/types/lib/core/projectTooling.d.ts +41 -0
- package/types/lib/core/projectTooling.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +26 -0
- package/types/lib/core/propertySanitizer.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.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/cmakeResolver.d.ts +5 -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 +19 -3
- 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 +81 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +59 -7
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +15 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +155 -5
- 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 +119 -5
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +6 -1
- 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 +74 -3
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +21 -59
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +57 -6
- package/types/lib/ecosystems/parsers-python.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 +26 -1
- 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 +21 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- 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 +100 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +10 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +80 -4
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
- package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
- package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +96 -10
- 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/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/certificates.d.ts +205 -0
- package/types/lib/inventory/certificates.d.ts.map +1 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts +61 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts +18 -0
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +202 -14
- 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 +16 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts +40 -2
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +48 -14
- 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/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/goBuildInfo.d.ts +10 -0
- package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/inventory/hostTopology.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/osPackagePurl.d.ts +99 -0
- package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +6 -2
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/osinfo.d.ts +31 -6
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/osqueryTransform.d.ts +51 -0
- package/types/lib/inventory/osqueryTransform.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/phpBuildInfo.d.ts +142 -0
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/phpHostProbe.d.ts +35 -0
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rootfsCerts.d.ts +27 -0
- package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.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 +28 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +24 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +16 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +64 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +10 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +3 -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 +2 -1
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
- 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/email.d.ts +19 -0
- package/types/lib/parsers/email.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +13 -0
- package/types/lib/parsers/largeJson.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 +8 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +14 -1
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +8 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
|
@@ -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,12 @@ 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 { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
27
|
+
import { parseToml } from "../core/toml.js";
|
|
28
|
+
import {
|
|
29
|
+
isBuildToolRateLimited,
|
|
30
|
+
noteBuildToolRateLimit,
|
|
31
|
+
} from "../inventory/buildToolRateLimit.js";
|
|
25
32
|
import { collectJarNS } from "../inventory/deps.js";
|
|
26
33
|
import { minimumGradleVersionForJava } from "../inventory/jvmToolEnv.js";
|
|
27
34
|
import { applyPurl, tryBuildPurl } from "../inventory/purl.js";
|
|
@@ -38,13 +45,16 @@ function isJvmToolPinned() {
|
|
|
38
45
|
);
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
// The gate lives in lib/core so that every layer can reach it; it stays
|
|
49
|
+
// exported here for existing callers.
|
|
50
|
+
export { isProjectToolingAllowed };
|
|
51
|
+
|
|
41
52
|
/**
|
|
42
53
|
* Method to return the gradle command to use.
|
|
43
54
|
*
|
|
44
55
|
* Project-local wrapper scripts (`gradlew`) are attacker-controlled files in
|
|
45
|
-
* the scanned project, so they are only used when
|
|
46
|
-
*
|
|
47
|
-
* used.
|
|
56
|
+
* the scanned project, so they are only used when the project's own tooling
|
|
57
|
+
* may run (see isProjectToolingAllowed); otherwise the system Gradle is used.
|
|
48
58
|
*
|
|
49
59
|
* @param {string} srcPath Path to look for gradlew wrapper
|
|
50
60
|
* @param {string|null} rootPath Root directory to look for gradlew wrapper
|
|
@@ -53,10 +63,7 @@ function isJvmToolPinned() {
|
|
|
53
63
|
export function getGradleCommand(srcPath, rootPath, options = {}) {
|
|
54
64
|
let gradleCmd = "gradle";
|
|
55
65
|
const pinnedToolSelected = isJvmToolPinned();
|
|
56
|
-
|
|
57
|
-
// keeps using wrappers even when dependency installation is disabled.
|
|
58
|
-
const allowProjectWrappers =
|
|
59
|
-
options.installDeps !== false || options.introspect === true;
|
|
66
|
+
const allowProjectWrappers = isProjectToolingAllowed(options);
|
|
60
67
|
|
|
61
68
|
let findGradleFile = "gradlew";
|
|
62
69
|
if (platform() === "win32") {
|
|
@@ -152,6 +159,65 @@ export function getGradleCommand(srcPath, rootPath, options = {}) {
|
|
|
152
159
|
return gradleCmd;
|
|
153
160
|
}
|
|
154
161
|
|
|
162
|
+
/**
|
|
163
|
+
* System properties that force the evaluation model cdxgen's Gradle
|
|
164
|
+
* introspection depends on.
|
|
165
|
+
*
|
|
166
|
+
* The bundled init script registers `afterEvaluate` on every project and a
|
|
167
|
+
* `taskGraph.whenReady` listener, so it cannot run under isolated projects
|
|
168
|
+
* (which forbid reaching into another project's model and fail the build at
|
|
169
|
+
* configuration time, see issue #4444) nor under the configuration cache (whose
|
|
170
|
+
* reuse skips init scripts entirely, dropping the `<CDXGEN:repository>` and
|
|
171
|
+
* `<CDXGEN:includedBuild>` markers even when the first run succeeded). Both
|
|
172
|
+
* features are commonly enabled from `gradle.properties` (e.g. okhttp), which
|
|
173
|
+
* command-line `-D` flags override, so every cdxgen invocation turns them
|
|
174
|
+
* off: these are ephemeral introspection builds, not the user's build. The
|
|
175
|
+
* isolated-projects property is spelled `org.gradle.unsafe.isolated-projects`
|
|
176
|
+
* from Gradle 8.6 until Gradle 9.7 renamed it, so both spellings are forced
|
|
177
|
+
* off to cover every Gradle in between. The configuration cache likewise
|
|
178
|
+
* keeps its pre-8.1 `org.gradle.unsafe.configuration-cache` spelling.
|
|
179
|
+
*
|
|
180
|
+
* Each feature is handled as one group. Gradle disables a feature when any of
|
|
181
|
+
* its spellings is explicitly `false`, so if the user already chose a value
|
|
182
|
+
* via `GRADLE_ARGS`, either as a `-D` property under any spelling or as the
|
|
183
|
+
* `--[no-]isolated-projects` / `--[no-]configuration-cache` option (which
|
|
184
|
+
* beats `-D` properties), cdxgen adds no override for that feature at all.
|
|
185
|
+
*
|
|
186
|
+
* @param {string[]} gradleArguments User-supplied general gradle arguments.
|
|
187
|
+
* @returns {string[]} The `-D` flags to inject before the user's arguments.
|
|
188
|
+
*/
|
|
189
|
+
function gradleEvaluationOverrides(gradleArguments) {
|
|
190
|
+
const features = [
|
|
191
|
+
{
|
|
192
|
+
option: "isolated-projects",
|
|
193
|
+
properties: [
|
|
194
|
+
"org.gradle.isolated-projects",
|
|
195
|
+
"org.gradle.unsafe.isolated-projects",
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
option: "configuration-cache",
|
|
200
|
+
properties: [
|
|
201
|
+
"org.gradle.configuration-cache",
|
|
202
|
+
"org.gradle.unsafe.configuration-cache",
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
const overrides = [];
|
|
207
|
+
for (const { option, properties } of features) {
|
|
208
|
+
const userChose = gradleArguments.some(
|
|
209
|
+
(arg) =>
|
|
210
|
+
arg === `--${option}` ||
|
|
211
|
+
arg === `--no-${option}` ||
|
|
212
|
+
properties.some((property) => arg.startsWith(`-D${property}=`)),
|
|
213
|
+
);
|
|
214
|
+
if (!userChose) {
|
|
215
|
+
overrides.push(...properties.map((property) => `-D${property}=false`));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return overrides;
|
|
219
|
+
}
|
|
220
|
+
|
|
155
221
|
/**
|
|
156
222
|
* Method to combine the general gradle arguments, the sub-commands and the sub-commands' arguments in the correct way
|
|
157
223
|
*
|
|
@@ -175,6 +241,7 @@ export function buildGradleCommandArguments(
|
|
|
175
241
|
"--no-parallel",
|
|
176
242
|
]
|
|
177
243
|
.concat(getGradleDaemonParameter())
|
|
244
|
+
.concat(gradleEvaluationOverrides(gradleArguments))
|
|
178
245
|
.concat(gradleArguments);
|
|
179
246
|
const maxCliArgsLength = isWin
|
|
180
247
|
? 7500 - gradleCommandLength - mainGradleArguments.join(" ").length - 2
|
|
@@ -217,47 +284,87 @@ function getGradleDaemonParameter() {
|
|
|
217
284
|
}
|
|
218
285
|
}
|
|
219
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Parse a Gradle task header line such as `> Task :app:dependencies FAILED`
|
|
289
|
+
* into the path of the project that owns the task and the task's name.
|
|
290
|
+
*
|
|
291
|
+
* @param {string} line A line of Gradle output
|
|
292
|
+
* @returns {{projectPath: string, taskName: string}|undefined} The owning
|
|
293
|
+
* project's path (`""` for a root task such as `:dependencies`) and the task
|
|
294
|
+
* name, or undefined when the line is not a task header
|
|
295
|
+
*/
|
|
296
|
+
function parseGradleTaskHeader(line) {
|
|
297
|
+
if (!line.startsWith("> Task :")) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
// The path is the first token: a status such as UP-TO-DATE or FAILED may
|
|
301
|
+
// follow it, and a CRLF line keeps its carriage return.
|
|
302
|
+
const taskPath = line.slice("> Task ".length).trim().split(/\s+/)[0];
|
|
303
|
+
const lastColon = taskPath.lastIndexOf(":");
|
|
304
|
+
return {
|
|
305
|
+
projectPath: taskPath.slice(0, lastColon),
|
|
306
|
+
taskName: taskPath.slice(lastColon + 1),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
220
310
|
/**
|
|
221
311
|
* Method to split the output produced by Gradle using parallel processing by project
|
|
222
312
|
*
|
|
313
|
+
* Gradle groups console output by task and prints a `> Task :path` header
|
|
314
|
+
* before each group, repeating it whenever a task's output resumes after
|
|
315
|
+
* another task's. Each group is therefore keyed by the project in its own
|
|
316
|
+
* header (`> Task :app:dependencies` belongs to `:app`), never by whatever
|
|
317
|
+
* report header happened to precede it, and the groups of one project are
|
|
318
|
+
* concatenated in order, so reordering or interrupting the task reports cannot
|
|
319
|
+
* move a report to another project or overwrite one (issue 4465). The groups
|
|
320
|
+
* of a root task (`:dependencies`) all take the root project's name from the
|
|
321
|
+
* `Root project '…'` header that only a root task prints, so a continuation
|
|
322
|
+
* group, which carries no report header, stays with the rest of the root
|
|
323
|
+
* report. The groups of other tasks are dropped along with their headers.
|
|
324
|
+
*
|
|
223
325
|
* @param {string} rawOutput Full output produced by Gradle using parallel processing
|
|
224
326
|
* @param {string[]} relevantTasks The list of gradle tasks whose output need to be considered.
|
|
225
327
|
* @returns {map} Map with subProject names as keys and corresponding dependency task outputs as values.
|
|
226
328
|
*/
|
|
227
329
|
export function splitOutputByGradleProjects(rawOutput, relevantTasks) {
|
|
228
330
|
const outputSplitBySubprojects = new Map();
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
331
|
+
const relevantTaskNames = new Set(relevantTasks);
|
|
332
|
+
const taskChunks = [];
|
|
333
|
+
// Lines before the first task header (build logs) belong to no task.
|
|
334
|
+
let currentChunk;
|
|
335
|
+
for (const line of rawOutput.split("\n")) {
|
|
336
|
+
const taskHeader = parseGradleTaskHeader(line);
|
|
337
|
+
if (taskHeader) {
|
|
338
|
+
currentChunk = relevantTaskNames.has(taskHeader.taskName)
|
|
339
|
+
? { projectPath: taskHeader.projectPath, lines: [line] }
|
|
340
|
+
: undefined;
|
|
341
|
+
if (currentChunk) {
|
|
342
|
+
taskChunks.push(currentChunk);
|
|
343
|
+
}
|
|
344
|
+
} else if (currentChunk) {
|
|
345
|
+
currentChunk.lines.push(line);
|
|
238
346
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
347
|
+
}
|
|
348
|
+
let rootProjectName;
|
|
349
|
+
for (const chunk of taskChunks) {
|
|
350
|
+
if (chunk.projectPath) {
|
|
242
351
|
continue;
|
|
243
352
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (line.startsWith("> Task :") && subProjectOut !== "") {
|
|
251
|
-
outputSplitBySubprojects.set(currentProjectName, subProjectOut);
|
|
252
|
-
subProjectOut = "";
|
|
253
|
-
}
|
|
254
|
-
//if in subproject block, keep appending to string
|
|
255
|
-
subProjectOut += `${line}\n`;
|
|
256
|
-
//if end of last dependencies output block, push to array
|
|
257
|
-
if (i === outSplitByLine.length - 1) {
|
|
258
|
-
outputSplitBySubprojects.set(currentProjectName, subProjectOut);
|
|
353
|
+
const rootHeader = chunk.lines.find((line) =>
|
|
354
|
+
line.startsWith("Root project '"),
|
|
355
|
+
);
|
|
356
|
+
if (rootHeader) {
|
|
357
|
+
rootProjectName = rootHeader.split("'")[1];
|
|
358
|
+
break;
|
|
259
359
|
}
|
|
260
360
|
}
|
|
361
|
+
for (const chunk of taskChunks) {
|
|
362
|
+
const projectName = chunk.projectPath || rootProjectName || "root";
|
|
363
|
+
outputSplitBySubprojects.set(
|
|
364
|
+
projectName,
|
|
365
|
+
`${outputSplitBySubprojects.get(projectName) || ""}${chunk.lines.join("\n")}\n`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
261
368
|
return outputSplitBySubprojects;
|
|
262
369
|
}
|
|
263
370
|
|
|
@@ -397,6 +504,11 @@ export function executeParallelGradleProperties(
|
|
|
397
504
|
extraArgs = [],
|
|
398
505
|
options = {},
|
|
399
506
|
) {
|
|
507
|
+
// This runs once per properties round, so a rate limit an earlier round met
|
|
508
|
+
// ends the later rounds too.
|
|
509
|
+
if (isBuildToolRateLimited("gradle")) {
|
|
510
|
+
return "";
|
|
511
|
+
}
|
|
400
512
|
const gradleCmd = getGradleCommand(dir, null, options);
|
|
401
513
|
const gradleArgs = buildGradleCommandArguments(
|
|
402
514
|
extraArgs.concat(
|
|
@@ -423,6 +535,22 @@ export function executeParallelGradleProperties(
|
|
|
423
535
|
cwd: dir,
|
|
424
536
|
shell: isWin,
|
|
425
537
|
});
|
|
538
|
+
// A properties invocation needs the same repository the build does, so a
|
|
539
|
+
// 429 answer here ends every further gradle invocation in this scan. The
|
|
540
|
+
// dependencies task can even exit 0 under a rate limit, so the output is
|
|
541
|
+
// read whatever the status is. The properties this invocation did print
|
|
542
|
+
// are kept.
|
|
543
|
+
if (
|
|
544
|
+
noteBuildToolRateLimit("gradle", commandOutputText(result), {
|
|
545
|
+
command: `${basename(gradleCmd)} ${gradleArg.join(" ")}`,
|
|
546
|
+
exitCode: typeof result.status === "number" ? result.status : undefined,
|
|
547
|
+
})
|
|
548
|
+
) {
|
|
549
|
+
if (result.stdout) {
|
|
550
|
+
allOutputs.push(result.stdout);
|
|
551
|
+
}
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
426
554
|
if (result.status !== 0 || result.error) {
|
|
427
555
|
recordGradleInvocationFailure(result, {
|
|
428
556
|
command: `${basename(gradleCmd)} ${gradleArg.join(" ")}`,
|
|
@@ -1222,15 +1350,18 @@ export async function collectGradleDependencies(
|
|
|
1222
1350
|
* Method to return the mill command to use.
|
|
1223
1351
|
*
|
|
1224
1352
|
* The project-local `mill` script is an attacker-controlled file in the
|
|
1225
|
-
* scanned project, so it is only used when
|
|
1226
|
-
* (
|
|
1353
|
+
* scanned project, so it is only used when the project's own tooling may run
|
|
1354
|
+
* (see isProjectToolingAllowed).
|
|
1227
1355
|
*
|
|
1228
1356
|
* @param {string} srcPath Path to look for mill wrapper
|
|
1229
1357
|
* @param {Object} [options] CLI options (`installDeps` gates wrapper use)
|
|
1230
1358
|
*/
|
|
1231
1359
|
export function getMillCommand(srcPath, options = {}) {
|
|
1232
1360
|
let millCmd = `mill${platform() === "win32" ? ".bat" : ""}`;
|
|
1233
|
-
if (
|
|
1361
|
+
if (
|
|
1362
|
+
isProjectToolingAllowed(options) &&
|
|
1363
|
+
safeExistsSync(join(srcPath, millCmd))
|
|
1364
|
+
) {
|
|
1234
1365
|
// Use local mill wrapper if available
|
|
1235
1366
|
// Enable execute permission
|
|
1236
1367
|
try {
|
|
@@ -1244,12 +1375,178 @@ export function getMillCommand(srcPath, options = {}) {
|
|
|
1244
1375
|
return millCmd;
|
|
1245
1376
|
}
|
|
1246
1377
|
|
|
1378
|
+
/**
|
|
1379
|
+
* Determine the Mill version of a build.
|
|
1380
|
+
*
|
|
1381
|
+
* The version is read from the `.mill-version` file, from the
|
|
1382
|
+
* `//| mill-version:` header of the build file, or from the launcher itself.
|
|
1383
|
+
*
|
|
1384
|
+
* @param {string} millRootPath Root of the Mill build
|
|
1385
|
+
* @param {string} millCmd Mill command to use for the fallback probe
|
|
1386
|
+
* @returns {string|null} The Mill version, or null when it cannot be determined
|
|
1387
|
+
*/
|
|
1388
|
+
export function getMillVersion(millRootPath, millCmd) {
|
|
1389
|
+
const millVersionFile = join(millRootPath, ".mill-version");
|
|
1390
|
+
if (safeExistsSync(millVersionFile)) {
|
|
1391
|
+
const version = (readFileSync(millVersionFile, "utf-8") || "").trim();
|
|
1392
|
+
if (valid(version)) {
|
|
1393
|
+
return version;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
for (const buildFile of ["build.mill", "build.sc"]) {
|
|
1397
|
+
const buildFilePath = join(millRootPath, buildFile);
|
|
1398
|
+
if (!safeExistsSync(buildFilePath)) {
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
const headerMatch = /^\/\/[|\s]\s*mill-version:\s*(\S+)/m.exec(
|
|
1402
|
+
readFileSync(buildFilePath, "utf-8"),
|
|
1403
|
+
);
|
|
1404
|
+
if (headerMatch?.[1] && valid(headerMatch[1])) {
|
|
1405
|
+
return headerMatch[1];
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
const versionResult = safeSpawnSync(millCmd, ["--no-server", "--version"], {
|
|
1409
|
+
cwd: millRootPath,
|
|
1410
|
+
shell: isWin,
|
|
1411
|
+
});
|
|
1412
|
+
const versionMatch = /version\s+(\S+)/.exec(`${versionResult.stdout || ""}`);
|
|
1413
|
+
return versionMatch?.[1] && valid(versionMatch[1]) ? versionMatch[1] : null;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Find the Mill daemons running under a tree.
|
|
1418
|
+
*
|
|
1419
|
+
* Daemons record their process id in `out/mill-daemon*` (Mill 1.x) or
|
|
1420
|
+
* `out/mill-server*` directories, directly or one level below (Mill 0.12
|
|
1421
|
+
* keeps one subdirectory per server). Only processes that are still alive
|
|
1422
|
+
* are returned.
|
|
1423
|
+
*
|
|
1424
|
+
* @param {string} rootPath Tree to search
|
|
1425
|
+
* @returns {Map<string, Set<number>>} Live daemon process ids by the root of
|
|
1426
|
+
* the build they belong to
|
|
1427
|
+
*/
|
|
1428
|
+
export function findMillDaemons(rootPath) {
|
|
1429
|
+
const daemons = new Map();
|
|
1430
|
+
const readPid = (dir) => {
|
|
1431
|
+
for (const candidate of [dir, ...safeSubdirs(dir)]) {
|
|
1432
|
+
try {
|
|
1433
|
+
const pid = Number.parseInt(
|
|
1434
|
+
readFileSync(join(candidate, "processId"), "utf-8").trim(),
|
|
1435
|
+
10,
|
|
1436
|
+
);
|
|
1437
|
+
if (pid > 0 && isProcessAlive(pid)) {
|
|
1438
|
+
return pid;
|
|
1439
|
+
}
|
|
1440
|
+
} catch (_err) {
|
|
1441
|
+
// no process id recorded in this directory
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return undefined;
|
|
1445
|
+
};
|
|
1446
|
+
const visit = (dir, depth) => {
|
|
1447
|
+
if (depth > 4) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
let entries;
|
|
1451
|
+
try {
|
|
1452
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
1453
|
+
} catch (_err) {
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
for (const entry of entries) {
|
|
1457
|
+
if (!entry.isDirectory()) {
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
if (entry.name === "out") {
|
|
1461
|
+
for (const name of safeSubdirs(join(dir, entry.name))) {
|
|
1462
|
+
const base = basename(name);
|
|
1463
|
+
if (
|
|
1464
|
+
!base.startsWith("mill-daemon") &&
|
|
1465
|
+
!base.startsWith("mill-server")
|
|
1466
|
+
) {
|
|
1467
|
+
continue;
|
|
1468
|
+
}
|
|
1469
|
+
const pid = readPid(name);
|
|
1470
|
+
if (pid) {
|
|
1471
|
+
if (!daemons.has(dir)) {
|
|
1472
|
+
daemons.set(dir, new Set());
|
|
1473
|
+
}
|
|
1474
|
+
daemons.get(dir).add(pid);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
} else if (!["target", "node_modules", ".git"].includes(entry.name)) {
|
|
1478
|
+
visit(join(dir, entry.name), depth + 1);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
visit(rootPath, 0);
|
|
1483
|
+
return daemons;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
function safeSubdirs(dir) {
|
|
1487
|
+
try {
|
|
1488
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
1489
|
+
.filter((entry) => entry.isDirectory())
|
|
1490
|
+
.map((entry) => join(dir, entry.name));
|
|
1491
|
+
} catch (_err) {
|
|
1492
|
+
return [];
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
function isProcessAlive(pid) {
|
|
1497
|
+
try {
|
|
1498
|
+
process.kill(pid, 0);
|
|
1499
|
+
return true;
|
|
1500
|
+
} catch (err) {
|
|
1501
|
+
return err?.code === "EPERM";
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* Stop the Mill daemons that started under the scanned tree during the scan.
|
|
1507
|
+
*
|
|
1508
|
+
* cdxgen's own Mill calls run with --no-server, but a project build cdxgen
|
|
1509
|
+
* runs can shell out to Mill without it and leave a daemon behind. Daemons
|
|
1510
|
+
* that were already running when the scan started belong to the user, for
|
|
1511
|
+
* example to an IDE, and are left alone; every other live daemon is shut
|
|
1512
|
+
* down through the launcher of the build it belongs to.
|
|
1513
|
+
*
|
|
1514
|
+
* @param {string} rootPath Scanned tree
|
|
1515
|
+
* @param {Map<string, Set<number>>} [runningBefore] Result of
|
|
1516
|
+
* {@link findMillDaemons} taken before the scan
|
|
1517
|
+
* @param {Object} [options] CLI options
|
|
1518
|
+
*/
|
|
1519
|
+
export function stopMillDaemons(
|
|
1520
|
+
rootPath,
|
|
1521
|
+
runningBefore = new Map(),
|
|
1522
|
+
options = {},
|
|
1523
|
+
) {
|
|
1524
|
+
// The daemon PID files live in the scanned tree. When the scan was not
|
|
1525
|
+
// allowed to run Mill it started no daemon, and running the build's own
|
|
1526
|
+
// launcher here would execute a script from the scanned project.
|
|
1527
|
+
if (!isProjectToolingAllowed(options)) {
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
for (const [buildRoot, pids] of findMillDaemons(rootPath)) {
|
|
1531
|
+
const before = runningBefore.get(buildRoot) || new Set();
|
|
1532
|
+
if ([...pids].every((pid) => before.has(pid))) {
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
const launcher = getMillCommand(buildRoot, options);
|
|
1536
|
+
console.log("Stopping the Mill daemon the scan started in", buildRoot);
|
|
1537
|
+
safeSpawnSync(launcher, ["shutdown"], {
|
|
1538
|
+
cwd: buildRoot,
|
|
1539
|
+
shell: isWin,
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1247
1544
|
/**
|
|
1248
1545
|
* Method to return the maven command to use.
|
|
1249
1546
|
*
|
|
1250
1547
|
* Project-local wrapper scripts (`mvnw`) are attacker-controlled files in the
|
|
1251
|
-
* scanned project, so they are only used when
|
|
1252
|
-
* (
|
|
1548
|
+
* scanned project, so they are only used when the project's own tooling may
|
|
1549
|
+
* run (see isProjectToolingAllowed); otherwise the installed Maven is used.
|
|
1253
1550
|
*
|
|
1254
1551
|
* @param {string} srcPath Path to look for maven wrapper
|
|
1255
1552
|
* @param {string} rootPath Root directory to look for maven wrapper
|
|
@@ -1261,10 +1558,7 @@ export function getMavenCommand(srcPath, rootPath, options = {}) {
|
|
|
1261
1558
|
let isWrapperReady = false;
|
|
1262
1559
|
let isWrapperFound = false;
|
|
1263
1560
|
const pinnedToolSelected = isJvmToolPinned();
|
|
1264
|
-
|
|
1265
|
-
// keeps using wrappers even when dependency installation is disabled.
|
|
1266
|
-
const allowProjectWrappers =
|
|
1267
|
-
options.installDeps !== false || options.introspect === true;
|
|
1561
|
+
const allowProjectWrappers = isProjectToolingAllowed(options);
|
|
1268
1562
|
let findMavenFile = "mvnw";
|
|
1269
1563
|
let mavenWrapperCmd = null;
|
|
1270
1564
|
if (platform() === "win32") {
|
|
@@ -1445,7 +1739,7 @@ export function getMavenCommand(srcPath, rootPath, options = {}) {
|
|
|
1445
1739
|
export function parseGradleVersionCatalog(catalogFile) {
|
|
1446
1740
|
let catalog;
|
|
1447
1741
|
try {
|
|
1448
|
-
catalog =
|
|
1742
|
+
catalog = parseToml(readFileSync(catalogFile, "utf-8"));
|
|
1449
1743
|
} catch (error) {
|
|
1450
1744
|
console.warn(`Failed to parse ${catalogFile}: ${error.message}`);
|
|
1451
1745
|
return [];
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// import parsers-js directly instead of having parsePkgJson threaded in as an
|
|
7
7
|
// injected helper from the CLI.
|
|
8
8
|
|
|
9
|
-
import { join, resolve } from "node:path";
|
|
9
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
10
|
|
|
11
11
|
import { safeExistsSync } from "../core/fs.js";
|
|
12
12
|
import { createOccurrenceEvidence } from "../inventory/evidenceUtils.js";
|
|
@@ -104,6 +104,85 @@ function applyNpmBinCommandEvidence(pkg, allImports) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Full npm package name (`@scope/name` or `name`) of a component or manifest.
|
|
109
|
+
*
|
|
110
|
+
* @param {{group?: string, name?: string}} pkg Component or parsed manifest.
|
|
111
|
+
* @returns {string} The package name npm installs it under.
|
|
112
|
+
*/
|
|
113
|
+
function npmPackageName({ group, name }) {
|
|
114
|
+
const scope = group?.startsWith("@") ? group.substring(1) : group;
|
|
115
|
+
return scope ? `@${scope}/${name}` : name;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Fill in a component's missing npm metadata (description, author, license,
|
|
120
|
+
* homepage, repository) from the package.json of its installed copy.
|
|
121
|
+
*
|
|
122
|
+
* `internal:LocalNodeModulesPath` holds either an absolute install directory
|
|
123
|
+
* (pnpm) or an arborist `node.location`. A location that starts with
|
|
124
|
+
* `node_modules/` is relative to the directory of the component's
|
|
125
|
+
* `internal:SrcFile`. Other relative locations, such as a workspace member or a
|
|
126
|
+
* package nested under one, have no recorded base and are skipped; the deep npm
|
|
127
|
+
* parse reads those manifests from the arborist node itself. The manifest is
|
|
128
|
+
* used only when it names the component's package and version, and it fills
|
|
129
|
+
* only the fields the component lacks.
|
|
130
|
+
*
|
|
131
|
+
* @param {object} pkg Package component to enrich in place.
|
|
132
|
+
*/
|
|
133
|
+
async function fillMissingMetadataFromLocalNodeModules(pkg) {
|
|
134
|
+
if (pkg.description || !Array.isArray(pkg.properties)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const propValue = (propName) =>
|
|
138
|
+
pkg.properties.find((property) => property.name === propName)?.value;
|
|
139
|
+
const localNodeModulesPath = propValue("internal:LocalNodeModulesPath");
|
|
140
|
+
if (!localNodeModulesPath) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
let pkgJsonFile;
|
|
144
|
+
if (isAbsolute(localNodeModulesPath)) {
|
|
145
|
+
pkgJsonFile = join(localNodeModulesPath, "package.json");
|
|
146
|
+
} else {
|
|
147
|
+
const srcFile = propValue("internal:SrcFile");
|
|
148
|
+
if (!srcFile || !localNodeModulesPath.startsWith("node_modules/")) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
pkgJsonFile = resolve(
|
|
152
|
+
dirname(srcFile),
|
|
153
|
+
localNodeModulesPath,
|
|
154
|
+
"package.json",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
if (!safeExistsSync(pkgJsonFile)) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const lnmPkgList = await parsePkgJson(pkgJsonFile, true);
|
|
161
|
+
const lnmMetadata = lnmPkgList?.length === 1 ? lnmPkgList[0] : undefined;
|
|
162
|
+
if (
|
|
163
|
+
!lnmMetadata ||
|
|
164
|
+
npmPackageName(lnmMetadata) !== npmPackageName(pkg) ||
|
|
165
|
+
lnmMetadata.version !== pkg.version
|
|
166
|
+
) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!pkg.description && lnmMetadata.description) {
|
|
170
|
+
pkg.description = lnmMetadata.description;
|
|
171
|
+
}
|
|
172
|
+
if (!pkg.author && lnmMetadata.author) {
|
|
173
|
+
pkg.author = lnmMetadata.author;
|
|
174
|
+
}
|
|
175
|
+
if (!pkg.license && lnmMetadata.license) {
|
|
176
|
+
pkg.license = lnmMetadata.license;
|
|
177
|
+
}
|
|
178
|
+
if (!pkg.homepage && lnmMetadata.homepage) {
|
|
179
|
+
pkg.homepage = lnmMetadata.homepage;
|
|
180
|
+
}
|
|
181
|
+
if (!pkg.repository && lnmMetadata.repository) {
|
|
182
|
+
pkg.repository = lnmMetadata.repository;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
107
186
|
/**
|
|
108
187
|
* Attach JS/TS import and export usage evidence to matching package components.
|
|
109
188
|
*
|
|
@@ -136,6 +215,11 @@ export async function addEvidenceForImports(
|
|
|
136
215
|
if (group === "@types") {
|
|
137
216
|
continue;
|
|
138
217
|
}
|
|
218
|
+
// Runs ahead of the alias loop, which a matching import ends with a break,
|
|
219
|
+
// so that every component is enriched whichever alias the source imports.
|
|
220
|
+
if (deep) {
|
|
221
|
+
await fillMissingMetadataFromLocalNodeModules(pkg);
|
|
222
|
+
}
|
|
139
223
|
const aliases = group?.length
|
|
140
224
|
? [name, `${group}/${name}`, `@${group}/${name}`]
|
|
141
225
|
: [name];
|
|
@@ -282,29 +366,6 @@ export async function addEvidenceForImports(
|
|
|
282
366
|
}
|
|
283
367
|
break;
|
|
284
368
|
}
|
|
285
|
-
// Capture metadata such as description from local node_modules in deep mode
|
|
286
|
-
if (deep && !pkg.description && pkg.properties) {
|
|
287
|
-
let localNodeModulesPath;
|
|
288
|
-
for (const aprop of pkg.properties) {
|
|
289
|
-
if (aprop.name === "internal:LocalNodeModulesPath") {
|
|
290
|
-
localNodeModulesPath = resolve(join(aprop.value, "package.json"));
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
if (localNodeModulesPath && safeExistsSync(localNodeModulesPath)) {
|
|
295
|
-
const lnmPkgList = await parsePkgJson(localNodeModulesPath, true);
|
|
296
|
-
if (lnmPkgList && lnmPkgList.length === 1) {
|
|
297
|
-
const lnmMetadata = lnmPkgList[0];
|
|
298
|
-
if (lnmMetadata && Object.keys(lnmMetadata).length) {
|
|
299
|
-
pkg.description = lnmMetadata.description;
|
|
300
|
-
pkg.author = lnmMetadata.author;
|
|
301
|
-
pkg.license = lnmMetadata.license;
|
|
302
|
-
pkg.homepage = lnmMetadata.homepage;
|
|
303
|
-
pkg.repository = lnmMetadata.repository;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
369
|
} // for alias
|
|
309
370
|
// Trim the properties
|
|
310
371
|
applyNpmBinCommandEvidence(pkg, allImports);
|
package/lib/ecosystems/kosi.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
safeRmSync,
|
|
11
11
|
safeSpawnSync,
|
|
12
12
|
} from "../core/fs.js";
|
|
13
|
+
import { resolvePluginSourceDir } from "../core/pluginRun.js";
|
|
13
14
|
import { resolvePluginBinary } from "../inventory/plugins.js";
|
|
14
15
|
import {
|
|
15
16
|
parsePurlLoose,
|
|
@@ -174,14 +175,25 @@ export function analyzeKosiProject(src, options = {}) {
|
|
|
174
175
|
);
|
|
175
176
|
return undefined;
|
|
176
177
|
}
|
|
178
|
+
// cdxgen may run with its working directory already inside the project
|
|
179
|
+
// while the source argument is relative to a repository root; a plain
|
|
180
|
+
// resolve would join the two into a directory that does not exist.
|
|
181
|
+
const analysisDir = resolvePluginSourceDir(src, [
|
|
182
|
+
"pom.xml",
|
|
183
|
+
"build.gradle",
|
|
184
|
+
"build.gradle.kts",
|
|
185
|
+
"settings.gradle",
|
|
186
|
+
"settings.gradle.kts",
|
|
187
|
+
"classpath.txt",
|
|
188
|
+
]);
|
|
177
189
|
const tempDir = safeMkdtempSync(join(getTmpDir(), "kosi-"));
|
|
178
190
|
try {
|
|
179
191
|
const outputFile = join(tempDir, "kosi-all.json");
|
|
180
|
-
const allArgs = kosiArgs(kosi,
|
|
192
|
+
const allArgs = kosiArgs(kosi, analysisDir, outputFile, "all");
|
|
181
193
|
if (DEBUG_MODE) {
|
|
182
194
|
console.log("Executing", kosi.cmd, allArgs.join(" "));
|
|
183
195
|
}
|
|
184
|
-
const report = runKosi(kosi,
|
|
196
|
+
const report = runKosi(kosi, analysisDir, allArgs, outputFile, options);
|
|
185
197
|
if (!report) {
|
|
186
198
|
return undefined;
|
|
187
199
|
}
|
|
@@ -191,10 +203,15 @@ export function analyzeKosiProject(src, options = {}) {
|
|
|
191
203
|
// all-pass's. Losing it would silently drop the filtered evidence,
|
|
192
204
|
// so a failure here names itself but keeps the all-pass evidence.
|
|
193
205
|
const reachableFile = join(tempDir, "kosi-reachable.json");
|
|
194
|
-
const reachableArgs = kosiArgs(
|
|
206
|
+
const reachableArgs = kosiArgs(
|
|
207
|
+
kosi,
|
|
208
|
+
analysisDir,
|
|
209
|
+
reachableFile,
|
|
210
|
+
"reachable",
|
|
211
|
+
);
|
|
195
212
|
const reachableReport = runKosi(
|
|
196
213
|
kosi,
|
|
197
|
-
|
|
214
|
+
analysisDir,
|
|
198
215
|
reachableArgs,
|
|
199
216
|
reachableFile,
|
|
200
217
|
options,
|