# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Agent module Protect module Rule # Module to hold required generic filters (prefilter, infilter, postfilter) module Filters POSTFILTER_MODES = Set.new(%i[BLOCK MONITOR]).cs__freeze # Actions required for the rules that have to happen before the # application has completed its processing of the request. # # For most rules, these actions are performed within the analysis # engine and communicated as an input analysis result. Those that # require specific action need to provide that action. # # @param context [Contrast::Agent::RequestContext] the context for # the current request def prefilter context return unless prefilter?(context) ia_results = gather_ia_results(context) ia_results.each do |ia_result| result = build_attack_result(context) result = build_attack_without_match(context, ia_result, result) next unless result append_to_activity(context, result) record_triggered(context) raise(Contrast::SecurityException.new(self, block_message)) if blocked_violation?(result) end end # Prefilter check always called before infilter to check if the rule is infilter # capable, not disabled or in other way excluded by url or input exclusions. # # @param context [Contrast::Agent::RequestContext] # @return [Boolean] def prefilter? context return false unless enabled? return false if protect_excluded_by_url?(rule_name) return false unless context return false unless (results = gather_ia_results(context)) && results.any? return false if protect_excluded_by_input?(results) true end # This should only ever be called directly from patched code and will # have a different implementation based on the rule. As such, there # is not parent implementation. # # @param context [Contrast::Agent::RequestContext] the context for # the current request # @param match_string [String] the input that violated the rule and # matched the attack detection logic # @param _kwargs [Hash] key-value pairs used by the rule to build a # report. def infilter context, match_string, _kwargs return unless infilter?(context) return unless (result = build_violation(context, match_string)) append_to_activity(context, result) record_triggered(context) raise(Contrast::SecurityException.new(self, block_message)) if blocked? end # Infilter check always called before infilter to check if the rule is infilter # capable, not disabled or in other way excluded by url or input exclusions. # # @param context [Contrast::Agent::RequestContext] # @return [Boolean] def infilter? context return false unless enabled? return false unless (results = gather_ia_results(context)) && results.any? return false if protect_excluded_by_url?(rule_name) return false if protect_excluded_by_input?(results) true end # Check befor commiting infilter # # @param context [Contrast::Agent::RequestContext] def postfilter? context return false unless enabled? && POSTFILTER_MODES.include?(mode) return false if protect_excluded_by_url?(rule_name) return false if protect_excluded_by_input?(gather_ia_results(context)) return false if mode == :NO_ACTION || mode == :PERMIT true end # Actions required for the rules that have to happen after the # application has completed its processing of the request. # # Any implementation here needs to account for the fact that # responses may be streaming and, as such, transformations of the # response itself may not be permissible. # # Override for rules that need the response # Currently postfilter can be applied to streamed responses, if any logic within postfilter changes to modify # the response streamed responses will break # # @param context [Contrast::Agent::RequestContext] # @raise [Contrast::SecurityException] def postfilter context return unless postfilter?(context) result = find_postfilter_attacker(context, nil) return unless result&.samples&.any? append_to_activity(context, result) record_triggered(context) return unless blocked_violation?(result) raise(Contrast::SecurityException.new(self, "#{ rule_name } triggered in postfilter. Response blocked.")) end end end end end end