# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/utils/object_share' require 'contrast/agent/reporting/input_analysis/input_type' require 'contrast/agent/protect/rule/sqli' require 'contrast/agent/reporting/input_analysis/score_level' require 'contrast/agent/protect/rule/sqli/sqli_worth_watching' require 'contrast/agent/protect/input_analyzer/input_analyzer' module Contrast module Agent module Protect module Rule # This module will do the Input Classification stage of SQLI rule # as a result input would be marked as WORTHWATCHING or IGNORE, # to be analyzed at the sink level. module SqliInputClassification class << self include Contrast::Agent::Reporting::InputType include Contrast::Agent::Reporting::ScoreLevel include Contrast::Agent::Protect::Rule::SqliWorthWatching WORTHWATCHING_MATCH = 'sqli-worth-watching-v2' SQLI_KEYS_NEEDED = [ COOKIE_VALUE, PARAMETER_VALUE, JSON_VALUE, MULTIPART_VALUE, XML_VALUE, DWR_VALUE ].cs__freeze # Input Classification stage is done to determine if an user input is # WORTHWATCHING or to be ignored. # # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input. # @param value [String, Array] the value of the input. # @param input_analysis [Contrast::Agent::Reporting::InputAnalysis] Holds all the results from the # agent analysis from the current # Request. # @return ia [Contrast::Agent::Reporting::InputAnalysis] with updated results. def classify input_type, value, input_analysis return unless Contrast::Agent::Protect::Rule::Sqli::APPLICABLE_USER_INPUTS.include?(input_type) return unless input_analysis.request rule_id = Contrast::Agent::Protect::Rule::Sqli::NAME results = [] # double check the input to avoid calling match? on array Array(value).each do |val| Array(val).each do |v| results << sqli_create_new_input_result(input_analysis.request, rule_id, input_type, v) end end input_analysis.results = results input_analysis end private # Creates new isntance of InputAnalysisResult with basic info. # # @param rule_id [String] The name of the Protect Rule. # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input. # @param score_level [Contrast::Agent::Reporting::ScoreLevel] the score tag after analysis. # @param value [String, Array] the value of the input. # @param path [String] the path of the current request context. # # @return res [Contrast::Agent::Reporting::InputAnalysisResult] def new_ia_result rule_id, input_type, score_level, path, value res = Contrast::Agent::Reporting::InputAnalysisResult.new res.rule_id = rule_id res.input_type = input_type res.path = path res.score_level = score_level res.value = value res end # This methods checks if input is tagged WORTHWATCHING or IGNORE matches value with it's # key if needed and Creates new isntance of InputAnalysisResult. # # @param request [Contrast::Agent::Request] the current request context. # @param rule_id [String] The name of the Protect Rule. # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input. # @param value [String, Array] the value of the input. # # @return res [Contrast::Agent::Reporting::InputAnalysisResult] def sqli_create_new_input_result request, rule_id, input_type, value result = if sqli_worth_watching? value ia_result = new_ia_result(rule_id, input_type, WORTHWATCHING, request.path, value) ia_result.ids << WORTHWATCHING_MATCH ia_result else new_ia_result(rule_id, input_type, IGNORE, request.path, value) end if SQLI_KEYS_NEEDED.include? input_type result.key = sqli_add_needed_key request, result, input_type, value end result end # This methods checks if input is value that matches a key in the input. # # @param request [Contrast::Agent::Request] the current request context. # @param result [Contrast::Agent::Reporting::InputAnalysisResult] result to be updated. # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input. # @param value [String, Array] the value of the input. # # @return result [Contrast::Agent::Reporting::InputAnalysisResult] updated with key result. def sqli_add_needed_key request, result, input_type, value case input_type when COOKIE_VALUE result.key = request.cookies.key(value) when PARAMETER_VALUE result.key = request.parameters.key(value) else result.key end result.key end end end end end end end