dependabot-npm_and_yarn 0.303.0 → 0.304.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.
- checksums.yaml +4 -4
- data/lib/dependabot/npm_and_yarn/package/package_details_fetcher.rb +161 -36
- data/lib/dependabot/npm_and_yarn/package/registry_finder.rb +122 -33
- data/lib/dependabot/npm_and_yarn/update_checker/latest_version_finder.rb +531 -29
- data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +270 -61
- data/lib/dependabot/npm_and_yarn/update_checker.rb +134 -21
- metadata +5 -5
@@ -1,9 +1,10 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "excon"
|
5
5
|
require "dependabot/npm_and_yarn/update_checker"
|
6
6
|
require "dependabot/registry_client"
|
7
|
+
require "sorbet-runtime"
|
7
8
|
|
8
9
|
module Dependabot
|
9
10
|
module NpmAndYarn
|
@@ -11,6 +12,8 @@ module Dependabot
|
|
11
12
|
class RegistryFinder
|
12
13
|
extend T::Sig
|
13
14
|
|
15
|
+
GLOBAL_NPM_REGISTRY = "https://registry.npmjs.org"
|
16
|
+
|
14
17
|
CENTRAL_REGISTRIES = %w(
|
15
18
|
https://registry.npmjs.org
|
16
19
|
http://registry.npmjs.org
|
@@ -23,6 +26,15 @@ module Dependabot
|
|
23
26
|
NPM_SCOPED_REGISTRY_REGEX = /^(?<scope>@[^:]+)\s*:registry\s*=\s*['"]?(?<registry>.*?)['"]?$/
|
24
27
|
YARN_SCOPED_REGISTRY_REGEX = /['"](?<scope>@[^:]+):registry['"]\s((['"](?<registry>.*)['"])|(?<registry>.*))/
|
25
28
|
|
29
|
+
sig do
|
30
|
+
params(
|
31
|
+
dependency: T.nilable(Dependabot::Dependency),
|
32
|
+
credentials: T::Array[Dependabot::Credential],
|
33
|
+
npmrc_file: T.nilable(Dependabot::DependencyFile),
|
34
|
+
yarnrc_file: T.nilable(Dependabot::DependencyFile),
|
35
|
+
yarnrc_yml_file: T.nilable(Dependabot::DependencyFile)
|
36
|
+
).void
|
37
|
+
end
|
26
38
|
def initialize(dependency:, credentials:, npmrc_file: nil,
|
27
39
|
yarnrc_file: nil, yarnrc_yml_file: nil)
|
28
40
|
@dependency = dependency
|
@@ -30,58 +42,91 @@ module Dependabot
|
|
30
42
|
@npmrc_file = npmrc_file
|
31
43
|
@yarnrc_file = yarnrc_file
|
32
44
|
@yarnrc_yml_file = yarnrc_yml_file
|
45
|
+
|
46
|
+
@registry = T.let(nil, T.nilable(String))
|
47
|
+
@first_registry_with_dependency_details = T.let(nil, T.nilable(String))
|
48
|
+
@known_registries = T.let([], T::Array[T::Hash[String, T.nilable(String)]])
|
49
|
+
@configured_global_registry = T.let(nil, T.nilable(String))
|
50
|
+
@global_registry = T.let(nil, T.nilable(String))
|
51
|
+
@parsed_yarnrc_yml = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
33
52
|
end
|
34
53
|
|
54
|
+
sig { returns(String) }
|
35
55
|
def registry
|
36
|
-
@registry
|
56
|
+
return @registry if @registry
|
57
|
+
|
58
|
+
@registry = locked_registry || configured_registry || first_registry_with_dependency_details
|
59
|
+
T.must(@registry)
|
37
60
|
end
|
38
61
|
|
62
|
+
sig { returns(T::Hash[String, String]) }
|
39
63
|
def auth_headers
|
40
64
|
auth_header_for(auth_token)
|
41
65
|
end
|
42
66
|
|
67
|
+
sig { returns(String) }
|
43
68
|
def dependency_url
|
44
69
|
"#{registry_url}/#{escaped_dependency_name}"
|
45
70
|
end
|
46
71
|
|
72
|
+
sig { params(version: T.any(String, Gem::Version)).returns(String) }
|
47
73
|
def tarball_url(version)
|
48
74
|
version_without_build_metadata = version.to_s.gsub(/\+.*/, "")
|
49
75
|
|
50
76
|
# Dependency name needs to be unescaped since tarball URLs don't always work with escaped slashes
|
51
|
-
"#{registry_url}/#{dependency
|
77
|
+
"#{registry_url}/#{dependency&.name}/-/#{scopeless_name}-#{version_without_build_metadata}.tgz"
|
52
78
|
end
|
53
79
|
|
80
|
+
sig { params(registry: String).returns(T::Boolean) }
|
54
81
|
def self.central_registry?(registry)
|
55
82
|
CENTRAL_REGISTRIES.any? do |r|
|
56
83
|
r.include?(registry)
|
57
84
|
end
|
58
85
|
end
|
59
86
|
|
87
|
+
sig { params(dependency_name: String).returns(T.nilable(String)) }
|
60
88
|
def registry_from_rc(dependency_name)
|
61
89
|
explicit_registry_from_rc(dependency_name) || global_registry
|
62
90
|
end
|
63
91
|
|
92
|
+
sig { returns(T::Boolean) }
|
93
|
+
def custom_registry?
|
94
|
+
return false if CENTRAL_REGISTRIES.include?(registry_url)
|
95
|
+
|
96
|
+
!(registry_url || "").match?(/registry\.npmjs\.(org|com)/)
|
97
|
+
end
|
98
|
+
|
64
99
|
private
|
65
100
|
|
101
|
+
sig { returns(T.nilable(Dependabot::Dependency)) }
|
66
102
|
attr_reader :dependency
|
103
|
+
|
104
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
67
105
|
attr_reader :credentials
|
106
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
68
107
|
attr_reader :npmrc_file
|
108
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
69
109
|
attr_reader :yarnrc_file
|
110
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
70
111
|
attr_reader :yarnrc_yml_file
|
71
112
|
|
113
|
+
sig { params(dependency_name: T.nilable(String)).returns(T.nilable(String)) }
|
72
114
|
def explicit_registry_from_rc(dependency_name)
|
73
|
-
if dependency_name
|
115
|
+
if dependency_name&.start_with?("@") && dependency_name.include?("/")
|
74
116
|
scope = dependency_name.split("/").first
|
75
|
-
scoped_registry(scope) || configured_global_registry
|
117
|
+
scoped_registry(T.must(scope)) || configured_global_registry
|
76
118
|
else
|
77
119
|
configured_global_registry
|
78
120
|
end
|
79
121
|
end
|
80
122
|
|
123
|
+
sig { returns(T.nilable(String)) }
|
81
124
|
def first_registry_with_dependency_details
|
125
|
+
return @first_registry_with_dependency_details if @first_registry_with_dependency_details
|
126
|
+
|
82
127
|
@first_registry_with_dependency_details ||=
|
83
128
|
known_registries.find do |details|
|
84
|
-
url = "#{details['registry']
|
129
|
+
url = "#{details['registry']&.gsub(%r{/+$}, '')}/#{escaped_dependency_name}"
|
85
130
|
url = "https://#{url}" unless url.start_with?("http")
|
86
131
|
response = Dependabot::RegistryClient.get(
|
87
132
|
url: url,
|
@@ -99,6 +144,7 @@ module Dependabot
|
|
99
144
|
@first_registry_with_dependency_details ||= global_registry.sub(%r{/+$}, "").sub(%r{^.*?//}, "")
|
100
145
|
end
|
101
146
|
|
147
|
+
sig { returns(T.nilable(String)) }
|
102
148
|
def registry_url
|
103
149
|
url =
|
104
150
|
if registry.start_with?("http")
|
@@ -106,7 +152,7 @@ module Dependabot
|
|
106
152
|
else
|
107
153
|
protocol =
|
108
154
|
if registry_source_url
|
109
|
-
registry_source_url
|
155
|
+
registry_source_url&.split("://")&.first
|
110
156
|
else
|
111
157
|
"https"
|
112
158
|
end
|
@@ -117,6 +163,7 @@ module Dependabot
|
|
117
163
|
url.gsub(%r{/+$}, "")
|
118
164
|
end
|
119
165
|
|
166
|
+
sig { params(token: T.nilable(String)).returns(T::Hash[String, String]) }
|
120
167
|
def auth_header_for(token)
|
121
168
|
return {} unless token
|
122
169
|
|
@@ -131,36 +178,45 @@ module Dependabot
|
|
131
178
|
end
|
132
179
|
end
|
133
180
|
|
181
|
+
sig { returns(T.nilable(String)) }
|
134
182
|
def auth_token
|
135
183
|
known_registries
|
136
184
|
.find { |cred| cred["registry"] == registry }
|
137
185
|
&.fetch("token", nil)
|
138
186
|
end
|
139
187
|
|
188
|
+
sig { returns(T.nilable(String)) }
|
140
189
|
def locked_registry
|
141
190
|
return unless registry_source_url
|
142
191
|
|
143
192
|
lockfile_registry =
|
144
193
|
registry_source_url
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
194
|
+
&.gsub("https://", "")
|
195
|
+
&.gsub("http://", "")
|
196
|
+
|
197
|
+
if lockfile_registry
|
198
|
+
detailed_registry =
|
199
|
+
known_registries
|
200
|
+
.find { |h| h["registry"]&.include?(lockfile_registry) }
|
201
|
+
&.fetch("registry")
|
202
|
+
end
|
151
203
|
|
152
204
|
detailed_registry || lockfile_registry
|
153
205
|
end
|
154
206
|
|
207
|
+
sig { returns(T.nilable(String)) }
|
155
208
|
def configured_registry
|
156
|
-
configured_registry_url = explicit_registry_from_rc(dependency
|
209
|
+
configured_registry_url = explicit_registry_from_rc(dependency&.name)
|
157
210
|
return unless configured_registry_url
|
158
211
|
|
159
212
|
normalize_configured_registry(configured_registry_url)
|
160
213
|
end
|
161
214
|
|
215
|
+
sig { returns(T::Array[T::Hash[String, T.nilable(String)]]) }
|
162
216
|
def known_registries
|
163
|
-
@known_registries
|
217
|
+
return @known_registries if @known_registries.any?
|
218
|
+
|
219
|
+
@known_registries =
|
164
220
|
begin
|
165
221
|
registries = []
|
166
222
|
registries += credentials
|
@@ -171,13 +227,15 @@ module Dependabot
|
|
171
227
|
|
172
228
|
unique_registries(registries)
|
173
229
|
end
|
230
|
+
@known_registries
|
174
231
|
end
|
175
232
|
|
233
|
+
sig { returns(T::Array[T::Hash[String, T.nilable(String)]]) }
|
176
234
|
def npmrc_registries
|
177
235
|
return [] unless npmrc_file
|
178
236
|
|
179
237
|
registries = []
|
180
|
-
npmrc_file
|
238
|
+
npmrc_file&.content&.scan(NPM_AUTH_TOKEN_REGEX) do
|
181
239
|
next if Regexp.last_match&.[](:registry)&.include?("${")
|
182
240
|
|
183
241
|
registry = T.must(Regexp.last_match)[:registry]
|
@@ -193,12 +251,17 @@ module Dependabot
|
|
193
251
|
registries += npmrc_global_registries
|
194
252
|
end
|
195
253
|
|
254
|
+
sig { returns(T::Array[T::Hash[String, T.nilable(String)]]) }
|
196
255
|
def yarnrc_registries
|
197
256
|
return [] unless yarnrc_file
|
198
257
|
|
199
258
|
yarnrc_global_registries
|
200
259
|
end
|
201
260
|
|
261
|
+
sig do
|
262
|
+
params(registries: T::Array[T::Hash[String, T.nilable(String)]])
|
263
|
+
.returns(T::Array[T::Hash[String, T.nilable(String)]])
|
264
|
+
end
|
202
265
|
def unique_registries(registries)
|
203
266
|
registries.uniq.reject do |registry|
|
204
267
|
next if registry["token"]
|
@@ -210,28 +273,31 @@ module Dependabot
|
|
210
273
|
end
|
211
274
|
end
|
212
275
|
|
276
|
+
sig { returns(String) }
|
213
277
|
def global_registry
|
214
|
-
return @global_registry if
|
278
|
+
return @global_registry if @global_registry
|
215
279
|
|
216
|
-
@global_registry
|
280
|
+
@global_registry = configured_global_registry || GLOBAL_NPM_REGISTRY
|
281
|
+
@global_registry
|
217
282
|
end
|
218
283
|
|
219
284
|
# rubocop:disable Metrics/PerceivedComplexity
|
285
|
+
sig { returns(T.nilable(String)) }
|
220
286
|
def configured_global_registry
|
221
|
-
return @configured_global_registry if
|
287
|
+
return @configured_global_registry if @configured_global_registry
|
222
288
|
|
223
289
|
@configured_global_registry = (npmrc_file && npmrc_global_registries.first&.fetch("url")) ||
|
224
290
|
(yarnrc_file && yarnrc_global_registries.first&.fetch("url"))
|
225
291
|
return @configured_global_registry if @configured_global_registry
|
226
292
|
|
227
293
|
if parsed_yarnrc_yml&.key?("npmRegistryServer")
|
228
|
-
return @configured_global_registry = parsed_yarnrc_yml["npmRegistryServer"]
|
294
|
+
return @configured_global_registry = T.must(parsed_yarnrc_yml)["npmRegistryServer"]
|
229
295
|
end
|
230
296
|
|
231
297
|
replaces_base = credentials.find { |cred| cred["type"] == "npm_registry" && cred.replaces_base? }
|
232
298
|
if replaces_base
|
233
299
|
registry = replaces_base["registry"]
|
234
|
-
registry = "https://#{registry}" unless registry
|
300
|
+
registry = "https://#{registry}" unless registry&.start_with?("http")
|
235
301
|
return @configured_global_registry = registry
|
236
302
|
end
|
237
303
|
|
@@ -239,31 +305,40 @@ module Dependabot
|
|
239
305
|
end
|
240
306
|
# rubocop:enable Metrics/PerceivedComplexity
|
241
307
|
|
308
|
+
sig { returns(T::Array[T::Hash[String, String]]) }
|
242
309
|
def npmrc_global_registries
|
243
310
|
global_rc_registries(npmrc_file, syntax: NPM_GLOBAL_REGISTRY_REGEX)
|
244
311
|
end
|
245
312
|
|
313
|
+
sig { returns(T::Array[T::Hash[String, String]]) }
|
246
314
|
def yarnrc_global_registries
|
247
315
|
global_rc_registries(yarnrc_file, syntax: YARN_GLOBAL_REGISTRY_REGEX)
|
248
316
|
end
|
249
317
|
|
318
|
+
sig { params(scope: String).returns(T.nilable(String)) }
|
250
319
|
def scoped_registry(scope)
|
251
320
|
scoped_rc_registry = scoped_rc_registry(npmrc_file, syntax: NPM_SCOPED_REGISTRY_REGEX, scope: scope) ||
|
252
321
|
scoped_rc_registry(yarnrc_file, syntax: YARN_SCOPED_REGISTRY_REGEX, scope: scope)
|
253
322
|
return scoped_rc_registry if scoped_rc_registry
|
254
323
|
|
255
324
|
if parsed_yarnrc_yml
|
256
|
-
yarn_berry_registry = parsed_yarnrc_yml
|
325
|
+
yarn_berry_registry = parsed_yarnrc_yml&.dig("npmScopes", scope.delete_prefix("@"), "npmRegistryServer")
|
257
326
|
return yarn_berry_registry if yarn_berry_registry
|
258
327
|
end
|
259
328
|
|
260
329
|
nil
|
261
330
|
end
|
262
331
|
|
332
|
+
sig do
|
333
|
+
params(
|
334
|
+
file: T.nilable(Dependabot::DependencyFile),
|
335
|
+
syntax: T.any(String, Regexp)
|
336
|
+
).returns(T::Array[T::Hash[String, String]])
|
337
|
+
end
|
263
338
|
def global_rc_registries(file, syntax:)
|
264
339
|
registries = []
|
265
340
|
|
266
|
-
file
|
341
|
+
file&.content&.scan(syntax) do
|
267
342
|
next if Regexp.last_match&.[](:registry)&.include?("${")
|
268
343
|
|
269
344
|
url = T.must(T.must(Regexp.last_match)[:registry]).strip
|
@@ -279,6 +354,13 @@ module Dependabot
|
|
279
354
|
registries
|
280
355
|
end
|
281
356
|
|
357
|
+
sig do
|
358
|
+
params(
|
359
|
+
file: T.nilable(Dependabot::DependencyFile),
|
360
|
+
syntax: T.any(String, Regexp),
|
361
|
+
scope: String
|
362
|
+
).returns(T.nilable(String))
|
363
|
+
end
|
282
364
|
def scoped_rc_registry(file, syntax:, scope:)
|
283
365
|
file&.content.to_s.scan(syntax) do
|
284
366
|
next if Regexp.last_match&.[](:registry)&.include?("${") || Regexp.last_match&.[](:scope) != scope
|
@@ -290,29 +372,36 @@ module Dependabot
|
|
290
372
|
end
|
291
373
|
|
292
374
|
# npm registries expect slashes to be escaped
|
375
|
+
sig { returns(T.nilable(String)) }
|
293
376
|
def escaped_dependency_name
|
294
|
-
dependency
|
377
|
+
dependency&.name&.gsub("/", "%2F")
|
295
378
|
end
|
296
379
|
|
380
|
+
sig { returns(T.nilable(String)) }
|
297
381
|
def scopeless_name
|
298
|
-
dependency
|
382
|
+
dependency&.name&.split("/")&.last
|
299
383
|
end
|
300
384
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
385
|
+
sig { returns(T.nilable(String)) }
|
386
|
+
def registry_source_url # rubocop:disable Metrics/PerceivedComplexity
|
387
|
+
sources = dependency&.requirements
|
388
|
+
&.map { |r| r.fetch(:source) }&.uniq&.compact
|
389
|
+
&.sort_by { |source| self.class.central_registry?(source[:url]) ? 1 : 0 }
|
305
390
|
|
306
|
-
sources
|
391
|
+
sources&.find { |s| s[:type] == "registry" }&.fetch(:url)
|
307
392
|
end
|
308
393
|
|
394
|
+
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
309
395
|
def parsed_yarnrc_yml
|
310
|
-
|
311
|
-
return
|
396
|
+
yarnrc_yml_file_content = yarnrc_yml_file&.content
|
397
|
+
return unless yarnrc_yml_file_content
|
398
|
+
return @parsed_yarnrc_yml if @parsed_yarnrc_yml
|
312
399
|
|
313
|
-
@parsed_yarnrc_yml = YAML.safe_load(
|
400
|
+
@parsed_yarnrc_yml = YAML.safe_load(yarnrc_yml_file_content)
|
401
|
+
@parsed_yarnrc_yml
|
314
402
|
end
|
315
403
|
|
404
|
+
sig { params(url: String).returns(String) }
|
316
405
|
def normalize_configured_registry(url)
|
317
406
|
url.sub(%r{/+$}, "")
|
318
407
|
.sub(%r{^.*?//}, "")
|