# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true cs__scoped_require 'contrast/core_extensions/module' cs__scoped_require 'contrast/agent/patching/policy/policy_node' 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, # specifically for those methods which result in the trigger of an # attack (indicate points in the application where uncontrolled user # input attempted to or did do damage). class TriggerNode < PolicyNode JSON_NAME = 'name' JSON_NODES = 'nodes' JSON_APPLICATOR = 'applicator' JSON_APPLICATOR_METHOD = 'applicator_method' JSON_REQUIRED_PROPS = 'required_properties' JSON_OPTIONAL_PROPS = 'optional_properties' JSON_ON_EXCEPTION = 'on_exception' attr_reader :rule_id attr_accessor :applicator_method, :applicator, :on_exception, :required_properties, :optional_properties def initialize trigger_hash = {}, rule_hash = {} super(trigger_hash) @rule_id = rule_hash[JSON_NAME] @on_exception = rule_hash[JSON_ON_EXCEPTION] # returns nil in most cases @required_properties = rule_hash[JSON_REQUIRED_PROPS] @optional_properties = rule_hash[JSON_OPTIONAL_PROPS] @applicator = class_from_string(rule_hash[JSON_APPLICATOR]) # if a unique applicator method is defined for this method (rare case), preference getting that one. # otherwise, fall back to the normal applicator method for this rule @applicator_method = (trigger_hash[JSON_APPLICATOR_METHOD] || rule_hash[JSON_APPLICATOR_METHOD]).to_sym end NODE = 'Trigger' def node_class NODE end def validate super unless applicator.public_methods(false).any? { |method| method == applicator_method } raise(ArgumentError, "#{ id } did not have a proper applicator method: #{ applicator } does not respond to #{ applicator_method }. Unable to create.") end if (required_properties & optional_properties).any? raise(ArgumentError, "#{ rule_id } had overlapping elements between required and optional properties. Unable to create.") end if (properties.keys - (required_properties | optional_properties)).any? raise(ArgumentError, "#{ id } had an unexpected property. Unable to create.") end raise(ArgumentError, "#{ id } did not have a required property. Unable to create.") if (required_properties - properties.keys).any? validate_rule end def validate_rule raise(ArgumentError, 'Unknown rule did not have a proper name. Unable to create.') unless rule_id raise(ArgumentError, "#{ id } did not have a proper applicator. Unable to create.") unless applicator raise(ArgumentError, "#{ id } did not have a proper applicator method. Unable to create.") unless applicator_method raise(ArgumentError, "#{ id } did not have a proper set of required properties. Unable to create.") unless required_properties raise(ArgumentError, "#{ id } did not have a proper set of optional properties. Unable to create.") unless optional_properties end private def class_from_string str return unless str Object.cs__const_get(str) end end end end end end