@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
package/lib/cli/index.js
CHANGED
|
@@ -50,9 +50,14 @@ import {
|
|
|
50
50
|
buildDependencyTrackBomRequest,
|
|
51
51
|
getDependencyTrackBomApiUrl,
|
|
52
52
|
} from "../helpers/remote/dependency-track.js";
|
|
53
|
+
import { collectObomIdeInventory } from "../helpers/vsixutils.js";
|
|
53
54
|
import { optionIncludesAiInventoryProjectType } from "../inventory/aiInventory.js";
|
|
54
55
|
import { readApkInstalledDb } from "../inventory/apkInstalledDb.js";
|
|
55
|
-
import {
|
|
56
|
+
import {
|
|
57
|
+
isCycloneDx20SpecVersion,
|
|
58
|
+
isCycloneDxComponentTypeEnabled,
|
|
59
|
+
} from "../inventory/bomUtils.js";
|
|
60
|
+
import { DEPENDENCY_TRACK_CDX20_ERROR } from "../inventory/cyclonedx20.js";
|
|
56
61
|
import {
|
|
57
62
|
mergeDependencies,
|
|
58
63
|
mergeServices,
|
|
@@ -70,16 +75,28 @@ import {
|
|
|
70
75
|
hasHbomProjectType,
|
|
71
76
|
} from "../inventory/hbom.js";
|
|
72
77
|
import { mergeHostInventoryBoms } from "../inventory/hostTopology.js";
|
|
78
|
+
import { probeHostNode } from "../inventory/nodeHostProbe.js";
|
|
79
|
+
import { markOsInstalledLanguagePackages } from "../inventory/osPackageResolver.js";
|
|
73
80
|
import { orderParentCandidates } from "../inventory/parentSelection.js";
|
|
74
81
|
import { probeHostPhp } from "../inventory/phpHostProbe.js";
|
|
75
82
|
// Imported directly rather than through the utils.js barrel: purl validity is
|
|
76
83
|
// checked in a hot path and the barrel is already the root of several import
|
|
77
84
|
// cycles.
|
|
78
85
|
import { applyPurl, ociPurl } from "../inventory/purl.js";
|
|
86
|
+
import {
|
|
87
|
+
deriveOsPackagedRubyRuntimes,
|
|
88
|
+
linkDefaultGemsToRuntimes,
|
|
89
|
+
} from "../inventory/rubyBuildInfo.js";
|
|
90
|
+
import { probeHostRuby } from "../inventory/rubyHostProbe.js";
|
|
79
91
|
import { table } from "../inventory/table.js";
|
|
80
92
|
|
|
81
93
|
export { summarizeAiInventory } from "../inventory/aiInventory.js";
|
|
82
94
|
|
|
95
|
+
import { probeHostRubyGems } from "../ecosystems/gemHostProbe.js";
|
|
96
|
+
import {
|
|
97
|
+
mergeHostPythonEnvironmentPackages,
|
|
98
|
+
probeHostPythonEnvironments,
|
|
99
|
+
} from "../ecosystems/venvProbe.js";
|
|
83
100
|
import {
|
|
84
101
|
enrichOSComponentsWithTrustData,
|
|
85
102
|
executeOsQuery,
|
|
@@ -240,6 +257,28 @@ switch (_platform()) {
|
|
|
240
257
|
break;
|
|
241
258
|
}
|
|
242
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Home directories whose editor extension folders an OBOM reads: every user's
|
|
262
|
+
* home from osquery when cdxgen runs as root, and otherwise the current
|
|
263
|
+
* user's, which is the only one it can read.
|
|
264
|
+
*
|
|
265
|
+
* @returns {string[]} Home directories
|
|
266
|
+
*/
|
|
267
|
+
function obomUserHomes() {
|
|
268
|
+
const ownHome = homedir();
|
|
269
|
+
if (typeof process.getuid !== "function" || process.getuid() !== 0) {
|
|
270
|
+
return [ownHome];
|
|
271
|
+
}
|
|
272
|
+
const homes = new Set([ownHome]);
|
|
273
|
+
for (const row of executeOsQuery("SELECT directory FROM users;") || []) {
|
|
274
|
+
const dir = row?.directory;
|
|
275
|
+
if (typeof dir === "string" && dir.startsWith("/") && safeExistsSync(dir)) {
|
|
276
|
+
homes.add(dir);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return [...homes];
|
|
280
|
+
}
|
|
281
|
+
|
|
243
282
|
/**
|
|
244
283
|
* Function to create obom string for the current OS using osquery
|
|
245
284
|
*
|
|
@@ -247,7 +286,7 @@ switch (_platform()) {
|
|
|
247
286
|
* @param {Object} options Parse options from the cli
|
|
248
287
|
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
249
288
|
*/
|
|
250
|
-
export function createOSBom(_path, options) {
|
|
289
|
+
export async function createOSBom(_path, options) {
|
|
251
290
|
console.warn(
|
|
252
291
|
"About to generate OBOM for the current OS installation. This will take several minutes ...",
|
|
253
292
|
);
|
|
@@ -309,6 +348,53 @@ export function createOSBom(_path, options) {
|
|
|
309
348
|
if (hostPhp.components.length) {
|
|
310
349
|
pkgList = pkgList.concat(hostPhp.components);
|
|
311
350
|
}
|
|
351
|
+
// Nor a Node.js from a release tarball or a version manager, or the Brotli
|
|
352
|
+
// decoder compiled into it.
|
|
353
|
+
const hostNode = probeHostNode({
|
|
354
|
+
executeOsQuery,
|
|
355
|
+
packageComponents: pkgList,
|
|
356
|
+
});
|
|
357
|
+
if (hostNode.components.length) {
|
|
358
|
+
pkgList = pkgList.concat(hostNode.components);
|
|
359
|
+
}
|
|
360
|
+
// Nor a Ruby from ruby-build, a version manager or a toolcache tarball.
|
|
361
|
+
const hostRuby = probeHostRuby({
|
|
362
|
+
executeOsQuery,
|
|
363
|
+
packageComponents: pkgList,
|
|
364
|
+
});
|
|
365
|
+
if (hostRuby.components.length) {
|
|
366
|
+
pkgList = pkgList.concat(hostRuby.components);
|
|
367
|
+
}
|
|
368
|
+
// A Ruby a package manager installed is also named by its upstream version.
|
|
369
|
+
pkgList = pkgList.concat(deriveOsPackagedRubyRuntimes(pkgList));
|
|
370
|
+
// osquery has no table for RubyGems; the gems of the host's gem homes are
|
|
371
|
+
// read from their specifications.
|
|
372
|
+
const hostGems = await probeHostRubyGems({
|
|
373
|
+
executeOsQuery,
|
|
374
|
+
packageComponents: pkgList,
|
|
375
|
+
});
|
|
376
|
+
if (hostGems.length) {
|
|
377
|
+
const knownRefs = new Set(pkgList.map((component) => component["bom-ref"]));
|
|
378
|
+
pkgList = pkgList.concat(
|
|
379
|
+
hostGems.filter((gem) => !knownRefs.has(gem["bom-ref"])),
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
// osquery reads the interpreter's own site-packages; packages installed into
|
|
383
|
+
// a virtual environment or a pyenv interpreter are read from their METADATA.
|
|
384
|
+
pkgList = mergeHostPythonEnvironmentPackages(
|
|
385
|
+
pkgList,
|
|
386
|
+
probeHostPythonEnvironments(),
|
|
387
|
+
);
|
|
388
|
+
// osquery reads the extension registry of a fixed list of editors; the
|
|
389
|
+
// editors themselves, and the extensions of the others, come from the
|
|
390
|
+
// extension folders. A root scan reads every user's home, as osquery does.
|
|
391
|
+
try {
|
|
392
|
+
pkgList = pkgList.concat(
|
|
393
|
+
collectObomIdeInventory(pkgList, { homes: obomUserHomes() }),
|
|
394
|
+
);
|
|
395
|
+
} catch (err) {
|
|
396
|
+
thoughtLog(`Unable to read the editor extension folders: ${err.message}`);
|
|
397
|
+
}
|
|
312
398
|
const hostTrustInventory = enrichOSComponentsWithTrustData(pkgList);
|
|
313
399
|
if (hostTrustInventory?.components?.length) {
|
|
314
400
|
pkgList = hostTrustInventory.components;
|
|
@@ -770,13 +856,52 @@ export function summarizePurlTypes(components) {
|
|
|
770
856
|
* data object (components, dependencies, bomJson, parentComponent, etc.)
|
|
771
857
|
*/
|
|
772
858
|
export async function createMultiXBom(pathList, options) {
|
|
859
|
+
/**
|
|
860
|
+
* What createMultiXBom reads from the result of a create*Bom builder.
|
|
861
|
+
*
|
|
862
|
+
* @typedef {Object} BuilderBomData
|
|
863
|
+
* @property {{components?: Object[], dependencies?: Object[], services?: Object[]}} [bomJson] The generated BOM
|
|
864
|
+
* @property {Object} [parentComponent] The project the builder detected
|
|
865
|
+
* @property {Object[]} [dependencies] Dependencies returned beside bomJson (crypto scan)
|
|
866
|
+
* @property {Object[]} [formulationList] Formulation components for postProcess
|
|
867
|
+
* @property {Object[]} [metadataProperties] Properties for metadata (php platform data)
|
|
868
|
+
*/
|
|
869
|
+
|
|
870
|
+
/**
|
|
871
|
+
* One ecosystem that createMultiXBom scans every path for.
|
|
872
|
+
*
|
|
873
|
+
* @typedef {Object} PathScanStep
|
|
874
|
+
* @property {(path: string, options: Object) => boolean} applies Whether the step runs for this path
|
|
875
|
+
* @property {(path: string, options: Object) => any} [prepare] Computes a value once for the hooks below
|
|
876
|
+
* @property {(path: string, options: Object, context: any) => void} [announce] Thought logs written before the scan
|
|
877
|
+
* @property {string|((context: any) => string)} activityType Project type recorded in the activity context
|
|
878
|
+
* @property {(path: string) => void} [beforeBuild] Runs after the activity context is set
|
|
879
|
+
* @property {(path: string, options: Object) => any} build The create*Bom builder, whose result is read as BuilderBomData
|
|
880
|
+
* @property {(bomData: BuilderBomData | undefined) => boolean} [isUseful] Whether the result is merged; by default, when it has components
|
|
881
|
+
* @property {(count: number, path: string, context: any) => string} [foundThought] Thought log for the components found
|
|
882
|
+
* @property {(count: number, path: string, context: any) => string} debugMessage Debug log for the components found
|
|
883
|
+
* @property {"concat"|"topLevel"|"mergeWithParent"} [dependencyMerge] How dependencies are merged; mergeDependencies by default
|
|
884
|
+
* @property {boolean} [mergeServices] Merge bomJson.services into options.services
|
|
885
|
+
* @property {boolean} [keepParent] Record the detected project as a sub-component; true by default
|
|
886
|
+
* @property {"plain"|"gradle"} [hoistSubComponents] Hoist the project's own sub-components
|
|
887
|
+
* @property {boolean} [collectMetadataProperties] Collect metadataProperties whether or not components were found
|
|
888
|
+
* @property {boolean} [collectFormulation] Collect the formulation list whether or not components were found
|
|
889
|
+
*/
|
|
890
|
+
|
|
891
|
+
/** @type {Object[]} */
|
|
773
892
|
let components = [];
|
|
893
|
+
/** @type {Object[]} */
|
|
774
894
|
let dependencies = [];
|
|
895
|
+
/** @type {BuilderBomData | undefined} */
|
|
775
896
|
let bomData;
|
|
897
|
+
/** @type {Object[]} */
|
|
776
898
|
let formulationList = [];
|
|
899
|
+
/** @type {Object[]} */
|
|
777
900
|
let metadataProperties = [];
|
|
778
901
|
let parentComponent = determineParentComponent(options) || {};
|
|
902
|
+
/** @type {Object[]} */
|
|
779
903
|
let parentSubComponents = [];
|
|
904
|
+
let osFileOwners;
|
|
780
905
|
const setProjectTypeActivityContext = (projectType, sourcePath) => {
|
|
781
906
|
setActivityContext({
|
|
782
907
|
projectType,
|
|
@@ -797,6 +922,8 @@ export async function createMultiXBom(pathList, options) {
|
|
|
797
922
|
const {
|
|
798
923
|
osPackages,
|
|
799
924
|
osPackageFiles,
|
|
925
|
+
nativeExtensionFiles,
|
|
926
|
+
ownerByPath,
|
|
800
927
|
dependenciesList,
|
|
801
928
|
allTypes,
|
|
802
929
|
bundledSdks,
|
|
@@ -811,6 +938,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
811
938
|
options.exportData?.inspectData?.Config,
|
|
812
939
|
options,
|
|
813
940
|
);
|
|
941
|
+
osFileOwners = ownerByPath;
|
|
814
942
|
osPhase.succeed(
|
|
815
943
|
summarizeCounts([
|
|
816
944
|
[osPackages.length, "OS package", "OS packages"],
|
|
@@ -859,6 +987,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
859
987
|
components = components.concat(osPackageFiles || []);
|
|
860
988
|
components = components.concat(executables);
|
|
861
989
|
components = components.concat(sharedLibs);
|
|
990
|
+
components = components.concat(nativeExtensionFiles || []);
|
|
862
991
|
if (dependenciesList?.length) {
|
|
863
992
|
dependencies = mergeDependencies(dependencies, dependenciesList);
|
|
864
993
|
}
|
|
@@ -871,945 +1000,602 @@ export async function createMultiXBom(pathList, options) {
|
|
|
871
1000
|
osPackages.concat(osPackageFiles || []).map((p) => p["bom-ref"]),
|
|
872
1001
|
);
|
|
873
1002
|
dependencies.splice(0, 0, {
|
|
874
|
-
ref: parentComponent["bom-ref"],
|
|
875
|
-
dependsOn: Array.from(parentDependsOn).sort(),
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
if (hasAnyProjectType(["os"], options, false) && options.bomData) {
|
|
880
|
-
bomData = options.bomData;
|
|
881
|
-
if (bomData?.bomJson?.components) {
|
|
882
|
-
if (DEBUG_MODE) {
|
|
883
|
-
console.log(`Found ${bomData.bomJson.components.length} OS components`);
|
|
884
|
-
}
|
|
885
|
-
if (bomData.bomJson.components.length) {
|
|
886
|
-
thoughtLog(
|
|
887
|
-
`I found ${bomData.bomJson.components.length} OS packages 😎.`,
|
|
888
|
-
);
|
|
889
|
-
}
|
|
890
|
-
components = components.concat(bomData.bomJson.components);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
const sharedOptions = options;
|
|
894
|
-
// The exploded image layers are written once by the extractor and only read
|
|
895
|
-
// from here on, so repeated walks of them can share one view of the tree.
|
|
896
|
-
setDirWalkCacheRoot(options.allLayersExplodedDir);
|
|
897
|
-
const scanOnePath = async (path) => {
|
|
898
|
-
try {
|
|
899
|
-
return await scanOnePathInner(path);
|
|
900
|
-
} catch (err) {
|
|
901
|
-
if (!isDeferredFailOnError(err)) {
|
|
902
|
-
throw err;
|
|
903
|
-
}
|
|
904
|
-
// The failing path contributes nothing, and the remaining paths still
|
|
905
|
-
// do: fail-on-error scopes the loss to the leg that failed.
|
|
906
|
-
return {
|
|
907
|
-
components: [],
|
|
908
|
-
dependencies: [],
|
|
909
|
-
options: { ...sharedOptions },
|
|
910
|
-
parentSubComponents: [],
|
|
911
|
-
metadataProperties: [],
|
|
912
|
-
};
|
|
913
|
-
}
|
|
914
|
-
};
|
|
915
|
-
const scanOnePathInner = async (path) => {
|
|
916
|
-
// Each path accumulates into its own lists and its own view of options, so
|
|
917
|
-
// that legs which record a parent component or a tool cannot overwrite
|
|
918
|
-
// another path's. The lists are merged below in pathList order.
|
|
919
|
-
const options = { ...sharedOptions };
|
|
920
|
-
let bomData;
|
|
921
|
-
let components = [];
|
|
922
|
-
let dependencies = [];
|
|
923
|
-
let parentSubComponents = [];
|
|
924
|
-
let metadataProperties = [];
|
|
925
|
-
// Each builder detects the project of its own ecosystem. A builder that sets
|
|
926
|
-
// options.parentComponent to force its project into its own BOM must not
|
|
927
|
-
// make that project the next builder's too, or the next ecosystem's project
|
|
928
|
-
// is lost: every builder starts from the parent the caller gave.
|
|
929
|
-
const callerParent = options.parentComponent;
|
|
930
|
-
const withOwnParent = async (build) => {
|
|
931
|
-
options.parentComponent = callerParent;
|
|
932
|
-
try {
|
|
933
|
-
return await build();
|
|
934
|
-
} finally {
|
|
935
|
-
options.parentComponent = callerParent;
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
setActivityContext({
|
|
939
|
-
projectType: Array.isArray(options.projectType)
|
|
940
|
-
? options.projectType.join(",")
|
|
941
|
-
: options.projectType,
|
|
942
|
-
sourcePath: path,
|
|
943
|
-
});
|
|
944
|
-
recordActivity({
|
|
945
|
-
kind: "read",
|
|
946
|
-
reason:
|
|
947
|
-
"Scanning project path for supported languages and package managers.",
|
|
948
|
-
status: "completed",
|
|
949
|
-
target: path,
|
|
950
|
-
});
|
|
951
|
-
if (DEBUG_MODE) {
|
|
952
|
-
console.log("Scanning", path);
|
|
953
|
-
}
|
|
954
|
-
if (pathList.length > 2) {
|
|
955
|
-
thoughtLog(`Let's thoroughly check the path ${path}.`);
|
|
956
|
-
}
|
|
957
|
-
// Node.js and AI inventory
|
|
958
|
-
if (
|
|
959
|
-
hasAnyProjectType(["oci", "js"], options) ||
|
|
960
|
-
hasAnyProjectType(["mcp", "ai-skill", "ai"], options) ||
|
|
961
|
-
getDirectAiInventoryType(path, options)
|
|
962
|
-
) {
|
|
963
|
-
const exactAiInventoryType = getDirectAiInventoryType(path, options);
|
|
964
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
965
|
-
if (exactAiInventoryType === "mcp") {
|
|
966
|
-
thoughtLog(
|
|
967
|
-
"**MCP**: Looking for MCP services, MCP configs, and related AI control-plane artifacts.",
|
|
968
|
-
);
|
|
969
|
-
} else if (exactAiInventoryType === "ai") {
|
|
970
|
-
thoughtLog(
|
|
971
|
-
"**AI-BOM**: Looking for AI models, direct model metadata, pedigree, quantization, and operator-facing AI context.",
|
|
972
|
-
);
|
|
973
|
-
} else if (exactAiInventoryType === "ai-skill") {
|
|
974
|
-
thoughtLog(
|
|
975
|
-
"**AI-SKILL**: Looking for AI instruction, skill, and agent-definition files that can influence build or release flows.",
|
|
976
|
-
);
|
|
977
|
-
} else {
|
|
978
|
-
thoughtLog(
|
|
979
|
-
"**JS**: Now looking for JavaScript projects (npm, yarn, pnpm) and files.",
|
|
980
|
-
);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
setProjectTypeActivityContext(exactAiInventoryType || "js", path);
|
|
984
|
-
bomData = await withOwnParent(() => createNodejsBom(path, options));
|
|
985
|
-
if (bomData?.bomJson?.components?.length) {
|
|
986
|
-
if (exactAiInventoryType) {
|
|
987
|
-
thoughtLog(
|
|
988
|
-
`I found ${bomData.bomJson.components.length} ${exactAiInventoryType} component(s). Let's keep looking.`,
|
|
989
|
-
);
|
|
990
|
-
} else {
|
|
991
|
-
thoughtLog(
|
|
992
|
-
`I found ${bomData.bomJson.components.length} npm packages. Let's keep looking.`,
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
if (DEBUG_MODE) {
|
|
996
|
-
console.log(
|
|
997
|
-
`Found ${bomData.bomJson.components.length} ${exactAiInventoryType || "npm"} components at ${path}`,
|
|
998
|
-
);
|
|
999
|
-
}
|
|
1000
|
-
components = components.concat(bomData.bomJson.components);
|
|
1001
|
-
dependencies = mergeDependencies(
|
|
1002
|
-
dependencies,
|
|
1003
|
-
bomData.bomJson.dependencies,
|
|
1004
|
-
);
|
|
1005
|
-
if (bomData?.bomJson?.services?.length) {
|
|
1006
|
-
options.services = mergeServices(
|
|
1007
|
-
options.services || [],
|
|
1008
|
-
bomData.bomJson.services,
|
|
1009
|
-
);
|
|
1010
|
-
}
|
|
1011
|
-
if (
|
|
1012
|
-
bomData.parentComponent &&
|
|
1013
|
-
Object.keys(bomData.parentComponent).length
|
|
1014
|
-
) {
|
|
1015
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1016
|
-
}
|
|
1017
|
-
// Retain metadata.component.components
|
|
1018
|
-
if (bomData.parentComponent.components?.length) {
|
|
1019
|
-
parentSubComponents = parentSubComponents.concat(
|
|
1020
|
-
bomData.parentComponent.components,
|
|
1021
|
-
);
|
|
1022
|
-
delete bomData.parentComponent.components;
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
// Java
|
|
1027
|
-
if (hasAnyProjectType(["oci", "java"], options)) {
|
|
1028
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1029
|
-
thoughtLog(
|
|
1030
|
-
"**JAVA**: Looking for Java projects (e.g., Maven, Gradle, SBT). I hope all configurations—from Java version to individual build settings—are correctly aligned.",
|
|
1031
|
-
);
|
|
1032
|
-
}
|
|
1033
|
-
setProjectTypeActivityContext("java", path);
|
|
1034
|
-
bomData = await withOwnParent(() => createJavaBom(path, options));
|
|
1035
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1036
|
-
thoughtLog(
|
|
1037
|
-
`I found ${bomData.bomJson.components.length} java packages.`,
|
|
1038
|
-
);
|
|
1039
|
-
if (DEBUG_MODE) {
|
|
1040
|
-
console.log(
|
|
1041
|
-
`Found ${bomData.bomJson.components.length} java packages at ${path}`,
|
|
1042
|
-
);
|
|
1043
|
-
}
|
|
1044
|
-
components = components.concat(bomData.bomJson.components);
|
|
1045
|
-
dependencies = mergeDependencies(
|
|
1046
|
-
dependencies,
|
|
1047
|
-
bomData.bomJson.dependencies,
|
|
1048
|
-
);
|
|
1049
|
-
if (
|
|
1050
|
-
bomData.parentComponent &&
|
|
1051
|
-
Object.keys(bomData.parentComponent).length
|
|
1052
|
-
) {
|
|
1053
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1054
|
-
}
|
|
1055
|
-
// Retain metadata.component.components, but add duplicates to the list of current components
|
|
1056
|
-
// and removing them from metadata.component.components -- the components are merged later
|
|
1057
|
-
if (bomData.parentComponent.components?.length) {
|
|
1058
|
-
let bomSubComponents = bomData.parentComponent.components;
|
|
1059
|
-
if (
|
|
1060
|
-
!["false", "0"].includes(
|
|
1061
|
-
readEnvironmentVariable("GRADLE_RESOLVE_FROM_NODE"),
|
|
1062
|
-
)
|
|
1063
|
-
) {
|
|
1064
|
-
thoughtLog(
|
|
1065
|
-
"Wait, the user wants me to resolve gradle projects from npm.",
|
|
1066
|
-
);
|
|
1067
|
-
const allRefs = components.map((c) => c["bom-ref"]);
|
|
1068
|
-
const duplicateComponents = bomSubComponents.filter((c) =>
|
|
1069
|
-
allRefs.includes(c["bom-ref"]),
|
|
1070
|
-
);
|
|
1071
|
-
components = components.concat(duplicateComponents);
|
|
1072
|
-
const duplicateComponentRefs = duplicateComponents.map(
|
|
1073
|
-
(c) => c["bom-ref"],
|
|
1074
|
-
);
|
|
1075
|
-
bomSubComponents = bomSubComponents.filter(
|
|
1076
|
-
(c) => !duplicateComponentRefs.includes(c["bom-ref"]),
|
|
1077
|
-
);
|
|
1078
|
-
}
|
|
1079
|
-
parentSubComponents = parentSubComponents.concat(bomSubComponents);
|
|
1080
|
-
delete bomData.parentComponent.components;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
if (hasAnyProjectType(["oci", "py"], options)) {
|
|
1085
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1086
|
-
thoughtLog(
|
|
1087
|
-
"**PYTHON**: Looking for Python projects with package managers such as pip, poetry, uv, etc. Wish me good luck!",
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
if (
|
|
1091
|
-
readEnvironmentVariable("CDXGEN_IN_CONTAINER") !== "true" &&
|
|
1092
|
-
pathList.length <= 2
|
|
1093
|
-
) {
|
|
1094
|
-
thoughtLog(
|
|
1095
|
-
"I'm running in a non-container environment. Let's hope the correct build tools are available ✌️.",
|
|
1096
|
-
);
|
|
1097
|
-
}
|
|
1098
|
-
setProjectTypeActivityContext("py", path);
|
|
1099
|
-
bomData = await withOwnParent(() => createPythonBom(path, options));
|
|
1100
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1101
|
-
thoughtLog(
|
|
1102
|
-
`I found ${bomData.bomJson.components.length} python packages.`,
|
|
1103
|
-
);
|
|
1104
|
-
if (DEBUG_MODE) {
|
|
1105
|
-
console.log(
|
|
1106
|
-
`Found ${bomData.bomJson.components.length} python packages at ${path}`,
|
|
1107
|
-
);
|
|
1108
|
-
}
|
|
1109
|
-
components = components.concat(bomData.bomJson.components);
|
|
1110
|
-
dependencies = mergeDependencies(
|
|
1111
|
-
dependencies,
|
|
1112
|
-
bomData.bomJson.dependencies,
|
|
1113
|
-
);
|
|
1114
|
-
if (
|
|
1115
|
-
bomData.parentComponent &&
|
|
1116
|
-
Object.keys(bomData.parentComponent).length
|
|
1117
|
-
) {
|
|
1118
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1119
|
-
}
|
|
1120
|
-
// Retain metadata.component.components. uv workspace members are
|
|
1121
|
-
// nested under the python project, so hoisting them keeps them
|
|
1122
|
-
// siblings of the other sub-components instead of riding one level
|
|
1123
|
-
// deeper (or being dropped by the same-name collapse) when another
|
|
1124
|
-
// ecosystem's project becomes the parent.
|
|
1125
|
-
if (bomData.parentComponent?.components?.length) {
|
|
1126
|
-
parentSubComponents = parentSubComponents.concat(
|
|
1127
|
-
bomData.parentComponent.components,
|
|
1128
|
-
);
|
|
1129
|
-
delete bomData.parentComponent.components;
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
if (bomData?.formulationList?.length) {
|
|
1133
|
-
formulationList = formulationList.concat(bomData.formulationList);
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
if (hasAnyProjectType(["oci", "go"], options)) {
|
|
1137
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1138
|
-
thoughtLog(
|
|
1139
|
-
"**GO**: Looking for go projects. I need to be cautious about purl namespaces and potential failures with the 'go list' command.",
|
|
1140
|
-
);
|
|
1141
|
-
}
|
|
1142
|
-
setProjectTypeActivityContext("go", path);
|
|
1143
|
-
bomData = await withOwnParent(() => createGoBom(path, options));
|
|
1144
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1145
|
-
thoughtLog(`I found ${bomData.bomJson.components.length} go packages.`);
|
|
1146
|
-
if (DEBUG_MODE) {
|
|
1147
|
-
console.log(
|
|
1148
|
-
`Found ${bomData.bomJson.components.length} go packages at ${path}`,
|
|
1149
|
-
);
|
|
1150
|
-
}
|
|
1151
|
-
components = components.concat(bomData.bomJson.components);
|
|
1152
|
-
dependencies = mergeDependencies(
|
|
1153
|
-
dependencies,
|
|
1154
|
-
bomData.bomJson.dependencies,
|
|
1155
|
-
);
|
|
1156
|
-
if (
|
|
1157
|
-
bomData.parentComponent &&
|
|
1158
|
-
Object.keys(bomData.parentComponent).length
|
|
1159
|
-
) {
|
|
1160
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
if (hasAnyProjectType(["oci", "rust"], options)) {
|
|
1165
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1166
|
-
thoughtLog(
|
|
1167
|
-
"**RUST**: Let's search for Cargo/Rust projects. Should I warn the user that we don't support Cargo 'features' and native dependencies, which may lead to both false positives and false negatives? 🤔?",
|
|
1168
|
-
);
|
|
1169
|
-
}
|
|
1170
|
-
setProjectTypeActivityContext("rust", path);
|
|
1171
|
-
bomData = await withOwnParent(() => createRustBom(path, options));
|
|
1172
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1173
|
-
thoughtLog(
|
|
1174
|
-
`I found ${bomData.bomJson.components.length} rust packages.`,
|
|
1175
|
-
);
|
|
1176
|
-
if (DEBUG_MODE) {
|
|
1177
|
-
console.log(
|
|
1178
|
-
`Found ${bomData.bomJson.components.length} rust packages at ${path}`,
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
components = components.concat(bomData.bomJson.components);
|
|
1182
|
-
dependencies = mergeDependencies(
|
|
1183
|
-
dependencies,
|
|
1184
|
-
bomData.bomJson.dependencies,
|
|
1185
|
-
);
|
|
1186
|
-
if (
|
|
1187
|
-
bomData.parentComponent &&
|
|
1188
|
-
Object.keys(bomData.parentComponent).length
|
|
1189
|
-
) {
|
|
1190
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1191
|
-
}
|
|
1192
|
-
// Retain metadata.component.components
|
|
1193
|
-
if (bomData.parentComponent?.components?.length) {
|
|
1194
|
-
parentSubComponents = parentSubComponents.concat(
|
|
1195
|
-
bomData.parentComponent.components,
|
|
1196
|
-
);
|
|
1197
|
-
delete bomData.parentComponent.components;
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
if (hasAnyProjectType(["oci", "php"], options)) {
|
|
1202
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1203
|
-
thoughtLog(
|
|
1204
|
-
"**PHP**: About to search for Composer-based projects. I hope lock files are available; otherwise, the 'composer install' command might fail for various reasons.",
|
|
1205
|
-
);
|
|
1206
|
-
}
|
|
1207
|
-
setProjectTypeActivityContext("php", path);
|
|
1208
|
-
bomData = await withOwnParent(() => createPHPBom(path, options));
|
|
1209
|
-
// A lock with no packages can still hold the project's platform data.
|
|
1210
|
-
if (bomData?.metadataProperties?.length) {
|
|
1211
|
-
metadataProperties = metadataProperties.concat(
|
|
1212
|
-
bomData.metadataProperties,
|
|
1213
|
-
);
|
|
1214
|
-
}
|
|
1215
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1216
|
-
thoughtLog(
|
|
1217
|
-
`I found ${bomData.bomJson.components.length} php packages.`,
|
|
1218
|
-
);
|
|
1219
|
-
if (DEBUG_MODE) {
|
|
1220
|
-
console.log(
|
|
1221
|
-
`Found ${bomData.bomJson.components.length} php packages at ${path}`,
|
|
1222
|
-
);
|
|
1223
|
-
}
|
|
1224
|
-
components = components.concat(bomData.bomJson.components);
|
|
1225
|
-
dependencies = mergeDependencies(
|
|
1226
|
-
dependencies,
|
|
1227
|
-
bomData.bomJson.dependencies,
|
|
1228
|
-
);
|
|
1229
|
-
if (
|
|
1230
|
-
bomData.parentComponent &&
|
|
1231
|
-
Object.keys(bomData.parentComponent).length
|
|
1232
|
-
) {
|
|
1233
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1234
|
-
}
|
|
1235
|
-
// Retain metadata.component.components
|
|
1236
|
-
if (bomData.parentComponent?.components?.length) {
|
|
1237
|
-
parentSubComponents = parentSubComponents.concat(
|
|
1238
|
-
bomData.parentComponent.components,
|
|
1239
|
-
);
|
|
1240
|
-
delete bomData.parentComponent.components;
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
if (hasAnyProjectType(["oci", "ruby"], options)) {
|
|
1245
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1246
|
-
thoughtLog(
|
|
1247
|
-
"**RUBY**: Are there any Ruby projects in this path? There's only one way to know.",
|
|
1248
|
-
);
|
|
1249
|
-
}
|
|
1250
|
-
setProjectTypeActivityContext("ruby", path);
|
|
1251
|
-
bomData = await withOwnParent(() => createRubyBom(path, options));
|
|
1252
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1253
|
-
thoughtLog(
|
|
1254
|
-
`We got ${bomData.bomJson.components.length} ruby packages.`,
|
|
1255
|
-
);
|
|
1256
|
-
if (DEBUG_MODE) {
|
|
1257
|
-
console.log(
|
|
1258
|
-
`Found ${bomData.bomJson.components.length} ruby packages at ${path}`,
|
|
1259
|
-
);
|
|
1260
|
-
}
|
|
1261
|
-
components = components.concat(bomData.bomJson.components);
|
|
1262
|
-
dependencies = mergeDependencies(
|
|
1263
|
-
dependencies,
|
|
1264
|
-
bomData.bomJson.dependencies,
|
|
1265
|
-
bomData.parentComponent,
|
|
1266
|
-
);
|
|
1267
|
-
if (
|
|
1268
|
-
bomData.parentComponent &&
|
|
1269
|
-
Object.keys(bomData.parentComponent).length
|
|
1270
|
-
) {
|
|
1271
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1272
|
-
}
|
|
1273
|
-
// Retain metadata.component.components
|
|
1274
|
-
if (bomData.parentComponent?.components?.length) {
|
|
1275
|
-
parentSubComponents = parentSubComponents.concat(
|
|
1276
|
-
bomData.parentComponent.components,
|
|
1277
|
-
);
|
|
1278
|
-
delete bomData.parentComponent.components;
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
if (hasAnyProjectType(["oci", "csharp"], options)) {
|
|
1283
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1284
|
-
thoughtLog("**CSHARP**: What about csharp and fsharp projects?");
|
|
1285
|
-
}
|
|
1286
|
-
setProjectTypeActivityContext("csharp", path);
|
|
1287
|
-
bomData = await withOwnParent(() => createCsharpBom(path, options));
|
|
1288
|
-
// Every project the scan read has a dependency entry of its own, so a
|
|
1289
|
-
// project that uses no packages is kept along with its parent.
|
|
1290
|
-
if (
|
|
1291
|
-
bomData?.bomJson?.components?.length ||
|
|
1292
|
-
bomData?.bomJson?.dependencies?.length
|
|
1293
|
-
) {
|
|
1294
|
-
thoughtLog(
|
|
1295
|
-
`There are ${bomData.bomJson.components.length} csharp packages.`,
|
|
1296
|
-
);
|
|
1297
|
-
if (DEBUG_MODE) {
|
|
1298
|
-
console.log(
|
|
1299
|
-
`Found ${bomData.bomJson.components.length} csharp packages at ${path}`,
|
|
1300
|
-
);
|
|
1301
|
-
}
|
|
1302
|
-
components = components.concat(bomData.bomJson.components);
|
|
1303
|
-
dependencies = mergeDependencies(
|
|
1304
|
-
dependencies,
|
|
1305
|
-
bomData.bomJson.dependencies,
|
|
1306
|
-
);
|
|
1307
|
-
if (
|
|
1308
|
-
bomData.parentComponent &&
|
|
1309
|
-
Object.keys(bomData.parentComponent).length
|
|
1310
|
-
) {
|
|
1311
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1312
|
-
}
|
|
1313
|
-
// Retain metadata.component.components
|
|
1314
|
-
if (bomData.parentComponent?.components?.length) {
|
|
1315
|
-
parentSubComponents = parentSubComponents.concat(
|
|
1316
|
-
bomData.parentComponent.components,
|
|
1317
|
-
);
|
|
1318
|
-
delete bomData.parentComponent.components;
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
if (hasAnyProjectType(["oci", "dart"], options)) {
|
|
1323
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1324
|
-
thoughtLog(
|
|
1325
|
-
"**DART**: Looking for Dart projects. These are rare ones. Should I inform the user that they can pass the types argument via the command-line to speed things up?",
|
|
1326
|
-
);
|
|
1327
|
-
}
|
|
1328
|
-
setProjectTypeActivityContext("dart", path);
|
|
1329
|
-
bomData = await withOwnParent(() => createDartBom(path, options));
|
|
1330
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1331
|
-
thoughtLog(
|
|
1332
|
-
`I found ${bomData.bomJson.components.length} pub packages.`,
|
|
1333
|
-
);
|
|
1334
|
-
if (DEBUG_MODE) {
|
|
1335
|
-
console.log(
|
|
1336
|
-
`Found ${bomData.bomJson.components.length} pub packages at ${path}`,
|
|
1337
|
-
);
|
|
1338
|
-
}
|
|
1339
|
-
components = components.concat(bomData.bomJson.components);
|
|
1340
|
-
dependencies = mergeDependencies(
|
|
1341
|
-
dependencies,
|
|
1342
|
-
bomData.bomJson.dependencies,
|
|
1343
|
-
);
|
|
1344
|
-
if (
|
|
1345
|
-
bomData.parentComponent &&
|
|
1346
|
-
Object.keys(bomData.parentComponent).length
|
|
1347
|
-
) {
|
|
1348
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
if (hasAnyProjectType(["oci", "haskell"], options)) {
|
|
1353
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1354
|
-
thoughtLog(
|
|
1355
|
-
"**HASKELL**: Looking for Haskell projects. They're rarely encountered.",
|
|
1356
|
-
);
|
|
1357
|
-
}
|
|
1358
|
-
setProjectTypeActivityContext("haskell", path);
|
|
1359
|
-
bomData = await withOwnParent(() => createHaskellBom(path, options));
|
|
1360
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1361
|
-
thoughtLog(
|
|
1362
|
-
`I found ${bomData.bomJson.components.length} hackage packages.`,
|
|
1363
|
-
);
|
|
1364
|
-
if (DEBUG_MODE) {
|
|
1365
|
-
console.log(
|
|
1366
|
-
`Found ${bomData.bomJson.components.length} hackage packages at ${path}`,
|
|
1367
|
-
);
|
|
1368
|
-
}
|
|
1369
|
-
components = components.concat(bomData.bomJson.components);
|
|
1370
|
-
dependencies = mergeDependencies(
|
|
1371
|
-
dependencies,
|
|
1372
|
-
bomData.bomJson.dependencies,
|
|
1373
|
-
);
|
|
1374
|
-
if (
|
|
1375
|
-
bomData.parentComponent &&
|
|
1376
|
-
Object.keys(bomData.parentComponent).length
|
|
1377
|
-
) {
|
|
1378
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
if (hasAnyProjectType(["oci", "elixir"], options)) {
|
|
1383
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1384
|
-
thoughtLog(
|
|
1385
|
-
"**ELIXIR**: Looking for Elixir projects—they're quite rare as well.",
|
|
1386
|
-
);
|
|
1387
|
-
}
|
|
1388
|
-
setProjectTypeActivityContext("elixir", path);
|
|
1389
|
-
bomData = await withOwnParent(() => createElixirBom(path, options));
|
|
1390
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1391
|
-
thoughtLog(
|
|
1392
|
-
`I found ${bomData.bomJson.components.length} mix packages.`,
|
|
1393
|
-
);
|
|
1394
|
-
if (DEBUG_MODE) {
|
|
1395
|
-
console.log(
|
|
1396
|
-
`Found ${bomData.bomJson.components.length} mix packages at ${path}`,
|
|
1397
|
-
);
|
|
1398
|
-
}
|
|
1399
|
-
components = components.concat(bomData.bomJson.components);
|
|
1400
|
-
dependencies = mergeDependencies(
|
|
1401
|
-
dependencies,
|
|
1402
|
-
bomData.bomJson.dependencies,
|
|
1403
|
-
);
|
|
1404
|
-
if (
|
|
1405
|
-
bomData.parentComponent &&
|
|
1406
|
-
Object.keys(bomData.parentComponent).length
|
|
1407
|
-
) {
|
|
1408
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
if (hasAnyProjectType(["oci", "c"], options)) {
|
|
1413
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1414
|
-
thoughtLog(
|
|
1415
|
-
"**C/C++**: Looking for C/C++ projects. Should I warn the user that the generated SBOM might have low accuracy and contain errors?",
|
|
1416
|
-
);
|
|
1417
|
-
}
|
|
1418
|
-
setProjectTypeActivityContext("c", path);
|
|
1419
|
-
bomData = await withOwnParent(() => createCppBom(path, options));
|
|
1420
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1421
|
-
thoughtLog(
|
|
1422
|
-
`I found ${bomData.bomJson.components.length} cpp packages.`,
|
|
1423
|
-
);
|
|
1424
|
-
if (DEBUG_MODE) {
|
|
1425
|
-
console.log(
|
|
1426
|
-
`Found ${bomData.bomJson.components.length} cpp packages at ${path}`,
|
|
1427
|
-
);
|
|
1428
|
-
}
|
|
1429
|
-
components = components.concat(bomData.bomJson.components);
|
|
1430
|
-
dependencies = mergeDependencies(
|
|
1431
|
-
dependencies,
|
|
1432
|
-
bomData.bomJson.dependencies,
|
|
1433
|
-
);
|
|
1434
|
-
if (
|
|
1435
|
-
bomData.parentComponent &&
|
|
1436
|
-
Object.keys(bomData.parentComponent).length
|
|
1437
|
-
) {
|
|
1438
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
// presets and compilers describe the build, whatever it depends on
|
|
1442
|
-
if (bomData?.formulationList?.length) {
|
|
1443
|
-
formulationList = formulationList.concat(bomData.formulationList);
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
if (hasAnyProjectType(["oci", "clojure"], options)) {
|
|
1447
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1448
|
-
thoughtLog(
|
|
1449
|
-
"**CLOJURE**: Looking for Clojure projects. Should I warn the user that the purl namespace 'clojars' isn't widely supported by tools like Dependency-Track?",
|
|
1450
|
-
);
|
|
1451
|
-
}
|
|
1452
|
-
setProjectTypeActivityContext("clojure", path);
|
|
1453
|
-
bomData = await withOwnParent(() => createClojureBom(path, options));
|
|
1454
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1455
|
-
thoughtLog(
|
|
1456
|
-
`I found ${bomData.bomJson.components.length} clojure packages.`,
|
|
1457
|
-
);
|
|
1458
|
-
if (DEBUG_MODE) {
|
|
1459
|
-
console.log(
|
|
1460
|
-
`Found ${bomData.bomJson.components.length} clojure packages at ${path}`,
|
|
1461
|
-
);
|
|
1462
|
-
}
|
|
1463
|
-
components = components.concat(bomData.bomJson.components);
|
|
1464
|
-
dependencies = mergeDependencies(
|
|
1465
|
-
dependencies,
|
|
1466
|
-
bomData.bomJson.dependencies,
|
|
1467
|
-
);
|
|
1468
|
-
if (
|
|
1469
|
-
bomData.parentComponent &&
|
|
1470
|
-
Object.keys(bomData.parentComponent).length
|
|
1471
|
-
) {
|
|
1472
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
if (hasAnyProjectType(["oci", "github"], options)) {
|
|
1477
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1478
|
-
thoughtLog(
|
|
1479
|
-
"**GITHUB**: Looking for any github packages and workflows.",
|
|
1480
|
-
);
|
|
1481
|
-
}
|
|
1482
|
-
setProjectTypeActivityContext("github", path);
|
|
1483
|
-
bomData = await withOwnParent(() => createGitHubBom(path, options));
|
|
1484
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1485
|
-
thoughtLog(
|
|
1486
|
-
`I found ${bomData.bomJson.components.length} github action packages as well. Should I convert these to formulation instead 🤔`,
|
|
1487
|
-
);
|
|
1488
|
-
if (DEBUG_MODE) {
|
|
1489
|
-
console.log(
|
|
1490
|
-
`Found ${bomData.bomJson.components.length} GitHub action packages at ${path}`,
|
|
1491
|
-
);
|
|
1492
|
-
}
|
|
1493
|
-
components = components.concat(bomData.bomJson.components);
|
|
1494
|
-
dependencies = mergeDependencies(
|
|
1495
|
-
dependencies,
|
|
1496
|
-
bomData.bomJson.dependencies,
|
|
1497
|
-
);
|
|
1498
|
-
if (
|
|
1499
|
-
bomData.parentComponent &&
|
|
1500
|
-
Object.keys(bomData.parentComponent).length
|
|
1501
|
-
) {
|
|
1502
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
if (hasAnyProjectType(["oci", "cloudbuild"], options)) {
|
|
1507
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1508
|
-
thoughtLog(
|
|
1509
|
-
"**CLOUDBUILD**: Let's check for CloudBuild configuration files that include package dependencies.",
|
|
1510
|
-
);
|
|
1511
|
-
}
|
|
1512
|
-
setProjectTypeActivityContext("cloudbuild", path);
|
|
1513
|
-
bomData = await withOwnParent(() => createCloudBuildBom(path, options));
|
|
1514
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1515
|
-
thoughtLog(
|
|
1516
|
-
`I found ${bomData.bomJson.components.length} cloudbuild packages.`,
|
|
1517
|
-
);
|
|
1518
|
-
if (DEBUG_MODE) {
|
|
1519
|
-
console.log(
|
|
1520
|
-
`Found ${bomData.bomJson.components.length} CloudBuild configuration at ${path}`,
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
components = components.concat(bomData.bomJson.components);
|
|
1524
|
-
dependencies = mergeDependencies(
|
|
1525
|
-
dependencies,
|
|
1526
|
-
bomData.bomJson.dependencies,
|
|
1527
|
-
);
|
|
1528
|
-
if (
|
|
1529
|
-
bomData.parentComponent &&
|
|
1530
|
-
Object.keys(bomData.parentComponent).length
|
|
1531
|
-
) {
|
|
1532
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
if (hasAnyProjectType(["oci", "swift"], options)) {
|
|
1537
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1538
|
-
thoughtLog(
|
|
1539
|
-
"**SWIFT**: Now checking for Swift projects. We don't support CocoaPods, Objective-C, or pure Xcode projects, so the SBOM will be incomplete.",
|
|
1540
|
-
);
|
|
1541
|
-
}
|
|
1542
|
-
setProjectTypeActivityContext("swift", path);
|
|
1543
|
-
bomData = await withOwnParent(() => createSwiftBom(path, options));
|
|
1544
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1545
|
-
thoughtLog(
|
|
1546
|
-
`I found ${bomData.bomJson.components.length} swift packages here.`,
|
|
1547
|
-
);
|
|
1548
|
-
if (DEBUG_MODE) {
|
|
1549
|
-
console.log(
|
|
1550
|
-
`Found ${bomData.bomJson.components.length} Swift packages at ${path}`,
|
|
1551
|
-
);
|
|
1552
|
-
}
|
|
1553
|
-
components = components.concat(bomData.bomJson.components);
|
|
1554
|
-
dependencies = mergeDependencies(
|
|
1555
|
-
dependencies,
|
|
1556
|
-
bomData.bomJson.dependencies,
|
|
1557
|
-
);
|
|
1558
|
-
if (
|
|
1559
|
-
bomData.parentComponent &&
|
|
1560
|
-
Object.keys(bomData.parentComponent).length
|
|
1561
|
-
) {
|
|
1562
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
if (hasAnyProjectType(["oci", "jar", "war", "ear"], options)) {
|
|
1567
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1568
|
-
thoughtLog(
|
|
1569
|
-
"**JAR**: Let's check for any bundled jar/war/ear files to improve the SBOM accuracy.",
|
|
1570
|
-
);
|
|
1571
|
-
}
|
|
1572
|
-
setProjectTypeActivityContext("jar", path);
|
|
1573
|
-
recordDegradation("jvm.jar.heuristic", {
|
|
1574
|
-
ecosystem: "java",
|
|
1575
|
-
tool: "jar",
|
|
1576
|
-
impact: "versions",
|
|
1577
|
-
path,
|
|
1578
|
-
detail:
|
|
1579
|
-
"Components are being inferred by scanning jar/war/ear files from disk.",
|
|
1003
|
+
ref: parentComponent["bom-ref"],
|
|
1004
|
+
dependsOn: Array.from(parentDependsOn).sort(),
|
|
1580
1005
|
});
|
|
1581
|
-
|
|
1582
|
-
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (hasAnyProjectType(["os"], options, false) && options.bomData) {
|
|
1009
|
+
bomData = options.bomData;
|
|
1010
|
+
if (bomData?.bomJson?.components) {
|
|
1011
|
+
if (DEBUG_MODE) {
|
|
1012
|
+
console.log(`Found ${bomData.bomJson.components.length} OS components`);
|
|
1013
|
+
}
|
|
1014
|
+
if (bomData.bomJson.components.length) {
|
|
1583
1015
|
thoughtLog(
|
|
1584
|
-
`I found ${bomData.bomJson.components.length}
|
|
1585
|
-
);
|
|
1586
|
-
if (DEBUG_MODE) {
|
|
1587
|
-
console.log(
|
|
1588
|
-
`Found ${bomData.bomJson.components.length} jar packages at ${path}`,
|
|
1589
|
-
);
|
|
1590
|
-
}
|
|
1591
|
-
components = components.concat(bomData.bomJson.components);
|
|
1592
|
-
dependencies = mergeDependencies(
|
|
1593
|
-
dependencies,
|
|
1594
|
-
bomData.bomJson.dependencies,
|
|
1016
|
+
`I found ${bomData.bomJson.components.length} OS packages 😎.`,
|
|
1595
1017
|
);
|
|
1596
|
-
if (
|
|
1597
|
-
bomData.parentComponent &&
|
|
1598
|
-
Object.keys(bomData.parentComponent).length
|
|
1599
|
-
) {
|
|
1600
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1601
|
-
}
|
|
1602
1018
|
}
|
|
1019
|
+
components = components.concat(bomData.bomJson.components);
|
|
1603
1020
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1021
|
+
}
|
|
1022
|
+
const sharedOptions = options;
|
|
1023
|
+
// The exploded image layers are written once by the extractor and only read
|
|
1024
|
+
// from here on, so repeated walks of them can share one view of the tree.
|
|
1025
|
+
setDirWalkCacheRoot(options.allLayersExplodedDir);
|
|
1026
|
+
// The ecosystems every path is scanned for, in the order their builders run.
|
|
1027
|
+
// Each step reads the per-path copy of options that scanOnePathInner passes
|
|
1028
|
+
// in. One list merged by one loop also keeps type checking of this function
|
|
1029
|
+
// linear: a separate block per ecosystem, each reassigning an untyped
|
|
1030
|
+
// evolving array, makes TypeScript's control-flow analysis exponential in
|
|
1031
|
+
// the number of blocks (issue #4705).
|
|
1032
|
+
/**
|
|
1033
|
+
* @param {string} path Scanned path
|
|
1034
|
+
* @returns {boolean} Whether the path is a Chrome extension directory or its manifest.json
|
|
1035
|
+
*/
|
|
1036
|
+
const isChromeExtensionPath = (path) => {
|
|
1037
|
+
if (!safeExistsSync(path)) {
|
|
1038
|
+
return false;
|
|
1622
1039
|
}
|
|
1623
|
-
if (
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1040
|
+
if (basename(path) === "manifest.json") {
|
|
1041
|
+
return true;
|
|
1042
|
+
}
|
|
1043
|
+
try {
|
|
1044
|
+
return (
|
|
1045
|
+
statSync(path).isDirectory() &&
|
|
1046
|
+
safeExistsSync(join(path, "manifest.json"))
|
|
1047
|
+
);
|
|
1048
|
+
} catch (_err) {
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
/**
|
|
1053
|
+
* @param {string} thought Thought log written unless the scan is of a container image
|
|
1054
|
+
* @returns {(path: string, scanOptions: Object) => void} The announce hook
|
|
1055
|
+
*/
|
|
1056
|
+
const ociQuiet = (thought) => (_path, scanOptions) => {
|
|
1057
|
+
if (!hasAnyProjectType(["oci"], scanOptions, false)) {
|
|
1058
|
+
thoughtLog(thought);
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
/** @type {PathScanStep[]} */
|
|
1062
|
+
const pathScanSteps = [
|
|
1063
|
+
{
|
|
1064
|
+
// Node.js and AI inventory
|
|
1065
|
+
applies: (path, scanOptions) =>
|
|
1066
|
+
hasAnyProjectType(["oci", "js"], scanOptions) ||
|
|
1067
|
+
hasAnyProjectType(["mcp", "ai-skill", "ai"], scanOptions) ||
|
|
1068
|
+
!!getDirectAiInventoryType(path, scanOptions),
|
|
1069
|
+
prepare: (path, scanOptions) =>
|
|
1070
|
+
getDirectAiInventoryType(path, scanOptions),
|
|
1071
|
+
announce: (_path, scanOptions, exactAiInventoryType) => {
|
|
1072
|
+
if (hasAnyProjectType(["oci"], scanOptions, false)) {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
if (exactAiInventoryType === "mcp") {
|
|
1076
|
+
thoughtLog(
|
|
1077
|
+
"**MCP**: Looking for MCP services, MCP configs, and related AI control-plane artifacts.",
|
|
1078
|
+
);
|
|
1079
|
+
} else if (exactAiInventoryType === "ai") {
|
|
1080
|
+
thoughtLog(
|
|
1081
|
+
"**AI-BOM**: Looking for AI models, direct model metadata, pedigree, quantization, and operator-facing AI context.",
|
|
1082
|
+
);
|
|
1083
|
+
} else if (exactAiInventoryType === "ai-skill") {
|
|
1084
|
+
thoughtLog(
|
|
1085
|
+
"**AI-SKILL**: Looking for AI instruction, skill, and agent-definition files that can influence build or release flows.",
|
|
1086
|
+
);
|
|
1087
|
+
} else {
|
|
1088
|
+
thoughtLog(
|
|
1089
|
+
"**JS**: Now looking for JavaScript projects (npm, yarn, pnpm) and files.",
|
|
1630
1090
|
);
|
|
1631
1091
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1092
|
+
},
|
|
1093
|
+
activityType: (exactAiInventoryType) => exactAiInventoryType || "js",
|
|
1094
|
+
build: createNodejsBom,
|
|
1095
|
+
foundThought: (count, _path, exactAiInventoryType) =>
|
|
1096
|
+
exactAiInventoryType
|
|
1097
|
+
? `I found ${count} ${exactAiInventoryType} component(s). Let's keep looking.`
|
|
1098
|
+
: `I found ${count} npm packages. Let's keep looking.`,
|
|
1099
|
+
debugMessage: (count, path, exactAiInventoryType) =>
|
|
1100
|
+
`Found ${count} ${exactAiInventoryType || "npm"} components at ${path}`,
|
|
1101
|
+
mergeServices: true,
|
|
1102
|
+
hoistSubComponents: "plain",
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
applies: (_path, scanOptions) =>
|
|
1106
|
+
hasAnyProjectType(["oci", "java"], scanOptions),
|
|
1107
|
+
announce: ociQuiet(
|
|
1108
|
+
"**JAVA**: Looking for Java projects (e.g., Maven, Gradle, SBT). I hope all configurations—from Java version to individual build settings—are correctly aligned.",
|
|
1109
|
+
),
|
|
1110
|
+
activityType: "java",
|
|
1111
|
+
build: createJavaBom,
|
|
1112
|
+
foundThought: (count) => `I found ${count} java packages.`,
|
|
1113
|
+
debugMessage: (count, path) => `Found ${count} java packages at ${path}`,
|
|
1114
|
+
hoistSubComponents: "gradle",
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
applies: (_path, scanOptions) =>
|
|
1118
|
+
hasAnyProjectType(["oci", "py"], scanOptions),
|
|
1119
|
+
announce: (path, scanOptions) => {
|
|
1120
|
+
ociQuiet(
|
|
1121
|
+
"**PYTHON**: Looking for Python projects with package managers such as pip, poetry, uv, etc. Wish me good luck!",
|
|
1122
|
+
)(path, scanOptions);
|
|
1637
1123
|
if (
|
|
1638
|
-
|
|
1639
|
-
|
|
1124
|
+
readEnvironmentVariable("CDXGEN_IN_CONTAINER") !== "true" &&
|
|
1125
|
+
pathList.length <= 2
|
|
1640
1126
|
) {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
if (hasAnyProjectType(["caxa"], options, false)) {
|
|
1646
|
-
setProjectTypeActivityContext("caxa", path);
|
|
1647
|
-
bomData = await withOwnParent(() => createCaxaBom(path, options));
|
|
1648
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1649
|
-
if (DEBUG_MODE) {
|
|
1650
|
-
console.log(
|
|
1651
|
-
`Found ${bomData.bomJson.components.length} caxa packages at ${path}`,
|
|
1127
|
+
thoughtLog(
|
|
1128
|
+
"I'm running in a non-container environment. Let's hope the correct build tools are available ✌️.",
|
|
1652
1129
|
);
|
|
1653
1130
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1131
|
+
},
|
|
1132
|
+
activityType: "py",
|
|
1133
|
+
build: createPythonBom,
|
|
1134
|
+
foundThought: (count) => `I found ${count} python packages.`,
|
|
1135
|
+
debugMessage: (count, path) =>
|
|
1136
|
+
`Found ${count} python packages at ${path}`,
|
|
1137
|
+
// uv workspace members are nested under the python project, so hoisting
|
|
1138
|
+
// them keeps them siblings of the other sub-components instead of riding
|
|
1139
|
+
// one level deeper (or being dropped by the same-name collapse) when
|
|
1140
|
+
// another ecosystem's project becomes the parent.
|
|
1141
|
+
hoistSubComponents: "plain",
|
|
1142
|
+
collectFormulation: true,
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
applies: (_path, scanOptions) =>
|
|
1146
|
+
hasAnyProjectType(["oci", "go"], scanOptions),
|
|
1147
|
+
announce: ociQuiet(
|
|
1148
|
+
"**GO**: Looking for go projects. I need to be cautious about purl namespaces and potential failures with the 'go list' command.",
|
|
1149
|
+
),
|
|
1150
|
+
activityType: "go",
|
|
1151
|
+
build: createGoBom,
|
|
1152
|
+
foundThought: (count) => `I found ${count} go packages.`,
|
|
1153
|
+
debugMessage: (count, path) => `Found ${count} go packages at ${path}`,
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
applies: (_path, scanOptions) =>
|
|
1157
|
+
hasAnyProjectType(["oci", "rust"], scanOptions),
|
|
1158
|
+
announce: ociQuiet(
|
|
1159
|
+
"**RUST**: Let's search for Cargo/Rust projects. Should I warn the user that we don't support Cargo 'features' and native dependencies, which may lead to both false positives and false negatives? 🤔?",
|
|
1160
|
+
),
|
|
1161
|
+
activityType: "rust",
|
|
1162
|
+
build: createRustBom,
|
|
1163
|
+
foundThought: (count) => `I found ${count} rust packages.`,
|
|
1164
|
+
debugMessage: (count, path) => `Found ${count} rust packages at ${path}`,
|
|
1165
|
+
hoistSubComponents: "plain",
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
applies: (_path, scanOptions) =>
|
|
1169
|
+
hasAnyProjectType(["oci", "php"], scanOptions),
|
|
1170
|
+
announce: ociQuiet(
|
|
1171
|
+
"**PHP**: About to search for Composer-based projects. I hope lock files are available; otherwise, the 'composer install' command might fail for various reasons.",
|
|
1172
|
+
),
|
|
1173
|
+
activityType: "php",
|
|
1174
|
+
build: createPHPBom,
|
|
1175
|
+
// A lock with no packages can still hold the project's platform data.
|
|
1176
|
+
collectMetadataProperties: true,
|
|
1177
|
+
foundThought: (count) => `I found ${count} php packages.`,
|
|
1178
|
+
debugMessage: (count, path) => `Found ${count} php packages at ${path}`,
|
|
1179
|
+
hoistSubComponents: "plain",
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
applies: (_path, scanOptions) =>
|
|
1183
|
+
hasAnyProjectType(["oci", "ruby"], scanOptions),
|
|
1184
|
+
announce: ociQuiet(
|
|
1185
|
+
"**RUBY**: Are there any Ruby projects in this path? There's only one way to know.",
|
|
1186
|
+
),
|
|
1187
|
+
activityType: "ruby",
|
|
1188
|
+
build: createRubyBom,
|
|
1189
|
+
foundThought: (count) => `We got ${count} ruby packages.`,
|
|
1190
|
+
debugMessage: (count, path) => `Found ${count} ruby packages at ${path}`,
|
|
1191
|
+
dependencyMerge: "mergeWithParent",
|
|
1192
|
+
hoistSubComponents: "plain",
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
applies: (_path, scanOptions) =>
|
|
1196
|
+
hasAnyProjectType(["oci", "csharp"], scanOptions),
|
|
1197
|
+
announce: ociQuiet("**CSHARP**: What about csharp and fsharp projects?"),
|
|
1198
|
+
activityType: "csharp",
|
|
1199
|
+
build: createCsharpBom,
|
|
1200
|
+
// Every project the scan read has a dependency entry of its own, so a
|
|
1201
|
+
// project that uses no packages is kept along with its parent.
|
|
1202
|
+
isUseful: (bomData) =>
|
|
1203
|
+
!!(
|
|
1204
|
+
bomData?.bomJson?.components?.length ||
|
|
1205
|
+
bomData?.bomJson?.dependencies?.length
|
|
1206
|
+
),
|
|
1207
|
+
foundThought: (count) => `There are ${count} csharp packages.`,
|
|
1208
|
+
debugMessage: (count, path) =>
|
|
1209
|
+
`Found ${count} csharp packages at ${path}`,
|
|
1210
|
+
hoistSubComponents: "plain",
|
|
1211
|
+
},
|
|
1212
|
+
{
|
|
1213
|
+
applies: (_path, scanOptions) =>
|
|
1214
|
+
hasAnyProjectType(["oci", "dart"], scanOptions),
|
|
1215
|
+
announce: ociQuiet(
|
|
1216
|
+
"**DART**: Looking for Dart projects. These are rare ones. Should I inform the user that they can pass the types argument via the command-line to speed things up?",
|
|
1217
|
+
),
|
|
1218
|
+
activityType: "dart",
|
|
1219
|
+
build: createDartBom,
|
|
1220
|
+
foundThought: (count) => `I found ${count} pub packages.`,
|
|
1221
|
+
debugMessage: (count, path) => `Found ${count} pub packages at ${path}`,
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
applies: (_path, scanOptions) =>
|
|
1225
|
+
hasAnyProjectType(["oci", "haskell"], scanOptions),
|
|
1226
|
+
announce: ociQuiet(
|
|
1227
|
+
"**HASKELL**: Looking for Haskell projects. They're rarely encountered.",
|
|
1228
|
+
),
|
|
1229
|
+
activityType: "haskell",
|
|
1230
|
+
build: createHaskellBom,
|
|
1231
|
+
foundThought: (count) => `I found ${count} hackage packages.`,
|
|
1232
|
+
debugMessage: (count, path) =>
|
|
1233
|
+
`Found ${count} hackage packages at ${path}`,
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
applies: (_path, scanOptions) =>
|
|
1237
|
+
hasAnyProjectType(["oci", "elixir"], scanOptions),
|
|
1238
|
+
announce: ociQuiet(
|
|
1239
|
+
"**ELIXIR**: Looking for Elixir projects—they're quite rare as well.",
|
|
1240
|
+
),
|
|
1241
|
+
activityType: "elixir",
|
|
1242
|
+
build: createElixirBom,
|
|
1243
|
+
foundThought: (count) => `I found ${count} mix packages.`,
|
|
1244
|
+
debugMessage: (count, path) => `Found ${count} mix packages at ${path}`,
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
applies: (_path, scanOptions) =>
|
|
1248
|
+
hasAnyProjectType(["oci", "c"], scanOptions),
|
|
1249
|
+
announce: ociQuiet(
|
|
1250
|
+
"**C/C++**: Looking for C/C++ projects. Should I warn the user that the generated SBOM might have low accuracy and contain errors?",
|
|
1251
|
+
),
|
|
1252
|
+
activityType: "c",
|
|
1253
|
+
build: createCppBom,
|
|
1254
|
+
foundThought: (count) => `I found ${count} cpp packages.`,
|
|
1255
|
+
debugMessage: (count, path) => `Found ${count} cpp packages at ${path}`,
|
|
1256
|
+
// presets and compilers describe the build, whatever it depends on
|
|
1257
|
+
collectFormulation: true,
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
applies: (_path, scanOptions) =>
|
|
1261
|
+
hasAnyProjectType(["oci", "clojure"], scanOptions),
|
|
1262
|
+
announce: ociQuiet(
|
|
1263
|
+
"**CLOJURE**: Looking for Clojure projects. Should I warn the user that the purl namespace 'clojars' isn't widely supported by tools like Dependency-Track?",
|
|
1264
|
+
),
|
|
1265
|
+
activityType: "clojure",
|
|
1266
|
+
build: createClojureBom,
|
|
1267
|
+
foundThought: (count) => `I found ${count} clojure packages.`,
|
|
1268
|
+
debugMessage: (count, path) =>
|
|
1269
|
+
`Found ${count} clojure packages at ${path}`,
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
applies: (_path, scanOptions) =>
|
|
1273
|
+
hasAnyProjectType(["oci", "github"], scanOptions),
|
|
1274
|
+
announce: ociQuiet(
|
|
1275
|
+
"**GITHUB**: Looking for any github packages and workflows.",
|
|
1276
|
+
),
|
|
1277
|
+
activityType: "github",
|
|
1278
|
+
build: createGitHubBom,
|
|
1279
|
+
foundThought: (count) =>
|
|
1280
|
+
`I found ${count} github action packages as well. Should I convert these to formulation instead 🤔`,
|
|
1281
|
+
debugMessage: (count, path) =>
|
|
1282
|
+
`Found ${count} GitHub action packages at ${path}`,
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
applies: (_path, scanOptions) =>
|
|
1286
|
+
hasAnyProjectType(["oci", "cloudbuild"], scanOptions),
|
|
1287
|
+
announce: ociQuiet(
|
|
1288
|
+
"**CLOUDBUILD**: Let's check for CloudBuild configuration files that include package dependencies.",
|
|
1289
|
+
),
|
|
1290
|
+
activityType: "cloudbuild",
|
|
1291
|
+
build: createCloudBuildBom,
|
|
1292
|
+
foundThought: (count) => `I found ${count} cloudbuild packages.`,
|
|
1293
|
+
debugMessage: (count, path) =>
|
|
1294
|
+
`Found ${count} CloudBuild configuration at ${path}`,
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
applies: (_path, scanOptions) =>
|
|
1298
|
+
hasAnyProjectType(["oci", "swift"], scanOptions),
|
|
1299
|
+
announce: ociQuiet(
|
|
1300
|
+
"**SWIFT**: Now checking for Swift projects. We don't support CocoaPods, Objective-C, or pure Xcode projects, so the SBOM will be incomplete.",
|
|
1301
|
+
),
|
|
1302
|
+
activityType: "swift",
|
|
1303
|
+
build: createSwiftBom,
|
|
1304
|
+
foundThought: (count) => `I found ${count} swift packages here.`,
|
|
1305
|
+
debugMessage: (count, path) => `Found ${count} Swift packages at ${path}`,
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
applies: (_path, scanOptions) =>
|
|
1309
|
+
hasAnyProjectType(["oci", "jar", "war", "ear"], scanOptions),
|
|
1310
|
+
announce: ociQuiet(
|
|
1311
|
+
"**JAR**: Let's check for any bundled jar/war/ear files to improve the SBOM accuracy.",
|
|
1312
|
+
),
|
|
1313
|
+
activityType: "jar",
|
|
1314
|
+
beforeBuild: (path) => {
|
|
1315
|
+
recordDegradation("jvm.jar.heuristic", {
|
|
1316
|
+
ecosystem: "java",
|
|
1317
|
+
tool: "jar",
|
|
1318
|
+
impact: "versions",
|
|
1319
|
+
path,
|
|
1320
|
+
detail:
|
|
1321
|
+
"Components are being inferred by scanning jar/war/ear files from disk.",
|
|
1322
|
+
});
|
|
1323
|
+
},
|
|
1324
|
+
build: createJarBom,
|
|
1325
|
+
foundThought: (count) => `I found ${count} jar packages as well.`,
|
|
1326
|
+
debugMessage: (count, path) => `Found ${count} jar packages at ${path}`,
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
applies: (_path, scanOptions) =>
|
|
1330
|
+
hasAnyProjectType(["oci", "cocoa"], scanOptions),
|
|
1331
|
+
activityType: "cocoa",
|
|
1332
|
+
build: createCocoaBom,
|
|
1333
|
+
debugMessage: (count, path) => `Found ${count} Cocoa packages at ${path}`,
|
|
1334
|
+
dependencyMerge: "concat",
|
|
1335
|
+
},
|
|
1336
|
+
{
|
|
1337
|
+
applies: (_path, scanOptions) =>
|
|
1338
|
+
hasAnyProjectType(["oci", "nix"], scanOptions),
|
|
1339
|
+
activityType: "nix",
|
|
1340
|
+
build: createNixBom,
|
|
1341
|
+
debugMessage: (count, path) =>
|
|
1342
|
+
`Found ${count} Nix flake packages at ${path}`,
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
applies: (_path, scanOptions) =>
|
|
1346
|
+
hasAnyProjectType(["caxa"], scanOptions, false),
|
|
1347
|
+
activityType: "caxa",
|
|
1348
|
+
build: createCaxaBom,
|
|
1349
|
+
debugMessage: (count, path) => `Found ${count} caxa packages at ${path}`,
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
applies: (_path, scanOptions) =>
|
|
1353
|
+
hasAnyProjectType(["asar"], scanOptions, false),
|
|
1354
|
+
activityType: "asar",
|
|
1355
|
+
build: createAsarBom,
|
|
1356
|
+
debugMessage: (count, path) =>
|
|
1357
|
+
`Found ${count} ASAR component(s) at ${path}`,
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
applies: (_path, scanOptions) =>
|
|
1361
|
+
hasAnyProjectType(["vscode-extension"], scanOptions, false),
|
|
1362
|
+
activityType: "vscode-extension",
|
|
1363
|
+
build: createVscodeExtensionBom,
|
|
1364
|
+
debugMessage: (count, path) =>
|
|
1365
|
+
`Found ${count} VS Code extension(s) at ${path}`,
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
// A path that is a Chrome extension is always scanned. Any other path
|
|
1369
|
+
// scans the extensions installed on this host, which happens once per
|
|
1370
|
+
// run however many paths there are.
|
|
1371
|
+
applies: (path, scanOptions) =>
|
|
1372
|
+
hasAnyProjectType(["chrome-extension"], scanOptions, false) &&
|
|
1373
|
+
(isChromeExtensionPath(path) ||
|
|
1374
|
+
!sharedOptions.__didScanChromeExtensions),
|
|
1375
|
+
beforeBuild: (path) => {
|
|
1376
|
+
if (!isChromeExtensionPath(path)) {
|
|
1377
|
+
sharedOptions.__didScanChromeExtensions = true;
|
|
1664
1378
|
}
|
|
1379
|
+
},
|
|
1380
|
+
activityType: "chrome-extension",
|
|
1381
|
+
build: createChromeExtensionBom,
|
|
1382
|
+
debugMessage: (count) =>
|
|
1383
|
+
`Found ${count} Chrome extension(s) on this host`,
|
|
1384
|
+
},
|
|
1385
|
+
{
|
|
1386
|
+
applies: (_path, scanOptions) =>
|
|
1387
|
+
hasAnyProjectType(["terraform"], scanOptions),
|
|
1388
|
+
announce: () => {
|
|
1389
|
+
thoughtLog(
|
|
1390
|
+
"**TERRAFORM**: Looking for Terraform and OpenTofu modules and providers. The registry metadata fetch is opt-in, so I will rely on the lock file, the module manifest and the configuration files.",
|
|
1391
|
+
);
|
|
1392
|
+
},
|
|
1393
|
+
activityType: "terraform",
|
|
1394
|
+
build: createTerraformBom,
|
|
1395
|
+
foundThought: (count) =>
|
|
1396
|
+
`I found ${count} terraform components. Should I also resolve their licenses from the registry when FETCH_LICENSE is enabled?`,
|
|
1397
|
+
debugMessage: (count, path) =>
|
|
1398
|
+
`Found ${count} terraform components at ${path}`,
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
// Collect any crypto keys
|
|
1402
|
+
applies: (_path, scanOptions) =>
|
|
1403
|
+
scanOptions.specVersion >= 1.6 &&
|
|
1404
|
+
!!scanOptions.includeCrypto &&
|
|
1405
|
+
isCycloneDxComponentTypeEnabled("cryptographic-asset", scanOptions),
|
|
1406
|
+
announce: ociQuiet(
|
|
1407
|
+
"**CBOM**: Wait, the user wants me to look for cryptographic assets. Let's check thoroughly.",
|
|
1408
|
+
),
|
|
1409
|
+
activityType: "cbom",
|
|
1410
|
+
build: createCryptoCertsBom,
|
|
1411
|
+
foundThought: (count) => `I found ${count} crypto assets.`,
|
|
1412
|
+
debugMessage: (count, path) => `Found ${count} crypto assets at ${path}`,
|
|
1413
|
+
dependencyMerge: "topLevel",
|
|
1414
|
+
keepParent: false,
|
|
1415
|
+
},
|
|
1416
|
+
];
|
|
1417
|
+
const scanOnePath = async (path) => {
|
|
1418
|
+
try {
|
|
1419
|
+
return await scanOnePathInner(path);
|
|
1420
|
+
} catch (err) {
|
|
1421
|
+
if (!isDeferredFailOnError(err)) {
|
|
1422
|
+
throw err;
|
|
1665
1423
|
}
|
|
1424
|
+
// The failing path contributes nothing, and the remaining paths still
|
|
1425
|
+
// do: fail-on-error scopes the loss to the leg that failed.
|
|
1426
|
+
return {
|
|
1427
|
+
components: [],
|
|
1428
|
+
dependencies: [],
|
|
1429
|
+
options: { ...sharedOptions },
|
|
1430
|
+
parentSubComponents: [],
|
|
1431
|
+
metadataProperties: [],
|
|
1432
|
+
formulationList: [],
|
|
1433
|
+
};
|
|
1666
1434
|
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1435
|
+
};
|
|
1436
|
+
const scanOnePathInner = async (path) => {
|
|
1437
|
+
// Each path accumulates into its own lists and its own view of options, so
|
|
1438
|
+
// that legs which record a parent component or a tool cannot overwrite
|
|
1439
|
+
// another path's. The lists are merged below in pathList order.
|
|
1440
|
+
const options = { ...sharedOptions };
|
|
1441
|
+
/** @type {BuilderBomData | undefined} */
|
|
1442
|
+
let bomData;
|
|
1443
|
+
/** @type {Object[]} */
|
|
1444
|
+
let components = [];
|
|
1445
|
+
/** @type {Object[]} */
|
|
1446
|
+
let dependencies = [];
|
|
1447
|
+
/** @type {Object[]} */
|
|
1448
|
+
let parentSubComponents = [];
|
|
1449
|
+
/** @type {Object[]} */
|
|
1450
|
+
let metadataProperties = [];
|
|
1451
|
+
/** @type {Object[]} */
|
|
1452
|
+
let pathFormulationList = [];
|
|
1453
|
+
// Each builder detects the project of its own ecosystem. A builder that sets
|
|
1454
|
+
// options.parentComponent to force its project into its own BOM must not
|
|
1455
|
+
// make that project the next builder's too, or the next ecosystem's project
|
|
1456
|
+
// is lost: every builder starts from the parent the caller gave.
|
|
1457
|
+
const callerParent = options.parentComponent;
|
|
1458
|
+
const withOwnParent = async (build) => {
|
|
1459
|
+
options.parentComponent = callerParent;
|
|
1460
|
+
try {
|
|
1461
|
+
return await build();
|
|
1462
|
+
} finally {
|
|
1463
|
+
options.parentComponent = callerParent;
|
|
1687
1464
|
}
|
|
1465
|
+
};
|
|
1466
|
+
setActivityContext({
|
|
1467
|
+
projectType: Array.isArray(options.projectType)
|
|
1468
|
+
? options.projectType.join(",")
|
|
1469
|
+
: options.projectType,
|
|
1470
|
+
sourcePath: path,
|
|
1471
|
+
});
|
|
1472
|
+
recordActivity({
|
|
1473
|
+
kind: "read",
|
|
1474
|
+
reason:
|
|
1475
|
+
"Scanning project path for supported languages and package managers.",
|
|
1476
|
+
status: "completed",
|
|
1477
|
+
target: path,
|
|
1478
|
+
});
|
|
1479
|
+
if (DEBUG_MODE) {
|
|
1480
|
+
console.log("Scanning", path);
|
|
1688
1481
|
}
|
|
1689
|
-
if (
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1482
|
+
if (pathList.length > 2) {
|
|
1483
|
+
thoughtLog(`Let's thoroughly check the path ${path}.`);
|
|
1484
|
+
}
|
|
1485
|
+
for (const step of pathScanSteps) {
|
|
1486
|
+
if (!step.applies(path, options)) {
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
const context = step.prepare?.(path, options);
|
|
1490
|
+
step.announce?.(path, options, context);
|
|
1491
|
+
setProjectTypeActivityContext(
|
|
1492
|
+
typeof step.activityType === "function"
|
|
1493
|
+
? step.activityType(context)
|
|
1494
|
+
: step.activityType,
|
|
1495
|
+
path,
|
|
1693
1496
|
);
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1497
|
+
step.beforeBuild?.(path);
|
|
1498
|
+
const { build } = step;
|
|
1499
|
+
bomData = /** @type {BuilderBomData | undefined} */ (
|
|
1500
|
+
await withOwnParent(() => build(path, options))
|
|
1501
|
+
);
|
|
1502
|
+
if (
|
|
1503
|
+
step.collectMetadataProperties &&
|
|
1504
|
+
bomData?.metadataProperties?.length
|
|
1505
|
+
) {
|
|
1506
|
+
metadataProperties = metadataProperties.concat(
|
|
1507
|
+
bomData.metadataProperties,
|
|
1704
1508
|
);
|
|
1705
|
-
if (
|
|
1706
|
-
bomData.parentComponent &&
|
|
1707
|
-
Object.keys(bomData.parentComponent).length
|
|
1708
|
-
) {
|
|
1709
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1710
|
-
}
|
|
1711
1509
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
if (
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
statSync(path).isDirectory() &&
|
|
1722
|
-
safeExistsSync(join(path, "manifest.json"));
|
|
1723
|
-
} catch (_err) {
|
|
1724
|
-
isExplicitChromeExtensionPath = false;
|
|
1725
|
-
}
|
|
1510
|
+
const isUseful = step.isUseful
|
|
1511
|
+
? step.isUseful(bomData)
|
|
1512
|
+
: !!bomData?.bomJson?.components?.length;
|
|
1513
|
+
if (isUseful) {
|
|
1514
|
+
// a csharp result can be kept for its dependencies alone
|
|
1515
|
+
const count = bomData.bomJson.components?.length || 0;
|
|
1516
|
+
const thought = step.foundThought?.(count, path, context);
|
|
1517
|
+
if (thought) {
|
|
1518
|
+
thoughtLog(thought);
|
|
1726
1519
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
isExplicitChromeExtensionPath ||
|
|
1730
|
-
!sharedOptions.__didScanChromeExtensions
|
|
1731
|
-
) {
|
|
1732
|
-
if (!isExplicitChromeExtensionPath) {
|
|
1733
|
-
sharedOptions.__didScanChromeExtensions = true;
|
|
1520
|
+
if (DEBUG_MODE) {
|
|
1521
|
+
console.log(step.debugMessage(count, path, context));
|
|
1734
1522
|
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1523
|
+
components = components.concat(bomData.bomJson.components || []);
|
|
1524
|
+
if (step.dependencyMerge === "concat") {
|
|
1525
|
+
dependencies = dependencies.concat(
|
|
1526
|
+
bomData.bomJson.dependencies || [],
|
|
1527
|
+
);
|
|
1528
|
+
} else if (step.dependencyMerge === "topLevel") {
|
|
1529
|
+
// the crypto scan returns its dependencies beside bomJson
|
|
1530
|
+
if (bomData.dependencies?.length) {
|
|
1531
|
+
dependencies = mergeDependencies(
|
|
1532
|
+
dependencies,
|
|
1533
|
+
bomData.dependencies,
|
|
1743
1534
|
);
|
|
1744
1535
|
}
|
|
1745
|
-
|
|
1536
|
+
} else if (step.dependencyMerge === "mergeWithParent") {
|
|
1537
|
+
dependencies = mergeDependencies(
|
|
1538
|
+
dependencies,
|
|
1539
|
+
bomData.bomJson.dependencies,
|
|
1540
|
+
bomData.parentComponent,
|
|
1541
|
+
);
|
|
1542
|
+
} else {
|
|
1746
1543
|
dependencies = mergeDependencies(
|
|
1747
1544
|
dependencies,
|
|
1748
1545
|
bomData.bomJson.dependencies,
|
|
1749
1546
|
);
|
|
1750
|
-
if (
|
|
1751
|
-
bomData.parentComponent &&
|
|
1752
|
-
Object.keys(bomData.parentComponent).length
|
|
1753
|
-
) {
|
|
1754
|
-
parentSubComponents.push(bomData.parentComponent);
|
|
1755
|
-
}
|
|
1756
1547
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
"**TERRAFORM**: Looking for Terraform and OpenTofu modules and providers. The registry metadata fetch is opt-in, so I will rely on the lock file, the module manifest and the configuration files.",
|
|
1762
|
-
);
|
|
1763
|
-
setProjectTypeActivityContext("terraform", path);
|
|
1764
|
-
bomData = await withOwnParent(() => createTerraformBom(path, options));
|
|
1765
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1766
|
-
thoughtLog(
|
|
1767
|
-
`I found ${bomData.bomJson.components.length} terraform components. Should I also resolve their licenses from the registry when FETCH_LICENSE is enabled?`,
|
|
1768
|
-
);
|
|
1769
|
-
if (DEBUG_MODE) {
|
|
1770
|
-
console.log(
|
|
1771
|
-
`Found ${bomData.bomJson.components.length} terraform components at ${path}`,
|
|
1548
|
+
if (step.mergeServices && bomData?.bomJson?.services?.length) {
|
|
1549
|
+
options.services = mergeServices(
|
|
1550
|
+
options.services || [],
|
|
1551
|
+
bomData.bomJson.services,
|
|
1772
1552
|
);
|
|
1773
1553
|
}
|
|
1774
|
-
components = components.concat(bomData.bomJson.components);
|
|
1775
|
-
dependencies = mergeDependencies(
|
|
1776
|
-
dependencies,
|
|
1777
|
-
bomData.bomJson.dependencies,
|
|
1778
|
-
);
|
|
1779
1554
|
if (
|
|
1555
|
+
step.keepParent !== false &&
|
|
1780
1556
|
bomData.parentComponent &&
|
|
1781
1557
|
Object.keys(bomData.parentComponent).length
|
|
1782
1558
|
) {
|
|
1783
1559
|
parentSubComponents.push(bomData.parentComponent);
|
|
1784
1560
|
}
|
|
1561
|
+
// Retain metadata.component.components
|
|
1562
|
+
if (
|
|
1563
|
+
step.hoistSubComponents &&
|
|
1564
|
+
bomData.parentComponent?.components?.length
|
|
1565
|
+
) {
|
|
1566
|
+
let bomSubComponents = bomData.parentComponent.components;
|
|
1567
|
+
// For java, sub-components that are already components are added
|
|
1568
|
+
// to the list of current components and removed from
|
|
1569
|
+
// metadata.component.components; the components are merged later.
|
|
1570
|
+
if (
|
|
1571
|
+
step.hoistSubComponents === "gradle" &&
|
|
1572
|
+
!["false", "0"].includes(
|
|
1573
|
+
readEnvironmentVariable("GRADLE_RESOLVE_FROM_NODE"),
|
|
1574
|
+
)
|
|
1575
|
+
) {
|
|
1576
|
+
thoughtLog(
|
|
1577
|
+
"Wait, the user wants me to resolve gradle projects from npm.",
|
|
1578
|
+
);
|
|
1579
|
+
const allRefs = components.map((c) => c["bom-ref"]);
|
|
1580
|
+
const duplicateComponents = bomSubComponents.filter((c) =>
|
|
1581
|
+
allRefs.includes(c["bom-ref"]),
|
|
1582
|
+
);
|
|
1583
|
+
components = components.concat(duplicateComponents);
|
|
1584
|
+
const duplicateComponentRefs = duplicateComponents.map(
|
|
1585
|
+
(c) => c["bom-ref"],
|
|
1586
|
+
);
|
|
1587
|
+
bomSubComponents = bomSubComponents.filter(
|
|
1588
|
+
(c) => !duplicateComponentRefs.includes(c["bom-ref"]),
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
parentSubComponents = parentSubComponents.concat(bomSubComponents);
|
|
1592
|
+
delete bomData.parentComponent.components;
|
|
1593
|
+
}
|
|
1785
1594
|
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
options.specVersion >= 1.6 &&
|
|
1790
|
-
options.includeCrypto &&
|
|
1791
|
-
isCycloneDxComponentTypeEnabled("cryptographic-asset", options)
|
|
1792
|
-
) {
|
|
1793
|
-
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
1794
|
-
thoughtLog(
|
|
1795
|
-
"**CBOM**: Wait, the user wants me to look for cryptographic assets. Let's check thoroughly.",
|
|
1796
|
-
);
|
|
1797
|
-
}
|
|
1798
|
-
setProjectTypeActivityContext("cbom", path);
|
|
1799
|
-
bomData = await withOwnParent(() => createCryptoCertsBom(path, options));
|
|
1800
|
-
if (bomData?.bomJson?.components?.length) {
|
|
1801
|
-
thoughtLog(
|
|
1802
|
-
`I found ${bomData.bomJson.components.length} crypto assets.`,
|
|
1595
|
+
if (step.collectFormulation && bomData?.formulationList?.length) {
|
|
1596
|
+
pathFormulationList = pathFormulationList.concat(
|
|
1597
|
+
bomData.formulationList,
|
|
1803
1598
|
);
|
|
1804
|
-
if (DEBUG_MODE) {
|
|
1805
|
-
console.log(
|
|
1806
|
-
`Found ${bomData.bomJson.components.length} crypto assets at ${path}`,
|
|
1807
|
-
);
|
|
1808
|
-
}
|
|
1809
|
-
components = components.concat(bomData.bomJson.components);
|
|
1810
|
-
if (bomData.dependencies?.length) {
|
|
1811
|
-
dependencies = mergeDependencies(dependencies, bomData.dependencies);
|
|
1812
|
-
}
|
|
1813
1599
|
}
|
|
1814
1600
|
}
|
|
1815
1601
|
return {
|
|
@@ -1818,6 +1604,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1818
1604
|
options,
|
|
1819
1605
|
parentSubComponents,
|
|
1820
1606
|
metadataProperties,
|
|
1607
|
+
formulationList: pathFormulationList,
|
|
1821
1608
|
};
|
|
1822
1609
|
};
|
|
1823
1610
|
const pathResults = [];
|
|
@@ -1884,6 +1671,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1884
1671
|
metadataProperties = metadataProperties.concat(
|
|
1885
1672
|
pathResult.metadataProperties,
|
|
1886
1673
|
);
|
|
1674
|
+
formulationList = formulationList.concat(pathResult.formulationList);
|
|
1887
1675
|
if (pathResult.options.services?.length) {
|
|
1888
1676
|
sharedOptions.services = mergeServices(
|
|
1889
1677
|
sharedOptions.services || [],
|
|
@@ -1975,6 +1763,26 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1975
1763
|
dependencies,
|
|
1976
1764
|
metadataProperties,
|
|
1977
1765
|
);
|
|
1766
|
+
// Language packages an OS package installed are judged by that package.
|
|
1767
|
+
// Duplicates have merged by now, so a component found both where the distro
|
|
1768
|
+
// put it and where pip did is seen whole and left unmarked.
|
|
1769
|
+
markOsInstalledLanguagePackages(
|
|
1770
|
+
multiResult.bomJson?.components,
|
|
1771
|
+
osFileOwners,
|
|
1772
|
+
options.allLayersExplodedDir,
|
|
1773
|
+
);
|
|
1774
|
+
// The default gems of an unpackaged Ruby, such as resolv in a Docker
|
|
1775
|
+
// Official image, ship with that Ruby and are updated by its releases.
|
|
1776
|
+
const defaultGemEdges = linkDefaultGemsToRuntimes(
|
|
1777
|
+
multiResult.bomJson?.components,
|
|
1778
|
+
options.allLayersExplodedDir,
|
|
1779
|
+
);
|
|
1780
|
+
if (defaultGemEdges.length && multiResult.bomJson) {
|
|
1781
|
+
multiResult.bomJson.dependencies = mergeDependencies(
|
|
1782
|
+
multiResult.bomJson.dependencies || [],
|
|
1783
|
+
defaultGemEdges,
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1978
1786
|
if (formulationList.length) {
|
|
1979
1787
|
multiResult.formulationList = formulationList;
|
|
1980
1788
|
}
|
|
@@ -3051,6 +2859,16 @@ async function createBomForPath(path, options) {
|
|
|
3051
2859
|
* @throws {Error} if the request fails
|
|
3052
2860
|
*/
|
|
3053
2861
|
export async function submitBom(args, bomContents) {
|
|
2862
|
+
// Dependency-Track ingests CycloneDX 1.x only. The CLI and the server refuse
|
|
2863
|
+
// a 2.0 upload before generating; this covers library callers.
|
|
2864
|
+
if (
|
|
2865
|
+
isCycloneDx20SpecVersion(bomContents?.specVersion) ||
|
|
2866
|
+
bomContents?.specFormat !== undefined
|
|
2867
|
+
) {
|
|
2868
|
+
console.log(DEPENDENCY_TRACK_CDX20_ERROR);
|
|
2869
|
+
args.failOnError && process.exit(1);
|
|
2870
|
+
return undefined;
|
|
2871
|
+
}
|
|
3054
2872
|
const dependencyTrackApiUrl = getDependencyTrackBomApiUrl(args.serverUrl);
|
|
3055
2873
|
const serverUrl = dependencyTrackApiUrl?.toString();
|
|
3056
2874
|
if (!dependencyTrackApiUrl || !serverUrl) {
|