# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/agent/assess/policy/propagation_node' require 'contrast/components/interface' module Contrast module Extension module Assess # This Class provides us with a way to invoke Regexp propagation for those # methods which are too complex to fit into one of the standard # Contrast::Agent::Assess::Policy::Propagator molds without cluttering up the # Regexp Class or exposing our methods there. class RegexpPropagator include Contrast::Components::Interface access_component :analysis, :logging, :scope REGEXP_EQUAL_SQUIGGLE_HASH = { 'id' => 'regexp_100', 'class_name' => 'Regexp', 'instance_method' => true, 'method_visibility' => 'public', 'method_name' => '=~', 'action' => 'CUSTOM', 'source' => 'P0', 'target' => 'R', 'patch_class' => 'Contrast::Extension::Assess::RegexpPropagator', 'patch_method' => 'track_equal_squiggle' }.cs__freeze REGEXP_EQUAL_SQUIGGLE_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(REGEXP_EQUAL_SQUIGGLE_HASH) private_constant :REGEXP_EQUAL_SQUIGGLE_HASH private_constant :REGEXP_EQUAL_SQUIGGLE_NODE class << self def track_equal_squiggle info_hash return unless ASSESS.enabled? # Because we have a special case for this propagation, # it falls out of regular scoping. As such, any patch to the `=~` method # that goes through normal channels, like that for the redos rule, # will force this to be in a scope of 1 (instead of the normal 0). # As such, a scope of 1 here indicates that, # so we know that we're in the top level call for this method. # normal patch [-alias-]> special case patch [-alias-]> original method # TODO: RUBY-686 return if scope_for_current_ec.instance_variable_get(:@contrast_scope) > 1 target = info_hash[:back_ref] return unless Contrast::Utils::DuckUtils.trackable?(target) with_contrast_scope do result = info_hash[:result] return unless result string = info_hash[:string] return unless string string.cs__splat_tags(target) target.cs__properties.build_event( REGEXP_EQUAL_SQUIGGLE_NODE, target, self, result, [string]) end rescue Exception => e # rubocop:disable Lint/RescueException logger.error('Unable to propagate during Regexp#=~', e) end def instrument_regexp_track @_instrument_regexp_track ||= begin require 'cs__assess_regexp/cs__assess_regexp' true end rescue StandardError, LoadError => e logger.error('Error loading regexp track patch', e) false end end end end end end