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,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