param_accessible 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  *.gem
2
2
  *.rbc
3
3
  .bundle
4
+ .rvmrc
4
5
  .config
5
6
  .yardoc
6
7
  .rspec
data/README.md CHANGED
@@ -1,3 +1,9 @@
1
+ # Important Note
2
+
3
+ Soon after I created this project, @dhh (creator of Rails) made his own parameter protection gem public: https://github.com/rails/strong_parameters
4
+
5
+ DHH's strong_parameters gem will be integrated into rails4, so I suggest you use it instead.
6
+
1
7
  # ParamAccessible
2
8
 
3
9
  [![Build Status](https://secure.travis-ci.org/topdan/param_accessible.png)](https://secure.travis-ci.org/topdan/param_accessible.png)
@@ -24,33 +30,61 @@ Or install it yourself as:
24
30
 
25
31
  ## Usage
26
32
 
33
+ This gem does not add any functionality by default. To activate it create a before_filter in any ActionController::Base subclass. We only use the filter for create and update actions because those are the normally only the only harmful ones:
34
+
35
+ before_filter :ensure_params_are_accessible, :only => [:create, :update]
36
+
37
+ Now let's expose the parameters that are common across our application:
38
+
39
+ param_accessible :page, :sort
40
+
41
+ You may want to allow all base-level parameters since most Rails controllers only send nested parameters to models (i.e. params[:user]):
42
+
43
+ # allow all base parameters (// is a regex that matches all strings)
44
+ param_accessible //
45
+
46
+ # allow any base parameter starting with "my_"
47
+ param_accessible /^my_/
48
+
49
+ We also want to make sure only admins can change a user's "is_admin" and "is_active" attributes:
50
+
51
+ param_accessible :user => [:is_admin, :is_active], :if => :is_admin?
52
+
53
+ Rinse and repeat for all your controllers and you're Rails Application will be much safer.
54
+
55
+ ## Example
56
+
57
+ Placing the before_filter in our ApplicationController makes all our create and update actions secure by default. We also expose application-wide parameters and provide a friendly error message when an invalid parameter is provided.
58
+
27
59
  class ApplicationController < ActionController::Base
28
60
 
29
61
  # make all your controllers secure by default
30
62
  before_filter :ensure_params_are_accessible, :only => [:create, :update]
31
63
 
32
- # expose the common rails parameters
33
- param_accessible :controller, :action, :format, :id
64
+ # expose the your common application parameters
65
+ param_accessible :page, :sort
34
66
 
35
- # this error is thrown when the user tries to access an inaccessible param
67
+ # this error is thrown when the user submits an inaccessible param
36
68
  rescue_from ParamAccessible::Error, :with => :handle_param_not_accessible
37
69
 
38
70
  protected
39
71
 
40
72
  def handle_param_not_accessible e
41
- flash[:error] = "You gave me some invalid parameters: #{e.inaccessible_params.join(', )}"
73
+ flash[:error] = "You gave me some invalid parameters: #{e.inaccessible_params.join(', ')}"
42
74
  redirect_to :back
43
75
  end
44
76
 
45
77
  end
46
-
47
- class UserController < ApplicationController
78
+
79
+ Inheriting from the class above, we now need to specify our accessible parameters for the create and update actions.
80
+
81
+ class UsersController < ApplicationController
48
82
 
49
83
  # these attributes are available for everyone
50
- param_accessible :user => {:name, :email, :password, :password_confirmation}
84
+ param_accessible :user => [:name, :email, :password, :password_confirmation]
51
85
 
52
86
  # these attributes are only available if the controller instance method is_admin? is true
53
- param_accessible :user => {:is_admin, :is_locked_out}, :if => :is_admin?
87
+ param_accessible :user => [:is_admin, :is_locked_out], :if => :is_admin?
54
88
 
55
89
  def update
56
90
  @user = User.find(params[:id])
@@ -63,7 +97,9 @@ Or install it yourself as:
63
97
  end
64
98
  end
65
99
  end
66
-
100
+
101
+ Showcase a helper module for handling errors and some more options.
102
+
67
103
  class DemoController < ApplicationController
68
104
 
69
105
  # rescue_from ParamAccessible::Error and respond with a 406 Not Acceptable status
@@ -76,14 +112,25 @@ Or install it yourself as:
76
112
  param_accessible :nut, :except => :index
77
113
 
78
114
  end
79
-
115
+
116
+ Using Rails' skip_before_filter to make a controller insecure again
117
+
80
118
  class InsecureController < ApplicationController
81
119
 
82
120
  # skip the filter ApplicationController set up to avoid the accessible parameter checks
83
121
  skip_before_filter :ensure_params_are_accessible
84
122
 
85
123
  end
86
-
124
+
125
+ ## Nested attributes
126
+
127
+ Arrays of attributes like the ones used with Rails' nested forms feature can be specified using
128
+ the following syntax:
129
+
130
+ class DemoController < ApplicationController
131
+ param_accessible :"foo[]" => [:bar, :baz]
132
+ end
133
+
87
134
  ## Contributing
88
135
 
89
136
  1. Fork it
@@ -30,7 +30,8 @@ module ParamAccessible
30
30
  if superclass.respond_to?(:param_accessible_rules)
31
31
  @param_accessible_rules = Rules.new superclass.param_accessible_rules
32
32
  else
33
- @param_accessible_rules = Rules.new
33
+ common_rails_parameters_rule = Rule.new :controller, :action, :id, :format, :authenticity_token, :commit, :utf8
34
+ @param_accessible_rules = Rules.new [common_rails_parameters_rule]
34
35
  end
35
36
  end
36
37
 
@@ -40,27 +40,67 @@ module ParamAccessible
40
40
  return if @only_options != nil && !@only_options.include?(controller.action_name)
41
41
  return if @except_options != nil && @except_options.include?(controller.action_name)
42
42
 
43
- accessible_hash_for controller, @attributes, dest
43
+ accessible_hash_for controller.params, @attributes, dest
44
44
  end
45
45
 
46
46
  protected
47
47
 
48
- def accessible_hash_for controller, attributes, dest
48
+ def accessible_hash_for params, attributes, dest
49
49
  attributes.each do |key, value|
50
- if value.is_a?(Hash)
50
+ if key.to_s =~ /\[\]$/
51
+ accessible_array_for key, params, value, dest
52
+ elsif value.is_a?(Hash)
51
53
  attrs = dest[key]
52
54
  if attrs.nil?
53
55
  attrs = {}
54
56
  dest[key] = attrs
55
57
  end
56
58
 
57
- accessible_hash_for controller, value, attrs
58
- else
59
+ nested_params = params[key] if params.is_a?(Hash)
60
+ accessible_hash_for nested_params, value, attrs
61
+
62
+ elsif key.is_a?(String)
59
63
  dest[key] = value
64
+
65
+ elsif key.is_a?(Regexp) && params
66
+ accessible_params_for_regex key, params, dest
60
67
  end
61
68
  end
62
69
  end
63
70
 
71
+ def accessible_params_for_regex regex, params, dest
72
+ params.keys.each do |key|
73
+ if key.to_s =~ regex
74
+ dest[key] = nil
75
+ end
76
+ end
77
+
78
+ dest
79
+ end
80
+
81
+ def accessible_array_for key, params, value, dest
82
+ key = key.to_s.chomp('[]')
83
+
84
+ if params and params[key].is_a? Hash
85
+ params[key].each do |index, nested_params|
86
+ dest[key] ||= {}
87
+ attrs = dest[key][index] = {}
88
+ accessible_hash_for nested_params, value, attrs if value
89
+ end
90
+ elsif params and params[key].is_a? Array
91
+ params[key].each do |nested_params|
92
+ if nested_params.is_a? Hash
93
+ dest[key] ||= []
94
+ attrs = {}
95
+ accessible_hash_for nested_params, value, attrs if value
96
+ dest[key].push(attrs)
97
+ else
98
+ dest[key] = nil
99
+ end
100
+ end
101
+ end
102
+ end
103
+
64
104
  # When specifying params to protect, we allow a combination of arrays and hashes much like how
65
105
  # ActiveRecord::Base#find's :include options works. This method normalizes that into just nested hashes,
66
106
  # stringifying the keys and setting all values to nil. This format is easier/faster to work with when
@@ -85,7 +125,11 @@ module ParamAccessible
85
125
  end
86
126
 
87
127
  def normalize_key(k)
88
- k.to_s
128
+ if k.is_a?(Regexp)
129
+ k
130
+ else
131
+ k.to_s
132
+ end
89
133
  end
90
134
 
91
135
  end
@@ -33,10 +33,9 @@ module ParamAccessible
33
33
  detect_inaccessible_hash value, nested, errors, prefix_for(prefix, key)
34
34
 
35
35
  elsif value.is_a?(Array)
36
- nested = accessible[key] || {}
37
- value.each do |v|
36
+ value.each_with_index do |v, i|
38
37
  if v.is_a?(Hash)
39
- detect_inaccessible_hash v, nested, errors, prefix_for(prefix, key)
38
+ detect_inaccessible_hash v, accessible[key][i], errors, prefix_for(prefix_for(prefix, key), "")
40
39
  end
41
40
  end
42
41
  end
@@ -1,3 +1,3 @@
1
1
  module ParamAccessible
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -2,8 +2,6 @@ class ApplicationController < ActionController::Base
2
2
  before_filter :ensure_params_are_accessible, :only => [:create, :update]
3
3
  before_filter :render_nothing
4
4
 
5
- param_accessible :action, :controller, :format
6
-
7
5
  def index
8
6
  end
9
7
 
@@ -0,0 +1,4 @@
1
+ class NestedController < ApplicationController
2
+ param_accessible :a => [{:"b[]" => [:d, :e, :q]}, :c]
3
+ param_accessible :"o[]", :p
4
+ end
@@ -0,0 +1,6 @@
1
+ class RegexController < ApplicationController
2
+
3
+ param_accessible /^foo/
4
+ param_accessible :user => [/^bar/]
5
+
6
+ end
@@ -0,0 +1,88 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe NestedController do
4
+ include RSpec::Rails::ControllerExampleGroup
5
+
6
+ it "should allow arrays given as rails style pseudo hashes" do
7
+ post :create, :a => { :b => {"0" => {"d"=>"foo", "e"=>"bar"}, "1" => {"d"=>"foo", "e"=>"bar"}}}
8
+ response.code.should == "200"
9
+ end
10
+
11
+ it "should allow arrays given as arrays hashes" do
12
+ post :create, :a => { :b => [{"d"=>"foo", "e"=>"bar"}, {"d"=>"foo", "e"=>"bar"}]}
13
+ response.code.should == "200"
14
+ end
15
+
16
+ it "should allow arrays given as arrays hashes" do
17
+ post :create, :o => ["foo", "bar"]
18
+ response.code.should == "200"
19
+ end
20
+
21
+ it "should allow arrays given as rails style hash arrays when only arrays of strings are allowed" do
22
+ post :create, :o => { "0" => "foo", "1" => "bar" }
23
+ response.code.should == "200"
24
+ end
25
+
26
+ it "should not allow options which are not in the list" do
27
+ begin
28
+ post :create, :a => { :b => {"0" => {"x"=>"foo" }}}
29
+ raise "should fail"
30
+ rescue ParamAccessible::Error => e
31
+ e.inaccessible_params.should == %w(a[b][0][x])
32
+ end
33
+ end
34
+
35
+ it "should not allow strings for an array" do
36
+ begin
37
+ post :create, :a => { :b => "foo" }
38
+ raise "should fail"
39
+ rescue ParamAccessible::Error => e
40
+ e.inaccessible_params.should == %w(a[b])
41
+ end
42
+ end
43
+
44
+ it "should not allow arrays with invalid values" do
45
+ begin
46
+ post :create, :a => { :b => [{"x"=>"foo"}]}
47
+ raise "should fail"
48
+ rescue ParamAccessible::Error => e
49
+ e.inaccessible_params.should == %w(a[b][][x])
50
+ end
51
+ end
52
+
53
+ it "should not allow arrays with invalid values at other index" do
54
+ begin
55
+ post :create, :a => { :b => [{"d" => "foo"}, {"x"=>"foo"}]}
56
+ raise "should fail"
57
+ rescue ParamAccessible::Error => e
58
+ e.inaccessible_params.should == %w(a[b][][x])
59
+ end
60
+ end
61
+
62
+ it "should not allow hashes when only arrays of strings are allowed" do
63
+ begin
64
+ post :create, :o => [{ :foo => "bar" }]
65
+ raise "should fail"
66
+ rescue ParamAccessible::Error => e
67
+ e.inaccessible_params.should == %w(o[][foo])
68
+ end
69
+ end
70
+
71
+ it "should not allow string when only arrays are allowed" do
72
+ begin
73
+ post :create, :o => "foo"
74
+ raise "should fail"
75
+ rescue ParamAccessible::Error => e
76
+ e.inaccessible_params.should == %w(o)
77
+ end
78
+ end
79
+
80
+ it "should not allow arrays given as rails style hash arrays containing hashes when only arrays of strings are allowed" do
81
+ begin
82
+ post :create, :o => { "0" => { "x" => "foo" } }
83
+ raise "should fail"
84
+ rescue ParamAccessible::Error => e
85
+ e.inaccessible_params.should == %w(o[0][x])
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe RegexController do
4
+ include RSpec::Rails::ControllerExampleGroup
5
+
6
+ it "should allow base parameters matching a regex" do
7
+ post :create, :foo => 'hi', :foobar => 'hey'
8
+ response.code.should == '200'
9
+ end
10
+
11
+ it "should not allow base parameters not matching a regex" do
12
+ begin
13
+ post :create, :nuts => "hi"
14
+ raise "should fail"
15
+ rescue ParamAccessible::Error => e
16
+ e.inaccessible_params.should == %w(nuts)
17
+ end
18
+ end
19
+
20
+ it "should allow nested parameters matching the regex" do
21
+ post :create, :user => {:bar => 'hi', :bar_me => 'hey'}
22
+ response.code.should == '200'
23
+ end
24
+
25
+ it "should not allow nested parameters not matching a regex" do
26
+ begin
27
+ post :create, :user => {:nuts => "hi"}
28
+ raise "should fail"
29
+ rescue ParamAccessible::Error => e
30
+ e.inaccessible_params.should == %w(user[nuts])
31
+ end
32
+ end
33
+
34
+ end
@@ -8,6 +8,11 @@ describe SimpleController do
8
8
  response.code.should == "200"
9
9
  end
10
10
 
11
+ it "should not complain about common rails parameters" do
12
+ post :update, :format => "json", :id => "foo"
13
+ response.code.should == "200"
14
+ end
15
+
11
16
  it "should not complain if a subset of those attributes are given" do
12
17
  post :create, :foo => 'hi'
13
18
  response.code.should == "200"
@@ -40,12 +45,7 @@ describe SimpleController do
40
45
  e.inaccessible_params.should == %w(bar[unknown])
41
46
  end
42
47
  end
43
-
44
- it "should handle nested arrays" do
45
- post :create, :bar => [:baz => 'hi']
46
- response.code.should == '200'
47
- end
48
-
48
+
49
49
  it "should be fine when the ONLY value is a value but not an options hash" do
50
50
  SimpleController.param_accessible :bar => []
51
51
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: param_accessible
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-15 00:00:00.000000000 Z
12
+ date: 2012-06-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &177970 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 3.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *177970
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: actionpack
27
- requirement: &177640 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 3.0.0
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *177640
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rails
38
- requirement: &177410 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: 3.0.0
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *177410
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rspec-rails
49
- requirement: &177110 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,10 +69,15 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *177110
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: simplecov
60
- requirement: &176790 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,7 +85,12 @@ dependencies:
65
85
  version: '0'
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *176790
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
69
94
  description: Help secure your controllers from malicious parameters
70
95
  email:
71
96
  - dan@topdan.com
@@ -92,8 +117,10 @@ files:
92
117
  - spec/app_root/app/controllers/if_false_controller.rb
93
118
  - spec/app_root/app/controllers/if_true_controller.rb
94
119
  - spec/app_root/app/controllers/merge_controller.rb
120
+ - spec/app_root/app/controllers/nested_controller.rb
95
121
  - spec/app_root/app/controllers/not_acceptable_controller.rb
96
122
  - spec/app_root/app/controllers/only_controller.rb
123
+ - spec/app_root/app/controllers/regex_controller.rb
97
124
  - spec/app_root/app/controllers/simple_controller.rb
98
125
  - spec/app_root/app/controllers/unless_false_controller.rb
99
126
  - spec/app_root/app/controllers/unless_true_controller.rb
@@ -104,8 +131,10 @@ files:
104
131
  - spec/lib/if_false_spec.rb
105
132
  - spec/lib/if_true_spec.rb
106
133
  - spec/lib/merge_spec.rb
134
+ - spec/lib/nested_spec.rb
107
135
  - spec/lib/not_acceptable_helper_spec.rb
108
136
  - spec/lib/only_spec.rb
137
+ - spec/lib/regex_spec.rb
109
138
  - spec/lib/simple_spec.rb
110
139
  - spec/lib/unless_false_spec.rb
111
140
  - spec/lib/unless_true_spec.rb
@@ -122,15 +151,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
122
151
  - - ! '>='
123
152
  - !ruby/object:Gem::Version
124
153
  version: '0'
154
+ segments:
155
+ - 0
156
+ hash: -3120574609072739979
125
157
  required_rubygems_version: !ruby/object:Gem::Requirement
126
158
  none: false
127
159
  requirements:
128
160
  - - ! '>='
129
161
  - !ruby/object:Gem::Version
130
162
  version: '0'
163
+ segments:
164
+ - 0
165
+ hash: -3120574609072739979
131
166
  requirements: []
132
167
  rubyforge_project:
133
- rubygems_version: 1.8.10
168
+ rubygems_version: 1.8.24
134
169
  signing_key:
135
170
  specification_version: 3
136
171
  summary: Help secure your controllers from malicious parameters
@@ -140,8 +175,10 @@ test_files:
140
175
  - spec/app_root/app/controllers/if_false_controller.rb
141
176
  - spec/app_root/app/controllers/if_true_controller.rb
142
177
  - spec/app_root/app/controllers/merge_controller.rb
178
+ - spec/app_root/app/controllers/nested_controller.rb
143
179
  - spec/app_root/app/controllers/not_acceptable_controller.rb
144
180
  - spec/app_root/app/controllers/only_controller.rb
181
+ - spec/app_root/app/controllers/regex_controller.rb
145
182
  - spec/app_root/app/controllers/simple_controller.rb
146
183
  - spec/app_root/app/controllers/unless_false_controller.rb
147
184
  - spec/app_root/app/controllers/unless_true_controller.rb
@@ -152,8 +189,10 @@ test_files:
152
189
  - spec/lib/if_false_spec.rb
153
190
  - spec/lib/if_true_spec.rb
154
191
  - spec/lib/merge_spec.rb
192
+ - spec/lib/nested_spec.rb
155
193
  - spec/lib/not_acceptable_helper_spec.rb
156
194
  - spec/lib/only_spec.rb
195
+ - spec/lib/regex_spec.rb
157
196
  - spec/lib/simple_spec.rb
158
197
  - spec/lib/unless_false_spec.rb
159
198
  - spec/lib/unless_true_spec.rb