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.
- checksums.yaml +4 -4
- data/lib/dependabot/clients/azure.rb +42 -22
- data/lib/dependabot/clients/bitbucket.rb +52 -26
- data/lib/dependabot/clients/bitbucket_with_retries.rb +11 -12
- data/lib/dependabot/clients/codecommit.rb +73 -53
- data/lib/dependabot/clients/github_resource_parser.rb +70 -0
- data/lib/dependabot/clients/github_with_retries.rb +63 -23
- data/lib/dependabot/clients/gitlab_with_retries.rb +19 -17
- data/lib/dependabot/clients/json_response_parser.rb +103 -0
- data/lib/dependabot/command_helpers.rb +89 -13
- data/lib/dependabot/credential.rb +12 -3
- data/lib/dependabot/dependency_requirement.rb +28 -15
- data/lib/dependabot/errors.rb +7 -0
- data/lib/dependabot/file_fetchers/base.rb +196 -140
- data/lib/dependabot/file_updaters/vendor_updater.rb +8 -2
- data/lib/dependabot/git_tag_details.rb +8 -14
- data/lib/dependabot/metadata_finders/base/changelog_finder.rb +211 -110
- data/lib/dependabot/metadata_finders/base/commit_response_parser.rb +68 -0
- data/lib/dependabot/metadata_finders/base/commits_finder.rb +44 -38
- data/lib/dependabot/pull_request_creator/azure.rb +1 -1
- data/lib/dependabot/pull_request_creator/branch_name_template.rb +1 -1
- data/lib/dependabot/pull_request_creator/codecommit.rb +10 -12
- data/lib/dependabot/pull_request_creator/github.rb +55 -35
- data/lib/dependabot/pull_request_creator/gitlab.rb +37 -11
- data/lib/dependabot/pull_request_creator/labeler.rb +65 -15
- data/lib/dependabot/pull_request_creator/message_builder/issue_linker.rb +21 -2
- data/lib/dependabot/pull_request_creator/message_builder/link_and_mention_sanitizer.rb +53 -13
- data/lib/dependabot/pull_request_creator/message_builder/metadata_presenter.rb +20 -13
- data/lib/dependabot/pull_request_creator/pr_name_prefixer.rb +179 -78
- data/lib/dependabot/pull_request_updater/azure.rb +63 -11
- data/lib/dependabot/pull_request_updater/github.rb +44 -34
- data/lib/dependabot/pull_request_updater/gitlab.rb +23 -12
- data/lib/dependabot/pull_request_updater.rb +3 -3
- data/lib/dependabot/shared_helpers.rb +107 -39
- data/lib/dependabot/update_checkers/base.rb +9 -5
- data/lib/dependabot/update_checkers/version_filters.rb +5 -0
- data/lib/dependabot/update_checkers/vulnerability_audit.rb +282 -0
- data/lib/dependabot/workspace/git.rb +36 -5
- data/lib/dependabot.rb +1 -1
- metadata +6 -2
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
# typed:
|
|
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,10 +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
|
-
T::
|
|
31
|
+
T::Array[String],
|
|
32
|
+
Environment,
|
|
33
|
+
SpawnOptions
|
|
30
34
|
)
|
|
31
35
|
end
|
|
32
36
|
|
|
@@ -100,25 +104,31 @@ module Dependabot
|
|
|
100
104
|
start_time = Time.now
|
|
101
105
|
|
|
102
106
|
begin
|
|
103
|
-
|
|
107
|
+
stdout_io, stderr_io, wait_thr = open_process(env_cmd)
|
|
108
|
+
begin
|
|
104
109
|
pid = wait_thr.pid
|
|
105
110
|
command_string = command_string_for_logging(env_cmd)
|
|
106
111
|
log_level = short_git_config_command?(command_string) ? :debug : :info
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
+
)
|
|
122
|
+
command_for_log = sanitized_env_cmd.map { |item| item.is_a?(Array) ? item.first : item }.join(" ")
|
|
113
123
|
Dependabot.logger.public_send(log_level, "Started process PID: #{pid} with command: #{command_for_log}")
|
|
114
124
|
|
|
115
125
|
# Write to stdin if input data is provided
|
|
116
126
|
begin
|
|
117
|
-
|
|
127
|
+
stdout_io.write(stdin_data) if stdin_data
|
|
118
128
|
rescue Errno::EPIPE
|
|
119
129
|
# Process exited before reading stdin - continue to collect output
|
|
120
130
|
end
|
|
121
|
-
|
|
131
|
+
stdout_io.close_write
|
|
122
132
|
|
|
123
133
|
stdout_io.sync = true
|
|
124
134
|
stderr_io.sync = true
|
|
@@ -190,6 +200,10 @@ module Dependabot
|
|
|
190
200
|
|
|
191
201
|
status = ProcessStatus.new(wait_thr.value)
|
|
192
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
|
|
193
207
|
end
|
|
194
208
|
rescue Timeout::Error => e
|
|
195
209
|
Dependabot.logger.error("Process PID: #{pid} failed due to timeout: #{e.message}")
|
|
@@ -214,6 +228,61 @@ module Dependabot
|
|
|
214
228
|
# rubocop:enable Metrics/PerceivedComplexity
|
|
215
229
|
# rubocop:enable Metrics/CyclomaticComplexity
|
|
216
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
|
+
|
|
217
286
|
# Terminate a process by PID
|
|
218
287
|
sig { params(pid: T.nilable(Integer)).void }
|
|
219
288
|
def self.terminate_process(pid)
|
|
@@ -263,7 +332,14 @@ module Dependabot
|
|
|
263
332
|
|
|
264
333
|
sig { params(env_cmd: T::Array[EnvCmdItem]).returns(T.nilable(String)) }
|
|
265
334
|
def self.command_string_for_logging(env_cmd)
|
|
266
|
-
|
|
335
|
+
command_parts = env_cmd.filter_map do |item|
|
|
336
|
+
case item
|
|
337
|
+
when Array then item.first
|
|
338
|
+
when String then item
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
command_parts.join(" ") unless command_parts.empty?
|
|
267
343
|
end
|
|
268
344
|
private_class_method :command_string_for_logging
|
|
269
345
|
|
|
@@ -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(
|
|
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,
|
|
58
|
+
sig { returns(T::Hash[String, Value]) }
|
|
50
59
|
def to_h
|
|
51
60
|
@credential
|
|
52
61
|
end
|
|
@@ -16,7 +16,7 @@ module Dependabot
|
|
|
16
16
|
#
|
|
17
17
|
# Subclasses Hash so it is a drop-in replacement at call sites (and in
|
|
18
18
|
# type annotations) that treat requirement entries as
|
|
19
|
-
# T::Hash[Symbol,
|
|
19
|
+
# T::Hash[Symbol, Object], while exposing typed readers for the
|
|
20
20
|
# well-known keys. New code should prefer the typed readers; hash-style
|
|
21
21
|
# access remains supported while call sites are migrated gradually.
|
|
22
22
|
#
|
|
@@ -37,15 +37,11 @@ module Dependabot
|
|
|
37
37
|
ObjectHash = T.type_alias { T::Hash[T.any(Symbol, String), Object] }
|
|
38
38
|
Requirement = T.type_alias { T.any(String, Symbol) }
|
|
39
39
|
Source = T.type_alias { T.any(String, ObjectHash) }
|
|
40
|
-
Input = T.type_alias { T.any(
|
|
40
|
+
Input = T.type_alias { T::Hash[T.any(Symbol, String), T.anything] }
|
|
41
41
|
|
|
42
42
|
K = type_member { { fixed: Symbol } }
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# rubocop:disable Sorbet/ForbidTUntyped
|
|
46
|
-
V = type_member { { fixed: T.untyped } }
|
|
47
|
-
Elem = type_member { { fixed: [Symbol, T.untyped] } }
|
|
48
|
-
# rubocop:enable Sorbet/ForbidTUntyped
|
|
43
|
+
V = type_member { { fixed: Object } }
|
|
44
|
+
Elem = type_member { { fixed: [Symbol, Object] } }
|
|
49
45
|
|
|
50
46
|
# Builds a DependencyRequirement from a requirement hash, symbolising
|
|
51
47
|
# top-level keys. Accepts both plain hashes and existing
|
|
@@ -53,7 +49,12 @@ module Dependabot
|
|
|
53
49
|
sig { params(hash: Input).returns(DependencyRequirement) }
|
|
54
50
|
def self.create(hash)
|
|
55
51
|
requirement = new
|
|
56
|
-
|
|
52
|
+
hash.each do |key, value|
|
|
53
|
+
case value
|
|
54
|
+
when Object then requirement[key.to_sym] = value
|
|
55
|
+
else raise TypeError, "requirement values must inherit Object"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
57
58
|
requirement
|
|
58
59
|
end
|
|
59
60
|
|
|
@@ -62,7 +63,7 @@ module Dependabot
|
|
|
62
63
|
# or :unfixable when no valid updated requirement can be generated.
|
|
63
64
|
sig { returns(T.nilable(Requirement)) }
|
|
64
65
|
def requirement
|
|
65
|
-
value =
|
|
66
|
+
value = self[:requirement]
|
|
66
67
|
return if value.nil?
|
|
67
68
|
return value if value.is_a?(String) || value == :unfixable
|
|
68
69
|
|
|
@@ -82,7 +83,7 @@ module Dependabot
|
|
|
82
83
|
# the underlying hash access under sorbet-runtime.
|
|
83
84
|
sig { returns(T.nilable(T::Array[Group])) }
|
|
84
85
|
def groups
|
|
85
|
-
value =
|
|
86
|
+
value = self[:groups]
|
|
86
87
|
return if value.nil?
|
|
87
88
|
raise TypeError, "groups must be an array of strings or symbols, or nil" unless value.is_a?(Array)
|
|
88
89
|
|
|
@@ -101,7 +102,7 @@ module Dependabot
|
|
|
101
102
|
# parser or deserialised from a job definition.
|
|
102
103
|
sig { returns(T.nilable(Source)) }
|
|
103
104
|
def source
|
|
104
|
-
value =
|
|
105
|
+
value = self[:source]
|
|
105
106
|
return if value.nil?
|
|
106
107
|
return value if value.is_a?(String)
|
|
107
108
|
|
|
@@ -175,6 +176,16 @@ module Dependabot
|
|
|
175
176
|
raise TypeError, "metadata #{key} must be a symbol or nil"
|
|
176
177
|
end
|
|
177
178
|
|
|
179
|
+
# Reads a boolean metadata field such as "include_debug_script".
|
|
180
|
+
sig { params(key: String).returns(T.nilable(T::Boolean)) }
|
|
181
|
+
def metadata_boolean(key)
|
|
182
|
+
value = metadata_value(key)
|
|
183
|
+
return if value.nil?
|
|
184
|
+
return value if value == true || value == false
|
|
185
|
+
|
|
186
|
+
raise TypeError, "metadata #{key} must be a boolean or nil"
|
|
187
|
+
end
|
|
188
|
+
|
|
178
189
|
# Reads a nested metadata hash whose values are all strings, such as
|
|
179
190
|
# "dependency_set" (`{ group: "my.group", version: "1.4.0" }`). Keys are
|
|
180
191
|
# symbolised so callers can read them with symbols regardless of how the
|
|
@@ -205,12 +216,14 @@ module Dependabot
|
|
|
205
216
|
details = metadata
|
|
206
217
|
return if details.nil?
|
|
207
218
|
|
|
208
|
-
details[key.to_sym]
|
|
219
|
+
return details[key.to_sym] if details.key?(key.to_sym)
|
|
220
|
+
|
|
221
|
+
details[key]
|
|
209
222
|
end
|
|
210
223
|
|
|
211
224
|
sig { params(key: Symbol).returns(T.nilable(String)) }
|
|
212
225
|
def optional_string(key)
|
|
213
|
-
value =
|
|
226
|
+
value = self[key]
|
|
214
227
|
return if value.nil?
|
|
215
228
|
return value if value.is_a?(String)
|
|
216
229
|
|
|
@@ -219,7 +232,7 @@ module Dependabot
|
|
|
219
232
|
|
|
220
233
|
sig { params(key: Symbol).returns(T.nilable(ObjectHash)) }
|
|
221
234
|
def optional_object_hash(key)
|
|
222
|
-
value =
|
|
235
|
+
value = self[key]
|
|
223
236
|
return if value.nil?
|
|
224
237
|
|
|
225
238
|
object_hash(value, key)
|
data/lib/dependabot/errors.rb
CHANGED
|
@@ -246,6 +246,11 @@ module Dependabot
|
|
|
246
246
|
"error-type": "dependency_file_not_resolvable",
|
|
247
247
|
"error-detail": { message: error.message }
|
|
248
248
|
}
|
|
249
|
+
when Dependabot::BranchNameFormattingError
|
|
250
|
+
{
|
|
251
|
+
"error-type": "branch_name_formatting_error",
|
|
252
|
+
"error-detail": { message: error.message }
|
|
253
|
+
}
|
|
249
254
|
when Dependabot::BlockedDependencyVersion
|
|
250
255
|
{
|
|
251
256
|
"error-type": "blocked_dependency_version",
|
|
@@ -509,6 +514,8 @@ module Dependabot
|
|
|
509
514
|
|
|
510
515
|
class RefNamespaceConflictError < DependabotError; end
|
|
511
516
|
|
|
517
|
+
class BranchNameFormattingError < DependabotError; end
|
|
518
|
+
|
|
512
519
|
#####################
|
|
513
520
|
# Repo level errors #
|
|
514
521
|
#####################
|