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

require 'contrast/agent/protect/rule/base_service'

module Contrast
  module Agent
    module Protect
      module Rule
        # The Ruby implementation of the Protect HTTP Method Tampering rule.
        class HttpMethodTampering < Contrast::Agent::Protect::Rule::BaseService
          NAME = 'method-tampering'
          STANDARD_METHODS = %w[GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATCH].cs__freeze

          def name
            NAME
          end

          def postfilter context
            return unless enabled? && POSTFILTER_MODES.include?(mode)
            return if normal_request?(context)

            # The only way to be here in postfilter with a result is if the rule mode was MONITOR
            ia_results = gather_ia_results(context)
            return if ia_results.empty?

            # does the status code start with 4 or 5? Rails responds with 404 (but java is checking 501)
            response_code = context&.response&.response_code
            return unless response_code

            method = ia_results.first.value
            result = if response_code.to_s.start_with?('4', '5')
                       build_attack_without_match(
                           context,
                           nil,
                           nil,
                           method: method,
                           response_code: response_code)
                     else
                       build_attack_with_match(
                           context,
                           nil,
                           nil,
                           nil,
                           method: method,
                           response_code: response_code)
                     end
            append_to_activity(context, result) if result
          end

          protected

          def build_sample context, evaluation, _candidate_string, **kwargs
            sample = build_base_sample(context, evaluation)
            sample.user_input.value = kwargs[:method]
            sample.user_input.input_type = :METHOD

            sample.method_tampering = Contrast::Api::Dtm::HttpMethodTamperingDetails.new
            sample.method_tampering.method = Contrast::Utils::StringUtils.protobuf_safe_string(kwargs[:method])
            code = kwargs[:response_code] || -1
            sample.method_tampering.response_code = code.to_i
            sample
          end

          private

          def normal_request? context
            method = context.request.request_method
            context.request.static? || method.nil? || STANDARD_METHODS.include?(method.upcase)
          end
        end
      end
    end
  end
end