dependabot-uv 0.299.1
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.
- checksums.yaml +7 -0
- data/helpers/build +34 -0
- data/helpers/lib/__init__.py +0 -0
- data/helpers/lib/hasher.py +36 -0
- data/helpers/lib/parser.py +270 -0
- data/helpers/requirements.txt +13 -0
- data/helpers/run.py +22 -0
- data/lib/dependabot/uv/authed_url_builder.rb +31 -0
- data/lib/dependabot/uv/file_fetcher.rb +328 -0
- data/lib/dependabot/uv/file_parser/pipfile_files_parser.rb +192 -0
- data/lib/dependabot/uv/file_parser/pyproject_files_parser.rb +345 -0
- data/lib/dependabot/uv/file_parser/python_requirement_parser.rb +185 -0
- data/lib/dependabot/uv/file_parser/setup_file_parser.rb +193 -0
- data/lib/dependabot/uv/file_parser.rb +437 -0
- data/lib/dependabot/uv/file_updater/compile_file_updater.rb +576 -0
- data/lib/dependabot/uv/file_updater/pyproject_preparer.rb +124 -0
- data/lib/dependabot/uv/file_updater/requirement_file_updater.rb +73 -0
- data/lib/dependabot/uv/file_updater/requirement_replacer.rb +214 -0
- data/lib/dependabot/uv/file_updater.rb +105 -0
- data/lib/dependabot/uv/language.rb +76 -0
- data/lib/dependabot/uv/language_version_manager.rb +114 -0
- data/lib/dependabot/uv/metadata_finder.rb +186 -0
- data/lib/dependabot/uv/name_normaliser.rb +26 -0
- data/lib/dependabot/uv/native_helpers.rb +38 -0
- data/lib/dependabot/uv/package_manager.rb +54 -0
- data/lib/dependabot/uv/pip_compile_file_matcher.rb +38 -0
- data/lib/dependabot/uv/pipenv_runner.rb +108 -0
- data/lib/dependabot/uv/requirement.rb +163 -0
- data/lib/dependabot/uv/requirement_parser.rb +60 -0
- data/lib/dependabot/uv/update_checker/index_finder.rb +227 -0
- data/lib/dependabot/uv/update_checker/latest_version_finder.rb +297 -0
- data/lib/dependabot/uv/update_checker/pip_compile_version_resolver.rb +506 -0
- data/lib/dependabot/uv/update_checker/pip_version_resolver.rb +73 -0
- data/lib/dependabot/uv/update_checker/requirements_updater.rb +391 -0
- data/lib/dependabot/uv/update_checker.rb +317 -0
- data/lib/dependabot/uv/version.rb +321 -0
- data/lib/dependabot/uv.rb +35 -0
- metadata +306 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/uv/update_checker"
|
5
|
+
require "dependabot/uv/authed_url_builder"
|
6
|
+
require "dependabot/errors"
|
7
|
+
|
8
|
+
module Dependabot
|
9
|
+
module Uv
|
10
|
+
class UpdateChecker
|
11
|
+
class IndexFinder
|
12
|
+
PYPI_BASE_URL = "https://pypi.org/simple/"
|
13
|
+
ENVIRONMENT_VARIABLE_REGEX = /\$\{.+\}/
|
14
|
+
|
15
|
+
def initialize(dependency_files:, credentials:, dependency:)
|
16
|
+
@dependency_files = dependency_files
|
17
|
+
@credentials = credentials
|
18
|
+
@dependency = dependency
|
19
|
+
end
|
20
|
+
|
21
|
+
def index_urls
|
22
|
+
extra_index_urls =
|
23
|
+
config_variable_index_urls[:extra] +
|
24
|
+
pipfile_index_urls[:extra] +
|
25
|
+
requirement_file_index_urls[:extra] +
|
26
|
+
pip_conf_index_urls[:extra] +
|
27
|
+
pyproject_index_urls[:extra]
|
28
|
+
|
29
|
+
extra_index_urls = extra_index_urls.map do |url|
|
30
|
+
clean_check_and_remove_environment_variables(url)
|
31
|
+
end
|
32
|
+
|
33
|
+
# URL encode any `@` characters within registry URL creds.
|
34
|
+
# TODO: The test that fails if the `map` here is removed is likely a
|
35
|
+
# bug in Ruby's URI parser, and should be fixed there.
|
36
|
+
[main_index_url, *extra_index_urls].map do |url|
|
37
|
+
url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join
|
38
|
+
end.uniq
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :dependency_files
|
44
|
+
attr_reader :credentials
|
45
|
+
|
46
|
+
def main_index_url
|
47
|
+
url =
|
48
|
+
config_variable_index_urls[:main] ||
|
49
|
+
pipfile_index_urls[:main] ||
|
50
|
+
requirement_file_index_urls[:main] ||
|
51
|
+
pip_conf_index_urls[:main] ||
|
52
|
+
pyproject_index_urls[:main] ||
|
53
|
+
PYPI_BASE_URL
|
54
|
+
|
55
|
+
clean_check_and_remove_environment_variables(url)
|
56
|
+
end
|
57
|
+
|
58
|
+
def requirement_file_index_urls
|
59
|
+
urls = { main: nil, extra: [] }
|
60
|
+
|
61
|
+
requirements_files.each do |file|
|
62
|
+
if file.content.match?(/^--index-url\s+['"]?([^\s'"]+)['"]?/)
|
63
|
+
urls[:main] =
|
64
|
+
file.content.match(/^--index-url\s+['"]?([^\s'"]+)['"]?/)
|
65
|
+
.captures.first&.strip
|
66
|
+
end
|
67
|
+
urls[:extra] +=
|
68
|
+
file.content
|
69
|
+
.scan(/^--extra-index-url\s+['"]?([^\s'"]+)['"]?/)
|
70
|
+
.flatten
|
71
|
+
.map(&:strip)
|
72
|
+
end
|
73
|
+
|
74
|
+
urls
|
75
|
+
end
|
76
|
+
|
77
|
+
def pip_conf_index_urls
|
78
|
+
urls = { main: nil, extra: [] }
|
79
|
+
|
80
|
+
return urls unless pip_conf
|
81
|
+
|
82
|
+
content = pip_conf.content
|
83
|
+
|
84
|
+
if content.match?(/^index-url\s*=/x)
|
85
|
+
urls[:main] = content.match(/^index-url\s*=\s*(.+)/)
|
86
|
+
.captures.first
|
87
|
+
end
|
88
|
+
urls[:extra] += content.scan(/^extra-index-url\s*=(.+)/).flatten
|
89
|
+
|
90
|
+
urls
|
91
|
+
end
|
92
|
+
|
93
|
+
def pipfile_index_urls
|
94
|
+
urls = { main: nil, extra: [] }
|
95
|
+
|
96
|
+
return urls unless pipfile
|
97
|
+
|
98
|
+
pipfile_object = TomlRB.parse(pipfile.content)
|
99
|
+
|
100
|
+
urls[:main] = pipfile_object["source"]&.first&.fetch("url", nil)
|
101
|
+
|
102
|
+
pipfile_object["source"]&.each do |source|
|
103
|
+
urls[:extra] << source.fetch("url") if source["url"]
|
104
|
+
end
|
105
|
+
urls[:extra] = urls[:extra].uniq
|
106
|
+
|
107
|
+
urls
|
108
|
+
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
109
|
+
urls
|
110
|
+
end
|
111
|
+
|
112
|
+
def pyproject_index_urls
|
113
|
+
urls = { main: nil, extra: [] }
|
114
|
+
|
115
|
+
return urls unless pyproject
|
116
|
+
|
117
|
+
sources =
|
118
|
+
TomlRB.parse(pyproject.content).dig("tool", "poetry", "source") ||
|
119
|
+
[]
|
120
|
+
|
121
|
+
sources.each do |source|
|
122
|
+
# If source is PyPI, skip it, and let it pick the default URI
|
123
|
+
next if source["name"].casecmp?("PyPI")
|
124
|
+
|
125
|
+
if @dependency.all_sources.include?(source["name"])
|
126
|
+
# If dependency has specified this source, use it
|
127
|
+
return { main: source["url"], extra: [] }
|
128
|
+
elsif source["default"]
|
129
|
+
urls[:main] = source["url"]
|
130
|
+
elsif source["priority"] != "explicit"
|
131
|
+
# if source is not explicit, add it to extra
|
132
|
+
urls[:extra] << source["url"]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
urls[:extra] = urls[:extra].uniq
|
136
|
+
|
137
|
+
urls
|
138
|
+
rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
|
139
|
+
urls
|
140
|
+
end
|
141
|
+
|
142
|
+
def config_variable_index_urls
|
143
|
+
urls = { main: nil, extra: [] }
|
144
|
+
|
145
|
+
index_url_creds = credentials
|
146
|
+
.select { |cred| cred["type"] == "python_index" }
|
147
|
+
|
148
|
+
if (main_cred = index_url_creds.find(&:replaces_base?))
|
149
|
+
urls[:main] = AuthedUrlBuilder.authed_url(credential: main_cred)
|
150
|
+
end
|
151
|
+
|
152
|
+
urls[:extra] =
|
153
|
+
index_url_creds
|
154
|
+
.reject(&:replaces_base?)
|
155
|
+
.map { |cred| AuthedUrlBuilder.authed_url(credential: cred) }
|
156
|
+
|
157
|
+
urls
|
158
|
+
end
|
159
|
+
|
160
|
+
def clean_check_and_remove_environment_variables(url)
|
161
|
+
url = url.strip.gsub(%r{/*$}, "") + "/"
|
162
|
+
|
163
|
+
return authed_base_url(url) unless url.match?(ENVIRONMENT_VARIABLE_REGEX)
|
164
|
+
|
165
|
+
config_variable_urls =
|
166
|
+
[
|
167
|
+
config_variable_index_urls[:main],
|
168
|
+
*config_variable_index_urls[:extra]
|
169
|
+
]
|
170
|
+
.compact
|
171
|
+
.map { |u| u.strip.gsub(%r{/*$}, "") + "/" }
|
172
|
+
|
173
|
+
regexp = url
|
174
|
+
.sub(%r{(?<=://).+@}, "")
|
175
|
+
.sub(%r{https?://}, "")
|
176
|
+
.split(ENVIRONMENT_VARIABLE_REGEX)
|
177
|
+
.map { |part| Regexp.quote(part) }
|
178
|
+
.join(".+")
|
179
|
+
authed_url = config_variable_urls.find { |u| u.match?(regexp) }
|
180
|
+
return authed_url if authed_url
|
181
|
+
|
182
|
+
cleaned_url = url.gsub(%r{#{ENVIRONMENT_VARIABLE_REGEX}/?}o, "")
|
183
|
+
authed_url = authed_base_url(cleaned_url)
|
184
|
+
return authed_url if credential_for(cleaned_url)
|
185
|
+
|
186
|
+
raise PrivateSourceAuthenticationFailure, url
|
187
|
+
end
|
188
|
+
|
189
|
+
def authed_base_url(base_url)
|
190
|
+
cred = credential_for(base_url)
|
191
|
+
return base_url unless cred
|
192
|
+
|
193
|
+
AuthedUrlBuilder.authed_url(credential: cred).gsub(%r{/*$}, "") + "/"
|
194
|
+
end
|
195
|
+
|
196
|
+
def credential_for(url)
|
197
|
+
credentials
|
198
|
+
.select { |c| c["type"] == "python_index" }
|
199
|
+
.find do |c|
|
200
|
+
cred_url = c.fetch("index-url").gsub(%r{/*$}, "") + "/"
|
201
|
+
cred_url.include?(url)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def pip_conf
|
206
|
+
dependency_files.find { |f| f.name == "pip.conf" }
|
207
|
+
end
|
208
|
+
|
209
|
+
def pipfile
|
210
|
+
dependency_files.find { |f| f.name == "Pipfile" }
|
211
|
+
end
|
212
|
+
|
213
|
+
def pyproject
|
214
|
+
dependency_files.find { |f| f.name == "pyproject.toml" }
|
215
|
+
end
|
216
|
+
|
217
|
+
def requirements_files
|
218
|
+
dependency_files.select { |f| f.name.match?(/requirements/x) }
|
219
|
+
end
|
220
|
+
|
221
|
+
def pip_compile_files
|
222
|
+
dependency_files.select { |f| f.name.end_with?(".in") }
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "cgi"
|
5
|
+
require "excon"
|
6
|
+
require "nokogiri"
|
7
|
+
require "sorbet-runtime"
|
8
|
+
|
9
|
+
require "dependabot/dependency"
|
10
|
+
require "dependabot/uv/update_checker"
|
11
|
+
require "dependabot/update_checkers/version_filters"
|
12
|
+
require "dependabot/registry_client"
|
13
|
+
require "dependabot/uv/authed_url_builder"
|
14
|
+
require "dependabot/uv/name_normaliser"
|
15
|
+
|
16
|
+
module Dependabot
|
17
|
+
module Uv
|
18
|
+
class UpdateChecker
|
19
|
+
class LatestVersionFinder
|
20
|
+
extend T::Sig
|
21
|
+
|
22
|
+
require_relative "index_finder"
|
23
|
+
|
24
|
+
def initialize(dependency:, dependency_files:, credentials:,
|
25
|
+
ignored_versions:, raise_on_ignored: false,
|
26
|
+
security_advisories:)
|
27
|
+
@dependency = dependency
|
28
|
+
@dependency_files = dependency_files
|
29
|
+
@credentials = credentials
|
30
|
+
@ignored_versions = ignored_versions
|
31
|
+
@raise_on_ignored = raise_on_ignored
|
32
|
+
@security_advisories = security_advisories
|
33
|
+
end
|
34
|
+
|
35
|
+
def latest_version(python_version: nil)
|
36
|
+
@latest_version ||=
|
37
|
+
fetch_latest_version(python_version: python_version)
|
38
|
+
end
|
39
|
+
|
40
|
+
def latest_version_with_no_unlock(python_version: nil)
|
41
|
+
@latest_version_with_no_unlock ||=
|
42
|
+
fetch_latest_version_with_no_unlock(python_version: python_version)
|
43
|
+
end
|
44
|
+
|
45
|
+
def lowest_security_fix_version(python_version: nil)
|
46
|
+
@lowest_security_fix_version ||=
|
47
|
+
fetch_lowest_security_fix_version(python_version: python_version)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :dependency
|
53
|
+
attr_reader :dependency_files
|
54
|
+
attr_reader :credentials
|
55
|
+
attr_reader :ignored_versions
|
56
|
+
attr_reader :security_advisories
|
57
|
+
|
58
|
+
def fetch_latest_version(python_version:)
|
59
|
+
versions = available_versions
|
60
|
+
versions = filter_yanked_versions(versions)
|
61
|
+
versions = filter_unsupported_versions(versions, python_version)
|
62
|
+
versions = filter_prerelease_versions(versions)
|
63
|
+
versions = filter_ignored_versions(versions)
|
64
|
+
versions.max
|
65
|
+
end
|
66
|
+
|
67
|
+
def fetch_latest_version_with_no_unlock(python_version:)
|
68
|
+
versions = available_versions
|
69
|
+
versions = filter_yanked_versions(versions)
|
70
|
+
versions = filter_unsupported_versions(versions, python_version)
|
71
|
+
versions = filter_prerelease_versions(versions)
|
72
|
+
versions = filter_ignored_versions(versions)
|
73
|
+
versions = filter_out_of_range_versions(versions)
|
74
|
+
versions.max
|
75
|
+
end
|
76
|
+
|
77
|
+
def fetch_lowest_security_fix_version(python_version:)
|
78
|
+
versions = available_versions
|
79
|
+
versions = filter_yanked_versions(versions)
|
80
|
+
versions = filter_unsupported_versions(versions, python_version)
|
81
|
+
versions = filter_prerelease_versions(versions)
|
82
|
+
versions = Dependabot::UpdateCheckers::VersionFilters.filter_vulnerable_versions(versions,
|
83
|
+
security_advisories)
|
84
|
+
versions = filter_ignored_versions(versions)
|
85
|
+
versions = filter_lower_versions(versions)
|
86
|
+
|
87
|
+
versions.min
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { params(versions_array: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
91
|
+
def filter_yanked_versions(versions_array)
|
92
|
+
filtered = versions_array.reject { |details| details.fetch(:yanked) }
|
93
|
+
if versions_array.count > filtered.count
|
94
|
+
Dependabot.logger.info("Filtered out #{versions_array.count - filtered.count} yanked versions")
|
95
|
+
end
|
96
|
+
filtered
|
97
|
+
end
|
98
|
+
|
99
|
+
sig do
|
100
|
+
params(versions_array: T::Array[T.untyped], python_version: T.nilable(T.any(String, Version)))
|
101
|
+
.returns(T::Array[T.untyped])
|
102
|
+
end
|
103
|
+
def filter_unsupported_versions(versions_array, python_version)
|
104
|
+
filtered = versions_array.filter_map do |details|
|
105
|
+
python_requirement = details.fetch(:python_requirement)
|
106
|
+
next details.fetch(:version) unless python_version
|
107
|
+
next details.fetch(:version) unless python_requirement
|
108
|
+
next unless python_requirement.satisfied_by?(python_version)
|
109
|
+
|
110
|
+
details.fetch(:version)
|
111
|
+
end
|
112
|
+
if versions_array.count > filtered.count
|
113
|
+
delta = versions_array.count - filtered.count
|
114
|
+
Dependabot.logger.info("Filtered out #{delta} unsupported Python #{python_version} versions")
|
115
|
+
end
|
116
|
+
filtered
|
117
|
+
end
|
118
|
+
|
119
|
+
sig { params(versions_array: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
120
|
+
def filter_prerelease_versions(versions_array)
|
121
|
+
return versions_array if wants_prerelease?
|
122
|
+
|
123
|
+
filtered = versions_array.reject(&:prerelease?)
|
124
|
+
|
125
|
+
if versions_array.count > filtered.count
|
126
|
+
Dependabot.logger.info("Filtered out #{versions_array.count - filtered.count} pre-release versions")
|
127
|
+
end
|
128
|
+
|
129
|
+
filtered
|
130
|
+
end
|
131
|
+
|
132
|
+
sig { params(versions_array: T::Array[T.untyped]).returns(T::Array[T.untyped]) }
|
133
|
+
def filter_ignored_versions(versions_array)
|
134
|
+
filtered = versions_array
|
135
|
+
.reject { |v| ignore_requirements.any? { |r| r.satisfied_by?(v) } }
|
136
|
+
if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(versions_array).any?
|
137
|
+
raise Dependabot::AllVersionsIgnored
|
138
|
+
end
|
139
|
+
|
140
|
+
if versions_array.count > filtered.count
|
141
|
+
Dependabot.logger.info("Filtered out #{versions_array.count - filtered.count} ignored versions")
|
142
|
+
end
|
143
|
+
filtered
|
144
|
+
end
|
145
|
+
|
146
|
+
def filter_lower_versions(versions_array)
|
147
|
+
return versions_array unless dependency.numeric_version
|
148
|
+
|
149
|
+
versions_array.select { |version| version > dependency.numeric_version }
|
150
|
+
end
|
151
|
+
|
152
|
+
def filter_out_of_range_versions(versions_array)
|
153
|
+
reqs = dependency.requirements.filter_map do |r|
|
154
|
+
requirement_class.requirements_array(r.fetch(:requirement))
|
155
|
+
end
|
156
|
+
|
157
|
+
versions_array
|
158
|
+
.select { |v| reqs.all? { |r| r.any? { |o| o.satisfied_by?(v) } } }
|
159
|
+
end
|
160
|
+
|
161
|
+
def wants_prerelease?
|
162
|
+
return version_class.new(dependency.version).prerelease? if dependency.version
|
163
|
+
|
164
|
+
dependency.requirements.any? do |req|
|
165
|
+
reqs = (req.fetch(:requirement) || "").split(",").map(&:strip)
|
166
|
+
reqs.any? { |r| r.match?(/[A-Za-z]/) }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# See https://www.python.org/dev/peps/pep-0503/ for details of the
|
171
|
+
# Simple Repository API we use here.
|
172
|
+
def available_versions
|
173
|
+
@available_versions ||=
|
174
|
+
index_urls.flat_map do |index_url|
|
175
|
+
validate_index(index_url)
|
176
|
+
|
177
|
+
sanitized_url = index_url.gsub(%r{(?<=//).*(?=@)}, "redacted")
|
178
|
+
|
179
|
+
index_response = registry_response_for_dependency(index_url)
|
180
|
+
if index_response.status == 401 || index_response.status == 403
|
181
|
+
registry_index_response = registry_index_response(index_url)
|
182
|
+
|
183
|
+
if registry_index_response.status == 401 || registry_index_response.status == 403
|
184
|
+
raise PrivateSourceAuthenticationFailure, sanitized_url
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
version_links = []
|
189
|
+
index_response.body.scan(%r{<a\s.*?>.*?</a>}m) do
|
190
|
+
details = version_details_from_link(Regexp.last_match.to_s)
|
191
|
+
version_links << details if details
|
192
|
+
end
|
193
|
+
|
194
|
+
version_links.compact
|
195
|
+
rescue Excon::Error::Timeout, Excon::Error::Socket
|
196
|
+
raise if MAIN_PYPI_INDEXES.include?(index_url)
|
197
|
+
|
198
|
+
raise PrivateSourceTimedOut, sanitized_url
|
199
|
+
rescue URI::InvalidURIError
|
200
|
+
raise DependencyFileNotResolvable, "Invalid URL: #{sanitized_url}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
205
|
+
def version_details_from_link(link)
|
206
|
+
doc = Nokogiri::XML(link)
|
207
|
+
filename = doc.at_css("a")&.content
|
208
|
+
url = doc.at_css("a")&.attributes&.fetch("href", nil)&.value
|
209
|
+
return unless filename&.match?(name_regex) || url&.match?(name_regex)
|
210
|
+
|
211
|
+
version = get_version_from_filename(filename)
|
212
|
+
return unless version_class.correct?(version)
|
213
|
+
|
214
|
+
{
|
215
|
+
version: version_class.new(version),
|
216
|
+
python_requirement: build_python_requirement_from_link(link),
|
217
|
+
yanked: link&.include?("data-yanked")
|
218
|
+
}
|
219
|
+
end
|
220
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
221
|
+
|
222
|
+
def get_version_from_filename(filename)
|
223
|
+
filename
|
224
|
+
.gsub(/#{name_regex}-/i, "")
|
225
|
+
.split(/-|\.tar\.|\.zip|\.whl/)
|
226
|
+
.first
|
227
|
+
end
|
228
|
+
|
229
|
+
def build_python_requirement_from_link(link)
|
230
|
+
req_string = Nokogiri::XML(link)
|
231
|
+
.at_css("a")
|
232
|
+
&.attribute("data-requires-python")
|
233
|
+
&.content
|
234
|
+
|
235
|
+
return unless req_string
|
236
|
+
|
237
|
+
requirement_class.new(CGI.unescapeHTML(req_string))
|
238
|
+
rescue Gem::Requirement::BadRequirementError
|
239
|
+
nil
|
240
|
+
end
|
241
|
+
|
242
|
+
def index_urls
|
243
|
+
@index_urls ||=
|
244
|
+
IndexFinder.new(
|
245
|
+
dependency_files: dependency_files,
|
246
|
+
credentials: credentials,
|
247
|
+
dependency: dependency
|
248
|
+
).index_urls
|
249
|
+
end
|
250
|
+
|
251
|
+
def registry_response_for_dependency(index_url)
|
252
|
+
Dependabot::RegistryClient.get(
|
253
|
+
url: index_url + normalised_name + "/",
|
254
|
+
headers: { "Accept" => "text/html" }
|
255
|
+
)
|
256
|
+
end
|
257
|
+
|
258
|
+
def registry_index_response(index_url)
|
259
|
+
Dependabot::RegistryClient.get(
|
260
|
+
url: index_url,
|
261
|
+
headers: { "Accept" => "text/html" }
|
262
|
+
)
|
263
|
+
end
|
264
|
+
|
265
|
+
def ignore_requirements
|
266
|
+
ignored_versions.flat_map { |req| requirement_class.requirements_array(req) }
|
267
|
+
end
|
268
|
+
|
269
|
+
def normalised_name
|
270
|
+
NameNormaliser.normalise(dependency.name)
|
271
|
+
end
|
272
|
+
|
273
|
+
def name_regex
|
274
|
+
parts = normalised_name.split(/[\s_.-]/).map { |n| Regexp.quote(n) }
|
275
|
+
/#{parts.join("[\s_.-]")}/i
|
276
|
+
end
|
277
|
+
|
278
|
+
def version_class
|
279
|
+
dependency.version_class
|
280
|
+
end
|
281
|
+
|
282
|
+
def requirement_class
|
283
|
+
dependency.requirement_class
|
284
|
+
end
|
285
|
+
|
286
|
+
def validate_index(index_url)
|
287
|
+
sanitized_url = index_url.gsub(%r{(?<=//).*(?=@)}, "redacted")
|
288
|
+
|
289
|
+
return if index_url&.match?(URI::DEFAULT_PARSER.regexp[:ABS_URI])
|
290
|
+
|
291
|
+
raise Dependabot::DependencyFileNotResolvable,
|
292
|
+
"Invalid URL: #{sanitized_url}"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|