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,466 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "excon"
5
- require "nokogiri"
6
- require "sorbet-runtime"
7
-
8
- require "dependabot/errors"
9
- require "dependabot/update_checkers/base"
10
- require "dependabot/registry_client"
11
- require "dependabot/nuget/cache_manager"
12
- require "dependabot/nuget/http_response_helpers"
13
-
14
- module Dependabot
15
- module Nuget
16
- # rubocop:disable Metrics/ClassLength
17
- class RepositoryFinder
18
- extend T::Sig
19
-
20
- DEFAULT_REPOSITORY_URL = "https://api.nuget.org/v3/index.json"
21
- DEFAULT_REPOSITORY_API_KEY = "nuget.org"
22
-
23
- sig do
24
- params(
25
- dependency: Dependabot::Dependency,
26
- credentials: T::Array[Dependabot::Credential],
27
- config_files: T::Array[Dependabot::DependencyFile]
28
- ).void
29
- end
30
- def initialize(dependency:, credentials:, config_files: [])
31
- @dependency = dependency
32
- @credentials = credentials
33
- @config_files = config_files
34
- end
35
-
36
- sig { returns(T::Array[T::Hash[Symbol, String]]) }
37
- def dependency_urls
38
- find_dependency_urls
39
- end
40
-
41
- sig { returns(T::Array[T::Hash[Symbol, String]]) }
42
- def known_repositories
43
- return @known_repositories if @known_repositories
44
-
45
- @known_repositories ||= T.let([], T.nilable(T::Array[T::Hash[Symbol, String]]))
46
- @known_repositories += credential_repositories
47
- @known_repositories += config_file_repositories
48
-
49
- @known_repositories << { url: DEFAULT_REPOSITORY_URL, token: nil } if @known_repositories.empty?
50
-
51
- @known_repositories = @known_repositories.map do |repo|
52
- url = repo[:url]
53
- begin
54
- url = URI::DEFAULT_PARSER.parse(url).to_s
55
- rescue URI::InvalidURIError
56
- # e.g., the url has spaces or unacceptable symbols
57
- url = URI::DEFAULT_PARSER.escape(url)
58
- end
59
-
60
- { url: url, token: repo[:token] }
61
- end
62
- @known_repositories.uniq
63
- end
64
-
65
- sig { params(dependency_name: String).returns(T::Hash[Symbol, T.untyped]) }
66
- def self.get_default_repository_details(dependency_name)
67
- {
68
- base_url: "https://api.nuget.org/v3-flatcontainer/",
69
- registration_url: "https://api.nuget.org/v3/registration5-gz-semver2/#{dependency_name.downcase}/index.json",
70
- repository_url: DEFAULT_REPOSITORY_URL,
71
- versions_url: "https://api.nuget.org/v3-flatcontainer/" \
72
- "#{dependency_name.downcase}/index.json",
73
- search_url: "https://azuresearch-usnc.nuget.org/query" \
74
- "?q=#{dependency_name.downcase}&prerelease=true&semVerLevel=2.0.0",
75
- auth_header: {},
76
- repository_type: "v3"
77
- }
78
- end
79
-
80
- sig { params(source_name: String).returns(String) }
81
- def self.escape_source_name_to_element_name(source_name)
82
- source_name.chars.map do |c|
83
- case c
84
- when /[A-Za-z0-9\-_.]/
85
- # letters, digits, hyphens, underscores, and periods are all directly allowed
86
- c
87
- else
88
- # otherwise it needs to be escaped as a 4 digit hex value
89
- "_x#{c.ord.to_s(16).rjust(4, '0')}_"
90
- end
91
- end.join
92
- end
93
-
94
- private
95
-
96
- sig { returns(Dependabot::Dependency) }
97
- attr_reader :dependency
98
-
99
- sig { returns(T::Array[Dependabot::Credential]) }
100
- attr_reader :credentials
101
-
102
- sig { returns(T::Array[Dependabot::DependencyFile]) }
103
- attr_reader :config_files
104
-
105
- sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
106
- def find_dependency_urls
107
- @find_dependency_urls ||=
108
- T.let(
109
- known_repositories.flat_map do |details|
110
- if details.fetch(:url) == DEFAULT_REPOSITORY_URL
111
- # Save a request for the default URL, since we already know how
112
- # it addresses packages
113
- next default_repository_details
114
- end
115
-
116
- build_url_for_details(details)
117
- end.compact.uniq,
118
- T.nilable(T::Array[T::Hash[Symbol, T.untyped]])
119
- )
120
- end
121
-
122
- sig { params(repo_details: T::Hash[Symbol, T.untyped]).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
123
- def build_url_for_details(repo_details)
124
- url = repo_details.fetch(:url)
125
- url_obj = URI.parse(url)
126
- if url_obj.is_a?(URI::HTTP)
127
- details = build_url_for_details_remote(repo_details)
128
- elsif url_obj.is_a?(URI::File)
129
- details = {
130
- base_url: url,
131
- repository_type: "local"
132
- }
133
- end
134
-
135
- details
136
- end
137
-
138
- sig { params(repo_details: T::Hash[Symbol, T.untyped]).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
139
- def build_url_for_details_remote(repo_details)
140
- response = get_repo_metadata(repo_details)
141
- check_repo_response(response, repo_details)
142
- return unless response.status == 200
143
-
144
- body = HttpResponseHelpers.remove_wrapping_zero_width_chars(response.body)
145
- parsed_json = JSON.parse(body)
146
- base_url = base_url_from_v3_metadata(parsed_json)
147
- search_url = search_url_from_v3_metadata(parsed_json)
148
- registration_url = registration_url_from_v3_metadata(parsed_json)
149
-
150
- details = {
151
- base_url: base_url,
152
- repository_url: repo_details.fetch(:url),
153
- auth_header: auth_header_for_token(repo_details.fetch(:token)),
154
- repository_type: "v3"
155
- }
156
- if base_url
157
- details[:versions_url] =
158
- File.join(base_url, dependency.name.downcase, "index.json")
159
- end
160
- if search_url
161
- details[:search_url] =
162
- search_url + "?q=#{dependency.name.downcase}&prerelease=true&semVerLevel=2.0.0"
163
- end
164
-
165
- if registration_url
166
- details[:registration_url] = File.join(registration_url, dependency.name.downcase, "index.json")
167
- end
168
-
169
- details
170
- rescue JSON::ParserError
171
- build_v2_url(T.must(response), repo_details)
172
- rescue Excon::Error::Timeout, Excon::Error::Socket
173
- handle_timeout(repo_metadata_url: repo_details.fetch(:url))
174
- end
175
-
176
- sig { params(repo_details: T::Hash[Symbol, T.untyped]).returns(Excon::Response) }
177
- def get_repo_metadata(repo_details)
178
- url = repo_details.fetch(:url)
179
- cache = CacheManager.cache("repo_finder_metadatacache")
180
- if cache[url]
181
- cache[url]
182
- else
183
- result = Dependabot::RegistryClient.get(
184
- url: url,
185
- headers: auth_header_for_token(repo_details.fetch(:token))
186
- )
187
- cache[url] = result
188
- result
189
- end
190
- end
191
-
192
- sig { params(metadata: T::Hash[String, T::Array[T::Hash[String, T.untyped]]]).returns(T.nilable(String)) }
193
- def base_url_from_v3_metadata(metadata)
194
- metadata
195
- .fetch("resources", [])
196
- .find { |r| r.fetch("@type") == "PackageBaseAddress/3.0.0" }
197
- &.fetch("@id")
198
- end
199
-
200
- sig { params(metadata: T::Hash[String, T::Array[T::Hash[String, T.untyped]]]).returns(T.nilable(String)) }
201
- def registration_url_from_v3_metadata(metadata)
202
- allowed_registration_types = %w(
203
- RegistrationsBaseUrl
204
- RegistrationsBaseUrl/3.0.0-beta
205
- RegistrationsBaseUrl/3.0.0-rc
206
- RegistrationsBaseUrl/3.4.0
207
- RegistrationsBaseUrl/3.6.0
208
- )
209
- metadata
210
- .fetch("resources", [])
211
- .find { |r| allowed_registration_types.find { |s| r.fetch("@type") == s } }
212
- &.fetch("@id")
213
- end
214
-
215
- sig { params(metadata: T::Hash[String, T::Array[T::Hash[String, T.untyped]]]).returns(T.nilable(String)) }
216
- def search_url_from_v3_metadata(metadata)
217
- # allowable values from here: https://learn.microsoft.com/en-us/nuget/api/search-query-service-resource#versioning
218
- allowed_search_types = %w(
219
- SearchQueryService
220
- SearchQueryService/3.0.0-beta
221
- SearchQueryService/3.0.0-rc
222
- SearchQueryService/3.5.0
223
- )
224
- metadata
225
- .fetch("resources", [])
226
- .find { |r| allowed_search_types.find { |s| r.fetch("@type") == s } }
227
- &.fetch("@id")
228
- end
229
-
230
- sig do
231
- params(
232
- response: Excon::Response,
233
- repo_details: T::Hash[Symbol, T.untyped]
234
- )
235
- .returns(T::Hash[Symbol, T.untyped])
236
- end
237
- def build_v2_url(response, repo_details)
238
- doc = Nokogiri::XML(response.body)
239
-
240
- doc.remove_namespaces!
241
- base_url = doc.at_xpath("service")&.attributes
242
- &.fetch("base", nil)&.value
243
-
244
- base_url ||= repo_details.fetch(:url)
245
-
246
- {
247
- base_url: base_url,
248
- repository_url: base_url,
249
- versions_url: File.join(
250
- base_url.delete_suffix("/"),
251
- "FindPackagesById()?id='#{dependency.name}'"
252
- ),
253
- auth_header: auth_header_for_token(repo_details.fetch(:token)),
254
- repository_type: "v2"
255
- }
256
- end
257
-
258
- sig { params(response: Excon::Response, details: T::Hash[Symbol, T.untyped]).void }
259
- def check_repo_response(response, details)
260
- return unless [401, 402, 403].include?(response.status)
261
- raise if details.fetch(:url) == DEFAULT_REPOSITORY_URL
262
-
263
- raise PrivateSourceAuthenticationFailure, details.fetch(:url)
264
- end
265
-
266
- sig { params(repo_metadata_url: String).returns(T.noreturn) }
267
- def handle_timeout(repo_metadata_url:)
268
- raise if repo_metadata_url == DEFAULT_REPOSITORY_URL
269
-
270
- raise PrivateSourceTimedOut, repo_metadata_url
271
- end
272
-
273
- sig { returns(T::Array[T::Hash[Symbol, String]]) }
274
- def credential_repositories
275
- @credential_repositories ||=
276
- T.let(
277
- credentials
278
- .select { |cred| cred["type"] == "nuget_feed" && cred["url"] }
279
- .map { |c| { url: c.fetch("url"), token: c["token"] } },
280
- T.nilable(T::Array[T::Hash[Symbol, String]])
281
- )
282
- end
283
-
284
- sig { returns(T::Array[T::Hash[Symbol, String]]) }
285
- def config_file_repositories
286
- config_files.flat_map { |file| repos_from_config_file(file) }
287
- end
288
-
289
- # rubocop:disable Metrics/CyclomaticComplexity
290
- # rubocop:disable Metrics/PerceivedComplexity
291
- # rubocop:disable Metrics/MethodLength
292
- # rubocop:disable Metrics/AbcSize
293
- sig { params(config_file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, String]]) }
294
- def repos_from_config_file(config_file)
295
- doc = Nokogiri::XML(config_file.content)
296
- doc.remove_namespaces!
297
- # analogous to having a root config with the default repository
298
- base_sources = [{ url: DEFAULT_REPOSITORY_URL, key: "nuget.org" }]
299
-
300
- sources = T.let([], T::Array[T::Hash[Symbol, String]])
301
-
302
- # regular package sources
303
- doc.css("configuration > packageSources").children.each do |node|
304
- if node.name == "clear"
305
- sources.clear
306
- base_sources.clear
307
- else
308
- key = node.attribute("key")&.value&.strip || node.at_xpath("./key")&.content&.strip
309
- url = node.attribute("value")&.value&.strip || node.at_xpath("./value")&.content&.strip
310
- url = expand_windows_style_environment_variables(url) if url
311
-
312
- # if the path isn't absolute it's relative to the nuget.config file
313
- if url
314
- unless url.include?("://") || Pathname.new(url).absolute?
315
- url = Pathname(config_file.directory).join(url).to_path
316
- end
317
- sources << { url: url, key: key }
318
- end
319
- end
320
- end
321
-
322
- # signed package sources
323
- # https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file#trustedsigners-section
324
- doc.xpath("/configuration/trustedSigners/repository").each do |node|
325
- name = node.attribute("name")&.value&.strip
326
- service_index = node.attribute("serviceIndex")&.value&.strip
327
- sources << { url: service_index, key: name }
328
- end
329
-
330
- sources += base_sources # TODO: quirky overwrite behavior
331
- disabled_sources = disabled_sources(doc)
332
- sources.reject! do |s|
333
- disabled_sources.include?(s[:key])
334
- end
335
-
336
- sources.reject! do |s|
337
- known_urls = credential_repositories.map { |cr| cr.fetch(:url) }
338
- known_urls.include?(s.fetch(:url))
339
- end
340
-
341
- # filter out based on packageSourceMapping
342
- package_mapping_elements = doc.xpath("/configuration/packageSourceMapping/packageSource/package[@pattern]")
343
- matching_package_elements = package_mapping_elements.select do |package_element|
344
- pattern = package_element.attribute("pattern").value
345
-
346
- # reusing this function for a case insensitive GLOB pattern patch (e.g., "Microsoft.Azure.*")
347
- File.fnmatch(pattern, @dependency.name, File::FNM_CASEFOLD)
348
- end
349
- longest_matching_package_element = matching_package_elements.max_by do |package_element|
350
- package_element.attribute("pattern").value.length
351
- end
352
- matching_key = longest_matching_package_element&.parent&.attribute("key")&.value
353
- if matching_key
354
- # found a matching source, only keep that one
355
- sources.select! { |s| s.fetch(:key) == matching_key }
356
- end
357
-
358
- add_config_file_credentials(sources: sources, doc: doc)
359
- sources.each { |details| details.delete(:key) }
360
-
361
- sources
362
- end
363
- # rubocop:enable Metrics/AbcSize
364
- # rubocop:enable Metrics/MethodLength
365
- # rubocop:enable Metrics/PerceivedComplexity
366
- # rubocop:enable Metrics/CyclomaticComplexity
367
-
368
- sig { returns(T::Hash[Symbol, T.untyped]) }
369
- def default_repository_details
370
- RepositoryFinder.get_default_repository_details(dependency.name)
371
- end
372
-
373
- # rubocop:disable Metrics/PerceivedComplexity
374
- sig { params(doc: Nokogiri::XML::Document).returns(T::Array[String]) }
375
- def disabled_sources(doc)
376
- doc.css("configuration > disabledPackageSources > add").filter_map do |node|
377
- value = node.attribute("value")&.value ||
378
- node.at_xpath("./value")&.content
379
-
380
- if value&.strip&.downcase == "true"
381
- node.attribute("key")&.value&.strip ||
382
- node.at_xpath("./key")&.content&.strip
383
- end
384
- end
385
- end
386
- # rubocop:enable Metrics/PerceivedComplexity
387
-
388
- # rubocop:disable Metrics/PerceivedComplexity
389
- sig do
390
- params(
391
- sources: T::Array[T::Hash[Symbol, T.nilable(String)]],
392
- doc: Nokogiri::XML::Document
393
- )
394
- .void
395
- end
396
- def add_config_file_credentials(sources:, doc:)
397
- sources.each do |source_details|
398
- key = source_details.fetch(:key)
399
- next source_details[:token] = nil unless key
400
- next source_details[:token] = nil if key.match?(/^\d/)
401
-
402
- tag = RepositoryFinder.escape_source_name_to_element_name(key)
403
- creds_nodes = doc.css("configuration > packageSourceCredentials " \
404
- "> #{tag} > add")
405
-
406
- username =
407
- creds_nodes
408
- .find { |n| n.attribute("key")&.value == "Username" }
409
- &.attribute("value")&.value
410
- password =
411
- creds_nodes
412
- .find { |n| n.attribute("key")&.value == "ClearTextPassword" }
413
- &.attribute("value")&.value
414
-
415
- # NOTE: We have to look for plain text passwords, as we have no
416
- # way of decrypting encrypted passwords. For the same reason we
417
- # don't fetch API keys from the nuget.config at all.
418
- next source_details[:token] = nil unless username && password
419
-
420
- expanded_username = expand_windows_style_environment_variables(username)
421
- expanded_password = expand_windows_style_environment_variables(password)
422
- source_details[:token] = "#{expanded_username}:#{expanded_password}"
423
- rescue Nokogiri::XML::XPath::SyntaxError
424
- # Any non-ascii characters in the tag with cause a syntax error
425
- next source_details[:token] = nil
426
- end
427
- end
428
- # rubocop:enable Metrics/PerceivedComplexity
429
-
430
- sig { params(string: String).returns(String) }
431
- def expand_windows_style_environment_variables(string)
432
- # NuGet.Config files can have Windows-style environment variables that need to be replaced
433
- # https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file#using-environment-variables
434
- string.gsub(/%([^%]+)%/) do
435
- environment_variable_name = T.must(::Regexp.last_match(1))
436
- environment_variable_value = ENV.fetch(environment_variable_name, nil)
437
- if environment_variable_value
438
- environment_variable_value
439
- else
440
- # report that the variable couldn't be expanded, then replace it as-is
441
- Dependabot.logger.warn <<~WARN
442
- The variable '%#{environment_variable_name}%' could not be expanded in NuGet.Config
443
- WARN
444
- "%#{environment_variable_name}%"
445
- end
446
- end
447
- end
448
-
449
- sig { params(token: T.nilable(String)).returns(T::Hash[String, String]) }
450
- def auth_header_for_token(token)
451
- return {} unless token
452
-
453
- if token.include?(":")
454
- encoded_token = Base64.encode64(token).delete("\n")
455
- { "Authorization" => "Basic #{encoded_token}" }
456
- elsif Base64.decode64(token).ascii_only? &&
457
- Base64.decode64(token).include?(":")
458
- { "Authorization" => "Basic #{token.delete("\n")}" }
459
- else
460
- { "Authorization" => "Bearer #{token}" }
461
- end
462
- end
463
- end
464
- # rubocop:enable Metrics/ClassLength
465
- end
466
- end
@@ -1,34 +0,0 @@
1
- # typed: strong
2
- # frozen_string_literal: true
3
-
4
- require "sorbet-runtime"
5
-
6
- require "dependabot/update_checkers/base"
7
- require "dependabot/nuget/version"
8
- require "dependabot/nuget/requirement"
9
- require "dependabot/nuget/native_helpers"
10
- require "dependabot/shared_helpers"
11
-
12
- module Dependabot
13
- module Nuget
14
- class TfmComparer
15
- extend T::Sig
16
-
17
- sig { params(project_tfms: T::Array[String], package_tfms: T::Array[String]).returns(T::Boolean) }
18
- def self.are_frameworks_compatible?(project_tfms, package_tfms)
19
- return false if package_tfms.empty?
20
- return false if project_tfms.empty?
21
-
22
- key = "project_ftms:#{project_tfms.sort.join(',')}:package_tfms:#{package_tfms.sort.join(',')}".downcase
23
-
24
- @cached_framework_check ||= T.let({}, T.nilable(T::Hash[String, T::Boolean]))
25
- unless @cached_framework_check.key?(key)
26
- @cached_framework_check[key] =
27
- NativeHelpers.run_nuget_framework_check(project_tfms,
28
- package_tfms)
29
- end
30
- T.must(@cached_framework_check[key])
31
- end
32
- end
33
- end
34
- end
@@ -1,30 +0,0 @@
1
- # typed: strong
2
- # frozen_string_literal: true
3
-
4
- require "dependabot/nuget/discovery/discovery_json_reader"
5
-
6
- module Dependabot
7
- module Nuget
8
- class TfmFinder
9
- extend T::Sig
10
-
11
- sig { params(dependency: Dependency).returns(T::Array[String]) }
12
- def self.frameworks(dependency)
13
- discovery_json = DiscoveryJsonReader.discovery_json
14
- return [] unless discovery_json
15
-
16
- workspace = DiscoveryJsonReader.new(
17
- discovery_json: discovery_json
18
- ).workspace_discovery
19
- return [] unless workspace
20
-
21
- workspace.projects.select do |project|
22
- all_dependencies = project.dependencies + project.referenced_project_paths.flat_map do |ref|
23
- workspace.projects.find { |p| p.file_path == ref }&.dependencies || []
24
- end
25
- all_dependencies.any? { |d| d.name.casecmp?(dependency.name) }
26
- end.flat_map(&:target_frameworks).uniq
27
- end
28
- end
29
- end
30
- end