dependabot-composer 0.310.0 → 0.312.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/composer/file_fetcher.rb +50 -18
- data/lib/dependabot/composer/file_updater/lockfile_updater.rb +138 -49
- data/lib/dependabot/composer/file_updater/manifest_updater.rb +29 -10
- data/lib/dependabot/composer/file_updater.rb +1 -1
- data/lib/dependabot/composer/helpers.rb +1 -1
- data/lib/dependabot/composer/metadata_finder.rb +18 -1
- data/lib/dependabot/composer/update_checker/latest_version_finder.rb +66 -14
- data/lib/dependabot/composer/update_checker/requirements_updater.rb +64 -30
- data/lib/dependabot/composer/update_checker/version_resolver.rb +146 -47
- data/lib/dependabot/composer/update_checker.rb +10 -10
- data/lib/dependabot/composer/version.rb +8 -2
- metadata +27 -27
@@ -1,8 +1,9 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "json"
|
5
5
|
require "uri"
|
6
|
+
require "sorbet-runtime"
|
6
7
|
|
7
8
|
require "dependabot/errors"
|
8
9
|
require "dependabot/shared_helpers"
|
@@ -17,30 +18,49 @@ module Dependabot
|
|
17
18
|
module Composer
|
18
19
|
class UpdateChecker
|
19
20
|
class VersionResolver # rubocop:disable Metrics/ClassLength
|
21
|
+
extend T::Sig
|
22
|
+
|
20
23
|
class MissingExtensions < StandardError
|
24
|
+
extend T::Sig
|
25
|
+
|
26
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
21
27
|
attr_reader :extensions
|
22
28
|
|
29
|
+
sig { params(extensions: T::Array[T::Hash[Symbol, T.untyped]]).void }
|
23
30
|
def initialize(extensions)
|
24
|
-
@extensions = extensions
|
31
|
+
@extensions = T.let(extensions, T::Array[T::Hash[Symbol, T.untyped]])
|
25
32
|
super
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
29
|
-
MISSING_EXPLICIT_PLATFORM_REQ_REGEX =
|
36
|
+
MISSING_EXPLICIT_PLATFORM_REQ_REGEX = T.let(
|
30
37
|
%r{
|
31
38
|
(?<=PHP\sextension\s)ext\-[^\s\/]+\s.*?\s(?=is|but)|
|
32
39
|
(?<=requires\s)php(?:\-[^\s\/]+)?\s.*?\s(?=but)
|
33
|
-
}x
|
34
|
-
|
40
|
+
}x,
|
41
|
+
Regexp
|
42
|
+
)
|
43
|
+
MISSING_IMPLICIT_PLATFORM_REQ_REGEX = T.let(
|
35
44
|
%r{
|
36
45
|
(?<!with|for|by)\sext\-[^\s\/]+\s.*?\s(?=->)|
|
37
46
|
(?<=require\s)php(?:\-[^\s\/]+)?\s.*?\s(?=->) # composer v2
|
38
|
-
}x
|
39
|
-
|
47
|
+
}x,
|
48
|
+
Regexp
|
49
|
+
)
|
50
|
+
VERSION_REGEX = T.let(/[0-9]+(?:\.[A-Za-z0-9\-_]+)*/, Regexp)
|
40
51
|
|
41
52
|
# Example Timeout error from Composer 2.7.7: "curl error 28 while downloading https://example.com:81/packages.json: Failed to connect to example.com port 81 after 9853 ms: Connection timed out" # rubocop:disable Layout/LineLength
|
42
|
-
SOURCE_TIMED_OUT_REGEX = %r{curl error 28 while downloading (?<url>https?://.+/packages\.json): }
|
53
|
+
SOURCE_TIMED_OUT_REGEX = T.let(%r{curl error 28 while downloading (?<url>https?://.+/packages\.json): }, Regexp)
|
43
54
|
|
55
|
+
sig do
|
56
|
+
params(
|
57
|
+
credentials: T::Array[Dependabot::Credential],
|
58
|
+
dependency: Dependabot::Dependency,
|
59
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
60
|
+
requirements_to_unlock: Symbol,
|
61
|
+
latest_allowable_version: T.nilable(Gem::Version)
|
62
|
+
).void
|
63
|
+
end
|
44
64
|
def initialize(credentials:, dependency:, dependency_files:,
|
45
65
|
requirements_to_unlock:, latest_allowable_version:)
|
46
66
|
@credentials = credentials
|
@@ -48,24 +68,43 @@ module Dependabot
|
|
48
68
|
@dependency_files = dependency_files
|
49
69
|
@requirements_to_unlock = requirements_to_unlock
|
50
70
|
@latest_allowable_version = latest_allowable_version
|
51
|
-
@composer_platform_extensions = initial_platform
|
52
|
-
@error_handler = ComposerErrorHandler.new
|
71
|
+
@composer_platform_extensions = T.let(initial_platform, T::Hash[String, T::Array[String]])
|
72
|
+
@error_handler = T.let(ComposerErrorHandler.new, ComposerErrorHandler)
|
53
73
|
end
|
54
74
|
|
75
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
55
76
|
def latest_resolvable_version
|
56
|
-
@latest_resolvable_version ||=
|
77
|
+
@latest_resolvable_version ||= T.let(
|
78
|
+
fetch_latest_resolvable_version,
|
79
|
+
T.nilable(Dependabot::Version)
|
80
|
+
)
|
57
81
|
end
|
58
82
|
|
59
83
|
private
|
60
84
|
|
85
|
+
# Initialize instance variables with T.let for strict typing
|
86
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
61
87
|
attr_reader :credentials
|
88
|
+
|
89
|
+
sig { returns(Dependabot::Dependency) }
|
62
90
|
attr_reader :dependency
|
91
|
+
|
92
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
63
93
|
attr_reader :dependency_files
|
94
|
+
|
95
|
+
sig { returns(Symbol) }
|
64
96
|
attr_reader :requirements_to_unlock
|
97
|
+
|
98
|
+
sig { returns(T.nilable(Gem::Version)) }
|
65
99
|
attr_reader :latest_allowable_version
|
100
|
+
|
101
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
66
102
|
attr_reader :composer_platform_extensions
|
103
|
+
|
104
|
+
sig { returns(ComposerErrorHandler) }
|
67
105
|
attr_reader :error_handler
|
68
106
|
|
107
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
69
108
|
def fetch_latest_resolvable_version
|
70
109
|
version = fetch_latest_resolvable_version_string
|
71
110
|
return if version.nil?
|
@@ -80,8 +119,9 @@ module Dependabot
|
|
80
119
|
retry
|
81
120
|
end
|
82
121
|
|
122
|
+
sig { returns(T.nilable(String)) }
|
83
123
|
def fetch_latest_resolvable_version_string
|
84
|
-
base_directory = dependency_files.first.directory
|
124
|
+
base_directory = T.must(dependency_files.first).directory
|
85
125
|
SharedHelpers.in_a_temporary_directory(base_directory) do
|
86
126
|
write_temporary_dependency_files
|
87
127
|
run_update_checker
|
@@ -93,6 +133,7 @@ module Dependabot
|
|
93
133
|
handle_composer_errors(e)
|
94
134
|
end
|
95
135
|
|
136
|
+
sig { params(unlock_requirement: T::Boolean).void }
|
96
137
|
def write_temporary_dependency_files(unlock_requirement: true)
|
97
138
|
write_dependency_file(unlock_requirement: unlock_requirement)
|
98
139
|
write_path_dependency_files
|
@@ -101,6 +142,7 @@ module Dependabot
|
|
101
142
|
write_auth_file
|
102
143
|
end
|
103
144
|
|
145
|
+
sig { void }
|
104
146
|
def write_zipped_path_dependency_files
|
105
147
|
zipped_path_dependency_files.each do |file|
|
106
148
|
FileUtils.mkdir_p(Pathname.new(file.name).dirname)
|
@@ -108,6 +150,7 @@ module Dependabot
|
|
108
150
|
end
|
109
151
|
end
|
110
152
|
|
153
|
+
sig { params(unlock_requirement: T::Boolean).void }
|
111
154
|
def write_dependency_file(unlock_requirement:)
|
112
155
|
File.write(
|
113
156
|
PackageManager::MANIFEST_FILENAME,
|
@@ -117,6 +160,7 @@ module Dependabot
|
|
117
160
|
)
|
118
161
|
end
|
119
162
|
|
163
|
+
sig { void }
|
120
164
|
def write_path_dependency_files
|
121
165
|
path_dependency_files.each do |file|
|
122
166
|
FileUtils.mkdir_p(Pathname.new(file.name).dirname)
|
@@ -124,14 +168,17 @@ module Dependabot
|
|
124
168
|
end
|
125
169
|
end
|
126
170
|
|
171
|
+
sig { void }
|
127
172
|
def write_lockfile
|
128
|
-
File.write(PackageManager::LOCKFILE_FILENAME, lockfile.content) if lockfile
|
173
|
+
File.write(PackageManager::LOCKFILE_FILENAME, T.must(lockfile).content) if lockfile
|
129
174
|
end
|
130
175
|
|
176
|
+
sig { void }
|
131
177
|
def write_auth_file
|
132
|
-
File.write(PackageManager::AUTH_FILENAME, auth_json.content) if auth_json
|
178
|
+
File.write(PackageManager::AUTH_FILENAME, T.must(auth_json).content) if auth_json
|
133
179
|
end
|
134
180
|
|
181
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
|
135
182
|
def transitory_failure?(error)
|
136
183
|
return true if error.message.include?("404 Not Found")
|
137
184
|
return true if error.message.include?("timed out")
|
@@ -140,6 +187,7 @@ module Dependabot
|
|
140
187
|
error.message.include?("Content-Length mismatch")
|
141
188
|
end
|
142
189
|
|
190
|
+
sig { returns(String) }
|
143
191
|
def run_update_checker
|
144
192
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
145
193
|
SharedHelpers.run_helper_subprocess(
|
@@ -156,14 +204,16 @@ module Dependabot
|
|
156
204
|
end
|
157
205
|
end
|
158
206
|
|
207
|
+
sig { params(unlock_requirement: T::Boolean).returns(String) }
|
159
208
|
def prepared_composer_json_content(unlock_requirement: true)
|
160
|
-
content = composer_file.content
|
209
|
+
content = T.must(T.must(composer_file).content)
|
161
210
|
content = unlock_dep_being_updated(content) if unlock_requirement
|
162
211
|
content = lock_git_dependencies(content) if lockfile
|
163
212
|
content = add_temporary_platform_extensions(content)
|
164
213
|
content
|
165
214
|
end
|
166
215
|
|
216
|
+
sig { params(content: String).returns(String) }
|
167
217
|
def unlock_dep_being_updated(content)
|
168
218
|
content.gsub(
|
169
219
|
/"#{Regexp.escape(dependency.name)}"\s*:\s*".*"/,
|
@@ -171,6 +221,7 @@ module Dependabot
|
|
171
221
|
)
|
172
222
|
end
|
173
223
|
|
224
|
+
sig { params(content: String).returns(String) }
|
174
225
|
def add_temporary_platform_extensions(content)
|
175
226
|
json = JSON.parse(content)
|
176
227
|
|
@@ -186,6 +237,7 @@ module Dependabot
|
|
186
237
|
JSON.dump(json)
|
187
238
|
end
|
188
239
|
|
240
|
+
sig { params(content: String).returns(String) }
|
189
241
|
def lock_git_dependencies(content)
|
190
242
|
json = JSON.parse(content)
|
191
243
|
|
@@ -197,7 +249,7 @@ module Dependabot
|
|
197
249
|
next if req.include?("#")
|
198
250
|
|
199
251
|
commit_sha = parsed_lockfile
|
200
|
-
.fetch(keys[:lockfile], [])
|
252
|
+
.fetch(T.must(keys[:lockfile]), [])
|
201
253
|
.find { |d| d["name"] == name }
|
202
254
|
&.dig("source", "reference")
|
203
255
|
updated_req_parts = req.split
|
@@ -211,6 +263,7 @@ module Dependabot
|
|
211
263
|
|
212
264
|
# rubocop:disable Metrics/PerceivedComplexity
|
213
265
|
# rubocop:disable Metrics/AbcSize
|
266
|
+
sig { returns(String) }
|
214
267
|
def updated_version_requirement_string
|
215
268
|
lower_bound =
|
216
269
|
if requirements_to_unlock == :none
|
@@ -253,6 +306,7 @@ module Dependabot
|
|
253
306
|
# rubocop:disable Metrics/AbcSize
|
254
307
|
# rubocop:disable Metrics/CyclomaticComplexity
|
255
308
|
# rubocop:disable Metrics/MethodLength
|
309
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T.nilable(NilClass)) }
|
256
310
|
def handle_composer_errors(error)
|
257
311
|
# Special case for Laravel Nova, which will fall back to attempting
|
258
312
|
# to close a private repo if given invalid (or no) credentials
|
@@ -271,7 +325,11 @@ module Dependabot
|
|
271
325
|
missing_extensions =
|
272
326
|
error.message.scan(MISSING_EXPLICIT_PLATFORM_REQ_REGEX)
|
273
327
|
.map do |extension_string|
|
274
|
-
name, requirement = extension_string.
|
328
|
+
name, requirement = if extension_string.is_a?(Array)
|
329
|
+
[extension_string.first.to_s.strip, extension_string.last.to_s]
|
330
|
+
else
|
331
|
+
extension_string.to_s.strip.split(" ", 2)
|
332
|
+
end
|
275
333
|
{ name: name, requirement: requirement }
|
276
334
|
end
|
277
335
|
raise MissingExtensions, missing_extensions
|
@@ -282,7 +340,7 @@ module Dependabot
|
|
282
340
|
missing_extensions =
|
283
341
|
error.message.scan(MISSING_IMPLICIT_PLATFORM_REQ_REGEX)
|
284
342
|
.map do |extension_string|
|
285
|
-
name, requirement = extension_string.strip.split(" ", 2)
|
343
|
+
name, requirement = T.cast(extension_string, String).strip.split(" ", 2)
|
286
344
|
{ name: name, requirement: requirement }
|
287
345
|
end
|
288
346
|
|
@@ -291,7 +349,7 @@ module Dependabot
|
|
291
349
|
version_for_reqs(existing_reqs + [hash[:requirement]])
|
292
350
|
end
|
293
351
|
|
294
|
-
raise MissingExtensions, [missing_extension]
|
352
|
+
raise MissingExtensions, [missing_extension].compact
|
295
353
|
elsif error.message.include?("cannot require itself") ||
|
296
354
|
error.message.include?('packages.json" file could not be down')
|
297
355
|
raise Dependabot::DependencyFileNotResolvable, error.message
|
@@ -312,14 +370,14 @@ module Dependabot
|
|
312
370
|
# now, we therefore just ignore the dependency and log the error.
|
313
371
|
|
314
372
|
Dependabot.logger.error(error.message)
|
315
|
-
error.backtrace
|
373
|
+
error.backtrace&.each { |line| Dependabot.logger.error(line) }
|
316
374
|
nil
|
317
375
|
elsif error.message.include?("URL required authentication") ||
|
318
376
|
error.message.include?("403 Forbidden")
|
319
|
-
source = error.message.match(%r{https?://(?<source>[^/]+)/})
|
377
|
+
source = error.message.match(%r{https?://(?<source>[^/]+)/})&.named_captures&.fetch("source")
|
320
378
|
raise Dependabot::PrivateSourceAuthenticationFailure, source
|
321
379
|
elsif error.message.match?(SOURCE_TIMED_OUT_REGEX)
|
322
|
-
url = error.message.match(SOURCE_TIMED_OUT_REGEX)
|
380
|
+
url = T.must(error.message.match(SOURCE_TIMED_OUT_REGEX)&.named_captures&.fetch("url"))
|
323
381
|
raise if [
|
324
382
|
"packagist.org",
|
325
383
|
"www.packagist.org"
|
@@ -357,6 +415,7 @@ module Dependabot
|
|
357
415
|
# rubocop:enable Metrics/CyclomaticComplexity
|
358
416
|
# rubocop:enable Metrics/MethodLength
|
359
417
|
|
418
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
|
360
419
|
def unresolvable_error?(error)
|
361
420
|
error.message.start_with?("Could not parse version") ||
|
362
421
|
error.message.include?("does not allow connections to http://") ||
|
@@ -364,15 +423,17 @@ module Dependabot
|
|
364
423
|
error.message.start_with?("Invalid version string")
|
365
424
|
end
|
366
425
|
|
426
|
+
sig { returns(T::Boolean) }
|
367
427
|
def library?
|
368
428
|
parsed_composer_file["type"] == "library"
|
369
429
|
end
|
370
430
|
|
431
|
+
sig { params(message: String).returns(T::Boolean) }
|
371
432
|
def implicit_platform_reqs_satisfiable?(message)
|
372
433
|
missing_extensions =
|
373
434
|
message.scan(MISSING_IMPLICIT_PLATFORM_REQ_REGEX)
|
374
435
|
.map do |extension_string|
|
375
|
-
name, requirement = extension_string.strip.split(" ", 2)
|
436
|
+
name, requirement = T.cast(extension_string, String).strip.split(" ", 2)
|
376
437
|
{ name: name, requirement: requirement }
|
377
438
|
end
|
378
439
|
|
@@ -382,8 +443,9 @@ module Dependabot
|
|
382
443
|
end
|
383
444
|
end
|
384
445
|
|
446
|
+
sig { returns(T::Boolean) }
|
385
447
|
def check_original_requirements_resolvable
|
386
|
-
base_directory = dependency_files.first.directory
|
448
|
+
base_directory = T.must(dependency_files.first).directory
|
387
449
|
SharedHelpers.in_a_temporary_directory(base_directory) do
|
388
450
|
write_temporary_dependency_files(unlock_requirement: false)
|
389
451
|
|
@@ -414,6 +476,7 @@ module Dependabot
|
|
414
476
|
raise Dependabot::DependencyFileNotResolvable, e.message
|
415
477
|
end
|
416
478
|
|
479
|
+
sig { params(requirements: T::Array[String]).returns(T.nilable(String)) }
|
417
480
|
def version_for_reqs(requirements)
|
418
481
|
req_arrays =
|
419
482
|
requirements
|
@@ -438,25 +501,32 @@ module Dependabot
|
|
438
501
|
version.to_s
|
439
502
|
end
|
440
503
|
|
504
|
+
sig { params(additional_extensions: T::Array[T::Hash[Symbol, String]]).void }
|
441
505
|
def update_required_extensions(additional_extensions)
|
442
506
|
additional_extensions.each do |ext|
|
443
507
|
composer_platform_extensions[ext.fetch(:name)] ||= []
|
444
|
-
composer_platform_extensions[ext.fetch(:name)] +=
|
445
|
-
[ext.fetch(:requirement)]
|
446
508
|
composer_platform_extensions[ext.fetch(:name)] =
|
447
|
-
composer_platform_extensions[ext.fetch(:name)].
|
509
|
+
T.must(composer_platform_extensions[ext.fetch(:name)]) + [ext.fetch(:requirement)]
|
510
|
+
composer_platform_extensions[ext.fetch(:name)] =
|
511
|
+
T.must(composer_platform_extensions[ext.fetch(:name)]).uniq
|
448
512
|
end
|
449
513
|
end
|
450
514
|
|
515
|
+
sig { returns(String) }
|
451
516
|
def php_helper_path
|
452
517
|
NativeHelpers.composer_helper_path(composer_version: composer_version)
|
453
518
|
end
|
454
519
|
|
520
|
+
sig { returns(String) }
|
455
521
|
def composer_version
|
456
522
|
parsed_lockfile_or_nil = lockfile ? parsed_lockfile : nil
|
457
|
-
@composer_version ||=
|
523
|
+
@composer_version ||= T.let(
|
524
|
+
Helpers.composer_version(parsed_composer_file, parsed_lockfile_or_nil),
|
525
|
+
T.nilable(String)
|
526
|
+
)
|
458
527
|
end
|
459
528
|
|
529
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
460
530
|
def initial_platform
|
461
531
|
platform_php = Helpers.capture_platform_php(parsed_composer_file)
|
462
532
|
|
@@ -477,38 +547,63 @@ module Dependabot
|
|
477
547
|
platform
|
478
548
|
end
|
479
549
|
|
550
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
480
551
|
def parsed_composer_file
|
481
|
-
@parsed_composer_file ||=
|
552
|
+
@parsed_composer_file ||= T.let(
|
553
|
+
JSON.parse(T.must(T.must(composer_file).content)),
|
554
|
+
T.nilable(T::Hash[String, T.untyped])
|
555
|
+
)
|
482
556
|
end
|
483
557
|
|
558
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
484
559
|
def parsed_lockfile
|
485
|
-
@parsed_lockfile ||=
|
560
|
+
@parsed_lockfile ||= T.let(
|
561
|
+
JSON.parse(T.must(lockfile&.content)),
|
562
|
+
T.nilable(T::Hash[String, T.untyped])
|
563
|
+
)
|
486
564
|
end
|
487
565
|
|
566
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
488
567
|
def composer_file
|
489
|
-
@composer_file ||=
|
490
|
-
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
|
568
|
+
@composer_file ||= T.let(
|
569
|
+
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME },
|
570
|
+
T.nilable(Dependabot::DependencyFile)
|
571
|
+
)
|
491
572
|
end
|
492
573
|
|
574
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
493
575
|
def path_dependency_files
|
494
|
-
@path_dependency_files ||=
|
495
|
-
dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") }
|
576
|
+
@path_dependency_files ||= T.let(
|
577
|
+
dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") },
|
578
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
579
|
+
)
|
496
580
|
end
|
497
581
|
|
582
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
498
583
|
def zipped_path_dependency_files
|
499
|
-
@zipped_path_dependency_files ||=
|
500
|
-
dependency_files.select { |f| f.name.end_with?(".zip", ".gitkeep") }
|
584
|
+
@zipped_path_dependency_files ||= T.let(
|
585
|
+
dependency_files.select { |f| f.name.end_with?(".zip", ".gitkeep") },
|
586
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
587
|
+
)
|
501
588
|
end
|
502
589
|
|
590
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
503
591
|
def lockfile
|
504
|
-
@lockfile ||=
|
505
|
-
dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME }
|
592
|
+
@lockfile ||= T.let(
|
593
|
+
dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME },
|
594
|
+
T.nilable(Dependabot::DependencyFile)
|
595
|
+
)
|
506
596
|
end
|
507
597
|
|
598
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
508
599
|
def auth_json
|
509
|
-
@auth_json ||=
|
600
|
+
@auth_json ||= T.let(
|
601
|
+
dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME },
|
602
|
+
T.nilable(Dependabot::DependencyFile)
|
603
|
+
)
|
510
604
|
end
|
511
605
|
|
606
|
+
sig { params(req_string: String).returns(T::Boolean) }
|
512
607
|
def requirement_valid?(req_string)
|
513
608
|
Composer::Requirement.requirements_array(req_string)
|
514
609
|
true
|
@@ -516,12 +611,14 @@ module Dependabot
|
|
516
611
|
false
|
517
612
|
end
|
518
613
|
|
614
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
519
615
|
def git_credentials
|
520
616
|
credentials
|
521
617
|
.select { |cred| cred["type"] == "git_source" }
|
522
618
|
.select { |cred| cred["password"] }
|
523
619
|
end
|
524
620
|
|
621
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
525
622
|
def registry_credentials
|
526
623
|
credentials
|
527
624
|
.select { |cred| cred["type"] == PackageManager::REPOSITORY_KEY }
|
@@ -534,23 +631,24 @@ module Dependabot
|
|
534
631
|
extend T::Sig
|
535
632
|
|
536
633
|
# Private source errors
|
537
|
-
CURL_ERROR = /curl error 52 while downloading (?<url>.*): Empty reply from server
|
634
|
+
CURL_ERROR = T.let(/curl error 52 while downloading (?<url>.*): Empty reply from server/, Regexp)
|
538
635
|
|
539
|
-
PRIVATE_SOURCE_AUTH_FAIL = [
|
636
|
+
PRIVATE_SOURCE_AUTH_FAIL = T.let([
|
540
637
|
/Could not authenticate against (?<url>.*)/,
|
541
638
|
/The '(?<url>.*)' URL could not be accessed \(HTTP 403\)/,
|
542
639
|
/The "(?<url>.*)" file could not be downloaded/
|
543
|
-
].freeze
|
640
|
+
].freeze, T::Array[Regexp])
|
544
641
|
|
545
|
-
REQUIREMENT_ERROR = /^(?<req>.*) is invalid, it should not contain uppercase characters
|
642
|
+
REQUIREMENT_ERROR = T.let(/^(?<req>.*) is invalid, it should not contain uppercase characters/, Regexp)
|
546
643
|
|
547
|
-
NO_URL = "No URL specified"
|
644
|
+
NO_URL = T.let("No URL specified", String)
|
548
645
|
|
646
|
+
sig { params(url: String).returns(String) }
|
549
647
|
def sanitize_uri(url)
|
550
648
|
url = "http://#{url}" unless url.start_with?("http")
|
551
649
|
uri = URI.parse(url)
|
552
650
|
host = T.must(uri.host).downcase
|
553
|
-
host.start_with?("www.") ? host[4..-1] : host
|
651
|
+
host.start_with?("www.") ? T.must(host[4..-1]) : host
|
554
652
|
end
|
555
653
|
|
556
654
|
# Handles errors with specific to composer error codes
|
@@ -561,7 +659,8 @@ module Dependabot
|
|
561
659
|
next unless error.message.match?(regex)
|
562
660
|
|
563
661
|
url = T.must(error.message.match(regex)).named_captures["url"]
|
564
|
-
|
662
|
+
sanitized_url = sanitize_uri(T.must(url))
|
663
|
+
raise Dependabot::PrivateSourceAuthenticationFailure, sanitized_url.empty? ? NO_URL : sanitized_url
|
565
664
|
end
|
566
665
|
|
567
666
|
# invalid requirement mentioned in manifest file
|
@@ -573,7 +672,7 @@ module Dependabot
|
|
573
672
|
return unless error.message.match?(CURL_ERROR)
|
574
673
|
|
575
674
|
url = T.must(error.message.match(CURL_ERROR)).named_captures["url"]
|
576
|
-
raise PrivateSourceBadResponse, url
|
675
|
+
raise PrivateSourceBadResponse, T.must(url)
|
577
676
|
end
|
578
677
|
end
|
579
678
|
end
|
@@ -28,7 +28,7 @@ module Dependabot
|
|
28
28
|
latest_version_from_registry || latest_resolvable_version
|
29
29
|
end
|
30
30
|
|
31
|
-
sig { override.returns(T.nilable(Dependabot::
|
31
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
32
32
|
def latest_resolvable_version
|
33
33
|
return nil if path_dependency? || git_dependency?
|
34
34
|
|
@@ -40,26 +40,26 @@ module Dependabot
|
|
40
40
|
latest_allowable_version: latest_version_from_registry,
|
41
41
|
requirements_to_unlock: :own
|
42
42
|
).latest_resolvable_version,
|
43
|
-
T.nilable(Dependabot::
|
43
|
+
T.nilable(Dependabot::Version)
|
44
44
|
)
|
45
45
|
end
|
46
46
|
|
47
|
-
sig { override.returns(T.nilable(Dependabot::
|
47
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
48
48
|
def lowest_security_fix_version
|
49
49
|
latest_version_finder.lowest_security_fix_version
|
50
50
|
end
|
51
51
|
|
52
|
-
sig { override.returns(T.nilable(Dependabot::
|
52
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
53
53
|
def lowest_resolvable_security_fix_version
|
54
54
|
raise "Dependency not vulnerable!" unless vulnerable?
|
55
55
|
|
56
56
|
@lowest_resolvable_security_fix_version ||= T.let(
|
57
57
|
fetch_lowest_resolvable_security_fix_version,
|
58
|
-
T.nilable(Dependabot::
|
58
|
+
T.nilable(Dependabot::Version)
|
59
59
|
)
|
60
60
|
end
|
61
61
|
|
62
|
-
sig { override.returns(T.nilable(Dependabot::
|
62
|
+
sig { override.returns(T.nilable(Dependabot::Version)) }
|
63
63
|
def latest_resolvable_version_with_no_unlock
|
64
64
|
return nil if path_dependency? || git_dependency?
|
65
65
|
|
@@ -71,7 +71,7 @@ module Dependabot
|
|
71
71
|
latest_allowable_version: latest_version_from_registry,
|
72
72
|
requirements_to_unlock: :none
|
73
73
|
).latest_resolvable_version,
|
74
|
-
T.nilable(Dependabot::
|
74
|
+
T.nilable(Dependabot::Version)
|
75
75
|
)
|
76
76
|
end
|
77
77
|
|
@@ -80,7 +80,7 @@ module Dependabot
|
|
80
80
|
RequirementsUpdater.new(
|
81
81
|
requirements: dependency.requirements,
|
82
82
|
latest_resolvable_version: preferred_resolvable_version&.to_s,
|
83
|
-
update_strategy: requirements_update_strategy
|
83
|
+
update_strategy: T.must(requirements_update_strategy)
|
84
84
|
).updated_requirements
|
85
85
|
end
|
86
86
|
|
@@ -111,7 +111,7 @@ module Dependabot
|
|
111
111
|
raise NotImplementedError
|
112
112
|
end
|
113
113
|
|
114
|
-
sig { returns(T.nilable(Dependabot::
|
114
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
115
115
|
def latest_version_from_registry
|
116
116
|
latest_version_finder.latest_version
|
117
117
|
end
|
@@ -131,7 +131,7 @@ module Dependabot
|
|
131
131
|
)
|
132
132
|
end
|
133
133
|
|
134
|
-
sig { returns(T.nilable(Dependabot::
|
134
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
135
135
|
def fetch_lowest_resolvable_security_fix_version
|
136
136
|
return nil if path_dependency? || git_dependency?
|
137
137
|
|
@@ -1,6 +1,8 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
require "dependabot/version"
|
5
7
|
require "dependabot/utils"
|
6
8
|
|
@@ -11,11 +13,15 @@ require "dependabot/utils"
|
|
11
13
|
module Dependabot
|
12
14
|
module Composer
|
13
15
|
class Version < Dependabot::Version
|
16
|
+
extend T::Sig
|
17
|
+
|
18
|
+
sig { override.params(version: VersionParameter).void }
|
14
19
|
def initialize(version)
|
15
|
-
@version_string = version.to_s
|
20
|
+
@version_string = T.let(version.to_s, String)
|
16
21
|
super
|
17
22
|
end
|
18
23
|
|
24
|
+
sig { returns(String) }
|
19
25
|
def to_s
|
20
26
|
@version_string
|
21
27
|
end
|