# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/extension/module' 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_APPLICATOR = 'applicator' JSON_APPLICATOR_METHOD = 'applicator_method' JSON_REQUIRED_PROPS = 'required_properties' JSON_OPTIONAL_PROPS = 'optional_properties' JSON_ON_EXCEPTION = 'on_exception' attr_reader :applicator, :applicator_method, :on_exception, :optional_properties, :required_properties, :rule_id 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?(applicator_method) raise(ArgumentError, "#{ id } did not have a proper applicator method: "\ "#{ applicator } does not respond to #{ applicator_method }. Unable to create.") end validate_properties validate_rule end def validate_properties 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 return unless (required_properties - properties.keys).any? raise(ArgumentError, "#{ id } did not have a required property. Unable to create.") 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 unless applicator_method raise(ArgumentError, "#{ id } did not have a proper applicator method. Unable to create.") end unless required_properties raise(ArgumentError, "#{ id } did not have a proper set of required properties. Unable to create.") end return if optional_properties raise(ArgumentError, "#{ id } did not have a proper set of optional properties. Unable to create.") end private def class_from_string str return unless str Object.cs__const_get(str) end end end end end end