@cdxgen/cdxgen 13.5.0 → 13.7.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 +17 -10
- package/bin/audit.js +7 -4
- package/bin/cdxgen.js +260 -37
- package/bin/convert.js +146 -22
- package/bin/evinse.js +28 -3
- package/bin/repl.js +47 -3
- package/bin/sign.js +211 -34
- package/bin/tracebom.js +18 -6
- package/bin/validate.js +24 -3
- package/bin/verify.js +113 -5
- package/data/behavior-taxonomy.schema.json +1491 -0
- package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
- package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
- package/data/license-aliases.json +24 -2
- package/data/pqc-migration.json +239 -0
- package/data/remediations.json +176 -0
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -0
- package/data/rules/cbom-security.yaml +4 -3
- package/data/rules/package-integrity.yaml +28 -0
- package/data/rules/vscode-extensions.yaml +75 -0
- package/data/spdx-license-list.json +121 -7
- package/data/spdx-licenses.json +69 -0
- package/data/spdx.schema.json +845 -2
- package/lib/audit/index.js +25 -3
- package/lib/cli/bomAssembly.js +172 -4
- package/lib/cli/cliOptions.js +121 -17
- package/lib/cli/index.js +720 -902
- package/lib/cli/jsBom.js +156 -64
- package/lib/cli/jvmBom.js +602 -86
- package/lib/cli/managedBom.js +161 -19
- package/lib/cli/nativeBom.js +52 -5
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +34 -0
- package/lib/core/fs.js +138 -3
- package/lib/core/globPatterns.js +218 -0
- package/lib/core/httpClient.js +98 -1
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +100 -8
- package/lib/core/projectConfig.js +332 -25
- package/lib/core/toolRegistry.js +184 -0
- package/lib/ecosystems/cppEvidence.js +263 -106
- package/lib/ecosystems/cppIncludeResolver.js +16 -5
- package/lib/ecosystems/denoutils.js +2 -2
- package/lib/ecosystems/ecosystems.js +25 -10
- package/lib/ecosystems/gemHostProbe.js +301 -0
- package/lib/ecosystems/golem.js +3 -8
- package/lib/ecosystems/gradleutils.js +1909 -229
- package/lib/ecosystems/jsEvidence.js +90 -38
- package/lib/ecosystems/kosi.js +3 -5
- package/lib/ecosystems/npmVersionResolution.js +376 -0
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-misc.js +209 -20
- package/lib/ecosystems/parsers-python.js +178 -30
- package/lib/ecosystems/parsers-rust.js +117 -15
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/remote/huggingface.js +22 -0
- package/lib/ecosystems/rubyutils.js +538 -31
- package/lib/ecosystems/rusi.js +2 -4
- package/lib/ecosystems/scalacliutils.js +2 -0
- package/lib/ecosystems/scalasem.js +798 -152
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +536 -71
- package/lib/evinser/scalasem.js +5 -1
- package/lib/helpers/agenticToolutils.js +525 -94
- package/lib/helpers/bomSigner.js +43 -9
- package/lib/helpers/jssSigner.js +1276 -0
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/agentBlueprint.js +193 -0
- package/lib/inventory/agentToolNames.js +145 -0
- package/lib/inventory/aiCollector.js +153 -7
- package/lib/inventory/aiInventory.js +44 -8
- package/lib/inventory/aiProviders.js +324 -0
- package/lib/inventory/atomUtils.js +77 -0
- package/lib/inventory/bomUtils.js +8 -1
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
- package/lib/inventory/citations.js +31 -2
- package/lib/inventory/communityAiConfigParser.js +978 -80
- package/lib/inventory/cyclonedx20.js +152 -0
- package/lib/inventory/cyclonedx20Common.js +286 -0
- package/lib/inventory/cyclonedx20Derived.js +159 -0
- package/lib/inventory/cyclonedx20Parties.js +552 -0
- package/lib/inventory/cyclonedx20Read.js +768 -0
- package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
- package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
- package/lib/inventory/cyclonedx20ReadContext.js +380 -0
- package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
- package/lib/inventory/cyclonedx20ReadSections.js +599 -0
- package/lib/inventory/cyclonedx20Render.js +766 -0
- package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
- package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
- package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
- package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
- package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
- package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
- package/lib/inventory/cyclonedx20RenderSections.js +499 -0
- package/lib/inventory/cyclonedx20Tables.js +594 -0
- package/lib/inventory/deps.js +141 -13
- package/lib/inventory/dosai.js +375 -6
- package/lib/inventory/dosaiParsers.js +3 -6
- package/lib/inventory/dotnetRuntime.js +367 -0
- package/lib/inventory/ecosystemFormulationParsers.js +979 -0
- package/lib/inventory/envcontext.js +436 -48
- package/lib/inventory/evidenceUtils.js +13 -1
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +124 -14
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hbom.js +45 -6
- package/lib/inventory/hbomLoader.js +166 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -0
- package/lib/inventory/jvmLocalRepos.js +9 -2
- package/lib/inventory/jvmToolEnv.js +29 -0
- package/lib/inventory/licenseEnhancer.js +12 -0
- package/lib/inventory/mavenCentralMirror.js +534 -0
- package/lib/inventory/mcpConfigParser.js +5 -0
- package/lib/inventory/nodeBuildInfo.js +218 -0
- package/lib/inventory/nodeHostProbe.js +202 -0
- package/lib/inventory/occurrenceKey.js +90 -0
- package/lib/inventory/osPackageResolver.js +182 -0
- package/lib/inventory/phpBuildInfo.js +10 -59
- package/lib/inventory/phpHostProbe.js +32 -488
- package/lib/inventory/pqcMigration.js +426 -0
- package/lib/inventory/rubyBuildInfo.js +972 -0
- package/lib/inventory/rubyHostProbe.js +218 -0
- package/lib/inventory/rustFormulationParser.js +21 -10
- package/lib/inventory/scalaCoords.js +4 -3
- package/lib/inventory/scalaDedupe.js +440 -0
- package/lib/inventory/source.js +114 -8
- package/lib/managers/binary.js +337 -3
- package/lib/managers/docker.js +79 -0
- package/lib/parsers/huggingfaceManifest.js +9 -1
- package/lib/parsers/huggingfaceUtils.js +18 -0
- package/lib/parsers/kiroPermissions.js +166 -0
- package/lib/parsers/largeJson.js +28 -11
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +48 -35
- package/lib/stages/postgen/postgen.js +41 -1
- package/lib/stages/postgen/ruleEngine.js +13 -5
- package/lib/stages/postgen/specVersionCompat.js +76 -0
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +114 -57
- package/lib/validator/bomValidator.js +14 -2
- package/lib/validator/complianceEngine.js +30 -3
- package/lib/validator/complianceRules.js +1416 -4
- package/lib/validator/generated/validate-1.6.mjs +1 -1
- package/lib/validator/generated/validate-1.7.mjs +1 -1
- package/lib/validator/generated/validate-2.0.mjs +57554 -13851
- package/lib/validator/index.js +49 -8
- package/lib/validator/reporters/json.js +6 -0
- package/package.json +11 -10
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +6 -2
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +29 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +6 -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 +3 -18
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +15 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/boundedFile.d.ts +100 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -0
- package/types/lib/core/brotliProbe.d.ts +37 -0
- package/types/lib/core/brotliProbe.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +14 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +4 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/globPatterns.d.ts +70 -0
- package/types/lib/core/globPatterns.d.ts.map +1 -0
- package/types/lib/core/httpClient.d.ts +24 -0
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/lockPaths.d.ts +19 -0
- package/types/lib/core/lockPaths.d.ts.map +1 -0
- package/types/lib/core/paths.d.ts +9 -0
- package/types/lib/core/paths.d.ts.map +1 -1
- package/types/lib/core/processTree.d.ts +17 -4
- package/types/lib/core/processTree.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts +47 -8
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/toolRegistry.d.ts +36 -0
- package/types/lib/core/toolRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
- package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +294 -20
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +14 -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/npmVersionResolution.d.ts +119 -0
- package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +10 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +19 -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 +144 -12
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts +58 -16
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
- package/types/lib/ecosystems/venvProbe.d.ts +40 -0
- package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +120 -13
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/scalasem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +53 -19
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +20 -3
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/helpers/jssSigner.d.ts +288 -0
- package/types/lib/helpers/jssSigner.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +303 -19
- package/types/lib/helpers/vsixutils.d.ts.map +1 -1
- package/types/lib/inventory/agentBlueprint.d.ts +70 -0
- package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
- package/types/lib/inventory/agentToolNames.d.ts +51 -0
- package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/aiProviders.d.ts +85 -0
- package/types/lib/inventory/aiProviders.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +19 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/bomUtils.d.ts.map +1 -1
- package/types/lib/inventory/brotliDecoder.d.ts +58 -0
- package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
- package/types/lib/inventory/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
- package/types/lib/inventory/citations.d.ts +5 -0
- package/types/lib/inventory/citations.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/cyclonedx20.d.ts +99 -0
- package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +35 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +36 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
- package/types/lib/inventory/envcontext.d.ts +74 -12
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/gtfobins.d.ts.map +1 -1
- package/types/lib/inventory/hbom.d.ts +11 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomLoader.d.ts +54 -0
- package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
- package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
- package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
- package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
- package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
- package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/occurrenceKey.d.ts +52 -0
- package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +38 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
- package/types/lib/inventory/pqcMigration.d.ts +84 -0
- package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
- package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
- package/types/lib/inventory/scalaDedupe.d.ts +43 -0
- package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +25 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- 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/parsers/huggingfaceManifest.d.ts +10 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/kiroPermissions.d.ts +62 -0
- package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +4 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
- package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +7 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +12 -8
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts +5 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceEngine.d.ts +4 -4
- package/types/lib/validator/complianceEngine.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts +14 -2
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { Purl } from "@cdxgen/cdx-purl";
|
|
4
|
+
|
|
5
|
+
import { mergeDependencies, trimComponents } from "./depsUtils.js";
|
|
6
|
+
import { mavenPurl } from "./purl.js";
|
|
7
|
+
import { publishedArtifactId, scalaCoordinateOfPurl } from "./scalaCoords.js";
|
|
8
|
+
|
|
9
|
+
const COMPILER_VERSION_PROPERTY = "cdx:scala:compilerVersion";
|
|
10
|
+
|
|
11
|
+
// A jar scan that read the coordinates from the jar's own pom.properties
|
|
12
|
+
// records them with this confidence; a guess from the file name or the
|
|
13
|
+
// manifest records less.
|
|
14
|
+
const DESCRIPTOR_CONFIDENCE = 0.5;
|
|
15
|
+
|
|
16
|
+
function compilerVersionsOf(component) {
|
|
17
|
+
return (component.properties || [])
|
|
18
|
+
.filter((property) => property.name === COMPILER_VERSION_PROPERTY)
|
|
19
|
+
.map((property) => property.value)
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parsedPurl(component) {
|
|
24
|
+
try {
|
|
25
|
+
return Purl.parse(component?.purl || "");
|
|
26
|
+
} catch (_err) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// The jar names a component was read from, without the extension.
|
|
32
|
+
function jarStems(component) {
|
|
33
|
+
return (component.properties || [])
|
|
34
|
+
.filter((property) => property.name === "internal:SrcFile")
|
|
35
|
+
.map((property) => basename(String(property.value || "")))
|
|
36
|
+
.filter((name) => name.toLowerCase().endsWith(".jar"))
|
|
37
|
+
.map((name) => name.slice(0, -".jar".length));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isJarSourced(component) {
|
|
41
|
+
return jarStems(component).length > 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Whether a jar scan guessed the coordinates rather than reading them from the
|
|
45
|
+
// jar's Maven descriptor. A jar with a descriptor names its own artifact, even
|
|
46
|
+
// when that artifact is spelled `<group>.<name>` like a staged copy.
|
|
47
|
+
function hasGuessedIdentity(component) {
|
|
48
|
+
const identities = [component.evidence?.identity || []].flat();
|
|
49
|
+
return !identities.some(
|
|
50
|
+
(identity) =>
|
|
51
|
+
(identity?.confidence ?? 0) >= DESCRIPTOR_CONFIDENCE ||
|
|
52
|
+
(identity?.methods || []).some(
|
|
53
|
+
(method) => (method?.confidence ?? 0) >= DESCRIPTOR_CONFIDENCE,
|
|
54
|
+
),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// A classifier or a type other than jar names a different artifact of the same
|
|
59
|
+
// coordinates, which is not a duplicate.
|
|
60
|
+
function isPlainJar(component) {
|
|
61
|
+
const purl = parsedPurl(component);
|
|
62
|
+
if (!purl) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const qualifiers = purl.qualifiers || {};
|
|
66
|
+
return (
|
|
67
|
+
!qualifiers.classifier && (!qualifiers.type || qualifiers.type === "jar")
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The Scala binary versions a component was built for: the suffix its name
|
|
72
|
+
// keeps, or the compiler versions a build tool recorded. Empty for a library
|
|
73
|
+
// that is not a Scala artifact.
|
|
74
|
+
function binaryVersionsOf(component) {
|
|
75
|
+
const suffixVersion = scalaCoordinateOfPurl(component.purl)?.binaryVersion;
|
|
76
|
+
return new Set(
|
|
77
|
+
suffixVersion ? [suffixVersion] : compilerVersionsOf(component),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Two spellings name one artifact only when they were built for the same Scala
|
|
82
|
+
// binary version, or both for none. `kafka_2.12` and `kafka_2.13` share the
|
|
83
|
+
// normalized coordinates but are different artifacts.
|
|
84
|
+
function sameBinaryVersion(a, b) {
|
|
85
|
+
const left = binaryVersionsOf(a);
|
|
86
|
+
const right = binaryVersionsOf(b);
|
|
87
|
+
if (!left.size || !right.size) {
|
|
88
|
+
return !left.size && !right.size;
|
|
89
|
+
}
|
|
90
|
+
return [...left].some((version) => right.has(version));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Lower is better. A component a build tool reported outranks one a jar scan
|
|
94
|
+
// named; then the name without a `<group>.` prefix, then the purl that names
|
|
95
|
+
// its repository, then the shorter purl, then the lexicographically first, so
|
|
96
|
+
// the choice never depends on the order of the BOM.
|
|
97
|
+
function rank(component) {
|
|
98
|
+
return [
|
|
99
|
+
isJarSourced(component) ? 1 : 0,
|
|
100
|
+
component.name?.startsWith(`${component.group}.`) ? 1 : 0,
|
|
101
|
+
component.purl.includes("repository_url=") ? 0 : 1,
|
|
102
|
+
component.purl.length,
|
|
103
|
+
component.purl,
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isBetter(candidate, component) {
|
|
108
|
+
const a = rank(candidate);
|
|
109
|
+
const b = rank(component);
|
|
110
|
+
for (let i = 0; i < a.length; i++) {
|
|
111
|
+
if (a[i] !== b[i]) {
|
|
112
|
+
return a[i] < b[i];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function best(components) {
|
|
119
|
+
let chosen;
|
|
120
|
+
for (const component of components) {
|
|
121
|
+
if (!chosen || isBetter(component, chosen)) {
|
|
122
|
+
chosen = component;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return chosen;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function refOf(component) {
|
|
129
|
+
return component["bom-ref"] || component.purl;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// The file names sbt-native-packager gives a staged copy of a library,
|
|
133
|
+
// without the version: `<group>.<artifact>` with the artifact as published,
|
|
134
|
+
// once for each Scala binary version it was built for, and once as named,
|
|
135
|
+
// since a module of the build itself is staged without the suffix when it
|
|
136
|
+
// turns cross paths off.
|
|
137
|
+
function stagedStems(component) {
|
|
138
|
+
return [
|
|
139
|
+
...new Set([
|
|
140
|
+
`${component.group}.${component.name}`,
|
|
141
|
+
...compilerVersionsOf(component).map(
|
|
142
|
+
(version) =>
|
|
143
|
+
`${component.group}.${publishedArtifactId({
|
|
144
|
+
name: component.name,
|
|
145
|
+
properties: [{ name: COMPILER_VERSION_PROPERTY, value: version }],
|
|
146
|
+
})}`,
|
|
147
|
+
),
|
|
148
|
+
]),
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The listed libraries a staged file name can be a copy of, with the version
|
|
153
|
+
// the name carries: every split of the name at a hyphen whose left side is the
|
|
154
|
+
// staged stem of a library.
|
|
155
|
+
function stagedOwners(stem, ownersByStem) {
|
|
156
|
+
const found = [];
|
|
157
|
+
for (
|
|
158
|
+
let index = stem.indexOf("-");
|
|
159
|
+
index > 0;
|
|
160
|
+
index = stem.indexOf("-", index + 1)
|
|
161
|
+
) {
|
|
162
|
+
const owners = ownersByStem.get(stem.slice(0, index));
|
|
163
|
+
if (owners) {
|
|
164
|
+
found.push({ owners, version: stem.slice(index + 1) });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return found;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function rewriteReferences(dependencies, refMap) {
|
|
171
|
+
if (!refMap.size) {
|
|
172
|
+
return dependencies;
|
|
173
|
+
}
|
|
174
|
+
const rewritten = (dependencies || []).map((dependency) => {
|
|
175
|
+
const ref = refMap.get(dependency.ref) || dependency.ref;
|
|
176
|
+
return {
|
|
177
|
+
...dependency,
|
|
178
|
+
ref,
|
|
179
|
+
...(dependency.dependsOn
|
|
180
|
+
? {
|
|
181
|
+
dependsOn: [
|
|
182
|
+
...new Set(
|
|
183
|
+
dependency.dependsOn.map(
|
|
184
|
+
(dependsOn) => refMap.get(dependsOn) || dependsOn,
|
|
185
|
+
),
|
|
186
|
+
),
|
|
187
|
+
].filter((dependsOn) => dependsOn !== ref),
|
|
188
|
+
}
|
|
189
|
+
: {}),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
return mergeDependencies([], rewritten);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Collapse the components that stand for one Scala library.
|
|
197
|
+
*
|
|
198
|
+
* A build tool such as sbt reports a library as `group:name@version` with the
|
|
199
|
+
* Scala binary suffix moved into `cdx:scala:compilerVersion`. The jars a scan
|
|
200
|
+
* finds on disk name the same library again, and not in the same way: with the
|
|
201
|
+
* suffix (`cats-core_3`), without a repository, or, for a jar without a Maven
|
|
202
|
+
* descriptor, only as the staged file name `<group>.<artifact>-<version>.jar`,
|
|
203
|
+
* which a filename-based guess splits at the wrong dot
|
|
204
|
+
* (`pkg:maven/cpg2-protos/io.appthreat.cpg2-protos_3`). Each of those is merged
|
|
205
|
+
* into the component the build tool reported. A staged jar of another version
|
|
206
|
+
* of a reported library, such as a stale build, keeps its own component but is
|
|
207
|
+
* renamed to the coordinates the reported library has.
|
|
208
|
+
*
|
|
209
|
+
* Spellings of the same coordinates merge only when they were built for the
|
|
210
|
+
* same Scala binary version, so the `_2.12` and `_2.13` artifacts of one
|
|
211
|
+
* library stay two components. Components that no reported library accounts
|
|
212
|
+
* for are left as they are.
|
|
213
|
+
*
|
|
214
|
+
* @param {Object[]} components BOM components
|
|
215
|
+
* @param {Object[]} dependencies BOM dependencies
|
|
216
|
+
* @returns {{components: Object[], dependencies: Object[], purlMap: Map<string, string>}}
|
|
217
|
+
* The collapsed components, the dependencies with references to merged
|
|
218
|
+
* components pointed at the component that remains, and the purl each
|
|
219
|
+
* merged or renamed component now has, keyed by the purl it had
|
|
220
|
+
*/
|
|
221
|
+
export function mergeScalaDuplicateComponents(components, dependencies) {
|
|
222
|
+
const purlMap = new Map();
|
|
223
|
+
const candidates = (components || []).filter(
|
|
224
|
+
(component) =>
|
|
225
|
+
component?.purl?.startsWith("pkg:maven/") &&
|
|
226
|
+
isPlainJar(component) &&
|
|
227
|
+
component.group &&
|
|
228
|
+
component.version,
|
|
229
|
+
);
|
|
230
|
+
if (candidates.length < 2) {
|
|
231
|
+
return { components, dependencies, purlMap };
|
|
232
|
+
}
|
|
233
|
+
const byKey = new Map();
|
|
234
|
+
const ownersByStem = new Map();
|
|
235
|
+
for (const component of candidates) {
|
|
236
|
+
const key = scalaCoordinateOfPurl(component.purl)?.key;
|
|
237
|
+
if (key) {
|
|
238
|
+
byKey.set(key, [...(byKey.get(key) || []), component]);
|
|
239
|
+
}
|
|
240
|
+
for (const stem of stagedStems(component)) {
|
|
241
|
+
ownersByStem.set(stem, [...(ownersByStem.get(stem) || []), component]);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Which component each one is a duplicate of.
|
|
245
|
+
const duplicateOf = new Map();
|
|
246
|
+
const repoints = new Map();
|
|
247
|
+
for (const component of candidates) {
|
|
248
|
+
const matches = new Set();
|
|
249
|
+
// The same coordinates, spelled with or without the Scala suffix, with or
|
|
250
|
+
// without a repository.
|
|
251
|
+
for (const other of byKey.get(scalaCoordinateOfPurl(component.purl)?.key) ||
|
|
252
|
+
[]) {
|
|
253
|
+
if (other !== component && sameBinaryVersion(component, other)) {
|
|
254
|
+
matches.add(other);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// The staged file name of another listed library, whatever group and name
|
|
258
|
+
// the guess made of it. A jar that names itself in its Maven descriptor is
|
|
259
|
+
// the artifact it says it is.
|
|
260
|
+
if (isJarSourced(component) && hasGuessedIdentity(component)) {
|
|
261
|
+
for (const stem of jarStems(component)) {
|
|
262
|
+
for (const { owners, version } of stagedOwners(stem, ownersByStem)) {
|
|
263
|
+
const sameVersion = owners.filter(
|
|
264
|
+
(owner) => owner !== component && owner.version === version,
|
|
265
|
+
);
|
|
266
|
+
if (sameVersion.length) {
|
|
267
|
+
for (const owner of sameVersion) {
|
|
268
|
+
matches.add(owner);
|
|
269
|
+
}
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
// Another version of a library a build tool reported, when the guess
|
|
273
|
+
// agrees on it. A version that extends a known one
|
|
274
|
+
// (`4.1.100.Final-linux-x86_64`) can as well be that version with a
|
|
275
|
+
// classifier, so it is left alone.
|
|
276
|
+
const reportedOwners = owners.filter(
|
|
277
|
+
(owner) => owner !== component && !isJarSourced(owner),
|
|
278
|
+
);
|
|
279
|
+
const extendsKnown = owners.some((owner) =>
|
|
280
|
+
version.startsWith(`${owner.version}-`),
|
|
281
|
+
);
|
|
282
|
+
if (
|
|
283
|
+
reportedOwners.length &&
|
|
284
|
+
version === component.version &&
|
|
285
|
+
/^\d/.test(version) &&
|
|
286
|
+
!extendsKnown
|
|
287
|
+
) {
|
|
288
|
+
const owner = best(reportedOwners);
|
|
289
|
+
const known = repoints.get(component);
|
|
290
|
+
if (!known || isBetter(owner, known)) {
|
|
291
|
+
repoints.set(component, owner);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const target = best(
|
|
298
|
+
[...matches].filter((other) => isBetter(other, component)),
|
|
299
|
+
);
|
|
300
|
+
if (target) {
|
|
301
|
+
duplicateOf.set(component, target);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// A chain of merges never carries a component past one built for another
|
|
305
|
+
// Scala binary version.
|
|
306
|
+
const root = (component) => {
|
|
307
|
+
let current = component;
|
|
308
|
+
while (duplicateOf.has(current)) {
|
|
309
|
+
const next = duplicateOf.get(current);
|
|
310
|
+
if (current !== component && !sameBinaryVersion(component, next)) {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
current = next;
|
|
314
|
+
}
|
|
315
|
+
return current;
|
|
316
|
+
};
|
|
317
|
+
const refMap = new Map();
|
|
318
|
+
const survivors = new Set(components);
|
|
319
|
+
for (const [component] of duplicateOf) {
|
|
320
|
+
const target = root(component);
|
|
321
|
+
const duplicate = { ...component };
|
|
322
|
+
const targetRef = refOf(target);
|
|
323
|
+
if (refOf(component) && targetRef && refOf(component) !== targetRef) {
|
|
324
|
+
refMap.set(refOf(component), targetRef);
|
|
325
|
+
}
|
|
326
|
+
if (component.purl !== target.purl) {
|
|
327
|
+
purlMap.set(component.purl, target.purl);
|
|
328
|
+
}
|
|
329
|
+
duplicate.purl = target.purl;
|
|
330
|
+
duplicate["bom-ref"] = target["bom-ref"];
|
|
331
|
+
trimComponents([target, duplicate]);
|
|
332
|
+
survivors.delete(component);
|
|
333
|
+
}
|
|
334
|
+
for (const [component, owner] of repoints) {
|
|
335
|
+
if (survivors.has(component)) {
|
|
336
|
+
repointToAnchor(component, root(owner), refMap, purlMap);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// A component merged into one that was renamed afterwards follows it.
|
|
340
|
+
resolveChains(refMap);
|
|
341
|
+
resolveChains(purlMap);
|
|
342
|
+
// Repointed components can now spell the same coordinates twice.
|
|
343
|
+
return {
|
|
344
|
+
components: trimComponents([...survivors]),
|
|
345
|
+
dependencies: rewriteReferences(dependencies, refMap),
|
|
346
|
+
purlMap,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function resolveChains(map) {
|
|
351
|
+
for (const [key, value] of map) {
|
|
352
|
+
let current = value;
|
|
353
|
+
const seen = new Set([key]);
|
|
354
|
+
while (map.has(current) && !seen.has(current)) {
|
|
355
|
+
seen.add(current);
|
|
356
|
+
current = map.get(current);
|
|
357
|
+
}
|
|
358
|
+
map.set(key, current);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Give a staged jar of another version the group, name and purl shape the
|
|
363
|
+
// reported library has, so it is the same library at an older or newer version
|
|
364
|
+
// and not a differently named one.
|
|
365
|
+
function repointToAnchor(component, anchor, refMap, purlMap) {
|
|
366
|
+
const anchorPurl = parsedPurl(anchor);
|
|
367
|
+
if (!anchorPurl) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
// The repository the reported version came from says nothing about where
|
|
371
|
+
// this one came from.
|
|
372
|
+
const { repository_url: _repository, ...qualifiers } =
|
|
373
|
+
anchorPurl.qualifiers || {};
|
|
374
|
+
const purl = mavenPurl(
|
|
375
|
+
anchor.group,
|
|
376
|
+
anchorPurl.name,
|
|
377
|
+
component.version,
|
|
378
|
+
qualifiers,
|
|
379
|
+
);
|
|
380
|
+
if (!purl) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const previousRef = refOf(component);
|
|
384
|
+
purlMap.set(component.purl, purl);
|
|
385
|
+
component.group = anchor.group;
|
|
386
|
+
component.name = anchor.name;
|
|
387
|
+
component.purl = purl;
|
|
388
|
+
component["bom-ref"] = decodeURIComponent(purl);
|
|
389
|
+
const known = new Set(compilerVersionsOf(component));
|
|
390
|
+
component.properties = [
|
|
391
|
+
...(component.properties || []),
|
|
392
|
+
...(anchor.properties || []).filter(
|
|
393
|
+
(property) =>
|
|
394
|
+
property.name === COMPILER_VERSION_PROPERTY &&
|
|
395
|
+
!known.has(property.value),
|
|
396
|
+
),
|
|
397
|
+
];
|
|
398
|
+
if (previousRef && previousRef !== component["bom-ref"]) {
|
|
399
|
+
refMap.set(previousRef, component["bom-ref"]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Key the jar namespace map by the purls the components have after
|
|
405
|
+
* {@link mergeScalaDuplicateComponents}, so a merged or renamed jar still
|
|
406
|
+
* finds its namespaces. A purl that already has an entry keeps it, with the
|
|
407
|
+
* namespaces of the merged entry added.
|
|
408
|
+
*
|
|
409
|
+
* @param {Object} mapping Jar namespace map keyed by purl
|
|
410
|
+
* @param {Map<string, string>} purlMap New purl keyed by old purl
|
|
411
|
+
* @returns {Object} The re-keyed map
|
|
412
|
+
*/
|
|
413
|
+
export function rekeyNamespaceMapping(mapping, purlMap) {
|
|
414
|
+
if (!mapping || !purlMap?.size) {
|
|
415
|
+
return mapping;
|
|
416
|
+
}
|
|
417
|
+
const rekeyed = { ...mapping };
|
|
418
|
+
for (const [oldPurl, newPurl] of purlMap) {
|
|
419
|
+
const entry = rekeyed[oldPurl];
|
|
420
|
+
if (!entry || oldPurl === newPurl) {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
delete rekeyed[oldPurl];
|
|
424
|
+
const existing = rekeyed[newPurl];
|
|
425
|
+
if (!existing) {
|
|
426
|
+
rekeyed[newPurl] = entry;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
rekeyed[newPurl] = {
|
|
430
|
+
...existing,
|
|
431
|
+
namespaces: [
|
|
432
|
+
...new Set([
|
|
433
|
+
...(existing.namespaces || []),
|
|
434
|
+
...(entry.namespaces || []),
|
|
435
|
+
]),
|
|
436
|
+
],
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
return rekeyed;
|
|
440
|
+
}
|
package/lib/inventory/source.js
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
prefetchedResponse,
|
|
31
31
|
prefetchJson,
|
|
32
32
|
} from "./fetchBatch.js";
|
|
33
|
+
import { mavenCentralFetchUrl } from "./mavenCentralMirror.js";
|
|
33
34
|
import { canonicalPurlFromLooseString } from "./purl.js";
|
|
34
35
|
|
|
35
36
|
/** Raised when a caller omits a registry helper that the purl type requires. */
|
|
@@ -105,9 +106,48 @@ function inferGitOperation(args = []) {
|
|
|
105
106
|
return "command";
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
let gitRedirectRefusalReported = false;
|
|
110
|
+
|
|
111
|
+
registerRunReset("source:git-redirect-refusal", () => {
|
|
112
|
+
gitRedirectRefusalReported = false;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Determine whether git must refuse HTTP redirects. A git host allowlist is
|
|
117
|
+
* checked against the URL cdxgen is given, and git would otherwise follow
|
|
118
|
+
* the server's redirect on its first request to a host the allowlist never
|
|
119
|
+
* saw. Secure mode refuses redirects in the HTTP client too.
|
|
120
|
+
*
|
|
121
|
+
* @returns {boolean} true when a git host allowlist or secure mode applies
|
|
122
|
+
*/
|
|
123
|
+
export function gitRedirectsRefused() {
|
|
124
|
+
return isSecureMode || getAllowedHosts().length > 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Determine whether a failed git command stopped at an HTTP redirect.
|
|
129
|
+
*
|
|
130
|
+
* @param {string|undefined} stderr git error output
|
|
131
|
+
* @returns {boolean} true when git reports a 3xx response
|
|
132
|
+
*/
|
|
133
|
+
function isGitRedirectRefusal(stderr) {
|
|
134
|
+
if (typeof stderr !== "string") {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
const marker = "The requested URL returned error: 30";
|
|
138
|
+
const index = stderr.indexOf(marker);
|
|
139
|
+
return index !== -1 && "12378".includes(stderr[index + marker.length]);
|
|
140
|
+
}
|
|
141
|
+
|
|
108
142
|
/**
|
|
109
143
|
* Execute git with hardened defaults.
|
|
110
144
|
*
|
|
145
|
+
* Every call runs with command-scope settings that keep the repository from
|
|
146
|
+
* running fsmonitor hooks or being opened as a bare repository. While a git
|
|
147
|
+
* host allowlist or secure mode applies, git also refuses HTTP redirects, so
|
|
148
|
+
* an allowed host cannot send a clone or ls-remote to a host the allowlist
|
|
149
|
+
* does not name.
|
|
150
|
+
*
|
|
111
151
|
* @param {string[]} args git arguments
|
|
112
152
|
* @param {Object} options command options
|
|
113
153
|
* @param {string|undefined} options.cwd working directory
|
|
@@ -119,15 +159,23 @@ function inferGitOperation(args = []) {
|
|
|
119
159
|
export function hardenedGitCommand(args, options = {}) {
|
|
120
160
|
const gitOperation = inferGitOperation(args);
|
|
121
161
|
const gitAllowProtocol = getGitAllowProtocol();
|
|
162
|
+
const refuseRedirects = gitRedirectsRefused();
|
|
163
|
+
const commandConfig = [
|
|
164
|
+
["core.fsmonitor", "false"],
|
|
165
|
+
["safe.bareRepository", "explicit"],
|
|
166
|
+
];
|
|
167
|
+
if (refuseRedirects) {
|
|
168
|
+
commandConfig.push(["http.followRedirects", "false"]);
|
|
169
|
+
}
|
|
122
170
|
const envConfigs = {
|
|
123
|
-
GIT_CONFIG_COUNT:
|
|
124
|
-
GIT_CONFIG_KEY_0: "core.fsmonitor",
|
|
125
|
-
GIT_CONFIG_VALUE_0: "false",
|
|
126
|
-
GIT_CONFIG_KEY_1: "safe.bareRepository",
|
|
127
|
-
GIT_CONFIG_VALUE_1: "explicit",
|
|
171
|
+
GIT_CONFIG_COUNT: String(commandConfig.length),
|
|
128
172
|
GIT_NO_LAZY_FETCH: "1",
|
|
129
173
|
GIT_TERMINAL_PROMPT: "0",
|
|
130
174
|
};
|
|
175
|
+
commandConfig.forEach(([key, value], index) => {
|
|
176
|
+
envConfigs[`GIT_CONFIG_KEY_${index}`] = key;
|
|
177
|
+
envConfigs[`GIT_CONFIG_VALUE_${index}`] = value;
|
|
178
|
+
});
|
|
131
179
|
if (options.noRepository) {
|
|
132
180
|
// ls-remote works without a repository. Pointing GIT_DIR at a path that
|
|
133
181
|
// does not exist stops git from discovering one in the working directory.
|
|
@@ -146,7 +194,7 @@ export function hardenedGitCommand(args, options = {}) {
|
|
|
146
194
|
...envConfigs,
|
|
147
195
|
GIT_ALLOW_PROTOCOL: gitAllowProtocol,
|
|
148
196
|
};
|
|
149
|
-
|
|
197
|
+
const result = safeSpawnSync("git", args, {
|
|
150
198
|
cdxgenActivity: {
|
|
151
199
|
blockedReason: `Dry run mode blocks git ${gitOperation} operations.`,
|
|
152
200
|
gitOperation,
|
|
@@ -159,6 +207,18 @@ export function hardenedGitCommand(args, options = {}) {
|
|
|
159
207
|
cwd: options.cwd,
|
|
160
208
|
env,
|
|
161
209
|
});
|
|
210
|
+
if (
|
|
211
|
+
refuseRedirects &&
|
|
212
|
+
!gitRedirectRefusalReported &&
|
|
213
|
+
result?.status !== 0 &&
|
|
214
|
+
isGitRedirectRefusal(result?.stderr)
|
|
215
|
+
) {
|
|
216
|
+
gitRedirectRefusalReported = true;
|
|
217
|
+
console.warn(
|
|
218
|
+
"A Git server answered with a redirect, which cdxgen does not follow while CDXGEN_GIT_ALLOWED_HOSTS, CDXGEN_SERVER_ALLOWED_HOSTS or secure mode is in effect. Use the repository's current URL instead.",
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
162
222
|
}
|
|
163
223
|
|
|
164
224
|
function normalizeTagName(tagName) {
|
|
@@ -795,6 +855,48 @@ export function getGitRemoteHost(remote) {
|
|
|
795
855
|
return host.toLowerCase();
|
|
796
856
|
}
|
|
797
857
|
|
|
858
|
+
/**
|
|
859
|
+
* Remove credentials from git output before it is logged. Every URL in the
|
|
860
|
+
* text loses its user name and password, which git copies into its error
|
|
861
|
+
* messages from the remote it was given.
|
|
862
|
+
*
|
|
863
|
+
* @param {string|undefined} text Output of a git command
|
|
864
|
+
* @param {string|undefined} repoUrl Remote the command was given
|
|
865
|
+
* @returns {string|undefined} Output with credentials replaced by ***
|
|
866
|
+
*/
|
|
867
|
+
export function redactGitOutput(text, repoUrl) {
|
|
868
|
+
if (!text || typeof text !== "string") {
|
|
869
|
+
return text;
|
|
870
|
+
}
|
|
871
|
+
let redacted = text;
|
|
872
|
+
if (repoUrl && typeof repoUrl === "string") {
|
|
873
|
+
redacted = redacted.split(repoUrl).join(sanitizeRemoteUrlForLogs(repoUrl));
|
|
874
|
+
}
|
|
875
|
+
let from = 0;
|
|
876
|
+
while (from < redacted.length) {
|
|
877
|
+
const schemeEnd = redacted.indexOf("://", from);
|
|
878
|
+
if (schemeEnd === -1) {
|
|
879
|
+
break;
|
|
880
|
+
}
|
|
881
|
+
const authorityStart = schemeEnd + 3;
|
|
882
|
+
let authorityEnd = authorityStart;
|
|
883
|
+
while (
|
|
884
|
+
authorityEnd < redacted.length &&
|
|
885
|
+
!"/?#'\"<> \t\r\n".includes(redacted[authorityEnd])
|
|
886
|
+
) {
|
|
887
|
+
authorityEnd++;
|
|
888
|
+
}
|
|
889
|
+
const at = redacted.lastIndexOf("@", authorityEnd - 1);
|
|
890
|
+
if (at >= authorityStart) {
|
|
891
|
+
redacted = `${redacted.slice(0, authorityStart)}***:***${redacted.slice(at)}`;
|
|
892
|
+
from = authorityStart + "***:***".length;
|
|
893
|
+
} else {
|
|
894
|
+
from = authorityEnd;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return redacted;
|
|
898
|
+
}
|
|
899
|
+
|
|
798
900
|
/**
|
|
799
901
|
* Clone a git repository into a temporary directory.
|
|
800
902
|
*
|
|
@@ -859,8 +961,11 @@ export function gitClone(repoUrl, branch = null) {
|
|
|
859
961
|
`Cloning Repo${branch ? ` with branch ${branch}` : ""} to ${tempDir}`,
|
|
860
962
|
);
|
|
861
963
|
const result = hardenedGitCommand(gitArgs);
|
|
862
|
-
if (result.status !== 0) {
|
|
863
|
-
console.error(result.stderr);
|
|
964
|
+
if (result.status !== 0 || result.error) {
|
|
965
|
+
console.error(redactGitOutput(result.stderr, repoUrl));
|
|
966
|
+
// The directory was created by this call, so it is always safe to remove.
|
|
967
|
+
safeRmSync(tempDir, { recursive: true, force: true });
|
|
968
|
+
throw new Error("Git clone failed.");
|
|
864
969
|
}
|
|
865
970
|
|
|
866
971
|
return tempDir;
|
|
@@ -1588,6 +1693,7 @@ export async function resolveGitUrlFromPurl(purlString, helpers = {}) {
|
|
|
1588
1693
|
break;
|
|
1589
1694
|
}
|
|
1590
1695
|
const mavenCentralUrl =
|
|
1696
|
+
mavenCentralFetchUrl() ||
|
|
1591
1697
|
readEnvironmentVariable("MAVEN_CENTRAL_URL") ||
|
|
1592
1698
|
"https://repo1.maven.org/maven2/";
|
|
1593
1699
|
registry = mavenCentralUrl.endsWith("/")
|