# Copyright (c) 2023 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/utils/stack_trace_utils' require 'contrast/utils/object_share' require 'contrast/components/logger' require 'contrast/agent/reporting/input_analysis/input_type' require 'contrast/agent_lib/interface' require 'contrast/agent/protect/rule/cmdi/cmdi_chained_command' require 'contrast/agent/protect/rule/cmdi/cmdi_dangerous_path' require 'contrast/agent/protect/rule/cmdi/cmdi_base_rule' require 'contrast/agent/protect/rule/cmdi/cmdi_backdoors' module Contrast module Agent module Protect module Rule # The Ruby implementation of the Protect Command Injection rule. class CmdInjection < Contrast::Agent::Protect::Rule::CmdiBaseRule include Contrast::Components::Logger::InstanceMethods include Contrast::Agent::Reporting::InputType NAME = 'cmd-injection' APPLICABLE_USER_INPUTS = [ BODY, COOKIE_VALUE, HEADER, PARAMETER_NAME, PARAMETER_VALUE, JSON_VALUE, MULTIPART_VALUE, MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE ].cs__freeze def rule_name NAME end # Array of sub_rules: # # @return [Array] def sub_rules @_sub_rules ||= [ Contrast::Agent::Protect::Rule::CmdiBackdoors.new, Contrast::Agent::Protect::Rule::CmdiChainedCommand.new, Contrast::Agent::Protect::Rule::CmdiDangerousPath.new ].cs__freeze end def applicable_user_inputs APPLICABLE_USER_INPUTS end # CMDI infilter: # # @param context [Contrast::Agent::RequestContext] current request context # @param classname [String] Name of the class # @param method [String] name of the method triggering the rule # @param command [String] potential dangerous command executed. # @raise [Contrast::SecurityException] if the rule mode is set # to BLOCK and valid cdmi is detected. def infilter context, classname, method, command return unless infilter?(context) ia_results = gather_ia_results(context) return if ia_results.empty? if ::Contrast::APP_CONTEXT.in_new_process? logger.trace('Running cmd-injection infilter within new process - creating new context') context = Contrast::Agent::RequestContext.new(context.request.rack_request) Contrast::Agent::REQUEST_TRACKER.update_current_context(context) end result = find_attacker_with_results(context, command, ia_results, **{ classname: classname, method: method }) result ||= report_command_execution(context, command, **{ classname: classname, method: method }) return unless result append_to_activity(context, result) cef_logging(result, :successful_attack) return unless blocked? # Raise cmdi error raise_error(classname, method) end end end end end end