# 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 totality of the target and then those sections # which have been removed from the target are removed from the # tags. The target's preexisting tags are also updated by this # removal. class Remove < Contrast::Agent::Assess::Policy::Propagator::Base class << self # For the source, append its tags to the target. # Once the tag is applied, remove the section that was removed by the delete. # Unlike additive propagation, this currently only supports one source def propagate propagation_node, preshift, target properties = Contrast::Agent::Assess::Tracker.properties(target) return unless properties source = find_source(propagation_node.sources[0], preshift) properties.copy_from(source, target, 0, propagation_node.untags) source_chars = source.is_a?(String) ? source.chars : source.string.chars handle_removal(source_chars, target) end def handle_removal source_chars, target properties = Contrast::Agent::Assess::Tracker.properties(target) return unless properties source_idx = 0 target_chars = target.chars target_idx = 0 remove_ranges = [] start = nil # loop over the target, the result of the delete # every range of characters that it differs from the source # represents a section that was deleted. these sections # need to have their tags updated target_len = target_chars.length while target_idx < target_len target_char = target_chars[target_idx] source_char = source_chars[source_idx] if target_char == source_char target_idx += 1 if start remove_ranges << (start...source_idx) start = nil end else start ||= source_idx end source_idx += 1 end # once we're done looping over the target, anything left # over is extra from the source that was deleted. tags # applying to it need to be removed. remove_ranges << (source_idx...source_chars.length) if source_idx != source_chars.length # handle deleting the removed ranges properties.delete_tags_at_ranges(remove_ranges) end end end end end end end end