# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Agent module Assess module Policy module Propagator # Propagation that results in all the tags of the source being # applied to the end of the target. The target's preexisting tags are # unaffected beyond any merging of overlapping tags. class Append < Contrast::Agent::Assess::Policy::Propagator::Base class << self # For the source, append its tags to the target. # if the target length is greater than the source # copy tags from the param to the target in chunks of param size or less # if param is appended in space less than param length def propagate propagation_node, preshift, target return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target)) sources = propagation_node.sources source1 = find_source(sources[0], preshift) # Some appends have two args. If they don't this is probably something # obnoxious, like String.* source2 = sources[1] ? find_source(sources[1], preshift) : source1 # if the object and the return are the same length just copy the tags # from the object(since nothing from args was added to return) if source1.length == target.length properties.copy_from(source1, target, 0, propagation_node.untags) else handle_append(propagation_node, source1, source2, target, properties) end properties.cleanup_tags end private # Given the append operation on source 1 added source 2 to it, changing the target output, modify the # tags on the target to account for the change. # # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the # propagation action required by this method # @param source1 [Object] the thing being appended to # @param source2 [Object] the thing being appended # @param target [Object] the result of the append operation # @param properties [Contrast::Agent::Assess::Properties] the properties of the target def handle_append propagation_node, source1, source2, target, properties # find original in the target, copy tags to the new position in # target original_start_index = target.index(source1) properties.copy_from(source1, target, original_start_index, propagation_node.untags) start = original_start_index + source1.length while start < target.length properties.copy_from(source2, target, start, propagation_node.untags) start += source2.length next unless start > target.length properties.tags_at(start - source2.length).each do |tag| tag.update_end(target.length) end end end end end end end end end end