dependabot-common 0.391.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 (40) 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 +89 -13
  11. data/lib/dependabot/credential.rb +12 -3
  12. data/lib/dependabot/dependency_requirement.rb +28 -15
  13. data/lib/dependabot/errors.rb +7 -0
  14. data/lib/dependabot/file_fetchers/base.rb +196 -140
  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/pull_request_creator/azure.rb +1 -1
  21. data/lib/dependabot/pull_request_creator/branch_name_template.rb +1 -1
  22. data/lib/dependabot/pull_request_creator/codecommit.rb +10 -12
  23. data/lib/dependabot/pull_request_creator/github.rb +55 -35
  24. data/lib/dependabot/pull_request_creator/gitlab.rb +37 -11
  25. data/lib/dependabot/pull_request_creator/labeler.rb +65 -15
  26. data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +21 -2
  27. data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +53 -13
  28. data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +20 -13
  29. data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +179 -78
  30. data/lib/dependabot/pull_request_updater/azure.rb +63 -11
  31. data/lib/dependabot/pull_request_updater/github.rb +44 -34
  32. data/lib/dependabot/pull_request_updater/gitlab.rb +23 -12
  33. data/lib/dependabot/pull_request_updater.rb +3 -3
  34. data/lib/dependabot/shared_helpers.rb +107 -39
  35. data/lib/dependabot/update_checkers/base.rb +9 -5
  36. data/lib/dependabot/update_checkers/version_filters.rb +5 -0
  37. data/lib/dependabot/update_checkers/vulnerability_audit.rb +282 -0
  38. data/lib/dependabot/workspace/git.rb +36 -5
  39. data/lib/dependabot.rb +1 -1
  40. metadata +6 -2
@@ -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"
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "sorbet-runtime"
5
+ require "uri"
5
6
  require "dependabot/pull_request_creator/message_builder"
6
7
 
7
8
  module Dependabot
@@ -53,19 +54,37 @@ module Dependabot
53
54
  &.fetch("repo", nil)
54
55
 
55
56
  source = if repo
56
- "https://github.com/#{repo}"
57
+ "#{qualified_reference_base_url}/#{repo}"
57
58
  elsif source_url
58
59
  source_url
59
60
  end
60
61
 
61
62
  if source
62
- "[#{repo ? (repo + tag) : tag}](#{source}/issues/#{number})"
63
+ "[#{repo ? (repo + tag) : tag}](#{source}#{issue_path(source)}/#{number})"
63
64
  else
64
65
  issue_link
65
66
  end
66
67
  end
67
68
  end
68
69
  end
70
+
71
+ private
72
+
73
+ sig { params(source: String).returns(String) }
74
+ def issue_path(source)
75
+ URI.parse(source).host&.downcase == "gitlab.com" ? "/-/work_items" : "/issues"
76
+ rescue URI::InvalidURIError
77
+ "/issues"
78
+ end
79
+
80
+ sig { returns(String) }
81
+ def qualified_reference_base_url
82
+ return "https://github.com" unless source_url
83
+
84
+ URI.parse(T.must(source_url)).host&.downcase == "gitlab.com" ? "https://gitlab.com" : "https://github.com"
85
+ rescue URI::InvalidURIError
86
+ "https://github.com"
87
+ end
69
88
  end
70
89
  end
71
90
  end
@@ -4,6 +4,7 @@
4
4
  require "commonmarker"
5
5
  require "sorbet-runtime"
6
6
  require "strscan"
7
+ require "uri"
7
8
  require "dependabot/pull_request_creator/message_builder"
8
9
 
9
10
  module Dependabot
@@ -13,6 +14,9 @@ module Dependabot
13
14
  extend T::Sig
14
15
 
15
16
  GITHUB_USERNAME = /[a-z0-9]+(-[a-z0-9]+)*/i
17
+ GITLAB_USERNAME = /[a-z0-9](?:[a-z0-9_.-]*[a-z0-9_-])?/i
18
+ GITHUB_HOST = "github.com"
19
+ GITLAB_HOST = "gitlab.com"
16
20
  GITHUB_REF_REGEX = %r{
17
21
  (?:https?://)?
18
22
  github\.com/(?<repo>#{GITHUB_USERNAME}/[^/\s]+)/
@@ -20,9 +24,6 @@ module Dependabot
20
24
  }x
21
25
  # [^/\s#]+ means one or more characters not matching (^) the class /, whitespace (\s), or #
22
26
  GITHUB_NWO_REGEX = %r{(?<repo>#{GITHUB_USERNAME}/[^/\s#]+)#(?<number>\d+)}
23
- MENTION_REGEX = %r{(?<![A-Za-z0-9`~])@#{GITHUB_USERNAME}/?}
24
- # regex to match a team mention on github
25
- TEAM_MENTION_REGEX = %r{(?<![A-Za-z0-9`~])@(?<org>#{GITHUB_USERNAME})/(?<team>#{GITHUB_USERNAME})/?}
26
27
  # End of string
27
28
  EOS_REGEX = /\z/
28
29
 
@@ -49,9 +50,18 @@ module Dependabot
49
50
  sig { returns(T.nilable(String)) }
50
51
  attr_reader :github_redirection_service
51
52
 
52
- sig { params(github_redirection_service: T.nilable(String)).void }
53
- def initialize(github_redirection_service:)
53
+ sig { returns(T.nilable(String)) }
54
+ attr_reader :metadata_source_url
55
+
56
+ sig do
57
+ params(
58
+ github_redirection_service: T.nilable(String),
59
+ metadata_source_url: T.nilable(String)
60
+ ).void
61
+ end
62
+ def initialize(github_redirection_service:, metadata_source_url: nil)
54
63
  @github_redirection_service = github_redirection_service
64
+ @metadata_source_url = metadata_source_url
55
65
  end
56
66
 
57
67
  sig { params(text: String, unsafe: T::Boolean, format_html: T::Boolean).returns(String) }
@@ -90,7 +100,7 @@ module Dependabot
90
100
  def sanitize_mentions(doc)
91
101
  doc.walk do |node|
92
102
  if node.type == :text &&
93
- node.string_content.match?(MENTION_REGEX)
103
+ node.string_content.match?(mention_regex)
94
104
  nodes = if parent_node_link?(node)
95
105
  build_mention_link_text_nodes(node.string_content)
96
106
  else
@@ -111,10 +121,10 @@ module Dependabot
111
121
  def sanitize_team_mentions(doc)
112
122
  doc.walk do |node|
113
123
  if node.type == :text &&
114
- node.string_content.match?(TEAM_MENTION_REGEX)
124
+ node.string_content.match?(team_mention_regex)
115
125
  if parent_node_link?(node)
116
126
  # Preserve text node formatting while preventing notifications with zero-width space
117
- node.string_content = node.string_content.gsub(TEAM_MENTION_REGEX) do |match|
127
+ node.string_content = node.string_content.gsub(team_mention_regex) do |match|
118
128
  insert_zero_width_space_in_mention(match)
119
129
  end
120
130
  else
@@ -189,9 +199,9 @@ module Dependabot
189
199
  scan = StringScanner.new(text)
190
200
 
191
201
  until scan.eos?
192
- line = scan.scan_until(MENTION_REGEX) ||
202
+ line = scan.scan_until(mention_regex) ||
193
203
  scan.scan_until(EOS_REGEX)
194
- line_match = T.must(line).match(MENTION_REGEX)
204
+ line_match = T.must(line).match(mention_regex)
195
205
  mention = line_match&.to_s
196
206
  text_node = Commonmarker::Node.new(:text)
197
207
 
@@ -199,7 +209,7 @@ module Dependabot
199
209
  text_node.string_content = line_match.pre_match
200
210
  nodes << text_node
201
211
  nodes << create_link_node(
202
- "https://github.com/#{mention.tr('@', '')}", mention.to_s
212
+ "#{mention_profile_base_url}/#{mention.delete_prefix('@')}", mention.to_s
203
213
  )
204
214
  else
205
215
  text_node.string_content = line
@@ -216,9 +226,9 @@ module Dependabot
216
226
 
217
227
  scan = StringScanner.new(text)
218
228
  until scan.eos?
219
- line = scan.scan_until(TEAM_MENTION_REGEX) ||
229
+ line = scan.scan_until(team_mention_regex) ||
220
230
  scan.scan_until(EOS_REGEX)
221
- line_match = T.must(line).match(TEAM_MENTION_REGEX)
231
+ line_match = T.must(line).match(team_mention_regex)
222
232
  mention = line_match&.to_s
223
233
  text_node = Commonmarker::Node.new(:text)
224
234
 
@@ -235,6 +245,36 @@ module Dependabot
235
245
  nodes
236
246
  end
237
247
 
248
+ sig { returns(Regexp) }
249
+ def mention_regex
250
+ %r{(?<![A-Za-z0-9`~])@#{mention_username_regex}/?}
251
+ end
252
+
253
+ sig { returns(Regexp) }
254
+ def team_mention_regex
255
+ username_regex = mention_username_regex
256
+ %r{(?<![A-Za-z0-9`~])@(?<org>#{username_regex})/(?<team>#{username_regex})/?}
257
+ end
258
+
259
+ sig { returns(Regexp) }
260
+ def mention_username_regex
261
+ metadata_source_host == GITLAB_HOST ? GITLAB_USERNAME : GITHUB_USERNAME
262
+ end
263
+
264
+ sig { returns(String) }
265
+ def mention_profile_base_url
266
+ "https://#{metadata_source_host == GITLAB_HOST ? GITLAB_HOST : GITHUB_HOST}"
267
+ end
268
+
269
+ sig { returns(T.nilable(String)) }
270
+ def metadata_source_host
271
+ return unless metadata_source_url
272
+
273
+ URI.parse(T.must(metadata_source_url)).host&.downcase
274
+ rescue URI::InvalidURIError
275
+ nil
276
+ end
277
+
238
278
  sig { params(text: String).returns(T::Array[Commonmarker::Node]) }
239
279
  def build_mention_link_text_nodes(text)
240
280
  code_node = Commonmarker::Node.new(:code)
@@ -85,13 +85,11 @@ module Dependabot
85
85
  def vulnerabilities_cascade
86
86
  return "" unless vulnerabilities_fixed&.any?
87
87
 
88
- msg = ""
89
- T.must(vulnerabilities_fixed).each do |v|
90
- msg += serialized_vulnerability_details(v)
88
+ msg = T.must(vulnerabilities_fixed).map do |vulnerability|
89
+ details = sanitize_template_tags(serialized_vulnerability_details(vulnerability))
90
+ sanitize_links_and_mentions(details, metadata_source_url: vulnerability["source_url"])
91
91
  end
92
-
93
- msg = sanitize_template_tags(msg)
94
- msg = sanitize_links_and_mentions(msg)
92
+ msg = msg.join
95
93
 
96
94
  build_details_tag(summary: "Vulnerabilities fixed", body: msg)
97
95
  end
@@ -109,7 +107,7 @@ module Dependabot
109
107
  base_url: source_url + "/blob/HEAD/"
110
108
  )
111
109
  msg = sanitize_template_tags(msg)
112
- msg = sanitize_links_and_mentions(msg)
110
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: releases_url)
113
111
 
114
112
  build_details_tag(summary: "Release notes", body: msg)
115
113
  end
@@ -125,7 +123,7 @@ module Dependabot
125
123
  msg = link_issues(text: msg)
126
124
  msg = fix_relative_links(text: msg, base_url: changelog_url)
127
125
  msg = sanitize_template_tags(msg)
128
- msg = sanitize_links_and_mentions(msg)
126
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: changelog_url)
129
127
 
130
128
  build_details_tag(summary: "Changelog", body: msg)
131
129
  end
@@ -141,7 +139,7 @@ module Dependabot
141
139
  msg = link_issues(text: msg)
142
140
  msg = fix_relative_links(text: msg, base_url: upgrade_guide_url)
143
141
  msg = sanitize_template_tags(msg)
144
- msg = sanitize_links_and_mentions(msg)
142
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: upgrade_guide_url)
145
143
 
146
144
  build_details_tag(summary: "Upgrade guide", body: msg)
147
145
  end
@@ -170,7 +168,7 @@ module Dependabot
170
168
  "- See full diff in [compare view](#{commits_url})\n"
171
169
  end
172
170
  msg = link_issues(text: msg)
173
- msg = sanitize_links_and_mentions(msg)
171
+ msg = sanitize_links_and_mentions(msg, metadata_source_url: commits_url)
174
172
 
175
173
  build_details_tag(summary: "Commits", body: msg)
176
174
  end
@@ -317,10 +315,19 @@ module Dependabot
317
315
  !%w(bitbucket codecommit).include?(source.provider)
318
316
  end
319
317
 
320
- sig { params(text: String, unsafe: T::Boolean).returns(String) }
321
- def sanitize_links_and_mentions(text, unsafe: false)
318
+ sig do
319
+ params(
320
+ text: String,
321
+ unsafe: T::Boolean,
322
+ metadata_source_url: T.nilable(String)
323
+ ).returns(String)
324
+ end
325
+ def sanitize_links_and_mentions(text, unsafe: false, metadata_source_url: source_url)
322
326
  LinkAndMentionSanitizer
323
- .new(github_redirection_service: github_redirection_service)
327
+ .new(
328
+ github_redirection_service: github_redirection_service,
329
+ metadata_source_url: metadata_source_url
330
+ )
324
331
  .sanitize_links_and_mentions(text: text, unsafe: unsafe, format_html: source_provider_supports_html?)
325
332
  end
326
333