Class: Scryer::Rules::AuthenticationBypassRule

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

Overview

Flags a controller class that calls skip_before_action/ skip_action_callback naming a common authentication filter (authenticate_user!, authenticate!, ...) — same shape and reasoning as CsrfProtectionRule, just for auth filters instead of CSRF: skipping one is sometimes correct (a public endpoint, a webhook) but is also a common way to accidentally leave an action reachable without login, especially with a broad except:/no scoping at all.

Constant Summary collapse

SKIP_METHODS =
%w[skip_before_action skip_action_callback skip_before_filter].freeze
AUTH_FILTER_NAMES =
%w[
  authenticate_user! authenticate! authenticate_admin! authenticate_account!
  require_login require_authentication authorize_request
].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



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/scryer/rules/authentication_bypass_rule.rb', line 22

def scan
  findings = []

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

    class_name = Ast.ident_text(node[1].is_a?(Array) ? node[1][1] : nil)
    next unless class_name.to_s.end_with?("Controller")

    each_skip_call(node[3]).each do |skip_node, filter_name|
      line = Ast.line_of(skip_node)
      findings << finding(
        line: line,
        message: "`#{class_name}` skips the `#{filter_name}` authentication filter " \
                  "(`#{skip_call_method(skip_node)} :#{filter_name}`) — every action this " \
                  "applies to is reachable without logging in unless something else in " \
                  "this controller re-checks authentication.",
        suggested_fix: "If this is genuinely a public action (a webhook, a login/signup " \
                        "page), scope the skip tightly with `only: [:action_name]` rather " \
                        "than leaving it unscoped or using a broad `except:`. If it's not " \
                        "meant to be public, remove the skip."
      )
    end
  end

  findings
end