dependabot-common 0.392.0 → 0.394.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/clients/azure.rb +42 -22
  3. data/lib/dependabot/clients/bitbucket.rb +52 -26
  4. data/lib/dependabot/clients/bitbucket_with_retries.rb +11 -12
  5. data/lib/dependabot/clients/codecommit.rb +73 -53
  6. data/lib/dependabot/clients/github_resource_parser.rb +70 -0
  7. data/lib/dependabot/clients/github_with_retries.rb +63 -23
  8. data/lib/dependabot/clients/gitlab_with_retries.rb +19 -17
  9. data/lib/dependabot/clients/json_response_parser.rb +103 -0
  10. data/lib/dependabot/command_helpers.rb +79 -11
  11. data/lib/dependabot/credential.rb +12 -3
  12. data/lib/dependabot/errors.rb +4 -1
  13. data/lib/dependabot/file_fetchers/base.rb +199 -151
  14. data/lib/dependabot/file_filtering.rb +0 -2
  15. data/lib/dependabot/file_updaters/vendor_updater.rb +8 -2
  16. data/lib/dependabot/git_tag_details.rb +8 -14
  17. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +211 -110
  18. data/lib/dependabot/metadata_finders/base/commit_response_parser.rb +68 -0
  19. data/lib/dependabot/metadata_finders/base/commits_finder.rb +44 -38
  20. data/lib/dependabot/package/npm_package_manager_config.rb +72 -0
  21. data/lib/dependabot/package/npm_registry_package.rb +186 -0
  22. data/lib/dependabot/pull_request_creator/azure.rb +1 -1
  23. data/lib/dependabot/pull_request_creator/codecommit.rb +10 -12
  24. data/lib/dependabot/pull_request_creator/github.rb +55 -35
  25. data/lib/dependabot/pull_request_creator/gitlab.rb +37 -11
  26. data/lib/dependabot/pull_request_creator/labeler.rb +65 -15
  27. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +179 -78
  28. data/lib/dependabot/pull_request_updater/azure.rb +63 -11
  29. data/lib/dependabot/pull_request_updater/github.rb +44 -34
  30. data/lib/dependabot/pull_request_updater/gitlab.rb +23 -12
  31. data/lib/dependabot/pull_request_updater.rb +3 -3
  32. data/lib/dependabot/shared_helpers.rb +77 -32
  33. data/lib/dependabot/update_checkers/base.rb +9 -5
  34. data/lib/dependabot/update_checkers/version_filters.rb +5 -0
  35. data/lib/dependabot/update_checkers/vulnerability_audit.rb +282 -0
  36. data/lib/dependabot/workspace/git.rb +35 -4
  37. data/lib/dependabot.rb +1 -1
  38. metadata +10 -4
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "octokit"
@@ -6,6 +6,7 @@ require "securerandom"
6
6
  require "sorbet-runtime"
7
7
 
8
8
  require "dependabot/clients/github_with_retries"
9
+ require "dependabot/clients/github_resource_parser"
9
10
  require "dependabot/pull_request_creator"
10
11
  require "dependabot/pull_request_creator/commit_signer"
11
12
 
@@ -14,6 +15,7 @@ module Dependabot
14
15
  # rubocop:disable Metrics/ClassLength
15
16
  class Github
16
17
  extend T::Sig
18
+ include Dependabot::Clients::GithubResourceParser
17
19
 
18
20
  # GitHub limits PR descriptions to a max of 65,536 characters:
19
21
  # https://github.com/orgs/community/discussions/27190#discussioncomment-3726017
@@ -121,7 +123,7 @@ module Dependabot
121
123
  @require_up_to_date_base = require_up_to_date_base
122
124
  end
123
125
 
124
- sig { returns(T.untyped) }
126
+ sig { returns(Sawyer::Resource) }
125
127
  def create
126
128
  Dependabot.logger.info(
127
129
  "Initiating Github pull request."
@@ -135,7 +137,8 @@ module Dependabot
135
137
  end
136
138
 
137
139
  if branch_exists?(branch_name) && open_pull_request_exists?
138
- raise UnmergedPRExists, "PR ##{open_pull_requests.first.number} already exists"
140
+ number = github_integer(T.must(open_pull_requests.first), :number, "pull request")
141
+ raise UnmergedPRExists, "PR ##{number} already exists"
139
142
  end
140
143
  if require_up_to_date_base? && !base_commit_is_up_to_date?
141
144
  raise BaseCommitNotUpToDate, "HEAD #{head_commit} does not match base #{base_commit}"
@@ -186,12 +189,22 @@ module Dependabot
186
189
  open_pull_requests.any?
187
190
  end
188
191
 
189
- sig { returns(T::Array[T.untyped]) }
192
+ sig { returns(T::Array[Sawyer::Resource]) }
190
193
  def open_pull_requests
191
- pull_requests_for_branch.reject(&:closed).reject(&:merged)
194
+ pull_requests_for_branch
195
+ .reject { |pull_request| github_pull_request_merged?(pull_request) }
192
196
  end
193
197
 
194
- sig { returns(T::Array[T.untyped]) }
198
+ sig { params(pull_request: Sawyer::Resource).returns(T::Boolean) }
199
+ def github_pull_request_merged?(pull_request)
200
+ merged_at = T.cast(pull_request[:merged_at], Object)
201
+ return false if merged_at.nil?
202
+ return true if merged_at.is_a?(String) || merged_at.is_a?(Time)
203
+
204
+ raise_bad_github_response("pull request merged_at must be a time, string, or nil")
205
+ end
206
+
207
+ sig { returns(T::Array[Sawyer::Resource]) }
195
208
  def pull_requests_for_branch
196
209
  @pull_requests_for_branch ||=
197
210
  T.let(
@@ -217,7 +230,7 @@ module Dependabot
217
230
  )
218
231
  [*open_prs, *closed_prs]
219
232
  end,
220
- T.nilable(T::Array[T.untyped])
233
+ T.nilable(T::Array[Sawyer::Resource])
221
234
  )
222
235
  end
223
236
 
@@ -234,7 +247,7 @@ module Dependabot
234
247
  )
235
248
  end
236
249
 
237
- sig { returns(T.untyped) }
250
+ sig { returns(Sawyer::Resource) }
238
251
  def create_annotated_pull_request
239
252
  commit = create_commit
240
253
  branch = create_or_update_branch(commit)
@@ -260,7 +273,7 @@ module Dependabot
260
273
  false
261
274
  end
262
275
 
263
- sig { returns(T.untyped) }
276
+ sig { returns(Sawyer::Resource) }
264
277
  def create_commit
265
278
  tree = create_tree
266
279
 
@@ -268,7 +281,7 @@ module Dependabot
268
281
  github_client_for_source.create_commit(
269
282
  source.repo,
270
283
  commit_message,
271
- tree.sha,
284
+ github_string(tree, :sha, "tree"),
272
285
  base_commit,
273
286
  commit_options(tree)
274
287
  )
@@ -291,19 +304,21 @@ module Dependabot
291
304
  retry
292
305
  end
293
306
 
294
- sig { params(tree: T.untyped).returns(T::Hash[Symbol, T.untyped]) }
307
+ sig { params(tree: Sawyer::Resource).returns(T::Hash[Symbol, Object]) }
295
308
  def commit_options(tree)
296
- options = author_details&.any? ? { author: author_details } : {}
309
+ options = T.let({}, T::Hash[Symbol, Object])
310
+ author = author_details&.dup
311
+ options[:author] = author if author&.any?
297
312
 
298
- if options[:author]&.any? && signature_key
299
- options[:author][:date] = Time.now.utc.iso8601
300
- options[:signature] = commit_signature(tree, options[:author])
313
+ if author&.any? && signature_key
314
+ author[:date] = Time.now.utc.iso8601
315
+ options[:signature] = commit_signature(tree, author)
301
316
  end
302
317
 
303
318
  options
304
319
  end
305
320
 
306
- sig { returns(T.untyped) }
321
+ sig { returns(Sawyer::Resource) }
307
322
  def create_tree
308
323
  file_trees = files.map do |file|
309
324
  if file.type == "submodule"
@@ -340,7 +355,7 @@ module Dependabot
340
355
  )
341
356
  end
342
357
 
343
- sig { params(commit: T.untyped).returns(T.untyped) }
358
+ sig { params(commit: Sawyer::Resource).returns(T.nilable(Sawyer::Resource)) }
344
359
  def create_or_update_branch(commit)
345
360
  if branch_exists?(branch_name)
346
361
  update_branch(commit)
@@ -359,13 +374,13 @@ module Dependabot
359
374
  retry
360
375
  end
361
376
 
362
- sig { params(commit: T.untyped).returns(T.untyped) }
377
+ sig { params(commit: Sawyer::Resource).returns(Sawyer::Resource) }
363
378
  def create_branch(commit)
364
379
  ref = "refs/heads/#{branch_name}"
365
380
 
366
381
  begin
367
382
  branch =
368
- github_client_for_source.create_ref(source.repo, ref, commit.sha)
383
+ github_client_for_source.create_ref(source.repo, ref, github_string(commit, :sha, "commit"))
369
384
  @branch_name = ref.gsub(%r{^refs/heads/}, "")
370
385
  branch
371
386
  rescue Octokit::UnprocessableEntity => e
@@ -383,32 +398,32 @@ module Dependabot
383
398
  end
384
399
  end
385
400
 
386
- sig { params(commit: T.untyped).void }
401
+ sig { params(commit: Sawyer::Resource).returns(Sawyer::Resource) }
387
402
  def update_branch(commit)
388
403
  github_client_for_source.update_ref(
389
404
  source.repo,
390
405
  "heads/#{branch_name}",
391
- commit.sha,
406
+ github_string(commit, :sha, "commit"),
392
407
  true
393
408
  )
394
409
  end
395
410
 
396
- sig { params(pull_request: T.untyped).void }
411
+ sig { params(pull_request: Sawyer::Resource).void }
397
412
  def annotate_pull_request(pull_request)
398
- labeler.label_pull_request(pull_request.number)
413
+ labeler.label_pull_request(github_integer(pull_request, :number, "pull request"))
399
414
  add_reviewers_to_pull_request(pull_request) if reviewers&.any?
400
415
  add_assignees_to_pull_request(pull_request) if assignees&.any?
401
416
  add_milestone_to_pull_request(pull_request) if milestone
402
417
  end
403
418
 
404
- sig { params(pull_request: T.untyped).void }
419
+ sig { params(pull_request: Sawyer::Resource).void }
405
420
  def add_reviewers_to_pull_request(pull_request)
406
421
  reviewers_hash =
407
422
  T.must(reviewers).keys.to_h { |k| [k.to_sym, T.must(reviewers)[k]] }
408
423
 
409
424
  github_client_for_source.request_pull_request_review(
410
425
  source.repo,
411
- pull_request.number,
426
+ github_integer(pull_request, :number, "pull request"),
412
427
  reviewers: reviewers_hash[:reviewers] || [],
413
428
  team_reviewers: reviewers_hash[:team_reviewers] || []
414
429
  )
@@ -434,7 +449,7 @@ module Dependabot
434
449
  false
435
450
  end
436
451
 
437
- sig { params(pull_request: T.untyped, message: String).void }
452
+ sig { params(pull_request: Sawyer::Resource, message: String).void }
438
453
  def comment_with_invalid_reviewer(pull_request, message)
439
454
  reviewers_hash =
440
455
  T.must(reviewers).keys.to_h { |k| [k.to_sym, T.must(reviewers)[k]] }
@@ -460,16 +475,16 @@ module Dependabot
460
475
 
461
476
  github_client_for_source.add_comment(
462
477
  source.repo,
463
- pull_request.number,
478
+ github_integer(pull_request, :number, "pull request"),
464
479
  msg
465
480
  )
466
481
  end
467
482
 
468
- sig { params(pull_request: T.untyped).void }
483
+ sig { params(pull_request: Sawyer::Resource).void }
469
484
  def add_assignees_to_pull_request(pull_request)
470
485
  github_client_for_source.add_assignees(
471
486
  source.repo,
472
- pull_request.number,
487
+ github_integer(pull_request, :number, "pull request"),
473
488
  T.must(assignees)
474
489
  )
475
490
  rescue Octokit::NotFound
@@ -480,18 +495,18 @@ module Dependabot
480
495
  raise unless e.message.include?("Could not add assignees")
481
496
  end
482
497
 
483
- sig { params(pull_request: T.untyped).void }
498
+ sig { params(pull_request: Sawyer::Resource).void }
484
499
  def add_milestone_to_pull_request(pull_request)
485
500
  github_client_for_source.update_issue(
486
501
  source.repo,
487
- pull_request.number,
502
+ github_integer(pull_request, :number, "pull request"),
488
503
  milestone: milestone
489
504
  )
490
505
  rescue Octokit::UnprocessableEntity => e
491
506
  raise unless e.message.include?("code: invalid")
492
507
  end
493
508
 
494
- sig { returns(T.untyped) }
509
+ sig { returns(T.nilable(Sawyer::Resource)) }
495
510
  def create_pull_request
496
511
  github_client_for_source.create_pull_request(
497
512
  source.repo,
@@ -521,7 +536,7 @@ module Dependabot
521
536
  def default_branch
522
537
  @default_branch ||=
523
538
  T.let(
524
- T.unsafe(github_client_for_source.repo(source.repo)).default_branch,
539
+ github_client_for_source.fetch_default_branch(source.repo),
525
540
  T.nilable(String)
526
541
  )
527
542
  end
@@ -539,13 +554,13 @@ module Dependabot
539
554
  end
540
555
 
541
556
  sig do
542
- params(tree: T.untyped, author_details_with_date: T::Hash[Symbol, String]).returns(String)
557
+ params(tree: Sawyer::Resource, author_details_with_date: T::Hash[Symbol, String]).returns(String)
543
558
  end
544
559
  def commit_signature(tree, author_details_with_date)
545
560
  CommitSigner.new(
546
561
  author_details: author_details_with_date,
547
562
  commit_message: commit_message,
548
- tree_sha: tree.sha,
563
+ tree_sha: github_string(tree, :sha, "tree"),
549
564
  parent_sha: base_commit,
550
565
  signature_key: T.must(signature_key)
551
566
  ).signature
@@ -570,6 +585,11 @@ module Dependabot
570
585
  )
571
586
  end
572
587
 
588
+ sig { returns(String) }
589
+ def github_response_source
590
+ source.url
591
+ end
592
+
573
593
  sig { params(err: StandardError).returns(T.noreturn) }
574
594
  def handle_error(err)
575
595
  cause = case err
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "gitlab"
@@ -6,6 +6,7 @@ require "sorbet-runtime"
6
6
 
7
7
  require "dependabot/clients/gitlab_with_retries"
8
8
  require "dependabot/pull_request_creator"
9
+ require "dependabot/errors"
9
10
 
10
11
  module Dependabot
11
12
  class PullRequestCreator
@@ -158,18 +159,19 @@ module Dependabot
158
159
  gitlab_client_for_source.commits(source.repo, ref_name: branch_name),
159
160
  T.nilable(::Gitlab::PaginatedResponse)
160
161
  )
161
- T.unsafe(@commits).first.message == commit_message
162
+ commit = @commits.first
163
+ return false unless commit
164
+
165
+ gitlab_string(commit, "message", "commit") == commit_message
162
166
  end
163
167
 
164
168
  sig { returns(T::Boolean) }
165
169
  def merge_request_exists?
166
- T.unsafe(
167
- gitlab_client_for_source.merge_requests(
168
- (target_project_id || source.repo).to_s,
169
- source_branch: branch_name,
170
- target_branch: source.branch || default_branch,
171
- state: "all"
172
- )
170
+ gitlab_client_for_source.merge_requests(
171
+ (target_project_id || source.repo).to_s,
172
+ source_branch: branch_name,
173
+ target_branch: source.branch || default_branch,
174
+ state: "all"
173
175
  ).any?
174
176
  end
175
177
 
@@ -240,7 +242,7 @@ module Dependabot
240
242
 
241
243
  gitlab_client_for_source.create_merge_request_level_rule(
242
244
  (target_project_id || source.repo).to_s,
243
- T.unsafe(merge_request).iid,
245
+ gitlab_integer(merge_request, "iid", "merge request"),
244
246
  name: "dependency-updates",
245
247
  approvals_required: 1,
246
248
  user_ids: approvers_hash[:approvers],
@@ -260,10 +262,34 @@ module Dependabot
260
262
  def default_branch
261
263
  @default_branch ||=
262
264
  T.let(
263
- T.unsafe(gitlab_client_for_source.project(source.repo)).default_branch,
265
+ gitlab_client_for_source.fetch_default_branch(source.repo),
264
266
  T.nilable(String)
265
267
  )
266
268
  end
269
+
270
+ sig { params(resource: ::Gitlab::ObjectifiedHash, key: String, context: String).returns(String) }
271
+ def gitlab_string(resource, key, context)
272
+ value = T.cast(resource[key], Object)
273
+ return value if value.is_a?(String)
274
+
275
+ raise_bad_gitlab_response("#{context} #{key} must be a string")
276
+ end
277
+
278
+ sig { params(resource: ::Gitlab::ObjectifiedHash, key: String, context: String).returns(Integer) }
279
+ def gitlab_integer(resource, key, context)
280
+ value = T.cast(resource[key], Object)
281
+ return value if value.is_a?(Integer)
282
+
283
+ raise_bad_gitlab_response("#{context} #{key} must be an integer")
284
+ end
285
+
286
+ sig { params(message: String).returns(T.noreturn) }
287
+ def raise_bad_gitlab_response(message)
288
+ raise Dependabot::PrivateSourceBadResponse.new(
289
+ source.url,
290
+ "Malformed GitLab response: #{message}"
291
+ )
292
+ end
267
293
  end
268
294
  end
269
295
  end
@@ -1,9 +1,10 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "octokit"
5
5
  require "sorbet-runtime"
6
6
  require "dependabot/pull_request_creator"
7
+ require "dependabot/errors"
7
8
  require "dependabot/credential"
8
9
 
9
10
  module Dependabot
@@ -313,16 +314,13 @@ module Dependabot
313
314
  def fetch_github_labels
314
315
  client = github_client_for_source
315
316
 
316
- labels = T.let(
317
- T.unsafe(client.labels(source.repo, per_page: 100)).map(&:name),
318
- T::Array[String]
319
- )
320
-
317
+ raw_labels = client.labels(source.repo, per_page: 100)
318
+ labels = github_label_names(raw_labels)
321
319
  next_link = T.let(client.last_response.rels[:next], T.nilable(Sawyer::Relation))
322
320
 
323
321
  while next_link
324
322
  next_page = T.let(next_link.get, Sawyer::Response)
325
- labels += T.unsafe(next_page.data).map(&:name)
323
+ labels.concat(github_labels_from_page(next_page))
326
324
  next_link = next_page.rels[:next]
327
325
  end
328
326
 
@@ -331,12 +329,51 @@ module Dependabot
331
329
 
332
330
  sig { returns(T::Array[String]) }
333
331
  def fetch_gitlab_labels
334
- T.unsafe(
335
- gitlab_client_for_source
336
- .labels(source.repo, per_page: 100)
337
- .auto_paginate
332
+ gitlab_client_for_source
333
+ .labels(source.repo, per_page: 100)
334
+ .auto_paginate
335
+ .map { |label| gitlab_label_name(label) }
336
+ end
337
+
338
+ sig { params(label: Sawyer::Resource).returns(String) }
339
+ def github_label_name(label)
340
+ value = T.cast(label[:name], Object)
341
+ return value if value.is_a?(String)
342
+
343
+ raise_bad_label_response("GitHub", "name must be a string")
344
+ end
345
+
346
+ sig { params(response: Sawyer::Response).returns(T::Array[String]) }
347
+ def github_labels_from_page(response)
348
+ github_label_names(T.cast(response.data, Object))
349
+ end
350
+
351
+ sig { params(data: Object).returns(T::Array[String]) }
352
+ def github_label_names(data)
353
+ raise_bad_label_response("GitHub", "page data must be an array") unless data.is_a?(Array)
354
+
355
+ data.map do |raw_label|
356
+ label = T.cast(raw_label, Object)
357
+ raise_bad_label_response("GitHub", "label must be an object") unless label.is_a?(Sawyer::Resource)
358
+
359
+ github_label_name(label)
360
+ end
361
+ end
362
+
363
+ sig { params(label: ::Gitlab::ObjectifiedHash).returns(String) }
364
+ def gitlab_label_name(label)
365
+ value = T.cast(label["name"], Object)
366
+ return value if value.is_a?(String)
367
+
368
+ raise_bad_label_response("GitLab", "name must be a string")
369
+ end
370
+
371
+ sig { params(provider: String, message: String).returns(T.noreturn) }
372
+ def raise_bad_label_response(provider, message)
373
+ raise Dependabot::PrivateSourceBadResponse.new(
374
+ source.url,
375
+ "Malformed #{provider} label response: #{message}"
338
376
  )
339
- .map(&:name)
340
377
  end
341
378
 
342
379
  sig { returns(T::Array[String]) }
@@ -394,7 +431,7 @@ module Dependabot
394
431
  )
395
432
  @labels = [*@labels, DEFAULT_DEPENDENCIES_LABEL].uniq
396
433
  rescue Octokit::UnprocessableEntity => e
397
- raise unless e.errors.first.fetch(:code) == "already_exists"
434
+ raise unless github_label_already_exists?(e)
398
435
 
399
436
  @labels = [*@labels, DEFAULT_DEPENDENCIES_LABEL].uniq
400
437
  end
@@ -421,7 +458,7 @@ module Dependabot
421
458
  )
422
459
  @labels = [*@labels, DEFAULT_SECURITY_LABEL].uniq
423
460
  rescue Octokit::UnprocessableEntity => e
424
- raise unless e.errors.first.fetch(:code) == "already_exists"
461
+ raise unless github_label_already_exists?(e)
425
462
 
426
463
  @labels = [*@labels, DEFAULT_SECURITY_LABEL].uniq
427
464
  end
@@ -450,11 +487,24 @@ module Dependabot
450
487
  )
451
488
  @labels = [*@labels, language_name].uniq
452
489
  rescue Octokit::UnprocessableEntity => e
453
- raise unless e.errors.first.fetch(:code) == "already_exists"
490
+ raise unless github_label_already_exists?(e)
454
491
 
455
492
  @labels = [*@labels, language_name].uniq.compact
456
493
  end
457
494
 
495
+ sig { params(error: Octokit::UnprocessableEntity).returns(T::Boolean) }
496
+ def github_label_already_exists?(error)
497
+ errors = T.cast(error.errors, Object)
498
+ return false unless errors.is_a?(Array)
499
+
500
+ first_error = T.cast(errors.first, Object)
501
+ return false unless first_error.is_a?(Hash)
502
+
503
+ typed_error = T.let(first_error, T::Hash[T.any(Symbol, String), Object])
504
+ code = typed_error[:code] || typed_error["code"]
505
+ code == "already_exists"
506
+ end
507
+
458
508
  sig { params(language: String).returns(String) }
459
509
  def default_description_for(language)
460
510
  "Pull requests that update #{language.capitalize} code"