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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13ef83ab41f6ecd9046acb7a33190abc7648805878ffb1c5cf1e3741fa3e7ae5
4
- data.tar.gz: 5c8982aeb33abb1e1dec9b17a04693f6dfb7395ec29761680cdd3ca5c85df20e
3
+ metadata.gz: 9a8066da910f228939dadbc96afeadcf5122c99ed9a06a67b9858c4720d7e070
4
+ data.tar.gz: 59204f511b9adfb7863d97f754915fe96725093e6f0fa4dc7bfc3f2703e5e387
5
5
  SHA512:
6
- metadata.gz: c5375be69c79738814d42123b157392448603c12c4b5a7f54bde3107bbf12b9f885d08efbe44cddf1c5f2d78b48394eefa73cd720e77928259227a883db273aa
7
- data.tar.gz: a6aa16f032331174794e130096751ae08b37cf04ebae8da23ac07a022959ee493c766b98a873908e436d2d69d234623352857b4fd04dce4c71168c6ca4255317
6
+ metadata.gz: e4af64390c9efd167082d891eb192f051a04e231a889e751a646e7ce93d55d5736c5f98368ca1b83b6e4adaebc7f962ce4282155eabc245c522ed142a63dda27
7
+ data.tar.gz: 119edf96cc65e3fc88d2f89b543ecbae593c6afeb136db0144793c981999476925ae5c671508965676b4d31d2a2cf1d4858feaaab45f443110561a5b74c00b06
@@ -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
- constraint_strings = constraints_string.split(/[|,]/)
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.0"
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt