# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'rack' require 'contrast/utils/hash_digest' require 'contrast/utils/string_utils' require 'contrast/agent/assess/rule/response/base_rule' module Contrast module Agent module Assess module Rule module Response # These rules check the content of the HTTP Response to determine if something was set incorrectly or # insecurely in it. class HeaderRule < BaseRule HEADER_TYPE = 'Header' # Rules discern which responses they can/should analyze. # # @param response [Contrast::Agent::Response] the response of the application def analyze_response? response super && headers?(response) end # Determine if the Response violates the Rule or not. If it does, return the evidence that proves it so. # # @param response [Contrast::Agent::Response] the response of the application # @return [Hash, nil] the evidence required to prove the violation of the rule def violated? response header_value = get_header_value(response) if header_value return evidence(header_value) unless valid_header?(header_value) else return evidence(header_value) unless cs__class::DEFAULT_SAFE end nil end # Determine if a response has headers. # # @param response [Contrast::Agent::Response] the response of the application # @return [Boolean] def headers? response !!response.headers&.any? end protected # @param response [Contrast::Agent::Response] # @return [Object] def get_header_value response response_headers = response.headers values = response_headers.values_at(*cs__class::HEADER_KEYS, *cs__class::HEADER_KEYS.map(&:to_sym)) values.compact.first end # Determine if the value of the Response Header has a valid value # # @param header [String] a response header # @return [Boolean] whether the header value is valid def valid_header? header cs__class::ACCEPTED_VALUES.any? { |val| val.match(header) } end end end end end end end