lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb in contrast-agent-6.6.5 vs lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb in contrast-agent-6.7.0

- old
+ new

@@ -9,37 +9,34 @@ module Contrast module Agent module Assess module Rule module Provider - # 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: + # 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 module HardcodedValueRule include Contrast::Components::Logger::InstanceMethods + # @return [Boolean] def disabled? !::Contrast::ASSESS.enabled? || ::Contrast::ASSESS.rule_disabled?(rule_id) end - # 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. + # 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. + # 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. # - # @param trace_point [TracePoint] the TracePoint event created on - # the :end of a Module being loaded - # @param ast [RubyVM::AbstractSyntaxTree::Node] the abstract syntax - # tree of the Module defined in the TracePoint end event + # @param trace_point [TracePoint] the TracePoint event created on the :end of a Module being loaded + # @param ast [RubyVM::AbstractSyntaxTree::Node] the abstract syntax tree of the Module defined in the + # TracePoint end event def parse trace_point, ast return if disabled? parse_ast(trace_point.self, ast) rescue StandardError => e @@ -54,12 +51,12 @@ SOURCE_KEY = 'source' private # @param mod [Module] the module to which this AST pertains - # @param ast [RubyVM::AbstractSyntaxTree::Node, Object] a node - # within the AST, which may be a leaf, so any Object + # @param ast [RubyVM::AbstractSyntaxTree::Node, Object] a node within the AST, which may be a leaf, so any + # Object def parse_ast mod, ast return unless ast.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) return unless ast.cs__respond_to?(:children) children = ast.children @@ -70,36 +67,31 @@ end # https://www.rubydoc.info/gems/ruby-internal/Node/CDECL return unless ast.type == :CDECL - # The CDECL Node has two children, the first being the Constant - # name as a symbol, the second as the value to assign to that - # constant + # The CDECL Node has two children, the first being the Constant name as a symbol, the second as the value + # to assign to that constant children = ast.children name = children[0].to_s # If that constant name doesn't pass our checks, move on. return unless name_passes?(name) value = children[1] - # The assignment node could be a direct value or a call of some - # sort. We leave it to each rule to properly handle these nodes. + # The assignment node could be a direct value or a call of some sort. We leave it to each rule to + # properly handle these nodes. return unless value_node_passes?(value) - if Contrast::Agent::Reporter.enabled? - new_finding_and_reporting(mod, name) - else # TODO: RUBY-1438 -- remove - build_finding(mod, name) - end + build_and_report(mod, name) + rescue StandardError => e + logger.error('Unable to parse AST for Hardcoded Rule analysis.', e) end - # Constants can be set as frozen directly. We need to account for - # this change as it means the Node given to the :CDECL call will be - # a :CALL, not a constant. + # Constants can be set as frozen directly. We need to account for this change as it means the Node given to + # the :CDECL call will be a :CALL, not a constant. # - # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to - # evaluate + # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to evaluate # @return [Boolean] is this a freeze call or not def freeze_call? value_node return false unless value_node.type == :CALL children = value_node.children @@ -107,66 +99,32 @@ return false unless children.length >= 2 children[1] == :freeze end - def build_finding clazz, constant_string - class_name = clazz.cs__name + # @param mod [Module] the module to which this AST pertains + # @param name [String] the name of the hardcoded constant + def build_and_report mod, name + finding = build_finding(mod, name) + return unless finding - finding = assign_finding(class_name, constant_string) - # TODO: RUBY-1705 - # The only place we still use dtm activity - activity = Contrast::Api::Dtm::Activity.new - activity.findings << finding - Contrast::Agent.messaging_queue.send_event_eventually(activity, force: true) - rescue StandardError => e - logger.error('Unable to build a finding for Hardcoded Rule', e) - nil - end + preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding) + return unless preflight - # @param class_name [String] the name of the class in which the hardcoded value is present - # @param constant_string [String] the name of the constant - # @return [Contrast::Api::Dtm::Finding] - def assign_finding class_name, constant_string - finding = Contrast::Api::Dtm::Finding.new - finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(rule_id) - finding.version = Contrast::Agent::Assess::Policy::TriggerMethod::CURRENT_FINDING_VERSION - - finding.properties[SOURCE_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(class_name) - finding.properties[CONSTANT_NAME_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(constant_string) - finding.properties[CODE_SOURCE_KEY] = - Contrast::Utils::StringUtils.protobuf_safe_string(constant_string + redacted_marker) - - hash = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding) - finding.hash_code = Contrast::Utils::StringUtils.protobuf_safe_string(hash) - finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding) - finding + Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding + Contrast::Agent.reporter&.send_event(preflight) end - def new_finding_and_reporting clazz, constant_string - return unless Contrast::Agent::Reporter.enabled? - - # sent to reporter - # and add logger message for the report of the preflight - new_preflight = Contrast::Agent::Reporting::Preflight.new - new_preflight_message = Contrast::Agent::Reporting::PreflightMessage.new - new_preflight_message.hash_code = hash - new_preflight_message.data = "#{ rule_id },#{ hash }" - new_preflight.messages << new_preflight_message - - # extract to new method - # here we will generate new type of finding - ruby_finding = Contrast::Agent::Reporting::Finding.new(rule_id) - ruby_finding.hash_code = hash - ruby_finding.properties[SOURCE_KEY] = clazz.cs__name - ruby_finding.properties[CONSTANT_NAME_KEY] = constant_string - ruby_finding.properties[CODE_SOURCE_KEY] = constant_string + redacted_marker - save_and_report_finding(ruby_finding, new_preflight) - end - - def save_and_report_finding ruby_finding, new_preflight - Contrast::Agent::Reporting::ReportingStorage[hash] = ruby_finding - Contrast::Agent.reporter&.send_event(new_preflight) + # @param clazz [Class] the Class or Module containing the constant + # @param constant_string [String] + # @return [Contrast::Agent::Reporting::Finding] + def build_finding clazz, constant_string + finding = Contrast::Agent::Reporting::Finding.new(rule_id) + finding.properties[SOURCE_KEY] = clazz.cs__name + finding.properties[CONSTANT_NAME_KEY] = constant_string + finding.properties[CODE_SOURCE_KEY] = constant_string + redacted_marker + finding.hash_code = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding) + finding end end end end end