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,10 +1,11 @@
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
 
7
7
  require "dependabot/clients/github_with_retries"
8
+ require "dependabot/clients/github_resource_parser"
8
9
  require "dependabot/pull_request_creator/commit_signer"
9
10
  require "dependabot/pull_request_updater"
10
11
 
@@ -12,6 +13,7 @@ module Dependabot
12
13
  class PullRequestUpdater
13
14
  class Github
14
15
  extend T::Sig
16
+ include Dependabot::Clients::GithubResourceParser
15
17
 
16
18
  sig { returns(Dependabot::Source) }
17
19
  attr_reader :source
@@ -31,7 +33,7 @@ module Dependabot
31
33
  sig { returns(Integer) }
32
34
  attr_reader :pull_request_number
33
35
 
34
- sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
36
+ sig { returns(T.nilable(T::Hash[Symbol, String])) }
35
37
  attr_reader :author_details
36
38
 
37
39
  sig { returns(T.nilable(String)) }
@@ -45,7 +47,7 @@ module Dependabot
45
47
  files: T::Array[Dependabot::DependencyFile],
46
48
  credentials: T::Array[Dependabot::Credential],
47
49
  pull_request_number: Integer,
48
- author_details: T.nilable(T::Hash[Symbol, T.untyped]),
50
+ author_details: T.nilable(T::Hash[Symbol, String]),
49
51
  signature_key: T.nilable(String),
50
52
  commit_message: T.nilable(String)
51
53
  )
@@ -76,7 +78,7 @@ module Dependabot
76
78
  sig { returns(T.nilable(Sawyer::Resource)) }
77
79
  def update
78
80
  return unless pull_request_exists?
79
- return unless branch_exists?(pull_request.head.ref)
81
+ return unless branch_exists?(github_string(github_resource(pull_request, :head, "pull request"), :ref, "head"))
80
82
 
81
83
  commit = create_commit
82
84
  branch = update_branch(commit)
@@ -88,8 +90,10 @@ module Dependabot
88
90
 
89
91
  sig { void }
90
92
  def update_pull_request_target_branch
91
- target_branch = source.branch || pull_request.base.repo.default_branch
92
- return if target_branch == pull_request.base.ref
93
+ base = github_resource(pull_request, :base, "pull request")
94
+ base_repo = github_resource(base, :repo, "pull request base")
95
+ target_branch = source.branch || github_string(base_repo, :default_branch, "base repository")
96
+ return if target_branch == github_string(base, :ref, "pull request base")
93
97
 
94
98
  github_client_for_source.update_pull_request(
95
99
  source.repo,
@@ -125,6 +129,11 @@ module Dependabot
125
129
  )
126
130
  end
127
131
 
132
+ sig { returns(String) }
133
+ def github_response_source
134
+ source.url
135
+ end
136
+
128
137
  sig { returns(T::Boolean) }
129
138
  def pull_request_exists?
130
139
  pull_request
@@ -133,7 +142,7 @@ module Dependabot
133
142
  false
134
143
  end
135
144
 
136
- sig { returns(T.untyped) }
145
+ sig { returns(Sawyer::Resource) }
137
146
  def pull_request
138
147
  @pull_request ||=
139
148
  T.let(
@@ -141,7 +150,7 @@ module Dependabot
141
150
  source.repo,
142
151
  pull_request_number
143
152
  ),
144
- T.untyped
153
+ T.nilable(Sawyer::Resource)
145
154
  )
146
155
  end
147
156
 
@@ -153,22 +162,24 @@ module Dependabot
153
162
  false
154
163
  end
155
164
 
156
- sig { returns(T.untyped) }
165
+ sig { returns(Sawyer::Resource) }
157
166
  def create_commit
158
167
  tree = create_tree
159
168
 
160
- options = author_details&.any? ? { author: author_details } : {}
169
+ options = T.let({}, T::Hash[Symbol, Object])
170
+ author = author_details&.dup
171
+ options[:author] = author if author&.any?
161
172
 
162
- if options[:author]&.any? && signature_key
163
- options[:author][:date] = Time.now.utc.iso8601
164
- options[:signature] = commit_signature(tree, options[:author])
173
+ if author&.any? && signature_key
174
+ author[:date] = Time.now.utc.iso8601
175
+ options[:signature] = commit_signature(tree, author)
165
176
  end
166
177
 
167
178
  begin
168
179
  github_client_for_source.create_commit(
169
180
  source.repo,
170
181
  commit_message,
171
- tree.sha,
182
+ github_string(tree, :sha, "tree"),
172
183
  base_commit,
173
184
  options
174
185
  )
@@ -186,7 +197,7 @@ module Dependabot
186
197
  end
187
198
  end
188
199
 
189
- sig { returns(T.untyped) }
200
+ sig { returns(Sawyer::Resource) }
190
201
  def create_tree
191
202
  file_trees = files.map do |file|
192
203
  if file.type == "submodule"
@@ -238,12 +249,13 @@ module Dependabot
238
249
  T::Array[Regexp]
239
250
  )
240
251
 
241
- sig { params(commit: T.untyped).returns(T.untyped) }
252
+ sig { params(commit: Sawyer::Resource).returns(T.nilable(Sawyer::Resource)) }
242
253
  def update_branch(commit)
254
+ head = github_resource(pull_request, :head, "pull request")
243
255
  github_client_for_source.update_ref(
244
256
  source.repo,
245
- "heads/" + pull_request.head.ref,
246
- commit.sha,
257
+ "heads/" + github_string(head, :ref, "pull request head"),
258
+ github_string(commit, :sha, "commit"),
247
259
  true
248
260
  )
249
261
  rescue Octokit::UnprocessableEntity => e
@@ -262,45 +274,43 @@ module Dependabot
262
274
  return msg unless msg.nil? || msg.empty?
263
275
 
264
276
  fallback_message =
265
- "#{pull_request.title}" \
277
+ "#{github_string(pull_request, :title, 'pull request')}" \
266
278
  "\n\n" \
267
279
  "Dependabot couldn't find the original pull request head commit, " \
268
280
  "#{old_commit}."
269
281
 
270
282
  # Take the commit message from the old commit. If the old commit can't
271
283
  # be found, use the PR title as the commit message.
272
- commit_being_updated&.message || fallback_message
284
+ commit = commit_being_updated
285
+ (commit && github_optional_string(commit, :message, "commit")) || fallback_message
273
286
  end
274
287
 
275
- sig { returns(T.untyped) }
288
+ sig { returns(T.nilable(Sawyer::Resource)) }
276
289
  def commit_being_updated
277
290
  return @commit_being_updated if defined?(@commit_being_updated)
278
291
 
279
292
  @commit_being_updated =
280
293
  T.let(
281
- if pull_request.commits == 1
294
+ if github_integer(pull_request, :commits, "pull request") == 1
295
+ head = github_resource(pull_request, :head, "pull request")
282
296
  github_client_for_source
283
- .git_commit(source.repo, pull_request.head.sha)
297
+ .git_commit(source.repo, github_string(head, :sha, "pull request head"))
284
298
  else
285
- commits =
286
- T.unsafe(
287
- github_client_for_source
288
- .pull_request_commits(source.repo, pull_request_number)
289
- )
290
-
291
- commit = commits.find { |c| c.sha == old_commit }
292
- commit&.commit
299
+ commits = github_client_for_source.pull_request_commits(source.repo, pull_request_number)
300
+
301
+ commit = commits.find { |candidate| github_string(candidate, :sha, "pull request commit") == old_commit }
302
+ commit && github_resource(commit, :commit, "pull request commit")
293
303
  end,
294
- T.untyped
304
+ T.nilable(Sawyer::Resource)
295
305
  )
296
306
  end
297
307
 
298
- sig { params(tree: T.untyped, author_details_with_date: T::Hash[Symbol, T.untyped]).returns(String) }
308
+ sig { params(tree: Sawyer::Resource, author_details_with_date: T::Hash[Symbol, String]).returns(String) }
299
309
  def commit_signature(tree, author_details_with_date)
300
310
  PullRequestCreator::CommitSigner.new(
301
311
  author_details: author_details_with_date,
302
312
  commit_message: commit_message,
303
- tree_sha: tree.sha,
313
+ tree_sha: github_string(tree, :sha, "tree"),
304
314
  parent_sha: base_commit,
305
315
  signature_key: T.must(signature_key)
306
316
  ).signature
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "gitlab"
@@ -7,6 +7,7 @@ require "sorbet-runtime"
7
7
  require "dependabot/clients/gitlab_with_retries"
8
8
  require "dependabot/credential"
9
9
  require "dependabot/pull_request_creator"
10
+ require "dependabot/errors"
10
11
 
11
12
  module Dependabot
12
13
  class PullRequestUpdater
@@ -67,10 +68,10 @@ module Dependabot
67
68
  sig { returns(T.nilable(String)) }
68
69
  def update
69
70
  return unless merge_request_exists?
70
- return unless branch_exists?(merge_request.source_branch)
71
+ return unless branch_exists?(gitlab_string(merge_request, "source_branch", "merge request"))
71
72
 
72
73
  create_commit
73
- merge_request.source_branch
74
+ gitlab_string(merge_request, "source_branch", "merge request")
74
75
  end
75
76
 
76
77
  private
@@ -83,14 +84,14 @@ module Dependabot
83
84
  false
84
85
  end
85
86
 
86
- sig { returns(T.untyped) }
87
+ sig { returns(::Gitlab::ObjectifiedHash) }
87
88
  def merge_request
88
89
  @merge_request ||= T.let(
89
- T.unsafe(gitlab_client_for_source).merge_request(
90
- target_project_id || source.repo,
90
+ gitlab_client_for_source.merge_request(
91
+ (target_project_id || source.repo).to_s,
91
92
  pull_request_number
92
93
  ),
93
- T.untyped
94
+ T.nilable(::Gitlab::ObjectifiedHash)
94
95
  )
95
96
  end
96
97
 
@@ -113,8 +114,7 @@ module Dependabot
113
114
  false
114
115
  end
115
116
 
116
- # TODO: This needs to be typed when the underlying client is
117
- sig { returns(T.untyped) }
117
+ sig { returns(::Gitlab::ObjectifiedHash) }
118
118
  def commit_being_updated
119
119
  gitlab_client_for_source.commit(source.repo, old_commit)
120
120
  end
@@ -123,11 +123,22 @@ module Dependabot
123
123
  def create_commit
124
124
  gitlab_client_for_source.create_commit(
125
125
  source.repo,
126
- merge_request.source_branch,
127
- commit_being_updated.title,
126
+ gitlab_string(merge_request, "source_branch", "merge request"),
127
+ gitlab_string(commit_being_updated, "title", "commit"),
128
128
  files,
129
129
  force: true,
130
- start_branch: merge_request.target_branch
130
+ start_branch: gitlab_string(merge_request, "target_branch", "merge request")
131
+ )
132
+ end
133
+
134
+ sig { params(resource: ::Gitlab::ObjectifiedHash, key: String, context: String).returns(String) }
135
+ def gitlab_string(resource, key, context)
136
+ value = T.cast(resource[key], Object)
137
+ return value if value.is_a?(String)
138
+
139
+ raise Dependabot::PrivateSourceBadResponse.new(
140
+ source.url,
141
+ "Malformed GitLab response: #{context} #{key} must be a string"
131
142
  )
132
143
  end
133
144
  end
@@ -11,6 +11,8 @@ module Dependabot
11
11
  class PullRequestUpdater
12
12
  extend T::Sig
13
13
 
14
+ UpdateResult = T.type_alias { T.nilable(T.any(Sawyer::Resource, String)) }
15
+
14
16
  class BranchProtected < StandardError; end
15
17
 
16
18
  sig { returns(Dependabot::Source) }
@@ -82,9 +84,7 @@ module Dependabot
82
84
  @provider_metadata = provider_metadata
83
85
  end
84
86
 
85
- # TODO: Each implementation returns a client-specific type.
86
- # We should standardise this to return a `Dependabot::Branch` type instead.
87
- sig { returns(T.untyped) } # rubocop:disable Sorbet/ForbidTUntyped
87
+ sig { returns(UpdateResult) }
88
88
  def update
89
89
  case source.provider
90
90
  when "github" then github_updater.update
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "digest"
@@ -23,6 +23,11 @@ module Dependabot
23
23
  module SharedHelpers # rubocop:disable Metrics/ModuleLength
24
24
  extend T::Sig
25
25
 
26
+ Command = T.type_alias { T.any(String, T::Array[String]) }
27
+ CommandArguments = T.type_alias { T::Array[T.any(String, T::Array[String])] }
28
+ ErrorContext = T.type_alias { T::Hash[Symbol, Object] }
29
+ ExconOptions = T.type_alias { T::Hash[Symbol, Object] }
30
+ HelperResponse = T.type_alias { T::Hash[String, Object] }
26
31
  USER_AGENT = T.let(
27
32
  "dependabot-core/#{Dependabot::VERSION} " \
28
33
  "#{Excon::USER_AGENT} ruby/#{RUBY_VERSION} " \
@@ -91,7 +96,7 @@ module Dependabot
91
96
  sig { returns(String) }
92
97
  attr_reader :error_class
93
98
 
94
- sig { returns(T::Hash[Symbol, T.untyped]) }
99
+ sig { returns(ErrorContext) }
95
100
  attr_reader :error_context
96
101
 
97
102
  sig { returns(T.nilable(T::Array[String])) }
@@ -100,7 +105,7 @@ module Dependabot
100
105
  sig do
101
106
  params(
102
107
  message: String,
103
- error_context: T::Hash[Symbol, String],
108
+ error_context: ErrorContext,
104
109
  error_class: T.nilable(String),
105
110
  trace: T.nilable(T::Array[String])
106
111
  ).void
@@ -109,11 +114,12 @@ module Dependabot
109
114
  super(message)
110
115
  @error_class = T.let(error_class || "HelperSubprocessFailed", String)
111
116
  @error_context = error_context
112
- @fingerprint = T.let(error_context[:fingerprint] || error_context[:command], T.nilable(String))
117
+ fingerprint = error_context[:fingerprint] || error_context[:command]
118
+ @fingerprint = T.let(fingerprint.is_a?(String) ? fingerprint : nil, T.nilable(String))
113
119
  @trace = trace
114
120
  end
115
121
 
116
- sig { override.returns(T::Hash[Symbol, T.untyped]) }
122
+ sig { override.returns(ErrorContext) }
117
123
  def sentry_context
118
124
  { fingerprint: [@fingerprint], extra: @error_context.except(:stderr_output, :fingerprint) }
119
125
  end
@@ -125,20 +131,40 @@ module Dependabot
125
131
  CommandHelpers.escape_command(command)
126
132
  end
127
133
 
134
+ sig do
135
+ params(command: Command, allow_unsafe_shell_command: T::Boolean)
136
+ .returns([CommandArguments, String])
137
+ end
138
+ def self.prepare_command(command, allow_unsafe_shell_command:)
139
+ unless command.is_a?(Array)
140
+ prepared_command = allow_unsafe_shell_command ? command : escape_command(command)
141
+ return [[prepared_command], prepared_command]
142
+ end
143
+
144
+ raise ArgumentError, "command must not be empty" if command.empty?
145
+ if allow_unsafe_shell_command
146
+ raise ArgumentError, "allow_unsafe_shell_command cannot be used with an argument vector"
147
+ end
148
+
149
+ executable = T.must(command.first)
150
+ [[[executable, executable], *command.drop(1)], Shellwords.join(command)]
151
+ end
152
+ private_class_method :prepare_command
153
+
128
154
  # rubocop:disable Metrics/MethodLength
129
155
  # rubocop:disable Metrics/AbcSize
130
156
  sig do
131
157
  params(
132
158
  command: String,
133
159
  function: String,
134
- args: T.untyped,
160
+ args: Object,
135
161
  env: T.nilable(T::Hash[String, String]),
136
162
  stderr_to_stdout: T::Boolean,
137
163
  allow_unsafe_shell_command: T::Boolean,
138
164
  error_class: T.class_of(HelperSubprocessFailed),
139
165
  timeout: Integer
140
166
  )
141
- .returns(T.untyped)
167
+ .returns(Object)
142
168
  end
143
169
  def self.run_helper_subprocess(
144
170
  command:,
@@ -151,7 +177,7 @@ module Dependabot
151
177
  timeout: CommandHelpers::TIMEOUTS::DEFAULT
152
178
  )
153
179
  start = Time.now
154
- stdin_data = JSON.dump(function: function, args: args)
180
+ stdin_data = T.cast(JSON.dump(function: function, args: args), String)
155
181
  cmd = allow_unsafe_shell_command ? command : escape_command(command)
156
182
 
157
183
  # NOTE: For debugging native helpers in specs and dry-run: outputs the
@@ -160,7 +186,8 @@ module Dependabot
160
186
  if ENV["DEBUG_FUNCTION"] == function
161
187
  puts helper_subprocess_bash_command(stdin_data: stdin_data, command: cmd, env: env)
162
188
  # Pause execution so we can run helpers inside the temporary directory
163
- T.unsafe(self).debugger
189
+ require "debug"
190
+ binding.break # rubocop:disable Lint/Debugger
164
191
  end
165
192
 
166
193
  env_cmd = [env, cmd].compact
@@ -199,25 +226,58 @@ module Dependabot
199
226
  check_out_of_memory_error(stderr, error_context, error_class)
200
227
 
201
228
  begin
202
- response = JSON.parse(stdout)
229
+ response = parse_helper_response(stdout)
203
230
  return response["result"] if process&.success?
204
231
 
232
+ message, helper_error_class, trace = parse_helper_error(response)
233
+
205
234
  raise error_class.new(
206
- message: response["error"],
207
- error_class: response["error_class"],
235
+ message: message,
236
+ error_class: helper_error_class,
208
237
  error_context: error_context,
209
- trace: response["trace"]
238
+ trace: trace
210
239
  )
211
240
  rescue JSON::ParserError
212
241
  raise handle_json_parse_error(stdout, stderr, error_context, error_class)
242
+ rescue TypeError => e
243
+ raise error_class.new(
244
+ message: e.message,
245
+ error_class: e.class.name,
246
+ error_context: error_context
247
+ )
213
248
  end
214
249
  end
215
250
 
251
+ sig { params(stdout: String).returns(HelperResponse) }
252
+ def self.parse_helper_response(stdout)
253
+ T.cast(JSON.parse(stdout), HelperResponse)
254
+ end
255
+ private_class_method :parse_helper_response
256
+
257
+ sig { params(response: HelperResponse).returns([String, T.nilable(String), T.nilable(T::Array[String])]) }
258
+ def self.parse_helper_error(response)
259
+ message = response["error"]
260
+ raise TypeError, "helper error must be a string" unless message.is_a?(String)
261
+
262
+ helper_error_class = response["error_class"]
263
+ unless helper_error_class.nil? || helper_error_class.is_a?(String)
264
+ raise TypeError, "helper error class must be a string or nil"
265
+ end
266
+
267
+ trace = response["trace"]
268
+ unless trace.nil? || (trace.is_a?(Array) && trace.all?(String))
269
+ raise TypeError, "helper trace must be an array of strings or nil"
270
+ end
271
+
272
+ [message, helper_error_class, T.cast(response["trace"], T.nilable(T::Array[String]))]
273
+ end
274
+ private_class_method :parse_helper_error
275
+
216
276
  sig do
217
277
  params(
218
278
  stdout: String,
219
279
  stderr: String,
220
- error_context: T::Hash[Symbol, T.untyped],
280
+ error_context: ErrorContext,
221
281
  error_class: T.class_of(HelperSubprocessFailed)
222
282
  )
223
283
  .returns(HelperSubprocessFailed)
@@ -243,7 +303,7 @@ module Dependabot
243
303
  sig do
244
304
  params(
245
305
  stderr: T.nilable(String),
246
- error_context: T::Hash[Symbol, String],
306
+ error_context: ErrorContext,
247
307
  error_class: T.class_of(HelperSubprocessFailed)
248
308
  ).void
249
309
  end
@@ -272,7 +332,7 @@ module Dependabot
272
332
  }.merge(headers)
273
333
  end
274
334
 
275
- sig { params(options: T.nilable(T::Hash[Symbol, T.untyped])).returns(T::Hash[Symbol, T.untyped]) }
335
+ sig { params(options: T.nilable(ExconOptions)).returns(ExconOptions) }
276
336
  def self.excon_defaults(options = nil)
277
337
  options ||= {}
278
338
  headers = T.cast(options.delete(:headers), T.nilable(T::Hash[String, String]))
@@ -343,7 +403,6 @@ module Dependabot
343
403
  File.join(__dir__, "../../bin/git-credential-store-immutable")
344
404
  end
345
405
 
346
- # rubocop:disable Metrics/PerceivedComplexity
347
406
  sig do
348
407
  params(
349
408
  credentials: T::Array[Dependabot::Credential],
@@ -396,27 +455,35 @@ module Dependabot
396
455
  github_credentials +
397
456
  [github_credential].compact
398
457
 
399
- # Build the content for our credentials file
400
- git_store_content = ""
401
- deduped_credentials.each do |cred|
402
- next unless cred["type"] == "git_source"
403
- next unless cred["host"]
458
+ File.write(git_store_path, git_store_content(deduped_credentials))
459
+ end
460
+ # rubocop:enable Metrics/AbcSize
404
461
 
405
- has_creds = cred["username"] && cred["password"]
462
+ sig { params(credentials: T::Array[Credential]).returns(String) }
463
+ def self.git_store_content(credentials)
464
+ credentials.each_with_object(+"") do |credential, content|
465
+ next unless credential["type"] == "git_source"
406
466
 
407
- # Build authenticated URL with credentials if available
408
- creds = has_creds ? "#{cred.fetch('username')}:#{cred.fetch('password')}@" : ""
409
- authenticated_url = "https://#{creds}#{cred.fetch('host')}"
467
+ host = credential["host"]
468
+ next unless host
410
469
 
411
- git_store_content += authenticated_url + "\n"
412
- configure_git_to_use_https(cred.fetch("host"))
470
+ content << authenticated_git_url(
471
+ username: credential["username"],
472
+ password: credential["password"],
473
+ host: host
474
+ ) << "\n"
475
+ configure_git_to_use_https(host)
413
476
  end
477
+ end
478
+ private_class_method :git_store_content
414
479
 
415
- # Save the file
416
- File.write(git_store_path, git_store_content)
480
+ sig { params(username: T.nilable(String), password: T.nilable(String), host: String).returns(String) }
481
+ def self.authenticated_git_url(username:, password:, host:)
482
+ credentials = username && password ? "#{username}:#{password}@" : ""
483
+
484
+ "https://#{credentials}#{host}"
417
485
  end
418
- # rubocop:enable Metrics/AbcSize
419
- # rubocop:enable Metrics/PerceivedComplexity
486
+ private_class_method :authenticated_git_url
420
487
 
421
488
  sig { params(host: String).void }
422
489
  def self.configure_git_to_use_https(host)
@@ -461,10 +528,9 @@ module Dependabot
461
528
  safe_directories
462
529
  end
463
530
 
464
- # rubocop:disable Metrics/PerceivedComplexity
465
531
  sig do
466
532
  params(
467
- command: String,
533
+ command: Command,
468
534
  allow_unsafe_shell_command: T::Boolean,
469
535
  cwd: T.nilable(String),
470
536
  env: T.nilable(T::Hash[String, String]),
@@ -485,14 +551,17 @@ module Dependabot
485
551
  output_observer: nil
486
552
  )
487
553
  start = Time.now
488
- cmd = allow_unsafe_shell_command ? command : escape_command(command)
554
+ command_args, command_for_output = prepare_command(
555
+ command,
556
+ allow_unsafe_shell_command: allow_unsafe_shell_command
557
+ )
489
558
 
490
- puts cmd if ENV["DEBUG_HELPERS"] == "true"
559
+ puts command_for_output if ENV["DEBUG_HELPERS"] == "true"
491
560
 
492
561
  opts = {}
493
562
  opts[:chdir] = cwd if cwd
494
563
 
495
- env_cmd = [env || {}, cmd, opts].compact
564
+ env_cmd = [env || {}, *command_args, opts].compact
496
565
  kwargs = {
497
566
  stderr_to_stdout: stderr_to_stdout,
498
567
  timeout: timeout
@@ -511,7 +580,7 @@ module Dependabot
511
580
  return stdout || "" if process&.success?
512
581
 
513
582
  error_context = {
514
- command: cmd,
583
+ command: command_for_output,
515
584
  fingerprint: fingerprint,
516
585
  time_taken: time_taken,
517
586
  process_exit_value: process.to_s
@@ -524,7 +593,6 @@ module Dependabot
524
593
  error_context: error_context
525
594
  )
526
595
  end
527
- # rubocop:enable Metrics/PerceivedComplexity
528
596
 
529
597
  sig { params(stderr: T.nilable(String), error_context: T::Hash[Symbol, String]).void }
530
598
  def self.check_out_of_disk_memory_error(stderr, error_context)
@@ -9,6 +9,7 @@ require "dependabot/security_advisory"
9
9
  require "dependabot/utils"
10
10
  require "dependabot/package/release_cooldown_options"
11
11
  require "dependabot/file_filtering"
12
+ require "dependabot/update_checkers/vulnerability_audit"
12
13
 
13
14
  module Dependabot
14
15
  module UpdateCheckers
@@ -168,16 +169,19 @@ module Dependabot
168
169
 
169
170
  # Finds any dependencies in the lockfile that have a subdependency on the
170
171
  # given dependency that do not satisfy the target_version.
171
- # @return [Array<Hash{String => String}]
172
+ # @return [Array<Hash{String => Object}]
172
173
  # name [String] the blocking dependencies name
173
174
  # version [String] the version of the blocking dependency
174
175
  # requirement [String] the requirement on the target_dependency
175
- sig { overridable.returns(T::Array[T::Hash[String, String]]) }
176
+ sig { overridable.returns(T::Array[Dependabot::UpdateCheckers::Conflict]) }
176
177
  def conflicting_dependencies
177
178
  [] # return an empty array for ecosystems that don't support this yet
178
179
  end
179
180
 
180
- sig { params(_updated_version: String).returns(T.nilable(String)) }
181
+ sig do
182
+ params(_updated_version: T.any(String, Gem::Version))
183
+ .returns(T.nilable(T.any(String, Gem::Version)))
184
+ end
181
185
  def latest_resolvable_previous_version(_updated_version)
182
186
  dependency.version
183
187
  end
@@ -253,7 +257,7 @@ module Dependabot
253
257
  sig { returns(Dependabot::Dependency) }
254
258
  def updated_dependency_without_unlock
255
259
  version = latest_resolvable_version_with_no_unlock.to_s
256
- previous_version = latest_resolvable_previous_version(version)
260
+ previous_version = latest_resolvable_previous_version(version)&.to_s
257
261
 
258
262
  Dependency.new(
259
263
  name: dependency.name,
@@ -270,7 +274,7 @@ module Dependabot
270
274
  sig { returns(Dependabot::Dependency) }
271
275
  def updated_dependency_with_own_req_unlock
272
276
  version = preferred_resolvable_version.to_s
273
- previous_version = latest_resolvable_previous_version(version)
277
+ previous_version = latest_resolvable_previous_version(version)&.to_s
274
278
 
275
279
  Dependency.new(
276
280
  name: dependency.name,
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "dependabot/security_advisory"
5
5
  require "dependabot/package/package_release"
6
+ require "dependabot/git_tag_details"
6
7
  require "sorbet-runtime"
7
8
 
8
9
  module Dependabot
@@ -22,6 +23,7 @@ module Dependabot
22
23
  T.any(
23
24
  T.all(T.type_parameter(:T), Gem::Version),
24
25
  T.all(T.type_parameter(:T), T::Hash[Symbol, Gem::Version]),
26
+ T.all(T.type_parameter(:T), Dependabot::GitTagDetails),
25
27
  T.all(T.type_parameter(:T), Dependabot::Package::PackageRelease)
26
28
  )],
27
29
  security_advisories: T::Array[SecurityAdvisory]
@@ -35,6 +37,9 @@ module Dependabot
35
37
  a.vulnerable?(v)
36
38
  elsif v.is_a?(Dependabot::Package::PackageRelease)
37
39
  a.vulnerable?(v.version)
40
+ elsif v.is_a?(Dependabot::GitTagDetails)
41
+ version = v.version
42
+ version ? a.vulnerable?(version) : false
38
43
  else
39
44
  a.vulnerable?(v.fetch(:version))
40
45
  end