Skip to content

Commit bbc5b26

Browse files
committed
Bound SemVer input length
1 parent c1a9121 commit bbc5b26

3 files changed

Lines changed: 42 additions & 1 deletion

File tree

‎Library/Homebrew/test/vulns/semver_spec.rb‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
.to eq [nil, nil, nil, nil]
1111
end
1212

13+
it "rejects an oversized prerelease" do
14+
expect(described_class.release_version("1.0.0-#{"a" * 251}")).to be_nil
15+
end
16+
1317
it "normalises a prerelease with a prefix and build metadata" do
1418
expect(described_class.release_version("v2026.2.22-rc.1+build.2")).to eq "2026.2.22"
1519
end
@@ -29,6 +33,23 @@
2933
end).to all(be_nil)
3034
end
3135

36+
it "accepts versions at the input limit" do
37+
version = "1.0.0-#{"a" * 250}"
38+
expect(described_class.compare(version, "1.0.0")).to eq(-1)
39+
end
40+
41+
it "rejects versions exceeding the input limit on either side" do
42+
version = "1.0.0-#{"a" * 251}"
43+
expect([described_class.compare(version, "1.0.0"), described_class.compare("1.0.0", version)])
44+
.to all(be_nil)
45+
end
46+
47+
it "rejects oversized core, prerelease, build and whitespace inputs" do
48+
versions = ["#{"9" * 5000}.0.0", "1.0.0-#{"9" * 5000}", "1.0.0+#{"a." * 2500}a",
49+
"#{" " * 5000}1.0.0"]
50+
expect(versions.map { |version| described_class.compare(version, "1.0.0") }).to all(be_nil)
51+
end
52+
3253
# From vers gem: basic numeric ordering
3354
it "orders major versions numerically" do
3455
expect(described_class.compare("1.0.0", "2.0.0")).to eq(-1)

‎Library/Homebrew/test/vulns/vulnerability_spec.rb‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,14 @@ def range(type, *events)
405405
.to all(be_nil)
406406
end
407407

408+
it "leaves oversized SEMVER boundaries uncheckable" do
409+
v = vuln("id" => "TEST-1", "affected" => [
410+
affected("npm", "example",
411+
range("SEMVER", { "introduced" => "0" }, { "fixed" => "1.0.0-#{"a" * 5000}" })),
412+
])
413+
expect(v.range_status("npm", "example", "2.0.0")).to be_nil
414+
end
415+
408416
it "checks an explicit versions list when present" do
409417
v = vuln("id" => "CVE-1", "affected" => [
410418
affected("PyPI", "requests", versions: ["2.30.0", "2.31.0"]),
@@ -518,6 +526,12 @@ def range(type, *events)
518526
expect(v.affects_version?("1.0.0")).to be true
519527
end
520528

529+
it "keeps a match when the SEMVER fixed boundary exceeds the input limit" do
530+
v = vuln({ "id" => "TEST-1" }.merge(semver_range({ "introduced" => "0" },
531+
{ "fixed" => "1.0.0-#{"a" * 5000}" })))
532+
expect(v.affects_version?("2.0.0")).to be true
533+
end
534+
521535
it "matches an open-ended range introduced at v0" do
522536
v = vuln({ "id" => "CVE-2024-1234" }.merge(semver_range({ "introduced" => "v0" })))
523537
expect(v.affects_version?("1.2.3")).to be true

‎Library/Homebrew/vulns/semver.rb‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ module Homebrew
55
module Vulns
66
# SemVer 2.0 comparison for OSV `SEMVER` ranges (https://semver.org/#spec-item-11).
77
# Kept separate from `::Version`, whose ordering differs for prerelease and
8-
# build metadata. Minor/patch may be omitted; other spec violations return `nil`.
8+
# build metadata. Minor/patch may be omitted; other spec violations or
9+
# inputs over 256 bytes return `nil`.
910
module Semver
11+
MAX_LENGTH = 256
12+
private_constant :MAX_LENGTH
13+
1014
CORE_SEGMENT = "(0|[1-9]\\d*)"
1115
private_constant :CORE_SEGMENT
1216

@@ -52,6 +56,8 @@ def self.release_version(version)
5256

5357
sig { params(version: String).returns(T.nilable({ core: [Integer, Integer, Integer], prerelease: T::Array[String] })) }
5458
private_class_method def self.parse(version)
59+
return if version.bytesize > MAX_LENGTH
60+
5561
match = version.strip.sub(/\Av/i, "").match(SEMVER_REGEX)
5662
return if match.nil?
5763

0 commit comments

Comments
 (0)