vers 1.3.0 → 1.3.1
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/vers/constraint.rb +6 -0
- data/lib/vers/parser.rb +34 -3
- data/lib/vers/version.rb +14 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9a8066da910f228939dadbc96afeadcf5122c99ed9a06a67b9858c4720d7e070
|
|
4
|
+
data.tar.gz: 59204f511b9adfb7863d97f754915fe96725093e6f0fa4dc7bfc3f2703e5e387
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4af64390c9efd167082d891eb192f051a04e231a889e751a646e7ce93d55d5736c5f98368ca1b83b6e4adaebc7f962ce4282155eabc245c522ed142a63dda27
|
|
7
|
+
data.tar.gz: 119edf96cc65e3fc88d2f89b543ecbae593c6afeb136db0144793c981999476925ae5c671508965676b4d31d2a2cf1d4858feaaab45f443110561a5b74c00b06
|
data/lib/vers/constraint.rb
CHANGED
|
@@ -54,6 +54,12 @@ module Vers
|
|
|
54
54
|
# Vers::Constraint.parse("!=2.0.0") # => #<Vers::Constraint:0x... @operator="!=", @version="2.0.0">
|
|
55
55
|
#
|
|
56
56
|
def self.parse(constraint_string)
|
|
57
|
+
# Bound input length before cache lookup so oversized strings never
|
|
58
|
+
# become cache keys and never trigger eviction.
|
|
59
|
+
if constraint_string.length > Version::MAX_LENGTH
|
|
60
|
+
raise ArgumentError, "Constraint string too long (#{constraint_string.length} > #{Version::MAX_LENGTH})"
|
|
61
|
+
end
|
|
62
|
+
|
|
57
63
|
return @@constraint_cache[constraint_string] if @@constraint_cache.key?(constraint_string)
|
|
58
64
|
|
|
59
65
|
if @@constraint_cache.size >= @@cache_size_limit
|
data/lib/vers/parser.rb
CHANGED
|
@@ -32,6 +32,19 @@ module Vers
|
|
|
32
32
|
@@parser_cache = {}
|
|
33
33
|
@@cache_size_limit = 500
|
|
34
34
|
|
|
35
|
+
# Maximum accepted length for a range string at parse/parse_native
|
|
36
|
+
# entry points. Range strings concatenate multiple constraints so this
|
|
37
|
+
# is set higher than Version::MAX_LENGTH while still bounding
|
|
38
|
+
# split/regex work to a few KB.
|
|
39
|
+
MAX_INPUT_LENGTH = 2048
|
|
40
|
+
|
|
41
|
+
# Maximum number of |-separated or ||-separated constraints in a
|
|
42
|
+
# single range. The exclusion loop in parse_constraints does
|
|
43
|
+
# O(n^2 log n) work as each != splits an interval and reconstructs the
|
|
44
|
+
# range; capping n keeps the worst case under a few thousand interval
|
|
45
|
+
# operations.
|
|
46
|
+
MAX_CONSTRAINTS = 64
|
|
47
|
+
|
|
35
48
|
##
|
|
36
49
|
# Parses a vers URI string into a VersionRange
|
|
37
50
|
#
|
|
@@ -47,6 +60,8 @@ module Vers
|
|
|
47
60
|
# parser.parse("vers:pypi/==1.2.3")
|
|
48
61
|
#
|
|
49
62
|
def parse(vers_string)
|
|
63
|
+
validate_input_length!(vers_string)
|
|
64
|
+
|
|
50
65
|
if vers_string == "*"
|
|
51
66
|
return VersionRange.unbounded
|
|
52
67
|
end
|
|
@@ -75,6 +90,8 @@ module Vers
|
|
|
75
90
|
# parser.parse_native(">=1.0,<2.0", "pypi")
|
|
76
91
|
#
|
|
77
92
|
def parse_native(range_string, scheme)
|
|
93
|
+
validate_input_length!(range_string)
|
|
94
|
+
|
|
78
95
|
case scheme
|
|
79
96
|
when "npm"
|
|
80
97
|
parse_npm_range(range_string)
|
|
@@ -151,6 +168,12 @@ module Vers
|
|
|
151
168
|
|
|
152
169
|
private
|
|
153
170
|
|
|
171
|
+
def validate_input_length!(input)
|
|
172
|
+
return if input.nil?
|
|
173
|
+
return if input.length <= MAX_INPUT_LENGTH
|
|
174
|
+
raise ArgumentError, "Range string too long (#{input.length} > #{MAX_INPUT_LENGTH})"
|
|
175
|
+
end
|
|
176
|
+
|
|
154
177
|
def sort_key_for_constraint(constraint)
|
|
155
178
|
version = constraint.sub(OPERATOR_PREFIX_REGEX, '')
|
|
156
179
|
v = Version.cached_new(version)
|
|
@@ -158,7 +181,12 @@ module Vers
|
|
|
158
181
|
end
|
|
159
182
|
|
|
160
183
|
def parse_constraints(constraints_string, scheme)
|
|
161
|
-
|
|
184
|
+
# Limit constraint count to bound the O(n^2 log n) exclusion loop
|
|
185
|
+
# below: each != splits an interval and reconstructs the range.
|
|
186
|
+
constraint_strings = constraints_string.split(/[|,]/, MAX_CONSTRAINTS + 1)
|
|
187
|
+
if constraint_strings.length > MAX_CONSTRAINTS
|
|
188
|
+
raise ArgumentError, "Too many constraints (> #{MAX_CONSTRAINTS})"
|
|
189
|
+
end
|
|
162
190
|
intervals = []
|
|
163
191
|
exclusions = []
|
|
164
192
|
interval_scheme = %w[maven nuget].include?(scheme) ? scheme : nil
|
|
@@ -200,7 +228,10 @@ module Vers
|
|
|
200
228
|
|
|
201
229
|
# Handle || (OR) operator
|
|
202
230
|
if range_string.include?('||')
|
|
203
|
-
or_parts = range_string.split('||').map(&:strip)
|
|
231
|
+
or_parts = range_string.split('||', MAX_CONSTRAINTS + 1).map(&:strip)
|
|
232
|
+
if or_parts.length > MAX_CONSTRAINTS
|
|
233
|
+
raise ArgumentError, "Too many || clauses (> #{MAX_CONSTRAINTS})"
|
|
234
|
+
end
|
|
204
235
|
ranges = or_parts.map { |part| parse_npm_range(part) }
|
|
205
236
|
return ranges.reduce { |acc, range| acc.union(range) }
|
|
206
237
|
end
|
|
@@ -498,7 +529,7 @@ module Vers
|
|
|
498
529
|
begin
|
|
499
530
|
parsed_range = parse_maven_range(range_part)
|
|
500
531
|
ranges << parsed_range
|
|
501
|
-
rescue
|
|
532
|
+
rescue ArgumentError
|
|
502
533
|
# If parsing fails, skip this part
|
|
503
534
|
end
|
|
504
535
|
end
|
data/lib/vers/version.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Vers
|
|
4
|
-
VERSION = "1.3.
|
|
4
|
+
VERSION = "1.3.1"
|
|
5
5
|
|
|
6
6
|
##
|
|
7
7
|
# Handles version comparison and normalization across different package ecosystems.
|
|
@@ -22,15 +22,24 @@ module Vers
|
|
|
22
22
|
# Regex for parsing semantic version components including build metadata
|
|
23
23
|
SEMANTIC_VERSION_REGEX = /\A(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([^+]+))?(?:\+(.+))?\z/
|
|
24
24
|
|
|
25
|
+
# Maximum accepted length for a version string. Real-world version
|
|
26
|
+
# strings rarely exceed 100 characters; 256 leaves headroom for unusual
|
|
27
|
+
# prerelease tags while bounding regex/split work and cache key size.
|
|
28
|
+
MAX_LENGTH = 256
|
|
29
|
+
|
|
25
30
|
attr_reader :major, :minor, :patch, :prerelease, :build
|
|
26
31
|
|
|
27
32
|
##
|
|
28
33
|
# Creates a new Version object
|
|
29
34
|
#
|
|
30
35
|
# @param version_string [String] The version string to parse
|
|
36
|
+
# @raise [ArgumentError] if the version string exceeds MAX_LENGTH
|
|
31
37
|
#
|
|
32
38
|
def initialize(version_string)
|
|
33
39
|
@original = version_string.to_s
|
|
40
|
+
if @original.length > MAX_LENGTH
|
|
41
|
+
raise ArgumentError, "Version string too long (#{@original.length} > #{MAX_LENGTH})"
|
|
42
|
+
end
|
|
34
43
|
parse_version
|
|
35
44
|
end
|
|
36
45
|
|
|
@@ -41,6 +50,10 @@ module Vers
|
|
|
41
50
|
# @return [Version] Cached or new Version object
|
|
42
51
|
#
|
|
43
52
|
def self.cached_new(version_string)
|
|
53
|
+
# Skip caching for oversized keys to bound cache memory by entry
|
|
54
|
+
# count, not by attacker-controlled key length.
|
|
55
|
+
return new(version_string) if version_string.to_s.length > MAX_LENGTH
|
|
56
|
+
|
|
44
57
|
if @@version_cache.size >= @@cache_size_limit
|
|
45
58
|
# Keep the most recent half instead of clearing everything
|
|
46
59
|
keys = @@version_cache.keys
|