# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: true

cs__scoped_require 'contrast/components/interface'

# This patch installs our extension as early as possible. The alternative is to
# litter our code with responds_to? checks.
class String
  include Contrast::CoreExtensions::Assess::AssessExtension
end

module Contrast
  module CoreExtensions
    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
        include Contrast::Components::Interface

        access_component :analysis, :agent, :logging, :scope

        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 AGENT.interpolation_enabled?
            return if in_contrast_scope?
            return unless inputs.any?(&:cs__tracked?)

            with_contrast_scope do
              offset = 0
              inputs.each do |input|
                result.cs__copy_from(input, offset)
                offset += input.length
              end
              result.cs__properties.build_event(INTERPOLATION_NODE, result, inputs, result, inputs)
            end
          end

          def instrument_string_interpolation
            if @_instrument_string_interpolation.nil?
              @_instrument_string_interpolation = begin
                                                    if AGENT.patch_interpolation?
                                                      cs__scoped_require 'cs__assess_string_interpolation26/cs__assess_string_interpolation26'
                                                    end
                                                    true
                                                  rescue StandardError => e
                                                    logger.error(e, 'Error loading interpolation patch')
                                                    false
                                                  end
            end
            @_instrument_string_interpolation
          end
        end
      end
    end
  end
end

cs__scoped_require 'cs__assess_string/cs__assess_string'

Contrast::CoreExtensions::Assess::StringPropagator.instrument_string_interpolation