# typed: ignore # Copyright (c) 2015 Sqreen. All Rights Reserved. # Please refer to our terms for more information: https://www.sqreen.com/terms.html require 'sqreen/deprecation' require 'sqreen/framework_cb' require 'sqreen/context' require 'sqreen/conditionable' require 'sqreen/call_countable' require 'sqreen/rules/attrs' require 'sqreen/events/attack' require 'sqreen/events/remote_exception' require 'sqreen/payload_creator' # Rules defined here can be instanciated from JSON. module Sqreen module Rules # Base class for callback that are initialized by rules from Sqreen class RuleCB < FrameworkCB include Conditionable include CallCountable # If nothing was asked by the rule we will ask for all sections available # These information will be pruned later when exporting in #to_hash DEFAULT_PAYLOAD = (PayloadCreator::METHODS.keys - ['local'] + ['context']).freeze attr_reader :test attr_reader :payload_tpl attr_reader :block # @params klass [String] class instrumented # @params method [String] method that was instrumented # @params rule_hash [Hash] Rule data that govern the current behavior def initialize(klass, method, rule_hash) super(klass, method) @block = rule_hash[Attrs::BLOCK] == true @test = rule_hash[Attrs::TEST] == true @data = rule_hash[Attrs::DATA] @rule = rule_hash @payload_tpl = @rule[Attrs::PAYLOAD] || DEFAULT_PAYLOAD @overtimeable = true condition_callbacks(@rule[Attrs::CONDITIONS]) count_callback_calls(@rule[Attrs::CALL_COUNT_INTERVAL]) end def rule_name @rule[Attrs::NAME] end def rulespack_id @rule[Attrs::RULESPACK_ID] end def priority @rule[Attrs::PRIORITY] || super end # Record an attack event into Sqreen system # @param infos [Hash] Additional information about request def record_event(infos, at = Time.now.utc) return unless framework payload = { :infos => infos, :rulespack_id => rulespack_id, :rule_name => rule_name, :attack_type => @rule['attack_type'], # for signal :test => test, :block => @rule['block'], # for signal :time => at, } if payload_tpl.include?('context') payload[:backtrace] = Sqreen::Context.new.bt end if framework.respond_to?(:datadog_span) && (datadog_span = framework.datadog_span) Sqreen::Weave.logger.debug { "attack datadog:true span_id:#{datadog_span.span_id} parent_id:#{datadog_span.parent_id} trace_id:#{datadog_span.trace_id}" } payload.merge!( :datadog_trace_id => datadog_span.trace_id, :datadog_span_id => datadog_span.span_id, ) datadog_span.set_tag(Datadog::Ext::ManualTracing::TAG_KEEP, true) datadog_span.set_tag('sqreen.event', true) end framework.observe(:attacks, payload, payload_tpl) end # Record an exception that just occurred # @param exception [Exception] Exception to send over # @param infos [Hash] Additional contextual information def record_exception(exception, infos = {}, at = Time.now.utc) return unless framework payload = { :exception => exception, :infos => infos, :rulespack_id => rulespack_id, :rule_name => rule_name, :test => test, :time => at, :backtrace => exception.backtrace || Sqreen::Context.bt, } framework.observe(:sqreen_exceptions, payload) end # Recommend taking an action (optionnally adding more data/context) # # This will format the requested action and optionnally # override it if it should not be taken (should not block for example) def advise_action(action, additional_data = {}) return if action.nil? && additional_data.empty? additional_data.merge(:status => action) end def overtime! return false unless @overtimeable Sqreen.log.debug { "rulecb #{self} is overtime!" } return true if framework.nil? || !framework.mark_request_overtime! record_observation( 'request_overtime', rule_name, 1 ) true end Sqreen::Deprecation.deprecate(instance_method(:overtime!)) end end end