# Copyright (c) 2020 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 class MethodPolicy attr_accessor :source_node, :propagation_node, :trigger_node, :inventory_node, :protect_node, :deadzone_node, :method_name, :method_visibility, :instance_method def initialize(source_node: nil, propagation_node: nil, trigger_node: nil, inventory_node: nil, protect_node: nil, deadzone_node: nil, method_name: nil, method_visibility: nil, instance_method: nil) @method_name = method_name @method_visibility = method_visibility @instance_method = instance_method @source_node = source_node @propagation_node = propagation_node @trigger_node = trigger_node @inventory_node = inventory_node @protect_node = protect_node @deadzone_node = deadzone_node end def private_method? method_visibility == :private end def empty? return false if source_node return false if propagation_node return false if trigger_node return false if inventory_node return false if protect_node return false if deadzone_node true end def requires_custom_patch? !!@trigger_node&.custom_patch? end class << self # 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) 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) end def find_method_node nodes, method_name, is_instance_method return nil 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 end end end end end end