sinatra-my-params 0.0.9 → 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.
- checksums.yaml +4 -4
- data/lib/permit_params.rb +59 -8
- data/spec/permit_params_shape_spec.rb +50 -0
- data/spec/permit_params_spec.rb +84 -96
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b1f2c6fc4b59ce732264ff359866988b740c93b3dd585c43dcd57ee0834a8a1
|
|
4
|
+
data.tar.gz: b9f6692a739211d236c32b1658a66dd71b2f0e4e40ecb22010d63031bc7c03a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 =
|
|
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
|
|
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
|
|
|
@@ -66,35 +88,64 @@ module PermitParams
|
|
|
66
88
|
end
|
|
67
89
|
|
|
68
90
|
def coerce_array(param, options = {})
|
|
69
|
-
|
|
91
|
+
delimiter = valid_delimiter?(param, options[:delimiter])
|
|
92
|
+
return unless delimiter
|
|
93
|
+
|
|
94
|
+
Array(param.split(delimiter).map(&:strip))
|
|
70
95
|
end
|
|
71
96
|
|
|
72
97
|
def coerce_hash(param, options = {})
|
|
73
98
|
return param if param.is_a?(Hash)
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
delimiter = valid_delimiter?(param, options[:delimiter])
|
|
101
|
+
return unless delimiter
|
|
102
|
+
|
|
103
|
+
separator = valid_separator?(param, options[:separator])
|
|
104
|
+
return unless separator
|
|
105
|
+
|
|
106
|
+
key_value = param.split(delimiter).map(&:strip).map do |c|
|
|
107
|
+
c.split(separator).map(&:strip)
|
|
77
108
|
end
|
|
78
109
|
Hash[key_value]
|
|
79
110
|
end
|
|
80
111
|
|
|
81
112
|
def coerce_boolean(param)
|
|
82
|
-
|
|
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
|
|
83
117
|
false
|
|
84
118
|
else
|
|
85
|
-
|
|
119
|
+
/\A(true|t|yes|y|1)\z/i === param.to_s ? true : nil
|
|
86
120
|
end
|
|
87
121
|
raise ArgumentError if coerced.nil?
|
|
88
122
|
|
|
89
123
|
coerced
|
|
90
124
|
end
|
|
91
125
|
|
|
126
|
+
def valid_delimiter?(param, delimiter)
|
|
127
|
+
delimiter ||= ','
|
|
128
|
+
delimiter if delimiter && param.include?(delimiter)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def valid_separator?(param, separator)
|
|
132
|
+
separator ||= ':'
|
|
133
|
+
separator if separator && param.include?(separator)
|
|
134
|
+
end
|
|
135
|
+
|
|
92
136
|
def coerce_shape(param, options = {})
|
|
93
|
-
hash = coerce_hash(param)
|
|
137
|
+
hash = coerce_hash(param, options)
|
|
94
138
|
has_shape?(hash, options[:shape]) ? hash : nil
|
|
95
139
|
end
|
|
96
140
|
|
|
97
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
|
+
|
|
98
149
|
hash.all? do |k, v|
|
|
99
150
|
v.is_a?(Hash) ? has_shape?(v, shape[k]) : shape[k] === v
|
|
100
151
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
require 'permit_params'
|
|
5
|
+
require 'rspec'
|
|
6
|
+
require 'rack/test'
|
|
7
|
+
|
|
8
|
+
include PermitParams
|
|
9
|
+
|
|
10
|
+
describe 'shape behaviours' do
|
|
11
|
+
it 'should return a hash when a pemitted is of shape' do
|
|
12
|
+
input = { param: { a: 1, b: 2 } }
|
|
13
|
+
expect(input).to eq permitted_params(
|
|
14
|
+
input,
|
|
15
|
+
{ param: Shape },
|
|
16
|
+
false,
|
|
17
|
+
{ shape: { a: Integer, b: Integer } }
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should return a hash when a pemitted is of Integer shape(deep)' do
|
|
22
|
+
input = { param: { a: { b: 2 } } }
|
|
23
|
+
expect(input).to eq permitted_params(
|
|
24
|
+
input,
|
|
25
|
+
{ param: Shape },
|
|
26
|
+
false,
|
|
27
|
+
{ shape: { a: { b: Integer } } }
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should return a hash when a pemitted is of Array shape(deep)' do
|
|
32
|
+
input = { param: { a: { b: [1, 2] } } }
|
|
33
|
+
expect(input).to eq permitted_params(
|
|
34
|
+
input,
|
|
35
|
+
{ param: Shape },
|
|
36
|
+
false,
|
|
37
|
+
{ shape: { a: { b: Array } } }
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should return a empty object when a pemitted is shape not defined' do
|
|
42
|
+
input = { param: { a: 1, b: 2 } }
|
|
43
|
+
expect({}).to eq permitted_params(
|
|
44
|
+
input,
|
|
45
|
+
{ param: Shape },
|
|
46
|
+
false,
|
|
47
|
+
{ shape: { a: Integer } }
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
data/spec/permit_params_spec.rb
CHANGED
|
@@ -7,140 +7,128 @@ require 'rack/test'
|
|
|
7
7
|
|
|
8
8
|
include PermitParams
|
|
9
9
|
|
|
10
|
-
describe '
|
|
10
|
+
describe 'behaviours' do
|
|
11
11
|
it 'should raise error when at least one param is invalid' do
|
|
12
|
-
input = {
|
|
12
|
+
input = { param: 'a' }
|
|
13
13
|
expect do
|
|
14
|
-
permitted_params(input, {
|
|
14
|
+
permitted_params(input, { param: Integer }, true)
|
|
15
15
|
end.to raise_error(InvalidParameterError, "'a' is not a valid Integer")
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it 'should allow all params when no restriction is given' do
|
|
19
|
-
input = {
|
|
19
|
+
input = { param: 'a string' }
|
|
20
20
|
expect(input).to eq permitted_params(input)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
it 'should remove a string when
|
|
24
|
-
input = {
|
|
23
|
+
it 'should remove a string when permitted param is integer' do
|
|
24
|
+
input = { param: 'a string' }
|
|
25
25
|
output = {}
|
|
26
|
-
expect(output).to eq permitted_params(input, {
|
|
26
|
+
expect(output).to eq permitted_params(input, { param: Integer })
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
it 'should return an integer when
|
|
30
|
-
input = {
|
|
31
|
-
expect(input).to eq permitted_params(input, {
|
|
29
|
+
it 'should return an integer when permitted param is integer' do
|
|
30
|
+
input = { param: 1 }
|
|
31
|
+
expect(input).to eq permitted_params(input, { param: Integer })
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
it 'should return an integer when
|
|
35
|
-
input = {
|
|
36
|
-
output = {
|
|
37
|
-
expect(output).to eq permitted_params(input, {
|
|
34
|
+
it 'should return an integer when permitted param can be cast into integer' do
|
|
35
|
+
input = { param: '1' }
|
|
36
|
+
output = { param: 1 }
|
|
37
|
+
expect(output).to eq permitted_params(input, { param: Integer })
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
it 'should return a string when
|
|
41
|
-
input = {
|
|
42
|
-
expect(input).to eq permitted_params(input, {
|
|
40
|
+
it 'should return a string when permitted param is string' do
|
|
41
|
+
input = { param: 'a string' }
|
|
42
|
+
expect(input).to eq permitted_params(input, { param: String })
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
it 'should return a float when
|
|
46
|
-
input = {
|
|
47
|
-
expect(input).to eq permitted_params(input, {
|
|
45
|
+
it 'should return a float when permitted param is float' do
|
|
46
|
+
input = { param: 10.0 }
|
|
47
|
+
expect(input).to eq permitted_params(input, { param: Float })
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
it 'should return a date when
|
|
51
|
-
input = {
|
|
52
|
-
expect(input).to eq permitted_params(input, {
|
|
50
|
+
it 'should return a date when permitted param is date' do
|
|
51
|
+
input = { param: Date.new }
|
|
52
|
+
expect(input).to eq permitted_params(input, { param: Date })
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
it 'should return a time when
|
|
56
|
-
input = {
|
|
57
|
-
expect(input).to eq permitted_params(input, {
|
|
55
|
+
it 'should return a time when permitted param is time' do
|
|
56
|
+
input = { param: Time.new }
|
|
57
|
+
expect(input).to eq permitted_params(input, { param: Time })
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
it 'should return a false(boolean) when
|
|
61
|
-
input = {
|
|
62
|
-
output = {
|
|
63
|
-
expect(output).to eq permitted_params(input, {
|
|
60
|
+
it 'should return a false(boolean) when permitted param is boolean' do
|
|
61
|
+
input = { param: 'false' }
|
|
62
|
+
output = { param: false }
|
|
63
|
+
expect(output).to eq permitted_params(input, { param: Boolean })
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
it 'should return a true(boolean) when
|
|
67
|
-
input = {
|
|
68
|
-
output = {
|
|
69
|
-
expect(output).to eq permitted_params(input, {
|
|
66
|
+
it 'should return a true(boolean) when permitted param is boolean' do
|
|
67
|
+
input = { param: 'true' }
|
|
68
|
+
output = { param: true }
|
|
69
|
+
expect(output).to eq permitted_params(input, { param: Boolean })
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
it 'should return an array when
|
|
73
|
-
input = {
|
|
74
|
-
expect(input).to eq permitted_params(input, {
|
|
72
|
+
it 'should return an array when permitted param is array' do
|
|
73
|
+
input = { param: [1, 2] }
|
|
74
|
+
expect(input).to eq permitted_params(input, { param: Array })
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
it 'should return an array when
|
|
78
|
-
input = {
|
|
79
|
-
output = {
|
|
80
|
-
expect(output).to eq permitted_params(input, {
|
|
77
|
+
it 'should return an array when permitted param is array' do
|
|
78
|
+
input = { param: '1, 2' }
|
|
79
|
+
output = { param: %w[1 2] }
|
|
80
|
+
expect(output).to eq permitted_params(input, { param: Array })
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
it 'should return an array when
|
|
84
|
-
input = {
|
|
85
|
-
output = {
|
|
86
|
-
expect(output).to eq permitted_params(input, {
|
|
83
|
+
it 'should return an array when permitted param is array' do
|
|
84
|
+
input = { param: '1; 2' }
|
|
85
|
+
output = { param: %w[1 2] }
|
|
86
|
+
expect(output).to eq permitted_params(input, { param: Array }, false, { delimiter: ';' })
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
it 'should return
|
|
90
|
-
input = {
|
|
91
|
-
output = {
|
|
92
|
-
expect(output).to eq permitted_params(input, {
|
|
89
|
+
it 'should return an empty hash when permitted param is array with wrong delimiter' do
|
|
90
|
+
input = { param: '1; 2' }
|
|
91
|
+
output = {}
|
|
92
|
+
expect(output).to eq permitted_params(input, { param: Array }, false, { delimiter: ',' })
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
it 'should return a hash when
|
|
96
|
-
input = {
|
|
97
|
-
output = {
|
|
98
|
-
expect(output).to eq permitted_params(input, {
|
|
95
|
+
it 'should return a hash when permitted param is hash' do
|
|
96
|
+
input = { param: 'a: 1, b: 2' }
|
|
97
|
+
output = { param: { 'a' => '1', 'b' => '2' } }
|
|
98
|
+
expect(output).to eq permitted_params(input, { param: Hash })
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
it 'should return a hash when
|
|
102
|
-
input = {
|
|
103
|
-
|
|
101
|
+
it 'should return a hash when permitted param is hash' do
|
|
102
|
+
input = { param: 'a: 1; b: 2' }
|
|
103
|
+
output = { param: { 'a' => '1', 'b' => '2' } }
|
|
104
|
+
expect(output).to eq permitted_params(input, { param: Hash }, false, { delimiter: ';' })
|
|
104
105
|
end
|
|
105
106
|
|
|
106
|
-
it 'should return
|
|
107
|
-
input = {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
{ param_1: Shape },
|
|
111
|
-
false,
|
|
112
|
-
{ shape: { a: Integer, b: Integer } }
|
|
113
|
-
)
|
|
107
|
+
it 'should return an empty hash when permitted param is hash with wrong separator' do
|
|
108
|
+
input = { param: 'a: 1; b: 2' }
|
|
109
|
+
output = {}
|
|
110
|
+
expect(output).to eq permitted_params(input, { param: Hash }, false, { separator: '.' })
|
|
114
111
|
end
|
|
115
112
|
|
|
116
|
-
it 'should return
|
|
117
|
-
input = {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
{ param_1: Shape },
|
|
121
|
-
false,
|
|
122
|
-
{ shape: { a: { b: Integer } } }
|
|
123
|
-
)
|
|
113
|
+
it 'should return an empty hash when permitted param is hash with wrong delimiter' do
|
|
114
|
+
input = { param: 'a: 1; b: 2' }
|
|
115
|
+
output = {}
|
|
116
|
+
expect(output).to eq permitted_params(input, { param: Hash }, false, { delimiter: ',' })
|
|
124
117
|
end
|
|
125
118
|
|
|
126
|
-
it 'should return a
|
|
127
|
-
input = {
|
|
128
|
-
expect(
|
|
129
|
-
input,
|
|
130
|
-
{ param_1: Shape },
|
|
131
|
-
false,
|
|
132
|
-
{ shape: { a: Integer } }
|
|
133
|
-
)
|
|
119
|
+
it 'should return a hash when permitted param is hash' do
|
|
120
|
+
input = { param: { a: 1 } }
|
|
121
|
+
expect(input).to eq permitted_params(input, { param: Hash })
|
|
134
122
|
end
|
|
135
123
|
|
|
136
|
-
it 'should return a hash when
|
|
137
|
-
input = {
|
|
138
|
-
expect(input).to eq permitted_params(input, {
|
|
124
|
+
it 'should return a hash when permitted param is hash' do
|
|
125
|
+
input = { param: { a: { b: 1 } } }
|
|
126
|
+
expect(input).to eq permitted_params(input, { param: Hash })
|
|
139
127
|
end
|
|
140
128
|
|
|
141
|
-
it 'should return a hash when
|
|
142
|
-
input = {
|
|
143
|
-
expect(input).to eq permitted_params(input, {
|
|
129
|
+
it 'should return a hash when permitted param is hash' do
|
|
130
|
+
input = { param: { "a": 1 } }
|
|
131
|
+
expect(input).to eq permitted_params(input, { param: Hash })
|
|
144
132
|
end
|
|
145
133
|
|
|
146
134
|
it 'should return a several types for several inputs' do
|
|
@@ -167,21 +155,21 @@ describe 'exceptions' do
|
|
|
167
155
|
end
|
|
168
156
|
end
|
|
169
157
|
|
|
170
|
-
input = {
|
|
171
|
-
output = {
|
|
172
|
-
expect(output).to eq permitted_params(input, {
|
|
158
|
+
input = { param: '1' }
|
|
159
|
+
output = { param: '1' }
|
|
160
|
+
expect(output).to eq permitted_params(input, { param: Any })
|
|
173
161
|
|
|
174
162
|
input = {
|
|
175
|
-
|
|
163
|
+
param: TestClass.new(some_attribute: 'a string')
|
|
176
164
|
}
|
|
177
|
-
output = permitted_params(input, {
|
|
178
|
-
expect(input[:
|
|
165
|
+
output = permitted_params(input, { param: Any })
|
|
166
|
+
expect(input[:param].some_attribute).to eq output[:param].some_attribute
|
|
179
167
|
|
|
180
168
|
input = {
|
|
181
|
-
|
|
182
|
-
|
|
169
|
+
param: TestClass.new(some_attribute: 1),
|
|
170
|
+
antoher_param: 2
|
|
183
171
|
}
|
|
184
|
-
output = permitted_params(input, {
|
|
185
|
-
expect(input[:
|
|
172
|
+
output = permitted_params(input, { param: Any })
|
|
173
|
+
expect(input[:param].some_attribute).to eq output[:param].some_attribute
|
|
186
174
|
end
|
|
187
175
|
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.
|
|
4
|
+
version: 0.0.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Aviles
|
|
@@ -10,7 +10,7 @@ bindir: bin
|
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 2021-09-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description: A simple params sanitizer(originally created for sinatra)
|
|
13
|
+
description: A simple params sanitizer (originally created for sinatra)
|
|
14
14
|
email: gdmarav374@gmail.com
|
|
15
15
|
executables: []
|
|
16
16
|
extensions: []
|
|
@@ -19,6 +19,7 @@ files:
|
|
|
19
19
|
- Rakefile
|
|
20
20
|
- bin/permit_params
|
|
21
21
|
- lib/permit_params.rb
|
|
22
|
+
- spec/permit_params_shape_spec.rb
|
|
22
23
|
- spec/permit_params_spec.rb
|
|
23
24
|
homepage: https://github.com/mhero/sinatra-my-params
|
|
24
25
|
licenses: []
|
|
@@ -38,9 +39,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
38
39
|
- !ruby/object:Gem::Version
|
|
39
40
|
version: '0'
|
|
40
41
|
requirements: []
|
|
41
|
-
rubygems_version: 3.
|
|
42
|
+
rubygems_version: 3.4.19
|
|
42
43
|
signing_key:
|
|
43
44
|
specification_version: 3
|
|
44
45
|
summary: permit_params!
|
|
45
46
|
test_files:
|
|
46
47
|
- spec/permit_params_spec.rb
|
|
48
|
+
- spec/permit_params_shape_spec.rb
|