dalli 4.3.3 → 4.3.4

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: 57b78e30ee409a2d742fc47ee57ccdd482fe9c75f369366ae315b7ef8b649934
4
- data.tar.gz: b8cad66f3cba53bbcb84b18f406eed60f22209c10dd4a312063a1e13189185ca
3
+ metadata.gz: d296cbb71a7d6d7f57969a942e7270bd3b7145160c2ba4c971426dfbdb352368
4
+ data.tar.gz: 14929b5d79fa9d709dd7ccf5b633a44a91fdb2cbc7139eecaa1687e0e63751db
5
5
  SHA512:
6
- metadata.gz: 18b26e3c4aa30e5f8b4195d95307afca6c9240dd1154a36966d40d52047e638758850ec06e2a40fd1bd8e69fe150dee7409b1f4a565f0a80e397aaf115315463
7
- data.tar.gz: b6db69ecbc1e6d34587d8ec29b861f6a71c863b36229d3c642e3d0e646ba6234e3bac04225d64ff174d12f35b728663e82e1d2b5f7e93dc9ac3e1ff33fd58db3
6
+ metadata.gz: 04d254a2ea7d37a29634aab3441634ec660fe430efcdf64e3ae547bd456f53a2d9a0c47eb17ae8b8a5407d0fdd6c811d8e4b681213f5089011b217465265388f
7
+ data.tar.gz: 55928535640b642c258efecfbede5a423638d0a45b1c814b7aaff03af67ae65feb5d344e9ab31fa963c25fcb318a25538c7e6e495dec1ae9e2b85c6bcfcc06aa
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
+ Unreleased
5
+ ==========
6
+
7
+ 4.3.4
8
+ ==========
9
+
10
+ Security:
11
+
12
+ - Fix memcached command injection through numeric arguments with the meta protocol (GHSA-6wmv-xq9m-fmp7)
13
+ - With `protocol: :meta`, the `default` argument of `incr`/`decr`, and `fetch_with_lock`'s `lock_ttl` and `recache_threshold`, were written into the command without conversion, so a String containing CRLF injected additional memcached commands (e.g. `set`, `flush_all`) on the connection. The default binary protocol is not affected
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, the meta `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
+
4
18
  4.3.3
5
19
  ==========
6
20
 
data/lib/dalli/client.rb CHANGED
@@ -237,6 +237,8 @@ module Dalli
237
237
  raise ArgumentError, 'Block is required for fetch_with_lock' unless block_given?
238
238
 
239
239
  raise_unless_meta_protocol!
240
+ validate_integer!(:lock_ttl, lock_ttl)
241
+ validate_integer!(:recache_threshold, recache_threshold)
240
242
 
241
243
  key = key.to_s
242
244
  key = @key_manager.validate_key(key)
@@ -402,6 +404,8 @@ module Dalli
402
404
  ##
403
405
  # Incr adds the given amount to the counter on the memcached server.
404
406
  # Amt must be a positive integer value.
407
+ # Default, if given, must be an Integer (or a String of decimal digits);
408
+ # anything else raises ArgumentError.
405
409
  #
406
410
  # If default is nil, the counter must already exist or the operation
407
411
  # will fail and will return nil. Otherwise this method will return
@@ -414,6 +418,7 @@ module Dalli
414
418
  # If the value already exists, it must have been set with raw: true
415
419
  def incr(key, amt = 1, ttl = nil, default = nil)
416
420
  check_positive!(amt)
421
+ validate_integer!(:default, default)
417
422
 
418
423
  perform(:incr, key, amt.to_i, ttl_or_default(ttl), default)
419
424
  end
@@ -421,6 +426,8 @@ module Dalli
421
426
  ##
422
427
  # Decr subtracts the given amount from the counter on the memcached server.
423
428
  # Amt must be a positive integer value.
429
+ # Default, if given, must be an Integer (or a String of decimal digits);
430
+ # anything else raises ArgumentError.
424
431
  #
425
432
  # memcached counters are unsigned and cannot hold negative values. Calling
426
433
  # decr on a counter which is 0 will just return 0.
@@ -436,6 +443,7 @@ module Dalli
436
443
  # If the value already exists, it must have been set with raw: true
437
444
  def decr(key, amt = 1, ttl = nil, default = nil)
438
445
  check_positive!(amt)
446
+ validate_integer!(:default, default)
439
447
 
440
448
  perform(:decr, key, amt.to_i, ttl_or_default(ttl), default)
441
449
  end
@@ -577,6 +585,14 @@ module Dalli
577
585
  raise ArgumentError, "Positive values only: #{amt}" if amt.negative?
578
586
  end
579
587
 
588
+ # Numeric arguments that become meta protocol flags (GHSA-6wmv-xq9m-fmp7).
589
+ # Checked before the request starts so a bad value raises a clean
590
+ # ArgumentError, instead of Protocol::Base#request logging it as
591
+ # unexpected and closing the connection.
592
+ def validate_integer!(name, value)
593
+ Dalli::Protocol::Meta::RequestFormatter.integer_flag(name, value) unless value.nil?
594
+ end
595
+
580
596
  def cas_core(key, always_set, ttl = nil, req_options = nil)
581
597
  (value, cas) = perform(:cas, key)
582
598
  return if value.nil? && !always_set
@@ -45,10 +45,11 @@ module Dalli
45
45
  cmd << (skip_flags ? ' v' : ' v f') if value
46
46
  cmd << ' c' if return_cas
47
47
  cmd << ' b' if base64
48
- cmd << " T#{ttl}" if ttl
48
+ cmd << " T#{integer_flag(:ttl, ttl)}" if ttl
49
49
  cmd << ' k q s' if quiet # Return the key in the response if quiet
50
- cmd << " N#{vivify_ttl}" if vivify_ttl # Thundering herd: vivify on miss
51
- cmd << " R#{recache_ttl}" if recache_ttl # Thundering herd: win recache if TTL below threshold
50
+ cmd << " N#{integer_flag(:vivify_ttl, vivify_ttl)}" if vivify_ttl # Thundering herd: vivify on miss
51
+ # Thundering herd: win recache if TTL below threshold
52
+ cmd << " R#{integer_flag(:recache_ttl, recache_ttl)}" if recache_ttl
52
53
  cmd << ' h' if return_hit_status # Return hit status (0 or 1)
53
54
  cmd << ' l' if return_last_access # Return seconds since last access
54
55
  cmd << ' u' if skip_lru_bump # Don't bump LRU or update access stats
@@ -61,7 +62,7 @@ module Dalli
61
62
  cmd << ' b' if base64
62
63
  cmd << " F#{bitflags}" if bitflags
63
64
  cmd << cas_string(cas)
64
- cmd << " T#{ttl}" if ttl
65
+ cmd << " T#{integer_flag(:ttl, ttl)}" if ttl
65
66
  cmd << " M#{mode_to_token(mode)}"
66
67
  cmd << ' q' if quiet
67
68
  cmd << TERMINATOR
@@ -74,7 +75,7 @@ module Dalli
74
75
  cmd = "md #{key}"
75
76
  cmd << ' b' if base64
76
77
  cmd << cas_string(cas)
77
- cmd << " T#{ttl}" if ttl
78
+ cmd << " T#{integer_flag(:ttl, ttl)}" if ttl
78
79
  cmd << ' I' if stale # Mark stale instead of deleting
79
80
  cmd << ' q' if quiet
80
81
  cmd + TERMINATOR
@@ -83,10 +84,10 @@ module Dalli
83
84
  def self.meta_arithmetic(key:, delta:, initial:, incr: true, cas: nil, ttl: nil, base64: false, quiet: false)
84
85
  cmd = "ma #{key} v"
85
86
  cmd << ' b' if base64
86
- cmd << " D#{delta}" if delta
87
- cmd << " J#{initial}" if initial
87
+ cmd << " D#{integer_flag(:delta, delta)}" if delta
88
+ cmd << " J#{integer_flag(:initial, initial)}" if initial
88
89
  # Always set a TTL if an initial value is specified
89
- cmd << " N#{ttl || 0}" if ttl || initial
90
+ cmd << " N#{ttl ? integer_flag(:ttl, ttl) : 0}" if ttl || initial
90
91
  cmd << cas_string(cas)
91
92
  cmd << ' q' if quiet
92
93
  cmd << " M#{incr ? 'I' : 'D'}"
@@ -136,6 +137,18 @@ module Dalli
136
137
  end
137
138
  end
138
139
 
140
+ # Numeric flag values are written straight into the command line, so a
141
+ # value that isn't an integer -- e.g. a String carrying CRLF -- would let
142
+ # the caller inject further memcached commands (GHSA-6wmv-xq9m-fmp7).
143
+ # Converting to Integer means only digits ever reach the wire. Strings
144
+ # are parsed as base 10 so "010" means 10, as memcached would read it,
145
+ # rather than octal 8.
146
+ def self.integer_flag(name, value)
147
+ value.is_a?(String) ? Integer(value, 10) : Integer(value)
148
+ rescue ArgumentError, TypeError, FloatDomainError
149
+ raise ArgumentError, "#{name} must be an Integer, got #{value.inspect}"
150
+ end
151
+
139
152
  def self.cas_string(cas)
140
153
  cas = parse_to_64_bit_int(cas, nil)
141
154
  cas.nil? || cas.zero? ? '' : " C#{cas}"
data/lib/dalli/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = '4.3.3'
4
+ VERSION = '4.3.4'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.4'
7
7
  end
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: 4.3.3
4
+ version: 4.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -93,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
95
  requirements: []
96
- rubygems_version: 4.0.6
96
+ rubygems_version: 4.0.21
97
97
  specification_version: 4
98
98
  summary: High performance memcached client for Ruby
99
99
  test_files: []