# Copyright (c) 2023 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 exactly as is. The target's preexisting tags # are unaffected beyond any merging of overlapping tags. class MatchData < Contrast::Agent::Assess::Policy::Propagator::Base class << self # This patch method is used to track through the MatchData#[] and # MatchData#match methods, since the last was introduced after # Ruby 3.1.0, but shares similar functionality, except it does not # support ranges. # # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the # propagation action required by this method. # @param preshift [Object] pre call state of the things. # @param ret [Object] the return value of the method. # @param _block [Proc] the block passed to the method. # @return [Object] the return value of the method. def square_bracket_tagger propagation_node, preshift, ret, _block case ret when Array idx = 0 while idx < ret.length return_value = ret[idx] index = idx idx += 1 next unless return_value square_bracket_single(target_matchdata_index(preshift, index), preshift, return_value, propagation_node) end when String target_matchdata_index = preshift.args[0] square_bracket_single(target_matchdata_index, preshift, ret, propagation_node) end ret end # Captures is a method that returns an array of MatchData objects. # # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the # propagation action required by this method. # @param preshift [Object] pre call state of the things. # @param ret [Object] the return value of the method. # @param _block [Proc] the block passed to the method. # @return [Object] the return value of the method. def captures_tagger propagation_node, preshift, ret, _block return unless ret idx = 0 while idx < ret.length return_value = ret[idx] index = idx idx += 1 next unless return_value targetted_index = index + 1 square_bracket_single(targetted_index, preshift, return_value, propagation_node) end ret end # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the # propagation action required by this method. # @param preshift [Object] pre call state of the things. # @param ret [Object] the return value of the method. # @param _block [Proc] the block passed to the method. # @return [Object] the return value of the method. def to_a_tagger propagation_node, preshift, ret, _block idx = 0 while idx < ret.length return_value = ret[idx] index = idx idx += 1 next unless return_value square_bracket_single(index, preshift, return_value, propagation_node) end ret end # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the # propagation action required by this method. # @param preshift [Object] pre call state of the things. # @param ret [Object] the return value of the method. # @param _block [Proc] the block passed to the method. # @return [Object] the return value of the method. def values_at_tagger propagation_node, preshift, ret, _block idx = 0 while idx < ret.length return_value = ret[idx] return_index = idx idx += 1 next unless return_value original_group_arg_index = preshift.args[return_index] square_bracket_single(original_group_arg_index, preshift, return_value, propagation_node) end ret end private # @param preshift [Object] pre call state of the things. # @param index [Integer] the index of the argument to retrieve. # @return [Integer] the index of the argument to retrieve. def target_matchdata_index preshift, index if preshift.args[0].is_a?(Range) arg_range = preshift.args[0] arg_range.to_a.empty? ? index + 1 : arg_range.to_a[index] else preshift.args[index] end end # Handles the propagation of a single argument. # # @param argument_index [Integer] the index of the argument to retrieve. # @param preshift [Object] pre call state of the things. # @param return_value [Object] the return value of the method. # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the # propagation action required by this method. # @return [Object] the return value of the method. def square_bracket_single argument_index, preshift, return_value, propagation_node original_start_index = preshift.object.begin(argument_index) original_end_index = preshift.object.end(argument_index) return unless (original_properties = Contrast::Agent::Assess::Tracker.properties(preshift.object)) applicable_tags = original_properties.tags_at_range(original_start_index...original_end_index) return if applicable_tags.empty? return unless (return_properties = Contrast::Agent::Assess::Tracker.properties!(return_value)) applicable_tags.each do |tag_name, tag_ranges| return_properties.set_tags(tag_name, tag_ranges) end event_data = Contrast::Agent::Assess::Events::EventData.new(propagation_node, return_value, preshift.object, return_value, preshift.args) return_properties.build_event(event_data) end end end end end end end end