dependabot-nuget 0.288.0 → 0.290.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +19 -17
  3. data/helpers/lib/NuGetUpdater/NuGetProjects/NuGet.Packaging/NuGet.Packaging.csproj +0 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +7 -3
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +29 -2
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +25 -4
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +0 -6
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +33 -16
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +25 -10
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +0 -13
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementArrayConverter.cs +39 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +1 -1
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Clone/ShellGitCommandHandler.cs +1 -1
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +60 -66
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DotNetToolsJsonDiscovery.cs +2 -2
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/GlobalJsonDiscovery.cs +2 -2
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +11 -3
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscoveryResult.cs +1 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +2 -4
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +54 -11
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/WorkspaceDiscoveryResult.cs +0 -1
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +1 -2
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Files/JsonBuildFile.cs +1 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/CompatabilityChecker.cs +2 -2
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +13 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/AllowedUpdate.cs +18 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +8 -0
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Condition.cs +19 -0
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +8 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/GroupPullRequest.cs +9 -0
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +13 -10
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PullRequest.cs +11 -0
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/RequirementsUpdateStrategy.cs +15 -0
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +67 -58
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/VersionConverter.cs +19 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +15 -44
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/DotNetToolsJsonUpdater.cs +4 -4
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/GlobalJsonUpdater.cs +5 -5
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +2 -10
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +38 -33
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +25 -23
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +16 -12
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ConsoleLogger.cs +1 -1
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/DependencyConflictResolver.cs +19 -19
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ILogger.cs +11 -1
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +2 -0
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +18 -17
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +1 -17
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/PathHelper.cs +17 -9
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +96 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +5 -2
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +87 -5
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +2 -5
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +45 -1
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +35 -1
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +16 -0
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Proj.cs +6 -0
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +143 -36
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +184 -48
  61. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +5 -5
  62. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +32 -10
  63. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +85 -0
  64. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +402 -102
  65. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +342 -2
  66. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatedDependencyListTests.cs +60 -2
  67. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +18 -7
  68. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TestLogger.cs +1 -1
  69. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/BindingRedirectsTests.cs +1 -1
  70. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +24 -0
  71. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +4 -14
  72. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +84 -0
  73. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +66 -0
  74. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +95 -0
  75. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +1 -7
  76. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/AssertEx.cs +1 -1
  77. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/LinuxOnlyAttribute.cs +12 -0
  78. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +558 -711
  79. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +47 -2
  80. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +4 -2
  81. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -3
  82. data/lib/dependabot/nuget/discovery/dependency_details.rb +10 -3
  83. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +8 -12
  84. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +214 -29
  85. data/lib/dependabot/nuget/discovery/project_discovery.rb +41 -8
  86. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +14 -19
  87. data/lib/dependabot/nuget/file_fetcher.rb +11 -393
  88. data/lib/dependabot/nuget/file_parser.rb +23 -61
  89. data/lib/dependabot/nuget/file_updater.rb +28 -23
  90. data/lib/dependabot/nuget/native_helpers.rb +14 -5
  91. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +23 -27
  92. data/lib/dependabot/nuget/update_checker.rb +116 -190
  93. metadata +20 -32
  94. data/helpers/lib/NuGetUpdater/NuGetProjects/Directory.Packages.props +0 -29
  95. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +0 -43
  96. data/lib/dependabot/nuget/file_fetcher/import_paths_finder.rb +0 -73
  97. data/lib/dependabot/nuget/file_fetcher/sln_project_paths_finder.rb +0 -60
  98. data/lib/dependabot/nuget/http_response_helpers.rb +0 -19
  99. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +0 -102
  100. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +0 -129
  101. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +0 -171
  102. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +0 -63
  103. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +0 -82
  104. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +0 -43
  105. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +0 -68
  106. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +0 -105
  107. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +0 -201
  108. data/lib/dependabot/nuget/nuget_client.rb +0 -223
  109. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +0 -116
  110. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +0 -297
  111. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +0 -221
  112. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +0 -110
  113. data/lib/dependabot/nuget/update_checker/property_updater.rb +0 -196
  114. data/lib/dependabot/nuget/update_checker/repository_finder.rb +0 -466
  115. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +0 -34
  116. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +0 -30
  117. data/lib/dependabot/nuget/update_checker/version_finder.rb +0 -449
@@ -1,201 +0,0 @@
1
- # typed: strong
2
- # frozen_string_literal: true
3
-
4
- require "dependabot/nuget/analysis/analysis_json_reader"
5
- require "dependabot/nuget/native_discovery/native_discovery_json_reader"
6
- require "dependabot/update_checkers"
7
- require "dependabot/update_checkers/base"
8
- require "sorbet-runtime"
9
-
10
- module Dependabot
11
- module Nuget
12
- class NativeUpdateChecker < Dependabot::UpdateCheckers::Base
13
- extend T::Sig
14
-
15
- require_relative "native_requirements_updater"
16
-
17
- sig { override.returns(T.nilable(String)) }
18
- def latest_version
19
- # No need to find latest version for transitive dependencies unless they have a vulnerability.
20
- return dependency.version if !dependency.top_level? && !vulnerable?
21
-
22
- # if no update sources have the requisite package, then we can only assume that the current version is correct
23
- @latest_version = T.let(
24
- update_analysis.dependency_analysis.updated_version,
25
- T.nilable(String)
26
- )
27
- end
28
-
29
- sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
30
- def latest_resolvable_version
31
- # We always want a full unlock since any package update could update peer dependencies as well.
32
- # To force a full unlock instead of an own unlock, we return nil.
33
- nil
34
- end
35
-
36
- sig { override.returns(Dependabot::Nuget::Version) }
37
- def lowest_security_fix_version
38
- update_analysis.dependency_analysis.numeric_updated_version
39
- end
40
-
41
- sig { override.returns(T.nilable(Dependabot::Nuget::Version)) }
42
- def lowest_resolvable_security_fix_version
43
- return nil if version_comes_from_multi_dependency_property?
44
-
45
- update_analysis.dependency_analysis.numeric_updated_version
46
- end
47
-
48
- sig { override.returns(NilClass) }
49
- def latest_resolvable_version_with_no_unlock
50
- # Irrelevant, since Nuget has a single dependency file
51
- nil
52
- end
53
-
54
- sig { override.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
55
- def updated_requirements
56
- dep_details = updated_dependency_details.find { |d| d.name.casecmp?(dependency.name) }
57
- NativeRequirementsUpdater.new(
58
- requirements: dependency.requirements,
59
- dependency_details: dep_details
60
- ).updated_requirements
61
- end
62
-
63
- sig { returns(T::Boolean) }
64
- def up_to_date?
65
- !update_analysis.dependency_analysis.can_update
66
- end
67
-
68
- sig { returns(T::Boolean) }
69
- def requirements_unlocked_or_can_be?
70
- update_analysis.dependency_analysis.can_update
71
- end
72
-
73
- sig { returns(T::Boolean) }
74
- def public_latest_version_resolvable_with_full_unlock?
75
- latest_version_resolvable_with_full_unlock?
76
- end
77
-
78
- sig { returns(T::Array[Dependabot::Dependency]) }
79
- def public_updated_dependencies_after_full_unlock
80
- updated_dependencies_after_full_unlock
81
- end
82
-
83
- private
84
-
85
- sig { returns(AnalysisJsonReader) }
86
- def update_analysis
87
- @update_analysis ||= T.let(request_analysis, T.nilable(AnalysisJsonReader))
88
- end
89
-
90
- sig { returns(String) }
91
- def dependency_file_path
92
- File.join(NativeDiscoveryJsonReader.temp_directory, "dependency", "#{dependency.name}.json")
93
- end
94
-
95
- sig { returns(AnalysisJsonReader) }
96
- def request_analysis
97
- discovery_file_path = NativeDiscoveryJsonReader.get_discovery_file_path_from_dependency_files(dependency_files)
98
- analysis_folder_path = AnalysisJsonReader.temp_directory
99
-
100
- write_dependency_info
101
-
102
- NativeHelpers.run_nuget_analyze_tool(repo_root: T.must(repo_contents_path),
103
- discovery_file_path: discovery_file_path,
104
- dependency_file_path: dependency_file_path,
105
- analysis_folder_path: analysis_folder_path,
106
- credentials: credentials)
107
-
108
- analysis_json = AnalysisJsonReader.analysis_json(dependency_name: dependency.name)
109
-
110
- AnalysisJsonReader.new(analysis_json: T.must(analysis_json))
111
- end
112
-
113
- sig { void }
114
- def write_dependency_info
115
- dependency_info = {
116
- Name: dependency.name,
117
- Version: dependency.version.to_s,
118
- IsVulnerable: vulnerable?,
119
- IgnoredVersions: ignored_versions,
120
- Vulnerabilities: security_advisories.map do |vulnerability|
121
- {
122
- DependencyName: vulnerability.dependency_name,
123
- PackageManager: vulnerability.package_manager,
124
- VulnerableVersions: vulnerability.vulnerable_versions.map(&:to_s),
125
- SafeVersions: vulnerability.safe_versions.map(&:to_s)
126
- }
127
- end
128
- }.to_json
129
- dependency_directory = File.dirname(dependency_file_path)
130
-
131
- begin
132
- Dir.mkdir(dependency_directory)
133
- rescue StandardError
134
- nil?
135
- end
136
-
137
- Dependabot.logger.info("Writing dependency info: #{dependency_info}")
138
- File.write(dependency_file_path, dependency_info)
139
- end
140
-
141
- sig { returns(Dependabot::FileParsers::Base::DependencySet) }
142
- def discovered_dependencies
143
- discovery_json_reader = NativeDiscoveryJsonReader.get_discovery_from_dependency_files(dependency_files)
144
- discovery_json_reader.dependency_set
145
- end
146
-
147
- sig { override.returns(T::Boolean) }
148
- def latest_version_resolvable_with_full_unlock?
149
- # We always want a full unlock since any package update could update peer dependencies as well.
150
- true
151
- end
152
-
153
- sig { override.returns(T::Array[Dependabot::Dependency]) }
154
- def updated_dependencies_after_full_unlock
155
- dependencies = discovered_dependencies.dependencies
156
- updated_dependency_details.filter_map do |dependency_details|
157
- dep = dependencies.find { |d| d.name.casecmp(dependency_details.name)&.zero? }
158
- next unless dep
159
-
160
- metadata = {}
161
- # For peer dependencies, instruct updater to not directly update this dependency
162
- metadata = { information_only: true } unless dependency.name.casecmp(dependency_details.name)&.zero?
163
-
164
- # rebuild the new requirements with the updated dependency details
165
- updated_reqs = dep.requirements.map do |r|
166
- r = r.clone
167
- r[:requirement] = dependency_details.version
168
- r[:source] = {
169
- type: "nuget_repo",
170
- source_url: dependency_details.info_url
171
- }
172
- r
173
- end
174
-
175
- Dependency.new(
176
- name: dep.name,
177
- version: dependency_details.version,
178
- requirements: updated_reqs,
179
- previous_version: dep.version,
180
- previous_requirements: dep.requirements,
181
- package_manager: dep.package_manager,
182
- metadata: metadata
183
- )
184
- end
185
- end
186
-
187
- sig { returns(T::Array[Dependabot::Nuget::NativeDependencyDetails]) }
188
- def updated_dependency_details
189
- @updated_dependency_details ||= T.let(update_analysis.dependency_analysis.updated_dependencies,
190
- T.nilable(T::Array[Dependabot::Nuget::NativeDependencyDetails]))
191
- end
192
-
193
- sig { returns(T::Boolean) }
194
- def version_comes_from_multi_dependency_property?
195
- update_analysis.dependency_analysis.version_comes_from_multi_dependency_property
196
- end
197
- end
198
- end
199
- end
200
-
201
- Dependabot::UpdateCheckers.register("nuget", Dependabot::Nuget::UpdateChecker)
@@ -1,223 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "dependabot/nuget/cache_manager"
5
- require "dependabot/nuget/http_response_helpers"
6
- require "dependabot/nuget/update_checker/repository_finder"
7
- require "sorbet-runtime"
8
-
9
- module Dependabot
10
- module Nuget
11
- class NugetClient
12
- extend T::Sig
13
-
14
- sig do
15
- params(dependency_name: String, repository_details: T::Hash[Symbol, String])
16
- .returns(T.nilable(T::Set[String]))
17
- end
18
- def self.get_package_versions(dependency_name, repository_details)
19
- repository_type = repository_details.fetch(:repository_type)
20
- if repository_type == "v3"
21
- get_package_versions_v3(dependency_name, repository_details)
22
- elsif repository_type == "v2"
23
- get_package_versions_v2(dependency_name, repository_details)
24
- elsif repository_type == "local"
25
- get_package_versions_local(dependency_name, repository_details)
26
- else
27
- raise "Unknown repository type: #{repository_type}"
28
- end
29
- end
30
-
31
- sig do
32
- params(dependency_name: String, repository_details: T::Hash[Symbol, String])
33
- .returns(T.nilable(T::Set[String]))
34
- end
35
- private_class_method def self.get_package_versions_local(dependency_name, repository_details)
36
- url = repository_details.fetch(:base_url)
37
- raise "Local repo #{url} doesn't exist or isn't a directory" unless File.exist?(url) && File.directory?(url)
38
-
39
- package_dir = File.join(url, dependency_name)
40
-
41
- versions = Set.new
42
- return versions unless File.exist?(package_dir) && File.directory?(package_dir)
43
-
44
- Dir.each_child(package_dir) do |child|
45
- versions.add(child) if File.directory?(File.join(package_dir, child))
46
- end
47
-
48
- versions
49
- end
50
-
51
- sig do
52
- params(dependency_name: String, repository_details: T::Hash[Symbol, String])
53
- .returns(T.nilable(T::Set[String]))
54
- end
55
- private_class_method def self.get_package_versions_v3(dependency_name, repository_details)
56
- # Use the registration URL if possible because it is fast and correct
57
- if repository_details[:registration_url]
58
- get_versions_from_registration_v3(repository_details)
59
- # use the search API if not because it is slow but correct
60
- elsif repository_details[:search_url]
61
- get_versions_from_search_url_v3(repository_details, dependency_name)
62
- # Otherwise, use the versions URL (fast but wrong because it includes unlisted versions)
63
- elsif repository_details[:versions_url]
64
- get_versions_from_versions_url_v3(repository_details)
65
- else
66
- raise "No version sources were available for #{dependency_name} in #{repository_details}"
67
- end
68
- end
69
-
70
- sig do
71
- params(dependency_name: String, repository_details: T::Hash[Symbol, String])
72
- .returns(T.nilable(T::Set[String]))
73
- end
74
- private_class_method def self.get_package_versions_v2(dependency_name, repository_details)
75
- doc = execute_xml_nuget_request(repository_details.fetch(:versions_url), repository_details)
76
- return unless doc
77
-
78
- # v2 APIs can differ, but all tested have this title value set to the name of the package
79
- title_nodes = doc.xpath("/feed/entry/title")
80
- matching_versions = Set.new
81
- title_nodes.each do |title_node|
82
- return nil unless title_node.text
83
-
84
- next unless title_node.text.casecmp?(dependency_name)
85
-
86
- version_node = title_node.parent.xpath("properties/Version")
87
- matching_versions << version_node.text if version_node && version_node.text
88
- end
89
-
90
- matching_versions
91
- end
92
-
93
- sig { params(repository_details: T::Hash[Symbol, String]).returns(T.nilable(T::Set[String])) }
94
- private_class_method def self.get_versions_from_versions_url_v3(repository_details)
95
- body = execute_json_nuget_request(repository_details.fetch(:versions_url), repository_details)
96
- ver_array = T.let(body&.fetch("versions"), T.nilable(T::Array[String]))
97
- ver_array&.to_set
98
- end
99
-
100
- sig { params(repository_details: T::Hash[Symbol, String]).returns(T.nilable(T::Set[String])) }
101
- private_class_method def self.get_versions_from_registration_v3(repository_details)
102
- url = repository_details.fetch(:registration_url)
103
- body = execute_json_nuget_request(url, repository_details)
104
-
105
- return unless body
106
-
107
- pages = body.fetch("items")
108
- versions = T.let(Set.new, T::Set[String])
109
- pages.each do |page|
110
- items = page["items"]
111
- if items
112
- # inlined entries
113
- get_versions_from_inline_page(items, versions)
114
- else
115
- # paged entries
116
- page_url = page["@id"]
117
- page_body = execute_json_nuget_request(page_url, repository_details)
118
- next unless page_body
119
-
120
- items = page_body.fetch("items")
121
- items.each do |item|
122
- catalog_entry = item.fetch("catalogEntry")
123
- versions << catalog_entry.fetch("version") if catalog_entry["listed"] == true
124
- end
125
- end
126
- end
127
-
128
- versions
129
- end
130
-
131
- sig { params(items: T::Array[T::Hash[String, T.untyped]], versions: T::Set[String]).void }
132
- private_class_method def self.get_versions_from_inline_page(items, versions)
133
- items.each do |item|
134
- catalog_entry = item["catalogEntry"]
135
-
136
- # a package is considered listed if the `listed` property is either `true` or missing
137
- listed_property = catalog_entry["listed"]
138
- is_listed = listed_property.nil? || listed_property == true
139
- if is_listed
140
- vers = catalog_entry["version"]
141
- versions << vers
142
- end
143
- end
144
- end
145
-
146
- sig do
147
- params(repository_details: T::Hash[Symbol, String], dependency_name: String)
148
- .returns(T.nilable(T::Set[String]))
149
- end
150
- private_class_method def self.get_versions_from_search_url_v3(repository_details, dependency_name)
151
- search_url = repository_details.fetch(:search_url)
152
- body = execute_json_nuget_request(search_url, repository_details)
153
-
154
- body&.fetch("data")
155
- &.find { |d| d.fetch("id").casecmp(dependency_name.downcase).zero? }
156
- &.fetch("versions")
157
- &.map { |d| d.fetch("version") }
158
- &.to_set
159
- end
160
-
161
- sig do
162
- params(url: String, repository_details: T::Hash[Symbol, T.untyped]).returns(T.nilable(Nokogiri::XML::Document))
163
- end
164
- private_class_method def self.execute_xml_nuget_request(url, repository_details)
165
- response = execute_nuget_request_internal(
166
- url: url,
167
- auth_header: repository_details.fetch(:auth_header),
168
- repository_url: repository_details.fetch(:repository_url)
169
- )
170
- return unless response.status == 200
171
-
172
- doc = Nokogiri::XML(response.body)
173
- doc.remove_namespaces!
174
- doc
175
- end
176
-
177
- sig do
178
- params(url: String,
179
- repository_details: T::Hash[Symbol, T.untyped])
180
- .returns(T.nilable(T::Hash[T.untyped, T.untyped]))
181
- end
182
- private_class_method def self.execute_json_nuget_request(url, repository_details)
183
- response = execute_nuget_request_internal(
184
- url: url,
185
- auth_header: repository_details.fetch(:auth_header),
186
- repository_url: repository_details.fetch(:repository_url)
187
- )
188
- return unless response.status == 200
189
-
190
- body = HttpResponseHelpers.remove_wrapping_zero_width_chars(response.body)
191
- JSON.parse(body)
192
- end
193
-
194
- sig do
195
- params(url: String, auth_header: T::Hash[Symbol, T.untyped], repository_url: String).returns(Excon::Response)
196
- end
197
- private_class_method def self.execute_nuget_request_internal(url:, auth_header:, repository_url:)
198
- cache = CacheManager.cache("dependency_url_search_cache")
199
- if cache[url].nil?
200
- response = Dependabot::RegistryClient.get(
201
- url: url,
202
- headers: auth_header
203
- )
204
-
205
- if [401, 402, 403].include?(response.status)
206
- raise Dependabot::PrivateSourceAuthenticationFailure, repository_url
207
- end
208
-
209
- cache[url] = response if !CacheManager.caching_disabled? && response.status == 200
210
- else
211
- response = cache[url]
212
- end
213
-
214
- response
215
- rescue Excon::Error::Timeout, Excon::Error::Socket
216
- repo_url = repository_url
217
- raise if repo_url == Dependabot::Nuget::RepositoryFinder::DEFAULT_REPOSITORY_URL
218
-
219
- raise PrivateSourceTimedOut, repo_url
220
- end
221
- end
222
- end
223
- end
@@ -1,116 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "sorbet-runtime"
5
-
6
- require "dependabot/update_checkers/base"
7
-
8
- module Dependabot
9
- module Nuget
10
- class CompatibilityChecker
11
- extend T::Sig
12
-
13
- require_relative "nuspec_fetcher"
14
- require_relative "nupkg_fetcher"
15
- require_relative "tfm_finder"
16
- require_relative "tfm_comparer"
17
-
18
- sig do
19
- params(
20
- dependency_urls: T::Array[T::Hash[Symbol, String]],
21
- dependency: Dependabot::Dependency
22
- ).void
23
- end
24
- def initialize(dependency_urls:, dependency:)
25
- @dependency_urls = dependency_urls
26
- @dependency = dependency
27
- end
28
-
29
- sig { params(version: String).returns(T::Boolean) }
30
- def compatible?(version)
31
- nuspec_xml = NuspecFetcher.fetch_nuspec(dependency_urls, dependency.name, version)
32
- return false unless nuspec_xml
33
-
34
- # development dependencies are packages such as analyzers which need to be compatible with the compiler not the
35
- # project itself, but some packages that report themselves as development dependencies still contain target
36
- # framework dependencies and should be checked for compatibility through the regular means
37
- return true if pure_development_dependency?(nuspec_xml)
38
-
39
- package_tfms = parse_package_tfms(nuspec_xml)
40
- package_tfms = fetch_package_tfms(version) if package_tfms.empty?
41
- # nil is a special return value that indicates that the package is likely a development dependency
42
- return true if package_tfms.nil?
43
- return false if package_tfms.empty?
44
-
45
- return false if project_tfms.nil? || project_tfms&.empty?
46
-
47
- TfmComparer.are_frameworks_compatible?(T.must(project_tfms), package_tfms)
48
- end
49
-
50
- private
51
-
52
- sig { returns(T::Array[T::Hash[Symbol, String]]) }
53
- attr_reader :dependency_urls
54
-
55
- sig { returns(Dependabot::Dependency) }
56
- attr_reader :dependency
57
-
58
- sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Boolean) }
59
- def pure_development_dependency?(nuspec_xml)
60
- contents = nuspec_xml.at_xpath("package/metadata/developmentDependency")&.content&.strip
61
- return false unless contents # no `developmentDependency` element
62
-
63
- self_reports_as_development_dependency = contents.casecmp?("true")
64
- return false unless self_reports_as_development_dependency
65
-
66
- # even though a package self-reports as a development dependency, it might not be if it has dependency groups
67
- # with a target framework
68
- dependency_groups_with_target_framework =
69
- nuspec_xml.at_xpath("/package/metadata/dependencies/group[@targetFramework]")
70
- dependency_groups_with_target_framework.to_a.empty?
71
- end
72
-
73
- sig { params(nuspec_xml: Nokogiri::XML::Document).returns(T::Array[String]) }
74
- def parse_package_tfms(nuspec_xml)
75
- nuspec_xml.xpath("//dependencies/group").filter_map { |group| group.attribute("targetFramework") }
76
- end
77
-
78
- sig { returns(T.nilable(T::Array[String])) }
79
- def project_tfms
80
- @project_tfms ||= T.let(TfmFinder.frameworks(dependency), T.nilable(T::Array[String]))
81
- end
82
-
83
- sig { params(dependency_version: String).returns(T.nilable(T::Array[String])) }
84
- def fetch_package_tfms(dependency_version)
85
- cache = CacheManager.cache("compatibility_checker_tfms_cache")
86
- key = "#{dependency.name}::#{dependency_version}"
87
-
88
- cache[key] ||= begin
89
- nupkg_buffer = NupkgFetcher.fetch_nupkg_buffer(dependency_urls, dependency.name, dependency_version)
90
- return [] unless nupkg_buffer
91
-
92
- # Parse tfms from the folders beneath the lib folder
93
- folder_name = "lib/"
94
- tfms = Set.new
95
- Zip::File.open_buffer(nupkg_buffer) do |zip|
96
- lib_file_entries = zip.select { |entry| entry.name.start_with?(folder_name) }
97
- # If there is no lib folder in this package, assume it is a development dependency
98
- return nil if lib_file_entries.empty?
99
-
100
- lib_file_entries.each do |entry|
101
- _, tfm = entry.name.split("/").first(2)
102
-
103
- # some zip compressors create empty directory entries (in this case `lib/`) which can cause the string
104
- # split to return `nil`, so we have to explicitly guard against that
105
- tfms << tfm if tfm
106
- end
107
- end
108
-
109
- tfms.to_a
110
- end
111
-
112
- cache[key]
113
- end
114
- end
115
- end
116
- end