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,7 +1,6 @@
1
- # typed: strict
1
+ # typed: strong
2
2
  # frozen_string_literal: true
3
3
 
4
- require "open3"
5
4
  require "timeout"
6
5
  require "sorbet-runtime"
7
6
  require "shellwords"
@@ -23,11 +22,15 @@ module Dependabot
23
22
  T.nilable(T.proc.params(data: String).returns(T::Hash[Symbol, T.anything]))
24
23
  end
25
24
 
25
+ Environment = T.type_alias { T::Hash[String, String] }
26
+ SpawnOptions = T.type_alias { T::Hash[Symbol, T.anything] }
27
+
26
28
  EnvCmdItem = T.type_alias do
27
29
  T.any(
28
30
  String,
29
31
  T::Array[String],
30
- T::Hash[T.any(String, Symbol), T.anything]
32
+ Environment,
33
+ SpawnOptions
31
34
  )
32
35
  end
33
36
 
@@ -101,25 +104,31 @@ module Dependabot
101
104
  start_time = Time.now
102
105
 
103
106
  begin
104
- T.unsafe(Open3).popen3(*env_cmd) do |stdin, stdout_io, stderr_io, wait_thr| # rubocop:disable Metrics/BlockLength
107
+ stdout_io, stderr_io, wait_thr = open_process(env_cmd)
108
+ begin
105
109
  pid = wait_thr.pid
106
110
  command_string = command_string_for_logging(env_cmd)
107
111
  log_level = short_git_config_command?(command_string) ? :debug : :info
108
- sanitized_env_cmd = if env_cmd.first.is_a?(Hash)
109
- [SharedHelpers.send(:sanitize_env_for_logging, env_cmd.first), *env_cmd[1..]]
110
- else
111
- env_cmd
112
- end
112
+ first_item = env_cmd.first
113
+ sanitized_env_cmd = T.let(
114
+ if first_item.is_a?(Hash)
115
+ environment = extract_environment(env_cmd.dup)
116
+ [T.must(SharedHelpers.sanitize_env_for_logging(environment)), *env_cmd.drop(1)]
117
+ else
118
+ env_cmd
119
+ end,
120
+ T::Array[EnvCmdItem]
121
+ )
113
122
  command_for_log = sanitized_env_cmd.map { |item| item.is_a?(Array) ? item.first : item }.join(" ")
114
123
  Dependabot.logger.public_send(log_level, "Started process PID: #{pid} with command: #{command_for_log}")
115
124
 
116
125
  # Write to stdin if input data is provided
117
126
  begin
118
- stdin&.write(stdin_data) if stdin_data
127
+ stdout_io.write(stdin_data) if stdin_data
119
128
  rescue Errno::EPIPE
120
129
  # Process exited before reading stdin - continue to collect output
121
130
  end
122
- stdin&.close
131
+ stdout_io.close_write
123
132
 
124
133
  stdout_io.sync = true
125
134
  stderr_io.sync = true
@@ -191,6 +200,10 @@ module Dependabot
191
200
 
192
201
  status = ProcessStatus.new(wait_thr.value)
193
202
  Dependabot.logger.public_send(log_level, "Process PID: #{pid} completed with status: #{status}")
203
+ ensure
204
+ stdout_io.close unless stdout_io.closed?
205
+ stderr_io.close unless stderr_io.closed?
206
+ wait_thr.join
194
207
  end
195
208
  rescue Timeout::Error => e
196
209
  Dependabot.logger.error("Process PID: #{pid} failed due to timeout: #{e.message}")
@@ -215,6 +228,61 @@ module Dependabot
215
228
  # rubocop:enable Metrics/PerceivedComplexity
216
229
  # rubocop:enable Metrics/CyclomaticComplexity
217
230
 
231
+ sig { params(env_cmd: T::Array[EnvCmdItem]).returns([IO, IO, Process::Waiter]) }
232
+ def self.open_process(env_cmd)
233
+ process_io = T.let(nil, T.nilable(IO))
234
+ stderr_io = T.let(nil, T.nilable(IO))
235
+ stderr_writer = T.let(nil, T.nilable(IO))
236
+ arguments = env_cmd.dup
237
+ environment = extract_environment(arguments)
238
+ options = extract_options(arguments)
239
+ command = command_for_popen(arguments)
240
+
241
+ stderr_io, stderr_writer = IO.pipe
242
+ options[:err] = stderr_writer
243
+ process_io = T.cast(IO.popen(environment, command, "r+", options), IO)
244
+ stderr_writer.close
245
+
246
+ wait_thread = T.cast(Process.detach(process_io.pid), Process::Waiter)
247
+ [process_io, stderr_io, wait_thread]
248
+ rescue StandardError
249
+ process_io.close if process_io && !process_io.closed?
250
+ stderr_io.close if stderr_io && !stderr_io.closed?
251
+ stderr_writer.close if stderr_writer && !stderr_writer.closed?
252
+ raise
253
+ end
254
+ private_class_method :open_process
255
+
256
+ sig { params(arguments: T::Array[EnvCmdItem]).returns(Environment) }
257
+ def self.extract_environment(arguments)
258
+ return {} unless arguments.first.is_a?(Hash)
259
+
260
+ T.cast(arguments.shift, Environment)
261
+ end
262
+ private_class_method :extract_environment
263
+
264
+ sig { params(arguments: T::Array[EnvCmdItem]).returns(SpawnOptions) }
265
+ def self.extract_options(arguments)
266
+ return {} unless arguments.last.is_a?(Hash)
267
+
268
+ T.cast(arguments.pop, SpawnOptions).dup
269
+ end
270
+ private_class_method :extract_options
271
+
272
+ sig do
273
+ params(arguments: T::Array[EnvCmdItem])
274
+ .returns(T.any(String, T::Array[T.any(String, T::Array[String])]))
275
+ end
276
+ def self.command_for_popen(arguments)
277
+ raise ArgumentError, "command must not be empty" if arguments.empty?
278
+
279
+ first_argument = arguments.first
280
+ return first_argument if arguments.one? && first_argument.is_a?(String)
281
+
282
+ T.cast(arguments, T::Array[T.any(String, T::Array[String])])
283
+ end
284
+ private_class_method :command_for_popen
285
+
218
286
  # Terminate a process by PID
219
287
  sig { params(pid: T.nilable(Integer)).void }
220
288
  def self.terminate_process(pid)
@@ -9,9 +9,11 @@ module Dependabot
9
9
  extend T::Sig
10
10
  extend Forwardable
11
11
 
12
+ Value = T.type_alias { T.nilable(String) }
13
+
12
14
  def_delegators :@credential, :fetch, :keys, :[]=, :delete, :slice, :values, :entries
13
15
 
14
- sig { params(credential: T::Hash[String, T.any(T::Boolean, String, T::Array[String])]).void }
16
+ sig { params(credential: T::Hash[String, T.nilable(T.any(T::Boolean, String, T::Array[String]))]).void }
15
17
  def initialize(credential)
16
18
  @replaces_base = T.let(credential["replaces-base"] == true, T::Boolean)
17
19
  credential.delete("replaces-base")
@@ -25,7 +27,14 @@ module Dependabot
25
27
  T.nilable(T::Array[String])
26
28
  )
27
29
 
28
- @credential = T.let(T.unsafe(credential), T::Hash[String, String])
30
+ @credential = T.let(
31
+ credential.to_h do |key, value|
32
+ raise TypeError, "credential #{key} must be a string or nil" unless value.nil? || value.is_a?(String)
33
+
34
+ [key, value]
35
+ end,
36
+ T::Hash[String, Value]
37
+ )
29
38
  end
30
39
 
31
40
  sig { returns(T::Boolean) }
@@ -46,7 +55,7 @@ module Dependabot
46
55
  Credential.new(@credential.merge(other.to_h))
47
56
  end
48
57
 
49
- sig { returns(T::Hash[String, String]) }
58
+ sig { returns(T::Hash[String, Value]) }
50
59
  def to_h
51
60
  @credential
52
61
  end
@@ -580,7 +580,10 @@ module Dependabot
580
580
  end
581
581
  def initialize(tool_name, tool_message)
582
582
  @tool_name = tool_name
583
- @tool_message = tool_message
583
+ # Sanitize here as well as via `super`: the raw `tool_message` attribute is read
584
+ # directly during error serialization (see `updater_error_details`), so it must not
585
+ # carry credentials (e.g. basic-auth URLs) that the base message sanitization strips.
586
+ @tool_message = T.let(filter_sensitive_data(tool_message), String)
584
587
 
585
588
  msg = "Dependabot detected that #{tool_name} is misconfigured in this repository. " \
586
589
  "Running `#{tool_name.downcase}` results in the following error: #{tool_message}"