dependabot-nuget 0.288.0 → 0.290.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,196 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "sorbet-runtime"
5
-
6
- require "dependabot/update_checkers/base"
7
- require "dependabot/nuget/file_parser"
8
-
9
- module Dependabot
10
- module Nuget
11
- class UpdateChecker < Dependabot::UpdateCheckers::Base
12
- class PropertyUpdater
13
- extend T::Sig
14
-
15
- require_relative "version_finder"
16
- require_relative "requirements_updater"
17
- require_relative "dependency_finder"
18
-
19
- sig do
20
- params(
21
- dependency: Dependabot::Dependency,
22
- dependency_files: T::Array[Dependabot::DependencyFile],
23
- credentials: T::Array[Dependabot::Credential],
24
- target_version_details: T.nilable(T::Hash[Symbol, String]),
25
- ignored_versions: T::Array[String],
26
- repo_contents_path: T.nilable(String),
27
- raise_on_ignored: T::Boolean
28
- ).void
29
- end
30
- def initialize(dependency:, dependency_files:, credentials:,
31
- target_version_details:, ignored_versions:,
32
- repo_contents_path:, raise_on_ignored: false)
33
- @dependency = dependency
34
- @dependency_files = dependency_files
35
- @credentials = credentials
36
- @ignored_versions = ignored_versions
37
- @raise_on_ignored = raise_on_ignored
38
- @target_version = T.let(
39
- target_version_details&.fetch(:version),
40
- T.nilable(T.any(String, Dependabot::Nuget::Version))
41
- )
42
- @source_details = T.let(
43
- target_version_details&.slice(:nuspec_url, :repo_url, :source_url),
44
- T.nilable(T::Hash[Symbol, String])
45
- )
46
- @repo_contents_path = repo_contents_path
47
- end
48
-
49
- sig { returns(T::Boolean) }
50
- def update_possible?
51
- return false unless target_version
52
-
53
- @update_possible ||= T.let(
54
- dependencies_using_property.all? do |dep|
55
- versions = VersionFinder.new(
56
- dependency: dep,
57
- dependency_files: dependency_files,
58
- credentials: credentials,
59
- ignored_versions: ignored_versions,
60
- raise_on_ignored: @raise_on_ignored,
61
- security_advisories: [],
62
- repo_contents_path: repo_contents_path
63
- ).versions.map { |v| v.fetch(:version) }
64
-
65
- versions.include?(target_version) || versions.none?
66
- end,
67
- T.nilable(T::Boolean)
68
- )
69
- end
70
-
71
- sig { returns(T::Array[Dependabot::Dependency]) }
72
- def updated_dependencies
73
- raise "Update not possible!" unless update_possible?
74
-
75
- @updated_dependencies ||= T.let(
76
- begin
77
- dependencies = T.let({}, T::Hash[String, Dependabot::Dependency])
78
-
79
- dependencies_using_property.each do |dep|
80
- # Only keep one copy of each dependency, the one with the highest target version.
81
- visited_dependency = dependencies[dep.name.downcase]
82
- next unless visited_dependency.nil? || T.must(visited_dependency.numeric_version) < target_version
83
-
84
- updated_dependency = Dependency.new(
85
- name: dep.name,
86
- version: target_version.to_s,
87
- requirements: updated_requirements(dep),
88
- previous_version: dep.version,
89
- previous_requirements: dep.requirements,
90
- package_manager: dep.package_manager
91
- )
92
- dependencies[updated_dependency.name.downcase] = updated_dependency
93
- # Add peer dependencies to the list of updated dependencies.
94
- process_updated_peer_dependencies(updated_dependency, dependencies)
95
- end
96
-
97
- dependencies.map { |_, dependency| dependency }
98
- end,
99
- T.nilable(T::Array[Dependabot::Dependency])
100
- )
101
- end
102
-
103
- private
104
-
105
- sig { returns(Dependabot::Dependency) }
106
- attr_reader :dependency
107
-
108
- sig { returns(T::Array[Dependabot::DependencyFile]) }
109
- attr_reader :dependency_files
110
-
111
- sig { returns(T.nilable(T.any(String, Dependabot::Nuget::Version))) }
112
- attr_reader :target_version
113
-
114
- sig { returns(T.nilable(T::Hash[Symbol, String])) }
115
- attr_reader :source_details
116
-
117
- sig { returns(T::Array[Dependabot::Credential]) }
118
- attr_reader :credentials
119
-
120
- sig { returns(T::Array[String]) }
121
- attr_reader :ignored_versions
122
-
123
- sig { returns(T.nilable(String)) }
124
- attr_reader :repo_contents_path
125
-
126
- sig do
127
- params(
128
- dependency: Dependabot::Dependency,
129
- dependencies: T::Hash[String, Dependabot::Dependency]
130
- )
131
- .returns(T::Array[Dependabot::Dependency])
132
- end
133
- def process_updated_peer_dependencies(dependency, dependencies)
134
- DependencyFinder.new(
135
- dependency: dependency,
136
- dependency_files: dependency_files,
137
- ignored_versions: ignored_versions,
138
- credentials: credentials,
139
- repo_contents_path: repo_contents_path
140
- ).updated_peer_dependencies.each do |peer_dependency|
141
- # Only keep one copy of each dependency, the one with the highest target version.
142
- visited_dependency = dependencies[peer_dependency.name.downcase]
143
- unless visited_dependency.nil? ||
144
- T.must(visited_dependency.numeric_version) < peer_dependency.numeric_version
145
- next
146
- end
147
-
148
- dependencies[peer_dependency.name.downcase] = peer_dependency
149
- end
150
- end
151
-
152
- sig { returns(T::Array[Dependabot::Dependency]) }
153
- def dependencies_using_property
154
- @dependencies_using_property ||=
155
- T.let(
156
- Nuget::FileParser.new(
157
- dependency_files: dependency_files,
158
- repo_contents_path: repo_contents_path,
159
- source: nil
160
- ).parse.select do |dep|
161
- dep.requirements.any? do |r|
162
- r.dig(:metadata, :property_name) == property_name
163
- end
164
- end,
165
- T.nilable(T::Array[Dependabot::Dependency])
166
- )
167
- end
168
-
169
- sig { returns(String) }
170
- def property_name
171
- @property_name ||= T.let(
172
- dependency.requirements
173
- .find { |r| r.dig(:metadata, :property_name) }
174
- &.dig(:metadata, :property_name),
175
- T.nilable(String)
176
- )
177
-
178
- raise "No requirement with a property name!" unless @property_name
179
-
180
- @property_name
181
- end
182
-
183
- sig { params(dep: Dependabot::Dependency).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
184
- def updated_requirements(dep)
185
- @updated_requirements ||= T.let({}, T.nilable(T::Hash[String, T::Array[T::Hash[Symbol, T.untyped]]]))
186
- @updated_requirements[dep.name] ||=
187
- RequirementsUpdater.new(
188
- requirements: dep.requirements,
189
- latest_version: target_version,
190
- source_details: source_details
191
- ).updated_requirements
192
- end
193
- end
194
- end
195
- end
196
- end
@@ -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