lib/contrast/agent/assess/rule/provider/hardcoded_key.rb in contrast-agent-3.15.0 vs lib/contrast/agent/assess/rule/provider/hardcoded_key.rb in contrast-agent-3.16.0

- old
+ new

@@ -31,10 +31,68 @@ 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 + BYTE_HOLDERS = %i[ARRAY LIST].cs__freeze + # Determine if the given value node violates the hardcode key rule + # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to + # evaluate + # @return [Boolean] + 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 + + REDACTED_MARKER = ' = [**REDACTED**]' + def redacted_marker + REDACTED_MARKER + end + + # 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. + # + # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to + # evaluate + # @return [Boolean] is this a node for String#bytes or not + 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 + + # 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 def value_type_passes? value return false unless value.is_a?(Array) && value.any? @@ -46,14 +104,9 @@ # There isn't a filter for the byte value. The check is not evaluated # for this rule def value_passes? _value true - end - - REDACTED_MARKER = ' = [**REDACTED**]' - def redacted_marker - REDACTED_MARKER end end end end end