@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
package/lib/cli/jsBom.js
ADDED
|
@@ -0,0 +1,2087 @@
|
|
|
1
|
+
import { lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { platform as _platform, arch } from "node:os";
|
|
3
|
+
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
|
|
6
|
+
import { build } from "@cdxgen/cdx-purl";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
DEBUG_MODE,
|
|
10
|
+
isSecureMode,
|
|
11
|
+
readEnvironmentVariable,
|
|
12
|
+
recordSensitiveFileRead,
|
|
13
|
+
} from "../core/activity.js";
|
|
14
|
+
import { hasAnyProjectType, isPackageManagerAllowed } from "../core/env.js";
|
|
15
|
+
import { getAllFiles, safeExistsSync, safeSpawnSync } from "../core/fs.js";
|
|
16
|
+
import { thoughtLog } from "../core/logger.js";
|
|
17
|
+
import {
|
|
18
|
+
cleanupAsarTempDir,
|
|
19
|
+
extractAsarToTempDir,
|
|
20
|
+
isAsarArchiveSync,
|
|
21
|
+
parseAsarArchive,
|
|
22
|
+
rewriteExtractedArchivePaths,
|
|
23
|
+
} from "../ecosystems/asarutils.js";
|
|
24
|
+
import { parseBunLock } from "../ecosystems/bunutils.js";
|
|
25
|
+
import { parseCaxaMetadata } from "../ecosystems/caxa.js";
|
|
26
|
+
import {
|
|
27
|
+
CHROME_EXTENSION_PURL_TYPE,
|
|
28
|
+
collectChromeExtensionsFromPath,
|
|
29
|
+
collectInstalledChromeExtensions,
|
|
30
|
+
discoverChromiumExtensionDirs,
|
|
31
|
+
} from "../ecosystems/chromextutils.js";
|
|
32
|
+
import {
|
|
33
|
+
findDenoJson,
|
|
34
|
+
parseDenoJsonFile,
|
|
35
|
+
parseDenoLock,
|
|
36
|
+
} from "../ecosystems/denoutils.js";
|
|
37
|
+
import { addEvidenceForImports } from "../ecosystems/jsEvidence.js";
|
|
38
|
+
import { createNpmWorkspacePurl } from "../ecosystems/npmutils.js";
|
|
39
|
+
import {
|
|
40
|
+
parseBowerJson,
|
|
41
|
+
parseMinJs,
|
|
42
|
+
parseNodeShrinkwrap,
|
|
43
|
+
parsePkgJson,
|
|
44
|
+
parsePkgLock,
|
|
45
|
+
parsePnpmLock,
|
|
46
|
+
parsePnpmWorkspace,
|
|
47
|
+
parseYarnLock,
|
|
48
|
+
parseYarnWorkspace,
|
|
49
|
+
} from "../ecosystems/parsers-js.js";
|
|
50
|
+
import {
|
|
51
|
+
fetchHuggingFaceAssetInventory,
|
|
52
|
+
normalizeHuggingFaceReference,
|
|
53
|
+
} from "../ecosystems/remote/huggingface.js";
|
|
54
|
+
import {
|
|
55
|
+
cleanupTempDir,
|
|
56
|
+
collectInstalledExtensions,
|
|
57
|
+
discoverIdeExtensionDirs,
|
|
58
|
+
extractVsixToTempDir,
|
|
59
|
+
parseVsixFile,
|
|
60
|
+
VSCODE_EXTENSION_PURL_TYPE,
|
|
61
|
+
} from "../helpers/vsixutils.js";
|
|
62
|
+
import {
|
|
63
|
+
collectAiInventory,
|
|
64
|
+
filterInventoryDependencies,
|
|
65
|
+
filterInventorySubjectsByTypes,
|
|
66
|
+
summarizeAiInventory,
|
|
67
|
+
} from "../inventory/aiInventory.js";
|
|
68
|
+
import {
|
|
69
|
+
detectMcpInventory,
|
|
70
|
+
findJSImportsExports,
|
|
71
|
+
} from "../inventory/analyzer.js";
|
|
72
|
+
import {
|
|
73
|
+
markNpmTypesPackagesAsExcluded,
|
|
74
|
+
mergeDependencies,
|
|
75
|
+
mergeServices,
|
|
76
|
+
propagateRequiredScopeFromDependencies,
|
|
77
|
+
trimComponents,
|
|
78
|
+
} from "../inventory/depsUtils.js";
|
|
79
|
+
import { GIT_COMMAND } from "../inventory/envcontext.js";
|
|
80
|
+
import { tryBuildPurl } from "../inventory/purl.js";
|
|
81
|
+
import { DEFAULT_NPMRC_BLOCKLIST, parseNpmrc } from "../parsers/npmrc.js";
|
|
82
|
+
import {
|
|
83
|
+
buildBomNSData,
|
|
84
|
+
createDefaultParentComponent,
|
|
85
|
+
filterIncludedAiInventoryTypes,
|
|
86
|
+
getExactAiInventoryType,
|
|
87
|
+
getExcludedAiInventoryTypes,
|
|
88
|
+
getIncludedAiInventoryTypes,
|
|
89
|
+
getRequestedAiInventoryTypes,
|
|
90
|
+
hasExplicitProjectTypeSelection,
|
|
91
|
+
shouldDetectMcpInventory,
|
|
92
|
+
} from "./bomAssembly.js";
|
|
93
|
+
|
|
94
|
+
const isWin = _platform() === "win32";
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Detects an exact AI inventory type from a direct HuggingFace reference or a
|
|
98
|
+
* local Modelfile/`.gguf` file input. Returns the explicit selection from
|
|
99
|
+
* `getExactAiInventoryType` when one is present.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} path Project or file path
|
|
102
|
+
* @param {object} options CLI options
|
|
103
|
+
* @returns {string|undefined} The detected AI inventory type (`"ai"`) or undefined
|
|
104
|
+
*/
|
|
105
|
+
export function getDirectAiInventoryType(path, options) {
|
|
106
|
+
const exactAiInventoryType = getExactAiInventoryType(options);
|
|
107
|
+
if (exactAiInventoryType) {
|
|
108
|
+
return exactAiInventoryType;
|
|
109
|
+
}
|
|
110
|
+
if (normalizeHuggingFaceReference(path)) {
|
|
111
|
+
return "ai";
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const stat = lstatSync(path);
|
|
115
|
+
if (
|
|
116
|
+
stat.isFile() &&
|
|
117
|
+
(basename(path) === "Modelfile" ||
|
|
118
|
+
basename(path).startsWith("Modelfile.") ||
|
|
119
|
+
path.toLowerCase().endsWith(".gguf"))
|
|
120
|
+
) {
|
|
121
|
+
return "ai";
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
// ignore invalid local direct inputs
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Main Node.js/npm BOM generator. Parses manifests, lockfiles, and import
|
|
131
|
+
* evidence to assemble components, dependencies, formulation data, AI inventory,
|
|
132
|
+
* and the parent component for a Node.js project.
|
|
133
|
+
*
|
|
134
|
+
* @param {string} path Path to the project
|
|
135
|
+
* @param {object} options CLI options
|
|
136
|
+
* @returns {Promise<object>} Promise resolving to a BOM namespace data object
|
|
137
|
+
*/
|
|
138
|
+
export async function createNodejsBom(path, options) {
|
|
139
|
+
let pkgList = [];
|
|
140
|
+
let manifestFiles = [];
|
|
141
|
+
let dependencies = [];
|
|
142
|
+
let parentComponent = {};
|
|
143
|
+
const parentSubComponents = [];
|
|
144
|
+
let ppurl = "";
|
|
145
|
+
const exactAiInventoryType = getDirectAiInventoryType(path, options);
|
|
146
|
+
const requestedAiInventoryTypes = getRequestedAiInventoryTypes(options);
|
|
147
|
+
const excludedAiInventoryTypes = getExcludedAiInventoryTypes(options);
|
|
148
|
+
const includedAiInventoryTypes = exactAiInventoryType
|
|
149
|
+
? filterIncludedAiInventoryTypes(
|
|
150
|
+
[exactAiInventoryType],
|
|
151
|
+
excludedAiInventoryTypes,
|
|
152
|
+
)
|
|
153
|
+
: filterIncludedAiInventoryTypes(
|
|
154
|
+
[...getIncludedAiInventoryTypes(options), ...requestedAiInventoryTypes],
|
|
155
|
+
excludedAiInventoryTypes,
|
|
156
|
+
);
|
|
157
|
+
let aiInventory = { components: [], dependencies: [], services: [] };
|
|
158
|
+
const directHuggingFaceSource =
|
|
159
|
+
exactAiInventoryType === "ai"
|
|
160
|
+
? normalizeHuggingFaceReference(path)
|
|
161
|
+
: undefined;
|
|
162
|
+
if (directHuggingFaceSource?.repoId) {
|
|
163
|
+
const inventory = await fetchHuggingFaceAssetInventory(
|
|
164
|
+
directHuggingFaceSource.assetType,
|
|
165
|
+
directHuggingFaceSource.repoId,
|
|
166
|
+
{
|
|
167
|
+
...options,
|
|
168
|
+
resolveHuggingFaceRemote: true,
|
|
169
|
+
version: directHuggingFaceSource.version,
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
if (inventory?.primaryComponent) {
|
|
173
|
+
parentComponent = createDefaultParentComponent(path, "generic", options);
|
|
174
|
+
return buildBomNSData(options, inventory.components, "generic", {
|
|
175
|
+
dependencies: inventory.dependencies || [],
|
|
176
|
+
filename: path,
|
|
177
|
+
parentComponent,
|
|
178
|
+
projectType: exactAiInventoryType,
|
|
179
|
+
services: [],
|
|
180
|
+
src: path,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (exactAiInventoryType === "ai" || exactAiInventoryType === "ai-skill") {
|
|
185
|
+
aiInventory = collectAiInventory(path, options, includedAiInventoryTypes);
|
|
186
|
+
const exactComponents = trimComponents([...(aiInventory.components || [])]);
|
|
187
|
+
const exactDependencies = mergeDependencies([], aiInventory.dependencies);
|
|
188
|
+
const exactServices = mergeServices([], aiInventory.services);
|
|
189
|
+
parentComponent = createDefaultParentComponent(path, "generic", options);
|
|
190
|
+
return buildBomNSData(options, exactComponents, "generic", {
|
|
191
|
+
dependencies: exactDependencies,
|
|
192
|
+
filename: path,
|
|
193
|
+
parentComponent,
|
|
194
|
+
projectType: exactAiInventoryType,
|
|
195
|
+
services: exactServices,
|
|
196
|
+
src: path,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
// Docker mode requires special handling
|
|
200
|
+
if (hasAnyProjectType(["docker", "oci", "container", "os"], options, false)) {
|
|
201
|
+
const pkgJsonFiles = getAllFiles(path, "**/package.json", options);
|
|
202
|
+
// Are there any package.json files in the container?
|
|
203
|
+
if (pkgJsonFiles.length) {
|
|
204
|
+
for (const pj of pkgJsonFiles) {
|
|
205
|
+
const dlist = await parsePkgJson(pj);
|
|
206
|
+
if (dlist?.length) {
|
|
207
|
+
pkgList = pkgList.concat(dlist);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (includedAiInventoryTypes.length) {
|
|
211
|
+
aiInventory = collectAiInventory(
|
|
212
|
+
path,
|
|
213
|
+
options,
|
|
214
|
+
includedAiInventoryTypes,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (aiInventory.components?.length) {
|
|
218
|
+
pkgList = trimComponents(pkgList.concat(aiInventory.components));
|
|
219
|
+
}
|
|
220
|
+
return buildBomNSData(options, pkgList, "npm", {
|
|
221
|
+
allImports: {},
|
|
222
|
+
src: path,
|
|
223
|
+
filename: "package.json",
|
|
224
|
+
dependencies: mergeDependencies(
|
|
225
|
+
[],
|
|
226
|
+
aiInventory.dependencies,
|
|
227
|
+
parentComponent,
|
|
228
|
+
),
|
|
229
|
+
parentComponent,
|
|
230
|
+
services: aiInventory.services,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
let allImports = {};
|
|
235
|
+
let allExports = {};
|
|
236
|
+
let mcpInventory = {};
|
|
237
|
+
if (
|
|
238
|
+
!hasAnyProjectType(["docker", "oci", "container", "os"], options, false) &&
|
|
239
|
+
!options.noBabel
|
|
240
|
+
) {
|
|
241
|
+
if (DEBUG_MODE) {
|
|
242
|
+
console.log(
|
|
243
|
+
`Performing babel-based package usage analysis with source code at ${path}`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
const retData = await findJSImportsExports(path, options);
|
|
247
|
+
allImports = retData.allImports;
|
|
248
|
+
allExports = retData.allExports;
|
|
249
|
+
if (shouldDetectMcpInventory(includedAiInventoryTypes)) {
|
|
250
|
+
mcpInventory = detectMcpInventory(path, options.deep);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (includedAiInventoryTypes.length) {
|
|
254
|
+
aiInventory = collectAiInventory(path, options, includedAiInventoryTypes);
|
|
255
|
+
}
|
|
256
|
+
const aiInventorySummary = summarizeAiInventory(aiInventory);
|
|
257
|
+
if (!exactAiInventoryType) {
|
|
258
|
+
if (
|
|
259
|
+
aiInventorySummary.aiComponentCount ||
|
|
260
|
+
aiInventorySummary.aiServiceCount
|
|
261
|
+
) {
|
|
262
|
+
thoughtLog(
|
|
263
|
+
`I found ${aiInventorySummary.aiComponentCount} AI asset component(s) and ${aiInventorySummary.aiServiceCount} AI service(s) from JavaScript usage signals. Use --exclude-type ai to drop them from the BOM.`,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
if (aiInventorySummary.instructionCount || aiInventorySummary.skillCount) {
|
|
267
|
+
thoughtLog(
|
|
268
|
+
`I found ${aiInventorySummary.instructionCount + aiInventorySummary.skillCount} AI skill/instruction file component(s). Use '--exclude-type ai-skill' for a package-only BOM, or '--bom-audit --bom-audit-categories ai-inventory' for review-friendly reporting.`,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
if (aiInventorySummary.mcpConfigCount) {
|
|
272
|
+
thoughtLog(
|
|
273
|
+
`I found ${aiInventorySummary.mcpConfigCount} MCP config component(s). Use '--exclude-type mcp' to drop them, or '--bom-audit --bom-audit-categories ai-inventory --tlp-classification AMBER' to keep and flag them.`,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Committed lockfiles and their manifests can live under dot-directories
|
|
278
|
+
// (eg: .github/scripts), so include hidden paths when discovering them -
|
|
279
|
+
// otherwise the dependencies of those nested projects are silently dropped
|
|
280
|
+
// from the BOM (see #4224). node_modules and .git stay ignored by getAllFiles.
|
|
281
|
+
const lockSearchOptions = { ...options, includeDot: true };
|
|
282
|
+
let yarnLockFiles = getAllFiles(
|
|
283
|
+
path,
|
|
284
|
+
`${options.multiProject ? "**/" : ""}yarn.lock`,
|
|
285
|
+
lockSearchOptions,
|
|
286
|
+
);
|
|
287
|
+
const shrinkwrapFiles = getAllFiles(
|
|
288
|
+
path,
|
|
289
|
+
`${options.multiProject ? "**/" : ""}npm-shrinkwrap.json`,
|
|
290
|
+
lockSearchOptions,
|
|
291
|
+
);
|
|
292
|
+
let pkgLockFiles = getAllFiles(
|
|
293
|
+
path,
|
|
294
|
+
`${options.multiProject ? "**/" : ""}package-lock.json`,
|
|
295
|
+
lockSearchOptions,
|
|
296
|
+
);
|
|
297
|
+
if (shrinkwrapFiles.length) {
|
|
298
|
+
pkgLockFiles = pkgLockFiles.concat(shrinkwrapFiles);
|
|
299
|
+
}
|
|
300
|
+
let pnpmLockFiles = getAllFiles(
|
|
301
|
+
path,
|
|
302
|
+
`${options.multiProject ? "**/" : ""}pnpm-lock.yaml`,
|
|
303
|
+
lockSearchOptions,
|
|
304
|
+
);
|
|
305
|
+
const bunLockFiles = getAllFiles(
|
|
306
|
+
path,
|
|
307
|
+
`${options.multiProject ? "**/" : ""}bun.lock`,
|
|
308
|
+
lockSearchOptions,
|
|
309
|
+
);
|
|
310
|
+
// Warn about the binary bun lockfile which cdxgen cannot parse. Users should
|
|
311
|
+
// regenerate a text lockfile with `bun install --save-text-lockfile`.
|
|
312
|
+
const bunBinaryLockFiles = getAllFiles(
|
|
313
|
+
path,
|
|
314
|
+
`${options.multiProject ? "**/" : ""}bun.lockb`,
|
|
315
|
+
lockSearchOptions,
|
|
316
|
+
);
|
|
317
|
+
if (bunBinaryLockFiles.length && !bunLockFiles.length) {
|
|
318
|
+
console.log(
|
|
319
|
+
"Found a binary bun lockfile (bun.lockb) which cdxgen cannot parse. Regenerate a text lockfile with 'bun install --save-text-lockfile' (bun >= 1.2) and try again.",
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const denoLockFiles = getAllFiles(
|
|
323
|
+
path,
|
|
324
|
+
`${options.multiProject ? "**/" : ""}deno.lock`,
|
|
325
|
+
lockSearchOptions,
|
|
326
|
+
);
|
|
327
|
+
if (
|
|
328
|
+
!denoLockFiles?.length &&
|
|
329
|
+
isPackageManagerAllowed("deno", ["npm", "pnpm", "yarn", "bun"], options)
|
|
330
|
+
) {
|
|
331
|
+
const denoManifestFiles = getAllFiles(
|
|
332
|
+
path,
|
|
333
|
+
`${options.multiProject ? "**/" : ""}deno.{json,jsonc}`,
|
|
334
|
+
lockSearchOptions,
|
|
335
|
+
);
|
|
336
|
+
if (denoManifestFiles?.length) {
|
|
337
|
+
console.log(
|
|
338
|
+
"Found a Deno manifest (deno.json/deno.jsonc) without a deno.lock. Run 'deno install' to generate a lockfile so cdxgen can capture the resolved dependencies.",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
let pnpmWorkspaceFiles = getAllFiles(
|
|
343
|
+
path,
|
|
344
|
+
`${options.multiProject ? "**/" : ""}pnpm-workspace.yaml`,
|
|
345
|
+
lockSearchOptions,
|
|
346
|
+
);
|
|
347
|
+
const rootPnpmLockFile = resolve(path, "pnpm-lock.yaml");
|
|
348
|
+
const rootPnpmWorkspaceFile = resolve(path, "pnpm-workspace.yaml");
|
|
349
|
+
const workspaceFilesCache = new Map();
|
|
350
|
+
if (
|
|
351
|
+
safeExistsSync(rootPnpmLockFile) &&
|
|
352
|
+
safeExistsSync(rootPnpmWorkspaceFile) &&
|
|
353
|
+
pnpmLockFiles.includes(rootPnpmLockFile)
|
|
354
|
+
) {
|
|
355
|
+
// The root pnpm-lock.yaml resolves the dependencies of every declared
|
|
356
|
+
// workspace member, so their nested lockfiles are redundant. However,
|
|
357
|
+
// independent nested projects that are NOT listed in pnpm-workspace.yaml
|
|
358
|
+
// keep their own committed lockfile, and those must still be parsed -
|
|
359
|
+
// otherwise their dependencies go missing from the BOM.
|
|
360
|
+
const workspaceObj = parsePnpmWorkspace(rootPnpmWorkspaceFile);
|
|
361
|
+
const workspaceMemberDirs = new Set();
|
|
362
|
+
const workspacePackages = workspaceObj?.packagePatterns || [];
|
|
363
|
+
const workspaceSearchOptions = {
|
|
364
|
+
...options,
|
|
365
|
+
exclude: [
|
|
366
|
+
...(options.exclude || []),
|
|
367
|
+
...(workspaceObj?.excludePackages || []),
|
|
368
|
+
],
|
|
369
|
+
includeNodeModulesDir: false,
|
|
370
|
+
};
|
|
371
|
+
for (const awp of workspacePackages) {
|
|
372
|
+
const workspacePackagePattern = awp.endsWith("package.json")
|
|
373
|
+
? awp
|
|
374
|
+
: `${awp.replace(/\/+$/, "")}/package.json`;
|
|
375
|
+
const wpkgJsonFiles = getAllFiles(
|
|
376
|
+
path,
|
|
377
|
+
workspacePackagePattern,
|
|
378
|
+
workspaceSearchOptions,
|
|
379
|
+
);
|
|
380
|
+
workspaceFilesCache.set(workspacePackagePattern, wpkgJsonFiles);
|
|
381
|
+
for (const apj of wpkgJsonFiles || []) {
|
|
382
|
+
workspaceMemberDirs.add(resolve(dirname(apj)));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// A nested lockfile is redundant only when it sits in a declared workspace
|
|
386
|
+
// member directory. The root lockfile itself is always retained.
|
|
387
|
+
const isRedundantNestedLock = (lockFile) => {
|
|
388
|
+
const resolvedLockFile = resolve(lockFile);
|
|
389
|
+
if (resolvedLockFile === rootPnpmLockFile) {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
return workspaceMemberDirs.has(resolve(dirname(lockFile)));
|
|
393
|
+
};
|
|
394
|
+
pnpmLockFiles = pnpmLockFiles.filter((f) => !isRedundantNestedLock(f));
|
|
395
|
+
yarnLockFiles = yarnLockFiles.filter((f) => !isRedundantNestedLock(f));
|
|
396
|
+
pkgLockFiles = pkgLockFiles.filter((f) => !isRedundantNestedLock(f));
|
|
397
|
+
pnpmWorkspaceFiles = [rootPnpmWorkspaceFile];
|
|
398
|
+
}
|
|
399
|
+
const minJsFiles = getAllFiles(
|
|
400
|
+
path,
|
|
401
|
+
`${options.multiProject ? "**/" : ""}*min.js`,
|
|
402
|
+
options,
|
|
403
|
+
);
|
|
404
|
+
const bowerFiles = getAllFiles(
|
|
405
|
+
path,
|
|
406
|
+
`${options.multiProject ? "**/" : ""}bower.json`,
|
|
407
|
+
options,
|
|
408
|
+
);
|
|
409
|
+
if (DEBUG_MODE) {
|
|
410
|
+
const wasmFiles = getAllFiles(
|
|
411
|
+
path,
|
|
412
|
+
`${options.multiProject ? "**/" : ""}*.wasm`,
|
|
413
|
+
{
|
|
414
|
+
...options,
|
|
415
|
+
includeNodeModulesDir: true,
|
|
416
|
+
},
|
|
417
|
+
);
|
|
418
|
+
if (wasmFiles?.length) {
|
|
419
|
+
console.log(
|
|
420
|
+
`Found ${wasmFiles.length} wasm files in this project. cdxgen will make a best attempt to identify the exports and imports from these files.`,
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// Parse min js files
|
|
425
|
+
if (minJsFiles?.length) {
|
|
426
|
+
manifestFiles = manifestFiles.concat(minJsFiles);
|
|
427
|
+
for (const f of minJsFiles) {
|
|
428
|
+
const dlist = await parseMinJs(f);
|
|
429
|
+
if (dlist?.length) {
|
|
430
|
+
pkgList = pkgList.concat(dlist);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// Parse bower json files
|
|
435
|
+
if (bowerFiles?.length) {
|
|
436
|
+
manifestFiles = manifestFiles.concat(bowerFiles);
|
|
437
|
+
for (const f of bowerFiles) {
|
|
438
|
+
const dlist = await parseBowerJson(f);
|
|
439
|
+
if (dlist?.length) {
|
|
440
|
+
pkgList = pkgList.concat(dlist);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const pkgJsonLockFiles = getAllFiles(
|
|
445
|
+
path,
|
|
446
|
+
`${options.multiProject ? "**/" : ""}package-lock.json`,
|
|
447
|
+
lockSearchOptions,
|
|
448
|
+
);
|
|
449
|
+
const pkgJsonFiles = getAllFiles(
|
|
450
|
+
path,
|
|
451
|
+
`${options.multiProject ? "**/" : ""}package.json`,
|
|
452
|
+
lockSearchOptions,
|
|
453
|
+
);
|
|
454
|
+
const npmInstallCount =
|
|
455
|
+
Number.parseInt(readEnvironmentVariable("NPM_INSTALL_COUNT"), 10) || 2;
|
|
456
|
+
let anyInstallSuccess = false;
|
|
457
|
+
// Automatic npm install logic.
|
|
458
|
+
// Only perform npm install for smaller projects (< 2 package.json) without the correct number of lock files
|
|
459
|
+
if (
|
|
460
|
+
(pkgJsonLockFiles?.length === 0 ||
|
|
461
|
+
pkgJsonLockFiles?.length < pkgJsonFiles?.length - 1) &&
|
|
462
|
+
yarnLockFiles?.length === 0 &&
|
|
463
|
+
pnpmLockFiles?.length === 0 &&
|
|
464
|
+
bunLockFiles?.length === 0 &&
|
|
465
|
+
denoLockFiles?.length === 0 &&
|
|
466
|
+
pkgJsonFiles?.length <= npmInstallCount &&
|
|
467
|
+
options.installDeps
|
|
468
|
+
) {
|
|
469
|
+
for (const apkgJson of pkgJsonFiles) {
|
|
470
|
+
let pkgMgr = "npm";
|
|
471
|
+
const supPkgMgrs = ["npm", "yarn", "yarnpkg", "pnpm", "pnpx"];
|
|
472
|
+
const pkgData = JSON.parse(readFileSync(apkgJson, "utf8"));
|
|
473
|
+
const mgrData = pkgData.packageManager;
|
|
474
|
+
let mgr = "";
|
|
475
|
+
if (mgrData) {
|
|
476
|
+
mgr = mgrData.split("@")[0];
|
|
477
|
+
}
|
|
478
|
+
// Try harder to identify the correct package manager
|
|
479
|
+
if (options?.projectType?.includes("npm")) {
|
|
480
|
+
pkgMgr = "npm";
|
|
481
|
+
} else if (supPkgMgrs.includes(mgr)) {
|
|
482
|
+
pkgMgr = mgr;
|
|
483
|
+
} else if (pkgData?.engines?.yarn) {
|
|
484
|
+
pkgMgr = "yarn";
|
|
485
|
+
} else if (
|
|
486
|
+
isPackageManagerAllowed("npm", ["yarn", "pnpm", "rush"], options)
|
|
487
|
+
) {
|
|
488
|
+
pkgMgr = "npm";
|
|
489
|
+
} else if (
|
|
490
|
+
isPackageManagerAllowed("yarn", ["npm", "pnpm", "rush"], options)
|
|
491
|
+
) {
|
|
492
|
+
pkgMgr = "yarn";
|
|
493
|
+
} else if (
|
|
494
|
+
isPackageManagerAllowed("pnpm", ["npm", "yarn", "rush"], options)
|
|
495
|
+
) {
|
|
496
|
+
pkgMgr = "pnpm";
|
|
497
|
+
}
|
|
498
|
+
let installCommand = "install";
|
|
499
|
+
if (pkgMgr === "npm" && isSecureMode && pkgJsonLockFiles?.length > 0) {
|
|
500
|
+
installCommand = "ci";
|
|
501
|
+
}
|
|
502
|
+
let installArgs = [installCommand];
|
|
503
|
+
// Support for passing additional args to the install command
|
|
504
|
+
if (readEnvironmentVariable(`${pkgMgr.toUpperCase()}_INSTALL_ARGS`)) {
|
|
505
|
+
const addArgs = readEnvironmentVariable(
|
|
506
|
+
`${pkgMgr.toUpperCase()}_INSTALL_ARGS`,
|
|
507
|
+
).split(" ");
|
|
508
|
+
installArgs = installArgs.concat(addArgs);
|
|
509
|
+
}
|
|
510
|
+
// Always invoke the install command with ignore-scripts to guard against version spoofing
|
|
511
|
+
if (["npm", "pnpm", "yarn"].includes(pkgMgr)) {
|
|
512
|
+
if (!installArgs.includes("--ignore-scripts")) {
|
|
513
|
+
installArgs.push("--ignore-scripts");
|
|
514
|
+
}
|
|
515
|
+
if (pkgMgr === "pnpm") {
|
|
516
|
+
installArgs.push("--ignore-pnpmfile");
|
|
517
|
+
}
|
|
518
|
+
if (pkgMgr === "npm") {
|
|
519
|
+
for (const c of ["--no-audit", "--no-bin-links"]) {
|
|
520
|
+
if (!installArgs.includes(c)) {
|
|
521
|
+
installArgs.push(c);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
installArgs.push(`--git=${GIT_COMMAND}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (
|
|
528
|
+
pkgMgr === "npm" &&
|
|
529
|
+
isSecureMode &&
|
|
530
|
+
!installArgs.join(" ").includes("--allow-git")
|
|
531
|
+
) {
|
|
532
|
+
console.log(
|
|
533
|
+
"Consider passing '--allow-git=none' via the environment variable NPM_INSTALL_ARGS to prevent any git dependencies from being fetched and installed via npm.",
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
const basePath = dirname(apkgJson);
|
|
537
|
+
let npmrcData;
|
|
538
|
+
const npmrcFile = join(basePath, ".npmrc");
|
|
539
|
+
if (safeExistsSync(npmrcFile)) {
|
|
540
|
+
thoughtLog(
|
|
541
|
+
"Wait, there is a .npmrc file here! I'm going to check if it has anything malicious.",
|
|
542
|
+
);
|
|
543
|
+
npmrcData = readFileSync(npmrcFile, "utf-8");
|
|
544
|
+
recordSensitiveFileRead(npmrcFile, {
|
|
545
|
+
label: "npm registry configuration",
|
|
546
|
+
});
|
|
547
|
+
const npmrcObj = parseNpmrc(npmrcData);
|
|
548
|
+
for (const [key, value] of Object.entries(npmrcObj)) {
|
|
549
|
+
const baseKey = key.replace(/^(?:\/\/[^/]+\/|@[^:]+:)/, "");
|
|
550
|
+
if (
|
|
551
|
+
DEFAULT_NPMRC_BLOCKLIST.has(baseKey) ||
|
|
552
|
+
DEFAULT_NPMRC_BLOCKLIST.has(key)
|
|
553
|
+
) {
|
|
554
|
+
console.warn(
|
|
555
|
+
`\x1b[1;35mSECURE MODE: Dangerous configuration ${key}=${value} detected in .npmrc! Verify if this is a trusted project. Remove this setting or any other problematic configurations to proceed.\x1b[0m`,
|
|
556
|
+
);
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// juice-shop mode
|
|
562
|
+
// Projects such as juice-shop prevent lockfile creations using .npmrc files
|
|
563
|
+
// Plus, they might require specific npm install args such as --legacy-peer-deps that could lead to strange node_modules structure
|
|
564
|
+
// To keep life simple, let's look for any .npmrc file that has package-lock=false to toggle before npm install
|
|
565
|
+
if (pkgMgr === "npm") {
|
|
566
|
+
if (
|
|
567
|
+
npmrcData?.includes("package-lock=false") &&
|
|
568
|
+
!installArgs.includes("--package-lock")
|
|
569
|
+
) {
|
|
570
|
+
installArgs.push("--package-lock");
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (pkgMgr === "npm" && yarnLockFiles.length) {
|
|
574
|
+
thoughtLog(
|
|
575
|
+
`Wait, there are ${yarnLockFiles.length} yarn.lock files in this project; however, I'm about to invoke the '${pkgMgr} ${installArgs[0]}' command.`,
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
if (pkgMgr !== "npm") {
|
|
579
|
+
thoughtLog(
|
|
580
|
+
`**PACKAGE MANAGER**: Let's run the '${pkgMgr}' command with the arguments '${installArgs.join(" ")}' to generate the needed lock files.`,
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
console.warn(
|
|
584
|
+
"\x1b[1;35mNotice: Generating an SBOM without a lockfile is risky and non-deterministic. Consider generating and committing the lockfile to your repository to ensure reproducible builds and SBOMs.\x1b[0m",
|
|
585
|
+
);
|
|
586
|
+
console.log(
|
|
587
|
+
`Executing '${pkgMgr} ${installArgs.join(" ")}' in`,
|
|
588
|
+
basePath,
|
|
589
|
+
);
|
|
590
|
+
const result = safeSpawnSync(pkgMgr, installArgs, {
|
|
591
|
+
cwd: basePath,
|
|
592
|
+
shell: isWin,
|
|
593
|
+
});
|
|
594
|
+
if (result.status !== 0 || result.error) {
|
|
595
|
+
console.error(
|
|
596
|
+
`${pkgMgr} install has failed. Generated SBOM will be empty or with a lower precision.`,
|
|
597
|
+
);
|
|
598
|
+
thoughtLog(
|
|
599
|
+
"It looks like the install command has failed. I'm considering some troubleshooting ideas.",
|
|
600
|
+
);
|
|
601
|
+
if (DEBUG_MODE && result.stdout) {
|
|
602
|
+
if (result.stdout.includes("EBADENGINE Unsupported engine")) {
|
|
603
|
+
console.log(
|
|
604
|
+
"TIP: The current version of node.js is incompatible with this project. Re-run cdxgen with an install-version type to install a matching node.js for the project. Eg: `-t node20`",
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
console.log("---------------");
|
|
608
|
+
console.log(result.stdout);
|
|
609
|
+
}
|
|
610
|
+
if (result.stderr) {
|
|
611
|
+
if (result.stderr.includes("--legacy-peer-deps")) {
|
|
612
|
+
console.log(
|
|
613
|
+
"Set the environment variable `NPM_INSTALL_ARGS=--legacy-peer-deps` to resolve the dependency resolution issue reported.",
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
if (
|
|
617
|
+
result.stderr.includes(
|
|
618
|
+
"npm error command sh -c node-pre-gyp install",
|
|
619
|
+
)
|
|
620
|
+
) {
|
|
621
|
+
console.log(
|
|
622
|
+
"cdxgen has detected errors with the native build using node-gyp.",
|
|
623
|
+
);
|
|
624
|
+
if (readEnvironmentVariable("CDXGEN_IN_CONTAINER") === "true") {
|
|
625
|
+
if (arch() !== "x64") {
|
|
626
|
+
console.log(
|
|
627
|
+
`INFO: Many npm packages have limited support for ${arch()} architecture. Run the cdxgen container image with --platform=linux/amd64 for best experience.`,
|
|
628
|
+
);
|
|
629
|
+
} else {
|
|
630
|
+
console.log(
|
|
631
|
+
"TIP: The default image bundles node >= 24, which might be incompatible with this project. Re-run cdxgen with an install-version type to install an older node.js for the project. Eg: `-t node20`",
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
} else {
|
|
635
|
+
console.log(
|
|
636
|
+
"TIP: Try running the cdxgen container image with --platform=linux/amd64, and pass an install-version type such as `-t node20` to build the project with an older node.js.",
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
console.log("---------------");
|
|
641
|
+
console.log(result.stderr);
|
|
642
|
+
}
|
|
643
|
+
options.failOnError && process.exit(1);
|
|
644
|
+
} else {
|
|
645
|
+
anyInstallSuccess = true;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
pkgLockFiles = getAllFiles(
|
|
649
|
+
path,
|
|
650
|
+
`${options.multiProject ? "**/" : ""}package-lock.json`,
|
|
651
|
+
options,
|
|
652
|
+
);
|
|
653
|
+
pnpmLockFiles = getAllFiles(
|
|
654
|
+
path,
|
|
655
|
+
`${options.multiProject ? "**/" : ""}pnpm-lock.yaml`,
|
|
656
|
+
options,
|
|
657
|
+
);
|
|
658
|
+
yarnLockFiles = getAllFiles(
|
|
659
|
+
path,
|
|
660
|
+
`${options.multiProject ? "**/" : ""}yarn.lock`,
|
|
661
|
+
options,
|
|
662
|
+
);
|
|
663
|
+
if (
|
|
664
|
+
anyInstallSuccess &&
|
|
665
|
+
!pkgLockFiles.length &&
|
|
666
|
+
!pnpmLockFiles.length &&
|
|
667
|
+
!yarnLockFiles.length
|
|
668
|
+
) {
|
|
669
|
+
thoughtLog(
|
|
670
|
+
`Despite a successful installation step, I didn't find any lock files. Perhaps they're being created elsewhere, such as in the root directory. I am currently checking the directory at ${path}.`,
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (
|
|
675
|
+
pnpmLockFiles?.length &&
|
|
676
|
+
isPackageManagerAllowed("pnpm", ["npm", "yarn", "rush"], options)
|
|
677
|
+
) {
|
|
678
|
+
manifestFiles = manifestFiles.concat(pnpmLockFiles);
|
|
679
|
+
const workspacePackages = [];
|
|
680
|
+
const workspaceSrcFiles = {};
|
|
681
|
+
const workspaceDirectDeps = {};
|
|
682
|
+
const depsWorkspaceRefs = {};
|
|
683
|
+
let workspaceCatalogs = {};
|
|
684
|
+
const seenPkgJsonFiles = {};
|
|
685
|
+
// Is this a pnpm workspace?
|
|
686
|
+
for (const f of pnpmWorkspaceFiles) {
|
|
687
|
+
if (DEBUG_MODE) {
|
|
688
|
+
console.log(`Parsing workspace definition ${f}`);
|
|
689
|
+
}
|
|
690
|
+
const workspaceObj = parsePnpmWorkspace(f);
|
|
691
|
+
if (workspaceObj?.packages) {
|
|
692
|
+
const workspaceBasePath = dirname(f);
|
|
693
|
+
const workspacePatterns =
|
|
694
|
+
workspaceObj.packagePatterns || workspaceObj.packages;
|
|
695
|
+
// We need the precise purl for all workspace packages and their direct dependencies
|
|
696
|
+
for (const awp of workspacePatterns) {
|
|
697
|
+
const workspaceExcludes = [...(workspaceObj.excludePackages || [])];
|
|
698
|
+
const workspaceSearchOptions = {
|
|
699
|
+
...options,
|
|
700
|
+
exclude: [...(options.exclude || []), ...workspaceExcludes],
|
|
701
|
+
includeNodeModulesDir: false,
|
|
702
|
+
};
|
|
703
|
+
const workspacePackagePattern = awp.endsWith("package.json")
|
|
704
|
+
? awp
|
|
705
|
+
: `${awp.replace(/\/+$/, "")}/package.json`;
|
|
706
|
+
let wpkgJsonFiles = workspaceFilesCache.get(workspacePackagePattern);
|
|
707
|
+
if (!wpkgJsonFiles) {
|
|
708
|
+
wpkgJsonFiles = getAllFiles(
|
|
709
|
+
workspaceBasePath,
|
|
710
|
+
workspacePackagePattern,
|
|
711
|
+
workspaceSearchOptions,
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
if (!wpkgJsonFiles?.length) {
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
for (const apj of wpkgJsonFiles) {
|
|
718
|
+
if (seenPkgJsonFiles[apj]) {
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
seenPkgJsonFiles[apj] = true;
|
|
722
|
+
let pkgData;
|
|
723
|
+
try {
|
|
724
|
+
pkgData = JSON.parse(readFileSync(apj, "utf-8"));
|
|
725
|
+
} catch (_err) {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
if (pkgData?.name) {
|
|
729
|
+
const relativePkgJsonFile = relative(path, apj);
|
|
730
|
+
let workspaceRef = `pkg:npm/${pkgData.name}`;
|
|
731
|
+
if (pkgData?.version) {
|
|
732
|
+
workspaceRef = `${workspaceRef}@${pkgData.version}`;
|
|
733
|
+
}
|
|
734
|
+
// Track all workspace purls. When we face duplicates, let's try to expand the purl to
|
|
735
|
+
// include the subpath.
|
|
736
|
+
if (!workspacePackages.includes(workspaceRef)) {
|
|
737
|
+
workspacePackages.push(workspaceRef);
|
|
738
|
+
} else {
|
|
739
|
+
console.log(
|
|
740
|
+
`Found a duplicate workspace with the name: ${pkgData.name}, ref: ${workspaceRef} at ${relativePkgJsonFile} and ${workspaceSrcFiles[workspaceRef]}. This is likely an error in the project that needs fixing.`,
|
|
741
|
+
);
|
|
742
|
+
workspaceRef = `${workspaceRef}#${relativePkgJsonFile.replace(`${sep}package.json`, "")}`;
|
|
743
|
+
if (!workspacePackages.includes(workspaceRef)) {
|
|
744
|
+
workspacePackages.push(workspaceRef);
|
|
745
|
+
console.log(
|
|
746
|
+
`Duplicate workspace tracked as ${workspaceRef} under metadata.component.components`,
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
workspaceSrcFiles[workspaceRef] = relativePkgJsonFile;
|
|
751
|
+
// Track the direct dependencies of each workspace and workspace refs for each direct deps.
|
|
752
|
+
const allDeps = {
|
|
753
|
+
...(pkgData.dependencies || {}),
|
|
754
|
+
...(pkgData.devDependencies || {}),
|
|
755
|
+
...(pkgData.peerDependencies || {}),
|
|
756
|
+
};
|
|
757
|
+
for (const adep of Object.keys(allDeps)) {
|
|
758
|
+
if (!workspaceDirectDeps[workspaceRef]) {
|
|
759
|
+
workspaceDirectDeps[workspaceRef] = new Set();
|
|
760
|
+
}
|
|
761
|
+
const apkgRef = `pkg:npm/${adep}`;
|
|
762
|
+
workspaceDirectDeps[workspaceRef].add(apkgRef);
|
|
763
|
+
if (!depsWorkspaceRefs[apkgRef]) {
|
|
764
|
+
depsWorkspaceRefs[apkgRef] = [];
|
|
765
|
+
}
|
|
766
|
+
depsWorkspaceRefs[apkgRef].push(workspaceRef);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
workspaceCatalogs = {
|
|
773
|
+
...workspaceCatalogs,
|
|
774
|
+
...(workspaceObj.catalogs || {}),
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (DEBUG_MODE && Object.keys(seenPkgJsonFiles).length) {
|
|
778
|
+
console.log(
|
|
779
|
+
`${Object.keys(seenPkgJsonFiles).length} package.json files were parsed to identify workspace names. Total number of package.json files: ${pkgJsonFiles.length}`,
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
for (const f of pnpmLockFiles) {
|
|
783
|
+
if (DEBUG_MODE) {
|
|
784
|
+
console.log(`Parsing ${f}`);
|
|
785
|
+
}
|
|
786
|
+
const basePath = dirname(f);
|
|
787
|
+
// Determine the parent component
|
|
788
|
+
const packageJsonF = join(basePath, "package.json");
|
|
789
|
+
const pnpmCjsHooks = join(basePath, ".pnpmfile.cjs");
|
|
790
|
+
const pnpmMjsHooks = join(basePath, ".pnpmfile.mjs");
|
|
791
|
+
if (safeExistsSync(pnpmMjsHooks) || safeExistsSync(pnpmCjsHooks)) {
|
|
792
|
+
thoughtLog("Wait, this pnpm project uses install hooks.");
|
|
793
|
+
}
|
|
794
|
+
if (!Object.keys(parentComponent).length) {
|
|
795
|
+
if (safeExistsSync(packageJsonF)) {
|
|
796
|
+
const pcs = await parsePkgJson(packageJsonF, true, true);
|
|
797
|
+
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
798
|
+
parentComponent = { ...pcs[0] };
|
|
799
|
+
parentComponent.type = "application";
|
|
800
|
+
ppurl = build({
|
|
801
|
+
type: "npm",
|
|
802
|
+
namespace: options.projectGroup || parentComponent.group || null,
|
|
803
|
+
name: parentComponent.name,
|
|
804
|
+
version:
|
|
805
|
+
options.projectVersion || parentComponent.version || null,
|
|
806
|
+
});
|
|
807
|
+
parentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
808
|
+
parentComponent["purl"] = ppurl;
|
|
809
|
+
}
|
|
810
|
+
} else {
|
|
811
|
+
let dirName = dirname(f);
|
|
812
|
+
const tmpA = dirName.split(sep);
|
|
813
|
+
dirName = tmpA[tmpA.length - 1];
|
|
814
|
+
parentComponent = {
|
|
815
|
+
group: "",
|
|
816
|
+
name: dirName,
|
|
817
|
+
type: "application",
|
|
818
|
+
};
|
|
819
|
+
ppurl = build({
|
|
820
|
+
type: "npm",
|
|
821
|
+
namespace: options.projectGroup || parentComponent.group || null,
|
|
822
|
+
name:
|
|
823
|
+
"project-name" in options
|
|
824
|
+
? options.projectName
|
|
825
|
+
: parentComponent.name,
|
|
826
|
+
version: options.projectVersion || parentComponent.version || null,
|
|
827
|
+
});
|
|
828
|
+
parentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
829
|
+
parentComponent["purl"] = ppurl;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
// Parse the pnpm file
|
|
833
|
+
const parsedList = await parsePnpmLock(
|
|
834
|
+
f,
|
|
835
|
+
parentComponent,
|
|
836
|
+
workspacePackages,
|
|
837
|
+
workspaceSrcFiles,
|
|
838
|
+
workspaceCatalogs,
|
|
839
|
+
workspaceDirectDeps,
|
|
840
|
+
depsWorkspaceRefs,
|
|
841
|
+
path,
|
|
842
|
+
);
|
|
843
|
+
const dlist = parsedList.pkgList;
|
|
844
|
+
if (dlist?.length) {
|
|
845
|
+
pkgList = pkgList.concat(dlist);
|
|
846
|
+
}
|
|
847
|
+
if (parsedList?.parentSubComponents?.length) {
|
|
848
|
+
parentComponent.components = parsedList.parentSubComponents;
|
|
849
|
+
}
|
|
850
|
+
if (parsedList?.dependenciesList?.length) {
|
|
851
|
+
dependencies = mergeDependencies(
|
|
852
|
+
dependencies,
|
|
853
|
+
parsedList.dependenciesList,
|
|
854
|
+
parentComponent,
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (
|
|
860
|
+
pkgLockFiles?.length &&
|
|
861
|
+
isPackageManagerAllowed("npm", ["pnpm", "yarn"], options)
|
|
862
|
+
) {
|
|
863
|
+
if (anyInstallSuccess) {
|
|
864
|
+
thoughtLog(
|
|
865
|
+
`I have ${pkgLockFiles.length} package-lock.json file(s) now after a successful npm install.`,
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
manifestFiles = manifestFiles.concat(pkgLockFiles);
|
|
869
|
+
for (const f of pkgLockFiles) {
|
|
870
|
+
if (DEBUG_MODE) {
|
|
871
|
+
console.log(`Parsing ${f}`);
|
|
872
|
+
}
|
|
873
|
+
// Parse package-lock.json if available
|
|
874
|
+
const parsedList = await parsePkgLock(f, options);
|
|
875
|
+
const dlist = parsedList.pkgList;
|
|
876
|
+
let tmpParentComponent = dlist.splice(0, 1)[0] || {};
|
|
877
|
+
if (!Object.keys(parentComponent).length) {
|
|
878
|
+
const basePath = dirname(f);
|
|
879
|
+
const packageJsonF = join(basePath, "package.json");
|
|
880
|
+
if (safeExistsSync(packageJsonF)) {
|
|
881
|
+
const pcs = await parsePkgJson(packageJsonF, true, true);
|
|
882
|
+
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
883
|
+
tmpParentComponent = { ...pcs[0] };
|
|
884
|
+
tmpParentComponent.type = "application";
|
|
885
|
+
tmpParentComponent.name =
|
|
886
|
+
"project-name" in options
|
|
887
|
+
? options.projectName
|
|
888
|
+
: tmpParentComponent.name;
|
|
889
|
+
ppurl = build({
|
|
890
|
+
type: "npm",
|
|
891
|
+
namespace:
|
|
892
|
+
options.projectGroup || tmpParentComponent.group || null,
|
|
893
|
+
name:
|
|
894
|
+
"project-name" in options
|
|
895
|
+
? options.projectName
|
|
896
|
+
: tmpParentComponent.name,
|
|
897
|
+
version:
|
|
898
|
+
options.projectVersion || tmpParentComponent.version || null,
|
|
899
|
+
});
|
|
900
|
+
tmpParentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
901
|
+
tmpParentComponent["purl"] = ppurl;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
parentComponent = tmpParentComponent;
|
|
905
|
+
} else {
|
|
906
|
+
parentSubComponents.push(tmpParentComponent);
|
|
907
|
+
}
|
|
908
|
+
if (dlist?.length) {
|
|
909
|
+
pkgList = pkgList.concat(dlist);
|
|
910
|
+
}
|
|
911
|
+
if (parsedList?.dependenciesList?.length) {
|
|
912
|
+
dependencies = mergeDependencies(
|
|
913
|
+
dependencies,
|
|
914
|
+
parsedList.dependenciesList,
|
|
915
|
+
parentComponent,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (
|
|
921
|
+
safeExistsSync(join(path, "rush.json")) &&
|
|
922
|
+
isPackageManagerAllowed("rush", ["npm", "yarn", "pnpm"], options)
|
|
923
|
+
) {
|
|
924
|
+
// Rush.js creates node_modules inside common/temp directory
|
|
925
|
+
const nmDir = join(path, "common", "temp", "node_modules");
|
|
926
|
+
// Do rush install if we don't have node_modules directory
|
|
927
|
+
if (!safeExistsSync(nmDir)) {
|
|
928
|
+
console.log("Executing 'rush install --no-link'", path);
|
|
929
|
+
const result = safeSpawnSync(
|
|
930
|
+
"rush",
|
|
931
|
+
["install", "--no-link", "--bypass-policy"],
|
|
932
|
+
{
|
|
933
|
+
cwd: path,
|
|
934
|
+
shell: isWin,
|
|
935
|
+
},
|
|
936
|
+
);
|
|
937
|
+
if (result.status === 1 || result.error) {
|
|
938
|
+
console.error(result.stdout, result.stderr);
|
|
939
|
+
options.failOnError && process.exit(1);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
// Look for shrinkwrap file
|
|
943
|
+
const swFile = join(
|
|
944
|
+
path,
|
|
945
|
+
"tools",
|
|
946
|
+
"build-tasks",
|
|
947
|
+
".rush",
|
|
948
|
+
"temp",
|
|
949
|
+
"shrinkwrap-deps.json",
|
|
950
|
+
);
|
|
951
|
+
const pnpmLock = join(path, "common", "config", "rush", "pnpm-lock.yaml");
|
|
952
|
+
if (safeExistsSync(swFile)) {
|
|
953
|
+
let pkgList = await parseNodeShrinkwrap(swFile);
|
|
954
|
+
pkgList = addWasmComponentsFromImports(pkgList, allImports);
|
|
955
|
+
if (allImports && Object.keys(allImports).length) {
|
|
956
|
+
pkgList = await addEvidenceForImports(
|
|
957
|
+
pkgList,
|
|
958
|
+
allImports,
|
|
959
|
+
allExports,
|
|
960
|
+
options.deep,
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
return buildBomNSData(options, pkgList, "npm", {
|
|
964
|
+
allImports,
|
|
965
|
+
src: path,
|
|
966
|
+
filename: "shrinkwrap-deps.json",
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
if (safeExistsSync(pnpmLock)) {
|
|
970
|
+
const pnpmLockObj = await parsePnpmLock(
|
|
971
|
+
pnpmLock,
|
|
972
|
+
null,
|
|
973
|
+
[],
|
|
974
|
+
{},
|
|
975
|
+
{},
|
|
976
|
+
{},
|
|
977
|
+
{},
|
|
978
|
+
path,
|
|
979
|
+
);
|
|
980
|
+
let pkgList = addWasmComponentsFromImports(
|
|
981
|
+
pnpmLockObj.pkgList,
|
|
982
|
+
allImports,
|
|
983
|
+
);
|
|
984
|
+
let dependencies = [];
|
|
985
|
+
if (pnpmLockObj?.dependenciesList?.length) {
|
|
986
|
+
dependencies = mergeDependencies(
|
|
987
|
+
dependencies,
|
|
988
|
+
pnpmLockObj.dependenciesList,
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
if (allImports && Object.keys(allImports).length) {
|
|
992
|
+
pkgList = await addEvidenceForImports(
|
|
993
|
+
pkgList,
|
|
994
|
+
allImports,
|
|
995
|
+
allExports,
|
|
996
|
+
options.deep,
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
pkgList = propagateRequiredScopeFromDependencies(pkgList, dependencies);
|
|
1000
|
+
pkgList = markNpmTypesPackagesAsExcluded(pkgList);
|
|
1001
|
+
return buildBomNSData(options, pkgList, "npm", {
|
|
1002
|
+
allImports,
|
|
1003
|
+
allExports,
|
|
1004
|
+
dependencies,
|
|
1005
|
+
src: path,
|
|
1006
|
+
filename: "pnpm-lock.yaml",
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
console.log(
|
|
1010
|
+
"Neither shrinkwrap file: ",
|
|
1011
|
+
swFile,
|
|
1012
|
+
" nor pnpm lockfile",
|
|
1013
|
+
pnpmLock,
|
|
1014
|
+
"was found!",
|
|
1015
|
+
);
|
|
1016
|
+
options.failOnError && process.exit(1);
|
|
1017
|
+
}
|
|
1018
|
+
if (
|
|
1019
|
+
yarnLockFiles?.length &&
|
|
1020
|
+
isPackageManagerAllowed("yarn", ["npm", "pnpm"], options)
|
|
1021
|
+
) {
|
|
1022
|
+
manifestFiles = manifestFiles.concat(yarnLockFiles);
|
|
1023
|
+
const workspacePackages = [];
|
|
1024
|
+
const workspaceSrcFiles = {};
|
|
1025
|
+
const workspaceDirectDeps = {};
|
|
1026
|
+
const depsWorkspaceRefs = {};
|
|
1027
|
+
const seenPkgJsonFiles = {};
|
|
1028
|
+
|
|
1029
|
+
// Check if this is a yarn workspace by examining package.json files for workspace definitions
|
|
1030
|
+
const packageJsonFilesWithWorkspaces = yarnLockFiles
|
|
1031
|
+
.map((f) => join(dirname(f), "package.json"))
|
|
1032
|
+
.filter((pkgFile) => safeExistsSync(pkgFile));
|
|
1033
|
+
|
|
1034
|
+
for (const packageJsonFile of packageJsonFilesWithWorkspaces) {
|
|
1035
|
+
const workspaceObj = parseYarnWorkspace(packageJsonFile);
|
|
1036
|
+
if (workspaceObj?.packages) {
|
|
1037
|
+
if (DEBUG_MODE) {
|
|
1038
|
+
console.log(`Found yarn workspace definition in ${packageJsonFile}`);
|
|
1039
|
+
}
|
|
1040
|
+
// We need the precise purl for all workspace packages and their direct dependencies
|
|
1041
|
+
const workspaceBasePath = dirname(packageJsonFile);
|
|
1042
|
+
for (const awp of workspaceObj.packages) {
|
|
1043
|
+
let wpkgJsonFiles = [];
|
|
1044
|
+
|
|
1045
|
+
// Handle different yarn workspace patterns
|
|
1046
|
+
if (awp.includes("*")) {
|
|
1047
|
+
// Replace all '*' with '**' for recursive glob matching and ensure package.json suffix
|
|
1048
|
+
const pattern =
|
|
1049
|
+
awp.replaceAll("*", "**") +
|
|
1050
|
+
(awp.endsWith(".json") ? "" : "/package.json");
|
|
1051
|
+
wpkgJsonFiles = getAllFiles(workspaceBasePath, pattern, {
|
|
1052
|
+
...options,
|
|
1053
|
+
includeNodeModulesDir: false,
|
|
1054
|
+
});
|
|
1055
|
+
} else {
|
|
1056
|
+
// Direct path pattern
|
|
1057
|
+
wpkgJsonFiles = getAllFiles(
|
|
1058
|
+
join(workspaceBasePath, awp),
|
|
1059
|
+
"**/package.json",
|
|
1060
|
+
{
|
|
1061
|
+
...options,
|
|
1062
|
+
includeNodeModulesDir: false,
|
|
1063
|
+
},
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
for (const apj of wpkgJsonFiles) {
|
|
1067
|
+
if (seenPkgJsonFiles[apj]) {
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
seenPkgJsonFiles[apj] = true;
|
|
1071
|
+
const pkgData = JSON.parse(readFileSync(apj, "utf-8"));
|
|
1072
|
+
if (pkgData?.name) {
|
|
1073
|
+
const relativePkgJsonFile = relative(path, apj);
|
|
1074
|
+
// Create properly encoded PURL using helper function
|
|
1075
|
+
const workspaceRef = createNpmWorkspacePurl(
|
|
1076
|
+
pkgData.name,
|
|
1077
|
+
pkgData.version,
|
|
1078
|
+
);
|
|
1079
|
+
workspacePackages.push(workspaceRef);
|
|
1080
|
+
workspaceSrcFiles[workspaceRef] = relativePkgJsonFile;
|
|
1081
|
+
if (pkgData?.dependencies || pkgData?.devDependencies) {
|
|
1082
|
+
const dlist = new Set();
|
|
1083
|
+
for (const [k, v] of Object.entries({
|
|
1084
|
+
...(pkgData.dependencies || {}),
|
|
1085
|
+
...(pkgData.devDependencies || {}),
|
|
1086
|
+
})) {
|
|
1087
|
+
if (v.match(/^\d/)) {
|
|
1088
|
+
dlist.add(`pkg:npm/${k}@${v}`);
|
|
1089
|
+
} else {
|
|
1090
|
+
dlist.add(`pkg:npm/${k}`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
workspaceDirectDeps[workspaceRef] = Array.from(dlist);
|
|
1094
|
+
for (const dep of workspaceDirectDeps[workspaceRef]) {
|
|
1095
|
+
if (!depsWorkspaceRefs[dep]) {
|
|
1096
|
+
depsWorkspaceRefs[dep] = [];
|
|
1097
|
+
}
|
|
1098
|
+
depsWorkspaceRefs[dep].push(workspaceRef);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
for (const f of yarnLockFiles) {
|
|
1107
|
+
if (DEBUG_MODE) {
|
|
1108
|
+
console.log(`Parsing ${f}`);
|
|
1109
|
+
}
|
|
1110
|
+
const basePath = dirname(f);
|
|
1111
|
+
// Determine the parent component
|
|
1112
|
+
const packageJsonF = join(basePath, "package.json");
|
|
1113
|
+
if (safeExistsSync(packageJsonF)) {
|
|
1114
|
+
const pcs = await parsePkgJson(packageJsonF, true, true);
|
|
1115
|
+
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
1116
|
+
const tmpParentComponent = { ...pcs[0] };
|
|
1117
|
+
tmpParentComponent.type = "application";
|
|
1118
|
+
ppurl = build({
|
|
1119
|
+
type: "npm",
|
|
1120
|
+
namespace: options.projectGroup || tmpParentComponent.group || null,
|
|
1121
|
+
name: tmpParentComponent.name,
|
|
1122
|
+
version:
|
|
1123
|
+
options.projectVersion || tmpParentComponent.version || null,
|
|
1124
|
+
});
|
|
1125
|
+
tmpParentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
1126
|
+
tmpParentComponent["purl"] = ppurl;
|
|
1127
|
+
if (!Object.keys(parentComponent).length) {
|
|
1128
|
+
parentComponent = tmpParentComponent;
|
|
1129
|
+
} else {
|
|
1130
|
+
parentSubComponents.push(tmpParentComponent);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
} else {
|
|
1134
|
+
let dirName = dirname(f);
|
|
1135
|
+
const tmpA = dirName.split(sep);
|
|
1136
|
+
dirName = tmpA[tmpA.length - 1];
|
|
1137
|
+
const tmpParentComponent = {
|
|
1138
|
+
group: options.projectGroup || "",
|
|
1139
|
+
name: "project-name" in options ? options.projectName : dirName,
|
|
1140
|
+
type: "application",
|
|
1141
|
+
};
|
|
1142
|
+
ppurl = build({
|
|
1143
|
+
type: "npm",
|
|
1144
|
+
namespace: tmpParentComponent.group || null,
|
|
1145
|
+
name: tmpParentComponent.name,
|
|
1146
|
+
version: options.projectVersion || tmpParentComponent.version || null,
|
|
1147
|
+
});
|
|
1148
|
+
tmpParentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
1149
|
+
tmpParentComponent["purl"] = ppurl;
|
|
1150
|
+
if (!Object.keys(parentComponent).length) {
|
|
1151
|
+
parentComponent = tmpParentComponent;
|
|
1152
|
+
} else {
|
|
1153
|
+
parentSubComponents.push(tmpParentComponent);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
// Parse yarn.lock if available. This check is after rush.json since
|
|
1157
|
+
// rush.js could include yarn.lock :(
|
|
1158
|
+
const parsedList = await parseYarnLock(
|
|
1159
|
+
f,
|
|
1160
|
+
parentComponent,
|
|
1161
|
+
workspacePackages,
|
|
1162
|
+
workspaceSrcFiles,
|
|
1163
|
+
workspaceDirectDeps,
|
|
1164
|
+
depsWorkspaceRefs,
|
|
1165
|
+
);
|
|
1166
|
+
const dlist = parsedList.pkgList;
|
|
1167
|
+
if (dlist?.length) {
|
|
1168
|
+
pkgList = pkgList.concat(dlist);
|
|
1169
|
+
}
|
|
1170
|
+
const rdeplist = [];
|
|
1171
|
+
if (parsedList?.dependenciesList?.length) {
|
|
1172
|
+
// Inject parent component to the dependency tree to make it complete
|
|
1173
|
+
// In case of yarn, yarn list command lists every root package as a direct dependency
|
|
1174
|
+
// The same logic is matched with this for loop although this is incorrect since even dev dependencies would get included here
|
|
1175
|
+
for (const dobj of parsedList.dependenciesList) {
|
|
1176
|
+
rdeplist.push(dobj.ref);
|
|
1177
|
+
}
|
|
1178
|
+
// Fixes: 212. Handle case where there are no package.json to determine the parent package
|
|
1179
|
+
// Bug fix: We need to consistently override the parent component group, name and version here
|
|
1180
|
+
if (Object.keys(parentComponent).length && parentComponent.name) {
|
|
1181
|
+
const ppurl = build({
|
|
1182
|
+
type: "npm",
|
|
1183
|
+
namespace: options.projectGroup || parentComponent.group || null,
|
|
1184
|
+
name: parentComponent.name,
|
|
1185
|
+
version: options.projectVersion || parentComponent.version || null,
|
|
1186
|
+
});
|
|
1187
|
+
parsedList.dependenciesList.push({
|
|
1188
|
+
ref: decodeURIComponent(ppurl),
|
|
1189
|
+
dependsOn: [...new Set(rdeplist)].sort(),
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
dependencies = mergeDependencies(
|
|
1193
|
+
dependencies,
|
|
1194
|
+
parsedList.dependenciesList,
|
|
1195
|
+
parentComponent,
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (
|
|
1201
|
+
bunLockFiles?.length &&
|
|
1202
|
+
isPackageManagerAllowed("bun", ["npm", "pnpm", "yarn"], options)
|
|
1203
|
+
) {
|
|
1204
|
+
manifestFiles = manifestFiles.concat(bunLockFiles);
|
|
1205
|
+
for (const f of bunLockFiles) {
|
|
1206
|
+
if (DEBUG_MODE) {
|
|
1207
|
+
console.log(`Parsing ${f}`);
|
|
1208
|
+
}
|
|
1209
|
+
const basePath = dirname(f);
|
|
1210
|
+
// Determine the parent component from the adjacent package.json.
|
|
1211
|
+
const packageJsonF = join(basePath, "package.json");
|
|
1212
|
+
let bunParentComponent = {};
|
|
1213
|
+
if (safeExistsSync(packageJsonF)) {
|
|
1214
|
+
const pcs = await parsePkgJson(packageJsonF, true, true);
|
|
1215
|
+
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
1216
|
+
bunParentComponent = { ...pcs[0] };
|
|
1217
|
+
bunParentComponent.type = "application";
|
|
1218
|
+
ppurl = build({
|
|
1219
|
+
type: "npm",
|
|
1220
|
+
namespace: options.projectGroup || bunParentComponent.group || null,
|
|
1221
|
+
name: bunParentComponent.name,
|
|
1222
|
+
version:
|
|
1223
|
+
options.projectVersion || bunParentComponent.version || null,
|
|
1224
|
+
});
|
|
1225
|
+
bunParentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
1226
|
+
bunParentComponent["purl"] = ppurl;
|
|
1227
|
+
}
|
|
1228
|
+
} else {
|
|
1229
|
+
let dirName = dirname(f);
|
|
1230
|
+
const tmpA = dirName.split(sep);
|
|
1231
|
+
dirName = tmpA[tmpA.length - 1];
|
|
1232
|
+
bunParentComponent = {
|
|
1233
|
+
group: options.projectGroup || "",
|
|
1234
|
+
name: "project-name" in options ? options.projectName : dirName,
|
|
1235
|
+
type: "application",
|
|
1236
|
+
};
|
|
1237
|
+
ppurl = build({
|
|
1238
|
+
type: "npm",
|
|
1239
|
+
namespace: bunParentComponent.group || null,
|
|
1240
|
+
name: bunParentComponent.name,
|
|
1241
|
+
version: options.projectVersion || bunParentComponent.version || null,
|
|
1242
|
+
});
|
|
1243
|
+
bunParentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
1244
|
+
bunParentComponent["purl"] = ppurl;
|
|
1245
|
+
}
|
|
1246
|
+
if (Object.keys(bunParentComponent).length) {
|
|
1247
|
+
if (!Object.keys(parentComponent).length) {
|
|
1248
|
+
parentComponent = bunParentComponent;
|
|
1249
|
+
} else if (
|
|
1250
|
+
bunParentComponent["bom-ref"] !== parentComponent["bom-ref"]
|
|
1251
|
+
) {
|
|
1252
|
+
parentSubComponents.push(bunParentComponent);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
const parsedList = await parseBunLock(f, {
|
|
1256
|
+
...options,
|
|
1257
|
+
projectRoot: basePath,
|
|
1258
|
+
parentComponent: bunParentComponent,
|
|
1259
|
+
});
|
|
1260
|
+
if (parsedList.pkgList?.length) {
|
|
1261
|
+
pkgList = pkgList.concat(parsedList.pkgList);
|
|
1262
|
+
}
|
|
1263
|
+
if (parsedList.dependenciesList?.length) {
|
|
1264
|
+
dependencies = mergeDependencies(
|
|
1265
|
+
dependencies,
|
|
1266
|
+
parsedList.dependenciesList,
|
|
1267
|
+
bunParentComponent,
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
if (
|
|
1273
|
+
denoLockFiles?.length &&
|
|
1274
|
+
isPackageManagerAllowed("deno", ["npm", "pnpm", "yarn", "bun"], options)
|
|
1275
|
+
) {
|
|
1276
|
+
manifestFiles = manifestFiles.concat(denoLockFiles);
|
|
1277
|
+
for (const f of denoLockFiles) {
|
|
1278
|
+
if (DEBUG_MODE) {
|
|
1279
|
+
console.log(`Parsing ${f}`);
|
|
1280
|
+
}
|
|
1281
|
+
const basePath = dirname(f);
|
|
1282
|
+
// Derive the parent component from the adjacent deno.json(c), falling
|
|
1283
|
+
// back to the directory name like the bun block does.
|
|
1284
|
+
const denoJsonFile = findDenoJson(basePath);
|
|
1285
|
+
let denoParentComponent = {};
|
|
1286
|
+
if (denoJsonFile) {
|
|
1287
|
+
const denoConfig = parseDenoJsonFile(denoJsonFile);
|
|
1288
|
+
if (denoConfig?.name) {
|
|
1289
|
+
// deno.json names are frequently scoped (e.g. "@demo/deno-app").
|
|
1290
|
+
// Split the scope into the purl group so the name does not end up
|
|
1291
|
+
// carrying an encoded slash.
|
|
1292
|
+
let denoGroup = options.projectGroup || "";
|
|
1293
|
+
let denoName = denoConfig.name;
|
|
1294
|
+
if (!denoGroup && denoName.startsWith("@")) {
|
|
1295
|
+
const slashIndex = denoName.indexOf("/");
|
|
1296
|
+
if (slashIndex > 0) {
|
|
1297
|
+
denoGroup = denoName.substring(0, slashIndex);
|
|
1298
|
+
denoName = denoName.substring(slashIndex + 1);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
denoParentComponent = {
|
|
1302
|
+
group: denoGroup,
|
|
1303
|
+
name: denoName,
|
|
1304
|
+
version: options.projectVersion || denoConfig.version || "",
|
|
1305
|
+
type: "application",
|
|
1306
|
+
};
|
|
1307
|
+
ppurl = build({
|
|
1308
|
+
type: "npm",
|
|
1309
|
+
namespace: denoParentComponent.group || null,
|
|
1310
|
+
name: denoParentComponent.name,
|
|
1311
|
+
version: denoParentComponent.version || null,
|
|
1312
|
+
});
|
|
1313
|
+
denoParentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
1314
|
+
denoParentComponent["purl"] = ppurl;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
if (!Object.keys(denoParentComponent).length) {
|
|
1318
|
+
let dirName = basePath;
|
|
1319
|
+
const tmpA = dirName.split(sep);
|
|
1320
|
+
dirName = tmpA[tmpA.length - 1];
|
|
1321
|
+
denoParentComponent = {
|
|
1322
|
+
group: options.projectGroup || "",
|
|
1323
|
+
name: "project-name" in options ? options.projectName : dirName,
|
|
1324
|
+
type: "application",
|
|
1325
|
+
};
|
|
1326
|
+
ppurl = build({
|
|
1327
|
+
type: "npm",
|
|
1328
|
+
namespace: denoParentComponent.group || null,
|
|
1329
|
+
name: denoParentComponent.name,
|
|
1330
|
+
version: denoParentComponent.version || null,
|
|
1331
|
+
});
|
|
1332
|
+
denoParentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
1333
|
+
denoParentComponent["purl"] = ppurl;
|
|
1334
|
+
}
|
|
1335
|
+
if (Object.keys(denoParentComponent).length) {
|
|
1336
|
+
if (!Object.keys(parentComponent).length) {
|
|
1337
|
+
parentComponent = denoParentComponent;
|
|
1338
|
+
} else if (
|
|
1339
|
+
denoParentComponent["bom-ref"] !== parentComponent["bom-ref"]
|
|
1340
|
+
) {
|
|
1341
|
+
parentSubComponents.push(denoParentComponent);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
const parsedList = await parseDenoLock(f, {
|
|
1345
|
+
...options,
|
|
1346
|
+
projectRoot: basePath,
|
|
1347
|
+
parentComponent: denoParentComponent,
|
|
1348
|
+
});
|
|
1349
|
+
if (parsedList.pkgList?.length) {
|
|
1350
|
+
pkgList = pkgList.concat(parsedList.pkgList);
|
|
1351
|
+
}
|
|
1352
|
+
if (parsedList.dependenciesList?.length) {
|
|
1353
|
+
dependencies = mergeDependencies(
|
|
1354
|
+
dependencies,
|
|
1355
|
+
parsedList.dependenciesList,
|
|
1356
|
+
denoParentComponent,
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
// We might reach here if the project has no lock files
|
|
1362
|
+
// Eg: juice-shop
|
|
1363
|
+
if (!pkgList.length && safeExistsSync(join(path, "node_modules"))) {
|
|
1364
|
+
// Collect all package.json files from all node_modules directory
|
|
1365
|
+
const pkgJsonFiles = getAllFiles(
|
|
1366
|
+
path,
|
|
1367
|
+
"**/node_modules/**/package.json",
|
|
1368
|
+
options,
|
|
1369
|
+
);
|
|
1370
|
+
manifestFiles = manifestFiles.concat(pkgJsonFiles);
|
|
1371
|
+
for (const pkgjf of pkgJsonFiles) {
|
|
1372
|
+
const dlist = await parsePkgJson(pkgjf);
|
|
1373
|
+
if (dlist?.length) {
|
|
1374
|
+
pkgList = pkgList.concat(dlist);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
if (!parentComponent || !Object.keys(parentComponent).length) {
|
|
1378
|
+
if (safeExistsSync(join(path, "package.json"))) {
|
|
1379
|
+
const pcs = await parsePkgJson(join(path, "package.json"), true, true);
|
|
1380
|
+
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
1381
|
+
parentComponent = { ...pcs[0] };
|
|
1382
|
+
parentComponent.type = "application";
|
|
1383
|
+
ppurl = build({
|
|
1384
|
+
type: "npm",
|
|
1385
|
+
namespace: options.projectGroup || parentComponent.group || null,
|
|
1386
|
+
name: parentComponent.name,
|
|
1387
|
+
version: options.projectVersion || parentComponent.version || null,
|
|
1388
|
+
});
|
|
1389
|
+
parentComponent["bom-ref"] = decodeURIComponent(ppurl);
|
|
1390
|
+
parentComponent["purl"] = ppurl;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (
|
|
1396
|
+
!pkgList.length &&
|
|
1397
|
+
(yarnLockFiles.length ||
|
|
1398
|
+
pkgLockFiles.length ||
|
|
1399
|
+
bunLockFiles.length ||
|
|
1400
|
+
denoLockFiles.length)
|
|
1401
|
+
) {
|
|
1402
|
+
if (options.projectType?.length) {
|
|
1403
|
+
thoughtLog(
|
|
1404
|
+
`Despite seeing some lock files, I didn't find any components in this Node.js project. Is there an issue with the project type '${options.projectType?.join(", ")}' used 🤔? I recommend trying again with a different type.`,
|
|
1405
|
+
);
|
|
1406
|
+
} else {
|
|
1407
|
+
thoughtLog(
|
|
1408
|
+
"Despite seeing some lock files, I didn't find any components in this Node.js project. Feels like a bug.",
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
// Retain the components of parent component
|
|
1413
|
+
if (parentSubComponents.length) {
|
|
1414
|
+
parentComponent.components = parentSubComponents;
|
|
1415
|
+
}
|
|
1416
|
+
// We need to set this to force our version to be used rather than the directory name based one.
|
|
1417
|
+
// Fix #1550. Do not blindly force the npm parent to always become the overall parent.
|
|
1418
|
+
if ("project-name" in options && !options.parentComponent) {
|
|
1419
|
+
options.parentComponent = parentComponent;
|
|
1420
|
+
}
|
|
1421
|
+
if (allImports && Object.keys(allImports).length) {
|
|
1422
|
+
pkgList = addWasmComponentsFromImports(pkgList, allImports);
|
|
1423
|
+
pkgList = await addEvidenceForImports(
|
|
1424
|
+
pkgList,
|
|
1425
|
+
allImports,
|
|
1426
|
+
allExports,
|
|
1427
|
+
options.deep,
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
if (mcpInventory.components?.length) {
|
|
1431
|
+
pkgList = trimComponents(pkgList.concat(mcpInventory.components));
|
|
1432
|
+
}
|
|
1433
|
+
if (mcpInventory.dependencies?.length) {
|
|
1434
|
+
dependencies = mergeDependencies(
|
|
1435
|
+
dependencies,
|
|
1436
|
+
mcpInventory.dependencies,
|
|
1437
|
+
parentComponent,
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
if (aiInventory.components?.length) {
|
|
1441
|
+
pkgList = trimComponents(pkgList.concat(aiInventory.components));
|
|
1442
|
+
}
|
|
1443
|
+
if (aiInventory.dependencies?.length) {
|
|
1444
|
+
dependencies = mergeDependencies(
|
|
1445
|
+
dependencies,
|
|
1446
|
+
aiInventory.dependencies,
|
|
1447
|
+
parentComponent,
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
const inventoryServices = mergeServices(
|
|
1451
|
+
mergeServices([], mcpInventory.services || []),
|
|
1452
|
+
aiInventory.services || [],
|
|
1453
|
+
);
|
|
1454
|
+
pkgList = propagateRequiredScopeFromDependencies(pkgList, dependencies);
|
|
1455
|
+
pkgList = markNpmTypesPackagesAsExcluded(pkgList);
|
|
1456
|
+
if (exactAiInventoryType === "mcp") {
|
|
1457
|
+
pkgList = trimComponents(filterInventorySubjectsByTypes(pkgList, ["mcp"]));
|
|
1458
|
+
dependencies = filterInventoryDependencies(
|
|
1459
|
+
dependencies,
|
|
1460
|
+
pkgList,
|
|
1461
|
+
inventoryServices,
|
|
1462
|
+
);
|
|
1463
|
+
if (!parentComponent || !Object.keys(parentComponent).length) {
|
|
1464
|
+
parentComponent = createDefaultParentComponent(path, "generic", options);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
return buildBomNSData(options, pkgList, "npm", {
|
|
1468
|
+
allImports,
|
|
1469
|
+
allExports,
|
|
1470
|
+
src: path,
|
|
1471
|
+
filename: manifestFiles.join(", "),
|
|
1472
|
+
dependencies,
|
|
1473
|
+
parentComponent,
|
|
1474
|
+
services: inventoryServices,
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
/**
|
|
1479
|
+
* Regex matching `.wasm` import paths in analyzer output, including optional
|
|
1480
|
+
* query/fragment suffixes.
|
|
1481
|
+
*
|
|
1482
|
+
* @type {RegExp}
|
|
1483
|
+
*/
|
|
1484
|
+
export const WASM_IMPORT_PATTERN = /\.wasm([?#].*)?$/i;
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* Adds generic wasm components from discovered source imports.
|
|
1488
|
+
*
|
|
1489
|
+
* @param {Array<Object>} pkgList Node.js package list
|
|
1490
|
+
* @param {Object} allImports analyzer imports map
|
|
1491
|
+
* @returns {Array<Object>} pkgList enriched with wasm components
|
|
1492
|
+
*/
|
|
1493
|
+
export const addWasmComponentsFromImports = (pkgList, allImports) => {
|
|
1494
|
+
if (!allImports || !Object.keys(allImports).length) {
|
|
1495
|
+
return pkgList;
|
|
1496
|
+
}
|
|
1497
|
+
const existingPurls = new Set();
|
|
1498
|
+
for (const pkg of pkgList) {
|
|
1499
|
+
if (pkg?.purl) {
|
|
1500
|
+
existingPurls.add(pkg.purl);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
for (const [importPath, occurrences] of Object.entries(allImports)) {
|
|
1504
|
+
if (!WASM_IMPORT_PATTERN.test(importPath)) {
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1507
|
+
const cleanImportPath = importPath.replace(/[?#].*$/, "");
|
|
1508
|
+
const normalizedImportPath = cleanImportPath
|
|
1509
|
+
.replace(/\\/g, "/")
|
|
1510
|
+
.replace(/^\.\//, "");
|
|
1511
|
+
const wasmComponentName = normalizedImportPath || cleanImportPath;
|
|
1512
|
+
const wasmFileName = basename(cleanImportPath);
|
|
1513
|
+
if (!allImports[wasmComponentName]) {
|
|
1514
|
+
allImports[wasmComponentName] = new Set();
|
|
1515
|
+
}
|
|
1516
|
+
for (const occurrence of occurrences) {
|
|
1517
|
+
allImports[wasmComponentName].add(occurrence);
|
|
1518
|
+
}
|
|
1519
|
+
// The import path is the file's location within the project, which is what a
|
|
1520
|
+
// purl subpath means. It was previously passed as a `path` qualifier, which
|
|
1521
|
+
// the generic type does not allow (it permits only checksum, download_url,
|
|
1522
|
+
// repository_url and vcs_url) — that threw out of createBom for any project
|
|
1523
|
+
// with a wasm import.
|
|
1524
|
+
const wasmPurl = tryBuildPurl({
|
|
1525
|
+
type: "generic",
|
|
1526
|
+
name: wasmFileName,
|
|
1527
|
+
subpath: normalizedImportPath
|
|
1528
|
+
? normalizedImportPath.replace(/^\/+/, "")
|
|
1529
|
+
: null,
|
|
1530
|
+
});
|
|
1531
|
+
if (existingPurls.has(wasmPurl)) {
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
const firstOccurrence = Array.from(occurrences)[0];
|
|
1535
|
+
const srcFile = firstOccurrence?.importedAs || importPath;
|
|
1536
|
+
pkgList.push({
|
|
1537
|
+
name: wasmComponentName,
|
|
1538
|
+
type: "library",
|
|
1539
|
+
...(wasmPurl ? { purl: wasmPurl, "bom-ref": wasmPurl } : {}),
|
|
1540
|
+
properties: [
|
|
1541
|
+
{
|
|
1542
|
+
name: "internal:SrcFile",
|
|
1543
|
+
value: srcFile,
|
|
1544
|
+
},
|
|
1545
|
+
],
|
|
1546
|
+
evidence: {
|
|
1547
|
+
identity: {
|
|
1548
|
+
field: "purl",
|
|
1549
|
+
confidence: 0.3,
|
|
1550
|
+
methods: [
|
|
1551
|
+
{
|
|
1552
|
+
technique: "filename",
|
|
1553
|
+
confidence: 0.3,
|
|
1554
|
+
value: srcFile,
|
|
1555
|
+
},
|
|
1556
|
+
],
|
|
1557
|
+
},
|
|
1558
|
+
},
|
|
1559
|
+
});
|
|
1560
|
+
existingPurls.add(wasmPurl);
|
|
1561
|
+
}
|
|
1562
|
+
return pkgList;
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
/**
|
|
1566
|
+
* Function to create bom string for caxa SEA binaries
|
|
1567
|
+
*
|
|
1568
|
+
* @param {string} path to the project
|
|
1569
|
+
* @param {Object} options Parse options from the cli
|
|
1570
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
1571
|
+
*/
|
|
1572
|
+
export async function createCaxaBom(path, options) {
|
|
1573
|
+
let pkgList = [];
|
|
1574
|
+
let allDependencies = [];
|
|
1575
|
+
let finalParentComponent;
|
|
1576
|
+
const caxaMetadataFiles = getAllFiles(
|
|
1577
|
+
path,
|
|
1578
|
+
`${options.multiProject ? "**/" : ""}*metadata.json`,
|
|
1579
|
+
{ ...options, includeDot: true },
|
|
1580
|
+
);
|
|
1581
|
+
for (const mfile of caxaMetadataFiles) {
|
|
1582
|
+
if (DEBUG_MODE) {
|
|
1583
|
+
console.log(`Parsing ${mfile}`);
|
|
1584
|
+
}
|
|
1585
|
+
const { parentComponent, components, dependencies } =
|
|
1586
|
+
await parseCaxaMetadata(mfile);
|
|
1587
|
+
if (parentComponent) {
|
|
1588
|
+
if (!finalParentComponent) {
|
|
1589
|
+
finalParentComponent = parentComponent;
|
|
1590
|
+
if (!finalParentComponent.type) {
|
|
1591
|
+
finalParentComponent.type = "application";
|
|
1592
|
+
}
|
|
1593
|
+
} else {
|
|
1594
|
+
finalParentComponent.components = finalParentComponent.components || [];
|
|
1595
|
+
finalParentComponent.components.push(parentComponent);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
if (components?.length) {
|
|
1599
|
+
pkgList = pkgList.concat(components);
|
|
1600
|
+
}
|
|
1601
|
+
if (dependencies?.length) {
|
|
1602
|
+
allDependencies = mergeDependencies(
|
|
1603
|
+
allDependencies,
|
|
1604
|
+
dependencies,
|
|
1605
|
+
finalParentComponent,
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return buildBomNSData(options, pkgList, "npm", {
|
|
1610
|
+
src: path,
|
|
1611
|
+
filename: caxaMetadataFiles.join(", "),
|
|
1612
|
+
dependencies: allDependencies,
|
|
1613
|
+
parentComponent: finalParentComponent,
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* Function to create BOM for VS Code / IDE extensions.
|
|
1619
|
+
* Supports two modes:
|
|
1620
|
+
* 1. Directory scan: Discovers `.vsix` files and installed extension directories
|
|
1621
|
+
* 2. IDE discovery: Automatically finds extensions installed by known IDEs
|
|
1622
|
+
*
|
|
1623
|
+
* @param {string} path to the project or directory to scan
|
|
1624
|
+
* @param {Object} options Parse options from the cli
|
|
1625
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
1626
|
+
*/
|
|
1627
|
+
export async function createVscodeExtensionBom(path, options) {
|
|
1628
|
+
let pkgList = [];
|
|
1629
|
+
let dependencies = [];
|
|
1630
|
+
const tempDirs = [];
|
|
1631
|
+
const shouldDiscoverInstalledIdeExtensions = hasExplicitProjectTypeSelection(
|
|
1632
|
+
options,
|
|
1633
|
+
"vscode-extension",
|
|
1634
|
+
);
|
|
1635
|
+
|
|
1636
|
+
// Mode 1: Scan for .vsix files in the given directory, or treat the input
|
|
1637
|
+
// path as a single .vsix file.
|
|
1638
|
+
let vsixFiles = [];
|
|
1639
|
+
if (path.endsWith(".vsix")) {
|
|
1640
|
+
vsixFiles = [resolve(path)];
|
|
1641
|
+
} else {
|
|
1642
|
+
vsixFiles = getAllFiles(
|
|
1643
|
+
path,
|
|
1644
|
+
`${options.multiProject ? "**/" : ""}*.vsix`,
|
|
1645
|
+
options,
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
if (vsixFiles.length) {
|
|
1649
|
+
if (DEBUG_MODE) {
|
|
1650
|
+
console.log(`Found ${vsixFiles.length} .vsix file(s) to parse`);
|
|
1651
|
+
}
|
|
1652
|
+
for (const f of vsixFiles) {
|
|
1653
|
+
if (DEBUG_MODE) {
|
|
1654
|
+
console.log(`Parsing ${f}`);
|
|
1655
|
+
}
|
|
1656
|
+
// Get the extension component metadata
|
|
1657
|
+
const component = await parseVsixFile(f);
|
|
1658
|
+
if (component) {
|
|
1659
|
+
pkgList.push(component);
|
|
1660
|
+
}
|
|
1661
|
+
// Extract the vsix to a temp dir and run deep analysis
|
|
1662
|
+
const extractedDir = await extractVsixToTempDir(f);
|
|
1663
|
+
if (extractedDir) {
|
|
1664
|
+
tempDirs.push(extractedDir);
|
|
1665
|
+
const deepResult = await analyzeExtensionDir(extractedDir, options);
|
|
1666
|
+
if (deepResult.pkgList.length) {
|
|
1667
|
+
pkgList = pkgList.concat(deepResult.pkgList);
|
|
1668
|
+
}
|
|
1669
|
+
if (deepResult.dependencies.length) {
|
|
1670
|
+
dependencies = mergeDependencies(
|
|
1671
|
+
dependencies,
|
|
1672
|
+
deepResult.dependencies,
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// Mode 2: Auto-discover extensions from known IDE locations
|
|
1680
|
+
if (shouldDiscoverInstalledIdeExtensions) {
|
|
1681
|
+
const ideDirs = discoverIdeExtensionDirs();
|
|
1682
|
+
if (ideDirs.length) {
|
|
1683
|
+
if (DEBUG_MODE) {
|
|
1684
|
+
console.log(
|
|
1685
|
+
`Discovered IDE extension directories: ${ideDirs.map((d) => `${d.name}: ${d.dir}`).join(", ")}`,
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
const ideExtensions = collectInstalledExtensions(ideDirs);
|
|
1689
|
+
if (ideExtensions.length) {
|
|
1690
|
+
if (DEBUG_MODE) {
|
|
1691
|
+
console.log(
|
|
1692
|
+
`Found ${ideExtensions.length} IDE extension(s) from ${ideDirs.length} IDE location(s)`,
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
pkgList = pkgList.concat(ideExtensions);
|
|
1696
|
+
// Deep analysis for IDE extension directories
|
|
1697
|
+
for (const ideDir of ideDirs) {
|
|
1698
|
+
await analyzeInstalledExtensionDirs(
|
|
1699
|
+
ideDir.dir,
|
|
1700
|
+
options,
|
|
1701
|
+
pkgList,
|
|
1702
|
+
dependencies,
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Clean up temp directories from vsix extraction
|
|
1710
|
+
for (const td of tempDirs) {
|
|
1711
|
+
cleanupTempDir(td);
|
|
1712
|
+
}
|
|
1713
|
+
pkgList = trimComponents(pkgList);
|
|
1714
|
+
return buildBomNSData(options, pkgList, VSCODE_EXTENSION_PURL_TYPE, {
|
|
1715
|
+
src: path,
|
|
1716
|
+
filename: vsixFiles.join(", "),
|
|
1717
|
+
nsMapping: {},
|
|
1718
|
+
dependencies,
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* Function to create BOM for Electron ASAR archives.
|
|
1724
|
+
*
|
|
1725
|
+
* @param {string} path to a single archive or a directory to scan
|
|
1726
|
+
* @param {Object} options Parse options from the cli
|
|
1727
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
1728
|
+
*/
|
|
1729
|
+
export async function createAsarBom(path, options) {
|
|
1730
|
+
let pkgList = [];
|
|
1731
|
+
let dependencies = [];
|
|
1732
|
+
let parentComponent = {};
|
|
1733
|
+
const tempDirs = [];
|
|
1734
|
+
const processedArchives = new Set();
|
|
1735
|
+
const maxNestedAsarDepth = 4;
|
|
1736
|
+
const explicitAsarPath = path.endsWith(".asar") ? resolve(path) : undefined;
|
|
1737
|
+
let asarFiles = explicitAsarPath
|
|
1738
|
+
? [explicitAsarPath]
|
|
1739
|
+
: getAllFiles(path, `${options.multiProject ? "**/" : ""}*.asar`, options);
|
|
1740
|
+
|
|
1741
|
+
if (!explicitAsarPath) {
|
|
1742
|
+
asarFiles = asarFiles.filter((f) => isAsarArchiveSync(f));
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
const aggregateArchiveResults = (
|
|
1746
|
+
archiveAnalysis,
|
|
1747
|
+
isPrimaryArchive = false,
|
|
1748
|
+
) => {
|
|
1749
|
+
if (
|
|
1750
|
+
archiveAnalysis.parentComponent &&
|
|
1751
|
+
Object.keys(archiveAnalysis.parentComponent).length
|
|
1752
|
+
) {
|
|
1753
|
+
if (isPrimaryArchive && !Object.keys(parentComponent).length) {
|
|
1754
|
+
parentComponent = archiveAnalysis.parentComponent;
|
|
1755
|
+
} else {
|
|
1756
|
+
pkgList.push(archiveAnalysis.parentComponent);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
if (archiveAnalysis.components?.length) {
|
|
1760
|
+
pkgList = pkgList.concat(archiveAnalysis.components);
|
|
1761
|
+
}
|
|
1762
|
+
if (archiveAnalysis.dependencies?.length) {
|
|
1763
|
+
dependencies = mergeDependencies(
|
|
1764
|
+
dependencies,
|
|
1765
|
+
archiveAnalysis.dependencies,
|
|
1766
|
+
parentComponent,
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
|
|
1771
|
+
const analyzeExtractedArchive = async (
|
|
1772
|
+
extractedDir,
|
|
1773
|
+
archiveAnalysis,
|
|
1774
|
+
archiveIdentityPath,
|
|
1775
|
+
) => {
|
|
1776
|
+
if (!archiveAnalysis.packageManifestPaths?.length) {
|
|
1777
|
+
return undefined;
|
|
1778
|
+
}
|
|
1779
|
+
const nodeBomOptions = {
|
|
1780
|
+
...options,
|
|
1781
|
+
installDeps: false,
|
|
1782
|
+
multiProject: true,
|
|
1783
|
+
noBabel: false,
|
|
1784
|
+
path: extractedDir,
|
|
1785
|
+
projectType: ["js"],
|
|
1786
|
+
};
|
|
1787
|
+
const nodeBomData = await createNodejsBom(extractedDir, nodeBomOptions);
|
|
1788
|
+
if (nodeBomData?.bomJson?.components?.length) {
|
|
1789
|
+
rewriteExtractedArchivePaths(
|
|
1790
|
+
nodeBomData.bomJson.components,
|
|
1791
|
+
extractedDir,
|
|
1792
|
+
archiveIdentityPath,
|
|
1793
|
+
);
|
|
1794
|
+
pkgList = pkgList.concat(nodeBomData.bomJson.components);
|
|
1795
|
+
}
|
|
1796
|
+
if (nodeBomData?.bomJson?.dependencies?.length) {
|
|
1797
|
+
dependencies = mergeDependencies(
|
|
1798
|
+
dependencies,
|
|
1799
|
+
nodeBomData.bomJson.dependencies,
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
if (
|
|
1803
|
+
archiveAnalysis.parentComponent?.["bom-ref"] &&
|
|
1804
|
+
nodeBomData?.parentComponent?.["bom-ref"] &&
|
|
1805
|
+
archiveAnalysis.parentComponent["bom-ref"] !==
|
|
1806
|
+
nodeBomData.parentComponent["bom-ref"]
|
|
1807
|
+
) {
|
|
1808
|
+
rewriteExtractedArchivePaths(
|
|
1809
|
+
nodeBomData.parentComponent,
|
|
1810
|
+
extractedDir,
|
|
1811
|
+
archiveIdentityPath,
|
|
1812
|
+
);
|
|
1813
|
+
dependencies = mergeDependencies(dependencies, [
|
|
1814
|
+
{
|
|
1815
|
+
ref: archiveAnalysis.parentComponent["bom-ref"],
|
|
1816
|
+
dependsOn: [nodeBomData.parentComponent["bom-ref"]],
|
|
1817
|
+
},
|
|
1818
|
+
]);
|
|
1819
|
+
}
|
|
1820
|
+
return nodeBomData;
|
|
1821
|
+
};
|
|
1822
|
+
|
|
1823
|
+
const processAsarArchive = async (
|
|
1824
|
+
archivePath,
|
|
1825
|
+
archiveIdentityPath,
|
|
1826
|
+
isPrimaryArchive = false,
|
|
1827
|
+
depth = 0,
|
|
1828
|
+
) => {
|
|
1829
|
+
const processedKey = archiveIdentityPath;
|
|
1830
|
+
if (processedArchives.has(processedKey)) {
|
|
1831
|
+
return undefined;
|
|
1832
|
+
}
|
|
1833
|
+
processedArchives.add(processedKey);
|
|
1834
|
+
const archiveAnalysis = await parseAsarArchive(archivePath, {
|
|
1835
|
+
...options,
|
|
1836
|
+
asarVirtualPath: archiveIdentityPath,
|
|
1837
|
+
});
|
|
1838
|
+
aggregateArchiveResults(archiveAnalysis, isPrimaryArchive);
|
|
1839
|
+
const shouldExtract =
|
|
1840
|
+
archiveAnalysis.packageManifestPaths?.length ||
|
|
1841
|
+
(depth < maxNestedAsarDepth &&
|
|
1842
|
+
archiveAnalysis.summary?.nestedArchiveCount > 0);
|
|
1843
|
+
if (!shouldExtract) {
|
|
1844
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
1845
|
+
}
|
|
1846
|
+
const extractedDir = await extractAsarToTempDir(archivePath);
|
|
1847
|
+
if (!extractedDir) {
|
|
1848
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
1849
|
+
}
|
|
1850
|
+
tempDirs.push(extractedDir);
|
|
1851
|
+
await analyzeExtractedArchive(
|
|
1852
|
+
extractedDir,
|
|
1853
|
+
archiveAnalysis,
|
|
1854
|
+
archiveIdentityPath,
|
|
1855
|
+
);
|
|
1856
|
+
if (depth < maxNestedAsarDepth) {
|
|
1857
|
+
const nestedAsarFiles = getAllFiles(extractedDir, "**/*.asar", options);
|
|
1858
|
+
for (const nestedArchivePath of nestedAsarFiles) {
|
|
1859
|
+
const relativeNestedArchivePath = relative(
|
|
1860
|
+
extractedDir,
|
|
1861
|
+
nestedArchivePath,
|
|
1862
|
+
).replaceAll("\\", "/");
|
|
1863
|
+
if (
|
|
1864
|
+
!relativeNestedArchivePath ||
|
|
1865
|
+
relativeNestedArchivePath.startsWith("..")
|
|
1866
|
+
) {
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
const nestedArchiveIdentityPath = `${archiveIdentityPath}#/${relativeNestedArchivePath}`;
|
|
1870
|
+
let nestedParentRef;
|
|
1871
|
+
try {
|
|
1872
|
+
nestedParentRef = await processAsarArchive(
|
|
1873
|
+
nestedArchivePath,
|
|
1874
|
+
nestedArchiveIdentityPath,
|
|
1875
|
+
false,
|
|
1876
|
+
depth + 1,
|
|
1877
|
+
);
|
|
1878
|
+
} catch (err) {
|
|
1879
|
+
if (options.failOnError) {
|
|
1880
|
+
console.error(
|
|
1881
|
+
`Error processing nested ASAR archive ${nestedArchivePath}: ${err.message}`,
|
|
1882
|
+
);
|
|
1883
|
+
process.exit(1);
|
|
1884
|
+
}
|
|
1885
|
+
console.warn(
|
|
1886
|
+
`Skipping nested ASAR archive ${nestedArchivePath}: ${err.message}`,
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
if (
|
|
1890
|
+
archiveAnalysis.parentComponent?.["bom-ref"] &&
|
|
1891
|
+
nestedParentRef &&
|
|
1892
|
+
archiveAnalysis.parentComponent["bom-ref"] !== nestedParentRef
|
|
1893
|
+
) {
|
|
1894
|
+
dependencies = mergeDependencies(dependencies, [
|
|
1895
|
+
{
|
|
1896
|
+
ref: archiveAnalysis.parentComponent["bom-ref"],
|
|
1897
|
+
dependsOn: [nestedParentRef],
|
|
1898
|
+
},
|
|
1899
|
+
]);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
return archiveAnalysis.parentComponent?.["bom-ref"];
|
|
1904
|
+
};
|
|
1905
|
+
try {
|
|
1906
|
+
for (const archivePath of asarFiles) {
|
|
1907
|
+
const isPrimaryArchive =
|
|
1908
|
+
explicitAsarPath && resolve(archivePath) === explicitAsarPath;
|
|
1909
|
+
try {
|
|
1910
|
+
await processAsarArchive(
|
|
1911
|
+
archivePath,
|
|
1912
|
+
resolve(archivePath),
|
|
1913
|
+
isPrimaryArchive,
|
|
1914
|
+
0,
|
|
1915
|
+
);
|
|
1916
|
+
} catch (err) {
|
|
1917
|
+
if (options.failOnError) {
|
|
1918
|
+
console.error(
|
|
1919
|
+
`Error processing ASAR archive ${archivePath}: ${err.message}`,
|
|
1920
|
+
);
|
|
1921
|
+
process.exit(1);
|
|
1922
|
+
}
|
|
1923
|
+
console.warn(`Skipping ASAR archive ${archivePath}: ${err.message}`);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
} finally {
|
|
1927
|
+
for (const tempDir of tempDirs) {
|
|
1928
|
+
cleanupAsarTempDir(tempDir);
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
pkgList = trimComponents(pkgList);
|
|
1932
|
+
return buildBomNSData(options, pkgList, "asar", {
|
|
1933
|
+
src: path,
|
|
1934
|
+
filename: asarFiles.join(", "),
|
|
1935
|
+
nsMapping: {},
|
|
1936
|
+
dependencies,
|
|
1937
|
+
parentComponent,
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
/**
|
|
1942
|
+
* Function to create BOM for installed Chrome and Chromium-based browser extensions.
|
|
1943
|
+
*
|
|
1944
|
+
* @param {string} path to the project path or a directly provided extension path
|
|
1945
|
+
* @param {Object} options Parse options from the cli
|
|
1946
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
1947
|
+
*/
|
|
1948
|
+
export async function createChromeExtensionBom(path, options) {
|
|
1949
|
+
let dependencies = [];
|
|
1950
|
+
let sourcePaths = [];
|
|
1951
|
+
const directResult = collectChromeExtensionsFromPath(path);
|
|
1952
|
+
const shouldDiscoverInstalledChromeExtensions =
|
|
1953
|
+
hasExplicitProjectTypeSelection(options, "chrome-extension");
|
|
1954
|
+
let pkgList = directResult.components || [];
|
|
1955
|
+
if (directResult.extensionDirs?.length) {
|
|
1956
|
+
sourcePaths = directResult.extensionDirs.slice();
|
|
1957
|
+
for (const extDir of directResult.extensionDirs) {
|
|
1958
|
+
const deepResult = await analyzeExtensionDir(extDir, options);
|
|
1959
|
+
if (deepResult.pkgList.length) {
|
|
1960
|
+
pkgList = pkgList.concat(deepResult.pkgList);
|
|
1961
|
+
}
|
|
1962
|
+
if (deepResult.dependencies.length) {
|
|
1963
|
+
dependencies = mergeDependencies(dependencies, deepResult.dependencies);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
if (pkgList.length && DEBUG_MODE) {
|
|
1968
|
+
thoughtLog(
|
|
1969
|
+
`Found ${pkgList.length} component(s) from direct Chrome extension path scan`,
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
if (shouldDiscoverInstalledChromeExtensions) {
|
|
1973
|
+
const chromeDirs = discoverChromiumExtensionDirs();
|
|
1974
|
+
if (chromeDirs.length) {
|
|
1975
|
+
if (DEBUG_MODE) {
|
|
1976
|
+
thoughtLog(
|
|
1977
|
+
`Discovered Chromium extension directories: ${chromeDirs.map((d) => `${d.browser} (${d.channel}): ${d.dir}`).join(", ")}`,
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
if (!pkgList.length) {
|
|
1981
|
+
pkgList = collectInstalledChromeExtensions(chromeDirs);
|
|
1982
|
+
sourcePaths = chromeDirs.map((d) => d.dir);
|
|
1983
|
+
}
|
|
1984
|
+
if (DEBUG_MODE && pkgList.length && !directResult.components?.length) {
|
|
1985
|
+
thoughtLog(
|
|
1986
|
+
`Found ${pkgList.length} Chrome/Chromium extension(s) from ${chromeDirs.length} browser location(s)`,
|
|
1987
|
+
);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
pkgList = trimComponents(pkgList);
|
|
1992
|
+
return buildBomNSData(options, pkgList, CHROME_EXTENSION_PURL_TYPE, {
|
|
1993
|
+
src: path,
|
|
1994
|
+
filename: sourcePaths.join(", "),
|
|
1995
|
+
nsMapping: {},
|
|
1996
|
+
dependencies,
|
|
1997
|
+
});
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
/**
|
|
2001
|
+
* Analyze an extracted extension directory for bundled dependencies.
|
|
2002
|
+
* Looks for npm lock files, node_modules, package.json files, minified JS,
|
|
2003
|
+
* and runs the babel-based analyzer on the source.
|
|
2004
|
+
*
|
|
2005
|
+
* @param {string} extDir Path to the extracted extension directory
|
|
2006
|
+
* @param {Object} options CLI options
|
|
2007
|
+
* @returns {Promise<{pkgList: Object[], dependencies: Object[]}>}
|
|
2008
|
+
*/
|
|
2009
|
+
export async function analyzeExtensionDir(extDir, options) {
|
|
2010
|
+
const pkgList = [];
|
|
2011
|
+
let dependencies = [];
|
|
2012
|
+
// Check if the extension directory contains node.js project artifacts
|
|
2013
|
+
const hasPackageJson = safeExistsSync(join(extDir, "package.json"));
|
|
2014
|
+
const hasNodeModules = safeExistsSync(join(extDir, "node_modules"));
|
|
2015
|
+
const hasLockFile =
|
|
2016
|
+
safeExistsSync(join(extDir, "package-lock.json")) ||
|
|
2017
|
+
safeExistsSync(join(extDir, "yarn.lock")) ||
|
|
2018
|
+
safeExistsSync(join(extDir, "pnpm-lock.yaml"));
|
|
2019
|
+
|
|
2020
|
+
// If there are lock files or node_modules, run the full Node.js BOM generator
|
|
2021
|
+
if (hasPackageJson && (hasLockFile || hasNodeModules)) {
|
|
2022
|
+
if (DEBUG_MODE) {
|
|
2023
|
+
console.log(
|
|
2024
|
+
`Running Node.js BOM analysis on extension directory: ${extDir}`,
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
const nodeBomOptions = {
|
|
2028
|
+
...options,
|
|
2029
|
+
path: extDir,
|
|
2030
|
+
multiProject: true,
|
|
2031
|
+
installDeps: false,
|
|
2032
|
+
noBabel: false,
|
|
2033
|
+
projectType: ["js"],
|
|
2034
|
+
};
|
|
2035
|
+
const bomData = await createNodejsBom(extDir, nodeBomOptions);
|
|
2036
|
+
if (bomData?.bomJson?.components?.length) {
|
|
2037
|
+
for (const comp of bomData.bomJson.components) {
|
|
2038
|
+
pkgList.push(comp);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
if (bomData?.bomJson?.dependencies?.length) {
|
|
2042
|
+
dependencies = mergeDependencies(
|
|
2043
|
+
dependencies,
|
|
2044
|
+
bomData.bomJson.dependencies,
|
|
2045
|
+
);
|
|
2046
|
+
}
|
|
2047
|
+
return { pkgList, dependencies };
|
|
2048
|
+
}
|
|
2049
|
+
return { pkgList, dependencies };
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
/**
|
|
2053
|
+
* Run deep analysis on installed extension subdirectories within a parent
|
|
2054
|
+
* extensions directory. Each subdirectory represents an installed extension.
|
|
2055
|
+
*
|
|
2056
|
+
* @param {string} extensionsDir Parent directory containing extension subdirs
|
|
2057
|
+
* @param {Object} options CLI options
|
|
2058
|
+
* @param {Object[]} pkgList Mutable array to push discovered components into
|
|
2059
|
+
* @param {Object[]} dependencies Mutable array to merge dependencies into
|
|
2060
|
+
*/
|
|
2061
|
+
export async function analyzeInstalledExtensionDirs(
|
|
2062
|
+
extensionsDir,
|
|
2063
|
+
options,
|
|
2064
|
+
pkgList,
|
|
2065
|
+
dependencies,
|
|
2066
|
+
) {
|
|
2067
|
+
let entries;
|
|
2068
|
+
try {
|
|
2069
|
+
entries = readdirSync(extensionsDir, { withFileTypes: true });
|
|
2070
|
+
} catch (_e) {
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
for (const entry of entries) {
|
|
2074
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
2075
|
+
continue;
|
|
2076
|
+
}
|
|
2077
|
+
const extDir = join(extensionsDir, entry.name);
|
|
2078
|
+
const deepResult = await analyzeExtensionDir(extDir, options);
|
|
2079
|
+
if (deepResult.pkgList.length) {
|
|
2080
|
+
pkgList.push(...deepResult.pkgList);
|
|
2081
|
+
}
|
|
2082
|
+
if (deepResult.dependencies.length) {
|
|
2083
|
+
const merged = mergeDependencies(dependencies, deepResult.dependencies);
|
|
2084
|
+
dependencies.splice(0, dependencies.length, ...merged);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|