sinatra-my-params 0.0.10 → 0.0.11

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/permit_params.rb +37 -5
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8b53c81f8e6331c9f40d1b36cd45393132c6345478ae80132def61c44f5e6bd
4
- data.tar.gz: d049d0ec154d84196d13ac5db1ffe7d07ca8926e8c9a8e84f1ae0d7707b9a79c
3
+ metadata.gz: 6b1f2c6fc4b59ce732264ff359866988b740c93b3dd585c43dcd57ee0834a8a1
4
+ data.tar.gz: b9f6692a739211d236c32b1658a66dd71b2f0e4e40ecb22010d63031bc7c03a8
5
5
  SHA512:
6
- metadata.gz: c424f3ff45c18ada07d4e9377177793849c665ae69b6198ab1742c533b3c1dc647ff302b69e987e1d10caa87024787fa9197c940807e8c2367e54db9a31f1661
7
- data.tar.gz: 4ea6c14c51e42bc649ad6251503173a9765188f3281bd7c56e3cd7aa2bb8aa62dc2c0e24b2fb8560c0fe06ed6868ad60675b0ea34c3462bae4cb520823250309
6
+ metadata.gz: 906f99e74e6bede4cbf4ee24dbf281a08d1c4c0856ea18a2434e445c01f0f028aa5b165e8a3219716043b4be82141744e2a10894b5aada1f677c9e0960d504cc
7
+ data.tar.gz: a07cdd0764f4f0c39bf83c17aca176204d9469ebc2583bbba7804a12d55f3fc60e8f07896536df5a7fc4f8961082ed91d05e1da8e9a9a9a2cb05ecd8bcc0557c
data/lib/permit_params.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'date'
4
+ require 'time'
5
+
3
6
  module PermitParams
4
7
  class InvalidParameterError < StandardError
5
8
  attr_accessor :param, :options
@@ -8,7 +11,7 @@ module PermitParams
8
11
  def permitted_params(params, permitted = {}, strong_validation = false, options = {})
9
12
  return params if permitted.empty?
10
13
 
11
- coerced_params = Hash.new({})
14
+ coerced_params = {}
12
15
 
13
16
  params.each do |key, value|
14
17
  next unless permitted?(permitted: permitted, key: key, value: value)
@@ -30,6 +33,14 @@ module PermitParams
30
33
  Any = :any
31
34
  Shape = :shape
32
35
 
36
+ # Mirrors the fix Ruby itself shipped for CVE-2021-41817 (ReDoS in
37
+ # Date/Time/DateTime parsing methods): never hand an unbounded,
38
+ # attacker-controlled string to a parser that uses backtracking regexes.
39
+ PARSE_LENGTH_LIMIT = 128
40
+
41
+ DATE_TIME_TYPES = [Date, Time, DateTime].freeze
42
+ NUMERIC_PARSE_TYPES = [Integer, Float].freeze
43
+
33
44
  def permitted?(permitted:, key:, value:)
34
45
  permitted.keys.map(&:to_s).include?(key.to_s) && !value.nil?
35
46
  end
@@ -44,6 +55,11 @@ module PermitParams
44
55
  rescue StandardError
45
56
  false
46
57
  end
58
+
59
+ if param.is_a?(String) && (DATE_TIME_TYPES.include?(type) || NUMERIC_PARSE_TYPES.include?(type))
60
+ raise ArgumentError, "'#{type}' input exceeds #{PARSE_LENGTH_LIMIT} bytes" if param.bytesize > PARSE_LENGTH_LIMIT
61
+ end
62
+
47
63
  return coerce_integer(param, options) if type == Integer
48
64
  return Float(param) if type == Float
49
65
  return String(param) if type == String
@@ -56,8 +72,14 @@ module PermitParams
56
72
  return coerce_boolean(param) if [TrueClass, FalseClass, Boolean].include? type
57
73
 
58
74
  nil
59
- rescue ArgumentError
75
+ rescue StandardError
76
+ # Any failure while coercing untrusted input (malformed value,
77
+ # unexpected nested shape, missing stdlib constant, etc.) must never
78
+ # crash the caller's request handler - it's either rejected loudly
79
+ # (strong_validation) or dropped silently, same as an invalid value.
60
80
  raise InvalidParameterError, "'#{param}' is not a valid #{type}" if strong_validation
81
+
82
+ nil
61
83
  end
62
84
  end
63
85
 
@@ -88,10 +110,13 @@ module PermitParams
88
110
  end
89
111
 
90
112
  def coerce_boolean(param)
91
- coerced = if /^(false|f|no|n|0)$/i === param.to_s
113
+ # \A/\z (not ^/$) anchor to the start/end of the *whole string*. In Ruby
114
+ # ^ and $ only anchor to line boundaries, so e.g. "true\nDROP TABLE..."
115
+ # would incorrectly match as a clean boolean with ^/$.
116
+ coerced = if /\A(false|f|no|n|0)\z/i === param.to_s
92
117
  false
93
118
  else
94
- /^(true|t|yes|y|1)$/i === param.to_s ? true : nil
119
+ /\A(true|t|yes|y|1)\z/i === param.to_s ? true : nil
95
120
  end
96
121
  raise ArgumentError if coerced.nil?
97
122
 
@@ -109,11 +134,18 @@ module PermitParams
109
134
  end
110
135
 
111
136
  def coerce_shape(param, options = {})
112
- hash = coerce_hash(param)
137
+ hash = coerce_hash(param, options)
113
138
  has_shape?(hash, options[:shape]) ? hash : nil
114
139
  end
115
140
 
116
141
  def has_shape?(hash, shape)
142
+ # `hash` can be nil (coerce_hash gave up on a malformed string) and
143
+ # `shape` can be nil/non-Hash (caller forgot to pass shape:, or an
144
+ # attacker sent an unexpected nested hash the shape never described).
145
+ # Either used to raise NoMethodError deep inside a supposedly "safe"
146
+ # input filter, crashing the whole request. Reject instead of raising.
147
+ return false unless hash.is_a?(Hash) && shape.is_a?(Hash)
148
+
117
149
  hash.all? do |k, v|
118
150
  v.is_a?(Hash) ? has_shape?(v, shape[k]) : shape[k] === v
119
151
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra-my-params
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.10
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Aviles
@@ -39,7 +39,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  requirements: []
42
- rubygems_version: 3.2.3
42
+ rubygems_version: 3.4.19
43
43
  signing_key:
44
44
  specification_version: 3
45
45
  summary: permit_params!