# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/agent/patching/policy/patch' require 'contrast/agent/patching/policy/patcher' require 'contrast/components/interface' module Contrast module Extension module Assess # This is our patch of the Array class required to handle propagation # Disclaimer: there may be a better way, but we're in a 'get it work' state. # Hopefully, we'll be in a 'get it right' state soon. class ArrayPropagator include Contrast::Components::Interface access_component :scope ARRAY_JOIN_HASH = { 'class_name' => 'Array', 'instance_method' => true, 'method_visibility' => 'public', 'method_name' => 'join', 'action' => 'CUSTOM', 'source' => 'O', 'target' => 'R', 'patch_class' => 'NOOP', 'patch_method' => 'cs__track_join' }.cs__freeze ARRAY_JOIN_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(ARRAY_JOIN_HASH) class << self # When you call join, they use an internal thing, so there's no good way to get at the thing being returned. # Multiple Strings are appended with the #join method. Because that # operation happens in C, we have to do it here rather than rely on the # patch of our String append or concat methods. def cs__track_join ary, separator, ret return unless ary return ret if Contrast::Agent::Patching::Policy::Patch.skip_assess_analysis? with_contrast_scope do shift = 0 separator_length = separator.nil? ? 0 : separator.to_s.length ary.each do |obj| if obj # skip nil here ret.cs__copy_from(obj, shift) shift += obj.to_s.length end shift += separator_length end return ret unless ret.cs__tracked? ret.cs__properties.cleanup_tags ret.cs__properties.build_event( ARRAY_JOIN_NODE, ret, ary, ret, [separator]) ret end end def instrument_array_track @_instrument_array_track ||= begin require 'cs__assess_array/cs__assess_array' true end rescue StandardError, LoadError => e logger.error('Error loading assess track patch', e) false end end end end end end