Class: Contrast::Agent::Assess::Rule::Provider::HardcodedKey

Inherits:
Object
  • Object
show all
Includes:
HardcodedValueRule
Defined in:
lib/contrast/agent/assess/rule/provider/hardcoded_key.rb

Overview

Determine if there are any cryptographic keys hardcoded into the sourcecode of the application. A constant is a cryptographic key if: 1) the name contains a KEY_FIELD_NAME value 2) the value is a non-empty array of only Fixnums

Constant Summary collapse

NAME =
'hardcoded-key'
KEY_FIELD_NAMES =

These are names, determined by the security team (Matt & Ar), that indicate a field is likely to be a password or secret token of some sort.

%w[KEY AES DES IV SECRET].cs__freeze
NON_KEY_PARTIAL_NAMES =

These are markers whose presence indicates that a field is more likely to be a descriptor or requirement than an actual key. We should ignore fields that contain them.

%w[CONTENT_CODES RESPONSE_CODES ERROR_CODES].cs__freeze
BYTE_HOLDERS =
%i[ARRAY LIST].cs__freeze
REDACTED_MARKER =
' = [**REDACTED**]'

Constants included from HardcodedValueRule

Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule::CODE_SOURCE_KEY, Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule::COMMON_CONSTANTS, Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule::CONSTANT_NAME_KEY, Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule::CONSTANT_NAME_PATTERN, Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule::SOURCE_KEY

Instance Method Summary collapse

Methods included from HardcodedValueRule

#analyze, #constant_name?, #disabled?, #parse

Methods included from Components::Interface

included

Instance Method Details

#bytes_call?(value_node) ⇒ Boolean

A node is a bytes_call if it’s the Node for String#bytes. We care about this specifically as it’s likely to be a common way to generate a key constant, rather than directly declaring an integer array.

Parameters:

  • value_node (RubyVM::AbstractSyntaxTree::Node)

    the node to evaluate

Returns:

  • (Boolean)

    is this a node for String#bytes or not



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 79

def bytes_call? value_node
  return false unless value_node.type == :CALL

  children = value_node.children
  return false unless children
  return false unless children.length >= 2

  potential_string_node = children[0]
  return false unless potential_string_node.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) &&
      potential_string_node.type == :STR

  children[1] == :bytes
end

#name_passes?(constant_string) ⇒ Boolean

Returns:

  • (Boolean)


31
32
33
34
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 31

def name_passes? constant_string
  KEY_FIELD_NAMES.any? { |name| constant_string.index(name) } &&
      NON_KEY_PARTIAL_NAMES.none? { |name| constant_string.index(name) }
end

#redacted_markerObject



67
68
69
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 67

def redacted_marker
  REDACTED_MARKER
end

#rule_idObject



17
18
19
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 17

def rule_id
  NAME
end

#value_node_passes?(value_node) ⇒ Boolean

Determine if the given value node violates the hardcode key rule

Parameters:

  • value_node (RubyVM::AbstractSyntaxTree::Node)

    the node to evaluate

Returns:

  • (Boolean)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 41

def value_node_passes? value_node
  # If it's a freeze call, then evaluate the entity being frozen
  value_node = value_node.children[0] if freeze_call?(value_node)
  # If it's a String being turned into bytes, then it matches key
  # expectations
  return true if bytes_call?(value_node)

  type = value_node.type
  return false unless BYTE_HOLDERS.include?(type)
  return false unless value_node.children.any?

  # Unless this is an array of literal numerics, we don't match.
  # That array seems to always end in a nil value, so we allow
  # those as well.
  value_node.children.each do |child|
    next unless child

    return false unless child.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) &&
        child.type == :LIT &&
        child.children[0]&.cs__is_a?(Integer)
  end

  true
end

#value_passes?(_value) ⇒ Boolean

There isn’t a filter for the byte value. The check is not evaluated for this rule

Returns:

  • (Boolean)


107
108
109
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 107

def value_passes? _value
  true
end

#value_type_passes?(value) ⇒ Boolean

TODO: RUBY-1014 remove ‘#value_type_passes?` and `#value_passes?` If the value is a byte array, or at least an array of numbers, it passes for this rule

Returns:

  • (Boolean)


96
97
98
99
100
101
102
103
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 96

def value_type_passes? value
  return false unless value.is_a?(Array) && value.any?

  value.each do |byte|
    return false unless byte.is_a?(Integer)
  end
  true
end