dependabot-bun 0.331.0 → 0.333.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/bun/bun_package_manager.rb +1 -0
- data/lib/dependabot/bun/file_fetcher.rb +13 -1
- data/lib/dependabot/bun/file_updater/bun_lockfile_updater.rb +60 -19
- data/lib/dependabot/bun/file_updater/package_json_preparer.rb +16 -4
- data/lib/dependabot/bun/file_updater/package_json_updater.rb +1 -1
- data/lib/dependabot/bun/file_updater.rb +2 -2
- data/lib/dependabot/bun/language.rb +1 -0
- data/lib/dependabot/bun/metadata_finder.rb +1 -1
- data/lib/dependabot/bun/native_helpers.rb +7 -1
- data/lib/dependabot/bun/pnpm_package_manager.rb +1 -0
- data/lib/dependabot/bun/requirement.rb +22 -8
- data/lib/dependabot/bun/update_checker/conflicting_dependency_resolver.rb +29 -5
- data/lib/dependabot/bun/update_checker/dependency_files_builder.rb +39 -8
- data/lib/dependabot/bun/update_checker/library_detector.rb +35 -5
- data/lib/dependabot/bun/update_checker/requirements_updater.rb +61 -23
- data/lib/dependabot/bun/update_checker/subdependency_version_resolver.rb +57 -15
- data/lib/dependabot/bun/update_checker/vulnerability_auditor.rb +60 -7
- data/lib/dependabot/bun/update_checker.rb +2 -2
- metadata +6 -6
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "excon"
|
|
5
|
+
require "sorbet-runtime"
|
|
6
|
+
|
|
5
7
|
require "dependabot/bun/update_checker"
|
|
6
8
|
require "dependabot/shared_helpers"
|
|
7
9
|
|
|
@@ -9,12 +11,23 @@ module Dependabot
|
|
|
9
11
|
module Bun
|
|
10
12
|
class UpdateChecker
|
|
11
13
|
class LibraryDetector
|
|
14
|
+
extend T::Sig
|
|
15
|
+
|
|
16
|
+
sig do
|
|
17
|
+
params(
|
|
18
|
+
package_json_file: Dependabot::DependencyFile,
|
|
19
|
+
credentials: T::Array[Dependabot::Credential],
|
|
20
|
+
dependency_files: T::Array[Dependabot::DependencyFile]
|
|
21
|
+
)
|
|
22
|
+
.void
|
|
23
|
+
end
|
|
12
24
|
def initialize(package_json_file:, credentials:, dependency_files:)
|
|
13
25
|
@package_json_file = package_json_file
|
|
14
26
|
@credentials = credentials
|
|
15
27
|
@dependency_files = dependency_files
|
|
16
28
|
end
|
|
17
29
|
|
|
30
|
+
sig { returns(T::Boolean) }
|
|
18
31
|
def library?
|
|
19
32
|
return false unless package_json_may_be_for_library?
|
|
20
33
|
|
|
@@ -23,26 +36,36 @@ module Dependabot
|
|
|
23
36
|
|
|
24
37
|
private
|
|
25
38
|
|
|
39
|
+
sig { returns(Dependabot::DependencyFile) }
|
|
26
40
|
attr_reader :package_json_file
|
|
41
|
+
|
|
42
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
|
27
43
|
attr_reader :credentials
|
|
44
|
+
|
|
45
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
28
46
|
attr_reader :dependency_files
|
|
29
47
|
|
|
48
|
+
sig { returns(T::Boolean) }
|
|
30
49
|
def package_json_may_be_for_library?
|
|
31
50
|
return false unless project_name
|
|
32
|
-
return false if project_name.match?(/\{\{.*\}\}/)
|
|
51
|
+
return false if T.must(project_name).match?(/\{\{.*\}\}/)
|
|
33
52
|
return false unless parsed_package_json["version"]
|
|
34
53
|
return false if parsed_package_json["private"]
|
|
35
54
|
|
|
36
55
|
true
|
|
37
56
|
end
|
|
38
57
|
|
|
58
|
+
sig { returns(T::Boolean) }
|
|
39
59
|
def npm_response_matches_package_json?
|
|
40
60
|
project_description = parsed_package_json["description"]
|
|
41
61
|
return false unless project_description
|
|
42
62
|
|
|
43
63
|
# Check if the project is listed on npm. If it is, it's a library
|
|
44
64
|
url = "#{registry.chomp('/')}/#{escaped_project_name}"
|
|
45
|
-
@project_npm_response ||=
|
|
65
|
+
@project_npm_response ||= T.let(
|
|
66
|
+
Dependabot::RegistryClient.get(url: url),
|
|
67
|
+
T.nilable(Excon::Response)
|
|
68
|
+
)
|
|
46
69
|
return false unless @project_npm_response.status == 200
|
|
47
70
|
|
|
48
71
|
@project_npm_response.body.dup.force_encoding("UTF-8").encode
|
|
@@ -51,24 +74,31 @@ module Dependabot
|
|
|
51
74
|
false
|
|
52
75
|
end
|
|
53
76
|
|
|
77
|
+
sig { returns(T.nilable(String)) }
|
|
54
78
|
def project_name
|
|
55
79
|
parsed_package_json.fetch("name", nil)
|
|
56
80
|
end
|
|
57
81
|
|
|
82
|
+
sig { returns(T.nilable(String)) }
|
|
58
83
|
def escaped_project_name
|
|
59
84
|
project_name&.gsub("/", "%2F")
|
|
60
85
|
end
|
|
61
86
|
|
|
87
|
+
sig { returns(T::Hash[String, T.untyped]) }
|
|
62
88
|
def parsed_package_json
|
|
63
|
-
@parsed_package_json ||=
|
|
89
|
+
@parsed_package_json ||= T.let(
|
|
90
|
+
JSON.parse(T.must(package_json_file.content)),
|
|
91
|
+
T.nilable(T::Hash[String, T.untyped])
|
|
92
|
+
)
|
|
64
93
|
end
|
|
65
94
|
|
|
95
|
+
sig { returns(String) }
|
|
66
96
|
def registry
|
|
67
97
|
Bun::Package::RegistryFinder.new(
|
|
68
98
|
dependency: nil,
|
|
69
99
|
credentials: credentials,
|
|
70
100
|
npmrc_file: dependency_files.find { |f| f.name.end_with?(".npmrc") }
|
|
71
|
-
).registry_from_rc(project_name)
|
|
101
|
+
).registry_from_rc(T.must(project_name)) || "https://registry.npmjs.org"
|
|
72
102
|
end
|
|
73
103
|
end
|
|
74
104
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
################################################################################
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# https://docs.npmjs.com/misc/semver #
|
|
7
7
|
################################################################################
|
|
8
8
|
|
|
9
|
+
require "sorbet-runtime"
|
|
10
|
+
|
|
9
11
|
require "dependabot/bun/requirement"
|
|
10
12
|
require "dependabot/bun/update_checker"
|
|
11
13
|
require "dependabot/bun/version"
|
|
@@ -15,6 +17,8 @@ module Dependabot
|
|
|
15
17
|
module Bun
|
|
16
18
|
class UpdateChecker
|
|
17
19
|
class RequirementsUpdater
|
|
20
|
+
extend T::Sig
|
|
21
|
+
|
|
18
22
|
VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/
|
|
19
23
|
SEPARATOR = /(?<=[a-zA-Z0-9*])[\s|]+(?![\s|-])/
|
|
20
24
|
ALLOWED_UPDATE_STRATEGIES = T.let(
|
|
@@ -27,6 +31,15 @@ module Dependabot
|
|
|
27
31
|
T::Array[Dependabot::RequirementsUpdateStrategy]
|
|
28
32
|
)
|
|
29
33
|
|
|
34
|
+
sig do
|
|
35
|
+
params(
|
|
36
|
+
requirements: T::Array[T::Hash[Symbol, T.untyped]],
|
|
37
|
+
updated_source: T.nilable(T::Hash[Symbol, T.untyped]),
|
|
38
|
+
update_strategy: Dependabot::RequirementsUpdateStrategy,
|
|
39
|
+
latest_resolvable_version: T.nilable(T.any(String, Gem::Version))
|
|
40
|
+
)
|
|
41
|
+
.void
|
|
42
|
+
end
|
|
30
43
|
def initialize(requirements:, updated_source:, update_strategy:,
|
|
31
44
|
latest_resolvable_version:)
|
|
32
45
|
@requirements = requirements
|
|
@@ -37,10 +50,13 @@ module Dependabot
|
|
|
37
50
|
|
|
38
51
|
return unless latest_resolvable_version
|
|
39
52
|
|
|
40
|
-
@latest_resolvable_version =
|
|
41
|
-
version_class.new(latest_resolvable_version)
|
|
53
|
+
@latest_resolvable_version = T.let(
|
|
54
|
+
version_class.new(latest_resolvable_version),
|
|
55
|
+
Bun::Version
|
|
56
|
+
)
|
|
42
57
|
end
|
|
43
58
|
|
|
59
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
44
60
|
def updated_requirements
|
|
45
61
|
return requirements if update_strategy.lockfile_only?
|
|
46
62
|
|
|
@@ -62,17 +78,26 @@ module Dependabot
|
|
|
62
78
|
|
|
63
79
|
private
|
|
64
80
|
|
|
81
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
65
82
|
attr_reader :requirements
|
|
83
|
+
|
|
84
|
+
sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
|
66
85
|
attr_reader :updated_source
|
|
86
|
+
|
|
87
|
+
sig { returns(Dependabot::RequirementsUpdateStrategy) }
|
|
67
88
|
attr_reader :update_strategy
|
|
89
|
+
|
|
90
|
+
sig { returns(T.nilable(Bun::Version)) }
|
|
68
91
|
attr_reader :latest_resolvable_version
|
|
69
92
|
|
|
93
|
+
sig { void }
|
|
70
94
|
def check_update_strategy
|
|
71
95
|
return if ALLOWED_UPDATE_STRATEGIES.include?(update_strategy)
|
|
72
96
|
|
|
73
97
|
raise "Unknown update strategy: #{update_strategy}"
|
|
74
98
|
end
|
|
75
99
|
|
|
100
|
+
sig { returns(T::Boolean) }
|
|
76
101
|
def updating_from_git_to_npm?
|
|
77
102
|
return false unless updated_source.nil?
|
|
78
103
|
|
|
@@ -80,6 +105,7 @@ module Dependabot
|
|
|
80
105
|
original_source&.fetch(:type) == "git"
|
|
81
106
|
end
|
|
82
107
|
|
|
108
|
+
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
83
109
|
def initial_req_after_source_change(req)
|
|
84
110
|
return req unless updating_from_git_to_npm?
|
|
85
111
|
return req unless req[:requirement].nil?
|
|
@@ -87,12 +113,13 @@ module Dependabot
|
|
|
87
113
|
req.merge(requirement: "^#{latest_resolvable_version}")
|
|
88
114
|
end
|
|
89
115
|
|
|
116
|
+
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
90
117
|
def update_version_requirement(req)
|
|
91
118
|
current_requirement = req[:requirement]
|
|
92
119
|
|
|
93
120
|
if current_requirement.match?(/(<|-\s)/i)
|
|
94
121
|
ruby_req = ruby_requirements(current_requirement).first
|
|
95
|
-
return req if ruby_req
|
|
122
|
+
return req if ruby_req&.satisfied_by?(latest_resolvable_version)
|
|
96
123
|
|
|
97
124
|
updated_req = update_range_requirement(current_requirement)
|
|
98
125
|
return req.merge(requirement: updated_req)
|
|
@@ -102,6 +129,7 @@ module Dependabot
|
|
|
102
129
|
req.merge(requirement: update_version_string(reqs.first))
|
|
103
130
|
end
|
|
104
131
|
|
|
132
|
+
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
105
133
|
def update_version_requirement_if_needed(req)
|
|
106
134
|
current_requirement = req[:requirement]
|
|
107
135
|
version = latest_resolvable_version
|
|
@@ -113,6 +141,7 @@ module Dependabot
|
|
|
113
141
|
update_version_requirement(req)
|
|
114
142
|
end
|
|
115
143
|
|
|
144
|
+
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
116
145
|
def widen_requirement(req)
|
|
117
146
|
current_requirement = req[:requirement]
|
|
118
147
|
version = latest_resolvable_version
|
|
@@ -126,7 +155,7 @@ module Dependabot
|
|
|
126
155
|
updated_requirement =
|
|
127
156
|
if reqs.any? { |r| r.match?(/(<|-\s)/i) }
|
|
128
157
|
update_range_requirement(current_requirement)
|
|
129
|
-
elsif current_requirement.strip.split(SEPARATOR).
|
|
158
|
+
elsif current_requirement.strip.split(SEPARATOR).one?
|
|
130
159
|
update_version_string(current_requirement)
|
|
131
160
|
else
|
|
132
161
|
current_requirement
|
|
@@ -135,22 +164,25 @@ module Dependabot
|
|
|
135
164
|
req.merge(requirement: updated_requirement)
|
|
136
165
|
end
|
|
137
166
|
|
|
167
|
+
sig { params(requirement_string: String).returns(T::Array[Bun::Requirement]) }
|
|
138
168
|
def ruby_requirements(requirement_string)
|
|
139
169
|
Bun::Requirement
|
|
140
170
|
.requirements_array(requirement_string)
|
|
141
171
|
end
|
|
142
172
|
|
|
173
|
+
sig { params(req_string: String).returns(String) }
|
|
143
174
|
def update_range_requirement(req_string)
|
|
144
175
|
range_requirements =
|
|
145
176
|
req_string.split(SEPARATOR).select { |r| r.match?(/<|(\s+-\s+)/) }
|
|
146
177
|
|
|
147
|
-
if range_requirements.
|
|
148
|
-
range_requirement = range_requirements.first
|
|
178
|
+
if range_requirements.one?
|
|
179
|
+
range_requirement = T.must(range_requirements.first)
|
|
149
180
|
versions = range_requirement.scan(VERSION_REGEX)
|
|
150
|
-
|
|
181
|
+
version_objects = versions.map { |v| version_class.new(v.to_s) }
|
|
182
|
+
upper_bound = T.must(version_objects.max)
|
|
151
183
|
new_upper_bound = update_greatest_version(
|
|
152
|
-
upper_bound,
|
|
153
|
-
latest_resolvable_version
|
|
184
|
+
upper_bound.to_s,
|
|
185
|
+
T.must(latest_resolvable_version)
|
|
154
186
|
)
|
|
155
187
|
|
|
156
188
|
req_string.sub(
|
|
@@ -162,41 +194,47 @@ module Dependabot
|
|
|
162
194
|
end
|
|
163
195
|
end
|
|
164
196
|
|
|
197
|
+
sig { params(req_string: String).returns(String) }
|
|
165
198
|
def update_version_string(req_string)
|
|
166
199
|
req_string
|
|
167
200
|
.sub(VERSION_REGEX) do |old_version|
|
|
168
201
|
if old_version.match?(/\d-/) ||
|
|
169
|
-
latest_resolvable_version.to_s.match?(/\d-/)
|
|
170
|
-
latest_resolvable_version.to_s
|
|
202
|
+
T.must(latest_resolvable_version).to_s.match?(/\d-/)
|
|
203
|
+
T.must(latest_resolvable_version).to_s
|
|
171
204
|
else
|
|
172
205
|
old_parts = old_version.split(".")
|
|
173
|
-
new_parts = latest_resolvable_version.to_s.split(".")
|
|
174
|
-
|
|
206
|
+
new_parts = T.must(latest_resolvable_version).to_s.split(".")
|
|
207
|
+
.first(old_parts.count)
|
|
175
208
|
new_parts.map.with_index do |part, i|
|
|
176
|
-
old_parts[i]
|
|
209
|
+
old_parts[i]&.match?(/^x\b/) ? "x" : part
|
|
177
210
|
end.join(".")
|
|
178
211
|
end
|
|
179
212
|
end
|
|
180
213
|
end
|
|
181
214
|
|
|
215
|
+
sig { params(old_version: String, version_to_be_permitted: Bun::Version).returns(String) }
|
|
182
216
|
def update_greatest_version(old_version, version_to_be_permitted)
|
|
183
217
|
version = version_class.new(old_version)
|
|
184
218
|
version = version.release if version.prerelease?
|
|
185
219
|
|
|
186
220
|
index_to_update =
|
|
187
|
-
version.segments.map.with_index { |seg, i| seg.zero? ? 0 : i }.max
|
|
221
|
+
version.segments.map.with_index { |seg, i| T.cast(seg, Integer).zero? ? 0 : i }.max || 0
|
|
188
222
|
|
|
189
223
|
version.segments.map.with_index do |_, index|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
224
|
+
segment_value =
|
|
225
|
+
if index < index_to_update
|
|
226
|
+
T.cast(version_to_be_permitted.segments[index], Integer)
|
|
227
|
+
elsif index == index_to_update
|
|
228
|
+
# Cast to Integer before adding 1 to ensure correct type
|
|
229
|
+
T.cast(version_to_be_permitted.segments[index], Integer) + 1
|
|
230
|
+
else
|
|
231
|
+
0
|
|
232
|
+
end
|
|
233
|
+
segment_value.to_s
|
|
197
234
|
end.join(".")
|
|
198
235
|
end
|
|
199
236
|
|
|
237
|
+
sig { returns(T.class_of(Bun::Version)) }
|
|
200
238
|
def version_class
|
|
201
239
|
Bun::Version
|
|
202
240
|
end
|
|
@@ -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/dependency"
|
|
5
7
|
require "dependabot/errors"
|
|
6
8
|
require "dependabot/logger"
|
|
@@ -17,6 +19,36 @@ module Dependabot
|
|
|
17
19
|
module Bun
|
|
18
20
|
class UpdateChecker
|
|
19
21
|
class SubdependencyVersionResolver
|
|
22
|
+
extend T::Sig
|
|
23
|
+
|
|
24
|
+
sig { returns(Dependency) }
|
|
25
|
+
attr_reader :dependency
|
|
26
|
+
|
|
27
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
|
28
|
+
attr_reader :credentials
|
|
29
|
+
|
|
30
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
31
|
+
attr_reader :dependency_files
|
|
32
|
+
|
|
33
|
+
sig { returns(T::Array[String]) }
|
|
34
|
+
attr_reader :ignored_versions
|
|
35
|
+
|
|
36
|
+
sig { returns(T.nilable(T.any(String, Gem::Version))) }
|
|
37
|
+
attr_reader :latest_allowable_version
|
|
38
|
+
|
|
39
|
+
sig { returns(T.nilable(String)) }
|
|
40
|
+
attr_reader :repo_contents_path
|
|
41
|
+
|
|
42
|
+
sig do
|
|
43
|
+
params(
|
|
44
|
+
dependency: Dependency,
|
|
45
|
+
credentials: T::Array[Dependabot::Credential],
|
|
46
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
|
47
|
+
ignored_versions: T::Array[String],
|
|
48
|
+
latest_allowable_version: T.nilable(T.any(String, Gem::Version)),
|
|
49
|
+
repo_contents_path: T.nilable(String)
|
|
50
|
+
).void
|
|
51
|
+
end
|
|
20
52
|
def initialize(dependency:, credentials:, dependency_files:,
|
|
21
53
|
ignored_versions:, latest_allowable_version:, repo_contents_path:)
|
|
22
54
|
@dependency = dependency
|
|
@@ -27,11 +59,12 @@ module Dependabot
|
|
|
27
59
|
@repo_contents_path = repo_contents_path
|
|
28
60
|
end
|
|
29
61
|
|
|
62
|
+
sig { returns(T.nilable(T.any(String, Gem::Version))) }
|
|
30
63
|
def latest_resolvable_version
|
|
31
64
|
raise "Not a subdependency!" if dependency.requirements.any?
|
|
32
65
|
return if bundled_dependency?
|
|
33
66
|
|
|
34
|
-
base_dir = dependency_files.first.directory
|
|
67
|
+
base_dir = T.must(dependency_files.first).directory
|
|
35
68
|
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
|
36
69
|
dependency_files_builder.write_temporary_dependency_files
|
|
37
70
|
|
|
@@ -53,22 +86,21 @@ module Dependabot
|
|
|
53
86
|
|
|
54
87
|
private
|
|
55
88
|
|
|
56
|
-
|
|
57
|
-
attr_reader :credentials
|
|
58
|
-
attr_reader :dependency_files
|
|
59
|
-
attr_reader :ignored_versions
|
|
60
|
-
attr_reader :latest_allowable_version
|
|
61
|
-
attr_reader :repo_contents_path
|
|
62
|
-
|
|
89
|
+
sig { params(lockfile: Dependabot::DependencyFile).returns(String) }
|
|
63
90
|
def update_subdependency_in_lockfile(lockfile)
|
|
64
91
|
lockfile_name = Pathname.new(lockfile.name).basename.to_s
|
|
65
92
|
path = Pathname.new(lockfile.name).dirname.to_s
|
|
66
93
|
|
|
67
|
-
updated_files =
|
|
94
|
+
updated_files = if lockfile.name.end_with?("bun.lock")
|
|
95
|
+
run_bun_updater(path, lockfile_name)
|
|
96
|
+
else
|
|
97
|
+
raise "Unsupported lockfile type: #{lockfile.name}"
|
|
98
|
+
end
|
|
68
99
|
|
|
69
100
|
updated_files.fetch(lockfile_name)
|
|
70
101
|
end
|
|
71
102
|
|
|
103
|
+
sig { params(updated_lockfiles: T::Array[Dependabot::DependencyFile]).returns(T.nilable(Gem::Version)) }
|
|
72
104
|
def version_from_updated_lockfiles(updated_lockfiles)
|
|
73
105
|
updated_files = dependency_files -
|
|
74
106
|
dependency_files_builder.lockfiles +
|
|
@@ -84,6 +116,7 @@ module Dependabot
|
|
|
84
116
|
version_class.new(updated_version)
|
|
85
117
|
end
|
|
86
118
|
|
|
119
|
+
sig { params(path: String, lockfile_name: String).returns(T::Hash[String, String]) }
|
|
87
120
|
def run_bun_updater(path, lockfile_name)
|
|
88
121
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
|
89
122
|
Dir.chdir(path) do
|
|
@@ -96,35 +129,43 @@ module Dependabot
|
|
|
96
129
|
end
|
|
97
130
|
end
|
|
98
131
|
|
|
132
|
+
sig { returns(T.class_of(Dependabot::Version)) }
|
|
99
133
|
def version_class
|
|
100
134
|
dependency.version_class
|
|
101
135
|
end
|
|
102
136
|
|
|
137
|
+
sig { returns(Dependabot::Dependency) }
|
|
103
138
|
def updated_dependency
|
|
104
139
|
Dependabot::Dependency.new(
|
|
105
140
|
name: dependency.name,
|
|
106
|
-
version: latest_allowable_version,
|
|
141
|
+
version: T.cast(latest_allowable_version, T.nilable(T.any(String, Dependabot::Version))),
|
|
107
142
|
previous_version: dependency.version,
|
|
108
143
|
requirements: [],
|
|
109
144
|
package_manager: dependency.package_manager
|
|
110
145
|
)
|
|
111
146
|
end
|
|
112
147
|
|
|
148
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
113
149
|
def filtered_lockfiles
|
|
114
|
-
@filtered_lockfiles ||=
|
|
150
|
+
@filtered_lockfiles ||= T.let(
|
|
115
151
|
SubDependencyFilesFilterer.new(
|
|
116
152
|
dependency_files: dependency_files,
|
|
117
153
|
updated_dependencies: [updated_dependency]
|
|
118
|
-
).files_requiring_update
|
|
154
|
+
).files_requiring_update,
|
|
155
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
|
156
|
+
)
|
|
119
157
|
end
|
|
120
158
|
|
|
159
|
+
sig { returns(Dependabot::Bun::UpdateChecker::DependencyFilesBuilder) }
|
|
121
160
|
def dependency_files_builder
|
|
122
|
-
@dependency_files_builder ||=
|
|
161
|
+
@dependency_files_builder ||= T.let(
|
|
123
162
|
DependencyFilesBuilder.new(
|
|
124
163
|
dependency: dependency,
|
|
125
164
|
dependency_files: dependency_files,
|
|
126
165
|
credentials: credentials
|
|
127
|
-
)
|
|
166
|
+
),
|
|
167
|
+
T.nilable(Dependabot::Bun::UpdateChecker::DependencyFilesBuilder)
|
|
168
|
+
)
|
|
128
169
|
end
|
|
129
170
|
|
|
130
171
|
# TODO: We should try and fix this by updating the parent that's not
|
|
@@ -143,6 +184,7 @@ module Dependabot
|
|
|
143
184
|
# Updating the sub-dependency by deleting the entry works but it gets
|
|
144
185
|
# removed from the bundled set of dependencies and moved top level
|
|
145
186
|
# resulting in a bunch of package duplication which is pretty confusing.
|
|
187
|
+
sig { returns(T::Boolean) }
|
|
146
188
|
def bundled_dependency?
|
|
147
189
|
dependency.subdependency_metadata
|
|
148
190
|
&.any? { |h| h.fetch(:npm_bundled, false) } ||
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require "sorbet-runtime"
|
|
4
5
|
require "stringio"
|
|
6
|
+
|
|
5
7
|
require "dependabot/dependency"
|
|
6
8
|
require "dependabot/errors"
|
|
7
9
|
require "dependabot/logger"
|
|
@@ -16,6 +18,15 @@ module Dependabot
|
|
|
16
18
|
module Bun
|
|
17
19
|
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
18
20
|
class VulnerabilityAuditor
|
|
21
|
+
extend T::Sig
|
|
22
|
+
|
|
23
|
+
sig do
|
|
24
|
+
params(
|
|
25
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
|
26
|
+
credentials: T::Array[Dependabot::Credential]
|
|
27
|
+
)
|
|
28
|
+
.void
|
|
29
|
+
end
|
|
19
30
|
def initialize(dependency_files:, credentials:)
|
|
20
31
|
@dependency_files = dependency_files
|
|
21
32
|
@credentials = credentials
|
|
@@ -43,6 +54,13 @@ module Dependabot
|
|
|
43
54
|
# * :top_level_ancestors [Array<String>] the names of all top-level dependencies with a transitive
|
|
44
55
|
# dependency on the dependency
|
|
45
56
|
# * :explanation [String] an explanation for why the project failed the vulnerability auditor run
|
|
57
|
+
sig do
|
|
58
|
+
params(
|
|
59
|
+
dependency: Dependabot::Dependency,
|
|
60
|
+
security_advisories: T::Array[Dependabot::SecurityAdvisory]
|
|
61
|
+
)
|
|
62
|
+
.returns(T::Hash[String, T.untyped])
|
|
63
|
+
end
|
|
46
64
|
def audit(dependency:, security_advisories:)
|
|
47
65
|
Dependabot.logger.info("VulnerabilityAuditor: starting audit")
|
|
48
66
|
|
|
@@ -68,10 +86,13 @@ module Dependabot
|
|
|
68
86
|
}
|
|
69
87
|
end
|
|
70
88
|
|
|
71
|
-
audit_result =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
audit_result = T.cast(
|
|
90
|
+
SharedHelpers.run_helper_subprocess(
|
|
91
|
+
command: NativeHelpers.helper_path,
|
|
92
|
+
function: "npm:vulnerabilityAuditor",
|
|
93
|
+
args: [Dir.pwd, vuln_versions]
|
|
94
|
+
),
|
|
95
|
+
T::Hash[String, T.untyped]
|
|
75
96
|
)
|
|
76
97
|
|
|
77
98
|
validation_result = validate_audit_result(audit_result, security_advisories)
|
|
@@ -86,24 +107,36 @@ module Dependabot
|
|
|
86
107
|
end
|
|
87
108
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
88
109
|
log_helper_subprocess_failure(dependency, e)
|
|
89
|
-
fix_unavailable
|
|
110
|
+
T.must(fix_unavailable)
|
|
90
111
|
end
|
|
91
112
|
# rubocop:enable Metrics/MethodLength
|
|
92
113
|
|
|
93
114
|
private
|
|
94
115
|
|
|
116
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
95
117
|
attr_reader :dependency_files
|
|
118
|
+
|
|
119
|
+
sig { returns(T::Array[Dependabot::Credential]) }
|
|
96
120
|
attr_reader :credentials
|
|
97
121
|
|
|
122
|
+
sig { params(validation_result: Symbol, dependency: Dependabot::Dependency).returns(String) }
|
|
98
123
|
def explain_fix_unavailable(validation_result, dependency)
|
|
99
124
|
case validation_result
|
|
100
125
|
when :fix_unavailable, :dependency_still_vulnerable, :downgrades_dependencies
|
|
101
126
|
"No patched version available for #{dependency.name}"
|
|
102
127
|
when :fix_incomplete
|
|
103
128
|
"The lockfile might be out of sync?"
|
|
129
|
+
else
|
|
130
|
+
raise "Unexpected validation result: #{validation_result}"
|
|
104
131
|
end
|
|
105
132
|
end
|
|
106
133
|
|
|
134
|
+
sig do
|
|
135
|
+
params(
|
|
136
|
+
audit_result: T::Hash[String, T.untyped],
|
|
137
|
+
security_advisories: T::Array[Dependabot::SecurityAdvisory]
|
|
138
|
+
).returns(Symbol)
|
|
139
|
+
end
|
|
107
140
|
def validate_audit_result(audit_result, security_advisories)
|
|
108
141
|
return :fix_unavailable unless audit_result["fix_available"]
|
|
109
142
|
return :dependency_still_vulnerable if dependency_still_vulnerable?(audit_result, security_advisories)
|
|
@@ -113,6 +146,13 @@ module Dependabot
|
|
|
113
146
|
:viable
|
|
114
147
|
end
|
|
115
148
|
|
|
149
|
+
sig do
|
|
150
|
+
params(
|
|
151
|
+
audit_result: T::Hash[String, T.untyped],
|
|
152
|
+
security_advisories: T::Array[Dependabot::SecurityAdvisory]
|
|
153
|
+
)
|
|
154
|
+
.returns(T::Boolean)
|
|
155
|
+
end
|
|
116
156
|
def dependency_still_vulnerable?(audit_result, security_advisories)
|
|
117
157
|
# vulnerable dependency is removed if the target version is nil
|
|
118
158
|
return false unless audit_result["target_version"]
|
|
@@ -121,6 +161,7 @@ module Dependabot
|
|
|
121
161
|
security_advisories.any? { |a| a.vulnerable?(version) }
|
|
122
162
|
end
|
|
123
163
|
|
|
164
|
+
sig { params(audit_result: T::Hash[String, T.untyped]).returns(T::Boolean) }
|
|
124
165
|
def downgrades_dependencies?(audit_result)
|
|
125
166
|
return true if downgrades_version?(audit_result["current_version"], audit_result["target_version"])
|
|
126
167
|
|
|
@@ -129,6 +170,13 @@ module Dependabot
|
|
|
129
170
|
end
|
|
130
171
|
end
|
|
131
172
|
|
|
173
|
+
sig do
|
|
174
|
+
params(
|
|
175
|
+
current_version: T.nilable(T.any(String, Integer, Gem::Version)),
|
|
176
|
+
target_version: T.nilable(T.any(String, Integer, Gem::Version))
|
|
177
|
+
)
|
|
178
|
+
.returns(T::Boolean)
|
|
179
|
+
end
|
|
132
180
|
def downgrades_version?(current_version, target_version)
|
|
133
181
|
return false unless target_version
|
|
134
182
|
|
|
@@ -137,14 +185,19 @@ module Dependabot
|
|
|
137
185
|
current > target
|
|
138
186
|
end
|
|
139
187
|
|
|
188
|
+
sig { params(audit_result: T::Hash[String, T.untyped]).returns(T::Boolean) }
|
|
140
189
|
def fix_incomplete?(audit_result)
|
|
141
190
|
audit_result["fix_updates"].any? { |update| !update.key?("target_version") } ||
|
|
142
191
|
audit_result["fix_updates"].empty?
|
|
143
192
|
end
|
|
144
193
|
|
|
194
|
+
sig do
|
|
195
|
+
params(dependency: Dependabot::Dependency,
|
|
196
|
+
error: Dependabot::SharedHelpers::HelperSubprocessFailed).void
|
|
197
|
+
end
|
|
145
198
|
def log_helper_subprocess_failure(dependency, error)
|
|
146
199
|
# See `Dependabot::SharedHelpers.run_helper_subprocess` for details on error context
|
|
147
|
-
context = error.error_context
|
|
200
|
+
context = error.error_context
|
|
148
201
|
|
|
149
202
|
builder = ::StringIO.new
|
|
150
203
|
builder << "VulnerabilityAuditor: "
|
|
@@ -11,6 +11,7 @@ module Dependabot
|
|
|
11
11
|
module Bun
|
|
12
12
|
class UpdateChecker < Dependabot::UpdateCheckers::Base # rubocop:disable Metrics/ClassLength
|
|
13
13
|
extend T::Sig
|
|
14
|
+
|
|
14
15
|
require_relative "update_checker/requirements_updater"
|
|
15
16
|
require_relative "update_checker/library_detector"
|
|
16
17
|
require_relative "update_checker/latest_version_finder"
|
|
@@ -31,7 +32,6 @@ module Dependabot
|
|
|
31
32
|
requirements_update_strategy: T.nilable(Dependabot::RequirementsUpdateStrategy),
|
|
32
33
|
dependency_group: T.nilable(Dependabot::DependencyGroup),
|
|
33
34
|
update_cooldown: T.nilable(Dependabot::Package::ReleaseCooldownOptions),
|
|
34
|
-
exclude_paths: T.nilable(T::Array[String]),
|
|
35
35
|
options: T::Hash[Symbol, T.untyped]
|
|
36
36
|
)
|
|
37
37
|
.void
|
|
@@ -40,7 +40,7 @@ module Dependabot
|
|
|
40
40
|
repo_contents_path: nil, ignored_versions: [],
|
|
41
41
|
raise_on_ignored: false, security_advisories: [],
|
|
42
42
|
requirements_update_strategy: nil, dependency_group: nil,
|
|
43
|
-
update_cooldown: nil,
|
|
43
|
+
update_cooldown: nil, options: {})
|
|
44
44
|
@latest_version = T.let(nil, T.nilable(T.any(String, Gem::Version)))
|
|
45
45
|
@latest_resolvable_version = T.let(nil, T.nilable(T.any(String, Dependabot::Version)))
|
|
46
46
|
@updated_requirements = T.let(nil, T.nilable(T::Array[T::Hash[Symbol, T.untyped]]))
|