# Copyright (c) 2015 Sqreen. All Rights Reserved. # Please refer to our terms for more information: https://www.sqreen.io/terms.html require 'set' require 'sqreen/shared_storage' require 'sqreen/log' module Sqreen class CB # Callback class. # # Three methods can be defined: # - pre(*args, &block) # To be called prior to the hooked method. # - post(return_value, *args, &block) # To be called after the hooked method. The return_value argument is # the value returned by the hooked method. # - failing(exception, ...) # To be called when the method raise # The method pre, post and exception may return nil or a Hash. # - nil: the original method is called and the callback has no further # effect # - { :status => :skip }: we skip the original method call # - { :status => :raise}: # # - nil: the original return value is returned, as if coallback had no # effect # - { :status => :raise}: # - { :status => :override }: # # - nil: reraise # - { :status => :reraise }: reraise # - { :status => :override }: eat exception # - { :retry => :retry }: try the block again # # CB can also declare that they are whitelisted and should not be run at the moment. attr_reader :klass, :method attr_reader :overtimeable def initialize(klass, method) @method = method @klass = klass @has_pre = respond_to? :pre @has_post = respond_to? :post @has_failing = respond_to? :failing @overtimeable = false raise(Sqreen::Exception, 'No callback provided') unless @has_pre || @has_post || @has_failing end def whitelisted? false end def pre? @has_pre end def post? @has_post end def failing? @has_failing end def to_s format('#<%s: %s.%s>', self.class, @klass, @method) end def overtime! Sqreen.log.debug { "#{self} is overtime!" } @overtimeable end def framework nil end end # target_method, position, callback, callback class class DefaultCB < CB def pre(_inst, args, _budget = nil, &_block) Sqreen.log.debug "<< #{@klass} #{@method} #{Thread.current}" Sqreen.log.debug args.join ' ' # log params end def post(_rv, _inst, _args, _budget = nil, &_block) # log "#{rv}" Sqreen.log.debug ">> #{@klass} #{@method} #{Thread.current}" end end class RunWhenCalledCB < CB def initialize(klass, method, &block) super(klass, method) raise 'missing block' unless block_given? @block = block end def pre(_inst, _args, _budget = nil, &_block) # FIXME: implement this removal @remove_me = true @block.call end end # Framework-aware callback class FrameworkCB < CB attr_accessor :framework def whitelisted? whitelisted = SharedStorage.get(:whitelisted) return whitelisted unless whitelisted.nil? framework && !framework.whitelisted_match.nil? 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, :test => test, :time => at, } if payload_tpl.include?('context') payload[:backtrace] = Sqreen::Context.new.bt end framework.observe(:attacks, payload, payload_tpl) end # Record a metric observation # @param category [String] Name of the metric observed # @param key [String] aggregation key # @param observation [Object] data observed # @param at [Time] time when observation was made def record_observation(category, key, observation, at = Time.now.utc) return unless framework framework.observe(:observations, [category, key, observation, at], [], false) 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 end end