Class: Scryer::Rules::MissingAuthorizationRule

Inherits:
Scryer::Rule
  • Object
show all
Defined in:
lib/scryer/rules/missing_authorization_rule.rb

Overview

Flags a *Controller class that defines create/update/destroy (Rails' three standard write actions) with NO authorization evidence anywhere in the class — broader than IdorRule, which only fires when there's also a params-derived Model.find call in the same class. Plenty of write actions don't call .find at all (Model.create(...) needs no lookup; a destroy might act on an object set up in a before_action), so IdorRule's narrower pattern misses them even though "this write action has no visible authorization check at all" is exactly as real a concern. Restricted to the three standard write actions (not every public method) specifically to avoid flagging genuinely public controllers (a marketing page, a login form) that have no write actions at all and legitimately need no authorization — a controller that defines create/update/destroy at all is already a much stronger "this manages a real resource" signal than any action name would be.

Expect overlap with idor on controllers that both .find a record AND have no authorization — that's two different rules independently confirming the same underlying gap from two different code shapes (a lookup vs. a write action), not double-counting a bug.

Constant Summary collapse

WRITE_ACTIONS =
%w[create update destroy].freeze
AUTHORIZATION_METHODS =

Duplicated from IdorRule intentionally, same as NON_MODEL_RECEIVERS is duplicated between IdorRule and MassAssignmentRule — a small, stable list not worth a shared-module indirection for two callers.

%w[
  authorize authorize! policy_scope can? cannot?
  load_and_authorize_resource authorize_resource
  verify_authorized verify_policy_scoped
].freeze

Instance Attribute Summary

Attributes inherited from Scryer::Rule

#file, #sexp, #source

Instance Method Summary collapse

Methods inherited from Scryer::Rule

inherited, #initialize

Constructor Details

This class inherits a constructor from Scryer::Rule

Instance Method Details

#scanObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/scryer/rules/missing_authorization_rule.rb', line 43

def scan
  findings = []

  Ast.each_node(sexp) do |node|
    next unless Ast.tagged?(node, :class)

    class_name = Ast.class_name(node[1])
    next unless class_name.to_s.end_with?("Controller")

    body = node[3]
    next if each_call_names(body).any? { |name| AUTHORIZATION_METHODS.include?(name) }

    each_write_action_def(body).each do |def_node, action_name|
      findings << finding(
        line: Ast.line_of(def_node),
        message: "`#{class_name}##{action_name}` writes data (a standard Rails write " \
                  "action), and `#{class_name}` has no visible authorization check anywhere " \
                  "in it (no `authorize`, `policy_scope`, `can?`/`cannot?`, or CanCanCan/" \
                  "Pundit callback) — any logged-in (or, if this controller skips " \
                  "authentication too, any) user may be able to call this action.",
        suggested_fix: "Add an explicit authorization check before the write happens — " \
                        "`authorize @thing` (Pundit) or `authorize! :#{action_name}, @thing` " \
                        "(CanCanCan) — or `load_and_authorize_resource`/`after_action " \
                        ":verify_authorized` at the class level if every action here should " \
                        "be gated the same way."
      )
    end
  end

  findings
end