Module: Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule

Includes:
Components::Interface
Included in:
HardcodedKey, HardcodedPassword
Defined in:
lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb

Overview

Hardcoded rules detect if any secret value has been written directly into the sourcecode of the application. To use this base class, a provider must implement three methods: 1) name_passes? : does the constant name match a given value set 2) value_node_passes? : does the value of the constant match a

given value set

3) redacted_marker : the value to plug in for the obfuscated value

Constant Summary collapse

COMMON_CONSTANTS =

TODO: RUBY-1014 - remove ‘#analyze`

%i[
  CONTRAST_ASSESS_POLICY_STATUS
  VERSION
].cs__freeze
CONSTANT_NAME_PATTERN =

Constants can be variable or classes defined in the given class. We ONLY want the variables, which should be defined in the MACRO_CASE (upper case & underscore format)

/^[A-Z_]+$/.cs__freeze
CONSTANT_NAME_KEY =

The name of the field

'name'
CODE_SOURCE_KEY =

The code line, recreated, with the password obfuscated

'codeSource'
SOURCE_KEY =

The constant name

'source'

Instance Method Summary collapse

Methods included from Components::Interface

included

Instance Method Details

#analyze(clazz) ⇒ Object



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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb', line 33

def analyze clazz
  return if disabled?

  # we only want the constants explicitly defined in this class, not
  # those of its ancestor(s)
  constants = clazz.cs__constants(false)

  # if there are no constants, let's just leave
  return unless constants&.any?

  constants.each do |constant|
    next if COMMON_CONSTANTS.include?(constant)

    # if this class autoloads its constant, get the hell away from it
    # I mean it! Don't even think about it.
    #
    # Autoload means this constant (usually [always?] a class or
    # module) won't be required until something in the application
    # tries to load it. We CANNOT be that thing. We'll just have to
    # wait until it's loaded, at which point we'll be handed it
    # again.
    next if clazz.cs__autoload?(constant)

    # constant comes to us as a symbol. that sucks. we need to do
    # some string methods on it, so stringify it.
    constant_string = constant.to_s

    # if this is another class or a module, move on
    next unless constant_name?(constant_string)

    next unless name_passes?(constant_string)

    value = clazz.cs__const_get(constant, false)

    # if the constant isn't holding a string, skip it
    next unless value_type_passes?(value)

    # if it looks like a placeholder / pointer to a config, skip it
    next unless value_passes?(value)

    build_finding(clazz, constant_string)
  end
end

#constant_name?(constant) ⇒ Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb', line 102

def constant_name? constant
  constant.match?(CONSTANT_NAME_PATTERN)
end

#disabled?Boolean

Returns:

  • (Boolean)


24
25
26
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb', line 24

def disabled?
  !ASSESS.enabled? || ASSESS.rule_disabled?(rule_id)
end

#parse(trace_point, ast) ⇒ Object

Parse the file pertaining to the given TracePoint to walk its AST to determine if a Constant is hardcoded. For our purposes, this hard coding means directly set rather than as an interpolated String or through a method call.

Note: This is a top layer check, we make no assertions about what the methods or interpolations do. Their presence, even if only calling a hardcoded thing, causes this check to not report.

Parameters:

  • trace_point (TracePoint)

    the TracePoint event created on the :end of a Module being loaded

  • ast (RubyVM::AbstractSyntaxTree::Node)

    the abstract syntax tree of the Module defined in the TracePoint end event



90
91
92
93
94
95
96
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb', line 90

def parse trace_point, ast
  return if disabled?

  parse_ast(trace_point.self, ast)
rescue StandardError => e
  logger.error('Unable to parse AST for hardcoded keys', e, module: trace_point.self)
end