# Copyright (c) 2021 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/logger' require 'contrast/components/scope' module Contrast module Extension module Assess # This Class provides us with a way to invoke String 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 # String Class or exposing our methods there. class StringPropagator extend Contrast::Components::Logger::InstanceMethods extend Contrast::Components::Scope::InstanceMethods NODE_HASH = { 'class_name' => 'String', 'instance_method' => true, 'method_name' => 'interpolate', 'method_visibility' => 'public', 'action' => 'CUSTOM', 'source' => 'O,P0', 'target' => 'R', 'patch_class' => 'NOOP', 'patch_method' => 'track_interpolation' }.cs__freeze INTERPOLATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH) class << self def track_interpolation inputs, result return unless ::Contrast::AGENT.interpolation_enabled? return if in_contrast_scope? return unless inputs.any? { |input| Contrast::Agent::Assess::Tracker.tracked?(input) } with_contrast_scope do return unless (properties = Contrast::Agent::Assess::Tracker.properties!(result)) parent_events = [] offset = 0 inputs.each do |input| properties.copy_from(input, result, offset) offset += input.length parent_event = Contrast::Agent::Assess::Tracker.properties(input)&.event parent_events << parent_event if parent_event end properties.build_event(INTERPOLATION_NODE, result, inputs, result, inputs) properties.event.instance_variable_set(:@_parent_events, parent_events) end rescue StandardError => e logger.error('Unable to track interpolation', e) end def instrument_string @_instrument_string ||= begin require 'cs__assess_string/cs__assess_string' true end rescue StandardError, LoadError => e logger.error('Error loading hash track patch', e) false end def instrument_string_interpolation if @_instrument_string_interpolation.nil? @_instrument_string_interpolation = begin if ::Contrast::AGENT.patch_interpolation? && Funchook.available? require 'cs__assess_string_interpolation26/cs__assess_string_interpolation26' end true rescue StandardError, LoadError => e logger.error('Error loading interpolation patch', e) false end end @_instrument_string_interpolation end end end end end end