@cdxgen/cdxgen 0.0.1 → 13.0.1
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 +921 -87
- package/bin/audit.js +239 -0
- package/bin/cdxgen.js +2335 -0
- package/bin/convert.js +220 -0
- package/bin/evinse.js +353 -0
- package/bin/hbom.js +504 -0
- package/bin/repl.js +2224 -0
- package/bin/sign.js +195 -0
- package/bin/tracebom.js +303 -0
- package/bin/validate.js +268 -0
- package/bin/verify.js +189 -0
- package/data/README.md +307 -0
- package/data/bom-1.4.schema.json +1605 -0
- package/data/bom-1.5.schema.json +3660 -0
- package/data/bom-1.6.schema.json +5511 -0
- package/data/bom-1.7.schema.json +6700 -0
- package/data/cbomosdb-queries.json +68 -0
- package/data/component-tags.json +339 -0
- package/data/container-knowledge-index.json +125 -0
- package/data/cosdb-queries.json +17 -0
- package/data/crypto-oid.json +1998 -0
- package/data/cryptography-defs.json +3793 -0
- package/data/cryptography-defs.schema.json +576 -0
- package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
- package/data/frameworks-list.json +219 -0
- package/data/glibc-stdlib.json +117 -0
- package/data/gtfobins-index.json +6296 -0
- package/data/helpers/init.gradle +71 -0
- package/data/jsf-0.82.schema.json +211 -0
- package/data/known-licenses.json +82 -0
- package/data/lic-mapping.json +338 -0
- package/data/license-aliases.json +6624 -0
- package/data/license-db.json +39059 -0
- package/data/license-deprecations.json +302 -0
- package/data/lolbas-index.json +150 -0
- package/data/predictive-audit-allowlist.json +11 -0
- package/data/pypi-pkg-aliases.json +1199 -0
- package/data/python-stdlib.json +308 -0
- package/data/queries-darwin.json +193 -0
- package/data/queries-win.json +257 -0
- package/data/queries.json +268 -0
- package/data/ruby-known-modules.json +281 -0
- package/data/rules/ai-agent-governance.yaml +323 -0
- package/data/rules/ai-bom.yaml +268 -0
- package/data/rules/ai-oversight.yaml +186 -0
- package/data/rules/ai-provenance.yaml +92 -0
- package/data/rules/asar-archives.yaml +150 -0
- package/data/rules/cbom-security.yaml +165 -0
- package/data/rules/chrome-extensions.yaml +248 -0
- package/data/rules/ci-permissions.yaml +802 -0
- package/data/rules/container-risk.yaml +353 -0
- package/data/rules/dependency-sources.yaml +236 -0
- package/data/rules/golem-go.yaml +229 -0
- package/data/rules/hbom-compliance.yaml +325 -0
- package/data/rules/hbom-performance.yaml +307 -0
- package/data/rules/hbom-security.yaml +248 -0
- package/data/rules/host-topology.yaml +165 -0
- package/data/rules/mcp-servers.yaml +388 -0
- package/data/rules/obom-runtime.yaml +1776 -0
- package/data/rules/package-integrity.yaml +364 -0
- package/data/rules/rootfs-hardening.yaml +179 -0
- package/data/rules/vscode-extensions.yaml +237 -0
- package/data/spdx-export.schema.json +6794 -0
- package/data/spdx-license-list.json +7449 -0
- package/data/spdx-licenses.json +759 -0
- package/data/spdx.schema.json +819 -0
- package/data/templates/README.md +3 -0
- package/data/templates/asvs-4.0.3.cdx.json +2943 -0
- package/data/templates/asvs-5.0.cdx.json +3046 -0
- package/data/templates/bsimm-v13.cdx.json +1008 -0
- package/data/templates/masvs-2.0.0.cdx.json +225 -0
- package/data/templates/nist-ssdf-1.1.cdx.json +828 -0
- package/data/templates/pcissc-secure-slc-1.1.cdx.json +1027 -0
- package/data/templates/scvs-1.0.0.cdx.json +813 -0
- package/data/templates/ssaf-DRAFT-2023-11.cdx.json +127 -0
- package/data/vendor-alias.json +41967 -0
- package/data/wrapdb-releases.json +4227 -0
- package/index.cjs +15 -0
- package/lib/audit/index.js +2800 -0
- package/lib/audit/progress.js +114 -0
- package/lib/audit/reporters.js +937 -0
- package/lib/audit/scoring.js +560 -0
- package/lib/audit/targets.js +798 -0
- package/lib/cli/bomAssembly.js +1882 -0
- package/lib/cli/cliOptions.js +543 -0
- package/lib/cli/index.js +2759 -0
- package/lib/cli/jsBom.js +2087 -0
- package/lib/cli/jvmBom.js +1858 -0
- package/lib/cli/managedBom.js +2083 -0
- package/lib/cli/nativeBom.js +2125 -0
- package/lib/core/activity.js +1087 -0
- package/lib/core/env.js +719 -0
- package/lib/core/fs.js +1058 -0
- package/lib/core/httpClient.js +730 -0
- package/lib/core/logger.js +280 -0
- package/lib/core/parallel.js +323 -0
- package/lib/core/paths.js +70 -0
- package/lib/core/projectConfig.js +122 -0
- package/lib/core/propertySanitizer.js +174 -0
- package/lib/core/spdx.js +5 -0
- package/lib/core/state.js +59 -0
- package/lib/core/text.js +207 -0
- package/lib/core/ui.js +920 -0
- package/lib/core/urls.js +84 -0
- package/lib/ecosystems/asarutils.js +1636 -0
- package/lib/ecosystems/bunutils.js +402 -0
- package/lib/ecosystems/caxa.js +77 -0
- package/lib/ecosystems/chromextutils.js +1174 -0
- package/lib/ecosystems/cmakeResolver.js +587 -0
- package/lib/ecosystems/cppEvidence.js +305 -0
- package/lib/ecosystems/denoutils.js +1094 -0
- package/lib/ecosystems/dotnetutils.js +132 -0
- package/lib/ecosystems/ecosystems.js +2772 -0
- package/lib/ecosystems/golem.js +1807 -0
- package/lib/ecosystems/gradleutils.js +1048 -0
- package/lib/ecosystems/jsEvidence.js +317 -0
- package/lib/ecosystems/npmutils.js +586 -0
- package/lib/ecosystems/parsers-bazel.js +875 -0
- package/lib/ecosystems/parsers-dotnet.js +1430 -0
- package/lib/ecosystems/parsers-gleam.js +302 -0
- package/lib/ecosystems/parsers-go.js +818 -0
- package/lib/ecosystems/parsers-js.js +4222 -0
- package/lib/ecosystems/parsers-jvm.js +948 -0
- package/lib/ecosystems/parsers-misc.js +3191 -0
- package/lib/ecosystems/parsers-python.js +2070 -0
- package/lib/ecosystems/parsers-rust.js +1294 -0
- package/lib/ecosystems/parsers-zig.js +861 -0
- package/lib/ecosystems/pep770.js +341 -0
- package/lib/ecosystems/pylockutils.js +427 -0
- package/lib/ecosystems/pythonutils.js +1281 -0
- package/lib/ecosystems/registryProvenance.js +1003 -0
- package/lib/ecosystems/remote/huggingface.js +1343 -0
- package/lib/ecosystems/rubyutils.js +1629 -0
- package/lib/ecosystems/rusi.js +820 -0
- package/lib/ecosystems/sbtutils.js +972 -0
- package/lib/ecosystems/tea.js +607 -0
- package/lib/ecosystems/utils.js +583 -0
- package/lib/ecosystems/zigResolver.js +410 -0
- package/lib/evinser/db.js +145 -0
- package/lib/evinser/evinser.js +2362 -0
- package/lib/evinser/scalasem.js +65 -0
- package/lib/evinser/swiftsem.js +767 -0
- package/lib/helpers/annotationFormatter.js +84 -0
- package/lib/helpers/bomSigner.js +312 -0
- package/lib/helpers/exportUtils.js +160 -0
- package/lib/helpers/remote/dependency-track.js +117 -0
- package/lib/helpers/versutils.js +202 -0
- package/lib/helpers/vsixutils.js +1069 -0
- package/lib/inventory/agentFormulationParser.js +322 -0
- package/lib/inventory/aiCollector.js +2140 -0
- package/lib/inventory/aiInventory.js +414 -0
- package/lib/inventory/aiModelVariants.js +121 -0
- package/lib/inventory/aiOversightCollector.js +661 -0
- package/lib/inventory/aiProvenanceCollector.js +822 -0
- package/lib/inventory/analyzer.js +6089 -0
- package/lib/inventory/analyzerScope.js +807 -0
- package/lib/inventory/astWorker.js +20 -0
- package/lib/inventory/atomUtils.js +1092 -0
- package/lib/inventory/auditCategories.js +192 -0
- package/lib/inventory/bomUtils.js +369 -0
- package/lib/inventory/cacheCommands.js +182 -0
- package/lib/inventory/cacheDir.js +104 -0
- package/lib/inventory/cbomutils.js +590 -0
- package/lib/inventory/cdxrs.js +434 -0
- package/lib/inventory/ciParsers/azurePipelines.js +295 -0
- package/lib/inventory/ciParsers/circleCi.js +286 -0
- package/lib/inventory/ciParsers/common.js +24 -0
- package/lib/inventory/ciParsers/githubActions.js +2434 -0
- package/lib/inventory/ciParsers/gitlabCi.js +213 -0
- package/lib/inventory/ciParsers/jenkins.js +180 -0
- package/lib/inventory/citations.js +234 -0
- package/lib/inventory/communityAiConfigParser.js +756 -0
- package/lib/inventory/containerRisk.js +204 -0
- package/lib/inventory/cryptoAlgorithmFamily.js +258 -0
- package/lib/inventory/deps.js +807 -0
- package/lib/inventory/depsUtils.js +588 -0
- package/lib/inventory/display.js +2128 -0
- package/lib/inventory/dosai.js +601 -0
- package/lib/inventory/dosaiParsers.js +154 -0
- package/lib/inventory/dynamic.js +145 -0
- package/lib/inventory/envcontext.js +1793 -0
- package/lib/inventory/evidenceUtils.js +774 -0
- package/lib/inventory/fetchBatch.js +779 -0
- package/lib/inventory/fetchRate.js +255 -0
- package/lib/inventory/forgeEnricher.js +300 -0
- package/lib/inventory/formulationParsers.js +422 -0
- package/lib/inventory/gtfobins.js +339 -0
- package/lib/inventory/hbom.js +719 -0
- package/lib/inventory/hbomAnalysis.js +310 -0
- package/lib/inventory/hbomLoader.js +35 -0
- package/lib/inventory/hostTopology.js +835 -0
- package/lib/inventory/inventoryStats.js +100 -0
- package/lib/inventory/jsonLike.js +121 -0
- package/lib/inventory/licenseEnhancer.js +918 -0
- package/lib/inventory/lolbas.js +285 -0
- package/lib/inventory/mcp.js +253 -0
- package/lib/inventory/mcpConfigParser.js +683 -0
- package/lib/inventory/mcpDiscovery.js +112 -0
- package/lib/inventory/mcpPinning.js +209 -0
- package/lib/inventory/osPackageResolver.js +493 -0
- package/lib/inventory/osinfo.js +383 -0
- package/lib/inventory/osqueryTransform.js +274 -0
- package/lib/inventory/plugins.js +466 -0
- package/lib/inventory/protobom.js +231 -0
- package/lib/inventory/protobomLoader.js +43 -0
- package/lib/inventory/provenanceUtils.js +224 -0
- package/lib/inventory/purl.js +749 -0
- package/lib/inventory/rustFormulationParser.js +339 -0
- package/lib/inventory/sbomDocument.js +340 -0
- package/lib/inventory/source.js +1488 -0
- package/lib/inventory/spdx.js +361 -0
- package/lib/inventory/spdxExpression.js +512 -0
- package/lib/inventory/spdxUtils.js +107 -0
- package/lib/inventory/table.js +267 -0
- package/lib/inventory/traceRunner.js +439 -0
- package/lib/inventory/unicodeScan.js +147 -0
- package/lib/managers/binary.js +3459 -0
- package/lib/managers/containerutils.js +125 -0
- package/lib/managers/docker.js +2145 -0
- package/lib/managers/dockerConnection.js +162 -0
- package/lib/managers/oci.js +704 -0
- package/lib/managers/piptree.js +267 -0
- package/lib/parsers/cmakeCache.js +199 -0
- package/lib/parsers/gguf.js +492 -0
- package/lib/parsers/gitAiNotes.js +233 -0
- package/lib/parsers/gitmodules.js +239 -0
- package/lib/parsers/htmlExtract.js +610 -0
- package/lib/parsers/huggingfaceManifest.js +551 -0
- package/lib/parsers/huggingfaceUtils.js +506 -0
- package/lib/parsers/iri.js +618 -0
- package/lib/parsers/npmrc.js +99 -0
- package/lib/parsers/ollama.js +101 -0
- package/lib/parsers/xml.js +841 -0
- package/lib/server/openapi.yaml +433 -0
- package/lib/server/server.js +616 -0
- package/lib/stages/postgen/annotator.js +977 -0
- package/lib/stages/postgen/auditBom.js +468 -0
- package/lib/stages/postgen/postgen.js +1239 -0
- package/lib/stages/postgen/ruleEngine.js +731 -0
- package/lib/stages/postgen/sortBom.js +195 -0
- package/lib/stages/postgen/spdxConverter.js +796 -0
- package/lib/stages/postgen/specVersionCompat.js +703 -0
- package/lib/stages/pregen/envAudit.js +388 -0
- package/lib/stages/pregen/pregen.js +637 -0
- package/lib/third-party/README.md +9 -0
- package/lib/third-party/arborist/CHANGELOG.md +1109 -0
- package/lib/third-party/arborist/LICENSE.md +20 -0
- package/lib/third-party/arborist/README.md +315 -0
- package/lib/third-party/arborist/lib/arborist/index.js +258 -0
- package/lib/third-party/arborist/lib/arborist/load-actual.js +605 -0
- package/lib/third-party/arborist/lib/arborist/load-virtual.js +300 -0
- package/lib/third-party/arborist/lib/bin-links.js +120 -0
- package/lib/third-party/arborist/lib/calc-dep-flags.js +130 -0
- package/lib/third-party/arborist/lib/case-insensitive-map.js +51 -0
- package/lib/third-party/arborist/lib/consistent-resolve.js +42 -0
- package/lib/third-party/arborist/lib/debug.js +50 -0
- package/lib/third-party/arborist/lib/dep-valid.js +190 -0
- package/lib/third-party/arborist/lib/edge.js +417 -0
- package/lib/third-party/arborist/lib/from-path.js +36 -0
- package/lib/third-party/arborist/lib/gather-dep-set.js +43 -0
- package/lib/third-party/arborist/lib/index.js +22 -0
- package/lib/third-party/arborist/lib/inventory.js +138 -0
- package/lib/third-party/arborist/lib/link.js +169 -0
- package/lib/third-party/arborist/lib/node.js +1673 -0
- package/lib/third-party/arborist/lib/npm-extension.js +45 -0
- package/lib/third-party/arborist/lib/override-resolves.js +11 -0
- package/lib/third-party/arborist/lib/override-set.js +319 -0
- package/lib/third-party/arborist/lib/package-extensions.js +289 -0
- package/lib/third-party/arborist/lib/printable.js +210 -0
- package/lib/third-party/arborist/lib/proc-log.js +30 -0
- package/lib/third-party/arborist/lib/realpath.js +100 -0
- package/lib/third-party/arborist/lib/relpath.js +4 -0
- package/lib/third-party/arborist/lib/shrinkwrap.js +1291 -0
- package/lib/third-party/arborist/lib/spec-from-lock.js +34 -0
- package/lib/third-party/arborist/lib/tree-check.js +165 -0
- package/lib/third-party/arborist/lib/validate-npm-package-name.js +107 -0
- package/lib/third-party/arborist/lib/version-from-tgz.js +51 -0
- package/lib/third-party/arborist/lib/yarn-lock.js +397 -0
- package/lib/validator/bomValidator.js +985 -0
- package/lib/validator/complianceEngine.js +241 -0
- package/lib/validator/complianceRules.js +1680 -0
- package/lib/validator/generated/runtime.mjs +17 -0
- package/lib/validator/generated/validate-1.6.mjs +21258 -0
- package/lib/validator/generated/validate-1.7.mjs +24803 -0
- package/lib/validator/generated/validate-2.0.mjs +22931 -0
- package/lib/validator/index.js +246 -0
- package/lib/validator/reporters/annotations.js +142 -0
- package/lib/validator/reporters/console.js +160 -0
- package/lib/validator/reporters/index.js +41 -0
- package/lib/validator/reporters/json.js +37 -0
- package/lib/validator/reporters/sarif.js +184 -0
- package/package.json +209 -26
- package/types/bin/audit.d.ts +3 -0
- package/types/bin/audit.d.ts.map +1 -0
- package/types/bin/cdxgen.d.ts +3 -0
- package/types/bin/cdxgen.d.ts.map +1 -0
- package/types/bin/convert.d.ts +3 -0
- package/types/bin/convert.d.ts.map +1 -0
- package/types/bin/evinse.d.ts +3 -0
- package/types/bin/evinse.d.ts.map +1 -0
- package/types/bin/hbom.d.ts +3 -0
- package/types/bin/hbom.d.ts.map +1 -0
- package/types/bin/repl.d.ts +3 -0
- package/types/bin/repl.d.ts.map +1 -0
- package/types/bin/sign.d.ts +3 -0
- package/types/bin/sign.d.ts.map +1 -0
- package/types/bin/tracebom.d.ts +3 -0
- package/types/bin/tracebom.d.ts.map +1 -0
- package/types/bin/validate.d.ts +14 -0
- package/types/bin/validate.d.ts.map +1 -0
- package/types/bin/verify.d.ts +3 -0
- package/types/bin/verify.d.ts.map +1 -0
- package/types/lib/audit/index.d.ts +153 -0
- package/types/lib/audit/index.d.ts.map +1 -0
- package/types/lib/audit/progress.d.ts +31 -0
- package/types/lib/audit/progress.d.ts.map +1 -0
- package/types/lib/audit/reporters.d.ts +62 -0
- package/types/lib/audit/reporters.d.ts.map +1 -0
- package/types/lib/audit/scoring.d.ts +36 -0
- package/types/lib/audit/scoring.d.ts.map +1 -0
- package/types/lib/audit/targets.d.ts +77 -0
- package/types/lib/audit/targets.d.ts.map +1 -0
- package/types/lib/cli/bomAssembly.d.ts +329 -0
- package/types/lib/cli/bomAssembly.d.ts.map +1 -0
- package/types/lib/cli/cliOptions.d.ts +156 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts +107 -0
- package/types/lib/cli/index.d.ts.map +1 -0
- package/types/lib/cli/jsBom.d.ts +94 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -0
- package/types/lib/cli/jvmBom.d.ts +54 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -0
- package/types/lib/cli/managedBom.d.ts +105 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -0
- package/types/lib/cli/nativeBom.d.ts +143 -0
- package/types/lib/cli/nativeBom.d.ts.map +1 -0
- package/types/lib/core/activity.d.ts +178 -0
- package/types/lib/core/activity.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +192 -0
- package/types/lib/core/env.d.ts.map +1 -0
- package/types/lib/core/fs.d.ts +158 -0
- package/types/lib/core/fs.d.ts.map +1 -0
- package/types/lib/core/httpClient.d.ts +149 -0
- package/types/lib/core/httpClient.d.ts.map +1 -0
- package/types/lib/core/logger.d.ts +50 -0
- package/types/lib/core/logger.d.ts.map +1 -0
- package/types/lib/core/parallel.d.ts +95 -0
- package/types/lib/core/parallel.d.ts.map +1 -0
- package/types/lib/core/paths.d.ts +36 -0
- package/types/lib/core/paths.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +23 -0
- package/types/lib/core/projectConfig.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +23 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -0
- package/types/lib/core/spdx.d.ts +5 -0
- package/types/lib/core/spdx.d.ts.map +1 -0
- package/types/lib/core/state.d.ts +20 -0
- package/types/lib/core/state.d.ts.map +1 -0
- package/types/lib/core/text.d.ts +78 -0
- package/types/lib/core/text.d.ts.map +1 -0
- package/types/lib/core/ui.d.ts +87 -0
- package/types/lib/core/ui.d.ts.map +1 -0
- package/types/lib/core/urls.d.ts +43 -0
- package/types/lib/core/urls.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts +71 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -0
- package/types/lib/ecosystems/bunutils.d.ts +38 -0
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts +8 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -0
- package/types/lib/ecosystems/chromextutils.d.ts +97 -0
- package/types/lib/ecosystems/chromextutils.d.ts.map +1 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts +176 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +35 -0
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +76 -0
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -0
- package/types/lib/ecosystems/dotnetutils.d.ts +45 -0
- package/types/lib/ecosystems/dotnetutils.d.ts.map +1 -0
- package/types/lib/ecosystems/ecosystems.d.ts +279 -0
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -0
- package/types/lib/ecosystems/golem.d.ts +60 -0
- package/types/lib/ecosystems/golem.d.ts.map +1 -0
- package/types/lib/ecosystems/gradleutils.d.ts +139 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -0
- package/types/lib/ecosystems/jsEvidence.d.ts +17 -0
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +183 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-bazel.d.ts +36 -0
- package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +116 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-gleam.d.ts +33 -0
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-go.d.ts +92 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-js.d.ts +242 -0
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-jvm.d.ts +100 -0
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +450 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts +145 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +56 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-zig.d.ts +83 -0
- package/types/lib/ecosystems/parsers-zig.d.ts.map +1 -0
- package/types/lib/ecosystems/pep770.d.ts +38 -0
- package/types/lib/ecosystems/pep770.d.ts.map +1 -0
- package/types/lib/ecosystems/pylockutils.d.ts +68 -0
- package/types/lib/ecosystems/pylockutils.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +70 -0
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -0
- package/types/lib/ecosystems/registryProvenance.d.ts +26 -0
- package/types/lib/ecosystems/registryProvenance.d.ts.map +1 -0
- package/types/lib/ecosystems/remote/huggingface.d.ts +31 -0
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -0
- package/types/lib/ecosystems/rubyutils.d.ts +182 -0
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -0
- package/types/lib/ecosystems/rusi.d.ts +40 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -0
- package/types/lib/ecosystems/sbtutils.d.ts +179 -0
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -0
- package/types/lib/ecosystems/tea.d.ts +180 -0
- package/types/lib/ecosystems/tea.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +41 -0
- package/types/lib/ecosystems/utils.d.ts.map +1 -0
- package/types/lib/ecosystems/zigResolver.d.ts +70 -0
- package/types/lib/ecosystems/zigResolver.d.ts.map +1 -0
- package/types/lib/evinser/db.d.ts +17 -0
- package/types/lib/evinser/db.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +354 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -0
- package/types/lib/evinser/scalasem.d.ts +15 -0
- package/types/lib/evinser/scalasem.d.ts.map +1 -0
- package/types/lib/evinser/swiftsem.d.ts +103 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -0
- package/types/lib/helpers/annotationFormatter.d.ts +23 -0
- package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
- package/types/lib/helpers/bomSigner.d.ts +28 -0
- package/types/lib/helpers/bomSigner.d.ts.map +1 -0
- package/types/lib/helpers/exportUtils.d.ts +52 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +23 -0
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
- package/types/lib/helpers/versutils.d.ts +8 -0
- package/types/lib/helpers/versutils.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +130 -0
- package/types/lib/helpers/vsixutils.d.ts.map +1 -0
- package/types/lib/inventory/agentFormulationParser.d.ts +21 -0
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts +61 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -0
- package/types/lib/inventory/aiInventory.d.ts +103 -0
- package/types/lib/inventory/aiInventory.d.ts.map +1 -0
- package/types/lib/inventory/aiModelVariants.d.ts +31 -0
- package/types/lib/inventory/aiModelVariants.d.ts.map +1 -0
- package/types/lib/inventory/aiOversightCollector.d.ts +28 -0
- package/types/lib/inventory/aiOversightCollector.d.ts.map +1 -0
- package/types/lib/inventory/aiProvenanceCollector.d.ts +13 -0
- package/types/lib/inventory/aiProvenanceCollector.d.ts.map +1 -0
- package/types/lib/inventory/analyzer.d.ts +157 -0
- package/types/lib/inventory/analyzer.d.ts.map +1 -0
- package/types/lib/inventory/analyzerScope.d.ts +106 -0
- package/types/lib/inventory/analyzerScope.d.ts.map +1 -0
- package/types/lib/inventory/astWorker.d.ts +9 -0
- package/types/lib/inventory/astWorker.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +159 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -0
- package/types/lib/inventory/auditCategories.d.ts +80 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -0
- package/types/lib/inventory/bomUtils.d.ts +145 -0
- package/types/lib/inventory/bomUtils.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +28 -0
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -0
- package/types/lib/inventory/cacheDir.d.ts +55 -0
- package/types/lib/inventory/cacheDir.d.ts.map +1 -0
- package/types/lib/inventory/cbomutils.d.ts +37 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -0
- package/types/lib/inventory/cdxrs.d.ts +90 -0
- package/types/lib/inventory/cdxrs.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/azurePipelines.d.ts +27 -0
- package/types/lib/inventory/ciParsers/azurePipelines.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/circleCi.d.ts +27 -0
- package/types/lib/inventory/ciParsers/circleCi.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/common.d.ts +11 -0
- package/types/lib/inventory/ciParsers/common.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/githubActions.d.ts +38 -0
- package/types/lib/inventory/ciParsers/githubActions.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/gitlabCi.d.ts +26 -0
- package/types/lib/inventory/ciParsers/gitlabCi.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/jenkins.d.ts +26 -0
- package/types/lib/inventory/ciParsers/jenkins.d.ts.map +1 -0
- package/types/lib/inventory/citations.d.ts +90 -0
- package/types/lib/inventory/citations.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts +14 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -0
- package/types/lib/inventory/containerRisk.d.ts +20 -0
- package/types/lib/inventory/containerRisk.d.ts.map +1 -0
- package/types/lib/inventory/cryptoAlgorithmFamily.d.ts +37 -0
- package/types/lib/inventory/cryptoAlgorithmFamily.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +140 -0
- package/types/lib/inventory/deps.d.ts.map +1 -0
- package/types/lib/inventory/depsUtils.d.ts +74 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -0
- package/types/lib/inventory/display.d.ts +227 -0
- package/types/lib/inventory/display.d.ts.map +1 -0
- package/types/lib/inventory/dosai.d.ts +130 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -0
- package/types/lib/inventory/dosaiParsers.d.ts +59 -0
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -0
- package/types/lib/inventory/dynamic.d.ts +25 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -0
- package/types/lib/inventory/envcontext.d.ts +377 -0
- package/types/lib/inventory/envcontext.d.ts.map +1 -0
- package/types/lib/inventory/evidenceUtils.d.ts +74 -0
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -0
- package/types/lib/inventory/fetchBatch.d.ts +127 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -0
- package/types/lib/inventory/fetchRate.d.ts +118 -0
- package/types/lib/inventory/fetchRate.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts +31 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -0
- package/types/lib/inventory/formulationParsers.d.ts +50 -0
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -0
- package/types/lib/inventory/gtfobins.d.ts +27 -0
- package/types/lib/inventory/gtfobins.d.ts.map +1 -0
- package/types/lib/inventory/hbom.d.ts +58 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -0
- package/types/lib/inventory/hbomAnalysis.d.ts +56 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -0
- package/types/lib/inventory/hbomLoader.d.ts +7 -0
- package/types/lib/inventory/hbomLoader.d.ts.map +1 -0
- package/types/lib/inventory/hostTopology.d.ts +47 -0
- package/types/lib/inventory/hostTopology.d.ts.map +1 -0
- package/types/lib/inventory/inventoryStats.d.ts +42 -0
- package/types/lib/inventory/inventoryStats.d.ts.map +1 -0
- package/types/lib/inventory/jsonLike.d.ts +23 -0
- package/types/lib/inventory/jsonLike.d.ts.map +1 -0
- package/types/lib/inventory/licenseEnhancer.d.ts +70 -0
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -0
- package/types/lib/inventory/lolbas.d.ts +16 -0
- package/types/lib/inventory/lolbas.d.ts.map +1 -0
- package/types/lib/inventory/mcp.d.ts +29 -0
- package/types/lib/inventory/mcp.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts +20 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -0
- package/types/lib/inventory/mcpDiscovery.d.ts +33 -0
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -0
- package/types/lib/inventory/mcpPinning.d.ts +11 -0
- package/types/lib/inventory/mcpPinning.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +49 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -0
- package/types/lib/inventory/osinfo.d.ts +201 -0
- package/types/lib/inventory/osinfo.d.ts.map +1 -0
- package/types/lib/inventory/osqueryTransform.d.ts +84 -0
- package/types/lib/inventory/osqueryTransform.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts +58 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -0
- package/types/lib/inventory/protobom.d.ts +71 -0
- package/types/lib/inventory/protobom.d.ts.map +1 -0
- package/types/lib/inventory/protobomLoader.d.ts +17 -0
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -0
- package/types/lib/inventory/provenanceUtils.d.ts +98 -0
- package/types/lib/inventory/provenanceUtils.d.ts.map +1 -0
- package/types/lib/inventory/purl.d.ts +313 -0
- package/types/lib/inventory/purl.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts +17 -0
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -0
- package/types/lib/inventory/sbomDocument.d.ts +59 -0
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +163 -0
- package/types/lib/inventory/source.d.ts.map +1 -0
- package/types/lib/inventory/spdx.d.ts +66 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -0
- package/types/lib/inventory/spdxExpression.d.ts +58 -0
- package/types/lib/inventory/spdxExpression.d.ts.map +1 -0
- package/types/lib/inventory/spdxUtils.d.ts +12 -0
- package/types/lib/inventory/spdxUtils.d.ts.map +1 -0
- package/types/lib/inventory/table.d.ts +22 -0
- package/types/lib/inventory/table.d.ts.map +1 -0
- package/types/lib/inventory/traceRunner.d.ts +80 -0
- package/types/lib/inventory/traceRunner.d.ts.map +1 -0
- package/types/lib/inventory/unicodeScan.d.ts +46 -0
- package/types/lib/inventory/unicodeScan.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +147 -0
- package/types/lib/managers/binary.d.ts.map +1 -0
- package/types/lib/managers/containerutils.d.ts +30 -0
- package/types/lib/managers/containerutils.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts +157 -0
- package/types/lib/managers/docker.d.ts.map +1 -0
- package/types/lib/managers/dockerConnection.d.ts +42 -0
- package/types/lib/managers/dockerConnection.d.ts.map +1 -0
- package/types/lib/managers/oci.d.ts +21 -0
- package/types/lib/managers/oci.d.ts.map +1 -0
- package/types/lib/managers/piptree.d.ts +13 -0
- package/types/lib/managers/piptree.d.ts.map +1 -0
- package/types/lib/parsers/cmakeCache.d.ts +70 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -0
- package/types/lib/parsers/gguf.d.ts +39 -0
- package/types/lib/parsers/gguf.d.ts.map +1 -0
- package/types/lib/parsers/gitAiNotes.d.ts +15 -0
- package/types/lib/parsers/gitAiNotes.d.ts.map +1 -0
- package/types/lib/parsers/gitmodules.d.ts +88 -0
- package/types/lib/parsers/gitmodules.d.ts.map +1 -0
- package/types/lib/parsers/htmlExtract.d.ts +34 -0
- package/types/lib/parsers/htmlExtract.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceManifest.d.ts +153 -0
- package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +155 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -0
- package/types/lib/parsers/iri.d.ts +68 -0
- package/types/lib/parsers/iri.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +33 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -0
- package/types/lib/parsers/ollama.d.ts +22 -0
- package/types/lib/parsers/ollama.d.ts.map +1 -0
- package/types/lib/parsers/xml.d.ts +47 -0
- package/types/lib/parsers/xml.d.ts.map +1 -0
- package/types/lib/server/server.d.ts +51 -0
- package/types/lib/server/server.d.ts.map +1 -0
- package/types/lib/stages/postgen/annotator.d.ts +36 -0
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -0
- package/types/lib/stages/postgen/auditBom.d.ts +92 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +59 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -0
- package/types/lib/stages/postgen/ruleEngine.d.ts +18 -0
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -0
- package/types/lib/stages/postgen/sortBom.d.ts +34 -0
- package/types/lib/stages/postgen/sortBom.d.ts.map +1 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts +71 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -0
- package/types/lib/stages/pregen/envAudit.d.ts +14 -0
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -0
- package/types/lib/stages/pregen/pregen.d.ts +59 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/arborist/index.d.ts +47 -0
- package/types/lib/third-party/arborist/lib/arborist/index.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts +4 -0
- package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts +4 -0
- package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/bin-links.d.ts +18 -0
- package/types/lib/third-party/arborist/lib/bin-links.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts +9 -0
- package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/debug.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/debug.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/dep-valid.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/dep-valid.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/edge.d.ts +46 -0
- package/types/lib/third-party/arborist/lib/edge.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/from-path.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/from-path.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/index.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/index.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/inventory.d.ts +14 -0
- package/types/lib/third-party/arborist/lib/inventory.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/link.d.ts +18 -0
- package/types/lib/third-party/arborist/lib/link.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/node.d.ts +112 -0
- package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/npm-extension.d.ts +18 -0
- package/types/lib/third-party/arborist/lib/npm-extension.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/override-resolves.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/override-resolves.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/override-set.d.ts +27 -0
- package/types/lib/third-party/arborist/lib/override-set.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/package-extensions.d.ts +36 -0
- package/types/lib/third-party/arborist/lib/package-extensions.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/printable.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/printable.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/proc-log.d.ts +20 -0
- package/types/lib/third-party/arborist/lib/proc-log.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/realpath.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/realpath.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/relpath.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/relpath.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts +54 -0
- package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/tree-check.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/tree-check.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts +13 -0
- package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts +6 -0
- package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/yarn-lock.d.ts +35 -0
- package/types/lib/third-party/arborist/lib/yarn-lock.d.ts.map +1 -0
- package/types/lib/validator/bomValidator.d.ts +40 -0
- package/types/lib/validator/bomValidator.d.ts.map +1 -0
- package/types/lib/validator/complianceEngine.d.ts +72 -0
- package/types/lib/validator/complianceEngine.d.ts.map +1 -0
- package/types/lib/validator/complianceRules.d.ts +61 -0
- package/types/lib/validator/complianceRules.d.ts.map +1 -0
- package/types/lib/validator/index.d.ts +101 -0
- package/types/lib/validator/index.d.ts.map +1 -0
- package/types/lib/validator/reporters/annotations.d.ts +37 -0
- package/types/lib/validator/reporters/annotations.d.ts.map +1 -0
- package/types/lib/validator/reporters/console.d.ts +34 -0
- package/types/lib/validator/reporters/console.d.ts.map +1 -0
- package/types/lib/validator/reporters/index.d.ts +30 -0
- package/types/lib/validator/reporters/index.d.ts.map +1 -0
- package/types/lib/validator/reporters/json.d.ts +11 -0
- package/types/lib/validator/reporters/json.d.ts.map +1 -0
- package/types/lib/validator/reporters/sarif.d.ts +22 -0
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -0
- package/mod.d.ts +0 -7
- package/mod.js +0 -22
|
@@ -0,0 +1,1882 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { randomUUID as uuidv4 } from "node:crypto";
|
|
3
|
+
import { lstatSync, readFileSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
7
|
+
import { parse } from "ssri";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
DEBUG_MODE,
|
|
11
|
+
recordActivity,
|
|
12
|
+
resetActivityContext,
|
|
13
|
+
setActivityContext,
|
|
14
|
+
} from "../core/activity.js";
|
|
15
|
+
import { PROJECT_TYPE_ALIASES } from "../core/env.js";
|
|
16
|
+
import {
|
|
17
|
+
getTimestamp,
|
|
18
|
+
getTmpDir,
|
|
19
|
+
safeExistsSync,
|
|
20
|
+
safeMkdtempSync,
|
|
21
|
+
} from "../core/fs.js";
|
|
22
|
+
import {
|
|
23
|
+
CDXGEN_TOOL_GROUP,
|
|
24
|
+
CDXGEN_TOOL_NAME,
|
|
25
|
+
CDXGEN_VERSION,
|
|
26
|
+
frameworksList,
|
|
27
|
+
} from "../core/state.js";
|
|
28
|
+
import { isPkgTypeOnlyImport } from "../ecosystems/npmutils.js";
|
|
29
|
+
import { parsePackageJsonName } from "../ecosystems/parsers-js.js";
|
|
30
|
+
import {
|
|
31
|
+
AI_INSTRUCTION_FILE_KINDS,
|
|
32
|
+
AI_INVENTORY_PROJECT_TYPES,
|
|
33
|
+
AI_SKILL_FILE_KIND,
|
|
34
|
+
inventoryPropertyValue,
|
|
35
|
+
MCP_CONFIG_FILE_KIND,
|
|
36
|
+
optionIncludesAiInventoryProjectType,
|
|
37
|
+
summarizeAiInventory,
|
|
38
|
+
} from "../inventory/aiInventory.js";
|
|
39
|
+
import { expandBomAuditCategories } from "../inventory/auditCategories.js";
|
|
40
|
+
import {
|
|
41
|
+
DEFAULT_CDX_SPEC_VERSION,
|
|
42
|
+
setCycloneDxFormat,
|
|
43
|
+
toCycloneDxSpecVersionString,
|
|
44
|
+
} from "../inventory/bomUtils.js";
|
|
45
|
+
import { mergeServices, trimComponents } from "../inventory/depsUtils.js";
|
|
46
|
+
import {
|
|
47
|
+
attachIdentityTools,
|
|
48
|
+
extractToolRefs,
|
|
49
|
+
} from "../inventory/evidenceUtils.js";
|
|
50
|
+
import { enrichComponentWithMcpMetadata } from "../inventory/mcp.js";
|
|
51
|
+
import {
|
|
52
|
+
applyPurl,
|
|
53
|
+
encodeForPurl,
|
|
54
|
+
isValidPurl,
|
|
55
|
+
npmPurl,
|
|
56
|
+
tryBuildPurl,
|
|
57
|
+
} from "../inventory/purl.js";
|
|
58
|
+
import { repairPurl } from "../inventory/sbomDocument.js";
|
|
59
|
+
import { getLicenses } from "../inventory/spdx.js";
|
|
60
|
+
import { getBinaryBom } from "../managers/binary.js";
|
|
61
|
+
import { isValidIriReference } from "../parsers/iri.js";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Determines whether the `node_modules` directory should be scanned for the
|
|
65
|
+
* current run. Scanning is always performed in deep mode or when no project
|
|
66
|
+
* type is selected; otherwise the selected types must include at least one of
|
|
67
|
+
* the requested base project types.
|
|
68
|
+
*
|
|
69
|
+
* @param {object} options CLI options
|
|
70
|
+
* @param {string[]} baseProjectTypes Base project types that require the scan
|
|
71
|
+
* @returns {boolean} True when `node_modules` should be included in the scan
|
|
72
|
+
*/
|
|
73
|
+
export const shouldIncludeNodeModulesDir = (
|
|
74
|
+
options = {},
|
|
75
|
+
baseProjectTypes = [],
|
|
76
|
+
) => {
|
|
77
|
+
if (options.deep) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const projectTypes = Array.isArray(options.projectType)
|
|
81
|
+
? options.projectType
|
|
82
|
+
: options.projectType
|
|
83
|
+
? [options.projectType]
|
|
84
|
+
: [];
|
|
85
|
+
if (!projectTypes.length) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
return baseProjectTypes.some((projectType) =>
|
|
89
|
+
projectTypes.every((selectedProjectType) =>
|
|
90
|
+
PROJECT_TYPE_ALIASES[projectType]?.includes(selectedProjectType),
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Regex source string that matches hex digests of common lengths
|
|
97
|
+
* (32, 40, 64, 96, or 128 characters) used for integrity validation.
|
|
98
|
+
*
|
|
99
|
+
* @type {string}
|
|
100
|
+
*/
|
|
101
|
+
export const HASH_PATTERN =
|
|
102
|
+
"^([a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128})$";
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates a default parent component based on the directory name.
|
|
106
|
+
*
|
|
107
|
+
* @param {String} path Directory or file name
|
|
108
|
+
* @param {String} type Package type
|
|
109
|
+
* @param {Object} options CLI options
|
|
110
|
+
* @returns component object
|
|
111
|
+
*/
|
|
112
|
+
export const createDefaultParentComponent = (
|
|
113
|
+
path,
|
|
114
|
+
type = "application",
|
|
115
|
+
options = {},
|
|
116
|
+
) => {
|
|
117
|
+
// Expands any relative path such as dot
|
|
118
|
+
path = resolve(path);
|
|
119
|
+
// Create a parent component based on the directory name
|
|
120
|
+
let dirNameStr =
|
|
121
|
+
safeExistsSync(path) && lstatSync(path).isDirectory()
|
|
122
|
+
? basename(path)
|
|
123
|
+
: dirname(path);
|
|
124
|
+
const tmpA = dirNameStr.split(sep);
|
|
125
|
+
dirNameStr = tmpA[tmpA.length - 1];
|
|
126
|
+
const compName = "project-name" in options ? options.projectName : dirNameStr;
|
|
127
|
+
const parentComponent = {
|
|
128
|
+
group: options.projectGroup || "",
|
|
129
|
+
name: compName,
|
|
130
|
+
version: `${options.projectVersion || "latest"}`,
|
|
131
|
+
type:
|
|
132
|
+
type === "container" || compName.endsWith(".tar")
|
|
133
|
+
? "container"
|
|
134
|
+
: "application",
|
|
135
|
+
};
|
|
136
|
+
// Strict purl validation (swift requires a namespace, maven a groupId)
|
|
137
|
+
// rejects root application components that have no group. applyPurl then
|
|
138
|
+
// omits the purl rather than inventing one, and derives a unique bom-ref.
|
|
139
|
+
applyPurl(
|
|
140
|
+
parentComponent,
|
|
141
|
+
tryBuildPurl({
|
|
142
|
+
type: type,
|
|
143
|
+
namespace: parentComponent.group || null,
|
|
144
|
+
name: parentComponent.name,
|
|
145
|
+
version: parentComponent.version || null,
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
return parentComponent;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Builds the parent (metadata.component) for the BOM from the
|
|
153
|
+
* `--project-name`/`--project-version`/`--project-group` CLI options,
|
|
154
|
+
* constructing a purl and bom-ref. Returns the caller-supplied
|
|
155
|
+
* `options.parentComponent` unchanged when present.
|
|
156
|
+
*
|
|
157
|
+
* @param {object} options CLI options
|
|
158
|
+
* @returns {object|undefined} Parent component object, or undefined when no
|
|
159
|
+
* project name/version is available
|
|
160
|
+
*/
|
|
161
|
+
export const determineParentComponent = (options) => {
|
|
162
|
+
let parentComponent;
|
|
163
|
+
if (options.parentComponent && Object.keys(options.parentComponent).length) {
|
|
164
|
+
return options.parentComponent;
|
|
165
|
+
}
|
|
166
|
+
if (options.projectName && options.projectVersion) {
|
|
167
|
+
parentComponent = {
|
|
168
|
+
group: options.projectGroup || "",
|
|
169
|
+
name: options.projectName,
|
|
170
|
+
version: `${options.projectVersion}` || "",
|
|
171
|
+
type: "application",
|
|
172
|
+
};
|
|
173
|
+
const ppurl = build({
|
|
174
|
+
type: parentComponent.type,
|
|
175
|
+
namespace: parentComponent.group || null,
|
|
176
|
+
name: parentComponent.name,
|
|
177
|
+
version: parentComponent.version || null,
|
|
178
|
+
});
|
|
179
|
+
parentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
180
|
+
parentComponent["purl"] = ppurl;
|
|
181
|
+
}
|
|
182
|
+
return parentComponent;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Re-anchor a dependency graph onto a caller-supplied parent component.
|
|
187
|
+
*
|
|
188
|
+
* A generator builds its `dependencies[]` around the parent it detected from
|
|
189
|
+
* the manifest it read. `--project-name`/`--project-version` and an explicit
|
|
190
|
+
* `parentComponent` replace that parent in `metadata.component`, which leaves
|
|
191
|
+
* the graph rooted at a `bom-ref` that appears nowhere in the document — every
|
|
192
|
+
* edge the generator produced hangs off a dangling ref.
|
|
193
|
+
*
|
|
194
|
+
* The detected parent is real: it is the project the manifest describes, and
|
|
195
|
+
* the supplied parent is the larger thing that contains it. So it is kept as a
|
|
196
|
+
* component and an edge is drawn from the supplied parent to it, which is the
|
|
197
|
+
* CycloneDX way to express a subproject and preserves every edge below it.
|
|
198
|
+
*
|
|
199
|
+
* @param {object|undefined} overrideParent Parent supplied through the options
|
|
200
|
+
* @param {object|undefined} detectedParent Parent the generator derived
|
|
201
|
+
* @param {object[]} components The component list, appended to in place
|
|
202
|
+
* @param {object[]} dependencies Edges the generator produced
|
|
203
|
+
* @returns {object[]} Edges to emit, re-anchored when an override applies
|
|
204
|
+
*/
|
|
205
|
+
function anchorDetectedParent(
|
|
206
|
+
overrideParent,
|
|
207
|
+
detectedParent,
|
|
208
|
+
components,
|
|
209
|
+
dependencies,
|
|
210
|
+
) {
|
|
211
|
+
const detectedRef = detectedParent?.["bom-ref"];
|
|
212
|
+
const overrideRef = overrideParent?.["bom-ref"];
|
|
213
|
+
if (!detectedRef || !overrideRef || detectedRef === overrideRef) {
|
|
214
|
+
return dependencies;
|
|
215
|
+
}
|
|
216
|
+
if (!dependencies.some((dep) => dep.ref === detectedRef)) {
|
|
217
|
+
return dependencies;
|
|
218
|
+
}
|
|
219
|
+
if (
|
|
220
|
+
Array.isArray(components) &&
|
|
221
|
+
!components.some((comp) => comp["bom-ref"] === detectedRef)
|
|
222
|
+
) {
|
|
223
|
+
// The detected parent carries transient keys (`license`, `evidence`,
|
|
224
|
+
// `_integrity`, `qualifiers`, `repository`, `homepage`) that only
|
|
225
|
+
// `metadata.component` is cleaned of. As a regular component it must go
|
|
226
|
+
// through the same conversion or schema validation rejects the document
|
|
227
|
+
// (issue #4320). The copy keeps the caller's context object untouched.
|
|
228
|
+
components.push(cleanParentComponent({ ...detectedParent }));
|
|
229
|
+
}
|
|
230
|
+
// The caller owns the array it passed, and postProcess merges these edges
|
|
231
|
+
// across generators, so the re-anchoring is returned rather than spliced in.
|
|
232
|
+
const anchored = [];
|
|
233
|
+
let linked = false;
|
|
234
|
+
for (const dep of dependencies) {
|
|
235
|
+
if (dep.ref !== overrideRef) {
|
|
236
|
+
anchored.push(dep);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
linked = true;
|
|
240
|
+
anchored.push({
|
|
241
|
+
...dep,
|
|
242
|
+
dependsOn: Array.from(new Set([...(dep.dependsOn || []), detectedRef])),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (!linked) {
|
|
246
|
+
anchored.unshift({ ref: overrideRef, dependsOn: [detectedRef] });
|
|
247
|
+
}
|
|
248
|
+
return anchored;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Assembles the `metadata.tools` block, including the cdxgen tool entry and,
|
|
253
|
+
* for spec versions prior to 1.5, the legacy tools array. For spec version
|
|
254
|
+
* 1.6+ the author field is converted to the authors array.
|
|
255
|
+
*
|
|
256
|
+
* @param {object} options CLI options
|
|
257
|
+
* @param {object} [context={}] Additional context carrying existing components
|
|
258
|
+
* @returns {{ components?: object[], tools?: object[] }} Object containing the
|
|
259
|
+
* tools array (1.4) or a components array with tool components (1.5+)
|
|
260
|
+
*/
|
|
261
|
+
export const addToolsSection = (options, context = {}) => {
|
|
262
|
+
if (options.specVersion === 1.4) {
|
|
263
|
+
return [
|
|
264
|
+
{
|
|
265
|
+
vendor: "cyclonedx",
|
|
266
|
+
name: "cdxgen",
|
|
267
|
+
version: CDXGEN_VERSION,
|
|
268
|
+
},
|
|
269
|
+
];
|
|
270
|
+
}
|
|
271
|
+
let components = [];
|
|
272
|
+
const tools = options.tools || context.tools || [];
|
|
273
|
+
// tools can be an object or array
|
|
274
|
+
if (Array.isArray(tools) && tools.length) {
|
|
275
|
+
// cyclonedx-maven-plugin has the legacy tools metadata which needs to be patched
|
|
276
|
+
for (const tool of tools) {
|
|
277
|
+
if (!tool.type) {
|
|
278
|
+
tool.type = "application";
|
|
279
|
+
if (tool.vendor) {
|
|
280
|
+
tool.publisher = tool.vendor;
|
|
281
|
+
delete tool.vendor;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
components = components.concat(tools);
|
|
286
|
+
} else if (tools && Object.keys(tools).length && tools.components) {
|
|
287
|
+
components = components.concat(tools.components);
|
|
288
|
+
}
|
|
289
|
+
const cdxToolComponent = {
|
|
290
|
+
group: CDXGEN_TOOL_GROUP,
|
|
291
|
+
name: CDXGEN_TOOL_NAME,
|
|
292
|
+
version: CDXGEN_VERSION,
|
|
293
|
+
purl: npmPurl(`${CDXGEN_TOOL_GROUP}/${CDXGEN_TOOL_NAME}`, CDXGEN_VERSION),
|
|
294
|
+
type: "application",
|
|
295
|
+
"bom-ref": `pkg:npm/${CDXGEN_TOOL_GROUP}/${CDXGEN_TOOL_NAME}@${CDXGEN_VERSION}`,
|
|
296
|
+
author: "OWASP Foundation",
|
|
297
|
+
publisher: "OWASP Foundation",
|
|
298
|
+
};
|
|
299
|
+
if (options.specVersion >= 1.6) {
|
|
300
|
+
cdxToolComponent.authors = [{ name: "OWASP Foundation" }];
|
|
301
|
+
delete cdxToolComponent.author;
|
|
302
|
+
}
|
|
303
|
+
components.push(cdxToolComponent);
|
|
304
|
+
return { components };
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Renders a component's group/name@version as a simple display string.
|
|
309
|
+
*
|
|
310
|
+
* @param {object} comp Component object
|
|
311
|
+
* @returns {string} `group/name@version`, `name@version`, or `name`
|
|
312
|
+
*/
|
|
313
|
+
export const componentToSimpleFullName = (comp) => {
|
|
314
|
+
let fullName = comp.group?.length ? `${comp.group}/${comp.name}` : comp.name;
|
|
315
|
+
if (comp.version?.length) {
|
|
316
|
+
fullName = `${fullName}@${comp.version}`;
|
|
317
|
+
}
|
|
318
|
+
return fullName;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Strips transient keys (`evidence`, `_integrity`, `license`, `qualifiers`,
|
|
323
|
+
* `repository`, `homepage`) from a parent component while preserving licenses
|
|
324
|
+
* and external references (bug #1519).
|
|
325
|
+
*
|
|
326
|
+
* @param {object} comp Parent component to clean in place
|
|
327
|
+
* @returns {object} The cleaned parent component
|
|
328
|
+
*/
|
|
329
|
+
// Remove unwanted properties from parent component
|
|
330
|
+
// Bug #1519 - Retain licenses and external references
|
|
331
|
+
export const cleanParentComponent = (comp) => {
|
|
332
|
+
delete comp.evidence;
|
|
333
|
+
delete comp._integrity;
|
|
334
|
+
if (comp.license) {
|
|
335
|
+
const licenses = getLicenses(comp);
|
|
336
|
+
if (licenses?.length) {
|
|
337
|
+
comp.licenses = licenses;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
delete comp.license;
|
|
341
|
+
delete comp.qualifiers;
|
|
342
|
+
if (comp.repository || comp.homepage) {
|
|
343
|
+
const externalReferences = addExternalReferences(comp);
|
|
344
|
+
if (externalReferences?.length) {
|
|
345
|
+
comp.externalReferences = externalReferences;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
delete comp.repository;
|
|
349
|
+
delete comp.homepage;
|
|
350
|
+
return comp;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Builds the `metadata.authors` array from `options.author`, which may be a
|
|
355
|
+
* single string or an array of author strings. Entries shorter than two
|
|
356
|
+
* characters are ignored.
|
|
357
|
+
*
|
|
358
|
+
* @param {object} options CLI options
|
|
359
|
+
* @returns {object[]} Author objects of the form `{ name }`
|
|
360
|
+
*/
|
|
361
|
+
export const addAuthorsSection = (options) => {
|
|
362
|
+
const authors = [];
|
|
363
|
+
if (options.author) {
|
|
364
|
+
const oauthors = Array.isArray(options.author)
|
|
365
|
+
? options.author
|
|
366
|
+
: [options.author];
|
|
367
|
+
for (const aauthor of oauthors) {
|
|
368
|
+
if (aauthor.trim().length < 2) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
authors.push({ name: aauthor });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return authors;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Method to generate metadata.lifecycles section. We assume that we operate during "build"
|
|
379
|
+
* most of the time and under "post-build" for containers.
|
|
380
|
+
*
|
|
381
|
+
* @param {Object} options
|
|
382
|
+
* @returns {Array} Lifecycles array
|
|
383
|
+
*/
|
|
384
|
+
export const addLifecyclesSection = (options) => {
|
|
385
|
+
// If lifecycle was set via CLI arguments, reuse the value
|
|
386
|
+
if (options.lifecycle) {
|
|
387
|
+
return [{ phase: options.lifecycle }];
|
|
388
|
+
}
|
|
389
|
+
const lifecycles = [{ phase: options.installDeps ? "build" : "pre-build" }];
|
|
390
|
+
if (options.exportData) {
|
|
391
|
+
const inspectData = options.exportData.inspectData;
|
|
392
|
+
if (inspectData) {
|
|
393
|
+
lifecycles.push({ phase: "post-build" });
|
|
394
|
+
}
|
|
395
|
+
} else if (
|
|
396
|
+
options?.projectType?.length &&
|
|
397
|
+
options?.projectType?.includes("binary")
|
|
398
|
+
) {
|
|
399
|
+
lifecycles.push({ phase: "post-build" });
|
|
400
|
+
}
|
|
401
|
+
if (options.projectType?.includes("os")) {
|
|
402
|
+
lifecycles.push({ phase: "operations" });
|
|
403
|
+
}
|
|
404
|
+
return lifecycles;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Function to create metadata block
|
|
409
|
+
*
|
|
410
|
+
*/
|
|
411
|
+
export function addMetadata(parentComponent = {}, options = {}, context = {}) {
|
|
412
|
+
// DO NOT fork this project to just change the vendor or author's name
|
|
413
|
+
// Try to contribute to this project by sending PR or filing issues
|
|
414
|
+
const tools = addToolsSection(options, context);
|
|
415
|
+
const authors = addAuthorsSection(options);
|
|
416
|
+
const lifecycles =
|
|
417
|
+
options.specVersion >= 1.5 ? addLifecyclesSection(options) : undefined;
|
|
418
|
+
const metadata = {
|
|
419
|
+
timestamp: getTimestamp(),
|
|
420
|
+
tools,
|
|
421
|
+
authors,
|
|
422
|
+
supplier: undefined,
|
|
423
|
+
};
|
|
424
|
+
if (lifecycles) {
|
|
425
|
+
metadata.lifecycles = lifecycles;
|
|
426
|
+
}
|
|
427
|
+
// TLP classification
|
|
428
|
+
if (options.specVersion >= 1.7 && options?.tlpClassification) {
|
|
429
|
+
metadata.distributionConstraints = { tlp: options.tlpClassification };
|
|
430
|
+
}
|
|
431
|
+
if (parentComponent && Object.keys(parentComponent).length) {
|
|
432
|
+
if (parentComponent) {
|
|
433
|
+
cleanParentComponent(parentComponent);
|
|
434
|
+
// Recover a purl from the bom-ref only when the bom-ref genuinely is
|
|
435
|
+
// one. Parsers that cannot build a valid purl (a swift package with no
|
|
436
|
+
// remote URL, a maven module with no groupId) deliberately fall back to a
|
|
437
|
+
// name-based bom-ref, and blindly copying that here manufactured invalid
|
|
438
|
+
// purls such as `"purl": "swift-smoke"`. CycloneDX requires `purl` to be
|
|
439
|
+
// a valid Package URL when present, so absent is the only correct answer.
|
|
440
|
+
if (!parentComponent["purl"] && parentComponent["bom-ref"]) {
|
|
441
|
+
const candidate = encodeForPurl(parentComponent["bom-ref"]);
|
|
442
|
+
if (isValidPurl(candidate)) {
|
|
443
|
+
parentComponent["purl"] = candidate;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (parentComponent?.components) {
|
|
448
|
+
parentComponent.components = listComponents(
|
|
449
|
+
options,
|
|
450
|
+
{},
|
|
451
|
+
parentComponent.components,
|
|
452
|
+
);
|
|
453
|
+
const parentFullName = componentToSimpleFullName(parentComponent);
|
|
454
|
+
const subComponents = [];
|
|
455
|
+
const addedSubComponents = {};
|
|
456
|
+
for (const comp of parentComponent.components) {
|
|
457
|
+
cleanParentComponent(comp);
|
|
458
|
+
if (comp.name && comp.type) {
|
|
459
|
+
const fullName = componentToSimpleFullName(comp);
|
|
460
|
+
// Fixes #479
|
|
461
|
+
// Prevent the parent component from also appearing as a sub-component
|
|
462
|
+
// We cannot use purl or bom-ref here since they would not match
|
|
463
|
+
// purl - could have application on one side and a different type
|
|
464
|
+
// bom-ref could have qualifiers on one side
|
|
465
|
+
if (fullName !== parentFullName) {
|
|
466
|
+
if (!comp["bom-ref"]) {
|
|
467
|
+
comp["bom-ref"] = `pkg:${comp.type}/${decodeURIComponent(
|
|
468
|
+
fullName,
|
|
469
|
+
)}`;
|
|
470
|
+
}
|
|
471
|
+
if (!addedSubComponents[comp["bom-ref"]]) {
|
|
472
|
+
subComponents.push(comp);
|
|
473
|
+
addedSubComponents[comp["bom-ref"]] = true;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} // for
|
|
478
|
+
// Avoid creating empty component.components attribute
|
|
479
|
+
if (subComponents.length) {
|
|
480
|
+
parentComponent.components = subComponents;
|
|
481
|
+
} else {
|
|
482
|
+
parentComponent.components = undefined;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
// Convert authors to author for specVersion < 1.6
|
|
486
|
+
const parentComponentCopy = { ...parentComponent };
|
|
487
|
+
|
|
488
|
+
if (options.specVersion < 1.6 && parentComponent?.authors) {
|
|
489
|
+
parentComponentCopy.author = parentComponentCopy.authors
|
|
490
|
+
.map((a) => (a.email ? `${a.name} <${a.email}>` : a.name))
|
|
491
|
+
.join(",");
|
|
492
|
+
delete parentComponentCopy.authors;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
metadata.component = parentComponent;
|
|
496
|
+
}
|
|
497
|
+
// Have we already captured the oci properties
|
|
498
|
+
if (metadata?.properties?.some((prop) => prop.name === "oci:image:Id")) {
|
|
499
|
+
return metadata;
|
|
500
|
+
}
|
|
501
|
+
if (options) {
|
|
502
|
+
const mproperties = [];
|
|
503
|
+
if (options.exportData) {
|
|
504
|
+
const inspectData = options.exportData.inspectData;
|
|
505
|
+
if (inspectData) {
|
|
506
|
+
if (inspectData.Id) {
|
|
507
|
+
mproperties.push({
|
|
508
|
+
name: "oci:image:Id",
|
|
509
|
+
value: inspectData.Id,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (
|
|
513
|
+
inspectData.RepoTags &&
|
|
514
|
+
Array.isArray(inspectData.RepoTags) &&
|
|
515
|
+
inspectData.RepoTags.length
|
|
516
|
+
) {
|
|
517
|
+
mproperties.push({
|
|
518
|
+
name: "oci:image:RepoTag",
|
|
519
|
+
value: inspectData.RepoTags[0],
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (
|
|
523
|
+
inspectData.RepoDigests &&
|
|
524
|
+
Array.isArray(inspectData.RepoDigests) &&
|
|
525
|
+
inspectData.RepoDigests.length
|
|
526
|
+
) {
|
|
527
|
+
mproperties.push({
|
|
528
|
+
name: "oci:image:RepoDigest",
|
|
529
|
+
value: inspectData.RepoDigests[0],
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
if (inspectData.Created) {
|
|
533
|
+
mproperties.push({
|
|
534
|
+
name: "oci:image:Created",
|
|
535
|
+
value: inspectData.Created,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
if (inspectData.Architecture) {
|
|
539
|
+
mproperties.push({
|
|
540
|
+
name: "oci:image:Architecture",
|
|
541
|
+
value: inspectData.Architecture,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (inspectData.Os) {
|
|
545
|
+
mproperties.push({
|
|
546
|
+
name: "oci:image:Os",
|
|
547
|
+
value: inspectData.Os,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const manifestList = options.exportData.manifest;
|
|
552
|
+
if (manifestList && Array.isArray(manifestList) && manifestList.length) {
|
|
553
|
+
const manifest = manifestList[0] || {};
|
|
554
|
+
if (manifest.Config) {
|
|
555
|
+
mproperties.push({
|
|
556
|
+
name: "oci:image:manifest:Config",
|
|
557
|
+
value: manifest.Config,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
if (
|
|
561
|
+
manifest.Layers &&
|
|
562
|
+
Array.isArray(manifest.Layers) &&
|
|
563
|
+
manifest.Layers.length
|
|
564
|
+
) {
|
|
565
|
+
mproperties.push({
|
|
566
|
+
name: "oci:image:manifest:Layers",
|
|
567
|
+
value: manifest.Layers.join("\\n"),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const lastLayerConfig = options.exportData.lastLayerConfig;
|
|
572
|
+
if (lastLayerConfig) {
|
|
573
|
+
if (lastLayerConfig.id) {
|
|
574
|
+
mproperties.push({
|
|
575
|
+
name: "oci:image:lastLayer:Id",
|
|
576
|
+
value: lastLayerConfig.id,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
if (lastLayerConfig.parent) {
|
|
580
|
+
mproperties.push({
|
|
581
|
+
name: "oci:image:lastLayer:ParentId",
|
|
582
|
+
value: lastLayerConfig.parent,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
if (lastLayerConfig.created) {
|
|
586
|
+
mproperties.push({
|
|
587
|
+
name: "oci:image:lastLayer:Created",
|
|
588
|
+
value: lastLayerConfig.created,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const layerConfig =
|
|
593
|
+
lastLayerConfig?.config || options.exportData?.inspectData;
|
|
594
|
+
if (layerConfig) {
|
|
595
|
+
const env = layerConfig?.config?.Env || layerConfig?.Config?.Env;
|
|
596
|
+
if (env && Array.isArray(env) && env.length) {
|
|
597
|
+
mproperties.push({
|
|
598
|
+
name: "oci:image:lastLayer:Env",
|
|
599
|
+
value: env.join("\\n"),
|
|
600
|
+
});
|
|
601
|
+
// Does the image have any special packages that cdxgen cannot detect such as android-sdk and sdkman
|
|
602
|
+
const evalue = env.join(":");
|
|
603
|
+
if (
|
|
604
|
+
evalue.includes("android-sdk") ||
|
|
605
|
+
evalue.includes("commandlinetools")
|
|
606
|
+
) {
|
|
607
|
+
mproperties.push({
|
|
608
|
+
name: "oci:image:bundles:AndroidSdk",
|
|
609
|
+
value: "true",
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
// Track the use of special environment variables that could influence the search paths for libraries
|
|
613
|
+
// This list was generated by repeatedly prompting ChatGPT with examples.
|
|
614
|
+
// FIXME: Move these to a config file
|
|
615
|
+
for (const senvValue of [
|
|
616
|
+
"LD_LIBRARY_PATH",
|
|
617
|
+
"DYLD_LIBRARY_PATH",
|
|
618
|
+
"LD_PRELOAD",
|
|
619
|
+
"PYTHONPATH",
|
|
620
|
+
"CLASSPATH",
|
|
621
|
+
"PERL5LIB",
|
|
622
|
+
"PERLLIB",
|
|
623
|
+
"RUBYLIB",
|
|
624
|
+
"NODE_PATH",
|
|
625
|
+
"LUA_PATH",
|
|
626
|
+
"JULIA_LOAD_PATH",
|
|
627
|
+
"R_LIBS",
|
|
628
|
+
"R_LIBS_USER",
|
|
629
|
+
"GEM_PATH",
|
|
630
|
+
"DOTNET_ROOT",
|
|
631
|
+
"DOTNET_ADDITIONAL_DEPS",
|
|
632
|
+
"DOTNET_SHARED_STORE",
|
|
633
|
+
"DOTNET_STARTUP_HOOKS",
|
|
634
|
+
"DOTNET_BUNDLE_EXTRACT_BASE_DIR",
|
|
635
|
+
"JAVA_OPTIONS",
|
|
636
|
+
"JAVA_TOOL_OPTIONS",
|
|
637
|
+
"NODE_OPTIONS",
|
|
638
|
+
"PYTHONSTARTUP",
|
|
639
|
+
"RUBYOPT",
|
|
640
|
+
"WGETRC",
|
|
641
|
+
"APT_CONFIG",
|
|
642
|
+
"NPM_CONFIG_PREFIX",
|
|
643
|
+
"NPM_CONFIG_REGISTRY",
|
|
644
|
+
"YARN_CACHE_FOLDER",
|
|
645
|
+
"PNPM_STORE_PATH",
|
|
646
|
+
"PNPM_HOME",
|
|
647
|
+
"PNPM_CONFIG_",
|
|
648
|
+
"GIO_MODULE_DIR",
|
|
649
|
+
"GST_PLUGIN_PATH",
|
|
650
|
+
"GST_PLUGIN_SYSTEM_PATH",
|
|
651
|
+
"APPDIR_LIBRARY_PATH", // appimage specific which gets prepended to LD_LIBRARY_PATH
|
|
652
|
+
]) {
|
|
653
|
+
if (evalue.includes(senvValue)) {
|
|
654
|
+
mproperties.push({
|
|
655
|
+
name: `oci:image:env:${senvValue}`,
|
|
656
|
+
value: "true",
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
// This value represents a filtered and expanded path
|
|
661
|
+
if (options?.binPaths?.length) {
|
|
662
|
+
mproperties.push({
|
|
663
|
+
name: "oci:image:env:PATH",
|
|
664
|
+
value: options.binPaths.join(":"),
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
if (evalue.includes(".sdkman")) {
|
|
668
|
+
mproperties.push({
|
|
669
|
+
name: "oci:image:bundles:Sdkman",
|
|
670
|
+
value: "true",
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
if (evalue.includes(".nvm")) {
|
|
674
|
+
mproperties.push({
|
|
675
|
+
name: "oci:image:bundles:Nvm",
|
|
676
|
+
value: "true",
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
if (evalue.includes(".rbenv")) {
|
|
680
|
+
mproperties.push({
|
|
681
|
+
name: "oci:image:bundles:Rbenv",
|
|
682
|
+
value: "true",
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const ccmd =
|
|
687
|
+
layerConfig?.config?.Cmd ||
|
|
688
|
+
layerConfig?.Config?.Cmd ||
|
|
689
|
+
layerConfig?.Config?.EntryPoint;
|
|
690
|
+
if (ccmd) {
|
|
691
|
+
if (Array.isArray(ccmd) && ccmd.length) {
|
|
692
|
+
const fullCommand = ccmd.join(" ");
|
|
693
|
+
mproperties.push({
|
|
694
|
+
name: "oci:image:lastLayer:Cmd",
|
|
695
|
+
value: fullCommand,
|
|
696
|
+
});
|
|
697
|
+
let appLanguage;
|
|
698
|
+
// TODO: Move these lists to a config file.
|
|
699
|
+
for (const lang in [
|
|
700
|
+
"java",
|
|
701
|
+
"node",
|
|
702
|
+
"dotnet",
|
|
703
|
+
"python",
|
|
704
|
+
"python3",
|
|
705
|
+
"ruby",
|
|
706
|
+
"php",
|
|
707
|
+
"php7",
|
|
708
|
+
"php8",
|
|
709
|
+
"perl",
|
|
710
|
+
]) {
|
|
711
|
+
if (fullCommand.includes(`${lang} `)) {
|
|
712
|
+
appLanguage = lang;
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (appLanguage) {
|
|
717
|
+
mproperties.push({
|
|
718
|
+
name: "oci:image:appLanguage",
|
|
719
|
+
value: appLanguage,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
} else {
|
|
723
|
+
mproperties.push({
|
|
724
|
+
name: "oci:image:lastLayer:Cmd",
|
|
725
|
+
value: ccmd.toString(),
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (options.allOSComponentTypes?.length) {
|
|
732
|
+
mproperties.push({
|
|
733
|
+
name: "oci:image:componentTypes",
|
|
734
|
+
value: options.allOSComponentTypes.sort().join("\\n"),
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
if (Number.isInteger(options?.unpackagedExecutableCount)) {
|
|
738
|
+
mproperties.push({
|
|
739
|
+
name: "cdx:container:unpackagedExecutableCount",
|
|
740
|
+
value: String(options.unpackagedExecutableCount),
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
if (Number.isInteger(options?.unpackagedSharedLibraryCount)) {
|
|
744
|
+
mproperties.push({
|
|
745
|
+
name: "cdx:container:unpackagedSharedLibraryCount",
|
|
746
|
+
value: String(options.unpackagedSharedLibraryCount),
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
// Should we move these to formulation?
|
|
750
|
+
if (options?.bundledSdks?.length) {
|
|
751
|
+
for (const sdk of options.bundledSdks) {
|
|
752
|
+
try {
|
|
753
|
+
const purlObj = Purl.parse(sdk);
|
|
754
|
+
const sdkName = purlObj.name.split("-")[0].toLowerCase();
|
|
755
|
+
mproperties.push({
|
|
756
|
+
name: `oci:image:bundles:${sdkName}Sdk`,
|
|
757
|
+
value: "true",
|
|
758
|
+
});
|
|
759
|
+
} catch (_e) {
|
|
760
|
+
// ignore
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (options?.bundledRuntimes?.length) {
|
|
765
|
+
for (const runt of options.bundledRuntimes) {
|
|
766
|
+
mproperties.push({
|
|
767
|
+
name: `oci:image:bundles:${runt}Runtime`,
|
|
768
|
+
value: "true",
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
// AI provenance/oversight detection (`-t ai-provenance`) is injected into the
|
|
773
|
+
// BOM document root (bomJson.properties) after post-processing, not into
|
|
774
|
+
// metadata.properties. See ensureAiProvenanceProperties/ensureAiOversightProperties.
|
|
775
|
+
if (mproperties.length) {
|
|
776
|
+
metadata.properties = mproperties;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return metadata;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Method to create external references
|
|
784
|
+
*
|
|
785
|
+
* @param {Array | Object} opkg
|
|
786
|
+
* @returns {Array}
|
|
787
|
+
*/
|
|
788
|
+
export function addExternalReferences(opkg) {
|
|
789
|
+
let externalReferences = [];
|
|
790
|
+
let pkgList;
|
|
791
|
+
if (Array.isArray(opkg)) {
|
|
792
|
+
pkgList = opkg;
|
|
793
|
+
} else {
|
|
794
|
+
pkgList = [opkg];
|
|
795
|
+
}
|
|
796
|
+
for (const pkg of pkgList) {
|
|
797
|
+
if (pkg.externalReferences) {
|
|
798
|
+
externalReferences = externalReferences.concat(pkg.externalReferences);
|
|
799
|
+
} else {
|
|
800
|
+
if (pkg.homepage?.url) {
|
|
801
|
+
externalReferences.push({
|
|
802
|
+
type: pkg.homepage.url.includes("git") ? "vcs" : "website",
|
|
803
|
+
url: pkg.homepage.url,
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
if (pkg.bugs?.url) {
|
|
807
|
+
externalReferences.push({
|
|
808
|
+
type: "issue-tracker",
|
|
809
|
+
url: pkg.bugs.url,
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
if (pkg.repository?.url) {
|
|
813
|
+
externalReferences.push({
|
|
814
|
+
type: "vcs",
|
|
815
|
+
url: pkg.repository.url,
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
if (pkg.distribution?.url) {
|
|
819
|
+
externalReferences.push({
|
|
820
|
+
type: "distribution",
|
|
821
|
+
url: pkg.distribution.url,
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return externalReferences
|
|
827
|
+
.map((reference) => ({ ...reference, url: reference.url.trim() }))
|
|
828
|
+
.filter((reference) => isValidIriReference(reference.url));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* For all modules in the specified package, creates a list of
|
|
833
|
+
* component objects from each one.
|
|
834
|
+
*
|
|
835
|
+
* @param {Object} options CLI options
|
|
836
|
+
* @param {Object} allImports All imports
|
|
837
|
+
* @param {Object} pkg Package object
|
|
838
|
+
* @param {string} ptype Package type
|
|
839
|
+
* @returns {Object[]} Array of component objects
|
|
840
|
+
*/
|
|
841
|
+
export function listComponents(options, allImports, pkg, ptype = "npm") {
|
|
842
|
+
const compMap = {};
|
|
843
|
+
const isRootPkg = ptype === "npm";
|
|
844
|
+
if (Array.isArray(pkg)) {
|
|
845
|
+
pkg.forEach((p) => {
|
|
846
|
+
addComponent(options, allImports, p, ptype, compMap, false);
|
|
847
|
+
});
|
|
848
|
+
} else {
|
|
849
|
+
addComponent(options, allImports, pkg, ptype, compMap, isRootPkg);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return Object.keys(compMap).map((k) => compMap[k]);
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Component types that never receive an ecosystem purl.
|
|
857
|
+
*
|
|
858
|
+
* @type {string[]}
|
|
859
|
+
*/
|
|
860
|
+
// These component types do not have PURLs
|
|
861
|
+
export const NON_PURL_TYPES = ["cryptographic-asset", "file", "data"];
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Property-name prefix used to key npm bin-command import evidence
|
|
865
|
+
* (`cdx:npm:bin/`).
|
|
866
|
+
*
|
|
867
|
+
* @type {string}
|
|
868
|
+
*/
|
|
869
|
+
export const NPM_BIN_IMPORT_PREFIX = "cdx:npm:bin/";
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Returns all non-empty property values for a given property name on a package.
|
|
873
|
+
*
|
|
874
|
+
* @param {object} pkg Package/component object with an optional `properties` array
|
|
875
|
+
* @param {string} propertyName Property name to match
|
|
876
|
+
* @returns {string[]} Matching property values as strings
|
|
877
|
+
*/
|
|
878
|
+
export function getPackagePropertyValues(pkg, propertyName) {
|
|
879
|
+
if (!Array.isArray(pkg?.properties)) {
|
|
880
|
+
return [];
|
|
881
|
+
}
|
|
882
|
+
return pkg.properties
|
|
883
|
+
.filter((property) => property.name === propertyName && property.value)
|
|
884
|
+
.map((property) => String(property.value));
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Splits a comma-separated property value into a trimmed, non-empty string array.
|
|
889
|
+
*
|
|
890
|
+
* @param {string} value Comma-separated value
|
|
891
|
+
* @returns {string[]} Trimmed list entries
|
|
892
|
+
*/
|
|
893
|
+
export function splitPackagePropertyList(value) {
|
|
894
|
+
return String(value || "")
|
|
895
|
+
.split(",")
|
|
896
|
+
.map((item) => item.trim())
|
|
897
|
+
.filter(Boolean);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Collects the set of bin command names an npm package exposes, derived from
|
|
902
|
+
* the `bin` field and the `cdx:npm:bin` / `cdx:npm:binPaths` properties.
|
|
903
|
+
*
|
|
904
|
+
* @param {object} pkg Package object
|
|
905
|
+
* @returns {Set<string>} Bin command names
|
|
906
|
+
*/
|
|
907
|
+
export function getPackageBinCommandNames(pkg) {
|
|
908
|
+
const binNames = new Set();
|
|
909
|
+
if (pkg?.bin && typeof pkg.bin === "object") {
|
|
910
|
+
for (const binName of Object.keys(pkg.bin)) {
|
|
911
|
+
binNames.add(binName);
|
|
912
|
+
}
|
|
913
|
+
} else if (typeof pkg?.bin === "string") {
|
|
914
|
+
binNames.add(pkg.name);
|
|
915
|
+
}
|
|
916
|
+
for (const binValue of getPackagePropertyValues(pkg, "cdx:npm:bin")) {
|
|
917
|
+
for (const binName of splitPackagePropertyList(binValue)) {
|
|
918
|
+
binNames.add(binName);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
for (const binPathValue of getPackagePropertyValues(
|
|
922
|
+
pkg,
|
|
923
|
+
"cdx:npm:binPaths",
|
|
924
|
+
)) {
|
|
925
|
+
for (const binPath of splitPackagePropertyList(binPathValue)) {
|
|
926
|
+
const normalizedBinPath = binPath.replaceAll("\\", "/");
|
|
927
|
+
const binName = normalizedBinPath.split("/").filter(Boolean).pop();
|
|
928
|
+
if (binName) {
|
|
929
|
+
binNames.add(binName);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return binNames;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Checks whether any bin command of a package appears in the analyzer import
|
|
938
|
+
* evidence (keyed under `cdx:npm:bin/`).
|
|
939
|
+
*
|
|
940
|
+
* @param {object} allImports Map of import names to usage counts
|
|
941
|
+
* @param {object} pkg Package object
|
|
942
|
+
* @returns {boolean} True when at least one bin command is imported
|
|
943
|
+
*/
|
|
944
|
+
export function hasNpmBinCommandEvidence(allImports, pkg) {
|
|
945
|
+
const binCommandNames = getPackageBinCommandNames(pkg);
|
|
946
|
+
if (!binCommandNames.size) {
|
|
947
|
+
return false;
|
|
948
|
+
}
|
|
949
|
+
for (const importName of Object.keys(allImports || {})) {
|
|
950
|
+
if (!importName.startsWith(NPM_BIN_IMPORT_PREFIX)) {
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
const commandName = importName.slice(NPM_BIN_IMPORT_PREFIX.length);
|
|
954
|
+
if (binCommandNames.has(commandName)) {
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return false;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Given the specified package, create a CycloneDX component and add it to the list.
|
|
963
|
+
*/
|
|
964
|
+
export function addComponent(
|
|
965
|
+
options,
|
|
966
|
+
allImports,
|
|
967
|
+
pkg,
|
|
968
|
+
ptype,
|
|
969
|
+
compMap,
|
|
970
|
+
isRootPkg = false,
|
|
971
|
+
) {
|
|
972
|
+
if (!pkg || pkg.extraneous) {
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (!isRootPkg) {
|
|
976
|
+
const pkgIdentifier = parsePackageJsonName(pkg.name);
|
|
977
|
+
const author = pkg.author || undefined;
|
|
978
|
+
const authors = pkg.authors || undefined;
|
|
979
|
+
const publisher = pkg.publisher || undefined;
|
|
980
|
+
let group = pkg.group || pkgIdentifier.scope;
|
|
981
|
+
// Create empty group
|
|
982
|
+
group = group || "";
|
|
983
|
+
const name = pkgIdentifier.fullName || pkg.name || "";
|
|
984
|
+
// name is mandatory
|
|
985
|
+
if (!name) {
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
// Do we need this still?
|
|
989
|
+
if (
|
|
990
|
+
!ptype &&
|
|
991
|
+
["jar", "war", "ear", "pom"].includes(pkg?.qualifiers?.type)
|
|
992
|
+
) {
|
|
993
|
+
ptype = "maven";
|
|
994
|
+
}
|
|
995
|
+
const version = pkg.version || "";
|
|
996
|
+
const licenses = pkg.licenses || getLicenses(pkg);
|
|
997
|
+
let purl = pkg.purl;
|
|
998
|
+
if (!purl && ptype) {
|
|
999
|
+
try {
|
|
1000
|
+
purl = new Purl({
|
|
1001
|
+
type: ptype,
|
|
1002
|
+
namespace: encodeForPurl(group) || null,
|
|
1003
|
+
name: encodeForPurl(name),
|
|
1004
|
+
version: version || null,
|
|
1005
|
+
qualifiers: pkg.qualifiers || null,
|
|
1006
|
+
subpath: encodeForPurl(pkg.subpath) || null,
|
|
1007
|
+
});
|
|
1008
|
+
} catch {
|
|
1009
|
+
// purl construction may fail for types requiring a namespace (maven,
|
|
1010
|
+
// swift) when the component has no group; fall through without a purl.
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
let purlString = purl?.toString();
|
|
1014
|
+
// These types get no purl derived from the ecosystem being scanned, since a
|
|
1015
|
+
// file or a crypto asset is not a package of that ecosystem. A collector
|
|
1016
|
+
// that set a purl itself — file components carry `pkg:generic/...` — is
|
|
1017
|
+
// taken at its word.
|
|
1018
|
+
if (
|
|
1019
|
+
!pkg.purl &&
|
|
1020
|
+
(NON_PURL_TYPES.includes(ptype) || NON_PURL_TYPES.includes(pkg.type))
|
|
1021
|
+
) {
|
|
1022
|
+
purl = undefined;
|
|
1023
|
+
purlString = undefined;
|
|
1024
|
+
}
|
|
1025
|
+
// Some applications like github workflow steps and commands do not have purl
|
|
1026
|
+
if (
|
|
1027
|
+
pkg.purl === undefined &&
|
|
1028
|
+
!pkg?.["bom-ref"]?.startsWith("pkg:") &&
|
|
1029
|
+
pkg?.type === "application"
|
|
1030
|
+
) {
|
|
1031
|
+
purl = undefined;
|
|
1032
|
+
purlString = undefined;
|
|
1033
|
+
}
|
|
1034
|
+
const description = pkg.description || undefined;
|
|
1035
|
+
let compScope = pkg.scope;
|
|
1036
|
+
let isTypeOnlyPkg = false;
|
|
1037
|
+
if (allImports) {
|
|
1038
|
+
const impPkgs = Object.keys(allImports);
|
|
1039
|
+
const hasBinCommandEvidence = hasNpmBinCommandEvidence(allImports, pkg);
|
|
1040
|
+
if (
|
|
1041
|
+
impPkgs.includes(name) ||
|
|
1042
|
+
impPkgs.includes(`${group}/${name}`) ||
|
|
1043
|
+
impPkgs.includes(`@${group}/${name}`) ||
|
|
1044
|
+
(group.length > 0 && impPkgs.includes(group)) ||
|
|
1045
|
+
(group.length > 0 && impPkgs.includes(`@${group}`)) ||
|
|
1046
|
+
hasBinCommandEvidence
|
|
1047
|
+
) {
|
|
1048
|
+
if (
|
|
1049
|
+
!hasBinCommandEvidence &&
|
|
1050
|
+
isPkgTypeOnlyImport(allImports, group, name)
|
|
1051
|
+
) {
|
|
1052
|
+
compScope = "excluded";
|
|
1053
|
+
isTypeOnlyPkg = true;
|
|
1054
|
+
} else {
|
|
1055
|
+
compScope = "required";
|
|
1056
|
+
}
|
|
1057
|
+
} else if (impPkgs.length && compScope !== "excluded") {
|
|
1058
|
+
compScope = "optional";
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
let component = {
|
|
1062
|
+
author,
|
|
1063
|
+
authors,
|
|
1064
|
+
publisher,
|
|
1065
|
+
group,
|
|
1066
|
+
name,
|
|
1067
|
+
version,
|
|
1068
|
+
description,
|
|
1069
|
+
scope: compScope,
|
|
1070
|
+
hashes: [],
|
|
1071
|
+
licenses,
|
|
1072
|
+
purl: purlString,
|
|
1073
|
+
externalReferences: addExternalReferences(pkg),
|
|
1074
|
+
};
|
|
1075
|
+
if (options.specVersion >= 1.5) {
|
|
1076
|
+
component.pedigree = pkg.pedigree || undefined;
|
|
1077
|
+
}
|
|
1078
|
+
if (options.specVersion >= 1.6) {
|
|
1079
|
+
component.releaseNotes = pkg.releaseNotes || undefined;
|
|
1080
|
+
component.modelCard = pkg.modelCard || undefined;
|
|
1081
|
+
component.data = pkg.data || undefined;
|
|
1082
|
+
}
|
|
1083
|
+
component["type"] = determinePackageType(pkg);
|
|
1084
|
+
if (pkg["bom-ref"]) {
|
|
1085
|
+
component["bom-ref"] = pkg["bom-ref"];
|
|
1086
|
+
} else if (purlString) {
|
|
1087
|
+
component["bom-ref"] = decodeURIComponent(purlString);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
if (
|
|
1091
|
+
component.externalReferences === undefined ||
|
|
1092
|
+
component.externalReferences.length === 0
|
|
1093
|
+
) {
|
|
1094
|
+
delete component.externalReferences;
|
|
1095
|
+
}
|
|
1096
|
+
if (options.specVersion < 1.6) {
|
|
1097
|
+
delete component.omniborId;
|
|
1098
|
+
delete component.swhid;
|
|
1099
|
+
}
|
|
1100
|
+
processHashes(pkg, component);
|
|
1101
|
+
// Upgrade authors section
|
|
1102
|
+
if (options.specVersion >= 1.6 && component.author) {
|
|
1103
|
+
const authorsList = [];
|
|
1104
|
+
for (const aauthor of component.author.split(",")) {
|
|
1105
|
+
authorsList.push({ name: aauthor });
|
|
1106
|
+
}
|
|
1107
|
+
component.authors = authorsList;
|
|
1108
|
+
delete component.author;
|
|
1109
|
+
}
|
|
1110
|
+
// Downgrade authors section for < 1.5 :(
|
|
1111
|
+
if (options.specVersion < 1.6) {
|
|
1112
|
+
if (component?.authors?.length) {
|
|
1113
|
+
component.author = component.authors
|
|
1114
|
+
.map((a) => (a.email ? `${a.name} <${a.email}>` : a.name))
|
|
1115
|
+
.join(",");
|
|
1116
|
+
}
|
|
1117
|
+
delete component.authors;
|
|
1118
|
+
}
|
|
1119
|
+
// Downgrade from 1.7
|
|
1120
|
+
if (options.specVersion < 1.7) {
|
|
1121
|
+
if (component.isExternal) {
|
|
1122
|
+
delete component.isExternal;
|
|
1123
|
+
}
|
|
1124
|
+
if (component.versionRange) {
|
|
1125
|
+
console.warn(
|
|
1126
|
+
`Version Range is not supported in ${options.specVersion} specifications. Please run cdxgen with --spec-version 1.7`,
|
|
1127
|
+
);
|
|
1128
|
+
delete component.versionRange;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
// Retain any tags
|
|
1132
|
+
if (
|
|
1133
|
+
options.specVersion >= 1.6 &&
|
|
1134
|
+
pkg.tags &&
|
|
1135
|
+
Object.keys(pkg.tags).length
|
|
1136
|
+
) {
|
|
1137
|
+
component.tags = pkg.tags;
|
|
1138
|
+
}
|
|
1139
|
+
// Retain any component properties and crypto properties
|
|
1140
|
+
if (pkg.properties?.length) {
|
|
1141
|
+
component.properties = pkg.properties;
|
|
1142
|
+
}
|
|
1143
|
+
if (isTypeOnlyPkg) {
|
|
1144
|
+
component.properties = component.properties || [];
|
|
1145
|
+
if (
|
|
1146
|
+
!component.properties.some(
|
|
1147
|
+
(p) => p.name === "cdx:npm:package:type-only",
|
|
1148
|
+
)
|
|
1149
|
+
) {
|
|
1150
|
+
component.properties.push({
|
|
1151
|
+
name: "cdx:npm:package:type-only",
|
|
1152
|
+
value: "true",
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
if (pkg.cryptoProperties && typeof pkg.cryptoProperties === "object") {
|
|
1157
|
+
component.cryptoProperties = pkg.cryptoProperties;
|
|
1158
|
+
}
|
|
1159
|
+
// Retain nested components
|
|
1160
|
+
if (pkg.components) {
|
|
1161
|
+
component.components = pkg.components;
|
|
1162
|
+
}
|
|
1163
|
+
component = enrichComponentWithMcpMetadata(component);
|
|
1164
|
+
const compMapKey = component.purl || component["bom-ref"];
|
|
1165
|
+
// Issue: 1353. We need to keep merging the properties
|
|
1166
|
+
if (compMap[compMapKey]) {
|
|
1167
|
+
const mergedComponents = trimComponents([compMap[compMapKey], component]);
|
|
1168
|
+
if (mergedComponents?.length === 1) {
|
|
1169
|
+
component = mergedComponents[0];
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
// Retain evidence
|
|
1173
|
+
if (
|
|
1174
|
+
options.specVersion >= 1.5 &&
|
|
1175
|
+
pkg.evidence &&
|
|
1176
|
+
Object.keys(pkg.evidence).length
|
|
1177
|
+
) {
|
|
1178
|
+
component.evidence = pkg.evidence;
|
|
1179
|
+
// Convert evidence.identity section to an array for 1.6 and above
|
|
1180
|
+
if (
|
|
1181
|
+
options.specVersion >= 1.6 &&
|
|
1182
|
+
pkg.evidence &&
|
|
1183
|
+
pkg.evidence.identity &&
|
|
1184
|
+
!Array.isArray(pkg.evidence.identity)
|
|
1185
|
+
) {
|
|
1186
|
+
// Automatically add concludedValue
|
|
1187
|
+
if (pkg.evidence.identity?.methods?.length === 1) {
|
|
1188
|
+
pkg.evidence.identity.concludedValue =
|
|
1189
|
+
pkg.evidence.identity.methods[0].value;
|
|
1190
|
+
}
|
|
1191
|
+
component.evidence.identity = [pkg.evidence.identity];
|
|
1192
|
+
}
|
|
1193
|
+
// Convert evidence.identity section to an object for 1.5
|
|
1194
|
+
if (
|
|
1195
|
+
options.specVersion === 1.5 &&
|
|
1196
|
+
pkg.evidence &&
|
|
1197
|
+
pkg.evidence.identity &&
|
|
1198
|
+
Array.isArray(pkg.evidence.identity)
|
|
1199
|
+
) {
|
|
1200
|
+
component.evidence.identity = pkg.evidence.identity[0];
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
compMap[compMapKey] = component;
|
|
1204
|
+
}
|
|
1205
|
+
if (pkg.dependencies) {
|
|
1206
|
+
Object.keys(pkg.dependencies)
|
|
1207
|
+
.map((x) => pkg.dependencies[x])
|
|
1208
|
+
.filter((x) => typeof x !== "string") //remove cycles
|
|
1209
|
+
.map((x) => addComponent(options, allImports, x, ptype, compMap, false));
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* If the author has described the module as a 'framework', the take their
|
|
1215
|
+
* word for it, otherwise, identify the module as a 'library'.
|
|
1216
|
+
*/
|
|
1217
|
+
export function determinePackageType(pkg) {
|
|
1218
|
+
// Retain the exact component type in certain cases.
|
|
1219
|
+
if (
|
|
1220
|
+
[
|
|
1221
|
+
"container",
|
|
1222
|
+
"platform",
|
|
1223
|
+
"operating-system",
|
|
1224
|
+
"device",
|
|
1225
|
+
"device-driver",
|
|
1226
|
+
"firmware",
|
|
1227
|
+
"file",
|
|
1228
|
+
"machine-learning-model",
|
|
1229
|
+
"data",
|
|
1230
|
+
"cryptographic-asset",
|
|
1231
|
+
].includes(pkg.type)
|
|
1232
|
+
) {
|
|
1233
|
+
return pkg.type;
|
|
1234
|
+
}
|
|
1235
|
+
if (pkg.type === "application") {
|
|
1236
|
+
if (pkg?.name?.endsWith(".tar")) {
|
|
1237
|
+
return "container";
|
|
1238
|
+
}
|
|
1239
|
+
return pkg.type;
|
|
1240
|
+
}
|
|
1241
|
+
if (pkg.purl) {
|
|
1242
|
+
try {
|
|
1243
|
+
const purl = Purl.parse(pkg.purl);
|
|
1244
|
+
if (purl.type) {
|
|
1245
|
+
if (["docker", "oci", "container"].includes(purl.type)) {
|
|
1246
|
+
return "container";
|
|
1247
|
+
}
|
|
1248
|
+
if (["github"].includes(purl.type)) {
|
|
1249
|
+
return "application";
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
// See #1760
|
|
1253
|
+
if (
|
|
1254
|
+
purl.namespace?.startsWith("@types") ||
|
|
1255
|
+
(purl.namespace?.includes("-types") && pkg?.type === "npm")
|
|
1256
|
+
) {
|
|
1257
|
+
return "library";
|
|
1258
|
+
}
|
|
1259
|
+
for (const cf of frameworksList.all) {
|
|
1260
|
+
if (
|
|
1261
|
+
pkg.purl.startsWith(cf) ||
|
|
1262
|
+
purl.namespace?.includes(cf) ||
|
|
1263
|
+
purl.name.toLowerCase().startsWith(cf)
|
|
1264
|
+
) {
|
|
1265
|
+
return "framework";
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
} catch (_e) {
|
|
1269
|
+
// continue regardless of error
|
|
1270
|
+
}
|
|
1271
|
+
} else if (pkg.group) {
|
|
1272
|
+
if (["actions"].includes(pkg.group)) {
|
|
1273
|
+
return "application";
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (Object.hasOwn(pkg, "description")) {
|
|
1277
|
+
if (pkg.description?.toLowerCase().includes("framework")) {
|
|
1278
|
+
return "framework";
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
if (Object.hasOwn(pkg, "keywords")) {
|
|
1282
|
+
for (const keyword of pkg.keywords) {
|
|
1283
|
+
if (keyword && keyword.toLowerCase() === "framework") {
|
|
1284
|
+
return "framework";
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
if (Object.hasOwn(pkg, "tags")) {
|
|
1289
|
+
for (const tag of pkg.tags) {
|
|
1290
|
+
if (tag && tag.toLowerCase() === "framework") {
|
|
1291
|
+
return "framework";
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return "library";
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/**
|
|
1299
|
+
* Uses the SHA1 shasum (if present) otherwise utilizes Subresource Integrity
|
|
1300
|
+
* of the package with support for multiple hashing algorithms.
|
|
1301
|
+
*/
|
|
1302
|
+
export function processHashes(pkg, component) {
|
|
1303
|
+
if (pkg.hashes) {
|
|
1304
|
+
// This attribute would be available when we read a bom json directly
|
|
1305
|
+
// Eg: cyclonedx-maven-plugin. See: Bugs: #172, #175
|
|
1306
|
+
for (const ahash of pkg.hashes) {
|
|
1307
|
+
addComponentHash(ahash.alg, ahash.content, component);
|
|
1308
|
+
}
|
|
1309
|
+
} else if (pkg._shasum) {
|
|
1310
|
+
addComponentHash("SHA-1", pkg._shasum, component);
|
|
1311
|
+
} else if (pkg._integrity) {
|
|
1312
|
+
const integrity = parse(pkg._integrity) || {};
|
|
1313
|
+
// Components may have multiple hashes with various lengths. Check each one
|
|
1314
|
+
// that is supported by the CycloneDX specification.
|
|
1315
|
+
if (Object.hasOwn(integrity, "sha512")) {
|
|
1316
|
+
addComponentHash("SHA-512", integrity.sha512[0].digest, component);
|
|
1317
|
+
}
|
|
1318
|
+
if (Object.hasOwn(integrity, "sha384")) {
|
|
1319
|
+
addComponentHash("SHA-384", integrity.sha384[0].digest, component);
|
|
1320
|
+
}
|
|
1321
|
+
if (Object.hasOwn(integrity, "sha256")) {
|
|
1322
|
+
addComponentHash("SHA-256", integrity.sha256[0].digest, component);
|
|
1323
|
+
}
|
|
1324
|
+
if (Object.hasOwn(integrity, "sha1")) {
|
|
1325
|
+
addComponentHash("SHA-1", integrity.sha1[0].digest, component);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
if (component.hashes.length === 0) {
|
|
1329
|
+
delete component.hashes; // If no hashes exist, delete the hashes node (it's optional)
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// Digest length in hex characters for each algorithm CycloneDX names. A digest
|
|
1334
|
+
// that decodes to the wrong length for its algorithm is corrupt, whatever
|
|
1335
|
+
// encoding it arrived in.
|
|
1336
|
+
const HASH_HEX_LENGTHS = {
|
|
1337
|
+
MD5: 32,
|
|
1338
|
+
"SHA-1": 40,
|
|
1339
|
+
"SHA-256": 64,
|
|
1340
|
+
"SHA-384": 96,
|
|
1341
|
+
"SHA-512": 128,
|
|
1342
|
+
"SHA3-256": 64,
|
|
1343
|
+
"SHA3-384": 96,
|
|
1344
|
+
"SHA3-512": 128,
|
|
1345
|
+
"BLAKE2b-256": 64,
|
|
1346
|
+
"BLAKE2b-384": 96,
|
|
1347
|
+
"BLAKE2b-512": 128,
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
// Lengths the CycloneDX hash-content pattern accepts, for algorithms not in the
|
|
1351
|
+
// table above.
|
|
1352
|
+
const HASH_HEX_LENGTHS_ANY = new Set([32, 40, 64, 96, 128]);
|
|
1353
|
+
|
|
1354
|
+
/**
|
|
1355
|
+
* Adds a hash to component.
|
|
1356
|
+
*
|
|
1357
|
+
* Registries hand out digests in hex or in the base64 form npm uses for
|
|
1358
|
+
* `integrity`, and both are accepted. A digest that is neither, or that decodes
|
|
1359
|
+
* to the wrong length for its algorithm, is dropped rather than copied through:
|
|
1360
|
+
* CycloneDX constrains `hash.content` to a hex digest, so emitting it anyway
|
|
1361
|
+
* fails schema validation for the whole document over one bad package.
|
|
1362
|
+
*/
|
|
1363
|
+
export function addComponentHash(alg, digest, component) {
|
|
1364
|
+
const hash = normalizeHashContent(alg, digest);
|
|
1365
|
+
if (!hash) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
component.hashes.push({ alg: alg, content: hash });
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/**
|
|
1372
|
+
* Normalize a digest to the lower-case hex form CycloneDX requires.
|
|
1373
|
+
*
|
|
1374
|
+
* @param {string} alg Hash algorithm name
|
|
1375
|
+
* @param {string} digest Digest in hex or base64
|
|
1376
|
+
* @returns {string|undefined} Hex digest, or undefined when it cannot be trusted
|
|
1377
|
+
*/
|
|
1378
|
+
export function normalizeHashContent(alg, digest) {
|
|
1379
|
+
if (!digest || typeof digest !== "string") {
|
|
1380
|
+
return undefined;
|
|
1381
|
+
}
|
|
1382
|
+
const expectedLength = HASH_HEX_LENGTHS[alg];
|
|
1383
|
+
const isExpectedLength = (value) =>
|
|
1384
|
+
expectedLength
|
|
1385
|
+
? value.length === expectedLength
|
|
1386
|
+
: HASH_HEX_LENGTHS_ANY.has(value.length);
|
|
1387
|
+
|
|
1388
|
+
if (new RegExp(HASH_PATTERN).test(digest)) {
|
|
1389
|
+
return isExpectedLength(digest) ? digest : undefined;
|
|
1390
|
+
}
|
|
1391
|
+
// npm and friends carry the digest base64-encoded. Node decodes leniently, so
|
|
1392
|
+
// the decoded length is the check that matters: a truncated or invented value
|
|
1393
|
+
// decodes to something, just not to a digest of the right size.
|
|
1394
|
+
const decoded = Buffer.from(digest, "base64").toString("hex");
|
|
1395
|
+
return isExpectedLength(decoded) ? decoded : undefined;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
/**
|
|
1399
|
+
* Return the BOM in json format including any namespace mapping
|
|
1400
|
+
*
|
|
1401
|
+
* @param {Object} options Options
|
|
1402
|
+
* @param {Object} pkgInfo Package information
|
|
1403
|
+
* @param {string} ptype Package type
|
|
1404
|
+
* @param {Object} context Context
|
|
1405
|
+
*
|
|
1406
|
+
* @returns {Object} BOM with namespace mapping
|
|
1407
|
+
*/
|
|
1408
|
+
export const buildBomNSData = (options, pkgInfo, ptype, context) => {
|
|
1409
|
+
// Many create*Bom call sites provide only a source directory (`src`) when
|
|
1410
|
+
// there is no single manifest/lock file to report, so activity records must
|
|
1411
|
+
// fall back to that directory to keep the target populated.
|
|
1412
|
+
const sourcePath =
|
|
1413
|
+
context?.srcDir || context?.src || options.path || options.filePath;
|
|
1414
|
+
const activityProjectType =
|
|
1415
|
+
context?.projectType ||
|
|
1416
|
+
(Array.isArray(options.projectType)
|
|
1417
|
+
? options.projectType.length === 1
|
|
1418
|
+
? options.projectType[0]
|
|
1419
|
+
: undefined
|
|
1420
|
+
: options.projectType);
|
|
1421
|
+
setActivityContext({
|
|
1422
|
+
packageType: ptype,
|
|
1423
|
+
sourcePath,
|
|
1424
|
+
...(activityProjectType ? { projectType: activityProjectType } : {}),
|
|
1425
|
+
});
|
|
1426
|
+
const bomNSData = {
|
|
1427
|
+
bomJson: undefined,
|
|
1428
|
+
bomJsonFiles: undefined,
|
|
1429
|
+
nsMapping: undefined,
|
|
1430
|
+
dependencies: undefined,
|
|
1431
|
+
parentComponent: undefined,
|
|
1432
|
+
};
|
|
1433
|
+
const serialNum = `urn:uuid:${uuidv4()}`;
|
|
1434
|
+
let allImports = {};
|
|
1435
|
+
if (context?.allImports) {
|
|
1436
|
+
allImports = context.allImports;
|
|
1437
|
+
}
|
|
1438
|
+
const nsMapping = context.nsMapping || {};
|
|
1439
|
+
const services = context.services || [];
|
|
1440
|
+
const overrideParent = determineParentComponent(options);
|
|
1441
|
+
const parentComponent = overrideParent || context.parentComponent;
|
|
1442
|
+
const metadata = addMetadata(parentComponent, options, context);
|
|
1443
|
+
const components = listComponents(options, allImports, pkgInfo, ptype);
|
|
1444
|
+
const dependencies = anchorDetectedParent(
|
|
1445
|
+
overrideParent,
|
|
1446
|
+
context.parentComponent,
|
|
1447
|
+
components,
|
|
1448
|
+
context.dependencies || [],
|
|
1449
|
+
);
|
|
1450
|
+
if (
|
|
1451
|
+
components &&
|
|
1452
|
+
(components.length ||
|
|
1453
|
+
parentComponent ||
|
|
1454
|
+
options.projectType?.includes("dynamic") ||
|
|
1455
|
+
options.projectType?.includes("ai-provenance"))
|
|
1456
|
+
) {
|
|
1457
|
+
// CycloneDX Json Template
|
|
1458
|
+
const jsonTpl = {
|
|
1459
|
+
bomFormat: "CycloneDX",
|
|
1460
|
+
specVersion: toCycloneDxSpecVersionString(
|
|
1461
|
+
options.specVersion || DEFAULT_CDX_SPEC_VERSION,
|
|
1462
|
+
),
|
|
1463
|
+
serialNumber: serialNum,
|
|
1464
|
+
version: 1,
|
|
1465
|
+
metadata: metadata,
|
|
1466
|
+
components,
|
|
1467
|
+
dependencies,
|
|
1468
|
+
};
|
|
1469
|
+
setCycloneDxFormat(jsonTpl, jsonTpl.specVersion, {
|
|
1470
|
+
preserveLegacyBomFormat: true,
|
|
1471
|
+
});
|
|
1472
|
+
if (services.length) {
|
|
1473
|
+
jsonTpl.services = mergeServices([], services);
|
|
1474
|
+
}
|
|
1475
|
+
bomNSData.bomJson = jsonTpl;
|
|
1476
|
+
bomNSData.nsMapping = nsMapping;
|
|
1477
|
+
bomNSData.dependencies = dependencies;
|
|
1478
|
+
bomNSData.parentComponent = parentComponent;
|
|
1479
|
+
// Carry language-specific formulation data (e.g. Pixi) so that
|
|
1480
|
+
// postProcess can merge it when building the final formulation section.
|
|
1481
|
+
if (context?.formulationList?.length) {
|
|
1482
|
+
bomNSData.formulationList = context.formulationList;
|
|
1483
|
+
}
|
|
1484
|
+
// Carry language-specific provenance citations (e.g. PEP 770 embedded
|
|
1485
|
+
// SBOMs) so postProcess can attach them in the single root-level citations
|
|
1486
|
+
// section it builds.
|
|
1487
|
+
if (Array.isArray(context?.citations) && context.citations.length) {
|
|
1488
|
+
bomNSData.citations = context.citations;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
recordActivity({
|
|
1492
|
+
kind: "read",
|
|
1493
|
+
reason: `Collected ${ptype || "generic"} component metadata.`,
|
|
1494
|
+
status: components?.length || parentComponent ? "completed" : "failed",
|
|
1495
|
+
target: context?.filename || sourcePath,
|
|
1496
|
+
});
|
|
1497
|
+
resetActivityContext();
|
|
1498
|
+
return bomNSData;
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* Function to create bom string for Android apps using blint
|
|
1503
|
+
*
|
|
1504
|
+
* @param {string} path to the project
|
|
1505
|
+
* @param {Object} options Parse options from the cli
|
|
1506
|
+
* @returns {Object|undefined} BOM object
|
|
1507
|
+
*/
|
|
1508
|
+
export function createAndroidBom(path, options) {
|
|
1509
|
+
return createBinaryBom(path, options);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Escape the purls of a blint BOM and carry the change through its graph.
|
|
1514
|
+
*
|
|
1515
|
+
* blint composes purls by string interpolation, so a name holding a character
|
|
1516
|
+
* a purl reserves — `libstdc++.so.6` and its kin — arrives unescaped. blint
|
|
1517
|
+
* also uses the purl as the bom-ref, so every ref that quoted the old string,
|
|
1518
|
+
* in `dependencies` and in the parent component alike, moves with it.
|
|
1519
|
+
*
|
|
1520
|
+
* @param {Object} binaryBom BOM read from blint, rewritten in place
|
|
1521
|
+
*/
|
|
1522
|
+
function repairBinaryBomPurls(binaryBom) {
|
|
1523
|
+
const renamed = new Map();
|
|
1524
|
+
const repair = (component) => {
|
|
1525
|
+
const before = component?.purl;
|
|
1526
|
+
if (!before) {
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
repairPurl(component);
|
|
1530
|
+
if (component.purl === before) {
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
renamed.set(before, component.purl);
|
|
1534
|
+
if (component["bom-ref"] === before) {
|
|
1535
|
+
component["bom-ref"] = component.purl;
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
for (const component of binaryBom?.components || []) {
|
|
1539
|
+
repair(component);
|
|
1540
|
+
}
|
|
1541
|
+
repair(binaryBom?.parentComponent);
|
|
1542
|
+
repair(binaryBom?.metadata?.component);
|
|
1543
|
+
if (!renamed.size) {
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
const rename = (ref) => renamed.get(ref) || ref;
|
|
1547
|
+
for (const dependency of binaryBom?.dependencies || []) {
|
|
1548
|
+
if (dependency?.ref) {
|
|
1549
|
+
dependency.ref = rename(dependency.ref);
|
|
1550
|
+
}
|
|
1551
|
+
for (const key of ["dependsOn", "provides"]) {
|
|
1552
|
+
if (Array.isArray(dependency?.[key])) {
|
|
1553
|
+
dependency[key] = dependency[key].map(rename);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
/**
|
|
1560
|
+
* Function to create bom string for binaries using blint
|
|
1561
|
+
*
|
|
1562
|
+
* @param {string} path to the project
|
|
1563
|
+
* @param {Object} options Parse options from the cli
|
|
1564
|
+
* @returns {Object|undefined} BOM object
|
|
1565
|
+
*/
|
|
1566
|
+
export function createBinaryBom(path, options) {
|
|
1567
|
+
const tempDir = safeMkdtempSync(join(getTmpDir(), "blint-tmp-"));
|
|
1568
|
+
const binaryBomFile = join(tempDir, "bom.json");
|
|
1569
|
+
getBinaryBom(path, binaryBomFile, options.deep);
|
|
1570
|
+
if (safeExistsSync(binaryBomFile)) {
|
|
1571
|
+
const binaryBom = JSON.parse(
|
|
1572
|
+
readFileSync(binaryBomFile, { encoding: "utf-8" }),
|
|
1573
|
+
);
|
|
1574
|
+
repairBinaryBomPurls(binaryBom);
|
|
1575
|
+
attachIdentityTools(
|
|
1576
|
+
binaryBom?.components,
|
|
1577
|
+
extractToolRefs(
|
|
1578
|
+
binaryBom?.metadata?.tools,
|
|
1579
|
+
(tool) => tool?.name !== "cdxgen",
|
|
1580
|
+
),
|
|
1581
|
+
);
|
|
1582
|
+
return {
|
|
1583
|
+
bomJson: binaryBom,
|
|
1584
|
+
dependencies: binaryBom.dependencies,
|
|
1585
|
+
parentComponent: binaryBom.parentComponent,
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
return undefined;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/**
|
|
1592
|
+
* Identify the requested AI inventory project types.
|
|
1593
|
+
*
|
|
1594
|
+
* @param {Object} options Parse options from the cli
|
|
1595
|
+
* @returns {string[]} Requested AI inventory types
|
|
1596
|
+
*/
|
|
1597
|
+
export function getRequestedAiInventoryTypes(options) {
|
|
1598
|
+
return AI_INVENTORY_PROJECT_TYPES.filter((type) =>
|
|
1599
|
+
optionIncludesAiInventoryProjectType(options?.projectType, type),
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* Returns the AI inventory types excluded via `--exclude-type`.
|
|
1605
|
+
*
|
|
1606
|
+
* @param {object} options CLI options
|
|
1607
|
+
* @returns {string[]} Excluded AI inventory types
|
|
1608
|
+
*/
|
|
1609
|
+
export function getExcludedAiInventoryTypes(options) {
|
|
1610
|
+
return AI_INVENTORY_PROJECT_TYPES.filter((type) =>
|
|
1611
|
+
optionIncludesAiInventoryProjectType(options?.excludeType, type),
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/**
|
|
1616
|
+
* Removes the excluded AI inventory types from the included list.
|
|
1617
|
+
*
|
|
1618
|
+
* @param {string[]} includedAiInventoryTypes Types selected for collection
|
|
1619
|
+
* @param {string[]} excludedAiInventoryTypes Types excluded by the user
|
|
1620
|
+
* @returns {string[]} Filtered list of AI inventory types
|
|
1621
|
+
*/
|
|
1622
|
+
export function filterIncludedAiInventoryTypes(
|
|
1623
|
+
includedAiInventoryTypes,
|
|
1624
|
+
excludedAiInventoryTypes,
|
|
1625
|
+
) {
|
|
1626
|
+
return [...new Set(includedAiInventoryTypes)].filter(
|
|
1627
|
+
(type) => !excludedAiInventoryTypes.includes(type),
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
/**
|
|
1632
|
+
* Determine which AI inventory types should be collected for a scan.
|
|
1633
|
+
*
|
|
1634
|
+
* This combines explicit project-type opt-ins with BOM audit category-driven
|
|
1635
|
+
* opt-ins, then removes any explicitly excluded inventory types.
|
|
1636
|
+
*
|
|
1637
|
+
* @param {Object} options Parse options from the CLI
|
|
1638
|
+
* @returns {string[]} AI inventory types to collect
|
|
1639
|
+
*/
|
|
1640
|
+
export function getIncludedAiInventoryTypes(options) {
|
|
1641
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
1642
|
+
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
1643
|
+
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
1644
|
+
if (exactAiInventoryType) {
|
|
1645
|
+
return filterIncludedAiInventoryTypes(
|
|
1646
|
+
requestedAiInventoryTypes,
|
|
1647
|
+
excludedAiInventoryTypes,
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
const auditCategories = expandBomAuditCategories(options?.bomAuditCategories);
|
|
1651
|
+
const includedAiInventoryTypes = [...requestedAiInventoryTypes];
|
|
1652
|
+
if (auditCategories.includes("ai-agent")) {
|
|
1653
|
+
includedAiInventoryTypes.push("ai-skill");
|
|
1654
|
+
includedAiInventoryTypes.push("ai");
|
|
1655
|
+
}
|
|
1656
|
+
if (auditCategories.includes("mcp-server")) {
|
|
1657
|
+
includedAiInventoryTypes.push("mcp");
|
|
1658
|
+
}
|
|
1659
|
+
return filterIncludedAiInventoryTypes(
|
|
1660
|
+
includedAiInventoryTypes,
|
|
1661
|
+
excludedAiInventoryTypes,
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Returns the single AI inventory type when exactly one project type alias is
|
|
1667
|
+
* selected, otherwise undefined.
|
|
1668
|
+
*
|
|
1669
|
+
* @param {object} options CLI options
|
|
1670
|
+
* @returns {string|undefined} The exact AI inventory type or undefined
|
|
1671
|
+
*/
|
|
1672
|
+
export function getExactAiInventoryType(options) {
|
|
1673
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
1674
|
+
return requestedAiInventoryTypes.length === 1 &&
|
|
1675
|
+
Array.isArray(options?.projectType) &&
|
|
1676
|
+
options.projectType.length === 1
|
|
1677
|
+
? requestedAiInventoryTypes[0]
|
|
1678
|
+
: undefined;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* Determine whether MCP source-code analysis should run for the current scan.
|
|
1683
|
+
*
|
|
1684
|
+
* @param {string[]} includedAiInventoryTypes AI inventory types selected for collection
|
|
1685
|
+
* @returns {boolean} True when MCP inventory collection is enabled
|
|
1686
|
+
*/
|
|
1687
|
+
export function shouldDetectMcpInventory(includedAiInventoryTypes) {
|
|
1688
|
+
return includedAiInventoryTypes.includes("mcp");
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* Collects sorted, unique relative file names for AI inventory subjects whose
|
|
1693
|
+
* `cdx:file:kind` property matches one of the given kinds.
|
|
1694
|
+
*
|
|
1695
|
+
* @param {object[]} subjects Component or service subjects to inspect
|
|
1696
|
+
* @param {string} discoveryPath Base path used to compute relative file names
|
|
1697
|
+
* @param {Set<string>} kindSet Accepted kind values
|
|
1698
|
+
* @returns {string[]} Sorted unique relative file names
|
|
1699
|
+
*/
|
|
1700
|
+
export function summarizeAiInventoryNames(subjects, discoveryPath, kindSet) {
|
|
1701
|
+
return [
|
|
1702
|
+
...new Set(
|
|
1703
|
+
(subjects || [])
|
|
1704
|
+
.filter((subject) =>
|
|
1705
|
+
kindSet.has(inventoryPropertyValue(subject, "cdx:file:kind")),
|
|
1706
|
+
)
|
|
1707
|
+
.map((subject) => inventoryPropertyValue(subject, "internal:SrcFile"))
|
|
1708
|
+
.filter(Boolean)
|
|
1709
|
+
.map(
|
|
1710
|
+
(filePath) => relative(discoveryPath, filePath) || basename(filePath),
|
|
1711
|
+
),
|
|
1712
|
+
),
|
|
1713
|
+
].sort();
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
/**
|
|
1717
|
+
* Collects sorted unique service names from an AI inventory services list.
|
|
1718
|
+
*
|
|
1719
|
+
* @param {object[]} services Services with a `name` property
|
|
1720
|
+
* @returns {string[]} Sorted unique service names
|
|
1721
|
+
*/
|
|
1722
|
+
export function summarizeAiInventoryServiceNames(services) {
|
|
1723
|
+
return [
|
|
1724
|
+
...new Set(
|
|
1725
|
+
(services || []).map((service) => service?.name).filter(Boolean),
|
|
1726
|
+
),
|
|
1727
|
+
].sort();
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/**
|
|
1731
|
+
* Formats one padded summary line for the AI inventory console output.
|
|
1732
|
+
*
|
|
1733
|
+
* @param {string} label Left-aligned label (padded to 20 chars)
|
|
1734
|
+
* @param {number} count Item count
|
|
1735
|
+
* @param {string[]} nameList Optional list of names to append in parentheses
|
|
1736
|
+
* @returns {string} The formatted summary line
|
|
1737
|
+
*/
|
|
1738
|
+
export function formatAiInventorySummaryLine(label, count, nameList) {
|
|
1739
|
+
return ` ${label.padEnd(20)} ${count}${nameList.length ? ` (${nameList.join(", ")})` : ""}`;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* Prints a human-readable AI inventory summary to stderr, including counts and
|
|
1744
|
+
* names for instruction files, skill files, MCP configs, and MCP services.
|
|
1745
|
+
* Returns without printing when the inventory is empty.
|
|
1746
|
+
*
|
|
1747
|
+
* @param {object} aiInventory AI inventory object with components and services
|
|
1748
|
+
* @param {string} discoveryPath Base path used to compute relative file names
|
|
1749
|
+
* @returns {void}
|
|
1750
|
+
*/
|
|
1751
|
+
export function emitAiInventorySummary(aiInventory, discoveryPath) {
|
|
1752
|
+
const summary = summarizeAiInventory(aiInventory);
|
|
1753
|
+
const totalInventory =
|
|
1754
|
+
summary.instructionCount +
|
|
1755
|
+
summary.skillCount +
|
|
1756
|
+
summary.mcpConfigCount +
|
|
1757
|
+
summary.mcpServiceCount;
|
|
1758
|
+
if (!totalInventory) {
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
const instructionNames = summarizeAiInventoryNames(
|
|
1762
|
+
aiInventory.components,
|
|
1763
|
+
discoveryPath,
|
|
1764
|
+
AI_INSTRUCTION_FILE_KINDS,
|
|
1765
|
+
);
|
|
1766
|
+
const skillNames = summarizeAiInventoryNames(
|
|
1767
|
+
aiInventory.components,
|
|
1768
|
+
discoveryPath,
|
|
1769
|
+
new Set([AI_SKILL_FILE_KIND]),
|
|
1770
|
+
);
|
|
1771
|
+
const mcpConfigNames = summarizeAiInventoryNames(
|
|
1772
|
+
aiInventory.components,
|
|
1773
|
+
discoveryPath,
|
|
1774
|
+
new Set([MCP_CONFIG_FILE_KIND]),
|
|
1775
|
+
);
|
|
1776
|
+
const mcpServiceNames = summarizeAiInventoryServiceNames(
|
|
1777
|
+
aiInventory.services,
|
|
1778
|
+
);
|
|
1779
|
+
console.warn(
|
|
1780
|
+
[
|
|
1781
|
+
"AI Inventory Summary:",
|
|
1782
|
+
formatAiInventorySummaryLine(
|
|
1783
|
+
"AI instruction files:",
|
|
1784
|
+
summary.instructionCount,
|
|
1785
|
+
instructionNames,
|
|
1786
|
+
),
|
|
1787
|
+
formatAiInventorySummaryLine(
|
|
1788
|
+
"Skill files:",
|
|
1789
|
+
summary.skillCount,
|
|
1790
|
+
skillNames,
|
|
1791
|
+
),
|
|
1792
|
+
formatAiInventorySummaryLine(
|
|
1793
|
+
"MCP configs:",
|
|
1794
|
+
summary.mcpConfigCount,
|
|
1795
|
+
mcpConfigNames,
|
|
1796
|
+
),
|
|
1797
|
+
formatAiInventorySummaryLine(
|
|
1798
|
+
"MCP services:",
|
|
1799
|
+
summary.mcpServiceCount,
|
|
1800
|
+
mcpServiceNames,
|
|
1801
|
+
),
|
|
1802
|
+
"",
|
|
1803
|
+
"Run --bom-audit --bom-audit-categories ai-inventory to audit these surfaces.",
|
|
1804
|
+
].join("\n"),
|
|
1805
|
+
);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
/**
|
|
1809
|
+
* Dedupe components
|
|
1810
|
+
*
|
|
1811
|
+
* @param {Object} options Options
|
|
1812
|
+
* @param {Array} components Components
|
|
1813
|
+
* @param {Object} parentComponent Parent component
|
|
1814
|
+
* @param {Array} dependencies Dependencies
|
|
1815
|
+
*
|
|
1816
|
+
* @returns {Object} Object including BOM Json
|
|
1817
|
+
*/
|
|
1818
|
+
export function dedupeBom(options, components, parentComponent, dependencies) {
|
|
1819
|
+
if (!components) {
|
|
1820
|
+
return {};
|
|
1821
|
+
}
|
|
1822
|
+
if (!dependencies) {
|
|
1823
|
+
dependencies = [];
|
|
1824
|
+
}
|
|
1825
|
+
components = trimComponents(components);
|
|
1826
|
+
// Let's apply some common tweaks
|
|
1827
|
+
// Convert evidence.identity section to an object for 1.5
|
|
1828
|
+
if (options.specVersion === 1.5) {
|
|
1829
|
+
for (const comp of components) {
|
|
1830
|
+
if (comp?.evidence?.identity && Array.isArray(comp.evidence.identity)) {
|
|
1831
|
+
comp.evidence.identity = comp.evidence.identity[0];
|
|
1832
|
+
delete comp.evidence.identity.concludedValue;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
if (DEBUG_MODE) {
|
|
1837
|
+
console.log(
|
|
1838
|
+
`Obtained ${components.length} components and ${dependencies.length} dependencies after dedupe.`,
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
const serialNum = `urn:uuid:${uuidv4()}`;
|
|
1842
|
+
return {
|
|
1843
|
+
options,
|
|
1844
|
+
parentComponent,
|
|
1845
|
+
components,
|
|
1846
|
+
bomJson: setCycloneDxFormat(
|
|
1847
|
+
{
|
|
1848
|
+
specVersion: toCycloneDxSpecVersionString(
|
|
1849
|
+
options.specVersion || DEFAULT_CDX_SPEC_VERSION,
|
|
1850
|
+
),
|
|
1851
|
+
serialNumber: serialNum,
|
|
1852
|
+
version: 1,
|
|
1853
|
+
metadata: addMetadata(parentComponent, options, {}),
|
|
1854
|
+
components,
|
|
1855
|
+
services: options.services || [],
|
|
1856
|
+
dependencies,
|
|
1857
|
+
},
|
|
1858
|
+
options.specVersion || DEFAULT_CDX_SPEC_VERSION,
|
|
1859
|
+
{ preserveLegacyBomFormat: true },
|
|
1860
|
+
),
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* Checks whether the user explicitly selected a project type alias that
|
|
1866
|
+
* belongs to the given base project type.
|
|
1867
|
+
*
|
|
1868
|
+
* @param {object} options CLI options
|
|
1869
|
+
* @param {string} baseProjectType Base project type to check against
|
|
1870
|
+
* @returns {boolean} True when an explicit alias selection matches
|
|
1871
|
+
*/
|
|
1872
|
+
export const hasExplicitProjectTypeSelection = (options, baseProjectType) => {
|
|
1873
|
+
options = options || {};
|
|
1874
|
+
const projectTypes = Array.isArray(options.projectType)
|
|
1875
|
+
? options.projectType
|
|
1876
|
+
: options.projectType
|
|
1877
|
+
? [options.projectType]
|
|
1878
|
+
: [];
|
|
1879
|
+
return projectTypes.some((selectedProjectType) =>
|
|
1880
|
+
PROJECT_TYPE_ALIASES[baseProjectType]?.includes(selectedProjectType),
|
|
1881
|
+
);
|
|
1882
|
+
};
|