# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Agent module Patching module Policy # This class is used to map each method to the trigger node that applies to it module MethodPolicyExtend # Given a Contrast::Agent::Patching::Policy::ModulePolicy, parse # out its information for the given method in order to construct a # Contrast::Agent::Patching::Policy::MethodPolicy # # @param method_name [Symbol] the name of the method for this policy # @param module_policy [Contrast::Agent::Patching::Policy::ModulePolicy] # the entire policy for this module # @param instance_method [Boolean] true if this method is an # instance method # @return [Contrast::Agent::Patching::Policy::MethodPolicy] def build_method_policy method_name, module_policy, instance_method source_node = find_method_node(module_policy.source_nodes, method_name, instance_method) propagation_node = find_method_node(module_policy.propagator_nodes, method_name, instance_method) trigger_node = find_method_node(module_policy.trigger_nodes, method_name, instance_method) protect_node = find_method_node(module_policy.protect_nodes, method_name, instance_method) inventory_node = find_method_node(module_policy.inventory_nodes, method_name, instance_method) deadzone_node = find_method_node(module_policy.deadzone_nodes, method_name, instance_method) method_visibility = find_visibility(source_node, propagation_node, trigger_node, protect_node, inventory_node, deadzone_node) method_policy = MethodPolicy.new({ method_name: method_name, method_visibility: method_visibility, instance_method: instance_method, source_node: source_node, propagation_node: propagation_node, trigger_node: trigger_node, protect_node: protect_node, inventory_node: inventory_node, deadzone_node: deadzone_node }) return method_policy unless check_method_policy_nodes_empty?(source_node, propagation_node, trigger_node, protect_node, inventory_node, deadzone_node) create_new_node(module_policy, method_policy) if module_policy.deadzone_nodes&.any? method_policy end def find_method_node nodes, method_name, is_instance_method return unless nodes nodes.find do |node| node.instance_method? == is_instance_method && node.method_name == method_name end end def find_visibility *nodes nodes.find { |node| node }&.method_visibility end def check_method_policy_nodes_empty?(source_node, propagation_node, trigger_node, protect_node, inventory_node, deadzone_node) return false unless source_node.nil? && propagation_node.nil? && trigger_node.nil? && protect_node.nil? && inventory_node.nil? && deadzone_node.nil? true end private def create_new_node module_policy, method_policy return if module_policy.deadzone_nodes.empty? module_policy.deadzone_nodes.map do |node| next unless node.method_name.nil? klass = Module.cs__const_get(node.class_name) next unless it_defined?(klass, method_policy.method_name) new_node = set_new_node(method_policy, klass, node) method_policy.instance_variable_set(:@method_visibility, new_node.method_visibility) method_policy.instance_variable_set(:@deadzone_node, node) module_policy.deadzone_nodes << new_node break unless method_policy.deadzone_node.nil? end end # Helper method for creating new node # # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] # used to map each method to the trigger node that applies to it # @param klass [String] classname # @param node [Contrast::Agent::Patching::Policy::PolicyNode] # @return @_set_new_node [Contrast::Agent::Deadzone::Policy::DeadzoneNode] def set_new_node method_policy, klass, node new_node = {} new_node['instance_method'] = method_policy.instance_method new_node['method_visibility'] = klass.private_method_defined?(method_policy.method_name) ? 'private' : 'public' new_node['method_name'] = method_policy.method_name new_node['class_name'] = node.class_name @_set_new_node = Contrast::Agent::Deadzone::Policy::DeadzoneNode.new(new_node) end def it_defined? klass, method_name klass.instance_methods(false).include?(method_name) || klass.private_instance_methods(false).include?(method_name) || klass.singleton_methods(false).include?(method_name) end end end end end end