# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/components/scope' module Contrast module Agent module Patching module Policy # This class functions to translate our policy.json into an actionable # Ruby object, allowing for dynamic patching over hardcoded patching. # # @abstract class PolicyNode include Contrast::Components::Scope::InstanceMethods attr_accessor :class_name, :instance_method, :method_name, :method_visibility attr_reader :properties, :method_scope def node_class raise NoMethodError, 'specify the type of the feature for which this node patches' end def feature raise NoMethodError, 'specify the name of the feature for which this node patches' end def initialize policy_hash = {} @class_name = policy_hash[JSON_CLASS_NAME] @instance_method = policy_hash[JSON_INSTANCE_METHOD] @method_name = policy_hash[JSON_METHOD_NAME] @method_scope = policy_hash[JSON_METHOD_SCOPE] @method_visibility = policy_hash[JSON_METHOD_VISIBILITY] @properties = policy_hash[JSON_PROPERTIES] symbolize end def id @_id ||= "#{ feature }:#{ node_class }:#{ class_name }#{ instance_method? ? '#' : '.' }#{ method_name }" end # Don't let nodes be created that will be missing things we need # later on. Really, if they don't have these things, they couldn't have # done their jobs anyway. def validate unless class_name raise(ArgumentError, "#{ node_class } #{ id } did not have a proper class name. Unable to create.") end unless method_name raise(ArgumentError, "#{ node_class } #{ id } did not have a proper method name. Unable to create.") end unless method_name.is_a?(Symbol) raise(ArgumentError, "#{ node_class } #{ id } has a non symbol @method_name value. Unable to create.") end unless method_visibility.is_a?(Symbol) raise(ArgumentError, "#{ node_class } #{ id } has a non symbol @method_visibility value. Unable to create.") end unless method_scope.nil? || Contrast::Agent::Scope.valid_scope?(method_scope) raise(ArgumentError, "#{ node_class } #{ id } requires an undefined scope. Unable to create.") end nil end # just turns this into a ruby-ism def instance_method? instance_method end private # Convert strings to symbols here, once, to avoid doing so on every # comparison at runtime def symbolize @method_name = @method_name.to_sym if @method_name @method_visibility = @method_visibility.to_sym if @method_visibility @method_scope = @method_scope.to_sym if @method_scope end # The keys used to read from policy.json to create the individual # policy nodes. These are common across node types JSON_CLASS_NAME = 'class_name' JSON_INSTANCE_METHOD = 'instance_method' JSON_METHOD_NAME = 'method_name' JSON_METHOD_SCOPE = 'scope' JSON_METHOD_VISIBILITY = 'method_visibility' JSON_PROPERTIES = 'properties' end end end end end