Class: Scryer::Rules::MassAssignmentRule
- Inherits:
-
Scryer::Rule
- Object
- Scryer::Rule
- Scryer::Rules::MassAssignmentRule
- Defined in:
- lib/scryer/rules/mass_assignment_rule.rb
Overview
Flags Model.new(params[...]) / Model.new(params) / .update(params[...])
/ .assign_attributes(params) where the argument is params (or a
subscript of it) with no .permit(...) anywhere in the same argument
expression — i.e. attributes are being mass-assigned straight from the
request with no allow-list.
Constant Summary collapse
- ASSIGNMENT_METHODS =
%w[new create create! update update! assign_attributes attributes=].freeze
- NON_MODEL_RECEIVERS =
Common stdlib/gem constants with their own
.new/.create-style factory methods that have nothing to do with ActiveRecord mass assignment (e.g.BCrypt::Password.create(params[:password])is hashing a single value, not setting a hash of model attributes). Excluding these — plus anything referenced through a namespacedA::Bpath, which real Rails models are less commonly called via at the exact call site — cuts down false positives significantly. %w[ Struct OpenStruct Data Class Module BCrypt OpenSSL Net URI Digest JSON YAML Marshal String Array Hash Integer Float Symbol Comparable ].freeze
Instance Attribute Summary
Attributes inherited from Scryer::Rule
Instance Method Summary collapse
Methods inherited from Scryer::Rule
Constructor Details
This class inherits a constructor from Scryer::Rule
Instance Method Details
#scan ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/scryer/rules/mass_assignment_rule.rb', line 28 def scan findings = [] Ast.each_node(sexp) do |node| next unless Ast.tagged?(node, :method_add_arg, :command, :command_call) inner = Ast.tagged?(node, :method_add_arg) ? node[1] : node receiver_and_name = Ast.call_name(inner) next unless receiver_and_name receiver, method_name = receiver_and_name next unless ASSIGNMENT_METHODS.include?(method_name) next unless likely_model_receiver?(receiver) args = Ast.call_arguments(node) next if args.empty? arg = args.first next unless references_raw_params?(arg) line = Ast.line_of(arg) || Ast.line_of(node) findings << finding( line: line, message: "`#{method_name}` receives `params` (or a subscript of it) directly, " \ "with no `.permit(...)` call — every attribute in the request can be " \ "set, including ones the form/API was never meant to expose (e.g. `admin`, `role_id`).", suggested_fix: "Wrap the params in a strong-parameters method, e.g. " \ "`#{method_name}(order_params)` with `def order_params; " \ "params.require(:order).permit(:status, :total); end` — only the " \ "explicitly permitted keys get through." ) end findings end |