@cdxgen/cdxgen 13.5.0 → 13.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/bin/audit.js +7 -4
- package/bin/cdxgen.js +260 -37
- package/bin/convert.js +146 -22
- package/bin/evinse.js +28 -3
- package/bin/repl.js +47 -3
- package/bin/sign.js +211 -34
- package/bin/tracebom.js +18 -6
- package/bin/validate.js +24 -3
- package/bin/verify.js +113 -5
- package/data/behavior-taxonomy.schema.json +1491 -0
- package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
- package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
- package/data/license-aliases.json +24 -2
- package/data/pqc-migration.json +239 -0
- package/data/remediations.json +176 -0
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -0
- package/data/rules/cbom-security.yaml +4 -3
- package/data/rules/package-integrity.yaml +28 -0
- package/data/rules/vscode-extensions.yaml +75 -0
- package/data/spdx-license-list.json +121 -7
- package/data/spdx-licenses.json +69 -0
- package/data/spdx.schema.json +845 -2
- package/lib/audit/index.js +25 -3
- package/lib/cli/bomAssembly.js +172 -4
- package/lib/cli/cliOptions.js +121 -17
- package/lib/cli/index.js +720 -902
- package/lib/cli/jsBom.js +156 -64
- package/lib/cli/jvmBom.js +602 -86
- package/lib/cli/managedBom.js +161 -19
- package/lib/cli/nativeBom.js +52 -5
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +34 -0
- package/lib/core/fs.js +138 -3
- package/lib/core/globPatterns.js +218 -0
- package/lib/core/httpClient.js +98 -1
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +100 -8
- package/lib/core/projectConfig.js +332 -25
- package/lib/core/toolRegistry.js +184 -0
- package/lib/ecosystems/cppEvidence.js +263 -106
- package/lib/ecosystems/cppIncludeResolver.js +16 -5
- package/lib/ecosystems/denoutils.js +2 -2
- package/lib/ecosystems/ecosystems.js +25 -10
- package/lib/ecosystems/gemHostProbe.js +301 -0
- package/lib/ecosystems/golem.js +3 -8
- package/lib/ecosystems/gradleutils.js +1909 -229
- package/lib/ecosystems/jsEvidence.js +90 -38
- package/lib/ecosystems/kosi.js +3 -5
- package/lib/ecosystems/npmVersionResolution.js +376 -0
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-misc.js +209 -20
- package/lib/ecosystems/parsers-python.js +178 -30
- package/lib/ecosystems/parsers-rust.js +117 -15
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/remote/huggingface.js +22 -0
- package/lib/ecosystems/rubyutils.js +538 -31
- package/lib/ecosystems/rusi.js +2 -4
- package/lib/ecosystems/scalacliutils.js +2 -0
- package/lib/ecosystems/scalasem.js +798 -152
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +536 -71
- package/lib/evinser/scalasem.js +5 -1
- package/lib/helpers/agenticToolutils.js +525 -94
- package/lib/helpers/bomSigner.js +43 -9
- package/lib/helpers/jssSigner.js +1276 -0
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/agentBlueprint.js +193 -0
- package/lib/inventory/agentToolNames.js +145 -0
- package/lib/inventory/aiCollector.js +153 -7
- package/lib/inventory/aiInventory.js +44 -8
- package/lib/inventory/aiProviders.js +324 -0
- package/lib/inventory/atomUtils.js +77 -0
- package/lib/inventory/bomUtils.js +8 -1
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
- package/lib/inventory/citations.js +31 -2
- package/lib/inventory/communityAiConfigParser.js +978 -80
- package/lib/inventory/cyclonedx20.js +152 -0
- package/lib/inventory/cyclonedx20Common.js +286 -0
- package/lib/inventory/cyclonedx20Derived.js +159 -0
- package/lib/inventory/cyclonedx20Parties.js +552 -0
- package/lib/inventory/cyclonedx20Read.js +768 -0
- package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
- package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
- package/lib/inventory/cyclonedx20ReadContext.js +380 -0
- package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
- package/lib/inventory/cyclonedx20ReadSections.js +599 -0
- package/lib/inventory/cyclonedx20Render.js +766 -0
- package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
- package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
- package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
- package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
- package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
- package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
- package/lib/inventory/cyclonedx20RenderSections.js +499 -0
- package/lib/inventory/cyclonedx20Tables.js +594 -0
- package/lib/inventory/deps.js +141 -13
- package/lib/inventory/dosai.js +375 -6
- package/lib/inventory/dosaiParsers.js +3 -6
- package/lib/inventory/dotnetRuntime.js +367 -0
- package/lib/inventory/ecosystemFormulationParsers.js +979 -0
- package/lib/inventory/envcontext.js +436 -48
- package/lib/inventory/evidenceUtils.js +13 -1
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +124 -14
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hbom.js +45 -6
- package/lib/inventory/hbomLoader.js +166 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -0
- package/lib/inventory/jvmLocalRepos.js +9 -2
- package/lib/inventory/jvmToolEnv.js +29 -0
- package/lib/inventory/licenseEnhancer.js +12 -0
- package/lib/inventory/mavenCentralMirror.js +534 -0
- package/lib/inventory/mcpConfigParser.js +5 -0
- package/lib/inventory/nodeBuildInfo.js +218 -0
- package/lib/inventory/nodeHostProbe.js +202 -0
- package/lib/inventory/occurrenceKey.js +90 -0
- package/lib/inventory/osPackageResolver.js +182 -0
- package/lib/inventory/phpBuildInfo.js +10 -59
- package/lib/inventory/phpHostProbe.js +32 -488
- package/lib/inventory/pqcMigration.js +426 -0
- package/lib/inventory/rubyBuildInfo.js +972 -0
- package/lib/inventory/rubyHostProbe.js +218 -0
- package/lib/inventory/rustFormulationParser.js +21 -10
- package/lib/inventory/scalaCoords.js +4 -3
- package/lib/inventory/scalaDedupe.js +440 -0
- package/lib/inventory/source.js +114 -8
- package/lib/managers/binary.js +337 -3
- package/lib/managers/docker.js +79 -0
- package/lib/parsers/huggingfaceManifest.js +9 -1
- package/lib/parsers/huggingfaceUtils.js +18 -0
- package/lib/parsers/kiroPermissions.js +166 -0
- package/lib/parsers/largeJson.js +28 -11
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +48 -35
- package/lib/stages/postgen/postgen.js +41 -1
- package/lib/stages/postgen/ruleEngine.js +13 -5
- package/lib/stages/postgen/specVersionCompat.js +76 -0
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +114 -57
- package/lib/validator/bomValidator.js +14 -2
- package/lib/validator/complianceEngine.js +30 -3
- package/lib/validator/complianceRules.js +1416 -4
- package/lib/validator/generated/validate-1.6.mjs +1 -1
- package/lib/validator/generated/validate-1.7.mjs +1 -1
- package/lib/validator/generated/validate-2.0.mjs +57554 -13851
- package/lib/validator/index.js +49 -8
- package/lib/validator/reporters/json.js +6 -0
- package/package.json +11 -10
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +6 -2
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +29 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +6 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +3 -18
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +15 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/boundedFile.d.ts +100 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -0
- package/types/lib/core/brotliProbe.d.ts +37 -0
- package/types/lib/core/brotliProbe.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +14 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +4 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/globPatterns.d.ts +70 -0
- package/types/lib/core/globPatterns.d.ts.map +1 -0
- package/types/lib/core/httpClient.d.ts +24 -0
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/lockPaths.d.ts +19 -0
- package/types/lib/core/lockPaths.d.ts.map +1 -0
- package/types/lib/core/paths.d.ts +9 -0
- package/types/lib/core/paths.d.ts.map +1 -1
- package/types/lib/core/processTree.d.ts +17 -4
- package/types/lib/core/processTree.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts +47 -8
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/toolRegistry.d.ts +36 -0
- package/types/lib/core/toolRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
- package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +294 -20
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
- package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +10 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +19 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +144 -12
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts +58 -16
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
- package/types/lib/ecosystems/venvProbe.d.ts +40 -0
- package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +120 -13
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/scalasem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +53 -19
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +20 -3
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/helpers/jssSigner.d.ts +288 -0
- package/types/lib/helpers/jssSigner.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +303 -19
- package/types/lib/helpers/vsixutils.d.ts.map +1 -1
- package/types/lib/inventory/agentBlueprint.d.ts +70 -0
- package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
- package/types/lib/inventory/agentToolNames.d.ts +51 -0
- package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/aiProviders.d.ts +85 -0
- package/types/lib/inventory/aiProviders.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +19 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/bomUtils.d.ts.map +1 -1
- package/types/lib/inventory/brotliDecoder.d.ts +58 -0
- package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
- package/types/lib/inventory/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
- package/types/lib/inventory/citations.d.ts +5 -0
- package/types/lib/inventory/citations.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/cyclonedx20.d.ts +99 -0
- package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +35 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +36 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
- package/types/lib/inventory/envcontext.d.ts +74 -12
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/gtfobins.d.ts.map +1 -1
- package/types/lib/inventory/hbom.d.ts +11 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomLoader.d.ts +54 -0
- package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
- package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
- package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
- package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
- package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
- package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/occurrenceKey.d.ts +52 -0
- package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +38 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
- package/types/lib/inventory/pqcMigration.d.ts +84 -0
- package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
- package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
- package/types/lib/inventory/scalaDedupe.d.ts +43 -0
- package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +25 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/kiroPermissions.d.ts +62 -0
- package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +4 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
- package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +7 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +12 -8
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts +5 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceEngine.d.ts +4 -4
- package/types/lib/validator/complianceEngine.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts +14 -2
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
|
@@ -4,19 +4,26 @@ import path, {
|
|
|
4
4
|
sep as _sep,
|
|
5
5
|
basename,
|
|
6
6
|
dirname,
|
|
7
|
+
isAbsolute,
|
|
7
8
|
join,
|
|
8
9
|
relative,
|
|
9
10
|
resolve,
|
|
11
|
+
win32,
|
|
10
12
|
} from "node:path";
|
|
11
13
|
|
|
12
14
|
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
13
15
|
import ssri from "ssri";
|
|
14
16
|
import { parse as _load, parseAllDocuments } from "yaml";
|
|
15
17
|
|
|
16
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
DEBUG_MODE,
|
|
20
|
+
isSecureMode,
|
|
21
|
+
readEnvironmentVariable,
|
|
22
|
+
} from "../core/activity.js";
|
|
17
23
|
import { recordDegradation } from "../core/buildLedger.js";
|
|
18
24
|
import { shouldFetchPackageMetadata } from "../core/env.js";
|
|
19
25
|
import { safeExistsSync } from "../core/fs.js";
|
|
26
|
+
import { isPathInsideProject } from "../core/lockPaths.js";
|
|
20
27
|
import { analyzeSuspiciousJsFile } from "../inventory/analyzer.js";
|
|
21
28
|
import { npmPurl, parsePurlLoose } from "../inventory/purl.js";
|
|
22
29
|
import Arborist from "../third-party/arborist/lib/index.js";
|
|
@@ -864,6 +871,95 @@ function undoPackageExtensions(tree) {
|
|
|
864
871
|
}
|
|
865
872
|
}
|
|
866
873
|
|
|
874
|
+
/**
|
|
875
|
+
* True for a home-relative path such as `~/pkg`; a `~1.2.3` range is not one.
|
|
876
|
+
*
|
|
877
|
+
* @param {string} value Path or specifier
|
|
878
|
+
*
|
|
879
|
+
* @returns {Boolean} True when the value starts with `~/` or `~\`, or is `~`
|
|
880
|
+
*/
|
|
881
|
+
function isHomeRelativePath(value) {
|
|
882
|
+
return value === "~" || value.startsWith("~/") || value.startsWith("~\\");
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Extract the filesystem path a lockfile `version` or `resolved` value names,
|
|
887
|
+
* if any. `file:` and `link:` specifiers and bare relative or absolute paths
|
|
888
|
+
* are paths; registry and git URLs, other scheme-bearing specifiers such as
|
|
889
|
+
* `npm:` aliases, and plain versions are not.
|
|
890
|
+
*
|
|
891
|
+
* @param {string} value Lockfile `version` or `resolved` value
|
|
892
|
+
*
|
|
893
|
+
* @returns {string|undefined} The path, or undefined when the value is not one
|
|
894
|
+
*/
|
|
895
|
+
function lockfileValuePath(value) {
|
|
896
|
+
if (typeof value !== "string" || !value) {
|
|
897
|
+
return undefined;
|
|
898
|
+
}
|
|
899
|
+
if (value.startsWith("file:") || value.startsWith("link:")) {
|
|
900
|
+
return value.slice(5);
|
|
901
|
+
}
|
|
902
|
+
if (
|
|
903
|
+
value.startsWith(".") ||
|
|
904
|
+
value.startsWith("/") ||
|
|
905
|
+
value.startsWith("\\") ||
|
|
906
|
+
isHomeRelativePath(value) ||
|
|
907
|
+
value.includes("\0") ||
|
|
908
|
+
win32.isAbsolute(value)
|
|
909
|
+
) {
|
|
910
|
+
return value;
|
|
911
|
+
}
|
|
912
|
+
return undefined;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Identify a lockfile node whose `version`, `resolved`, or link target names a
|
|
917
|
+
* path outside the project root (issue #3816). A crafted package-lock.json
|
|
918
|
+
* can use such values to point at arbitrary directories and to put traversal
|
|
919
|
+
* strings into purls; a legitimate one uses them for sibling `file:` and
|
|
920
|
+
* `npm link` dependencies.
|
|
921
|
+
*
|
|
922
|
+
* Arborist reports a link's `resolved` relative to the folder holding the
|
|
923
|
+
* link, so links are judged by their absolute `realpath`. Other nodes carry
|
|
924
|
+
* `resolved` already absolutised against the lockfile, and a path-valued
|
|
925
|
+
* `version` is written relative to the lockfile, so the lockfile directory is
|
|
926
|
+
* their only base.
|
|
927
|
+
*
|
|
928
|
+
* @param {Object} node Arborist node from the virtual tree
|
|
929
|
+
* @param {string} projectRoot Directory the lockfile paths are relative to
|
|
930
|
+
*
|
|
931
|
+
* @returns {string|undefined} The offending path, relative to projectRoot when
|
|
932
|
+
* it was absolute, or undefined when the node stays inside.
|
|
933
|
+
*/
|
|
934
|
+
function findOutsideNpmNodePath(node, projectRoot) {
|
|
935
|
+
const pathValues = [];
|
|
936
|
+
if (node?.isLink) {
|
|
937
|
+
if (typeof node.realpath === "string" && node.realpath) {
|
|
938
|
+
pathValues.push(node.realpath);
|
|
939
|
+
}
|
|
940
|
+
} else {
|
|
941
|
+
pathValues.push(lockfileValuePath(node?.resolved));
|
|
942
|
+
}
|
|
943
|
+
pathValues.push(lockfileValuePath(node?.version));
|
|
944
|
+
for (const pathValue of pathValues) {
|
|
945
|
+
if (pathValue === undefined) {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
// `~` expands to the home directory, which never sits inside the project,
|
|
949
|
+
// and a Windows drive or UNC path read on POSIX names a foreign root.
|
|
950
|
+
if (
|
|
951
|
+
isHomeRelativePath(pathValue) ||
|
|
952
|
+
(win32.isAbsolute(pathValue) && !isAbsolute(pathValue)) ||
|
|
953
|
+
!isPathInsideProject(pathValue, projectRoot)
|
|
954
|
+
) {
|
|
955
|
+
return isAbsolute(pathValue)
|
|
956
|
+
? relative(projectRoot, pathValue)
|
|
957
|
+
: pathValue;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return undefined;
|
|
961
|
+
}
|
|
962
|
+
|
|
867
963
|
/**
|
|
868
964
|
* Parse nodejs package lock file
|
|
869
965
|
*
|
|
@@ -880,6 +976,33 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
880
976
|
options.projectRoot || dirname(pkgLockFile),
|
|
881
977
|
);
|
|
882
978
|
const pkgSpecVersionCache = {};
|
|
979
|
+
// Nodes whose lockfile paths point outside the project (issue #3816),
|
|
980
|
+
// reported once per lockfile after the walk instead of once per entry. In
|
|
981
|
+
// secure mode they are omitted together with the edges into them; otherwise
|
|
982
|
+
// they are kept, since sibling `file:` and `npm link` dependencies are
|
|
983
|
+
// legitimate.
|
|
984
|
+
const outsidePathEntries = [];
|
|
985
|
+
const outsidePathByNode = new Map();
|
|
986
|
+
const projectRoot = dirname(resolve(pkgLockFile));
|
|
987
|
+
const outsideNpmNodePath = (node, rootNode) => {
|
|
988
|
+
if (!node || node === rootNode) {
|
|
989
|
+
return undefined;
|
|
990
|
+
}
|
|
991
|
+
if (!outsidePathByNode.has(node)) {
|
|
992
|
+
const value = findOutsideNpmNodePath(node, projectRoot);
|
|
993
|
+
outsidePathByNode.set(node, value);
|
|
994
|
+
if (value) {
|
|
995
|
+
outsidePathEntries.push({
|
|
996
|
+
name: node.packageName || node.name || node.location,
|
|
997
|
+
value,
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return outsidePathByNode.get(node);
|
|
1002
|
+
};
|
|
1003
|
+
// Every node is checked, so the default mode can still report what it kept.
|
|
1004
|
+
const isOmittedNpmNode = (node, rootNode) =>
|
|
1005
|
+
Boolean(outsideNpmNodePath(node, rootNode)) && isSecureMode;
|
|
883
1006
|
if (!safeExistsSync(pkgLockFile)) {
|
|
884
1007
|
return {
|
|
885
1008
|
pkgList,
|
|
@@ -899,6 +1022,11 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
899
1022
|
return { pkgList: [], dependenciesList: [] };
|
|
900
1023
|
}
|
|
901
1024
|
visited.add(node);
|
|
1025
|
+
// A dependency whose lockfile paths point outside the project is omitted
|
|
1026
|
+
// in secure mode (issue #3816).
|
|
1027
|
+
if (isOmittedNpmNode(node, rootNode)) {
|
|
1028
|
+
return { pkgList: [], dependenciesList: [] };
|
|
1029
|
+
}
|
|
902
1030
|
let pkgList = [];
|
|
903
1031
|
let dependenciesList = [];
|
|
904
1032
|
|
|
@@ -1315,6 +1443,9 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
1315
1443
|
const workspaceDependsOn = [];
|
|
1316
1444
|
if (node.fsChildren && node.fsChildren.size > 0) {
|
|
1317
1445
|
for (const workspaceNode of node.fsChildren) {
|
|
1446
|
+
if (isOmittedNpmNode(workspaceNode, rootNode)) {
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1318
1449
|
const {
|
|
1319
1450
|
pkgList: childPkgList,
|
|
1320
1451
|
dependenciesList: childDependenciesList,
|
|
@@ -1366,6 +1497,9 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
1366
1497
|
if (node !== rootNode && !node.edgesOut.size) {
|
|
1367
1498
|
for (const child of node.children) {
|
|
1368
1499
|
const childNode = child[1];
|
|
1500
|
+
if (isOmittedNpmNode(childNode, rootNode)) {
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1369
1503
|
const {
|
|
1370
1504
|
pkgList: childPkgList,
|
|
1371
1505
|
dependenciesList: childDependenciesList,
|
|
@@ -1398,6 +1532,9 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
1398
1532
|
// this handles the case when a node has a ["requires"] key
|
|
1399
1533
|
const pkgDependsOn = [];
|
|
1400
1534
|
for (const edge of node.edgesOut.values()) {
|
|
1535
|
+
if (isOmittedNpmNode(edge.to, rootNode)) {
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1401
1538
|
let targetVersion;
|
|
1402
1539
|
let targetName;
|
|
1403
1540
|
let foundMatch = false;
|
|
@@ -1666,6 +1803,26 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
1666
1803
|
options,
|
|
1667
1804
|
));
|
|
1668
1805
|
|
|
1806
|
+
if (outsidePathEntries.length) {
|
|
1807
|
+
const first = outsidePathEntries[0];
|
|
1808
|
+
if (isSecureMode) {
|
|
1809
|
+
console.warn(
|
|
1810
|
+
`WARNING: Omitted ${outsidePathEntries.length} entries of ${pkgLockFile} whose file or link paths point outside the project root; first entry "${first.name}" (${first.value}).`,
|
|
1811
|
+
);
|
|
1812
|
+
recordDegradation("js.lockfile-unsafe-path", {
|
|
1813
|
+
ecosystem: "npm",
|
|
1814
|
+
tool: "npm",
|
|
1815
|
+
impact: "transitive-deps",
|
|
1816
|
+
path: pkgLockFile,
|
|
1817
|
+
detail: `The lockfile carries ${outsidePathEntries.length} entries with file or link paths outside the project root; secure mode omits them.`,
|
|
1818
|
+
});
|
|
1819
|
+
} else {
|
|
1820
|
+
console.warn(
|
|
1821
|
+
`WARNING: ${pkgLockFile} has ${outsidePathEntries.length} entries whose file or link paths point outside the project root; first entry "${first.name}" (${first.value}). They are kept; secure mode omits them.`,
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1669
1826
|
if (shouldFetchPackageMetadata() && pkgList?.length) {
|
|
1670
1827
|
if (DEBUG_MODE) {
|
|
1671
1828
|
console.log(
|
|
@@ -1721,12 +1878,23 @@ export function yarnLockToIdentMap(lockData) {
|
|
|
1721
1878
|
// - npm:^6.0.0
|
|
1722
1879
|
// - npm:@types/ioredis@^4.28.10
|
|
1723
1880
|
// - npm:strip-ansi@^6.0.1
|
|
1881
|
+
// - npm:@example/left-lib
|
|
1724
1882
|
// See test cases with yarn3.lock and yarn6.lock
|
|
1725
1883
|
if (range?.startsWith("npm:")) {
|
|
1726
|
-
|
|
1727
|
-
|
|
1884
|
+
// The target of an npm: alias is `name[@range]`, so split at the
|
|
1885
|
+
// last "@" beyond index 0 and a scope is never mistaken for a
|
|
1886
|
+
// version separator. A bare scoped target names a package with
|
|
1887
|
+
// no version; "*" is the placeholder the version lookup uses for
|
|
1888
|
+
// such targets. A bare unscoped target is a tag or range of the
|
|
1889
|
+
// declared package ("npm:latest"), never an alias.
|
|
1890
|
+
const spec = range.slice("npm:".length);
|
|
1891
|
+
const sepIdx = spec.lastIndexOf("@");
|
|
1892
|
+
if (sepIdx > 0) {
|
|
1893
|
+
range = spec.slice(sepIdx + 1);
|
|
1894
|
+
} else if (spec.startsWith("@")) {
|
|
1895
|
+
range = "*";
|
|
1728
1896
|
} else {
|
|
1729
|
-
range =
|
|
1897
|
+
range = spec;
|
|
1730
1898
|
}
|
|
1731
1899
|
if (
|
|
1732
1900
|
!l.includes(" ") &&
|
|
@@ -1796,6 +1964,13 @@ function _parseYarnLine(l) {
|
|
|
1796
1964
|
if (aliasSepIdx > -1) {
|
|
1797
1965
|
// Aliased dependency: use the real package name from the target.
|
|
1798
1966
|
fullName = target.slice(0, aliasSepIdx);
|
|
1967
|
+
} else if (target.startsWith("@")) {
|
|
1968
|
+
// A bare scoped target names a package with no version, so the alias
|
|
1969
|
+
// points at the target itself; a key line closes with ":".
|
|
1970
|
+
const bareName = target.endsWith(":") ? target.slice(0, -1) : target;
|
|
1971
|
+
if (bareName) {
|
|
1972
|
+
fullName = bareName;
|
|
1973
|
+
}
|
|
1799
1974
|
}
|
|
1800
1975
|
}
|
|
1801
1976
|
}
|
|
@@ -1823,13 +1998,22 @@ function _resolveYarnDependencyBomRef(
|
|
|
1823
1998
|
let packageNameToUse = packageName;
|
|
1824
1999
|
let versionRangeToUse = versionRange;
|
|
1825
2000
|
if (versionRange.startsWith("npm:")) {
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
2001
|
+
// The target of an npm: alias is `name[@range]`, so split at the last "@"
|
|
2002
|
+
// beyond index 0 and a scope is never mistaken for a version separator.
|
|
2003
|
+
const spec = versionRange.slice("npm:".length);
|
|
2004
|
+
const sepIdx = spec.lastIndexOf("@");
|
|
2005
|
+
if (sepIdx > 0) {
|
|
2006
|
+
packageNameToUse = spec.slice(0, sepIdx);
|
|
2007
|
+
versionRangeToUse = spec.slice(sepIdx + 1);
|
|
2008
|
+
} else if (spec.startsWith("@")) {
|
|
2009
|
+
// A bare scoped target names a package with no version; the version is
|
|
2010
|
+
// looked up from identMap below.
|
|
2011
|
+
packageNameToUse = spec;
|
|
2012
|
+
versionRangeToUse = "*";
|
|
1831
2013
|
} else {
|
|
1832
|
-
|
|
2014
|
+
// A bare unscoped target is a tag or range of the declared package
|
|
2015
|
+
// ("npm:latest"), never an alias.
|
|
2016
|
+
versionRangeToUse = spec;
|
|
1833
2017
|
}
|
|
1834
2018
|
}
|
|
1835
2019
|
let resolvedVersion =
|
|
@@ -3178,6 +3362,126 @@ export function parsePnpmAliasRef(value) {
|
|
|
3178
3362
|
};
|
|
3179
3363
|
}
|
|
3180
3364
|
|
|
3365
|
+
/**
|
|
3366
|
+
* Read the patch file paths that pnpm applies, keyed by name@version.
|
|
3367
|
+
*
|
|
3368
|
+
* The lockfile records only the hash of each patch. The paths live in the
|
|
3369
|
+
* `patchedDependencies` of pnpm-workspace.yaml, or in the `pnpm` field of
|
|
3370
|
+
* package.json for projects that predate pnpm-workspace.yaml settings.
|
|
3371
|
+
*
|
|
3372
|
+
* @param {string} projectDir Directory holding the lockfile
|
|
3373
|
+
*
|
|
3374
|
+
* @returns {Object} Patch file path by name@version
|
|
3375
|
+
*/
|
|
3376
|
+
function readPnpmPatchFiles(projectDir) {
|
|
3377
|
+
const patchFiles = {};
|
|
3378
|
+
const sources = [
|
|
3379
|
+
["pnpm-workspace.yaml", (data) => _load(data)?.patchedDependencies],
|
|
3380
|
+
["package.json", (data) => JSON.parse(data)?.pnpm?.patchedDependencies],
|
|
3381
|
+
];
|
|
3382
|
+
for (const [fileName, extract] of sources) {
|
|
3383
|
+
const sourceFile = join(projectDir, fileName);
|
|
3384
|
+
if (!safeExistsSync(sourceFile)) {
|
|
3385
|
+
continue;
|
|
3386
|
+
}
|
|
3387
|
+
try {
|
|
3388
|
+
for (const [patchKey, patchValue] of Object.entries(
|
|
3389
|
+
extract(readFileSync(sourceFile, "utf-8")) || {},
|
|
3390
|
+
)) {
|
|
3391
|
+
if (
|
|
3392
|
+
typeof patchValue === "string" &&
|
|
3393
|
+
patchValue &&
|
|
3394
|
+
!patchFiles[patchKey]
|
|
3395
|
+
) {
|
|
3396
|
+
patchFiles[patchKey] = patchValue;
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
} catch (_e) {
|
|
3400
|
+
// A malformed settings file must not break the lockfile parse
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
return patchFiles;
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3406
|
+
/**
|
|
3407
|
+
* Decide whether a parsed pnpm lockfile document is a boot document.
|
|
3408
|
+
*
|
|
3409
|
+
* Since pnpm 12 a lockfile can hold two YAML documents. The first, the boot
|
|
3410
|
+
* document, records the project's config dependencies and the resolved pnpm
|
|
3411
|
+
* version under its importers, and the last is the project lockfile. The
|
|
3412
|
+
* boot document is recognised by these keys rather than by its position
|
|
3413
|
+
* alone, and tolerates either key missing.
|
|
3414
|
+
*
|
|
3415
|
+
* See: https://pnpm.io/lockfile
|
|
3416
|
+
*
|
|
3417
|
+
* @param {Object} docObj Parsed YAML document
|
|
3418
|
+
*
|
|
3419
|
+
* @returns {boolean} true when the document looks like a boot document
|
|
3420
|
+
*/
|
|
3421
|
+
export function isPnpmBootDocument(docObj) {
|
|
3422
|
+
const importers = docObj?.importers;
|
|
3423
|
+
if (!importers || typeof importers !== "object") {
|
|
3424
|
+
return false;
|
|
3425
|
+
}
|
|
3426
|
+
for (const importer of Object.values(importers)) {
|
|
3427
|
+
if (
|
|
3428
|
+
importer &&
|
|
3429
|
+
typeof importer === "object" &&
|
|
3430
|
+
("configDependencies" in importer ||
|
|
3431
|
+
"packageManagerDependencies" in importer)
|
|
3432
|
+
) {
|
|
3433
|
+
return true;
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
return false;
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
/**
|
|
3440
|
+
* Find which pnpm catalog pinned each dependency of the workspace.
|
|
3441
|
+
*
|
|
3442
|
+
* A lockfile importer records the specifier next to the resolved version,
|
|
3443
|
+
* and a specifier of `catalog:` or `catalog:<name>` means the version came
|
|
3444
|
+
* from the default or the named catalog of pnpm-workspace.yaml. The lockfile
|
|
3445
|
+
* is enough, so the workspace file is not read.
|
|
3446
|
+
*
|
|
3447
|
+
* @param {Object} importers The importers section of a pnpm lockfile
|
|
3448
|
+
*
|
|
3449
|
+
* @returns {Object} Sorted catalog names, keyed by name@version
|
|
3450
|
+
*/
|
|
3451
|
+
function collectPnpmCatalogUsage(importers) {
|
|
3452
|
+
const usage = {};
|
|
3453
|
+
if (!importers || typeof importers !== "object") {
|
|
3454
|
+
return {};
|
|
3455
|
+
}
|
|
3456
|
+
for (const importer of Object.values(importers)) {
|
|
3457
|
+
for (const section of [
|
|
3458
|
+
"dependencies",
|
|
3459
|
+
"devDependencies",
|
|
3460
|
+
"optionalDependencies",
|
|
3461
|
+
]) {
|
|
3462
|
+
for (const [depName, depEntry] of Object.entries(
|
|
3463
|
+
importer?.[section] || {},
|
|
3464
|
+
)) {
|
|
3465
|
+
const specifier = depEntry?.specifier;
|
|
3466
|
+
if (
|
|
3467
|
+
typeof specifier !== "string" ||
|
|
3468
|
+
!specifier.startsWith("catalog:") ||
|
|
3469
|
+
typeof depEntry.version !== "string"
|
|
3470
|
+
) {
|
|
3471
|
+
continue;
|
|
3472
|
+
}
|
|
3473
|
+
const catalogName = specifier.slice("catalog:".length).trim();
|
|
3474
|
+
const key = `${depName}@${depEntry.version.split("(")[0]}`;
|
|
3475
|
+
usage[key] = usage[key] || new Set();
|
|
3476
|
+
usage[key].add(catalogName || "default");
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
return Object.fromEntries(
|
|
3481
|
+
Object.entries(usage).map(([key, names]) => [key, [...names].sort()]),
|
|
3482
|
+
);
|
|
3483
|
+
}
|
|
3484
|
+
|
|
3181
3485
|
/**
|
|
3182
3486
|
* Parse nodejs pnpm lock file
|
|
3183
3487
|
*
|
|
@@ -3185,7 +3489,6 @@ export function parsePnpmAliasRef(value) {
|
|
|
3185
3489
|
* @param {Object} parentComponent parent component
|
|
3186
3490
|
* @param {Array[String]} workspacePackages Workspace packages
|
|
3187
3491
|
* @param {Object} workspaceSrcFiles Workspace package.json files
|
|
3188
|
-
* @param {Object} _workspaceCatalogs Workspace catalogs
|
|
3189
3492
|
* @param {Object} _workspaceDirectDeps Direct dependencies of each workspace
|
|
3190
3493
|
* @param {Object} depsWorkspaceRefs Workspace references for each dependency
|
|
3191
3494
|
* @param {string} projectRoot Root path used to relativize pnpm-lock evidence paths
|
|
@@ -3195,13 +3498,13 @@ export async function parsePnpmLock(
|
|
|
3195
3498
|
parentComponent = null,
|
|
3196
3499
|
workspacePackages = [],
|
|
3197
3500
|
workspaceSrcFiles = {},
|
|
3198
|
-
_workspaceCatalogs = {},
|
|
3199
3501
|
_workspaceDirectDeps = {},
|
|
3200
3502
|
depsWorkspaceRefs = {},
|
|
3201
3503
|
projectRoot = undefined,
|
|
3202
3504
|
) {
|
|
3203
3505
|
let pkgList = [];
|
|
3204
3506
|
const dependenciesList = [];
|
|
3507
|
+
const formulationList = [];
|
|
3205
3508
|
const pnpmLockDir = dirname(pnpmLock);
|
|
3206
3509
|
const pnpmEvidenceRoot = projectRoot ? resolve(projectRoot) : pnpmLockDir;
|
|
3207
3510
|
const npmrcConfig = loadNpmrcConfig(projectRoot || pnpmLockDir);
|
|
@@ -3245,13 +3548,15 @@ export async function parsePnpmLock(
|
|
|
3245
3548
|
}
|
|
3246
3549
|
if (safeExistsSync(pnpmLock)) {
|
|
3247
3550
|
const lockData = readFileSync(pnpmLock, "utf8");
|
|
3248
|
-
|
|
3249
|
-
if (!
|
|
3551
|
+
const yamlDocs = parseAllDocuments(lockData);
|
|
3552
|
+
if (!yamlDocs) {
|
|
3250
3553
|
return {};
|
|
3251
3554
|
}
|
|
3252
|
-
|
|
3555
|
+
let yamlObj = yamlDocs;
|
|
3556
|
+
if (Array.isArray(yamlDocs)) {
|
|
3253
3557
|
try {
|
|
3254
|
-
|
|
3558
|
+
// The project lockfile is always the last document
|
|
3559
|
+
yamlObj = yamlDocs[yamlDocs.length - 1].toJS();
|
|
3255
3560
|
} catch (_e) {
|
|
3256
3561
|
recordDegradation("js.pnpm-lock-unparseable", {
|
|
3257
3562
|
ecosystem: "npm",
|
|
@@ -3265,12 +3570,44 @@ export async function parsePnpmLock(
|
|
|
3265
3570
|
return {};
|
|
3266
3571
|
}
|
|
3267
3572
|
}
|
|
3573
|
+
// Earlier documents can hold a boot document with the project's config
|
|
3574
|
+
// dependencies and the resolved pnpm version. It is detected by shape,
|
|
3575
|
+
// and a malformed document must not break the project parse.
|
|
3576
|
+
let bootDocObj = null;
|
|
3577
|
+
if (Array.isArray(yamlDocs) && yamlDocs.length > 1) {
|
|
3578
|
+
for (const yamlDoc of yamlDocs.slice(0, -1)) {
|
|
3579
|
+
let docObj;
|
|
3580
|
+
try {
|
|
3581
|
+
docObj = yamlDoc.toJS();
|
|
3582
|
+
} catch (_e) {
|
|
3583
|
+
continue;
|
|
3584
|
+
}
|
|
3585
|
+
if (isPnpmBootDocument(docObj)) {
|
|
3586
|
+
bootDocObj = docObj;
|
|
3587
|
+
break;
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3268
3591
|
lockfileVersion = yamlObj.lockfileVersion;
|
|
3269
3592
|
try {
|
|
3270
3593
|
lockfileVersion = Number.parseFloat(lockfileVersion, 10);
|
|
3271
3594
|
} catch (_e) {
|
|
3272
3595
|
// ignore parse errors
|
|
3273
3596
|
}
|
|
3597
|
+
// The lockfile's patchedDependencies maps name@version to a hash of the
|
|
3598
|
+
// patch file; the patch file path itself lives in pnpm-workspace.yaml.
|
|
3599
|
+
const patchedDependencies = {};
|
|
3600
|
+
for (const [patchKey, patchValue] of Object.entries(
|
|
3601
|
+
yamlObj.patchedDependencies || {},
|
|
3602
|
+
)) {
|
|
3603
|
+
if (typeof patchValue === "string" && patchValue) {
|
|
3604
|
+
patchedDependencies[patchKey] = patchValue;
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
const patchFiles = Object.keys(patchedDependencies).length
|
|
3608
|
+
? readPnpmPatchFiles(pnpmLockDir)
|
|
3609
|
+
: {};
|
|
3610
|
+
const catalogUsage = collectPnpmCatalogUsage(yamlObj.importers);
|
|
3274
3611
|
Object.assign(
|
|
3275
3612
|
gitPkgRefs,
|
|
3276
3613
|
buildPnpmGitPkgRefs(
|
|
@@ -3346,6 +3683,11 @@ export async function parsePnpmLock(
|
|
|
3346
3683
|
githubServerHost,
|
|
3347
3684
|
npmrcConfig,
|
|
3348
3685
|
);
|
|
3686
|
+
// Dev dependencies are still edges from the root: a monorepo root often
|
|
3687
|
+
// declares only devDependencies (the build tooling), and without this
|
|
3688
|
+
// edge the root component under-reports what the install pulls in.
|
|
3689
|
+
// See #2020.
|
|
3690
|
+
ddeplist.add(dpurl);
|
|
3349
3691
|
possibleDevelopmentDeps[dpurl] = true;
|
|
3350
3692
|
}
|
|
3351
3693
|
// Find the root optional and peer dependencies
|
|
@@ -3414,6 +3756,15 @@ export async function parsePnpmLock(
|
|
|
3414
3756
|
{};
|
|
3415
3757
|
const componentPeerDeps =
|
|
3416
3758
|
yamlObj?.importers[importedComponentName]["peerDependencies"] || {};
|
|
3759
|
+
if (
|
|
3760
|
+
DEBUG_MODE &&
|
|
3761
|
+
!Object.keys(componentDeps).length &&
|
|
3762
|
+
Object.keys(componentDevDeps).length
|
|
3763
|
+
) {
|
|
3764
|
+
console.log(
|
|
3765
|
+
`The workspace ${importedComponentName} declares only devDependencies, so its dependency tree is scoped as development. --required-only will exclude these components.`,
|
|
3766
|
+
);
|
|
3767
|
+
}
|
|
3417
3768
|
let compPurl;
|
|
3418
3769
|
let pkgSrcFile;
|
|
3419
3770
|
let fallbackMode = true;
|
|
@@ -3915,6 +4266,35 @@ export async function parsePnpmLock(
|
|
|
3915
4266
|
value: deprecatedMessage,
|
|
3916
4267
|
});
|
|
3917
4268
|
}
|
|
4269
|
+
const patchKey = `${group ? `${group}/${name}` : name}@${version}`;
|
|
4270
|
+
const patchHash = patchedDependencies[patchKey];
|
|
4271
|
+
if (patchHash) {
|
|
4272
|
+
// The component keeps its upstream purl, so vulnerability matching
|
|
4273
|
+
// sees the published version the patch was applied to. See #803.
|
|
4274
|
+
properties.push({
|
|
4275
|
+
name: "cdx:pnpm:patched",
|
|
4276
|
+
value: "true",
|
|
4277
|
+
});
|
|
4278
|
+
properties.push({
|
|
4279
|
+
name: "cdx:pnpm:patchHash",
|
|
4280
|
+
value: patchHash,
|
|
4281
|
+
});
|
|
4282
|
+
if (patchFiles[patchKey]) {
|
|
4283
|
+
properties.push({
|
|
4284
|
+
name: "cdx:pnpm:patchFile",
|
|
4285
|
+
value: patchFiles[patchKey],
|
|
4286
|
+
});
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
const catalogNames = catalogUsage[patchKey];
|
|
4290
|
+
if (catalogNames?.length) {
|
|
4291
|
+
// The version is pinned by a catalog entry in pnpm-workspace.yaml
|
|
4292
|
+
// and not by the importer's own package.json. See #4611.
|
|
4293
|
+
properties.push({
|
|
4294
|
+
name: "cdx:pnpm:catalog",
|
|
4295
|
+
value: catalogNames.join(","),
|
|
4296
|
+
});
|
|
4297
|
+
}
|
|
3918
4298
|
const binary_metadata = { os, cpu, libc };
|
|
3919
4299
|
Object.entries(binary_metadata).forEach(([key, value]) => {
|
|
3920
4300
|
if (!value) return;
|
|
@@ -4045,6 +4425,108 @@ export async function parsePnpmLock(
|
|
|
4045
4425
|
}
|
|
4046
4426
|
}
|
|
4047
4427
|
}
|
|
4428
|
+
// Config dependencies and the resolved pnpm version live in the boot
|
|
4429
|
+
// document. pnpm installs config dependencies before anything else and
|
|
4430
|
+
// they may run hooks, so they are reported as components even though
|
|
4431
|
+
// they are not part of the project dependency graph.
|
|
4432
|
+
if (bootDocObj) {
|
|
4433
|
+
const bootImporter = bootDocObj.importers?.["."];
|
|
4434
|
+
const configDeps = bootImporter?.configDependencies;
|
|
4435
|
+
if (configDeps && typeof configDeps === "object") {
|
|
4436
|
+
for (const [depName, depEntry] of Object.entries(configDeps)) {
|
|
4437
|
+
const version =
|
|
4438
|
+
typeof depEntry === "string" ? depEntry : depEntry?.version;
|
|
4439
|
+
if (!depName || !version || protoLockKey.test(depName)) {
|
|
4440
|
+
continue;
|
|
4441
|
+
}
|
|
4442
|
+
let group = "";
|
|
4443
|
+
let name = depName;
|
|
4444
|
+
const slashIdx = name.indexOf("/");
|
|
4445
|
+
if (name.startsWith("@") && slashIdx > 0) {
|
|
4446
|
+
group = name.slice(0, slashIdx);
|
|
4447
|
+
name = name.slice(slashIdx + 1);
|
|
4448
|
+
}
|
|
4449
|
+
const purlString = build({
|
|
4450
|
+
type: "npm",
|
|
4451
|
+
namespace: group || null,
|
|
4452
|
+
name,
|
|
4453
|
+
version,
|
|
4454
|
+
});
|
|
4455
|
+
const bomRef = decodeURIComponent(purlString);
|
|
4456
|
+
const bootLockKey = `${depName}@${version}`;
|
|
4457
|
+
const bootEntry =
|
|
4458
|
+
bootDocObj.packages?.[bootLockKey] ||
|
|
4459
|
+
bootDocObj.snapshots?.[bootLockKey];
|
|
4460
|
+
const existingPkg = pkgRefMap[bomRef];
|
|
4461
|
+
if (existingPkg) {
|
|
4462
|
+
// The package is also a runtime dependency of the project, so
|
|
4463
|
+
// keep that component and record the additional config role.
|
|
4464
|
+
existingPkg.properties = existingPkg.properties || [];
|
|
4465
|
+
if (
|
|
4466
|
+
!existingPkg.properties.some(
|
|
4467
|
+
(p) => p.name === "cdx:pnpm:configDependency",
|
|
4468
|
+
)
|
|
4469
|
+
) {
|
|
4470
|
+
existingPkg.properties.push({
|
|
4471
|
+
name: "cdx:pnpm:configDependency",
|
|
4472
|
+
value: "true",
|
|
4473
|
+
});
|
|
4474
|
+
}
|
|
4475
|
+
} else {
|
|
4476
|
+
const thePkg = {
|
|
4477
|
+
group,
|
|
4478
|
+
name,
|
|
4479
|
+
version,
|
|
4480
|
+
purl: purlString,
|
|
4481
|
+
"bom-ref": bomRef,
|
|
4482
|
+
type: "library",
|
|
4483
|
+
_integrity: bootEntry?.resolution?.integrity,
|
|
4484
|
+
properties: [
|
|
4485
|
+
{ name: "internal:SrcFile", value: pnpmLockSrcFile },
|
|
4486
|
+
{ name: "cdx:pnpm:configDependency", value: "true" },
|
|
4487
|
+
],
|
|
4488
|
+
evidence: {
|
|
4489
|
+
identity: {
|
|
4490
|
+
field: "purl",
|
|
4491
|
+
confidence: 1,
|
|
4492
|
+
methods: [
|
|
4493
|
+
{
|
|
4494
|
+
technique: "manifest-analysis",
|
|
4495
|
+
confidence: 1,
|
|
4496
|
+
value: pnpmLockSrcFile,
|
|
4497
|
+
},
|
|
4498
|
+
],
|
|
4499
|
+
},
|
|
4500
|
+
},
|
|
4501
|
+
};
|
|
4502
|
+
pkgList.push(thePkg);
|
|
4503
|
+
pkgRefMap[bomRef] = thePkg;
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
const pnpmVersion =
|
|
4508
|
+
bootImporter?.packageManagerDependencies?.pnpm?.version;
|
|
4509
|
+
if (pnpmVersion) {
|
|
4510
|
+
// The pnpm version resolved for this project belongs next to the
|
|
4511
|
+
// other build tools in the formulation section. Like the toolchain
|
|
4512
|
+
// components there, it carries no bom-ref so that it cannot collide
|
|
4513
|
+
// with the document's bom-ref namespace.
|
|
4514
|
+
formulationList.push({
|
|
4515
|
+
type: "application",
|
|
4516
|
+
name: "pnpm",
|
|
4517
|
+
version: pnpmVersion,
|
|
4518
|
+
purl: build({
|
|
4519
|
+
type: "npm",
|
|
4520
|
+
namespace: null,
|
|
4521
|
+
name: "pnpm",
|
|
4522
|
+
version: pnpmVersion,
|
|
4523
|
+
}),
|
|
4524
|
+
description:
|
|
4525
|
+
"pnpm version resolved for this project, read from the lockfile boot document.",
|
|
4526
|
+
properties: [{ name: "internal:SrcFile", value: pnpmLockSrcFile }],
|
|
4527
|
+
});
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4048
4530
|
}
|
|
4049
4531
|
// We need to repeat optional packages detection
|
|
4050
4532
|
if (Object.keys(possibleOptionalDeps).length) {
|
|
@@ -4192,17 +4674,18 @@ export async function parsePnpmLock(
|
|
|
4192
4674
|
);
|
|
4193
4675
|
}
|
|
4194
4676
|
pkgList = await getNpmMetadata(pkgList, undefined, { npmrcConfig });
|
|
4195
|
-
return {
|
|
4196
|
-
pkgList,
|
|
4197
|
-
dependenciesList,
|
|
4198
|
-
parentSubComponents,
|
|
4199
|
-
};
|
|
4200
4677
|
}
|
|
4201
|
-
|
|
4678
|
+
const result = {
|
|
4202
4679
|
pkgList,
|
|
4203
4680
|
dependenciesList,
|
|
4204
4681
|
parentSubComponents,
|
|
4205
4682
|
};
|
|
4683
|
+
// Reported only when the boot document contributed, so that lockfiles
|
|
4684
|
+
// without one keep producing the result shape they always did.
|
|
4685
|
+
if (formulationList.length) {
|
|
4686
|
+
result.formulationList = formulationList;
|
|
4687
|
+
}
|
|
4688
|
+
return result;
|
|
4206
4689
|
}
|
|
4207
4690
|
|
|
4208
4691
|
/**
|