durable_parameters 0.2.3
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +853 -0
- data/Rakefile +29 -0
- data/app/params/account_params.rb.example +38 -0
- data/app/params/application_params.rb +16 -0
- data/lib/durable_parameters/adapters/hanami.rb +138 -0
- data/lib/durable_parameters/adapters/rage.rb +124 -0
- data/lib/durable_parameters/adapters/rails.rb +280 -0
- data/lib/durable_parameters/adapters/sinatra.rb +91 -0
- data/lib/durable_parameters/core/application_params.rb +334 -0
- data/lib/durable_parameters/core/configuration.rb +83 -0
- data/lib/durable_parameters/core/forbidden_attributes_protection.rb +48 -0
- data/lib/durable_parameters/core/parameters.rb +643 -0
- data/lib/durable_parameters/core/params_registry.rb +110 -0
- data/lib/durable_parameters/core.rb +15 -0
- data/lib/durable_parameters/log_subscriber.rb +34 -0
- data/lib/durable_parameters/railtie.rb +65 -0
- data/lib/durable_parameters/version.rb +7 -0
- data/lib/durable_parameters.rb +41 -0
- data/lib/generators/rails/USAGE +12 -0
- data/lib/generators/rails/durable_parameters_controller_generator.rb +17 -0
- data/lib/generators/rails/templates/controller.rb +94 -0
- data/lib/legacy/action_controller/application_params.rb +235 -0
- data/lib/legacy/action_controller/parameters.rb +524 -0
- data/lib/legacy/action_controller/params_registry.rb +108 -0
- data/lib/legacy/active_model/forbidden_attributes_protection.rb +40 -0
- data/test/action_controller_required_params_test.rb +36 -0
- data/test/action_controller_tainted_params_test.rb +29 -0
- data/test/active_model_mass_assignment_taint_protection_test.rb +25 -0
- data/test/application_params_array_test.rb +245 -0
- data/test/application_params_edge_cases_test.rb +361 -0
- data/test/application_params_test.rb +893 -0
- data/test/controller_generator_test.rb +31 -0
- data/test/core_parameters_test.rb +2376 -0
- data/test/durable_parameters_test.rb +115 -0
- data/test/enhanced_error_messages_test.rb +120 -0
- data/test/gemfiles/Gemfile.rails-3.0.x +14 -0
- data/test/gemfiles/Gemfile.rails-3.1.x +14 -0
- data/test/gemfiles/Gemfile.rails-3.2.x +14 -0
- data/test/log_on_unpermitted_params_test.rb +49 -0
- data/test/metadata_validation_test.rb +294 -0
- data/test/multi_parameter_attributes_test.rb +38 -0
- data/test/parameters_core_methods_test.rb +503 -0
- data/test/parameters_integration_test.rb +553 -0
- data/test/parameters_permit_test.rb +491 -0
- data/test/parameters_require_test.rb +9 -0
- data/test/parameters_taint_test.rb +98 -0
- data/test/params_registry_concurrency_test.rb +422 -0
- data/test/params_registry_test.rb +112 -0
- data/test/permit_by_model_test.rb +227 -0
- data/test/raise_on_unpermitted_params_test.rb +32 -0
- data/test/test_helper.rb +38 -0
- data/test/transform_params_edge_cases_test.rb +526 -0
- data/test/transformation_test.rb +360 -0
- metadata +223 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'action_dispatch/http/upload'
|
|
3
|
+
|
|
4
|
+
class NestedParametersTest < Minitest::Test
|
|
5
|
+
def assert_filtered_out(params, key)
|
|
6
|
+
assert !params.has_key?(key), "key #{key.inspect} has not been filtered out"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# --- Basic interface --------------------------------------------------------
|
|
11
|
+
#
|
|
12
|
+
|
|
13
|
+
# --- nothing ----------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
def test_if_nothing_is_permitted_the_hash_becomes_empty
|
|
16
|
+
params = ActionController::Parameters.new(:id => '1234')
|
|
17
|
+
permitted = params.permit
|
|
18
|
+
assert permitted.permitted?
|
|
19
|
+
assert permitted.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# --- key --------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
def test_key_permitted_scalar_values
|
|
25
|
+
values = ['a', :a, nil]
|
|
26
|
+
values += [0, 1.0, 2**128, BigDecimal('1')]
|
|
27
|
+
values += [true, false]
|
|
28
|
+
values += [Date.today, Time.now, DateTime.now]
|
|
29
|
+
values += [StringIO.new, STDOUT, ActionDispatch::Http::UploadedFile.new(:tempfile => __FILE__), Rack::Test::UploadedFile.new(__FILE__)]
|
|
30
|
+
|
|
31
|
+
values.each do |value|
|
|
32
|
+
params = ActionController::Parameters.new(:id => value)
|
|
33
|
+
permitted = params.permit(:id)
|
|
34
|
+
if value.nil?
|
|
35
|
+
assert_nil permitted[:id]
|
|
36
|
+
else
|
|
37
|
+
assert_equal value, permitted[:id]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
%w(i f).each do |suffix|
|
|
41
|
+
params = ActionController::Parameters.new("foo(000#{suffix})" => value)
|
|
42
|
+
permitted = params.permit(:foo)
|
|
43
|
+
if value.nil?
|
|
44
|
+
assert_nil permitted["foo(000#{suffix})"]
|
|
45
|
+
else
|
|
46
|
+
assert_equal value, permitted["foo(000#{suffix})"]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_key_unknown_keys_are_filtered_out
|
|
53
|
+
params = ActionController::Parameters.new(:id => '1234', :injected => 'injected')
|
|
54
|
+
permitted = params.permit(:id)
|
|
55
|
+
assert_equal '1234', permitted[:id]
|
|
56
|
+
assert_filtered_out permitted, :injected
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_key_arrays_are_filtered_out
|
|
60
|
+
[[], [1], ['1']].each do |array|
|
|
61
|
+
params = ActionController::Parameters.new(:id => array)
|
|
62
|
+
permitted = params.permit(:id)
|
|
63
|
+
assert_filtered_out permitted, :id
|
|
64
|
+
|
|
65
|
+
%w(i f).each do |suffix|
|
|
66
|
+
params = ActionController::Parameters.new("foo(000#{suffix})" => array)
|
|
67
|
+
permitted = params.permit(:foo)
|
|
68
|
+
assert_filtered_out permitted, "foo(000#{suffix})"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_key_hashes_are_filtered_out
|
|
74
|
+
[{}, {:foo => 1}, {:foo => 'bar'}].each do |hash|
|
|
75
|
+
params = ActionController::Parameters.new(:id => hash)
|
|
76
|
+
permitted = params.permit(:id)
|
|
77
|
+
assert_filtered_out permitted, :id
|
|
78
|
+
|
|
79
|
+
%w(i f).each do |suffix|
|
|
80
|
+
params = ActionController::Parameters.new("foo(000#{suffix})" => hash)
|
|
81
|
+
permitted = params.permit(:foo)
|
|
82
|
+
assert_filtered_out permitted, "foo(000#{suffix})"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_key_non_permitted_scalar_values_are_filtered_out
|
|
88
|
+
params = ActionController::Parameters.new(:id => Object.new)
|
|
89
|
+
permitted = params.permit(:id)
|
|
90
|
+
assert_filtered_out permitted, :id
|
|
91
|
+
|
|
92
|
+
%w(i f).each do |suffix|
|
|
93
|
+
params = ActionController::Parameters.new("foo(000#{suffix})" => Object.new)
|
|
94
|
+
permitted = params.permit(:foo)
|
|
95
|
+
assert_filtered_out permitted, "foo(000#{suffix})"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_key_it_is_not_assigned_if_not_present_in_params
|
|
100
|
+
params = ActionController::Parameters.new(:name => 'Joe')
|
|
101
|
+
permitted = params.permit(:id)
|
|
102
|
+
assert !permitted.has_key?(:id)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_do_not_break_params_filtering_on_nil_values
|
|
106
|
+
params = ActionController::Parameters.new(:a => 1, :b => [1, 2, 3], :c => nil)
|
|
107
|
+
|
|
108
|
+
permitted = params.permit(:a, :c => [], :b => [])
|
|
109
|
+
assert_equal 1, permitted[:a]
|
|
110
|
+
assert_equal [1, 2, 3], permitted[:b]
|
|
111
|
+
assert_nil permitted[:c]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_permit_parameters_as_an_array
|
|
115
|
+
params = ActionController::Parameters.new(:foo => 'bar')
|
|
116
|
+
|
|
117
|
+
assert_equal 'bar', params.permit([:foo])[:foo]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# --- key to empty array -----------------------------------------------------
|
|
121
|
+
|
|
122
|
+
def test_key_to_empty_array_empty_arrays_pass
|
|
123
|
+
params = ActionController::Parameters.new(:id => [])
|
|
124
|
+
permitted = params.permit(:id => [])
|
|
125
|
+
assert_equal [], permitted[:id]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_key_to_empty_array_arrays_of_permitted_scalars_pass
|
|
129
|
+
[['foo'], [1], ['foo', 'bar'], [1, 2, 3]].each do |array|
|
|
130
|
+
params = ActionController::Parameters.new(:id => array)
|
|
131
|
+
permitted = params.permit(:id => [])
|
|
132
|
+
assert_equal array, permitted[:id]
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def test_key_to_empty_array_permitted_scalar_values_do_not_pass
|
|
137
|
+
['foo', 1].each do |permitted_scalar|
|
|
138
|
+
params = ActionController::Parameters.new(:id => permitted_scalar)
|
|
139
|
+
permitted = params.permit(:id => [])
|
|
140
|
+
assert_filtered_out permitted, :id
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_key_to_empty_array_arrays_of_non_permitted_scalar_do_not_pass
|
|
145
|
+
[[Object.new], [[]], [[1]], [{}], [{:id => '1'}]].each do |non_permitted_scalar|
|
|
146
|
+
params = ActionController::Parameters.new(:id => non_permitted_scalar)
|
|
147
|
+
permitted = params.permit(:id => [])
|
|
148
|
+
assert_filtered_out permitted, :id
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# --- Nesting ----------------------------------------------------------------
|
|
154
|
+
#
|
|
155
|
+
|
|
156
|
+
def test_permitted_nested_parameters
|
|
157
|
+
params = ActionController::Parameters.new({
|
|
158
|
+
:book => {
|
|
159
|
+
:title => "Romeo and Juliet",
|
|
160
|
+
:authors => [{
|
|
161
|
+
:name => "William Shakespeare",
|
|
162
|
+
:born => "1564-04-26"
|
|
163
|
+
}, {
|
|
164
|
+
:name => "Christopher Marlowe"
|
|
165
|
+
}, {
|
|
166
|
+
:name => %w(malicious injected names)
|
|
167
|
+
}],
|
|
168
|
+
:details => {
|
|
169
|
+
:pages => 200,
|
|
170
|
+
:genre => "Tragedy"
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
:magazine => "Mjallo!"
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
permitted = params.permit :book => [ :title, { :authors => [ :name ] }, { :details => :pages } ]
|
|
177
|
+
|
|
178
|
+
assert permitted.permitted?
|
|
179
|
+
assert_equal "Romeo and Juliet", permitted[:book][:title]
|
|
180
|
+
assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
|
|
181
|
+
assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
|
|
182
|
+
assert_equal 200, permitted[:book][:details][:pages]
|
|
183
|
+
|
|
184
|
+
assert_filtered_out permitted[:book][:authors][2], :name
|
|
185
|
+
|
|
186
|
+
assert_filtered_out permitted, :magazine
|
|
187
|
+
assert_filtered_out permitted[:book][:details], :genre
|
|
188
|
+
assert_filtered_out permitted[:book][:authors][0], :born
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def test_permitted_nested_parameters_with_a_string_or_a_symbol_as_a_key
|
|
192
|
+
params = ActionController::Parameters.new({
|
|
193
|
+
:book => {
|
|
194
|
+
'authors' => [
|
|
195
|
+
{ :name => "William Shakespeare", :born => "1564-04-26" },
|
|
196
|
+
{ :name => "Christopher Marlowe" }
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
permitted = params.permit :book => [ { 'authors' => [ :name ] } ]
|
|
202
|
+
|
|
203
|
+
assert_equal "William Shakespeare", permitted[:book]['authors'][0][:name]
|
|
204
|
+
assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
|
|
205
|
+
assert_equal "Christopher Marlowe", permitted[:book]['authors'][1][:name]
|
|
206
|
+
assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
|
|
207
|
+
|
|
208
|
+
permitted = params.permit :book => [ { :authors => [ :name ] } ]
|
|
209
|
+
|
|
210
|
+
assert_equal "William Shakespeare", permitted[:book]['authors'][0][:name]
|
|
211
|
+
assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
|
|
212
|
+
assert_equal "Christopher Marlowe", permitted[:book]['authors'][1][:name]
|
|
213
|
+
assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def test_nested_arrays_with_strings
|
|
217
|
+
params = ActionController::Parameters.new({
|
|
218
|
+
:book => {
|
|
219
|
+
:genres => ["Tragedy"]
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
permitted = params.permit :book => {:genres => []}
|
|
224
|
+
assert_equal ["Tragedy"], permitted[:book][:genres]
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def test_permit_may_specify_symbols_or_strings
|
|
228
|
+
params = ActionController::Parameters.new({
|
|
229
|
+
:book => {
|
|
230
|
+
:title => "Romeo and Juliet",
|
|
231
|
+
:author => "William Shakespeare"
|
|
232
|
+
},
|
|
233
|
+
:magazine => "Shakespeare Today"
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
permitted = params.permit({ :book => ["title", :author] }, "magazine")
|
|
237
|
+
assert_equal "Romeo and Juliet", permitted[:book][:title]
|
|
238
|
+
assert_equal "William Shakespeare", permitted[:book][:author]
|
|
239
|
+
assert_equal "Shakespeare Today", permitted[:magazine]
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def test_nested_array_with_strings_that_should_be_hashes
|
|
243
|
+
params = ActionController::Parameters.new({
|
|
244
|
+
:book => {
|
|
245
|
+
:genres => ["Tragedy"]
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
permitted = params.permit :book => { :genres => :type }
|
|
250
|
+
assert permitted[:book][:genres].empty?
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def test_nested_array_with_strings_that_should_be_hashes_and_additional_values
|
|
254
|
+
params = ActionController::Parameters.new({
|
|
255
|
+
:book => {
|
|
256
|
+
:title => "Romeo and Juliet",
|
|
257
|
+
:genres => ["Tragedy"]
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
permitted = params.permit :book => [ :title, { :genres => :type } ]
|
|
262
|
+
assert_equal "Romeo and Juliet", permitted[:book][:title]
|
|
263
|
+
assert permitted[:book][:genres].empty?
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def test_nested_string_that_should_be_a_hash
|
|
267
|
+
params = ActionController::Parameters.new({
|
|
268
|
+
:book => {
|
|
269
|
+
:genre => "Tragedy"
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
permitted = params.permit :book => { :genre => :type }
|
|
274
|
+
assert_nil permitted[:book][:genre]
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def test_fields_for_style_nested_params
|
|
278
|
+
params = ActionController::Parameters.new({
|
|
279
|
+
:book => {
|
|
280
|
+
:authors_attributes => {
|
|
281
|
+
:'0' => { :name => 'William Shakespeare', :age_of_death => '52' },
|
|
282
|
+
:'1' => { :name => 'Unattributed Assistant' },
|
|
283
|
+
:'2' => { :name => %w(injected names)}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
permitted = params.permit :book => { :authors_attributes => [ :name ] }
|
|
288
|
+
|
|
289
|
+
refute_nil permitted[:book][:authors_attributes]['0']
|
|
290
|
+
refute_nil permitted[:book][:authors_attributes]['1']
|
|
291
|
+
assert permitted[:book][:authors_attributes]['2'].empty?
|
|
292
|
+
assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][:name]
|
|
293
|
+
assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][:name]
|
|
294
|
+
|
|
295
|
+
assert_filtered_out permitted[:book][:authors_attributes]['0'], :age_of_death
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def test_fields_for_style_nested_params_with_negative_numbers
|
|
299
|
+
params = ActionController::Parameters.new({
|
|
300
|
+
:book => {
|
|
301
|
+
:authors_attributes => {
|
|
302
|
+
:'-1' => { :name => 'William Shakespeare', :age_of_death => '52' },
|
|
303
|
+
:'-2' => { :name => 'Unattributed Assistant' }
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
permitted = params.permit :book => { :authors_attributes => [:name] }
|
|
308
|
+
|
|
309
|
+
refute_nil permitted[:book][:authors_attributes]['-1']
|
|
310
|
+
refute_nil permitted[:book][:authors_attributes]['-2']
|
|
311
|
+
assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['-1'][:name]
|
|
312
|
+
assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-2'][:name]
|
|
313
|
+
|
|
314
|
+
assert_filtered_out permitted[:book][:authors_attributes]['-1'], :age_of_death
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def test_fields_for_style_nested_params_with_nested_arrays
|
|
318
|
+
params = ActionController::Parameters.new({
|
|
319
|
+
:book => {
|
|
320
|
+
:authors_attributes => {
|
|
321
|
+
:'0' => ['William Shakespeare', '52'],
|
|
322
|
+
:'1' => ['Unattributed Assistant']
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
permitted = params.permit :book => { :authors_attributes => { :'0' => [], :'1' => [] } }
|
|
327
|
+
|
|
328
|
+
refute_nil permitted[:book][:authors_attributes]['0']
|
|
329
|
+
refute_nil permitted[:book][:authors_attributes]['1']
|
|
330
|
+
assert_nil permitted[:book][:authors_attributes]['2']
|
|
331
|
+
assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][0]
|
|
332
|
+
assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][0]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def test_nested_number_as_key
|
|
336
|
+
params = ActionController::Parameters.new({
|
|
337
|
+
:product => {
|
|
338
|
+
:properties => {
|
|
339
|
+
'0' => "prop0",
|
|
340
|
+
'1' => "prop1"
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
params = params.require(:product).permit(:properties => ["0"])
|
|
345
|
+
refute_nil params[:properties]["0"]
|
|
346
|
+
assert_nil params[:properties]["1"]
|
|
347
|
+
assert_equal "prop0", params[:properties]["0"]
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def test_fetch_with_a_default_value_of_a_hash_does_not_mutate_the_object
|
|
351
|
+
params = ActionController::Parameters.new({})
|
|
352
|
+
params.fetch :foo, {}
|
|
353
|
+
assert_nil params[:foo]
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def test_hashes_in_array_values_get_wrapped
|
|
357
|
+
params = ActionController::Parameters.new(:foo => [{}, {}])
|
|
358
|
+
params[:foo].each do |hash|
|
|
359
|
+
assert !hash.permitted?
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def test_permit_with_deeply_nested_structures
|
|
364
|
+
params = ActionController::Parameters.new({
|
|
365
|
+
:user => {
|
|
366
|
+
:profile => {
|
|
367
|
+
:settings => {
|
|
368
|
+
:notifications => {
|
|
369
|
+
:email => true,
|
|
370
|
+
:sms => false
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
permitted = params.permit(:user => { :profile => { :settings => { :notifications => [:email, :sms] } } })
|
|
378
|
+
|
|
379
|
+
assert permitted.permitted?
|
|
380
|
+
assert_equal true, permitted[:user][:profile][:settings][:notifications][:email]
|
|
381
|
+
assert_equal false, permitted[:user][:profile][:settings][:notifications][:sms]
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def test_permit_with_mixed_array_and_hash
|
|
385
|
+
params = ActionController::Parameters.new({
|
|
386
|
+
:posts => [
|
|
387
|
+
{ :title => 'Post 1', :tags => ['ruby', 'rails'] },
|
|
388
|
+
{ :title => 'Post 2', :tags => ['js', 'react'] }
|
|
389
|
+
]
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
permitted = params.permit(:posts => [:title, { :tags => [] }])
|
|
393
|
+
|
|
394
|
+
assert permitted.permitted?
|
|
395
|
+
assert_equal 'Post 1', permitted[:posts][0][:title]
|
|
396
|
+
assert_equal ['ruby', 'rails'], permitted[:posts][0][:tags]
|
|
397
|
+
assert_equal 'Post 2', permitted[:posts][1][:title]
|
|
398
|
+
assert_equal ['js', 'react'], permitted[:posts][1][:tags]
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def test_permit_with_empty_nested_hash
|
|
402
|
+
params = ActionController::Parameters.new({
|
|
403
|
+
:user => {
|
|
404
|
+
:profile => {}
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
permitted = params.permit(:user => { :profile => {} })
|
|
409
|
+
|
|
410
|
+
assert permitted.permitted?
|
|
411
|
+
assert_instance_of ActionController::Parameters, permitted[:user][:profile]
|
|
412
|
+
assert permitted[:user][:profile].permitted?
|
|
413
|
+
assert_empty permitted[:user][:profile]
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def test_permit_with_symbol_keys_in_nested_structure
|
|
417
|
+
params = ActionController::Parameters.new({
|
|
418
|
+
user: {
|
|
419
|
+
profile: {
|
|
420
|
+
name: 'John',
|
|
421
|
+
'age' => 30
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
permitted = params.permit(user: { profile: [:name, :age] })
|
|
427
|
+
|
|
428
|
+
assert permitted.permitted?
|
|
429
|
+
assert_equal 'John', permitted[:user][:profile][:name]
|
|
430
|
+
assert_equal 30, permitted[:user][:profile][:age]
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def test_permit_filters_unknown_nested_keys
|
|
434
|
+
params = ActionController::Parameters.new({
|
|
435
|
+
:user => {
|
|
436
|
+
:name => 'John',
|
|
437
|
+
:secret => 'hidden'
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
permitted = params.permit(:user => [:name])
|
|
442
|
+
|
|
443
|
+
assert permitted.permitted?
|
|
444
|
+
assert_equal 'John', permitted[:user][:name]
|
|
445
|
+
assert_filtered_out permitted[:user], :secret
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def test_permit_with_multiple_levels_of_arrays
|
|
449
|
+
params = ActionController::Parameters.new({
|
|
450
|
+
:matrix => [
|
|
451
|
+
[1, 2, 3],
|
|
452
|
+
[4, 5, 6]
|
|
453
|
+
]
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
permitted = params.permit(:matrix => [])
|
|
457
|
+
|
|
458
|
+
assert permitted.permitted?
|
|
459
|
+
# Nested arrays are not permitted by default, so matrix should be filtered out
|
|
460
|
+
assert_filtered_out permitted, :matrix
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def test_permit_with_complex_mixed_structure
|
|
464
|
+
params = ActionController::Parameters.new({
|
|
465
|
+
:company => {
|
|
466
|
+
:name => 'ACME',
|
|
467
|
+
:employees => [
|
|
468
|
+
{ :name => 'Alice', :skills => ['Ruby', 'Rails'] },
|
|
469
|
+
{ :name => 'Bob', :skills => ['JS', 'React'] }
|
|
470
|
+
],
|
|
471
|
+
:offices => {
|
|
472
|
+
:headquarters => { :city => 'NYC', :country => 'USA' },
|
|
473
|
+
:branch => { :city => 'LA', :country => 'USA' }
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
permitted = params.permit(:company => [
|
|
479
|
+
:name,
|
|
480
|
+
{ :employees => [:name, { :skills => [] }] },
|
|
481
|
+
{ :offices => { :headquarters => [:city, :country], :branch => [:city, :country] } }
|
|
482
|
+
])
|
|
483
|
+
|
|
484
|
+
assert permitted.permitted?
|
|
485
|
+
assert_equal 'ACME', permitted[:company][:name]
|
|
486
|
+
assert_equal 'Alice', permitted[:company][:employees][0][:name]
|
|
487
|
+
assert_equal ['Ruby', 'Rails'], permitted[:company][:employees][0][:skills]
|
|
488
|
+
assert_equal 'NYC', permitted[:company][:offices][:headquarters][:city]
|
|
489
|
+
assert_equal 'USA', permitted[:company][:offices][:headquarters][:country]
|
|
490
|
+
end
|
|
491
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ParametersRequireTest < Minitest::Test
|
|
4
|
+
def test_required_parameters_must_be_present_not_merely_not_nil
|
|
5
|
+
assert_raises(ActionController::ParameterMissing) do
|
|
6
|
+
ActionController::Parameters.new(:person => {}).require(:person)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ParametersTaintTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@params = ActionController::Parameters.new(
|
|
6
|
+
:person => {
|
|
7
|
+
:age => '32',
|
|
8
|
+
:name => {
|
|
9
|
+
:first => 'David',
|
|
10
|
+
:last => 'Heinemeier Hansson'
|
|
11
|
+
},
|
|
12
|
+
:addresses => [{:city => 'Chicago', :state => 'Illinois'}]
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_fetch_raises_parameter_missing_exception
|
|
18
|
+
e = assert_raises(ActionController::ParameterMissing) do
|
|
19
|
+
@params.fetch :foo
|
|
20
|
+
end
|
|
21
|
+
assert_equal :foo, e.param
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_fetch_doesnt_raise_parameter_missing_exception_if_there_is_a_default
|
|
25
|
+
assert_equal "monkey", @params.fetch(:foo, "monkey")
|
|
26
|
+
assert_equal "monkey", @params.fetch(:foo) { "monkey" }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_not_permitted_is_sticky_on_accessors
|
|
30
|
+
assert !@params.slice(:person).permitted?
|
|
31
|
+
assert !@params[:person][:name].permitted?
|
|
32
|
+
assert !@params[:person].except(:name).permitted?
|
|
33
|
+
|
|
34
|
+
@params.each { |key, value| assert(!value.permitted?) if key == "person" }
|
|
35
|
+
|
|
36
|
+
assert !@params.fetch(:person).permitted?
|
|
37
|
+
|
|
38
|
+
assert !@params.values_at(:person).first.permitted?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_permitted_is_sticky_on_accessors
|
|
42
|
+
@params.permit!
|
|
43
|
+
assert @params.slice(:person).permitted?
|
|
44
|
+
assert @params[:person][:name].permitted?
|
|
45
|
+
assert @params[:person].except(:name).permitted?
|
|
46
|
+
|
|
47
|
+
@params.each { |key, value| assert(value.permitted?) if key == "person" }
|
|
48
|
+
|
|
49
|
+
assert @params.fetch(:person).permitted?
|
|
50
|
+
|
|
51
|
+
assert @params.values_at(:person).first.permitted?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_not_permitted_is_sticky_on_mutators
|
|
55
|
+
assert !@params.delete_if { |k, v| k == "person" }.permitted?
|
|
56
|
+
assert !@params.keep_if { |k, v| k == "person" }.permitted? if @params.respond_to?(:keep_if)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_permitted_is_sticky_on_mutators
|
|
60
|
+
@params.permit!
|
|
61
|
+
assert @params.delete_if { |k, v| k == "person" }.permitted?
|
|
62
|
+
assert @params.keep_if { |k, v| k == "person" }.permitted? if @params.respond_to?(:keep_if)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_not_permitted_is_sticky_beyond_merges
|
|
66
|
+
assert !@params.merge(:a => "b").permitted?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_permitted_is_sticky_beyond_merges
|
|
70
|
+
@params.permit!
|
|
71
|
+
assert @params.merge(:a => "b").permitted?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_modifying_the_parameters
|
|
75
|
+
@params[:person][:hometown] = "Chicago"
|
|
76
|
+
@params[:person][:family] = { :brother => "Jonas" }
|
|
77
|
+
|
|
78
|
+
assert_equal "Chicago", @params[:person][:hometown]
|
|
79
|
+
assert_equal "Jonas", @params[:person][:family][:brother]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_permitting_parameters_that_are_not_there_should_not_include_the_keys
|
|
83
|
+
assert !@params.permit(:person, :funky).has_key?(:funky)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_permit_state_is_kept_on_a_dup
|
|
87
|
+
@params.permit!
|
|
88
|
+
assert_equal @params.permitted?, @params.dup.permitted?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_permit_is_recursive
|
|
92
|
+
@params.permit!
|
|
93
|
+
assert @params.permitted?
|
|
94
|
+
assert @params[:person].permitted?
|
|
95
|
+
assert @params[:person][:name].permitted?
|
|
96
|
+
assert @params[:person][:addresses][0].permitted?
|
|
97
|
+
end
|
|
98
|
+
end
|