# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/components/logger' module Contrast module Utils # Utility for saving raw findings for later class Findings include Contrast::Components::Logger::InstanceMethods def initialize @_collection = [] end def collection @_collection ||= [] end def push trigger_node, source, object, ret, *args return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless trigger_node.collectable? @_collection << { trigger_node: trigger_node, source: source, object: object, ret: ret, args: args } end # Some rules requires response to be available before validating them correctly, # so we check if trigger_node.rule_id is collectable and then save them for # later report, when we have the response. # # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this # trigger event # @param source [Object] the source of the Trigger Event # @param object [Object] the Object on which the method was invoked # @param ret [Object] the Return of the invoked method # @param args [Array] the Arguments with which the method was invoked def collect_finding trigger_node, source, object, ret, *args push(trigger_node, source, object, ret, args) logger.trace('Finding collected', node_id: trigger_node.id, source_id: source.__id__, rule: trigger_node.rule_id) end # Build and report all collected findings for the collectable rules. # # We make sure the content-type is present before reporting, because some # findings do require it for validation. # # @return [true, nil] def report_collected_findings return if @_collection.empty? return if Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type.nil? while @_collection.any? finding = @_collection.pop collected = Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(finding[:trigger_node], finding[:source], finding[:object], finding[:ret], finding[:args]) Contrast::Agent::Assess::Policy::TriggerMethod.report_finding(collected) if collected end true end end end end