shields_up 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 391eded2ca4d95f1777b8a227f13905e7f01b975
4
+ data.tar.gz: 05c2a17280fb9c761b3743e688abcc3c9cbe97ce
5
+ SHA512:
6
+ metadata.gz: a13deb71b95918e3c1b7d297ec5f72a9d8b29a71d9a58c8f3259d09d9d8373aa2c6f7881b9f7c0c2c0b0df01adcd0f6f052383454ec527c2ac061faf045830a1
7
+ data.tar.gz: 4d99e218185e1db2828543e5c9ce39f469c593a9cc0a16c98516e1625ffe7d903acb7bf895c2b81ac830c85b49137aaf19d5562bf0bb785de84549da3a8732fd
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'mocha'
6
+ gem 'activemodel'
7
+ gem 'rails'
8
+ gem 'minitest', '~> 4.0'
9
+ gem "codeclimate-test-reporter", :require => nil
data/Gemfile.lock ADDED
@@ -0,0 +1,108 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ shields_up (0.16.0)
5
+ activesupport (~> 3.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionmailer (3.2.19)
11
+ actionpack (= 3.2.19)
12
+ mail (~> 2.5.4)
13
+ actionpack (3.2.19)
14
+ activemodel (= 3.2.19)
15
+ activesupport (= 3.2.19)
16
+ builder (~> 3.0.0)
17
+ erubis (~> 2.7.0)
18
+ journey (~> 1.0.4)
19
+ rack (~> 1.4.5)
20
+ rack-cache (~> 1.2)
21
+ rack-test (~> 0.6.1)
22
+ sprockets (~> 2.2.1)
23
+ activemodel (3.2.19)
24
+ activesupport (= 3.2.19)
25
+ builder (~> 3.0.0)
26
+ activerecord (3.2.19)
27
+ activemodel (= 3.2.19)
28
+ activesupport (= 3.2.19)
29
+ arel (~> 3.0.2)
30
+ tzinfo (~> 0.3.29)
31
+ activeresource (3.2.19)
32
+ activemodel (= 3.2.19)
33
+ activesupport (= 3.2.19)
34
+ activesupport (3.2.19)
35
+ i18n (~> 0.6, >= 0.6.4)
36
+ multi_json (~> 1.0)
37
+ arel (3.0.3)
38
+ builder (3.0.4)
39
+ codeclimate-test-reporter (0.4.1)
40
+ simplecov (>= 0.7.1, < 1.0.0)
41
+ docile (1.1.5)
42
+ erubis (2.7.0)
43
+ hike (1.2.3)
44
+ i18n (0.6.9)
45
+ journey (1.0.4)
46
+ json (1.8.1)
47
+ mail (2.5.4)
48
+ mime-types (~> 1.16)
49
+ treetop (~> 1.4.8)
50
+ metaclass (0.0.4)
51
+ mime-types (1.25.1)
52
+ minitest (4.7.5)
53
+ mocha (1.1.0)
54
+ metaclass (~> 0.0.1)
55
+ multi_json (1.10.1)
56
+ polyglot (0.3.5)
57
+ rack (1.4.5)
58
+ rack-cache (1.2)
59
+ rack (>= 0.4)
60
+ rack-ssl (1.3.4)
61
+ rack
62
+ rack-test (0.6.2)
63
+ rack (>= 1.0)
64
+ rails (3.2.19)
65
+ actionmailer (= 3.2.19)
66
+ actionpack (= 3.2.19)
67
+ activerecord (= 3.2.19)
68
+ activeresource (= 3.2.19)
69
+ activesupport (= 3.2.19)
70
+ bundler (~> 1.0)
71
+ railties (= 3.2.19)
72
+ railties (3.2.19)
73
+ actionpack (= 3.2.19)
74
+ activesupport (= 3.2.19)
75
+ rack-ssl (~> 1.3.2)
76
+ rake (>= 0.8.7)
77
+ rdoc (~> 3.4)
78
+ thor (>= 0.14.6, < 2.0)
79
+ rake (10.3.2)
80
+ rdoc (3.12.2)
81
+ json (~> 1.4)
82
+ simplecov (0.9.1)
83
+ docile (~> 1.1.0)
84
+ multi_json (~> 1.0)
85
+ simplecov-html (~> 0.8.0)
86
+ simplecov-html (0.8.0)
87
+ sprockets (2.2.2)
88
+ hike (~> 1.2)
89
+ multi_json (~> 1.0)
90
+ rack (~> 1.0)
91
+ tilt (~> 1.1, != 1.3.0)
92
+ thor (0.19.1)
93
+ tilt (1.4.1)
94
+ treetop (1.4.15)
95
+ polyglot
96
+ polyglot (>= 0.3.1)
97
+ tzinfo (0.3.41)
98
+
99
+ PLATFORMS
100
+ ruby
101
+
102
+ DEPENDENCIES
103
+ activemodel
104
+ codeclimate-test-reporter
105
+ minitest (~> 4.0)
106
+ mocha
107
+ rails
108
+ shields_up!
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Appfolio Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ [![Build Status](https://travis-ci.org/appfolio/shields_up.png)](https://travis-ci.org/appfolio/shields_up)
2
+ [![Code Climate](https://codeclimate.com/github/appfolio/shields_up/badges/gpa.svg)](https://codeclimate.com/github/appfolio/shields_up)
3
+ [![Test Coverage](https://codeclimate.com/github/appfolio/shields_up/badges/coverage.svg)](https://codeclimate.com/github/appfolio/shields_up)
4
+
5
+ #Shields Up
6
+ This gem provides an alternative implementation of strong_parameters.
7
+ ##usage (the grammar for permit statements is the same as strong_parameters)
8
+ - **Read this first**: [Strong Parameter Usage](http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters)
9
+
10
+
11
+ ##Differences to strong parameter
12
+ params.symbolize_keys (or similar functions) will disable strong_parameters
13
+ protection silently. With ShieldsUp this can not happen.
14
+
15
+ ## Enable ShieldsUp<br>
16
+
17
+ in Gemfile<br>
18
+ ```
19
+ gem 'shields_up'
20
+ ```
21
+ in controllers<br>
22
+ ```
23
+ include ShieldsUp
24
+ ```
25
+
26
+ - ShieldsUp::Parameter type only allows three operations: [], permit and require.
27
+ - You can use symbols or strings to access variables.<br>
28
+
29
+ ##Example:<br>
30
+ ```
31
+ params[:company]
32
+ ```
33
+ not:
34
+ ```
35
+ params["company"]
36
+ ```
37
+ or
38
+ ```
39
+ params.fetch(:company)
40
+ params.fetch("company")
41
+ ```
42
+
43
+ ##A more complicated example:<br>
44
+ ```
45
+ params.permit(:company => [:address, :enabled])
46
+ ```
47
+ not:
48
+ ```
49
+ params.permit("company" => [:address, "enabled"])
50
+ ```
51
+
52
+ ## How to disable shields up.<br>
53
+ If you have a bunch of legacy code in a controller or you call into gems whose
54
+ parameter handling
55
+ you do not control (e.g. devise) you can disable shields_up at your own risk.
56
+ Similar to holding a lock around critical sections these blocks should generally be kept as short as possible.
57
+ <pre>
58
+ params.with_shields_down do
59
+ call_some_legacy_stuff_or_gem()
60
+ end
61
+ </pre>
62
+ <br>
63
+ - with_shields_down can not be nested. This means inside with_shields_down there should be no with_shields_down
64
+ - the places in an application disabling parameter protection are such explicit and can be accounted for during an audit
65
+ </b>
66
+
67
+ ##Common Pitfalls:
68
+ - *to update and destroy associated records, permit <i>:id,</i> and <i> :_destroy </i> when you use accepts_nested_attributes_for in combination with a has_many association. <br>
69
+
70
+ Example:
71
+ ```
72
+ # To whitelist the following data:
73
+ # {"applicant" => {"email_address" => "some@email.com",
74
+ # "contact_info_attributes" => { "1" => {"salutation" => "First Salutation"},
75
+ # "2" => {"salutation" => "Second Salutation"}}}}
76
+ params.permit(
77
+ {:applicant => [
78
+ :email_address,
79
+ {:contact_info_attributes => [:salutation, :id, :_destroy]},
80
+ ]}
81
+ )
82
+ ```
83
+ - *use hash inside permit statment** for non scaler type.<br>
84
+
85
+ Example:
86
+ ```
87
+ # To whitelist the following data:
88
+ # {"applicant" => {"email_address" => "some@email.com",
89
+ # "info" => { "first_name" => 'First', "last_name" => 'Last'}}}
90
+ ```
91
+ <pre>
92
+ params.permit(
93
+ {:applicant => [
94
+ :email_address,
95
+ <b>{</b>:info => [:first_name, :last_name]<b>}</b>
96
+ ]}
97
+ )
98
+ </pre>
99
+
100
+ ##Limitations
101
+ Similar to strong_parameters, this gem was designed with the most common use cases in mind. It is not meant as a silver bullet to handle all your whitelisting problems. However you can easily disable shields_up, and sanitize the parameter with your own code to adapt to your situation.<br>
102
+
103
+ In order to keep our grammar compatible to strong_parameters it is not possible
104
+ to express permission for array of arrays:
105
+ ```
106
+ {'company' => [['a','b'],['a','b']]}
107
+ ```
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts 'Run `bundle install` to install missing gems'
9
+ exit e.status_code
10
+ end
11
+
12
+ require 'rake'
13
+ require 'rake/testtask'
14
+
15
+ namespace :test do
16
+ Rake::TestTask.new(:all) do |test|
17
+ test.libs << 'lib' << 'test'
18
+ test.pattern = 'test/shields_up/**/*_test.rb'
19
+ test.verbose = true
20
+ end
21
+ end
22
+
23
+ task :default => 'test:all'
@@ -0,0 +1,4 @@
1
+ module ShieldsUp
2
+ class ParameterMissing < RuntimeError; end
3
+ class UnsupportedParameterType < RuntimeError; end
4
+ end
@@ -0,0 +1,158 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ module ShieldsUp
3
+ class Parameters
4
+ if defined?(ActionController) && defined?(ActionController::Parameters)
5
+ PARAM_TYPE = ActionController::Parameters
6
+ else
7
+ PARAM_TYPE = ActiveSupport::HashWithIndifferentAccess
8
+ end
9
+
10
+ PERMITTED_SCALAR_TYPES = [
11
+ String,
12
+ Symbol,
13
+ NilClass,
14
+ Numeric,
15
+ TrueClass,
16
+ FalseClass,
17
+ Date,
18
+ Time,
19
+ # DateTimes are Dates, we document the type but avoid the redundant check.
20
+ StringIO,
21
+ IO,
22
+ ]
23
+ if defined?(ActionDispatch) && defined?(ActionDispatch::Http) && defined?(ActionDispatch::Http::UploadedFile)
24
+ PERMITTED_SCALAR_TYPES << ActionDispatch::Http::UploadedFile
25
+ end
26
+ if defined?(Rack) && defined?(Rack::Test) && defined?(Rack::Test::UploadedFile)
27
+ PERMITTED_SCALAR_TYPES << Rack::Test::UploadedFile
28
+ end
29
+
30
+ def with_shields_down
31
+ saved = @controller.params
32
+ @controller.params = @original_params
33
+ yield
34
+ ensure
35
+ @controller.params = saved
36
+ end
37
+
38
+ def initialize(params, controller)
39
+ raise UnsupportedParameterType.new unless params.is_a? ActiveSupport::HashWithIndifferentAccess
40
+ @original_params = params
41
+ @controller = controller
42
+ @params = deep_dup_to_hash(params)
43
+ end
44
+
45
+ def to_s
46
+ @params.inspect
47
+ end
48
+
49
+ def permit(*permissions)
50
+ {}.tap do |permitted|
51
+ permissions.each do |permission|
52
+ permission, key = (permission.is_a?(Symbol) || permission.is_a?(String)) ? [permission, permission.to_s] : [permission.values.first, permission.keys.first.to_s]
53
+ if @params.has_key?(key)
54
+ if permission.is_a?(Symbol) || permission.is_a?(String)
55
+ result = permit_scalar(key)
56
+ permitted[key] = result if @params[key] == result
57
+ else
58
+ result = permit_nested(key, permission)
59
+ permitted[key] = result if result
60
+ end
61
+ end
62
+ end
63
+ end.symbolize_keys
64
+ end
65
+
66
+ def require(key)
67
+ self[key] or raise ParameterMissing.new("Required parameter #{key} does not exist in #{to_s}")
68
+ end
69
+
70
+ # def permit!
71
+ # deep_dup_to_hash(@params).symbolize_keys
72
+ # end
73
+
74
+ def [](key)
75
+ key = key.to_s
76
+ value = @params[key]
77
+ if value.is_a?(Hash)
78
+ self.class.new(@original_params[key], @controller)
79
+ elsif value.is_a?(Array)
80
+ [].tap do |array|
81
+ value.each_with_index do |element, i|
82
+ if permitted_scalar?(element)
83
+ array << element
84
+ elsif element.is_a? Hash
85
+ array << self.class.new(@original_params[key][i], @controller)
86
+ end
87
+ end
88
+ end
89
+ else
90
+ permit_scalar(key)
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def permit_scalar(key)
97
+ permitted_scalar?(@params[key]) ? @params[key] : nil
98
+ end
99
+
100
+ def permit_simple_hash(name, permissions)
101
+ if @params[name].is_a? Hash
102
+ self.class.new(@original_params[name], @controller).permit(*permissions)
103
+ else
104
+ permit_scalar(name)
105
+ end
106
+ end
107
+
108
+ def permit_nested_attributes_for(name, permissions)
109
+ {}.tap do |result|
110
+ @params[name].each do |key, value|
111
+ result[key] = self.class.new(@original_params[name][key], @controller).permit(*permissions) if value.is_a? Hash
112
+ result[key] = value if permitted_scalar?(value)
113
+ end
114
+ end
115
+ end
116
+
117
+ def permit_array_of_hashes(name, permissions)
118
+ @params[name].zip(@original_params[name]).select{|el| el[0].is_a? Hash}.collect{|el| self.class.new(el[1], @controller).permit(*permissions)}
119
+ end
120
+
121
+ def permit_scalars(name)
122
+ @params[name].select { |element| permitted_scalar? element }
123
+ end
124
+
125
+ def integer_key?(k)
126
+ k =~ /\A-?\d+\z/
127
+ end
128
+
129
+ def permit_nested(nested_name, permissions_for_nested)
130
+ if permissions_for_nested == [] # Declaration {:comment_ids => []}.
131
+ @params[nested_name].is_a?(Array) ? permit_scalars(nested_name) : nil
132
+ elsif @params[nested_name].is_a? Array # Declaration {:user => :name} or {:user => [:name, :age, {:adress => ...}]}.
133
+ permit_array_of_hashes(nested_name, permissions_for_nested)
134
+ elsif @params[nested_name].is_a?(Hash) && @params[nested_name].keys.all? { |k| integer_key?(k) } #{ '1' => {'title' => 'First Chapter'}, '2' => {'title' => 'Second Chapter'}}
135
+ permit_nested_attributes_for(nested_name, permissions_for_nested)
136
+ else
137
+ permit_simple_hash(nested_name, permissions_for_nested)
138
+ end
139
+ end
140
+
141
+ def permitted_scalar?(value)
142
+ PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)}
143
+ end
144
+
145
+ def deep_dup_to_hash(params)
146
+ return dup_if_possible(params) unless params.is_a?(PARAM_TYPE)
147
+ {}.tap do |dup|
148
+ params.each do |key, value|
149
+ dup[key] = deep_dup_to_hash(value)
150
+ end
151
+ end
152
+ end
153
+
154
+ def dup_if_possible(v)
155
+ v.duplicable? ? v.dup : v
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,10 @@
1
+ module ShieldsUp
2
+ def self.included(klass)
3
+ klass.before_filter do
4
+ self.params = ShieldsUp::Parameters.new(params, self) unless params.is_a?(ShieldsUp::Parameters)
5
+ end
6
+ klass.rescue_from(ShieldsUp::ParameterMissing) do |parameter_missing_exception|
7
+ render :text => "Required parameter missing: #{parameter_missing_exception}", :status => :bad_request
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module ShieldsUp
2
+ VERSION = '0.17.0'.freeze
3
+ end
data/lib/shields_up.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'shields_up/shields_up'
2
+ require 'shields_up/exceptions'
3
+ require 'shields_up/parameters'
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shields_up/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'shields_up'
8
+ s.version = ShieldsUp::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ['AppFolio']
11
+ s.email = 'dev@appfolio.com'
12
+ s.description = 'Mass assignment Protection made by AppFolio Inc., inspired by strong_parameters.'
13
+ s.summary = s.description
14
+ s.homepage = 'http://github.com/appfolio/shields_up'
15
+ s.licenses = ['MIT']
16
+
17
+ s.files = Dir['**/*'].reject{ |f| f[%r{^pkg/}] || f[%r{^test/}] }
18
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ s.require_paths = ['lib']
20
+ s.add_dependency('activesupport', '~> 3.2')
21
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shields_up
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.17.0
5
+ platform: ruby
6
+ authors:
7
+ - AppFolio
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ description: Mass assignment Protection made by AppFolio Inc., inspired by strong_parameters.
28
+ email: dev@appfolio.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - Gemfile
34
+ - Gemfile.lock
35
+ - LICENSE
36
+ - README.md
37
+ - Rakefile
38
+ - lib/shields_up.rb
39
+ - lib/shields_up/exceptions.rb
40
+ - lib/shields_up/parameters.rb
41
+ - lib/shields_up/shields_up.rb
42
+ - lib/shields_up/version.rb
43
+ - shields_up.gemspec
44
+ homepage: http://github.com/appfolio/shields_up
45
+ licenses:
46
+ - MIT
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.2.2
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Mass assignment Protection made by AppFolio Inc., inspired by strong_parameters.
68
+ test_files: []