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

require 'contrast/agent/reporting/details/sqli_dangerous_functions'

module Contrast
  module Agent
    module Protect
      module Rule
        # This class will include the check for SQL Injection Semantic Dangerous Functions
        class SqliDangerousFunctions < Contrast::Agent::Protect::Rule::SqliBaseRule
          NAME = 'sql-injection-semantic-dangerous-functions'
          BLOCK_MESSAGE = 'SQLi Semantic Dangerous Functions rule triggered. Response blocked.'

          SQL_DANGEROUS_FUNCTIONS = %w[unhex waitfor xp_cmdshell exec].cs__freeze

          def rule_name
            NAME
          end

          def infilter context, query_string
            return unless infilter?(context)
            return unless violated?(query_string)

            result = build_violation(context, query_string)
            return unless result

            append_to_activity(context, result)

            cef_logging(result, :successful_attack)
            raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
          end

          protected

          def violated? attack_string
            SQL_DANGEROUS_FUNCTIONS.any? { |dang_func| attack_string.downcase.include?(dang_func) }
          end

          def build_violation context, potential_attack_string
            result = build_attack_result(context)
            update_successful_attack_response(context, nil, result, potential_attack_string)
            append_sample(context, nil, result, potential_attack_string)
            result
          end

          # Override if rule can make use of the candidate string or kwargs to
          # build rasp rule sample.
          def build_sample context, ia_result, candidate_string, **_kwargs
            sample = build_base_sample(context, nil)
            sample.details = Contrast::Agent::Reporting::Details::SqliDangerousFunctions.new
            sample.details.query = candidate_string

            if ia_result.nil?
              ui = Contrast::Agent::Reporting::UserInput.new
              ui.input_type = :UNKNOWN
              ui.value = candidate_string
              sample.user_input = ui
            end

            sample
          end
        end
      end
    end
  end
end