# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/agent/protect/rule/base_service' require 'contrast/agent/request_context' require 'contrast/utils/object_share' require 'contrast/agent/protect/rule/cmdi/cmdi_base_rule' module Contrast module Agent module Protect module Rule # The Ruby implementation of the Protect Command Injection Semantic # Chained Command sub-rule. This rule should report class CmdiChainedCommand < Contrast::Agent::Protect::Rule::CmdiBaseRule NAME = 'cmd-injection-semantic-chained-commands' def rule_name NAME end def sub_rules Contrast::Utils::ObjectShare::EMPTY_ARRAY end protected # Used to customize the raised error message. # # @param classname [String] Name of the class # @param method [String] name of the method triggering the rule # @raise [Contrast::SecurityException] def raise_error classname, method raise(Contrast::SecurityException.new(self, 'Command Injection Semantic Chained Commands rule triggered. ' \ "Call to #{ classname }.#{ method } blocked.")) end private def find_probable_attacker context, potential_attack_string, _ia_results, **kwargs chained = chained_command?(potential_attack_string) return unless chained build_attack_with_match(context, nil, nil, potential_attack_string, **kwargs) end # Check if potential chained attack is detected in user input. # # @param command [String] command to check. # @return index[Boolean Returns the index of the command chaining if found. # If the chaining index is >= 0, an injection is detected. Returns -1 when no # command chaining is found. def chained_command? command return false unless (agent_lib = Contrast::AGENT_LIB) && command return false if agent_lib.chained_cmdi_index(command).negative? true end end end end end end