@cdxgen/cdxgen 13.3.0 → 13.5.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 +12 -9
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +115 -31
- package/bin/evinse.js +23 -1
- package/bin/hbom.js +15 -7
- package/bin/repl.js +115 -0
- package/bin/sign.js +45 -15
- package/bin/tracebom.js +5 -1
- package/bin/validate.js +84 -18
- package/bin/verify.js +155 -86
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/helpers/init.gradle +46 -21
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +344 -1
- package/data/rules/cbom-security.yaml +235 -4
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/go-binaries.yaml +46 -0
- package/data/rules/golem-go.yaml +80 -0
- package/data/rules/hbom-compliance.yaml +40 -22
- package/data/rules/hbom-performance.yaml +7 -1
- package/data/rules/host-topology.yaml +1 -1
- package/data/rules/obom-runtime.yaml +31 -2
- package/data/rules/php-runtime.yaml +143 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +52 -6
- package/lib/cli/bomAssembly.js +28 -3
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +186 -58
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +779 -146
- package/lib/cli/managedBom.js +1021 -261
- package/lib/cli/nativeBom.js +364 -58
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +47 -17
- package/lib/core/fs.js +31 -21
- package/lib/core/httpClient.js +233 -69
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +5 -7
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/cmakeResolver.js +10 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +337 -22
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1967 -788
- package/lib/ecosystems/golem.js +578 -18
- package/lib/ecosystems/gradleutils.js +340 -46
- package/lib/ecosystems/jsEvidence.js +85 -24
- package/lib/ecosystems/kosi.js +21 -4
- package/lib/ecosystems/npmutils.js +64 -0
- package/lib/ecosystems/parsers-dotnet.js +668 -153
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +1283 -297
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +463 -29
- package/lib/ecosystems/parsers-misc.js +405 -93
- package/lib/ecosystems/parsers-python.js +152 -20
- package/lib/ecosystems/parsers-rust.js +4 -4
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +73 -20
- package/lib/ecosystems/rusi.js +32 -9
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +343 -0
- package/lib/ecosystems/scalasem.js +2620 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +695 -78
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +204 -48
- package/lib/inventory/auditCategories.js +10 -3
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +507 -128
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +747 -101
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +96 -8
- package/lib/inventory/envcontext.js +168 -5
- package/lib/inventory/evidenceUtils.js +590 -132
- package/lib/inventory/fetchBatch.js +574 -38
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/formulationParsers.js +3 -2
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +89 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/phpBuildInfo.js +487 -0
- package/lib/inventory/phpHostProbe.js +641 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +215 -11
- package/lib/inventory/spdx.js +245 -0
- package/lib/inventory/traceRunner.js +93 -8
- package/lib/inventory/tracedCrypto.js +423 -0
- package/lib/managers/binary.js +529 -183
- package/lib/managers/docker.js +199 -44
- package/lib/managers/oci.js +24 -2
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +16 -2
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +34 -2
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +209 -1
- package/lib/stages/postgen/annotator.js +17 -1
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/introspection/reflect.js +4 -1
- package/lib/stages/postgen/postgen.js +20 -6
- package/lib/stages/postgen/ruleEngine.js +58 -1
- package/lib/stages/postgen/specVersionCompat.js +62 -0
- package/lib/stages/pregen/pregen.js +117 -5
- package/lib/validator/bomValidator.js +26 -5
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +26 -7
- package/lib/validator/generated/validate-1.7.mjs +31 -12
- package/lib/validator/generated/validate-2.0.mjs +30 -11
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +23 -23
- package/types/bin/cdxgen-secure.d.ts +3 -0
- package/types/bin/cdxgen-secure.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +9 -2
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +43 -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.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +44 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +27 -2
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/projectTooling.d.ts +41 -0
- package/types/lib/core/projectTooling.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +26 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -1
- package/types/lib/core/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +19 -3
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts +81 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +59 -7
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -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/npmutils.d.ts +15 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +155 -5
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +119 -5
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +6 -1
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +74 -3
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +21 -59
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +57 -6
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +26 -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 +21 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +100 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +10 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +80 -4
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
- package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
- package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +96 -10
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
- package/types/lib/inventory/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/certificates.d.ts +205 -0
- package/types/lib/inventory/certificates.d.ts.map +1 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts +61 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts +18 -0
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +202 -14
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +16 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts +40 -2
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +48 -14
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/goBuildInfo.d.ts +10 -0
- package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/inventory/hostTopology.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/osPackagePurl.d.ts +99 -0
- package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +6 -2
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/osinfo.d.ts +31 -6
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/osqueryTransform.d.ts +51 -0
- package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/phpHostProbe.d.ts +35 -0
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rootfsCerts.d.ts +27 -0
- package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +28 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +24 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +16 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +64 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +10 -0
- 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/managers/oci.d.ts +3 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +2 -1
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- package/types/lib/parsers/email.d.ts +19 -0
- package/types/lib/parsers/email.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +13 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/parsers/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts +8 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +14 -1
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +8 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
package/lib/inventory/source.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
import { URL } from "node:url";
|
|
4
|
+
import { fileURLToPath, URL } from "node:url";
|
|
5
5
|
|
|
6
6
|
import { Purl } from "@cdxgen/cdx-purl";
|
|
7
7
|
import { coerce, diff, prerelease } from "semver";
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
|
-
cdxgenAgent,
|
|
11
10
|
createDryRunError,
|
|
12
11
|
DEBUG_MODE,
|
|
13
12
|
hasDangerousUnicode,
|
|
@@ -24,11 +23,12 @@ import {
|
|
|
24
23
|
} from "../core/fs.js";
|
|
25
24
|
import { thoughtLog } from "../core/logger.js";
|
|
26
25
|
import { isValidDriveRoot, isWin } from "../core/paths.js";
|
|
26
|
+
import { registerRunReset } from "../core/runState.js";
|
|
27
27
|
import {
|
|
28
|
+
gatedGet,
|
|
28
29
|
prefetchEnabled,
|
|
29
30
|
prefetchedResponse,
|
|
30
31
|
prefetchJson,
|
|
31
|
-
withHostRateLimit,
|
|
32
32
|
} from "./fetchBatch.js";
|
|
33
33
|
import { canonicalPurlFromLooseString } from "./purl.js";
|
|
34
34
|
|
|
@@ -111,6 +111,9 @@ function inferGitOperation(args = []) {
|
|
|
111
111
|
* @param {string[]} args git arguments
|
|
112
112
|
* @param {Object} options command options
|
|
113
113
|
* @param {string|undefined} options.cwd working directory
|
|
114
|
+
* @param {boolean|undefined} options.noRepository run outside any repository,
|
|
115
|
+
* so the config of a repository in the working directory, such as url.*.insteadOf
|
|
116
|
+
* or core.sshCommand, cannot change where a remote command connects
|
|
114
117
|
* @returns {Object} spawn result
|
|
115
118
|
*/
|
|
116
119
|
export function hardenedGitCommand(args, options = {}) {
|
|
@@ -122,8 +125,14 @@ export function hardenedGitCommand(args, options = {}) {
|
|
|
122
125
|
GIT_CONFIG_VALUE_0: "false",
|
|
123
126
|
GIT_CONFIG_KEY_1: "safe.bareRepository",
|
|
124
127
|
GIT_CONFIG_VALUE_1: "explicit",
|
|
128
|
+
GIT_NO_LAZY_FETCH: "1",
|
|
125
129
|
GIT_TERMINAL_PROMPT: "0",
|
|
126
130
|
};
|
|
131
|
+
if (options.noRepository) {
|
|
132
|
+
// ls-remote works without a repository. Pointing GIT_DIR at a path that
|
|
133
|
+
// does not exist stops git from discovering one in the working directory.
|
|
134
|
+
envConfigs.GIT_DIR = path.join(getTmpDir(), "cdxgen-no-repository.invalid");
|
|
135
|
+
}
|
|
127
136
|
const env = isSecureMode
|
|
128
137
|
? {
|
|
129
138
|
...process.env,
|
|
@@ -275,10 +284,14 @@ export function buildReleaseNotesFromGit(repoPath, options = {}) {
|
|
|
275
284
|
typeof remoteUrl === "string"
|
|
276
285
|
? validateAndRejectGitSource(remoteUrl)
|
|
277
286
|
: null;
|
|
287
|
+
// remote.origin.url comes from the scanned repository. Only a remote that
|
|
288
|
+
// validateAndRejectGitSource recognises and checks may reach ls-remote; git
|
|
289
|
+
// also treats forms such as host:path as remotes, which skip that check.
|
|
278
290
|
const canDiscoverRemoteTags =
|
|
279
291
|
(!currentTag || !previousTag) &&
|
|
280
292
|
typeof remoteUrl === "string" &&
|
|
281
293
|
!remoteUrl.startsWith("-") &&
|
|
294
|
+
maybeRemotePath(remoteUrl) &&
|
|
282
295
|
!remoteUrlValidationError &&
|
|
283
296
|
/github\.com[:/]/i.test(remoteUrl);
|
|
284
297
|
if (canDiscoverRemoteTags) {
|
|
@@ -298,7 +311,7 @@ export function buildReleaseNotesFromGit(repoPath, options = {}) {
|
|
|
298
311
|
"--",
|
|
299
312
|
remoteUrl,
|
|
300
313
|
],
|
|
301
|
-
{},
|
|
314
|
+
{ noRepository: true },
|
|
302
315
|
);
|
|
303
316
|
const remoteTags = parseLsRemoteTags(remoteTagsResult.stdout);
|
|
304
317
|
if (!currentTag && remoteTags.length) {
|
|
@@ -508,6 +521,59 @@ export function isAllowedPath(p) {
|
|
|
508
521
|
});
|
|
509
522
|
}
|
|
510
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Resolve symlinks in a path. When the path does not exist yet, the nearest
|
|
526
|
+
* existing parent is resolved and the remaining segments are appended, so
|
|
527
|
+
* /tmp/new on macOS still becomes /private/tmp/new.
|
|
528
|
+
*
|
|
529
|
+
* @param {string} p Path string
|
|
530
|
+
* @returns {string} real path
|
|
531
|
+
*/
|
|
532
|
+
function realPathOrResolved(p) {
|
|
533
|
+
let current = path.resolve(p);
|
|
534
|
+
const missing = [];
|
|
535
|
+
while (true) {
|
|
536
|
+
try {
|
|
537
|
+
return path.join(fs.realpathSync(current), ...missing);
|
|
538
|
+
} catch (_err) {
|
|
539
|
+
const parent = path.dirname(current);
|
|
540
|
+
if (parent === current) {
|
|
541
|
+
return path.resolve(p);
|
|
542
|
+
}
|
|
543
|
+
missing.unshift(path.basename(current));
|
|
544
|
+
current = parent;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Checks where the given path really points, after following symlinks, against
|
|
551
|
+
* where the allowed paths really point. Both sides are resolved, so an allowed
|
|
552
|
+
* directory that is itself reached through a link (such as /tmp on macOS) still
|
|
553
|
+
* matches, while a link inside an allowed directory that leads outside it does
|
|
554
|
+
* not.
|
|
555
|
+
*
|
|
556
|
+
* @param {string} p Path string to check
|
|
557
|
+
* @returns {boolean} true if the real path is inside a real allowed path
|
|
558
|
+
*/
|
|
559
|
+
export function isAllowedRealPath(p) {
|
|
560
|
+
if (typeof p !== "string" || hasDangerousUnicode(p)) {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
const allowedPaths = getAllowedPaths();
|
|
564
|
+
if (!allowedPaths.length) {
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
const realP = realPathOrResolved(p);
|
|
568
|
+
return allowedPaths.some((ap) => {
|
|
569
|
+
const relativePath = path.relative(realPathOrResolved(ap), realP);
|
|
570
|
+
return (
|
|
571
|
+
relativePath === "" ||
|
|
572
|
+
(!relativePath.startsWith("..") && !path.isAbsolute(relativePath))
|
|
573
|
+
);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
511
577
|
/**
|
|
512
578
|
* Determine if the path could be a package URL.
|
|
513
579
|
*
|
|
@@ -586,6 +652,49 @@ export function validateAndRejectGitSource(filePath) {
|
|
|
586
652
|
};
|
|
587
653
|
}
|
|
588
654
|
|
|
655
|
+
if (hasAmbiguousRemoteCharacters(filePath)) {
|
|
656
|
+
return {
|
|
657
|
+
status: 400,
|
|
658
|
+
error: "Invalid URL Format",
|
|
659
|
+
details:
|
|
660
|
+
"The provided Git URL contains backslashes, whitespace or control characters.",
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// git reads file:// URLs from the local disk, so they get the path
|
|
665
|
+
// allowlist rather than the host allowlist.
|
|
666
|
+
if (gitUrlObj.protocol === "file:") {
|
|
667
|
+
let localPath;
|
|
668
|
+
try {
|
|
669
|
+
localPath = fileURLToPath(gitUrlObj);
|
|
670
|
+
} catch (_err) {
|
|
671
|
+
localPath = undefined;
|
|
672
|
+
}
|
|
673
|
+
if (
|
|
674
|
+
gitUrlObj.hostname ||
|
|
675
|
+
!localPath ||
|
|
676
|
+
!isAllowedPath(localPath) ||
|
|
677
|
+
(isWin && !isAllowedWinPath(localPath))
|
|
678
|
+
) {
|
|
679
|
+
return {
|
|
680
|
+
status: 403,
|
|
681
|
+
error: "Path Not Allowed",
|
|
682
|
+
details: "Path is not allowed as per the allowlist.",
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// git, libcurl and OpenSSH must connect to the host that was checked.
|
|
689
|
+
const gitRemoteHost = getGitRemoteHost(filePath);
|
|
690
|
+
if (!gitRemoteHost || gitUrlObj.hostname.toLowerCase() !== gitRemoteHost) {
|
|
691
|
+
return {
|
|
692
|
+
status: 400,
|
|
693
|
+
error: "Invalid URL Format",
|
|
694
|
+
details: "The host of the provided Git URL is ambiguous.",
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
589
698
|
if (!isAllowedHost(gitUrlObj.hostname)) {
|
|
590
699
|
return {
|
|
591
700
|
status: 403,
|
|
@@ -598,6 +707,94 @@ export function validateAndRejectGitSource(filePath) {
|
|
|
598
707
|
return null;
|
|
599
708
|
}
|
|
600
709
|
|
|
710
|
+
/**
|
|
711
|
+
* Detect characters that git, libcurl or OpenSSH read differently from the
|
|
712
|
+
* WHATWG URL parser. A backslash ends the host for new URL() but is part of
|
|
713
|
+
* the user name for git, and new URL() silently drops tabs and newlines.
|
|
714
|
+
*
|
|
715
|
+
* @param {string} remote Git URL or SCP-like address
|
|
716
|
+
* @returns {boolean} true if the remote contains such characters
|
|
717
|
+
*/
|
|
718
|
+
function hasAmbiguousRemoteCharacters(remote) {
|
|
719
|
+
if (hasDangerousUnicode(remote)) {
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
for (const ch of remote) {
|
|
723
|
+
const code = ch.codePointAt(0);
|
|
724
|
+
if (ch === "\\" || code <= 0x20 || (code >= 0x7f && code <= 0x9f)) {
|
|
725
|
+
return true;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Return the host a git client connects to for a remote, read the way git,
|
|
733
|
+
* libcurl and OpenSSH read it rather than the way new URL() does. git ends the
|
|
734
|
+
* authority of ssh:// and git:// URLs only at the first slash, percent-decodes
|
|
735
|
+
* it before splitting off the host, and reads a leading "[...]" as the host.
|
|
736
|
+
* OpenSSH takes the host after the last "@". So a "?" or "#" before the first
|
|
737
|
+
* slash, a second "@", brackets in the user name, or a percent sign in an
|
|
738
|
+
* ssh:// or git:// authority make the host ambiguous.
|
|
739
|
+
*
|
|
740
|
+
* @param {string} remote Git URL or SCP-like address
|
|
741
|
+
* @returns {string|undefined} lower-cased host, or undefined when it is ambiguous
|
|
742
|
+
*/
|
|
743
|
+
export function getGitRemoteHost(remote) {
|
|
744
|
+
if (typeof remote !== "string") {
|
|
745
|
+
return undefined;
|
|
746
|
+
}
|
|
747
|
+
let authority;
|
|
748
|
+
const schemeEnd = remote.indexOf("://");
|
|
749
|
+
if (schemeEnd > -1) {
|
|
750
|
+
const scheme = remote.slice(0, schemeEnd).toLowerCase();
|
|
751
|
+
const rest = remote.slice(schemeEnd + 3);
|
|
752
|
+
const slash = rest.indexOf("/");
|
|
753
|
+
authority = slash > -1 ? rest.slice(0, slash) : rest;
|
|
754
|
+
// libcurl reads the http(s) authority as written. git decodes the others.
|
|
755
|
+
if (!["http", "https"].includes(scheme) && authority.includes("%")) {
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
758
|
+
} else {
|
|
759
|
+
// git reads user@host:path as SCP-like only when the colon comes before
|
|
760
|
+
// any slash. Otherwise it is a local path.
|
|
761
|
+
const colon = remote.indexOf(":");
|
|
762
|
+
const slash = remote.indexOf("/");
|
|
763
|
+
if (colon < 0 || (slash > -1 && slash < colon)) {
|
|
764
|
+
return undefined;
|
|
765
|
+
}
|
|
766
|
+
authority = remote.slice(0, colon);
|
|
767
|
+
}
|
|
768
|
+
if (authority.includes("?") || authority.includes("#")) {
|
|
769
|
+
return undefined;
|
|
770
|
+
}
|
|
771
|
+
const at = authority.indexOf("@");
|
|
772
|
+
if (at !== authority.lastIndexOf("@")) {
|
|
773
|
+
return undefined;
|
|
774
|
+
}
|
|
775
|
+
const userInfo = at > -1 ? authority.slice(0, at) : "";
|
|
776
|
+
if (userInfo.includes("[") || userInfo.includes("]")) {
|
|
777
|
+
return undefined;
|
|
778
|
+
}
|
|
779
|
+
let host = authority.slice(at + 1);
|
|
780
|
+
if (host.startsWith("[")) {
|
|
781
|
+
const close = host.indexOf("]");
|
|
782
|
+
if (close < 0) {
|
|
783
|
+
return undefined;
|
|
784
|
+
}
|
|
785
|
+
host = host.slice(0, close + 1);
|
|
786
|
+
} else {
|
|
787
|
+
const portSep = host.indexOf(":");
|
|
788
|
+
if (portSep > -1) {
|
|
789
|
+
host = host.slice(0, portSep);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (!host || host.startsWith("-")) {
|
|
793
|
+
return undefined;
|
|
794
|
+
}
|
|
795
|
+
return host.toLowerCase();
|
|
796
|
+
}
|
|
797
|
+
|
|
601
798
|
/**
|
|
602
799
|
* Clone a git repository into a temporary directory.
|
|
603
800
|
*
|
|
@@ -606,6 +803,11 @@ export function validateAndRejectGitSource(filePath) {
|
|
|
606
803
|
* @returns {string} cloned directory path
|
|
607
804
|
*/
|
|
608
805
|
export function gitClone(repoUrl, branch = null) {
|
|
806
|
+
// Callers validate first, but git must never see a remote that fails it.
|
|
807
|
+
const validationError = validateAndRejectGitSource(repoUrl);
|
|
808
|
+
if (validationError) {
|
|
809
|
+
throw new Error(`${validationError.error}: ${validationError.details}`);
|
|
810
|
+
}
|
|
609
811
|
let baseDirName = path.basename(repoUrl);
|
|
610
812
|
if (!/^[a-zA-Z0-9_-]+$/.test(baseDirName)) {
|
|
611
813
|
baseDirName = "repo-";
|
|
@@ -635,6 +837,8 @@ export function gitClone(repoUrl, branch = null) {
|
|
|
635
837
|
"safe.bareRepository=explicit",
|
|
636
838
|
"-c",
|
|
637
839
|
"core.hooksPath=/dev/null",
|
|
840
|
+
"-c",
|
|
841
|
+
"core.symlinks=false",
|
|
638
842
|
"clone",
|
|
639
843
|
"--template=",
|
|
640
844
|
repoUrl,
|
|
@@ -735,7 +939,7 @@ export function findGitRefForPurlVersion(repoUrl, purlResolution) {
|
|
|
735
939
|
"--",
|
|
736
940
|
repoUrl,
|
|
737
941
|
],
|
|
738
|
-
{},
|
|
942
|
+
{ noRepository: true },
|
|
739
943
|
);
|
|
740
944
|
if (result.status !== 0 || !result.stdout) {
|
|
741
945
|
return undefined;
|
|
@@ -1165,6 +1369,8 @@ export function resetGitUrlSourcePrefetch() {
|
|
|
1165
1369
|
gitUrlSourcePrefetch = new Map();
|
|
1166
1370
|
}
|
|
1167
1371
|
|
|
1372
|
+
registerRunReset("source:registry-documents", resetGitUrlSourcePrefetch);
|
|
1373
|
+
|
|
1168
1374
|
/**
|
|
1169
1375
|
* Prefetch the registry documents a list of purls will be resolved from.
|
|
1170
1376
|
*
|
|
@@ -1218,12 +1424,10 @@ async function fetchRegistryDocument(lookup) {
|
|
|
1218
1424
|
// whose policy is one request per second however the request was reached.
|
|
1219
1425
|
return (
|
|
1220
1426
|
prefetchedResponse(gitUrlSourcePrefetch, lookup.url) ||
|
|
1221
|
-
(await
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
}),
|
|
1226
|
-
))
|
|
1427
|
+
(await gatedGet(lookup.url, {
|
|
1428
|
+
...(lookup.accept ? { headers: { Accept: lookup.accept } } : {}),
|
|
1429
|
+
responseType: "json",
|
|
1430
|
+
}))
|
|
1227
1431
|
);
|
|
1228
1432
|
}
|
|
1229
1433
|
|
package/lib/inventory/spdx.js
CHANGED
|
@@ -343,12 +343,257 @@ export function findLicenseId(name) {
|
|
|
343
343
|
: name;
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Identify a license from the title and opening of its full text. A license
|
|
348
|
+
* text quotes other licenses (the GNU GPL's text names the GNU Lesser GPL),
|
|
349
|
+
* so the substring scan of `guessLicenseId` alone can name the wrong one.
|
|
350
|
+
*
|
|
351
|
+
* @param {string} content License file contents
|
|
352
|
+
* @returns {string|undefined} SPDX license id or expression
|
|
353
|
+
*/
|
|
354
|
+
export function licenseIdFromText(content) {
|
|
355
|
+
if (typeof content !== "string" || !content.trim()) {
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
const head = content.slice(0, 4000).replace(/\s+/g, " ").trim().toUpperCase();
|
|
359
|
+
const version = (prefix) => {
|
|
360
|
+
const at = head.indexOf(prefix);
|
|
361
|
+
if (at < 0) {
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
const tail = head.slice(at + prefix.length, at + prefix.length + 60);
|
|
365
|
+
const match = tail.match(/VERSION (\d(?:\.\d)?)/);
|
|
366
|
+
return match?.[1];
|
|
367
|
+
};
|
|
368
|
+
// the first lines name the license; quotations of others come later, so
|
|
369
|
+
// of the GNU licenses the one named first is the one the text is
|
|
370
|
+
const title = head.slice(0, 200);
|
|
371
|
+
const gnu = [
|
|
372
|
+
["GNU AFFERO GENERAL PUBLIC LICENSE", { 3: "AGPL-3.0-only" }],
|
|
373
|
+
[
|
|
374
|
+
"GNU LESSER GENERAL PUBLIC LICENSE",
|
|
375
|
+
{ 3: "LGPL-3.0-only", 2.1: "LGPL-2.1-only", 2: "LGPL-2.0-only" },
|
|
376
|
+
],
|
|
377
|
+
[
|
|
378
|
+
"GNU LIBRARY GENERAL PUBLIC LICENSE",
|
|
379
|
+
{ 2.1: "LGPL-2.1-only", 2: "LGPL-2.0-only" },
|
|
380
|
+
],
|
|
381
|
+
[
|
|
382
|
+
"GNU GENERAL PUBLIC LICENSE",
|
|
383
|
+
{ 3: "GPL-3.0-only", 2: "GPL-2.0-only", 1: "GPL-1.0-only" },
|
|
384
|
+
],
|
|
385
|
+
]
|
|
386
|
+
.map(([name, ids]) => ({ name, ids, at: title.indexOf(name) }))
|
|
387
|
+
.filter((t) => t.at >= 0)
|
|
388
|
+
.sort((a, b) => a.at - b.at)[0];
|
|
389
|
+
if (gnu) {
|
|
390
|
+
return gnu.ids[version(gnu.name)];
|
|
391
|
+
}
|
|
392
|
+
if (head.includes("APACHE LICENSE") && head.includes("VERSION 2.0")) {
|
|
393
|
+
return head.includes("LLVM EXCEPTION")
|
|
394
|
+
? "Apache-2.0 WITH LLVM-exception"
|
|
395
|
+
: "Apache-2.0";
|
|
396
|
+
}
|
|
397
|
+
if (
|
|
398
|
+
title.includes("MOZILLA PUBLIC LICENSE") &&
|
|
399
|
+
head.includes("VERSION 2.0")
|
|
400
|
+
) {
|
|
401
|
+
return "MPL-2.0";
|
|
402
|
+
}
|
|
403
|
+
if (title.includes("BOOST SOFTWARE LICENSE - VERSION 1.0")) {
|
|
404
|
+
return "BSL-1.0";
|
|
405
|
+
}
|
|
406
|
+
if (
|
|
407
|
+
head.includes(
|
|
408
|
+
"THIS IS FREE AND UNENCUMBERED SOFTWARE RELEASED INTO THE PUBLIC DOMAIN",
|
|
409
|
+
)
|
|
410
|
+
) {
|
|
411
|
+
return "Unlicense";
|
|
412
|
+
}
|
|
413
|
+
if (head.includes("PERMISSION IS HEREBY GRANTED, FREE OF CHARGE")) {
|
|
414
|
+
return "MIT";
|
|
415
|
+
}
|
|
416
|
+
if (
|
|
417
|
+
head.includes(
|
|
418
|
+
"PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED",
|
|
419
|
+
)
|
|
420
|
+
) {
|
|
421
|
+
return "ISC";
|
|
422
|
+
}
|
|
423
|
+
if (
|
|
424
|
+
head.includes("THIS SOFTWARE IS PROVIDED 'AS-IS'") &&
|
|
425
|
+
head.includes(
|
|
426
|
+
"PERMISSION IS GRANTED TO ANYONE TO USE THIS SOFTWARE FOR ANY PURPOSE",
|
|
427
|
+
)
|
|
428
|
+
) {
|
|
429
|
+
return "Zlib";
|
|
430
|
+
}
|
|
431
|
+
if (head.includes("REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS")) {
|
|
432
|
+
if (head.includes("ALL ADVERTISING MATERIALS MENTIONING")) {
|
|
433
|
+
return "BSD-4-Clause";
|
|
434
|
+
}
|
|
435
|
+
return head.includes("NEITHER THE NAME") ||
|
|
436
|
+
head.includes("THE NAME OF THE AUTHOR MAY NOT BE USED")
|
|
437
|
+
? "BSD-3-Clause"
|
|
438
|
+
: "BSD-2-Clause";
|
|
439
|
+
}
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// The GNU licenses by the title line their text opens with, and their SPDX
|
|
444
|
+
// ids by version.
|
|
445
|
+
const GNU_LICENSE_TITLES = {
|
|
446
|
+
"GNU AFFERO GENERAL PUBLIC LICENSE": { 3: "AGPL-3.0-only" },
|
|
447
|
+
"GNU LESSER GENERAL PUBLIC LICENSE": {
|
|
448
|
+
3: "LGPL-3.0-only",
|
|
449
|
+
2.1: "LGPL-2.1-only",
|
|
450
|
+
2: "LGPL-2.0-only",
|
|
451
|
+
},
|
|
452
|
+
"GNU LIBRARY GENERAL PUBLIC LICENSE": {
|
|
453
|
+
2.1: "LGPL-2.1-only",
|
|
454
|
+
2: "LGPL-2.0-only",
|
|
455
|
+
},
|
|
456
|
+
"GNU GENERAL PUBLIC LICENSE": {
|
|
457
|
+
3: "GPL-3.0-only",
|
|
458
|
+
2: "GPL-2.0-only",
|
|
459
|
+
1: "GPL-1.0-only",
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// Phrases that only the grant of one license carries, so finding one anywhere
|
|
464
|
+
// in a notice means that license is granted, not merely mentioned.
|
|
465
|
+
const LICENSE_GRANT_PHRASES = [
|
|
466
|
+
["PERMISSION IS HEREBY GRANTED, FREE OF CHARGE", "MIT"],
|
|
467
|
+
[
|
|
468
|
+
"PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED",
|
|
469
|
+
"ISC",
|
|
470
|
+
],
|
|
471
|
+
[
|
|
472
|
+
"PERMISSION IS GRANTED TO ANYONE TO USE THIS SOFTWARE FOR ANY PURPOSE",
|
|
473
|
+
"Zlib",
|
|
474
|
+
],
|
|
475
|
+
[
|
|
476
|
+
"THIS IS FREE AND UNENCUMBERED SOFTWARE RELEASED INTO THE PUBLIC DOMAIN",
|
|
477
|
+
"Unlicense",
|
|
478
|
+
],
|
|
479
|
+
["BOOST SOFTWARE LICENSE - VERSION 1.0", "BSL-1.0"],
|
|
480
|
+
["MOZILLA PUBLIC LICENSE VERSION 2.0", "MPL-2.0"],
|
|
481
|
+
["MOZILLA PUBLIC LICENSE, VERSION 2.0", "MPL-2.0"],
|
|
482
|
+
["MOZILLA PUBLIC LICENSE, V. 2.0", "MPL-2.0"],
|
|
483
|
+
[
|
|
484
|
+
"CREATIVE COMMONS ATTRIBUTION 4.0 INTERNATIONAL PUBLIC LICENSE",
|
|
485
|
+
"CC-BY-4.0",
|
|
486
|
+
],
|
|
487
|
+
[
|
|
488
|
+
"CREATIVE COMMONS ATTRIBUTION-SHAREALIKE 4.0 INTERNATIONAL PUBLIC LICENSE",
|
|
489
|
+
"CC-BY-SA-4.0",
|
|
490
|
+
],
|
|
491
|
+
["CC0 1.0 UNIVERSAL", "CC0-1.0"],
|
|
492
|
+
];
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Identify every license a notice grants, in the order the notice grants
|
|
496
|
+
* them. A notice can grant several: one LICENSE file holding an MIT grant and
|
|
497
|
+
* an Apache-2.0 grant (gopkg.in/yaml.v3), or a choice of licenses followed by
|
|
498
|
+
* each full text (github.com/spdx/tools-golang). A license text quotes others
|
|
499
|
+
* (the GNU GPL names the GNU Lesser GPL, the MPL names the GNU licenses), so
|
|
500
|
+
* only phrases that a grant carries count: a GNU license is found by its title
|
|
501
|
+
* standing on a line of its own, the others by a sentence of their grant.
|
|
502
|
+
* A notice none of these phrases match falls back to {@link guessLicenseId}.
|
|
503
|
+
*
|
|
504
|
+
* @param {string} content License file contents
|
|
505
|
+
* @returns {string[]} SPDX license ids or expressions; empty when none is
|
|
506
|
+
* recognized
|
|
507
|
+
*/
|
|
508
|
+
export function guessLicenseIds(content) {
|
|
509
|
+
if (typeof content !== "string" || !content.trim()) {
|
|
510
|
+
return [];
|
|
511
|
+
}
|
|
512
|
+
const text = content.replace(/\s+/g, " ").toUpperCase();
|
|
513
|
+
const found = [];
|
|
514
|
+
const add = (at, id) => {
|
|
515
|
+
if (at >= 0 && id) {
|
|
516
|
+
found.push({ at, id });
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
// Walk the lines alongside `text`, so a title line is placed in it.
|
|
520
|
+
let offset = 0;
|
|
521
|
+
for (const line of content.split("\n")) {
|
|
522
|
+
const normalized = line.replace(/\s+/g, " ").trim().toUpperCase();
|
|
523
|
+
const at = normalized ? text.indexOf(normalized, offset) : -1;
|
|
524
|
+
if (at < 0) {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
offset = at + normalized.length;
|
|
528
|
+
const ids = GNU_LICENSE_TITLES[normalized];
|
|
529
|
+
if (ids) {
|
|
530
|
+
const version = text
|
|
531
|
+
.slice(offset, offset + 60)
|
|
532
|
+
.match(/VERSION (\d(?:\.\d)?)/)?.[1];
|
|
533
|
+
add(at, ids[version]);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
for (const marker of [
|
|
537
|
+
"APACHE LICENSE VERSION 2.0",
|
|
538
|
+
"APACHE LICENSE, VERSION 2.0",
|
|
539
|
+
"APACHE LICENSE 2.0",
|
|
540
|
+
]) {
|
|
541
|
+
add(
|
|
542
|
+
text.indexOf(marker),
|
|
543
|
+
text.includes("LLVM EXCEPTION")
|
|
544
|
+
? "Apache-2.0 WITH LLVM-exception"
|
|
545
|
+
: "Apache-2.0",
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
for (const [phrase, id] of LICENSE_GRANT_PHRASES) {
|
|
549
|
+
add(text.indexOf(phrase), id);
|
|
550
|
+
}
|
|
551
|
+
// A notice can hold several BSD texts (one per copyright holder), each read
|
|
552
|
+
// up to where the next one starts.
|
|
553
|
+
const bsdPhrase = "REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS";
|
|
554
|
+
for (
|
|
555
|
+
let bsdAt = text.indexOf(bsdPhrase);
|
|
556
|
+
bsdAt >= 0;
|
|
557
|
+
bsdAt = text.indexOf(bsdPhrase, bsdAt + bsdPhrase.length)
|
|
558
|
+
) {
|
|
559
|
+
const nextAt = text.indexOf(bsdPhrase, bsdAt + bsdPhrase.length);
|
|
560
|
+
const clauses = text.slice(
|
|
561
|
+
bsdAt,
|
|
562
|
+
Math.min(bsdAt + 2000, nextAt < 0 ? text.length : nextAt),
|
|
563
|
+
);
|
|
564
|
+
add(
|
|
565
|
+
bsdAt,
|
|
566
|
+
clauses.includes("ALL ADVERTISING MATERIALS MENTIONING")
|
|
567
|
+
? "BSD-4-Clause"
|
|
568
|
+
: clauses.includes("NEITHER THE NAME") ||
|
|
569
|
+
clauses.includes("THE NAME OF THE AUTHOR MAY NOT BE USED")
|
|
570
|
+
? "BSD-3-Clause"
|
|
571
|
+
: "BSD-2-Clause",
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
const ids = [];
|
|
575
|
+
for (const { id } of found.sort((a, b) => a.at - b.at)) {
|
|
576
|
+
if (!ids.includes(id)) {
|
|
577
|
+
ids.push(id);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (ids.length) {
|
|
581
|
+
return ids;
|
|
582
|
+
}
|
|
583
|
+
const guessed = guessLicenseId(content);
|
|
584
|
+
return guessed ? [guessed] : [];
|
|
585
|
+
}
|
|
586
|
+
|
|
346
587
|
/**
|
|
347
588
|
* Method to guess the spdx license id from license contents
|
|
348
589
|
*
|
|
349
590
|
* @param {string} content License file contents
|
|
350
591
|
*/
|
|
351
592
|
export function guessLicenseId(content) {
|
|
593
|
+
const fromText = licenseIdFromText(content);
|
|
594
|
+
if (fromText) {
|
|
595
|
+
return fromText;
|
|
596
|
+
}
|
|
352
597
|
content = content.replace(/\n/g, " ");
|
|
353
598
|
for (const l of licenseMapping) {
|
|
354
599
|
for (const j in l.names) {
|