@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
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
basename,
|
|
4
|
+
dirname,
|
|
5
|
+
isAbsolute,
|
|
6
|
+
join,
|
|
7
|
+
posix,
|
|
8
|
+
resolve,
|
|
9
|
+
win32,
|
|
10
|
+
} from "node:path";
|
|
3
11
|
|
|
4
12
|
import { build } from "@cdxgen/cdx-purl";
|
|
5
13
|
|
|
@@ -41,6 +49,304 @@ export function applyNugetVersion(pkg, version) {
|
|
|
41
49
|
return applyPurl(pkg, nugetPurl(pkg.name, concrete));
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Compare two NuGet versions: up to four numeric release parts (missing parts
|
|
54
|
+
* read as 0), then the prerelease label, with a release sorting after its own
|
|
55
|
+
* prereleases. Build metadata after `+` is ignored, as NuGet ignores it.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} a Version
|
|
58
|
+
* @param {string} b Version
|
|
59
|
+
* @returns {number} Negative, zero or positive, like a sort comparator
|
|
60
|
+
*/
|
|
61
|
+
export function compareNugetVersions(a, b) {
|
|
62
|
+
const split = (version) => {
|
|
63
|
+
const withoutMetadata = `${version ?? ""}`.trim().split("+")[0];
|
|
64
|
+
const dash = withoutMetadata.indexOf("-");
|
|
65
|
+
const release =
|
|
66
|
+
dash >= 0 ? withoutMetadata.slice(0, dash) : withoutMetadata;
|
|
67
|
+
const prerelease = dash >= 0 ? withoutMetadata.slice(dash + 1) : "";
|
|
68
|
+
const parts = release.split(".").map((part) => Number.parseInt(part, 10));
|
|
69
|
+
return { parts, prerelease };
|
|
70
|
+
};
|
|
71
|
+
const left = split(a);
|
|
72
|
+
const right = split(b);
|
|
73
|
+
for (let index = 0; index < 4; index++) {
|
|
74
|
+
const l = Number.isNaN(left.parts[index]) ? 0 : (left.parts[index] ?? 0);
|
|
75
|
+
const r = Number.isNaN(right.parts[index]) ? 0 : (right.parts[index] ?? 0);
|
|
76
|
+
if (l !== r) {
|
|
77
|
+
return l < r ? -1 : 1;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (left.prerelease === right.prerelease) {
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
if (!left.prerelease) {
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
if (!right.prerelease) {
|
|
87
|
+
return -1;
|
|
88
|
+
}
|
|
89
|
+
const leftLabels = left.prerelease.toLowerCase().split(".");
|
|
90
|
+
const rightLabels = right.prerelease.toLowerCase().split(".");
|
|
91
|
+
for (
|
|
92
|
+
let index = 0;
|
|
93
|
+
index < Math.max(leftLabels.length, rightLabels.length);
|
|
94
|
+
index++
|
|
95
|
+
) {
|
|
96
|
+
const l = leftLabels[index];
|
|
97
|
+
const r = rightLabels[index];
|
|
98
|
+
if (l === undefined || r === undefined) {
|
|
99
|
+
return l === undefined ? -1 : 1;
|
|
100
|
+
}
|
|
101
|
+
if (l === r) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const lNumeric = /^\d+$/.test(l);
|
|
105
|
+
const rNumeric = /^\d+$/.test(r);
|
|
106
|
+
if (lNumeric && rNumeric) {
|
|
107
|
+
return Number(l) < Number(r) ? -1 : 1;
|
|
108
|
+
}
|
|
109
|
+
if (lNumeric !== rNumeric) {
|
|
110
|
+
return lNumeric ? -1 : 1;
|
|
111
|
+
}
|
|
112
|
+
return l < r ? -1 : 1;
|
|
113
|
+
}
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Whether a NuGet version range allows a version. A bare version is a minimum
|
|
119
|
+
* (`1.0` means at least 1.0); brackets are inclusive and parentheses exclusive
|
|
120
|
+
* (`[1.0]`, `[1.0,2.0)`, `(,2.0]`). An empty or unparseable range allows
|
|
121
|
+
* anything.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} range Range as a nuspec or lock file states it
|
|
124
|
+
* @param {string} version Version to test
|
|
125
|
+
* @returns {boolean} True when the range allows the version
|
|
126
|
+
*/
|
|
127
|
+
export function nugetRangeAllows(range, version) {
|
|
128
|
+
const text = `${range ?? ""}`.trim();
|
|
129
|
+
if (!text || !version) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
const opening = text[0];
|
|
133
|
+
if (opening !== "[" && opening !== "(") {
|
|
134
|
+
return compareNugetVersions(version, text) >= 0;
|
|
135
|
+
}
|
|
136
|
+
const closing = text[text.length - 1];
|
|
137
|
+
if (closing !== "]" && closing !== ")") {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
const inner = text.slice(1, -1);
|
|
141
|
+
const comma = inner.indexOf(",");
|
|
142
|
+
if (comma < 0) {
|
|
143
|
+
return compareNugetVersions(version, inner.trim()) === 0;
|
|
144
|
+
}
|
|
145
|
+
const minimum = inner.slice(0, comma).trim();
|
|
146
|
+
const maximum = inner.slice(comma + 1).trim();
|
|
147
|
+
if (minimum) {
|
|
148
|
+
const order = compareNugetVersions(version, minimum);
|
|
149
|
+
if (order < 0 || (order === 0 && opening === "(")) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (maximum) {
|
|
154
|
+
const order = compareNugetVersions(version, maximum);
|
|
155
|
+
if (order > 0 || (order === 0 && closing === ")")) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Choose the installed version a declared dependency resolves to: the lowest
|
|
164
|
+
* installed version the range allows, as NuGet picks the lowest applicable
|
|
165
|
+
* version; else the only installed version, which is what binding redirects
|
|
166
|
+
* unify a dependency onto. Undefined when nothing installed can be named.
|
|
167
|
+
*
|
|
168
|
+
* @param {string} range Declared range
|
|
169
|
+
* @param {string[]} installedVersions Versions installed for the package
|
|
170
|
+
* @returns {string|undefined} The chosen version
|
|
171
|
+
*/
|
|
172
|
+
export function resolveInstalledNugetVersion(range, installedVersions = []) {
|
|
173
|
+
const allowed = installedVersions
|
|
174
|
+
.filter((version) => nugetRangeAllows(range, version))
|
|
175
|
+
.sort(compareNugetVersions);
|
|
176
|
+
if (allowed.length) {
|
|
177
|
+
return allowed[0];
|
|
178
|
+
}
|
|
179
|
+
return installedVersions.length === 1 ? installedVersions[0] : undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The project directory a .NET manifest belongs to. Restore output records its
|
|
184
|
+
* project file; otherwise project.assets.json sits in the project's `obj/`,
|
|
185
|
+
* and lock files, packages.config and project files sit in the project
|
|
186
|
+
* directory itself.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} manifestFile Manifest path
|
|
189
|
+
* @param {string} [projectPath] Project file the manifest names, when it does
|
|
190
|
+
* @returns {string} Project directory
|
|
191
|
+
*/
|
|
192
|
+
export function dotnetManifestProjectDir(manifestFile, projectPath) {
|
|
193
|
+
// A path recorded on another operating system, such as `D:\src\App.csproj`
|
|
194
|
+
// read on Linux, is not absolute here and would resolve against the working
|
|
195
|
+
// directory.
|
|
196
|
+
if (projectPath && isAbsolute(projectPath)) {
|
|
197
|
+
const projectDir = dirname(resolve(projectPath));
|
|
198
|
+
if (safeExistsSync(projectDir)) {
|
|
199
|
+
return projectDir;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const dir = dirname(resolve(manifestFile));
|
|
203
|
+
return basename(dir) === "obj" ? dirname(dir) : dir;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The path, on this machine, of the project file a project.assets.json belongs to.
|
|
208
|
+
*
|
|
209
|
+
* Restore records the project file and the directory it wrote the assets file
|
|
210
|
+
* to (`outputPath`) as absolute paths on the machine that ran it, which can be
|
|
211
|
+
* a CI agent, a container, another operating system, or this tree reached
|
|
212
|
+
* through another path. Where the project file sits relative to that
|
|
213
|
+
* directory does not change when the tree moves, so the project file is that
|
|
214
|
+
* offset applied to the directory the assets file is in. An assets file that
|
|
215
|
+
* records no output path is taken to be in its project's `obj/`. The recorded
|
|
216
|
+
* path itself is used only when no offset can be worked out, as when restore
|
|
217
|
+
* wrote to another drive than the project's.
|
|
218
|
+
*
|
|
219
|
+
* Nothing is read from disk: the name is spelled as restore recorded it, which
|
|
220
|
+
* can differ in case from the file a case-insensitive file system holds.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} assetsFile project.assets.json path
|
|
223
|
+
* @param {string} [projectPath] Project file restore recorded
|
|
224
|
+
* @param {string} [outputPath] Directory restore recorded writing the assets file to
|
|
225
|
+
* @returns {string|undefined} Project file path, or undefined when none can be named
|
|
226
|
+
*/
|
|
227
|
+
export function dotnetRestoredProjectFile(assetsFile, projectPath, outputPath) {
|
|
228
|
+
if (!assetsFile || typeof projectPath !== "string" || !projectPath.trim()) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
// Read the recorded paths the way the machine that wrote them would
|
|
232
|
+
const recorded =
|
|
233
|
+
win32.isAbsolute(projectPath) && !posix.isAbsolute(projectPath)
|
|
234
|
+
? win32
|
|
235
|
+
: posix;
|
|
236
|
+
const assetsDir = dirname(resolve(assetsFile));
|
|
237
|
+
let offset;
|
|
238
|
+
if (typeof outputPath === "string" && outputPath.trim()) {
|
|
239
|
+
const relativePath = recorded.relative(outputPath, projectPath);
|
|
240
|
+
// Across two drives there is no relative path, only the recorded one
|
|
241
|
+
if (!recorded.isAbsolute(relativePath)) {
|
|
242
|
+
offset = relativePath.split(recorded.sep);
|
|
243
|
+
}
|
|
244
|
+
} else if (basename(assetsDir) === "obj") {
|
|
245
|
+
offset = ["..", recorded.basename(projectPath)];
|
|
246
|
+
}
|
|
247
|
+
// The offset must end at a file, not at the output directory or a parent
|
|
248
|
+
const fileName = offset?.at(-1);
|
|
249
|
+
if (fileName && fileName !== "." && fileName !== "..") {
|
|
250
|
+
return resolve(assetsDir, ...offset);
|
|
251
|
+
}
|
|
252
|
+
return isAbsolute(projectPath) ? resolve(projectPath) : undefined;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Index the MSBuild properties that props files define, by the directory each
|
|
257
|
+
* file sits in and across the whole scan, so the candidates for every project
|
|
258
|
+
* come from one read of each file.
|
|
259
|
+
*
|
|
260
|
+
* @param {string[]} propsFiles Props files of the scan
|
|
261
|
+
* @returns {{byDir: Object, treeWide: Object}} Property values per directory and
|
|
262
|
+
* across the scan, each a map of property name to values
|
|
263
|
+
*/
|
|
264
|
+
export function indexPropsProperties(propsFiles = []) {
|
|
265
|
+
const byDir = {};
|
|
266
|
+
const treeWide = {};
|
|
267
|
+
for (const propsFile of propsFiles) {
|
|
268
|
+
if (!propsFile) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const nodes = getPropertyGroupTextNodes([propsFile]);
|
|
272
|
+
const dir = dirname(resolve(propsFile));
|
|
273
|
+
byDir[dir] ??= {};
|
|
274
|
+
for (const [key, values] of Object.entries(nodes)) {
|
|
275
|
+
byDir[dir][key] ??= [];
|
|
276
|
+
treeWide[key] ??= [];
|
|
277
|
+
for (const value of values) {
|
|
278
|
+
if (!byDir[dir][key].includes(value)) {
|
|
279
|
+
byDir[dir][key].push(value);
|
|
280
|
+
}
|
|
281
|
+
if (!treeWide[key].includes(value)) {
|
|
282
|
+
treeWide[key].push(value);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return { byDir, treeWide };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* The candidate values of MSBuild properties that can apply to a project file,
|
|
292
|
+
* from the props files beside it and above it: the nearest directory that
|
|
293
|
+
* defines a property wins, as the nearest Directory.Build.props is the one
|
|
294
|
+
* MSBuild imports. A property no such directory defines falls back to the
|
|
295
|
+
* values the whole scan's props files give it, which a caller only trusts when
|
|
296
|
+
* there is exactly one.
|
|
297
|
+
*
|
|
298
|
+
* @param {string} projFile Project file
|
|
299
|
+
* @param {{byDir: Object, treeWide: Object}} propsIndex Index built by {@link indexPropsProperties}
|
|
300
|
+
* @returns {Object} Map of property name to candidate values
|
|
301
|
+
*/
|
|
302
|
+
export function projectPropertyCandidates(projFile, propsIndex = {}) {
|
|
303
|
+
const candidates = { ...(propsIndex.treeWide || {}) };
|
|
304
|
+
const settled = new Set();
|
|
305
|
+
let dir = dirname(resolve(projFile));
|
|
306
|
+
for (;;) {
|
|
307
|
+
for (const [key, values] of Object.entries(propsIndex.byDir?.[dir] || {})) {
|
|
308
|
+
if (!settled.has(key)) {
|
|
309
|
+
candidates[key] = values;
|
|
310
|
+
settled.add(key);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const parent = dirname(dir);
|
|
314
|
+
if (parent === dir) {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
dir = parent;
|
|
318
|
+
}
|
|
319
|
+
return candidates;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The version of each package that every manifest of a scan agrees on, keyed by
|
|
324
|
+
* lowercased package id. A package that two manifests give different versions
|
|
325
|
+
* is left out: without knowing which project a declaration belongs to, picking
|
|
326
|
+
* one of them would be a guess.
|
|
327
|
+
*
|
|
328
|
+
* @param {Object[]} pkgList Components collected from the scan's manifests
|
|
329
|
+
* @returns {Object} Map of lowercased package id to version
|
|
330
|
+
*/
|
|
331
|
+
export function agreedNugetVersions(pkgList = []) {
|
|
332
|
+
const versions = {};
|
|
333
|
+
for (const p of pkgList) {
|
|
334
|
+
if (!p?.name || !p.version || ["application", "project"].includes(p.type)) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const key = p.name.toLowerCase();
|
|
338
|
+
versions[key] ??= new Set();
|
|
339
|
+
versions[key].add(p.version);
|
|
340
|
+
}
|
|
341
|
+
const agreed = {};
|
|
342
|
+
for (const [key, values] of Object.entries(versions)) {
|
|
343
|
+
if (values.size === 1) {
|
|
344
|
+
agreed[key] = [...values][0];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return agreed;
|
|
348
|
+
}
|
|
349
|
+
|
|
44
350
|
/**
|
|
45
351
|
* Method to parse .nupkg files
|
|
46
352
|
*
|
|
@@ -70,9 +376,16 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
70
376
|
const pkg = { group: "" };
|
|
71
377
|
let npkg;
|
|
72
378
|
const dependenciesMap = {};
|
|
379
|
+
// The version range each dependency accepts, per depending package, so a
|
|
380
|
+
// caller holding the installed packages can point an edge at one of them.
|
|
381
|
+
const dependencyRanges = {};
|
|
73
382
|
const addedMap = {};
|
|
74
383
|
try {
|
|
75
|
-
|
|
384
|
+
// A nuspec written on Windows often starts with a UTF-8 byte order mark,
|
|
385
|
+
// which the XML parser rejects as text before the declaration.
|
|
386
|
+
const data =
|
|
387
|
+
nuspecData.charCodeAt(0) === 0xfeff ? nuspecData.slice(1) : nuspecData;
|
|
388
|
+
npkg = xml2js(data, {
|
|
76
389
|
compact: true,
|
|
77
390
|
alwaysArray: false,
|
|
78
391
|
spaces: 4,
|
|
@@ -94,6 +407,7 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
94
407
|
return {
|
|
95
408
|
pkgList,
|
|
96
409
|
dependenciesMap,
|
|
410
|
+
dependencyRanges,
|
|
97
411
|
};
|
|
98
412
|
}
|
|
99
413
|
const m = npkg.metadata;
|
|
@@ -101,7 +415,12 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
101
415
|
pkg.version = m.version._;
|
|
102
416
|
pkg.description = m.description._;
|
|
103
417
|
applyPurl(pkg, nugetPurl(pkg.name, pkg.version));
|
|
104
|
-
|
|
418
|
+
// A licence expression is the machine-readable licence NuGet records; the
|
|
419
|
+
// licence URL is the older, often unresolvable form, so the expression wins.
|
|
420
|
+
if (m.license?.$?.type === "expression" && m.license?._?.trim()) {
|
|
421
|
+
pkg.license = findLicenseId(m.license._.trim()) || m.license._.trim();
|
|
422
|
+
}
|
|
423
|
+
if (!pkg.license && m.licenseUrl) {
|
|
105
424
|
pkg.license = findLicenseId(m.licenseUrl._);
|
|
106
425
|
}
|
|
107
426
|
if (m.authors) {
|
|
@@ -128,18 +447,19 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
128
447
|
};
|
|
129
448
|
pkg.scope = "required";
|
|
130
449
|
pkgList.push(pkg);
|
|
450
|
+
const ranges = {};
|
|
131
451
|
if (m?.dependencies?.dependency) {
|
|
132
452
|
const dependsOn = [];
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
} else {
|
|
139
|
-
const d = m.dependencies.dependency.$;
|
|
453
|
+
const declared = Array.isArray(m.dependencies.dependency)
|
|
454
|
+
? m.dependencies.dependency
|
|
455
|
+
: [m.dependencies.dependency];
|
|
456
|
+
for (const adep of declared) {
|
|
457
|
+
const d = adep.$;
|
|
140
458
|
dependsOn.push(d.id);
|
|
459
|
+
ranges[d.id] ??= d.version;
|
|
141
460
|
}
|
|
142
461
|
dependenciesMap[pkg["bom-ref"]] = dependsOn;
|
|
462
|
+
dependencyRanges[pkg["bom-ref"]] = ranges;
|
|
143
463
|
} else if (m?.dependencies?.group) {
|
|
144
464
|
let dependencyGroups;
|
|
145
465
|
if (Array.isArray(m.dependencies.group)) {
|
|
@@ -168,6 +488,7 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
168
488
|
continue;
|
|
169
489
|
}
|
|
170
490
|
groupPkg.name = agroupdep.id;
|
|
491
|
+
ranges[agroupdep.id] ??= agroupdep.version;
|
|
171
492
|
let declaredRange;
|
|
172
493
|
if (agroupdep?.version) {
|
|
173
494
|
let versionStr = agroupdep.version;
|
|
@@ -225,11 +546,13 @@ export function parseNuspecData(nupkgFile, nuspecData) {
|
|
|
225
546
|
} // for
|
|
226
547
|
} // group dependency block
|
|
227
548
|
dependenciesMap[pkg["bom-ref"]] = dependsOn;
|
|
549
|
+
dependencyRanges[pkg["bom-ref"]] = ranges;
|
|
228
550
|
} // for
|
|
229
551
|
}
|
|
230
552
|
return {
|
|
231
553
|
pkgList,
|
|
232
554
|
dependenciesMap,
|
|
555
|
+
dependencyRanges,
|
|
233
556
|
};
|
|
234
557
|
}
|
|
235
558
|
|
|
@@ -289,7 +612,7 @@ export function parseCsPkgData(pkgData, pkgFile, pkgNameVersions = {}) {
|
|
|
289
612
|
// Backfill from a version already resolved by a more precise manifest.
|
|
290
613
|
// Concrete versions are never overridden - a disagreement means both
|
|
291
614
|
// versions must be tracked.
|
|
292
|
-
pkg.version = pkgNameVersions
|
|
615
|
+
pkg.version = lookupNugetVersion(pkgNameVersions, pkg.name);
|
|
293
616
|
}
|
|
294
617
|
applyNugetVersion(pkg, pkg.version);
|
|
295
618
|
if (pkgFile) {
|
|
@@ -481,18 +804,121 @@ function propertyGroupText(propertyGroup, key) {
|
|
|
481
804
|
return text.trim();
|
|
482
805
|
}
|
|
483
806
|
|
|
807
|
+
/**
|
|
808
|
+
* Look up a package version in a name - version map. NuGet package ids are
|
|
809
|
+
* case-insensitive, so a map may be keyed by the id as written or lowercased.
|
|
810
|
+
*
|
|
811
|
+
* @param {Object} versions Map of package id to version
|
|
812
|
+
* @param {string} name Package id
|
|
813
|
+
* @returns {string|undefined} The version, or undefined when the map has none
|
|
814
|
+
*/
|
|
815
|
+
export function lookupNugetVersion(versions, name) {
|
|
816
|
+
if (!versions || !name) {
|
|
817
|
+
return undefined;
|
|
818
|
+
}
|
|
819
|
+
return versions[name] ?? versions[name.toLowerCase()];
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Collect the MSBuild properties a project file defines itself, from every
|
|
824
|
+
* PropertyGroup, keyed by property name. A later definition wins, as it does
|
|
825
|
+
* when MSBuild evaluates the file.
|
|
826
|
+
*
|
|
827
|
+
* @param {Object} project Project element parsed by xml2js in compact mode
|
|
828
|
+
* @returns {Object} Map of property name to text value
|
|
829
|
+
*/
|
|
830
|
+
function projectOwnProperties(project) {
|
|
831
|
+
const properties = {};
|
|
832
|
+
for (const propertyGroup of project?.PropertyGroup || []) {
|
|
833
|
+
for (const key of Object.keys(propertyGroup)) {
|
|
834
|
+
if (key === "$" || key === "value") {
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
const text = propertyGroupText(propertyGroup, key);
|
|
838
|
+
if (text !== undefined) {
|
|
839
|
+
properties[key] = text;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return properties;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Evaluate MSBuild properties of one project with a single `dotnet msbuild`
|
|
848
|
+
* call. Asking for every property at once costs one evaluation per project,
|
|
849
|
+
* where one call per package reference evaluated the same project over and
|
|
850
|
+
* over.
|
|
851
|
+
*
|
|
852
|
+
* @param {string} projFile Project file
|
|
853
|
+
* @param {string[]} names Property names
|
|
854
|
+
* @returns {Object} Map of property name to evaluated value, without empty values
|
|
855
|
+
*/
|
|
856
|
+
function evaluateMsbuildProperties(projFile, names) {
|
|
857
|
+
const values = {};
|
|
858
|
+
if (!projFile || !names.length) {
|
|
859
|
+
return values;
|
|
860
|
+
}
|
|
861
|
+
const result = safeSpawnSync(
|
|
862
|
+
DOTNET_CMD,
|
|
863
|
+
[
|
|
864
|
+
"msbuild",
|
|
865
|
+
projFile,
|
|
866
|
+
"-nologo",
|
|
867
|
+
...names.map((name) => `-getProperty:${name}`),
|
|
868
|
+
],
|
|
869
|
+
{ shell: isWin },
|
|
870
|
+
);
|
|
871
|
+
if (result.status !== 0 || result.error || !result.stdout?.trim()) {
|
|
872
|
+
return values;
|
|
873
|
+
}
|
|
874
|
+
const stdout = result.stdout.trim();
|
|
875
|
+
if (names.length === 1) {
|
|
876
|
+
values[names[0]] = stdout;
|
|
877
|
+
return values;
|
|
878
|
+
}
|
|
879
|
+
// Several properties come back as one JSON document.
|
|
880
|
+
try {
|
|
881
|
+
const evaluated = JSON.parse(stdout)?.Properties || {};
|
|
882
|
+
for (const name of names) {
|
|
883
|
+
if (typeof evaluated[name] === "string" && evaluated[name].trim()) {
|
|
884
|
+
values[name] = evaluated[name].trim();
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
} catch (_e) {
|
|
888
|
+
// Unexpected output: treat every property as unresolved.
|
|
889
|
+
}
|
|
890
|
+
return values;
|
|
891
|
+
}
|
|
892
|
+
|
|
484
893
|
/**
|
|
485
894
|
* Method to parse .csproj like xml files
|
|
486
895
|
*
|
|
896
|
+
* A version the project file does not state is taken, in order, from the
|
|
897
|
+
* versions this project restored, from central package management, and last
|
|
898
|
+
* from a version the whole scan agrees on. Two projects of one tree routinely
|
|
899
|
+
* restore different versions of a package, so a version taken from another
|
|
900
|
+
* project would describe a package this project does not use.
|
|
901
|
+
*
|
|
487
902
|
* @param {String} csProjData Raw data
|
|
488
903
|
* @param {String} projFile File name
|
|
489
|
-
* @param {Object} pkgNameVersions
|
|
904
|
+
* @param {Object} pkgNameVersions Versions this project restored (its project.assets.json,
|
|
905
|
+
* packages.lock.json or packages.config), keyed by package id or lowercased id
|
|
490
906
|
* @param {Boolean} msbuildInstalled Whether msbuild is available to resolve properties
|
|
491
|
-
* @param {Object} pkgVersionLabelCandidates Candidate values for msbuild version properties
|
|
907
|
+
* @param {Object} pkgVersionLabelCandidates Candidate values for msbuild version properties,
|
|
908
|
+
* from the props files that can apply to this project
|
|
492
909
|
* @param {Object} centralVersions Versions declared centrally in Directory.Packages.props,
|
|
493
910
|
* keyed by lowercased package id. See {@link getCentralPackageVersions}.
|
|
911
|
+
* @param {Object} fallbackVersions Versions every project of the scan agrees on, keyed by
|
|
912
|
+
* lowercased package id, used only when nothing closer states one
|
|
913
|
+
* @param {Object} restoredNames Package ids as this project's restore output spells them,
|
|
914
|
+
* keyed by lowercased id. NuGet ids are case-insensitive, so a reference spelled
|
|
915
|
+
* differently names the same package and takes the restored spelling.
|
|
916
|
+
* @param {{name: string, version: (string|undefined)}} [projectIdentity] Package id and
|
|
917
|
+
* version this project's project.assets.json records for the project. They name the
|
|
918
|
+
* project in place of what the project file states.
|
|
494
919
|
*
|
|
495
|
-
* @returns {Object} Containing parent component, package, and
|
|
920
|
+
* @returns {Object} Containing parent component, package, dependencies, and the
|
|
921
|
+
* `$(Name)` version labels left unresolved without msbuild
|
|
496
922
|
*/
|
|
497
923
|
export function parseCsProjData(
|
|
498
924
|
csProjData,
|
|
@@ -501,6 +927,9 @@ export function parseCsProjData(
|
|
|
501
927
|
msbuildInstalled = false,
|
|
502
928
|
pkgVersionLabelCandidates = {},
|
|
503
929
|
centralVersions = {},
|
|
930
|
+
fallbackVersions = {},
|
|
931
|
+
restoredNames = {},
|
|
932
|
+
projectIdentity = undefined,
|
|
504
933
|
) {
|
|
505
934
|
const pkgList = [];
|
|
506
935
|
const parentComponent = { type: "application", properties: [] };
|
|
@@ -529,6 +958,39 @@ export function parseCsProjData(
|
|
|
529
958
|
return pkgList;
|
|
530
959
|
}
|
|
531
960
|
const project = projects[0];
|
|
961
|
+
const restoredName = (name) => restoredNames[name.toLowerCase()] || name;
|
|
962
|
+
const resolvedVersion = (name) =>
|
|
963
|
+
lookupNugetVersion(pkgNameVersions, name) ||
|
|
964
|
+
lookupNugetVersion(centralVersions, name) ||
|
|
965
|
+
lookupNugetVersion(fallbackVersions, name);
|
|
966
|
+
// `$(Name)` version labels: the project's own properties, then the props
|
|
967
|
+
// files that can apply to it, then (only for what neither settles) one
|
|
968
|
+
// msbuild evaluation of the project.
|
|
969
|
+
const ownProperties = projectOwnProperties(project);
|
|
970
|
+
const labelOf = (version) => version?.match(/^\$\((.*)\)$/)?.[1];
|
|
971
|
+
const staticLabelValue = (label) =>
|
|
972
|
+
ownProperties[label] ??
|
|
973
|
+
(pkgVersionLabelCandidates[label]?.length === 1
|
|
974
|
+
? pkgVersionLabelCandidates[label][0]
|
|
975
|
+
: undefined);
|
|
976
|
+
let msbuildValues = {};
|
|
977
|
+
const unresolved = new Set();
|
|
978
|
+
for (const item of project.ItemGroup || []) {
|
|
979
|
+
for (const reference of item.PackageReference || []) {
|
|
980
|
+
const label = labelOf(
|
|
981
|
+
reference.$?.VersionOverride ||
|
|
982
|
+
reference.VersionOverride?.[0]._?.[0] ||
|
|
983
|
+
reference.$?.Version ||
|
|
984
|
+
reference.Version?.[0]._?.[0],
|
|
985
|
+
);
|
|
986
|
+
if (label && staticLabelValue(label) === undefined) {
|
|
987
|
+
unresolved.add(label);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (msbuildInstalled) {
|
|
992
|
+
msbuildValues = evaluateMsbuildProperties(projFile, [...unresolved].sort());
|
|
993
|
+
}
|
|
532
994
|
// SDK-style projects declare their version through the MSBuild properties
|
|
533
995
|
// that `dotnet pack` reads: PackageVersion, which defaults to Version, which
|
|
534
996
|
// in turn defaults to VersionPrefix plus an optional `-VersionSuffix`. A
|
|
@@ -726,53 +1188,30 @@ export function parseCsProjData(
|
|
|
726
1188
|
if (!pref.Include || pref.Include.includes(".csproj")) {
|
|
727
1189
|
continue;
|
|
728
1190
|
}
|
|
729
|
-
pkg.name = pref.Include;
|
|
1191
|
+
pkg.name = restoredName(pref.Include);
|
|
730
1192
|
// Under central package management a PackageReference carries no Version at
|
|
731
1193
|
// all, and VersionOverride is the documented way for a project to opt out of
|
|
732
|
-
// the central version, so it wins over everything else.
|
|
733
|
-
// is
|
|
734
|
-
//
|
|
735
|
-
// more precise than a central declaration that may be a floating range.
|
|
1194
|
+
// the central version, so it wins over everything else. Otherwise the version
|
|
1195
|
+
// this project restored is more precise than a central declaration that may
|
|
1196
|
+
// be a floating range.
|
|
736
1197
|
pkg.version =
|
|
737
1198
|
pref.VersionOverride ||
|
|
738
1199
|
item.PackageReference[j].VersionOverride?.[0]._?.[0] ||
|
|
739
1200
|
pref.Version ||
|
|
740
1201
|
item.PackageReference[j].Version?.[0]._?.[0] ||
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
);
|
|
755
|
-
if (
|
|
756
|
-
result.status === 0 &&
|
|
757
|
-
!result.error &&
|
|
758
|
-
result.stdout.trim()
|
|
759
|
-
) {
|
|
760
|
-
pkg.version = result.stdout.trim();
|
|
761
|
-
version_resolved = true;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
if (!version_resolved) {
|
|
765
|
-
if (
|
|
766
|
-
pkgVersionLabelCandidates[version_label_match]?.length === 1
|
|
767
|
-
) {
|
|
768
|
-
// Prioritizing correctness over completeness: reject resolved package version label if it has more than one possible values.
|
|
769
|
-
pkg.version = pkgVersionLabelCandidates[version_label_match][0];
|
|
770
|
-
} else if (DEBUG_MODE) {
|
|
771
|
-
console.log(
|
|
772
|
-
`Could not resolve package version label ${version_label_match} for ${pkg.name}`,
|
|
773
|
-
);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
1202
|
+
resolvedVersion(pkg.name);
|
|
1203
|
+
const versionLabel = labelOf(pkg.version);
|
|
1204
|
+
if (versionLabel) {
|
|
1205
|
+
// Prioritizing correctness over completeness: a label with more than one
|
|
1206
|
+
// candidate value across the applicable props files stays unresolved.
|
|
1207
|
+
const labelValue =
|
|
1208
|
+
msbuildValues[versionLabel] ?? staticLabelValue(versionLabel);
|
|
1209
|
+
if (labelValue !== undefined) {
|
|
1210
|
+
pkg.version = labelValue;
|
|
1211
|
+
} else if (DEBUG_MODE) {
|
|
1212
|
+
console.log(
|
|
1213
|
+
`Could not resolve package version label ${versionLabel} for ${pkg.name}`,
|
|
1214
|
+
);
|
|
776
1215
|
}
|
|
777
1216
|
}
|
|
778
1217
|
// A `Version` attribute holds whatever the project declares, which may
|
|
@@ -789,9 +1228,7 @@ export function parseCsProjData(
|
|
|
789
1228
|
pkg.version = exactPin[1];
|
|
790
1229
|
} else if (!concreteVersion(pkg.version)) {
|
|
791
1230
|
declaredRange = pkg.version;
|
|
792
|
-
pkg.version =
|
|
793
|
-
pkgNameVersions[pkg.name] ||
|
|
794
|
-
centralVersions[pkg.name.toLowerCase()];
|
|
1231
|
+
pkg.version = resolvedVersion(pkg.name);
|
|
795
1232
|
}
|
|
796
1233
|
}
|
|
797
1234
|
// A version that could not be resolved from any source must not be
|
|
@@ -866,21 +1303,20 @@ export function parseCsProjData(
|
|
|
866
1303
|
}
|
|
867
1304
|
}
|
|
868
1305
|
}
|
|
1306
|
+
pkg.name = restoredName(pkg.name);
|
|
869
1307
|
let declaredAssemblyVersion;
|
|
870
1308
|
if (incParts.length > 1 && incParts[1].includes("Version")) {
|
|
871
1309
|
const declared = incParts[1].replace("Version=", "").trim();
|
|
872
|
-
// An assembly reference carries the version inline,
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
//
|
|
876
|
-
//
|
|
1310
|
+
// An assembly reference carries the assembly's version inline, which is
|
|
1311
|
+
// not the version of the package that ships it (Newtonsoft.Json 13.0.3
|
|
1312
|
+
// ships assembly 13.0.0.0), and can be an MSBuild property such as
|
|
1313
|
+
// `Version=$(TargetFSharpCoreVersion)` that only a build evaluates. A
|
|
1314
|
+
// concrete one is kept as the assembly version; a property as a
|
|
1315
|
+
// declaration.
|
|
877
1316
|
if (declared && !concreteVersion(declared)) {
|
|
878
1317
|
declaredAssemblyVersion = declared;
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
centralVersions[pkg.name.toLowerCase()];
|
|
882
|
-
} else {
|
|
883
|
-
assemblyVersion = declared || pkgNameVersions[pkg.name];
|
|
1318
|
+
} else if (declared) {
|
|
1319
|
+
assemblyVersion = declared;
|
|
884
1320
|
}
|
|
885
1321
|
}
|
|
886
1322
|
if (declaredAssemblyVersion) {
|
|
@@ -889,7 +1325,11 @@ export function parseCsProjData(
|
|
|
889
1325
|
value: declaredAssemblyVersion,
|
|
890
1326
|
});
|
|
891
1327
|
}
|
|
892
|
-
|
|
1328
|
+
// The package version comes from the HintPath's package folder, or from
|
|
1329
|
+
// what this project restored; never from the assembly version, and not
|
|
1330
|
+
// from a central pin, which applies to PackageReference items only.
|
|
1331
|
+
const version =
|
|
1332
|
+
hintVersion ?? lookupNugetVersion(pkgNameVersions, pkg.name);
|
|
893
1333
|
// An assembly reference that states no version anywhere - no HintPath to
|
|
894
1334
|
// read one from and no `Version=` on the Include - leaves the component
|
|
895
1335
|
// versionless in both fields. The registry's latest release is not a
|
|
@@ -917,7 +1357,7 @@ export function parseCsProjData(
|
|
|
917
1357
|
value: assemblyName,
|
|
918
1358
|
});
|
|
919
1359
|
}
|
|
920
|
-
if (
|
|
1360
|
+
if (assemblyVersion) {
|
|
921
1361
|
pkg.properties.push({
|
|
922
1362
|
name: "cdx:dotnet:assembly_version",
|
|
923
1363
|
value: assemblyVersion,
|
|
@@ -966,14 +1406,47 @@ export function parseCsProjData(
|
|
|
966
1406
|
}
|
|
967
1407
|
if (sdkVersion) {
|
|
968
1408
|
parentComponent.version = sdkVersion;
|
|
1409
|
+
} else if (
|
|
1410
|
+
!parentComponent.version &&
|
|
1411
|
+
!Object.keys(sdkVersionProps).length &&
|
|
1412
|
+
(project.$?.Sdk || project.Sdk)
|
|
1413
|
+
) {
|
|
1414
|
+
// An SDK-style project that sets no version builds as 1.0.0, the Version
|
|
1415
|
+
// its project.assets.json records too, so both describe one component. A
|
|
1416
|
+
// version set from a build-time property is unknown, not 1.0.0.
|
|
1417
|
+
parentComponent.version = "1.0.0";
|
|
1418
|
+
}
|
|
1419
|
+
// Only a build knows the value of a property that references another one
|
|
1420
|
+
const literal = (value) =>
|
|
1421
|
+
value && !value.includes("$(") ? value : undefined;
|
|
1422
|
+
// NuGet names a project's package by its PackageId, which defaults to the
|
|
1423
|
+
// assembly name, so a PackageId the project file states names the project.
|
|
1424
|
+
const declaredPackageId = literal(ownProperties.PackageId);
|
|
1425
|
+
if (declaredPackageId) {
|
|
1426
|
+
parentComponent.name = declaredPackageId;
|
|
1427
|
+
}
|
|
1428
|
+
// Restore evaluated the project with everything MSBuild imports and every
|
|
1429
|
+
// property it was given: a Version from Directory.Build.props, `-p:Version`
|
|
1430
|
+
// or `$(BuildNumber)`, a PackageId. Its project.assets.json roots the
|
|
1431
|
+
// restored dependency tree at that identity, so naming the project from its
|
|
1432
|
+
// file alone would make it two components.
|
|
1433
|
+
if (projectIdentity?.name) {
|
|
1434
|
+
parentComponent.name = projectIdentity.name;
|
|
1435
|
+
parentComponent.version = projectIdentity.version;
|
|
1436
|
+
}
|
|
1437
|
+
// The assembly the project builds keeps its name when the package differs
|
|
1438
|
+
const declaredAssemblyName = literal(ownProperties.AssemblyName);
|
|
1439
|
+
if (declaredAssemblyName && declaredAssemblyName !== parentComponent.name) {
|
|
1440
|
+
parentComponent.properties.push({
|
|
1441
|
+
name: "cdx:dotnet:assembly_name",
|
|
1442
|
+
value: declaredAssemblyName,
|
|
1443
|
+
});
|
|
969
1444
|
}
|
|
970
1445
|
// The purl is built once every PropertyGroup has been read, so a version
|
|
971
|
-
// declared after OutputType is not lost.
|
|
1446
|
+
// declared after OutputType is not lost. A project that states no version
|
|
1447
|
+
// gets a versionless purl: "latest" is not a version.
|
|
972
1448
|
if (parentComponent.name) {
|
|
973
|
-
|
|
974
|
-
parentComponent,
|
|
975
|
-
nugetPurl(parentComponent.name, parentComponent.version || "latest"),
|
|
976
|
-
);
|
|
1449
|
+
applyNugetVersion(parentComponent, parentComponent.version);
|
|
977
1450
|
}
|
|
978
1451
|
let dependencies = [];
|
|
979
1452
|
if (parentComponent?.["bom-ref"]) {
|
|
@@ -988,9 +1461,46 @@ export function parseCsProjData(
|
|
|
988
1461
|
pkgList,
|
|
989
1462
|
parentComponent,
|
|
990
1463
|
dependencies,
|
|
1464
|
+
// Version labels no static source settled, which only msbuild could
|
|
1465
|
+
unresolvedVersionLabels: msbuildInstalled ? [] : [...unresolved].sort(),
|
|
991
1466
|
};
|
|
992
1467
|
}
|
|
993
1468
|
|
|
1469
|
+
// The package folders a consuming project compiles against or loads at run
|
|
1470
|
+
// time. A DLL elsewhere in the package (build/, buildTransitive/, tools/,
|
|
1471
|
+
// analyzers/) is a build-time helper of the package itself, such as the
|
|
1472
|
+
// System.Formats.Asn1.dll coverlet.collector carries for its test-platform
|
|
1473
|
+
// collector: the project's code never calls it, so its name must not credit
|
|
1474
|
+
// the package with the project's framework calls (issue 4441).
|
|
1475
|
+
const DOTNET_REFERENCED_ASSET_FOLDERS = new Set(["lib", "ref", "runtimes"]);
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Whether a file of a NuGet package is an assembly a consuming project can
|
|
1479
|
+
* reference: a .dll, .exe, or .so under lib/, ref/, or runtimes/.
|
|
1480
|
+
*
|
|
1481
|
+
* @param {string} packageFile Path of the file inside the package, as project.assets.json lists it
|
|
1482
|
+
* @returns {boolean} true for a referenced assembly
|
|
1483
|
+
*/
|
|
1484
|
+
export function isReferencedDotnetPackageFile(packageFile) {
|
|
1485
|
+
if (typeof packageFile !== "string") {
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1488
|
+
const lowerFile = packageFile.toLowerCase();
|
|
1489
|
+
if (
|
|
1490
|
+
!lowerFile.endsWith(".dll") &&
|
|
1491
|
+
!lowerFile.endsWith(".exe") &&
|
|
1492
|
+
!lowerFile.endsWith(".so")
|
|
1493
|
+
) {
|
|
1494
|
+
return false;
|
|
1495
|
+
}
|
|
1496
|
+
const normalized = lowerFile.replaceAll("\\", "/");
|
|
1497
|
+
const slashIndex = normalized.indexOf("/");
|
|
1498
|
+
return (
|
|
1499
|
+
slashIndex > 0 &&
|
|
1500
|
+
DOTNET_REFERENCED_ASSET_FOLDERS.has(normalized.slice(0, slashIndex))
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
994
1504
|
/**
|
|
995
1505
|
* Parse a .NET project.assets.json file and return the package list and dependency tree.
|
|
996
1506
|
*
|
|
@@ -1000,7 +1510,10 @@ export function parseCsProjData(
|
|
|
1000
1510
|
*
|
|
1001
1511
|
* @param {string} csProjData Raw JSON string of the project.assets.json file
|
|
1002
1512
|
* @param {string} assetsJsonFile Path to the project.assets.json file, used for evidence properties
|
|
1003
|
-
* @returns {{ pkgList: Object[], dependenciesList: Object[] }}
|
|
1513
|
+
* @returns {{ pkgList: Object[], dependenciesList: Object[], projectPath: (string|undefined), outputPath: (string|undefined), projectIdentity: ({name: string, version: (string|undefined)}|undefined) }}
|
|
1514
|
+
* `projectPath` is the project file the restore output belongs to and `outputPath` the
|
|
1515
|
+
* directory restore wrote it to, both as restore recorded them. `projectIdentity` is the
|
|
1516
|
+
* package id and version restore evaluated for the project, which root the dependency tree.
|
|
1004
1517
|
*/
|
|
1005
1518
|
export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
|
|
1006
1519
|
// extract name, operator, version from .NET package representation
|
|
@@ -1040,22 +1553,18 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
|
|
|
1040
1553
|
return { pkgList, dependenciesList };
|
|
1041
1554
|
}
|
|
1042
1555
|
csProjData = JSON.parse(csProjData);
|
|
1043
|
-
|
|
1556
|
+
const projectPath = csProjData.project?.restore?.projectPath;
|
|
1557
|
+
let rootRef;
|
|
1044
1558
|
if (csProjData.project?.restore?.projectName) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
namespace: "" || null,
|
|
1048
|
-
name: csProjData.project?.restore?.projectName,
|
|
1049
|
-
version: csProjData.project.version || "latest" || null,
|
|
1050
|
-
});
|
|
1559
|
+
// The project's own version as restore evaluated it; "latest" is not a
|
|
1560
|
+
// version, so a project without one gets a versionless purl.
|
|
1051
1561
|
rootPkg = {
|
|
1052
1562
|
group: "",
|
|
1053
1563
|
name: csProjData.project.restore.projectName,
|
|
1054
|
-
version: csProjData.project.version || "latest",
|
|
1055
1564
|
type: "application",
|
|
1056
|
-
purl: purlString,
|
|
1057
|
-
"bom-ref": decodeURIComponent(purlString),
|
|
1058
1565
|
};
|
|
1566
|
+
applyNugetVersion(rootPkg, csProjData.project.version);
|
|
1567
|
+
rootRef = rootPkg["bom-ref"];
|
|
1059
1568
|
pkgList.push(rootPkg);
|
|
1060
1569
|
}
|
|
1061
1570
|
const rootPkgDeps = new Set();
|
|
@@ -1069,53 +1578,42 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
|
|
|
1069
1578
|
if (nameOperatorVersion == null) {
|
|
1070
1579
|
continue;
|
|
1071
1580
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
//
|
|
1075
|
-
//
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
break;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
if (!matchFound) {
|
|
1094
|
-
if (DEBUG_MODE) {
|
|
1095
|
-
console.log(
|
|
1096
|
-
"Unable to match",
|
|
1097
|
-
dependencyName,
|
|
1098
|
-
"with a target name. The dependency tree will be imprecise.",
|
|
1099
|
-
);
|
|
1100
|
-
}
|
|
1101
|
-
continue;
|
|
1581
|
+
// A direct dependency is declared as a constraint (`Moq >= 4.17.6`,
|
|
1582
|
+
// `X >= 7.0.0-*`, `[4.0.0, 5.0.0)`), and restore installs one version of
|
|
1583
|
+
// each package per target: the edge points at that installed version,
|
|
1584
|
+
// whatever the constraint's lower bound says.
|
|
1585
|
+
const resolvedTarget = Object.keys(
|
|
1586
|
+
csProjData.targets[frameworkTarget] || {},
|
|
1587
|
+
).find(
|
|
1588
|
+
(targetKey) =>
|
|
1589
|
+
targetKey.split("/")[0].toLowerCase() ===
|
|
1590
|
+
nameOperatorVersion.name.toLowerCase(),
|
|
1591
|
+
);
|
|
1592
|
+
if (!resolvedTarget) {
|
|
1593
|
+
if (DEBUG_MODE) {
|
|
1594
|
+
console.log(
|
|
1595
|
+
"Unable to match",
|
|
1596
|
+
dependencyName,
|
|
1597
|
+
"with a target name. The dependency tree will be imprecise.",
|
|
1598
|
+
);
|
|
1102
1599
|
}
|
|
1600
|
+
continue;
|
|
1103
1601
|
}
|
|
1104
|
-
|
|
1602
|
+
const [nameToUse, resolvedVersion] = resolvedTarget.split("/");
|
|
1105
1603
|
const dpurl = decodeURIComponent(
|
|
1106
1604
|
build({
|
|
1107
1605
|
type: "nuget",
|
|
1108
1606
|
namespace: "" || null,
|
|
1109
1607
|
name: nameToUse,
|
|
1110
|
-
version:
|
|
1608
|
+
version: resolvedVersion || null,
|
|
1111
1609
|
}),
|
|
1112
1610
|
);
|
|
1113
1611
|
rootPkgDeps.add(dpurl);
|
|
1114
1612
|
}
|
|
1115
1613
|
}
|
|
1116
|
-
if (
|
|
1614
|
+
if (rootRef && rootPkgDeps.size) {
|
|
1117
1615
|
dependenciesList.push({
|
|
1118
|
-
ref:
|
|
1616
|
+
ref: rootRef,
|
|
1119
1617
|
dependsOn: Array.from(rootPkgDeps).sort(),
|
|
1120
1618
|
});
|
|
1121
1619
|
}
|
|
@@ -1154,12 +1652,8 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
|
|
|
1154
1652
|
if (lib[rootDep].files && Array.isArray(lib[rootDep].files)) {
|
|
1155
1653
|
const dllFiles = new Set();
|
|
1156
1654
|
lib[rootDep].files.forEach((f) => {
|
|
1157
|
-
if (
|
|
1158
|
-
f.
|
|
1159
|
-
f.endsWith(".exe") ||
|
|
1160
|
-
f.endsWith(".so")
|
|
1161
|
-
) {
|
|
1162
|
-
dllFiles.add(basename(f));
|
|
1655
|
+
if (isReferencedDotnetPackageFile(f)) {
|
|
1656
|
+
dllFiles.add(basename(f.replaceAll("\\", "/")));
|
|
1163
1657
|
}
|
|
1164
1658
|
});
|
|
1165
1659
|
pkg.properties = [
|
|
@@ -1246,6 +1740,11 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
|
|
|
1246
1740
|
return {
|
|
1247
1741
|
pkgList,
|
|
1248
1742
|
dependenciesList,
|
|
1743
|
+
projectPath,
|
|
1744
|
+
outputPath: csProjData.project?.restore?.outputPath,
|
|
1745
|
+
projectIdentity: rootRef
|
|
1746
|
+
? { name: rootPkg.name, version: rootPkg.version }
|
|
1747
|
+
: undefined,
|
|
1249
1748
|
};
|
|
1250
1749
|
}
|
|
1251
1750
|
|
|
@@ -1321,39 +1820,55 @@ export function parseCsPkgLockData(csLockData, pkgLockFile) {
|
|
|
1321
1820
|
}
|
|
1322
1821
|
const dependsOn = new Set();
|
|
1323
1822
|
if (libData.dependencies) {
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
(assetData.dependencies[aversionNoRuntime]?.[adep.toLowerCase()] &&
|
|
1342
|
-
assetData.dependencies[aversionNoRuntime][adep.toLowerCase()]
|
|
1343
|
-
.type === "Project")
|
|
1344
|
-
) {
|
|
1345
|
-
adepResolvedVersion = undefined;
|
|
1346
|
-
adep = adep.toLowerCase();
|
|
1347
|
-
} else if (DEBUG_MODE) {
|
|
1348
|
-
console.warn(
|
|
1349
|
-
`Unable to find the resolved version for ${adep} ${aversion}. Using ${adepResolvedVersion} which may be incorrect.`,
|
|
1823
|
+
const aversionNoRuntime = aversion.split("/")[0];
|
|
1824
|
+
// The entry for a dependency: in this target, the target without its
|
|
1825
|
+
// runtime identifier, or a runtime-specific target of the same
|
|
1826
|
+
// framework. Package ids are case-insensitive, and the lock file writes
|
|
1827
|
+
// a dependency the way the depending package's nuspec spells it. See
|
|
1828
|
+
// #930 and #937.
|
|
1829
|
+
const sections = [
|
|
1830
|
+
assetData.dependencies[aversion],
|
|
1831
|
+
assetData.dependencies[aversionNoRuntime],
|
|
1832
|
+
...Object.keys(assetData.dependencies)
|
|
1833
|
+
.filter((key) => key.startsWith(`${aversionNoRuntime}/`))
|
|
1834
|
+
.map((key) => assetData.dependencies[key]),
|
|
1835
|
+
].filter(Boolean);
|
|
1836
|
+
const entryFor = (name) => {
|
|
1837
|
+
for (const section of sections) {
|
|
1838
|
+
const key = Object.keys(section).find(
|
|
1839
|
+
(candidate) => candidate.toLowerCase() === name.toLowerCase(),
|
|
1350
1840
|
);
|
|
1841
|
+
if (key) {
|
|
1842
|
+
return { name: key, entry: section[key] };
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
return undefined;
|
|
1846
|
+
};
|
|
1847
|
+
for (const adep of Object.keys(libData.dependencies)) {
|
|
1848
|
+
const found = entryFor(adep);
|
|
1849
|
+
let depName;
|
|
1850
|
+
let depVersion;
|
|
1851
|
+
if (found?.entry?.type === "Project") {
|
|
1852
|
+
depName = found.name;
|
|
1853
|
+
} else if (found?.entry?.resolved) {
|
|
1854
|
+
depName = found.name;
|
|
1855
|
+
depVersion = found.entry.resolved;
|
|
1856
|
+
} else {
|
|
1857
|
+
// The dependency value is the declared range (`[6.0.0, )`), which
|
|
1858
|
+
// names no package version: without a resolved entry there is no
|
|
1859
|
+
// component to point at.
|
|
1860
|
+
if (DEBUG_MODE) {
|
|
1861
|
+
console.warn(
|
|
1862
|
+
`Unable to find the resolved version for ${adep} ${aversion}. The dependency tree will be imprecise.`,
|
|
1863
|
+
);
|
|
1864
|
+
}
|
|
1865
|
+
continue;
|
|
1351
1866
|
}
|
|
1352
1867
|
const adpurl = build({
|
|
1353
1868
|
type: "nuget",
|
|
1354
1869
|
namespace: "" || null,
|
|
1355
|
-
name:
|
|
1356
|
-
version:
|
|
1870
|
+
name: depName,
|
|
1871
|
+
version: depVersion || null,
|
|
1357
1872
|
});
|
|
1358
1873
|
dependsOn.add(decodeURIComponent(adpurl));
|
|
1359
1874
|
}
|