@cdxgen/cdxgen 13.5.0 → 13.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -10
- package/bin/audit.js +7 -4
- package/bin/cdxgen.js +260 -37
- package/bin/convert.js +146 -22
- package/bin/evinse.js +28 -3
- package/bin/repl.js +47 -3
- package/bin/sign.js +211 -34
- package/bin/tracebom.js +18 -6
- package/bin/validate.js +24 -3
- package/bin/verify.js +113 -5
- package/data/behavior-taxonomy.schema.json +1491 -0
- package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
- package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
- package/data/license-aliases.json +24 -2
- package/data/pqc-migration.json +239 -0
- package/data/remediations.json +176 -0
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -0
- package/data/rules/cbom-security.yaml +4 -3
- package/data/rules/package-integrity.yaml +28 -0
- package/data/rules/vscode-extensions.yaml +75 -0
- package/data/spdx-license-list.json +121 -7
- package/data/spdx-licenses.json +69 -0
- package/data/spdx.schema.json +845 -2
- package/lib/audit/index.js +25 -3
- package/lib/cli/bomAssembly.js +172 -4
- package/lib/cli/cliOptions.js +121 -17
- package/lib/cli/index.js +720 -902
- package/lib/cli/jsBom.js +156 -64
- package/lib/cli/jvmBom.js +602 -86
- package/lib/cli/managedBom.js +161 -19
- package/lib/cli/nativeBom.js +52 -5
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +34 -0
- package/lib/core/fs.js +138 -3
- package/lib/core/globPatterns.js +218 -0
- package/lib/core/httpClient.js +98 -1
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +100 -8
- package/lib/core/projectConfig.js +332 -25
- package/lib/core/toolRegistry.js +184 -0
- package/lib/ecosystems/cppEvidence.js +263 -106
- package/lib/ecosystems/cppIncludeResolver.js +16 -5
- package/lib/ecosystems/denoutils.js +2 -2
- package/lib/ecosystems/ecosystems.js +25 -10
- package/lib/ecosystems/gemHostProbe.js +301 -0
- package/lib/ecosystems/golem.js +3 -8
- package/lib/ecosystems/gradleutils.js +1909 -229
- package/lib/ecosystems/jsEvidence.js +90 -38
- package/lib/ecosystems/kosi.js +3 -5
- package/lib/ecosystems/npmVersionResolution.js +376 -0
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-misc.js +209 -20
- package/lib/ecosystems/parsers-python.js +178 -30
- package/lib/ecosystems/parsers-rust.js +117 -15
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/remote/huggingface.js +22 -0
- package/lib/ecosystems/rubyutils.js +538 -31
- package/lib/ecosystems/rusi.js +2 -4
- package/lib/ecosystems/scalacliutils.js +2 -0
- package/lib/ecosystems/scalasem.js +798 -152
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +536 -71
- package/lib/evinser/scalasem.js +5 -1
- package/lib/helpers/agenticToolutils.js +525 -94
- package/lib/helpers/bomSigner.js +43 -9
- package/lib/helpers/jssSigner.js +1276 -0
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/agentBlueprint.js +193 -0
- package/lib/inventory/agentToolNames.js +145 -0
- package/lib/inventory/aiCollector.js +153 -7
- package/lib/inventory/aiInventory.js +44 -8
- package/lib/inventory/aiProviders.js +324 -0
- package/lib/inventory/atomUtils.js +77 -0
- package/lib/inventory/bomUtils.js +8 -1
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
- package/lib/inventory/citations.js +31 -2
- package/lib/inventory/communityAiConfigParser.js +978 -80
- package/lib/inventory/cyclonedx20.js +152 -0
- package/lib/inventory/cyclonedx20Common.js +286 -0
- package/lib/inventory/cyclonedx20Derived.js +159 -0
- package/lib/inventory/cyclonedx20Parties.js +552 -0
- package/lib/inventory/cyclonedx20Read.js +768 -0
- package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
- package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
- package/lib/inventory/cyclonedx20ReadContext.js +380 -0
- package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
- package/lib/inventory/cyclonedx20ReadSections.js +599 -0
- package/lib/inventory/cyclonedx20Render.js +766 -0
- package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
- package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
- package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
- package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
- package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
- package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
- package/lib/inventory/cyclonedx20RenderSections.js +499 -0
- package/lib/inventory/cyclonedx20Tables.js +594 -0
- package/lib/inventory/deps.js +141 -13
- package/lib/inventory/dosai.js +375 -6
- package/lib/inventory/dosaiParsers.js +3 -6
- package/lib/inventory/dotnetRuntime.js +367 -0
- package/lib/inventory/ecosystemFormulationParsers.js +979 -0
- package/lib/inventory/envcontext.js +436 -48
- package/lib/inventory/evidenceUtils.js +13 -1
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +124 -14
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hbom.js +45 -6
- package/lib/inventory/hbomLoader.js +166 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -0
- package/lib/inventory/jvmLocalRepos.js +9 -2
- package/lib/inventory/jvmToolEnv.js +29 -0
- package/lib/inventory/licenseEnhancer.js +12 -0
- package/lib/inventory/mavenCentralMirror.js +534 -0
- package/lib/inventory/mcpConfigParser.js +5 -0
- package/lib/inventory/nodeBuildInfo.js +218 -0
- package/lib/inventory/nodeHostProbe.js +202 -0
- package/lib/inventory/occurrenceKey.js +90 -0
- package/lib/inventory/osPackageResolver.js +182 -0
- package/lib/inventory/phpBuildInfo.js +10 -59
- package/lib/inventory/phpHostProbe.js +32 -488
- package/lib/inventory/pqcMigration.js +426 -0
- package/lib/inventory/rubyBuildInfo.js +972 -0
- package/lib/inventory/rubyHostProbe.js +218 -0
- package/lib/inventory/rustFormulationParser.js +21 -10
- package/lib/inventory/scalaCoords.js +4 -3
- package/lib/inventory/scalaDedupe.js +440 -0
- package/lib/inventory/source.js +114 -8
- package/lib/managers/binary.js +337 -3
- package/lib/managers/docker.js +79 -0
- package/lib/parsers/huggingfaceManifest.js +9 -1
- package/lib/parsers/huggingfaceUtils.js +18 -0
- package/lib/parsers/kiroPermissions.js +166 -0
- package/lib/parsers/largeJson.js +28 -11
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +48 -35
- package/lib/stages/postgen/postgen.js +41 -1
- package/lib/stages/postgen/ruleEngine.js +13 -5
- package/lib/stages/postgen/specVersionCompat.js +76 -0
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +114 -57
- package/lib/validator/bomValidator.js +14 -2
- package/lib/validator/complianceEngine.js +30 -3
- package/lib/validator/complianceRules.js +1416 -4
- package/lib/validator/generated/validate-1.6.mjs +1 -1
- package/lib/validator/generated/validate-1.7.mjs +1 -1
- package/lib/validator/generated/validate-2.0.mjs +57554 -13851
- package/lib/validator/index.js +49 -8
- package/lib/validator/reporters/json.js +6 -0
- package/package.json +11 -10
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +6 -2
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +29 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +6 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +3 -18
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +15 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/boundedFile.d.ts +100 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -0
- package/types/lib/core/brotliProbe.d.ts +37 -0
- package/types/lib/core/brotliProbe.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +14 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +4 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/globPatterns.d.ts +70 -0
- package/types/lib/core/globPatterns.d.ts.map +1 -0
- package/types/lib/core/httpClient.d.ts +24 -0
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/lockPaths.d.ts +19 -0
- package/types/lib/core/lockPaths.d.ts.map +1 -0
- package/types/lib/core/paths.d.ts +9 -0
- package/types/lib/core/paths.d.ts.map +1 -1
- package/types/lib/core/processTree.d.ts +17 -4
- package/types/lib/core/processTree.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts +47 -8
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/toolRegistry.d.ts +36 -0
- package/types/lib/core/toolRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
- package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +294 -20
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
- package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +10 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +19 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +144 -12
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts +58 -16
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
- package/types/lib/ecosystems/venvProbe.d.ts +40 -0
- package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +120 -13
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/scalasem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +53 -19
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +20 -3
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/helpers/jssSigner.d.ts +288 -0
- package/types/lib/helpers/jssSigner.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +303 -19
- package/types/lib/helpers/vsixutils.d.ts.map +1 -1
- package/types/lib/inventory/agentBlueprint.d.ts +70 -0
- package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
- package/types/lib/inventory/agentToolNames.d.ts +51 -0
- package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/aiProviders.d.ts +85 -0
- package/types/lib/inventory/aiProviders.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +19 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/bomUtils.d.ts.map +1 -1
- package/types/lib/inventory/brotliDecoder.d.ts +58 -0
- package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
- package/types/lib/inventory/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
- package/types/lib/inventory/citations.d.ts +5 -0
- package/types/lib/inventory/citations.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/cyclonedx20.d.ts +99 -0
- package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +35 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +36 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
- package/types/lib/inventory/envcontext.d.ts +74 -12
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/gtfobins.d.ts.map +1 -1
- package/types/lib/inventory/hbom.d.ts +11 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomLoader.d.ts +54 -0
- package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
- package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
- package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
- package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
- package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
- package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/occurrenceKey.d.ts +52 -0
- package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +38 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
- package/types/lib/inventory/pqcMigration.d.ts +84 -0
- package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
- package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
- package/types/lib/inventory/scalaDedupe.d.ts +43 -0
- package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +25 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/kiroPermissions.d.ts +62 -0
- package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +4 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
- package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +7 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +12 -8
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts +5 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceEngine.d.ts +4 -4
- package/types/lib/validator/complianceEngine.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts +14 -2
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
package/lib/core/fs.js
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
} from "./activity.js";
|
|
44
44
|
import { thoughtLog, traceLog } from "./logger.js";
|
|
45
45
|
import { isWin } from "./paths.js";
|
|
46
|
+
import { recordExecutedTool } from "./toolRegistry.js";
|
|
46
47
|
|
|
47
48
|
function getArchiveSourceByteSize(sourcePath) {
|
|
48
49
|
if (!sourcePath || !safeExistsSync(sourcePath)) {
|
|
@@ -139,10 +140,126 @@ export function safeIsExecutableSync(filePath) {
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
// writeFileSync flags that create or replace a file, which the no-follow
|
|
144
|
+
// write below reproduces with open(2) flags.
|
|
145
|
+
const NO_FOLLOW_WRITE_FLAGS = new Set([
|
|
146
|
+
"a",
|
|
147
|
+
"a+",
|
|
148
|
+
"as",
|
|
149
|
+
"as+",
|
|
150
|
+
"ax",
|
|
151
|
+
"ax+",
|
|
152
|
+
"w",
|
|
153
|
+
"w+",
|
|
154
|
+
"wx",
|
|
155
|
+
"wx+",
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Determine whether a path names a device or a process file, such as
|
|
160
|
+
* /dev/stdout, /dev/null or the /dev/fd entry of a shell process
|
|
161
|
+
* substitution. These are symbolic links an operator names on purpose, so
|
|
162
|
+
* they are written through as given.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} filePath File path to check.
|
|
165
|
+
* @returns {boolean} True for paths under /dev or /proc on POSIX systems.
|
|
166
|
+
*/
|
|
167
|
+
function isDeviceOrProcessPath(filePath) {
|
|
168
|
+
if (isWin) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
const absolutePath = resolve(filePath);
|
|
172
|
+
return absolutePath.startsWith("/dev/") || absolutePath.startsWith("/proc/");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Convert a writeFileSync flag string into open(2) flags.
|
|
177
|
+
*
|
|
178
|
+
* @param {string} flag Flag such as "w", "a" or "wx".
|
|
179
|
+
* @returns {number} Numeric open flags.
|
|
180
|
+
*/
|
|
181
|
+
function writeOpenFlags(flag) {
|
|
182
|
+
let flags =
|
|
183
|
+
fsConstants.O_CREAT |
|
|
184
|
+
(flag.includes("+") ? fsConstants.O_RDWR : fsConstants.O_WRONLY) |
|
|
185
|
+
(flag.startsWith("a") ? fsConstants.O_APPEND : fsConstants.O_TRUNC);
|
|
186
|
+
if (flag.includes("x")) {
|
|
187
|
+
flags |= fsConstants.O_EXCL;
|
|
188
|
+
}
|
|
189
|
+
return flags;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Write a file without following a symbolic link in the last component of
|
|
194
|
+
* its path. A link found there is removed and a regular file is created in
|
|
195
|
+
* its place, so a link committed to a scanned repository under a name cdxgen
|
|
196
|
+
* writes, such as bom.json, cannot send the write to the file it points at.
|
|
197
|
+
* The open uses O_NOFOLLOW where the platform has it, so a link that appears
|
|
198
|
+
* after the check makes the write fail rather than follow it. Links in the
|
|
199
|
+
* parent directories are followed as usual.
|
|
200
|
+
*
|
|
201
|
+
* @param {string} filePath File path to write.
|
|
202
|
+
* @param {string|Buffer|Uint8Array} data Data to write.
|
|
203
|
+
* @param {Object|string} [options] writeFileSync options or encoding.
|
|
204
|
+
* @returns {boolean} True when a symbolic link was replaced.
|
|
205
|
+
*/
|
|
206
|
+
function writeFileWithoutFollowingLink(filePath, data, options) {
|
|
207
|
+
const flag =
|
|
208
|
+
(typeof options === "object" && options?.flag
|
|
209
|
+
? String(options.flag)
|
|
210
|
+
: undefined) || "w";
|
|
211
|
+
if (
|
|
212
|
+
typeof filePath !== "string" ||
|
|
213
|
+
!NO_FOLLOW_WRITE_FLAGS.has(flag) ||
|
|
214
|
+
isDeviceOrProcessPath(filePath)
|
|
215
|
+
) {
|
|
216
|
+
writeFileSync(filePath, data, options);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
let replacedLink = false;
|
|
220
|
+
let pathStats;
|
|
221
|
+
try {
|
|
222
|
+
pathStats = lstatSync(filePath);
|
|
223
|
+
} catch {
|
|
224
|
+
pathStats = undefined;
|
|
225
|
+
}
|
|
226
|
+
if (pathStats?.isSymbolicLink()) {
|
|
227
|
+
// rmSync removes the link itself, including a directory link on Windows.
|
|
228
|
+
rmSync(filePath, { force: true });
|
|
229
|
+
replacedLink = true;
|
|
230
|
+
}
|
|
231
|
+
const openFlags =
|
|
232
|
+
writeOpenFlags(flag) |
|
|
233
|
+
(fsConstants.O_NOFOLLOW || 0) |
|
|
234
|
+
(replacedLink ? fsConstants.O_EXCL : 0);
|
|
235
|
+
const mode =
|
|
236
|
+
typeof options === "object" && options?.mode !== undefined
|
|
237
|
+
? options.mode
|
|
238
|
+
: 0o666;
|
|
239
|
+
const fd = openSync(filePath, openFlags, mode);
|
|
240
|
+
try {
|
|
241
|
+
writeFileSync(fd, data, {
|
|
242
|
+
encoding: typeof options === "string" ? options : options?.encoding,
|
|
243
|
+
});
|
|
244
|
+
} finally {
|
|
245
|
+
closeSync(fd);
|
|
246
|
+
}
|
|
247
|
+
if (replacedLink) {
|
|
248
|
+
console.warn(
|
|
249
|
+
`'${filePath}' was a symbolic link. cdxgen replaced the link with a regular file instead of writing to the file it pointed at.`,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
return replacedLink;
|
|
253
|
+
}
|
|
254
|
+
|
|
142
255
|
/**
|
|
143
256
|
* Permission- and dry-run-aware wrapper around writeFileSync. Records the
|
|
144
257
|
* activity and returns undefined when blocked.
|
|
145
258
|
*
|
|
259
|
+
* The last component of the path is never followed when it is a symbolic
|
|
260
|
+
* link: the link is replaced with a regular file. Paths under /dev and /proc,
|
|
261
|
+
* such as /dev/stdout, are written through as given.
|
|
262
|
+
*
|
|
146
263
|
* @param {string} filePath File path to write.
|
|
147
264
|
* @param {string|Buffer} data Data to write.
|
|
148
265
|
* @param {Object} [options] writeFileSync options (encoding, mode, flag).
|
|
@@ -152,8 +269,15 @@ export function safeWriteSync(filePath, data, options) {
|
|
|
152
269
|
if (isWriteBlocked(filePath)) {
|
|
153
270
|
return undefined;
|
|
154
271
|
}
|
|
155
|
-
|
|
156
|
-
recordFilesystemActivity(
|
|
272
|
+
const replacedLink = writeFileWithoutFollowingLink(filePath, data, options);
|
|
273
|
+
recordFilesystemActivity(
|
|
274
|
+
"write",
|
|
275
|
+
filePath,
|
|
276
|
+
"completed",
|
|
277
|
+
replacedLink
|
|
278
|
+
? "Replaced a symbolic link with a regular file instead of following it."
|
|
279
|
+
: undefined,
|
|
280
|
+
);
|
|
157
281
|
}
|
|
158
282
|
|
|
159
283
|
/**
|
|
@@ -172,7 +296,7 @@ export function safeWriteChunksSync(filePath, chunks) {
|
|
|
172
296
|
return undefined;
|
|
173
297
|
}
|
|
174
298
|
const tempPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
175
|
-
const fd = openSync(tempPath, "
|
|
299
|
+
const fd = openSync(tempPath, "wx");
|
|
176
300
|
try {
|
|
177
301
|
for (const chunk of chunks) {
|
|
178
302
|
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
@@ -991,6 +1115,8 @@ export function safeSpawnSync(command, args, options) {
|
|
|
991
1115
|
}
|
|
992
1116
|
traceLog("spawn", { command, args, ...options });
|
|
993
1117
|
commandsExecuted.add(command);
|
|
1118
|
+
// The tool as invoked, before a shell invocation folds the arguments in.
|
|
1119
|
+
const executedCommand = command;
|
|
994
1120
|
// Fix for DEP0190 warning
|
|
995
1121
|
if (options?.shell === true) {
|
|
996
1122
|
const joined = joinShellCommand(command, args, rawShellArgs);
|
|
@@ -1000,6 +1126,15 @@ export function safeSpawnSync(command, args, options) {
|
|
|
1000
1126
|
}
|
|
1001
1127
|
}
|
|
1002
1128
|
const result = spawnSync(command, args, options);
|
|
1129
|
+
// The run-scoped tool registry feeds metadata.tools (issue #598); every
|
|
1130
|
+
// external execution funnels through this wrapper. A spawn that failed to
|
|
1131
|
+
// start, such as a missing binary, ran nothing and is not recorded; through
|
|
1132
|
+
// a shell, a missing command shows as exit code 127, or 9009 on cmd.exe.
|
|
1133
|
+
const shellCommandNotFound =
|
|
1134
|
+
options?.shell === true && [127, 9009].includes(result.status);
|
|
1135
|
+
if (!result.error && !shellCommandNotFound) {
|
|
1136
|
+
recordExecutedTool(executedCommand);
|
|
1137
|
+
}
|
|
1003
1138
|
recordActivity({
|
|
1004
1139
|
kind: activityDescriptor.kind,
|
|
1005
1140
|
...activityDescriptor.metadata,
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Longest exclude or include pattern cdxgen accepts from an untrusted source,
|
|
3
|
+
* such as a server request or a config file in the scanned directory.
|
|
4
|
+
*/
|
|
5
|
+
export const UNTRUSTED_GLOB_PATTERN_MAX_LENGTH = 512;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Most patterns a list of untrusted exclude or include patterns may stand
|
|
9
|
+
* for once brace expressions are expanded. Every expanded pattern is matched
|
|
10
|
+
* against every file the scan walks, so the total, not the length of the
|
|
11
|
+
* list as written, is what decides the cost.
|
|
12
|
+
*/
|
|
13
|
+
export const UNTRUSTED_GLOB_EXPANSION_BUDGET = 128;
|
|
14
|
+
|
|
15
|
+
function isIntegerText(text) {
|
|
16
|
+
const digits = text.startsWith("-") ? text.slice(1) : text;
|
|
17
|
+
return (
|
|
18
|
+
digits.length > 0 && [...digits].every((char) => char >= "0" && char <= "9")
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isLetter(text) {
|
|
23
|
+
return (
|
|
24
|
+
text.length === 1 &&
|
|
25
|
+
((text >= "a" && text <= "z") || (text >= "A" && text <= "Z"))
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Count the members of a brace range such as `1..10`, `a..z` or `0..100..5`.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} body text between the braces
|
|
33
|
+
* @param {number} limit value returned for ranges at or beyond the limit
|
|
34
|
+
* @returns {number|undefined} member count, or undefined when the body is
|
|
35
|
+
* not a range
|
|
36
|
+
*/
|
|
37
|
+
function braceRangeSize(body, limit) {
|
|
38
|
+
const parts = body.split("..");
|
|
39
|
+
if (parts.length !== 2 && parts.length !== 3) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const [start, end, step = "1"] = parts;
|
|
43
|
+
if (!isIntegerText(step)) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const increment = Math.abs(Number(step)) || 1;
|
|
47
|
+
let distance;
|
|
48
|
+
if (isIntegerText(start) && isIntegerText(end)) {
|
|
49
|
+
distance = Math.abs(Number(end) - Number(start));
|
|
50
|
+
} else if (isLetter(start) && isLetter(end)) {
|
|
51
|
+
distance = Math.abs(end.charCodeAt(0) - start.charCodeAt(0));
|
|
52
|
+
} else {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const size = Math.floor(distance / increment) + 1;
|
|
56
|
+
return Number.isFinite(size) && size < limit ? size : limit;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function newBraceGroup(start) {
|
|
60
|
+
return {
|
|
61
|
+
current: 1,
|
|
62
|
+
hasAnyComma: false,
|
|
63
|
+
hasComma: false,
|
|
64
|
+
hasDots: false,
|
|
65
|
+
hasGroup: false,
|
|
66
|
+
start,
|
|
67
|
+
sum: 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Count, from above, how many patterns a glob's brace expressions expand to,
|
|
73
|
+
* following the brace-expansion package that minimatch uses:
|
|
74
|
+
*
|
|
75
|
+
* - alternatives in `{a,b}` add up, and groups side by side or inside one
|
|
76
|
+
* another multiply;
|
|
77
|
+
* - a range such as `{1..9}`, `{a..z}` or `{0..100..5}` counts its members;
|
|
78
|
+
* - a group with no comma of its own but a list inside it, such as
|
|
79
|
+
* `{{a,b}c}`, is expanded and each result is braced and expanded again,
|
|
80
|
+
* so it counts as its contents, unless its text holds `..` and a result
|
|
81
|
+
* could become a range of any size.
|
|
82
|
+
*
|
|
83
|
+
* brace-expansion reads braces that form neither a list nor a range in ways
|
|
84
|
+
* that depend on the rest of the pattern, so a group that is neither, a
|
|
85
|
+
* closing brace with no group open, a group left open, and the `..` case
|
|
86
|
+
* above all count as more than `cap`. Patterns that list alternatives or
|
|
87
|
+
* ranges, such as `{test,tests}` or `*.{js,ts}`, get the exact count, or one
|
|
88
|
+
* slightly above it where a letter range such as `{Z..c}` spans the `\` that
|
|
89
|
+
* brace-expansion leaves out. `\` escapes the next character. The count stops growing at
|
|
90
|
+
* `cap + 1` and each character is read once, so a pattern that would expand
|
|
91
|
+
* to millions costs no more to count than its length.
|
|
92
|
+
*
|
|
93
|
+
* @param {string} pattern glob pattern
|
|
94
|
+
* @param {number} [cap] largest count of interest
|
|
95
|
+
* @returns {number} upper bound on the number of expanded patterns, at most
|
|
96
|
+
* `cap + 1`
|
|
97
|
+
*/
|
|
98
|
+
export function globBraceExpansionCount(
|
|
99
|
+
pattern,
|
|
100
|
+
cap = Number.MAX_SAFE_INTEGER - 1,
|
|
101
|
+
) {
|
|
102
|
+
const limit = cap + 1;
|
|
103
|
+
const clamp = (value) => Math.min(value, limit);
|
|
104
|
+
const groups = [newBraceGroup(0)];
|
|
105
|
+
const text = String(pattern);
|
|
106
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
107
|
+
const character = text[index];
|
|
108
|
+
if (character === "\\") {
|
|
109
|
+
index += 1;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const group = groups.at(-1);
|
|
113
|
+
if (character === "{") {
|
|
114
|
+
group.hasGroup = true;
|
|
115
|
+
groups.push(newBraceGroup(index + 1));
|
|
116
|
+
} else if (character === "." && text[index + 1] === ".") {
|
|
117
|
+
group.hasDots = true;
|
|
118
|
+
} else if (character === "," && groups.length > 1) {
|
|
119
|
+
group.sum = clamp(group.sum + group.current);
|
|
120
|
+
group.current = 1;
|
|
121
|
+
group.hasComma = true;
|
|
122
|
+
group.hasAnyComma = true;
|
|
123
|
+
} else if (character === "}") {
|
|
124
|
+
if (groups.length === 1) {
|
|
125
|
+
return limit;
|
|
126
|
+
}
|
|
127
|
+
let count;
|
|
128
|
+
if (group.hasComma) {
|
|
129
|
+
count = clamp(group.sum + group.current);
|
|
130
|
+
} else if (group.hasAnyComma) {
|
|
131
|
+
count = group.hasDots ? limit : group.current;
|
|
132
|
+
} else if (!group.hasGroup) {
|
|
133
|
+
count = braceRangeSize(text.slice(group.start, index), limit);
|
|
134
|
+
}
|
|
135
|
+
if (count === undefined) {
|
|
136
|
+
return limit;
|
|
137
|
+
}
|
|
138
|
+
groups.pop();
|
|
139
|
+
const parent = groups.at(-1);
|
|
140
|
+
parent.current = clamp(parent.current * count);
|
|
141
|
+
parent.hasAnyComma ||= group.hasAnyComma;
|
|
142
|
+
parent.hasDots ||= group.hasDots;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return groups.length === 1 ? groups[0].current : limit;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Split exclude or include patterns from an untrusted source into the ones
|
|
150
|
+
* cdxgen uses and the ones it refuses. Patterns are taken in order while
|
|
151
|
+
* they fit the expansion budget, so a list cannot multiply the work of a
|
|
152
|
+
* scan by brace expansion or sheer number.
|
|
153
|
+
*
|
|
154
|
+
* @param {unknown} patterns a pattern, a list of patterns, or nothing
|
|
155
|
+
* @param {Object} [options] bound options
|
|
156
|
+
* @param {boolean} [options.allowBraces=true] refuse every pattern with a
|
|
157
|
+
* brace when false
|
|
158
|
+
* @param {number} [options.budget] expanded patterns allowed in all
|
|
159
|
+
* @param {number} [options.used=0] expanded patterns earlier lists have
|
|
160
|
+
* already taken from the budget
|
|
161
|
+
* @returns {{accepted: string[], refused: {pattern: string, reason: string}[], expansions: number}}
|
|
162
|
+
* the patterns to use, the ones refused with the reason, and the expanded
|
|
163
|
+
* patterns the accepted ones use
|
|
164
|
+
*/
|
|
165
|
+
export function boundUntrustedGlobPatterns(
|
|
166
|
+
patterns,
|
|
167
|
+
{
|
|
168
|
+
allowBraces = true,
|
|
169
|
+
budget = UNTRUSTED_GLOB_EXPANSION_BUDGET,
|
|
170
|
+
used = 0,
|
|
171
|
+
} = {},
|
|
172
|
+
) {
|
|
173
|
+
const entries =
|
|
174
|
+
patterns === undefined || patterns === null
|
|
175
|
+
? []
|
|
176
|
+
: Array.isArray(patterns)
|
|
177
|
+
? patterns
|
|
178
|
+
: [patterns];
|
|
179
|
+
const accepted = [];
|
|
180
|
+
const refused = [];
|
|
181
|
+
let expansions = 0;
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
if (typeof entry !== "string" && typeof entry !== "number") {
|
|
184
|
+
refused.push({ pattern: String(entry), reason: "is not a string" });
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const pattern = String(entry);
|
|
188
|
+
if (pattern.length > UNTRUSTED_GLOB_PATTERN_MAX_LENGTH) {
|
|
189
|
+
refused.push({
|
|
190
|
+
pattern,
|
|
191
|
+
reason: `is longer than ${UNTRUSTED_GLOB_PATTERN_MAX_LENGTH} characters`,
|
|
192
|
+
});
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (!allowBraces && pattern.includes("{")) {
|
|
196
|
+
refused.push({ pattern, reason: "uses braces" });
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const count = globBraceExpansionCount(pattern, budget);
|
|
200
|
+
if (count > budget) {
|
|
201
|
+
refused.push({
|
|
202
|
+
pattern,
|
|
203
|
+
reason: `expands to more than ${budget} patterns`,
|
|
204
|
+
});
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (used + expansions + count > budget) {
|
|
208
|
+
refused.push({
|
|
209
|
+
pattern,
|
|
210
|
+
reason: `would bring the patterns past ${budget} after brace expansion`,
|
|
211
|
+
});
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
expansions += count;
|
|
215
|
+
accepted.push(pattern);
|
|
216
|
+
}
|
|
217
|
+
return { accepted, expansions, refused };
|
|
218
|
+
}
|
package/lib/core/httpClient.js
CHANGED
|
@@ -5,6 +5,7 @@ import { brotliDecompressSync, gunzipSync, inflateSync } from "node:zlib";
|
|
|
5
5
|
|
|
6
6
|
import { Agent, EnvHttpProxyAgent, request as undiciRequest } from "undici";
|
|
7
7
|
|
|
8
|
+
import { readBrotliDecoder } from "./brotliProbe.js";
|
|
8
9
|
import { registerRunReset } from "./runState.js";
|
|
9
10
|
|
|
10
11
|
// Environment variables here are read with raw `process.env` rather than
|
|
@@ -208,6 +209,75 @@ export function resolveTimeout(timeout) {
|
|
|
208
209
|
return undefined;
|
|
209
210
|
}
|
|
210
211
|
|
|
212
|
+
let runtimeBrotliVerdict;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Set or clear the cached runtime Brotli verdict. Exported for unit tests only.
|
|
216
|
+
*
|
|
217
|
+
* @param {boolean|undefined} verdict verdict to use, or undefined to re-check
|
|
218
|
+
*/
|
|
219
|
+
export function _setRuntimeBrotliVerdict(verdict) {
|
|
220
|
+
runtimeBrotliVerdict = verdict;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Whether the Brotli decoder of the runtime cdxgen runs on carries the
|
|
225
|
+
* CVE-2026-111497 fix, read once from the decoder itself: the node binary, or
|
|
226
|
+
* the shared libbrotlidec a `--shared-brotli` build (Homebrew, distro
|
|
227
|
+
* packages) loads; under Bun, the bun binary, which carries its own copy.
|
|
228
|
+
* process.versions.brotli cannot answer this, since Node.js backports the fix
|
|
229
|
+
* without changing it. Deno decodes Brotli in Rust, which was never affected.
|
|
230
|
+
*
|
|
231
|
+
* On Node.js cdxgen never asks for `br`, so a Brotli response comes only from
|
|
232
|
+
* a server that sends it unasked. When the decoder cannot be shown to be
|
|
233
|
+
* patched, such a body is left undecoded rather than handed to a decoder an
|
|
234
|
+
* attacker can write past. Bun decodes in its transport, so there the client
|
|
235
|
+
* stops asking for `br` instead.
|
|
236
|
+
*
|
|
237
|
+
* @param {Object} [runtime] the runtime to judge, for tests
|
|
238
|
+
* @returns {boolean} true when Brotli bodies may be decoded
|
|
239
|
+
*/
|
|
240
|
+
export function runtimeBrotliDecoderIsPatched(runtime = undefined) {
|
|
241
|
+
if (!runtime && runtimeBrotliVerdict !== undefined) {
|
|
242
|
+
return runtimeBrotliVerdict;
|
|
243
|
+
}
|
|
244
|
+
const subject = runtime || {
|
|
245
|
+
isDeno: globalThis.Deno?.version?.deno !== undefined,
|
|
246
|
+
execPath: process.execPath,
|
|
247
|
+
sharedBrotli: [true, "true"].includes(
|
|
248
|
+
process.config?.variables?.node_shared_brotli,
|
|
249
|
+
),
|
|
250
|
+
sharedObjects: () => process.report?.getReport?.()?.sharedObjects || [],
|
|
251
|
+
};
|
|
252
|
+
let verdict = false;
|
|
253
|
+
if (subject.isDeno) {
|
|
254
|
+
verdict = true;
|
|
255
|
+
} else {
|
|
256
|
+
const decoderFile = subject.sharedBrotli
|
|
257
|
+
? subject
|
|
258
|
+
.sharedObjects()
|
|
259
|
+
.find((path) =>
|
|
260
|
+
`${path}`.split(/[\\/]/).pop().startsWith("libbrotlidec"),
|
|
261
|
+
)
|
|
262
|
+
: subject.execPath;
|
|
263
|
+
verdict = Boolean(
|
|
264
|
+
decoderFile &&
|
|
265
|
+
readBrotliDecoder(decoderFile)?.c?.blockSwitchFix === "true",
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
if (!runtime) {
|
|
269
|
+
runtimeBrotliVerdict = verdict;
|
|
270
|
+
if (!verdict) {
|
|
271
|
+
console.warn(
|
|
272
|
+
isBun
|
|
273
|
+
? "The Brotli decoder in this Bun is not patched for CVE-2026-111497, or could not be checked, so cdxgen no longer asks servers for Brotli-encoded responses. Bun still decodes one a server sends unasked; upgrade Bun to a release with a patched Brotli."
|
|
274
|
+
: "The Brotli decoder of this runtime is not patched for CVE-2026-111497, or could not be checked, so Brotli-encoded responses are left undecoded. Upgrade Node.js to 22.23.4, 24.22.1 or 26.12.1 or later (and the shared brotli library on Homebrew or distro builds).",
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return verdict;
|
|
279
|
+
}
|
|
280
|
+
|
|
211
281
|
/**
|
|
212
282
|
* Decompress a response body buffer according to its `Content-Encoding`. undici
|
|
213
283
|
* performs no automatic decompression, so we replicate the behaviour `got` gave
|
|
@@ -235,6 +305,9 @@ function decompressBody(rawBody, contentEncoding) {
|
|
|
235
305
|
} else if (encoding === "deflate") {
|
|
236
306
|
buffer = inflateSync(buffer);
|
|
237
307
|
} else if (encoding === "br") {
|
|
308
|
+
if (!runtimeBrotliDecoderIsPatched()) {
|
|
309
|
+
return buffer;
|
|
310
|
+
}
|
|
238
311
|
buffer = brotliDecompressSync(buffer);
|
|
239
312
|
} else if (encoding === "identity") {
|
|
240
313
|
// no-op
|
|
@@ -296,6 +369,30 @@ async function readBody(undiciResponse, responseType) {
|
|
|
296
369
|
return { body, rawBody };
|
|
297
370
|
}
|
|
298
371
|
|
|
372
|
+
/**
|
|
373
|
+
* The headers to put on the wire. Bun's bundled undici asks for br by default
|
|
374
|
+
* and decodes it with Bun's own decoder before this client sees the body, so
|
|
375
|
+
* the check in decompressBody never runs there. Where that decoder is not
|
|
376
|
+
* patched, ask only for the encodings this client decodes itself. A server
|
|
377
|
+
* that sends br unasked is still decoded by Bun; only upgrading Bun closes
|
|
378
|
+
* that. The cache key is built from the caller's headers, not these.
|
|
379
|
+
*
|
|
380
|
+
* @param {Object} headers Request headers.
|
|
381
|
+
* @returns {Object} Headers to send.
|
|
382
|
+
*/
|
|
383
|
+
function transportHeaders(headers) {
|
|
384
|
+
if (
|
|
385
|
+
!isBun ||
|
|
386
|
+
Object.keys(headers).some(
|
|
387
|
+
(key) => key.toLowerCase() === "accept-encoding",
|
|
388
|
+
) ||
|
|
389
|
+
runtimeBrotliDecoderIsPatched()
|
|
390
|
+
) {
|
|
391
|
+
return headers;
|
|
392
|
+
}
|
|
393
|
+
return { ...headers, "accept-encoding": "gzip, deflate" };
|
|
394
|
+
}
|
|
395
|
+
|
|
299
396
|
/**
|
|
300
397
|
* Convert a body into the `[body, headers]` pair to hand to undici. When the
|
|
301
398
|
* `json` option is present it is serialized and a JSON `content-type` is added
|
|
@@ -752,7 +849,7 @@ async function doRequest(mergedOptions) {
|
|
|
752
849
|
const insecure = options.https?.rejectUnauthorized === false;
|
|
753
850
|
const requestOptions = {
|
|
754
851
|
method,
|
|
755
|
-
headers,
|
|
852
|
+
headers: transportHeaders(headers),
|
|
756
853
|
body: method === "GET" || method === "HEAD" ? undefined : body,
|
|
757
854
|
dispatcher: selectDispatcher(insecure),
|
|
758
855
|
signal,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve, win32 } from "node:path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Containment check for a path taken from a lockfile (issue #3816). A crafted
|
|
5
|
+
* npm lockfile can name `file:`/`link:` specifiers or link targets built from
|
|
6
|
+
* `..` chains or absolute paths, so a parser that is about to list such an
|
|
7
|
+
* entry checks the resolved path against the project root first.
|
|
8
|
+
*
|
|
9
|
+
* The candidate is resolved against `projectRoot`, so both relative values
|
|
10
|
+
* and the absolute paths arborist already normalised are accepted. The root
|
|
11
|
+
* itself counts as inside: a lockfile may legitimately link the project to
|
|
12
|
+
* its own directory.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} candidate Path to check, absolute or relative to projectRoot
|
|
15
|
+
* @param {string} projectRoot Project root the path must stay inside; a falsy
|
|
16
|
+
* root means the current working directory, mirroring `node:path`
|
|
17
|
+
*
|
|
18
|
+
* @returns {Boolean} True when the path is inside projectRoot
|
|
19
|
+
*/
|
|
20
|
+
export function isPathInsideProject(candidate, projectRoot) {
|
|
21
|
+
if (typeof candidate !== "string" || !candidate) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
if (candidate.includes("\0")) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const rootAbs = resolve(projectRoot || process.cwd());
|
|
28
|
+
const rel = relative(rootAbs, resolve(rootAbs, candidate));
|
|
29
|
+
if (!rel) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// relative() returns an absolute path when the two sit on different
|
|
33
|
+
// Windows drives.
|
|
34
|
+
if (isAbsolute(rel) || win32.isAbsolute(rel)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return rel !== ".." && !rel.startsWith("../") && !rel.startsWith("..\\");
|
|
38
|
+
}
|
package/lib/core/paths.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* `https://` URL, and `fileURLToPath` throws `ERR_INVALID_URL_SCHEME` on those.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { platform } from "node:os";
|
|
17
|
+
import { homedir, platform } from "node:os";
|
|
18
18
|
import { dirname } from "node:path";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
|
|
@@ -68,3 +68,26 @@ export function isValidDriveRoot(root) {
|
|
|
68
68
|
export function toCamel(str) {
|
|
69
69
|
return str.replace(/-([a-z])/g, (_, g) => g.toUpperCase());
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Rewrite an absolute path under the user's home directory to a `~`-relative
|
|
74
|
+
* form so the emitted BOM does not carry the local username.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} filePath Absolute path
|
|
77
|
+
* @param {string} [home] Home directory, defaulting to the current user's
|
|
78
|
+
* @returns {string} Home-relative path when applicable, else the input
|
|
79
|
+
*/
|
|
80
|
+
export function redactHomePath(filePath, home = homedir()) {
|
|
81
|
+
const value = String(filePath || "");
|
|
82
|
+
if (!home || !value.startsWith(home)) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
// Require a path boundary so a sibling home such as /Users/alice-backup is
|
|
86
|
+
// not rewritten as if it sat under /Users/alice.
|
|
87
|
+
const remainder = value.slice(home.length);
|
|
88
|
+
if (remainder && !/^[/\\]/u.test(remainder)) {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
const rest = remainder.replace(/^[/\\]/u, "");
|
|
92
|
+
return rest ? `~/${rest.replaceAll("\\", "/")}` : "~";
|
|
93
|
+
}
|