# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'json' require 'contrast/agent/assess/rule/provider/hardcoded_value_rule' require 'contrast/agent/assess/rule/provider/hardcoded_key' require 'contrast/agent/assess/rule/provider/hardcoded_password' require 'contrast/agent/assess/policy/policy_node' require 'contrast/agent/assess/policy/source_node' require 'contrast/agent/assess/policy/propagation_node' require 'contrast/agent/assess/policy/trigger_node' require 'contrast/agent/patching/policy/policy' module Contrast module Agent module Assess module Policy # This is just a holder for our policy. Takes the policy JSON and # converts it into hashes that we can access nicely class Policy < Contrast::Agent::Patching::Policy::Policy PROVIDER_CLASSES = [ Contrast::Agent::Assess::Rule::Provider::HardcodedKey, Contrast::Agent::Assess::Rule::Provider::HardcodedPassword ].cs__freeze # Indicates the folder in `resources` where this policy lives. def self.policy_folder 'assess' end def initialize super load_providers end # Indicates is this feature has been disabled by the configuration, # read at startup, and therefore can never be enabled. # # @return [Boolean] if this feature is disabled def disabled_globally? ::Contrast::ASSESS.forcibly_disabled? end def node_type Contrast::Agent::Assess::Policy::TriggerNode end # Our policy for dataflow rules is a 'dope ass' JSON file. Rather than # hard code in a bunch of things to monkey patch, we let the JSON file # define the conditions in which sources, propagators, and triggers are # applied. # This let's us be flexible and extensible # * when we want to do lvl 2 rules, we could have the customers unzip # our gem, insert things into the json, zip, and go * # # @param string [String] def from_hash_string string # The default behavior of the agent is to load the policy on startup, # as at this point we do not know in which mode we'll be run. # # If the configuration file explicitly disables a feature, we know # that we will not ever be able to enable it, so in that case, we # can skip policy loading. return if disabled_globally? policy_data = Contrast::Utils::Json.parse(string) policy_data[SOURCES_KEY].each do |source_hash| source = Contrast::Agent::Assess::Policy::SourceNode.new(source_hash) add_node(source, :source) end policy_data[PROPAGATION_KEY].each do |propagator_hash| prop = Contrast::Agent::Assess::Policy::PropagationNode.new(propagator_hash) add_node(prop, :propagator) end policy_data[RULES_KEY].each do |rule_hash| rule_hash[TRIGGERS_KEY].each do |trigger_hash| trigger_node = node_type.new(trigger_hash, rule_hash) add_node(trigger_node) end end end # Providers is a term that we're taking from Java until we come up with # a name that we (I) don't hate. Basically, these are more static like # rules. They don't do dataflow or response scanning. Instead, they # watch for things to be loaded (configs, classes, whateves) and # determine if these loaded things are unsafe. # # ** if we want, we could add this as a section to the aforementioned # 'dope ass' JSON def load_providers PROVIDER_CLASSES.each do |clazz| instance = clazz.new providers[instance.rule_id] = instance end end end end end end end