# 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 Agent module Protect module Policy # This Module is the base of our Protect Applicators. It lays out the # form of the Applicator, which will override specific implementations # in order to properly invoke its Rule. module RuleApplicator include Contrast::Components::Logger::InstanceMethods # Calls the actual invocation for this applicator, if required. Will # attempt to transform the data as required prior to invocation and # provides a common interface for those rules that have the same # implementation regardless of the method patched. # # For those methods with different transformations depending on the # method instrumented, variations of this method, including an # indication of for which instrumented method they apply, will exist. # # @param method [Symbol] the name of the method for which this rule # is invoked # @param exception [Exception] any exception raised; used for rules # like Padding Oracle Attack (now defunct), which determine if the # number and type of exceptions are an attack # @param properties [Hash] set of extra information provided by the # applicator in an attempt to build a better story for the user # @param object [Object] the thing on which the triggering method was # invoked # @param args [Array] the arguments passed to the triggering # method at invocation # @raise [Contrast::SecurityException] on block, will pass the # exception from the rule def apply_rule method, exception, properties, object, args invoke(method, exception, properties, object, args) rescue Contrast::SecurityException => e raise(e) rescue StandardError => e logger.error('Error applying protect rule', e, module: object.cs__class.cs__name, method: method, rule: rule_name) end protected # Calls the actual rule for this applicator, if required. Most rules # invoke this from within their apply_rule method after doing # whatever transformations they need to get into this common format. # # @param _method [Symbol] the name of the method for which this rule # is invoked # @param _exception [Exception] any exception raised; used for rules # like Padding Oracle Attack (now defunct), which determine if the # number and type of exceptions are an attack # @param _properties [Hash] set of extra information provided by the # applicator in an attempt to build a better story for the user # @param _object [Object] the thing on which the triggering method # was invoked # @param _args [Array] the arguments passed to the triggering # method at invocation # @raise [NoMethodError] This is abstract method def invoke _method, _exception, _properties, _object, _args raise(NoMethodError, 'This is abstract, override it.') end # The name of the rule, as expected by the Contrast Service and Contrast UI. # # @return [String] # @raise [NoMethodError] This is abstract method def rule_name raise(NoMethodError, 'This is abstract, override it.') end # The rule for which this applicator applies. It'll be a concrete # sub-class of Contrast::Agent::Protect::Rule::Base, found based on # the value of Contrast::Agent::Protect::Policy::RuleApplicator#name. # # @return [Contrast::Agent::Protect::Rule::Base] def rule ::Contrast::PROTECT.rule(rule_name) end # Should we skip analysis for this rule for this method invocation? # This allows us to short circuit in those cases for which the rule # will not apply. # # @return [Boolean] def skip_analysis? context = Contrast::Agent::REQUEST_TRACKER.current return true unless context&.app_loaded? return true unless rule&.enabled? false end end end end end end