@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,768 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a CycloneDX 2.0 document into the 1.x-shaped internal model. The
|
|
3
|
+
* public entry point is re-exported from cyclonedx20.js.
|
|
4
|
+
*
|
|
5
|
+
* Where definitions.components entries go (2.0 records no membership, so the
|
|
6
|
+
* rule is fixed and deterministic, applied in document order):
|
|
7
|
+
*
|
|
8
|
+
* 0. An entry whose refId a formula lists in its
|
|
9
|
+
* `cdx:formulation:componentRefs` property (a JSON array of refIds, which
|
|
10
|
+
* renderCycloneDx20 writes for a subject-less document) is a component of
|
|
11
|
+
* every formula that lists it.
|
|
12
|
+
* 1. An entry whose refId a formula references (a `ref` member, such as a
|
|
13
|
+
* resource reference, anywhere in its tree) is a component of the first
|
|
14
|
+
* such formula. Other strings that happen to equal the refId, such as a
|
|
15
|
+
* task type, do not count.
|
|
16
|
+
* 2. In a document with inventories, an entry a componentReference the
|
|
17
|
+
* reader resolves points to (from an inventory, a tool, an annotator, a
|
|
18
|
+
* vulnerability or another definitions component) stays in definitions
|
|
19
|
+
* (kept for 2.0 output); its content reaches 1.x through the referencing
|
|
20
|
+
* entries.
|
|
21
|
+
* 3. In a document with inventories, any other entry is a formula component
|
|
22
|
+
* when the document has a formulation (the renderer moves 1.x
|
|
23
|
+
* formula.components there), and otherwise stays in definitions with a
|
|
24
|
+
* loss: 1.x has no home for it.
|
|
25
|
+
* 4. In a document without inventories (the renderer puts a subject-less
|
|
26
|
+
* document's components there), any other entry is a root component.
|
|
27
|
+
* Entries without a refId follow the same rules and are never dropped.
|
|
28
|
+
* 5. Before all of these, an entry whose type 1.x does not have (`material`)
|
|
29
|
+
* stays in definitions with a loss.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { isDeepStrictEqual } from "node:util";
|
|
33
|
+
|
|
34
|
+
import {
|
|
35
|
+
clone,
|
|
36
|
+
collectDeclaredRefIds,
|
|
37
|
+
isNonEmptyString,
|
|
38
|
+
isObjectRecord,
|
|
39
|
+
referencesParty,
|
|
40
|
+
} from "./cyclonedx20Common.js";
|
|
41
|
+
import {
|
|
42
|
+
deriveCycloneDx20Sections,
|
|
43
|
+
isCdxgenListedTool,
|
|
44
|
+
isDerivedAgentParty,
|
|
45
|
+
isDerivedBlueprint,
|
|
46
|
+
isDerivedRisk,
|
|
47
|
+
splitDerivedEntries,
|
|
48
|
+
} from "./cyclonedx20Derived.js";
|
|
49
|
+
import {
|
|
50
|
+
EXTERNAL_REFERENCES_RULE,
|
|
51
|
+
PROPERTIES_RULE,
|
|
52
|
+
} from "./cyclonedx20ReadCommon.js";
|
|
53
|
+
import {
|
|
54
|
+
flushComponentShadows,
|
|
55
|
+
keepComponentShadow,
|
|
56
|
+
readComponentEntry,
|
|
57
|
+
} from "./cyclonedx20ReadComponents.js";
|
|
58
|
+
import {
|
|
59
|
+
addLoss,
|
|
60
|
+
cloneWithDepthLimit,
|
|
61
|
+
createReadContext,
|
|
62
|
+
jsonPointer,
|
|
63
|
+
MAX_READ_DEPTH,
|
|
64
|
+
markInternalModel,
|
|
65
|
+
registerShadow,
|
|
66
|
+
reportUnmapped,
|
|
67
|
+
reverseFields,
|
|
68
|
+
} from "./cyclonedx20ReadContext.js";
|
|
69
|
+
import { flushIdentityShadows } from "./cyclonedx20ReadEvidence.js";
|
|
70
|
+
import {
|
|
71
|
+
readAnnotations,
|
|
72
|
+
readCitations,
|
|
73
|
+
readCompositions,
|
|
74
|
+
readFormulation,
|
|
75
|
+
readMetadata,
|
|
76
|
+
readVulnerabilities,
|
|
77
|
+
} from "./cyclonedx20ReadSections.js";
|
|
78
|
+
import {
|
|
79
|
+
derivedPartyDeclarations,
|
|
80
|
+
identityClaimContext,
|
|
81
|
+
} from "./cyclonedx20RenderEvidence.js";
|
|
82
|
+
import {
|
|
83
|
+
CDX20_INTERNAL_SECTIONS_KEY,
|
|
84
|
+
COMPONENT_TYPES_1X,
|
|
85
|
+
FORMULA_COMPONENT_REFS_PROPERTY,
|
|
86
|
+
} from "./cyclonedx20Tables.js";
|
|
87
|
+
|
|
88
|
+
// 2.0-only root sections, carried verbatim for 2.0 output.
|
|
89
|
+
const CARRIED_SECTIONS = Object.freeze([
|
|
90
|
+
"risks",
|
|
91
|
+
"blueprints",
|
|
92
|
+
"threats",
|
|
93
|
+
"controls",
|
|
94
|
+
"perspectives",
|
|
95
|
+
"profiles",
|
|
96
|
+
"declarations",
|
|
97
|
+
"signatures",
|
|
98
|
+
]);
|
|
99
|
+
const ROOT_KEYS = Object.freeze(
|
|
100
|
+
new Set([
|
|
101
|
+
"$schema",
|
|
102
|
+
"$comment",
|
|
103
|
+
"specFormat",
|
|
104
|
+
"specVersion",
|
|
105
|
+
"serialNumber",
|
|
106
|
+
"version",
|
|
107
|
+
"metadata",
|
|
108
|
+
"inventories",
|
|
109
|
+
"dependencies",
|
|
110
|
+
"compositions",
|
|
111
|
+
"vulnerabilities",
|
|
112
|
+
"annotations",
|
|
113
|
+
"formulation",
|
|
114
|
+
"definitions",
|
|
115
|
+
"citations",
|
|
116
|
+
"properties",
|
|
117
|
+
"externalReferences",
|
|
118
|
+
...CARRIED_SECTIONS,
|
|
119
|
+
]),
|
|
120
|
+
);
|
|
121
|
+
const INVENTORY_KEYS = Object.freeze(
|
|
122
|
+
new Set(["refId", "subject", "components"]),
|
|
123
|
+
);
|
|
124
|
+
const CARRIED_REASON =
|
|
125
|
+
"2.0-only section with no 1.x field: dropped from 1.x and SPDX output (a 2.0 render of the read model keeps it)";
|
|
126
|
+
const DERIVED_REASON =
|
|
127
|
+
"differs from what cdxgen derives from the 1.x model: a 2.0 render replaces it with the derived entry";
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Read a 2.0 document into the internal 1.x-shaped model. Never throws on a
|
|
131
|
+
* schema-valid 2.0 document. Objects nested deeper than MAX_READ_DEPTH are
|
|
132
|
+
* left out, each with a loss. The returned model carries the internal model
|
|
133
|
+
* mark (markInternalModel), which loadBomForAnalysis checks. Every member the reader sees but cannot carry
|
|
134
|
+
* into 1.x is reported in `losses` as `{pointer, reason}`, where `pointer` is
|
|
135
|
+
* an RFC 6901 JSON pointer into the input. Objects whose 1.x form would not
|
|
136
|
+
* render back to the same 2.0 object are kept under the internal sections
|
|
137
|
+
* key, so renderCycloneDx20 restores them while they are unchanged.
|
|
138
|
+
*
|
|
139
|
+
* @param {object} bom20 BOM in the 2.0 shape
|
|
140
|
+
* @returns {{internalBom: object, losses: Array<{pointer: string, reason: string}>}}
|
|
141
|
+
*/
|
|
142
|
+
export function readCycloneDx20(bom20) {
|
|
143
|
+
const { value: doc, truncated } = isObjectRecord(bom20)
|
|
144
|
+
? cloneWithDepthLimit(bom20)
|
|
145
|
+
: { value: {}, truncated: [] };
|
|
146
|
+
const ctx = createReadContext(doc);
|
|
147
|
+
for (const pointer of truncated) {
|
|
148
|
+
addLoss(
|
|
149
|
+
ctx,
|
|
150
|
+
pointer,
|
|
151
|
+
`nested deeper than ${MAX_READ_DEPTH} objects: not read`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
ctx.componentIndex = indexComponents(doc);
|
|
155
|
+
reportUnmapped(ctx, "document", doc, "", ROOT_KEYS);
|
|
156
|
+
const internal = readHeader(doc);
|
|
157
|
+
const metadata = readMetadata(ctx, doc.metadata);
|
|
158
|
+
ctx.claims = identityClaimContext({ metadata });
|
|
159
|
+
const inventory = readInventories(ctx, doc, metadata);
|
|
160
|
+
const placement = classifyDefinitions(ctx, doc);
|
|
161
|
+
readRootDefinitions(ctx, doc, placement, inventory);
|
|
162
|
+
setList(internal, "components", inventory.components);
|
|
163
|
+
setList(internal, "services", inventory.services);
|
|
164
|
+
readDependencies(ctx, doc, internal);
|
|
165
|
+
setList(
|
|
166
|
+
internal,
|
|
167
|
+
"compositions",
|
|
168
|
+
doc.compositions !== undefined
|
|
169
|
+
? readCompositions(ctx, doc.compositions, inventory.subjects)
|
|
170
|
+
: undefined,
|
|
171
|
+
);
|
|
172
|
+
readOptionalSections(ctx, doc, internal, placement);
|
|
173
|
+
if (Object.keys(metadata).length) {
|
|
174
|
+
internal.metadata = metadata;
|
|
175
|
+
}
|
|
176
|
+
flushComponentShadows(ctx);
|
|
177
|
+
flushIdentityShadows(ctx);
|
|
178
|
+
const sections = collectCarriedSections(ctx, doc, placement, internal);
|
|
179
|
+
if (Object.keys(sections).length) {
|
|
180
|
+
internal[CDX20_INTERNAL_SECTIONS_KEY] = sections;
|
|
181
|
+
}
|
|
182
|
+
return { internalBom: markInternalModel(internal), losses: ctx.losses };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Root header: 1.x has no `$schema` for 2.0 (it is carried for 2.0). */
|
|
186
|
+
function readHeader(doc) {
|
|
187
|
+
const internal = { bomFormat: "CycloneDX", specVersion: "2.0" };
|
|
188
|
+
for (const field of ["serialNumber", "version"]) {
|
|
189
|
+
if (doc[field] !== undefined) {
|
|
190
|
+
internal[field] = doc[field];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return internal;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Assign a non-empty list. */
|
|
197
|
+
function setList(target, key, value) {
|
|
198
|
+
if (Array.isArray(value) && value.length) {
|
|
199
|
+
target[key] = value;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Map refId → the first concrete declaration (document order: tools,
|
|
205
|
+
* inventories, definitions), so componentReferences resolve.
|
|
206
|
+
*/
|
|
207
|
+
function indexComponents(doc) {
|
|
208
|
+
const index = new Map();
|
|
209
|
+
const visit = (entry, pointer) => {
|
|
210
|
+
if (!isObjectRecord(entry)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const isReference =
|
|
214
|
+
typeof entry.ref === "string" && entry.name === undefined;
|
|
215
|
+
if (
|
|
216
|
+
!isReference &&
|
|
217
|
+
isNonEmptyString(entry.refId) &&
|
|
218
|
+
!index.has(entry.refId)
|
|
219
|
+
) {
|
|
220
|
+
index.set(entry.refId, { entry, pointer });
|
|
221
|
+
}
|
|
222
|
+
(Array.isArray(entry.components) ? entry.components : []).forEach(
|
|
223
|
+
(child, childIndex) => {
|
|
224
|
+
visit(child, jsonPointer(pointer, "components", childIndex));
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
(doc.metadata?.tools?.components || []).forEach((entry, i) => {
|
|
229
|
+
visit(entry, jsonPointer("/metadata/tools/components", i));
|
|
230
|
+
});
|
|
231
|
+
(Array.isArray(doc.inventories) ? doc.inventories : []).forEach(
|
|
232
|
+
(inventory, i) => {
|
|
233
|
+
visit(inventory?.subject, jsonPointer("/inventories", i, "subject"));
|
|
234
|
+
(inventory?.components || []).forEach((entry, j) => {
|
|
235
|
+
visit(entry, jsonPointer("/inventories", i, "components", j));
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
(doc.definitions?.components || []).forEach((entry, i) => {
|
|
240
|
+
visit(entry, jsonPointer("/definitions/components", i));
|
|
241
|
+
});
|
|
242
|
+
return index;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Push a read entry once per refId; a repeat is a loss. */
|
|
246
|
+
function addInventoryEntry(ctx, inventory, read, pointer) {
|
|
247
|
+
if (!read) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (read.refId !== undefined) {
|
|
251
|
+
if (inventory.seen.has(read.refId)) {
|
|
252
|
+
addLoss(
|
|
253
|
+
ctx,
|
|
254
|
+
pointer,
|
|
255
|
+
"the component is already listed; 1.x lists it once",
|
|
256
|
+
);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
inventory.seen.add(read.refId);
|
|
260
|
+
}
|
|
261
|
+
keepComponentShadow(ctx, read);
|
|
262
|
+
inventory[read.isService ? "services" : "components"].push(read.internal);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Read the inventories: the first subject becomes metadata.component, the
|
|
267
|
+
* components of every inventory become root components and services.
|
|
268
|
+
*/
|
|
269
|
+
function readInventories(ctx, doc, metadata) {
|
|
270
|
+
const inventory = {
|
|
271
|
+
components: [],
|
|
272
|
+
services: [],
|
|
273
|
+
seen: new Set(),
|
|
274
|
+
subjects: new Map(),
|
|
275
|
+
};
|
|
276
|
+
if (doc.inventories !== undefined && !Array.isArray(doc.inventories)) {
|
|
277
|
+
addLoss(ctx, "/inventories", "expected an array");
|
|
278
|
+
return inventory;
|
|
279
|
+
}
|
|
280
|
+
(doc.inventories || []).forEach((entry, index) => {
|
|
281
|
+
const pointer = jsonPointer("/inventories", index);
|
|
282
|
+
if (!isObjectRecord(entry)) {
|
|
283
|
+
addLoss(ctx, pointer, "inventory is not an object");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
reportUnmapped(ctx, "inventory", entry, pointer, INVENTORY_KEYS);
|
|
287
|
+
readInventorySubject(ctx, entry, pointer, metadata, inventory);
|
|
288
|
+
(Array.isArray(entry.components) ? entry.components : []).forEach(
|
|
289
|
+
(component, componentIndex) => {
|
|
290
|
+
const entryPointer = jsonPointer(pointer, "components", componentIndex);
|
|
291
|
+
addInventoryEntry(
|
|
292
|
+
ctx,
|
|
293
|
+
inventory,
|
|
294
|
+
readComponentEntry(ctx, component, entryPointer),
|
|
295
|
+
entryPointer,
|
|
296
|
+
);
|
|
297
|
+
},
|
|
298
|
+
);
|
|
299
|
+
});
|
|
300
|
+
return inventory;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** One inventory subject: the first is metadata.component. */
|
|
304
|
+
function readInventorySubject(ctx, entry, pointer, metadata, inventory) {
|
|
305
|
+
const subjectPointer = jsonPointer(pointer, "subject");
|
|
306
|
+
const read = isObjectRecord(entry.subject)
|
|
307
|
+
? readComponentEntry(ctx, entry.subject, subjectPointer)
|
|
308
|
+
: undefined;
|
|
309
|
+
if (entry.refId !== undefined) {
|
|
310
|
+
inventory.subjects.set(entry.refId, read?.refId);
|
|
311
|
+
}
|
|
312
|
+
if (!read) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (metadata.component === undefined && !read.isService) {
|
|
316
|
+
keepComponentShadow(ctx, read);
|
|
317
|
+
metadata.component = read.internal;
|
|
318
|
+
if (read.refId !== undefined) {
|
|
319
|
+
inventory.seen.add(read.refId);
|
|
320
|
+
}
|
|
321
|
+
// The renderer derives the inventory refId from the subject; keep the
|
|
322
|
+
// inventory's own members for the 2.0 round trip.
|
|
323
|
+
const { subject: _subject, components: _components, ...own } = entry;
|
|
324
|
+
registerShadow(ctx, "inventory", read.internal, own);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
addLoss(
|
|
328
|
+
ctx,
|
|
329
|
+
subjectPointer,
|
|
330
|
+
"1.x has one metadata.component; this inventory subject is read as a root component",
|
|
331
|
+
);
|
|
332
|
+
addInventoryEntry(ctx, inventory, read, subjectPointer);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** `ref` values of componentReference-shaped objects under `node`. */
|
|
336
|
+
function collectReferences(node, into = new Set()) {
|
|
337
|
+
if (Array.isArray(node)) {
|
|
338
|
+
for (const value of node) {
|
|
339
|
+
collectReferences(value, into);
|
|
340
|
+
}
|
|
341
|
+
} else if (isObjectRecord(node)) {
|
|
342
|
+
if (typeof node.ref === "string" && node.name === undefined) {
|
|
343
|
+
into.add(node.ref);
|
|
344
|
+
}
|
|
345
|
+
for (const value of Object.values(node)) {
|
|
346
|
+
collectReferences(value, into);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return into;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The refIds the componentReferences the reader resolves into 1.x name:
|
|
354
|
+
* those under the inventories, metadata (tools), annotations,
|
|
355
|
+
* vulnerabilities and the definitions components themselves. References from
|
|
356
|
+
* 2.0-only sections do not count, since 1.x drops those sections.
|
|
357
|
+
*/
|
|
358
|
+
function resolvedReferences(doc) {
|
|
359
|
+
const refs = new Set();
|
|
360
|
+
for (const section of [
|
|
361
|
+
doc.inventories,
|
|
362
|
+
doc.metadata,
|
|
363
|
+
doc.annotations,
|
|
364
|
+
doc.vulnerabilities,
|
|
365
|
+
doc.definitions?.components,
|
|
366
|
+
]) {
|
|
367
|
+
collectReferences(section, refs);
|
|
368
|
+
}
|
|
369
|
+
return refs;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** Where one definitions entry goes, per the rule in the module comment. */
|
|
373
|
+
function placeDefinition(entry, facts) {
|
|
374
|
+
if (
|
|
375
|
+
isObjectRecord(entry) &&
|
|
376
|
+
typeof entry.type === "string" &&
|
|
377
|
+
entry.type !== "service" &&
|
|
378
|
+
entry.type !== "component-choice" &&
|
|
379
|
+
!COMPONENT_TYPES_1X.has(entry.type)
|
|
380
|
+
) {
|
|
381
|
+
return { place: "carried", reason: "type" };
|
|
382
|
+
}
|
|
383
|
+
const refId = isNonEmptyString(entry?.refId) ? entry.refId : undefined;
|
|
384
|
+
if (refId !== undefined) {
|
|
385
|
+
const listed = facts.formulaMembers
|
|
386
|
+
.map((refs, index) => (refs.has(refId) ? index : -1))
|
|
387
|
+
.filter((index) => index >= 0);
|
|
388
|
+
if (listed.length) {
|
|
389
|
+
return { place: "formula", formula: listed[0], alsoIn: listed.slice(1) };
|
|
390
|
+
}
|
|
391
|
+
const formula = facts.formulaRefs.findIndex((refs) => refs.has(refId));
|
|
392
|
+
if (formula >= 0) {
|
|
393
|
+
return { place: "formula", formula };
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (facts.hasInventories) {
|
|
397
|
+
if (refId !== undefined && facts.referenced.has(refId)) {
|
|
398
|
+
return { place: "carried" };
|
|
399
|
+
}
|
|
400
|
+
return facts.hasFormulation
|
|
401
|
+
? { place: "formula", formula: 0, unreferenced: true }
|
|
402
|
+
: { place: "carried", reason: "unreferenced" };
|
|
403
|
+
}
|
|
404
|
+
return { place: "root" };
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const PLACE_ORDER = Object.freeze({ carried: 0, root: 1, formula: 2 });
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* The refIds a formula lists in its FORMULA_COMPONENT_REFS_PROPERTY property;
|
|
411
|
+
* a value that is not a JSON array of strings lists none.
|
|
412
|
+
*/
|
|
413
|
+
function formulaMembers(formula) {
|
|
414
|
+
const members = new Set();
|
|
415
|
+
const properties = Array.isArray(formula?.properties)
|
|
416
|
+
? formula.properties
|
|
417
|
+
: [];
|
|
418
|
+
for (const property of properties) {
|
|
419
|
+
if (
|
|
420
|
+
property?.name !== FORMULA_COMPONENT_REFS_PROPERTY ||
|
|
421
|
+
typeof property.value !== "string"
|
|
422
|
+
) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
let refIds;
|
|
426
|
+
try {
|
|
427
|
+
refIds = JSON.parse(property.value);
|
|
428
|
+
} catch {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (Array.isArray(refIds)) {
|
|
432
|
+
for (const refId of refIds) {
|
|
433
|
+
if (isNonEmptyString(refId)) {
|
|
434
|
+
members.add(refId);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return members;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** Classify every definitions.components entry. */
|
|
443
|
+
function classifyDefinitions(ctx, doc) {
|
|
444
|
+
const entries = Array.isArray(doc.definitions?.components)
|
|
445
|
+
? doc.definitions.components
|
|
446
|
+
: [];
|
|
447
|
+
const formulation = Array.isArray(doc.formulation) ? doc.formulation : [];
|
|
448
|
+
const facts = {
|
|
449
|
+
formulaRefs: formulation.map((formula) => collectReferences(formula)),
|
|
450
|
+
formulaMembers: formulation.map(formulaMembers),
|
|
451
|
+
referenced: resolvedReferences(doc),
|
|
452
|
+
hasInventories:
|
|
453
|
+
Array.isArray(doc.inventories) && doc.inventories.length > 0,
|
|
454
|
+
hasFormulation: formulation.length > 0,
|
|
455
|
+
};
|
|
456
|
+
const placement = entries.map((entry) => placeDefinition(entry, facts));
|
|
457
|
+
let rank = 0;
|
|
458
|
+
placement.forEach((placed, index) => {
|
|
459
|
+
const pointer = jsonPointer("/definitions/components", index);
|
|
460
|
+
if (placed.reason === "type") {
|
|
461
|
+
addLoss(
|
|
462
|
+
ctx,
|
|
463
|
+
jsonPointer(pointer, "type"),
|
|
464
|
+
`component type '${doc.definitions.components[index].type}' has no 1.x equivalent: dropped from 1.x and SPDX output (a 2.0 render of the read model keeps it)`,
|
|
465
|
+
);
|
|
466
|
+
} else if (placed.reason === "unreferenced") {
|
|
467
|
+
addLoss(
|
|
468
|
+
ctx,
|
|
469
|
+
pointer,
|
|
470
|
+
"a definitions component nothing references has no 1.x home",
|
|
471
|
+
);
|
|
472
|
+
} else if (placed.unreferenced && formulation.length > 1) {
|
|
473
|
+
addLoss(
|
|
474
|
+
ctx,
|
|
475
|
+
pointer,
|
|
476
|
+
"formula membership is not recorded; read into the first formula",
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
if (PLACE_ORDER[placed.place] < rank) {
|
|
480
|
+
addLoss(ctx, pointer, "definitions.components order changes on render");
|
|
481
|
+
}
|
|
482
|
+
rank = Math.max(rank, PLACE_ORDER[placed.place]);
|
|
483
|
+
});
|
|
484
|
+
return placement;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Root-placed definitions entries become root components and services. */
|
|
488
|
+
function readRootDefinitions(ctx, doc, placement, inventory) {
|
|
489
|
+
placement.forEach((placed, index) => {
|
|
490
|
+
if (placed.place !== "root") {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const pointer = jsonPointer("/definitions/components", index);
|
|
494
|
+
addInventoryEntry(
|
|
495
|
+
ctx,
|
|
496
|
+
inventory,
|
|
497
|
+
readComponentEntry(ctx, doc.definitions.components[index], pointer),
|
|
498
|
+
pointer,
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** dependencies: ref, dependsOn and provides keep their names. */
|
|
504
|
+
function readDependencies(ctx, doc, internal) {
|
|
505
|
+
if (doc.dependencies === undefined) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (!Array.isArray(doc.dependencies)) {
|
|
509
|
+
addLoss(ctx, "/dependencies", "expected an array");
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
internal.dependencies = doc.dependencies
|
|
513
|
+
.map((dependency, index) =>
|
|
514
|
+
isObjectRecord(dependency)
|
|
515
|
+
? reverseFields(
|
|
516
|
+
ctx,
|
|
517
|
+
"dependency",
|
|
518
|
+
dependency,
|
|
519
|
+
jsonPointer("/dependencies", index),
|
|
520
|
+
{
|
|
521
|
+
ref: true,
|
|
522
|
+
dependsOn: true,
|
|
523
|
+
provides: true,
|
|
524
|
+
},
|
|
525
|
+
)
|
|
526
|
+
: undefined,
|
|
527
|
+
)
|
|
528
|
+
.filter((dependency) => typeof dependency?.ref === "string");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** Formula components go back into their formula. */
|
|
532
|
+
function attachFormulaComponents(ctx, doc, formulation, placement) {
|
|
533
|
+
placement.forEach((placed, index) => {
|
|
534
|
+
if (placed.place !== "formula" || !formulation[placed.formula]) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const read = readComponentEntry(
|
|
538
|
+
ctx,
|
|
539
|
+
doc.definitions.components[index],
|
|
540
|
+
jsonPointer("/definitions/components", index),
|
|
541
|
+
);
|
|
542
|
+
if (read) {
|
|
543
|
+
keepComponentShadow(ctx, read);
|
|
544
|
+
const member = read.isService ? "services" : "components";
|
|
545
|
+
(formulation[placed.formula][member] ||= []).push(read.internal);
|
|
546
|
+
for (const other of placed.alsoIn || []) {
|
|
547
|
+
if (formulation[other]) {
|
|
548
|
+
(formulation[other][member] ||= []).push(clone(read.internal));
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/** annotations, formulation, citations, vulnerabilities and root lists. */
|
|
556
|
+
function readOptionalSections(ctx, doc, internal, placement) {
|
|
557
|
+
if (doc.annotations !== undefined) {
|
|
558
|
+
setList(internal, "annotations", readAnnotations(ctx, doc.annotations));
|
|
559
|
+
}
|
|
560
|
+
if (doc.formulation !== undefined) {
|
|
561
|
+
const formulation = readFormulation(ctx, doc.formulation) || [];
|
|
562
|
+
attachFormulaComponents(ctx, doc, formulation, placement);
|
|
563
|
+
setList(internal, "formulation", formulation);
|
|
564
|
+
}
|
|
565
|
+
if (doc.citations !== undefined) {
|
|
566
|
+
setList(internal, "citations", readCitations(ctx, doc.citations));
|
|
567
|
+
}
|
|
568
|
+
if (doc.vulnerabilities !== undefined) {
|
|
569
|
+
setList(
|
|
570
|
+
internal,
|
|
571
|
+
"vulnerabilities",
|
|
572
|
+
readVulnerabilities(ctx, doc.vulnerabilities),
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
for (const [key, rule] of [
|
|
576
|
+
["properties", PROPERTIES_RULE],
|
|
577
|
+
["externalReferences", EXTERNAL_REFERENCES_RULE],
|
|
578
|
+
]) {
|
|
579
|
+
if (doc[key] !== undefined) {
|
|
580
|
+
setList(internal, key, rule.convert(ctx, doc[key], jsonPointer("", key)));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* How many trailing definitions.parties entries are parties renderCycloneDx20
|
|
587
|
+
* derives again from the read 1.x model (derivedPartyDeclarations): the
|
|
588
|
+
* renderer appends, in its order, each derived party the finished document
|
|
589
|
+
* references and does not declare yet. The longest such tail that renders
|
|
590
|
+
* back identically is dropped by the reader, so a derived party neither
|
|
591
|
+
* reaches the internal model nor goes stale when the 1.x tools change. With
|
|
592
|
+
* no matching tail the count is 0 and every party is carried.
|
|
593
|
+
*/
|
|
594
|
+
function countDerivedParties(ctx, doc) {
|
|
595
|
+
const parties = doc.definitions.parties;
|
|
596
|
+
if (!Array.isArray(parties) || !parties.length) {
|
|
597
|
+
return 0;
|
|
598
|
+
}
|
|
599
|
+
const { parties: _parties, ...definitions } = doc.definitions;
|
|
600
|
+
const rest = { ...doc, definitions };
|
|
601
|
+
const candidates = derivedPartyDeclarations(
|
|
602
|
+
ctx.claims,
|
|
603
|
+
collectDeclaredRefIds([rest, parties]),
|
|
604
|
+
).filter((party) => referencesParty(rest, party.refId));
|
|
605
|
+
for (let count = candidates.length; count > 0; count--) {
|
|
606
|
+
if (count > parties.length) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
const remaining = parties.slice(0, parties.length - count);
|
|
610
|
+
const remainingRefIds = new Set(remaining.map((party) => party?.refId));
|
|
611
|
+
const expected = derivedPartyDeclarations(
|
|
612
|
+
ctx.claims,
|
|
613
|
+
collectDeclaredRefIds([rest, remaining]),
|
|
614
|
+
).filter(
|
|
615
|
+
(party) =>
|
|
616
|
+
referencesParty(rest, party.refId) && !remainingRefIds.has(party.refId),
|
|
617
|
+
);
|
|
618
|
+
if (isDeepStrictEqual(expected, parties.slice(remaining.length))) {
|
|
619
|
+
return count;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return 0;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* The 2.0 value of a carried root section without the entries the renderer
|
|
627
|
+
* derives (cyclonedx20Derived.js): those that match the derivation from the
|
|
628
|
+
* read model are dropped silently, those that differ with a loss. Returns
|
|
629
|
+
* undefined when nothing else is left.
|
|
630
|
+
*/
|
|
631
|
+
function carriedSectionValue(ctx, doc, section, derived) {
|
|
632
|
+
const value = doc[section];
|
|
633
|
+
if (!derived.cdxgenListed) {
|
|
634
|
+
// cdxgen derives nothing for another tool's document, so entries that
|
|
635
|
+
// look derived (risk-pqc-*, party-agent-*, the agent blueprint) are that
|
|
636
|
+
// tool's own and are carried like any other.
|
|
637
|
+
return value;
|
|
638
|
+
}
|
|
639
|
+
if (section === "blueprints" && Array.isArray(value)) {
|
|
640
|
+
const { kept, differing } = splitDerivedEntries(
|
|
641
|
+
value,
|
|
642
|
+
derived.blueprints,
|
|
643
|
+
isDerivedBlueprint,
|
|
644
|
+
);
|
|
645
|
+
differing.forEach((index) => {
|
|
646
|
+
addLoss(ctx, jsonPointer("/blueprints", index), DERIVED_REASON);
|
|
647
|
+
});
|
|
648
|
+
return kept.length || !value.length ? kept : undefined;
|
|
649
|
+
}
|
|
650
|
+
if (section === "risks" && Array.isArray(value?.risks)) {
|
|
651
|
+
const { kept, differing } = splitDerivedEntries(
|
|
652
|
+
value.risks,
|
|
653
|
+
derived.risks,
|
|
654
|
+
isDerivedRisk,
|
|
655
|
+
);
|
|
656
|
+
differing.forEach((index) => {
|
|
657
|
+
addLoss(ctx, jsonPointer("/risks/risks", index), DERIVED_REASON);
|
|
658
|
+
});
|
|
659
|
+
const { risks: _risks, ...members } = value;
|
|
660
|
+
const rest =
|
|
661
|
+
kept.length || !value.risks.length
|
|
662
|
+
? { ...clone(members), risks: kept }
|
|
663
|
+
: clone(members);
|
|
664
|
+
return Object.keys(rest).length ? rest : undefined;
|
|
665
|
+
}
|
|
666
|
+
return value;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Carry the definitions parties the renderer does not derive again: the
|
|
671
|
+
* trailing claim parties (countDerivedParties) and the agent parties that
|
|
672
|
+
* match the derivation are dropped silently, a differing agent party with a
|
|
673
|
+
* loss; every other party is carried with a loss, since 1.x has no home for
|
|
674
|
+
* it.
|
|
675
|
+
*/
|
|
676
|
+
function carryDefinitionsParties(ctx, doc, sections, derived) {
|
|
677
|
+
const parties = doc.definitions.parties;
|
|
678
|
+
if (!Array.isArray(parties)) {
|
|
679
|
+
sections.definitionsParties = parties;
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
const derivedAgents = new Map(
|
|
683
|
+
derived.agentParties.map((party) => [party.refId, party]),
|
|
684
|
+
);
|
|
685
|
+
const kept = [];
|
|
686
|
+
parties
|
|
687
|
+
.slice(0, parties.length - countDerivedParties(ctx, doc))
|
|
688
|
+
.forEach((party, index) => {
|
|
689
|
+
const pointer = jsonPointer("/definitions/parties", index);
|
|
690
|
+
if (!derived.cdxgenListed || !isDerivedAgentParty(party)) {
|
|
691
|
+
kept.push(party);
|
|
692
|
+
addLoss(ctx, pointer, CARRIED_REASON);
|
|
693
|
+
} else if (!isDeepStrictEqual(derivedAgents.get(party.refId), party)) {
|
|
694
|
+
addLoss(ctx, pointer, DERIVED_REASON);
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
if (kept.length || !parties.length) {
|
|
698
|
+
sections.definitionsParties = kept;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* The 2.0-only parts kept under the internal sections key: root sections,
|
|
704
|
+
* definitions other than the components read into 1.x, `$schema` and
|
|
705
|
+
* `$comment`, and the shadows. Entries the renderer derives from the model
|
|
706
|
+
* (post-quantum risks, agent parties and blueprint) are not carried.
|
|
707
|
+
*/
|
|
708
|
+
function collectCarriedSections(ctx, doc, placement, internal) {
|
|
709
|
+
const sections = {};
|
|
710
|
+
const derived = {
|
|
711
|
+
...deriveCycloneDx20Sections(internal),
|
|
712
|
+
cdxgenListed: isCdxgenListedTool(internal),
|
|
713
|
+
};
|
|
714
|
+
for (const section of CARRIED_SECTIONS) {
|
|
715
|
+
if (doc[section] === undefined) {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
const value = carriedSectionValue(ctx, doc, section, derived);
|
|
719
|
+
if (value !== undefined) {
|
|
720
|
+
sections[section] = value;
|
|
721
|
+
addLoss(ctx, jsonPointer("", section), CARRIED_REASON);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (isObjectRecord(doc.definitions)) {
|
|
725
|
+
const { components, parties, ...rest } = doc.definitions;
|
|
726
|
+
if (parties !== undefined) {
|
|
727
|
+
carryDefinitionsParties(ctx, doc, sections, derived);
|
|
728
|
+
}
|
|
729
|
+
const carriedComponents = (
|
|
730
|
+
Array.isArray(components) ? components : []
|
|
731
|
+
).filter((_entry, index) => placement[index]?.place === "carried");
|
|
732
|
+
if (
|
|
733
|
+
carriedComponents.length ||
|
|
734
|
+
(Array.isArray(components) && !components.length)
|
|
735
|
+
) {
|
|
736
|
+
rest.components = carriedComponents;
|
|
737
|
+
}
|
|
738
|
+
for (const key of Object.keys(rest)) {
|
|
739
|
+
if (key !== "components") {
|
|
740
|
+
addLoss(ctx, jsonPointer("/definitions", key), CARRIED_REASON);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (Object.keys(rest).length) {
|
|
744
|
+
sections.definitions = rest;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
// `$schema`, `$comment` and `ext:` extension members (reported as losses
|
|
748
|
+
// by the root allowlist, except `$schema`) ride along for 2.0 output.
|
|
749
|
+
const root = {};
|
|
750
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
751
|
+
if (key === "$schema" || key === "$comment" || key.startsWith("ext:")) {
|
|
752
|
+
root[key] = value;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
if (doc.$comment !== undefined) {
|
|
756
|
+
addLoss(ctx, "/$comment", "$comment has no 1.x field");
|
|
757
|
+
}
|
|
758
|
+
if (Object.keys(root).length) {
|
|
759
|
+
sections.root = root;
|
|
760
|
+
}
|
|
761
|
+
if (ctx.carriedCompositions?.length) {
|
|
762
|
+
sections.compositions = ctx.carriedCompositions;
|
|
763
|
+
}
|
|
764
|
+
if (Object.keys(ctx.shadows).length) {
|
|
765
|
+
sections.shadows = ctx.shadows;
|
|
766
|
+
}
|
|
767
|
+
return sections;
|
|
768
|
+
}
|