strong_parameters 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,129 @@
1
+ [![Travis CI](https://secure.travis-ci.org/rails/strong_parameters.png)](http://travis-ci.org/rails/strong_parameters) [![Gem Version](https://badge.fury.io/rb/strong_parameters.png)](http://badge.fury.io/rb/strong_parameters)
2
+ # Strong Parameters
3
+
4
+ With this plugin Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted. This means you'll have to make a conscious choice about which attributes to allow for mass updating and thus prevent accidentally exposing that which shouldn't be exposed.
5
+
6
+ In addition, parameters can be marked as required and flow through a predefined raise/rescue flow to end up as a 400 Bad Request with no effort.
7
+
8
+ ``` ruby
9
+ class PeopleController < ActionController::Base
10
+ # This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
11
+ # without an explicit permit step.
12
+ def create
13
+ Person.create(params[:person])
14
+ end
15
+
16
+ # This will pass with flying colors as long as there's a person key in the parameters, otherwise
17
+ # it'll raise a ActionController::MissingParameter exception, which will get caught by
18
+ # ActionController::Base and turned into that 400 Bad Request reply.
19
+ def update
20
+ person = current_account.people.find(params[:id])
21
+ person.update_attributes!(person_params)
22
+ redirect_to person
23
+ end
24
+
25
+ private
26
+ # Using a private method to encapsulate the permissible parameters is just a good pattern
27
+ # since you'll be able to reuse the same permit list between create and update. Also, you
28
+ # can specialize this method with per-user checking of permissible attributes.
29
+ def person_params
30
+ params.require(:person).permit(:name, :age)
31
+ end
32
+ end
33
+ ```
34
+
35
+ ## Permitted Scalar Values
36
+
37
+ Given
38
+
39
+ ``` ruby
40
+ params.permit(:id)
41
+ ```
42
+
43
+ the key `:id` will pass the whitelisting if it appears in `params` and it has a permitted scalar value associated. Otherwise the key is going to be filtered out, so arrays, hashes, or any other objects cannot be injected.
44
+
45
+ The permitted scalar types are `String`, `Symbol`, `NilClass`, `Numeric`, `TrueClass`, `FalseClass`, `Date`, `Time`, `DateTime`, `StringIO`, `IO`, `ActionDispatch::Http::UploadedFile` and `Rack::Test::UploadedFile`.
46
+
47
+ To declare that the value in `params` must be an array of permitted scalar values map the key to an empty array:
48
+
49
+ ``` ruby
50
+ params.permit(:id => [])
51
+ ```
52
+
53
+ To whitelist an entire hash of parameters, the `permit!` method can be used
54
+
55
+ ``` ruby
56
+ params.require(:log_entry).permit!
57
+ ```
58
+
59
+ This will mark the `:log_entry` parameters hash and any subhash of it permitted. Extreme care should be taken when using `permit!` as it will allow all current and future model attributes to be mass-assigned.
60
+
61
+ ## Nested Parameters
62
+
63
+ You can also use permit on nested parameters, like:
64
+
65
+ ``` ruby
66
+ params.permit(:name, {:emails => []}, :friends => [ :name, { :family => [ :name ], :hobbies => [] }])
67
+ ```
68
+
69
+ This declaration whitelists the `name`, `emails` and `friends` attributes. It is expected that `emails` will be an array of permitted scalar values and that `friends` will be an array of resources with specific attributes : they should have a `name` attribute (any permitted scalar values allowed), a `hobbies` attribute as an array of permitted scalar values, and a `family` attribute which is restricted to having a `name` (any permitted scalar values allowed, too).
70
+
71
+ Thanks to Nick Kallen for the permit idea!
72
+
73
+ ## Handling of Unpermitted Keys
74
+
75
+ By default parameter keys that are not explicitly permitted will be logged in the development and test environment. In other environments these parameters will simply be filtered out and ignored.
76
+
77
+ Additionally, this behaviour can be changed by changing the `config.action_controller.action_on_unpermitted_parameters` property in your environment files. If set to `:log` the unpermitted attributes will be logged, if set to `:raise` an exception will be raised.
78
+
79
+ ## Use Outside of Controllers
80
+
81
+ While Strong Parameters will enforce permitted and required values in your application controllers, keep in mind
82
+ that you will need to sanitize untrusted data used for mass assignment when in use outside of controllers.
83
+
84
+ For example, if you retrieve JSON data from a third party API call and pass the unchecked parsed result on to
85
+ `Model.create`, undesired mass assignments could take place. You can alleviate this risk by slicing the hash data,
86
+ or wrapping the data in a new instance of `ActionController::Parameters` and declaring permissions the same as
87
+ you would in a controller. For example:
88
+
89
+ ``` ruby
90
+ raw_parameters = { :email => "john@example.com", :name => "John", :admin => true }
91
+ parameters = ActionController::Parameters.new(raw_parameters)
92
+ user = User.create(parameters.permit(:name, :email))
93
+ ```
94
+
95
+ ## Installation
96
+
97
+ In Gemfile:
98
+
99
+ ``` ruby
100
+ gem 'strong_parameters'
101
+ ```
102
+
103
+ and then run `bundle`. To activate the strong parameters, you need to include this module in
104
+ every model you want protected.
105
+
106
+ ``` ruby
107
+ class Post < ActiveRecord::Base
108
+ include ActiveModel::ForbiddenAttributesProtection
109
+ end
110
+ ```
111
+
112
+ Alternatively, you can protect all Active Record resources by default by creating an initializer and pasting the line:
113
+
114
+ ``` ruby
115
+ ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
116
+ ```
117
+
118
+ If you want to now disable the default whitelisting that occurs in Rails 3.2, change the `config.active_record.whitelist_attributes` property in your `config/application.rb`:
119
+
120
+ ``` ruby
121
+ config.active_record.whitelist_attributes = false
122
+ ```
123
+
124
+ This will allow you to remove / not have to use `attr_accessible` and do mass assignment inside your code and tests.
125
+
126
+ ## Compatibility
127
+
128
+ This plugin is only fully compatible with Rails versions 3.0, 3.1 and 3.2 but not 4.0+, as it is part of Rails Core in 4.0.
129
+ An unofficial Rails 2 version is [strong_parameters_rails2](https://github.com/grosser/strong_parameters/tree/rails2).
data/Rakefile CHANGED
@@ -12,7 +12,8 @@ RDoc::Task.new(:rdoc) do |rdoc|
12
12
  rdoc.rdoc_dir = 'rdoc'
13
13
  rdoc.title = 'StrongParameters'
14
14
  rdoc.options << '--line-numbers'
15
- rdoc.rdoc_files.include('README.rdoc')
15
+ rdoc_main = 'README.md'
16
+ rdoc.rdoc_files.include('README.md')
16
17
  rdoc.rdoc_files.include('lib/**/*.rb')
17
18
  end
18
19
 
@@ -143,6 +143,7 @@ module ActionController
143
143
  StringIO,
144
144
  IO,
145
145
  ActionDispatch::Http::UploadedFile,
146
+ Rack::Test::UploadedFile,
146
147
  ]
147
148
 
148
149
  def permitted_scalar?(value)
@@ -167,9 +168,9 @@ module ActionController
167
168
  end
168
169
  end
169
170
 
170
- def array_of_permitted_scalars_filter(params, key)
171
- if has_key?(key) && array_of_permitted_scalars?(self[key])
172
- params[key] = self[key]
171
+ def array_of_permitted_scalars_filter(params, key, hash = self)
172
+ if hash.has_key?(key) && array_of_permitted_scalars?(hash[key])
173
+ params[key] = hash[key]
173
174
  end
174
175
  end
175
176
 
@@ -185,10 +186,12 @@ module ActionController
185
186
  array_of_permitted_scalars_filter(params, key)
186
187
  else
187
188
  # Declaration {:user => :name} or {:user => [:name, :age, {:adress => ...}]}.
188
- params[key] = each_element(value) do |element|
189
+ params[key] = each_element(value) do |element, index|
189
190
  if element.is_a?(Hash)
190
191
  element = self.class.new(element) unless element.respond_to?(:permit)
191
192
  element.permit(*Array.wrap(filter[key]))
193
+ elsif filter[key].is_a?(Hash) && filter[key][index] == []
194
+ array_of_permitted_scalars_filter(params, index, value)
192
195
  end
193
196
  end
194
197
  end
@@ -201,7 +204,7 @@ module ActionController
201
204
  # fields_for on an array of records uses numeric hash keys.
202
205
  elsif value.is_a?(Hash) && value.keys.all? { |k| k =~ /\A-?\d+\z/ }
203
206
  hash = value.class.new
204
- value.each { |k,v| hash[k] = yield v }
207
+ value.each { |k,v| hash[k] = yield(v, k) }
205
208
  hash
206
209
  else
207
210
  yield value
@@ -215,8 +218,9 @@ module ActionController
215
218
 
216
219
  if unpermitted_keys.any?
217
220
  case self.class.action_on_unpermitted_parameters
218
- when :log
219
- ActionController::Base.logger.debug "Unpermitted parameters: #{unpermitted_keys.join(", ")}"
221
+ when :log
222
+ name = "unpermitted_parameters.action_controller"
223
+ ActiveSupport::Notifications.instrument(name, :keys => unpermitted_keys)
220
224
  when :raise
221
225
  raise ActionController::UnpermittedParameters.new(unpermitted_keys)
222
226
  end
@@ -1,3 +1,4 @@
1
1
  require 'action_controller/parameters'
2
2
  require 'active_model/forbidden_attributes_protection'
3
3
  require 'strong_parameters/railtie'
4
+ require 'strong_parameters/log_subscriber'
@@ -0,0 +1,14 @@
1
+ module StrongParameters
2
+ class LogSubscriber < ActiveSupport::LogSubscriber
3
+ def unpermitted_parameters(event)
4
+ unpermitted_keys = event.payload[:keys]
5
+ debug("Unpermitted parameters: #{unpermitted_keys.join(", ")}")
6
+ end
7
+
8
+ def logger
9
+ ActionController::Base.logger
10
+ end
11
+ end
12
+ end
13
+
14
+ StrongParameters::LogSubscriber.attach_to :action_controller
@@ -1,3 +1,3 @@
1
1
  module StrongParameters
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -8,7 +8,6 @@ class StrongParametersControllerGeneratorTest < Rails::Generators::TestCase
8
8
  setup :prepare_destination
9
9
 
10
10
  def test_controller_content
11
- Rails.stubs(:application).returns(nil)
12
11
  run_generator
13
12
 
14
13
  assert_file "app/controllers/users_controller.rb" do |content|
@@ -30,9 +30,6 @@ GEM
30
30
  abstract (>= 1.0.0)
31
31
  i18n (0.5.0)
32
32
  json (1.7.5)
33
- metaclass (0.0.1)
34
- mocha (0.12.7)
35
- metaclass (~> 0.0.1)
36
33
  rack (1.2.5)
37
34
  rack-mount (0.6.14)
38
35
  rack (>= 1.0.0)
@@ -56,7 +53,6 @@ PLATFORMS
56
53
  DEPENDENCIES
57
54
  actionpack (~> 3.0.0)
58
55
  activemodel (~> 3.0.0)
59
- mocha (~> 0.12.0)
60
56
  railties (~> 3.0.0)
61
57
  rake
62
58
  strong_parameters!
@@ -27,7 +27,7 @@ class NestedParametersTest < ActiveSupport::TestCase
27
27
  values += [0, 1.0, 2**128, BigDecimal.new('1')]
28
28
  values += [true, false]
29
29
  values += [Date.today, Time.now, DateTime.now]
30
- values += [StringIO.new, STDOUT, ActionDispatch::Http::UploadedFile.new(:tempfile => __FILE__)]
30
+ values += [StringIO.new, STDOUT, ActionDispatch::Http::UploadedFile.new(:tempfile => __FILE__), Rack::Test::UploadedFile.new(__FILE__)]
31
31
 
32
32
  values.each do |value|
33
33
  params = ActionController::Parameters.new(:id => value)
@@ -291,4 +291,22 @@ class NestedParametersTest < ActiveSupport::TestCase
291
291
 
292
292
  assert_filtered_out permitted[:book][:authors_attributes]['-1'], :age_of_death
293
293
  end
294
+
295
+ test "fields_for_style_nested_params with nested arrays" do
296
+ params = ActionController::Parameters.new({
297
+ :book => {
298
+ :authors_attributes => {
299
+ :'0' => ['William Shakespeare', '52'],
300
+ :'1' => ['Unattributed Assistant']
301
+ }
302
+ }
303
+ })
304
+ permitted = params.permit :book => { :authors_attributes => { :'0' => [], :'1' => [] } }
305
+
306
+ assert_not_nil permitted[:book][:authors_attributes]['0']
307
+ assert_not_nil permitted[:book][:authors_attributes]['1']
308
+ assert_nil permitted[:book][:authors_attributes]['2']
309
+ assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][0]
310
+ assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][0]
311
+ end
294
312
  end
@@ -10,7 +10,6 @@ Rails.application = FakeApplication
10
10
  Rails.configuration.action_controller = ActiveSupport::OrderedOptions.new
11
11
 
12
12
  require 'strong_parameters'
13
- require 'mocha'
14
13
 
15
14
  module ActionController
16
15
  SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strong_parameters
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-18 00:00:00.000000000 Z
12
+ date: 2013-05-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -75,22 +75,6 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
- - !ruby/object:Gem::Dependency
79
- name: mocha
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: 0.12.0
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ~>
92
- - !ruby/object:Gem::Version
93
- version: 0.12.0
94
78
  description:
95
79
  email:
96
80
  - david@heinemeierhansson.com
@@ -103,12 +87,13 @@ files:
103
87
  - lib/generators/rails/strong_parameters_controller_generator.rb
104
88
  - lib/generators/rails/templates/controller.rb
105
89
  - lib/generators/rails/USAGE
90
+ - lib/strong_parameters/log_subscriber.rb
106
91
  - lib/strong_parameters/railtie.rb
107
92
  - lib/strong_parameters/version.rb
108
93
  - lib/strong_parameters.rb
109
94
  - MIT-LICENSE
110
95
  - Rakefile
111
- - README.rdoc
96
+ - README.md
112
97
  - test/action_controller_required_params_test.rb
113
98
  - test/action_controller_tainted_params_test.rb
114
99
  - test/active_model_mass_assignment_taint_protection_test.rb
@@ -1,102 +0,0 @@
1
- = Strong Parameters
2
-
3
- With this plugin Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted. This means you'll have to make a conscious choice about which attributes to allow for mass updating and thus prevent accidentally exposing that which shouldn't be exposed.
4
-
5
- In addition, parameters can be marked as required and flow through a predefined raise/rescue flow to end up as a 400 Bad Request with no effort.
6
-
7
- class PeopleController < ActionController::Base
8
- # This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
9
- # without an explicit permit step.
10
- def create
11
- Person.create(params[:person])
12
- end
13
-
14
- # This will pass with flying colors as long as there's a person key in the parameters, otherwise
15
- # it'll raise a ActionController::MissingParameter exception, which will get caught by
16
- # ActionController::Base and turned into that 400 Bad Request reply.
17
- def update
18
- person = current_account.people.find(params[:id])
19
- person.update_attributes!(person_params)
20
- redirect_to person
21
- end
22
-
23
- private
24
- # Using a private method to encapsulate the permissible parameters is just a good pattern
25
- # since you'll be able to reuse the same permit list between create and update. Also, you
26
- # can specialize this method with per-user checking of permissible attributes.
27
- def person_params
28
- params.require(:person).permit(:name, :age)
29
- end
30
- end
31
-
32
- == Permitted Scalar Values
33
-
34
- Given
35
-
36
- params.permit(:id)
37
-
38
- the key +:id+ will pass the whitelisting if it appears in +params+ and it has a permitted scalar value associated. Otherwise the key is going to be filtered out, so arrays, hashes, or any other objects cannot be injected.
39
-
40
- The permitted scalar types are +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+, +Date+, +Time+, +DateTime+, +StringIO+, +IO+, and +ActionDispatch::Http::UploadedFile+.
41
-
42
- To declare that the value in +params+ must be an array of permitted scalar values map the key to an empty array:
43
-
44
- params.permit(:id => [])
45
-
46
- == Nested Parameters
47
-
48
- You can also use permit on nested parameters, like:
49
-
50
- params.permit(:name, {:emails => []}, :friends => [ :name, { :family => [ :name ], :hobbies => [] }])
51
-
52
- This declaration whitelists the +name+, +emails+ and +friends+ attributes. It is expected that +emails+ will be an array of permitted scalar values and that +friends+ will be an array of resources with specific attributes : they should have a +name+ attribute (any permitted scalar values allowed), a +hobbies+ attribute as an array of permitted scalar values, and a +family+ attribute which is restricted to having a +name+ (any permitted scalar values allowed, too).
53
-
54
- Thanks to Nick Kallen for the permit idea!
55
-
56
- == Handling of Unpermitted Keys
57
-
58
- By default parameter keys that are not explicitly permitted will be logged in the development and test environment. In other environments these parameters will simply be filtered out and ignored.
59
-
60
- Additionally, this behaviour can be changed by changing the +config.action_controller.action_on_unpermitted_parameters+ property in your environment files. If set to +:log+ the unpermitted attributes will be logged, if set to +:raise+ an exception will be raised.
61
-
62
- == Use Outside of Controllers
63
-
64
- While Strong Parameters will enforce permitted and required values in your application controllers, keep in mind
65
- that you will need to sanitize untrusted data used for mass assignment when in use outside of controllers.
66
-
67
- For example, if you retrieve JSON data from a third party API call and pass the unchecked parsed result on to
68
- +Model.create+, undesired mass assignments could take place. You can alleviate this risk by slicing the hash data,
69
- or wrapping the data in a new instance of +ActionController::Parameters+ and declaring permissions the same as
70
- you would in a controller. For example:
71
-
72
- raw_parameters = { :email => "john@example.com", :name => "John", :admin => true }
73
- parameters = ActionController::Parameters.new(raw_parameters)
74
- user = User.create(parameters.permit(:name, :email))
75
-
76
- == Installation
77
-
78
- In Gemfile:
79
-
80
- gem 'strong_parameters'
81
-
82
- and then run `bundle`. To activate the strong parameters, you need to include this module in
83
- every model you want protected.
84
-
85
- class Post < ActiveRecord::Base
86
- include ActiveModel::ForbiddenAttributesProtection
87
- end
88
-
89
- Alternatively, you can protect all Active Record resources by default by creating an initializer and pasting the line:
90
-
91
- ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
92
-
93
- If you want to now disable the default whitelisting that occurs in later versions of Rails, change the +config.active_record.whitelist_attributes+ property in your +config/application.rb+:
94
-
95
- config.active_record.whitelist_attributes = false
96
-
97
- This will allow you to remove / not have to use +attr_accessible+ and do mass assignment inside your code and tests.
98
-
99
- == Compatibility
100
-
101
- This plugin is only fully compatible with Rails versions 3.0, 3.1 and 3.2 but not 4.0+, as it is part of Rails Core in 4.0.
102
- An unofficial Rails 2 version is {strong_parameters_rails2}[https://github.com/grosser/strong_parameters/tree/rails2].