Class: Scryer::Rules::HardcodedSecretKeyBaseRule

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

Overview

Flags config.secret_key_base = "literal" (or Rails.application.config.secret_key_base = "literal") — a plain string literal assigned to secret_key_base via a .field= target.

HardcodedSecretRule doesn't catch this shape: its target_name walks the assignment target depth-first and returns the first identifier it finds, which for a .field= target with a receiver is the receiver's own name (config) rather than the attribute being assigned (secret_key_base) — so config.secret_key_base = "..." never matches its NAME_PATTERN, even though the value is exactly the kind of credential that rule exists to catch. Bare assignment (secret_key_base = "..." with no receiver) IS already caught by HardcodedSecretRule, since there the identifier IS the target name — this rule only covers the receiver'd shape it misses.

Constant Summary collapse

PLACEHOLDER_VALUES =
/\A(x+|0+|change-?me|placeholder|example|dummy|fake|test|redacted|\*+)\z/i.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
# File 'lib/scryer/rules/hardcoded_secret_key_base_rule.rb', line 25

def scan
  findings = []

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

    target = node[1]
    next unless Ast.tagged?(target, :field)
    next unless Ast.ident_text(target[3]) == "secret_key_base"

    value = Ast.plain_string_value(node[2])
    next unless value && !value.strip.empty? && !PLACEHOLDER_VALUES.match?(value.strip)

    line = Ast.line_of(node)
    findings << finding(
      line: line,
      message: "`secret_key_base` is assigned a literal string — this key signs/encrypts " \
                "Rails sessions and other secrets; anyone with read access to this source " \
                "(including git history) can forge session cookies and other signed data.",
      suggested_fix: "Use `Rails.application.credentials.secret_key_base` (the Rails default, " \
                      "set via `bin/rails credentials:edit`) or `ENV.fetch(\"SECRET_KEY_BASE\")` " \
                      "instead, and rotate this key since it's likely already exposed in git " \
                      "history."
    )
  end

  findings
end