@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
package/lib/evinser/evinser.js
CHANGED
|
@@ -5,6 +5,7 @@ import process from "node:process";
|
|
|
5
5
|
import { Purl } from "@cdxgen/cdx-purl";
|
|
6
6
|
|
|
7
7
|
import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
|
|
8
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
8
9
|
import { PROJECT_TYPE_ALIASES } from "../core/env.js";
|
|
9
10
|
import {
|
|
10
11
|
getAllFiles,
|
|
@@ -15,14 +16,20 @@ import {
|
|
|
15
16
|
safeRmSync,
|
|
16
17
|
safeWriteSync,
|
|
17
18
|
} from "../core/fs.js";
|
|
19
|
+
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
20
|
+
import { hasCppSources } from "../ecosystems/cppEvidence.js";
|
|
21
|
+
import {
|
|
22
|
+
createIncludeAttributor,
|
|
23
|
+
parseCIncludeSlices,
|
|
24
|
+
} from "../ecosystems/cppIncludeResolver.js";
|
|
18
25
|
import {
|
|
19
26
|
analyzeGolemProject,
|
|
20
27
|
collectGolemEvidence,
|
|
28
|
+
collectGolemServices,
|
|
21
29
|
isGolemGoLanguage,
|
|
22
30
|
} from "../ecosystems/golem.js";
|
|
23
31
|
import {
|
|
24
32
|
collectGradleDependencies,
|
|
25
|
-
getGradleCommand,
|
|
26
33
|
getMavenCommand,
|
|
27
34
|
} from "../ecosystems/gradleutils.js";
|
|
28
35
|
import {
|
|
@@ -40,6 +47,14 @@ import {
|
|
|
40
47
|
isRusiRustLanguage,
|
|
41
48
|
} from "../ecosystems/rusi.js";
|
|
42
49
|
import {
|
|
50
|
+
analyzeScalaProject,
|
|
51
|
+
collectScalasemApiEndpoints,
|
|
52
|
+
collectScalasemEvidence,
|
|
53
|
+
collectScalasemServices,
|
|
54
|
+
isScalasemLanguage,
|
|
55
|
+
} from "../ecosystems/scalasem.js";
|
|
56
|
+
import {
|
|
57
|
+
atomBuildToolArgs,
|
|
43
58
|
atomCompileCommandsArgs,
|
|
44
59
|
buildAtomCommandEnv,
|
|
45
60
|
executeAtom,
|
|
@@ -53,6 +68,7 @@ import {
|
|
|
53
68
|
collectDosaiCryptoComponents,
|
|
54
69
|
findCryptoAlgos,
|
|
55
70
|
} from "../inventory/cbomutils.js";
|
|
71
|
+
import { cmakeBuildDirCandidates } from "../inventory/cmakeBuildDirs.js";
|
|
56
72
|
import { collectMvnDependencies } from "../inventory/deps.js";
|
|
57
73
|
import { mergeServices } from "../inventory/depsUtils.js";
|
|
58
74
|
import {
|
|
@@ -64,6 +80,10 @@ import {
|
|
|
64
80
|
collectDosaiServicesFromMethods,
|
|
65
81
|
createDosaiDataFlowSlice,
|
|
66
82
|
createDosaiMethodsSlice,
|
|
83
|
+
dosaiFailureReported,
|
|
84
|
+
dosaiOmittedSections,
|
|
85
|
+
dosaiReportFileOmittedSections,
|
|
86
|
+
dosaiSectionsCdxgenReads,
|
|
67
87
|
isDosaiDotnetLanguage,
|
|
68
88
|
normalizeDosaiServiceMap,
|
|
69
89
|
persistDosaiSemanticsReport,
|
|
@@ -141,6 +161,81 @@ function filterAtomSliceData(sliceData, options = {}) {
|
|
|
141
161
|
return filterAtomSlicesByExcludePatterns(sliceData, options.exclude);
|
|
142
162
|
}
|
|
143
163
|
|
|
164
|
+
/**
|
|
165
|
+
* The project type whose evidence a package URL type belongs to. Generic
|
|
166
|
+
* purls are left out because many collectors use that type; they count as C
|
|
167
|
+
* only when the tree holds C or C++ sources.
|
|
168
|
+
*/
|
|
169
|
+
const EVIDENCE_LANGUAGE_BY_PURL_TYPE = {
|
|
170
|
+
cargo: "rust",
|
|
171
|
+
cocoapods: "swift",
|
|
172
|
+
composer: "php",
|
|
173
|
+
conan: "c",
|
|
174
|
+
gem: "ruby",
|
|
175
|
+
golang: "go",
|
|
176
|
+
maven: "java",
|
|
177
|
+
npm: "js",
|
|
178
|
+
nuget: "csharp",
|
|
179
|
+
pypi: "python",
|
|
180
|
+
swift: "swift",
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The evidence language of a `cdxgen --evidence` run that names no project
|
|
185
|
+
* type.
|
|
186
|
+
*
|
|
187
|
+
* A directory with a Scala build takes the scalasem path. Otherwise the
|
|
188
|
+
* generated BOM decides: Java when it lists Maven packages, as before, and
|
|
189
|
+
* else the language most of its packages belong to. A BOM with no package in
|
|
190
|
+
* a language the evidence step analyses gives no language, so no analyzer
|
|
191
|
+
* runs over sources it cannot read.
|
|
192
|
+
*
|
|
193
|
+
* @param {string} dirPath Project directory
|
|
194
|
+
* @param {Object} [bomJson] The BOM generated for the directory
|
|
195
|
+
* @param {Object} [options] CLI options, for the exclude patterns
|
|
196
|
+
* @returns {string|undefined} Project type for the evidence phase
|
|
197
|
+
*/
|
|
198
|
+
export function defaultEvidenceLanguage(dirPath, bomJson, options = {}) {
|
|
199
|
+
const scalaBuildFiles = [
|
|
200
|
+
"build.sbt",
|
|
201
|
+
"build.sc",
|
|
202
|
+
"build.mill",
|
|
203
|
+
"project.scala",
|
|
204
|
+
];
|
|
205
|
+
if (
|
|
206
|
+
scalaBuildFiles.some((file) => safeExistsSync(join(dirPath || ".", file)))
|
|
207
|
+
) {
|
|
208
|
+
return "scala";
|
|
209
|
+
}
|
|
210
|
+
const counts = new Map();
|
|
211
|
+
let cppSources;
|
|
212
|
+
for (const component of bomJson?.components || []) {
|
|
213
|
+
const purl = `${component?.purl || ""}`;
|
|
214
|
+
if (!purl.startsWith("pkg:") || !purl.includes("/")) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const purlType = purl.slice(4, purl.indexOf("/")).toLowerCase();
|
|
218
|
+
let language = EVIDENCE_LANGUAGE_BY_PURL_TYPE[purlType];
|
|
219
|
+
if (!language && purlType === "generic") {
|
|
220
|
+
cppSources ??= hasCppSources(dirPath || ".", options);
|
|
221
|
+
language = cppSources ? "c" : undefined;
|
|
222
|
+
}
|
|
223
|
+
if (language) {
|
|
224
|
+
counts.set(language, (counts.get(language) || 0) + 1);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (counts.has("java")) {
|
|
228
|
+
return "java";
|
|
229
|
+
}
|
|
230
|
+
let chosen;
|
|
231
|
+
for (const [language, count] of counts) {
|
|
232
|
+
if (!chosen || count > counts.get(chosen)) {
|
|
233
|
+
chosen = language;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return chosen;
|
|
237
|
+
}
|
|
238
|
+
|
|
144
239
|
/**
|
|
145
240
|
* Build the options for the evinse phase (prepareDB, analyzeProject, and
|
|
146
241
|
* createEvinseFile) from the cdxgen CLI options.
|
|
@@ -178,6 +273,18 @@ export function buildEvinseOptions(options, args, input) {
|
|
|
178
273
|
profile: options.profile,
|
|
179
274
|
jsonPretty: options.jsonPretty,
|
|
180
275
|
exclude: options.exclude,
|
|
276
|
+
// Forwarded so a failed rusi/golem run can claim the --fail-on-error exit
|
|
277
|
+
// instead of degrading the reachability evidence in silence.
|
|
278
|
+
failOnError: options.failOnError,
|
|
279
|
+
// The scala analyzer reads these: a command override, the test-source
|
|
280
|
+
// switch and the install policy that decides whether builds may run.
|
|
281
|
+
scalasemCommand: options.scalasemCommand,
|
|
282
|
+
scalasemIncludeTests: options.scalasemIncludeTests,
|
|
283
|
+
scalasem: options.scalasem,
|
|
284
|
+
installDeps: options.installDeps,
|
|
285
|
+
requiredOnly: options.requiredOnly,
|
|
286
|
+
// --deep publishes the namespaces the Scala classpath join reads.
|
|
287
|
+
scalasemNamespaces: options.deep,
|
|
181
288
|
// Absolute path of the deps slice createCsharpBom writes for --deep runs,
|
|
182
289
|
// so analyzeProject can reuse it instead of rerunning dosai methods.
|
|
183
290
|
depsSlicesFile: options.depsSlicesFile
|
|
@@ -239,7 +346,7 @@ export async function prepareDB(options) {
|
|
|
239
346
|
await catalogMavenDeps(dirPath, purlsJars, Namespaces, options);
|
|
240
347
|
}
|
|
241
348
|
if (gradleFiles?.length) {
|
|
242
|
-
await catalogGradleDeps(dirPath, purlsJars, Namespaces);
|
|
349
|
+
await catalogGradleDeps(dirPath, purlsJars, Namespaces, options);
|
|
243
350
|
}
|
|
244
351
|
}
|
|
245
352
|
for (const purl of Object.keys(purlsToSlice)) {
|
|
@@ -248,11 +355,42 @@ export async function prepareDB(options) {
|
|
|
248
355
|
return { sequelize, Namespaces, Usages, DataFlows };
|
|
249
356
|
}
|
|
250
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Read the jar namespace map cdxgen writes next to the BOM it generates
|
|
360
|
+
* (`<bom>.map`). The map for the BOM passed as `--input` is preferred; a
|
|
361
|
+
* `bom.json.map` in the project directory is the fallback.
|
|
362
|
+
*
|
|
363
|
+
* @param {string} dirPath Project directory scanned by evinse
|
|
364
|
+
* @param {Object} [options] CLI options, for `input`
|
|
365
|
+
* @returns {Object|undefined} The mapping, when a non-empty one exists.
|
|
366
|
+
*/
|
|
367
|
+
export function readNamespaceMap(dirPath, options = {}) {
|
|
368
|
+
const candidates = [
|
|
369
|
+
options?.input ? `${options.input}.map` : undefined,
|
|
370
|
+
join(dirPath, "bom.json.map"),
|
|
371
|
+
];
|
|
372
|
+
for (const mapFile of candidates) {
|
|
373
|
+
if (!mapFile || !safeExistsSync(mapFile)) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
const mapData = JSON.parse(fs.readFileSync(mapFile, "utf-8"));
|
|
378
|
+
if (mapData && Object.keys(mapData).length) {
|
|
379
|
+
return mapData;
|
|
380
|
+
}
|
|
381
|
+
} catch (_err) {
|
|
382
|
+
// An unreadable map is the same as no map.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
|
|
251
388
|
/**
|
|
252
389
|
* Collect Maven jar namespace mappings for the purls found in the SBOM.
|
|
253
390
|
*
|
|
254
|
-
* If a
|
|
255
|
-
* otherwise the maven command is invoked to collect the jar
|
|
391
|
+
* If cdxgen left a namespace map next to the BOM (see {@link readNamespaceMap})
|
|
392
|
+
* it is reused; otherwise the maven command is invoked to collect the jar
|
|
393
|
+
* dependencies. Each
|
|
256
394
|
* resolved purl is recorded in the `purlsJars` map and persisted in the
|
|
257
395
|
* `Namespaces` model.
|
|
258
396
|
*
|
|
@@ -268,22 +406,26 @@ export async function catalogMavenDeps(
|
|
|
268
406
|
Namespaces,
|
|
269
407
|
options = {},
|
|
270
408
|
) {
|
|
271
|
-
let jarNSMapping;
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
409
|
+
let jarNSMapping = readNamespaceMap(dirPath, options);
|
|
410
|
+
if (!jarNSMapping && !isProjectToolingAllowed(options)) {
|
|
411
|
+
// `mvn dependency:copy-dependencies` evaluates the project's build and
|
|
412
|
+
// its `.mvn` configuration, so only the local repository is read.
|
|
413
|
+
console.log(
|
|
414
|
+
options.withDeepJarCollector
|
|
415
|
+
? "Skipping 'mvn dependency:copy-dependencies' since dependency installation is disabled. Only the jars already in the local Maven repository are used."
|
|
416
|
+
: "Skipping 'mvn dependency:copy-dependencies' since dependency installation is disabled. Pass --with-deep-jar-collector to use the jars already in the local Maven repository.",
|
|
417
|
+
);
|
|
418
|
+
if (options.withDeepJarCollector) {
|
|
419
|
+
jarNSMapping = await collectMvnDependencies(
|
|
420
|
+
undefined,
|
|
421
|
+
undefined,
|
|
422
|
+
true,
|
|
423
|
+
true,
|
|
276
424
|
);
|
|
277
|
-
if (mapData && Object.keys(mapData).length) {
|
|
278
|
-
jarNSMapping = mapData;
|
|
279
|
-
}
|
|
280
|
-
} catch (_err) {
|
|
281
|
-
// ignore
|
|
282
425
|
}
|
|
283
|
-
}
|
|
284
|
-
if (!jarNSMapping) {
|
|
426
|
+
} else if (!jarNSMapping) {
|
|
285
427
|
console.log("About to collect jar dependencies for the path", dirPath);
|
|
286
|
-
const mavenCmd = getMavenCommand(dirPath, dirPath);
|
|
428
|
+
const mavenCmd = getMavenCommand(dirPath, dirPath, options);
|
|
287
429
|
// collect all jars including from the cache if data-flow mode is enabled
|
|
288
430
|
jarNSMapping = await collectMvnDependencies(
|
|
289
431
|
mavenCmd,
|
|
@@ -324,18 +466,28 @@ export async function catalogMavenDeps(
|
|
|
324
466
|
* @param {Object} Namespaces Sequelize-like Namespaces model used to persist pom and namespaces
|
|
325
467
|
* @returns {Promise<void>}
|
|
326
468
|
*/
|
|
327
|
-
export async function catalogGradleDeps(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
469
|
+
export async function catalogGradleDeps(
|
|
470
|
+
dirPath,
|
|
471
|
+
purlsJars,
|
|
472
|
+
Namespaces,
|
|
473
|
+
options = {},
|
|
474
|
+
) {
|
|
475
|
+
// The map cdxgen wrote next to the BOM already holds the namespaces; the
|
|
476
|
+
// Gradle cache is walked only without it.
|
|
477
|
+
let jarNSMapping = readNamespaceMap(dirPath, options);
|
|
478
|
+
if (!jarNSMapping) {
|
|
479
|
+
console.log(
|
|
480
|
+
"About to collect jar dependencies from the gradle cache. This would take a while ...",
|
|
481
|
+
);
|
|
482
|
+
// Only the Gradle cache is read, so no Gradle command is resolved: that
|
|
483
|
+
// would probe the project's own gradlew.
|
|
484
|
+
jarNSMapping = await collectGradleDependencies(
|
|
485
|
+
undefined,
|
|
486
|
+
dirPath,
|
|
487
|
+
false,
|
|
488
|
+
true,
|
|
489
|
+
);
|
|
490
|
+
}
|
|
339
491
|
if (jarNSMapping) {
|
|
340
492
|
for (const purl of Object.keys(jarNSMapping)) {
|
|
341
493
|
purlsJars[purl] = jarNSMapping[purl].jarFile;
|
|
@@ -515,7 +667,9 @@ export async function createSlice(
|
|
|
515
667
|
resolve(slicesFile),
|
|
516
668
|
options,
|
|
517
669
|
);
|
|
518
|
-
|
|
670
|
+
// A stopped run, or one the .NET host refused to start, has already said
|
|
671
|
+
// why; it says nothing about whether the project is supported (issue 4438).
|
|
672
|
+
if (!sliceResult && !dosaiFailureReported(resolve(slicesFile))) {
|
|
519
673
|
console.warn(
|
|
520
674
|
`Unable to generate ${sliceType} slice using dosai. Check if this is a supported .NET project.`,
|
|
521
675
|
);
|
|
@@ -526,6 +680,13 @@ export async function createSlice(
|
|
|
526
680
|
slicesFile,
|
|
527
681
|
};
|
|
528
682
|
}
|
|
683
|
+
const buildToolArgs = atomBuildToolArgs(language, options);
|
|
684
|
+
if (!buildToolArgs) {
|
|
685
|
+
console.log(
|
|
686
|
+
`Skipping the ${sliceType} slice for ${resolve(filePath)} since dependency installation is disabled and the installed atom cannot be stopped from running Maven. Upgrade atom to slice this project without its build.`,
|
|
687
|
+
);
|
|
688
|
+
return { tempDir: sliceOutputDir, tempDirOwned };
|
|
689
|
+
}
|
|
529
690
|
console.log(
|
|
530
691
|
`Creating ${sliceType} slice for ${resolve(filePath)}. Please wait ...`,
|
|
531
692
|
);
|
|
@@ -552,15 +713,9 @@ export async function createSlice(
|
|
|
552
713
|
"Use an official cdxgen container image to improve the precision of endpoints detection (for SaaSBOM).",
|
|
553
714
|
);
|
|
554
715
|
}
|
|
555
|
-
if (
|
|
716
|
+
if (language === "ruby") {
|
|
556
717
|
args.push("--remove-atom");
|
|
557
718
|
}
|
|
558
|
-
if (["scala"].includes(language)) {
|
|
559
|
-
semanticsSlicesFile = join(
|
|
560
|
-
sliceOutputDir,
|
|
561
|
-
basename(options.semanticsSlicesFile || "semantics.slices.json"),
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
719
|
}
|
|
565
720
|
args = args.concat([
|
|
566
721
|
"-l",
|
|
@@ -580,6 +735,7 @@ export async function createSlice(
|
|
|
580
735
|
args.push(readEnvironmentVariable("ATOM_SLICE_DEPTH"));
|
|
581
736
|
}
|
|
582
737
|
args.push(...atomCompileCommandsArgs(filePath, language, options));
|
|
738
|
+
args.push(...buildToolArgs);
|
|
583
739
|
args.push(resolve(filePath));
|
|
584
740
|
const atomExcludeEnv = buildAtomCommandEnv(options, language);
|
|
585
741
|
// Execute atom
|
|
@@ -632,15 +788,6 @@ export async function createSlice(
|
|
|
632
788
|
`openapi spec file "${join(filePath, openapiSpecFile)}" was not generated successfully. Check if atom-tools pypi package is installed and available in PATH.`,
|
|
633
789
|
);
|
|
634
790
|
}
|
|
635
|
-
if (
|
|
636
|
-
["scala"].includes(language) &&
|
|
637
|
-
sliceType === "usages" &&
|
|
638
|
-
!safeExistsSync(semanticsSlicesFile)
|
|
639
|
-
) {
|
|
640
|
-
console.log(
|
|
641
|
-
`Semantics slices file "${semanticsSlicesFile}" was not generated successfully. Try running atom cli in Java mode.`,
|
|
642
|
-
);
|
|
643
|
-
}
|
|
644
791
|
return {
|
|
645
792
|
tempDir: sliceOutputDir,
|
|
646
793
|
tempDirOwned,
|
|
@@ -769,8 +916,8 @@ function usableSlicesFile(slicesFile) {
|
|
|
769
916
|
* describe the same project directory and must not predate the input SBOM:
|
|
770
917
|
* `cdxgen --evidence` writes a fresh SBOM before evinse runs, so an older
|
|
771
918
|
* slice was made from an earlier state of the sources and is regenerated.
|
|
772
|
-
* Scala
|
|
773
|
-
*
|
|
919
|
+
* Scala reports are handled by the scalasem analyzer, which applies its own
|
|
920
|
+
* rules.
|
|
774
921
|
*
|
|
775
922
|
* @param {string} language Project language
|
|
776
923
|
* @param {string} slicesFile Candidate semantics slices file
|
|
@@ -797,10 +944,7 @@ export function loadReusableSemanticsSlice(
|
|
|
797
944
|
return undefined;
|
|
798
945
|
}
|
|
799
946
|
if (!PROJECT_TYPE_ALIASES.swift.includes(language)) {
|
|
800
|
-
|
|
801
|
-
return Object.keys(parsed).some((key) => key.endsWith(".scala"))
|
|
802
|
-
? parsed
|
|
803
|
-
: undefined;
|
|
947
|
+
return undefined;
|
|
804
948
|
}
|
|
805
949
|
if (!(parsed.fileIndexes || parsed.buildSymbols || parsed.moduleInfos)) {
|
|
806
950
|
return undefined;
|
|
@@ -818,6 +962,260 @@ export function loadReusableSemanticsSlice(
|
|
|
818
962
|
return parsed;
|
|
819
963
|
}
|
|
820
964
|
|
|
965
|
+
/**
|
|
966
|
+
* The Scala evidence path: scalasem only, never atom. The branch returns
|
|
967
|
+
* early for every entry point (evinse, --evidence, cbom, saasbom) and every
|
|
968
|
+
* Scala project type, so no usages, reachables, data-flow or atom-run
|
|
969
|
+
* scalasem happens here. Slices the user passes are still read: a version 1
|
|
970
|
+
* semantics slice keeps its usedTypes join, usages and reachables slices
|
|
971
|
+
* keep their own evidence, and an OpenAPI spec keeps its services.
|
|
972
|
+
*
|
|
973
|
+
* @param {string} language Project language, normalized to `scala`.
|
|
974
|
+
* @param {string} dirPath Project directory.
|
|
975
|
+
* @param {Object} options Command line options.
|
|
976
|
+
* @param {Object} bomJson Parsed input SBOM.
|
|
977
|
+
* @param {Object[]} components Components of the input SBOM.
|
|
978
|
+
* @param {Object} purlLocationMap Occurrences seeded from the input SBOM.
|
|
979
|
+
* @param {Object} purlImportsMap Imports seeded from the input SBOM.
|
|
980
|
+
* @param {Object} dbObjMap The evinse database objects.
|
|
981
|
+
* @returns {Promise<Object>} Slice artefacts for `createEvinseFile`.
|
|
982
|
+
*/
|
|
983
|
+
async function analyzeScalaEvidenceProject(
|
|
984
|
+
language,
|
|
985
|
+
dirPath,
|
|
986
|
+
options,
|
|
987
|
+
bomJson,
|
|
988
|
+
components,
|
|
989
|
+
purlLocationMap,
|
|
990
|
+
purlImportsMap,
|
|
991
|
+
dbObjMap,
|
|
992
|
+
) {
|
|
993
|
+
let servicesMap = {};
|
|
994
|
+
const dataFlowFrames = {};
|
|
995
|
+
let componentPropertiesMap = {};
|
|
996
|
+
let metadataProperties = [];
|
|
997
|
+
let cryptoComponents = [];
|
|
998
|
+
const cryptoGeneratePurls = {};
|
|
999
|
+
const mergeLocations = (locationMap) => {
|
|
1000
|
+
for (const [purl, locations] of Object.entries(locationMap || {})) {
|
|
1001
|
+
purlLocationMap[purl] ??= new Set();
|
|
1002
|
+
for (const location of locations) {
|
|
1003
|
+
purlLocationMap[purl].add(location);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
const mergeFrames = (framesMap) => {
|
|
1008
|
+
for (const [purl, stacks] of Object.entries(framesMap || {})) {
|
|
1009
|
+
dataFlowFrames[purl] = [...(dataFlowFrames[purl] || []), ...stacks];
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
const mergeProvides = (providesMap) => {
|
|
1013
|
+
for (const [purl, refs] of Object.entries(providesMap || {})) {
|
|
1014
|
+
cryptoGeneratePurls[purl] ??= new Set();
|
|
1015
|
+
for (const ref of refs || []) {
|
|
1016
|
+
cryptoGeneratePurls[purl].add(ref);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
const analysis = analyzeScalaProject(dirPath, options);
|
|
1021
|
+
if (analysis?.report) {
|
|
1022
|
+
const evidence = await collectScalasemEvidence(
|
|
1023
|
+
analysis.report,
|
|
1024
|
+
[bomJson.metadata?.component, ...components].filter(Boolean),
|
|
1025
|
+
{ ...options, scalaNamespaceMap: readNamespaceMap(dirPath, options) },
|
|
1026
|
+
);
|
|
1027
|
+
mergeLocations(evidence.purlLocationMap);
|
|
1028
|
+
mergeFrames(evidence.dataFlowFrames);
|
|
1029
|
+
mergeProvides(evidence.cryptoGeneratePurls);
|
|
1030
|
+
componentPropertiesMap = evidence.componentPropertiesMap;
|
|
1031
|
+
cryptoComponents = evidence.cryptoComponents;
|
|
1032
|
+
metadataProperties = evidence.metadataProperties;
|
|
1033
|
+
servicesMap = collectScalasemServices(analysis.report, servicesMap);
|
|
1034
|
+
servicesMap = collectScalasemApiEndpoints(analysis.report, servicesMap);
|
|
1035
|
+
}
|
|
1036
|
+
if (analysis?.metadataProperties?.length) {
|
|
1037
|
+
for (const property of analysis.metadataProperties) {
|
|
1038
|
+
if (
|
|
1039
|
+
!metadataProperties.some(
|
|
1040
|
+
(known) =>
|
|
1041
|
+
known.name === property.name && known.value === property.value,
|
|
1042
|
+
)
|
|
1043
|
+
) {
|
|
1044
|
+
metadataProperties.push(property);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
// A version 1 semantics slice still goes through the usedTypes join, merged
|
|
1049
|
+
// with the report evidence rather than replacing it.
|
|
1050
|
+
if (analysis?.v1Slice) {
|
|
1051
|
+
mergeLocations(
|
|
1052
|
+
findPurlLocations(components, analysis.v1Slice).purlLocationMap,
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
// Usages and reachables slices the user made, with atom or otherwise, are
|
|
1056
|
+
// read as they are for any other language; none is ever generated here.
|
|
1057
|
+
let usagesSlicesFile;
|
|
1058
|
+
let reachablesSlicesFile;
|
|
1059
|
+
if (
|
|
1060
|
+
(options.withReachables || options.includeCrypto) &&
|
|
1061
|
+
usableSlicesFile(options.reachablesSlicesFile)
|
|
1062
|
+
) {
|
|
1063
|
+
reachablesSlicesFile = options.reachablesSlicesFile;
|
|
1064
|
+
const reachables = collectReachableFrames(
|
|
1065
|
+
language,
|
|
1066
|
+
filterAtomSliceData(readReachablesSlices(reachablesSlicesFile), options),
|
|
1067
|
+
);
|
|
1068
|
+
mergeFrames(reachables.dataFlowFrames);
|
|
1069
|
+
mergeProvides(reachables.cryptoGeneratePurls);
|
|
1070
|
+
const knownRefs = new Set(
|
|
1071
|
+
cryptoComponents.map((asset) => asset["bom-ref"]),
|
|
1072
|
+
);
|
|
1073
|
+
cryptoComponents = cryptoComponents.concat(
|
|
1074
|
+
(reachables.cryptoComponents || []).filter(
|
|
1075
|
+
(asset) => !knownRefs.has(asset["bom-ref"]),
|
|
1076
|
+
),
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
let userDefinedTypesMap = {};
|
|
1080
|
+
if (usableSlicesFile(options.usagesSlicesFile)) {
|
|
1081
|
+
usagesSlicesFile = options.usagesSlicesFile;
|
|
1082
|
+
const usageSlice = filterAtomSliceData(
|
|
1083
|
+
readSlicesFile(usagesSlicesFile, "usages"),
|
|
1084
|
+
options,
|
|
1085
|
+
);
|
|
1086
|
+
if (usageSlice && Object.keys(usageSlice).length) {
|
|
1087
|
+
const retMap = await parseObjectSlices(
|
|
1088
|
+
language,
|
|
1089
|
+
usageSlice,
|
|
1090
|
+
dbObjMap,
|
|
1091
|
+
servicesMap,
|
|
1092
|
+
purlLocationMap,
|
|
1093
|
+
purlImportsMap,
|
|
1094
|
+
);
|
|
1095
|
+
servicesMap = retMap.servicesMap || servicesMap;
|
|
1096
|
+
userDefinedTypesMap = retMap.userDefinedTypesMap || {};
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
// An OpenAPI spec the user has (atom-tools' conversion of this report, or
|
|
1100
|
+
// the project's own) adds its routes to the report's: a route both name is
|
|
1101
|
+
// one service.
|
|
1102
|
+
const openapiSpecFile = options.openapiSpecFile
|
|
1103
|
+
? resolve(dirPath, options.openapiSpecFile)
|
|
1104
|
+
: undefined;
|
|
1105
|
+
if (openapiSpecFile && safeExistsSync(openapiSpecFile)) {
|
|
1106
|
+
const openapiServices = {};
|
|
1107
|
+
detectServicesFromOpenAPI(language, openapiSpecFile, openapiServices);
|
|
1108
|
+
mergeServiceDefinitions(servicesMap, openapiServices);
|
|
1109
|
+
}
|
|
1110
|
+
// A BOM without a project component still needs somewhere for the run's
|
|
1111
|
+
// metadata properties to live; a nameless entry would explain nothing.
|
|
1112
|
+
let workspaceComponent;
|
|
1113
|
+
if (!bomJson.metadata?.component) {
|
|
1114
|
+
workspaceComponent = {
|
|
1115
|
+
type: "application",
|
|
1116
|
+
name: basename(resolve(dirPath)),
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
return {
|
|
1120
|
+
purlLocationMap,
|
|
1121
|
+
servicesMap,
|
|
1122
|
+
dataFlowFrames,
|
|
1123
|
+
componentPropertiesMap,
|
|
1124
|
+
metadataProperties,
|
|
1125
|
+
cryptoComponents,
|
|
1126
|
+
cryptoGeneratePurls,
|
|
1127
|
+
usagesSlicesFile,
|
|
1128
|
+
reachablesSlicesFile,
|
|
1129
|
+
semanticsSlicesFile: analysis?.reportFile,
|
|
1130
|
+
userDefinedTypesMap,
|
|
1131
|
+
workspaceComponent,
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Merge service definitions into a services map: endpoints join, and a
|
|
1137
|
+
* property another source already carries is not added again, whatever the
|
|
1138
|
+
* case of an HTTP method.
|
|
1139
|
+
*
|
|
1140
|
+
* @param {Object} target Services map that receives the definitions.
|
|
1141
|
+
* @param {Object} source Services map to merge in.
|
|
1142
|
+
* @returns {Object} The target map.
|
|
1143
|
+
*/
|
|
1144
|
+
export function mergeServiceDefinitions(target, source) {
|
|
1145
|
+
const sameProperty = (left, right) =>
|
|
1146
|
+
left.name === right.name &&
|
|
1147
|
+
(left.name === "cdx:service:httpMethod"
|
|
1148
|
+
? String(left.value).toUpperCase() === String(right.value).toUpperCase()
|
|
1149
|
+
: left.value === right.value);
|
|
1150
|
+
for (const [name, definition] of Object.entries(source || {})) {
|
|
1151
|
+
const existing = target[name];
|
|
1152
|
+
if (!existing) {
|
|
1153
|
+
target[name] = definition;
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
existing.endpoints ??= new Set();
|
|
1157
|
+
for (const endpoint of definition.endpoints || []) {
|
|
1158
|
+
existing.endpoints.add(endpoint);
|
|
1159
|
+
}
|
|
1160
|
+
existing.properties ??= [];
|
|
1161
|
+
for (const property of definition.properties || []) {
|
|
1162
|
+
if (!existing.properties.some((known) => sameProperty(known, property))) {
|
|
1163
|
+
existing.properties.push(property);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
if (definition.authenticated) {
|
|
1167
|
+
existing.authenticated = true;
|
|
1168
|
+
}
|
|
1169
|
+
if (definition.xTrustBoundary) {
|
|
1170
|
+
existing.xTrustBoundary = true;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
return target;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* Collect the services of a Go project for the golem evidence path (#4498).
|
|
1178
|
+
*
|
|
1179
|
+
* Two sources are kept and merged by service name, the way the Scala path
|
|
1180
|
+
* merges a user-provided spec into its report's services. They roll services
|
|
1181
|
+
* up differently — golem per server, OpenAPI per path and method — so a
|
|
1182
|
+
* route both name appears under both entries:
|
|
1183
|
+
*
|
|
1184
|
+
* - the golem report itself, whose `services[]` section rolls the registered
|
|
1185
|
+
* routes up per server (a gin engine, a net/http listener) with methods and
|
|
1186
|
+
* frameworks, and the outbound hosts it calls into `kind: "external"`
|
|
1187
|
+
* services flagged as trust-boundary crossings;
|
|
1188
|
+
* - an OpenAPI document, either the one `--openapi-spec-file` names or the
|
|
1189
|
+
* `openapi.json` an earlier `atom-tools convert -t go` left in the repo,
|
|
1190
|
+
* which is reused when present so `golem`/`atom-tools` and `cdxgen` can be
|
|
1191
|
+
* run as separate steps.
|
|
1192
|
+
*
|
|
1193
|
+
* @param {object|null|undefined} golemReport Parsed golem analysis report,
|
|
1194
|
+
* when one was produced; a missing or `null` report adds no services.
|
|
1195
|
+
* @param {string} dirPath Project directory the report was produced from.
|
|
1196
|
+
* @param {Object} options Command line options.
|
|
1197
|
+
* @param {Object} [servicesMap={}] Service map to populate.
|
|
1198
|
+
* @returns {Object} The service map
|
|
1199
|
+
*/
|
|
1200
|
+
export function collectGoServices(
|
|
1201
|
+
golemReport,
|
|
1202
|
+
dirPath,
|
|
1203
|
+
options = {},
|
|
1204
|
+
servicesMap = {},
|
|
1205
|
+
) {
|
|
1206
|
+
servicesMap = collectGolemServices(golemReport, servicesMap);
|
|
1207
|
+
const openapiSpecFile = resolve(
|
|
1208
|
+
dirPath,
|
|
1209
|
+
options.openapiSpecFile || "openapi.json",
|
|
1210
|
+
);
|
|
1211
|
+
if (safeExistsSync(openapiSpecFile)) {
|
|
1212
|
+
const openapiServices = {};
|
|
1213
|
+
detectServicesFromOpenAPI("go", openapiSpecFile, openapiServices);
|
|
1214
|
+
mergeServiceDefinitions(servicesMap, openapiServices);
|
|
1215
|
+
}
|
|
1216
|
+
return servicesMap;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
821
1219
|
/**
|
|
822
1220
|
* Function to analyze the project
|
|
823
1221
|
*
|
|
@@ -827,7 +1225,12 @@ export function loadReusableSemanticsSlice(
|
|
|
827
1225
|
export async function analyzeProject(dbObjMap, options) {
|
|
828
1226
|
const dirPath = options._[0] || ".";
|
|
829
1227
|
const languages = options.language;
|
|
830
|
-
|
|
1228
|
+
let language = Array.isArray(languages) ? languages[0] : languages;
|
|
1229
|
+
// cdxgen --evidence forwards the raw project type, so -t sbt, -t mill and
|
|
1230
|
+
// -t scala3 must be normalized once to behave like -l scala everywhere.
|
|
1231
|
+
if (PROJECT_TYPE_ALIASES.scala.includes(language)) {
|
|
1232
|
+
language = "scala";
|
|
1233
|
+
}
|
|
831
1234
|
let usageSlice;
|
|
832
1235
|
let dataFlowSlice;
|
|
833
1236
|
let reachablesSlice;
|
|
@@ -853,6 +1256,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
853
1256
|
|
|
854
1257
|
if (isGolemGoLanguage(language)) {
|
|
855
1258
|
const golemReport = analyzeGolemProject(dirPath, options);
|
|
1259
|
+
servicesMap = collectGoServices(golemReport, dirPath, options, servicesMap);
|
|
856
1260
|
if (golemReport && Object.keys(golemReport).length) {
|
|
857
1261
|
const golemEvidence = collectGolemEvidence(golemReport, [
|
|
858
1262
|
bomJson.metadata?.component,
|
|
@@ -981,29 +1385,76 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
981
1385
|
};
|
|
982
1386
|
}
|
|
983
1387
|
|
|
1388
|
+
if (isScalasemLanguage(language)) {
|
|
1389
|
+
return analyzeScalaEvidenceProject(
|
|
1390
|
+
language,
|
|
1391
|
+
dirPath,
|
|
1392
|
+
options,
|
|
1393
|
+
bomJson,
|
|
1394
|
+
components,
|
|
1395
|
+
purlLocationMap,
|
|
1396
|
+
purlImportsMap,
|
|
1397
|
+
dbObjMap,
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
|
|
984
1401
|
if (isDosaiDotnetLanguage(language)) {
|
|
985
1402
|
if (options.profile === "research") {
|
|
986
1403
|
options.withDataFlow = true;
|
|
987
1404
|
options.includeCrypto = true;
|
|
988
1405
|
}
|
|
1406
|
+
let needsUsagesRun = true;
|
|
989
1407
|
if (
|
|
990
1408
|
options.usagesSlicesFile &&
|
|
991
1409
|
usableSlicesFile(options.usagesSlicesFile)
|
|
992
1410
|
) {
|
|
993
1411
|
usageSlice = readDosaiMethodsReport(options.usagesSlicesFile);
|
|
994
1412
|
usagesSlicesFile = options.usagesSlicesFile;
|
|
1413
|
+
needsUsagesRun = false;
|
|
1414
|
+
// A user-supplied usages slice is used as given. When it was written
|
|
1415
|
+
// with --sections and left out a section cdxgen reads, the evidence
|
|
1416
|
+
// lacks that input; persistDosaiSemanticsReport reports separately
|
|
1417
|
+
// that the persisted report is partial.
|
|
1418
|
+
const missingInputs = dosaiSectionsCdxgenReads(
|
|
1419
|
+
dosaiOmittedSections(usageSlice),
|
|
1420
|
+
);
|
|
1421
|
+
if (missingInputs.length) {
|
|
1422
|
+
recordDegradation("dosai.usages.trimmed-report", {
|
|
1423
|
+
ecosystem: "csharp",
|
|
1424
|
+
tool: "dosai",
|
|
1425
|
+
impact: "components",
|
|
1426
|
+
command: `usages ${usagesSlicesFile}`,
|
|
1427
|
+
detail: `The supplied dosai methods report was written without sections cdxgen reads: ${missingInputs.join(", ")}. The evidence from those sections is missing. Run dosai methods without --sections, or with every section cdxgen reads.`,
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
995
1430
|
} else if (usableSlicesFile(options.depsSlicesFile)) {
|
|
996
1431
|
// createCsharpBom already ran dosai methods on this tree for --deep runs
|
|
997
1432
|
// and wrote the deps slice. For dosai, that output IS the usages slice,
|
|
998
|
-
// so reuse it instead of invoking dosai a second time
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1433
|
+
// so reuse it instead of invoking dosai a second time - unless it was
|
|
1434
|
+
// written with --sections (a --deep run that performs no evidence
|
|
1435
|
+
// analysis trims it): evinse persists this file as the full native
|
|
1436
|
+
// report, so it must not build on a trimmed one and dosai runs again.
|
|
1437
|
+
const omittedSections = dosaiReportFileOmittedSections(
|
|
1438
|
+
options.depsSlicesFile,
|
|
1439
|
+
);
|
|
1440
|
+
if (omittedSections.length) {
|
|
1441
|
+
if (DEBUG_MODE) {
|
|
1442
|
+
console.log(
|
|
1443
|
+
`The dosai deps slices file "${options.depsSlicesFile}" omits sections (${omittedSections.join(", ")}); running dosai for the full usages report.`,
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
} else {
|
|
1447
|
+
if (DEBUG_MODE) {
|
|
1448
|
+
console.log(
|
|
1449
|
+
`Reusing the dosai deps slices file "${options.depsSlicesFile}" for the usages analysis.`,
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
usageSlice = readDosaiMethodsReport(options.depsSlicesFile);
|
|
1453
|
+
usagesSlicesFile = options.depsSlicesFile;
|
|
1454
|
+
needsUsagesRun = false;
|
|
1003
1455
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
} else {
|
|
1456
|
+
}
|
|
1457
|
+
if (needsUsagesRun) {
|
|
1007
1458
|
retMap = await createSlice(language, dirPath, "usages", options);
|
|
1008
1459
|
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
1009
1460
|
usageSlice = readDosaiMethodsReport(retMap.slicesFile);
|
|
@@ -1011,11 +1462,17 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1011
1462
|
}
|
|
1012
1463
|
}
|
|
1013
1464
|
if (usageSlice && Object.keys(usageSlice).length) {
|
|
1014
|
-
|
|
1465
|
+
// dosai locations are relative to the analyzed directory; a package the
|
|
1466
|
+
// BOM holds in several versions is matched by the project of the file.
|
|
1467
|
+
const dosaiEvidence = collectDosaiPurlEvidence(usageSlice, components, {
|
|
1468
|
+
srcPath: resolve(dirPath),
|
|
1469
|
+
});
|
|
1015
1470
|
// Confidence/evidence facts ride along as properties so an unbuilt tree (Low
|
|
1016
1471
|
// confidence, unresolved evidence) stays distinguishable from an only-imported
|
|
1017
1472
|
// package in the emitted BOM.
|
|
1018
|
-
applyDosaiReachabilityEvidence(usageSlice, components
|
|
1473
|
+
applyDosaiReachabilityEvidence(usageSlice, components, {
|
|
1474
|
+
srcPath: resolve(dirPath),
|
|
1475
|
+
});
|
|
1019
1476
|
for (const [purl, locations] of Object.entries(
|
|
1020
1477
|
dosaiEvidence.purlLocationMap,
|
|
1021
1478
|
)) {
|
|
@@ -1049,7 +1506,9 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1049
1506
|
}
|
|
1050
1507
|
}
|
|
1051
1508
|
if (dataFlowSlice && Object.keys(dataFlowSlice).length) {
|
|
1052
|
-
dataFlowFrames = collectDosaiDataFlowFrames(dataFlowSlice, components
|
|
1509
|
+
dataFlowFrames = collectDosaiDataFlowFrames(dataFlowSlice, components, {
|
|
1510
|
+
srcPath: resolve(dirPath),
|
|
1511
|
+
});
|
|
1053
1512
|
}
|
|
1054
1513
|
}
|
|
1055
1514
|
if (options.includeCrypto) {
|
|
@@ -1145,12 +1604,8 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1145
1604
|
}
|
|
1146
1605
|
}
|
|
1147
1606
|
// Support for semantics slicing
|
|
1148
|
-
if (
|
|
1149
|
-
|
|
1150
|
-
PROJECT_TYPE_ALIASES.scala.includes(language)) &&
|
|
1151
|
-
components.length
|
|
1152
|
-
) {
|
|
1153
|
-
// Reuse existing semantics slices for swift and scala
|
|
1607
|
+
if (PROJECT_TYPE_ALIASES.swift.includes(language) && components.length) {
|
|
1608
|
+
// Reuse existing semantics slices for swift
|
|
1154
1609
|
const reusableSlice = loadReusableSemanticsSlice(
|
|
1155
1610
|
language,
|
|
1156
1611
|
options.semanticsSlicesFile,
|
|
@@ -1160,7 +1615,7 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1160
1615
|
if (reusableSlice) {
|
|
1161
1616
|
semanticsSlice = reusableSlice;
|
|
1162
1617
|
semanticsSlicesFile = options.semanticsSlicesFile;
|
|
1163
|
-
} else
|
|
1618
|
+
} else {
|
|
1164
1619
|
// Generate our own slices for swift
|
|
1165
1620
|
retMap = await createSlice(language, dirPath, "semantics", options);
|
|
1166
1621
|
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
@@ -1169,6 +1624,28 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1169
1624
|
}
|
|
1170
1625
|
}
|
|
1171
1626
|
}
|
|
1627
|
+
let cIncludeLocations = new Map();
|
|
1628
|
+
// A C/C++ include names the file it resolved to and the functions the
|
|
1629
|
+
// including file calls from it (atom 4 and later): those functions are the
|
|
1630
|
+
// usages of the component that provides the file
|
|
1631
|
+
if (
|
|
1632
|
+
["c", "cpp", "c++"].includes(language) &&
|
|
1633
|
+
usageSlice &&
|
|
1634
|
+
Object.keys(usageSlice).length
|
|
1635
|
+
) {
|
|
1636
|
+
const attributed = attributeCIncludes(
|
|
1637
|
+
usageSlice,
|
|
1638
|
+
components,
|
|
1639
|
+
dirPath,
|
|
1640
|
+
options,
|
|
1641
|
+
);
|
|
1642
|
+
for (const [purl, names] of attributed.symbols) {
|
|
1643
|
+
purlImportsMap[purl] = Array.from(
|
|
1644
|
+
new Set([...(purlImportsMap[purl] || []), ...names]),
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
cIncludeLocations = attributed.locations;
|
|
1648
|
+
}
|
|
1172
1649
|
// Parse usage slices
|
|
1173
1650
|
if (usageSlice && Object.keys(usageSlice).length) {
|
|
1174
1651
|
const retMap = await parseObjectSlices(
|
|
@@ -1184,7 +1661,14 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1184
1661
|
servicesMap = retMap.servicesMap;
|
|
1185
1662
|
userDefinedTypesMap = retMap.userDefinedTypesMap;
|
|
1186
1663
|
}
|
|
1187
|
-
//
|
|
1664
|
+
// A component whose calls the slices do not show (code inside the project,
|
|
1665
|
+
// such as a vendored library) is still used where its header is included
|
|
1666
|
+
for (const [purl, includeLocations] of cIncludeLocations) {
|
|
1667
|
+
if (!purlLocationMap[purl]?.size) {
|
|
1668
|
+
purlLocationMap[purl] = new Set(includeLocations);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
// Parse the semantics slices for swift
|
|
1188
1672
|
if (
|
|
1189
1673
|
semanticsSlice &&
|
|
1190
1674
|
Object.keys(semanticsSlice).length &&
|
|
@@ -1192,7 +1676,25 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1192
1676
|
) {
|
|
1193
1677
|
// Identify the purl locations
|
|
1194
1678
|
const retMap = parseSemanticSlices(language, components, semanticsSlice);
|
|
1195
|
-
|
|
1679
|
+
// The usages step may already have locations for a purl; the semantics
|
|
1680
|
+
// pass adds its own rather than replacing them.
|
|
1681
|
+
for (const [apurl, locations] of Object.entries(
|
|
1682
|
+
retMap.purlLocationMap || {},
|
|
1683
|
+
)) {
|
|
1684
|
+
purlLocationMap[apurl] ??= new Set();
|
|
1685
|
+
for (const alocation of locations) {
|
|
1686
|
+
purlLocationMap[apurl].add(alocation);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
// A user-supplied OpenAPI spec must yield services even when the usages
|
|
1691
|
+
// slice is empty.
|
|
1692
|
+
if (
|
|
1693
|
+
openapiSpecFile &&
|
|
1694
|
+
safeExistsSync(openapiSpecFile) &&
|
|
1695
|
+
!Object.keys(servicesMap).length
|
|
1696
|
+
) {
|
|
1697
|
+
detectServicesFromOpenAPI(language, openapiSpecFile, servicesMap);
|
|
1196
1698
|
}
|
|
1197
1699
|
if (options.withDataFlow) {
|
|
1198
1700
|
if (
|
|
@@ -1261,6 +1763,125 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
1261
1763
|
* @param {string} [openapiSpecFile=undefined] Optional path to an OpenAPI spec used for service detection
|
|
1262
1764
|
* @returns {Promise<Object>} Map with `purlLocationMap`, `servicesMap`, and `userDefinedTypesMap`
|
|
1263
1765
|
*/
|
|
1766
|
+
/**
|
|
1767
|
+
* Attribute the include slices of a C/C++ usages report to the BOM
|
|
1768
|
+
* components that provide the files they resolved to: each header's imported
|
|
1769
|
+
* symbols (the functions the including file calls that it declares), and the
|
|
1770
|
+
* include directive's location. Reports from atom releases that do not name
|
|
1771
|
+
* the resolved file attribute nothing.
|
|
1772
|
+
*
|
|
1773
|
+
* @param {Object} usageSlice Parsed usages report
|
|
1774
|
+
* @param {Object[]} components Components of the input BOM
|
|
1775
|
+
* @param {string} dirPath Source directory
|
|
1776
|
+
* @param {Object} options CLI options
|
|
1777
|
+
* @returns {{symbols: Map<string, Set<string>>, locations: Map<string, Set<string>>}}
|
|
1778
|
+
* Imported symbols and include locations (`file#line`) by purl
|
|
1779
|
+
*/
|
|
1780
|
+
export function attributeCIncludes(
|
|
1781
|
+
usageSlice,
|
|
1782
|
+
components,
|
|
1783
|
+
dirPath,
|
|
1784
|
+
options = {},
|
|
1785
|
+
) {
|
|
1786
|
+
const symbols = new Map();
|
|
1787
|
+
const locations = new Map();
|
|
1788
|
+
const includes = [...parseCIncludeSlices(usageSlice)].filter(
|
|
1789
|
+
([, e]) => e.paths.size && e.symbols.size,
|
|
1790
|
+
);
|
|
1791
|
+
if (!includes.length || !components?.length) {
|
|
1792
|
+
return { symbols, locations };
|
|
1793
|
+
}
|
|
1794
|
+
const attribute = createIncludeAttributor({
|
|
1795
|
+
src: dirPath,
|
|
1796
|
+
components,
|
|
1797
|
+
buildDirs: cmakeBuildDirCandidates(dirPath, options),
|
|
1798
|
+
});
|
|
1799
|
+
const withPurl = components.filter((c) => c?.purl);
|
|
1800
|
+
const byName = (name, purlType) =>
|
|
1801
|
+
withPurl.find(
|
|
1802
|
+
(c) =>
|
|
1803
|
+
c.name?.toLowerCase() === `${name}`.toLowerCase() &&
|
|
1804
|
+
(!purlType || c.purl.startsWith(`pkg:${purlType}/`)),
|
|
1805
|
+
);
|
|
1806
|
+
const purlByHeader = new Map();
|
|
1807
|
+
for (const [header, entry] of includes) {
|
|
1808
|
+
const target = attribute(entry.paths);
|
|
1809
|
+
let component;
|
|
1810
|
+
if (target?.kind === "component") {
|
|
1811
|
+
component = target.component;
|
|
1812
|
+
} else if (target?.kind === "os") {
|
|
1813
|
+
component =
|
|
1814
|
+
withPurl.find((c) => c.purl === target.pkgInfo.purl) ||
|
|
1815
|
+
byName(target.pkgInfo.name);
|
|
1816
|
+
} else if (target?.kind === "vcpkg") {
|
|
1817
|
+
component = byName(target.port);
|
|
1818
|
+
} else if (target?.kind === "conan") {
|
|
1819
|
+
component = byName(target.name, "conan") || byName(target.name);
|
|
1820
|
+
}
|
|
1821
|
+
if (!component?.purl) {
|
|
1822
|
+
continue;
|
|
1823
|
+
}
|
|
1824
|
+
purlByHeader.set(header, component.purl);
|
|
1825
|
+
const known = symbols.get(component.purl) || new Set();
|
|
1826
|
+
for (const symbol of entry.symbols) {
|
|
1827
|
+
known.add(symbol);
|
|
1828
|
+
}
|
|
1829
|
+
symbols.set(component.purl, known);
|
|
1830
|
+
}
|
|
1831
|
+
// the directives that include an attributed header and use it
|
|
1832
|
+
for (const slice of usageSlice?.objectSlices || []) {
|
|
1833
|
+
const purl = purlByHeader.get(slice?.fullName);
|
|
1834
|
+
if (
|
|
1835
|
+
!purl ||
|
|
1836
|
+
!`${slice.code || ""}`.startsWith("#include") ||
|
|
1837
|
+
!slice.fileName ||
|
|
1838
|
+
!slice.importedSymbols?.length
|
|
1839
|
+
) {
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
const known = locations.get(purl) || new Set();
|
|
1843
|
+
known.add(
|
|
1844
|
+
slice.lineNumber
|
|
1845
|
+
? `${slice.fileName}#${slice.lineNumber}`
|
|
1846
|
+
: slice.fileName,
|
|
1847
|
+
);
|
|
1848
|
+
locations.set(purl, known);
|
|
1849
|
+
}
|
|
1850
|
+
return { symbols, locations };
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
/**
|
|
1854
|
+
* Seed the C/C++ imports from the include slices of a usages report (see
|
|
1855
|
+
* `attributeCIncludes`).
|
|
1856
|
+
*
|
|
1857
|
+
* @param {Object} usageSlice Parsed usages report
|
|
1858
|
+
* @param {Object[]} components Components of the input BOM
|
|
1859
|
+
* @param {string} dirPath Source directory
|
|
1860
|
+
* @param {Object} purlImportsMap Imports by purl, extended in place
|
|
1861
|
+
* @param {Object} options CLI options
|
|
1862
|
+
* @returns {Object} purlImportsMap
|
|
1863
|
+
*/
|
|
1864
|
+
export function seedCImportsFromIncludes(
|
|
1865
|
+
usageSlice,
|
|
1866
|
+
components,
|
|
1867
|
+
dirPath,
|
|
1868
|
+
purlImportsMap = {},
|
|
1869
|
+
options = {},
|
|
1870
|
+
) {
|
|
1871
|
+
const { symbols } = attributeCIncludes(
|
|
1872
|
+
usageSlice,
|
|
1873
|
+
components,
|
|
1874
|
+
dirPath,
|
|
1875
|
+
options,
|
|
1876
|
+
);
|
|
1877
|
+
for (const [purl, names] of symbols) {
|
|
1878
|
+
purlImportsMap[purl] = Array.from(
|
|
1879
|
+
new Set([...(purlImportsMap[purl] || []), ...names]),
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1882
|
+
return purlImportsMap;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1264
1885
|
export async function parseObjectSlices(
|
|
1265
1886
|
language,
|
|
1266
1887
|
usageSlice,
|
|
@@ -1581,11 +2202,7 @@ export async function parseSliceUsages(
|
|
|
1581
2202
|
* @param {Object} semanticsSlice Semantic slice data
|
|
1582
2203
|
* @returns {Object} Parsed metadata
|
|
1583
2204
|
*/
|
|
1584
|
-
export function parseSemanticSlices(
|
|
1585
|
-
// For scala, use the dedicated scalasem module.
|
|
1586
|
-
if (language === "scala") {
|
|
1587
|
-
return findPurlLocations(components, semanticsSlice);
|
|
1588
|
-
}
|
|
2205
|
+
export function parseSemanticSlices(_language, components, semanticsSlice) {
|
|
1589
2206
|
if (!components) {
|
|
1590
2207
|
return undefined;
|
|
1591
2208
|
}
|