Class: Scryer::Rules::WeakCryptoRule

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

Overview

Flags Digest::MD5/Digest::SHA1 used in a context that looks like password/credential hashing (method or nearby variable name contains "password"/"passwd") — both are cryptographically broken for that use case; a fast general-purpose hash lets an attacker who steals the DB brute-force passwords far faster than a proper password hash (bcrypt/scrypt/argon2, which are deliberately slow).

Constant Summary collapse

WEAK_DIGESTS =
%w[MD5 SHA1].freeze
PASSWORD_HINT =
/password|passwd|credential/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



18
19
20
21
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/weak_crypto_rule.rb', line 18

def scan
  findings = []

  Ast.each_node(sexp) do |node|
    next unless Ast.tagged?(node, :top_const_ref, :const_path_ref, :var_ref)

    digest_name = digest_algorithm_name(node)
    next unless digest_name

    # Only flag when something nearby (same statement/line) mentions
    # password-ish naming — Digest::MD5/SHA1 have plenty of legitimate
    # non-credential uses (cache keys, ETags, checksums) that shouldn't
    # be flagged as a crypto weakness.
    line = Ast.line_of(node)
    context_line = Ast.source_line(source, line).to_s
    next unless PASSWORD_HINT.match?(context_line)

    findings << finding(
      line: line,
      message: "`Digest::#{digest_name}` is used near what looks like password/credential " \
                "handling — #{digest_name} is fast and unsalted by default, making stolen " \
                "hashes practical to brute-force.",
      suggested_fix: "Use `bcrypt` via Rails' `has_secure_password` for password storage " \
                      "instead of a general-purpose digest — it's deliberately slow and " \
                      "handles salting automatically. Reserve Digest::#{digest_name} for " \
                      "non-credential uses (cache keys, checksums)."
    )
  end

  findings
end