# frozen_string_literal: true require 'bson' require 'date' require 'opentracing/instrumentation/mongo/direct_sanitazer' module OpenTracing module Instrumentation module Mongo # QuerySanitazer clean private data from requests. class QuerySanitazer < DirectSanitazer DEFAULT_SAFE_CLASSES = [ TrueClass, FalseClass, Numeric, Date, Time, ].freeze DEFAULT_EXCLUDE_KEYS = %w[lsid].freeze def initialize( safety_argument_checker: SampleSafetyArgumentChecker.new, safe_classes: DEFAULT_SAFE_CLASSES, max_array_size: 4, exclude_keys: DEFAULT_EXCLUDE_KEYS ) super() @safety_argument_checker = safety_argument_checker @safe_classes = safe_classes @max_array_size = max_array_size @exclude_keys = exclude_keys end def sanitaze(command, command_name) command_without_command_name = super(command, command_name) exclude_keys.each do |key| command_without_command_name.delete(key) end sanitaze_value(command_without_command_name) end private attr_reader :safe_classes, :exclude_keys, :max_array_size, :safety_argument_checker OBJECT_ID_PLACEHOLDER = '$oid' STRING_PLACEHOLDER = '$string' PLACEHOLDER = '?' def sanitaze_value(value) case value when Hash sanitaze_hash(value) when Enumerable sanitaze_array(value) else sanitaze_simple(value) end end # rubocop:disable Metrics/MethodLength def sanitaze_simple(value) case value when BSON::ObjectId OBJECT_ID_PLACEHOLDER when String STRING_PLACEHOLDER when ArrayItemPlaceholder value.to_s when *safe_classes value else PLACEHOLDER end end # rubocop:enable Metrics/MethodLength def sanitaze_hash(hash) hash.map do |(key, value)| # TODO: pass command name. # TODO: recursive build path to key safe_value = if safety_argument_checker.argument_safe?(nil, key, value) value else sanitaze_value(value) end [key, safe_value] end.to_h end # Placehoder for skipped array items class ArrayItemPlaceholder def initialize(skipped_items) @skipped_items = skipped_items end def to_s "SKIPPED #{@skipped_items} ITEMS" end end def sanitaze_array(array) if max_array_size&.positive? && array.size > max_array_size array_with_placeholder(array) else array end.map do |value| sanitaze_value(value) end end def array_with_placeholder(array) prefix_size = (max_array_size / 2).ceil suffix_size = max_array_size - prefix_size array[0...prefix_size] \ + [ArrayItemPlaceholder.new(array.size - max_array_size)] \ + array[-suffix_size..-1] end end end end end