# Copyright (c) 2021 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 target at the point of insertion. The target's # preexisting tags are shifted to account for this insertion. class Insert < Contrast::Agent::Assess::Policy::Propagator::Base class << self # For the source, append its tags to the target. # Once the tag is applied, shift it to the location of the insert # Unlike additive propagation, this currently only supports one source # We assume that insert changes the preshift target def propagate propagation_node, preshift, target return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target)) source = find_source(propagation_node.sources[1], preshift) patcher_target = propagation_node.targets[0] preshift_target = case patcher_target when Contrast::Utils::ObjectShare::OBJECT_KEY preshift.object else preshift.args[int] end # Find the first difference between the source to which # we inserted and the result. That is the insertion # point on which all tags need to be adjusted # If the insertion point is the end of the string, preshift length is returned # https://stackoverflow.com/questions/31714522/find-the-first-differing-character-between-two-strings-in-ruby insert_point = (0...preshift_target.length).find { |i| preshift_target[i] != target[i] } || preshift_target.length # Depending what's inserted, we might be wrong. For instance, inserting 'foo' # into 'asdfasdf' could result in 'asdfoofasdf'. we'd be off by one b/c of the 'f' insert_point = target.rindex(source, insert_point) overflow = insert_point...(insert_point + source.length) # handle shifting the inserted range properties.shift_tags([overflow]) properties.copy_from(source, target, insert_point, propagation_node.untags) properties.cleanup_tags end end end end end end end end