# 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/agent/reporting/reporting_utilities/dtm_message'
require 'contrast/utils/hash_digest'
require 'contrast/utils/preflight_util'
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

            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 [Contrast::Agent::Response] 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