# 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/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