@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/processTree.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* and the dosai runners (layer 2) can share it.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import process from "node:process";
|
|
9
|
+
|
|
8
10
|
import { DEBUG_MODE } from "./activity.js";
|
|
9
11
|
import { safeSpawnSync } from "./fs.js";
|
|
10
12
|
import { isWin } from "./paths.js";
|
|
@@ -24,6 +26,54 @@ export function formatDuration(ms) {
|
|
|
24
26
|
return `${Math.round(ms / 100) / 10} seconds`;
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Longest the PowerShell process snapshot behind reapProcessTree may run.
|
|
31
|
+
*/
|
|
32
|
+
const REAP_TIMEOUT_MS = 60000;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Longest reapProcessTree waits for the processes it stopped to exit.
|
|
36
|
+
*/
|
|
37
|
+
const REAP_EXIT_WAIT_MS = 10000;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Block the calling thread for a number of milliseconds.
|
|
41
|
+
*
|
|
42
|
+
* @param {number} ms Milliseconds to wait
|
|
43
|
+
*/
|
|
44
|
+
function sleepSync(ms) {
|
|
45
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Whether a process with this pid is still running.
|
|
50
|
+
*
|
|
51
|
+
* @param {number} pid Process id
|
|
52
|
+
* @returns {boolean} true while the process exists
|
|
53
|
+
*/
|
|
54
|
+
function isRunning(pid) {
|
|
55
|
+
try {
|
|
56
|
+
process.kill(pid, 0);
|
|
57
|
+
return true;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
// EPERM means the process exists but belongs to someone else.
|
|
60
|
+
return err?.code === "EPERM";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A UTC instant as a DMTF datetime, the form WQL compares CIM_DATETIME
|
|
66
|
+
* properties such as Win32_Process.CreationDate against.
|
|
67
|
+
*
|
|
68
|
+
* @param {number} ms Epoch milliseconds
|
|
69
|
+
* @returns {string} The instant as `yyyymmddHHMMSS.mmmmmm+000`
|
|
70
|
+
*/
|
|
71
|
+
export function toDmtfUtc(ms) {
|
|
72
|
+
const date = new Date(ms);
|
|
73
|
+
const pad = (value, width = 2) => `${value}`.padStart(width, "0");
|
|
74
|
+
return `${date.getUTCFullYear()}${pad(date.getUTCMonth() + 1)}${pad(date.getUTCDate())}${pad(date.getUTCHours())}${pad(date.getUTCMinutes())}${pad(date.getUTCSeconds())}.${pad(date.getUTCMilliseconds(), 3)}000+000`;
|
|
75
|
+
}
|
|
76
|
+
|
|
27
77
|
/**
|
|
28
78
|
* Stop the descendants a timed-out run left behind on Windows.
|
|
29
79
|
*
|
|
@@ -32,11 +82,16 @@ export function formatDuration(ms) {
|
|
|
32
82
|
* workers, or the build hosts a .NET analyzer starts keep running while still
|
|
33
83
|
* pointing at the dead process's pid. `taskkill /T` on that pid finds nothing,
|
|
34
84
|
* because the root is already gone. The live descendants are therefore found
|
|
35
|
-
* by parent pid in a process snapshot
|
|
36
|
-
* created after the run started. The root's own
|
|
37
|
-
* its end: once the root is gone its pid can be
|
|
38
|
-
* process, whose children would otherwise look like
|
|
85
|
+
* by parent pid in a PowerShell process snapshot, limited to processes
|
|
86
|
+
* created after the run started, and stopped from Node.js. The root's own
|
|
87
|
+
* children must also predate its end: once the root is gone its pid can be
|
|
88
|
+
* handed to an unrelated process, whose children would otherwise look like
|
|
89
|
+
* the run's. Elsewhere this
|
|
39
90
|
* does nothing: an orphan is re-parented and can no longer be told apart.
|
|
91
|
+
* It returns once the stopped processes have exited, or after ten seconds,
|
|
92
|
+
* so the caller can remove files the run left half-written. When the
|
|
93
|
+
* snapshot itself fails or runs past its time limit it warns and returns an
|
|
94
|
+
* empty list, since processes may then still be running.
|
|
40
95
|
*
|
|
41
96
|
* @param {number|undefined} rootPid pid of the process cdxgen spawned
|
|
42
97
|
* @param {number} startedAt Epoch milliseconds at which the run started
|
|
@@ -53,15 +108,24 @@ export function reapProcessTree(
|
|
|
53
108
|
if (!isWin || !Number.isInteger(rootPid) || rootPid <= 0) {
|
|
54
109
|
return [];
|
|
55
110
|
}
|
|
56
|
-
// All three values are integers,
|
|
111
|
+
// All three values are integers, and the DMTF string is built from digits
|
|
112
|
+
// only, so interpolating them cannot inject anything.
|
|
57
113
|
const since = Math.max(0, Math.floor(startedAt) - 1000);
|
|
58
114
|
const until = Math.max(since, Math.floor(endedAt) + 1000);
|
|
115
|
+
const snapshotProperties = "-Property ProcessId,ParentProcessId,CreationDate";
|
|
59
116
|
const script = [
|
|
60
117
|
`$root = ${rootPid}`,
|
|
61
118
|
`$since = [DateTimeOffset]::FromUnixTimeMilliseconds(${since}).UtcDateTime`,
|
|
62
119
|
`$until = [DateTimeOffset]::FromUnixTimeMilliseconds(${until}).UtcDateTime`,
|
|
120
|
+
// WMI drops the processes that predate the run before PowerShell sees
|
|
121
|
+
// them, which keeps the snapshot small on a busy host. The loop below
|
|
122
|
+
// applies the same bound, so a WMI that rejects the filter only costs the
|
|
123
|
+
// full snapshot.
|
|
124
|
+
"$procs = @()",
|
|
125
|
+
`try { $procs = @(Get-CimInstance Win32_Process -Filter "CreationDate >= '${toDmtfUtc(since)}'" ${snapshotProperties} -ErrorAction Stop) }`,
|
|
126
|
+
`catch { $procs = @(Get-CimInstance Win32_Process ${snapshotProperties}) }`,
|
|
63
127
|
"$children = @{}",
|
|
64
|
-
"foreach ($p in
|
|
128
|
+
"foreach ($p in $procs) {",
|
|
65
129
|
" if (-not $p.CreationDate) { continue }",
|
|
66
130
|
" $created = $p.CreationDate.ToUniversalTime()",
|
|
67
131
|
" if ($created -lt $since) { continue }",
|
|
@@ -76,19 +140,47 @@ export function reapProcessTree(
|
|
|
76
140
|
" if ($child -and -not $found.Contains($child)) { $found.Add($child); $queue.Enqueue($child) }",
|
|
77
141
|
" }",
|
|
78
142
|
"}",
|
|
79
|
-
"foreach ($child in $found) { Stop-Process -Id $child -Force -ErrorAction SilentlyContinue }",
|
|
80
143
|
"$found -join ','",
|
|
81
144
|
].join("\n");
|
|
82
145
|
const result = safeSpawnSync(
|
|
83
146
|
"powershell",
|
|
84
147
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
85
|
-
{ timeout:
|
|
148
|
+
{ timeout: REAP_TIMEOUT_MS },
|
|
86
149
|
);
|
|
150
|
+
if (result?.error || result?.status !== 0) {
|
|
151
|
+
// An empty result here is not evidence that nothing was left running.
|
|
152
|
+
console.warn(
|
|
153
|
+
result?.error?.code === "ETIMEDOUT"
|
|
154
|
+
? `Could not look for processes the timed-out ${toolName} run left behind: the process snapshot took longer than ${formatDuration(REAP_TIMEOUT_MS)}.`
|
|
155
|
+
: `Could not look for processes the timed-out ${toolName} run left behind: PowerShell ${result?.error ? `failed to start (${result.error.message})` : `exited with status ${result?.status}`}.`,
|
|
156
|
+
);
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
87
159
|
const reaped = `${result?.stdout || ""}`
|
|
88
160
|
.trim()
|
|
89
161
|
.split(",")
|
|
90
162
|
.map((pid) => Number.parseInt(pid, 10))
|
|
91
163
|
.filter((pid) => pid > 0);
|
|
164
|
+
for (const pid of reaped) {
|
|
165
|
+
try {
|
|
166
|
+
process.kill(pid, "SIGKILL");
|
|
167
|
+
} catch {
|
|
168
|
+
// The process exited between the snapshot and the kill.
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// A process that is still exiting keeps its files open, so wait for the
|
|
172
|
+
// exits, but never longer than REAP_EXIT_WAIT_MS.
|
|
173
|
+
const deadline = Date.now() + REAP_EXIT_WAIT_MS;
|
|
174
|
+
let running = reaped.filter(isRunning);
|
|
175
|
+
while (running.length && Date.now() < deadline) {
|
|
176
|
+
sleepSync(100);
|
|
177
|
+
running = running.filter(isRunning);
|
|
178
|
+
}
|
|
179
|
+
if (running.length) {
|
|
180
|
+
console.warn(
|
|
181
|
+
`${running.length} process(es) the timed-out ${toolName} run left behind were still exiting after ${formatDuration(REAP_EXIT_WAIT_MS)}: ${running.join(", ")}.`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
92
184
|
if (DEBUG_MODE) {
|
|
93
185
|
console.log(
|
|
94
186
|
reaped.length
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
|
|
4
|
+
import { HOST_SCOPE_PROJECT_TYPES } from "./env.js";
|
|
5
|
+
import { boundUntrustedGlobPatterns } from "./globPatterns.js";
|
|
3
6
|
import {
|
|
4
7
|
isReservedMetadataPropertyName,
|
|
5
8
|
METADATA_PROPERTY_CONFIG_KEYS,
|
|
@@ -17,10 +20,162 @@ export const PROJECT_CONFIG_FILENAMES = [
|
|
|
17
20
|
".cdxgen.yaml",
|
|
18
21
|
];
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Options a config file may set, by their kebab-case name. A config file may
|
|
25
|
+
* also use the camelCase spelling of each, or one of the aliases in
|
|
26
|
+
* PROJECT_CONFIG_OPTION_ALIASES. Every other key is ignored: options that
|
|
27
|
+
* only library callers pass (such as `dosaiCommand` or `traceCmd`), yargs'
|
|
28
|
+
* own `extends`, and anything cdxgen does not know.
|
|
29
|
+
*
|
|
30
|
+
* Every option bin/cdxgen.js declares is either here or in
|
|
31
|
+
* OPERATOR_ONLY_OPTIONS, which projectConfig.poku.js checks.
|
|
32
|
+
*/
|
|
33
|
+
export const PROJECT_CONFIG_OPTIONS = new Set([
|
|
34
|
+
"activity-report",
|
|
35
|
+
"api-key",
|
|
36
|
+
"author",
|
|
37
|
+
"auto-compositions",
|
|
38
|
+
"auto-create",
|
|
39
|
+
"bom-audit",
|
|
40
|
+
"bom-audit-categories",
|
|
41
|
+
"bom-audit-fail-severity",
|
|
42
|
+
"bom-audit-include-trusted",
|
|
43
|
+
"bom-audit-max-targets",
|
|
44
|
+
"bom-audit-min-severity",
|
|
45
|
+
"bom-audit-only-trusted",
|
|
46
|
+
"bom-audit-rules-dir",
|
|
47
|
+
"bom-audit-scope",
|
|
48
|
+
"cache",
|
|
49
|
+
"cache-ttl",
|
|
50
|
+
"callsites-slices-file",
|
|
51
|
+
"caxa-app-dir",
|
|
52
|
+
"cmake-cache",
|
|
53
|
+
"color",
|
|
54
|
+
"compile-commands",
|
|
55
|
+
"component-type",
|
|
56
|
+
"data-flow-slices-file",
|
|
57
|
+
"deep",
|
|
58
|
+
"deps-slices-file",
|
|
59
|
+
"dry-run",
|
|
60
|
+
"env-audit",
|
|
61
|
+
"evidence",
|
|
62
|
+
"evinse-output",
|
|
63
|
+
"exclude",
|
|
64
|
+
"exclude-type",
|
|
65
|
+
"experimental-mcp-pinning",
|
|
66
|
+
"export-proto",
|
|
67
|
+
"fail-on-error",
|
|
68
|
+
"filter",
|
|
69
|
+
"format",
|
|
70
|
+
"generate-key-and-sign",
|
|
71
|
+
"git-branch",
|
|
72
|
+
"include-crypto",
|
|
73
|
+
"include-formulation",
|
|
74
|
+
"include-regex",
|
|
75
|
+
"include-release-notes",
|
|
76
|
+
"include-runtime",
|
|
77
|
+
"install-deps",
|
|
78
|
+
"introspect",
|
|
79
|
+
"introspect-annotate",
|
|
80
|
+
"introspect-fail-below",
|
|
81
|
+
"introspect-json",
|
|
82
|
+
"introspect-report",
|
|
83
|
+
"is-latest",
|
|
84
|
+
"json-pretty",
|
|
85
|
+
"license-policy",
|
|
86
|
+
"license-ref",
|
|
87
|
+
"lifecycle",
|
|
88
|
+
"log-format",
|
|
89
|
+
"metadata-property",
|
|
90
|
+
"min-confidence",
|
|
91
|
+
"no-babel",
|
|
92
|
+
"no-banner",
|
|
93
|
+
"no-ignore",
|
|
94
|
+
"only",
|
|
95
|
+
"openapi-spec-file",
|
|
96
|
+
"output",
|
|
97
|
+
"package-extensions",
|
|
98
|
+
"parent-project-id",
|
|
99
|
+
"parent-project-name",
|
|
100
|
+
"parent-project-version",
|
|
101
|
+
"print",
|
|
102
|
+
"profile",
|
|
103
|
+
"progress",
|
|
104
|
+
"project-group",
|
|
105
|
+
"project-id",
|
|
106
|
+
"project-name",
|
|
107
|
+
"project-tag",
|
|
108
|
+
"project-version",
|
|
109
|
+
"proto-bin-file",
|
|
110
|
+
"quiet",
|
|
111
|
+
"reachables-slices-file",
|
|
112
|
+
"recurse",
|
|
113
|
+
"release-notes-current-tag",
|
|
114
|
+
"release-notes-previous-tag",
|
|
115
|
+
"required-only",
|
|
116
|
+
"resolve-class",
|
|
117
|
+
"rust",
|
|
118
|
+
"scalasem",
|
|
119
|
+
"scalasem-include-tests",
|
|
120
|
+
"semantics-slices-file",
|
|
121
|
+
"server-url",
|
|
122
|
+
"spec-version",
|
|
123
|
+
"standard",
|
|
124
|
+
"tea-artifact-url",
|
|
125
|
+
"tea-author-email",
|
|
126
|
+
"tea-author-name",
|
|
127
|
+
"tea-collection-name",
|
|
128
|
+
"tea-fetch",
|
|
129
|
+
"tea-leaf-identifier",
|
|
130
|
+
"tea-publish",
|
|
131
|
+
"tea-reason",
|
|
132
|
+
"tea-token",
|
|
133
|
+
"technique",
|
|
134
|
+
"tlp-classification",
|
|
135
|
+
"tui",
|
|
136
|
+
"type",
|
|
137
|
+
"usages-slices-file",
|
|
138
|
+
"validate",
|
|
139
|
+
"verbose",
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Options only the operator may set, on the command line or through a
|
|
144
|
+
* CDXGEN_* environment variable. They choose an executable, start a listener,
|
|
145
|
+
* turn off certificate checks on the operator's own upload, opt into
|
|
146
|
+
* experimental tool installation, or end the run without a BOM. A config file
|
|
147
|
+
* that sets one is told so and ignored.
|
|
148
|
+
*/
|
|
149
|
+
export const OPERATOR_ONLY_OPTIONS = new Set([
|
|
150
|
+
"feature-flags",
|
|
151
|
+
"help",
|
|
152
|
+
"scalasem-command",
|
|
153
|
+
"server",
|
|
154
|
+
"server-host",
|
|
155
|
+
"server-port",
|
|
156
|
+
"skip-dt-tls-check",
|
|
157
|
+
]);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Alternate option names a config file may use, with the option each one
|
|
161
|
+
* stands for. yargs applies its aliases to config keys too, so each is
|
|
162
|
+
* checked as the option it names.
|
|
163
|
+
*/
|
|
164
|
+
export const PROJECT_CONFIG_OPTION_ALIASES = new Map([
|
|
165
|
+
["c", "resolve-class"],
|
|
166
|
+
["exclude-regex", "exclude"],
|
|
167
|
+
["h", "help"],
|
|
168
|
+
["o", "output"],
|
|
169
|
+
["p", "print"],
|
|
170
|
+
["q", "quiet"],
|
|
171
|
+
["r", "recurse"],
|
|
172
|
+
["t", "type"],
|
|
173
|
+
]);
|
|
174
|
+
|
|
20
175
|
/**
|
|
21
176
|
* Options whose values name a file or directory cdxgen writes to or reads
|
|
22
177
|
* from. A config file may set these, but only to a location inside its own
|
|
23
|
-
* directory.
|
|
178
|
+
* directory, after symbolic links are resolved.
|
|
24
179
|
*/
|
|
25
180
|
const PATH_OPTIONS = new Set([
|
|
26
181
|
"output",
|
|
@@ -28,14 +183,26 @@ const PATH_OPTIONS = new Set([
|
|
|
28
183
|
"proto-bin-file",
|
|
29
184
|
"deps-slices-file",
|
|
30
185
|
"usages-slices-file",
|
|
186
|
+
"callsites-slices-file",
|
|
31
187
|
"data-flow-slices-file",
|
|
32
188
|
"reachables-slices-file",
|
|
33
189
|
"semantics-slices-file",
|
|
34
190
|
"openapi-spec-file",
|
|
35
191
|
"license-policy",
|
|
36
192
|
"bom-audit-rules-dir",
|
|
193
|
+
"introspect-report",
|
|
194
|
+
"introspect-json",
|
|
195
|
+
"cmake-cache",
|
|
196
|
+
"compile-commands",
|
|
197
|
+
"caxa-app-dir",
|
|
37
198
|
]);
|
|
38
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Options whose values are glob patterns matched against every file a scan
|
|
202
|
+
* walks, in the order they draw on the shared expansion budget.
|
|
203
|
+
*/
|
|
204
|
+
const PATTERN_OPTIONS = ["exclude", "include-regex"];
|
|
205
|
+
|
|
39
206
|
/**
|
|
40
207
|
* Options that change where cdxgen sends data or how much of the host it
|
|
41
208
|
* touches. These stay usable, but never silently.
|
|
@@ -64,9 +231,53 @@ function optionSpellings(option) {
|
|
|
64
231
|
return camel === option ? [option] : [option, camel];
|
|
65
232
|
}
|
|
66
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Find the option a config key names.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} key config file key
|
|
238
|
+
* @returns {string|undefined} kebab-case option name, or undefined when the
|
|
239
|
+
* key is not the kebab-case spelling, the camelCase spelling or an alias of
|
|
240
|
+
* an option cdxgen declares
|
|
241
|
+
*/
|
|
242
|
+
function configKeyOption(key) {
|
|
243
|
+
if (PROJECT_CONFIG_OPTION_ALIASES.has(key)) {
|
|
244
|
+
return PROJECT_CONFIG_OPTION_ALIASES.get(key);
|
|
245
|
+
}
|
|
246
|
+
const kebab = key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
247
|
+
if (!PROJECT_CONFIG_OPTIONS.has(kebab) && !OPERATOR_ONLY_OPTIONS.has(kebab)) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
return optionSpellings(kebab).includes(key) ? kebab : undefined;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Resolve a path through the symbolic links of its longest existing prefix.
|
|
255
|
+
* The part that does not exist yet is appended unchanged.
|
|
256
|
+
*
|
|
257
|
+
* @param {string} absolutePath absolute path to resolve
|
|
258
|
+
* @returns {string} the path with every existing component resolved
|
|
259
|
+
*/
|
|
260
|
+
function resolveExistingPrefix(absolutePath) {
|
|
261
|
+
const missing = [];
|
|
262
|
+
let current = absolutePath;
|
|
263
|
+
for (;;) {
|
|
264
|
+
try {
|
|
265
|
+
return path.join(realpathSync(current), ...missing.reverse());
|
|
266
|
+
} catch {
|
|
267
|
+
const parent = path.dirname(current);
|
|
268
|
+
if (parent === current) {
|
|
269
|
+
return absolutePath;
|
|
270
|
+
}
|
|
271
|
+
missing.push(path.basename(current));
|
|
272
|
+
current = parent;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
67
277
|
/**
|
|
68
278
|
* Determine whether a config-supplied path stays inside the config's own
|
|
69
|
-
* directory
|
|
279
|
+
* directory once symbolic links are resolved, so a link committed to the
|
|
280
|
+
* scanned tree cannot carry a path option outside it.
|
|
70
281
|
*
|
|
71
282
|
* @param {string} configDir directory the config file was read from
|
|
72
283
|
* @param {unknown} value config-supplied path value
|
|
@@ -76,40 +287,95 @@ function isContainedPath(configDir, value) {
|
|
|
76
287
|
if (typeof value !== "string" || !value.length) {
|
|
77
288
|
return false;
|
|
78
289
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
290
|
+
const root = resolveExistingPrefix(path.resolve(configDir));
|
|
291
|
+
const target = resolveExistingPrefix(path.resolve(configDir, value));
|
|
292
|
+
const relativePath = path.relative(root, target);
|
|
83
293
|
return (
|
|
84
294
|
relativePath === "" ||
|
|
85
|
-
(
|
|
295
|
+
(relativePath !== ".." &&
|
|
296
|
+
!relativePath.startsWith(`..${path.sep}`) &&
|
|
297
|
+
!path.isAbsolute(relativePath))
|
|
86
298
|
);
|
|
87
299
|
}
|
|
88
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Split a config-supplied project type value into the individual types.
|
|
303
|
+
*
|
|
304
|
+
* @param {unknown} value `type` value from a config file
|
|
305
|
+
* @returns {string[]} trimmed, non-empty project types
|
|
306
|
+
*/
|
|
307
|
+
function projectTypeList(value) {
|
|
308
|
+
return (Array.isArray(value) ? value : [value])
|
|
309
|
+
.flatMap((entry) => `${entry ?? ""}`.split(","))
|
|
310
|
+
.map((entry) => entry.trim())
|
|
311
|
+
.filter(Boolean);
|
|
312
|
+
}
|
|
313
|
+
|
|
89
314
|
/**
|
|
90
315
|
* Apply the trust boundary between cdxgen and the directory it is scanning.
|
|
91
316
|
*
|
|
92
317
|
* A config file lives in the tree under analysis, so it is attacker-controlled
|
|
93
|
-
* whenever that tree is.
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
318
|
+
* whenever that tree is. It may therefore only set the options listed in
|
|
319
|
+
* PROJECT_CONFIG_OPTIONS: options that choose an executable, start a listener
|
|
320
|
+
* or weaken the operator's own checks, and every key cdxgen does not declare
|
|
321
|
+
* as an option, are dropped. Path options are confined to the config's own
|
|
322
|
+
* directory - a scanned project may choose where in itself the BOM lands, but
|
|
323
|
+
* not write outside it - and project types that inventory the host rather
|
|
324
|
+
* than the scan path are dropped. Exclude and include patterns are kept
|
|
325
|
+
* within the bounds of boundUntrustedGlobPatterns, so a project cannot make
|
|
326
|
+
* every file match against thousands of brace-expanded patterns. Options that
|
|
327
|
+
* redirect data off the host or widen what cdxgen does are announced on the
|
|
328
|
+
* console. A project may describe itself with custom metadata properties, but
|
|
329
|
+
* not in the namespaces cdxgen reserves for the facts it establishes, so a
|
|
330
|
+
* scanned tree cannot pass its own claims off as cdxgen's.
|
|
100
331
|
*
|
|
101
332
|
* @param {Object} config parsed config file contents
|
|
102
333
|
* @param {string} configDir directory the config file was read from
|
|
103
|
-
* @returns {{config: Object, rejected: string[], announced: string[], reservedProperties: string[]}} sanitized config and what it did
|
|
334
|
+
* @returns {{config: Object, rejected: string[], announced: string[], reservedProperties: string[], ignored: string[], operatorOnly: string[], hostScopeTypes: string[], refusedPatterns: {key: string, pattern: string, reason: string}[]}} sanitized config and what it did
|
|
104
335
|
*/
|
|
105
336
|
export function sanitizeProjectConfig(config, configDir) {
|
|
106
|
-
|
|
107
|
-
|
|
337
|
+
const result = {
|
|
338
|
+
announced: [],
|
|
339
|
+
config: {},
|
|
340
|
+
hostScopeTypes: [],
|
|
341
|
+
ignored: [],
|
|
342
|
+
operatorOnly: [],
|
|
343
|
+
refusedPatterns: [],
|
|
344
|
+
rejected: [],
|
|
345
|
+
reservedProperties: [],
|
|
346
|
+
};
|
|
347
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
const sanitized = result.config;
|
|
351
|
+
const aliasEntries = [];
|
|
352
|
+
for (const [key, value] of Object.entries(config)) {
|
|
353
|
+
if (METADATA_PROPERTY_CONFIG_KEYS.includes(key)) {
|
|
354
|
+
sanitized[key] = value;
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const option = configKeyOption(key);
|
|
358
|
+
if (!option) {
|
|
359
|
+
result.ignored.push(key);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (OPERATOR_ONLY_OPTIONS.has(option)) {
|
|
363
|
+
result.operatorOnly.push(key);
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (PROJECT_CONFIG_OPTION_ALIASES.has(key)) {
|
|
367
|
+
aliasEntries.push([option, value]);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
sanitized[key] = value;
|
|
371
|
+
}
|
|
372
|
+
// An alias is stored under the option it names, so the checks below see
|
|
373
|
+
// it. A spelled-out option in the same file takes precedence.
|
|
374
|
+
for (const [option, value] of aliasEntries) {
|
|
375
|
+
if (optionSpellings(option).every((key) => sanitized[key] === undefined)) {
|
|
376
|
+
sanitized[option] = value;
|
|
377
|
+
}
|
|
108
378
|
}
|
|
109
|
-
const sanitized = { ...config };
|
|
110
|
-
const rejected = [];
|
|
111
|
-
const announced = [];
|
|
112
|
-
const reservedProperties = [];
|
|
113
379
|
for (const key of METADATA_PROPERTY_CONFIG_KEYS) {
|
|
114
380
|
if (sanitized[key] === undefined) {
|
|
115
381
|
continue;
|
|
@@ -117,7 +383,7 @@ export function sanitizeProjectConfig(config, configDir) {
|
|
|
117
383
|
const properties = [];
|
|
118
384
|
for (const aprop of parseMetadataProperties(sanitized[key])) {
|
|
119
385
|
if (isReservedMetadataPropertyName(aprop.name)) {
|
|
120
|
-
reservedProperties.push(aprop.name);
|
|
386
|
+
result.reservedProperties.push(aprop.name);
|
|
121
387
|
} else {
|
|
122
388
|
properties.push(aprop);
|
|
123
389
|
}
|
|
@@ -134,17 +400,58 @@ export function sanitizeProjectConfig(config, configDir) {
|
|
|
134
400
|
continue;
|
|
135
401
|
}
|
|
136
402
|
if (!isContainedPath(configDir, sanitized[key])) {
|
|
137
|
-
rejected.push(`${key}=${sanitized[key]}`);
|
|
403
|
+
result.rejected.push(`${key}=${sanitized[key]}`);
|
|
138
404
|
delete sanitized[key];
|
|
139
405
|
}
|
|
140
406
|
}
|
|
141
407
|
}
|
|
408
|
+
if (sanitized.type !== undefined) {
|
|
409
|
+
const types = projectTypeList(sanitized.type);
|
|
410
|
+
const projectTypes = types.filter(
|
|
411
|
+
(type) => !HOST_SCOPE_PROJECT_TYPES.has(type.toLowerCase()),
|
|
412
|
+
);
|
|
413
|
+
result.hostScopeTypes = types.filter(
|
|
414
|
+
(type) => !projectTypes.includes(type),
|
|
415
|
+
);
|
|
416
|
+
if (result.hostScopeTypes.length) {
|
|
417
|
+
if (projectTypes.length) {
|
|
418
|
+
sanitized.type = projectTypes;
|
|
419
|
+
} else {
|
|
420
|
+
delete sanitized.type;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
// Exclude patterns, then include patterns, share one expansion budget.
|
|
425
|
+
let patternsUsed = 0;
|
|
426
|
+
for (const option of PATTERN_OPTIONS) {
|
|
427
|
+
for (const key of optionSpellings(option)) {
|
|
428
|
+
if (sanitized[key] === undefined) {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const { accepted, expansions, refused } = boundUntrustedGlobPatterns(
|
|
432
|
+
sanitized[key],
|
|
433
|
+
{ used: patternsUsed },
|
|
434
|
+
);
|
|
435
|
+
patternsUsed += expansions;
|
|
436
|
+
for (const entry of refused) {
|
|
437
|
+
result.refusedPatterns.push({ key, ...entry });
|
|
438
|
+
}
|
|
439
|
+
if (!refused.length) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (!accepted.length) {
|
|
443
|
+
delete sanitized[key];
|
|
444
|
+
} else {
|
|
445
|
+
sanitized[key] = Array.isArray(sanitized[key]) ? accepted : accepted[0];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
142
449
|
for (const option of ANNOUNCED_OPTIONS) {
|
|
143
450
|
for (const key of optionSpellings(option)) {
|
|
144
451
|
if (sanitized[key] !== undefined) {
|
|
145
|
-
announced.push(key);
|
|
452
|
+
result.announced.push(key);
|
|
146
453
|
}
|
|
147
454
|
}
|
|
148
455
|
}
|
|
149
|
-
return
|
|
456
|
+
return result;
|
|
150
457
|
}
|