Class: Scryer::Rules::JobRawParamsRule

Inherits:
Scryer::Rule show all
Defined in:
lib/scryer/rules/job_raw_params_rule.rb

Overview

Flags SomeJob.perform_async(...) / .perform_later(...) / .perform_now(...) (or the bare form inside the job's own class) where an argument references params more broadly than a single-key subscript — see raw_params_reference? below for exactly what's exempt (params[:id] and friends) versus what isn't.

Sidekiq stores job arguments in Redis in plaintext and displays them in its web UI; both Sidekiq and ActiveJob log job arguments by default. Passing a raw params hash risks leaking whatever it contains — passwords, tokens, full user-submitted fields — into logs, Redis, and the Sidekiq UI, instead of passing just the specific id/value the job actually needs. This is a distinct, well-documented Sidekiq/ActiveJob concern from mass assignment (which is about writing untrusted params to a model, not about where params end up once queued).

Constant Summary collapse

PERFORM_METHODS =
%w[perform_async perform_later perform_now].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



25
26
27
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
# File 'lib/scryer/rules/job_raw_params_rule.rb', line 25

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 PERFORM_METHODS.include?(method_name)

    args = Ast.call_arguments(node)
    next unless args.any? { |a| raw_params_reference?(a) }

    line = Ast.line_of(node)
    findings << finding(
      line: line,
      message: "`#{method_name}` is called with an argument that references `params` — " \
                "Sidekiq stores job arguments in Redis in plaintext (visible in its web UI), " \
                "and both Sidekiq and ActiveJob log job arguments by default, so anything in " \
                "the raw params hash (passwords, tokens, other sensitive fields) can end up " \
                "somewhere it wasn't meant to be readable.",
      suggested_fix: "Extract only the specific id/value(s) the job actually needs into " \
                      "local variables before enqueuing (e.g. `id = params[:id]; " \
                      "#{method_name}(id)`), instead of passing `params` (or a subscript of " \
                      "it) straight through, and have the job re-fetch/re-derive anything " \
                      "else it needs from that id."
    )
  end

  findings
end