├── .circleci
└── config.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── Appraisals
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── gemfiles
├── .bundle
│ └── config
├── rails_4_2.gemfile
├── rails_4_2.gemfile.lock
├── rails_5_0.gemfile
├── rails_5_0.gemfile.lock
├── rails_5_1.gemfile
├── rails_5_1.gemfile.lock
├── rails_5_2.gemfile
├── rails_5_2.gemfile.lock
├── rails_6_0.gemfile
└── rails_6_0.gemfile.lock
├── lib
├── generators
│ ├── moderate_parameters
│ │ └── install_generator.rb
│ └── templates
│ │ └── moderate_parameters.rb
├── moderate_parameters.rb
└── moderate_parameters
│ ├── breadcrumbs.rb
│ ├── logger.rb
│ ├── parameters.rb
│ └── version.rb
├── log
└── .gitkeep
├── moderate_parameters.gemspec
└── spec
├── moderate_parameters_spec.rb
└── spec_helper.rb
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Ruby CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4 | #
5 | version: 2
6 | jobs:
7 | build:
8 | docker:
9 | # specify the version you desire here
10 | - image: circleci/ruby:2.6.1
11 | environment:
12 | BUNDLE_JOBS: 3
13 | BUNDLE_RETRY: 3
14 | BUNDLE_PATH: vendor/bundle
15 | RAILS_ENV: test
16 |
17 | # Specify service dependencies here if necessary
18 | # CircleCI maintains a library of pre-built images
19 | # documented at https://circleci.com/docs/2.0/circleci-images/
20 | # - image: circleci/postgres:9.4
21 |
22 | working_directory: ~/repo
23 |
24 | steps:
25 | - checkout
26 | # Download and cache dependencies
27 | - restore_cache:
28 | keys:
29 | - v1-dependencies-{{ checksum "Gemfile.lock" }}
30 | # fallback to using the latest cache if no exact match is found
31 | - v1-dependencies-
32 |
33 | - run:
34 | name: Configure Bundler
35 | command: |
36 | echo 'export BUNDLER_VERSION=$(cat Gemfile.lock | tail -1 | tr -d " ")' >> $BASH_ENV
37 | source $BASH_ENV
38 | gem install bundler
39 |
40 | - run:
41 | name: install dependencies
42 | command: |
43 | bundle install --jobs=4 --retry=3 --path vendor/bundle
44 |
45 | - save_cache:
46 | paths:
47 | - ./vendor/bundle
48 | key: v1-dependencies-{{ checksum "Gemfile.lock" }}
49 |
50 | # run tests!
51 | - run:
52 | name: run tests
53 | command: |
54 | mkdir /tmp/test-results
55 | TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
56 | circleci tests split --split-by=timings)"
57 |
58 | bundle exec rspec \
59 | --format progress \
60 | --format RspecJunitFormatter \
61 | --out /tmp/test-results/rspec.xml \
62 | --format progress \
63 | $TEST_FILES
64 |
65 | # collect reports
66 | - store_test_results:
67 | path: /tmp/test-results
68 | - store_artifacts:
69 | path: /tmp/test-results
70 | destination: test-results
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /coverage/
5 | /doc/
6 | /pkg/
7 | /spec/reports/
8 | /tmp/
9 |
10 | *.gem
11 | # rspec failure tracking
12 | .rspec_status
13 | *.log
14 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | Metrics/BlockLength:
2 | Exclude:
3 | - 'spec/**/*'
4 | - 'moderate_parameters.gemspec'
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise 'rails-4-2' do
2 | gem 'actionpack', '4.2.11.1'
3 | gem 'activemodel', '4.2.11.1'
4 | gem 'activesupport', '4.2.11.1'
5 | gem 'railties', '4.2.11.1'
6 | end
7 |
8 | appraise 'rails-5-0' do
9 | gem 'actionpack', '5.0.7.2'
10 | gem 'activemodel', '5.0.7.2'
11 | gem 'activesupport', '5.0.7.2'
12 | gem 'railties', '5.0.7.2'
13 | end
14 |
15 | appraise 'rails-5-1' do
16 | gem 'actionpack', '5.1.7'
17 | gem 'activemodel', '5.1.7'
18 | gem 'activesupport', '5.1.7'
19 | gem 'railties', '5.1.7'
20 | end
21 |
22 | appraise 'rails-5-2' do
23 | gem 'actionpack', '5.2.3'
24 | gem 'activemodel', '5.2.3'
25 | gem 'activesupport', '5.2.3'
26 | gem 'railties', '5.2.3'
27 | end
28 |
29 | appraise 'rails-6-0' do
30 | gem 'actionpack', '6.0.0'
31 | gem 'activemodel', '6.0.0'
32 | gem 'activesupport', '6.0.0'
33 | gem 'railties', '6.0.0'
34 | end
35 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 0.2.1 - 2019-08-25
2 |
3 | * Bugfix deprecation warning with already defined constant
4 |
5 | ### 0.2.0 - 2019-08-25
6 |
7 | * Add Breadcrumbs functionality for mutation logging
8 | * Add generator for Breadcrumbs initializer config template
9 |
10 | ### 0.1.0 - 2019-03-25
11 |
12 | * Init Project
13 | * moderate_parameters.log
14 | * Extend ActionController::Parameters functionality
15 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at mike@hint.io. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4 |
5 | # Specify your gem's dependencies in moderate_parameters.gemspec
6 | gemspec
7 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | moderate_parameters (0.4.1)
5 | actionpack (>= 4.2, < 6.2)
6 | activemodel (>= 4.2, < 6.2)
7 | activesupport (>= 4.2, < 6.2)
8 | nokogiri (>= 1.13.9)
9 | railties (>= 4.2, < 6.2)
10 |
11 | GEM
12 | remote: https://rubygems.org/
13 | specs:
14 | actionpack (6.1.7)
15 | actionview (= 6.1.7)
16 | activesupport (= 6.1.7)
17 | rack (~> 2.0, >= 2.0.9)
18 | rack-test (>= 0.6.3)
19 | rails-dom-testing (~> 2.0)
20 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
21 | actionview (6.1.7)
22 | activesupport (= 6.1.7)
23 | builder (~> 3.1)
24 | erubi (~> 1.4)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
27 | activemodel (6.1.7)
28 | activesupport (= 6.1.7)
29 | activesupport (6.1.7)
30 | concurrent-ruby (~> 1.0, >= 1.0.2)
31 | i18n (>= 1.6, < 2)
32 | minitest (>= 5.1)
33 | tzinfo (~> 2.0)
34 | zeitwerk (~> 2.3)
35 | appraisal (2.2.0)
36 | bundler
37 | rake
38 | thor (>= 0.14.0)
39 | builder (3.2.4)
40 | coderay (1.1.2)
41 | concurrent-ruby (1.1.10)
42 | crass (1.0.6)
43 | diff-lcs (1.3)
44 | erubi (1.11.0)
45 | i18n (1.12.0)
46 | concurrent-ruby (~> 1.0)
47 | loofah (2.19.0)
48 | crass (~> 1.0.2)
49 | nokogiri (>= 1.5.9)
50 | method_source (0.9.2)
51 | mini_portile2 (2.8.0)
52 | minitest (5.16.3)
53 | nokogiri (1.13.9)
54 | mini_portile2 (~> 2.8.0)
55 | racc (~> 1.4)
56 | pry (0.12.2)
57 | coderay (~> 1.1.0)
58 | method_source (~> 0.9.0)
59 | racc (1.6.0)
60 | rack (2.2.4)
61 | rack-test (2.0.2)
62 | rack (>= 1.3)
63 | rails-dom-testing (2.0.3)
64 | activesupport (>= 4.2.0)
65 | nokogiri (>= 1.6)
66 | rails-html-sanitizer (1.4.3)
67 | loofah (~> 2.3)
68 | railties (6.1.7)
69 | actionpack (= 6.1.7)
70 | activesupport (= 6.1.7)
71 | method_source
72 | rake (>= 12.2)
73 | thor (~> 1.0)
74 | rake (13.0.1)
75 | rspec (3.9.0)
76 | rspec-core (~> 3.9.0)
77 | rspec-expectations (~> 3.9.0)
78 | rspec-mocks (~> 3.9.0)
79 | rspec-core (3.9.1)
80 | rspec-support (~> 3.9.1)
81 | rspec-expectations (3.9.0)
82 | diff-lcs (>= 1.2.0, < 2.0)
83 | rspec-support (~> 3.9.0)
84 | rspec-mocks (3.9.1)
85 | diff-lcs (>= 1.2.0, < 2.0)
86 | rspec-support (~> 3.9.0)
87 | rspec-support (3.9.2)
88 | rspec_junit_formatter (0.4.1)
89 | rspec-core (>= 2, < 4, != 2.12.0)
90 | thor (1.0.1)
91 | tzinfo (2.0.5)
92 | concurrent-ruby (~> 1.0)
93 | zeitwerk (2.6.6)
94 |
95 | PLATFORMS
96 | ruby
97 |
98 | DEPENDENCIES
99 | appraisal (= 2.2.0)
100 | bundler (~> 2.0)
101 | moderate_parameters!
102 | pry (~> 0.12.2)
103 | rake (~> 13.0)
104 | rspec (~> 3.0)
105 | rspec_junit_formatter (= 0.4.1)
106 |
107 | BUNDLED WITH
108 | 2.1.4
109 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2019 Hint Media Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | By [Hint.io](https://hint.io)
7 |
8 | [](https://badge.fury.io/rb/moderate_parameters)   [](https://codeclimate.com/github/hintmedia/moderate_parameters/maintainability)
9 |
10 | In our experience with [UpgradeRails](https://www.upgraderails.com), the migration from [protected_attributes](https://github.com/rails/protected_attributes) to [strong_parameters](https://api.rubyonrails.org/classes/ActionController/StrongParameters.html) can leave more questions than answers. It can be difficult to determine what data is originating from within the app and what is coming from the internet.
11 |
12 | Moderate Parameters is a set of tools providing logging of data sources in the controller by extending `ActionController::Parameters` functionality.
13 |
14 | ## Installation
15 |
16 | Add this line to your application's Gemfile:
17 |
18 | ```ruby
19 | gem 'moderate_parameters'
20 | ```
21 |
22 | And then execute:
23 |
24 | $ bundle
25 |
26 | Or install it yourself as:
27 |
28 | $ gem install moderate_parameters
29 |
30 | Then add the initializer by running:
31 |
32 | $ bundle exec rails g moderate_parameters:install
33 |
34 | This will add an initializer to your rails app for turning on/off functionality.
35 |
36 | ## Usage
37 |
38 | Given a form at `/people/new` that submits data to the `PeopleController#create` action like so:
39 |
40 | ```ruby
41 | { person: { name: 'Kyle', age: '26', height: '180' } }
42 | ```
43 |
44 | With a model that looks like:
45 |
46 | ```ruby
47 | class Person < ActiveRecord::Base
48 | attr_accessible :name, :age, :height
49 |
50 | . . .
51 |
52 | end
53 | ```
54 |
55 | And a controller looks like this:
56 |
57 | ```ruby
58 | class PeopleController < ActionController::Base
59 | def create
60 | Person.create(params[:person])
61 | end
62 |
63 | . . .
64 |
65 | end
66 | ```
67 |
68 | We can add `moderate_parameters` by following the `strong_parameters` implementation method with a couple slight changes.
69 |
70 | Add a private params method for the controller calling `moderate` (with `controller_name` and `action_name` as the first two args) instead of `permit`:
71 |
72 | ```ruby
73 | class PeopleController < ActionController::Base
74 | def create
75 | Person.create(person_params) # Was Person.create(params[:person])
76 | end
77 |
78 | . . .
79 |
80 | private
81 |
82 | def person_params
83 | params.require(:person).moderate(controller_name, action_name, :name)
84 | end
85 | end
86 | ```
87 |
88 | This will cause the `person_params` to flow the same way they did before (getting passed to the model without interruption),
89 | but the params that are not included in the argument of `moderate` will be logged to `/log/moderate_params.log`
90 |
91 | Meaning that, after submitting the aforementioned data, our `moderate_parameters.log` will look like so:
92 |
93 | people#create Top Level is missing: age
94 | people#create Top Level is missing: height
95 |
96 | We can fix this by adding `age` and `height` to `person_params` like so:
97 |
98 | ```ruby
99 | class PeopleController < ActionController::Base
100 | def create
101 | Person.create(person_params)
102 | end
103 |
104 | . . .
105 |
106 | private
107 |
108 | def person_params
109 | params.require(:person).moderate(controller_name, action_name, :name, :age, :height)
110 | end
111 | end
112 | ```
113 |
114 | We can then hit submit data from the form at `/people/new` and see that no new lines are added to the `moderate_parameters.log` file.
115 |
116 | This means that we can remove `moderate_parameters` and move to using `permit` as the final migration step of `strong_parameters`:
117 |
118 | ```ruby
119 | class PeopleController < ActionController::Base
120 | def create
121 | Person.create(person_params)
122 | end
123 |
124 | . . .
125 |
126 | private
127 |
128 | def person_params
129 | params.require(:person).permit(:name, :age, :height)
130 | end
131 | end
132 | ```
133 |
134 | It is only _**AFTER**_ this final step of the `strong_parameters` migration has been completed that you can safely remove the `protected_attributes` line in the model:
135 |
136 | ```ruby
137 | class Person < ActiveRecord::Base
138 | # attr_accessible :name, :age, :height
139 |
140 | . . .
141 |
142 | end
143 | ```
144 |
145 | ## Contributing
146 |
147 | Bug reports and pull requests are welcome on GitHub at https://github.com/hintmedia/moderate_parameters. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
148 |
149 | ## License
150 |
151 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
152 |
153 | ## Code of Conduct
154 |
155 | Everyone interacting in the moderate_parameters project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hintmedia/moderate_parameters/blob/master/CODE_OF_CONDUCT.md).
156 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rspec/core/rake_task"
3 |
4 | RSpec::Core::RakeTask.new(:spec)
5 |
6 | task :default => :spec
7 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/setup"
4 | require "moderate_parameters"
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 |
13 | require "irb"
14 | IRB.start(__FILE__)
15 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/gemfiles/.bundle/config:
--------------------------------------------------------------------------------
1 | ---
2 | BUNDLE_RETRY: "1"
3 |
--------------------------------------------------------------------------------
/gemfiles/rails_4_2.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "actionpack", "4.2.11.1"
6 | gem "activemodel", "4.2.11.1"
7 | gem "activesupport", "4.2.11.1"
8 | gem "railties", "4.2.11.1"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_4_2.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | moderate_parameters (0.2.1)
5 | actionpack (>= 4.2, < 6.1)
6 | activemodel (>= 4.2, < 6.1)
7 | activesupport (>= 4.2, < 6.1)
8 | railties (>= 4.2, < 6.1)
9 |
10 | GEM
11 | remote: https://rubygems.org/
12 | specs:
13 | actionpack (4.2.11.1)
14 | actionview (= 4.2.11.1)
15 | activesupport (= 4.2.11.1)
16 | rack (~> 1.6)
17 | rack-test (~> 0.6.2)
18 | rails-dom-testing (~> 1.0, >= 1.0.5)
19 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
20 | actionview (4.2.11.1)
21 | activesupport (= 4.2.11.1)
22 | builder (~> 3.1)
23 | erubis (~> 2.7.0)
24 | rails-dom-testing (~> 1.0, >= 1.0.5)
25 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
26 | activemodel (4.2.11.1)
27 | activesupport (= 4.2.11.1)
28 | builder (~> 3.1)
29 | activesupport (4.2.11.1)
30 | i18n (~> 0.7)
31 | minitest (~> 5.1)
32 | thread_safe (~> 0.3, >= 0.3.4)
33 | tzinfo (~> 1.1)
34 | appraisal (2.2.0)
35 | bundler
36 | rake
37 | thor (>= 0.14.0)
38 | builder (3.2.3)
39 | coderay (1.1.2)
40 | concurrent-ruby (1.1.5)
41 | crass (1.0.5)
42 | diff-lcs (1.3)
43 | erubis (2.7.0)
44 | i18n (0.9.5)
45 | concurrent-ruby (~> 1.0)
46 | loofah (2.3.1)
47 | crass (~> 1.0.2)
48 | nokogiri (>= 1.5.9)
49 | method_source (0.9.2)
50 | mini_portile2 (2.4.0)
51 | minitest (5.12.2)
52 | nokogiri (1.10.4)
53 | mini_portile2 (~> 2.4.0)
54 | pry (0.12.2)
55 | coderay (~> 1.1.0)
56 | method_source (~> 0.9.0)
57 | rack (1.6.11)
58 | rack-test (0.6.3)
59 | rack (>= 1.0)
60 | rails-deprecated_sanitizer (1.0.3)
61 | activesupport (>= 4.2.0.alpha)
62 | rails-dom-testing (1.0.9)
63 | activesupport (>= 4.2.0, < 5.0)
64 | nokogiri (~> 1.6)
65 | rails-deprecated_sanitizer (>= 1.0.1)
66 | rails-html-sanitizer (1.3.0)
67 | loofah (~> 2.3)
68 | railties (4.2.11.1)
69 | actionpack (= 4.2.11.1)
70 | activesupport (= 4.2.11.1)
71 | rake (>= 0.8.7)
72 | thor (>= 0.18.1, < 2.0)
73 | rake (10.5.0)
74 | rspec (3.9.0)
75 | rspec-core (~> 3.9.0)
76 | rspec-expectations (~> 3.9.0)
77 | rspec-mocks (~> 3.9.0)
78 | rspec-core (3.9.0)
79 | rspec-support (~> 3.9.0)
80 | rspec-expectations (3.9.0)
81 | diff-lcs (>= 1.2.0, < 2.0)
82 | rspec-support (~> 3.9.0)
83 | rspec-mocks (3.9.0)
84 | diff-lcs (>= 1.2.0, < 2.0)
85 | rspec-support (~> 3.9.0)
86 | rspec-support (3.9.0)
87 | rspec_junit_formatter (0.4.1)
88 | rspec-core (>= 2, < 4, != 2.12.0)
89 | thor (0.20.3)
90 | thread_safe (0.3.6)
91 | tzinfo (1.2.5)
92 | thread_safe (~> 0.1)
93 |
94 | PLATFORMS
95 | ruby
96 |
97 | DEPENDENCIES
98 | actionpack (= 4.2.11.1)
99 | activemodel (= 4.2.11.1)
100 | activesupport (= 4.2.11.1)
101 | appraisal (= 2.2.0)
102 | bundler (~> 2.0.1)
103 | moderate_parameters!
104 | pry (~> 0.12.2)
105 | railties (= 4.2.11.1)
106 | rake (~> 10.0)
107 | rspec (~> 3.0)
108 | rspec_junit_formatter (= 0.4.1)
109 |
110 | BUNDLED WITH
111 | 2.0.1
112 |
--------------------------------------------------------------------------------
/gemfiles/rails_5_0.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "actionpack", "5.0.7.2"
6 | gem "activemodel", "5.0.7.2"
7 | gem "activesupport", "5.0.7.2"
8 | gem "railties", "5.0.7.2"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_5_0.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | moderate_parameters (0.2.1)
5 | actionpack (>= 3.0, < 6.1)
6 | activemodel (>= 3.0, < 6.1)
7 | activesupport (>= 3.0, < 6.1)
8 | railties (>= 3.0, < 6.1)
9 |
10 | GEM
11 | remote: https://rubygems.org/
12 | specs:
13 | actionpack (5.0.7.2)
14 | actionview (= 5.0.7.2)
15 | activesupport (= 5.0.7.2)
16 | rack (~> 2.0)
17 | rack-test (~> 0.6.3)
18 | rails-dom-testing (~> 2.0)
19 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
20 | actionview (5.0.7.2)
21 | activesupport (= 5.0.7.2)
22 | builder (~> 3.1)
23 | erubis (~> 2.7.0)
24 | rails-dom-testing (~> 2.0)
25 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
26 | activemodel (5.0.7.2)
27 | activesupport (= 5.0.7.2)
28 | activesupport (5.0.7.2)
29 | concurrent-ruby (~> 1.0, >= 1.0.2)
30 | i18n (>= 0.7, < 2)
31 | minitest (~> 5.1)
32 | tzinfo (~> 1.1)
33 | appraisal (2.2.0)
34 | bundler
35 | rake
36 | thor (>= 0.14.0)
37 | builder (3.2.3)
38 | coderay (1.1.2)
39 | concurrent-ruby (1.1.5)
40 | crass (1.0.5)
41 | diff-lcs (1.3)
42 | erubis (2.7.0)
43 | i18n (1.7.0)
44 | concurrent-ruby (~> 1.0)
45 | loofah (2.3.1)
46 | crass (~> 1.0.2)
47 | nokogiri (>= 1.5.9)
48 | method_source (0.9.2)
49 | mini_portile2 (2.4.0)
50 | minitest (5.12.2)
51 | nokogiri (1.10.4)
52 | mini_portile2 (~> 2.4.0)
53 | pry (0.12.2)
54 | coderay (~> 1.1.0)
55 | method_source (~> 0.9.0)
56 | rack (2.0.7)
57 | rack-test (0.6.3)
58 | rack (>= 1.0)
59 | rails-dom-testing (2.0.3)
60 | activesupport (>= 4.2.0)
61 | nokogiri (>= 1.6)
62 | rails-html-sanitizer (1.3.0)
63 | loofah (~> 2.3)
64 | railties (5.0.7.2)
65 | actionpack (= 5.0.7.2)
66 | activesupport (= 5.0.7.2)
67 | method_source
68 | rake (>= 0.8.7)
69 | thor (>= 0.18.1, < 2.0)
70 | rake (10.5.0)
71 | rspec (3.9.0)
72 | rspec-core (~> 3.9.0)
73 | rspec-expectations (~> 3.9.0)
74 | rspec-mocks (~> 3.9.0)
75 | rspec-core (3.9.0)
76 | rspec-support (~> 3.9.0)
77 | rspec-expectations (3.9.0)
78 | diff-lcs (>= 1.2.0, < 2.0)
79 | rspec-support (~> 3.9.0)
80 | rspec-mocks (3.9.0)
81 | diff-lcs (>= 1.2.0, < 2.0)
82 | rspec-support (~> 3.9.0)
83 | rspec-support (3.9.0)
84 | rspec_junit_formatter (0.4.1)
85 | rspec-core (>= 2, < 4, != 2.12.0)
86 | thor (0.20.3)
87 | thread_safe (0.3.6)
88 | tzinfo (1.2.5)
89 | thread_safe (~> 0.1)
90 |
91 | PLATFORMS
92 | ruby
93 |
94 | DEPENDENCIES
95 | actionpack (= 5.0.7.2)
96 | activemodel (= 5.0.7.2)
97 | activesupport (= 5.0.7.2)
98 | appraisal (= 2.2.0)
99 | bundler (~> 2.0.1)
100 | moderate_parameters!
101 | pry (~> 0.12.2)
102 | railties (= 5.0.7.2)
103 | rake (~> 10.0)
104 | rspec (~> 3.0)
105 | rspec_junit_formatter (= 0.4.1)
106 |
107 | BUNDLED WITH
108 | 2.0.1
109 |
--------------------------------------------------------------------------------
/gemfiles/rails_5_1.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "actionpack", "5.1.7"
6 | gem "activemodel", "5.1.7"
7 | gem "activesupport", "5.1.7"
8 | gem "railties", "5.1.7"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_5_1.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | moderate_parameters (0.2.1)
5 | actionpack (>= 3.0, < 6.1)
6 | activemodel (>= 3.0, < 6.1)
7 | activesupport (>= 3.0, < 6.1)
8 | railties (>= 3.0, < 6.1)
9 |
10 | GEM
11 | remote: https://rubygems.org/
12 | specs:
13 | actionpack (5.1.7)
14 | actionview (= 5.1.7)
15 | activesupport (= 5.1.7)
16 | rack (~> 2.0)
17 | rack-test (>= 0.6.3)
18 | rails-dom-testing (~> 2.0)
19 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
20 | actionview (5.1.7)
21 | activesupport (= 5.1.7)
22 | builder (~> 3.1)
23 | erubi (~> 1.4)
24 | rails-dom-testing (~> 2.0)
25 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
26 | activemodel (5.1.7)
27 | activesupport (= 5.1.7)
28 | activesupport (5.1.7)
29 | concurrent-ruby (~> 1.0, >= 1.0.2)
30 | i18n (>= 0.7, < 2)
31 | minitest (~> 5.1)
32 | tzinfo (~> 1.1)
33 | appraisal (2.2.0)
34 | bundler
35 | rake
36 | thor (>= 0.14.0)
37 | builder (3.2.3)
38 | coderay (1.1.2)
39 | concurrent-ruby (1.1.5)
40 | crass (1.0.5)
41 | diff-lcs (1.3)
42 | erubi (1.9.0)
43 | i18n (1.7.0)
44 | concurrent-ruby (~> 1.0)
45 | loofah (2.3.1)
46 | crass (~> 1.0.2)
47 | nokogiri (>= 1.5.9)
48 | method_source (0.9.2)
49 | mini_portile2 (2.4.0)
50 | minitest (5.12.2)
51 | nokogiri (1.10.4)
52 | mini_portile2 (~> 2.4.0)
53 | pry (0.12.2)
54 | coderay (~> 1.1.0)
55 | method_source (~> 0.9.0)
56 | rack (2.0.7)
57 | rack-test (1.1.0)
58 | rack (>= 1.0, < 3)
59 | rails-dom-testing (2.0.3)
60 | activesupport (>= 4.2.0)
61 | nokogiri (>= 1.6)
62 | rails-html-sanitizer (1.3.0)
63 | loofah (~> 2.3)
64 | railties (5.1.7)
65 | actionpack (= 5.1.7)
66 | activesupport (= 5.1.7)
67 | method_source
68 | rake (>= 0.8.7)
69 | thor (>= 0.18.1, < 2.0)
70 | rake (10.5.0)
71 | rspec (3.9.0)
72 | rspec-core (~> 3.9.0)
73 | rspec-expectations (~> 3.9.0)
74 | rspec-mocks (~> 3.9.0)
75 | rspec-core (3.9.0)
76 | rspec-support (~> 3.9.0)
77 | rspec-expectations (3.9.0)
78 | diff-lcs (>= 1.2.0, < 2.0)
79 | rspec-support (~> 3.9.0)
80 | rspec-mocks (3.9.0)
81 | diff-lcs (>= 1.2.0, < 2.0)
82 | rspec-support (~> 3.9.0)
83 | rspec-support (3.9.0)
84 | rspec_junit_formatter (0.4.1)
85 | rspec-core (>= 2, < 4, != 2.12.0)
86 | thor (0.20.3)
87 | thread_safe (0.3.6)
88 | tzinfo (1.2.5)
89 | thread_safe (~> 0.1)
90 |
91 | PLATFORMS
92 | ruby
93 |
94 | DEPENDENCIES
95 | actionpack (= 5.1.7)
96 | activemodel (= 5.1.7)
97 | activesupport (= 5.1.7)
98 | appraisal (= 2.2.0)
99 | bundler (~> 2.0.1)
100 | moderate_parameters!
101 | pry (~> 0.12.2)
102 | railties (= 5.1.7)
103 | rake (~> 10.0)
104 | rspec (~> 3.0)
105 | rspec_junit_formatter (= 0.4.1)
106 |
107 | BUNDLED WITH
108 | 2.0.1
109 |
--------------------------------------------------------------------------------
/gemfiles/rails_5_2.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "actionpack", "5.2.3"
6 | gem "activemodel", "5.2.3"
7 | gem "activesupport", "5.2.3"
8 | gem "railties", "5.2.3"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_5_2.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | moderate_parameters (0.2.1)
5 | actionpack (>= 3.0, < 6.1)
6 | activemodel (>= 3.0, < 6.1)
7 | activesupport (>= 3.0, < 6.1)
8 | railties (>= 3.0, < 6.1)
9 |
10 | GEM
11 | remote: https://rubygems.org/
12 | specs:
13 | actionpack (5.2.3)
14 | actionview (= 5.2.3)
15 | activesupport (= 5.2.3)
16 | rack (~> 2.0)
17 | rack-test (>= 0.6.3)
18 | rails-dom-testing (~> 2.0)
19 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
20 | actionview (5.2.3)
21 | activesupport (= 5.2.3)
22 | builder (~> 3.1)
23 | erubi (~> 1.4)
24 | rails-dom-testing (~> 2.0)
25 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
26 | activemodel (5.2.3)
27 | activesupport (= 5.2.3)
28 | activesupport (5.2.3)
29 | concurrent-ruby (~> 1.0, >= 1.0.2)
30 | i18n (>= 0.7, < 2)
31 | minitest (~> 5.1)
32 | tzinfo (~> 1.1)
33 | appraisal (2.2.0)
34 | bundler
35 | rake
36 | thor (>= 0.14.0)
37 | builder (3.2.3)
38 | coderay (1.1.2)
39 | concurrent-ruby (1.1.5)
40 | crass (1.0.5)
41 | diff-lcs (1.3)
42 | erubi (1.9.0)
43 | i18n (1.7.0)
44 | concurrent-ruby (~> 1.0)
45 | loofah (2.3.1)
46 | crass (~> 1.0.2)
47 | nokogiri (>= 1.5.9)
48 | method_source (0.9.2)
49 | mini_portile2 (2.4.0)
50 | minitest (5.12.2)
51 | nokogiri (1.10.4)
52 | mini_portile2 (~> 2.4.0)
53 | pry (0.12.2)
54 | coderay (~> 1.1.0)
55 | method_source (~> 0.9.0)
56 | rack (2.0.7)
57 | rack-test (1.1.0)
58 | rack (>= 1.0, < 3)
59 | rails-dom-testing (2.0.3)
60 | activesupport (>= 4.2.0)
61 | nokogiri (>= 1.6)
62 | rails-html-sanitizer (1.3.0)
63 | loofah (~> 2.3)
64 | railties (5.2.3)
65 | actionpack (= 5.2.3)
66 | activesupport (= 5.2.3)
67 | method_source
68 | rake (>= 0.8.7)
69 | thor (>= 0.19.0, < 2.0)
70 | rake (10.5.0)
71 | rspec (3.9.0)
72 | rspec-core (~> 3.9.0)
73 | rspec-expectations (~> 3.9.0)
74 | rspec-mocks (~> 3.9.0)
75 | rspec-core (3.9.0)
76 | rspec-support (~> 3.9.0)
77 | rspec-expectations (3.9.0)
78 | diff-lcs (>= 1.2.0, < 2.0)
79 | rspec-support (~> 3.9.0)
80 | rspec-mocks (3.9.0)
81 | diff-lcs (>= 1.2.0, < 2.0)
82 | rspec-support (~> 3.9.0)
83 | rspec-support (3.9.0)
84 | rspec_junit_formatter (0.4.1)
85 | rspec-core (>= 2, < 4, != 2.12.0)
86 | thor (0.20.3)
87 | thread_safe (0.3.6)
88 | tzinfo (1.2.5)
89 | thread_safe (~> 0.1)
90 |
91 | PLATFORMS
92 | ruby
93 |
94 | DEPENDENCIES
95 | actionpack (= 5.2.3)
96 | activemodel (= 5.2.3)
97 | activesupport (= 5.2.3)
98 | appraisal (= 2.2.0)
99 | bundler (~> 2.0.1)
100 | moderate_parameters!
101 | pry (~> 0.12.2)
102 | railties (= 5.2.3)
103 | rake (~> 10.0)
104 | rspec (~> 3.0)
105 | rspec_junit_formatter (= 0.4.1)
106 |
107 | BUNDLED WITH
108 | 2.0.1
109 |
--------------------------------------------------------------------------------
/gemfiles/rails_6_0.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "actionpack", "6.0.0"
6 | gem "activemodel", "6.0.0"
7 | gem "activesupport", "6.0.0"
8 | gem "railties", "6.0.0"
9 |
10 | gemspec path: "../"
11 |
--------------------------------------------------------------------------------
/gemfiles/rails_6_0.gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | moderate_parameters (0.2.1)
5 | actionpack (>= 3.0, < 6.1)
6 | activemodel (>= 3.0, < 6.1)
7 | activesupport (>= 3.0, < 6.1)
8 | railties (>= 3.0, < 6.1)
9 |
10 | GEM
11 | remote: https://rubygems.org/
12 | specs:
13 | actionpack (6.0.0)
14 | actionview (= 6.0.0)
15 | activesupport (= 6.0.0)
16 | rack (~> 2.0)
17 | rack-test (>= 0.6.3)
18 | rails-dom-testing (~> 2.0)
19 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
20 | actionview (6.0.0)
21 | activesupport (= 6.0.0)
22 | builder (~> 3.1)
23 | erubi (~> 1.4)
24 | rails-dom-testing (~> 2.0)
25 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
26 | activemodel (6.0.0)
27 | activesupport (= 6.0.0)
28 | activesupport (6.0.0)
29 | concurrent-ruby (~> 1.0, >= 1.0.2)
30 | i18n (>= 0.7, < 2)
31 | minitest (~> 5.1)
32 | tzinfo (~> 1.1)
33 | zeitwerk (~> 2.1, >= 2.1.8)
34 | appraisal (2.2.0)
35 | bundler
36 | rake
37 | thor (>= 0.14.0)
38 | builder (3.2.3)
39 | coderay (1.1.2)
40 | concurrent-ruby (1.1.5)
41 | crass (1.0.5)
42 | diff-lcs (1.3)
43 | erubi (1.9.0)
44 | i18n (1.7.0)
45 | concurrent-ruby (~> 1.0)
46 | loofah (2.3.1)
47 | crass (~> 1.0.2)
48 | nokogiri (>= 1.5.9)
49 | method_source (0.9.2)
50 | mini_portile2 (2.4.0)
51 | minitest (5.12.2)
52 | nokogiri (1.10.4)
53 | mini_portile2 (~> 2.4.0)
54 | pry (0.12.2)
55 | coderay (~> 1.1.0)
56 | method_source (~> 0.9.0)
57 | rack (2.0.7)
58 | rack-test (1.1.0)
59 | rack (>= 1.0, < 3)
60 | rails-dom-testing (2.0.3)
61 | activesupport (>= 4.2.0)
62 | nokogiri (>= 1.6)
63 | rails-html-sanitizer (1.3.0)
64 | loofah (~> 2.3)
65 | railties (6.0.0)
66 | actionpack (= 6.0.0)
67 | activesupport (= 6.0.0)
68 | method_source
69 | rake (>= 0.8.7)
70 | thor (>= 0.20.3, < 2.0)
71 | rake (10.5.0)
72 | rspec (3.9.0)
73 | rspec-core (~> 3.9.0)
74 | rspec-expectations (~> 3.9.0)
75 | rspec-mocks (~> 3.9.0)
76 | rspec-core (3.9.0)
77 | rspec-support (~> 3.9.0)
78 | rspec-expectations (3.9.0)
79 | diff-lcs (>= 1.2.0, < 2.0)
80 | rspec-support (~> 3.9.0)
81 | rspec-mocks (3.9.0)
82 | diff-lcs (>= 1.2.0, < 2.0)
83 | rspec-support (~> 3.9.0)
84 | rspec-support (3.9.0)
85 | rspec_junit_formatter (0.4.1)
86 | rspec-core (>= 2, < 4, != 2.12.0)
87 | thor (0.20.3)
88 | thread_safe (0.3.6)
89 | tzinfo (1.2.5)
90 | thread_safe (~> 0.1)
91 | zeitwerk (2.2.0)
92 |
93 | PLATFORMS
94 | ruby
95 |
96 | DEPENDENCIES
97 | actionpack (= 6.0.0)
98 | activemodel (= 6.0.0)
99 | activesupport (= 6.0.0)
100 | appraisal (= 2.2.0)
101 | bundler (~> 2.0.1)
102 | moderate_parameters!
103 | pry (~> 0.12.2)
104 | railties (= 6.0.0)
105 | rake (~> 10.0)
106 | rspec (~> 3.0)
107 | rspec_junit_formatter (= 0.4.1)
108 |
109 | BUNDLED WITH
110 | 2.0.1
111 |
--------------------------------------------------------------------------------
/lib/generators/moderate_parameters/install_generator.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'rails/generators/base'
4 |
5 | module ModerateParameters
6 | module Generators
7 | class InstallGenerator < Rails::Generators::Base
8 | source_root File.expand_path('../templates', __dir__)
9 |
10 | desc 'Creates a ModerateParameters initializer.'
11 |
12 | def copy_initializer
13 | template 'moderate_parameters.rb', 'config/initializers/moderate_parameters.rb'
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/generators/templates/moderate_parameters.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ModerateParameters.configure do |config|
4 | # Enables/Disables logging occurrences of
5 | # reading/writing from ActionController::Parameters.
6 | config.breadcrumbs_enabled = false
7 | # Sets where to log the ModerateParameters output
8 | config.logger = ActiveSupport::Logger.new('log/moderate_parameters.log')
9 | end
10 |
--------------------------------------------------------------------------------
/lib/moderate_parameters.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'action_controller'
4 | require 'active_support'
5 | require 'moderate_parameters/version'
6 | require 'moderate_parameters/parameters'
7 | require 'moderate_parameters/breadcrumbs'
8 |
9 | module ModerateParameters
10 | mattr_accessor :breadcrumbs_enabled
11 | @@breadcrumbs_enabled = false
12 |
13 | mattr_accessor :logger
14 | @@logger = nil
15 |
16 | def self.configure
17 | yield self
18 | end
19 | end
20 |
21 | module ActionController
22 | class Parameters
23 | prepend ModerateParameters::Breadcrumbs
24 | prepend ModerateParameters::Parameters
25 | end
26 | end
27 |
28 | require 'moderate_parameters/logger'
29 |
--------------------------------------------------------------------------------
/lib/moderate_parameters/breadcrumbs.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ModerateParameters
4 | module Breadcrumbs
5 | def []=(key, _value)
6 | internal_param_logging(key, key?(key) ? 'overwritten' : 'added', caller_locations)
7 | super
8 | end
9 |
10 | def merge!(other_hash)
11 | internal_method_logging('merge!', other_hash.keys, caller_locations)
12 | super
13 | end
14 |
15 | def reverse_merge!(other_hash)
16 | internal_method_logging('reverse_merge!', other_hash.keys, caller_locations)
17 | super
18 | end
19 |
20 | def extract!(*keys)
21 | internal_method_logging('extract!', keys, caller_locations)
22 | super
23 | end
24 |
25 | def slice!(*keys)
26 | internal_method_logging('slice!', keys, caller_locations)
27 | super
28 | end
29 |
30 | def delete(*keys, &block)
31 | internal_method_logging('delete', keys, caller_locations)
32 | super
33 | end
34 |
35 | def reject!(&block)
36 | internal_block_logging('reject!', caller_locations)
37 | super
38 | end
39 |
40 | # Alias for #reject!
41 | def delete_if(&block)
42 | internal_block_logging('delete_if', caller_locations)
43 | super
44 | end
45 |
46 | def select!(&block)
47 | internal_block_logging('select!', caller_locations)
48 | super
49 | end
50 |
51 | # Alias for #select!
52 | def keep_if(&block)
53 | internal_block_logging('keep_if', caller_locations)
54 | super
55 | end
56 |
57 | private
58 |
59 | def needs_logged?
60 | ModerateParameters.breadcrumbs_enabled &&
61 | instance_variable_get(:@moderate_params_object_id) &&
62 | !permitted?
63 | end
64 |
65 | def internal_param_logging(key, action, stack_array)
66 | return unless needs_logged?
67 |
68 | ActiveSupport::Notifications.instrument('moderate_parameters') do |payload|
69 | payload[:caller_locations] = stack_array
70 | payload[:message] = "#{key} is being #{action} on: #{stack_array.join("\n")}"
71 | end
72 | end
73 |
74 | def internal_method_logging(method, args, stack_array)
75 | return unless needs_logged?
76 |
77 | ActiveSupport::Notifications.instrument('moderate_parameters') do |payload|
78 | payload[:caller_locations] = stack_array
79 | payload[:message] = "#{method} is being called with #{args} on: #{stack_array.join("\n")}"
80 | end
81 | end
82 |
83 | def internal_block_logging(method, stack_array)
84 | return unless needs_logged?
85 |
86 | ActiveSupport::Notifications.instrument('moderate_parameters') do |payload|
87 | payload[:caller_locations] = stack_array
88 | payload[:message] = "#{method} is being called with a block on: #{stack_array.join("\n")}"
89 | end
90 | end
91 | end
92 | end
93 |
--------------------------------------------------------------------------------
/lib/moderate_parameters/logger.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | ActiveSupport::Notifications.subscribe('moderate_parameters') do |_, _, _, _, payload|
4 | (ModerateParameters.logger || ActiveSupport::Logger.new('/dev/null')).info(
5 | "#{payload[:controller]}##{payload[:action]} #{payload[:message]}"
6 | )
7 | end
8 |
--------------------------------------------------------------------------------
/lib/moderate_parameters/parameters.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ModerateParameters
4 | module Parameters
5 | MP_OBJECT_ID = :@moderate_params_object_id
6 | MP_PARENT_KEY = :@moderate_params_parent_key
7 |
8 | def moderate(controller_name, action, *filters)
9 | log_duplicate_moderate_warning(
10 | caller_locations,
11 | instance_variable_get(MP_PARENT_KEY),
12 | controller_name,
13 | action
14 | ) if instance_variable_get(MP_OBJECT_ID)
15 |
16 | params = self.class.new
17 |
18 | filters.each do |filter|
19 | case filter
20 | when Symbol, String
21 | if non_scalar?(self[filter])
22 | non_scalar_value_filter(params, filter, controller_name, action)
23 | else
24 | permitted_scalar_filter(params, filter)
25 | end
26 | when Hash
27 | cust_hash_filter(params, filter, controller_name, action)
28 | end
29 | end
30 |
31 | incoming_params_logging(params, controller_name, action)
32 | duplicate_params = dup
33 | instance_variable_set(MP_OBJECT_ID, duplicate_params.object_id)
34 | duplicate_params.permit!
35 | end
36 |
37 | def require(key)
38 | return super if key.is_a?(Array) || self[key].blank?
39 | self[key].instance_variable_set(MP_PARENT_KEY, key)
40 | super
41 | end
42 |
43 | private
44 |
45 | def write_to_log(options)
46 | ActiveSupport::Notifications.instrument('moderate_parameters') do |payload|
47 | payload.merge!(options)
48 | end
49 | end
50 |
51 | def incoming_params_logging(params, controller_name, action)
52 | unpermitted_keys(params).each do |k|
53 | write_to_log(message: "#{@context || 'Top Level'} is missing: #{k}",
54 | action: action,
55 | controller: controller_name)
56 | end
57 | end
58 |
59 | def log_duplicate_moderate_warning(stack_array, parent_key, controller_name, action)
60 | write_to_log(message: ".moderate has already been called on params.require(:#{parent_key}): #{stack_array.join("\n")}",
61 | action: action,
62 | controller: controller_name)
63 | end
64 |
65 | def non_scalar_value_filter(params, key, controller_name, action)
66 | if has_key?(key) && !permitted_scalar?(self[key])
67 | params[key] = self[key].class.new
68 | write_to_log(message: "#{@context || 'Top Level'} is missing: #{params[key]} value for #{key}",
69 | action: action,
70 | controller: controller_name)
71 | end
72 | end
73 |
74 | def array_of_permitted_scalars?(value)
75 | if value.is_a?(Array) && value.all? { |element| permitted_scalar?(element) }
76 | return true unless block_given?
77 |
78 | yield value
79 | end
80 | end
81 |
82 | def non_scalar?(value)
83 | value.is_a?(Array) || value.is_a?(Parameters)
84 | end
85 |
86 | def permit_any_in_array(array)
87 | [].tap do |sanitized|
88 | array.each do |element|
89 | case element
90 | when ->(e) { permitted_scalar?(e) }
91 | sanitized << element
92 | when Parameters
93 | sanitized << permit_any_in_parameters(element)
94 | else
95 | # Log it
96 | end
97 | end
98 | end
99 | end
100 |
101 | def permit_any_in_parameters(params)
102 | self.class.new.tap do |sanitized|
103 | params.each do |key, value|
104 | case value
105 | when ->(v) { permitted_scalar?(v) }
106 | sanitized[key] = value
107 | when Array
108 | sanitized[key] = permit_any_in_array(value)
109 | when Parameters
110 | sanitized[key] = permit_any_in_parameters(value)
111 | else
112 | # Log It
113 | end
114 | end
115 | end
116 | end
117 |
118 | EMPTY_HASH ||= {}
119 | EMPTY_ARRAY ||= []
120 | def cust_hash_filter(params, filter, controller_name, action)
121 | filter = filter.with_indifferent_access
122 |
123 | # Slicing filters out non-declared keys.
124 | slice(*filter.keys).each do |key, value|
125 | next unless value
126 | next unless has_key? key
127 |
128 | if filter[key] == EMPTY_ARRAY
129 | # Declaration { comment_ids: [] }.
130 | array_of_permitted_scalars?(self[key]) do |val|
131 | params[key] = val
132 | end
133 | elsif filter[key] == EMPTY_HASH
134 | # Declaration { preferences: {} }.
135 | if value.is_a?(Parameters)
136 | params[key] = permit_any_in_parameters(value)
137 | end
138 | elsif non_scalar?(value)
139 | # Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
140 | params[key] = each_element(value) do |element|
141 | element.instance_variable_set '@context', "Parent #{key}"
142 | element.moderate(controller_name, action, *Array.wrap(filter[key]))
143 | end
144 | end
145 | end
146 | end
147 | end
148 | end
149 |
--------------------------------------------------------------------------------
/lib/moderate_parameters/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module ModerateParameters
4 | VERSION = '0.4.1'
5 | end
6 |
--------------------------------------------------------------------------------
/log/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/testdouble/moderate_parameters/77c8148d2ee726c00ad763f6c3c68a25946ad288/log/.gitkeep
--------------------------------------------------------------------------------
/moderate_parameters.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | lib = File.expand_path('lib', __dir__)
4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5 | require 'moderate_parameters/version'
6 |
7 | Gem::Specification.new do |spec|
8 | spec.name = 'moderate_parameters'
9 | spec.version = ModerateParameters::VERSION
10 | spec.authors = ['Kyle Boe']
11 | spec.email = ['kyle@hint.io']
12 |
13 | spec.summary = 'Protected Attributes to Strong Parameters migration tool'
14 | spec.description = 'A tool for migrating Rails applications from Protected ' \
15 | 'Attributes to Strong Parameters.'
16 | spec.homepage = 'https://github.com/hintmedia/moderate_parameters'
17 | spec.license = 'MIT'
18 |
19 | if spec.respond_to?(:metadata)
20 | spec.metadata['homepage_uri'] = spec.homepage
21 | spec.metadata['source_code_uri'] = 'https://github.com/hintmedia/moderate_parameters'
22 | spec.metadata['changelog_uri'] = 'https://github.com/hintmedia/moderate_parameters/blob/master/CHANGELOG.md'
23 | else
24 | raise 'RubyGems 2.0 or newer is required to protect against ' \
25 | 'public gem pushes.'
26 | end
27 |
28 | spec.files = Dir.chdir(File.expand_path(__dir__)) do
29 | `git ls-files -z`.split("\x0").reject do |f|
30 | f.match(%r{^(test|spec|features)/})
31 | end
32 | end
33 | spec.bindir = 'bin'
34 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
35 | spec.require_paths = ['lib']
36 |
37 | spec.required_ruby_version = '>= 2.3.1'
38 |
39 | spec.add_dependency 'actionpack', '>= 4.2', '< 6.2'
40 | spec.add_dependency 'activemodel', '>= 4.2', '< 6.2'
41 | spec.add_dependency 'activesupport', '>= 4.2', '< 6.2'
42 | spec.add_dependency 'railties', '>= 4.2', '< 6.2'
43 | spec.add_dependency 'nokogiri', '>= 1.13.9'
44 |
45 | spec.add_development_dependency 'bundler', '~> 2.0'
46 | spec.add_development_dependency 'pry', '~> 0.12.2'
47 | spec.add_development_dependency 'rake', '~> 13.0'
48 | spec.add_development_dependency 'rspec', '~> 3.0'
49 | spec.add_development_dependency 'rspec_junit_formatter', '0.4.1'
50 | spec.add_development_dependency 'appraisal', '2.2.0'
51 | end
52 |
--------------------------------------------------------------------------------
/spec/moderate_parameters_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe ModerateParameters do
4 | let(:params) do
5 | ActionController::Parameters.new(
6 | {
7 | person: {
8 | name: 'Francesco',
9 | age: '25',
10 | sub_array: %i[foo bar],
11 | sub_hash: { baz: :bang }
12 | }
13 | }
14 | )
15 | end
16 | let(:payload) { notification_payload_for('moderate_parameters') { subject } }
17 | let(:valid_permission_keys) { [:name, :age, { sub_array: [], sub_hash: {} }] }
18 |
19 | it 'has a version number' do
20 | expect(ModerateParameters::VERSION).to be_an_instance_of(String)
21 | end
22 |
23 | describe '::Parameters' do
24 | describe '#moderate' do
25 | let(:permission_keys) { valid_permission_keys }
26 | let(:subject) { params.require(:person).moderate('controller', 'action', *permission_keys) }
27 | it 'sets the moderate_params_object_id instance variable on the original params object' do
28 | params.require(:person).moderate('controller', 'action', *permission_keys)
29 | expect(params[:person].instance_variable_get(:@moderate_params_object_id)).to be_a Integer
30 | expect(payload[:controller]).to eql('controller')
31 | expect(payload[:action]).to eql('action')
32 | expect(payload[:message]).to start_with('.moderate has already been called on params.require(:person):')
33 | end
34 |
35 | context 'with permitted params properly specified' do
36 | it 'does not log to a file' do
37 | expect(payload).to be nil
38 | end
39 | end
40 |
41 | context 'without a top level key' do
42 | let(:permission_keys) { [:name, { sub_array: [], sub_hash: {} }] }
43 | it 'logs to a file' do
44 | expect(payload[:controller]).to eql('controller')
45 | expect(payload[:action]).to eql('action')
46 | expect(payload[:message]).to eql('Top Level is missing: age')
47 | end
48 | end
49 |
50 | context 'key present but missing array value' do
51 | let(:permission_keys) { [:name, :age, :sub_array, { sub_hash: {} }] }
52 |
53 | it 'logs to a file' do
54 | expect(payload[:controller]).to eql('controller')
55 | expect(payload[:action]).to eql('action')
56 | expect(payload[:message]).to eql('Top Level is missing: [] value for sub_array')
57 | end
58 | end
59 |
60 | context 'key present but missing hash value' do
61 | let(:permission_keys) { [:name, :age, { sub_array: [] }, :sub_hash] }
62 |
63 | it 'logs to a file' do
64 | expect(payload[:controller]).to eql('controller')
65 | expect(payload[:action]).to eql('action')
66 | expect(payload[:message]).to eql('Top Level is missing: {} value for sub_hash')
67 | end
68 | end
69 | end
70 |
71 | describe '#require' do
72 | let(:subject) { params.require(:person) }
73 |
74 | it 'sets an instance variable on the child object' do
75 | expect(subject.instance_variable_get(:@moderate_params_parent_key)).to eql(:person)
76 | end
77 |
78 | context 'when the require is passed an array' do
79 | let(:params) { ActionController::Parameters.new({ person: { foo: :bar }, other: { baz: :bang } }) }
80 | let(:subject) { params.require([:person, :other]) }
81 |
82 | it 'sets an instance variable on each child object' do
83 | expect(subject.map { |o| o.instance_variable_get(:@moderate_params_parent_key) }).to eql([:person, :other])
84 | end
85 | end
86 |
87 | context 'when the require is called on params with a blank value' do
88 | let(:params) { ActionController::Parameters.new(person: nil) }
89 | let(:subject) { params.require(:person) }
90 |
91 | it 'sets an instance variable on each child object' do
92 | expect { subject }.to raise_error(ActionController::ParameterMissing, "param is missing or the value is empty: person")
93 | end
94 | end
95 | end
96 | end
97 |
98 | describe '::Breadcrumbs' do
99 | let(:subject) { a(params[:person]) }
100 |
101 | before(:each) do
102 | ModerateParameters.configure do |c|
103 | c.breadcrumbs_enabled = true
104 | end
105 | params.require(:person).moderate('controller', 'action', *valid_permission_keys)
106 | end
107 |
108 | describe '#[]=' do
109 | let(:relative_line) { __LINE__ + 2 }
110 | def a(test_params)
111 | test_params[:age] = nil
112 | end
113 |
114 | context 'with the key already being set' do
115 | it 'logs to a file' do
116 | expect(payload[:message]).to start_with('age is being overwritten on:')
117 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
118 | end
119 | end
120 |
121 | context 'without the key already being set' do
122 | let(:params) do
123 | ActionController::Parameters.new(
124 | {
125 | person: {
126 | name: 'Francesco',
127 | sub_array: %i[foo bar],
128 | sub_hash: { baz: :bang }
129 | }
130 | }
131 | )
132 | end
133 |
134 | it 'logs to a file' do
135 | expect(payload[:message]).to start_with('age is being added on:')
136 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
137 | end
138 | end
139 | end
140 |
141 | describe '#merge!' do
142 | let(:other_hash) { { name: 'Sophie'} }
143 | let(:relative_line) { __LINE__ + 2 }
144 | def a(test_params)
145 | test_params.merge!(other_hash)
146 | end
147 |
148 | it 'logs to a file' do
149 | expect(payload[:message]).to start_with("merge! is being called with #{other_hash.keys} on:")
150 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
151 | end
152 | end
153 |
154 | describe '#reverse_merge!' do
155 | let(:other_hash) { { name: 'Alyssa'} }
156 | let(:relative_line) { __LINE__ + 2 }
157 | def a(test_params)
158 | test_params.reverse_merge!(other_hash)
159 | end
160 |
161 | it 'logs to a file' do
162 | expect(payload[:message]).to start_with("reverse_merge! is being called with #{other_hash.keys} on:")
163 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
164 | end
165 | end
166 |
167 | describe '#extract!' do
168 | let(:relative_line) { __LINE__ + 2 }
169 | def a(test_params)
170 | test_params.extract!(:name)
171 | end
172 |
173 | it 'logs to a file' do
174 | expect(payload[:message]).to start_with('extract! is being called with [:name] on:')
175 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
176 | end
177 | end
178 |
179 | describe '#slice!' do
180 | let(:relative_line) { __LINE__ + 2 }
181 | def a(test_params)
182 | test_params.slice!(:name)
183 | end
184 |
185 | it 'logs to a file' do
186 | expect(payload[:message]).to start_with('slice! is being called with [:name] on:')
187 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
188 | end
189 | end
190 |
191 | describe '#delete' do
192 | let(:relative_line) { __LINE__ + 2 }
193 | def a(test_params)
194 | test_params.delete(:name)
195 | end
196 |
197 | it 'logs to a file' do
198 | expect(payload[:message]).to start_with('delete is being called with [:name] on:')
199 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
200 | end
201 | end
202 |
203 | describe '#reject!' do
204 | let(:relative_line) { __LINE__ + 2 }
205 | def a(test_params)
206 | test_params.reject! { |k, _v| k == :name }
207 | end
208 |
209 | it 'logs to a file' do
210 | expect(payload[:message]).to start_with('reject! is being called with a block on:')
211 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
212 | end
213 | end
214 |
215 | describe '#delete_if' do
216 | let(:relative_line) { __LINE__ + 2 }
217 | def a(test_params)
218 | test_params.delete_if { |k, _v| k == :name }
219 | end
220 |
221 | it 'logs to a file' do
222 | expect(payload[:message]).to start_with('delete_if is being called with a block on:')
223 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
224 | end
225 | end
226 |
227 | describe '#select!' do
228 | let(:relative_line) { __LINE__ + 2 }
229 | def a(test_params)
230 | test_params.select! { |k, _v| k == :name }
231 | end
232 |
233 | it 'logs to a file' do
234 | expect(payload[:message]).to start_with('select! is being called with a block on:')
235 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
236 | end
237 | end
238 |
239 | describe '#keep_if' do
240 | let(:relative_line) { __LINE__ + 2 }
241 | def a(test_params)
242 | test_params.keep_if { |k, _v| k == :name }
243 | end
244 |
245 | it 'logs to a file' do
246 | expect(payload[:message]).to start_with('keep_if is being called with a block on:')
247 | expect(payload[:caller_locations][0].to_s).to end_with("spec/moderate_parameters_spec.rb:#{relative_line}:in \`a'")
248 | end
249 | end
250 | end
251 | end
252 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'bundler/setup'
4 | require 'pry'
5 | require 'moderate_parameters'
6 |
7 | RSpec.configure do |config|
8 | # Enable flags like --only-failures and --next-failure
9 | config.example_status_persistence_file_path = '.rspec_status'
10 |
11 | # Disable RSpec exposing methods globally on `Module` and `main`
12 | config.disable_monkey_patching!
13 |
14 | config.expect_with :rspec do |c|
15 | c.syntax = :expect
16 | end
17 | end
18 |
19 | def notification_payload_for(notification)
20 | test_payload = nil
21 | ActiveSupport::Notifications.subscribe(notification) do |_, _, _, _, payload|
22 | test_payload = payload
23 | end
24 |
25 | yield
26 |
27 | ActiveSupport::Notifications.unsubscribe(notification)
28 |
29 | test_payload
30 | end
31 |
--------------------------------------------------------------------------------