# Copyright (c) 2015 Sqreen. All Rights Reserved. # Please refer to our terms for more information: https://www.sqreen.io/terms.html require 'json' require 'sqreen/event' module Sqreen # When a request is deeemed worthy of being sent to the backend class RequestRecord < Sqreen::Event def observed (payload && payload[:observed]) || {} end def to_hash res = { :version => '20171208' } if payload[:observed] res[:observed] = payload[:observed].dup rulespack = nil if observed[:attacks] res[:observed][:attacks] = observed[:attacks].map do |att| natt = att.dup rulespack = natt.delete(:rulespack_id) || rulespack natt end end if observed[:sqreen_exceptions] res[:observed][:sqreen_exceptions] = observed[:sqreen_exceptions].map do |exc| nex = exc.dup excp = nex.delete(:exception) if excp nex[:message] = excp.message nex[:klass] = excp.class.name end rulespack = nex.delete(:rulespack_id) || rulespack nex end end res[:rulespack_id] = rulespack unless rulespack.nil? if observed[:observations] res[:observed][:observations] = observed[:observations].map do |cat, key, value, time| { :category => cat, :key => key, :value => value, :time => time } end end if observed[:sdk] res[:observed][:sdk] = processed_sdk_calls end end res[:local] = payload['local'] if payload['local'] if payload['request'] res[:request] = payload['request'].dup res[:client_ip] = res[:request].delete(:client_ip) if res[:request][:client_ip] else res[:request] = {} end res[:request][:parameters] = payload['params'] if payload['params'] res[:request][:headers] = payload['headers'] if payload['headers'] if Sqreen.config_get(:strip_sensitive_data) res[:request] = SensitiveDataRedactor.redact(res[:request]) end res end private def processed_sdk_calls auth_keys = last_identify_id observed[:sdk].map do |meth, time, *args| { :name => meth, :time => time, :args => inject_identifiers(args, meth, auth_keys), } end end def inject_identifiers(args, meth, auth_keys) return args unless meth == :track && auth_keys track_opts = args[1] || {} if track_opts[:user_identifiers].nil? args[1] = track_opts.dup args[1][:user_identifiers] = auth_keys elsif track_opts[:user_identifiers] != auth_keys Sqreen.log.warn 'Sqreen.identify and Sqreen.track have been called ' \ 'with different user_identifiers values' end args end def last_identify_id return nil unless observed[:sdk] observed[:sdk].reverse_each do |meth, _time, *args| next unless meth == :identify return args.first if args.respond_to? :first end nil end end # For redacting sensitive data and avoid having it sent to our servers class SensitiveDataRedactor SENSITIVE_KEYS = Set.new(%w[password secret passwd authorization api_key apikey access_token]).freeze MASK = ''.freeze REGEX = /\A(?:\d[ -]*?){13,16}\z/ def self.redact(obj) case obj when String return MASK if obj =~ REGEX when Array return obj.map(&method(:redact)) when Hash return Hash[ obj.map do |k, v| ck = k.is_a?(String) ? k.downcase : k [k, SENSITIVE_KEYS.include?(ck) ? MASK : redact(v)] end ] end obj end end end