@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
|
@@ -11,6 +11,11 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
|
11
11
|
import { safeExistsSync } from "../core/fs.js";
|
|
12
12
|
import { createOccurrenceEvidence } from "../inventory/evidenceUtils.js";
|
|
13
13
|
import { isPkgTypeOnlyImport, setNpmTypeOnlyProperty } from "./npmutils.js";
|
|
14
|
+
import {
|
|
15
|
+
createNpmVersionChooser,
|
|
16
|
+
npmPackageName,
|
|
17
|
+
projectRootRefs,
|
|
18
|
+
} from "./npmVersionResolution.js";
|
|
14
19
|
import { parsePkgJson } from "./parsers-js.js";
|
|
15
20
|
|
|
16
21
|
const NPM_BIN_IMPORT_PREFIX = "cdx:npm:bin/";
|
|
@@ -69,10 +74,30 @@ function getNpmBinCommandEvidenceKeys(allImports, pkg) {
|
|
|
69
74
|
);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Credit a package with the `cdx:npm:bin/<command>` evidence of its bin
|
|
79
|
+
* commands, limited to the files that resolve to this version when several are
|
|
80
|
+
* installed.
|
|
81
|
+
*
|
|
82
|
+
* @param {object} pkg Package component, changed in place
|
|
83
|
+
* @param {object} allImports Map of import specifier to usage evidence objects
|
|
84
|
+
* @param {object} versionChooser Chooser from `createNpmVersionChooser`
|
|
85
|
+
* @returns {boolean} Whether a bin command of this version is used
|
|
86
|
+
*/
|
|
87
|
+
function applyNpmBinCommandEvidence(pkg, allImports, versionChooser) {
|
|
73
88
|
const binEvidenceKeys = getNpmBinCommandEvidenceKeys(allImports, pkg);
|
|
74
89
|
if (!binEvidenceKeys.length) {
|
|
75
|
-
return;
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const binEvidences = binEvidenceKeys.flatMap((key) =>
|
|
93
|
+
Array.from(allImports[key] || []).filter(
|
|
94
|
+
(evidence) =>
|
|
95
|
+
!versionChooser.isAmbiguous(pkg) ||
|
|
96
|
+
(evidence?.fileName && versionChooser.allows(pkg, evidence.fileName)),
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
if (versionChooser.isAmbiguous(pkg) && !binEvidences.length) {
|
|
100
|
+
return false;
|
|
76
101
|
}
|
|
77
102
|
pkg.scope = "required";
|
|
78
103
|
const seenOccurrenceLocations = new Set(
|
|
@@ -81,38 +106,26 @@ function applyNpmBinCommandEvidence(pkg, allImports) {
|
|
|
81
106
|
`${occurrence.location}${occurrence.line ? `#${occurrence.line}` : ""}`,
|
|
82
107
|
),
|
|
83
108
|
);
|
|
84
|
-
for (const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
pkg.evidence = pkg.evidence || {};
|
|
100
|
-
pkg.evidence.occurrences = pkg.evidence.occurrences || [];
|
|
101
|
-
pkg.evidence.occurrences.push(occurrence);
|
|
102
|
-
seenOccurrenceLocations.add(occurrenceLocation);
|
|
109
|
+
for (const evidence of binEvidences) {
|
|
110
|
+
if (!evidence?.fileName) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const occurrence = createOccurrenceEvidence(evidence.fileName, {
|
|
114
|
+
...(evidence.lineNumber ? { line: evidence.lineNumber } : {}),
|
|
115
|
+
});
|
|
116
|
+
if (!occurrence) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const occurrenceLocation = `${occurrence.location}${occurrence.line ? `#${occurrence.line}` : ""}`;
|
|
120
|
+
if (seenOccurrenceLocations.has(occurrenceLocation)) {
|
|
121
|
+
continue;
|
|
103
122
|
}
|
|
123
|
+
pkg.evidence = pkg.evidence || {};
|
|
124
|
+
pkg.evidence.occurrences = pkg.evidence.occurrences || [];
|
|
125
|
+
pkg.evidence.occurrences.push(occurrence);
|
|
126
|
+
seenOccurrenceLocations.add(occurrenceLocation);
|
|
104
127
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Full npm package name (`@scope/name` or `name`) of a component or manifest.
|
|
109
|
-
*
|
|
110
|
-
* @param {{group?: string, name?: string}} pkg Component or parsed manifest.
|
|
111
|
-
* @returns {string} The package name npm installs it under.
|
|
112
|
-
*/
|
|
113
|
-
function npmPackageName({ group, name }) {
|
|
114
|
-
const scope = group?.startsWith("@") ? group.substring(1) : group;
|
|
115
|
-
return scope ? `@${scope}/${name}` : name;
|
|
128
|
+
return true;
|
|
116
129
|
}
|
|
117
130
|
|
|
118
131
|
/**
|
|
@@ -194,11 +207,20 @@ async function fillMissingMetadataFromLocalNodeModules(pkg) {
|
|
|
194
207
|
* from configuration, bundler entrypoints, stylesheets, or plugins, so a
|
|
195
208
|
* missing import is not evidence that a package is optional.
|
|
196
209
|
*
|
|
210
|
+
* When the lock file holds several versions of a package, an import is
|
|
211
|
+
* evidence only for the version the importing file resolves to (see
|
|
212
|
+
* `createNpmVersionChooser`), so a copy nested under another package is not
|
|
213
|
+
* credited with, or promoted to required by, the project's own imports.
|
|
214
|
+
*
|
|
197
215
|
* @param {Array<object>} pkgList Package components to enrich.
|
|
198
216
|
* @param {object} allImports Map of import specifier to usage evidence objects.
|
|
199
217
|
* @param {object} allExports Map of export specifier to export evidence objects.
|
|
200
218
|
* @param {boolean} deep When true, fill in missing description/author/license
|
|
201
219
|
* metadata from the local node_modules copy of each package.
|
|
220
|
+
* @param {object} [context] Project facts that pick the version a file uses.
|
|
221
|
+
* @param {string} [context.srcDir] Directory the evidence file names are relative to.
|
|
222
|
+
* @param {Array<object>} [context.dependencies] Dependency tree of the packages.
|
|
223
|
+
* @param {object} [context.parentComponent] Project component, with any workspace members as its components.
|
|
202
224
|
* @returns {Promise<Array<object>>} The enriched package list (same reference as pkgList).
|
|
203
225
|
*/
|
|
204
226
|
export async function addEvidenceForImports(
|
|
@@ -206,9 +228,15 @@ export async function addEvidenceForImports(
|
|
|
206
228
|
allImports,
|
|
207
229
|
allExports,
|
|
208
230
|
deep,
|
|
231
|
+
context = {},
|
|
209
232
|
) {
|
|
210
233
|
const impPkgs = Object.keys(allImports);
|
|
211
234
|
const exportedPkgs = Object.keys(allExports);
|
|
235
|
+
const versionChooser = createNpmVersionChooser(pkgList, {
|
|
236
|
+
srcDir: context.srcDir,
|
|
237
|
+
dependencies: context.dependencies,
|
|
238
|
+
rootRefs: projectRootRefs(context.parentComponent),
|
|
239
|
+
});
|
|
212
240
|
for (const pkg of pkgList) {
|
|
213
241
|
const { group, name } = pkg;
|
|
214
242
|
// Evidence belonging to a type must be associated with the package
|
|
@@ -238,6 +266,7 @@ export async function addEvidenceForImports(
|
|
|
238
266
|
aliases.push(jsrSpecifier);
|
|
239
267
|
}
|
|
240
268
|
}
|
|
269
|
+
let creditedByImports = false;
|
|
241
270
|
for (const alias of aliases) {
|
|
242
271
|
const isWasmAlias = /\.wasm([?#].*)?$/i.test(alias);
|
|
243
272
|
const all_includes = impPkgs.filter(
|
|
@@ -293,8 +322,24 @@ export async function addEvidenceForImports(
|
|
|
293
322
|
});
|
|
294
323
|
}
|
|
295
324
|
}
|
|
296
|
-
// Identify all the imported modules of a component
|
|
297
|
-
|
|
325
|
+
// Identify all the imported modules of a component. Of several
|
|
326
|
+
// installed versions, only the one a file resolves to takes its imports.
|
|
327
|
+
const isAmbiguous = versionChooser.isAmbiguous(pkg);
|
|
328
|
+
const evidencesOf = (subevidence) => {
|
|
329
|
+
const evidences = Array.from(allImports[subevidence] || []);
|
|
330
|
+
return isAmbiguous
|
|
331
|
+
? evidences.filter(
|
|
332
|
+
(evidence) =>
|
|
333
|
+
evidence?.fileName &&
|
|
334
|
+
versionChooser.allows(pkg, evidence.fileName),
|
|
335
|
+
)
|
|
336
|
+
: evidences;
|
|
337
|
+
};
|
|
338
|
+
const usedIncludes = isAmbiguous
|
|
339
|
+
? all_includes.filter((subevidence) => evidencesOf(subevidence).length)
|
|
340
|
+
: all_includes;
|
|
341
|
+
if ((!isAmbiguous && impPkgs.includes(alias)) || usedIncludes.length) {
|
|
342
|
+
creditedByImports = true;
|
|
298
343
|
let importedModules = new Set();
|
|
299
344
|
let wasmExportedModules = new Set();
|
|
300
345
|
const seenOccurrenceLocations = new Set();
|
|
@@ -311,9 +356,8 @@ export async function addEvidenceForImports(
|
|
|
311
356
|
} else {
|
|
312
357
|
pkg.scope = "required";
|
|
313
358
|
}
|
|
314
|
-
for (const subevidence of
|
|
315
|
-
const
|
|
316
|
-
for (const evidence of evidences) {
|
|
359
|
+
for (const subevidence of usedIncludes) {
|
|
360
|
+
for (const evidence of evidencesOf(subevidence)) {
|
|
317
361
|
if (evidence && Object.keys(evidence).length && evidence.fileName) {
|
|
318
362
|
const occurrence = createOccurrenceEvidence(evidence.fileName, {
|
|
319
363
|
...(evidence.lineNumber ? { line: evidence.lineNumber } : {}),
|
|
@@ -367,8 +411,16 @@ export async function addEvidenceForImports(
|
|
|
367
411
|
break;
|
|
368
412
|
}
|
|
369
413
|
} // for alias
|
|
414
|
+
if (applyNpmBinCommandEvidence(pkg, allImports, versionChooser)) {
|
|
415
|
+
creditedByImports = true;
|
|
416
|
+
}
|
|
417
|
+
// Every import of this package's name resolves to another installed
|
|
418
|
+
// version. buildBomNSData reads the flag so that matching by name alone
|
|
419
|
+
// does not promote this copy to required.
|
|
420
|
+
if (versionChooser.isAmbiguous(pkg) && !creditedByImports) {
|
|
421
|
+
pkg._npmImportsResolveElsewhere = true;
|
|
422
|
+
}
|
|
370
423
|
// Trim the properties
|
|
371
|
-
applyNpmBinCommandEvidence(pkg, allImports);
|
|
372
424
|
pkg.properties = pkg.properties.filter(
|
|
373
425
|
(p) => p.name !== "internal:LocalNodeModulesPath",
|
|
374
426
|
);
|
package/lib/ecosystems/kosi.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
safeSpawnSync,
|
|
12
12
|
} from "../core/fs.js";
|
|
13
13
|
import { resolvePluginSourceDir } from "../core/pluginRun.js";
|
|
14
|
+
import { occurrenceLocationKey } from "../inventory/occurrenceKey.js";
|
|
14
15
|
import { resolvePluginBinary } from "../inventory/plugins.js";
|
|
15
16
|
import {
|
|
16
17
|
parsePurlLoose,
|
|
@@ -331,10 +332,7 @@ function positionLocation(position) {
|
|
|
331
332
|
if (!position?.filename) {
|
|
332
333
|
return undefined;
|
|
333
334
|
}
|
|
334
|
-
|
|
335
|
-
return `${position.filename}#${position.line}`;
|
|
336
|
-
}
|
|
337
|
-
return position.filename;
|
|
335
|
+
return occurrenceLocationKey(position.filename, position.line);
|
|
338
336
|
}
|
|
339
337
|
|
|
340
338
|
function purlWithoutVersion(purl) {
|
|
@@ -843,7 +841,7 @@ function addSecuritySignalEvidence(
|
|
|
843
841
|
// so the filePath is the only location it has.
|
|
844
842
|
const location =
|
|
845
843
|
positionLocation(signal.position) ||
|
|
846
|
-
(signal.filePath
|
|
844
|
+
occurrenceLocationKey(signal.filePath);
|
|
847
845
|
if (location) {
|
|
848
846
|
addSetValue(purlLocationMap, purl, location);
|
|
849
847
|
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// Which installed version of an npm package a source file uses.
|
|
2
|
+
//
|
|
3
|
+
// A lock file often holds several versions of one package: the one the project
|
|
4
|
+
// asked for, hoisted to the top-level node_modules, and older or newer copies
|
|
5
|
+
// nested under the packages that need them. A file of the project loads the
|
|
6
|
+
// copy Node.js finds first when it walks up from the file's directory, so only
|
|
7
|
+
// that version is used there. Crediting the file's imports to every version
|
|
8
|
+
// would mark the nested copies as used by the project.
|
|
9
|
+
|
|
10
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
11
|
+
|
|
12
|
+
import { Purl } from "@cdxgen/cdx-purl";
|
|
13
|
+
|
|
14
|
+
import { occurrenceKeyLocation } from "../inventory/occurrenceKey.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Full npm package name (`@scope/name` or `name`) of a component or manifest.
|
|
18
|
+
*
|
|
19
|
+
* @param {{group?: string, name?: string}} pkg Component or parsed manifest
|
|
20
|
+
* @returns {string} The package name npm installs it under
|
|
21
|
+
*/
|
|
22
|
+
export function npmPackageName({ group, name }) {
|
|
23
|
+
const scope = group?.startsWith("@") ? group.substring(1) : group;
|
|
24
|
+
return scope ? `@${scope}/${name}` : name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Full npm package name of an npm purl.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} purl Package URL
|
|
31
|
+
* @returns {string|undefined} `@scope/name` or `name`; undefined for a purl that is not npm or does not parse
|
|
32
|
+
*/
|
|
33
|
+
export function npmPackageNameOfPurl(purl) {
|
|
34
|
+
try {
|
|
35
|
+
const purlObj = Purl.parse(purl);
|
|
36
|
+
if (purlObj.type !== "npm") {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return purlObj.namespace
|
|
40
|
+
? `${purlObj.namespace}/${purlObj.name}`
|
|
41
|
+
: purlObj.name;
|
|
42
|
+
} catch (_e) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Directory a package-lock component is installed in.
|
|
49
|
+
*
|
|
50
|
+
* `internal:LocalNodeModulesPath` holds the arborist location, relative to the
|
|
51
|
+
* root of the tree: `node_modules/a/node_modules/b` for a nested copy and
|
|
52
|
+
* `packages/web/node_modules/b` for a copy under a workspace member. The
|
|
53
|
+
* component's `internal:SrcFile` names a `package-lock.json` in the directory
|
|
54
|
+
* that holds the location's first `node_modules` segment, so the install
|
|
55
|
+
* directory is that location's `node_modules/...` part resolved against the
|
|
56
|
+
* SrcFile's directory. A location without a `node_modules` segment (a
|
|
57
|
+
* workspace member itself) and an absolute location (pnpm metadata found by
|
|
58
|
+
* searching node_modules) name no install a file resolves to.
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} pkg Package component
|
|
61
|
+
* @returns {string|undefined} Absolute install directory
|
|
62
|
+
*/
|
|
63
|
+
export function npmInstallDirectory(pkg) {
|
|
64
|
+
const properties = Array.isArray(pkg?.properties) ? pkg.properties : [];
|
|
65
|
+
const location = properties.find(
|
|
66
|
+
(p) => p.name === "internal:LocalNodeModulesPath",
|
|
67
|
+
)?.value;
|
|
68
|
+
const srcFile = properties.find((p) => p.name === "internal:SrcFile")?.value;
|
|
69
|
+
if (!location || !srcFile || isAbsolute(location)) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const normalized = `${location}`.replaceAll("\\", "/");
|
|
73
|
+
let start = -1;
|
|
74
|
+
if (normalized.startsWith("node_modules/")) {
|
|
75
|
+
start = 0;
|
|
76
|
+
} else {
|
|
77
|
+
const nested = normalized.indexOf("/node_modules/");
|
|
78
|
+
start = nested >= 0 ? nested + 1 : -1;
|
|
79
|
+
}
|
|
80
|
+
if (start < 0) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
return resolve(dirname(srcFile), normalized.slice(start));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* References the given root components depend on directly.
|
|
88
|
+
*
|
|
89
|
+
* @param {Array<{ref: string, dependsOn?: string[]}>} dependencies Dependency tree
|
|
90
|
+
* @param {Iterable<string>} rootRefs bom-refs of the project and its workspace members
|
|
91
|
+
* @returns {Set<string>} bom-refs of their direct dependencies
|
|
92
|
+
*/
|
|
93
|
+
export function directDependencyRefs(dependencies, rootRefs) {
|
|
94
|
+
const roots = new Set(rootRefs || []);
|
|
95
|
+
const direct = new Set();
|
|
96
|
+
for (const dependency of dependencies || []) {
|
|
97
|
+
if (roots.has(dependency?.ref)) {
|
|
98
|
+
for (const ref of dependency.dependsOn || []) {
|
|
99
|
+
direct.add(ref);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return direct;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The direct dependencies that apply to a project file, nearest first.
|
|
108
|
+
*
|
|
109
|
+
* A file inside an npm workspace member (a component with
|
|
110
|
+
* `cdx:npm:isWorkspace=true` and its directory in `cdx:npm:resolvedPath`,
|
|
111
|
+
* relative to the project) depends first on what that member declares, then
|
|
112
|
+
* on what the project root declares, since Node.js finds the root's hoisted
|
|
113
|
+
* copies from inside a member too. Any other file depends on the root's.
|
|
114
|
+
*
|
|
115
|
+
* @param {Object[]} components Components or package list
|
|
116
|
+
* @param {Object[]} dependencies Dependency tree
|
|
117
|
+
* @param {string[]} rootRefs bom-refs of the project and the sub-components it lists
|
|
118
|
+
* @returns {function(string): Set<string>[]} Lookup from a file name, relative to the project, to sets of bom-refs
|
|
119
|
+
*/
|
|
120
|
+
export function createDirectDependencyLookup(
|
|
121
|
+
components,
|
|
122
|
+
dependencies,
|
|
123
|
+
rootRefs,
|
|
124
|
+
) {
|
|
125
|
+
const rootDirect = directDependencyRefs(dependencies, rootRefs);
|
|
126
|
+
const members = [];
|
|
127
|
+
for (const comp of components || []) {
|
|
128
|
+
const properties = Array.isArray(comp?.properties) ? comp.properties : [];
|
|
129
|
+
const isWorkspace = properties.some(
|
|
130
|
+
(p) => p.name === "cdx:npm:isWorkspace" && p.value === "true",
|
|
131
|
+
);
|
|
132
|
+
const memberPath = properties.find(
|
|
133
|
+
(p) => p.name === "cdx:npm:resolvedPath",
|
|
134
|
+
)?.value;
|
|
135
|
+
if (!isWorkspace || !memberPath || !comp["bom-ref"]) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const dir = `${memberPath}`.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
139
|
+
if (!dir || dir.startsWith("..") || isAbsolute(dir)) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
members.push({ dir, ref: comp["bom-ref"] });
|
|
143
|
+
}
|
|
144
|
+
members.sort((a, b) => b.dir.length - a.dir.length);
|
|
145
|
+
const memberDirect = new Map();
|
|
146
|
+
return (fileName) => {
|
|
147
|
+
const file = `${fileName ?? ""}`.replaceAll("\\", "/");
|
|
148
|
+
const member = members.find((m) => file.startsWith(`${m.dir}/`));
|
|
149
|
+
if (!member) {
|
|
150
|
+
return [rootDirect];
|
|
151
|
+
}
|
|
152
|
+
if (!memberDirect.has(member.ref)) {
|
|
153
|
+
memberDirect.set(
|
|
154
|
+
member.ref,
|
|
155
|
+
directDependencyRefs(dependencies, [member.ref]),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return [memberDirect.get(member.ref), rootDirect];
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Narrow candidate versions to the ones a file's nearest direct dependencies
|
|
164
|
+
* name.
|
|
165
|
+
*
|
|
166
|
+
* @param {Set<string>} candidates Candidate references
|
|
167
|
+
* @param {Set<string>[]} directSets Direct dependency sets, nearest first
|
|
168
|
+
* @param {function(string): string} [toRef] Maps a candidate to its bom-ref
|
|
169
|
+
* @returns {Set<string>} The candidates the nearest set names, or all of them
|
|
170
|
+
*/
|
|
171
|
+
function narrowToDirect(candidates, directSets, toRef = (c) => c) {
|
|
172
|
+
for (const direct of directSets) {
|
|
173
|
+
const hits = [...candidates].filter((c) => direct.has(toRef(c)));
|
|
174
|
+
if (hits.length) {
|
|
175
|
+
return new Set(hits);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return candidates;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* bom-refs of a parent component and the sub-components it lists, which are
|
|
183
|
+
* the roots whose direct dependencies a project's own files use.
|
|
184
|
+
*
|
|
185
|
+
* @param {Object} parentComponent Parent (metadata) component
|
|
186
|
+
* @returns {string[]} bom-refs
|
|
187
|
+
*/
|
|
188
|
+
export function projectRootRefs(parentComponent) {
|
|
189
|
+
return [parentComponent, ...(parentComponent?.components || [])]
|
|
190
|
+
.map((c) => c?.["bom-ref"])
|
|
191
|
+
.filter(Boolean);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Decide which installed versions of an npm package a project file uses.
|
|
196
|
+
*
|
|
197
|
+
* Only package names with more than one version in `pkgList` are decided; a
|
|
198
|
+
* file uses the single version of any other package. For the rest, the file
|
|
199
|
+
* uses:
|
|
200
|
+
*
|
|
201
|
+
* 1. the copy Node.js resolves from the file's directory, when the lock file
|
|
202
|
+
* records install locations (package-lock.json): the first
|
|
203
|
+
* `<dir>/node_modules/<name>` holding one of the versions, walking up from
|
|
204
|
+
* the file;
|
|
205
|
+
* 2. otherwise the versions the file's workspace member, or else the project,
|
|
206
|
+
* depends on directly (see `createDirectDependencyLookup`);
|
|
207
|
+
* 3. otherwise every version, since nothing tells them apart.
|
|
208
|
+
*
|
|
209
|
+
* @param {Object[]} pkgList Package components, duplicates per install location included
|
|
210
|
+
* @param {Object} [context] Project facts
|
|
211
|
+
* @param {string} [context.srcDir] Directory the file names are relative to
|
|
212
|
+
* @param {Object[]} [context.dependencies] Dependency tree
|
|
213
|
+
* @param {string[]} [context.rootRefs] bom-refs of the project and the sub-components it lists
|
|
214
|
+
* @returns {{isAmbiguous: function(Object): boolean, allows: function(Object, string): boolean}} Chooser
|
|
215
|
+
*/
|
|
216
|
+
export function createNpmVersionChooser(pkgList, context = {}) {
|
|
217
|
+
const refsByName = new Map();
|
|
218
|
+
const refByInstallDir = new Map();
|
|
219
|
+
for (const pkg of pkgList || []) {
|
|
220
|
+
const ref = pkg?.["bom-ref"];
|
|
221
|
+
if (!ref || !pkg.name) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (pkg.purl && npmPackageNameOfPurl(pkg.purl) === undefined) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const name = npmPackageName(pkg);
|
|
228
|
+
if (!refsByName.has(name)) {
|
|
229
|
+
refsByName.set(name, new Set());
|
|
230
|
+
}
|
|
231
|
+
refsByName.get(name).add(ref);
|
|
232
|
+
const installDir = npmInstallDirectory(pkg);
|
|
233
|
+
if (installDir) {
|
|
234
|
+
refByInstallDir.set(installDir, ref);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const directFor = createDirectDependencyLookup(
|
|
238
|
+
pkgList,
|
|
239
|
+
context.dependencies,
|
|
240
|
+
context.rootRefs || [],
|
|
241
|
+
);
|
|
242
|
+
const decided = new Map();
|
|
243
|
+
const chosenRefs = (name, fileName) => {
|
|
244
|
+
const candidates = refsByName.get(name);
|
|
245
|
+
const fileDir =
|
|
246
|
+
fileName && context.srcDir
|
|
247
|
+
? resolve(context.srcDir, dirname(`${fileName}`))
|
|
248
|
+
: undefined;
|
|
249
|
+
const cacheKey = `${name}\u0000${dirname(`${fileName ?? ""}`)}`;
|
|
250
|
+
if (decided.has(cacheKey)) {
|
|
251
|
+
return decided.get(cacheKey);
|
|
252
|
+
}
|
|
253
|
+
let chosen;
|
|
254
|
+
if (fileDir) {
|
|
255
|
+
let dir = fileDir;
|
|
256
|
+
while (!chosen) {
|
|
257
|
+
const ref = refByInstallDir.get(resolve(dir, "node_modules", name));
|
|
258
|
+
if (ref && candidates.has(ref)) {
|
|
259
|
+
chosen = new Set([ref]);
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
const parent = dirname(dir);
|
|
263
|
+
if (parent === dir) {
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
dir = parent;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!chosen) {
|
|
270
|
+
chosen = narrowToDirect(candidates, directFor(fileName));
|
|
271
|
+
}
|
|
272
|
+
decided.set(cacheKey, chosen);
|
|
273
|
+
return chosen;
|
|
274
|
+
};
|
|
275
|
+
const isAmbiguous = (pkg) =>
|
|
276
|
+
Boolean(pkg?.name) && (refsByName.get(npmPackageName(pkg))?.size || 0) > 1;
|
|
277
|
+
return {
|
|
278
|
+
isAmbiguous,
|
|
279
|
+
allows(pkg, fileName) {
|
|
280
|
+
if (!isAmbiguous(pkg)) {
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
return chosenRefs(npmPackageName(pkg), fileName).has(pkg["bom-ref"]);
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Keep each npm occurrence only on the versions the project file uses.
|
|
290
|
+
*
|
|
291
|
+
* evinse credits a slice record to every component of the record's package,
|
|
292
|
+
* one per installed version. For a package with several versions in the BOM,
|
|
293
|
+
* an occurrence in file F stays on a version when the input BOM's own import
|
|
294
|
+
* evidence (seeded before any slice is read) puts that version in F; when the
|
|
295
|
+
* BOM credits none of the versions in F, or several, the versions F's
|
|
296
|
+
* workspace member or else the project depends on directly are kept (see
|
|
297
|
+
* `createDirectDependencyLookup`); failing both, all stay.
|
|
298
|
+
*
|
|
299
|
+
* @param {Object} purlLocationMap purl -> Set of occurrence keys, changed in place
|
|
300
|
+
* @param {Object} purlSymbolMap purl -> occurrence key -> symbol, changed in place
|
|
301
|
+
* @param {Object} seededLocationMap purl -> Set of occurrence keys the input BOM carried
|
|
302
|
+
* @param {Object} bomJson Input BOM, for its components, dependencies and metadata component
|
|
303
|
+
* @returns {Set<string>} purls left with no occurrence, whose input occurrences belong to another version
|
|
304
|
+
*/
|
|
305
|
+
export function keepResolvedNpmVersions(
|
|
306
|
+
purlLocationMap,
|
|
307
|
+
purlSymbolMap,
|
|
308
|
+
seededLocationMap,
|
|
309
|
+
bomJson,
|
|
310
|
+
) {
|
|
311
|
+
const purlsByName = new Map();
|
|
312
|
+
const refByPurl = new Map();
|
|
313
|
+
for (const comp of bomJson?.components || []) {
|
|
314
|
+
const name = comp?.purl ? npmPackageNameOfPurl(comp.purl) : undefined;
|
|
315
|
+
if (!name) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (!purlsByName.has(name)) {
|
|
319
|
+
purlsByName.set(name, new Set());
|
|
320
|
+
}
|
|
321
|
+
purlsByName.get(name).add(comp.purl);
|
|
322
|
+
refByPurl.set(comp.purl, comp["bom-ref"] || comp.purl);
|
|
323
|
+
}
|
|
324
|
+
const clearedPurls = new Set();
|
|
325
|
+
const directFor = createDirectDependencyLookup(
|
|
326
|
+
bomJson?.components,
|
|
327
|
+
bomJson?.dependencies,
|
|
328
|
+
projectRootRefs(bomJson?.metadata?.component),
|
|
329
|
+
);
|
|
330
|
+
for (const purls of purlsByName.values()) {
|
|
331
|
+
if (purls.size < 2) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
const seededIn = new Map();
|
|
335
|
+
for (const purl of purls) {
|
|
336
|
+
for (const key of seededLocationMap?.[purl] || []) {
|
|
337
|
+
const location = occurrenceKeyLocation(key);
|
|
338
|
+
if (!seededIn.has(location)) {
|
|
339
|
+
seededIn.set(location, new Set());
|
|
340
|
+
}
|
|
341
|
+
seededIn.get(location).add(purl);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const decided = new Map();
|
|
345
|
+
const keeps = (purl, location) => {
|
|
346
|
+
if (!decided.has(location)) {
|
|
347
|
+
let chosen = seededIn.get(location) || purls;
|
|
348
|
+
if (chosen.size > 1) {
|
|
349
|
+
chosen = narrowToDirect(chosen, directFor(location), (purl) =>
|
|
350
|
+
refByPurl.get(purl),
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
decided.set(location, chosen);
|
|
354
|
+
}
|
|
355
|
+
return decided.get(location).has(purl);
|
|
356
|
+
};
|
|
357
|
+
for (const purl of purls) {
|
|
358
|
+
const keys = purlLocationMap[purl];
|
|
359
|
+
if (!keys?.size) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
for (const key of [...keys]) {
|
|
363
|
+
if (!keeps(purl, occurrenceKeyLocation(key))) {
|
|
364
|
+
keys.delete(key);
|
|
365
|
+
if (purlSymbolMap?.[purl]) {
|
|
366
|
+
delete purlSymbolMap[purl][key];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (!keys.size) {
|
|
371
|
+
clearedPurls.add(purl);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return clearedPurls;
|
|
376
|
+
}
|