dependabot-npm_and_yarn 0.309.0 → 0.311.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 (24) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/npm_and_yarn/file_parser/json_lock.rb +1 -1
  3. data/lib/dependabot/npm_and_yarn/file_parser/pnpm_lock.rb +60 -19
  4. data/lib/dependabot/npm_and_yarn/file_parser/yarn_lock.rb +37 -16
  5. data/lib/dependabot/npm_and_yarn/file_updater/bun_lockfile_updater.rb +60 -19
  6. data/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +4 -50
  7. data/lib/dependabot/npm_and_yarn/file_updater/package_json_preparer.rb +16 -4
  8. data/lib/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater.rb +161 -51
  9. data/lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb +211 -60
  10. data/lib/dependabot/npm_and_yarn/file_updater.rb +1 -1
  11. data/lib/dependabot/npm_and_yarn/helpers.rb +12 -59
  12. data/lib/dependabot/npm_and_yarn/metadata_finder.rb +64 -15
  13. data/lib/dependabot/npm_and_yarn/native_helpers.rb +8 -1
  14. data/lib/dependabot/npm_and_yarn/package/registry_finder.rb +1 -1
  15. data/lib/dependabot/npm_and_yarn/requirement.rb +23 -9
  16. data/lib/dependabot/npm_and_yarn/update_checker/conflicting_dependency_resolver.rb +36 -9
  17. data/lib/dependabot/npm_and_yarn/update_checker/dependency_files_builder.rb +82 -24
  18. data/lib/dependabot/npm_and_yarn/update_checker/library_detector.rb +35 -5
  19. data/lib/dependabot/npm_and_yarn/update_checker/requirements_updater.rb +61 -24
  20. data/lib/dependabot/npm_and_yarn/update_checker/subdependency_version_resolver.rb +66 -37
  21. data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +1 -1
  22. data/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb +60 -7
  23. data/lib/dependabot/npm_and_yarn/update_checker.rb +18 -8
  24. metadata +9 -9
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/npm_and_yarn/helpers"
@@ -9,24 +9,47 @@ require "dependabot/shared_helpers"
9
9
  module Dependabot
10
10
  module NpmAndYarn
11
11
  class FileUpdater < Dependabot::FileUpdaters::Base
12
+ # rubocop:disable Metrics/ClassLength
12
13
  class PnpmLockfileUpdater
14
+ extend T::Sig
15
+
13
16
  require_relative "npmrc_builder"
14
17
  require_relative "package_json_updater"
15
18
 
19
+ sig do
20
+ params(
21
+ dependencies: T::Array[Dependabot::Dependency],
22
+ dependency_files: T::Array[Dependabot::DependencyFile],
23
+ repo_contents_path: T.nilable(String),
24
+ credentials: T::Array[Dependabot::Credential]
25
+ ).void
26
+ end
16
27
  def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
17
28
  @dependencies = dependencies
18
29
  @dependency_files = dependency_files
19
30
  @repo_contents_path = repo_contents_path
20
31
  @credentials = credentials
21
- @error_handler = PnpmErrorHandler.new(
22
- dependencies: dependencies,
23
- dependency_files: dependency_files
32
+ @error_handler = T.let(
33
+ PnpmErrorHandler.new(
34
+ dependencies: dependencies,
35
+ dependency_files: dependency_files
36
+ ),
37
+ PnpmErrorHandler
24
38
  )
25
39
  end
26
40
 
41
+ sig do
42
+ params(
43
+ pnpm_lock: Dependabot::DependencyFile,
44
+ updated_pnpm_workspace_content: T.nilable(T::Hash[String, T.nilable(String)])
45
+ ).returns(String)
46
+ end
27
47
  def updated_pnpm_lock_content(pnpm_lock, updated_pnpm_workspace_content: nil)
28
- @updated_pnpm_lock_content ||= {}
29
- return @updated_pnpm_lock_content[pnpm_lock.name] if @updated_pnpm_lock_content[pnpm_lock.name]
48
+ @updated_pnpm_lock_content ||= T.let(
49
+ {},
50
+ T.nilable(T::Hash[String, String])
51
+ )
52
+ return T.must(@updated_pnpm_lock_content[pnpm_lock.name]) if @updated_pnpm_lock_content[pnpm_lock.name]
30
53
 
31
54
  new_content = run_pnpm_update(
32
55
  pnpm_lock: pnpm_lock,
@@ -39,10 +62,19 @@ module Dependabot
39
62
 
40
63
  private
41
64
 
65
+ sig { returns(T::Array[Dependabot::Dependency]) }
42
66
  attr_reader :dependencies
67
+
68
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
43
69
  attr_reader :dependency_files
70
+
71
+ sig { returns(T.nilable(String)) }
44
72
  attr_reader :repo_contents_path
73
+
74
+ sig { returns(T::Array[Dependabot::Credential]) }
45
75
  attr_reader :credentials
76
+
77
+ sig { returns(PnpmErrorHandler) }
46
78
  attr_reader :error_handler
47
79
 
48
80
  IRRESOLVABLE_PACKAGE = "ERR_PNPM_NO_MATCHING_VERSION"
@@ -103,6 +135,13 @@ module Dependabot
103
135
  # Peer dependencies configuration error
104
136
  ERR_PNPM_PEER_DEP_ISSUES = /ERR_PNPM_PEER_DEP_ISSUES/
105
137
 
138
+ sig do
139
+ params(
140
+ pnpm_lock: Dependabot::DependencyFile,
141
+ updated_pnpm_workspace_content: T.nilable(T::Hash[String, T.nilable(String)])
142
+ )
143
+ .returns(String)
144
+ end
106
145
  def run_pnpm_update(pnpm_lock:, updated_pnpm_workspace_content: nil)
107
146
  SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
108
147
  File.write(".npmrc", npmrc_content(pnpm_lock))
@@ -122,6 +161,7 @@ module Dependabot
122
161
  end
123
162
  end
124
163
 
164
+ sig { returns(T.nilable(String)) }
125
165
  def run_pnpm_update_packages
126
166
  dependency_updates = dependencies.map do |d|
127
167
  "#{d.name}@#{d.version}"
@@ -133,18 +173,24 @@ module Dependabot
133
173
  )
134
174
  end
135
175
 
176
+ sig { returns(T.nilable(String)) }
136
177
  def run_pnpm_install
137
178
  Helpers.run_pnpm_command(
138
179
  "install --lockfile-only"
139
180
  )
140
181
  end
141
182
 
183
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
142
184
  def workspace_files
143
- @workspace_files ||= dependency_files.select { |f| f.name.end_with?("pnpm-workspace.yaml") }
185
+ @workspace_files ||= T.let(
186
+ dependency_files.select { |f| f.name.end_with?("pnpm-workspace.yaml") },
187
+ T.nilable(T::Array[Dependabot::DependencyFile])
188
+ )
144
189
  end
145
190
 
191
+ sig { params(lockfile: Dependabot::DependencyFile).returns(T::Array[Dependabot::Dependency]) }
146
192
  def lockfile_dependencies(lockfile)
147
- @lockfile_dependencies ||= {}
193
+ @lockfile_dependencies ||= T.let({}, T.nilable(T::Hash[String, T::Array[Dependabot::Dependency]]))
148
194
  @lockfile_dependencies[lockfile.name] ||=
149
195
  NpmAndYarn::FileParser.new(
150
196
  dependency_files: [lockfile, *package_files, *workspace_files],
@@ -157,6 +203,13 @@ module Dependabot
157
203
  # rubocop:disable Metrics/PerceivedComplexity
158
204
  # rubocop:disable Metrics/MethodLength
159
205
  # rubocop:disable Metrics/CyclomaticComplexity
206
+ sig do
207
+ params(
208
+ error: SharedHelpers::HelperSubprocessFailed,
209
+ pnpm_lock: Dependabot::DependencyFile
210
+ )
211
+ .returns(T.noreturn)
212
+ end
160
213
  def handle_pnpm_lock_updater_error(error, pnpm_lock)
161
214
  error_message = error.message
162
215
 
@@ -165,15 +218,15 @@ module Dependabot
165
218
  end
166
219
 
167
220
  if error_message.match?(UNREACHABLE_GIT)
168
- url = error_message.match(UNREACHABLE_GIT).named_captures.fetch("url").gsub("git+ssh://git@", "https://").delete_suffix(".git")
221
+ url = error_message.match(UNREACHABLE_GIT)&.named_captures&.fetch("url")&.gsub("git+ssh://git@", "https://")&.delete_suffix(".git")
169
222
 
170
- raise Dependabot::GitDependenciesNotReachable, url
223
+ raise Dependabot::GitDependenciesNotReachable, T.must(url)
171
224
  end
172
225
 
173
226
  if error_message.match?(UNREACHABLE_GIT_V8)
174
- url = error_message.match(UNREACHABLE_GIT_V8).named_captures.fetch("url").gsub("codeload.", "")
227
+ url = error_message.match(UNREACHABLE_GIT_V8)&.named_captures&.fetch("url")&.gsub("codeload.", "")
175
228
 
176
- raise Dependabot::GitDependenciesNotReachable, url
229
+ raise Dependabot::GitDependenciesNotReachable, T.must(url)
177
230
  end
178
231
 
179
232
  [FORBIDDEN_PACKAGE, MISSING_PACKAGE, UNAUTHORIZED_PACKAGE, ERR_PNPM_FETCH_401,
@@ -181,14 +234,13 @@ module Dependabot
181
234
  .each do |regexp|
182
235
  next unless error_message.match?(regexp)
183
236
 
184
- dependency_url = error_message.match(regexp).named_captures["dependency_url"]
237
+ dependency_url = T.must(error_message.match(regexp)&.named_captures&.[]("dependency_url"))
185
238
  raise_package_access_error(error_message, dependency_url, pnpm_lock)
186
239
  end
187
240
 
188
241
  # TO-DO : subclassifcation of ERR_PNPM_TARBALL_INTEGRITY errors
189
242
  if error_message.match?(ERR_PNPM_TARBALL_INTEGRITY)
190
243
  dependency_names = dependencies.map(&:name).join(", ")
191
-
192
244
  msg = "Error (ERR_PNPM_TARBALL_INTEGRITY) while resolving \"#{dependency_names}\"."
193
245
  Dependabot.logger.warn(error_message)
194
246
  raise Dependabot::DependencyFileNotResolvable, msg
@@ -197,20 +249,17 @@ module Dependabot
197
249
  # TO-DO : investigate "packageManager" allowed regex
198
250
  if error_message.match?(INVALID_PACKAGE_SPEC)
199
251
  dependency_names = dependencies.map(&:name).join(", ")
200
-
201
252
  msg = "Invalid package manager specification in package.json while resolving \"#{dependency_names}\"."
202
253
  raise Dependabot::DependencyFileNotResolvable, msg
203
254
  end
204
255
 
205
256
  if error_message.match?(ERR_PNPM_META_FETCH_FAIL)
206
-
207
257
  msg = error_message.split(ERR_PNPM_META_FETCH_FAIL).last
208
258
  raise Dependabot::DependencyFileNotResolvable, msg
209
259
  end
210
260
 
211
261
  if error_message.match?(ERR_PNPM_WORKSPACE_PKG_NOT_FOUND)
212
262
  dependency_names = dependencies.map(&:name).join(", ")
213
-
214
263
  msg = "No package named \"#{dependency_names}\" present in workspace."
215
264
  Dependabot.logger.warn(error_message)
216
265
  raise Dependabot::DependencyFileNotResolvable, msg
@@ -223,8 +272,8 @@ module Dependabot
223
272
  end
224
273
 
225
274
  if error_message.match?(ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND)
226
- dir = error_message.match(ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND).named_captures.fetch("dir")
227
- msg = "Could not find linked package installation directory \"#{dir.split('/').last}\""
275
+ dir = error_message.match(ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND)&.named_captures&.fetch("dir")
276
+ msg = "Could not find linked package installation directory \"#{dir&.split('/')&.last}\""
228
277
  raise Dependabot::DependencyFileNotResolvable, msg
229
278
  end
230
279
 
@@ -246,13 +295,11 @@ module Dependabot
246
295
 
247
296
  if error_message.match?(ERR_PNPM_PEER_DEP_ISSUES)
248
297
  msg = "Missing or invalid configuration while installing peer dependencies."
249
-
250
298
  Dependabot.logger.warn(error_message)
251
299
  raise Dependabot::DependencyFileNotResolvable, msg
252
300
  end
253
301
 
254
302
  raise_patch_dependency_error(error_message) if error_message.match?(ERR_PNPM_PATCH_NOT_APPLIED)
255
-
256
303
  raise_unsupported_engine_error(error_message, pnpm_lock) if error_message.match?(ERR_PNPM_UNSUPPORTED_ENGINE)
257
304
 
258
305
  if error_message.match?(ERR_INVALID_THIS) && error_message.match?(URL_SEARCH_PARAMS)
@@ -262,8 +309,7 @@ module Dependabot
262
309
  end
263
310
 
264
311
  if error_message.match?(ERR_PNPM_UNSUPPORTED_PLATFORM)
265
- raise_unsupported_platform_error(error_message,
266
- pnpm_lock)
312
+ raise_unsupported_platform_error(error_message, pnpm_lock)
267
313
  end
268
314
 
269
315
  error_handler.handle_pnpm_error(error)
@@ -275,6 +321,7 @@ module Dependabot
275
321
  # rubocop:enable Metrics/MethodLength
276
322
  # rubocop:enable Metrics/CyclomaticComplexity
277
323
 
324
+ sig { params(error_message: String, pnpm_lock: Dependabot::DependencyFile).returns(T.noreturn) }
278
325
  def raise_resolvability_error(error_message, pnpm_lock)
279
326
  dependency_names = dependencies.map(&:name).join(", ")
280
327
  msg = "Error whilst updating #{dependency_names} in " \
@@ -282,6 +329,7 @@ module Dependabot
282
329
  raise Dependabot::DependencyFileNotResolvable, msg
283
330
  end
284
331
 
332
+ sig { params(error_message: String).returns(T.noreturn) }
285
333
  def raise_patch_dependency_error(error_message)
286
334
  dependency_names = dependencies.map(&:name).join(", ")
287
335
  msg = "Error while updating \"#{dependency_names}\" in " \
@@ -290,21 +338,50 @@ module Dependabot
290
338
  raise Dependabot::DependencyFileNotResolvable, msg
291
339
  end
292
340
 
341
+ sig do
342
+ params(
343
+ error_message: String,
344
+ _pnpm_lock: Dependabot::DependencyFile
345
+ ).returns(T.nilable(T.noreturn))
346
+ end
293
347
  def raise_unsupported_engine_error(error_message, _pnpm_lock)
294
- unless error_message.match(PACAKGE_MANAGER) &&
295
- error_message.match(VERSION_REQUIREMENT)
296
- return
348
+ match_pkg_mgr = error_message.match(PACAKGE_MANAGER)
349
+ match_version = error_message.match(VERSION_REQUIREMENT)
350
+
351
+ unless match_pkg_mgr && match_version &&
352
+ match_pkg_mgr.named_captures && match_version.named_captures
353
+ return nil
297
354
  end
298
355
 
299
- package_manager = error_message.match(PACAKGE_MANAGER).named_captures["pkg_mgr"]
300
- supported_version = error_message.match(VERSION_REQUIREMENT).named_captures["supported_ver"]
301
- detected_version = error_message.match(VERSION_REQUIREMENT).named_captures["detected_ver"]
356
+ captures_pkg_mgr = match_pkg_mgr.named_captures
357
+ captures_version = match_version.named_captures
358
+
359
+ pkg_mgr = captures_pkg_mgr["pkg_mgr"]
360
+ supported_ver = captures_version["supported_ver"]
361
+ detected_ver = captures_version["detected_ver"]
362
+
363
+ if pkg_mgr && supported_ver && detected_ver
364
+ raise Dependabot::ToolVersionNotSupported.new(
365
+ pkg_mgr,
366
+ supported_ver,
367
+ detected_ver
368
+ )
369
+ end
302
370
 
303
- raise Dependabot::ToolVersionNotSupported.new(package_manager, supported_version, detected_version)
371
+ nil
304
372
  end
305
373
 
374
+ sig do
375
+ params(
376
+ error_message: String,
377
+ dependency_url: String,
378
+ pnpm_lock: Dependabot::DependencyFile
379
+ )
380
+ .returns(T.noreturn)
381
+ end
306
382
  def raise_package_access_error(error_message, dependency_url, pnpm_lock)
307
- package_name = RegistryParser.new(resolved_url: dependency_url, credentials: credentials).dependency_name
383
+ package_name = RegistryParser.new(resolved_url: dependency_url,
384
+ credentials: credentials).dependency_name
308
385
  missing_dep = lockfile_dependencies(pnpm_lock)
309
386
  .find { |dep| dep.name == package_name }
310
387
  raise DependencyNotFound, package_name unless missing_dep
@@ -318,6 +395,7 @@ module Dependabot
318
395
  raise PrivateSourceAuthenticationFailure, reg
319
396
  end
320
397
 
398
+ sig { void }
321
399
  def write_final_package_json_files
322
400
  package_files.each do |file|
323
401
  path = file.name
@@ -326,57 +404,88 @@ module Dependabot
326
404
  end
327
405
  end
328
406
 
407
+ sig do
408
+ params(
409
+ error_message: String,
410
+ _pnpm_lock: Dependabot::DependencyFile
411
+ )
412
+ .returns(T.nilable(T.noreturn))
413
+ end
329
414
  def raise_unsupported_platform_error(error_message, _pnpm_lock)
330
- unless error_message.match(PLATFORM_PACAKGE_DEP) &&
331
- error_message.match(PLATFORM_VERSION_REQUIREMENT)
332
- return
415
+ match_dep = error_message.match(PLATFORM_PACAKGE_DEP)
416
+ match_version = error_message.match(PLATFORM_VERSION_REQUIREMENT)
417
+
418
+ unless match_dep && match_version &&
419
+ match_dep.named_captures && match_version.named_captures
420
+ return nil
333
421
  end
334
422
 
335
- supported_version = error_message.match(PLATFORM_VERSION_REQUIREMENT)
336
- .named_captures["supported_ver"]
337
- .then { sanitize_message(_1) }
338
- detected_version = error_message.match(PLATFORM_VERSION_REQUIREMENT)
339
- .named_captures["detected_ver"]
340
- .then { sanitize_message(_1) }
423
+ captures_version = match_version.named_captures
341
424
 
342
- Dependabot.logger.warn(error_message)
343
- raise Dependabot::ToolVersionNotSupported.new(PLATFORM_PACAKGE_MANAGER, supported_version, detected_version)
425
+ supported_ver = captures_version["supported_ver"]
426
+ detected_ver = captures_version["detected_ver"]
427
+
428
+ if supported_ver && detected_ver
429
+ supported_version = sanitize_message(supported_ver)
430
+ detected_version = sanitize_message(detected_ver)
431
+
432
+ Dependabot.logger.warn(error_message)
433
+ raise Dependabot::ToolVersionNotSupported.new(
434
+ PLATFORM_PACAKGE_MANAGER,
435
+ supported_version,
436
+ detected_version
437
+ )
438
+ end
439
+
440
+ nil
344
441
  end
345
442
 
443
+ sig { params(pnpm_lock: Dependabot::DependencyFile).returns(String) }
346
444
  def npmrc_content(pnpm_lock)
347
445
  NpmrcBuilder.new(
348
- credentials: credentials,
446
+ credentials: T.unsafe(credentials),
349
447
  dependency_files: dependency_files,
350
448
  dependencies: lockfile_dependencies(pnpm_lock)
351
449
  ).npmrc_content
352
450
  end
353
451
 
452
+ sig { params(file: Dependabot::DependencyFile).returns(String) }
354
453
  def updated_package_json_content(file)
355
- @updated_package_json_content ||= {}
454
+ @updated_package_json_content ||= T.let({}, T.nilable(T::Hash[String, String]))
356
455
  @updated_package_json_content[file.name] ||=
357
- PackageJsonUpdater.new(
358
- package_json: file,
359
- dependencies: dependencies
360
- ).updated_package_json.content
456
+ T.must(
457
+ PackageJsonUpdater.new(
458
+ package_json: file,
459
+ dependencies: dependencies
460
+ ).updated_package_json.content
461
+ )
361
462
  end
362
463
 
464
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
363
465
  def package_files
364
- @package_files ||= dependency_files.select { |f| f.name.end_with?("package.json") }
466
+ @package_files ||= T.let(
467
+ dependency_files.select { |f| f.name.end_with?("package.json") },
468
+ T.nilable(T::Array[Dependabot::DependencyFile])
469
+ )
365
470
  end
366
471
 
472
+ sig { returns(String) }
367
473
  def base_dir
368
- dependency_files.first.directory
474
+ T.must(dependency_files.first).directory
369
475
  end
370
476
 
477
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
371
478
  def npmrc_file
372
479
  dependency_files.find { |f| f.name == ".npmrc" }
373
480
  end
374
481
 
482
+ sig { params(message: String).returns(String) }
375
483
  def sanitize_message(message)
376
484
  message.gsub(/"|\[|\]|\}|\{/, "")
377
485
  end
378
486
  end
379
487
  end
488
+ # rubocop:enable Metrics/ClassLength
380
489
 
381
490
  class PnpmErrorHandler
382
491
  extend T::Sig
@@ -400,7 +509,8 @@ module Dependabot
400
509
  params(
401
510
  dependencies: T::Array[Dependabot::Dependency],
402
511
  dependency_files: T::Array[Dependabot::DependencyFile]
403
- ).void
512
+ )
513
+ .void
404
514
  end
405
515
  def initialize(dependencies:, dependency_files:)
406
516
  @dependencies = dependencies