@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,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hardware components at 2.0 (cdxgen/cdxgen#4755). What each `cdx:hbom:*`
|
|
3
|
+
* property means in the 2.0 draft is owned by @cdxgen/cdx-hbom, whose
|
|
4
|
+
* optional `./cdx20` export provides `toCdx20HardwareComponent(component,
|
|
5
|
+
* {partyRefId, includeSensitiveIdentifiers, identifierPolicy})`. The caller
|
|
6
|
+
* loads it (lib/inventory/hbomLoader.js: hardwareRenderContext) and passes
|
|
7
|
+
* it, with those options, to renderCycloneDx20 in the render context, so the
|
|
8
|
+
* render stays a pure function of its arguments.
|
|
9
|
+
*
|
|
10
|
+
* The renderer converts the component as usual and then takes only these
|
|
11
|
+
* members from the mapper's result, each checked against the pinned schema:
|
|
12
|
+
*
|
|
13
|
+
* - `identifiers`: only groups asserted by the party the renderer passed
|
|
14
|
+
* (the one its own identifiers use, which it declares when referenced).
|
|
15
|
+
* Their identities are appended to that party's group, or added as a new
|
|
16
|
+
* group, so a purl identity is never replaced. Only identities with a
|
|
17
|
+
* scheme from the identityScheme enum (or a custom `{name}`) and a
|
|
18
|
+
* non-empty value are taken.
|
|
19
|
+
* - `classification` (#/$defs/cyclonedx-physical-2.0/$defs/classification):
|
|
20
|
+
* `category`, `subcategory`, and `codes` that are taxonomyCode objects
|
|
21
|
+
* (`{standard, value}`); a bare string code is dropped and counted.
|
|
22
|
+
* - a manufacturer party, when the generic conversion produced none.
|
|
23
|
+
*
|
|
24
|
+
* Every `cdx:hbom:*` property stays: the hbom audit rules read them.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { thoughtLog } from "../core/logger.js";
|
|
28
|
+
import {
|
|
29
|
+
clone,
|
|
30
|
+
countDrop,
|
|
31
|
+
isNonEmptyString,
|
|
32
|
+
isObjectRecord,
|
|
33
|
+
uniqueByValue,
|
|
34
|
+
} from "./cyclonedx20Common.js";
|
|
35
|
+
import { identifierPartyRefId } from "./cyclonedx20RenderEvidence.js";
|
|
36
|
+
import { IDENTITY_SCHEME_ENUM } from "./cyclonedx20Tables.js";
|
|
37
|
+
|
|
38
|
+
const TAXONOMY_CODE_FIELDS = Object.freeze(["description", "url"]);
|
|
39
|
+
|
|
40
|
+
/** True when the 1.x component carries at least one cdx:hbom:* property. */
|
|
41
|
+
export function isHardwareComponent(component) {
|
|
42
|
+
return (
|
|
43
|
+
Array.isArray(component?.properties) ? component.properties : []
|
|
44
|
+
).some(
|
|
45
|
+
(property) =>
|
|
46
|
+
typeof property?.name === "string" &&
|
|
47
|
+
property.name.startsWith("cdx:hbom:"),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isValidIdentity(identity) {
|
|
52
|
+
if (!isObjectRecord(identity) || !isNonEmptyString(identity.value)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const { scheme } = identity;
|
|
56
|
+
return (
|
|
57
|
+
(typeof scheme === "string" && IDENTITY_SCHEME_ENUM.has(scheme)) ||
|
|
58
|
+
(isObjectRecord(scheme) && isNonEmptyString(scheme.name))
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function appendIdentifiers(converted, groups, partyRefId, stats) {
|
|
63
|
+
for (const group of Array.isArray(groups) ? groups : []) {
|
|
64
|
+
if (!isObjectRecord(group) || group.party !== partyRefId) {
|
|
65
|
+
// Any other party would have no declaration in this document.
|
|
66
|
+
countDrop(stats, "hardware identifier (undeclared party)");
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const identities = (Array.isArray(group.identities) ? group.identities : [])
|
|
70
|
+
.filter((identity) => {
|
|
71
|
+
const valid = isValidIdentity(identity);
|
|
72
|
+
if (!valid) {
|
|
73
|
+
countDrop(stats, "hardware identity (not a 2.0 identity)");
|
|
74
|
+
}
|
|
75
|
+
return valid;
|
|
76
|
+
})
|
|
77
|
+
.map((identity) => ({
|
|
78
|
+
scheme: clone(identity.scheme),
|
|
79
|
+
value: identity.value,
|
|
80
|
+
}));
|
|
81
|
+
if (!identities.length) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
converted.identifiers = Array.isArray(converted.identifiers)
|
|
85
|
+
? converted.identifiers
|
|
86
|
+
: [];
|
|
87
|
+
const existing = converted.identifiers.find(
|
|
88
|
+
(entry) => entry.party === group.party,
|
|
89
|
+
);
|
|
90
|
+
if (existing) {
|
|
91
|
+
existing.identities = uniqueByValue([
|
|
92
|
+
...existing.identities,
|
|
93
|
+
...identities,
|
|
94
|
+
]);
|
|
95
|
+
} else {
|
|
96
|
+
converted.identifiers.push({ party: group.party, identities });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function taxonomyCode(code, stats) {
|
|
102
|
+
if (
|
|
103
|
+
!isObjectRecord(code) ||
|
|
104
|
+
!isNonEmptyString(code.standard) ||
|
|
105
|
+
!isNonEmptyString(code.value)
|
|
106
|
+
) {
|
|
107
|
+
countDrop(stats, "hardware classification code (not a taxonomyCode)");
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const converted = { standard: code.standard, value: code.value };
|
|
111
|
+
for (const field of TAXONOMY_CODE_FIELDS) {
|
|
112
|
+
if (typeof code[field] === "string") {
|
|
113
|
+
converted[field] = code[field];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return converted;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function setClassification(converted, classification, stats) {
|
|
120
|
+
if (!isObjectRecord(classification) || converted.classification) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const result = {};
|
|
124
|
+
if (isNonEmptyString(classification.category)) {
|
|
125
|
+
result.category = classification.category;
|
|
126
|
+
}
|
|
127
|
+
if (typeof classification.subcategory === "string") {
|
|
128
|
+
result.subcategory = classification.subcategory;
|
|
129
|
+
}
|
|
130
|
+
const codes = uniqueByValue(
|
|
131
|
+
(Array.isArray(classification.codes) ? classification.codes : [])
|
|
132
|
+
.map((code) => taxonomyCode(code, stats))
|
|
133
|
+
.filter(Boolean),
|
|
134
|
+
);
|
|
135
|
+
if (codes.length) {
|
|
136
|
+
result.codes = codes;
|
|
137
|
+
}
|
|
138
|
+
if (Object.keys(result).length) {
|
|
139
|
+
converted.classification = result;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function addManufacturerParty(converted, parties) {
|
|
144
|
+
const hasManufacturer = (converted.parties || []).some((party) =>
|
|
145
|
+
(party?.roles || []).some((role) => role?.role === "manufacturer"),
|
|
146
|
+
);
|
|
147
|
+
if (hasManufacturer) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const mapped = (Array.isArray(parties) ? parties : []).find(
|
|
151
|
+
(party) =>
|
|
152
|
+
(party?.roles || []).some((role) => role?.role === "manufacturer") &&
|
|
153
|
+
isNonEmptyString(party?.organization?.name),
|
|
154
|
+
);
|
|
155
|
+
if (mapped) {
|
|
156
|
+
converted.parties = [
|
|
157
|
+
...(converted.parties || []),
|
|
158
|
+
{
|
|
159
|
+
roles: [{ role: "manufacturer" }],
|
|
160
|
+
organization: { name: mapped.organization.name },
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Add the hardware mapper's 2.0 members to a converted hardware component.
|
|
168
|
+
* Without a mapper, or for a component without cdx:hbom:* properties, the
|
|
169
|
+
* converted component is returned unchanged. The mapper gets a clone of the
|
|
170
|
+
* 1.x component, the asserting party refId the generic identifiers use (so
|
|
171
|
+
* its identifiers resolve to the party the renderer declares) and the
|
|
172
|
+
* identifier options of the render context (`stats.hardwareOptions`).
|
|
173
|
+
*
|
|
174
|
+
* @param {object} component The 1.x component
|
|
175
|
+
* @param {object} converted Its generic 2.0 conversion, extended in place
|
|
176
|
+
* @param {object} stats Render counters with the optional `hardwareMapper`
|
|
177
|
+
* @returns {object} The converted component
|
|
178
|
+
*/
|
|
179
|
+
export function applyHardwareMapping(component, converted, stats) {
|
|
180
|
+
const mapper = stats.hardwareMapper;
|
|
181
|
+
if (typeof mapper !== "function" || !isHardwareComponent(component)) {
|
|
182
|
+
return converted;
|
|
183
|
+
}
|
|
184
|
+
const partyRefId = identifierPartyRefId(stats.claims);
|
|
185
|
+
let mapped;
|
|
186
|
+
try {
|
|
187
|
+
mapped = mapper(clone(component), {
|
|
188
|
+
...stats.hardwareOptions,
|
|
189
|
+
partyRefId,
|
|
190
|
+
});
|
|
191
|
+
} catch (error) {
|
|
192
|
+
countDrop(stats, "hardware mapping (mapper failed)");
|
|
193
|
+
thoughtLog(`CycloneDX 2.0 hardware mapping failed: ${error?.message}`);
|
|
194
|
+
return converted;
|
|
195
|
+
}
|
|
196
|
+
if (!isObjectRecord(mapped)) {
|
|
197
|
+
return converted;
|
|
198
|
+
}
|
|
199
|
+
appendIdentifiers(converted, mapped.identifiers, partyRefId, stats);
|
|
200
|
+
setClassification(converted, mapped.classification, stats);
|
|
201
|
+
addManufacturerParty(converted, mapped.parties);
|
|
202
|
+
return converted;
|
|
203
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-side conversion of 1.x patents, patent families and patent
|
|
3
|
+
* assertions (#/$defs/cyclonedx-patent-2.0). The members are the same; the
|
|
4
|
+
* 1.x `bom-ref` becomes `refId`, and organizations and contacts take the
|
|
5
|
+
* 2.0 common shapes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
clone,
|
|
10
|
+
copyDefined,
|
|
11
|
+
countDrop,
|
|
12
|
+
countUnmapped,
|
|
13
|
+
isNonEmptyString,
|
|
14
|
+
isObjectRecord,
|
|
15
|
+
} from "./cyclonedx20Common.js";
|
|
16
|
+
import { toCommonContact, toCommonEntity } from "./cyclonedx20Parties.js";
|
|
17
|
+
|
|
18
|
+
const PATENT_FIELDS = [
|
|
19
|
+
"patentNumber",
|
|
20
|
+
"applicationNumber",
|
|
21
|
+
"jurisdiction",
|
|
22
|
+
"priorityApplication",
|
|
23
|
+
"publicationNumber",
|
|
24
|
+
"abstract",
|
|
25
|
+
"filingDate",
|
|
26
|
+
"grantDate",
|
|
27
|
+
"patentExpirationDate",
|
|
28
|
+
"patentLegalStatus",
|
|
29
|
+
"externalReferences",
|
|
30
|
+
];
|
|
31
|
+
const PATENT_HANDLED = new Set([...PATENT_FIELDS, "bom-ref", "patentAssignee"]);
|
|
32
|
+
|
|
33
|
+
const PATENT_FAMILY_FIELDS = [
|
|
34
|
+
"familyId",
|
|
35
|
+
"priorityApplication",
|
|
36
|
+
"members",
|
|
37
|
+
"externalReferences",
|
|
38
|
+
];
|
|
39
|
+
const PATENT_FAMILY_HANDLED = new Set([...PATENT_FAMILY_FIELDS, "bom-ref"]);
|
|
40
|
+
|
|
41
|
+
const ASSERTION_FIELDS = ["assertionType", "patentRefs", "notes"];
|
|
42
|
+
const ASSERTION_HANDLED = new Set([...ASSERTION_FIELDS, "bom-ref", "asserter"]);
|
|
43
|
+
|
|
44
|
+
/** Start a converted object with the 1.x `bom-ref` as its `refId`. */
|
|
45
|
+
function withRefId(source) {
|
|
46
|
+
return isNonEmptyString(source["bom-ref"])
|
|
47
|
+
? { refId: source["bom-ref"] }
|
|
48
|
+
: {};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A 1.x organizationalEntity or organizationalContact as its 2.0 common
|
|
53
|
+
* shape, for the members whose schema is a oneOf of the two. Only a contact
|
|
54
|
+
* has `email` or `phone`, only an organization `url`, `address` or
|
|
55
|
+
* `contact`. With neither (a bare name) the value matches both branches of
|
|
56
|
+
* the oneOf and so validates as neither; it is counted under `label` and
|
|
57
|
+
* undefined is returned.
|
|
58
|
+
*/
|
|
59
|
+
function toCommonEntityOrContact(value, label, stats) {
|
|
60
|
+
if (!isObjectRecord(value)) {
|
|
61
|
+
countDrop(stats, `${label} (not an object)`);
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const isContact =
|
|
65
|
+
(value.email !== undefined || value.phone !== undefined) &&
|
|
66
|
+
value.url === undefined &&
|
|
67
|
+
value.address === undefined &&
|
|
68
|
+
value.contact === undefined;
|
|
69
|
+
const converted = isContact
|
|
70
|
+
? toCommonContact(value, stats)
|
|
71
|
+
: toCommonEntity(value, stats);
|
|
72
|
+
if (
|
|
73
|
+
!converted ||
|
|
74
|
+
Object.keys(converted).every((key) => key === "refId" || key === "name")
|
|
75
|
+
) {
|
|
76
|
+
countDrop(stats, `${label} (neither an organization nor a contact)`);
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
return converted;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Convert 1.x definitions.patents: patents and patent families.
|
|
84
|
+
*
|
|
85
|
+
* @param {object[]} patents 1.x patents and patent families
|
|
86
|
+
* @param {object} stats Render counters
|
|
87
|
+
* @returns {object[]|undefined} 2.0 patents
|
|
88
|
+
*/
|
|
89
|
+
export function convertPatents(patents, stats) {
|
|
90
|
+
if (!Array.isArray(patents)) {
|
|
91
|
+
if (patents !== undefined) {
|
|
92
|
+
countDrop(stats, "definitions.patents (not a list)");
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const converted = [];
|
|
97
|
+
for (const patent of patents) {
|
|
98
|
+
if (!isObjectRecord(patent)) {
|
|
99
|
+
countDrop(stats, "definitions.patents (not an object)");
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const out = withRefId(patent);
|
|
103
|
+
if (patent.familyId !== undefined) {
|
|
104
|
+
copyDefined(out, patent, PATENT_FAMILY_FIELDS);
|
|
105
|
+
countUnmapped(
|
|
106
|
+
stats,
|
|
107
|
+
"definitions.patents",
|
|
108
|
+
patent,
|
|
109
|
+
PATENT_FAMILY_HANDLED,
|
|
110
|
+
);
|
|
111
|
+
} else {
|
|
112
|
+
copyDefined(out, patent, PATENT_FIELDS);
|
|
113
|
+
if (Array.isArray(patent.patentAssignee)) {
|
|
114
|
+
const assignees = patent.patentAssignee
|
|
115
|
+
.map((assignee) =>
|
|
116
|
+
toCommonEntityOrContact(
|
|
117
|
+
assignee,
|
|
118
|
+
"definitions.patents.patentAssignee",
|
|
119
|
+
stats,
|
|
120
|
+
),
|
|
121
|
+
)
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
if (assignees.length) {
|
|
124
|
+
out.patentAssignee = assignees;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
countUnmapped(stats, "definitions.patents", patent, PATENT_HANDLED);
|
|
128
|
+
}
|
|
129
|
+
converted.push(out);
|
|
130
|
+
}
|
|
131
|
+
return converted.length ? converted : undefined;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Convert 1.x patentAssertions
|
|
136
|
+
* (#/$defs/cyclonedx-patent-2.0/$defs/patentAssertions): the asserter is an
|
|
137
|
+
* organization, a contact or a refLinkType string.
|
|
138
|
+
*
|
|
139
|
+
* @param {object[]} assertions 1.x patent assertions
|
|
140
|
+
* @param {object} stats Render counters
|
|
141
|
+
* @returns {object[]|undefined} 2.0 patent assertions, or undefined when
|
|
142
|
+
* none is left
|
|
143
|
+
*/
|
|
144
|
+
export function convertPatentAssertions(assertions, stats) {
|
|
145
|
+
if (!Array.isArray(assertions)) {
|
|
146
|
+
return clone(assertions);
|
|
147
|
+
}
|
|
148
|
+
const converted = [];
|
|
149
|
+
for (const assertion of assertions) {
|
|
150
|
+
if (!isObjectRecord(assertion)) {
|
|
151
|
+
countDrop(stats, "patentAssertions (not an object)");
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// `asserter` is required.
|
|
155
|
+
const asserter =
|
|
156
|
+
typeof assertion.asserter === "string"
|
|
157
|
+
? assertion.asserter
|
|
158
|
+
: toCommonEntityOrContact(
|
|
159
|
+
assertion.asserter,
|
|
160
|
+
"patentAssertions.asserter",
|
|
161
|
+
stats,
|
|
162
|
+
);
|
|
163
|
+
if (!asserter) {
|
|
164
|
+
countDrop(stats, "patentAssertions (no asserter)");
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const out = withRefId(assertion);
|
|
168
|
+
copyDefined(out, assertion, ASSERTION_FIELDS);
|
|
169
|
+
out.asserter = asserter;
|
|
170
|
+
countUnmapped(stats, "patentAssertions", assertion, ASSERTION_HANDLED);
|
|
171
|
+
converted.push(out);
|
|
172
|
+
}
|
|
173
|
+
return converted.length ? converted : undefined;
|
|
174
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference integrity of a rendered CycloneDX 2.0 document: every refId is
|
|
3
|
+
* unique (the 2.0 refIdType rule "Every `refId` shall be unique within the
|
|
4
|
+
* document", which JSON Schema cannot check), and dependency and
|
|
5
|
+
* vulnerability references resolve to a declared refId or a BOM-Link.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { isDeepStrictEqual } from "node:util";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
clone,
|
|
12
|
+
countDrop,
|
|
13
|
+
countRewrite,
|
|
14
|
+
isBomLink,
|
|
15
|
+
isObjectRecord,
|
|
16
|
+
} from "./cyclonedx20Common.js";
|
|
17
|
+
import { COMPONENT_ENTRY_ARRAY_KEYS } from "./cyclonedx20Tables.js";
|
|
18
|
+
|
|
19
|
+
// Returned by the walk for a declaration-only entry that repeats an earlier
|
|
20
|
+
// declaration; the enclosing array leaves it out.
|
|
21
|
+
const REMOVE = Symbol("remove");
|
|
22
|
+
|
|
23
|
+
// Slots outside component and party lists: a duplicate there has no
|
|
24
|
+
// reference form.
|
|
25
|
+
const PLAIN_SLOT = Object.freeze({});
|
|
26
|
+
|
|
27
|
+
/** How many times each refId string is declared under the roots. */
|
|
28
|
+
function countRefIds(roots) {
|
|
29
|
+
const counts = new Map();
|
|
30
|
+
const visit = (node) => {
|
|
31
|
+
if (Array.isArray(node)) {
|
|
32
|
+
node.forEach(visit);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!isObjectRecord(node)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (typeof node.refId === "string") {
|
|
39
|
+
counts.set(node.refId, (counts.get(node.refId) || 0) + 1);
|
|
40
|
+
}
|
|
41
|
+
for (const value of Object.values(node)) {
|
|
42
|
+
visit(value);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
roots.forEach(({ value }) => {
|
|
46
|
+
visit(value);
|
|
47
|
+
});
|
|
48
|
+
return counts;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* True when a reference to `earlier` loses nothing of `later`: every member
|
|
53
|
+
* of `later` except its refId has a deep-equal counterpart in `earlier`.
|
|
54
|
+
*/
|
|
55
|
+
function isCoveredBy(later, earlier) {
|
|
56
|
+
if (!isObjectRecord(earlier)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
for (const [key, value] of Object.entries(later)) {
|
|
60
|
+
if (key !== "refId" && !isDeepStrictEqual(value, earlier[key])) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** `${refId}-dup-<n>` for the smallest n not already taken. */
|
|
68
|
+
function uniqueRefId(refId, taken) {
|
|
69
|
+
let index = 1;
|
|
70
|
+
while (taken.has(`${refId}-dup-${index}`)) {
|
|
71
|
+
index++;
|
|
72
|
+
}
|
|
73
|
+
const fresh = `${refId}-dup-${index}`;
|
|
74
|
+
taken.add(fresh);
|
|
75
|
+
return fresh;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The slot a member value sits in. Component entry lists, the inventory
|
|
80
|
+
* subject and an annotator component accept a componentReference
|
|
81
|
+
* (#/$defs/cyclonedx-component-2.0/$defs/componentEntry), party lists a
|
|
82
|
+
* partyReference (#/$defs/cyclonedx-party-2.0/$defs/partyChoice). The
|
|
83
|
+
* definitions registry lists components and parties but accepts no
|
|
84
|
+
* reference form (#/$defs/cyclonedx-definition-2.0/$defs/definitions).
|
|
85
|
+
*/
|
|
86
|
+
function memberSlot(key, value, ownKey, inDefinitions) {
|
|
87
|
+
const isList = Array.isArray(value);
|
|
88
|
+
if (inDefinitions && isList && (key === "components" || key === "parties")) {
|
|
89
|
+
return {
|
|
90
|
+
kind: key === "components" ? "component" : "party",
|
|
91
|
+
declarationOnly: true,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
(COMPONENT_ENTRY_ARRAY_KEYS.has(key) && isList) ||
|
|
96
|
+
key === "subject" ||
|
|
97
|
+
(key === "component" && ownKey === "annotator")
|
|
98
|
+
) {
|
|
99
|
+
return { kind: "component", reference: true };
|
|
100
|
+
}
|
|
101
|
+
if (key === "parties" && isList) {
|
|
102
|
+
return { kind: "party", reference: true };
|
|
103
|
+
}
|
|
104
|
+
return PLAIN_SLOT;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Make every refId unique. The roots are walked in order, so the earliest
|
|
109
|
+
* root keeps a repeated refId; pass the inventories before metadata so the
|
|
110
|
+
* subject of a self-scan keeps its members when it repeats the refId of the
|
|
111
|
+
* tool. A later declaration with an already declared refId becomes:
|
|
112
|
+
*
|
|
113
|
+
* - a componentReference or partyReference `{ref}` when it sits in a slot
|
|
114
|
+
* that accepts one and an earlier declaration of the same kind already
|
|
115
|
+
* holds every member it has (nothing is lost);
|
|
116
|
+
* - nothing, in the definitions registry, under the same condition (counted
|
|
117
|
+
* as already declared), and also when an inventory declares the refId
|
|
118
|
+
* with other members (the members only the registry entry had are
|
|
119
|
+
* counted as dropped);
|
|
120
|
+
* - unchanged, when neither it nor an earlier declaration sits in a
|
|
121
|
+
* component or party slot (counted);
|
|
122
|
+
* - otherwise the same object under a new refId `<refId>-dup-<n>`, so no data
|
|
123
|
+
* is lost and references to the refId keep resolving to the first
|
|
124
|
+
* declaration.
|
|
125
|
+
*
|
|
126
|
+
* @param {Array<{section: string, value: *}>} roots Rendered sections, the
|
|
127
|
+
* definitions root marked with `section: "definitions"`
|
|
128
|
+
* @param {object} stats Render counters
|
|
129
|
+
*/
|
|
130
|
+
export function dedupeRefIds(roots, stats) {
|
|
131
|
+
const counts = countRefIds(roots);
|
|
132
|
+
const taken = new Set(counts.keys());
|
|
133
|
+
const seen = new Map();
|
|
134
|
+
const declare = (node, slot, section) => {
|
|
135
|
+
const refId = node.refId;
|
|
136
|
+
const variants = seen.get(refId);
|
|
137
|
+
if (!variants) {
|
|
138
|
+
seen.set(refId, [
|
|
139
|
+
{
|
|
140
|
+
kind: slot.kind,
|
|
141
|
+
refId,
|
|
142
|
+
section,
|
|
143
|
+
snapshot: counts.get(refId) > 1 ? clone(node) : undefined,
|
|
144
|
+
},
|
|
145
|
+
]);
|
|
146
|
+
return node;
|
|
147
|
+
}
|
|
148
|
+
const match = slot.kind
|
|
149
|
+
? variants.find(
|
|
150
|
+
(variant) =>
|
|
151
|
+
variant.kind === slot.kind && isCoveredBy(node, variant.snapshot),
|
|
152
|
+
)
|
|
153
|
+
: undefined;
|
|
154
|
+
if (match && slot.declarationOnly) {
|
|
155
|
+
countRewrite(
|
|
156
|
+
stats,
|
|
157
|
+
`definitions ${slot.kind} (already declared in ${match.section === "inventories" ? "inventory" : match.section})`,
|
|
158
|
+
);
|
|
159
|
+
return REMOVE;
|
|
160
|
+
}
|
|
161
|
+
// A formula component the inventory declares too: 2.0 records no formula
|
|
162
|
+
// membership, so a second definition would only repeat the component
|
|
163
|
+
// under another refId. The inventory declaration stays, references keep
|
|
164
|
+
// resolving to it, and the members only this copy had are counted.
|
|
165
|
+
const inventoryVariant =
|
|
166
|
+
slot.declarationOnly &&
|
|
167
|
+
variants.find(
|
|
168
|
+
(variant) =>
|
|
169
|
+
variant.kind === slot.kind && variant.section === "inventories",
|
|
170
|
+
);
|
|
171
|
+
if (inventoryVariant) {
|
|
172
|
+
countDrop(
|
|
173
|
+
stats,
|
|
174
|
+
`definitions ${slot.kind} (already declared in inventory; members it adds dropped)`,
|
|
175
|
+
);
|
|
176
|
+
return REMOVE;
|
|
177
|
+
}
|
|
178
|
+
if (match && slot.reference) {
|
|
179
|
+
countRewrite(stats, "duplicate refId (rendered as a reference)");
|
|
180
|
+
return { ref: match.refId };
|
|
181
|
+
}
|
|
182
|
+
if (!slot.kind && !variants.some((variant) => variant.kind)) {
|
|
183
|
+
// Neither declaration sits where a componentReference or
|
|
184
|
+
// partyReference could name it (a formula and its workflow, say), so
|
|
185
|
+
// no reference can resolve to the wrong one. A document read from 2.0
|
|
186
|
+
// keeps its refIds as written; the repeat is counted.
|
|
187
|
+
countRewrite(stats, "duplicate refId (kept: no reference form)");
|
|
188
|
+
return node;
|
|
189
|
+
}
|
|
190
|
+
node.refId = uniqueRefId(refId, taken);
|
|
191
|
+
countRewrite(stats, "duplicate refId (kept under a new refId)");
|
|
192
|
+
variants.push({
|
|
193
|
+
kind: slot.kind,
|
|
194
|
+
refId: node.refId,
|
|
195
|
+
section,
|
|
196
|
+
snapshot: clone(node),
|
|
197
|
+
});
|
|
198
|
+
return node;
|
|
199
|
+
};
|
|
200
|
+
const visit = (node, slot, ownKey, section, inDefinitions) => {
|
|
201
|
+
if (Array.isArray(node)) {
|
|
202
|
+
let removed = false;
|
|
203
|
+
for (let index = 0; index < node.length; index++) {
|
|
204
|
+
node[index] = visit(node[index], slot, ownKey, section, false);
|
|
205
|
+
removed ||= node[index] === REMOVE;
|
|
206
|
+
}
|
|
207
|
+
if (removed) {
|
|
208
|
+
const kept = node.filter((entry) => entry !== REMOVE);
|
|
209
|
+
node.length = 0;
|
|
210
|
+
node.push(...kept);
|
|
211
|
+
}
|
|
212
|
+
return node;
|
|
213
|
+
}
|
|
214
|
+
if (!isObjectRecord(node)) {
|
|
215
|
+
return node;
|
|
216
|
+
}
|
|
217
|
+
if (typeof node.refId === "string") {
|
|
218
|
+
const declared = declare(node, slot, section);
|
|
219
|
+
if (declared !== node) {
|
|
220
|
+
return declared;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
for (const [key, value] of Object.entries(node)) {
|
|
224
|
+
node[key] = visit(
|
|
225
|
+
value,
|
|
226
|
+
memberSlot(key, value, ownKey, inDefinitions),
|
|
227
|
+
key,
|
|
228
|
+
section,
|
|
229
|
+
false,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return node;
|
|
233
|
+
};
|
|
234
|
+
for (const { section, value } of roots) {
|
|
235
|
+
visit(value, PLAIN_SLOT, undefined, section, section === "definitions");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const resolves = (ref, declared) =>
|
|
240
|
+
typeof ref === "string" && (declared.has(ref) || isBomLink(ref));
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Keep only dependency references that resolve: an entry whose `ref` names
|
|
244
|
+
* nothing declared is dropped, and so is each such `dependsOn` and
|
|
245
|
+
* `provides` member. An emptied `dependsOn` stays, since an empty list says
|
|
246
|
+
* the component has no dependencies.
|
|
247
|
+
*
|
|
248
|
+
* @param {object[]|undefined} dependencies Rendered dependencies
|
|
249
|
+
* @param {Set<string>} declared Every declared refId
|
|
250
|
+
* @param {object} stats Render counters
|
|
251
|
+
* @returns {object[]|undefined} The dependencies left, or undefined
|
|
252
|
+
*/
|
|
253
|
+
export function resolveDependencyRefs(dependencies, declared, stats) {
|
|
254
|
+
if (!Array.isArray(dependencies)) {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
const kept = [];
|
|
258
|
+
for (const dependency of dependencies) {
|
|
259
|
+
if (!resolves(dependency.ref, declared)) {
|
|
260
|
+
countDrop(stats, "dependency (undeclared ref)");
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
for (const key of ["dependsOn", "provides"]) {
|
|
264
|
+
if (!Array.isArray(dependency[key])) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const refs = dependency[key].filter((ref) => resolves(ref, declared));
|
|
268
|
+
if (refs.length !== dependency[key].length) {
|
|
269
|
+
countDrop(
|
|
270
|
+
stats,
|
|
271
|
+
`dependency.${key} (undeclared ref)`,
|
|
272
|
+
dependency[key].length - refs.length,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
dependency[key] = refs;
|
|
276
|
+
}
|
|
277
|
+
kept.push(dependency);
|
|
278
|
+
}
|
|
279
|
+
return kept.length ? kept : undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Drop vulnerability `affects` entries whose `ref` resolves to nothing, and
|
|
284
|
+
* an `affects` list left empty.
|
|
285
|
+
*
|
|
286
|
+
* @param {object[]|undefined} vulnerabilities Rendered vulnerabilities
|
|
287
|
+
* @param {Set<string>} declared Every declared refId
|
|
288
|
+
* @param {object} stats Render counters
|
|
289
|
+
*/
|
|
290
|
+
export function resolveAffectsRefs(vulnerabilities, declared, stats) {
|
|
291
|
+
for (const vulnerability of vulnerabilities || []) {
|
|
292
|
+
if (
|
|
293
|
+
!isObjectRecord(vulnerability) ||
|
|
294
|
+
!Array.isArray(vulnerability.affects)
|
|
295
|
+
) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
const affects = vulnerability.affects.filter((entry) =>
|
|
299
|
+
resolves(entry?.ref, declared),
|
|
300
|
+
);
|
|
301
|
+
if (affects.length !== vulnerability.affects.length) {
|
|
302
|
+
countDrop(
|
|
303
|
+
stats,
|
|
304
|
+
"vulnerability.affects (undeclared ref)",
|
|
305
|
+
vulnerability.affects.length - affects.length,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (affects.length) {
|
|
309
|
+
vulnerability.affects = affects;
|
|
310
|
+
} else {
|
|
311
|
+
delete vulnerability.affects;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|