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

REDACTED_MARKER =
' = [**REDACTED**]'
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

Constants included from HardcodedValueRule

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

Instance Method Summary collapse

Methods included from HardcodedValueRule

#disabled?, #parse

Methods included from Components::Logger::InstanceMethods

#cef_logger, #logger

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.



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

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]
  unless potential_string_node.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) &&
        potential_string_node.type == :STR

    return false
  end

  children[1] == :bytes
end

#name_passes?(constant_string) ⇒ 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



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

def redacted_marker
  REDACTED_MARKER
end

#rule_idObject



27
28
29
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 27

def rule_id
  NAME
end

#value_node_passes?(value_node) ⇒ Boolean

Determine if the given value node violates the hardcode key rule



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
# File 'lib/contrast/agent/assess/rule/provider/hardcoded_key.rb', line 40

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

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

      return false
    end
  end

  true
end