# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/agent/protect/rule/deserialization' require 'contrast/agent/protect/policy/rule_applicator' module Contrast module Agent module Protect module Policy # This Module is how we apply the Deserialization rule. It is called from # our patches of the targeted methods in which deserialization occurs. # It is responsible for deciding if the infilter methods of the rule # should be invoked. module AppliesDeserializationRule extend Contrast::Agent::Protect::Policy::RuleApplicator class << self # Calls the actual rule for this applicator, if required. Most rules # invoke this from within their apply_rule method after doing # whatever transformations they need to get into this common format. # # @param _method [Symbol] the name of the method for which this rule # is invoked # @param _exception [Exception] any exception raised; used for rules # like Padding Oracle Attack (now defunct), which determine if the # number and type of exceptions are an attack # @param _properties [Hash] set of extra information provided by the # applicator in an attempt to build a better story for the user # @param _object [Object] the thing on which the triggering method # was invoked # @param args [Array] the arguments passed to the triggering # method at invocation # @raise [Contrast::SecurityException] on block, will pass the # exception from the rule def invoke _method, _exception, _properties, _object, args return unless valid_input?(args) return if skip_analysis? rule.infilter(Contrast::Agent::REQUEST_TRACKER.current, args[0]) end # Calls the actual rule for this applicator, if required, when the # triggering method is called from Marshal.load when it has been # prepended. # # @param arg [Object] the argument passed to the triggering method # at invocation # @raise [Contrast::SecurityException] on block, will pass the # exception from the rule def prepended_invoke arg return unless arg&.cs__is_a?(String) return if skip_analysis? rule.infilter(Contrast::Agent::REQUEST_TRACKER.current, arg) end # Allow the rule to check if the given input is an attempt to # deserialize something in a way that will result in a command # execution # # @param command [String] user input that potentially contains a # Gadget, a Module that results in code execution on # deserialization, and some form of command. def apply_deserialization_command_check command return unless command return if skip_analysis? rule.check_command_scope(command) end protected def rule_name Contrast::Agent::Protect::Rule::Deserialization::NAME end private # Determine if the rule would care about the arguments passed in # this method invocation. Required b/c Ruby doesn't do type # checking on its method signatures. # # @param args [Array] the arguments provided to the # instrumented method # @return [Boolean] def valid_input? args return false unless args&.any? input = args[0] input.cs__is_a?(String) end end end end end end end