lib/contrast/utils/class_util.rb in contrast-agent-4.10.0 vs lib/contrast/utils/class_util.rb in contrast-agent-4.11.0

- old
+ new

@@ -1,15 +1,17 @@ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/extension/module' require 'contrast/utils/object_share' +require 'contrast/utils/lru_cache' module Contrast module Utils # Utility methods for exploring the complete space of Objects class ClassUtil + @lru_cache = LRUCache.new class << self # some classes have had things prepended to them, like Marshal in Rails # 5 and higher. Their ActiveSupport::MarshalWithAutoloading will break # our alias patching approach, as will any other prepend on something # that we touch. Prepend and Alias are inherently incompatible monkey @@ -45,30 +47,35 @@ # form expected by our dataflow events. # # @param object [Object, nil] the entity to convert to a String # @return [String] the human readable form of the String, as defined by # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/vulnerability/capture-snapshot.md + def to_contrast_string object - # Only treat object like a string if it actually is a string + # After implementing the LRU Cache, we firstly need to check if already had that object cached + # and if we have it - we can return it directly + return @lru_cache[object.__id__] if @lru_cache.key? object.__id__ + + # Only treat object like a string if it actually is a string+ # some subclasses of String override string methods we depend on - if object.cs__class == String - cached = to_cached_string(object) - return cached if cached + @lru_cache[object.__id__] = if object.cs__class == String + cached = to_cached_string(object) + return cached if cached - object.dup - elsif object.nil? - Contrast::Utils::ObjectShare::NIL_STRING - elsif object.cs__is_a?(Symbol) - ":#{ object }" - elsif object.cs__is_a?(Module) || object.cs__is_a?(Class) - "#{ object.cs__name }@#{ object.__id__ }" - elsif object.cs__is_a?(Regexp) - object.source - elsif use_to_s?(object) - object.to_s - else - "#{ object.cs__class.cs__name }@#{ object.__id__ }" - end + object.dup + elsif object.nil? + Contrast::Utils::ObjectShare::NIL_STRING + elsif object.cs__is_a?(Symbol) + ":#{ object }" + elsif object.cs__is_a?(Module) || object.cs__is_a?(Class) + "#{ object.cs__name }@#{ object.__id__ }" + elsif object.cs__is_a?(Regexp) + object.source + elsif use_to_s?(object) + object.to_s + else + "#{ object.cs__class.cs__name }@#{ object.__id__ }" + end end # The method const_defined? can cause autoload, which is bad for us. # The method autoload? doesn't traverse namespaces. This method lets us # provide a constant, as a String, and parse it to determine if it has