dalli 5.0.6 → 5.0.7
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/CHANGELOG.md +11 -0
- data/lib/dalli/client.rb +21 -0
- data/lib/dalli/protocol/request_formatter.rb +22 -8
- data/lib/dalli/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 10a8b21fed4728b6f98929e983262dc1fc32c59711fce78a84b0df82b0348ca1
|
|
4
|
+
data.tar.gz: 0253d48293d3b482e8362830acd9323873302e202cb230bca3fc1b1e09b1b457
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bda1d416cf1f88514d0181ed2e7f7b53e1b935fb07901398cb91183f63e42d0ab7c9e54c177264cf77fc793ead7b8183d79cf3c335d8dee11aae5049a6f0fff9
|
|
7
|
+
data.tar.gz: d81bec09db2c23cdc95204b0bec5c491fbcb60e12823245b9d9d338502c98b2e6af5f6e58a12b11c081889ea5d63cb079b6e8ef2fb869db147f67219db86ac55
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ Dalli Changelog
|
|
|
4
4
|
Unreleased
|
|
5
5
|
==========
|
|
6
6
|
|
|
7
|
+
5.0.7
|
|
8
|
+
==========
|
|
9
|
+
|
|
10
|
+
Security:
|
|
11
|
+
|
|
12
|
+
- Fix memcached command injection through numeric arguments (GHSA-6wmv-xq9m-fmp7)
|
|
13
|
+
- The `default` argument of `incr`/`decr`, and `fetch_with_lock`'s `lock_ttl` and `recache_threshold`, were written into the meta protocol command without conversion, so a String containing CRLF injected additional memcached commands (e.g. `set`, `flush_all`) on the connection
|
|
14
|
+
- These arguments must now be Integers, or Strings of decimal digits; anything else raises `ArgumentError` before a request is sent
|
|
15
|
+
- As defense in depth, `RequestFormatter` now converts every numeric flag it writes (`D`, `J`, `N`, `R`, `T`) to an Integer
|
|
16
|
+
- Thanks to oss-security-shop for the report
|
|
17
|
+
|
|
7
18
|
5.0.6
|
|
8
19
|
==========
|
|
9
20
|
|
data/lib/dalli/client.rb
CHANGED
|
@@ -225,6 +225,8 @@ module Dalli
|
|
|
225
225
|
def fetch_with_lock(key, ttl: nil, lock_ttl: 30, recache_threshold: nil, req_options: nil, &block)
|
|
226
226
|
raise ArgumentError, 'Block is required for fetch_with_lock' unless block_given?
|
|
227
227
|
|
|
228
|
+
validate_integer!(:lock_ttl, lock_ttl)
|
|
229
|
+
validate_integer!(:recache_threshold, recache_threshold)
|
|
228
230
|
key = key.to_s
|
|
229
231
|
key = @key_manager.validate_key(key)
|
|
230
232
|
|
|
@@ -400,6 +402,8 @@ module Dalli
|
|
|
400
402
|
##
|
|
401
403
|
# Incr adds the given amount to the counter on the memcached server.
|
|
402
404
|
# Amt must be a positive integer value.
|
|
405
|
+
# Default, if given, must be an Integer (or a String of decimal digits);
|
|
406
|
+
# anything else raises ArgumentError.
|
|
403
407
|
#
|
|
404
408
|
# If default is nil, the counter must already exist or the operation
|
|
405
409
|
# will fail and will return nil. Otherwise this method will return
|
|
@@ -412,6 +416,7 @@ module Dalli
|
|
|
412
416
|
# If the value already exists, it must have been set with raw: true
|
|
413
417
|
def incr(key, amt = 1, ttl = nil, default = nil)
|
|
414
418
|
check_positive!(amt)
|
|
419
|
+
validate_integer!(:default, default)
|
|
415
420
|
|
|
416
421
|
perform(:incr, key, amt.to_i, ttl_or_default(ttl), default)
|
|
417
422
|
end
|
|
@@ -419,6 +424,8 @@ module Dalli
|
|
|
419
424
|
##
|
|
420
425
|
# Decr subtracts the given amount from the counter on the memcached server.
|
|
421
426
|
# Amt must be a positive integer value.
|
|
427
|
+
# Default, if given, must be an Integer (or a String of decimal digits);
|
|
428
|
+
# anything else raises ArgumentError.
|
|
422
429
|
#
|
|
423
430
|
# memcached counters are unsigned and cannot hold negative values. Calling
|
|
424
431
|
# decr on a counter which is 0 will just return 0.
|
|
@@ -434,6 +441,7 @@ module Dalli
|
|
|
434
441
|
# If the value already exists, it must have been set with raw: true
|
|
435
442
|
def decr(key, amt = 1, ttl = nil, default = nil)
|
|
436
443
|
check_positive!(amt)
|
|
444
|
+
validate_integer!(:default, default)
|
|
437
445
|
|
|
438
446
|
perform(:decr, key, amt.to_i, ttl_or_default(ttl), default)
|
|
439
447
|
end
|
|
@@ -617,6 +625,19 @@ module Dalli
|
|
|
617
625
|
raise ArgumentError, "Positive values only: #{amt}" if amt.negative?
|
|
618
626
|
end
|
|
619
627
|
|
|
628
|
+
# Numeric arguments that become meta protocol flags (GHSA-6wmv-xq9m-fmp7).
|
|
629
|
+
# RequestFormatter converts them to Integer as the wire-level backstop;
|
|
630
|
+
# checking here too raises a clean ArgumentError before the request
|
|
631
|
+
# starts, instead of Protocol::Base#request logging it as unexpected and
|
|
632
|
+
# closing the connection.
|
|
633
|
+
def validate_integer!(name, value)
|
|
634
|
+
return if value.nil?
|
|
635
|
+
|
|
636
|
+
value.is_a?(String) ? Integer(value, 10) : Integer(value)
|
|
637
|
+
rescue ArgumentError, TypeError, FloatDomainError
|
|
638
|
+
raise ArgumentError, "#{name} must be an Integer, got #{value.inspect}"
|
|
639
|
+
end
|
|
640
|
+
|
|
620
641
|
def cas_core(key, always_set, ttl = nil, req_options = nil)
|
|
621
642
|
(value, cas) = perform(:cas, key)
|
|
622
643
|
return if value.nil? && !always_set
|
|
@@ -43,10 +43,11 @@ module Dalli
|
|
|
43
43
|
# This saves 2 bytes per request and skips parsing on response.
|
|
44
44
|
cmd << (skip_flags ? ' v' : ' v f') if value
|
|
45
45
|
cmd << ' c' if return_cas
|
|
46
|
-
cmd << " T#{ttl}" if ttl
|
|
46
|
+
cmd << " T#{integer_flag(:ttl, ttl)}" if ttl
|
|
47
47
|
cmd << ' k q s' if quiet # Return the key in the response if quiet
|
|
48
|
-
cmd << " N#{vivify_ttl}" if vivify_ttl # Thundering herd: vivify on miss
|
|
49
|
-
|
|
48
|
+
cmd << " N#{integer_flag(:vivify_ttl, vivify_ttl)}" if vivify_ttl # Thundering herd: vivify on miss
|
|
49
|
+
# Thundering herd: win recache if TTL below threshold
|
|
50
|
+
cmd << " R#{integer_flag(:recache_ttl, recache_ttl)}" if recache_ttl
|
|
50
51
|
cmd << ' h' if return_hit_status # Return hit status (0 or 1)
|
|
51
52
|
cmd << ' l' if return_last_access # Return seconds since last access
|
|
52
53
|
cmd << ' u' if skip_lru_bump # Don't bump LRU or update access stats
|
|
@@ -74,13 +75,14 @@ module Dalli
|
|
|
74
75
|
cmd << ' b' if base64
|
|
75
76
|
cmd << " F#{bitflags}" if bitflags
|
|
76
77
|
cmd << cas_string(cas)
|
|
77
|
-
cmd << " T#{ttl}" if ttl
|
|
78
|
+
cmd << " T#{integer_flag(:ttl, ttl)}" if ttl
|
|
78
79
|
cmd << " M#{mode_to_token(mode)}"
|
|
79
80
|
cmd << ' q' if quiet
|
|
80
81
|
cmd << TERMINATOR
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
def multi_meta_set(entries, ttl: nil)
|
|
85
|
+
ttl = integer_flag(:ttl, ttl) if ttl
|
|
84
86
|
buffer = ''.b
|
|
85
87
|
entries.each do |key, pair|
|
|
86
88
|
value, bitflags = pair
|
|
@@ -104,7 +106,7 @@ module Dalli
|
|
|
104
106
|
def meta_delete(key:, cas: nil, ttl: nil, quiet: false, stale: false)
|
|
105
107
|
cmd = "md #{encoded_key(key)}"
|
|
106
108
|
cmd << cas_string(cas)
|
|
107
|
-
cmd << " T#{ttl}" if ttl
|
|
109
|
+
cmd << " T#{integer_flag(:ttl, ttl)}" if ttl
|
|
108
110
|
cmd << ' I' if stale # Mark stale instead of deleting
|
|
109
111
|
cmd << ' q' if quiet
|
|
110
112
|
cmd << TERMINATOR
|
|
@@ -120,10 +122,10 @@ module Dalli
|
|
|
120
122
|
|
|
121
123
|
def meta_arithmetic(key:, delta:, initial:, incr: true, cas: nil, ttl: nil, quiet: false)
|
|
122
124
|
cmd = "ma #{encoded_key(key)} v"
|
|
123
|
-
cmd << " D#{delta}" if delta
|
|
124
|
-
cmd << " J#{initial}" if initial
|
|
125
|
+
cmd << " D#{integer_flag(:delta, delta)}" if delta
|
|
126
|
+
cmd << " J#{integer_flag(:initial, initial)}" if initial
|
|
125
127
|
# Always set a TTL if an initial value is specified
|
|
126
|
-
cmd << " N#{ttl
|
|
128
|
+
cmd << " N#{ttl ? integer_flag(:ttl, ttl) : 0}" if ttl || initial
|
|
127
129
|
cmd << cas_string(cas)
|
|
128
130
|
cmd << ' q' if quiet
|
|
129
131
|
cmd << " M#{incr ? 'I' : 'D'}"
|
|
@@ -169,6 +171,18 @@ module Dalli
|
|
|
169
171
|
|
|
170
172
|
private
|
|
171
173
|
|
|
174
|
+
# Numeric flag values are written straight into the command line, so a
|
|
175
|
+
# value that isn't an integer -- e.g. a String carrying CRLF -- would let
|
|
176
|
+
# the caller inject further memcached commands (GHSA-6wmv-xq9m-fmp7).
|
|
177
|
+
# Converting to Integer means only digits ever reach the wire. Strings
|
|
178
|
+
# are parsed as base 10 so "010" means 10, as memcached would read it,
|
|
179
|
+
# rather than octal 8.
|
|
180
|
+
def integer_flag(name, value)
|
|
181
|
+
value.is_a?(String) ? Integer(value, 10) : Integer(value)
|
|
182
|
+
rescue ArgumentError, TypeError, FloatDomainError
|
|
183
|
+
raise ArgumentError, "#{name} must be an Integer, got #{value.inspect}"
|
|
184
|
+
end
|
|
185
|
+
|
|
172
186
|
def mode_to_token(mode)
|
|
173
187
|
case mode
|
|
174
188
|
when :add
|
data/lib/dalli/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dalli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.
|
|
4
|
+
version: 5.0.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter M. Goldstein
|
|
@@ -87,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
88
|
version: '0'
|
|
89
89
|
requirements: []
|
|
90
|
-
rubygems_version: 4.0.
|
|
90
|
+
rubygems_version: 4.0.21
|
|
91
91
|
specification_version: 4
|
|
92
92
|
summary: High performance memcached client for Ruby
|
|
93
93
|
test_files: []
|