@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/ecosystems/golem.js
CHANGED
|
@@ -11,7 +11,17 @@ import {
|
|
|
11
11
|
safeSpawnSync,
|
|
12
12
|
} from "../core/fs.js";
|
|
13
13
|
import { dirNameStr } from "../core/paths.js";
|
|
14
|
+
import {
|
|
15
|
+
reportPluginRunFailure,
|
|
16
|
+
resolvePluginSourceDir,
|
|
17
|
+
} from "../core/pluginRun.js";
|
|
18
|
+
import {
|
|
19
|
+
encodeUriReference,
|
|
20
|
+
hasInlineCredential,
|
|
21
|
+
sanitizeBomUrl,
|
|
22
|
+
} from "../core/propertySanitizer.js";
|
|
14
23
|
import { resolvePluginBinary } from "../inventory/plugins.js";
|
|
24
|
+
import { canonicalPurlFromLooseString } from "../inventory/purl.js";
|
|
15
25
|
|
|
16
26
|
const GO_LANGUAGES = new Set(["go", "golang"]);
|
|
17
27
|
const GOLEM_CALLGRAPH_MODES = new Set(["none", "static", "cha", "rta", "vta"]);
|
|
@@ -104,7 +114,38 @@ function purlWithoutVersion(purl) {
|
|
|
104
114
|
}
|
|
105
115
|
|
|
106
116
|
function modulePurl(module) {
|
|
107
|
-
|
|
117
|
+
// cdxgen reports a module that a replace directive swapped out under the
|
|
118
|
+
// replacement's coordinates, which golem names as replace.purl. A directory
|
|
119
|
+
// replacement has none, and its versionless component matches module.purl.
|
|
120
|
+
return module?.replace?.purl || module?.purl || module?.PURL;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The canonical form of every purl an alias map was asked about, so a report
|
|
124
|
+
// naming one module in many usages parses its purl once.
|
|
125
|
+
const purlMatchKeyCache = new WeakMap();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The form two purls of one module share. Golem prints a module path as Go
|
|
129
|
+
* does, keeping its upper-case letters (github.com/BurntSushi/toml), while
|
|
130
|
+
* cdxgen components carry the canonical golang purl, which lower-cases them.
|
|
131
|
+
* Canonicalising both sides also reads an unescaped `+incompatible` like the
|
|
132
|
+
* escaped one.
|
|
133
|
+
*
|
|
134
|
+
* @param {string} purl A purl from the report or a component
|
|
135
|
+
* @param {Map} purlAliasMap The alias map the key belongs to
|
|
136
|
+
* @returns {string} The canonical purl, or the purl itself when it cannot be
|
|
137
|
+
* parsed
|
|
138
|
+
*/
|
|
139
|
+
function purlMatchKey(purl, purlAliasMap) {
|
|
140
|
+
let cache = purlMatchKeyCache.get(purlAliasMap);
|
|
141
|
+
if (!cache) {
|
|
142
|
+
cache = new Map();
|
|
143
|
+
purlMatchKeyCache.set(purlAliasMap, cache);
|
|
144
|
+
}
|
|
145
|
+
if (!cache.has(purl)) {
|
|
146
|
+
cache.set(purl, canonicalPurlFromLooseString(purl) || purl);
|
|
147
|
+
}
|
|
148
|
+
return cache.get(purl);
|
|
108
149
|
}
|
|
109
150
|
|
|
110
151
|
function createPurlAliasMap(components = []) {
|
|
@@ -113,8 +154,9 @@ function createPurlAliasMap(components = []) {
|
|
|
113
154
|
if (!component?.purl) {
|
|
114
155
|
continue;
|
|
115
156
|
}
|
|
116
|
-
|
|
117
|
-
|
|
157
|
+
const key = purlMatchKey(component.purl, purlAliasMap);
|
|
158
|
+
purlAliasMap.set(key, component.purl);
|
|
159
|
+
const noVersionPurl = purlWithoutVersion(key);
|
|
118
160
|
if (noVersionPurl && !purlAliasMap.has(noVersionPurl)) {
|
|
119
161
|
purlAliasMap.set(noVersionPurl, component.purl);
|
|
120
162
|
}
|
|
@@ -126,7 +168,13 @@ function resolveComponentPurl(purl, purlAliasMap) {
|
|
|
126
168
|
if (!purl) {
|
|
127
169
|
return undefined;
|
|
128
170
|
}
|
|
129
|
-
|
|
171
|
+
const key = purlMatchKey(purl, purlAliasMap);
|
|
172
|
+
return (
|
|
173
|
+
purlAliasMap.get(key) ||
|
|
174
|
+
// A package subpath (`#internal`) names a part of the module.
|
|
175
|
+
purlAliasMap.get(key.split("#")[0]) ||
|
|
176
|
+
purlAliasMap.get(purlWithoutVersion(key))
|
|
177
|
+
);
|
|
130
178
|
}
|
|
131
179
|
|
|
132
180
|
function addResolvedPurl(purls, purl, purlAliasMap) {
|
|
@@ -1342,10 +1390,13 @@ function normalizedGolemDataFlowMode(options = {}) {
|
|
|
1342
1390
|
return dataFlowMode;
|
|
1343
1391
|
}
|
|
1344
1392
|
|
|
1345
|
-
function normalizedGolemCallGraphMode(
|
|
1393
|
+
function normalizedGolemCallGraphMode(options = {}) {
|
|
1346
1394
|
let callgraphMode = String(options.golemCallgraph || "static").toLowerCase();
|
|
1347
1395
|
if (!GOLEM_CALLGRAPH_MODES.has(callgraphMode)) {
|
|
1348
|
-
|
|
1396
|
+
// An unrecognised mode falls back to the default. It used to fall back to
|
|
1397
|
+
// the data-flow mode ("security", "all"), which is not a call-graph mode
|
|
1398
|
+
// and made golem reject the run.
|
|
1399
|
+
callgraphMode = "static";
|
|
1349
1400
|
}
|
|
1350
1401
|
return callgraphMode;
|
|
1351
1402
|
}
|
|
@@ -1606,14 +1657,18 @@ export function isGolemGoLanguage(language) {
|
|
|
1606
1657
|
*
|
|
1607
1658
|
* @param {string} jsonFile Path to the golem JSON report.
|
|
1608
1659
|
* @returns {object|undefined} Parsed report object, or undefined when the file
|
|
1609
|
-
* is missing
|
|
1660
|
+
* is missing, cannot be parsed, or holds something other than a JSON object
|
|
1661
|
+
* such as `null`.
|
|
1610
1662
|
*/
|
|
1611
1663
|
export function readGolemJsonFile(jsonFile) {
|
|
1612
1664
|
if (!jsonFile || !safeExistsSync(jsonFile)) {
|
|
1613
1665
|
return undefined;
|
|
1614
1666
|
}
|
|
1615
1667
|
try {
|
|
1616
|
-
|
|
1668
|
+
const report = JSON.parse(readFileSync(jsonFile, "utf-8"));
|
|
1669
|
+
return report && typeof report === "object" && !Array.isArray(report)
|
|
1670
|
+
? report
|
|
1671
|
+
: undefined;
|
|
1617
1672
|
} catch (_err) {
|
|
1618
1673
|
return undefined;
|
|
1619
1674
|
}
|
|
@@ -1637,11 +1692,17 @@ export function runGolemAnalysis(src, outputFile, options = {}) {
|
|
|
1637
1692
|
return false;
|
|
1638
1693
|
}
|
|
1639
1694
|
const dataFlowMode = normalizedGolemDataFlowMode(options);
|
|
1640
|
-
const callgraphMode = normalizedGolemCallGraphMode(
|
|
1695
|
+
const callgraphMode = normalizedGolemCallGraphMode(options);
|
|
1696
|
+
// cdxgen may run with its working directory already inside the project
|
|
1697
|
+
// while the source argument is relative to a repository root; a plain
|
|
1698
|
+
// resolve would join the two into a directory that does not exist. The
|
|
1699
|
+
// helper checks the result and falls back to the working directory when it
|
|
1700
|
+
// is itself the Go module under analysis.
|
|
1701
|
+
const analysisDir = resolvePluginSourceDir(src, ["go.mod", "go.work"]);
|
|
1641
1702
|
const args = [
|
|
1642
1703
|
"analyze",
|
|
1643
1704
|
"--dir",
|
|
1644
|
-
|
|
1705
|
+
analysisDir,
|
|
1645
1706
|
"--format",
|
|
1646
1707
|
"json",
|
|
1647
1708
|
"--callgraph",
|
|
@@ -1666,17 +1727,20 @@ export function runGolemAnalysis(src, outputFile, options = {}) {
|
|
|
1666
1727
|
console.log("Executing", executable, args.join(" "));
|
|
1667
1728
|
}
|
|
1668
1729
|
const result = safeSpawnSync(executable, args, {
|
|
1669
|
-
cwd:
|
|
1730
|
+
cwd: analysisDir,
|
|
1670
1731
|
shell: false,
|
|
1671
1732
|
});
|
|
1672
1733
|
if (result?.status !== 0 || result?.error || !safeExistsSync(outputFile)) {
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1734
|
+
reportPluginRunFailure({
|
|
1735
|
+
tool: "golem",
|
|
1736
|
+
ecosystem: "go",
|
|
1737
|
+
executable,
|
|
1738
|
+
args,
|
|
1739
|
+
dir: analysisDir,
|
|
1740
|
+
result,
|
|
1741
|
+
outputFile,
|
|
1742
|
+
options,
|
|
1743
|
+
});
|
|
1680
1744
|
return false;
|
|
1681
1745
|
}
|
|
1682
1746
|
return true;
|
|
@@ -1815,3 +1879,499 @@ export function collectGolemEvidence(golemReport = {}, components = []) {
|
|
|
1815
1879
|
purlLocationMap,
|
|
1816
1880
|
};
|
|
1817
1881
|
}
|
|
1882
|
+
|
|
1883
|
+
// The methods a route can be registered for: the RFC 9110 methods, PATCH,
|
|
1884
|
+
// and the catch-all that gin's and echo's Any register.
|
|
1885
|
+
const GOLEM_HTTP_METHODS = new Set([
|
|
1886
|
+
"get",
|
|
1887
|
+
"head",
|
|
1888
|
+
"post",
|
|
1889
|
+
"put",
|
|
1890
|
+
"delete",
|
|
1891
|
+
"connect",
|
|
1892
|
+
"options",
|
|
1893
|
+
"trace",
|
|
1894
|
+
"patch",
|
|
1895
|
+
"any",
|
|
1896
|
+
]);
|
|
1897
|
+
|
|
1898
|
+
// An RFC 9110 token, which is what net/http accepts as a pattern's method.
|
|
1899
|
+
const HTTP_METHOD_TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
1900
|
+
|
|
1901
|
+
// The host of a net/http pattern: a name or address with an optional port.
|
|
1902
|
+
const GO_PATTERN_HOST = /^[A-Za-z0-9.:[\]-]+$/;
|
|
1903
|
+
|
|
1904
|
+
// Hosts whose URL path carries the credential itself — webhook and bot URLs
|
|
1905
|
+
// that authenticate by the path alone — with the route each keeps. Everything
|
|
1906
|
+
// below that route, the team, channel and hook ids and the secret, is dropped.
|
|
1907
|
+
const GOLEM_SECRET_PATH_ROUTES = [
|
|
1908
|
+
["hooks.slack.com", ["/services/", "/workflows/", "/triggers/"]],
|
|
1909
|
+
["discord.com", ["/api/webhooks/"]],
|
|
1910
|
+
["discordapp.com", ["/api/webhooks/"]],
|
|
1911
|
+
["hooks.zapier.com", ["/hooks/catch/", "/hooks/standard/"]],
|
|
1912
|
+
["maker.ifttt.com", ["/trigger/"]],
|
|
1913
|
+
["api.telegram.org", ["/bot", "/file/bot"]],
|
|
1914
|
+
["outlook.office.com", ["/webhook/", "/webhookb2/"]],
|
|
1915
|
+
["webhook.office.com", ["/webhookb2/"]],
|
|
1916
|
+
];
|
|
1917
|
+
|
|
1918
|
+
/**
|
|
1919
|
+
* Append a property to a service definition unless the same name and value are
|
|
1920
|
+
* already there, so repeated endpoints of one service do not repeat their
|
|
1921
|
+
* framework or method.
|
|
1922
|
+
*
|
|
1923
|
+
* @param {Object[]} properties Property array, appended to in place
|
|
1924
|
+
* @param {string} name Property name
|
|
1925
|
+
* @param {string} value Property value, skipped unless a non-empty string
|
|
1926
|
+
*/
|
|
1927
|
+
function appendGolemServiceProperty(properties, name, value) {
|
|
1928
|
+
if (typeof value !== "string" || !value) {
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
if (
|
|
1932
|
+
!properties.some((known) => known.name === name && known.value === value)
|
|
1933
|
+
) {
|
|
1934
|
+
properties.push({ name, value });
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
/**
|
|
1939
|
+
* The method a golem endpoint names, lower-cased the way the OpenAPI flow
|
|
1940
|
+
* spells `cdx:service:httpMethod`, or undefined when it is not an HTTP
|
|
1941
|
+
* method. golem takes the selector of a chained gorilla/mux call for the
|
|
1942
|
+
* method, so `.Methods("GET", "POST")` arrives as `METHODS` and
|
|
1943
|
+
* `.PathPrefix("/products")` as `PATHPREFIX`; neither names one.
|
|
1944
|
+
*
|
|
1945
|
+
* @param {unknown} method Method a golem endpoint reports
|
|
1946
|
+
* @returns {string|undefined} The lower-cased method
|
|
1947
|
+
*/
|
|
1948
|
+
function golemHttpMethod(method) {
|
|
1949
|
+
if (typeof method !== "string") {
|
|
1950
|
+
return undefined;
|
|
1951
|
+
}
|
|
1952
|
+
const lowered = method.trim().toLowerCase();
|
|
1953
|
+
return GOLEM_HTTP_METHODS.has(lowered) ? lowered : undefined;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* The host a golem service names, without the `/` golem puts in front of a
|
|
1958
|
+
* listener address that has a host (`/127.0.0.1:9095`) and without userinfo.
|
|
1959
|
+
*
|
|
1960
|
+
* @param {unknown} host Host a golem service reports
|
|
1961
|
+
* @returns {string|undefined} The host
|
|
1962
|
+
*/
|
|
1963
|
+
function golemServiceHost(host) {
|
|
1964
|
+
if (typeof host !== "string") {
|
|
1965
|
+
return undefined;
|
|
1966
|
+
}
|
|
1967
|
+
return host.slice(host.lastIndexOf("@") + 1).replace(/^\/+/, "") || undefined;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
/**
|
|
1971
|
+
* Index of the brace that closes the one at `open`, counting nesting the way
|
|
1972
|
+
* gorilla/mux's braceIndices does, so the `{3}` of `{code:[0-9]{3}}` stays
|
|
1973
|
+
* inside the variable. -1 when the braces are unbalanced.
|
|
1974
|
+
*
|
|
1975
|
+
* @param {string} text Route path
|
|
1976
|
+
* @param {number} open Index of an opening brace
|
|
1977
|
+
* @returns {number} Index of the closing brace, or -1
|
|
1978
|
+
*/
|
|
1979
|
+
function closingBraceIndex(text, open) {
|
|
1980
|
+
let depth = 0;
|
|
1981
|
+
for (let index = open; index < text.length; index++) {
|
|
1982
|
+
if (text[index] === "{") {
|
|
1983
|
+
depth++;
|
|
1984
|
+
} else if (text[index] === "}" && --depth === 0) {
|
|
1985
|
+
return index;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
return -1;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
/**
|
|
1992
|
+
* The OpenAPI spelling of a braced variable. gorilla/mux, chi and iris write
|
|
1993
|
+
* `{name:pattern}`; Go 1.22 net/http writes `{name...}` for a trailing
|
|
1994
|
+
* wildcard and `{$}` for an exact match of a trailing slash, which names no
|
|
1995
|
+
* parameter and is dropped.
|
|
1996
|
+
*
|
|
1997
|
+
* @param {string} raw Text between the braces
|
|
1998
|
+
* @returns {string} The placeholder
|
|
1999
|
+
*/
|
|
2000
|
+
function bracedPlaceholder(raw) {
|
|
2001
|
+
let name = raw.split(":", 1)[0].trim();
|
|
2002
|
+
if (name.endsWith("...")) {
|
|
2003
|
+
name = name.slice(0, -3);
|
|
2004
|
+
}
|
|
2005
|
+
if (name === "$") {
|
|
2006
|
+
return "";
|
|
2007
|
+
}
|
|
2008
|
+
return name ? `{${name}}` : `{${raw}}`;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
/**
|
|
2012
|
+
* The name of a `:name` parameter starting at `start` and the index after it.
|
|
2013
|
+
* gin and echo end the name at the next `/`, so `:user-id` names `user-id`.
|
|
2014
|
+
* fiber also ends it at `-` and `.`, which lets one segment hold two
|
|
2015
|
+
* parameters (`:from-:to`), and allows a constraint and an optional marker
|
|
2016
|
+
* after it (`:id<int>?`); both are dropped.
|
|
2017
|
+
*
|
|
2018
|
+
* @param {string} routePath Route path
|
|
2019
|
+
* @param {number} start Index after the colon
|
|
2020
|
+
* @param {boolean} fiber Whether the route is a fiber route
|
|
2021
|
+
* @returns {{name: string, end: number}|undefined} The parameter
|
|
2022
|
+
*/
|
|
2023
|
+
function colonParameter(routePath, start, fiber) {
|
|
2024
|
+
const stops = fiber ? "/-.<?" : "/<?";
|
|
2025
|
+
let end = start;
|
|
2026
|
+
while (end < routePath.length && !stops.includes(routePath[end])) {
|
|
2027
|
+
end++;
|
|
2028
|
+
}
|
|
2029
|
+
const name = routePath.slice(start, end);
|
|
2030
|
+
if (!name) {
|
|
2031
|
+
return undefined;
|
|
2032
|
+
}
|
|
2033
|
+
if (routePath[end] === "<") {
|
|
2034
|
+
const close = routePath.indexOf(">", end);
|
|
2035
|
+
if (close >= 0) {
|
|
2036
|
+
end = close + 1;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
if (routePath[end] === "?") {
|
|
2040
|
+
end++;
|
|
2041
|
+
}
|
|
2042
|
+
return { name, end };
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
/**
|
|
2046
|
+
* Normalise a Go route path to the placeholder spelling OpenAPI — and
|
|
2047
|
+
* therefore the rest of the services a BOM carries — uses. gin, echo and
|
|
2048
|
+
* fiber write `:id`, gin writes `*filepath`, chi and gorilla/mux write `{id}`
|
|
2049
|
+
* or `{id:[0-9]+}`, and Go 1.22's net/http patterns write `{id...}`; all
|
|
2050
|
+
* become `{id}`. Braces are matched by depth, so a regular expression with
|
|
2051
|
+
* its own braces or slashes (`{code:[0-9]{3}}`, `{rest:[a-z/]+}`) stays one
|
|
2052
|
+
* variable. A fiber constraint or optional marker (`:id<int>`, `:name?`) is
|
|
2053
|
+
* dropped, and net/http's `{$}` is removed. A `:` or `*` placeholder is only
|
|
2054
|
+
* recognised at the start of a segment — or, for fiber, after a `-` or `.` —
|
|
2055
|
+
* so a literal colon such as `/time/09:30` is left alone. Values that do not
|
|
2056
|
+
* start with `/` are returned as they are, and so is echo's catch-all, whose
|
|
2057
|
+
* bare `*` names no placeholder.
|
|
2058
|
+
*
|
|
2059
|
+
* @param {string} routePath Path a golem endpoint reports
|
|
2060
|
+
* @param {string} [framework] Framework the route was registered with
|
|
2061
|
+
* @returns {string} The normalised path
|
|
2062
|
+
*/
|
|
2063
|
+
export function normalizeGolemRoutePath(routePath, framework) {
|
|
2064
|
+
if (typeof routePath !== "string" || !routePath.startsWith("/")) {
|
|
2065
|
+
return routePath;
|
|
2066
|
+
}
|
|
2067
|
+
const fiber = framework === "fiber";
|
|
2068
|
+
let normalized = "";
|
|
2069
|
+
let index = 0;
|
|
2070
|
+
while (index < routePath.length) {
|
|
2071
|
+
const character = routePath[index];
|
|
2072
|
+
const previous = routePath[index - 1];
|
|
2073
|
+
if (character === "{") {
|
|
2074
|
+
const close = closingBraceIndex(routePath, index);
|
|
2075
|
+
if (close < 0) {
|
|
2076
|
+
// gorilla/mux rejects unbalanced braces; keep the rest as written.
|
|
2077
|
+
normalized += routePath.slice(index);
|
|
2078
|
+
break;
|
|
2079
|
+
}
|
|
2080
|
+
normalized += bracedPlaceholder(routePath.slice(index + 1, close));
|
|
2081
|
+
index = close + 1;
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
if (
|
|
2085
|
+
character === ":" &&
|
|
2086
|
+
(previous === "/" || (fiber && (previous === "-" || previous === ".")))
|
|
2087
|
+
) {
|
|
2088
|
+
const parameter = colonParameter(routePath, index + 1, fiber);
|
|
2089
|
+
if (parameter) {
|
|
2090
|
+
normalized += `{${parameter.name}}`;
|
|
2091
|
+
index = parameter.end;
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
if (character === "*" && previous === "/") {
|
|
2096
|
+
let end = routePath.indexOf("/", index);
|
|
2097
|
+
if (end < 0) {
|
|
2098
|
+
end = routePath.length;
|
|
2099
|
+
}
|
|
2100
|
+
const name = routePath.slice(index + 1, end);
|
|
2101
|
+
if (/^[A-Za-z0-9_]+$/.test(name)) {
|
|
2102
|
+
normalized += `{${name}}`;
|
|
2103
|
+
index = end;
|
|
2104
|
+
continue;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
normalized += character;
|
|
2108
|
+
index++;
|
|
2109
|
+
}
|
|
2110
|
+
return normalized;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
/**
|
|
2114
|
+
* Split a route golem reports into the `[METHOD ][HOST]/PATH` parts of a Go
|
|
2115
|
+
* 1.22 net/http ServeMux pattern. golem puts a `/` in front of the pattern it
|
|
2116
|
+
* read, so `GET /items/{id}` arrives as `/GET /items/{id}` and
|
|
2117
|
+
* `GET example.com/items` as `/GET example.com/items`. A method followed by a
|
|
2118
|
+
* space or tab is split off, and so is a host between the method and the
|
|
2119
|
+
* path. Without a method, only a value that starts with `/` is a route: the
|
|
2120
|
+
* `:8080` a listener reports and the `GET` that a gorilla/mux
|
|
2121
|
+
* `.Methods("GET")` call leaves behind are not, and give undefined. A host
|
|
2122
|
+
* pattern without a method reaches golem's report as a plain path
|
|
2123
|
+
* (`/example.com/items`) and stays one.
|
|
2124
|
+
*
|
|
2125
|
+
* @param {string} routePath Path a golem endpoint reports
|
|
2126
|
+
* @returns {{method: (string|undefined), host: (string|undefined), path: string}|undefined}
|
|
2127
|
+
* The parts, or undefined when the value is not a route
|
|
2128
|
+
*/
|
|
2129
|
+
export function parseGolemRoutePattern(routePath) {
|
|
2130
|
+
if (typeof routePath !== "string") {
|
|
2131
|
+
return undefined;
|
|
2132
|
+
}
|
|
2133
|
+
const body = routePath.startsWith("/") ? routePath.slice(1) : routePath;
|
|
2134
|
+
const gap = body.search(/[ \t]/);
|
|
2135
|
+
if (gap > 0) {
|
|
2136
|
+
const method = body.slice(0, gap);
|
|
2137
|
+
const rest = body.slice(gap + 1).replace(/^[ \t]+/, "");
|
|
2138
|
+
const slash = rest.indexOf("/");
|
|
2139
|
+
if (HTTP_METHOD_TOKEN.test(method) && slash >= 0) {
|
|
2140
|
+
const host = rest.slice(0, slash);
|
|
2141
|
+
if (host && !GO_PATTERN_HOST.test(host)) {
|
|
2142
|
+
return undefined;
|
|
2143
|
+
}
|
|
2144
|
+
return { method, host: host || undefined, path: rest.slice(slash) };
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
if (!routePath.startsWith("/")) {
|
|
2148
|
+
return undefined;
|
|
2149
|
+
}
|
|
2150
|
+
return { method: undefined, host: undefined, path: routePath };
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
/**
|
|
2154
|
+
* The route a golem endpoint of a server names: its CycloneDX endpoint, HTTP
|
|
2155
|
+
* method and, for a host-specific net/http pattern, host. Undefined when the
|
|
2156
|
+
* entry is not a route — the listener entry that repeats the address a server
|
|
2157
|
+
* binds, the leftovers of a chained gorilla/mux call, or the `/PATCH` golem
|
|
2158
|
+
* reports for gin's `Handle("PATCH", "/path")`, whose method it read as the
|
|
2159
|
+
* path.
|
|
2160
|
+
*
|
|
2161
|
+
* @param {object} endpoint Endpoint a golem server service reports
|
|
2162
|
+
* @param {object} service The service
|
|
2163
|
+
* @returns {{endpoint: string, method: (string|undefined), host: (string|undefined)}|undefined}
|
|
2164
|
+
* The route
|
|
2165
|
+
*/
|
|
2166
|
+
function golemServerRoute(endpoint, service) {
|
|
2167
|
+
const routePath = endpoint.path;
|
|
2168
|
+
if (typeof routePath !== "string" || !routePath) {
|
|
2169
|
+
return undefined;
|
|
2170
|
+
}
|
|
2171
|
+
const listener = golemServiceHost(service.host);
|
|
2172
|
+
if (listener && routePath.replace(/^\//, "") === listener) {
|
|
2173
|
+
return undefined;
|
|
2174
|
+
}
|
|
2175
|
+
const pattern = parseGolemRoutePattern(routePath);
|
|
2176
|
+
if (!pattern) {
|
|
2177
|
+
return undefined;
|
|
2178
|
+
}
|
|
2179
|
+
const method =
|
|
2180
|
+
golemHttpMethod(endpoint.method) || golemHttpMethod(pattern.method);
|
|
2181
|
+
if (
|
|
2182
|
+
!method &&
|
|
2183
|
+
/^\/[A-Z]+$/.test(pattern.path) &&
|
|
2184
|
+
GOLEM_HTTP_METHODS.has(pattern.path.slice(1).toLowerCase())
|
|
2185
|
+
) {
|
|
2186
|
+
return undefined;
|
|
2187
|
+
}
|
|
2188
|
+
const path = normalizeGolemRoutePath(
|
|
2189
|
+
pattern.path,
|
|
2190
|
+
endpoint.properties?.framework,
|
|
2191
|
+
).split(/[?#]/, 1)[0];
|
|
2192
|
+
return {
|
|
2193
|
+
endpoint: encodeUriReference(path),
|
|
2194
|
+
method,
|
|
2195
|
+
host: pattern.host,
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
/**
|
|
2200
|
+
* Reduce an outbound URL golem reports to what is safe to keep in a BOM:
|
|
2201
|
+
* scheme, host and path, without userinfo, query, fragment or `;name=value`
|
|
2202
|
+
* path parameters such as `;jsessionid=` and `;token=`. Webhook and bot URLs
|
|
2203
|
+
* that authenticate by their path — Slack, Discord, Teams, Zapier, IFTTT,
|
|
2204
|
+
* Telegram — keep only their route (`https://hooks.slack.com/services`), and a
|
|
2205
|
+
* path segment holding a token shape the BOM sanitizer recognises ends the
|
|
2206
|
+
* path there. Undefined when the value is not a URL with a host.
|
|
2207
|
+
*
|
|
2208
|
+
* @param {string} value URL golem reports
|
|
2209
|
+
* @returns {string|undefined} The sanitised URL
|
|
2210
|
+
*/
|
|
2211
|
+
export function sanitizeGolemEndpointUrl(value) {
|
|
2212
|
+
if (typeof value !== "string") {
|
|
2213
|
+
return undefined;
|
|
2214
|
+
}
|
|
2215
|
+
let parsed;
|
|
2216
|
+
try {
|
|
2217
|
+
// sanitizeBomUrl returns what it cannot parse as it is, which this parse
|
|
2218
|
+
// then rejects.
|
|
2219
|
+
parsed = new URL(sanitizeBomUrl(value));
|
|
2220
|
+
} catch (_err) {
|
|
2221
|
+
return undefined;
|
|
2222
|
+
}
|
|
2223
|
+
if (!parsed.host) {
|
|
2224
|
+
return undefined;
|
|
2225
|
+
}
|
|
2226
|
+
const segments = [];
|
|
2227
|
+
for (const segment of parsed.pathname.split("/").slice(1)) {
|
|
2228
|
+
const bare = segment.split(";", 1)[0];
|
|
2229
|
+
let decoded = bare;
|
|
2230
|
+
try {
|
|
2231
|
+
decoded = decodeURIComponent(bare);
|
|
2232
|
+
} catch (_err) {
|
|
2233
|
+
// A malformed escape is checked as written.
|
|
2234
|
+
}
|
|
2235
|
+
if (hasInlineCredential(decoded)) {
|
|
2236
|
+
break;
|
|
2237
|
+
}
|
|
2238
|
+
segments.push(bare);
|
|
2239
|
+
}
|
|
2240
|
+
let path = `/${segments.join("/")}`;
|
|
2241
|
+
for (const [host, routes] of GOLEM_SECRET_PATH_ROUTES) {
|
|
2242
|
+
if (parsed.hostname !== host && !parsed.hostname.endsWith(`.${host}`)) {
|
|
2243
|
+
continue;
|
|
2244
|
+
}
|
|
2245
|
+
const route = routes.find((prefix) => path.startsWith(prefix));
|
|
2246
|
+
if (route) {
|
|
2247
|
+
path = route.endsWith("/") ? route.slice(0, -1) : route;
|
|
2248
|
+
break;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
return encodeUriReference(`${parsed.protocol}//${parsed.host}${path}`);
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
/**
|
|
2255
|
+
* The URL a golem endpoint of an external service names, sanitised, with its
|
|
2256
|
+
* HTTP method. golem reports the full URL; a report without one has the
|
|
2257
|
+
* service's scheme and host put in front of the path.
|
|
2258
|
+
*
|
|
2259
|
+
* @param {object} endpoint Endpoint a golem external service reports
|
|
2260
|
+
* @param {object} service The service
|
|
2261
|
+
* @returns {{endpoint: string, method: (string|undefined)}|undefined} The URL
|
|
2262
|
+
*/
|
|
2263
|
+
function golemExternalEndpoint(endpoint, service) {
|
|
2264
|
+
let url = endpoint.url;
|
|
2265
|
+
if (typeof url !== "string" || !url) {
|
|
2266
|
+
const host = golemServiceHost(service.host);
|
|
2267
|
+
if (
|
|
2268
|
+
!host ||
|
|
2269
|
+
typeof service.scheme !== "string" ||
|
|
2270
|
+
typeof endpoint.path !== "string" ||
|
|
2271
|
+
!endpoint.path.startsWith("/")
|
|
2272
|
+
) {
|
|
2273
|
+
return undefined;
|
|
2274
|
+
}
|
|
2275
|
+
url = `${service.scheme}://${host}${endpoint.path}`;
|
|
2276
|
+
}
|
|
2277
|
+
const sanitized = sanitizeGolemEndpointUrl(url);
|
|
2278
|
+
return sanitized
|
|
2279
|
+
? { endpoint: sanitized, method: golemHttpMethod(endpoint.method) }
|
|
2280
|
+
: undefined;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
/**
|
|
2284
|
+
* Project the services and API endpoints of a golem report into evinse's
|
|
2285
|
+
* service map, so a Go `evinse`/`saasbom` run lists its services the way the
|
|
2286
|
+
* kosi (Kotlin) and dosai (.NET) collectors already do (#4498).
|
|
2287
|
+
*
|
|
2288
|
+
* golem rolls one service up per server it saw — a `gin` engine, a net/http
|
|
2289
|
+
* listener on `:8080` — and lists the routes registered on it, each with its
|
|
2290
|
+
* HTTP method and framework. Route paths are normalised to OpenAPI
|
|
2291
|
+
* placeholders and Go 1.22 ServeMux patterns are split into method, host and
|
|
2292
|
+
* path; entries that are not routes are skipped. It also rolls the outbound
|
|
2293
|
+
* hosts the source calls into services of `kind: "external"`; those carry
|
|
2294
|
+
* `x-trust-boundary: true`, since a host the application calls sits outside
|
|
2295
|
+
* its own trust boundary, and their URLs are sanitised before they become
|
|
2296
|
+
* endpoints. Every endpoint is percent-encoded to an RFC 3986 reference. The
|
|
2297
|
+
* map is keyed by the golem service id, which is unique per server, while
|
|
2298
|
+
* `name` carries the display name.
|
|
2299
|
+
*
|
|
2300
|
+
* HTTP methods are lower-cased to match the property values the OpenAPI flow
|
|
2301
|
+
* emits, so a consumer grouping by `cdx:service:httpMethod` sees one value
|
|
2302
|
+
* per method.
|
|
2303
|
+
*
|
|
2304
|
+
* @param {object|null} [golemReport={}] Parsed golem analysis report.
|
|
2305
|
+
* @param {Object} [servicesMap={}] Service map to populate.
|
|
2306
|
+
* @returns {Object} The service map
|
|
2307
|
+
*/
|
|
2308
|
+
export function collectGolemServices(golemReport = {}, servicesMap = {}) {
|
|
2309
|
+
const services = Array.isArray(golemReport?.services)
|
|
2310
|
+
? golemReport.services
|
|
2311
|
+
: [];
|
|
2312
|
+
for (const service of services) {
|
|
2313
|
+
const id = typeof service?.id === "string" ? service.id : undefined;
|
|
2314
|
+
const name = typeof service?.name === "string" ? service.name : undefined;
|
|
2315
|
+
if (!id && !name) {
|
|
2316
|
+
continue;
|
|
2317
|
+
}
|
|
2318
|
+
const definition = (servicesMap[id || name] ??= {
|
|
2319
|
+
name: name || id,
|
|
2320
|
+
bomRef: id || name,
|
|
2321
|
+
endpoints: new Set(),
|
|
2322
|
+
properties: [],
|
|
2323
|
+
});
|
|
2324
|
+
const external = service.kind === "external";
|
|
2325
|
+
if (external) {
|
|
2326
|
+
definition["x-trust-boundary"] = true;
|
|
2327
|
+
}
|
|
2328
|
+
const endpoints = Array.isArray(service.endpoints) ? service.endpoints : [];
|
|
2329
|
+
for (const endpoint of endpoints) {
|
|
2330
|
+
if (!endpoint || typeof endpoint !== "object") {
|
|
2331
|
+
continue;
|
|
2332
|
+
}
|
|
2333
|
+
const route = external
|
|
2334
|
+
? golemExternalEndpoint(endpoint, service)
|
|
2335
|
+
: golemServerRoute(endpoint, service);
|
|
2336
|
+
if (route) {
|
|
2337
|
+
definition.endpoints.add(route.endpoint);
|
|
2338
|
+
appendGolemServiceProperty(
|
|
2339
|
+
definition.properties,
|
|
2340
|
+
"cdx:service:httpMethod",
|
|
2341
|
+
route.method,
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
appendGolemServiceProperty(
|
|
2345
|
+
definition.properties,
|
|
2346
|
+
"cdx:golem:service:framework",
|
|
2347
|
+
endpoint.properties?.framework,
|
|
2348
|
+
);
|
|
2349
|
+
appendGolemServiceProperty(
|
|
2350
|
+
definition.properties,
|
|
2351
|
+
"cdx:golem:service:host",
|
|
2352
|
+
route?.host,
|
|
2353
|
+
);
|
|
2354
|
+
}
|
|
2355
|
+
appendGolemServiceProperty(
|
|
2356
|
+
definition.properties,
|
|
2357
|
+
"cdx:golem:service:kind",
|
|
2358
|
+
service.kind,
|
|
2359
|
+
);
|
|
2360
|
+
appendGolemServiceProperty(
|
|
2361
|
+
definition.properties,
|
|
2362
|
+
"cdx:golem:service:scheme",
|
|
2363
|
+
service.scheme,
|
|
2364
|
+
);
|
|
2365
|
+
appendGolemServiceProperty(
|
|
2366
|
+
definition.properties,
|
|
2367
|
+
"cdx:golem:service:host",
|
|
2368
|
+
golemServiceHost(service.host),
|
|
2369
|
+
);
|
|
2370
|
+
appendGolemServiceProperty(
|
|
2371
|
+
definition.properties,
|
|
2372
|
+
"cdx:golem:service:package",
|
|
2373
|
+
service.packagePath,
|
|
2374
|
+
);
|
|
2375
|
+
}
|
|
2376
|
+
return servicesMap;
|
|
2377
|
+
}
|