class QueryHandler

Public Class Methods

new(opts) click to toggle source
# File lib/prevoty/query_handler.rb, line 10
def initialize(opts)
    @base = opts[:api_base] ||= 'https://api.prevoty.com'
    @client = ::Prevoty::Client.new(opts[:api_key], @base)
    @policy_key = opts[:policy_key] ||= ''
    @mode = opts[:mode] ||= 'monitor'
    @log_verbosity = opts[:log_verbosity] ||= 'incident'
    @reporting_milliseconds = opts[:reporting_milliseconds] ||= 10000
    @reporting_count = opts[:reporting_count] ||= 50
    @db_vendor = opts[:db_vendor] ||= ''
    @db_version = opts[:db_version] ||= ''
    @db_name = opts[:db_name] ||= ''
    @violation_mode = opts[:violation_mode] ||= 'block'
    @failure_mode = opts[:failure_mode] ||= 'continue'

    if @mode === 'monitor'
      @monitor = ::Prevoty::QueryMonitor.new(@client, opts)
    end
end

Public Instance Methods

handle(name, start, finish, id, payload) click to toggle source
# File lib/prevoty/query_handler.rb, line 29
def handle(name, start, finish, id, payload)
  # ActiveRecord does some wonky stuff on startup that causes a query to be
  # executed that doesn't seem to pass through the rack middleware so the
  # request_storage key doesn't exist. It's unclear if there are other
  # situations that also cause this but because of it we need to use try here
  # to ensure there are no nil pointer exceptions
  if Thread.current[:request_storage].try(:[], :skip_processing)
    return
  end

  unless payload[:name] =~ /SQL|Load/
    return
  end

  case @mode
  when 'monitor'
    package = {vendor: @db_vendor, database: @db_name, version: @db_version, query: payload[:sql]}
    @monitor.process(package)
  when 'protect'
    begin
      resp = nil
      ActiveSupport::Notifications.instrument('prevoty:query:protect') do |resp_payload|
        resp = resp_payload[:response] = @client.analyze_query(payload[:sql], @policy_key)
      end

      # always log because we're either set to all or incident
      if resp.processed and not resp.compliant
        ::Prevoty::LOGGER << build_result(@mode, payload[:sql], resp).to_json + "\n"
        raise ::Prevoty::QueryViolation.new if @violation_mode === 'block'
      elsif resp.processed and resp.compliant
        # all good, only log if log verbosity is all
        if @log_verbosity === 'all'
          ::Prevoty::LOGGER << build_result(@mode, payload[:sql], resp).to_json + "\n"
        end
      elsif not resp.processed and not resp.compliant
        # query failure
        if @log_verbosity === 'all'
          ::Prevoty::LOGGER << build_result(@mode, payload[:sql], resp).to_json + "\n"
        end
        ActiveSupport::Notifications.instrument('prevoty:query:failure', response: resp)
        raise ::Prevoty::QueryFailure.new
      end
    rescue ::Prevoty::QueryViolation => e
      # need to catch and rethrow to catch other exceptions
      raise e
    rescue ::Prevoty::QueryFailure => e
      # need to catch and rethrow to catch other exceptions
      raise e
    rescue Exception => e
      Rails.logger.error e.message
      raise e if @failure_mode === 'block'
    end
  end
end