@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
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plumbing for plugin (rusi/golem/kosi) invocations: resolving the
|
|
3
|
+
* directory to analyze, and reporting a failed run.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isAbsolute, join, normalize, resolve, sep } from "node:path";
|
|
7
|
+
import process from "node:process";
|
|
8
|
+
|
|
9
|
+
import { DEBUG_MODE } from "./activity.js";
|
|
10
|
+
import { deferFailOnError } from "./deferredExit.js";
|
|
11
|
+
import { safeExistsSync } from "./fs.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The working directory, when a relative source argument names it.
|
|
15
|
+
*
|
|
16
|
+
* cdxgen is frequently launched with its working directory already set to the
|
|
17
|
+
* project (depscan does exactly that) while the path argument stays relative
|
|
18
|
+
* to some repository root, e.g. cwd `/work/repo/test/data/app` with argument
|
|
19
|
+
* `test/data/app`. A plain `path.resolve` then produces the doubled
|
|
20
|
+
* `/work/repo/test/data/app/test/data/app`. This recognises that shape and
|
|
21
|
+
* nothing looser: the argument's path segments must be the trailing segments
|
|
22
|
+
* of the working directory, so a mistyped argument run from some other
|
|
23
|
+
* project is never silently redirected to that project.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} src The source path as the caller typed it.
|
|
26
|
+
* @param {string} [cwd] Working directory; defaults to `process.cwd()`.
|
|
27
|
+
* @returns {string|undefined} The absolute working directory when the
|
|
28
|
+
* argument names it, otherwise undefined.
|
|
29
|
+
*/
|
|
30
|
+
export function cwdNamedByRelativeSource(src, cwd = process.cwd()) {
|
|
31
|
+
if (typeof src !== "string" || !src || isAbsolute(src)) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
// normalize() converts separators to the platform's, so one split on sep
|
|
35
|
+
// handles both "a/b" and "a\\b" on Windows; on POSIX a backslash is an
|
|
36
|
+
// ordinary file-name character and must not split.
|
|
37
|
+
const relParts = normalize(src)
|
|
38
|
+
.split(sep)
|
|
39
|
+
.filter((part) => part && part !== ".");
|
|
40
|
+
if (!relParts.length || relParts.includes("..")) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const absCwd = resolve(cwd);
|
|
44
|
+
const cwdParts = absCwd.split(sep).filter(Boolean);
|
|
45
|
+
if (relParts.length > cwdParts.length) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const tail = cwdParts.slice(cwdParts.length - relParts.length);
|
|
49
|
+
const caseInsensitive = process.platform === "win32";
|
|
50
|
+
const same = (a, b) =>
|
|
51
|
+
caseInsensitive ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
52
|
+
return relParts.every((part, i) => same(part, tail[i])) ? absCwd : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the source path argument once, at cdxgen's entry point.
|
|
57
|
+
*
|
|
58
|
+
* Everything downstream — the BOM generators, evinse, and the rusi/golem/kosi
|
|
59
|
+
* launchers — resolves the argument against the working directory, so the
|
|
60
|
+
* doubled-directory shape has to be corrected before any of them run;
|
|
61
|
+
* correcting it only in the plugin launchers leaves the main BOM built from a
|
|
62
|
+
* directory that does not exist.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} src The source path as the caller typed it.
|
|
65
|
+
* @param {string} [cwd] Working directory; defaults to `process.cwd()`.
|
|
66
|
+
* @returns {{path: string, rewritten: boolean, missing: boolean}} `path` is
|
|
67
|
+
* the argument to use: unchanged when it resolves to an existing path, the
|
|
68
|
+
* working directory when the argument names it (`rewritten`), and otherwise
|
|
69
|
+
* unchanged with `missing` set.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveSourcePathArgument(src, cwd = process.cwd()) {
|
|
72
|
+
if (typeof src !== "string" || !src) {
|
|
73
|
+
return { path: src, rewritten: false, missing: false };
|
|
74
|
+
}
|
|
75
|
+
if (safeExistsSync(resolve(cwd, src))) {
|
|
76
|
+
return { path: src, rewritten: false, missing: false };
|
|
77
|
+
}
|
|
78
|
+
const named = cwdNamedByRelativeSource(src, cwd);
|
|
79
|
+
if (named) {
|
|
80
|
+
return { path: named, rewritten: true, missing: false };
|
|
81
|
+
}
|
|
82
|
+
return { path: src, rewritten: false, missing: true };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Resolve the directory a plugin should analyze.
|
|
87
|
+
*
|
|
88
|
+
* The entry point already corrects the doubled-directory shape (see
|
|
89
|
+
* resolveSourcePathArgument); this is the same rule for callers that reach a
|
|
90
|
+
* plugin launcher directly (evinse, the library API). An existing directory
|
|
91
|
+
* wins and an absolute path is never rewritten. Otherwise the working
|
|
92
|
+
* directory is used only when the argument names it AND it carries the
|
|
93
|
+
* project marker the plugin needs (a `Cargo.toml` for rusi, a `go.mod` for
|
|
94
|
+
* golem). Anything else keeps the resolved path so the plugin's own error
|
|
95
|
+
* names the directory that was actually attempted.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} src The source path as the caller typed it.
|
|
98
|
+
* @param {string[]} markerFiles File names that, present in the working
|
|
99
|
+
* directory, identify it as the project the plugin should analyze.
|
|
100
|
+
* @returns {string} The directory to pass to the plugin.
|
|
101
|
+
*/
|
|
102
|
+
export function resolvePluginSourceDir(src, markerFiles = []) {
|
|
103
|
+
const resolved = resolve(src);
|
|
104
|
+
if (safeExistsSync(resolved) || isAbsolute(src)) {
|
|
105
|
+
return resolved;
|
|
106
|
+
}
|
|
107
|
+
const cwd = cwdNamedByRelativeSource(src);
|
|
108
|
+
if (!cwd) {
|
|
109
|
+
return resolved;
|
|
110
|
+
}
|
|
111
|
+
for (const marker of markerFiles) {
|
|
112
|
+
if (marker && safeExistsSync(join(cwd, marker))) {
|
|
113
|
+
console.error(
|
|
114
|
+
`Resolved source directory ${resolved} does not exist; the working directory ${cwd} is the project the argument names (found ${marker}), using it instead.`,
|
|
115
|
+
);
|
|
116
|
+
return cwd;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return resolved;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Report a failed plugin run, keeping the two failure classes apart.
|
|
124
|
+
*
|
|
125
|
+
* A spawn error means the plugin binary itself could not run — not installed,
|
|
126
|
+
* not executable, blocked by the command policy — which is an installation
|
|
127
|
+
* problem. A non-zero exit status or a missing report means the plugin ran
|
|
128
|
+
* and the analysis failed, which must not be dressed up as an installation
|
|
129
|
+
* problem: the message names the directory and says the reachability
|
|
130
|
+
* evidence will be missing, because a silent degradation is what let a
|
|
131
|
+
* doubled `--dir` go unnoticed in the first place.
|
|
132
|
+
*
|
|
133
|
+
* Under `--fail-on-error` the failure additionally claims the process exit
|
|
134
|
+
* status through the deferred-exit contract.
|
|
135
|
+
*
|
|
136
|
+
* @param {Object} run What ran and how it failed.
|
|
137
|
+
* @param {string} run.tool Plugin name, e.g. "rusi".
|
|
138
|
+
* @param {string} run.ecosystem Ecosystem the plugin serves, e.g. "rust".
|
|
139
|
+
* @param {string} run.executable The binary that was invoked.
|
|
140
|
+
* @param {string[]} run.args The arguments it was invoked with.
|
|
141
|
+
* @param {string} run.dir The directory that was analyzed.
|
|
142
|
+
* @param {Object} run.result The spawn result.
|
|
143
|
+
* @param {string} run.outputFile The report file the plugin should have written.
|
|
144
|
+
* @param {Object} [run.options] CLI options, for the `--fail-on-error` contract.
|
|
145
|
+
* @returns {void}
|
|
146
|
+
*/
|
|
147
|
+
export function reportPluginRunFailure({
|
|
148
|
+
tool,
|
|
149
|
+
ecosystem,
|
|
150
|
+
executable,
|
|
151
|
+
args,
|
|
152
|
+
dir,
|
|
153
|
+
result,
|
|
154
|
+
outputFile,
|
|
155
|
+
options,
|
|
156
|
+
}) {
|
|
157
|
+
const command = `${executable} ${(args || []).join(" ")}`.trim();
|
|
158
|
+
if (result?.error) {
|
|
159
|
+
console.error(
|
|
160
|
+
`${tool} plugin could not be executed for ${dir}: ${result.error.message}. Check if the ${tool} plugin was installed successfully.`,
|
|
161
|
+
);
|
|
162
|
+
} else {
|
|
163
|
+
const status =
|
|
164
|
+
typeof result?.status === "number" ? result.status : "unknown";
|
|
165
|
+
const reason = safeExistsSync(outputFile)
|
|
166
|
+
? "exited unsuccessfully"
|
|
167
|
+
: "wrote no report";
|
|
168
|
+
console.error(
|
|
169
|
+
`${tool} analysis ${reason} for directory ${dir} (exit status ${status}); ${ecosystem} reachability evidence will be missing from the BOM.`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
if (DEBUG_MODE && (result?.stdout || result?.stderr)) {
|
|
173
|
+
console.error(result.stdout, result.stderr);
|
|
174
|
+
}
|
|
175
|
+
deferFailOnError(options, {
|
|
176
|
+
ecosystem,
|
|
177
|
+
tool,
|
|
178
|
+
detail: result?.error
|
|
179
|
+
? `${tool} plugin could not be executed: ${result.error.message}`
|
|
180
|
+
: `${tool} analysis failed for directory ${dir}`,
|
|
181
|
+
exitCode: typeof result?.status === "number" ? result.status : undefined,
|
|
182
|
+
command,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for external commands stopped by cdxgen's spawn timeout.
|
|
3
|
+
*
|
|
4
|
+
* Layer 0: this module imports nothing outside `lib/core`, so both the atom
|
|
5
|
+
* and the dosai runners (layer 2) can share it.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { DEBUG_MODE } from "./activity.js";
|
|
9
|
+
import { safeSpawnSync } from "./fs.js";
|
|
10
|
+
import { isWin } from "./paths.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A duration for a message: seconds to one decimal below a minute, minutes to
|
|
14
|
+
* one decimal from there.
|
|
15
|
+
*
|
|
16
|
+
* @param {number} ms Duration in milliseconds
|
|
17
|
+
* @returns {string} Human-readable duration
|
|
18
|
+
*/
|
|
19
|
+
export function formatDuration(ms) {
|
|
20
|
+
if (ms >= 60_000) {
|
|
21
|
+
const minutes = Math.round(ms / 6_000) / 10;
|
|
22
|
+
return `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
23
|
+
}
|
|
24
|
+
return `${Math.round(ms / 100) / 10} seconds`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Stop the descendants a timed-out run left behind on Windows.
|
|
29
|
+
*
|
|
30
|
+
* The spawn timeout terminates only the process cdxgen spawned, and Windows
|
|
31
|
+
* neither kills nor re-parents its children: a shell's command, a runtime's
|
|
32
|
+
* workers, or the build hosts a .NET analyzer starts keep running while still
|
|
33
|
+
* pointing at the dead process's pid. `taskkill /T` on that pid finds nothing,
|
|
34
|
+
* because the root is already gone. The live descendants are therefore found
|
|
35
|
+
* by parent pid in a process snapshot and stopped, limited to processes
|
|
36
|
+
* created after the run started. The root's own children must also predate
|
|
37
|
+
* its end: once the root is gone its pid can be handed to an unrelated
|
|
38
|
+
* process, whose children would otherwise look like the run's. Elsewhere this
|
|
39
|
+
* does nothing: an orphan is re-parented and can no longer be told apart.
|
|
40
|
+
*
|
|
41
|
+
* @param {number|undefined} rootPid pid of the process cdxgen spawned
|
|
42
|
+
* @param {number} startedAt Epoch milliseconds at which the run started
|
|
43
|
+
* @param {number} [endedAt] Epoch milliseconds at which the spawned process ended
|
|
44
|
+
* @param {string} [toolName] Name of the tool, for the debug message
|
|
45
|
+
* @returns {number[]} The pids that were stopped
|
|
46
|
+
*/
|
|
47
|
+
export function reapProcessTree(
|
|
48
|
+
rootPid,
|
|
49
|
+
startedAt,
|
|
50
|
+
endedAt = Date.now(),
|
|
51
|
+
toolName = "external",
|
|
52
|
+
) {
|
|
53
|
+
if (!isWin || !Number.isInteger(rootPid) || rootPid <= 0) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
// All three values are integers, so interpolating them cannot inject anything.
|
|
57
|
+
const since = Math.max(0, Math.floor(startedAt) - 1000);
|
|
58
|
+
const until = Math.max(since, Math.floor(endedAt) + 1000);
|
|
59
|
+
const script = [
|
|
60
|
+
`$root = ${rootPid}`,
|
|
61
|
+
`$since = [DateTimeOffset]::FromUnixTimeMilliseconds(${since}).UtcDateTime`,
|
|
62
|
+
`$until = [DateTimeOffset]::FromUnixTimeMilliseconds(${until}).UtcDateTime`,
|
|
63
|
+
"$children = @{}",
|
|
64
|
+
"foreach ($p in Get-CimInstance Win32_Process -Property ProcessId,ParentProcessId,CreationDate) {",
|
|
65
|
+
" if (-not $p.CreationDate) { continue }",
|
|
66
|
+
" $created = $p.CreationDate.ToUniversalTime()",
|
|
67
|
+
" if ($created -lt $since) { continue }",
|
|
68
|
+
" if ([int]$p.ParentProcessId -eq $root -and $created -gt $until) { continue }",
|
|
69
|
+
" $children[[int]$p.ParentProcessId] += @([int]$p.ProcessId)",
|
|
70
|
+
"}",
|
|
71
|
+
"$found = [System.Collections.Generic.List[int]]::new()",
|
|
72
|
+
"$queue = [System.Collections.Generic.Queue[int]]::new()",
|
|
73
|
+
"$queue.Enqueue($root)",
|
|
74
|
+
"while ($queue.Count) {",
|
|
75
|
+
" foreach ($child in @($children[$queue.Dequeue()])) {",
|
|
76
|
+
" if ($child -and -not $found.Contains($child)) { $found.Add($child); $queue.Enqueue($child) }",
|
|
77
|
+
" }",
|
|
78
|
+
"}",
|
|
79
|
+
"foreach ($child in $found) { Stop-Process -Id $child -Force -ErrorAction SilentlyContinue }",
|
|
80
|
+
"$found -join ','",
|
|
81
|
+
].join("\n");
|
|
82
|
+
const result = safeSpawnSync(
|
|
83
|
+
"powershell",
|
|
84
|
+
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
85
|
+
{ timeout: 60000 },
|
|
86
|
+
);
|
|
87
|
+
const reaped = `${result?.stdout || ""}`
|
|
88
|
+
.trim()
|
|
89
|
+
.split(",")
|
|
90
|
+
.map((pid) => Number.parseInt(pid, 10))
|
|
91
|
+
.filter((pid) => pid > 0);
|
|
92
|
+
if (DEBUG_MODE) {
|
|
93
|
+
console.log(
|
|
94
|
+
reaped.length
|
|
95
|
+
? `Stopped ${reaped.length} process(es) the timed-out ${toolName} run left behind: ${reaped.join(", ")}.`
|
|
96
|
+
: `The timed-out ${toolName} run left no processes behind.`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return reaped;
|
|
100
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy for running the scanned project's own build tooling.
|
|
3
|
+
*
|
|
4
|
+
* Build tools such as Maven, Gradle, Poetry, MSBuild, SwiftPM, Leiningen and
|
|
5
|
+
* CocoaPods read configuration or run code that ships with the project
|
|
6
|
+
* (`.mvn/jvm.config`, `.poetry/plugins`, `Directory.Build.rsp`, `Package.swift`,
|
|
7
|
+
* `project.clj`, podspecs). Running them on an untrusted project runs that
|
|
8
|
+
* project's code, so they belong to the same surface as dependency
|
|
9
|
+
* installation and are skipped whenever installation is disabled.
|
|
10
|
+
*/
|
|
11
|
+
import { isSecureMode, readEnvironmentVariable } from "./activity.js";
|
|
12
|
+
import { recordDegradation } from "./buildLedger.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether secure mode is on right now. `isSecureMode` is read once when the
|
|
16
|
+
* modules load, but the `cdxgen-secure` command turns secure mode on later,
|
|
17
|
+
* by setting CDXGEN_SECURE_MODE before the scan starts.
|
|
18
|
+
*
|
|
19
|
+
* @returns {boolean} true when secure mode is on
|
|
20
|
+
*/
|
|
21
|
+
export function isSecureModeActive() {
|
|
22
|
+
return (
|
|
23
|
+
isSecureMode ||
|
|
24
|
+
["true", "1"].includes(readEnvironmentVariable("CDXGEN_SECURE_MODE"))
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether the scan may run the scanned project's own build tooling, such as
|
|
30
|
+
* wrapper scripts or build definitions that Maven, Gradle, sbt, Mill, Poetry,
|
|
31
|
+
* PDM, MSBuild, SwiftPM, Leiningen and CocoaPods evaluate.
|
|
32
|
+
*
|
|
33
|
+
* Tooling follows `options.installDeps`, which defaults to off in secure mode
|
|
34
|
+
* when a caller does not set it. Introspection mode exists to observe the
|
|
35
|
+
* project's own toolchain, so it keeps that tooling on even when dependency
|
|
36
|
+
* installation is disabled, as long as the operator asked for it rather than
|
|
37
|
+
* a config file in the scanned project. Secure mode still wins:
|
|
38
|
+
* CDXGEN_INTROSPECT alone must not make a secure-mode scan run the scanned
|
|
39
|
+
* project's code.
|
|
40
|
+
*
|
|
41
|
+
* @param {Object} [options] CLI options
|
|
42
|
+
* @returns {boolean} true when project tooling may run
|
|
43
|
+
*/
|
|
44
|
+
export function isProjectToolingAllowed(options) {
|
|
45
|
+
const secureMode = isSecureModeActive();
|
|
46
|
+
const installDeps = options?.installDeps ?? !secureMode;
|
|
47
|
+
if (installDeps !== false) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return (
|
|
51
|
+
options?.introspect === true &&
|
|
52
|
+
options?.introspectFromConfigFile !== true &&
|
|
53
|
+
!secureMode
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The skips already reported for each scan, keyed by the scan's options.
|
|
58
|
+
// Callers without options share one key.
|
|
59
|
+
const reportedSkips = new WeakMap();
|
|
60
|
+
const noOptionsKey = {};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Tell the user that a tool was skipped because project tooling is not
|
|
64
|
+
* allowed, and record the matching degradation for the fidelity report.
|
|
65
|
+
* Each skip is reported once per scan, however many manifests it applies to.
|
|
66
|
+
* A skip that only costs evidence, not BOM content, passes no remediation id
|
|
67
|
+
* and is only shown.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} options CLI options of the scan
|
|
70
|
+
* @param {string|null} remediationId Remediation id from
|
|
71
|
+
* data/remediations.json, or null for a message only
|
|
72
|
+
* @param {string} message Console message shown to the user
|
|
73
|
+
* @param {Object} [fields] Degradation fields (ecosystem, tool, impact, detail)
|
|
74
|
+
* @returns {void}
|
|
75
|
+
*/
|
|
76
|
+
export function reportProjectToolingSkipped(
|
|
77
|
+
options,
|
|
78
|
+
remediationId,
|
|
79
|
+
message,
|
|
80
|
+
fields,
|
|
81
|
+
) {
|
|
82
|
+
const key = options && typeof options === "object" ? options : noOptionsKey;
|
|
83
|
+
let reported = reportedSkips.get(key);
|
|
84
|
+
if (!reported) {
|
|
85
|
+
reported = new Set();
|
|
86
|
+
reportedSkips.set(key, reported);
|
|
87
|
+
}
|
|
88
|
+
const skipKey = remediationId || message;
|
|
89
|
+
if (reported.has(skipKey)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
reported.add(skipKey);
|
|
93
|
+
console.log(message);
|
|
94
|
+
if (remediationId) {
|
|
95
|
+
recordDegradation(remediationId, fields);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -26,6 +26,15 @@ const JSON_PROPERTY_NAMES = new Set([
|
|
|
26
26
|
]);
|
|
27
27
|
const URL_PATTERN = /https?:\/\/[^\s<>"'),\]}]+/giu;
|
|
28
28
|
const MAX_STRUCTURE_DEPTH = 6;
|
|
29
|
+
// What an RFC 3986 path may hold as it is: unreserved characters, sub-delims,
|
|
30
|
+
// ":", "@", "/" and well-formed percent-escapes. A query or fragment may also
|
|
31
|
+
// hold "?". Everything else is percent-encoded.
|
|
32
|
+
const URI_PATH_UNSAFE =
|
|
33
|
+
/%(?![0-9A-Fa-f]{2})|[^A-Za-z0-9\-._~!$&'()*+,;=:@/%]/gu;
|
|
34
|
+
const URI_QUERY_UNSAFE =
|
|
35
|
+
/%(?![0-9A-Fa-f]{2})|[^A-Za-z0-9\-._~!$&'()*+,;=:@/?%]/gu;
|
|
36
|
+
const URI_SCHEME_AUTHORITY = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/?#]*/u;
|
|
37
|
+
const UTF8_ENCODER = new TextEncoder();
|
|
29
38
|
|
|
30
39
|
function sanitizeUrlForBom(value) {
|
|
31
40
|
const input = String(value || "").trim();
|
|
@@ -146,6 +155,69 @@ export function sanitizeBomUrl(value) {
|
|
|
146
155
|
return sanitizeUrlForBom(value);
|
|
147
156
|
}
|
|
148
157
|
|
|
158
|
+
function percentEncodeUnsafe(text, unsafe) {
|
|
159
|
+
return text.replace(unsafe, (character) =>
|
|
160
|
+
Array.from(
|
|
161
|
+
UTF8_ENCODER.encode(character),
|
|
162
|
+
(byte) => `%${byte.toString(16).toUpperCase().padStart(2, "0")}`,
|
|
163
|
+
).join(""),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Percent-encode what an RFC 3986 URI reference cannot hold as it is, so a
|
|
169
|
+
* route template or URL can be emitted where CycloneDX expects an
|
|
170
|
+
* iri-reference, such as `services[].endpoints[]`. The braces of a route
|
|
171
|
+
* template, spaces, `<>`, `[]` outside an IPv6 host, `|`, `^`, non-ASCII
|
|
172
|
+
* characters and a `%` that starts no escape are encoded:
|
|
173
|
+
* `/users/{id}` becomes `/users/%7Bid%7D`. The scheme and authority of an
|
|
174
|
+
* absolute URL are kept as written, the query and fragment stay a query and
|
|
175
|
+
* fragment, and existing escapes are kept, so encoding twice changes
|
|
176
|
+
* nothing. Nothing is removed; strip credentials with {@link sanitizeBomUrl}
|
|
177
|
+
* first.
|
|
178
|
+
*
|
|
179
|
+
* @param {unknown} value Route template, path or URL
|
|
180
|
+
* @returns {string} The encoded reference
|
|
181
|
+
*/
|
|
182
|
+
export function encodeUriReference(value) {
|
|
183
|
+
const text = String(value ?? "");
|
|
184
|
+
const authority = URI_SCHEME_AUTHORITY.exec(text)?.[0] ?? "";
|
|
185
|
+
const rest = text.slice(authority.length);
|
|
186
|
+
const queryStart = rest.indexOf("?");
|
|
187
|
+
const fragmentStart = rest.indexOf("#");
|
|
188
|
+
const pathEnd = Math.min(
|
|
189
|
+
queryStart < 0 ? rest.length : queryStart,
|
|
190
|
+
fragmentStart < 0 ? rest.length : fragmentStart,
|
|
191
|
+
);
|
|
192
|
+
let encoded =
|
|
193
|
+
authority + percentEncodeUnsafe(rest.slice(0, pathEnd), URI_PATH_UNSAFE);
|
|
194
|
+
if (pathEnd === queryStart) {
|
|
195
|
+
const queryEnd = fragmentStart > queryStart ? fragmentStart : rest.length;
|
|
196
|
+
encoded += `?${percentEncodeUnsafe(rest.slice(queryStart + 1, queryEnd), URI_QUERY_UNSAFE)}`;
|
|
197
|
+
}
|
|
198
|
+
if (fragmentStart >= 0) {
|
|
199
|
+
encoded += `#${percentEncodeUnsafe(rest.slice(fragmentStart + 1), URI_QUERY_UNSAFE)}`;
|
|
200
|
+
}
|
|
201
|
+
return encoded;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Whether a value carries a token shape the BOM sanitizer redacts: cloud
|
|
206
|
+
* access keys, bearer tokens, GitHub, Slack and npm tokens, JWTs and the
|
|
207
|
+
* like. Callers that cannot splice a `[redacted]` marker into the value, such
|
|
208
|
+
* as a URL path segment, use it to drop the value instead.
|
|
209
|
+
*
|
|
210
|
+
* @param {unknown} value Value to check
|
|
211
|
+
* @returns {boolean} true when the value holds an inline credential
|
|
212
|
+
*/
|
|
213
|
+
export function hasInlineCredential(value) {
|
|
214
|
+
const text = String(value ?? "");
|
|
215
|
+
return INLINE_CREDENTIAL_PATTERNS.some((pattern) => {
|
|
216
|
+
pattern.lastIndex = 0;
|
|
217
|
+
return pattern.test(text);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
149
221
|
/**
|
|
150
222
|
* Sanitize a property value before serializing it into BOM properties.
|
|
151
223
|
*
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State that lasts for one run.
|
|
3
|
+
*
|
|
4
|
+
* Registry answers, prefetched documents, recorded misses and local cache
|
|
5
|
+
* lookups are kept in module state, so that a package several lockfiles name
|
|
6
|
+
* costs one lookup. A run is one BOM generation. The CLI exits when it ends, but
|
|
7
|
+
* a long-lived process such as the server performs many, and nothing a run
|
|
8
|
+
* remembered should outlive it: the registries publish new releases, builds
|
|
9
|
+
* download new artifacts, and the memory belongs to the next scan.
|
|
10
|
+
*
|
|
11
|
+
* Every module that keeps such state registers a reset when it loads. The
|
|
12
|
+
* server calls {@link resetRunState} between scans. A module that has not
|
|
13
|
+
* loaded yet holds nothing to reset.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** @type {Map<string, () => void>} */
|
|
17
|
+
const runResets = new Map();
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Register the function that empties a module's per-run state.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} name Unique name, normally the module and what it holds
|
|
23
|
+
* @param {() => void} reset Empties that state
|
|
24
|
+
* @returns {void}
|
|
25
|
+
*/
|
|
26
|
+
export function registerRunReset(name, reset) {
|
|
27
|
+
runResets.set(name, reset);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Empty the per-run state of every module that registered one.
|
|
32
|
+
*
|
|
33
|
+
* @returns {void}
|
|
34
|
+
*/
|
|
35
|
+
export function resetRunState() {
|
|
36
|
+
for (const reset of runResets.values()) {
|
|
37
|
+
reset();
|
|
38
|
+
}
|
|
39
|
+
}
|
package/lib/core/toml.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TOML parsing with stable, ordinary objects.
|
|
3
|
+
*
|
|
4
|
+
* smol-toml 1.9 began returning tables and inline tables with a `null`
|
|
5
|
+
* prototype so that a hostile `__proto__` key in a manifest cannot pollute
|
|
6
|
+
* `Object.prototype`. The SBOM data itself is unaffected, but cdxgen hands
|
|
7
|
+
* parsed manifests straight to callers (and `assert.deepStrictEqual` in the
|
|
8
|
+
* test suite), which compare by prototype and would see every table as a
|
|
9
|
+
* different kind of value. Re-wrapping the parse result with the standard
|
|
10
|
+
* prototype keeps the pre-1.9 contract: plain objects all the way down,
|
|
11
|
+
* `__proto__` keys included as ordinary own properties.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { parse as smolParse } from "smol-toml";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Recursively rebuild a parsed value with `Object.prototype` roots.
|
|
18
|
+
*
|
|
19
|
+
* Class instances other than plain tables (smol-toml dates, strings, numbers)
|
|
20
|
+
* are passed through untouched, and arrays are copied only when an element
|
|
21
|
+
* actually changes, so unmodified branches keep their original identity.
|
|
22
|
+
*
|
|
23
|
+
* @param {unknown} value value produced by smol-toml's parse
|
|
24
|
+
* @returns {unknown} the same data with plain-prototype objects
|
|
25
|
+
*/
|
|
26
|
+
function toPlainPrototype(value) {
|
|
27
|
+
if (!value || typeof value !== "object") {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
let changed = false;
|
|
32
|
+
const out = new Array(value.length);
|
|
33
|
+
for (let i = 0; i < value.length; i++) {
|
|
34
|
+
out[i] = toPlainPrototype(value[i]);
|
|
35
|
+
if (out[i] !== value[i]) {
|
|
36
|
+
changed = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return changed ? out : value;
|
|
40
|
+
}
|
|
41
|
+
const proto = Object.getPrototypeOf(value);
|
|
42
|
+
if (proto !== null && proto !== Object.prototype) {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
47
|
+
if (key === "__proto__") {
|
|
48
|
+
// Plain assignment would re-point the new object's prototype instead
|
|
49
|
+
// of recording the key, reintroducing the pollution smol-toml guards
|
|
50
|
+
// against.
|
|
51
|
+
Object.defineProperty(out, key, {
|
|
52
|
+
value: toPlainPrototype(entry),
|
|
53
|
+
enumerable: true,
|
|
54
|
+
writable: true,
|
|
55
|
+
configurable: true,
|
|
56
|
+
});
|
|
57
|
+
} else {
|
|
58
|
+
out[key] = toPlainPrototype(entry);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parse a TOML document into plain objects.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} text TOML source
|
|
68
|
+
* @returns {Object} parsed document whose tables use `Object.prototype`
|
|
69
|
+
*/
|
|
70
|
+
export function parseToml(text) {
|
|
71
|
+
return toPlainPrototype(smolParse(text));
|
|
72
|
+
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
} from "../inventory/analyzer.js";
|
|
31
31
|
import { tryBuildPurl } from "../inventory/purl.js";
|
|
32
32
|
import { xml2js } from "../parsers/xml.js";
|
|
33
|
+
import { resolveNpmLicense } from "./npmutils.js";
|
|
33
34
|
|
|
34
35
|
const ASAR_JS_ANALYSIS_EXTENSIONS = new Set([
|
|
35
36
|
".cjs",
|
|
@@ -1040,8 +1041,9 @@ function createArchiveParentComponent(
|
|
|
1040
1041
|
? primaryPackageJson.author
|
|
1041
1042
|
: primaryPackageJson.author?.name || "";
|
|
1042
1043
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1044
|
+
const license = resolveNpmLicense(primaryPackageJson);
|
|
1045
|
+
if (license) {
|
|
1046
|
+
component.license = license;
|
|
1045
1047
|
}
|
|
1046
1048
|
if (primaryPackageJson?.repository) {
|
|
1047
1049
|
const repositoryUrl =
|
|
@@ -3,8 +3,8 @@ import { dirname } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { build } from "@cdxgen/cdx-purl";
|
|
5
5
|
|
|
6
|
-
import { DEBUG_MODE
|
|
7
|
-
import {
|
|
6
|
+
import { DEBUG_MODE } from "../core/activity.js";
|
|
7
|
+
import { shouldFetchPackageMetadata } from "../core/env.js";
|
|
8
8
|
import { safeExistsSync } from "../core/fs.js";
|
|
9
9
|
import { getNpmMetadata } from "./ecosystems.js";
|
|
10
10
|
import {
|
|
@@ -554,11 +554,9 @@ export async function parseBunLock(bunLockFile, options = {}) {
|
|
|
554
554
|
});
|
|
555
555
|
}
|
|
556
556
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
readEnvironmentVariable("FETCH_LICENSE") === "true"
|
|
561
|
-
) {
|
|
557
|
+
// The shared metadata gate: FETCH_LICENSE and the provenance fetch that
|
|
558
|
+
// --bom-audit turns on both reach the registry from here.
|
|
559
|
+
if (shouldFetchPackageMetadata()) {
|
|
562
560
|
if (DEBUG_MODE) {
|
|
563
561
|
console.log(
|
|
564
562
|
`About to fetch npm registry metadata for ${pkgList.length} packages in parseBunLock`,
|