dependabot-nuget 0.289.0 → 0.291.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/Directory.Packages.props +1 -1
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/AnalyzeCommand.cs +7 -3
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli/Commands/RunCommand.cs +1 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +26 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +2 -1
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +0 -6
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +1 -1
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/AnalyzeWorker.cs +6 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/CompatabilityChecker.cs +24 -9
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyFinder.cs +2 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/NuGetContext.cs +0 -13
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/RequirementConverter.cs +17 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/DiscoveryWorker.cs +44 -5
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/PackagesConfigDiscovery.cs +2 -2
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/ProjectDiscoveryResult.cs +2 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +19 -11
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ErrorType.cs +1 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -0
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Advisory.cs +13 -0
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/AllowedUpdate.cs +18 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/CommitOptions.cs +8 -0
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Condition.cs +19 -0
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +8 -0
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/GroupPullRequest.cs +9 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +13 -10
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/PullRequest.cs +11 -0
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/RequirementsUpdateStrategy.cs +15 -0
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +24 -4
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/VersionConverter.cs +19 -0
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/BindingRedirectManager.cs +2 -1
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/LockFileUpdater.cs +3 -2
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +43 -18
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackagesConfigUpdater.cs +13 -12
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +1 -1
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/JsonHelper.cs +2 -0
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +40 -14
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/NuGetHelper.cs +2 -2
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProcessExtensions.cs +45 -7
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/ProjectHelper.cs +2 -2
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTestBase.cs +5 -2
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.DotNetToolsJson.cs +45 -1
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.GlobalJson.cs +35 -1
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +0 -4
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +41 -0
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/ExpectedDiscoveryResults.cs +1 -0
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +1 -1
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/MockNuGetPackage.cs +2 -1
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +85 -0
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +7 -31
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +340 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/TemporaryDirectory.cs +18 -7
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackagesConfigUpdaterTests.cs +24 -0
  54. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTestBase.cs +0 -12
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DotNetTools.cs +84 -0
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.GlobalJson.cs +66 -0
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackageReference.cs +55 -0
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.PackagesConfig.cs +0 -6
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +785 -755
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/PathHelperTests.cs +2 -2
  61. data/lib/dependabot/nuget/analysis/analysis_json_reader.rb +1 -1
  62. data/lib/dependabot/nuget/analysis/dependency_analysis.rb +3 -3
  63. data/lib/dependabot/nuget/discovery/dependency_details.rb +10 -3
  64. data/lib/dependabot/nuget/discovery/dependency_file_discovery.rb +8 -12
  65. data/lib/dependabot/nuget/discovery/discovery_json_reader.rb +214 -29
  66. data/lib/dependabot/nuget/discovery/project_discovery.rb +41 -8
  67. data/lib/dependabot/nuget/discovery/workspace_discovery.rb +14 -19
  68. data/lib/dependabot/nuget/file_fetcher.rb +3 -3
  69. data/lib/dependabot/nuget/file_parser.rb +92 -3
  70. data/lib/dependabot/nuget/file_updater.rb +13 -13
  71. data/lib/dependabot/nuget/language.rb +82 -0
  72. data/lib/dependabot/nuget/native_helpers.rb +37 -5
  73. data/lib/dependabot/nuget/package_manager.rb +51 -0
  74. data/lib/dependabot/nuget/update_checker/requirements_updater.rb +23 -27
  75. data/lib/dependabot/nuget/update_checker.rb +116 -190
  76. metadata +20 -29
  77. data/lib/dependabot/nuget/discovery/directory_packages_props_discovery.rb +0 -43
  78. data/lib/dependabot/nuget/http_response_helpers.rb +0 -19
  79. data/lib/dependabot/nuget/native_discovery/native_dependency_details.rb +0 -102
  80. data/lib/dependabot/nuget/native_discovery/native_dependency_file_discovery.rb +0 -122
  81. data/lib/dependabot/nuget/native_discovery/native_discovery_json_reader.rb +0 -277
  82. data/lib/dependabot/nuget/native_discovery/native_evaluation_details.rb +0 -63
  83. data/lib/dependabot/nuget/native_discovery/native_project_discovery.rb +0 -104
  84. data/lib/dependabot/nuget/native_discovery/native_property_details.rb +0 -43
  85. data/lib/dependabot/nuget/native_discovery/native_workspace_discovery.rb +0 -61
  86. data/lib/dependabot/nuget/native_update_checker/native_requirements_updater.rb +0 -105
  87. data/lib/dependabot/nuget/native_update_checker/native_update_checker.rb +0 -214
  88. data/lib/dependabot/nuget/nuget_client.rb +0 -223
  89. data/lib/dependabot/nuget/update_checker/compatibility_checker.rb +0 -116
  90. data/lib/dependabot/nuget/update_checker/dependency_finder.rb +0 -297
  91. data/lib/dependabot/nuget/update_checker/nupkg_fetcher.rb +0 -221
  92. data/lib/dependabot/nuget/update_checker/nuspec_fetcher.rb +0 -110
  93. data/lib/dependabot/nuget/update_checker/property_updater.rb +0 -196
  94. data/lib/dependabot/nuget/update_checker/repository_finder.rb +0 -466
  95. data/lib/dependabot/nuget/update_checker/tfm_comparer.rb +0 -34
  96. data/lib/dependabot/nuget/update_checker/tfm_finder.rb +0 -30
  97. data/lib/dependabot/nuget/update_checker/version_finder.rb +0 -449
@@ -1,449 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "sorbet-runtime"
5
-
6
- require "dependabot/nuget/version"
7
- require "dependabot/nuget/requirement"
8
- require "dependabot/update_checkers/base"
9
- require "dependabot/update_checkers/version_filters"
10
- require "dependabot/nuget/nuget_client"
11
-
12
- module Dependabot
13
- module Nuget
14
- class UpdateChecker < Dependabot::UpdateCheckers::Base
15
- # rubocop:disable Metrics/ClassLength
16
- class VersionFinder
17
- extend T::Sig
18
-
19
- require_relative "compatibility_checker"
20
- require_relative "repository_finder"
21
-
22
- NUGET_RANGE_REGEX = /[\(\[].*,.*[\)\]]/
23
-
24
- sig do
25
- params(
26
- dependency: Dependabot::Dependency,
27
- dependency_files: T::Array[Dependabot::DependencyFile],
28
- credentials: T::Array[Dependabot::Credential],
29
- ignored_versions: T::Array[String],
30
- security_advisories: T::Array[Dependabot::SecurityAdvisory],
31
- repo_contents_path: T.nilable(String),
32
- raise_on_ignored: T::Boolean
33
- ).void
34
- end
35
- def initialize(dependency:,
36
- dependency_files:,
37
- credentials:,
38
- ignored_versions:,
39
- security_advisories:,
40
- repo_contents_path:,
41
- raise_on_ignored: false)
42
- @dependency = dependency
43
- @dependency_files = dependency_files
44
- @credentials = credentials
45
- @ignored_versions = ignored_versions
46
- @raise_on_ignored = raise_on_ignored
47
- @security_advisories = security_advisories
48
- @repo_contents_path = repo_contents_path
49
- end
50
-
51
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
52
- def latest_version_details
53
- @latest_version_details ||=
54
- T.let(
55
- begin
56
- possible_versions = versions
57
- possible_versions = filter_prereleases(possible_versions)
58
- possible_versions = filter_ignored_versions(possible_versions)
59
-
60
- find_highest_compatible_version(possible_versions)
61
- end,
62
- T.nilable(T::Hash[Symbol, T.untyped])
63
- )
64
- end
65
-
66
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
67
- def lowest_security_fix_version_details
68
- @lowest_security_fix_version_details ||=
69
- T.let(
70
- begin
71
- possible_versions = versions
72
- possible_versions = filter_prereleases(possible_versions)
73
- possible_versions = Dependabot::UpdateCheckers::VersionFilters.filter_vulnerable_versions(
74
- possible_versions, security_advisories
75
- )
76
- possible_versions = filter_ignored_versions(possible_versions)
77
- possible_versions = filter_lower_versions(possible_versions)
78
-
79
- find_lowest_compatible_version(possible_versions)
80
- end,
81
- T.nilable(T::Hash[Symbol, T.untyped])
82
- )
83
- end
84
-
85
- sig { returns(T::Array[T::Hash[Symbol, T.nilable(T.any(Dependabot::Version, String))]]) }
86
- def versions
87
- available_v3_versions + available_v2_versions
88
- end
89
-
90
- sig { returns(Dependabot::Dependency) }
91
- attr_reader :dependency
92
-
93
- sig { returns(T::Array[Dependabot::DependencyFile]) }
94
- attr_reader :dependency_files
95
-
96
- sig { returns(T::Array[Dependabot::Credential]) }
97
- attr_reader :credentials
98
-
99
- sig { returns(T::Array[String]) }
100
- attr_reader :ignored_versions
101
-
102
- sig { returns(T::Array[Dependabot::SecurityAdvisory]) }
103
- attr_reader :security_advisories
104
-
105
- sig { returns(T.nilable(String)) }
106
- attr_reader :repo_contents_path
107
-
108
- private
109
-
110
- sig do
111
- params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
112
- .returns(T.nilable(T::Hash[Symbol, T.untyped]))
113
- end
114
- def find_highest_compatible_version(possible_versions)
115
- # sorted versions descending
116
- sorted_versions = possible_versions.sort_by { |v| v.fetch(:version) }.reverse
117
- find_compatible_version(sorted_versions)
118
- end
119
-
120
- sig do
121
- params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
122
- .returns(T.nilable(T::Hash[Symbol, T.untyped]))
123
- end
124
- def find_lowest_compatible_version(possible_versions)
125
- # sorted versions ascending
126
- sorted_versions = possible_versions.sort_by { |v| v.fetch(:version) }
127
- find_compatible_version(sorted_versions)
128
- end
129
-
130
- sig do
131
- params(sorted_versions: T::Array[T::Hash[Symbol, T.untyped]])
132
- .returns(T.nilable(T::Hash[Symbol, T.untyped]))
133
- end
134
- def find_compatible_version(sorted_versions)
135
- # By checking the first version separately, we can avoid additional network requests
136
- first_version = sorted_versions.first
137
- return unless first_version
138
- # If the current package version is incompatible, then we don't enforce compatibility.
139
- # It could appear incompatible because they are ignoring NU1701 or the package is poorly authored.
140
- return first_version unless version_compatible?(dependency.version)
141
-
142
- # once sorted by version, the best we can do is search every package, because it's entirely possible for there
143
- # to be incompatible packages both with a higher and lower version number, so no smart searching can be done.
144
- sorted_versions.find { |v| version_compatible?(v.fetch(:version)) }
145
- end
146
-
147
- sig { params(version: T.nilable(T.any(Dependabot::Version, String))).returns(T::Boolean) }
148
- def version_compatible?(version)
149
- str_version_compatible?(version.to_s)
150
- end
151
-
152
- sig { params(version: String).returns(T::Boolean) }
153
- def str_version_compatible?(version)
154
- compatibility_checker.compatible?(version)
155
- end
156
-
157
- sig { returns(Dependabot::Nuget::CompatibilityChecker) }
158
- def compatibility_checker
159
- @compatibility_checker ||=
160
- T.let(
161
- CompatibilityChecker.new(
162
- dependency_urls: dependency_urls,
163
- dependency: dependency
164
- ),
165
- T.nilable(Dependabot::Nuget::CompatibilityChecker)
166
- )
167
- end
168
-
169
- sig do
170
- params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
171
- .returns(T::Array[T::Hash[Symbol, T.untyped]])
172
- end
173
- def filter_prereleases(possible_versions)
174
- filtered = possible_versions.reject do |d|
175
- version = d.fetch(:version)
176
- version.prerelease? && !related_to_current_pre?(version)
177
- end
178
- if possible_versions.count > filtered.count
179
- Dependabot.logger.info("Filtered out #{possible_versions.count - filtered.count} pre-release versions")
180
- end
181
- filtered
182
- end
183
-
184
- sig do
185
- params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
186
- .returns(T::Array[T::Hash[Symbol, T.untyped]])
187
- end
188
- def filter_ignored_versions(possible_versions)
189
- filtered = possible_versions
190
- ignored_versions.each do |req|
191
- ignore_reqs = parse_requirement_string(req).map { |r| requirement_class.new(r) }
192
- filtered =
193
- filtered
194
- .reject { |v| ignore_reqs.any? { |r| r.satisfied_by?(v.fetch(:version)) } }
195
- end
196
-
197
- if @raise_on_ignored && filter_lower_versions(filtered).empty? &&
198
- filter_lower_versions(possible_versions).any?
199
- raise AllVersionsIgnored
200
- end
201
-
202
- if possible_versions.count > filtered.count
203
- Dependabot.logger.info("Filtered out #{possible_versions.count - filtered.count} ignored versions")
204
- end
205
-
206
- filtered
207
- end
208
-
209
- sig do
210
- params(possible_versions: T::Array[T::Hash[Symbol, T.untyped]])
211
- .returns(T::Array[T::Hash[Symbol, T.untyped]])
212
- end
213
- def filter_lower_versions(possible_versions)
214
- return possible_versions unless dependency.numeric_version
215
-
216
- possible_versions.select do |v|
217
- v.fetch(:version) > dependency.numeric_version
218
- end
219
- end
220
-
221
- sig { params(string: String).returns(T::Array[String]) }
222
- def parse_requirement_string(string)
223
- return [string] if string.match?(NUGET_RANGE_REGEX)
224
-
225
- string.split(",").map(&:strip)
226
- end
227
-
228
- sig { returns(T::Array[T::Hash[Symbol, T.any(Dependabot::Version, String, NilClass)]]) }
229
- def available_v3_versions
230
- v3_nuget_listings.flat_map do |listing|
231
- listing
232
- .fetch("versions", [])
233
- .map do |v|
234
- listing_details = listing.fetch("listing_details")
235
- nuspec_url = listing_details
236
- .fetch(:versions_url, nil)
237
- &.gsub(/index\.json$/, "#{v}/#{sanitized_name}.nuspec")
238
-
239
- {
240
- version: version_class.new(v),
241
- nuspec_url: nuspec_url,
242
- source_url: nil,
243
- repo_url: listing_details.fetch(:repository_url)
244
- }
245
- end
246
- end
247
- end
248
-
249
- sig { returns(T::Array[T::Hash[Symbol, T.any(Dependabot::Version, String, NilClass)]]) }
250
- def available_v2_versions
251
- v2_nuget_listings.flat_map do |listing|
252
- body = listing.fetch("xml_body", [])
253
- doc = Nokogiri::XML(body)
254
- doc.remove_namespaces!
255
-
256
- doc.xpath("/feed/entry").filter_map do |entry|
257
- listed = entry.at_xpath("./properties/Listed")&.content&.strip
258
- next if listed&.casecmp("false")&.zero?
259
-
260
- entry_details = dependency_details_from_v2_entry(entry)
261
- entry_details.merge(
262
- repo_url: listing.fetch("listing_details")
263
- .fetch(:repository_url)
264
- )
265
- end
266
- end
267
- end
268
-
269
- sig do
270
- params(entry: Nokogiri::XML::Element)
271
- .returns(T::Hash[Symbol, T.any(Dependabot::Version, String, NilClass)])
272
- end
273
- def dependency_details_from_v2_entry(entry)
274
- version = entry.at_xpath("./properties/Version").content.strip
275
- source_urls = []
276
- [
277
- entry.at_xpath("./properties/ProjectUrl")&.content,
278
- entry.at_xpath("./properties/ReleaseNotes")&.content
279
- ].compact.join(" ").scan(Source::SOURCE_REGEX) do
280
- source_urls << Regexp.last_match.to_s
281
- end
282
-
283
- source_url = source_urls.find { |url| Source.from_url(url) }
284
- source_url = Source.from_url(source_url)&.url if source_url
285
-
286
- {
287
- version: version_class.new(version),
288
- nuspec_url: nil,
289
- source_url: source_url
290
- }
291
- end
292
-
293
- # rubocop:disable Metrics/PerceivedComplexity
294
- sig { params(version: Dependabot::Version).returns(T::Boolean) }
295
- def related_to_current_pre?(version)
296
- current_version = dependency.numeric_version
297
- if current_version&.prerelease? &&
298
- current_version.release == version.release
299
- return true
300
- end
301
-
302
- dependency.requirements.any? do |req|
303
- reqs = parse_requirement_string(req.fetch(:requirement) || "")
304
- return true if reqs.any?("*-*")
305
- next unless reqs.any? { |r| r.include?("-") }
306
-
307
- requirement_class
308
- .requirements_array(req.fetch(:requirement))
309
- .any? do |r|
310
- r.requirements.any? { |a| a.last.release == version.release }
311
- end
312
- rescue Gem::Requirement::BadRequirementError
313
- false
314
- end
315
- end
316
- # rubocop:enable Metrics/PerceivedComplexity
317
-
318
- sig { returns(T::Array[T::Hash[String, T.untyped]]) }
319
- def v3_nuget_listings
320
- @v3_nuget_listings ||=
321
- T.let(
322
- dependency_urls
323
- .select { |details| details.fetch(:repository_type) == "v3" }
324
- .filter_map do |url_details|
325
- versions = NugetClient.get_package_versions(dependency.name, url_details)
326
- next unless versions
327
-
328
- { "versions" => versions, "listing_details" => url_details }
329
- end,
330
- T.nilable(T::Array[T::Hash[String, T.untyped]])
331
- )
332
- end
333
-
334
- sig { returns(T::Array[T::Hash[String, T.untyped]]) }
335
- def v2_nuget_listings
336
- @v2_nuget_listings ||=
337
- T.let(
338
- dependency_urls
339
- .select { |details| details.fetch(:repository_type) == "v2" }
340
- .flat_map { |url_details| fetch_paginated_v2_nuget_listings(url_details) }
341
- .filter_map do |url_details, response|
342
- next unless response.status == 200
343
-
344
- {
345
- "xml_body" => response.body,
346
- "listing_details" => url_details
347
- }
348
- end,
349
- T.nilable(T::Array[T::Hash[String, T.untyped]])
350
- )
351
- end
352
-
353
- sig do
354
- params(
355
- url_details: T::Hash[Symbol, T.untyped],
356
- results: T::Hash[T::Hash[Symbol, T.untyped], Excon::Response]
357
- )
358
- .returns(T::Array[T::Array[T.untyped]])
359
- end
360
- def fetch_paginated_v2_nuget_listings(url_details, results = {})
361
- response = Dependabot::RegistryClient.get(
362
- url: url_details[:versions_url],
363
- headers: url_details[:auth_header]
364
- )
365
-
366
- # NOTE: Short circuit if we get a circular next link
367
- return results.to_a if results.key?(url_details)
368
-
369
- results[url_details] = response
370
-
371
- if (link_href = fetch_v2_next_link_href(response.body))
372
- url_details = url_details.dup
373
- # Some Nuget repositories, such as JFrog's Artifactory, URL encode the "next" href
374
- # link in the paged results. If the href is not URL decoded, the paging parameters
375
- # are ignored and the first page is always returned.
376
- url_details[:versions_url] = CGI.unescape(link_href)
377
- fetch_paginated_v2_nuget_listings(url_details, results)
378
- end
379
-
380
- results.to_a
381
- end
382
-
383
- sig { params(xml_body: String).returns(T.nilable(String)) }
384
- def fetch_v2_next_link_href(xml_body)
385
- doc = Nokogiri::XML(xml_body)
386
- doc.remove_namespaces!
387
- link_node = doc.xpath("/feed/link").find do |node|
388
- rel = node.attribute("rel").value.strip
389
- rel == "next"
390
- end
391
- link_node.attribute("href").value.strip if link_node
392
- rescue Nokogiri::XML::XPath::SyntaxError
393
- nil
394
- end
395
-
396
- sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
397
- def dependency_urls
398
- @dependency_urls ||=
399
- T.let(
400
- RepositoryFinder.new(
401
- dependency: dependency,
402
- credentials: credentials,
403
- config_files: nuget_configs
404
- ).dependency_urls,
405
- T.nilable(T::Array[T::Hash[Symbol, T.untyped]])
406
- )
407
- end
408
-
409
- sig { returns(T::Array[Dependabot::DependencyFile]) }
410
- def nuget_configs
411
- @nuget_configs ||=
412
- T.let(
413
- dependency_files.select { |f| f.name.match?(/nuget\.config$/i) },
414
- T.nilable(T::Array[Dependabot::DependencyFile])
415
- )
416
- end
417
-
418
- sig { returns(String) }
419
- def sanitized_name
420
- dependency.name.downcase
421
- end
422
-
423
- sig { returns(T.class_of(Gem::Version)) }
424
- def version_class
425
- dependency.version_class
426
- end
427
-
428
- sig { returns(T.class_of(Dependabot::Requirement)) }
429
- def requirement_class
430
- dependency.requirement_class
431
- end
432
-
433
- sig { returns(T::Hash[Symbol, Integer]) }
434
- def excon_options
435
- # For large JSON files we sometimes need a little longer than for
436
- # other languages. For example, see:
437
- # https://dotnet.myget.org/F/aspnetcore-dev/api/v3/query?
438
- # q=microsoft.aspnetcore.mvc&prerelease=true&semVerLevel=2.0.0
439
- {
440
- connect_timeout: 30,
441
- write_timeout: 30,
442
- read_timeout: 30
443
- }
444
- end
445
- end
446
- # rubocop:enable Metrics/ClassLength
447
- end
448
- end
449
- end