# Copyright (c) 2015 Sqreen. All Rights Reserved. # Please refer to our terms for more information: https://www.sqreen.io/terms.html require 'sqreen/context' require 'sqreen/runtime_infos' require 'sqreen/events/remote_exception' module Sqreen # Create a payload from a given query # # Template elements are made of sections and subsections. # This class is able to send the full content of section or # only the required subsections as needed. # # The payload will always be outputed as a # Hash of section => subsection. class PayloadCreator def initialize(query) self.query = query end def query=(keys) @sections = {} keys.each do |key| section, subsection = key.split('.', 2) @sections[section] = true if subsection.nil? next if @sections[section] == true @sections[section] ||= [] @sections[section].push(subsection) end end def payload(framework, rule = {}) ret = {} METHODS.each_key do |section| ret = fill(section, ret, framework, rule) end ret end protected def fill(key, base, framework, rule) subsection = @sections[key] return base if subsection.nil? if subsection == true return base.merge!(key => full_section(key, framework, rule)) end return base if subsection.size == 0 base[key] = fields(key, framework, rule) base end FULL_SECTIONS = { 'request' => 'request_infos', 'params' => 'filtered_request_params', 'local' => 'local_infos', }.freeze METHODS = { 'request' => { 'addr' => 'client_ip', 'rid' => 'request_id', }, 'local' => { 'name' => 'hostname', }, 'params' => { 'form' => 'form_params', 'query' => 'query_params', 'cookies' => 'cookies_params', 'rails' => 'rails_params', }, 'rule' => {}, 'context' => { 'backtrace' => 'get_current_backtrace', }, }.freeze def section_object(section, framework, rule) return RuntimeInfos if section == 'local' return rule if section == 'rule' return Context.new if section == 'context' framework end def full_section(section, framework, rule) return section_rule(framework, rule) if section == 'rule' return section_context(framework, rule) if section == 'context' so = section_object(section, framework, rule) so.send(FULL_SECTIONS[section]) end def fields(section, framework, rule) out = {} object = section_object(section, framework, rule) remove = [] @sections[section].each do |key| meth = METHODS[section][key] invoke(out, key, object, meth || key, remove) end remove.each { |k| @sections[section].delete(k) } Hash[out] end def invoke(out, key, object, method, remove) out[key] = if object.respond_to?(:[]) object[method] else object.send(method) end rescue NoMethodError => e remove.push(key) Sqreen::RemoteException.record(e) end def section_context(framework, rule) obj = section_object('context', framework, rule) { 'backtrace' => obj.get_current_backtrace, } end def section_rule(_framework, rule) { 'name' => rule['name'], 'rulespack_id' => rule['rulespack_id'], 'test' => rule['test'], } end end end