dependabot-cargo 0.305.0 → 0.306.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72b4039f8ac30bc8421818e11b43de5d2d9997cd5fdb5a4e53bba00b3bc7df15
|
4
|
+
data.tar.gz: c4a81d07aecd06538167a7c628e17c4f1aea1e0214816e5a8aaf29cad9796dc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0730b405c1f82cecdd1f1efb647e6eea37420b9d241fe46c6225bd41dd329388bff05b007051e6f901e110b27cd567b8a5dbe7a86600056f5f78d6dd657a4155
|
7
|
+
data.tar.gz: eb7cfa179819ae14421be61f9c847d1e79c94590ba90b0063d74a6c314e7b56740a2d9a3b9ef54ca68abeae8c2de72079426702ef641da9293dd1a4eab5c0df2
|
@@ -0,0 +1,231 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "json"
|
5
|
+
require "time"
|
6
|
+
require "cgi"
|
7
|
+
require "excon"
|
8
|
+
require "nokogiri"
|
9
|
+
require "sorbet-runtime"
|
10
|
+
require "dependabot/registry_client"
|
11
|
+
require "dependabot/cargo"
|
12
|
+
require "dependabot/package/package_release"
|
13
|
+
require "dependabot/package/package_details"
|
14
|
+
|
15
|
+
module Dependabot
|
16
|
+
module Cargo
|
17
|
+
module Package
|
18
|
+
class PackageDetailsFetcher
|
19
|
+
extend T::Sig
|
20
|
+
|
21
|
+
CRATES_IO_API = "https://crates.io/api/v1/crates"
|
22
|
+
PACKAGE_TYPE = "gem"
|
23
|
+
PACKAGE_LANGUAGE = "rust"
|
24
|
+
|
25
|
+
# fallback for empty requirement
|
26
|
+
RUST_REQUIREMENT = "1.0"
|
27
|
+
|
28
|
+
sig do
|
29
|
+
params(
|
30
|
+
dependency: Dependabot::Dependency,
|
31
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
32
|
+
credentials: T::Array[Dependabot::Credential]
|
33
|
+
).void
|
34
|
+
end
|
35
|
+
def initialize(dependency:, dependency_files:, credentials:)
|
36
|
+
@dependency = dependency
|
37
|
+
@dependency_files = dependency_files
|
38
|
+
@credentials = credentials
|
39
|
+
|
40
|
+
@source_type = T.let(nil, T.nilable(String))
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { returns(Dependabot::Dependency) }
|
44
|
+
attr_reader :dependency
|
45
|
+
|
46
|
+
sig { returns(T::Array[T.untyped]) }
|
47
|
+
attr_reader :dependency_files
|
48
|
+
|
49
|
+
sig { returns(T::Array[T.untyped]) }
|
50
|
+
attr_reader :credentials
|
51
|
+
|
52
|
+
sig do
|
53
|
+
returns(T.nilable(Dependabot::Package::PackageDetails))
|
54
|
+
end
|
55
|
+
def fetch
|
56
|
+
package_releases = crates_listing.fetch("versions", []).reject { |v| v["yanked"] }.map do |release|
|
57
|
+
package_release(
|
58
|
+
version: release["num"] || release["vers"],
|
59
|
+
released_at: release["created_at"] ? Time.parse(release["created_at"]) : nil,
|
60
|
+
downloads: release["downloads"],
|
61
|
+
url: "#{CRATES_IO_API}/#{release['dl_path']}",
|
62
|
+
rust_version: release["rust"]
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
package_details(package_releases)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
sig do
|
72
|
+
returns(T.untyped)
|
73
|
+
end
|
74
|
+
def crates_listing
|
75
|
+
return @crates_listing unless @crates_listing.nil?
|
76
|
+
|
77
|
+
info = fetch_dependency_info
|
78
|
+
index = fetch_index(info)
|
79
|
+
|
80
|
+
hdrs = default_headers
|
81
|
+
hdrs.merge!(auth_headers(info)) if index != CRATES_IO_API
|
82
|
+
|
83
|
+
url = metadata_fetch_url(dependency, index)
|
84
|
+
|
85
|
+
Dependabot.logger.info("Calling #{url} to fetch metadata for #{dependency.name} from #{index}")
|
86
|
+
|
87
|
+
response = fetch_response(url, hdrs)
|
88
|
+
return {} if response.status == 404
|
89
|
+
|
90
|
+
@crates_listing = T.let(parse_response(response, index), T.nilable(T::Hash[T.untyped, T.untyped]))
|
91
|
+
|
92
|
+
Dependabot.logger.info("Fetched metadata for #{dependency.name} from #{index} successfully")
|
93
|
+
|
94
|
+
@crates_listing
|
95
|
+
end
|
96
|
+
|
97
|
+
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
98
|
+
def fetch_dependency_info
|
99
|
+
dependency.requirements.filter_map { |r| r[:source] }.first
|
100
|
+
end
|
101
|
+
|
102
|
+
sig { params(info: T.untyped).returns(String) }
|
103
|
+
def fetch_index(info)
|
104
|
+
(info && info[:index]) || CRATES_IO_API
|
105
|
+
end
|
106
|
+
|
107
|
+
sig { returns(T::Hash[T.untyped, T.untyped]) }
|
108
|
+
def default_headers
|
109
|
+
{ "User-Agent" => "Dependabot (dependabot.com)" }
|
110
|
+
end
|
111
|
+
|
112
|
+
sig { params(info: T.untyped).returns(T::Hash[T.untyped, T.untyped]) }
|
113
|
+
def auth_headers(info)
|
114
|
+
registry_creds = credentials.find do |cred|
|
115
|
+
cred["type"] == "cargo_registry" && cred["registry"] == info[:name]
|
116
|
+
end
|
117
|
+
|
118
|
+
return {} if registry_creds.nil?
|
119
|
+
|
120
|
+
token = registry_creds["token"] || "placeholder_token"
|
121
|
+
{ "Authorization" => token }
|
122
|
+
end
|
123
|
+
|
124
|
+
sig { params(url: String, headers: T.untyped).returns(Excon::Response) }
|
125
|
+
def fetch_response(url, headers)
|
126
|
+
Excon.get(
|
127
|
+
url,
|
128
|
+
idempotent: true,
|
129
|
+
**SharedHelpers.excon_defaults(headers: headers)
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
sig { params(response: Excon::Response, index: T.untyped).returns(T::Hash[T.untyped, T.untyped]) }
|
134
|
+
def parse_response(response, index)
|
135
|
+
if index.start_with?("sparse+")
|
136
|
+
parsed_response = response.body.lines.map { |line| JSON.parse(line) }
|
137
|
+
{ "versions" => parsed_response }
|
138
|
+
else
|
139
|
+
JSON.parse(response.body)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
sig { params(dependency: T.untyped, index: T.untyped).returns(String) }
|
144
|
+
def metadata_fetch_url(dependency, index)
|
145
|
+
return "#{index}/#{dependency.name}" if index == CRATES_IO_API
|
146
|
+
|
147
|
+
# Determine cargo's index file path for the dependency
|
148
|
+
index = index.delete_prefix("sparse+")
|
149
|
+
name_length = dependency.name.length
|
150
|
+
dependency_path = case name_length
|
151
|
+
when 1, 2
|
152
|
+
"#{name_length}/#{dependency.name}"
|
153
|
+
when 3
|
154
|
+
"#{name_length}/#{dependency.name[0..1]}/#{dependency.name}"
|
155
|
+
else
|
156
|
+
"#{dependency.name[0..1]}/#{dependency.name[2..3]}/#{dependency.name}"
|
157
|
+
end
|
158
|
+
|
159
|
+
"#{index}#{'/' unless index.end_with?('/')}#{dependency_path}"
|
160
|
+
end
|
161
|
+
|
162
|
+
sig { returns(T::Boolean) }
|
163
|
+
def wants_prerelease?
|
164
|
+
return true if dependency.numeric_version&.prerelease?
|
165
|
+
|
166
|
+
dependency.requirements.any? do |req|
|
167
|
+
reqs = (req.fetch(:requirement) || "").split(",").map(&:strip)
|
168
|
+
reqs.any? { |r| r.match?(/[A-Za-z]/) }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
sig { returns(T.class_of(Dependabot::Version)) }
|
173
|
+
def version_class
|
174
|
+
dependency.version_class
|
175
|
+
end
|
176
|
+
|
177
|
+
sig { returns(T.class_of(Dependabot::Requirement)) }
|
178
|
+
def requirement_class
|
179
|
+
dependency.requirement_class
|
180
|
+
end
|
181
|
+
|
182
|
+
sig do
|
183
|
+
params(
|
184
|
+
version: String,
|
185
|
+
released_at: T.nilable(Time),
|
186
|
+
downloads: T.nilable(Integer),
|
187
|
+
url: T.nilable(String),
|
188
|
+
rust_version: T.nilable(String),
|
189
|
+
yanked: T::Boolean
|
190
|
+
).returns(Dependabot::Package::PackageRelease)
|
191
|
+
end
|
192
|
+
def package_release(version:, released_at:, downloads:, url:, rust_version:, yanked: false)
|
193
|
+
Dependabot::Package::PackageRelease.new(
|
194
|
+
version: Cargo::Version.new(version),
|
195
|
+
released_at: released_at,
|
196
|
+
yanked: yanked,
|
197
|
+
yanked_reason: nil,
|
198
|
+
downloads: downloads,
|
199
|
+
url: url,
|
200
|
+
package_type: PACKAGE_TYPE,
|
201
|
+
language: Dependabot::Package::PackageLanguage.new(
|
202
|
+
name: PACKAGE_LANGUAGE,
|
203
|
+
version: nil,
|
204
|
+
requirement: language_requirement(rust_version)
|
205
|
+
)
|
206
|
+
)
|
207
|
+
end
|
208
|
+
|
209
|
+
sig { params(req_string: T.nilable(String)).returns(T.nilable(Requirement)) }
|
210
|
+
def language_requirement(req_string)
|
211
|
+
return Requirement.new(req_string) if req_string && !req_string.empty?
|
212
|
+
|
213
|
+
nil
|
214
|
+
end
|
215
|
+
|
216
|
+
sig do
|
217
|
+
params(releases: T::Array[Dependabot::Package::PackageRelease])
|
218
|
+
.returns(Dependabot::Package::PackageDetails)
|
219
|
+
end
|
220
|
+
def package_details(releases)
|
221
|
+
@package_details ||= T.let(
|
222
|
+
Dependabot::Package::PackageDetails.new(
|
223
|
+
dependency: dependency,
|
224
|
+
releases: releases.reverse.uniq(&:version)
|
225
|
+
), T.nilable(Dependabot::Package::PackageDetails)
|
226
|
+
)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -5,16 +5,16 @@ require "excon"
|
|
5
5
|
require "dependabot/cargo/update_checker"
|
6
6
|
require "dependabot/update_checkers/version_filters"
|
7
7
|
require "dependabot/registry_client"
|
8
|
+
require "dependabot/cargo/package/package_details_fetcher"
|
9
|
+
require "dependabot/package/package_latest_version_finder"
|
8
10
|
require "sorbet-runtime"
|
9
11
|
|
10
12
|
module Dependabot
|
11
13
|
module Cargo
|
12
14
|
class UpdateChecker
|
13
|
-
class LatestVersionFinder
|
15
|
+
class LatestVersionFinder < Dependabot::Package::PackageLatestVersionFinder
|
14
16
|
extend T::Sig
|
15
17
|
|
16
|
-
CRATES_IO_API = "https://crates.io/api/v1/crates"
|
17
|
-
|
18
18
|
def initialize(dependency:, dependency_files:, credentials:,
|
19
19
|
ignored_versions:, raise_on_ignored: false,
|
20
20
|
security_advisories:)
|
@@ -26,169 +26,28 @@ module Dependabot
|
|
26
26
|
@security_advisories = security_advisories
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
def lowest_security_fix_version
|
34
|
-
@lowest_security_fix_version ||= fetch_lowest_security_fix_version
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
attr_reader :dependency
|
40
|
-
attr_reader :dependency_files
|
41
|
-
attr_reader :credentials
|
42
|
-
attr_reader :ignored_versions
|
43
|
-
attr_reader :security_advisories
|
44
|
-
|
45
|
-
def fetch_latest_version
|
46
|
-
versions = available_versions
|
47
|
-
versions = filter_prerelease_versions(versions)
|
48
|
-
versions = filter_ignored_versions(versions)
|
49
|
-
versions.max
|
29
|
+
sig do
|
30
|
+
override.returns(T.nilable(Dependabot::Package::PackageDetails))
|
50
31
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
versions = filter_ignored_versions(versions)
|
58
|
-
versions = filter_lower_versions(versions)
|
59
|
-
|
60
|
-
versions.min
|
32
|
+
def package_details
|
33
|
+
@package_details ||= Package::PackageDetailsFetcher.new(
|
34
|
+
dependency: dependency,
|
35
|
+
dependency_files: dependency_files,
|
36
|
+
credentials: credentials
|
37
|
+
).fetch
|
61
38
|
end
|
62
39
|
|
63
|
-
|
64
|
-
|
65
|
-
return versions_array if wants_prerelease?
|
66
|
-
|
67
|
-
filtered = versions_array.reject(&:prerelease?)
|
68
|
-
if versions_array.count > filtered.count
|
69
|
-
Dependabot.logger.info("Filtered out #{versions_array.count - filtered.count} pre-release versions")
|
70
|
-
end
|
71
|
-
filtered
|
72
|
-
end
|
73
|
-
|
74
|
-
sig { params(versions_array: T::Array[Gem::Version]).returns(T::Array[Gem::Version]) }
|
75
|
-
def filter_ignored_versions(versions_array)
|
76
|
-
filtered = versions_array
|
77
|
-
.reject { |v| ignore_requirements.any? { |r| r.satisfied_by?(v) } }
|
78
|
-
if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(versions_array).any?
|
79
|
-
raise Dependabot::AllVersionsIgnored
|
80
|
-
end
|
81
|
-
|
82
|
-
if versions_array.count > filtered.count
|
83
|
-
Dependabot.logger.info("Filtered out #{versions_array.count - filtered.count} ignored versions")
|
84
|
-
end
|
85
|
-
|
86
|
-
filtered
|
87
|
-
end
|
88
|
-
|
89
|
-
def filter_lower_versions(versions_array)
|
90
|
-
return versions_array unless dependency.numeric_version
|
91
|
-
|
92
|
-
versions_array
|
93
|
-
.select { |version| version > dependency.numeric_version }
|
94
|
-
end
|
95
|
-
|
96
|
-
def available_versions
|
97
|
-
crates_listing
|
98
|
-
.fetch("versions", [])
|
99
|
-
.reject { |v| v["yanked"] }
|
100
|
-
# Handle both default and sparse registry responses.
|
101
|
-
# Default registry uses "num" for version number.
|
102
|
-
# Sparse registry uses "vers" for version number.
|
103
|
-
.map do |v|
|
104
|
-
version_number = v["num"] || v["vers"]
|
105
|
-
version_class.new(version_number)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def crates_listing
|
110
|
-
return @crates_listing unless @crates_listing.nil?
|
111
|
-
|
112
|
-
info = fetch_dependency_info
|
113
|
-
index = fetch_index(info)
|
114
|
-
|
115
|
-
hdrs = default_headers
|
116
|
-
hdrs.merge!(auth_headers(info)) if index != CRATES_IO_API
|
117
|
-
|
118
|
-
url = metadata_fetch_url(dependency, index)
|
119
|
-
|
120
|
-
# B4PR
|
121
|
-
Dependabot.logger.info("Calling #{url} to fetch metadata for #{dependency.name} from #{index}")
|
122
|
-
|
123
|
-
response = fetch_response(url, hdrs)
|
124
|
-
return {} if response.status == 404
|
125
|
-
|
126
|
-
@crates_listing = parse_response(response, index)
|
127
|
-
|
128
|
-
# B4PR
|
129
|
-
Dependabot.logger.info("Fetched metadata for #{dependency.name} from #{index} successfully")
|
130
|
-
|
131
|
-
@crates_listing
|
132
|
-
end
|
133
|
-
|
134
|
-
def fetch_dependency_info
|
135
|
-
dependency.requirements.filter_map { |r| r[:source] }.first
|
136
|
-
end
|
137
|
-
|
138
|
-
def fetch_index(info)
|
139
|
-
(info && info[:index]) || CRATES_IO_API
|
140
|
-
end
|
141
|
-
|
142
|
-
def default_headers
|
143
|
-
{ "User-Agent" => "Dependabot (dependabot.com)" }
|
144
|
-
end
|
145
|
-
|
146
|
-
def auth_headers(info)
|
147
|
-
registry_creds = credentials.find do |cred|
|
148
|
-
cred["type"] == "cargo_registry" && cred["registry"] == info[:name]
|
149
|
-
end
|
150
|
-
|
151
|
-
return {} if registry_creds.nil?
|
152
|
-
|
153
|
-
token = registry_creds["token"] || "placeholder_token"
|
154
|
-
{ "Authorization" => token }
|
155
|
-
end
|
156
|
-
|
157
|
-
def fetch_response(url, headers)
|
158
|
-
Excon.get(
|
159
|
-
url,
|
160
|
-
idempotent: true,
|
161
|
-
**SharedHelpers.excon_defaults(headers: headers)
|
162
|
-
)
|
40
|
+
def latest_version
|
41
|
+
@latest_version ||= fetch_latest_version
|
163
42
|
end
|
164
43
|
|
165
|
-
def
|
166
|
-
|
167
|
-
parsed_response = response.body.lines.map { |line| JSON.parse(line) }
|
168
|
-
{ "versions" => parsed_response }
|
169
|
-
else
|
170
|
-
JSON.parse(response.body)
|
171
|
-
end
|
44
|
+
def lowest_security_fix_version
|
45
|
+
@lowest_security_fix_version ||= fetch_lowest_security_fix_version(language_version: nil)
|
172
46
|
end
|
173
47
|
|
174
|
-
|
175
|
-
return "#{index}/#{dependency.name}" if index == CRATES_IO_API
|
176
|
-
|
177
|
-
# Determine cargo's index file path for the dependency
|
178
|
-
index = index.delete_prefix("sparse+")
|
179
|
-
name_length = dependency.name.length
|
180
|
-
dependency_path = case name_length
|
181
|
-
when 1, 2
|
182
|
-
"#{name_length}/#{dependency.name}"
|
183
|
-
when 3
|
184
|
-
"#{name_length}/#{dependency.name[0..1]}/#{dependency.name}"
|
185
|
-
else
|
186
|
-
"#{dependency.name[0..1]}/#{dependency.name[2..3]}/#{dependency.name}"
|
187
|
-
end
|
188
|
-
|
189
|
-
"#{index}#{'/' unless index.end_with?('/')}#{dependency_path}"
|
190
|
-
end
|
48
|
+
protected
|
191
49
|
|
50
|
+
sig { override.returns(T::Boolean) }
|
192
51
|
def wants_prerelease?
|
193
52
|
return true if dependency.numeric_version&.prerelease?
|
194
53
|
|
@@ -198,16 +57,16 @@ module Dependabot
|
|
198
57
|
end
|
199
58
|
end
|
200
59
|
|
201
|
-
|
202
|
-
ignored_versions.flat_map { |req| requirement_class.requirements_array(req) }
|
203
|
-
end
|
60
|
+
private
|
204
61
|
|
205
|
-
|
206
|
-
|
207
|
-
|
62
|
+
attr_reader :dependency
|
63
|
+
attr_reader :dependency_files
|
64
|
+
attr_reader :credentials
|
65
|
+
attr_reader :ignored_versions
|
66
|
+
attr_reader :security_advisories
|
208
67
|
|
209
|
-
def
|
210
|
-
|
68
|
+
def apply_post_fetch_lowest_security_fix_versions_filter(versions)
|
69
|
+
filter_prerelease_versions(versions)
|
211
70
|
end
|
212
71
|
end
|
213
72
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-cargo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.306.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.306.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.306.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -251,6 +251,7 @@ files:
|
|
251
251
|
- lib/dependabot/cargo/helpers.rb
|
252
252
|
- lib/dependabot/cargo/language.rb
|
253
253
|
- lib/dependabot/cargo/metadata_finder.rb
|
254
|
+
- lib/dependabot/cargo/package/package_details_fetcher.rb
|
254
255
|
- lib/dependabot/cargo/package_manager.rb
|
255
256
|
- lib/dependabot/cargo/registry_fetcher.rb
|
256
257
|
- lib/dependabot/cargo/requirement.rb
|
@@ -265,7 +266,7 @@ licenses:
|
|
265
266
|
- MIT
|
266
267
|
metadata:
|
267
268
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
268
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
269
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.306.0
|
269
270
|
post_install_message:
|
270
271
|
rdoc_options: []
|
271
272
|
require_paths:
|