Class: RuboCop::Cop::DevDoc::Auth::LoadResourceCurrentUserGuard

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/dev_doc/auth/load_resource_current_user_guard.rb

Overview

Require an early-return nil-guard before using current_user inside the load-resource lifecycle method, and forbid safe-navigation (&.).

Rationale

glib_load_resource (or a similarly named hook) runs before the Pundit policy. At that point current_user may still be nil — an anonymous visitor hasn't been denied yet. Code that calls current_user.foo without guarding first crashes for anonymous visitors; code that uses current_user&.foo hides the problem with soft nil-handling instead of making it explicit.

The correct pattern is an early-return guard at the top of any branch that needs current_user:

✔️
def glib_load_resource
return unless current_user

@post = current_user.posts.find(params[:id])
end

Guarded branches within a case/if are also fine:

✔️
def glib_load_resource
case action_name.to_sym
when :new, :create
  return unless current_user
  @post = current_user.posts.new
when :index
  # Nothing to do
end
end

glib's assert_current_user_present raises when current_user is nil, so it guarantees non-nil just as well as the early return — and any raise guard works like the return form:

✔️
def glib_load_resource
assert_current_user_present

@post = current_user.posts.find(params[:id])
end

✔️
def glib_load_resource
raise UnauthorizedError unless current_user

@post = current_user.posts.find(params[:id])
end

❌ Safe navigation — hides the pre-auth nil risk
def glib_load_resource
@post = current_user&.posts&.find(params[:id])
end

❌ Unguarded — crashes for anonymous visitors
def glib_load_resource
@post = current_user.posts.find(params[:id])
end

Configuration

LoadResourceMethodNames (default: [glib_load_resource]) — list of method names where the guard rule applies. Projects standardising on a different lifecycle method can add it here.

CurrentUserAssertionMethodNames (default: [assert_current_user_present]) — calls that raise when current_user is nil. A call to one of these before the first current_user use satisfies the guard. Add project-specific assertion helpers here. NOTE: the cop trusts the named method to actually raise on nil — a configured name that doesn't will turn into a false negative.

NOTE: The cop performs structural analysis of the method body and does not track aliasing. If you assign current_user to a local variable and then call methods on that variable, the cop will not flag it — reviewers must catch that pattern manually.

Examples:

# bad — safe navigation inside load hook
def glib_load_resource
  @post = current_user&.posts&.find(params[:id])
end

# bad — unguarded call inside load hook
def glib_load_resource
  @post = current_user.posts.find(params[:id])
end

# good — guarded with early return
def glib_load_resource
  return unless current_user

  @post = current_user.posts.find(params[:id])
end

# good — guarded with the glib assertion (raises when nil)
def glib_load_resource
  assert_current_user_present

  @post = current_user.posts.find(params[:id])
end

Constant Summary collapse

MSG_SAFE_NAV =
'Avoid `current_user&.` inside `%<method>s` — use ' \
'`return unless current_user` then `current_user.` instead.'.freeze
MSG_MISSING_GUARD =
'`current_user` is used without a prior ' \
'`return unless current_user` (or `assert_current_user_present`) ' \
'guard in `%<method>s`. ' \
'This method runs before the policy, so `current_user` may be nil.'.freeze
NIL_CHECK_METHODS =

Predicates safe to call on a nil current_user — a current_user.nil? etc. is not itself an unguarded use that risks NoMethodError.

%i[nil? blank? present? empty?].freeze
ABSENCE_CHECK_METHODS =

Predicates split by polarity, so a guard's exit branch can be matched to the path on which current_user is nil. present? is a PRESENCE check (reversed from nil?/blank?), so unless current_user.present? is a valid guard while if current_user.present? is not.

%i[nil? blank? empty?].freeze
PRESENCE_CHECK_METHODS =
%i[present?].freeze

Instance Method Summary collapse

Instance Method Details

#on_def(node) ⇒ Object Also known as: on_defs



127
128
129
# File 'lib/rubocop/cop/dev_doc/auth/load_resource_current_user_guard.rb', line 127

def on_def(node)
  check_load_resource_method(node)
end