@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,426 @@
|
|
|
1
|
+
import {
|
|
2
|
+
closeSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
openSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
readSync,
|
|
7
|
+
realpathSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { basename, join, relative, sep } from "node:path";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
androidNetworkSecurityConfigPins,
|
|
13
|
+
CertificateCollector,
|
|
14
|
+
certRecordFromDer,
|
|
15
|
+
classifyCryptoFile,
|
|
16
|
+
kubernetesTlsSecretRecords,
|
|
17
|
+
linkFallbackIssuers,
|
|
18
|
+
} from "./certificates.js";
|
|
19
|
+
|
|
20
|
+
// Bounds mirror trustinspector's rootfs mode (PLAN section 4.1): 16 MiB per
|
|
21
|
+
// file, 20,000 files read and 500,000 entries visited per tree, depth 12.
|
|
22
|
+
export const ROOTFS_CERT_LIMITS = Object.freeze({
|
|
23
|
+
maxFileBytes: 16 * 1024 * 1024,
|
|
24
|
+
maxFiles: 20000,
|
|
25
|
+
maxEntries: 500000,
|
|
26
|
+
maxDepth: 12,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Coverage table: the same locations and sourceTypes as trustinspector's
|
|
30
|
+
// rootfs mode (thirdparty/trustinspector/rootfs.go candidateDirs).
|
|
31
|
+
const CA_STORE_DIRS = [
|
|
32
|
+
"etc/ssl/certs",
|
|
33
|
+
"usr/local/share/ca-certificates",
|
|
34
|
+
"usr/share/ca-certificates",
|
|
35
|
+
"etc/pki/tls/certs",
|
|
36
|
+
"etc/pki/ca-trust/source/anchors",
|
|
37
|
+
"etc/pki/ca-trust/extracted",
|
|
38
|
+
"var/lib/ca-certificates",
|
|
39
|
+
"usr/share/pki/ca-trust-source/anchors",
|
|
40
|
+
];
|
|
41
|
+
const CA_STORE_FILES = ["etc/ssl/cert.pem"];
|
|
42
|
+
const CA_BLOCKLIST_DIRS = [
|
|
43
|
+
"etc/pki/ca-trust/source/blocklist",
|
|
44
|
+
"etc/pki/ca-trust/source/blacklist",
|
|
45
|
+
"usr/share/pki/ca-trust-source/blocklist",
|
|
46
|
+
"usr/share/pki/ca-trust-source/blacklist",
|
|
47
|
+
];
|
|
48
|
+
const SERVER_CONFIG_DIRS = [
|
|
49
|
+
"etc/nginx",
|
|
50
|
+
"etc/apache2",
|
|
51
|
+
"etc/httpd",
|
|
52
|
+
"etc/haproxy",
|
|
53
|
+
"etc/letsencrypt/live",
|
|
54
|
+
"etc/letsencrypt/archive",
|
|
55
|
+
"etc/kubernetes/pki",
|
|
56
|
+
"var/lib/kubelet/pki",
|
|
57
|
+
"etc/docker/certs.d",
|
|
58
|
+
"etc/containerd/certs.d",
|
|
59
|
+
"etc/envoy",
|
|
60
|
+
"etc/traefik",
|
|
61
|
+
];
|
|
62
|
+
const JAVA_DIRS = [
|
|
63
|
+
"usr/lib/jvm",
|
|
64
|
+
"opt",
|
|
65
|
+
"etc/ssl/certs/java",
|
|
66
|
+
"etc/pki/java",
|
|
67
|
+
"usr/share/elasticsearch",
|
|
68
|
+
];
|
|
69
|
+
const LANGUAGE_BUNDLE_DIRS = ["usr/lib", "usr/local/lib", "opt"];
|
|
70
|
+
// Kubernetes secrets and Android configs are cdxgen-side producers: the
|
|
71
|
+
// plugin has no YAML or Android parser.
|
|
72
|
+
const SECRET_YAML_DIRS = [
|
|
73
|
+
"etc/kubernetes",
|
|
74
|
+
"var/run/secrets",
|
|
75
|
+
"run/secrets",
|
|
76
|
+
"var/lib/kubelet",
|
|
77
|
+
"srv",
|
|
78
|
+
"app",
|
|
79
|
+
"opt",
|
|
80
|
+
"home",
|
|
81
|
+
"root",
|
|
82
|
+
];
|
|
83
|
+
const ANDROID_DIRS = ["app", "opt", "srv", "data", "home", "root"];
|
|
84
|
+
|
|
85
|
+
const CERT_SUFFIXES = [
|
|
86
|
+
".pem",
|
|
87
|
+
".crt",
|
|
88
|
+
".cer",
|
|
89
|
+
".der",
|
|
90
|
+
".p7b",
|
|
91
|
+
".p7c",
|
|
92
|
+
".p12",
|
|
93
|
+
".pfx",
|
|
94
|
+
".jks",
|
|
95
|
+
".jceks",
|
|
96
|
+
".truststore",
|
|
97
|
+
".key",
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
function isJavaKeystoreName(base) {
|
|
101
|
+
if (base === "cacerts" || base === "jssecacerts") {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return [".jks", ".jceks", ".truststore", ".p12", ".pfx"].some((suffix) =>
|
|
105
|
+
base.endsWith(suffix),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Mirrors looksLikeTrustMaterialPath in trustinspector: private-key
|
|
110
|
+
// directories are never read.
|
|
111
|
+
function looksLikeTrustMaterialPath(rel, sourceType) {
|
|
112
|
+
const lower = rel.toLowerCase();
|
|
113
|
+
if (
|
|
114
|
+
lower.startsWith("etc/ssl/private/") ||
|
|
115
|
+
lower.startsWith("etc/pki/tls/private/")
|
|
116
|
+
) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
const base = basename(lower);
|
|
120
|
+
if (sourceType === "language-bundle") {
|
|
121
|
+
return base === "cacert.pem";
|
|
122
|
+
}
|
|
123
|
+
if (sourceType === "java-truststore") {
|
|
124
|
+
return isJavaKeystoreName(base);
|
|
125
|
+
}
|
|
126
|
+
if (isJavaKeystoreName(base)) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return (
|
|
130
|
+
CERT_SUFFIXES.some((suffix) => lower.endsWith(suffix)) ||
|
|
131
|
+
lower.includes("/certs/")
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Read at most `maxBytes` of a regular file. The cap applies to the read
|
|
137
|
+
* itself, so a file that grows after the size check cannot exceed it. Files
|
|
138
|
+
* that are not regular (FIFOs, devices, sockets) are never opened.
|
|
139
|
+
*/
|
|
140
|
+
function readCapped(absolute, maxBytes) {
|
|
141
|
+
const before = lstatSync(absolute);
|
|
142
|
+
if (!before.isFile() || before.size > maxBytes) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
const fd = openSync(absolute, "r");
|
|
146
|
+
try {
|
|
147
|
+
const buffer = Buffer.alloc(Math.min(before.size, maxBytes) + 1);
|
|
148
|
+
let total = 0;
|
|
149
|
+
while (total < buffer.length) {
|
|
150
|
+
const n = readSync(fd, buffer, total, buffer.length - total, null);
|
|
151
|
+
if (n === 0) {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
total += n;
|
|
155
|
+
}
|
|
156
|
+
if (total > maxBytes) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
return buffer.subarray(0, total);
|
|
160
|
+
} finally {
|
|
161
|
+
closeSync(fd);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Pure-JS inventory of the certificate locations trustinspector's rootfs
|
|
167
|
+
* mode covers, plus Kubernetes TLS secrets and Android pins. Used when the
|
|
168
|
+
* plugin is not available. Produces components through the shared
|
|
169
|
+
* CertificateCollector, so bom-refs, names and deduplication match the other
|
|
170
|
+
* producers; locations are root-relative ("/etc/ssl/certs/x.crt").
|
|
171
|
+
*
|
|
172
|
+
* Every access stays inside the root: symlinks (files or directories) are
|
|
173
|
+
* skipped, only regular files are opened, and the walk is bounded by entry,
|
|
174
|
+
* file, size and depth limits.
|
|
175
|
+
*
|
|
176
|
+
* @param {string} rootDir extracted root filesystem
|
|
177
|
+
* @param {object} [limits] overrides for ROOTFS_CERT_LIMITS (tests)
|
|
178
|
+
* @returns {{components: object[], dependencies: object[], warnings: string[]}}
|
|
179
|
+
*/
|
|
180
|
+
export function collectRootfsCertificates(rootDir, limits = {}) {
|
|
181
|
+
const bounds = { ...ROOTFS_CERT_LIMITS, ...limits };
|
|
182
|
+
const warnings = [];
|
|
183
|
+
let realRoot;
|
|
184
|
+
try {
|
|
185
|
+
realRoot = realpathSync(rootDir);
|
|
186
|
+
} catch {
|
|
187
|
+
return { components: [], dependencies: [], warnings };
|
|
188
|
+
}
|
|
189
|
+
const collector = new CertificateCollector();
|
|
190
|
+
const records = [];
|
|
191
|
+
const components = [];
|
|
192
|
+
const seenFiles = new Set();
|
|
193
|
+
let filesRead = 0;
|
|
194
|
+
|
|
195
|
+
// Resolve a root-relative directory to an absolute path that is proven to
|
|
196
|
+
// stay inside the root; undefined when it does not exist, is not a
|
|
197
|
+
// directory, or any symlink on the way leaves the root.
|
|
198
|
+
const resolveInside = (rel) => {
|
|
199
|
+
const absolute = join(realRoot, rel);
|
|
200
|
+
let real;
|
|
201
|
+
try {
|
|
202
|
+
real = realpathSync(absolute);
|
|
203
|
+
} catch {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const inside = relative(realRoot, real);
|
|
207
|
+
if (inside.startsWith("..") || inside.split(sep).includes("..")) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return real;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const walk = (relBase, visit) => {
|
|
214
|
+
const start = resolveInside(relBase);
|
|
215
|
+
if (!start) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
let startStat;
|
|
219
|
+
try {
|
|
220
|
+
startStat = lstatSync(start);
|
|
221
|
+
} catch {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (!startStat.isDirectory()) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
let entries = 0;
|
|
228
|
+
const stack = [{ abs: start, rel: relBase, depth: 0 }];
|
|
229
|
+
while (stack.length) {
|
|
230
|
+
const { abs, rel, depth } = stack.pop();
|
|
231
|
+
let dirents;
|
|
232
|
+
try {
|
|
233
|
+
dirents = readdirSync(abs, { withFileTypes: true });
|
|
234
|
+
} catch {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
dirents.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
238
|
+
for (const dirent of dirents) {
|
|
239
|
+
entries += 1;
|
|
240
|
+
if (entries > bounds.maxEntries) {
|
|
241
|
+
warnings.push(
|
|
242
|
+
`entry limit (${bounds.maxEntries}) reached in /${relBase}`,
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
// Symlinks and special files are skipped outright.
|
|
247
|
+
if (dirent.isSymbolicLink()) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const childRel = `${rel}/${dirent.name}`;
|
|
251
|
+
if (dirent.isDirectory()) {
|
|
252
|
+
if (depth + 1 >= bounds.maxDepth) {
|
|
253
|
+
warnings.push(
|
|
254
|
+
`depth limit (${bounds.maxDepth}) exceeded in /${relBase}`,
|
|
255
|
+
);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
stack.push({
|
|
259
|
+
abs: join(abs, dirent.name),
|
|
260
|
+
rel: childRel,
|
|
261
|
+
depth: depth + 1,
|
|
262
|
+
});
|
|
263
|
+
} else if (dirent.isFile()) {
|
|
264
|
+
if (visit(childRel, join(abs, dirent.name)) === false) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const readRel = (rel, abs) => {
|
|
273
|
+
if (filesRead >= bounds.maxFiles) {
|
|
274
|
+
warnings.push(`file count limit (${bounds.maxFiles}) reached`);
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
filesRead += 1;
|
|
278
|
+
try {
|
|
279
|
+
const bytes = readCapped(abs, bounds.maxFileBytes);
|
|
280
|
+
if (!bytes) {
|
|
281
|
+
warnings.push(
|
|
282
|
+
`file over ${bounds.maxFileBytes} bytes skipped: /${rel}`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
return bytes;
|
|
286
|
+
} catch {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const consumeMaterial = (rel, abs, sourceType) => {
|
|
292
|
+
if (seenFiles.has(rel) || !looksLikeTrustMaterialPath(rel, sourceType)) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const bytes = readRel(rel, abs);
|
|
296
|
+
if (!bytes?.length) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
seenFiles.add(rel);
|
|
300
|
+
const location = `/${rel}`;
|
|
301
|
+
const classification = classifyCryptoFile(bytes);
|
|
302
|
+
if (classification.kind === "certificates") {
|
|
303
|
+
const dot = basename(rel).lastIndexOf(".");
|
|
304
|
+
for (const der of classification.ders) {
|
|
305
|
+
const record = certRecordFromDer(der, { location, sourceType });
|
|
306
|
+
if (record) {
|
|
307
|
+
record.fileExtension =
|
|
308
|
+
dot > 0 ? basename(rel).slice(dot + 1) : undefined;
|
|
309
|
+
records.push(record);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} else if (classification.kind === "private-key") {
|
|
313
|
+
components.push(
|
|
314
|
+
rootfsMaterialComponent("private-key", location, {
|
|
315
|
+
format: classification.format,
|
|
316
|
+
sourceType,
|
|
317
|
+
}),
|
|
318
|
+
);
|
|
319
|
+
} else if (classification.kind === "keystore") {
|
|
320
|
+
components.push(
|
|
321
|
+
rootfsMaterialComponent("keystore", location, {
|
|
322
|
+
format: classification.format,
|
|
323
|
+
sourceType,
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Most specific location first, so a file under two candidates takes the
|
|
330
|
+
// sourceType of the narrower one (same rule as the plugin).
|
|
331
|
+
const candidates = [
|
|
332
|
+
...CA_STORE_DIRS.map((rel) => ({ rel, sourceType: "ca-store" })),
|
|
333
|
+
...CA_BLOCKLIST_DIRS.map((rel) => ({ rel, sourceType: "ca-blocklist" })),
|
|
334
|
+
...SERVER_CONFIG_DIRS.map((rel) => ({ rel, sourceType: "server-config" })),
|
|
335
|
+
...JAVA_DIRS.map((rel) => ({ rel, sourceType: "java-truststore" })),
|
|
336
|
+
...LANGUAGE_BUNDLE_DIRS.map((rel) => ({
|
|
337
|
+
rel,
|
|
338
|
+
sourceType: "language-bundle",
|
|
339
|
+
})),
|
|
340
|
+
].sort((a, b) => b.rel.split("/").length - a.rel.split("/").length);
|
|
341
|
+
for (const file of CA_STORE_FILES) {
|
|
342
|
+
const abs = resolveInside(file);
|
|
343
|
+
if (abs && lstatSync(abs).isFile()) {
|
|
344
|
+
consumeMaterial(file, abs, "ca-store");
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
for (const { rel, sourceType } of candidates) {
|
|
348
|
+
walk(rel, (childRel, abs) => {
|
|
349
|
+
consumeMaterial(childRel, abs, sourceType);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const secretFiles = new Set();
|
|
354
|
+
for (const dir of SECRET_YAML_DIRS) {
|
|
355
|
+
walk(dir, (childRel, abs) => {
|
|
356
|
+
if (/\.ya?ml$/i.test(childRel) && !secretFiles.has(childRel)) {
|
|
357
|
+
secretFiles.add(childRel);
|
|
358
|
+
const bytes = readRel(childRel, abs);
|
|
359
|
+
const text = bytes?.toString("utf-8");
|
|
360
|
+
// Cheap pre-filter, as in the source scan.
|
|
361
|
+
if (text?.includes("kubernetes.io/tls")) {
|
|
362
|
+
records.push(
|
|
363
|
+
...kubernetesTlsSecretRecords(text, { file: `/${childRel}` }),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
const androidFiles = new Set();
|
|
370
|
+
for (const dir of ANDROID_DIRS) {
|
|
371
|
+
walk(dir, (childRel, abs) => {
|
|
372
|
+
if (
|
|
373
|
+
/\/res\/xml\/[^/]+\.xml$/i.test(childRel) &&
|
|
374
|
+
!androidFiles.has(childRel)
|
|
375
|
+
) {
|
|
376
|
+
androidFiles.add(childRel);
|
|
377
|
+
const text = readRel(childRel, abs)?.toString("utf-8");
|
|
378
|
+
if (text?.includes("<network-security-config")) {
|
|
379
|
+
for (const pin of androidNetworkSecurityConfigPins(text, {
|
|
380
|
+
file: `/${childRel}`,
|
|
381
|
+
})) {
|
|
382
|
+
collector.addPin(pin);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (records.length) {
|
|
390
|
+
linkFallbackIssuers(records);
|
|
391
|
+
collector.addRecords(records);
|
|
392
|
+
}
|
|
393
|
+
const built = collector.build();
|
|
394
|
+
return {
|
|
395
|
+
components: [...built.components, ...components],
|
|
396
|
+
dependencies: built.dependencies,
|
|
397
|
+
warnings,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Same shape as the keystore and private-key components the other producers
|
|
402
|
+
// emit (managedBom sourceKeystoreComponent / sourcePrivateKeyComponent).
|
|
403
|
+
function rootfsMaterialComponent(kind, location, { format, sourceType }) {
|
|
404
|
+
return {
|
|
405
|
+
name: basename(location),
|
|
406
|
+
type: "cryptographic-asset",
|
|
407
|
+
"bom-ref": `crypto/related-crypto-material/${kind}/${encodeURIComponent(location)}`,
|
|
408
|
+
cryptoProperties: {
|
|
409
|
+
assetType: "related-crypto-material",
|
|
410
|
+
relatedCryptoMaterialProperties: {
|
|
411
|
+
type: kind === "keystore" ? "other" : "private-key",
|
|
412
|
+
...(format ? { format } : {}),
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
evidence: { occurrences: [{ location }] },
|
|
416
|
+
properties: [
|
|
417
|
+
{ name: "internal:SrcFile", value: location },
|
|
418
|
+
...(sourceType
|
|
419
|
+
? [{ name: "cdx:crypto:sourceType", value: sourceType }]
|
|
420
|
+
: []),
|
|
421
|
+
...(kind === "keystore"
|
|
422
|
+
? [{ name: "cdx:crypto:keystoreReadable", value: "false" }]
|
|
423
|
+
: []),
|
|
424
|
+
],
|
|
425
|
+
};
|
|
426
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, join } from "node:path";
|
|
3
3
|
|
|
4
|
-
import * as toml from "smol-toml";
|
|
5
|
-
|
|
6
4
|
import { safeExistsSync } from "../core/fs.js";
|
|
5
|
+
import { parseToml } from "../core/toml.js";
|
|
7
6
|
|
|
8
7
|
// Keep this list conservative and high-signal: it is meant to surface common
|
|
9
8
|
// Rust native build helpers for audit triage, not to exhaustively model every
|
|
@@ -67,7 +66,7 @@ function createFormulationComponent(name, filePath, toolName) {
|
|
|
67
66
|
function parseCargoManifest(filePath) {
|
|
68
67
|
let cargoData;
|
|
69
68
|
try {
|
|
70
|
-
cargoData =
|
|
69
|
+
cargoData = parseToml(readFileSync(filePath, { encoding: "utf-8" }));
|
|
71
70
|
} catch {
|
|
72
71
|
return undefined;
|
|
73
72
|
}
|
|
@@ -252,7 +251,7 @@ function parseCargoManifest(filePath) {
|
|
|
252
251
|
function parsePyProject(filePath) {
|
|
253
252
|
let pyprojectData;
|
|
254
253
|
try {
|
|
255
|
-
pyprojectData =
|
|
254
|
+
pyprojectData = parseToml(readFileSync(filePath, { encoding: "utf-8" }));
|
|
256
255
|
} catch {
|
|
257
256
|
return undefined;
|
|
258
257
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Purl } from "@cdxgen/cdx-purl";
|
|
2
|
+
|
|
3
|
+
// Scala libraries publish their Maven artifacts with a trailing Scala binary
|
|
4
|
+
// version suffix (`jwt-core_3`, `coursier_2.13`), optionally preceded by a
|
|
5
|
+
// platform suffix for Scala.js and Scala Native builds (`upickle_sjs1_3`,
|
|
6
|
+
// `upickle_native0.5_3`). sbt components strip the binary suffix and keep the
|
|
7
|
+
// platform suffix (`jwt-core`, `upickle_sjs1`), so the same library is named
|
|
8
|
+
// differently depending on the build tool that reported it.
|
|
9
|
+
const SCALA_BINARY_SUFFIX_REGEX = /_(2\.\d+|3)$/;
|
|
10
|
+
const SCALA_PLATFORM_SUFFIX_REGEX = /_(sjs1|sjs0\.6|native0\.\d+)$/;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse a Scala-published Maven artifact name into its coordinate parts.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} name Maven artifactId, for example `upickle_sjs1_3`
|
|
16
|
+
* @returns {{ originalName: string, artifactBase: string, binaryVersion: string|null,
|
|
17
|
+
* platformSuffix: string, purlName: string }} The artifact name without any
|
|
18
|
+
* Scala suffix (`upickle`), the Scala binary version from the suffix (`3`),
|
|
19
|
+
* the platform suffix including the underscore (`_sjs1`) or an empty string,
|
|
20
|
+
* and the artifact name sbt-style purls emit (binary suffix dropped, platform
|
|
21
|
+
* suffix kept).
|
|
22
|
+
*/
|
|
23
|
+
export function parseScalaArtifact(name) {
|
|
24
|
+
const result = {
|
|
25
|
+
originalName: name,
|
|
26
|
+
artifactBase: name,
|
|
27
|
+
binaryVersion: null,
|
|
28
|
+
platformSuffix: "",
|
|
29
|
+
purlName: name,
|
|
30
|
+
};
|
|
31
|
+
if (!name) {
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
let rest = name;
|
|
35
|
+
const binaryMatch = rest.match(SCALA_BINARY_SUFFIX_REGEX);
|
|
36
|
+
if (binaryMatch) {
|
|
37
|
+
result.binaryVersion = binaryMatch[1];
|
|
38
|
+
rest = rest.slice(0, rest.length - binaryMatch[0].length);
|
|
39
|
+
}
|
|
40
|
+
const platformMatch = rest.match(SCALA_PLATFORM_SUFFIX_REGEX);
|
|
41
|
+
if (platformMatch) {
|
|
42
|
+
result.platformSuffix = platformMatch[0];
|
|
43
|
+
rest = rest.slice(0, rest.length - platformMatch[0].length);
|
|
44
|
+
}
|
|
45
|
+
result.artifactBase = rest;
|
|
46
|
+
result.purlName = binaryMatch ? `${rest}${result.platformSuffix}` : name;
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build a qualifier-free coordinate key for a Scala-published artifact.
|
|
52
|
+
*
|
|
53
|
+
* The key ignores purl qualifiers such as `repository_url` and `type`, so the
|
|
54
|
+
* POM-coordinate keys of the jar namespace mapping and the sbt-style purls of
|
|
55
|
+
* the components resolve to the same entry.
|
|
56
|
+
*
|
|
57
|
+
* @param {Object} parts Coordinate parts
|
|
58
|
+
* @param {string} [parts.type] Purl type, defaults to `maven`
|
|
59
|
+
* @param {string} [parts.group] Maven groupId
|
|
60
|
+
* @param {string} parts.artifactBase Artifact name without any Scala suffix
|
|
61
|
+
* @param {string} [parts.platformSuffix] `_sjs1`, `_native0.5` or empty
|
|
62
|
+
* @param {string} [parts.version] Package version
|
|
63
|
+
* @returns {string} The coordinate key
|
|
64
|
+
*/
|
|
65
|
+
export function scalaCoordinateKey({
|
|
66
|
+
type = "maven",
|
|
67
|
+
group = "",
|
|
68
|
+
artifactBase = "",
|
|
69
|
+
platformSuffix = "",
|
|
70
|
+
version = "",
|
|
71
|
+
} = {}) {
|
|
72
|
+
return [type, group, artifactBase, platformSuffix, version].join(":");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build the qualifier-free coordinate key of a purl string.
|
|
77
|
+
*
|
|
78
|
+
* The artifact name is normalized with {@link parseScalaArtifact}, so
|
|
79
|
+
* `pkg:maven/g/foo_3@1.0` and the sbt-style `pkg:maven/g/foo@1.0` share a key,
|
|
80
|
+
* while the Scala.js build `foo_sjs1_3` keeps its own key (`foo` plus the
|
|
81
|
+
* `_sjs1` platform suffix) and never joins the plain `foo` component.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} purlString Package URL to normalize
|
|
84
|
+
* @returns {string|undefined} The coordinate key, or undefined when the string
|
|
85
|
+
* is not a parseable purl
|
|
86
|
+
*/
|
|
87
|
+
export function coordinateKeyFromPurl(purlString) {
|
|
88
|
+
return scalaCoordinateOfPurl(purlString)?.key;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The coordinate key of a purl together with the Scala binary version its
|
|
93
|
+
* artifact name carries, if any.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} purlString Package URL to normalize
|
|
96
|
+
* @returns {{ key: string, binaryVersion: string|null }|undefined} The key
|
|
97
|
+
* and binary version, or undefined when the string is not a parseable purl
|
|
98
|
+
*/
|
|
99
|
+
export function scalaCoordinateOfPurl(purlString) {
|
|
100
|
+
if (!purlString?.startsWith("pkg:")) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
let purlObj;
|
|
104
|
+
try {
|
|
105
|
+
purlObj = Purl.parse(purlString);
|
|
106
|
+
} catch (_err) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
if (!purlObj?.name) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const scalaInfo = parseScalaArtifact(purlObj.name);
|
|
113
|
+
return {
|
|
114
|
+
key: scalaCoordinateKey({
|
|
115
|
+
type: purlObj.type,
|
|
116
|
+
group: purlObj.namespace || "",
|
|
117
|
+
artifactBase: scalaInfo.artifactBase,
|
|
118
|
+
platformSuffix: scalaInfo.platformSuffix,
|
|
119
|
+
version: purlObj.version || "",
|
|
120
|
+
}),
|
|
121
|
+
binaryVersion: scalaInfo.binaryVersion,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// An artifact name that already ends in a full Scala version, as scala-cli
|
|
126
|
+
// `:::` and sbt `CrossVersion.full` artifacts do (`_3.3.7`, `_2.13.16`). A
|
|
127
|
+
// binary suffix such as `_3` left on a name is part of the name: the
|
|
128
|
+
// component producers strip exactly one, into the compiler version.
|
|
129
|
+
const SCALA_FULL_VERSION_SUFFIX_REGEX = /_\d+\.\d+\.\d+(?:-[A-Za-z0-9.]+)?$/;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The artifactId the library is published under, for cache and repository
|
|
133
|
+
* lookups only. Components a Scala build tool reports carry the name with the
|
|
134
|
+
* Scala binary suffix stripped (the platform suffix kept) together with
|
|
135
|
+
* `cdx:scala:compilerVersion`; on disk and in the repository the artifact is
|
|
136
|
+
* `cats-core_2.13` or `upickle_sjs1_3`. Names that end in a full Scala version
|
|
137
|
+
* are used as they are.
|
|
138
|
+
*
|
|
139
|
+
* The component's own `name`, `purl` and `bom-ref` never change.
|
|
140
|
+
*
|
|
141
|
+
* @param {Object} pkg Package with `name` and `properties`.
|
|
142
|
+
* @returns {string} The artifactId to look up.
|
|
143
|
+
*/
|
|
144
|
+
export function publishedArtifactId(pkg) {
|
|
145
|
+
const name = pkg?.name;
|
|
146
|
+
if (!name || SCALA_FULL_VERSION_SUFFIX_REGEX.test(name)) {
|
|
147
|
+
return name;
|
|
148
|
+
}
|
|
149
|
+
const binaryVersion = (pkg.properties || []).find(
|
|
150
|
+
(property) => property.name === "cdx:scala:compilerVersion",
|
|
151
|
+
)?.value;
|
|
152
|
+
return binaryVersion ? `${name}_${binaryVersion}` : name;
|
|
153
|
+
}
|