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