# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/components/logger' require 'contrast/agent/telemetry/exception/event' module Contrast module Agent module Telemetry # This is the Telemetry::Hash, which will store Contrast::Agent::Telemetry::Exception::Event, so we can push # freely, without worrying about validating the event before that. Telemetry::Hash has a max size of events, # default is 10 events class Hash < Hash include Contrast::Components::Logger::InstanceMethods HASH_SIZE_LIMIT = 10 # Wrapper to set a value in this Telemetry::Hash only if the provided value is of the data_type for this # Telemetry::Hash or the hash has not reached its limit of for unique keys # # @param key [Object] the key to which to associate the value # @param value [Object] the value to store # @return [Object, nil] echo back out the value as the Hash#[]= method does, or nil if not of the expected # data_type def []= key, value # If telemetry is not running, do not add more as we want to avoid a memory leak. return unless Contrast::Agent.telemetry_queue&.running? # If the Hash is full, do not add more as we want to avoid consuming all application resources. return if exception_limit? # If the given value is of unexpected type, do not add it to avoid issues later where type is assumed. return unless valid_value?(value) super(key, value) end # Increment the occurrences for the exception object contained in this Telemetry::Hash # # @param key [Object] the key to check for the exception stored in this Telemetry::Hash def increment key value = self[key] return unless value&.exceptions&.any? value.exceptions[0].increment_occurrences end # Determine if hash has reached exception event limit # @return [Boolean] def exception_limit? unless size < HASH_SIZE_LIMIT logger.debug("Number of TelemetryExceptions exceeds limit of #{ HASH_SIZE_LIMIT }") return true end false end private # Determine if the given value is valid based on the datatype which this Telemetry::Hash contains # @param value [Object] # @return boolean def valid_value? value unless value.cs__is_a?(Contrast::Agent::Telemetry::Exception::Event) logger.debug('The following key will be omitted', value: value) return false end true end end end end end