@cdxgen/cdxgen 0.0.1 → 13.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +921 -87
- package/bin/audit.js +239 -0
- package/bin/cdxgen.js +2335 -0
- package/bin/convert.js +220 -0
- package/bin/evinse.js +353 -0
- package/bin/hbom.js +504 -0
- package/bin/repl.js +2224 -0
- package/bin/sign.js +195 -0
- package/bin/tracebom.js +303 -0
- package/bin/validate.js +268 -0
- package/bin/verify.js +189 -0
- package/data/README.md +307 -0
- package/data/bom-1.4.schema.json +1605 -0
- package/data/bom-1.5.schema.json +3660 -0
- package/data/bom-1.6.schema.json +5511 -0
- package/data/bom-1.7.schema.json +6700 -0
- package/data/cbomosdb-queries.json +68 -0
- package/data/component-tags.json +339 -0
- package/data/container-knowledge-index.json +125 -0
- package/data/cosdb-queries.json +17 -0
- package/data/crypto-oid.json +1998 -0
- package/data/cryptography-defs.json +3793 -0
- package/data/cryptography-defs.schema.json +576 -0
- package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
- package/data/frameworks-list.json +219 -0
- package/data/glibc-stdlib.json +117 -0
- package/data/gtfobins-index.json +6296 -0
- package/data/helpers/init.gradle +71 -0
- package/data/jsf-0.82.schema.json +211 -0
- package/data/known-licenses.json +82 -0
- package/data/lic-mapping.json +338 -0
- package/data/license-aliases.json +6624 -0
- package/data/license-db.json +39059 -0
- package/data/license-deprecations.json +302 -0
- package/data/lolbas-index.json +150 -0
- package/data/predictive-audit-allowlist.json +11 -0
- package/data/pypi-pkg-aliases.json +1199 -0
- package/data/python-stdlib.json +308 -0
- package/data/queries-darwin.json +193 -0
- package/data/queries-win.json +257 -0
- package/data/queries.json +268 -0
- package/data/ruby-known-modules.json +281 -0
- package/data/rules/ai-agent-governance.yaml +323 -0
- package/data/rules/ai-bom.yaml +268 -0
- package/data/rules/ai-oversight.yaml +186 -0
- package/data/rules/ai-provenance.yaml +92 -0
- package/data/rules/asar-archives.yaml +150 -0
- package/data/rules/cbom-security.yaml +165 -0
- package/data/rules/chrome-extensions.yaml +248 -0
- package/data/rules/ci-permissions.yaml +802 -0
- package/data/rules/container-risk.yaml +353 -0
- package/data/rules/dependency-sources.yaml +236 -0
- package/data/rules/golem-go.yaml +229 -0
- package/data/rules/hbom-compliance.yaml +325 -0
- package/data/rules/hbom-performance.yaml +307 -0
- package/data/rules/hbom-security.yaml +248 -0
- package/data/rules/host-topology.yaml +165 -0
- package/data/rules/mcp-servers.yaml +388 -0
- package/data/rules/obom-runtime.yaml +1776 -0
- package/data/rules/package-integrity.yaml +364 -0
- package/data/rules/rootfs-hardening.yaml +179 -0
- package/data/rules/vscode-extensions.yaml +237 -0
- package/data/spdx-export.schema.json +6794 -0
- package/data/spdx-license-list.json +7449 -0
- package/data/spdx-licenses.json +759 -0
- package/data/spdx.schema.json +819 -0
- package/data/templates/README.md +3 -0
- package/data/templates/asvs-4.0.3.cdx.json +2943 -0
- package/data/templates/asvs-5.0.cdx.json +3046 -0
- package/data/templates/bsimm-v13.cdx.json +1008 -0
- package/data/templates/masvs-2.0.0.cdx.json +225 -0
- package/data/templates/nist-ssdf-1.1.cdx.json +828 -0
- package/data/templates/pcissc-secure-slc-1.1.cdx.json +1027 -0
- package/data/templates/scvs-1.0.0.cdx.json +813 -0
- package/data/templates/ssaf-DRAFT-2023-11.cdx.json +127 -0
- package/data/vendor-alias.json +41967 -0
- package/data/wrapdb-releases.json +4227 -0
- package/index.cjs +15 -0
- package/lib/audit/index.js +2800 -0
- package/lib/audit/progress.js +114 -0
- package/lib/audit/reporters.js +937 -0
- package/lib/audit/scoring.js +560 -0
- package/lib/audit/targets.js +798 -0
- package/lib/cli/bomAssembly.js +1882 -0
- package/lib/cli/cliOptions.js +543 -0
- package/lib/cli/index.js +2759 -0
- package/lib/cli/jsBom.js +2087 -0
- package/lib/cli/jvmBom.js +1858 -0
- package/lib/cli/managedBom.js +2083 -0
- package/lib/cli/nativeBom.js +2125 -0
- package/lib/core/activity.js +1087 -0
- package/lib/core/env.js +719 -0
- package/lib/core/fs.js +1058 -0
- package/lib/core/httpClient.js +730 -0
- package/lib/core/logger.js +280 -0
- package/lib/core/parallel.js +323 -0
- package/lib/core/paths.js +70 -0
- package/lib/core/projectConfig.js +122 -0
- package/lib/core/propertySanitizer.js +174 -0
- package/lib/core/spdx.js +5 -0
- package/lib/core/state.js +59 -0
- package/lib/core/text.js +207 -0
- package/lib/core/ui.js +920 -0
- package/lib/core/urls.js +84 -0
- package/lib/ecosystems/asarutils.js +1636 -0
- package/lib/ecosystems/bunutils.js +402 -0
- package/lib/ecosystems/caxa.js +77 -0
- package/lib/ecosystems/chromextutils.js +1174 -0
- package/lib/ecosystems/cmakeResolver.js +587 -0
- package/lib/ecosystems/cppEvidence.js +305 -0
- package/lib/ecosystems/denoutils.js +1094 -0
- package/lib/ecosystems/dotnetutils.js +132 -0
- package/lib/ecosystems/ecosystems.js +2772 -0
- package/lib/ecosystems/golem.js +1807 -0
- package/lib/ecosystems/gradleutils.js +1048 -0
- package/lib/ecosystems/jsEvidence.js +317 -0
- package/lib/ecosystems/npmutils.js +586 -0
- package/lib/ecosystems/parsers-bazel.js +875 -0
- package/lib/ecosystems/parsers-dotnet.js +1430 -0
- package/lib/ecosystems/parsers-gleam.js +302 -0
- package/lib/ecosystems/parsers-go.js +818 -0
- package/lib/ecosystems/parsers-js.js +4222 -0
- package/lib/ecosystems/parsers-jvm.js +948 -0
- package/lib/ecosystems/parsers-misc.js +3191 -0
- package/lib/ecosystems/parsers-python.js +2070 -0
- package/lib/ecosystems/parsers-rust.js +1294 -0
- package/lib/ecosystems/parsers-zig.js +861 -0
- package/lib/ecosystems/pep770.js +341 -0
- package/lib/ecosystems/pylockutils.js +427 -0
- package/lib/ecosystems/pythonutils.js +1281 -0
- package/lib/ecosystems/registryProvenance.js +1003 -0
- package/lib/ecosystems/remote/huggingface.js +1343 -0
- package/lib/ecosystems/rubyutils.js +1629 -0
- package/lib/ecosystems/rusi.js +820 -0
- package/lib/ecosystems/sbtutils.js +972 -0
- package/lib/ecosystems/tea.js +607 -0
- package/lib/ecosystems/utils.js +583 -0
- package/lib/ecosystems/zigResolver.js +410 -0
- package/lib/evinser/db.js +145 -0
- package/lib/evinser/evinser.js +2362 -0
- package/lib/evinser/scalasem.js +65 -0
- package/lib/evinser/swiftsem.js +767 -0
- package/lib/helpers/annotationFormatter.js +84 -0
- package/lib/helpers/bomSigner.js +312 -0
- package/lib/helpers/exportUtils.js +160 -0
- package/lib/helpers/remote/dependency-track.js +117 -0
- package/lib/helpers/versutils.js +202 -0
- package/lib/helpers/vsixutils.js +1069 -0
- package/lib/inventory/agentFormulationParser.js +322 -0
- package/lib/inventory/aiCollector.js +2140 -0
- package/lib/inventory/aiInventory.js +414 -0
- package/lib/inventory/aiModelVariants.js +121 -0
- package/lib/inventory/aiOversightCollector.js +661 -0
- package/lib/inventory/aiProvenanceCollector.js +822 -0
- package/lib/inventory/analyzer.js +6089 -0
- package/lib/inventory/analyzerScope.js +807 -0
- package/lib/inventory/astWorker.js +20 -0
- package/lib/inventory/atomUtils.js +1092 -0
- package/lib/inventory/auditCategories.js +192 -0
- package/lib/inventory/bomUtils.js +369 -0
- package/lib/inventory/cacheCommands.js +182 -0
- package/lib/inventory/cacheDir.js +104 -0
- package/lib/inventory/cbomutils.js +590 -0
- package/lib/inventory/cdxrs.js +434 -0
- package/lib/inventory/ciParsers/azurePipelines.js +295 -0
- package/lib/inventory/ciParsers/circleCi.js +286 -0
- package/lib/inventory/ciParsers/common.js +24 -0
- package/lib/inventory/ciParsers/githubActions.js +2434 -0
- package/lib/inventory/ciParsers/gitlabCi.js +213 -0
- package/lib/inventory/ciParsers/jenkins.js +180 -0
- package/lib/inventory/citations.js +234 -0
- package/lib/inventory/communityAiConfigParser.js +756 -0
- package/lib/inventory/containerRisk.js +204 -0
- package/lib/inventory/cryptoAlgorithmFamily.js +258 -0
- package/lib/inventory/deps.js +807 -0
- package/lib/inventory/depsUtils.js +588 -0
- package/lib/inventory/display.js +2128 -0
- package/lib/inventory/dosai.js +601 -0
- package/lib/inventory/dosaiParsers.js +154 -0
- package/lib/inventory/dynamic.js +145 -0
- package/lib/inventory/envcontext.js +1793 -0
- package/lib/inventory/evidenceUtils.js +774 -0
- package/lib/inventory/fetchBatch.js +779 -0
- package/lib/inventory/fetchRate.js +255 -0
- package/lib/inventory/forgeEnricher.js +300 -0
- package/lib/inventory/formulationParsers.js +422 -0
- package/lib/inventory/gtfobins.js +339 -0
- package/lib/inventory/hbom.js +719 -0
- package/lib/inventory/hbomAnalysis.js +310 -0
- package/lib/inventory/hbomLoader.js +35 -0
- package/lib/inventory/hostTopology.js +835 -0
- package/lib/inventory/inventoryStats.js +100 -0
- package/lib/inventory/jsonLike.js +121 -0
- package/lib/inventory/licenseEnhancer.js +918 -0
- package/lib/inventory/lolbas.js +285 -0
- package/lib/inventory/mcp.js +253 -0
- package/lib/inventory/mcpConfigParser.js +683 -0
- package/lib/inventory/mcpDiscovery.js +112 -0
- package/lib/inventory/mcpPinning.js +209 -0
- package/lib/inventory/osPackageResolver.js +493 -0
- package/lib/inventory/osinfo.js +383 -0
- package/lib/inventory/osqueryTransform.js +274 -0
- package/lib/inventory/plugins.js +466 -0
- package/lib/inventory/protobom.js +231 -0
- package/lib/inventory/protobomLoader.js +43 -0
- package/lib/inventory/provenanceUtils.js +224 -0
- package/lib/inventory/purl.js +749 -0
- package/lib/inventory/rustFormulationParser.js +339 -0
- package/lib/inventory/sbomDocument.js +340 -0
- package/lib/inventory/source.js +1488 -0
- package/lib/inventory/spdx.js +361 -0
- package/lib/inventory/spdxExpression.js +512 -0
- package/lib/inventory/spdxUtils.js +107 -0
- package/lib/inventory/table.js +267 -0
- package/lib/inventory/traceRunner.js +439 -0
- package/lib/inventory/unicodeScan.js +147 -0
- package/lib/managers/binary.js +3459 -0
- package/lib/managers/containerutils.js +125 -0
- package/lib/managers/docker.js +2145 -0
- package/lib/managers/dockerConnection.js +162 -0
- package/lib/managers/oci.js +704 -0
- package/lib/managers/piptree.js +267 -0
- package/lib/parsers/cmakeCache.js +199 -0
- package/lib/parsers/gguf.js +492 -0
- package/lib/parsers/gitAiNotes.js +233 -0
- package/lib/parsers/gitmodules.js +239 -0
- package/lib/parsers/htmlExtract.js +610 -0
- package/lib/parsers/huggingfaceManifest.js +551 -0
- package/lib/parsers/huggingfaceUtils.js +506 -0
- package/lib/parsers/iri.js +618 -0
- package/lib/parsers/npmrc.js +99 -0
- package/lib/parsers/ollama.js +101 -0
- package/lib/parsers/xml.js +841 -0
- package/lib/server/openapi.yaml +433 -0
- package/lib/server/server.js +616 -0
- package/lib/stages/postgen/annotator.js +977 -0
- package/lib/stages/postgen/auditBom.js +468 -0
- package/lib/stages/postgen/postgen.js +1239 -0
- package/lib/stages/postgen/ruleEngine.js +731 -0
- package/lib/stages/postgen/sortBom.js +195 -0
- package/lib/stages/postgen/spdxConverter.js +796 -0
- package/lib/stages/postgen/specVersionCompat.js +703 -0
- package/lib/stages/pregen/envAudit.js +388 -0
- package/lib/stages/pregen/pregen.js +637 -0
- package/lib/third-party/README.md +9 -0
- package/lib/third-party/arborist/CHANGELOG.md +1109 -0
- package/lib/third-party/arborist/LICENSE.md +20 -0
- package/lib/third-party/arborist/README.md +315 -0
- package/lib/third-party/arborist/lib/arborist/index.js +258 -0
- package/lib/third-party/arborist/lib/arborist/load-actual.js +605 -0
- package/lib/third-party/arborist/lib/arborist/load-virtual.js +300 -0
- package/lib/third-party/arborist/lib/bin-links.js +120 -0
- package/lib/third-party/arborist/lib/calc-dep-flags.js +130 -0
- package/lib/third-party/arborist/lib/case-insensitive-map.js +51 -0
- package/lib/third-party/arborist/lib/consistent-resolve.js +42 -0
- package/lib/third-party/arborist/lib/debug.js +50 -0
- package/lib/third-party/arborist/lib/dep-valid.js +190 -0
- package/lib/third-party/arborist/lib/edge.js +417 -0
- package/lib/third-party/arborist/lib/from-path.js +36 -0
- package/lib/third-party/arborist/lib/gather-dep-set.js +43 -0
- package/lib/third-party/arborist/lib/index.js +22 -0
- package/lib/third-party/arborist/lib/inventory.js +138 -0
- package/lib/third-party/arborist/lib/link.js +169 -0
- package/lib/third-party/arborist/lib/node.js +1673 -0
- package/lib/third-party/arborist/lib/npm-extension.js +45 -0
- package/lib/third-party/arborist/lib/override-resolves.js +11 -0
- package/lib/third-party/arborist/lib/override-set.js +319 -0
- package/lib/third-party/arborist/lib/package-extensions.js +289 -0
- package/lib/third-party/arborist/lib/printable.js +210 -0
- package/lib/third-party/arborist/lib/proc-log.js +30 -0
- package/lib/third-party/arborist/lib/realpath.js +100 -0
- package/lib/third-party/arborist/lib/relpath.js +4 -0
- package/lib/third-party/arborist/lib/shrinkwrap.js +1291 -0
- package/lib/third-party/arborist/lib/spec-from-lock.js +34 -0
- package/lib/third-party/arborist/lib/tree-check.js +165 -0
- package/lib/third-party/arborist/lib/validate-npm-package-name.js +107 -0
- package/lib/third-party/arborist/lib/version-from-tgz.js +51 -0
- package/lib/third-party/arborist/lib/yarn-lock.js +397 -0
- package/lib/validator/bomValidator.js +985 -0
- package/lib/validator/complianceEngine.js +241 -0
- package/lib/validator/complianceRules.js +1680 -0
- package/lib/validator/generated/runtime.mjs +17 -0
- package/lib/validator/generated/validate-1.6.mjs +21258 -0
- package/lib/validator/generated/validate-1.7.mjs +24803 -0
- package/lib/validator/generated/validate-2.0.mjs +22931 -0
- package/lib/validator/index.js +246 -0
- package/lib/validator/reporters/annotations.js +142 -0
- package/lib/validator/reporters/console.js +160 -0
- package/lib/validator/reporters/index.js +41 -0
- package/lib/validator/reporters/json.js +37 -0
- package/lib/validator/reporters/sarif.js +184 -0
- package/package.json +209 -26
- package/types/bin/audit.d.ts +3 -0
- package/types/bin/audit.d.ts.map +1 -0
- package/types/bin/cdxgen.d.ts +3 -0
- package/types/bin/cdxgen.d.ts.map +1 -0
- package/types/bin/convert.d.ts +3 -0
- package/types/bin/convert.d.ts.map +1 -0
- package/types/bin/evinse.d.ts +3 -0
- package/types/bin/evinse.d.ts.map +1 -0
- package/types/bin/hbom.d.ts +3 -0
- package/types/bin/hbom.d.ts.map +1 -0
- package/types/bin/repl.d.ts +3 -0
- package/types/bin/repl.d.ts.map +1 -0
- package/types/bin/sign.d.ts +3 -0
- package/types/bin/sign.d.ts.map +1 -0
- package/types/bin/tracebom.d.ts +3 -0
- package/types/bin/tracebom.d.ts.map +1 -0
- package/types/bin/validate.d.ts +14 -0
- package/types/bin/validate.d.ts.map +1 -0
- package/types/bin/verify.d.ts +3 -0
- package/types/bin/verify.d.ts.map +1 -0
- package/types/lib/audit/index.d.ts +153 -0
- package/types/lib/audit/index.d.ts.map +1 -0
- package/types/lib/audit/progress.d.ts +31 -0
- package/types/lib/audit/progress.d.ts.map +1 -0
- package/types/lib/audit/reporters.d.ts +62 -0
- package/types/lib/audit/reporters.d.ts.map +1 -0
- package/types/lib/audit/scoring.d.ts +36 -0
- package/types/lib/audit/scoring.d.ts.map +1 -0
- package/types/lib/audit/targets.d.ts +77 -0
- package/types/lib/audit/targets.d.ts.map +1 -0
- package/types/lib/cli/bomAssembly.d.ts +329 -0
- package/types/lib/cli/bomAssembly.d.ts.map +1 -0
- package/types/lib/cli/cliOptions.d.ts +156 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts +107 -0
- package/types/lib/cli/index.d.ts.map +1 -0
- package/types/lib/cli/jsBom.d.ts +94 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -0
- package/types/lib/cli/jvmBom.d.ts +54 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -0
- package/types/lib/cli/managedBom.d.ts +105 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -0
- package/types/lib/cli/nativeBom.d.ts +143 -0
- package/types/lib/cli/nativeBom.d.ts.map +1 -0
- package/types/lib/core/activity.d.ts +178 -0
- package/types/lib/core/activity.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +192 -0
- package/types/lib/core/env.d.ts.map +1 -0
- package/types/lib/core/fs.d.ts +158 -0
- package/types/lib/core/fs.d.ts.map +1 -0
- package/types/lib/core/httpClient.d.ts +149 -0
- package/types/lib/core/httpClient.d.ts.map +1 -0
- package/types/lib/core/logger.d.ts +50 -0
- package/types/lib/core/logger.d.ts.map +1 -0
- package/types/lib/core/parallel.d.ts +95 -0
- package/types/lib/core/parallel.d.ts.map +1 -0
- package/types/lib/core/paths.d.ts +36 -0
- package/types/lib/core/paths.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +23 -0
- package/types/lib/core/projectConfig.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +23 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -0
- package/types/lib/core/spdx.d.ts +5 -0
- package/types/lib/core/spdx.d.ts.map +1 -0
- package/types/lib/core/state.d.ts +20 -0
- package/types/lib/core/state.d.ts.map +1 -0
- package/types/lib/core/text.d.ts +78 -0
- package/types/lib/core/text.d.ts.map +1 -0
- package/types/lib/core/ui.d.ts +87 -0
- package/types/lib/core/ui.d.ts.map +1 -0
- package/types/lib/core/urls.d.ts +43 -0
- package/types/lib/core/urls.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts +71 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -0
- package/types/lib/ecosystems/bunutils.d.ts +38 -0
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts +8 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -0
- package/types/lib/ecosystems/chromextutils.d.ts +97 -0
- package/types/lib/ecosystems/chromextutils.d.ts.map +1 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts +176 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +35 -0
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +76 -0
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -0
- package/types/lib/ecosystems/dotnetutils.d.ts +45 -0
- package/types/lib/ecosystems/dotnetutils.d.ts.map +1 -0
- package/types/lib/ecosystems/ecosystems.d.ts +279 -0
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -0
- package/types/lib/ecosystems/golem.d.ts +60 -0
- package/types/lib/ecosystems/golem.d.ts.map +1 -0
- package/types/lib/ecosystems/gradleutils.d.ts +139 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -0
- package/types/lib/ecosystems/jsEvidence.d.ts +17 -0
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +183 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-bazel.d.ts +36 -0
- package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +116 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-gleam.d.ts +33 -0
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-go.d.ts +92 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-js.d.ts +242 -0
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-jvm.d.ts +100 -0
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +450 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts +145 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +56 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-zig.d.ts +83 -0
- package/types/lib/ecosystems/parsers-zig.d.ts.map +1 -0
- package/types/lib/ecosystems/pep770.d.ts +38 -0
- package/types/lib/ecosystems/pep770.d.ts.map +1 -0
- package/types/lib/ecosystems/pylockutils.d.ts +68 -0
- package/types/lib/ecosystems/pylockutils.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +70 -0
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -0
- package/types/lib/ecosystems/registryProvenance.d.ts +26 -0
- package/types/lib/ecosystems/registryProvenance.d.ts.map +1 -0
- package/types/lib/ecosystems/remote/huggingface.d.ts +31 -0
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -0
- package/types/lib/ecosystems/rubyutils.d.ts +182 -0
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -0
- package/types/lib/ecosystems/rusi.d.ts +40 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -0
- package/types/lib/ecosystems/sbtutils.d.ts +179 -0
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -0
- package/types/lib/ecosystems/tea.d.ts +180 -0
- package/types/lib/ecosystems/tea.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +41 -0
- package/types/lib/ecosystems/utils.d.ts.map +1 -0
- package/types/lib/ecosystems/zigResolver.d.ts +70 -0
- package/types/lib/ecosystems/zigResolver.d.ts.map +1 -0
- package/types/lib/evinser/db.d.ts +17 -0
- package/types/lib/evinser/db.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +354 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -0
- package/types/lib/evinser/scalasem.d.ts +15 -0
- package/types/lib/evinser/scalasem.d.ts.map +1 -0
- package/types/lib/evinser/swiftsem.d.ts +103 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -0
- package/types/lib/helpers/annotationFormatter.d.ts +23 -0
- package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
- package/types/lib/helpers/bomSigner.d.ts +28 -0
- package/types/lib/helpers/bomSigner.d.ts.map +1 -0
- package/types/lib/helpers/exportUtils.d.ts +52 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +23 -0
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
- package/types/lib/helpers/versutils.d.ts +8 -0
- package/types/lib/helpers/versutils.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +130 -0
- package/types/lib/helpers/vsixutils.d.ts.map +1 -0
- package/types/lib/inventory/agentFormulationParser.d.ts +21 -0
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts +61 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -0
- package/types/lib/inventory/aiInventory.d.ts +103 -0
- package/types/lib/inventory/aiInventory.d.ts.map +1 -0
- package/types/lib/inventory/aiModelVariants.d.ts +31 -0
- package/types/lib/inventory/aiModelVariants.d.ts.map +1 -0
- package/types/lib/inventory/aiOversightCollector.d.ts +28 -0
- package/types/lib/inventory/aiOversightCollector.d.ts.map +1 -0
- package/types/lib/inventory/aiProvenanceCollector.d.ts +13 -0
- package/types/lib/inventory/aiProvenanceCollector.d.ts.map +1 -0
- package/types/lib/inventory/analyzer.d.ts +157 -0
- package/types/lib/inventory/analyzer.d.ts.map +1 -0
- package/types/lib/inventory/analyzerScope.d.ts +106 -0
- package/types/lib/inventory/analyzerScope.d.ts.map +1 -0
- package/types/lib/inventory/astWorker.d.ts +9 -0
- package/types/lib/inventory/astWorker.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +159 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -0
- package/types/lib/inventory/auditCategories.d.ts +80 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -0
- package/types/lib/inventory/bomUtils.d.ts +145 -0
- package/types/lib/inventory/bomUtils.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +28 -0
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -0
- package/types/lib/inventory/cacheDir.d.ts +55 -0
- package/types/lib/inventory/cacheDir.d.ts.map +1 -0
- package/types/lib/inventory/cbomutils.d.ts +37 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -0
- package/types/lib/inventory/cdxrs.d.ts +90 -0
- package/types/lib/inventory/cdxrs.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/azurePipelines.d.ts +27 -0
- package/types/lib/inventory/ciParsers/azurePipelines.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/circleCi.d.ts +27 -0
- package/types/lib/inventory/ciParsers/circleCi.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/common.d.ts +11 -0
- package/types/lib/inventory/ciParsers/common.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/githubActions.d.ts +38 -0
- package/types/lib/inventory/ciParsers/githubActions.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/gitlabCi.d.ts +26 -0
- package/types/lib/inventory/ciParsers/gitlabCi.d.ts.map +1 -0
- package/types/lib/inventory/ciParsers/jenkins.d.ts +26 -0
- package/types/lib/inventory/ciParsers/jenkins.d.ts.map +1 -0
- package/types/lib/inventory/citations.d.ts +90 -0
- package/types/lib/inventory/citations.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts +14 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -0
- package/types/lib/inventory/containerRisk.d.ts +20 -0
- package/types/lib/inventory/containerRisk.d.ts.map +1 -0
- package/types/lib/inventory/cryptoAlgorithmFamily.d.ts +37 -0
- package/types/lib/inventory/cryptoAlgorithmFamily.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +140 -0
- package/types/lib/inventory/deps.d.ts.map +1 -0
- package/types/lib/inventory/depsUtils.d.ts +74 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -0
- package/types/lib/inventory/display.d.ts +227 -0
- package/types/lib/inventory/display.d.ts.map +1 -0
- package/types/lib/inventory/dosai.d.ts +130 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -0
- package/types/lib/inventory/dosaiParsers.d.ts +59 -0
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -0
- package/types/lib/inventory/dynamic.d.ts +25 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -0
- package/types/lib/inventory/envcontext.d.ts +377 -0
- package/types/lib/inventory/envcontext.d.ts.map +1 -0
- package/types/lib/inventory/evidenceUtils.d.ts +74 -0
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -0
- package/types/lib/inventory/fetchBatch.d.ts +127 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -0
- package/types/lib/inventory/fetchRate.d.ts +118 -0
- package/types/lib/inventory/fetchRate.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts +31 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -0
- package/types/lib/inventory/formulationParsers.d.ts +50 -0
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -0
- package/types/lib/inventory/gtfobins.d.ts +27 -0
- package/types/lib/inventory/gtfobins.d.ts.map +1 -0
- package/types/lib/inventory/hbom.d.ts +58 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -0
- package/types/lib/inventory/hbomAnalysis.d.ts +56 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -0
- package/types/lib/inventory/hbomLoader.d.ts +7 -0
- package/types/lib/inventory/hbomLoader.d.ts.map +1 -0
- package/types/lib/inventory/hostTopology.d.ts +47 -0
- package/types/lib/inventory/hostTopology.d.ts.map +1 -0
- package/types/lib/inventory/inventoryStats.d.ts +42 -0
- package/types/lib/inventory/inventoryStats.d.ts.map +1 -0
- package/types/lib/inventory/jsonLike.d.ts +23 -0
- package/types/lib/inventory/jsonLike.d.ts.map +1 -0
- package/types/lib/inventory/licenseEnhancer.d.ts +70 -0
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -0
- package/types/lib/inventory/lolbas.d.ts +16 -0
- package/types/lib/inventory/lolbas.d.ts.map +1 -0
- package/types/lib/inventory/mcp.d.ts +29 -0
- package/types/lib/inventory/mcp.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts +20 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -0
- package/types/lib/inventory/mcpDiscovery.d.ts +33 -0
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -0
- package/types/lib/inventory/mcpPinning.d.ts +11 -0
- package/types/lib/inventory/mcpPinning.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +49 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -0
- package/types/lib/inventory/osinfo.d.ts +201 -0
- package/types/lib/inventory/osinfo.d.ts.map +1 -0
- package/types/lib/inventory/osqueryTransform.d.ts +84 -0
- package/types/lib/inventory/osqueryTransform.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts +58 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -0
- package/types/lib/inventory/protobom.d.ts +71 -0
- package/types/lib/inventory/protobom.d.ts.map +1 -0
- package/types/lib/inventory/protobomLoader.d.ts +17 -0
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -0
- package/types/lib/inventory/provenanceUtils.d.ts +98 -0
- package/types/lib/inventory/provenanceUtils.d.ts.map +1 -0
- package/types/lib/inventory/purl.d.ts +313 -0
- package/types/lib/inventory/purl.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts +17 -0
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -0
- package/types/lib/inventory/sbomDocument.d.ts +59 -0
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +163 -0
- package/types/lib/inventory/source.d.ts.map +1 -0
- package/types/lib/inventory/spdx.d.ts +66 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -0
- package/types/lib/inventory/spdxExpression.d.ts +58 -0
- package/types/lib/inventory/spdxExpression.d.ts.map +1 -0
- package/types/lib/inventory/spdxUtils.d.ts +12 -0
- package/types/lib/inventory/spdxUtils.d.ts.map +1 -0
- package/types/lib/inventory/table.d.ts +22 -0
- package/types/lib/inventory/table.d.ts.map +1 -0
- package/types/lib/inventory/traceRunner.d.ts +80 -0
- package/types/lib/inventory/traceRunner.d.ts.map +1 -0
- package/types/lib/inventory/unicodeScan.d.ts +46 -0
- package/types/lib/inventory/unicodeScan.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +147 -0
- package/types/lib/managers/binary.d.ts.map +1 -0
- package/types/lib/managers/containerutils.d.ts +30 -0
- package/types/lib/managers/containerutils.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts +157 -0
- package/types/lib/managers/docker.d.ts.map +1 -0
- package/types/lib/managers/dockerConnection.d.ts +42 -0
- package/types/lib/managers/dockerConnection.d.ts.map +1 -0
- package/types/lib/managers/oci.d.ts +21 -0
- package/types/lib/managers/oci.d.ts.map +1 -0
- package/types/lib/managers/piptree.d.ts +13 -0
- package/types/lib/managers/piptree.d.ts.map +1 -0
- package/types/lib/parsers/cmakeCache.d.ts +70 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -0
- package/types/lib/parsers/gguf.d.ts +39 -0
- package/types/lib/parsers/gguf.d.ts.map +1 -0
- package/types/lib/parsers/gitAiNotes.d.ts +15 -0
- package/types/lib/parsers/gitAiNotes.d.ts.map +1 -0
- package/types/lib/parsers/gitmodules.d.ts +88 -0
- package/types/lib/parsers/gitmodules.d.ts.map +1 -0
- package/types/lib/parsers/htmlExtract.d.ts +34 -0
- package/types/lib/parsers/htmlExtract.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceManifest.d.ts +153 -0
- package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +155 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -0
- package/types/lib/parsers/iri.d.ts +68 -0
- package/types/lib/parsers/iri.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +33 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -0
- package/types/lib/parsers/ollama.d.ts +22 -0
- package/types/lib/parsers/ollama.d.ts.map +1 -0
- package/types/lib/parsers/xml.d.ts +47 -0
- package/types/lib/parsers/xml.d.ts.map +1 -0
- package/types/lib/server/server.d.ts +51 -0
- package/types/lib/server/server.d.ts.map +1 -0
- package/types/lib/stages/postgen/annotator.d.ts +36 -0
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -0
- package/types/lib/stages/postgen/auditBom.d.ts +92 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +59 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -0
- package/types/lib/stages/postgen/ruleEngine.d.ts +18 -0
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -0
- package/types/lib/stages/postgen/sortBom.d.ts +34 -0
- package/types/lib/stages/postgen/sortBom.d.ts.map +1 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts +71 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -0
- package/types/lib/stages/pregen/envAudit.d.ts +14 -0
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -0
- package/types/lib/stages/pregen/pregen.d.ts +59 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/arborist/index.d.ts +47 -0
- package/types/lib/third-party/arborist/lib/arborist/index.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts +4 -0
- package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts +4 -0
- package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/bin-links.d.ts +18 -0
- package/types/lib/third-party/arborist/lib/bin-links.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts +9 -0
- package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/debug.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/debug.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/dep-valid.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/dep-valid.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/edge.d.ts +46 -0
- package/types/lib/third-party/arborist/lib/edge.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/from-path.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/from-path.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/index.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/index.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/inventory.d.ts +14 -0
- package/types/lib/third-party/arborist/lib/inventory.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/link.d.ts +18 -0
- package/types/lib/third-party/arborist/lib/link.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/node.d.ts +112 -0
- package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/npm-extension.d.ts +18 -0
- package/types/lib/third-party/arborist/lib/npm-extension.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/override-resolves.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/override-resolves.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/override-set.d.ts +27 -0
- package/types/lib/third-party/arborist/lib/override-set.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/package-extensions.d.ts +36 -0
- package/types/lib/third-party/arborist/lib/package-extensions.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/printable.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/printable.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/proc-log.d.ts +20 -0
- package/types/lib/third-party/arborist/lib/proc-log.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/realpath.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/realpath.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/relpath.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/relpath.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts +54 -0
- package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/tree-check.d.ts +3 -0
- package/types/lib/third-party/arborist/lib/tree-check.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts +13 -0
- package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts +6 -0
- package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts.map +1 -0
- package/types/lib/third-party/arborist/lib/yarn-lock.d.ts +35 -0
- package/types/lib/third-party/arborist/lib/yarn-lock.d.ts.map +1 -0
- package/types/lib/validator/bomValidator.d.ts +40 -0
- package/types/lib/validator/bomValidator.d.ts.map +1 -0
- package/types/lib/validator/complianceEngine.d.ts +72 -0
- package/types/lib/validator/complianceEngine.d.ts.map +1 -0
- package/types/lib/validator/complianceRules.d.ts +61 -0
- package/types/lib/validator/complianceRules.d.ts.map +1 -0
- package/types/lib/validator/index.d.ts +101 -0
- package/types/lib/validator/index.d.ts.map +1 -0
- package/types/lib/validator/reporters/annotations.d.ts +37 -0
- package/types/lib/validator/reporters/annotations.d.ts.map +1 -0
- package/types/lib/validator/reporters/console.d.ts +34 -0
- package/types/lib/validator/reporters/console.d.ts.map +1 -0
- package/types/lib/validator/reporters/index.d.ts +30 -0
- package/types/lib/validator/reporters/index.d.ts.map +1 -0
- package/types/lib/validator/reporters/json.d.ts +11 -0
- package/types/lib/validator/reporters/json.d.ts.map +1 -0
- package/types/lib/validator/reporters/sarif.d.ts +22 -0
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -0
- package/mod.d.ts +0 -7
- package/mod.js +0 -22
|
@@ -0,0 +1,1488 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { URL } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { Purl } from "@cdxgen/cdx-purl";
|
|
7
|
+
import { coerce, diff, prerelease } from "semver";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
cdxgenAgent,
|
|
11
|
+
createDryRunError,
|
|
12
|
+
DEBUG_MODE,
|
|
13
|
+
hasDangerousUnicode,
|
|
14
|
+
isDryRun,
|
|
15
|
+
isSecureMode,
|
|
16
|
+
readEnvironmentVariable,
|
|
17
|
+
recordActivity,
|
|
18
|
+
} from "../core/activity.js";
|
|
19
|
+
import {
|
|
20
|
+
getTmpDir,
|
|
21
|
+
safeMkdtempSync,
|
|
22
|
+
safeRmSync,
|
|
23
|
+
safeSpawnSync,
|
|
24
|
+
} from "../core/fs.js";
|
|
25
|
+
import { thoughtLog } from "../core/logger.js";
|
|
26
|
+
import { isValidDriveRoot, isWin } from "../core/paths.js";
|
|
27
|
+
import {
|
|
28
|
+
prefetchEnabled,
|
|
29
|
+
prefetchedResponse,
|
|
30
|
+
prefetchJson,
|
|
31
|
+
} from "./fetchBatch.js";
|
|
32
|
+
|
|
33
|
+
/** Raised when a caller omits a registry helper that the purl type requires. */
|
|
34
|
+
class MissingRegistryHelperError extends Error {}
|
|
35
|
+
|
|
36
|
+
/** Warning emitted for repository URLs resolved from untrusted package registry metadata. */
|
|
37
|
+
export const PURL_REGISTRY_LOOKUP_WARNING =
|
|
38
|
+
"Resolved repository URL from package registry metadata. This source can be inaccurate or malicious; review before trusting results.";
|
|
39
|
+
|
|
40
|
+
/** Purl types supported for source repository resolution. */
|
|
41
|
+
export const SUPPORTED_PURL_SOURCE_TYPES = [
|
|
42
|
+
"npm",
|
|
43
|
+
"pypi",
|
|
44
|
+
"gem",
|
|
45
|
+
"cargo",
|
|
46
|
+
"pub",
|
|
47
|
+
"github",
|
|
48
|
+
"bitbucket",
|
|
49
|
+
"maven",
|
|
50
|
+
"composer",
|
|
51
|
+
"generic",
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const MAX_MONOREPO_PACKAGE_JSON_FILES = 2000;
|
|
55
|
+
const MAX_MONOREPO_DIRECTORIES = 5000;
|
|
56
|
+
const MAX_RELEASE_NOTE_RESOLVES = 50;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Build a scoped npm package name.
|
|
60
|
+
*
|
|
61
|
+
* @param {string|undefined} namespace package namespace
|
|
62
|
+
* @param {string|undefined} name package name
|
|
63
|
+
* @returns {string|undefined} scoped package name
|
|
64
|
+
*/
|
|
65
|
+
function buildScopedNpmPackageName(namespace, name) {
|
|
66
|
+
if (!name) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (!namespace) {
|
|
70
|
+
return name;
|
|
71
|
+
}
|
|
72
|
+
return `${namespace.startsWith("@") ? namespace : `@${namespace}`}/${name}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Validate git ref names used as branch/tag values.
|
|
77
|
+
*
|
|
78
|
+
* @param {string|undefined} refName branch or tag name
|
|
79
|
+
* @returns {boolean} true if safe
|
|
80
|
+
*/
|
|
81
|
+
export function isSafeGitRefName(refName) {
|
|
82
|
+
if (!refName || typeof refName !== "string") {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (refName.startsWith("-")) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return /^[A-Za-z0-9._/@+-]+$/.test(refName);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function inferGitOperation(args = []) {
|
|
92
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
93
|
+
const arg = args[index];
|
|
94
|
+
if (arg === "-c" || arg === "--config-env") {
|
|
95
|
+
index += 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (typeof arg === "string" && arg.startsWith("-")) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
return arg || "command";
|
|
102
|
+
}
|
|
103
|
+
return "command";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Execute git with hardened defaults.
|
|
108
|
+
*
|
|
109
|
+
* @param {string[]} args git arguments
|
|
110
|
+
* @param {Object} options command options
|
|
111
|
+
* @param {string|undefined} options.cwd working directory
|
|
112
|
+
* @returns {Object} spawn result
|
|
113
|
+
*/
|
|
114
|
+
export function hardenedGitCommand(args, options = {}) {
|
|
115
|
+
const gitOperation = inferGitOperation(args);
|
|
116
|
+
const gitAllowProtocol = getGitAllowProtocol();
|
|
117
|
+
const envConfigs = {
|
|
118
|
+
GIT_CONFIG_COUNT: "2",
|
|
119
|
+
GIT_CONFIG_KEY_0: "core.fsmonitor",
|
|
120
|
+
GIT_CONFIG_VALUE_0: "false",
|
|
121
|
+
GIT_CONFIG_KEY_1: "safe.bareRepository",
|
|
122
|
+
GIT_CONFIG_VALUE_1: "explicit",
|
|
123
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
124
|
+
};
|
|
125
|
+
const env = isSecureMode
|
|
126
|
+
? {
|
|
127
|
+
...process.env,
|
|
128
|
+
...envConfigs,
|
|
129
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
130
|
+
GIT_CONFIG_GLOBAL: "/dev/null",
|
|
131
|
+
GIT_ALLOW_PROTOCOL: gitAllowProtocol,
|
|
132
|
+
}
|
|
133
|
+
: {
|
|
134
|
+
...process.env,
|
|
135
|
+
...envConfigs,
|
|
136
|
+
GIT_ALLOW_PROTOCOL: gitAllowProtocol,
|
|
137
|
+
};
|
|
138
|
+
return safeSpawnSync("git", args, {
|
|
139
|
+
cdxgenActivity: {
|
|
140
|
+
blockedReason: `Dry run mode blocks git ${gitOperation} operations.`,
|
|
141
|
+
gitOperation,
|
|
142
|
+
kind: `git-${gitOperation}`,
|
|
143
|
+
metadata: {
|
|
144
|
+
capability: "git-operation",
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
shell: false,
|
|
148
|
+
cwd: options.cwd,
|
|
149
|
+
env,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function normalizeTagName(tagName) {
|
|
154
|
+
if (!tagName || typeof tagName !== "string") {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
return tagName
|
|
158
|
+
.trim()
|
|
159
|
+
.replace(/^refs\/tags\//, "")
|
|
160
|
+
.replace(/\^\{\}$/, "");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function releaseTypeFromTags(currentTag, previousTag) {
|
|
164
|
+
const currentVersion = coerce(currentTag || "");
|
|
165
|
+
const previousVersion = coerce(previousTag || "");
|
|
166
|
+
if (!currentVersion || !previousVersion) {
|
|
167
|
+
return "internal";
|
|
168
|
+
}
|
|
169
|
+
if (prerelease(currentVersion.version)?.length) {
|
|
170
|
+
return "pre-release";
|
|
171
|
+
}
|
|
172
|
+
const versionDiff = diff(previousVersion.version, currentVersion.version);
|
|
173
|
+
if (versionDiff === "major") {
|
|
174
|
+
return "major";
|
|
175
|
+
}
|
|
176
|
+
if (versionDiff === "minor") {
|
|
177
|
+
return "minor";
|
|
178
|
+
}
|
|
179
|
+
if (versionDiff === "patch") {
|
|
180
|
+
return "patch";
|
|
181
|
+
}
|
|
182
|
+
return "internal";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function parseTagList(output) {
|
|
186
|
+
return (output || "")
|
|
187
|
+
.split("\n")
|
|
188
|
+
.map((line) => normalizeTagName(line))
|
|
189
|
+
.filter((tagName) => isSafeGitRefName(tagName))
|
|
190
|
+
.filter(Boolean);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function parseLsRemoteTags(output) {
|
|
194
|
+
const tags = [];
|
|
195
|
+
for (const line of (output || "").split("\n")) {
|
|
196
|
+
const ref = normalizeTagName(line.trim().split(/\s+/)[1]);
|
|
197
|
+
if (ref && isSafeGitRefName(ref)) {
|
|
198
|
+
tags.push(ref);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return Array.from(new Set(tags)).sort((a, b) => {
|
|
202
|
+
const av = coerce(a || "");
|
|
203
|
+
const bv = coerce(b || "");
|
|
204
|
+
if (av && bv) {
|
|
205
|
+
return bv.compare(av);
|
|
206
|
+
}
|
|
207
|
+
return b.localeCompare(a);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function issueTypeFromCommitMessage(message) {
|
|
212
|
+
const normalized = (message || "").toLowerCase();
|
|
213
|
+
if (
|
|
214
|
+
normalized.includes("security") ||
|
|
215
|
+
normalized.includes("cve-") ||
|
|
216
|
+
normalized.includes("vuln")
|
|
217
|
+
) {
|
|
218
|
+
return "security";
|
|
219
|
+
}
|
|
220
|
+
if (
|
|
221
|
+
normalized.startsWith("fix") ||
|
|
222
|
+
normalized.includes(" bug") ||
|
|
223
|
+
normalized.includes("defect")
|
|
224
|
+
) {
|
|
225
|
+
return "defect";
|
|
226
|
+
}
|
|
227
|
+
return "enhancement";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Build CycloneDX release notes from git tags and commits.
|
|
232
|
+
*
|
|
233
|
+
* @param {string|undefined} repoPath local repository path
|
|
234
|
+
* @param {Object} options options carrying release notes hints
|
|
235
|
+
* @returns {Object|undefined} releaseNotes object
|
|
236
|
+
*/
|
|
237
|
+
export function buildReleaseNotesFromGit(repoPath, options = {}) {
|
|
238
|
+
let currentTag = normalizeTagName(options.releaseNotesCurrentTag);
|
|
239
|
+
let previousTag = normalizeTagName(options.releaseNotesPreviousTag);
|
|
240
|
+
let remoteUrl;
|
|
241
|
+
let localRepoAvailable = false;
|
|
242
|
+
if (repoPath && !maybeRemotePath(repoPath)) {
|
|
243
|
+
const repoCheck = hardenedGitCommand(
|
|
244
|
+
["rev-parse", "--is-inside-work-tree"],
|
|
245
|
+
{
|
|
246
|
+
cwd: repoPath,
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
localRepoAvailable = repoCheck.status === 0;
|
|
250
|
+
if (localRepoAvailable) {
|
|
251
|
+
const localTagsResult = hardenedGitCommand(
|
|
252
|
+
["tag", "--sort=-creatordate", "--merged", "HEAD"],
|
|
253
|
+
{ cwd: repoPath },
|
|
254
|
+
);
|
|
255
|
+
const localTags = parseTagList(localTagsResult.stdout);
|
|
256
|
+
if (!currentTag && localTags.length) {
|
|
257
|
+
currentTag = localTags[0];
|
|
258
|
+
}
|
|
259
|
+
if (!previousTag && localTags.length > 1) {
|
|
260
|
+
previousTag = localTags.find((t) => t !== currentTag);
|
|
261
|
+
}
|
|
262
|
+
const remoteResult = hardenedGitCommand(
|
|
263
|
+
["config", "--get", "remote.origin.url"],
|
|
264
|
+
{ cwd: repoPath },
|
|
265
|
+
);
|
|
266
|
+
if (remoteResult.status === 0 && remoteResult.stdout) {
|
|
267
|
+
remoteUrl = remoteResult.stdout.toString().trim();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
remoteUrl = remoteUrl || options.releaseNotesGitUrl;
|
|
272
|
+
const remoteUrlValidationError =
|
|
273
|
+
typeof remoteUrl === "string"
|
|
274
|
+
? validateAndRejectGitSource(remoteUrl)
|
|
275
|
+
: null;
|
|
276
|
+
const canDiscoverRemoteTags =
|
|
277
|
+
(!currentTag || !previousTag) &&
|
|
278
|
+
typeof remoteUrl === "string" &&
|
|
279
|
+
!remoteUrl.startsWith("-") &&
|
|
280
|
+
!remoteUrlValidationError &&
|
|
281
|
+
/github\.com[:/]/i.test(remoteUrl);
|
|
282
|
+
if (canDiscoverRemoteTags) {
|
|
283
|
+
const remoteTagsResult = hardenedGitCommand(
|
|
284
|
+
[
|
|
285
|
+
"-c",
|
|
286
|
+
"alias.ls-remote=",
|
|
287
|
+
"-c",
|
|
288
|
+
"core.fsmonitor=false",
|
|
289
|
+
"-c",
|
|
290
|
+
"safe.bareRepository=explicit",
|
|
291
|
+
"-c",
|
|
292
|
+
"core.hooksPath=/dev/null",
|
|
293
|
+
"ls-remote",
|
|
294
|
+
"--refs",
|
|
295
|
+
"--tags",
|
|
296
|
+
"--",
|
|
297
|
+
remoteUrl,
|
|
298
|
+
],
|
|
299
|
+
{},
|
|
300
|
+
);
|
|
301
|
+
const remoteTags = parseLsRemoteTags(remoteTagsResult.stdout);
|
|
302
|
+
if (!currentTag && remoteTags.length) {
|
|
303
|
+
currentTag = remoteTags[0];
|
|
304
|
+
}
|
|
305
|
+
if (!previousTag && remoteTags.length > 1) {
|
|
306
|
+
previousTag = remoteTags.find((t) => t !== currentTag);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (!currentTag) {
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
if (!isSafeGitRefName(currentTag)) {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
if (previousTag && !isSafeGitRefName(previousTag)) {
|
|
316
|
+
previousTag = undefined;
|
|
317
|
+
}
|
|
318
|
+
let timestamp;
|
|
319
|
+
if (localRepoAvailable) {
|
|
320
|
+
const tsResult = hardenedGitCommand(
|
|
321
|
+
["log", "-1", "--format=%cI", currentTag],
|
|
322
|
+
{
|
|
323
|
+
cwd: repoPath,
|
|
324
|
+
},
|
|
325
|
+
);
|
|
326
|
+
if (tsResult.status === 0 && tsResult.stdout) {
|
|
327
|
+
timestamp = tsResult.stdout.toString().trim();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (!timestamp) {
|
|
331
|
+
timestamp = new Date().toISOString();
|
|
332
|
+
}
|
|
333
|
+
const resolves = [];
|
|
334
|
+
if (localRepoAvailable && previousTag) {
|
|
335
|
+
const logResult = hardenedGitCommand(
|
|
336
|
+
["log", "--pretty=format:%H%x09%s", `${previousTag}..${currentTag}`],
|
|
337
|
+
{ cwd: repoPath },
|
|
338
|
+
);
|
|
339
|
+
if (logResult.status === 0 && logResult.stdout) {
|
|
340
|
+
// Keep the resolves list bounded to avoid excessive metadata growth.
|
|
341
|
+
for (const line of logResult.stdout
|
|
342
|
+
.toString()
|
|
343
|
+
.split("\n")
|
|
344
|
+
.filter(Boolean)) {
|
|
345
|
+
const [sha, ...rest] = line.split("\t");
|
|
346
|
+
const message = rest.join("\t").trim();
|
|
347
|
+
if (!sha || !message) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
resolves.push({
|
|
351
|
+
type: issueTypeFromCommitMessage(message),
|
|
352
|
+
id: sha.substring(0, 12),
|
|
353
|
+
name: message,
|
|
354
|
+
description: message,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if (resolves.length > MAX_RELEASE_NOTE_RESOLVES) {
|
|
358
|
+
resolves.length = MAX_RELEASE_NOTE_RESOLVES;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const tags = [currentTag];
|
|
363
|
+
if (previousTag && previousTag !== currentTag) {
|
|
364
|
+
tags.push(previousTag);
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
type: releaseTypeFromTags(currentTag, previousTag),
|
|
368
|
+
title: `Release notes for ${currentTag}`,
|
|
369
|
+
description: previousTag
|
|
370
|
+
? `Changes between ${previousTag} and ${currentTag}.`
|
|
371
|
+
: `Release notes for ${currentTag}.`,
|
|
372
|
+
timestamp,
|
|
373
|
+
tags,
|
|
374
|
+
resolves,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Return git allow protocol string from the environment variables.
|
|
380
|
+
*
|
|
381
|
+
* @returns {string} git allow protocol string
|
|
382
|
+
*/
|
|
383
|
+
export function getGitAllowProtocol() {
|
|
384
|
+
return (
|
|
385
|
+
readEnvironmentVariable("GIT_ALLOW_PROTOCOL") ||
|
|
386
|
+
readEnvironmentVariable("CDXGEN_GIT_ALLOW_PROTOCOL") ||
|
|
387
|
+
readEnvironmentVariable("CDXGEN_SERVER_GIT_ALLOW_PROTOCOL") ||
|
|
388
|
+
(isSecureMode ? "https:ssh" : "https:git:ssh")
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Return configured allowed git hosts.
|
|
394
|
+
*
|
|
395
|
+
* @returns {string[]} list of configured hosts
|
|
396
|
+
*/
|
|
397
|
+
function getAllowedHosts() {
|
|
398
|
+
const configuredHosts =
|
|
399
|
+
readEnvironmentVariable("CDXGEN_GIT_ALLOWED_HOSTS") ||
|
|
400
|
+
readEnvironmentVariable("CDXGEN_SERVER_ALLOWED_HOSTS") ||
|
|
401
|
+
"";
|
|
402
|
+
return configuredHosts
|
|
403
|
+
.split(",")
|
|
404
|
+
.map((h) => h.trim())
|
|
405
|
+
.filter(Boolean);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Checks the given hostname against the allowed list.
|
|
410
|
+
*
|
|
411
|
+
* @param {string} hostname Host name to check
|
|
412
|
+
* @returns {boolean} true if the hostname in its entirety is allowed. false otherwise.
|
|
413
|
+
*/
|
|
414
|
+
export function isAllowedHost(hostname) {
|
|
415
|
+
const allowedHosts = getAllowedHosts();
|
|
416
|
+
if (!allowedHosts.length) {
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
if (hasDangerousUnicode(hostname)) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return allowedHosts.includes(hostname);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Return configured allowed paths.
|
|
427
|
+
*
|
|
428
|
+
* @returns {string[]} list of configured paths
|
|
429
|
+
*/
|
|
430
|
+
function getAllowedPaths() {
|
|
431
|
+
const configuredPaths =
|
|
432
|
+
readEnvironmentVariable("CDXGEN_ALLOWED_PATHS") ||
|
|
433
|
+
readEnvironmentVariable("CDXGEN_SERVER_ALLOWED_PATHS") ||
|
|
434
|
+
"";
|
|
435
|
+
return configuredPaths
|
|
436
|
+
.split(",")
|
|
437
|
+
.map((p) => p.trim())
|
|
438
|
+
.filter(Boolean);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Checks the given path string to belong to a drive in Windows.
|
|
443
|
+
*
|
|
444
|
+
* @param {string} p Path string to check
|
|
445
|
+
* @returns {boolean} true if the windows path belongs to a drive. false otherwise (device names)
|
|
446
|
+
*/
|
|
447
|
+
export function isAllowedWinPath(p) {
|
|
448
|
+
if (typeof p !== "string") {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
if (p === "") {
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
if (hasDangerousUnicode(p)) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
if (!isWin) {
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
const normalized = path.normalize(p);
|
|
462
|
+
if (hasDangerousUnicode(normalized)) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
const { root } = path.parse(normalized);
|
|
466
|
+
if (root === "\\") {
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
if (root.startsWith("\\\\")) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
return isValidDriveRoot(root);
|
|
473
|
+
} catch (_err) {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Checks the given path against the allowed list.
|
|
480
|
+
*
|
|
481
|
+
* @param {string} p Path string to check
|
|
482
|
+
* @returns {boolean} true if the path is present in the allowed paths. false otherwise.
|
|
483
|
+
*/
|
|
484
|
+
export function isAllowedPath(p) {
|
|
485
|
+
if (typeof p !== "string") {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
if (hasDangerousUnicode(p)) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
const allowedPaths = getAllowedPaths();
|
|
492
|
+
if (!allowedPaths.length) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
if (isWin && !isAllowedWinPath(p)) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
return allowedPaths.some((ap) => {
|
|
499
|
+
const resolvedP = path.resolve(p);
|
|
500
|
+
const resolvedAp = path.resolve(ap);
|
|
501
|
+
const relativePath = path.relative(resolvedAp, resolvedP);
|
|
502
|
+
return (
|
|
503
|
+
relativePath === "" ||
|
|
504
|
+
(!relativePath.startsWith("..") && !path.isAbsolute(relativePath))
|
|
505
|
+
);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Determine if the path could be a package URL.
|
|
511
|
+
*
|
|
512
|
+
* @param {string} filePath Path or URL
|
|
513
|
+
* @returns {boolean} true if the file path looks like a purl
|
|
514
|
+
*/
|
|
515
|
+
export function maybePurlSource(filePath) {
|
|
516
|
+
return typeof filePath === "string" && filePath.startsWith("pkg:");
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Determine if the file path could be a remote URL.
|
|
521
|
+
*
|
|
522
|
+
* @param {string} filePath The Git URL or local path
|
|
523
|
+
* @returns {boolean} true if the file path is a remote URL. false otherwise.
|
|
524
|
+
*/
|
|
525
|
+
export function maybeRemotePath(filePath) {
|
|
526
|
+
return /^[a-zA-Z0-9+.-]+:\/\//.test(filePath) || filePath.startsWith("git@");
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Validates a given Git URL/Path against dangerous protocols and allowed hosts.
|
|
531
|
+
*
|
|
532
|
+
* @param {string} filePath The Git URL or local path
|
|
533
|
+
* @returns {Object|null} Error object if invalid, or null if valid
|
|
534
|
+
*/
|
|
535
|
+
export function validateAndRejectGitSource(filePath) {
|
|
536
|
+
if (/^(ext|fd)::/i.test(filePath)) {
|
|
537
|
+
return {
|
|
538
|
+
status: 400,
|
|
539
|
+
error: "Invalid Protocol",
|
|
540
|
+
details: "The provided protocol is not allowed.",
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
if (maybeRemotePath(filePath)) {
|
|
544
|
+
let gitUrlObj;
|
|
545
|
+
try {
|
|
546
|
+
let urlToParse = filePath;
|
|
547
|
+
if (filePath.startsWith("git@") && !filePath.includes("://")) {
|
|
548
|
+
urlToParse = `ssh://${filePath.replace(":", "/")}`;
|
|
549
|
+
}
|
|
550
|
+
gitUrlObj = new URL(urlToParse);
|
|
551
|
+
} catch (_err) {
|
|
552
|
+
return {
|
|
553
|
+
status: 400,
|
|
554
|
+
error: "Invalid URL Format",
|
|
555
|
+
details: "The provided Git URL is malformed.",
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
const gitAllowProtocol = getGitAllowProtocol();
|
|
559
|
+
const allowedSchemes = gitAllowProtocol
|
|
560
|
+
.split(":")
|
|
561
|
+
.filter(Boolean)
|
|
562
|
+
.map((p) => `${p.toLowerCase()}:`);
|
|
563
|
+
|
|
564
|
+
if (
|
|
565
|
+
allowedSchemes.includes("ssh:") &&
|
|
566
|
+
!allowedSchemes.includes("git+ssh:")
|
|
567
|
+
) {
|
|
568
|
+
allowedSchemes.push("git+ssh:");
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (!allowedSchemes.includes(gitUrlObj.protocol)) {
|
|
572
|
+
return {
|
|
573
|
+
status: 400,
|
|
574
|
+
error: "Protocol Not Allowed",
|
|
575
|
+
details: `The protocol '${gitUrlObj.protocol}' is not permitted by GIT_ALLOW_PROTOCOL.`,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (gitUrlObj.href.includes("::")) {
|
|
580
|
+
return {
|
|
581
|
+
status: 400,
|
|
582
|
+
error: "Invalid URL Syntax",
|
|
583
|
+
details: "Git remote helper syntax (::) is not allowed.",
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (!isAllowedHost(gitUrlObj.hostname)) {
|
|
588
|
+
return {
|
|
589
|
+
status: 403,
|
|
590
|
+
error: "Host Not Allowed",
|
|
591
|
+
details: "The Git URL host is not allowed as per the allowlist.",
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Clone a git repository into a temporary directory.
|
|
601
|
+
*
|
|
602
|
+
* @param {string} repoUrl Repository URL
|
|
603
|
+
* @param {string|string[]|null} branch Branch name
|
|
604
|
+
* @returns {string} cloned directory path
|
|
605
|
+
*/
|
|
606
|
+
export function gitClone(repoUrl, branch = null) {
|
|
607
|
+
let baseDirName = path.basename(repoUrl);
|
|
608
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(baseDirName)) {
|
|
609
|
+
baseDirName = "repo-";
|
|
610
|
+
}
|
|
611
|
+
if (isDryRun) {
|
|
612
|
+
const error = createDryRunError(
|
|
613
|
+
"clone",
|
|
614
|
+
repoUrl,
|
|
615
|
+
"Dry run mode blocks repository cloning.",
|
|
616
|
+
);
|
|
617
|
+
recordActivity({
|
|
618
|
+
kind: "clone",
|
|
619
|
+
reason: error.message,
|
|
620
|
+
status: "blocked",
|
|
621
|
+
target: repoUrl,
|
|
622
|
+
});
|
|
623
|
+
return path.join(getTmpDir(), `${baseDirName}${path.sep}dry-run-clone`);
|
|
624
|
+
}
|
|
625
|
+
const tempDir = safeMkdtempSync(path.join(getTmpDir(), baseDirName));
|
|
626
|
+
|
|
627
|
+
const gitArgs = [
|
|
628
|
+
"-c",
|
|
629
|
+
"alias.clone=",
|
|
630
|
+
"-c",
|
|
631
|
+
"core.fsmonitor=false",
|
|
632
|
+
"-c",
|
|
633
|
+
"safe.bareRepository=explicit",
|
|
634
|
+
"-c",
|
|
635
|
+
"core.hooksPath=/dev/null",
|
|
636
|
+
"clone",
|
|
637
|
+
"--template=",
|
|
638
|
+
repoUrl,
|
|
639
|
+
"--depth",
|
|
640
|
+
"1",
|
|
641
|
+
tempDir,
|
|
642
|
+
];
|
|
643
|
+
if (branch) {
|
|
644
|
+
const firstBranchStr = Array.isArray(branch) ? branch[0] : String(branch);
|
|
645
|
+
if (!isSafeGitRefName(firstBranchStr)) {
|
|
646
|
+
console.warn("Skipping branch clone: invalid branch name");
|
|
647
|
+
} else {
|
|
648
|
+
const cloneIndex = gitArgs.indexOf("clone");
|
|
649
|
+
gitArgs.splice(cloneIndex + 1, 0, "--branch", firstBranchStr);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
thoughtLog(
|
|
653
|
+
`Cloning Repo${branch ? ` with branch ${branch}` : ""} to ${tempDir}`,
|
|
654
|
+
);
|
|
655
|
+
const result = hardenedGitCommand(gitArgs);
|
|
656
|
+
if (result.status !== 0) {
|
|
657
|
+
console.error(result.stderr);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return tempDir;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Sanitize remote URL for logging.
|
|
665
|
+
*
|
|
666
|
+
* @param {string|undefined} remoteUrl Repository URL
|
|
667
|
+
* @returns {string|undefined} sanitized URL
|
|
668
|
+
*/
|
|
669
|
+
export function sanitizeRemoteUrlForLogs(remoteUrl) {
|
|
670
|
+
if (!remoteUrl || typeof remoteUrl !== "string") {
|
|
671
|
+
return remoteUrl;
|
|
672
|
+
}
|
|
673
|
+
try {
|
|
674
|
+
const parsed = new URL(remoteUrl);
|
|
675
|
+
if (parsed.username || parsed.password) {
|
|
676
|
+
parsed.username = "***";
|
|
677
|
+
parsed.password = "***";
|
|
678
|
+
}
|
|
679
|
+
return parsed.toString();
|
|
680
|
+
} catch (_err) {
|
|
681
|
+
return remoteUrl;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Find a matching git ref for a package version.
|
|
687
|
+
*
|
|
688
|
+
* @param {string} repoUrl Repository URL
|
|
689
|
+
* @param {Object|undefined} purlResolution purl resolution metadata
|
|
690
|
+
* @returns {string|undefined} matching tag or branch reference
|
|
691
|
+
*/
|
|
692
|
+
export function findGitRefForPurlVersion(repoUrl, purlResolution) {
|
|
693
|
+
const packageVersion = purlResolution?.version;
|
|
694
|
+
if (!packageVersion) {
|
|
695
|
+
return undefined;
|
|
696
|
+
}
|
|
697
|
+
const purlType = purlResolution?.type;
|
|
698
|
+
const purlNamespace = purlResolution?.namespace;
|
|
699
|
+
const purlName = purlResolution?.name;
|
|
700
|
+
const refCandidates = [packageVersion, `v${packageVersion}`];
|
|
701
|
+
if (purlType === "npm" && purlName) {
|
|
702
|
+
const scopedName = buildScopedNpmPackageName(purlNamespace, purlName);
|
|
703
|
+
refCandidates.push(
|
|
704
|
+
`${purlName}@${packageVersion}`,
|
|
705
|
+
`${scopedName}@${packageVersion}`,
|
|
706
|
+
`${purlName}-v${packageVersion}`,
|
|
707
|
+
`${scopedName}-v${packageVersion}`,
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
const filteredCandidates = [...new Set(refCandidates)].filter((candidate) =>
|
|
711
|
+
isSafeGitRefName(candidate),
|
|
712
|
+
);
|
|
713
|
+
if (!repoUrl || repoUrl.startsWith("-")) {
|
|
714
|
+
return undefined;
|
|
715
|
+
}
|
|
716
|
+
if (validateAndRejectGitSource(repoUrl)) {
|
|
717
|
+
return undefined;
|
|
718
|
+
}
|
|
719
|
+
const result = hardenedGitCommand(
|
|
720
|
+
[
|
|
721
|
+
"-c",
|
|
722
|
+
"alias.ls-remote=",
|
|
723
|
+
"-c",
|
|
724
|
+
"core.fsmonitor=false",
|
|
725
|
+
"-c",
|
|
726
|
+
"safe.bareRepository=explicit",
|
|
727
|
+
"-c",
|
|
728
|
+
"core.hooksPath=/dev/null",
|
|
729
|
+
"ls-remote",
|
|
730
|
+
"--refs",
|
|
731
|
+
"--tags",
|
|
732
|
+
"--heads",
|
|
733
|
+
"--",
|
|
734
|
+
repoUrl,
|
|
735
|
+
],
|
|
736
|
+
{},
|
|
737
|
+
);
|
|
738
|
+
if (result.status !== 0 || !result.stdout) {
|
|
739
|
+
return undefined;
|
|
740
|
+
}
|
|
741
|
+
const availableRefs = result.stdout
|
|
742
|
+
.split("\n")
|
|
743
|
+
.map((line) => line.trim().split(/\s+/)[1])
|
|
744
|
+
.filter(Boolean)
|
|
745
|
+
.map((ref) => ref.replace(/^refs\/(?:tags|heads)\//, ""));
|
|
746
|
+
for (const candidate of filteredCandidates) {
|
|
747
|
+
if (availableRefs.includes(candidate)) {
|
|
748
|
+
return candidate;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return undefined;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Find the best source directory for purl-based npm monorepo scans.
|
|
756
|
+
*
|
|
757
|
+
* @param {string} srcDir cloned source directory
|
|
758
|
+
* @param {Object|undefined} purlResolution purl resolution metadata
|
|
759
|
+
* @returns {string|undefined} preferred source directory
|
|
760
|
+
*/
|
|
761
|
+
export function resolvePurlSourceDirectory(srcDir, purlResolution) {
|
|
762
|
+
if (purlResolution?.type !== "npm" || !purlResolution?.name || !srcDir) {
|
|
763
|
+
return undefined;
|
|
764
|
+
}
|
|
765
|
+
const purlNamespace = purlResolution?.namespace;
|
|
766
|
+
const packageNameCandidates = [purlResolution.name];
|
|
767
|
+
if (purlNamespace) {
|
|
768
|
+
packageNameCandidates.push(
|
|
769
|
+
`${purlNamespace}/${purlResolution.name}`,
|
|
770
|
+
buildScopedNpmPackageName(purlNamespace, purlResolution.name),
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
const uniquePackageNameCandidates = [
|
|
774
|
+
...new Set(packageNameCandidates.filter(Boolean)),
|
|
775
|
+
];
|
|
776
|
+
const skipDirectories = new Set([
|
|
777
|
+
".git",
|
|
778
|
+
".idea",
|
|
779
|
+
".vscode",
|
|
780
|
+
"build",
|
|
781
|
+
"dist",
|
|
782
|
+
"node_modules",
|
|
783
|
+
"out",
|
|
784
|
+
"target",
|
|
785
|
+
"vendor",
|
|
786
|
+
]);
|
|
787
|
+
const queue = [srcDir];
|
|
788
|
+
const matches = new Set();
|
|
789
|
+
let packageJsonCount = 0;
|
|
790
|
+
let currentIndex = 0;
|
|
791
|
+
while (
|
|
792
|
+
currentIndex < queue.length &&
|
|
793
|
+
packageJsonCount < MAX_MONOREPO_PACKAGE_JSON_FILES
|
|
794
|
+
) {
|
|
795
|
+
if (currentIndex >= MAX_MONOREPO_DIRECTORIES) {
|
|
796
|
+
break;
|
|
797
|
+
}
|
|
798
|
+
const currentDir = queue[currentIndex];
|
|
799
|
+
currentIndex += 1;
|
|
800
|
+
if (!currentDir) {
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
let entries = [];
|
|
804
|
+
try {
|
|
805
|
+
entries = fs.readdirSync(currentDir, {
|
|
806
|
+
withFileTypes: true,
|
|
807
|
+
});
|
|
808
|
+
} catch (_err) {
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
for (const entry of entries) {
|
|
812
|
+
if (entry.isDirectory()) {
|
|
813
|
+
if (skipDirectories.has(entry.name)) {
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
queue.push(path.join(currentDir, entry.name));
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
if (!entry.isFile() || entry.name !== "package.json") {
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
packageJsonCount += 1;
|
|
823
|
+
const packageJsonPath = path.join(currentDir, entry.name);
|
|
824
|
+
try {
|
|
825
|
+
const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
826
|
+
if (uniquePackageNameCandidates.includes(pkgJson?.name)) {
|
|
827
|
+
matches.add(currentDir);
|
|
828
|
+
}
|
|
829
|
+
} catch (_err) {
|
|
830
|
+
// Ignore invalid package.json files in monorepos.
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (!matches.size) {
|
|
835
|
+
return undefined;
|
|
836
|
+
}
|
|
837
|
+
const dedupedMatches = [...matches];
|
|
838
|
+
dedupedMatches.sort((a, b) => a.length - b.length);
|
|
839
|
+
return dedupedMatches[0];
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Normalize repository URL values from registry metadata to a cloneable URL.
|
|
844
|
+
*
|
|
845
|
+
* @param {string|undefined} candidateUrl raw URL candidate
|
|
846
|
+
* @returns {string|undefined} normalized URL
|
|
847
|
+
*/
|
|
848
|
+
function normalizeRepositoryUrl(candidateUrl) {
|
|
849
|
+
if (!candidateUrl || typeof candidateUrl !== "string") {
|
|
850
|
+
return undefined;
|
|
851
|
+
}
|
|
852
|
+
let repoUrl = candidateUrl.trim();
|
|
853
|
+
if (!repoUrl) {
|
|
854
|
+
return undefined;
|
|
855
|
+
}
|
|
856
|
+
if (/^git\s+/.test(repoUrl)) {
|
|
857
|
+
repoUrl = repoUrl.replace(/^git\s+/, "git+");
|
|
858
|
+
}
|
|
859
|
+
if (repoUrl.startsWith("git+")) {
|
|
860
|
+
repoUrl = repoUrl.slice(4);
|
|
861
|
+
}
|
|
862
|
+
if (repoUrl.startsWith("scm:git:")) {
|
|
863
|
+
repoUrl = repoUrl.slice(8);
|
|
864
|
+
}
|
|
865
|
+
if (repoUrl.startsWith("github:")) {
|
|
866
|
+
repoUrl = `https://github.com/${repoUrl.slice("github:".length)}`;
|
|
867
|
+
}
|
|
868
|
+
if (repoUrl.startsWith("gitlab:")) {
|
|
869
|
+
repoUrl = `https://gitlab.com/${repoUrl.slice("gitlab:".length)}`;
|
|
870
|
+
}
|
|
871
|
+
if (repoUrl.startsWith("bitbucket:")) {
|
|
872
|
+
repoUrl = `https://bitbucket.org/${repoUrl.slice("bitbucket:".length)}`;
|
|
873
|
+
}
|
|
874
|
+
if (
|
|
875
|
+
!repoUrl.includes("://") &&
|
|
876
|
+
/^(?:[^/]+\.)?github\.com\/.+/.test(repoUrl)
|
|
877
|
+
) {
|
|
878
|
+
repoUrl = `https://${repoUrl}`;
|
|
879
|
+
}
|
|
880
|
+
if (!repoUrl.includes("://") && repoUrl.startsWith("www.")) {
|
|
881
|
+
repoUrl = `https://${repoUrl}`;
|
|
882
|
+
}
|
|
883
|
+
const hashIndex = repoUrl.indexOf("#");
|
|
884
|
+
if (hashIndex > -1) {
|
|
885
|
+
repoUrl = repoUrl.slice(0, hashIndex);
|
|
886
|
+
}
|
|
887
|
+
return repoUrl;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Normalize repository URL values represented as string/object metadata fields.
|
|
892
|
+
*
|
|
893
|
+
* @param {string|Object|undefined} candidate repository field value
|
|
894
|
+
* @returns {string|undefined} normalized URL
|
|
895
|
+
*/
|
|
896
|
+
function normalizeRepositoryObject(candidate) {
|
|
897
|
+
if (!candidate) {
|
|
898
|
+
return undefined;
|
|
899
|
+
}
|
|
900
|
+
if (typeof candidate === "string") {
|
|
901
|
+
return normalizeRepositoryUrl(candidate);
|
|
902
|
+
}
|
|
903
|
+
if (typeof candidate === "object") {
|
|
904
|
+
return normalizeRepositoryUrl(candidate.url);
|
|
905
|
+
}
|
|
906
|
+
return undefined;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Build a cloneable repository URL from known download URL patterns.
|
|
911
|
+
*
|
|
912
|
+
* @param {string|undefined} candidateUrl raw download URL
|
|
913
|
+
* @returns {string|undefined} normalized repository URL
|
|
914
|
+
*/
|
|
915
|
+
function normalizeDownloadRepositoryUrl(candidateUrl) {
|
|
916
|
+
const normalized = normalizeRepositoryUrl(candidateUrl);
|
|
917
|
+
if (!normalized) {
|
|
918
|
+
return undefined;
|
|
919
|
+
}
|
|
920
|
+
let parsed;
|
|
921
|
+
try {
|
|
922
|
+
parsed = new URL(normalized);
|
|
923
|
+
} catch (_err) {
|
|
924
|
+
return undefined;
|
|
925
|
+
}
|
|
926
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
927
|
+
if (parsed.hostname === "github.com" && segments.length >= 2) {
|
|
928
|
+
return `https://github.com/${segments[0]}/${segments[1]}`;
|
|
929
|
+
}
|
|
930
|
+
if (parsed.hostname === "codeload.github.com" && segments.length >= 2) {
|
|
931
|
+
return `https://github.com/${segments[0]}/${segments[1]}`;
|
|
932
|
+
}
|
|
933
|
+
if (parsed.hostname === "gitlab.com" && segments.length >= 2) {
|
|
934
|
+
return `https://gitlab.com/${segments[0]}/${segments[1]}`;
|
|
935
|
+
}
|
|
936
|
+
if (normalized.endsWith(".git")) {
|
|
937
|
+
return normalized;
|
|
938
|
+
}
|
|
939
|
+
return undefined;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Build package name key for registry lookup from a parsed package URL.
|
|
944
|
+
*
|
|
945
|
+
* @param {PackageURL} purlObj parsed package URL object
|
|
946
|
+
* @returns {string} package name suitable for registry API lookup
|
|
947
|
+
*/
|
|
948
|
+
function packageNameForLookup(purlObj) {
|
|
949
|
+
const namespace = purlObj.namespace;
|
|
950
|
+
if (!namespace) {
|
|
951
|
+
return purlObj.name;
|
|
952
|
+
}
|
|
953
|
+
return `${namespace}/${purlObj.name}`;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Validate package URL source input and return an error object when invalid.
|
|
958
|
+
*
|
|
959
|
+
* @param {string} purlString package URL string
|
|
960
|
+
* @returns {{status:number,error:string,details:string}|null} validation error or null
|
|
961
|
+
*/
|
|
962
|
+
export function validatePurlSource(purlString) {
|
|
963
|
+
if (!maybePurlSource(purlString)) {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
let purlObj;
|
|
967
|
+
try {
|
|
968
|
+
purlObj = Purl.parse(purlString);
|
|
969
|
+
} catch (_err) {
|
|
970
|
+
return {
|
|
971
|
+
status: 400,
|
|
972
|
+
error: "Invalid purl source",
|
|
973
|
+
details: "The provided package URL is malformed.",
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
const purlType = purlObj?.type;
|
|
977
|
+
if (!SUPPORTED_PURL_SOURCE_TYPES.includes(purlType)) {
|
|
978
|
+
return {
|
|
979
|
+
status: 400,
|
|
980
|
+
error: "Unsupported purl source type",
|
|
981
|
+
details: `Supported purl types for automatic git URL detection: ${SUPPORTED_PURL_SOURCE_TYPES.join(", ")}.`,
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
if (!purlObj?.name) {
|
|
985
|
+
return {
|
|
986
|
+
status: 400,
|
|
987
|
+
error: "Invalid purl source",
|
|
988
|
+
details: "The provided package URL does not include a package name.",
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
const purlQualifiers = purlObj?.qualifiers || {};
|
|
992
|
+
if (
|
|
993
|
+
["github", "bitbucket", "maven", "composer"].includes(purlType) &&
|
|
994
|
+
!purlObj?.namespace
|
|
995
|
+
) {
|
|
996
|
+
return {
|
|
997
|
+
status: 400,
|
|
998
|
+
error: "Invalid purl source",
|
|
999
|
+
details: `The provided ${purlType} package URL must include a namespace.`,
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
if (purlType === "maven" && !purlObj?.version) {
|
|
1003
|
+
return {
|
|
1004
|
+
status: 400,
|
|
1005
|
+
error: "Invalid purl source",
|
|
1006
|
+
details: "The provided maven package URL must include a version.",
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
if (
|
|
1010
|
+
purlType === "generic" &&
|
|
1011
|
+
!purlQualifiers.vcs_url &&
|
|
1012
|
+
!purlQualifiers.download_url
|
|
1013
|
+
) {
|
|
1014
|
+
return {
|
|
1015
|
+
status: 400,
|
|
1016
|
+
error: "Unsupported generic purl source",
|
|
1017
|
+
details:
|
|
1018
|
+
"generic purl sources must include a vcs_url or download_url qualifier.",
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Resolve a git repository URL from a package URL by querying package registries.
|
|
1026
|
+
*
|
|
1027
|
+
* Supported purl types:
|
|
1028
|
+
* - npm -> registry.npmjs.org
|
|
1029
|
+
* - pypi -> pypi.org
|
|
1030
|
+
* - gem -> rubygems.org
|
|
1031
|
+
* - cargo -> crates.io
|
|
1032
|
+
* - pub -> pub.dev
|
|
1033
|
+
* - github -> github.com/{namespace}/{name}
|
|
1034
|
+
* - bitbucket -> bitbucket.org/{namespace}/{name}
|
|
1035
|
+
* - maven -> repo1.maven.org POM scm metadata
|
|
1036
|
+
* - composer -> repo.packagist.org p2 metadata
|
|
1037
|
+
* - generic -> qualifiers: vcs_url, download_url
|
|
1038
|
+
*
|
|
1039
|
+
* `fetchPomXmlAsJson` is injected rather than imported because it is
|
|
1040
|
+
* maven-specific and lives a layer above this module. Every maven resolution
|
|
1041
|
+
* needs it, so it is required rather than optional: omitting it would silently
|
|
1042
|
+
* turn every maven purl into an unresolvable one.
|
|
1043
|
+
*
|
|
1044
|
+
* @param {string} purlString package URL string
|
|
1045
|
+
* @param {{ fetchPomXmlAsJson?: Function }} [helpers] injected registry fetchers
|
|
1046
|
+
* @returns {Promise<{repoUrl:string|undefined, registry:string|undefined, type:string}|undefined>} resolution result
|
|
1047
|
+
*/
|
|
1048
|
+
/**
|
|
1049
|
+
* The registry document `resolveGitUrlFromPurl` reads for a purl.
|
|
1050
|
+
*
|
|
1051
|
+
* Extracted so the batch round and the resolution switch cannot drift apart: a
|
|
1052
|
+
* prefetch that spelled a URL even slightly differently would fetch documents
|
|
1053
|
+
* nobody reads and leave the switch making its own serial request. Types that
|
|
1054
|
+
* need no network (`github`, `bitbucket`, `generic`) and `maven`, whose POM is
|
|
1055
|
+
* fetched through an injected helper with its own prefetch, return undefined.
|
|
1056
|
+
*
|
|
1057
|
+
* @param {Object} purlObj Parsed purl.
|
|
1058
|
+
* @returns {{registry: string, url: string, accept?: string}|undefined}
|
|
1059
|
+
*/
|
|
1060
|
+
function registryLookupForPurl(purlObj) {
|
|
1061
|
+
const packageName = packageNameForLookup(purlObj);
|
|
1062
|
+
const packageVersion = purlObj.version;
|
|
1063
|
+
switch (purlObj.type) {
|
|
1064
|
+
case "npm": {
|
|
1065
|
+
const registry =
|
|
1066
|
+
readEnvironmentVariable("NPM_URL") || "https://registry.npmjs.org/";
|
|
1067
|
+
return { registry, url: `${registry}${packageName}` };
|
|
1068
|
+
}
|
|
1069
|
+
case "pypi": {
|
|
1070
|
+
const registry =
|
|
1071
|
+
readEnvironmentVariable("PYPI_URL") || "https://pypi.org/pypi/";
|
|
1072
|
+
const suffix = packageVersion
|
|
1073
|
+
? `${purlObj.name}/${packageVersion}/json`
|
|
1074
|
+
: `${purlObj.name}/json`;
|
|
1075
|
+
return { registry, url: `${registry}${suffix}` };
|
|
1076
|
+
}
|
|
1077
|
+
case "gem": {
|
|
1078
|
+
const v1Url =
|
|
1079
|
+
readEnvironmentVariable("RUBYGEMS_V1_URL") ||
|
|
1080
|
+
"https://rubygems.org/api/v1/gems/";
|
|
1081
|
+
const v2Url =
|
|
1082
|
+
readEnvironmentVariable("RUBYGEMS_V2_URL") ||
|
|
1083
|
+
"https://rubygems.org/api/v2/rubygems/";
|
|
1084
|
+
return {
|
|
1085
|
+
registry: packageVersion ? v2Url : v1Url,
|
|
1086
|
+
url: packageVersion
|
|
1087
|
+
? `${v2Url}${purlObj.name}/versions/${packageVersion}.json`
|
|
1088
|
+
: `${v1Url}${purlObj.name}.json`,
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
case "cargo": {
|
|
1092
|
+
const registry =
|
|
1093
|
+
readEnvironmentVariable("RUST_CRATES_URL") ||
|
|
1094
|
+
"https://crates.io/api/v1/crates/";
|
|
1095
|
+
return { registry, url: `${registry}${purlObj.name}` };
|
|
1096
|
+
}
|
|
1097
|
+
case "pub": {
|
|
1098
|
+
const registry =
|
|
1099
|
+
readEnvironmentVariable("PUB_DEV_URL") || "https://pub.dev";
|
|
1100
|
+
return {
|
|
1101
|
+
accept: "application/vnd.pub.v2+json",
|
|
1102
|
+
registry,
|
|
1103
|
+
url: packageVersion
|
|
1104
|
+
? `${registry}/api/packages/${purlObj.name}/versions/${packageVersion}`
|
|
1105
|
+
: `${registry}/api/packages/${purlObj.name}`,
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
case "composer": {
|
|
1109
|
+
const packagistUrl =
|
|
1110
|
+
readEnvironmentVariable("PACKAGIST_URL") ||
|
|
1111
|
+
"https://repo.packagist.org/p2/";
|
|
1112
|
+
const registry = packagistUrl.endsWith("/")
|
|
1113
|
+
? packagistUrl
|
|
1114
|
+
: `${packagistUrl}/`;
|
|
1115
|
+
return { registry, url: `${registry}${packageName}.json` };
|
|
1116
|
+
}
|
|
1117
|
+
default:
|
|
1118
|
+
return undefined;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Registry documents, prefetched by {@link prefetchGitUrlSources}.
|
|
1124
|
+
*
|
|
1125
|
+
* Module state rather than a parameter because `resolveGitUrlFromPurl` takes
|
|
1126
|
+
* one purl and is called from three places; threading a map through them would
|
|
1127
|
+
* be a wide change for no gain. A miss means the lookup issues its own request.
|
|
1128
|
+
*/
|
|
1129
|
+
let gitUrlSourcePrefetch = new Map();
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Discard prefetched registry documents. Tests only.
|
|
1133
|
+
*/
|
|
1134
|
+
export function resetGitUrlSourcePrefetch() {
|
|
1135
|
+
gitUrlSourcePrefetch = new Map();
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Prefetch the registry documents a list of purls will be resolved from.
|
|
1140
|
+
*
|
|
1141
|
+
* `resolveGitUrlFromPurl` takes a single purl, so on its own it can only ever
|
|
1142
|
+
* make one request at a time. Callers that know the whole list up front — the
|
|
1143
|
+
* predictive audit knows every target before it starts — can hand it over here
|
|
1144
|
+
* and have the documents fetched in one round instead.
|
|
1145
|
+
*
|
|
1146
|
+
* @param {Array<string>} purlStrings Purls about to be resolved. Duplicates,
|
|
1147
|
+
* empties and unparseable entries are fine.
|
|
1148
|
+
* @returns {Promise<void>}
|
|
1149
|
+
*/
|
|
1150
|
+
export async function prefetchGitUrlSources(purlStrings) {
|
|
1151
|
+
if (!prefetchEnabled() || !Array.isArray(purlStrings)) {
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
const requests = [];
|
|
1155
|
+
const seen = new Set();
|
|
1156
|
+
for (const purlString of purlStrings) {
|
|
1157
|
+
const purlObj = parsePurlForSourceResolution(purlString);
|
|
1158
|
+
if (!purlObj) {
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
const lookup = registryLookupForPurl(purlObj);
|
|
1162
|
+
if (!lookup || seen.has(lookup.url)) {
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
seen.add(lookup.url);
|
|
1166
|
+
requests.push({
|
|
1167
|
+
...(lookup.accept ? { accept: lookup.accept } : {}),
|
|
1168
|
+
url: lookup.url,
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
if (!requests.length) {
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
for (const [key, value] of await prefetchJson(requests)) {
|
|
1175
|
+
gitUrlSourcePrefetch.set(key, value);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* Read a registry document, from the batch round when it ran.
|
|
1181
|
+
*
|
|
1182
|
+
* @param {{url: string, accept?: string}} lookup
|
|
1183
|
+
* @returns {Promise<Object>} A response-shaped object with a `body`.
|
|
1184
|
+
*/
|
|
1185
|
+
async function fetchRegistryDocument(lookup) {
|
|
1186
|
+
return (
|
|
1187
|
+
prefetchedResponse(gitUrlSourcePrefetch, lookup.url) ||
|
|
1188
|
+
(await cdxgenAgent.get(lookup.url, {
|
|
1189
|
+
...(lookup.accept ? { headers: { Accept: lookup.accept } } : {}),
|
|
1190
|
+
responseType: "json",
|
|
1191
|
+
}))
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* Parse a purl for source resolution, applying the same guards and qualifier
|
|
1197
|
+
* normalization {@link resolveGitUrlFromPurl} applies.
|
|
1198
|
+
*
|
|
1199
|
+
* @param {string} purlString
|
|
1200
|
+
* @returns {Object|undefined} Parsed purl, or undefined when unusable.
|
|
1201
|
+
*/
|
|
1202
|
+
function parsePurlForSourceResolution(purlString) {
|
|
1203
|
+
if (!maybePurlSource(purlString) || validatePurlSource(purlString)) {
|
|
1204
|
+
return undefined;
|
|
1205
|
+
}
|
|
1206
|
+
let purlObj;
|
|
1207
|
+
try {
|
|
1208
|
+
// Some purls in the wild use unencoded + in qualifier values (e.g.
|
|
1209
|
+
// git+https:// in vcs_url). cdx-purl requires percent-encoding; normalize
|
|
1210
|
+
// the qualifier section before parsing so real-world purls resolve.
|
|
1211
|
+
const qIdx = purlString.indexOf("?");
|
|
1212
|
+
const safePurlString =
|
|
1213
|
+
qIdx >= 0
|
|
1214
|
+
? purlString.slice(0, qIdx + 1) +
|
|
1215
|
+
purlString.slice(qIdx + 1).replaceAll("+", "%2B")
|
|
1216
|
+
: purlString;
|
|
1217
|
+
purlObj = Purl.parse(safePurlString);
|
|
1218
|
+
} catch (err) {
|
|
1219
|
+
if (DEBUG_MODE) {
|
|
1220
|
+
const errorMessage = err?.message || String(err);
|
|
1221
|
+
thoughtLog("Unable to resolve repository URL for purl.", {
|
|
1222
|
+
purlString,
|
|
1223
|
+
errorMessage,
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
return undefined;
|
|
1227
|
+
}
|
|
1228
|
+
if (!purlObj?.type || !purlObj?.name) {
|
|
1229
|
+
return undefined;
|
|
1230
|
+
}
|
|
1231
|
+
return purlObj;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Resolve source repository details for a package from its purl.
|
|
1236
|
+
*
|
|
1237
|
+
* Queries the package registry (or constructs the URL directly for VCS-hosted
|
|
1238
|
+
* purl types) and normalizes the repository or homepage URL found in registry
|
|
1239
|
+
* metadata. Network and lookup failures are logged and yield `undefined`.
|
|
1240
|
+
*
|
|
1241
|
+
* @param {string} purlString Package URL string
|
|
1242
|
+
* @param {Object} [helpers={}] Injected registry helpers (e.g. `fetchPomXmlAsJson` required for maven)
|
|
1243
|
+
* @returns {Promise<{type: string, registry: string, repoUrl: string, version: string, namespace: string, name: string}|undefined>} Resolved source details, or `undefined` when no remote repository URL could be resolved
|
|
1244
|
+
*/
|
|
1245
|
+
export async function resolveGitUrlFromPurl(purlString, helpers = {}) {
|
|
1246
|
+
const purlObj = parsePurlForSourceResolution(purlString);
|
|
1247
|
+
if (!purlObj) {
|
|
1248
|
+
return undefined;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
const packageName = packageNameForLookup(purlObj);
|
|
1252
|
+
const packageVersion = purlObj.version;
|
|
1253
|
+
let repoUrl;
|
|
1254
|
+
let registry;
|
|
1255
|
+
const logPurlResolutionError = (err) => {
|
|
1256
|
+
const errorMessage = err?.message || String(err);
|
|
1257
|
+
const errorDetails = [];
|
|
1258
|
+
if (err?.code) {
|
|
1259
|
+
errorDetails.push(`code=${err.code}`);
|
|
1260
|
+
}
|
|
1261
|
+
if (typeof err?.statusCode === "number") {
|
|
1262
|
+
errorDetails.push(`status=${err.statusCode}`);
|
|
1263
|
+
}
|
|
1264
|
+
if (err?.hostname) {
|
|
1265
|
+
errorDetails.push(`host=${err.hostname}`);
|
|
1266
|
+
}
|
|
1267
|
+
const sanitizedRegistry = sanitizeRemoteUrlForLogs(registry);
|
|
1268
|
+
console.error(
|
|
1269
|
+
`Unable to resolve repository URL for purl '${purlString}'${sanitizedRegistry ? ` using registry '${sanitizedRegistry}'` : ""}: ${errorMessage}${errorDetails.length ? ` (${errorDetails.join(", ")})` : ""}`,
|
|
1270
|
+
);
|
|
1271
|
+
if (DEBUG_MODE) {
|
|
1272
|
+
thoughtLog("Unable to resolve repository URL for purl.", {
|
|
1273
|
+
purlString,
|
|
1274
|
+
errorMessage,
|
|
1275
|
+
errorDetails,
|
|
1276
|
+
registry: sanitizedRegistry,
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
|
|
1281
|
+
try {
|
|
1282
|
+
switch (purlObj.type) {
|
|
1283
|
+
case "npm": {
|
|
1284
|
+
const lookup = registryLookupForPurl(purlObj);
|
|
1285
|
+
registry = lookup.registry;
|
|
1286
|
+
const res = await fetchRegistryDocument(lookup);
|
|
1287
|
+
const body = res.body;
|
|
1288
|
+
const versionBody = packageVersion
|
|
1289
|
+
? body.versions?.[packageVersion]
|
|
1290
|
+
: undefined;
|
|
1291
|
+
repoUrl =
|
|
1292
|
+
normalizeRepositoryObject(versionBody?.repository) ||
|
|
1293
|
+
normalizeRepositoryObject(body.repository) ||
|
|
1294
|
+
normalizeRepositoryUrl(versionBody?.homepage) ||
|
|
1295
|
+
normalizeRepositoryUrl(body.homepage);
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1298
|
+
case "pypi": {
|
|
1299
|
+
const lookup = registryLookupForPurl(purlObj);
|
|
1300
|
+
registry = lookup.registry;
|
|
1301
|
+
const res = await fetchRegistryDocument(lookup);
|
|
1302
|
+
const info = res.body?.info || {};
|
|
1303
|
+
const projectUrls = info.project_urls || {};
|
|
1304
|
+
repoUrl =
|
|
1305
|
+
normalizeRepositoryUrl(projectUrls.Source) ||
|
|
1306
|
+
normalizeRepositoryUrl(projectUrls.Repository) ||
|
|
1307
|
+
normalizeRepositoryUrl(projectUrls["Source Code"]) ||
|
|
1308
|
+
normalizeRepositoryUrl(projectUrls.Code) ||
|
|
1309
|
+
normalizeRepositoryUrl(info.home_page);
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
case "gem": {
|
|
1313
|
+
const lookup = registryLookupForPurl(purlObj);
|
|
1314
|
+
registry = lookup.registry;
|
|
1315
|
+
const res = await fetchRegistryDocument(lookup);
|
|
1316
|
+
const body = Array.isArray(res.body) ? res.body[0] : res.body;
|
|
1317
|
+
repoUrl =
|
|
1318
|
+
normalizeRepositoryUrl(body?.metadata?.source_code_uri) ||
|
|
1319
|
+
normalizeRepositoryUrl(body?.source_code_uri) ||
|
|
1320
|
+
normalizeRepositoryUrl(body?.homepage_uri);
|
|
1321
|
+
break;
|
|
1322
|
+
}
|
|
1323
|
+
case "cargo": {
|
|
1324
|
+
const lookup = registryLookupForPurl(purlObj);
|
|
1325
|
+
registry = lookup.registry;
|
|
1326
|
+
const res = await fetchRegistryDocument(lookup);
|
|
1327
|
+
repoUrl = normalizeRepositoryUrl(res.body?.crate?.repository);
|
|
1328
|
+
break;
|
|
1329
|
+
}
|
|
1330
|
+
case "pub": {
|
|
1331
|
+
const lookup = registryLookupForPurl(purlObj);
|
|
1332
|
+
registry = lookup.registry;
|
|
1333
|
+
const res = await fetchRegistryDocument(lookup);
|
|
1334
|
+
const pubspec = res.body?.pubspec || res.body?.latest?.pubspec || {};
|
|
1335
|
+
repoUrl =
|
|
1336
|
+
normalizeRepositoryUrl(pubspec.repository) ||
|
|
1337
|
+
normalizeRepositoryUrl(pubspec.homepage);
|
|
1338
|
+
break;
|
|
1339
|
+
}
|
|
1340
|
+
case "github": {
|
|
1341
|
+
registry = "https://github.com";
|
|
1342
|
+
if (purlObj.namespace) {
|
|
1343
|
+
repoUrl = normalizeRepositoryUrl(
|
|
1344
|
+
`${registry}/${purlObj.namespace}/${purlObj.name}`,
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
break;
|
|
1348
|
+
}
|
|
1349
|
+
case "bitbucket": {
|
|
1350
|
+
registry = "https://bitbucket.org";
|
|
1351
|
+
if (purlObj.namespace) {
|
|
1352
|
+
repoUrl = normalizeRepositoryUrl(
|
|
1353
|
+
`${registry}/${purlObj.namespace}/${purlObj.name}`,
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
case "maven": {
|
|
1359
|
+
if (!purlObj.namespace || !packageVersion) {
|
|
1360
|
+
break;
|
|
1361
|
+
}
|
|
1362
|
+
const mavenCentralUrl =
|
|
1363
|
+
readEnvironmentVariable("MAVEN_CENTRAL_URL") ||
|
|
1364
|
+
"https://repo1.maven.org/maven2/";
|
|
1365
|
+
registry = mavenCentralUrl.endsWith("/")
|
|
1366
|
+
? mavenCentralUrl
|
|
1367
|
+
: `${mavenCentralUrl}/`;
|
|
1368
|
+
if (typeof helpers.fetchPomXmlAsJson !== "function") {
|
|
1369
|
+
throw new MissingRegistryHelperError(
|
|
1370
|
+
"resolveGitUrlFromPurl cannot resolve a maven purl without an injected fetchPomXmlAsJson.",
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
const pomJson = await helpers.fetchPomXmlAsJson({
|
|
1374
|
+
urlPrefix: registry,
|
|
1375
|
+
group: purlObj.namespace,
|
|
1376
|
+
name: purlObj.name,
|
|
1377
|
+
version: packageVersion,
|
|
1378
|
+
});
|
|
1379
|
+
repoUrl =
|
|
1380
|
+
normalizeRepositoryUrl(pomJson?.scm?.url?._) ||
|
|
1381
|
+
normalizeRepositoryUrl(pomJson?.scm?.connection?._) ||
|
|
1382
|
+
normalizeRepositoryUrl(pomJson?.scm?.developerConnection?._);
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
1385
|
+
case "composer": {
|
|
1386
|
+
const lookup = registryLookupForPurl(purlObj);
|
|
1387
|
+
registry = lookup.registry;
|
|
1388
|
+
const res = await fetchRegistryDocument(lookup);
|
|
1389
|
+
const packageVersions = res.body?.packages?.[packageName];
|
|
1390
|
+
if (!Array.isArray(packageVersions) || !packageVersions.length) {
|
|
1391
|
+
break;
|
|
1392
|
+
}
|
|
1393
|
+
const selectedVersion = packageVersion
|
|
1394
|
+
? packageVersions.find(
|
|
1395
|
+
(v) =>
|
|
1396
|
+
v?.version === packageVersion ||
|
|
1397
|
+
v?.version_normalized === packageVersion,
|
|
1398
|
+
)
|
|
1399
|
+
: packageVersions[0];
|
|
1400
|
+
repoUrl =
|
|
1401
|
+
normalizeRepositoryUrl(selectedVersion?.source?.url) ||
|
|
1402
|
+
normalizeRepositoryUrl(selectedVersion?.homepage);
|
|
1403
|
+
break;
|
|
1404
|
+
}
|
|
1405
|
+
case "generic": {
|
|
1406
|
+
const genericVcsUrl = purlObj.qualifiers?.vcs_url;
|
|
1407
|
+
const genericDownloadUrl = purlObj.qualifiers?.download_url;
|
|
1408
|
+
repoUrl =
|
|
1409
|
+
normalizeRepositoryUrl(genericVcsUrl) ||
|
|
1410
|
+
normalizeDownloadRepositoryUrl(genericDownloadUrl);
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1413
|
+
default:
|
|
1414
|
+
return undefined;
|
|
1415
|
+
}
|
|
1416
|
+
} catch (err) {
|
|
1417
|
+
// Network and registry failures are expected and non-fatal. A missing
|
|
1418
|
+
// injected helper is a wiring bug in the caller, so it must not be
|
|
1419
|
+
// swallowed by the same handler.
|
|
1420
|
+
if (err instanceof MissingRegistryHelperError) {
|
|
1421
|
+
throw err;
|
|
1422
|
+
}
|
|
1423
|
+
logPurlResolutionError(err);
|
|
1424
|
+
return undefined;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
if (!repoUrl) {
|
|
1428
|
+
return undefined;
|
|
1429
|
+
}
|
|
1430
|
+
if (!maybeRemotePath(repoUrl)) {
|
|
1431
|
+
if (DEBUG_MODE) {
|
|
1432
|
+
console.log(
|
|
1433
|
+
`Ignoring non-remote repository URL '${repoUrl}' from purl lookup.`,
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
return undefined;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
return {
|
|
1440
|
+
type: purlObj.type,
|
|
1441
|
+
registry,
|
|
1442
|
+
repoUrl,
|
|
1443
|
+
version: purlObj.version,
|
|
1444
|
+
namespace: purlObj.namespace,
|
|
1445
|
+
name: purlObj.name,
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Clean up cloned source directories.
|
|
1451
|
+
*
|
|
1452
|
+
* @param {string} srcDir directory path to remove
|
|
1453
|
+
*/
|
|
1454
|
+
export function cleanupSourceDir(srcDir) {
|
|
1455
|
+
const normalizePath = (candidatePath) => {
|
|
1456
|
+
if (!candidatePath) {
|
|
1457
|
+
return undefined;
|
|
1458
|
+
}
|
|
1459
|
+
try {
|
|
1460
|
+
return fs.realpathSync.native
|
|
1461
|
+
? fs.realpathSync.native(candidatePath)
|
|
1462
|
+
: fs.realpathSync(candidatePath);
|
|
1463
|
+
} catch {
|
|
1464
|
+
return path.resolve(candidatePath);
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
const normalizedSrcDir = normalizePath(srcDir);
|
|
1468
|
+
const tempRoots = [getTmpDir()];
|
|
1469
|
+
if (process.platform !== "win32") {
|
|
1470
|
+
tempRoots.push("/tmp");
|
|
1471
|
+
tempRoots.push("/private/tmp");
|
|
1472
|
+
}
|
|
1473
|
+
const normalizedTmpDirs = tempRoots
|
|
1474
|
+
.map((candidatePath) => normalizePath(candidatePath))
|
|
1475
|
+
.filter(Boolean);
|
|
1476
|
+
if (
|
|
1477
|
+
normalizedSrcDir &&
|
|
1478
|
+
normalizedTmpDirs.some(
|
|
1479
|
+
(normalizedTmpDir) =>
|
|
1480
|
+
normalizedSrcDir === normalizedTmpDir ||
|
|
1481
|
+
normalizedSrcDir.startsWith(`${normalizedTmpDir}${path.sep}`),
|
|
1482
|
+
) &&
|
|
1483
|
+
fs.rmSync
|
|
1484
|
+
) {
|
|
1485
|
+
thoughtLog(`Cleaning up ${srcDir}`);
|
|
1486
|
+
safeRmSync(srcDir, { recursive: true, force: true });
|
|
1487
|
+
}
|
|
1488
|
+
}
|