dependabot-common 0.392.0 → 0.393.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 (34) 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/file_fetchers/base.rb +196 -140
  13. data/lib/dependabot/file_updaters/vendor_updater.rb +8 -2
  14. data/lib/dependabot/git_tag_details.rb +8 -14
  15. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +211 -110
  16. data/lib/dependabot/metadata_finders/base/commit_response_parser.rb +68 -0
  17. data/lib/dependabot/metadata_finders/base/commits_finder.rb +44 -38
  18. data/lib/dependabot/pull_request_creator/azure.rb +1 -1
  19. data/lib/dependabot/pull_request_creator/codecommit.rb +10 -12
  20. data/lib/dependabot/pull_request_creator/github.rb +55 -35
  21. data/lib/dependabot/pull_request_creator/gitlab.rb +37 -11
  22. data/lib/dependabot/pull_request_creator/labeler.rb +65 -15
  23. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +179 -78
  24. data/lib/dependabot/pull_request_updater/azure.rb +63 -11
  25. data/lib/dependabot/pull_request_updater/github.rb +44 -34
  26. data/lib/dependabot/pull_request_updater/gitlab.rb +23 -12
  27. data/lib/dependabot/pull_request_updater.rb +3 -3
  28. data/lib/dependabot/shared_helpers.rb +77 -32
  29. data/lib/dependabot/update_checkers/base.rb +9 -5
  30. data/lib/dependabot/update_checkers/version_filters.rb +5 -0
  31. data/lib/dependabot/update_checkers/vulnerability_audit.rb +282 -0
  32. data/lib/dependabot/workspace/git.rb +35 -4
  33. data/lib/dependabot.rb +1 -1
  34. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a574e02b5e7a33f7f245c56ae3df5942505a73cee3c1f32b8a07c1b36ba13a2
4
- data.tar.gz: 4ee0767b57e78681464704b7e41557692d57cf240f40f2577d0313a87f78d371
3
+ metadata.gz: 1a99920113509c66688f9ff8e4c3da84a054c3ce8d0f6c1d137cbdbc936ab4e9
4
+ data.tar.gz: 5c4de02216c87431010c69387a5ade5f4f296423e3b5e7f3e889e293743ee666
5
5
  SHA512:
6
- metadata.gz: b63a271847067f6adf096dcbbd9e97e0e986defc09125f55afa8be96cb820660c76551982b46d7c613e29ff93093d87bb1c1849d99c6e7bd75fab3f9abcc3a28
7
- data.tar.gz: 28753fa5c276536d7cb8e16cc7bf183796bdd968e31c0efcb92ded66fd8b07a6e05304d0da3eba70a64fc1bd990f9167a073a072791655bdeb47cfb2f8c01d14
6
+ metadata.gz: c84ada814badfb9efc4e7a60d67fa5a26154c9c110337bf77c0963bbb08393ffd13247508d2db349750fb26590f81dfdded077aaf74ad54bf0b4f01b769453cb
7
+ data.tar.gz: 1dc8a19ec787541adb136602ee00d101d7919b86c6d903c6d33ae4dedd65afd6fe677b83f4574e5c370a9e2e379093f4e5e7bf630cf95868e9bf91f0db66eccc
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/shared_helpers"
5
+ require "dependabot/clients/json_response_parser"
5
6
  require "excon"
6
7
  require "sorbet-runtime"
7
8
 
@@ -10,6 +11,10 @@ module Dependabot
10
11
  # rubocop:disable Metrics/ClassLength
11
12
  class Azure
12
13
  extend T::Sig
14
+ include JsonResponseParser
15
+
16
+ JsonObject = T.type_alias { JsonResponseParser::JsonObject }
17
+ JsonObjects = T.type_alias { T::Array[JsonObject] }
13
18
 
14
19
  class NotFound < StandardError; end
15
20
 
@@ -74,7 +79,9 @@ module Dependabot
74
79
 
75
80
  raise NotFound if response.status == 400
76
81
 
77
- JSON.parse(response.body).fetch("commit").fetch("commitId")
82
+ root = parse_json_object(response.body, "branch stats")
83
+ commit = object_field(root, "commit", "branch stats")
84
+ string_field(commit, "commitId", "branch stats")
78
85
  end
79
86
 
80
87
  sig { params(_repo: String).returns(String) }
@@ -85,7 +92,8 @@ module Dependabot
85
92
  "/_apis/git/repositories/" + source.unscoped_repo
86
93
  )
87
94
 
88
- JSON.parse(response.body).fetch("defaultBranch").gsub("refs/heads/", "")
95
+ root = parse_json_object(response.body, "repository")
96
+ string_field(root, "defaultBranch", "repository").gsub("refs/heads/", "")
89
97
  end
90
98
 
91
99
  sig do
@@ -93,7 +101,7 @@ module Dependabot
93
101
  commit: T.nilable(String),
94
102
  path: T.nilable(String)
95
103
  )
96
- .returns(T::Array[T::Hash[String, T.untyped]])
104
+ .returns(JsonObjects)
97
105
  end
98
106
  def fetch_repo_contents(commit = nil, path = nil)
99
107
  tree = fetch_repo_contents_treeroot(commit, path)
@@ -105,7 +113,8 @@ module Dependabot
105
113
  "/trees/" + tree + "?recursive=false"
106
114
  )
107
115
 
108
- JSON.parse(response.body).fetch("treeEntries")
116
+ root = parse_json_object(response.body, "repository tree")
117
+ object_array_field(root, "treeEntries", "repository tree")
109
118
  end
110
119
 
111
120
  sig { params(commit: T.nilable(String), path: T.nilable(String)).returns(String) }
@@ -125,7 +134,8 @@ module Dependabot
125
134
 
126
135
  tree_response = get(tree_url)
127
136
 
128
- JSON.parse(tree_response.body).fetch("objectId")
137
+ root = parse_json_object(tree_response.body, "repository item")
138
+ string_field(root, "objectId", "repository item")
129
139
  end
130
140
 
131
141
  sig { params(commit: String, path: String).returns(String) }
@@ -142,7 +152,7 @@ module Dependabot
142
152
  response.body
143
153
  end
144
154
 
145
- sig { params(branch_name: T.nilable(String)).returns(T::Array[T::Hash[String, T.untyped]]) }
155
+ sig { params(branch_name: T.nilable(String)).returns(JsonObjects) }
146
156
  def commits(branch_name = nil)
147
157
  commits_url = T.must(source.api_endpoint) +
148
158
  source.organization + "/" + source.project +
@@ -153,10 +163,11 @@ module Dependabot
153
163
 
154
164
  response = get(commits_url)
155
165
 
156
- JSON.parse(response.body).fetch("value")
166
+ root = parse_json_object(response.body, "commits")
167
+ object_array_field(root, "value", "commits")
157
168
  end
158
169
 
159
- sig { params(branch_name: String).returns(T.nilable(T::Hash[String, T.untyped])) }
170
+ sig { params(branch_name: String).returns(T.nilable(JsonObject)) }
160
171
  def branch(branch_name)
161
172
  response = get(
162
173
  T.must(source.api_endpoint) +
@@ -165,10 +176,11 @@ module Dependabot
165
176
  "/refs?filter=heads/" + branch_name
166
177
  )
167
178
 
168
- JSON.parse(response.body).fetch("value").first
179
+ root = parse_json_object(response.body, "branch")
180
+ object_array_field(root, "value", "branch").first
169
181
  end
170
182
 
171
- sig { params(source_branch: String, target_branch: String).returns(T::Array[T::Hash[String, T.untyped]]) }
183
+ sig { params(source_branch: String, target_branch: String).returns(JsonObjects) }
172
184
  def pull_requests(source_branch, target_branch)
173
185
  response = get(
174
186
  T.must(source.api_endpoint) +
@@ -179,7 +191,8 @@ module Dependabot
179
191
  "&searchCriteria.targetRefName=refs/heads/" + target_branch
180
192
  )
181
193
 
182
- JSON.parse(response.body).fetch("value")
194
+ root = parse_json_object(response.body, "pull requests")
195
+ object_array_field(root, "value", "pull requests")
183
196
  end
184
197
 
185
198
  sig do
@@ -190,7 +203,7 @@ module Dependabot
190
203
  files: T::Array[Dependabot::DependencyFile],
191
204
  author_details: T.nilable(T::Hash[Symbol, String])
192
205
  )
193
- .returns(T.untyped)
206
+ .returns(Excon::Response)
194
207
  end
195
208
  def create_commit(
196
209
  branch_name,
@@ -241,7 +254,7 @@ module Dependabot
241
254
  assignees: T.nilable(T::Array[String]),
242
255
  work_item: T.nilable(Integer)
243
256
  )
244
- .returns(T.untyped)
257
+ .returns(Excon::Response)
245
258
  end
246
259
  def create_pull_request(
247
260
  pr_name,
@@ -283,7 +296,7 @@ module Dependabot
283
296
  trans_work_items: T::Boolean,
284
297
  ignore_config_ids: T::Array[String]
285
298
  )
286
- .returns(T.untyped)
299
+ .returns(JsonObject)
287
300
  end
288
301
  def autocomplete_pull_request(
289
302
  pull_request_id,
@@ -317,10 +330,10 @@ module Dependabot
317
330
  content.to_json
318
331
  )
319
332
 
320
- JSON.parse(response.body)
333
+ parse_json_object(response.body, "pull request autocomplete")
321
334
  end
322
335
 
323
- sig { params(pull_request_id: String).returns(T::Hash[String, T.untyped]) }
336
+ sig { params(pull_request_id: String).returns(JsonObject) }
324
337
  def pull_request(pull_request_id)
325
338
  response = get(
326
339
  T.must(source.api_endpoint) +
@@ -328,10 +341,10 @@ module Dependabot
328
341
  "/_apis/git/pullrequests/" + pull_request_id
329
342
  )
330
343
 
331
- JSON.parse(response.body)
344
+ parse_json_object(response.body, "pull request")
332
345
  end
333
346
 
334
- sig { params(branch_name: String, old_commit: String, new_commit: String).returns(T::Hash[String, T.untyped]) }
347
+ sig { params(branch_name: String, old_commit: String, new_commit: String).returns(JsonObject) }
335
348
  def update_ref(branch_name, old_commit, new_commit)
336
349
  content = [
337
350
  {
@@ -348,7 +361,8 @@ module Dependabot
348
361
  content.to_json
349
362
  )
350
363
 
351
- JSON.parse(response.body).fetch("value").first
364
+ root = parse_json_object(response.body, "updated ref")
365
+ first_object_field(root, "value", "updated ref")
352
366
  end
353
367
  # rubocop:enable Metrics/ParameterLists
354
368
 
@@ -358,7 +372,7 @@ module Dependabot
358
372
  new_tag: T.nilable(String),
359
373
  type: String
360
374
  )
361
- .returns(T::Array[T::Hash[String, T.untyped]])
375
+ .returns(JsonObjects)
362
376
  end
363
377
  def compare(previous_tag, new_tag, type)
364
378
  response = get(
@@ -371,7 +385,8 @@ module Dependabot
371
385
  "&searchCriteria.compareVersion.version=#{new_tag}"
372
386
  )
373
387
 
374
- JSON.parse(response.body).fetch("value")
388
+ root = parse_json_object(response.body, "commit comparison")
389
+ object_array_field(root, "value", "commit comparison")
375
390
  end
376
391
 
377
392
  sig { params(url: String).returns(Excon::Response) }
@@ -472,6 +487,11 @@ module Dependabot
472
487
 
473
488
  private
474
489
 
490
+ sig { returns([String, String]) }
491
+ def response_identity
492
+ ["Azure", source.url]
493
+ end
494
+
475
495
  sig { params(blk: T.proc.void).void }
476
496
  def retry_connection_failures(&blk)
477
497
  retry_attempt = 0
@@ -512,7 +532,7 @@ module Dependabot
512
532
  reviewers: T.nilable(T::Array[String]),
513
533
  assignees: T.nilable(T::Array[String])
514
534
  )
515
- .returns(T::Array[T::Hash[Symbol, T.untyped]])
535
+ .returns(T::Array[T::Hash[Symbol, Object]])
516
536
  end
517
537
  def pr_reviewers(reviewers, assignees)
518
538
  return [] unless reviewers || assignees
@@ -4,12 +4,17 @@
4
4
  require "excon"
5
5
  require "sorbet-runtime"
6
6
 
7
+ require "dependabot/clients/json_response_parser"
7
8
  require "dependabot/shared_helpers"
8
9
 
9
10
  module Dependabot
10
11
  module Clients
11
12
  class Bitbucket
12
13
  extend T::Sig
14
+ include JsonResponseParser
15
+
16
+ JsonObject = T.type_alias { JsonResponseParser::JsonObject }
17
+ JsonObjects = T.type_alias { T::Array[JsonObject] }
13
18
 
14
19
  class NotFound < StandardError; end
15
20
 
@@ -54,14 +59,18 @@ module Dependabot
54
59
  path = "#{repo}/refs/branches/#{branch}"
55
60
  response = get(base_url + path)
56
61
 
57
- JSON.parse(response.body).fetch("target").fetch("hash")
62
+ root = parse_json_object(response.body, "branch")
63
+ target = object_field(root, "target", "branch")
64
+ string_field(target, "hash", "branch")
58
65
  end
59
66
 
60
67
  sig { params(repo: String).returns(String) }
61
68
  def fetch_default_branch(repo)
62
69
  response = get(base_url + repo)
63
70
 
64
- JSON.parse(response.body).fetch("mainbranch").fetch("name")
71
+ root = parse_json_object(response.body, "repository")
72
+ main_branch = object_field(root, "mainbranch", "repository")
73
+ string_field(main_branch, "name", "repository")
65
74
  end
66
75
 
67
76
  sig do
@@ -70,7 +79,7 @@ module Dependabot
70
79
  commit: T.nilable(String),
71
80
  path: T.nilable(String)
72
81
  )
73
- .returns(T::Array[T::Hash[String, T.untyped]])
82
+ .returns(JsonObjects)
74
83
  end
75
84
  def fetch_repo_contents(repo, commit = nil, path = nil)
76
85
  raise "Commit is required if path provided!" if commit.nil? && path
@@ -81,7 +90,8 @@ module Dependabot
81
90
  api_path += "?pagelen=100"
82
91
  response = get(base_url + api_path)
83
92
 
84
- JSON.parse(response.body).fetch("values")
93
+ root = parse_json_object(response.body, "repository contents")
94
+ object_array_field(root, "values", "repository contents")
85
95
  end
86
96
 
87
97
  sig do
@@ -104,7 +114,7 @@ module Dependabot
104
114
  repo: String,
105
115
  branch_name: T.nilable(String)
106
116
  )
107
- .returns(T::Enumerator[T::Hash[String, T.untyped]])
117
+ .returns(T::Enumerator[JsonObject])
108
118
  end
109
119
  def commits(repo, branch_name = nil)
110
120
  commits_path = "#{repo}/commits/#{branch_name}?pagelen=100"
@@ -117,13 +127,13 @@ module Dependabot
117
127
  repo: String,
118
128
  branch_name: String
119
129
  )
120
- .returns(T::Hash[String, T.untyped])
130
+ .returns(JsonObject)
121
131
  end
122
132
  def branch(repo, branch_name)
123
133
  branch_path = "#{repo}/refs/branches/#{branch_name}"
124
134
  response = get(base_url + branch_path)
125
135
 
126
- JSON.parse(response.body)
136
+ parse_json_object(response.body, "branch")
127
137
  end
128
138
 
129
139
  sig do
@@ -133,7 +143,7 @@ module Dependabot
133
143
  target_branch: T.nilable(String),
134
144
  status: T::Array[String]
135
145
  )
136
- .returns(T::Array[T::Hash[String, T.untyped]])
146
+ .returns(JsonObjects)
137
147
  end
138
148
  def pull_requests(repo, source_branch, target_branch, status = %w(OPEN MERGED DECLINED SUPERSEDED))
139
149
  pr_path = "#{repo}/pullrequests?"
@@ -148,10 +158,14 @@ module Dependabot
148
158
  if source_branch.nil?
149
159
  source_branch_matches = true
150
160
  else
151
- pr_source_branch = pr.fetch("source").fetch("branch").fetch("name")
161
+ source = object_field(pr, "source", "pull request")
162
+ source_branch_details = object_field(source, "branch", "pull request")
163
+ pr_source_branch = string_field(source_branch_details, "name", "pull request")
152
164
  source_branch_matches = pr_source_branch == source_branch
153
165
  end
154
- pr_target_branch = pr.fetch("destination").fetch("branch").fetch("name")
166
+ destination = object_field(pr, "destination", "pull request")
167
+ target_branch_details = object_field(destination, "branch", "pull request")
168
+ pr_target_branch = string_field(target_branch_details, "name", "pull request")
155
169
  source_branch_matches && pr_target_branch == target_branch
156
170
  end
157
171
  end
@@ -262,12 +276,13 @@ module Dependabot
262
276
  def current_user
263
277
  base_url = "https://api.bitbucket.org/2.0/user?fields=uuid"
264
278
  response = get(base_url)
265
- JSON.parse(response.body).fetch("uuid")
279
+ root = parse_json_object(response.body, "current user")
280
+ string_field(root, "uuid", "current user")
266
281
  rescue Unauthorized
267
282
  nil
268
283
  end
269
284
 
270
- sig { params(repo: String).returns(T::Array[T::Hash[String, String]]) }
285
+ sig { params(repo: String).returns(T::Array[T::Hash[Symbol, String]]) }
271
286
  def default_reviewers(repo)
272
287
  current_uuid = current_user
273
288
  path = "#{repo}/default-reviewers?pagelen=100&fields=values.uuid,next"
@@ -278,18 +293,20 @@ module Dependabot
278
293
  reviewer_data = []
279
294
 
280
295
  default_reviewers.each do |reviewer|
281
- reviewer_data.append({ uuid: reviewer.fetch("uuid") }) unless current_uuid == reviewer.fetch("uuid")
296
+ uuid = string_field(reviewer, "uuid", "default reviewer")
297
+ reviewer_data.append({ uuid: uuid }) unless current_uuid == uuid
282
298
  end
283
299
 
284
300
  reviewer_data
285
301
  end
286
302
 
287
- sig { params(repo: String).returns(T::Array[T::Hash[String, String]]) }
303
+ sig { params(repo: String).returns(JsonObjects) }
288
304
  def tags(repo)
289
305
  path = "#{repo}/refs/tags?pagelen=100"
290
306
  response = get(base_url + path)
291
307
 
292
- JSON.parse(response.body).fetch("values")
308
+ root = parse_json_object(response.body, "tags")
309
+ object_array_field(root, "values", "tags")
293
310
  end
294
311
 
295
312
  sig do
@@ -298,13 +315,18 @@ module Dependabot
298
315
  previous_tag: String,
299
316
  new_tag: String
300
317
  )
301
- .returns(T::Array[T::Hash[String, T.untyped]])
318
+ .returns(JsonObjects)
302
319
  end
303
320
  def compare(repo, previous_tag, new_tag)
304
321
  path = "#{repo}/commits/?include=#{new_tag}&exclude=#{previous_tag}"
305
322
  response = get(base_url + path)
306
323
 
307
- JSON.parse(response.body).fetch("values")
324
+ root = parse_json_object(response.body, "commit comparison")
325
+ commits = object_array_field(root, "values", "commit comparison")
326
+ commits.each do |commit|
327
+ validate_string_paths(commit, "commit comparison", [%w(hash), %w(summary raw), %w(links html href)])
328
+ end
329
+ commits
308
330
  end
309
331
 
310
332
  sig { params(url: String).returns(Excon::Response) }
@@ -396,23 +418,27 @@ module Dependabot
396
418
  # response = post(url, body)
397
419
  # first_page = JSON.parse(response.body)
398
420
  # paginate(first_page)
399
- sig do
400
- type_parameters(:T)
401
- .params(page: T.all(T.type_parameter(:T), T::Hash[String, T.untyped]))
402
- .returns(T::Enumerator[T.type_parameter(:T)])
403
- end
421
+ sig { params(page: JsonObject).returns(T::Enumerator[JsonObject]) }
404
422
  def paginate(page)
405
423
  Enumerator.new do |yielder|
406
424
  loop do
407
- page.fetch("values", []).each { |value| yielder << value }
408
- break unless page.key?("next")
425
+ values = page["values"]
426
+ object_array(values, "paginated response").each { |value| yielder << value } if values
409
427
 
410
- next_page_url = page.fetch("next")
411
- page = T.cast(JSON.parse(get(next_page_url).body), T.all(T::Hash[String, T.untyped], T.type_parameter(:T)))
428
+ next_page = page["next"]
429
+ break unless next_page
430
+
431
+ next_page_url = string_value(next_page, "paginated response")
432
+ page = parse_json_object(get(next_page_url).body, "paginated response")
412
433
  end
413
434
  end
414
435
  end
415
436
 
437
+ sig { returns([String, String]) }
438
+ def response_identity
439
+ ["Bitbucket", base_url]
440
+ end
441
+
416
442
  sig { returns(T::Hash[String, String]) }
417
443
  attr_reader :auth_header
418
444
 
@@ -2,12 +2,13 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "delegate"
5
6
 
6
7
  require_relative "bitbucket"
7
8
 
8
9
  module Dependabot
9
10
  module Clients
10
- class BitbucketWithRetries
11
+ class BitbucketWithRetries < SimpleDelegator
11
12
  extend T::Sig
12
13
 
13
14
  RETRYABLE_ERRORS = T.let(
@@ -37,24 +38,22 @@ module Dependabot
37
38
  def initialize(credentials:, max_retries: 3)
38
39
  @max_retries = T.let(max_retries || 3, Integer)
39
40
  @client = T.let(Bitbucket.new(credentials: credentials), Dependabot::Clients::Bitbucket)
41
+ super(@client)
40
42
  end
41
43
 
42
44
  sig do
43
45
  params(
44
46
  method_name: T.any(Symbol, String),
45
- args: T.untyped,
46
- block: T.nilable(T.proc.returns(T.untyped))
47
+ args: Object,
48
+ block: T.nilable(Proc)
47
49
  )
48
- .returns(T.untyped)
50
+ .returns(T.anything)
49
51
  end
50
52
  def method_missing(method_name, *args, &block)
53
+ original_args = args
51
54
  retry_connection_failures do
52
- if @client.respond_to?(method_name)
53
- mutatable_args = args.map(&:dup)
54
- T.unsafe(@client).public_send(method_name, *mutatable_args, &block)
55
- else
56
- super
57
- end
55
+ args = original_args.map(&:dup)
56
+ super
58
57
  end
59
58
  end
60
59
 
@@ -66,7 +65,7 @@ module Dependabot
66
65
  .returns(T::Boolean)
67
66
  end
68
67
  def respond_to_missing?(method_name, include_private = false)
69
- @client.respond_to?(method_name) || super
68
+ @client.respond_to?(method_name, include_private)
70
69
  end
71
70
 
72
71
  sig do
@@ -81,7 +80,7 @@ module Dependabot
81
80
  yield
82
81
  rescue *RETRYABLE_ERRORS
83
82
  retry_attempt += 1
84
- retry_attempt <= @max_retries ? retry : raise
83
+ retry_attempt <= @max_retries ? retry : Kernel.raise
85
84
  end
86
85
  end
87
86
  end