dependabot-nuget 0.263.0 → 0.264.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +37 -0
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/DiscoverCommand.cs +3 -3
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Program.cs +1 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +169 -0
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +79 -67
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.FrameworkCheck.cs +0 -4
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +10 -11
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalysisResult.cs +11 -0
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +441 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +177 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +47 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyInfo.cs +12 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Extensions.cs +36 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +128 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/Requirement.cs +105 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +17 -0
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/SecurityVulnerability.cs +11 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/SecurityVulnerabilityExtensions.cs +36 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +179 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionResult.cs +54 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Dependency.cs +5 -2
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +2 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +2 -2
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +0 -2
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +0 -3
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/SupportedFrameworks.cs +0 -3
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +0 -5
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectResolver.cs +0 -4
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SdkPackageUpdater.cs +6 -2
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/WebApplicationTargetsConditionPatcher.cs +0 -4
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/XmlFilePreAndPostProcessor.cs +0 -2
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/HashSetExtensions.cs +0 -2
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +0 -4
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/Logger.cs +0 -3
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +7 -8
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +0 -4
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +0 -3
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/XmlExtensions.cs +0 -4
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +90 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +304 -0
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/CompatibilityCheckerTests.cs +145 -0
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/ExpectedAnalysisResult.cs +8 -0
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/RequirementTests.cs +69 -0
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/SecurityVulnerabilityExtensionsTests.cs +78 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +193 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +1 -2
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +2 -2
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +2 -2
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +1 -1
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +102 -9
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +4 -4
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +2 -2
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +8 -2
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +2 -1
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +8 -7
  58. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +63 -0
  59. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +63 -0
  60. data/lib/dependabot/nuget/file_fetcher.rb +7 -6
  61. data/lib/dependabot/nuget/file_parser.rb +28 -21
  62. data/lib/dependabot/nuget/file_updater.rb +22 -25
  63. data/lib/dependabot/nuget/metadata_finder.rb +2 -160
  64. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +102 -0
  65. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +129 -0
  66. data/lib/dependabot/nuget/native_discovery/native_directory_packages_props_discovery.rb +44 -0
  67. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +174 -0
  68. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +63 -0
  69. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +82 -0
  70. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +43 -0
  71. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +68 -0
  72. data/lib/dependabot/nuget/native_helpers.rb +59 -0
  73. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +105 -0
  74. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +200 -0
  75. data/lib/dependabot/nuget/nuget_config_credential_helpers.rb +3 -2
  76. data/lib/dependabot/nuget/update_checker.rb +47 -0
  77. metadata +39 -5
@@ -0,0 +1,129 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/nuget/native_discovery/native_dependency_details"
5
+ require "sorbet-runtime"
6
+
7
+ module Dependabot
8
+ module Nuget
9
+ class NativeDependencyFileDiscovery
10
+ extend T::Sig
11
+
12
+ sig do
13
+ params(json: T.nilable(T::Hash[String, T.untyped]),
14
+ directory: String).returns(T.nilable(NativeDependencyFileDiscovery))
15
+ end
16
+ def self.from_json(json, directory)
17
+ return nil if json.nil?
18
+
19
+ file_path = File.join(directory, T.let(json.fetch("FilePath"), String))
20
+ dependencies = T.let(json.fetch("Dependencies"), T::Array[T::Hash[String, T.untyped]]).map do |dep|
21
+ NativeDependencyDetails.from_json(dep)
22
+ end
23
+
24
+ NativeDependencyFileDiscovery.new(file_path: file_path,
25
+ dependencies: dependencies)
26
+ end
27
+
28
+ sig do
29
+ params(file_path: String,
30
+ dependencies: T::Array[NativeDependencyDetails]).void
31
+ end
32
+ def initialize(file_path:, dependencies:)
33
+ @file_path = file_path
34
+ @dependencies = dependencies
35
+ end
36
+
37
+ sig { returns(String) }
38
+ attr_reader :file_path
39
+
40
+ sig { returns(T::Array[NativeDependencyDetails]) }
41
+ attr_reader :dependencies
42
+
43
+ sig { overridable.returns(Dependabot::FileParsers::Base::DependencySet) }
44
+ def dependency_set # rubocop:disable Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/AbcSize
45
+ dependency_set = Dependabot::FileParsers::Base::DependencySet.new
46
+
47
+ file_name = Pathname.new(file_path).cleanpath.to_path
48
+ dependencies.each do |dependency|
49
+ next if dependency.name.casecmp("Microsoft.NET.Sdk")&.zero?
50
+
51
+ # If the version string was evaluated it must have been successfully resolved
52
+ if dependency.evaluation && dependency.evaluation&.result_type != "Success"
53
+ logger.warn "Dependency '#{dependency.name}' excluded due to unparsable version: #{dependency.version}"
54
+ next
55
+ end
56
+
57
+ # Exclude any dependencies using version ranges or wildcards
58
+ next if dependency.version&.include?(",") ||
59
+ dependency.version&.include?("*")
60
+
61
+ # Exclude any dependencies specified using interpolation
62
+ next if dependency.name.include?("%(") ||
63
+ dependency.version&.include?("%(")
64
+
65
+ # Exclude any dependencies which reference an item type
66
+ next if dependency.name.include?("@(")
67
+
68
+ dependency_file_name = file_name
69
+ if dependency.type == "PackagesConfig"
70
+ dir_name = File.dirname(file_name)
71
+ dependency_file_name = "packages.config"
72
+ dependency_file_name = File.join(dir_name, "packages.config") unless dir_name == "."
73
+ end
74
+
75
+ dependency_set << build_dependency(dependency_file_name, dependency)
76
+ end
77
+
78
+ dependency_set
79
+ end
80
+
81
+ private
82
+
83
+ sig { returns(::Logger) }
84
+ def logger
85
+ Dependabot.logger
86
+ end
87
+
88
+ sig { params(file_name: String, dependency_details: NativeDependencyDetails).returns(Dependabot::Dependency) }
89
+ def build_dependency(file_name, dependency_details)
90
+ requirement = build_requirement(file_name, dependency_details)
91
+ requirements = requirement.nil? ? [] : [requirement]
92
+
93
+ version = dependency_details.version&.gsub(/[\(\)\[\]]/, "")&.strip
94
+ version = nil if version&.empty?
95
+
96
+ Dependency.new(
97
+ name: dependency_details.name,
98
+ version: version,
99
+ package_manager: "nuget",
100
+ requirements: requirements
101
+ )
102
+ end
103
+
104
+ sig do
105
+ params(file_name: String, dependency_details: NativeDependencyDetails)
106
+ .returns(T.nilable(T::Hash[Symbol, T.untyped]))
107
+ end
108
+ def build_requirement(file_name, dependency_details)
109
+ return if dependency_details.is_transitive
110
+
111
+ version = dependency_details.version
112
+ version = nil if version&.empty?
113
+
114
+ requirement = {
115
+ requirement: version,
116
+ file: file_name,
117
+ groups: [dependency_details.is_dev_dependency ? "devDependencies" : "dependencies"],
118
+ source: nil
119
+ }
120
+
121
+ property_name = dependency_details.evaluation&.root_property_name
122
+ return requirement unless property_name
123
+
124
+ requirement[:metadata] = { property_name: property_name }
125
+ requirement
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,44 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/nuget/native_discovery/native_dependency_details"
5
+ require "sorbet-runtime"
6
+
7
+ module Dependabot
8
+ module Nuget
9
+ class NativeDirectoryPackagesPropsDiscovery < NativeDependencyFileDiscovery
10
+ extend T::Sig
11
+
12
+ sig do
13
+ override.params(json: T.nilable(T::Hash[String, T.untyped]),
14
+ directory: String).returns(T.nilable(NativeDirectoryPackagesPropsDiscovery))
15
+ end
16
+ def self.from_json(json, directory)
17
+ return nil if json.nil?
18
+
19
+ file_path = File.join(directory, T.let(json.fetch("FilePath"), String))
20
+ is_transitive_pinning_enabled = T.let(json.fetch("IsTransitivePinningEnabled"), T::Boolean)
21
+ dependencies = T.let(json.fetch("Dependencies"), T::Array[T::Hash[String, T.untyped]]).map do |dep|
22
+ NativeDependencyDetails.from_json(dep)
23
+ end
24
+
25
+ NativeDirectoryPackagesPropsDiscovery.new(file_path: file_path,
26
+ is_transitive_pinning_enabled: is_transitive_pinning_enabled,
27
+ dependencies: dependencies)
28
+ end
29
+
30
+ sig do
31
+ params(file_path: String,
32
+ is_transitive_pinning_enabled: T::Boolean,
33
+ dependencies: T::Array[NativeDependencyDetails]).void
34
+ end
35
+ def initialize(file_path:, is_transitive_pinning_enabled:, dependencies:)
36
+ super(file_path: file_path, dependencies: dependencies)
37
+ @is_transitive_pinning_enabled = is_transitive_pinning_enabled
38
+ end
39
+
40
+ sig { returns(T::Boolean) }
41
+ attr_reader :is_transitive_pinning_enabled
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,174 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/dependency"
5
+ require "dependabot/nuget/native_discovery/native_workspace_discovery"
6
+ require "json"
7
+ require "sorbet-runtime"
8
+
9
+ module Dependabot
10
+ module Nuget
11
+ class NativeDiscoveryJsonReader
12
+ extend T::Sig
13
+
14
+ sig { returns(T::Hash[String, NativeDiscoveryJsonReader]) }
15
+ def self.discovery_result_cache
16
+ T.let(CacheManager.cache("discovery_json_cache"), T::Hash[String, NativeDiscoveryJsonReader])
17
+ end
18
+
19
+ sig { returns(T::Hash[String, String]) }
20
+ def self.discovery_path_cache
21
+ T.let(CacheManager.cache("discovery_path_cache"), T::Hash[String, String])
22
+ end
23
+
24
+ sig do
25
+ params(
26
+ dependency_files: T::Array[Dependabot::DependencyFile]
27
+ ).returns(NativeDiscoveryJsonReader)
28
+ end
29
+ def self.get_discovery_from_dependency_files(dependency_files)
30
+ key = create_cache_key(dependency_files)
31
+ discovery_json = discovery_result_cache[key]
32
+ raise "No discovery result for specified dependency files: #{key}" unless discovery_json
33
+
34
+ discovery_json
35
+ end
36
+
37
+ sig do
38
+ params(
39
+ dependency_files: T::Array[Dependabot::DependencyFile],
40
+ discovery: NativeDiscoveryJsonReader
41
+ ).void
42
+ end
43
+ def self.set_discovery_from_dependency_files(dependency_files:, discovery:)
44
+ key = create_cache_key(dependency_files)
45
+ discovery_result_cache[key] = discovery
46
+ end
47
+
48
+ sig do
49
+ params(
50
+ dependency_files: T::Array[Dependabot::DependencyFile]
51
+ ).returns(String)
52
+ end
53
+ def self.get_discovery_file_path_from_dependency_files(dependency_files)
54
+ key = create_cache_key(dependency_files)
55
+ discovery_path = discovery_path_cache[key]
56
+ raise "No discovery path found for specified dependency files: #{key}" unless discovery_path
57
+
58
+ discovery_path
59
+ end
60
+
61
+ sig do
62
+ params(
63
+ dependency_files: T::Array[Dependabot::DependencyFile]
64
+ ).returns(String)
65
+ end
66
+ def self.create_discovery_file_path_from_dependency_files(dependency_files)
67
+ discovery_key = create_cache_key(dependency_files)
68
+ if discovery_path_cache[discovery_key]
69
+ raise "Discovery file path already exists for the given dependency files: #{discovery_key}"
70
+ end
71
+
72
+ discovery_counter_cache = T.let(CacheManager.cache("discovery_counter_cache"), T::Hash[String, Integer])
73
+ counter_key = "counter"
74
+ current_counter = discovery_counter_cache[counter_key] || 0
75
+ current_counter += 1
76
+ discovery_counter_cache[counter_key] = current_counter
77
+ incremeted_discovery_file_path = File.join(temp_directory, "discovery.#{current_counter}.json")
78
+ discovery_path_cache[discovery_key] = incremeted_discovery_file_path
79
+ incremeted_discovery_file_path
80
+ end
81
+
82
+ # this is a test-only method
83
+ sig do
84
+ params(
85
+ dependency_files: T::Array[Dependabot::DependencyFile]
86
+ ).void
87
+ end
88
+ def self.clear_discovery_file_path_from_cache(dependency_files)
89
+ key = create_cache_key(dependency_files)
90
+ discovery_file_path = discovery_path_cache[key]
91
+ File.delete(discovery_file_path) if discovery_file_path && File.exist?(discovery_file_path)
92
+ discovery_path_cache.delete(key)
93
+ end
94
+
95
+ sig do
96
+ params(
97
+ dependency_files: T::Array[Dependabot::DependencyFile]
98
+ ).returns(String)
99
+ end
100
+ def self.create_cache_key(dependency_files)
101
+ dependency_files.map { |d| d.to_h.except("content") }.to_s
102
+ end
103
+
104
+ sig { returns(String) }
105
+ def self.temp_directory
106
+ File.join(Dir.tmpdir, ".dependabot")
107
+ end
108
+
109
+ sig do
110
+ params(
111
+ discovery_json_path: String
112
+ ).returns(T.nilable(DependencyFile))
113
+ end
114
+ def self.discovery_json_from_path(discovery_json_path)
115
+ return unless File.exist?(discovery_json_path)
116
+
117
+ DependencyFile.new(
118
+ name: Pathname.new(discovery_json_path).cleanpath.to_path,
119
+ directory: temp_directory,
120
+ type: "file",
121
+ content: File.read(discovery_json_path)
122
+ )
123
+ end
124
+
125
+ sig { returns(T.nilable(NativeWorkspaceDiscovery)) }
126
+ attr_reader :workspace_discovery
127
+
128
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
129
+ attr_reader :dependency_set
130
+
131
+ sig { params(discovery_json: DependencyFile).void }
132
+ def initialize(discovery_json:)
133
+ @discovery_json = discovery_json
134
+ @workspace_discovery = T.let(read_workspace_discovery, T.nilable(Dependabot::Nuget::NativeWorkspaceDiscovery))
135
+ @dependency_set = T.let(read_dependency_set, Dependabot::FileParsers::Base::DependencySet)
136
+ end
137
+
138
+ private
139
+
140
+ sig { returns(DependencyFile) }
141
+ attr_reader :discovery_json
142
+
143
+ sig { returns(T.nilable(NativeWorkspaceDiscovery)) }
144
+ def read_workspace_discovery
145
+ return nil unless discovery_json.content
146
+
147
+ parsed_json = T.let(JSON.parse(T.must(discovery_json.content)), T::Hash[String, T.untyped])
148
+ NativeWorkspaceDiscovery.from_json(parsed_json)
149
+ rescue JSON::ParserError
150
+ raise Dependabot::DependencyFileNotParseable, discovery_json.path
151
+ end
152
+
153
+ sig { returns(Dependabot::FileParsers::Base::DependencySet) }
154
+ def read_dependency_set
155
+ dependency_set = Dependabot::FileParsers::Base::DependencySet.new
156
+ return dependency_set unless workspace_discovery
157
+
158
+ workspace_result = T.must(workspace_discovery)
159
+ workspace_result.projects.each do |project|
160
+ dependency_set += project.dependency_set
161
+ end
162
+ if workspace_result.directory_packages_props
163
+ dependency_set += T.must(workspace_result.directory_packages_props).dependency_set
164
+ end
165
+ if workspace_result.dotnet_tools_json
166
+ dependency_set += T.must(workspace_result.dotnet_tools_json).dependency_set
167
+ end
168
+ dependency_set += T.must(workspace_result.global_json).dependency_set if workspace_result.global_json
169
+
170
+ dependency_set
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,63 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ module Nuget
8
+ class NativeEvaluationDetails
9
+ extend T::Sig
10
+
11
+ sig { params(json: T.nilable(T::Hash[String, T.untyped])).returns(T.nilable(NativeEvaluationDetails)) }
12
+ def self.from_json(json)
13
+ return nil if json.nil?
14
+
15
+ result_type = T.let(json.fetch("ResultType"), String)
16
+ original_value = T.let(json.fetch("OriginalValue"), String)
17
+ evaluated_value = T.let(json.fetch("EvaluatedValue"), String)
18
+ root_property_name = T.let(json.fetch("RootPropertyName", nil), T.nilable(String))
19
+ error_message = T.let(json.fetch("ErrorMessage", nil), T.nilable(String))
20
+
21
+ NativeEvaluationDetails.new(result_type: result_type,
22
+ original_value: original_value,
23
+ evaluated_value: evaluated_value,
24
+ root_property_name: root_property_name,
25
+ error_message: error_message)
26
+ end
27
+
28
+ sig do
29
+ params(result_type: String,
30
+ original_value: String,
31
+ evaluated_value: String,
32
+ root_property_name: T.nilable(String),
33
+ error_message: T.nilable(String)).void
34
+ end
35
+ def initialize(result_type:,
36
+ original_value:,
37
+ evaluated_value:,
38
+ root_property_name:,
39
+ error_message:)
40
+ @result_type = result_type
41
+ @original_value = original_value
42
+ @evaluated_value = evaluated_value
43
+ @root_property_name = root_property_name
44
+ @error_message = error_message
45
+ end
46
+
47
+ sig { returns(String) }
48
+ attr_reader :result_type
49
+
50
+ sig { returns(String) }
51
+ attr_reader :original_value
52
+
53
+ sig { returns(String) }
54
+ attr_reader :evaluated_value
55
+
56
+ sig { returns(T.nilable(String)) }
57
+ attr_reader :root_property_name
58
+
59
+ sig { returns(T.nilable(String)) }
60
+ attr_reader :error_message
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,82 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/nuget/native_discovery/native_dependency_details"
5
+ require "dependabot/nuget/native_discovery/native_property_details"
6
+ require "sorbet-runtime"
7
+
8
+ module Dependabot
9
+ module Nuget
10
+ class NativeProjectDiscovery < NativeDependencyFileDiscovery
11
+ extend T::Sig
12
+
13
+ sig do
14
+ override.params(json: T.nilable(T::Hash[String, T.untyped]),
15
+ directory: String).returns(T.nilable(NativeProjectDiscovery))
16
+ end
17
+ def self.from_json(json, directory)
18
+ return nil if json.nil?
19
+
20
+ file_path = File.join(directory, T.let(json.fetch("FilePath"), String))
21
+ properties = T.let(json.fetch("Properties"), T::Array[T::Hash[String, T.untyped]]).map do |prop|
22
+ NativePropertyDetails.from_json(prop)
23
+ end
24
+ target_frameworks = T.let(json.fetch("TargetFrameworks"), T::Array[String])
25
+ referenced_project_paths = T.let(json.fetch("ReferencedProjectPaths"), T::Array[String])
26
+ dependencies = T.let(json.fetch("Dependencies"), T::Array[T::Hash[String, T.untyped]]).filter_map do |dep|
27
+ details = NativeDependencyDetails.from_json(dep)
28
+ next unless details.version # can't do anything without a version
29
+
30
+ version = T.must(details.version)
31
+ next unless version.length.positive? # can't do anything with an empty version
32
+
33
+ next if version.include? "," # can't do anything with a range
34
+
35
+ next if version.include? "*" # can't do anything with a wildcard
36
+
37
+ details
38
+ end
39
+
40
+ NativeProjectDiscovery.new(file_path: file_path,
41
+ properties: properties,
42
+ target_frameworks: target_frameworks,
43
+ referenced_project_paths: referenced_project_paths,
44
+ dependencies: dependencies)
45
+ end
46
+
47
+ sig do
48
+ params(file_path: String,
49
+ properties: T::Array[NativePropertyDetails],
50
+ target_frameworks: T::Array[String],
51
+ referenced_project_paths: T::Array[String],
52
+ dependencies: T::Array[NativeDependencyDetails]).void
53
+ end
54
+ def initialize(file_path:, properties:, target_frameworks:, referenced_project_paths:, dependencies:)
55
+ super(file_path: file_path, dependencies: dependencies)
56
+ @properties = properties
57
+ @target_frameworks = target_frameworks
58
+ @referenced_project_paths = referenced_project_paths
59
+ end
60
+
61
+ sig { returns(T::Array[NativePropertyDetails]) }
62
+ attr_reader :properties
63
+
64
+ sig { returns(T::Array[String]) }
65
+ attr_reader :target_frameworks
66
+
67
+ sig { returns(T::Array[String]) }
68
+ attr_reader :referenced_project_paths
69
+
70
+ sig { override.returns(Dependabot::FileParsers::Base::DependencySet) }
71
+ def dependency_set
72
+ if target_frameworks.empty? && file_path.end_with?("proj")
73
+ Dependabot.logger.warn("Excluding project file '#{file_path}' due to unresolvable target framework")
74
+ dependency_set = Dependabot::FileParsers::Base::DependencySet.new
75
+ return dependency_set
76
+ end
77
+
78
+ super
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,43 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Dependabot
7
+ module Nuget
8
+ class NativePropertyDetails
9
+ extend T::Sig
10
+
11
+ sig { params(json: T::Hash[String, T.untyped]).returns(NativePropertyDetails) }
12
+ def self.from_json(json)
13
+ name = T.let(json.fetch("Name"), String)
14
+ value = T.let(json.fetch("Value"), String)
15
+ source_file_path = T.let(json.fetch("SourceFilePath"), String)
16
+
17
+ NativePropertyDetails.new(name: name,
18
+ value: value,
19
+ source_file_path: source_file_path)
20
+ end
21
+
22
+ sig do
23
+ params(name: String,
24
+ value: String,
25
+ source_file_path: String).void
26
+ end
27
+ def initialize(name:, value:, source_file_path:)
28
+ @name = name
29
+ @value = value
30
+ @source_file_path = source_file_path
31
+ end
32
+
33
+ sig { returns(String) }
34
+ attr_reader :name
35
+
36
+ sig { returns(String) }
37
+ attr_reader :value
38
+
39
+ sig { returns(String) }
40
+ attr_reader :source_file_path
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,68 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/nuget/native_discovery/native_dependency_file_discovery"
5
+ require "dependabot/nuget/native_discovery/native_directory_packages_props_discovery"
6
+ require "dependabot/nuget/native_discovery/native_project_discovery"
7
+ require "sorbet-runtime"
8
+
9
+ module Dependabot
10
+ module Nuget
11
+ class NativeWorkspaceDiscovery
12
+ extend T::Sig
13
+
14
+ sig { params(json: T::Hash[String, T.untyped]).returns(NativeWorkspaceDiscovery) }
15
+ def self.from_json(json)
16
+ path = T.let(json.fetch("Path"), String)
17
+ path = "/" + path unless path.start_with?("/")
18
+ projects = T.let(json.fetch("Projects"), T::Array[T::Hash[String, T.untyped]]).filter_map do |project|
19
+ NativeProjectDiscovery.from_json(project, path)
20
+ end
21
+ directory_packages_props = NativeDirectoryPackagesPropsDiscovery
22
+ .from_json(T.let(json.fetch("DirectoryPackagesProps"),
23
+ T.nilable(T::Hash[String, T.untyped])), path)
24
+ global_json = NativeDependencyFileDiscovery
25
+ .from_json(T.let(json.fetch("GlobalJson"), T.nilable(T::Hash[String, T.untyped])), path)
26
+ dotnet_tools_json = NativeDependencyFileDiscovery
27
+ .from_json(T.let(json.fetch("DotNetToolsJson"),
28
+ T.nilable(T::Hash[String, T.untyped])), path)
29
+
30
+ NativeWorkspaceDiscovery.new(path: path,
31
+ projects: projects,
32
+ directory_packages_props: directory_packages_props,
33
+ global_json: global_json,
34
+ dotnet_tools_json: dotnet_tools_json)
35
+ end
36
+
37
+ sig do
38
+ params(path: String,
39
+ projects: T::Array[NativeProjectDiscovery],
40
+ directory_packages_props: T.nilable(NativeDirectoryPackagesPropsDiscovery),
41
+ global_json: T.nilable(NativeDependencyFileDiscovery),
42
+ dotnet_tools_json: T.nilable(NativeDependencyFileDiscovery)).void
43
+ end
44
+ def initialize(path:, projects:, directory_packages_props:, global_json:, dotnet_tools_json:)
45
+ @path = path
46
+ @projects = projects
47
+ @directory_packages_props = directory_packages_props
48
+ @global_json = global_json
49
+ @dotnet_tools_json = dotnet_tools_json
50
+ end
51
+
52
+ sig { returns(String) }
53
+ attr_reader :path
54
+
55
+ sig { returns(T::Array[NativeProjectDiscovery]) }
56
+ attr_reader :projects
57
+
58
+ sig { returns(T.nilable(NativeDirectoryPackagesPropsDiscovery)) }
59
+ attr_reader :directory_packages_props
60
+
61
+ sig { returns(T.nilable(NativeDependencyFileDiscovery)) }
62
+ attr_reader :global_json
63
+
64
+ sig { returns(T.nilable(NativeDependencyFileDiscovery)) }
65
+ attr_reader :dotnet_tools_json
66
+ end
67
+ end
68
+ end
@@ -110,6 +110,65 @@ module Dependabot
110
110
  end
111
111
  end
112
112
 
113
+ sig do
114
+ params(repo_root: String, discovery_file_path: String, dependency_file_path: String,
115
+ analysis_folder_path: String).returns([String, String])
116
+ end
117
+ def self.get_nuget_analyze_tool_command(repo_root:, discovery_file_path:, dependency_file_path:,
118
+ analysis_folder_path:)
119
+ exe_path = File.join(native_helpers_root, "NuGetUpdater", "NuGetUpdater.Cli")
120
+ command_parts = [
121
+ exe_path,
122
+ "analyze",
123
+ "--repo-root",
124
+ repo_root,
125
+ "--discovery-file-path",
126
+ discovery_file_path,
127
+ "--dependency-file-path",
128
+ dependency_file_path,
129
+ "--analysis-folder-path",
130
+ analysis_folder_path,
131
+ "--verbose"
132
+ ].compact
133
+
134
+ command = Shellwords.join(command_parts)
135
+
136
+ fingerprint = [
137
+ exe_path,
138
+ "analyze",
139
+ "--discovery-file-path",
140
+ "<discovery-file-path>",
141
+ "--dependency-file-path",
142
+ "<dependency-file-path>",
143
+ "--analysis-folder-path",
144
+ "<analysis_folder_path>",
145
+ "--verbose"
146
+ ].compact.join(" ")
147
+
148
+ [command, fingerprint]
149
+ end
150
+
151
+ sig do
152
+ params(
153
+ repo_root: String, discovery_file_path: String, dependency_file_path: String,
154
+ analysis_folder_path: String, credentials: T::Array[Dependabot::Credential]
155
+ ).void
156
+ end
157
+ def self.run_nuget_analyze_tool(repo_root:, discovery_file_path:, dependency_file_path:,
158
+ analysis_folder_path:, credentials:)
159
+ (command, fingerprint) = get_nuget_analyze_tool_command(repo_root: repo_root,
160
+ discovery_file_path: discovery_file_path,
161
+ dependency_file_path: dependency_file_path,
162
+ analysis_folder_path: analysis_folder_path)
163
+
164
+ puts "running NuGet analyze:\n" + command
165
+
166
+ NuGetConfigCredentialHelpers.patch_nuget_config_for_action(credentials) do
167
+ output = SharedHelpers.run_shell_command(command, allow_unsafe_shell_command: true, fingerprint: fingerprint)
168
+ puts output
169
+ end
170
+ end
171
+
113
172
  sig do
114
173
  params(repo_root: String, proj_path: String, dependency: Dependency,
115
174
  is_transitive: T::Boolean).returns([String, String])