@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/bin/sign.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
getNonCycloneDxErrorMessage,
|
|
21
21
|
isCycloneDxBom,
|
|
22
22
|
} from "../lib/inventory/bomUtils.js";
|
|
23
|
+
import { parseStrictJson } from "../lib/parsers/strictJson.js";
|
|
23
24
|
|
|
24
25
|
const _yargs = yargs(hideBin(process.argv));
|
|
25
26
|
|
|
@@ -55,13 +56,19 @@ const args = _yargs
|
|
|
55
56
|
type: "array",
|
|
56
57
|
string: true,
|
|
57
58
|
description:
|
|
58
|
-
"Public key (PEM) of an earlier signer.
|
|
59
|
+
"Public key (PEM) of an earlier signer. Before a new entry is appended, every existing chain entry (--mode chain), or at least one existing entry (--mode signers), must verify with one of these keys.",
|
|
59
60
|
})
|
|
60
61
|
.option("allow-unverified-history", {
|
|
61
62
|
type: "boolean",
|
|
62
63
|
default: false,
|
|
63
64
|
description:
|
|
64
|
-
"With --mode chain, append even when existing
|
|
65
|
+
"With --mode chain or signers, append even when the existing signature entries cannot be verified with --verify-existing-with.",
|
|
66
|
+
})
|
|
67
|
+
.option("embed-public-key", {
|
|
68
|
+
type: "boolean",
|
|
69
|
+
default: true,
|
|
70
|
+
description:
|
|
71
|
+
"Embed the signer's public key in the root signature, which binds the signature to that key. Not used for HMAC (HS*) algorithms. Pass --no-embed-public-key to leave it out.",
|
|
65
72
|
})
|
|
66
73
|
.option("key-id", {
|
|
67
74
|
description:
|
|
@@ -173,16 +180,19 @@ function describeEntry(result) {
|
|
|
173
180
|
: `entry ${result.index}`;
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
// A new chain entry covers every earlier entry, so the signer vouches for
|
|
177
|
-
//
|
|
178
|
-
|
|
183
|
+
// A new chain entry covers every earlier entry, so the signer vouches for that
|
|
184
|
+
// history, and every entry must verify. A new signers entry covers the same
|
|
185
|
+
// content as the existing entries, so at least one must verify to show the
|
|
186
|
+
// content is still what an earlier signer signed.
|
|
187
|
+
function checkExistingSignatures(bomJson) {
|
|
179
188
|
const existing = bomJson.signature;
|
|
189
|
+
const otherForm = args.mode === "chain" ? "signers" : "chain";
|
|
180
190
|
if (
|
|
181
|
-
args.mode
|
|
191
|
+
args.mode === "replace" ||
|
|
182
192
|
existing === undefined ||
|
|
183
193
|
(existing !== null &&
|
|
184
194
|
typeof existing === "object" &&
|
|
185
|
-
Object.hasOwn(existing,
|
|
195
|
+
Object.hasOwn(existing, otherForm))
|
|
186
196
|
) {
|
|
187
197
|
return;
|
|
188
198
|
}
|
|
@@ -192,10 +202,10 @@ function checkChainHistory(bomJson) {
|
|
|
192
202
|
}
|
|
193
203
|
return fs.readFileSync(keyFile, "utf8");
|
|
194
204
|
});
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (!unverified.length) {
|
|
205
|
+
const results = checkSignatureEntries(bomJson, keys);
|
|
206
|
+
const unverified = results.filter((result) => !result.verified);
|
|
207
|
+
const chain = args.mode === "chain";
|
|
208
|
+
if (chain ? !unverified.length : unverified.length < results.length) {
|
|
199
209
|
return;
|
|
200
210
|
}
|
|
201
211
|
const details = unverified.map(
|
|
@@ -205,7 +215,9 @@ function checkChainHistory(bomJson) {
|
|
|
205
215
|
if (args.allowUnverifiedHistory) {
|
|
206
216
|
console.warn(
|
|
207
217
|
[
|
|
208
|
-
|
|
218
|
+
chain
|
|
219
|
+
? `Warning: appending to a chain with ${unverified.length} unverified entry(s):`
|
|
220
|
+
: `Warning: appending a signers entry although none of the ${unverified.length} existing entry(s) verified:`,
|
|
209
221
|
...details,
|
|
210
222
|
].join("\n"),
|
|
211
223
|
);
|
|
@@ -213,7 +225,9 @@ function checkChainHistory(bomJson) {
|
|
|
213
225
|
}
|
|
214
226
|
throw new Error(
|
|
215
227
|
[
|
|
216
|
-
|
|
228
|
+
chain
|
|
229
|
+
? `the new chain entry would vouch for ${unverified.length} existing entry(s) that could not be verified:`
|
|
230
|
+
: `none of the ${unverified.length} existing signature entry(s) could be verified, so the content may have changed since it was signed:`,
|
|
217
231
|
...details,
|
|
218
232
|
"Pass --verify-existing-with <public key> for each earlier signer, or --allow-unverified-history to sign anyway.",
|
|
219
233
|
].join("\n"),
|
|
@@ -221,7 +235,7 @@ function checkChainHistory(bomJson) {
|
|
|
221
235
|
}
|
|
222
236
|
|
|
223
237
|
try {
|
|
224
|
-
const bomJson =
|
|
238
|
+
const bomJson = parseStrictJson(fs.readFileSync(args.input));
|
|
225
239
|
if (!isCycloneDxBom(bomJson)) {
|
|
226
240
|
console.error(getNonCycloneDxErrorMessage(bomJson, "cdx-sign"));
|
|
227
241
|
process.exit(1);
|
|
@@ -229,10 +243,12 @@ try {
|
|
|
229
243
|
|
|
230
244
|
let signedBom = bomJson;
|
|
231
245
|
if (hasPrivateKey && privateKeyContent) {
|
|
232
|
-
|
|
246
|
+
checkExistingSignatures(bomJson);
|
|
233
247
|
signedBom = signBom(bomJson, {
|
|
234
248
|
privateKey: privateKeyContent,
|
|
235
249
|
algorithm: args.algorithm,
|
|
250
|
+
embedPublicKey: args.embedPublicKey,
|
|
251
|
+
embedPublicKeyNested: false,
|
|
236
252
|
keyId: args.keyId,
|
|
237
253
|
mode: args.mode,
|
|
238
254
|
signComponents: args.signComponents,
|
|
@@ -247,6 +263,20 @@ try {
|
|
|
247
263
|
console.log(
|
|
248
264
|
`Mode: ${args.mode} | Algorithm: ${args.algorithm}${args.keyId ? ` | KeyId: ${args.keyId}` : ""}`,
|
|
249
265
|
);
|
|
266
|
+
const rootSignature = signedBom.signature;
|
|
267
|
+
const newEntry =
|
|
268
|
+
rootSignature?.signers?.at(-1) ??
|
|
269
|
+
rootSignature?.chain?.at(-1) ??
|
|
270
|
+
rootSignature;
|
|
271
|
+
if (
|
|
272
|
+
args.embedPublicKey &&
|
|
273
|
+
!String(args.algorithm).startsWith("HS") &&
|
|
274
|
+
newEntry?.publicKey === undefined
|
|
275
|
+
) {
|
|
276
|
+
console.warn(
|
|
277
|
+
"Warning: this key type has no JWK form, so the public key was not embedded in the signature.",
|
|
278
|
+
);
|
|
279
|
+
}
|
|
250
280
|
} else {
|
|
251
281
|
if (!hasAnySignature(bomJson)) {
|
|
252
282
|
console.error(
|
package/bin/tracebom.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
safeWriteSync,
|
|
15
15
|
} from "../lib/ecosystems/utils.js";
|
|
16
16
|
import { DEFAULT_CDX_SPEC_VERSION } from "../lib/inventory/bomUtils.js";
|
|
17
|
+
import { applySpecVersionCompatibility } from "../lib/stages/postgen/specVersionCompat.js";
|
|
17
18
|
|
|
18
19
|
const _yargs = yargs(hideBin(process.argv));
|
|
19
20
|
|
|
@@ -166,7 +167,7 @@ const args = _yargs
|
|
|
166
167
|
})
|
|
167
168
|
.option("trace-crypto", {
|
|
168
169
|
description:
|
|
169
|
-
"
|
|
170
|
+
"Trace cryptographic libraries and TLS parameters: eBPF on Linux, crypto DLLs on Windows (elevated prompt), and TLS handshakes through --proxy-egress on every platform.",
|
|
170
171
|
default: true,
|
|
171
172
|
type: "boolean",
|
|
172
173
|
})
|
|
@@ -337,6 +338,9 @@ const options = {
|
|
|
337
338
|
console.error("Dynamic SBOM generation failed: no output was produced.");
|
|
338
339
|
process.exit(1);
|
|
339
340
|
}
|
|
341
|
+
// tracebom skips postProcess, so the downgrade to an older --spec-version
|
|
342
|
+
// (for example dropping the 1.7 algorithmFamily of traced crypto) runs here.
|
|
343
|
+
applySpecVersionCompatibility(bomJson, options);
|
|
340
344
|
|
|
341
345
|
const jsonPayload = JSON.stringify(bomJson, null, DEBUG_MODE ? 2 : null);
|
|
342
346
|
|
package/bin/validate.js
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
safeMkdirSync,
|
|
27
27
|
safeWriteSync,
|
|
28
28
|
} from "../lib/ecosystems/utils.js";
|
|
29
|
+
import { displayValue } from "../lib/helpers/bomSigner.js";
|
|
29
30
|
import {
|
|
30
31
|
getNonCycloneDxErrorMessage,
|
|
31
32
|
isCycloneDxBom,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
isProtoBomPath,
|
|
36
37
|
} from "../lib/inventory/protobomLoader.js";
|
|
37
38
|
import { getBomWithOras } from "../lib/managers/oci.js";
|
|
39
|
+
import { parseStrictJson } from "../lib/parsers/strictJson.js";
|
|
38
40
|
import { shouldFail, validateBomAdvanced } from "../lib/validator/index.js";
|
|
39
41
|
import { render as renderReport } from "../lib/validator/reporters/index.js";
|
|
40
42
|
|
|
@@ -126,9 +128,8 @@ const args = _yargs
|
|
|
126
128
|
})
|
|
127
129
|
.option("require-signature", {
|
|
128
130
|
type: "boolean",
|
|
129
|
-
default: false,
|
|
130
131
|
description:
|
|
131
|
-
"Exit non-zero (4) when --public-key or --secret-key is
|
|
132
|
+
"Exit non-zero (4) when signature verification fails. On by default when --public-key or --secret-key is given; pass --no-require-signature to only report the result.",
|
|
132
133
|
})
|
|
133
134
|
.option("strict", {
|
|
134
135
|
type: "boolean",
|
|
@@ -143,7 +144,11 @@ const args = _yargs
|
|
|
143
144
|
.help()
|
|
144
145
|
.wrap(Math.min(120, yargs().terminalWidth())).argv;
|
|
145
146
|
|
|
146
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Loads the BOM. When signatures are verified, the text must also pass
|
|
149
|
+
* parseStrictJson(), and `strictError` explains why it did not.
|
|
150
|
+
*/
|
|
151
|
+
async function loadBom(input, platform, strict, requireSignature) {
|
|
147
152
|
if (safeExistsSync(input)) {
|
|
148
153
|
try {
|
|
149
154
|
if (isProtoBomPath(input)) {
|
|
@@ -151,9 +156,18 @@ async function loadBom(input, platform) {
|
|
|
151
156
|
"cdx-validate",
|
|
152
157
|
"protobuf BOM input",
|
|
153
158
|
);
|
|
154
|
-
return readBinary(input, true);
|
|
159
|
+
return { bomJson: readBinary(input, true) };
|
|
155
160
|
}
|
|
156
|
-
|
|
161
|
+
const bytes = fs.readFileSync(input);
|
|
162
|
+
const bomJson = JSON.parse(bytes.toString("utf8"));
|
|
163
|
+
if (strict) {
|
|
164
|
+
try {
|
|
165
|
+
parseStrictJson(bytes);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
return { bomJson, strictError: err.message };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { bomJson };
|
|
157
171
|
} catch (err) {
|
|
158
172
|
console.error(`Failed to parse ${input}: ${err.message}`);
|
|
159
173
|
process.exit(1);
|
|
@@ -164,8 +178,27 @@ async function loadBom(input, platform) {
|
|
|
164
178
|
input.includes("docker") ||
|
|
165
179
|
input.includes("ghcr")
|
|
166
180
|
) {
|
|
167
|
-
|
|
168
|
-
|
|
181
|
+
let strictError;
|
|
182
|
+
try {
|
|
183
|
+
const bom = await getBomWithOras(
|
|
184
|
+
input,
|
|
185
|
+
platform,
|
|
186
|
+
strict ? { checkBytes: parseStrictJson } : {},
|
|
187
|
+
);
|
|
188
|
+
if (bom) return { bomJson: bom };
|
|
189
|
+
} catch (err) {
|
|
190
|
+
if (requireSignature) {
|
|
191
|
+
console.error(
|
|
192
|
+
`cdx-validate: signature verification failed — the attached BOM cannot be verified: ${err.message}`,
|
|
193
|
+
);
|
|
194
|
+
process.exit(4);
|
|
195
|
+
}
|
|
196
|
+
strictError = err.message;
|
|
197
|
+
}
|
|
198
|
+
if (strictError) {
|
|
199
|
+
const bom = await getBomWithOras(input, platform);
|
|
200
|
+
if (bom) return { bomJson: bom, strictError };
|
|
201
|
+
}
|
|
169
202
|
}
|
|
170
203
|
console.error(`Input '${input}' is not a readable SBOM.`);
|
|
171
204
|
process.exit(1);
|
|
@@ -173,12 +206,21 @@ async function loadBom(input, platform) {
|
|
|
173
206
|
}
|
|
174
207
|
|
|
175
208
|
function loadKeyFile(path, label, encoding) {
|
|
176
|
-
if (
|
|
209
|
+
if (path === undefined) return null;
|
|
210
|
+
if (typeof path !== "string" || !path.trim()) {
|
|
211
|
+
console.error(`cdx-validate: ${label} needs exactly one file path.`);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
177
214
|
if (!safeExistsSync(path)) {
|
|
178
215
|
console.error(`${label} '${path}' not found.`);
|
|
179
216
|
process.exit(1);
|
|
180
217
|
}
|
|
181
|
-
|
|
218
|
+
const content = fs.readFileSync(path, encoding);
|
|
219
|
+
if (!content.toString().trim()) {
|
|
220
|
+
console.error(`${label} '${path}' is empty.`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
return content;
|
|
182
224
|
}
|
|
183
225
|
|
|
184
226
|
function splitCsv(value) {
|
|
@@ -208,14 +250,21 @@ function isLocalProtoBomInput(input) {
|
|
|
208
250
|
return isProtoBomPath(input);
|
|
209
251
|
}
|
|
210
252
|
|
|
211
|
-
const
|
|
212
|
-
|
|
253
|
+
const keyGiven = args.publicKey !== undefined || args.secretKey !== undefined;
|
|
254
|
+
const requireSignature = args.requireSignature ?? keyGiven;
|
|
255
|
+
const { bomJson, strictError } = await loadBom(
|
|
256
|
+
args.input,
|
|
257
|
+
args.platform,
|
|
258
|
+
keyGiven,
|
|
259
|
+
requireSignature,
|
|
260
|
+
);
|
|
261
|
+
if (args.publicKey !== undefined && args.secretKey !== undefined) {
|
|
213
262
|
console.error("cdx-validate: use either --public-key or --secret-key.");
|
|
214
263
|
process.exit(1);
|
|
215
264
|
}
|
|
216
265
|
const publicKeyStr = loadKeyFile(args.publicKey, "Public key", "utf8");
|
|
217
266
|
const secretKey = loadKeyFile(args.secretKey, "Shared secret");
|
|
218
|
-
const verificationRequested =
|
|
267
|
+
const verificationRequested = keyGiven;
|
|
219
268
|
const inputIsLocalProtoBom = isLocalProtoBomInput(args.input);
|
|
220
269
|
if (!isCycloneDxBom(bomJson)) {
|
|
221
270
|
console.error(getNonCycloneDxErrorMessage(bomJson, "cdx-validate"));
|
|
@@ -226,7 +275,7 @@ if (inputIsLocalProtoBom && verificationRequested) {
|
|
|
226
275
|
console.error(
|
|
227
276
|
"cdx-validate: protobuf BOM input does not currently preserve JSF signature blocks. Verify signatures against the source JSON BOM instead.",
|
|
228
277
|
);
|
|
229
|
-
process.exit(
|
|
278
|
+
process.exit(requireSignature ? 4 : 1);
|
|
230
279
|
}
|
|
231
280
|
|
|
232
281
|
const report = await validateBomAdvanced(bomJson, {
|
|
@@ -237,10 +286,17 @@ const report = await validateBomAdvanced(bomJson, {
|
|
|
237
286
|
minSeverity: args.minSeverity,
|
|
238
287
|
includeManual: args.includeManual,
|
|
239
288
|
includePass: args.includePass,
|
|
240
|
-
publicKey: publicKeyStr
|
|
241
|
-
secretKey: secretKey
|
|
289
|
+
publicKey: publicKeyStr ?? undefined,
|
|
290
|
+
secretKey: secretKey ?? undefined,
|
|
242
291
|
nestedSignatures: args.nestedSignatures,
|
|
243
292
|
});
|
|
293
|
+
if (strictError && verificationRequested) {
|
|
294
|
+
report.signatureVerified = false;
|
|
295
|
+
report.signatureEntries = null;
|
|
296
|
+
report.signatureDetails = {
|
|
297
|
+
error: `the BOM cannot be verified: ${strictError}`,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
244
300
|
|
|
245
301
|
let output;
|
|
246
302
|
try {
|
|
@@ -267,14 +323,24 @@ for (const e of report.validationErrors || []) {
|
|
|
267
323
|
const { shouldFail: fail, reason } = shouldFail(report, {
|
|
268
324
|
failSeverity: args.failSeverity,
|
|
269
325
|
strict: args.strict,
|
|
270
|
-
requireSignature: Boolean(
|
|
326
|
+
requireSignature: Boolean(requireSignature && verificationRequested),
|
|
271
327
|
});
|
|
272
328
|
|
|
273
|
-
if (report.signatureVerified === false &&
|
|
329
|
+
if (report.signatureVerified === false && verificationRequested) {
|
|
274
330
|
console.error(
|
|
275
331
|
`cdx-validate: signature verification failed — ${report.signatureDetails?.error || "no matching key"}.`,
|
|
276
332
|
);
|
|
277
|
-
|
|
333
|
+
if (requireSignature) {
|
|
334
|
+
process.exit(4);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const unverifiedEntries = (report.signatureEntries || []).filter(
|
|
338
|
+
(entry) => !entry.verified,
|
|
339
|
+
);
|
|
340
|
+
if (report.signatureVerified && unverifiedEntries.length) {
|
|
341
|
+
console.error(
|
|
342
|
+
`cdx-validate: warning — ${unverifiedEntries.length} of ${report.signatureEntries.length} signature entries did not verify with the given key: ${unverifiedEntries.map((entry) => `entry ${entry.index}${entry.keyId === undefined ? "" : ` (keyId '${displayValue(entry.keyId)}')`}`).join(", ")}.`,
|
|
343
|
+
);
|
|
278
344
|
}
|
|
279
345
|
|
|
280
346
|
if (args.strict && report.schemaValid === false) {
|
package/bin/verify.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
displayValue,
|
|
18
18
|
loadSharedSecret,
|
|
19
19
|
loadVerificationKey,
|
|
20
|
-
|
|
20
|
+
verifyBomSignatures,
|
|
21
21
|
} from "../lib/helpers/bomSigner.js";
|
|
22
22
|
import {
|
|
23
23
|
getNonCycloneDxErrorMessage,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "../lib/inventory/bomUtils.js";
|
|
26
26
|
import { isProtoBomPath } from "../lib/inventory/protobomLoader.js";
|
|
27
27
|
import { getBomWithOras } from "../lib/managers/oci.js";
|
|
28
|
+
import { parseStrictJson } from "../lib/parsers/strictJson.js";
|
|
28
29
|
|
|
29
30
|
const dirName = dirNameStr;
|
|
30
31
|
|
|
@@ -41,10 +42,13 @@ const args = _yargs
|
|
|
41
42
|
description: "The platform to validate. No default",
|
|
42
43
|
})
|
|
43
44
|
.option("public-key", {
|
|
45
|
+
type: "array",
|
|
46
|
+
string: true,
|
|
44
47
|
description:
|
|
45
|
-
"
|
|
48
|
+
"Trusted public key in PEM format. Repeat to trust several keys; a signature entry verifies when one of them verifies it.",
|
|
46
49
|
})
|
|
47
50
|
.option("secret-key", {
|
|
51
|
+
type: "string",
|
|
48
52
|
description:
|
|
49
53
|
"File holding the shared secret for HMAC (HS256, HS384, HS512) signatures. HMAC signatures are only accepted with this option.",
|
|
50
54
|
})
|
|
@@ -52,7 +56,19 @@ const args = _yargs
|
|
|
52
56
|
type: "boolean",
|
|
53
57
|
default: true,
|
|
54
58
|
description:
|
|
55
|
-
"Strictly verify all nested component, service, and annotation signatures against the provided
|
|
59
|
+
"Strictly verify all nested component, service, and annotation signatures against the provided keys. Pass --no-deep to verify only the root signature.",
|
|
60
|
+
})
|
|
61
|
+
.option("require-all", {
|
|
62
|
+
type: "boolean",
|
|
63
|
+
default: false,
|
|
64
|
+
description:
|
|
65
|
+
"Require every entry of a signers or chain signature to verify with one of the keys, instead of at least one.",
|
|
66
|
+
})
|
|
67
|
+
.option("allow-nested-only", {
|
|
68
|
+
type: "boolean",
|
|
69
|
+
default: false,
|
|
70
|
+
description:
|
|
71
|
+
"Accept a BOM without a root signature when every component, service, and annotation carries a signature that verifies. No signature then covers the other members of the BOM.",
|
|
56
72
|
})
|
|
57
73
|
.completion("completion", "Generate bash/zsh completion")
|
|
58
74
|
.epilogue("for documentation, visit https://cdxgen.github.io/cdxgen")
|
|
@@ -96,7 +112,7 @@ async function getBom(args) {
|
|
|
96
112
|
process.exit(1);
|
|
97
113
|
}
|
|
98
114
|
try {
|
|
99
|
-
return
|
|
115
|
+
return parseStrictJson(fs.readFileSync(args.input));
|
|
100
116
|
} catch (error) {
|
|
101
117
|
console.log(`Failed to parse '${args.input}': ${error.message}`);
|
|
102
118
|
process.exit(1);
|
|
@@ -107,11 +123,32 @@ async function getBom(args) {
|
|
|
107
123
|
args.input.includes("docker") ||
|
|
108
124
|
args.input.includes("ghcr")
|
|
109
125
|
) {
|
|
110
|
-
|
|
126
|
+
try {
|
|
127
|
+
return await getBomWithOras(args.input, args.platform, {
|
|
128
|
+
checkBytes: parseStrictJson,
|
|
129
|
+
});
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.log(
|
|
132
|
+
`Failed to parse the BOM attached to '${args.input}': ${error.message}`,
|
|
133
|
+
);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
111
136
|
}
|
|
112
137
|
return undefined;
|
|
113
138
|
}
|
|
114
139
|
|
|
140
|
+
const publicKeyFiles = args.publicKey || [];
|
|
141
|
+
if (publicKeyFiles.length && args.secretKey) {
|
|
142
|
+
console.log("Use either --public-key or --secret-key, not both.");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
if (!publicKeyFiles.length && !args.secretKey) {
|
|
146
|
+
console.log(
|
|
147
|
+
"Name the trusted key with --public-key <file>, or --secret-key <file> for HMAC signatures.",
|
|
148
|
+
);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
|
|
115
152
|
const bomJson = await getBom(args);
|
|
116
153
|
|
|
117
154
|
if (!bomJson) {
|
|
@@ -123,107 +160,139 @@ if (!isCycloneDxBom(bomJson)) {
|
|
|
123
160
|
process.exit(1);
|
|
124
161
|
}
|
|
125
162
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
verificationKey = args.secretKey
|
|
148
|
-
? loadSharedSecret(fs.readFileSync(keyFile))
|
|
149
|
-
: loadVerificationKey(fs.readFileSync(keyFile, "utf8"));
|
|
150
|
-
} catch (error) {
|
|
151
|
-
console.log(`Unable to use '${keyFile}': ${error.message}`);
|
|
152
|
-
process.exit(1);
|
|
163
|
+
const verificationKeys = [];
|
|
164
|
+
for (const keyFile of args.secretKey ? [args.secretKey] : publicKeyFiles) {
|
|
165
|
+
if (!safeExistsSync(keyFile)) {
|
|
166
|
+
console.log(
|
|
167
|
+
args.secretKey
|
|
168
|
+
? `Shared secret '${keyFile}' for signature verification is missing!`
|
|
169
|
+
: `Public key '${keyFile}' for signature verification is missing!`,
|
|
170
|
+
);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
verificationKeys.push(
|
|
175
|
+
args.secretKey
|
|
176
|
+
? loadSharedSecret(fs.readFileSync(keyFile))
|
|
177
|
+
: loadVerificationKey(fs.readFileSync(keyFile, "utf8")),
|
|
178
|
+
);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log(`Unable to use '${keyFile}': ${error.message}`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
153
183
|
}
|
|
154
184
|
|
|
155
|
-
function
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
185
|
+
function describeEntry(node, entry) {
|
|
186
|
+
const parts = [displayValue(entry.algorithm)];
|
|
187
|
+
if (entry.keyId !== undefined) {
|
|
188
|
+
parts.push(`keyId '${displayValue(entry.keyId)}'`);
|
|
189
|
+
}
|
|
190
|
+
const position =
|
|
191
|
+
node.form === "single" ? "" : `${node.form}[${entry.index}] `;
|
|
192
|
+
return `${position}(${parts.join(", ")})`;
|
|
159
193
|
}
|
|
160
194
|
|
|
161
|
-
function
|
|
162
|
-
for (const reason of
|
|
195
|
+
function printRejections(node) {
|
|
196
|
+
for (const reason of node.reasons) {
|
|
163
197
|
console.log(` - ${reason}`);
|
|
164
198
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const verifyNested = args.deep || !bomJson.signature;
|
|
173
|
-
let hasInvalidNested = false;
|
|
174
|
-
let checkedNested = 0;
|
|
175
|
-
|
|
176
|
-
if (verifyNested) {
|
|
177
|
-
const nestedTargets = [
|
|
178
|
-
["components", "Component", (c) => c["bom-ref"] || c.name],
|
|
179
|
-
["services", "Service", (s) => s["bom-ref"] || s.name],
|
|
180
|
-
["annotations", "Annotation", (a) => a["bom-ref"] || a.subject],
|
|
181
|
-
];
|
|
182
|
-
for (const [field, label, nameOf] of nestedTargets) {
|
|
183
|
-
for (const node of bomJson[field] || []) {
|
|
184
|
-
if (node?.signature) {
|
|
185
|
-
checkedNested++;
|
|
186
|
-
const result = verify(node);
|
|
187
|
-
if (!result.match) {
|
|
188
|
-
console.log(
|
|
189
|
-
`${label} '${displayValue(nameOf(node))}' signature is invalid!`,
|
|
190
|
-
);
|
|
191
|
-
printReasons(result.reasons);
|
|
192
|
-
hasInvalidNested = true;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
199
|
+
for (const entry of node.entries) {
|
|
200
|
+
for (const reason of entry.reasons) {
|
|
201
|
+
console.log(
|
|
202
|
+
` - ${node.form === "single" ? "" : `${node.form}[${entry.index}]: `}${reason}`,
|
|
203
|
+
);
|
|
195
204
|
}
|
|
196
205
|
}
|
|
197
206
|
}
|
|
198
207
|
|
|
199
|
-
|
|
208
|
+
const report = verifyBomSignatures(bomJson, verificationKeys, {
|
|
209
|
+
deep: args.deep,
|
|
210
|
+
allowNestedOnly: args.allowNestedOnly,
|
|
211
|
+
requireAll: args.requireAll,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
for (const error of report.errors) {
|
|
215
|
+
console.log(error);
|
|
216
|
+
}
|
|
217
|
+
const keyFiles = args.secretKey ? [args.secretKey] : publicKeyFiles;
|
|
218
|
+
if (args.requireAll && report.root?.form) {
|
|
219
|
+
report.root.keysMatched.forEach((matched, index) => {
|
|
220
|
+
if (!matched) {
|
|
221
|
+
console.log(` - key #${index + 1}: ${keyFiles[index]}`);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const invalidNested = report.nested.filter((item) => !item.verified);
|
|
226
|
+
for (const item of invalidNested) {
|
|
227
|
+
console.log(`${item.label} signature is invalid!`);
|
|
228
|
+
printRejections(item);
|
|
229
|
+
}
|
|
230
|
+
if (invalidNested.length) {
|
|
200
231
|
console.log("One or more nested signatures are invalid!");
|
|
201
|
-
if (
|
|
232
|
+
if (report.root?.verified) {
|
|
202
233
|
console.log(
|
|
203
234
|
"The root signature verifies with this key. Nested signatures belong to the party that created them, so pass --no-deep to verify only the root signature with this key.",
|
|
204
235
|
);
|
|
205
236
|
}
|
|
237
|
+
}
|
|
238
|
+
if (report.root && !report.root.verified) {
|
|
239
|
+
console.log("BOM signature is invalid!");
|
|
240
|
+
printRejections(report.root);
|
|
241
|
+
if (!report.root.reasons.length && !report.root.match) {
|
|
242
|
+
console.log(
|
|
243
|
+
"If this BOM was signed with cdxgen 13.2.0 or earlier, re-sign it with this version of cdx-sign.",
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!report.root && !bomJson.signature && !args.allowNestedOnly) {
|
|
248
|
+
console.log(
|
|
249
|
+
"Pass --allow-nested-only to accept a BOM whose components, services, and annotations are all signed.",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
for (const notice of report.notices) {
|
|
253
|
+
console.log(`Note: ${notice}`);
|
|
254
|
+
}
|
|
255
|
+
if (!report.verified) {
|
|
206
256
|
process.exit(1);
|
|
207
257
|
}
|
|
208
258
|
|
|
209
|
-
if (
|
|
210
|
-
const rootMatch =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
console.log("BOM signature is invalid!");
|
|
218
|
-
printReasons(rootResult?.reasons || []);
|
|
259
|
+
if (report.root) {
|
|
260
|
+
const rootMatch = report.root.match;
|
|
261
|
+
const identifier = rootMatch.keyId
|
|
262
|
+
? `KeyId: '${displayValue(rootMatch.keyId)}'`
|
|
263
|
+
: `Algorithm: '${rootMatch.algorithm}'`;
|
|
264
|
+
console.log(`✓ Signature is valid! (Matched ${identifier})`);
|
|
265
|
+
const unverified = report.root.entries.filter((entry) => !entry.verified);
|
|
266
|
+
if (unverified.length) {
|
|
219
267
|
console.log(
|
|
220
|
-
|
|
268
|
+
`Warning: ${unverified.length} of ${report.root.entries.length} ${report.root.form} entries did not verify with the given key(s). Pass --require-all to fail in that case.`,
|
|
221
269
|
);
|
|
222
|
-
|
|
270
|
+
for (const entry of unverified) {
|
|
271
|
+
console.log(
|
|
272
|
+
` - ${describeEntry(report.root, entry)}: ${entry.reasons[0] || "not verified"}`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
223
275
|
}
|
|
224
|
-
} else if (checkedNested > 0 && !hasInvalidNested) {
|
|
225
|
-
console.log(`✓ ${checkedNested} nested signature(s) are valid!`);
|
|
226
276
|
} else {
|
|
227
|
-
console.log(
|
|
228
|
-
|
|
277
|
+
console.log(`✓ ${report.nested.length} nested signature(s) are valid!`);
|
|
278
|
+
}
|
|
279
|
+
const MAX_LISTED_ENTRIES = 10;
|
|
280
|
+
const unverifiedNested = report.nested.flatMap((item) =>
|
|
281
|
+
item.entries
|
|
282
|
+
.filter((entry) => !entry.verified)
|
|
283
|
+
.map(
|
|
284
|
+
(entry) =>
|
|
285
|
+
`${item.label} ${describeEntry(item, entry)}: ${entry.reasons[0] || "not verified"}`,
|
|
286
|
+
),
|
|
287
|
+
);
|
|
288
|
+
if (unverifiedNested.length) {
|
|
289
|
+
console.log(
|
|
290
|
+
`Warning: ${unverifiedNested.length} nested signature entries did not verify with the given key(s). Pass --require-all to fail in that case.`,
|
|
291
|
+
);
|
|
292
|
+
for (const line of unverifiedNested.slice(0, MAX_LISTED_ENTRIES)) {
|
|
293
|
+
console.log(` - ${line}`);
|
|
294
|
+
}
|
|
295
|
+
if (unverifiedNested.length > MAX_LISTED_ENTRIES) {
|
|
296
|
+
console.log(` - and ${unverifiedNested.length - MAX_LISTED_ENTRIES} more`);
|
|
297
|
+
}
|
|
229
298
|
}
|