# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/components/base' require 'contrast/config/exception_configuration' require 'contrast/config/protect_rule_configuration' module Contrast module Components module Protect # A wrapper build around the Common Agent Configuration project to allow for access of the values contained in # its parent_configuration_spec.yaml. Specifically, this allows for querying the state of the Protect product. class Interface include Contrast::Components::ComponentBase include Contrast::Config::BaseConfiguration CANON_NAME = 'protect' CONFIG_VALUES = %w[enabled?].cs__freeze RULES = 'rules' MODE = 'mode' # @return [Boolean, nil] attr_accessor :enable # @return [String] attr_reader :canon_name # @return [Array] attr_reader :config_values # @return [Boolean, nil] attr_accessor :agent_lib def initialize hsh = {} @config_values = CONFIG_VALUES @canon_name = CANON_NAME return unless hsh @_exceptions = Contrast::Config::ExceptionConfiguration.new(hsh[:exceptions]) @_rules = Contrast::Config::ProtectRulesConfiguration.new(hsh[:rules]) @enable = hsh[:enable] @agent_lib = hsh[:agent_lib] end # @return [Contrast::Config::ExceptionConfiguration] def exceptions @_exceptions ||= Contrast::Config::ExceptionConfiguration.new end # Name is kept the same - rules to correspond to config, # mapping. - protect.rules # # @return [Contrast::Config::ProtectRulesConfiguration] def rules @_rules ||= Contrast::Config::ProtectRulesConfiguration.new end def rules= new_rules @_rules = new_rules end def exceptions= new_exceptions @_exceptions = new_exceptions end def enabled? # config overrides if forcibly set return false if forcibly_disabled? return true if forcibly_enabled? ::Contrast::SETTINGS.protect_state.enabled == true end # Current Configuration for the protect rules # # @return [Contrast::Config::ProtectRulesConfiguration] def rule_config ::Contrast::CONFIG.protect.rules end # Returns Protect array of all initialized # protect rules. # # @return defend_rules[Hash] def defend_rules ::Contrast::SETTINGS.protect_state.rules end # The Contrast::CONFIG.protect.rules is object so we need to check it's # corresponding method call for each rule of interest. If there is no # status available we search for any Settings available received form # TS response. # # @param rule_id [String] # @return mode [Symbol] def rule_mode rule_id str = rule_id.tr('-', '_') ::Contrast::CONFIG.protect.rules[str]&.applicable_mode || ::Contrast::SETTINGS.application_state.modes_by_id[rule_id] || :NO_ACTION end # Name of the protect rule # # @return [String] def rule name ::Contrast::SETTINGS.protect_state.rules[name] end def report_any_command_execution? if @_report_any_command_execution.nil? ctrl = rule_config[Contrast::Agent::Protect::Rule::CmdInjection::NAME] @_report_any_command_execution = ctrl && true?(ctrl.disable_system_commands) end @_report_any_command_execution end def report_custom_code_sysfile_access? if @_report_custom_code_sysfile_access.nil? name_changed = Contrast::Agent::Protect::Rule::PathTraversal::NAME.tr('-', '_') ctrl = rule_config[name_changed] @_report_custom_code_sysfile_access = ctrl && true?(ctrl.detect_custom_code_accessing_system_files) end @_report_custom_code_sysfile_access end def forcibly_disabled? return @_forcibly_disabled unless @_forcibly_disabled.nil? @_forcibly_disabled = false?(::Contrast::CONFIG.protect.enable) end # Converts current configuration to effective config values class and appends them to # EffectiveConfig class. # # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig] def to_effective_config effective_config super protect_rules_to_effective_config(effective_config) end private # @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig] def protect_rules_to_effective_config effective_config return unless defend_rules defend_rules.each do |key, value| next unless key && value config_prefix = "#{ CANON_NAME }.#{ RULES }.#{ key }" name_prefix = "#{ CONTRAST }.#{ CANON_NAME }.#{ RULES }.#{ key }" add_single_effective_value(effective_config, ENABLE, value.enabled?, config_prefix, name_prefix) # Get the mode by checking Current Configs or Settings received: add_single_effective_value(effective_config, MODE, rule_mode(key), config_prefix, name_prefix) end end def forcibly_enabled? return @_forcibly_enabled unless @_forcibly_enabled.nil? @_forcibly_enabled ||= true?(::Contrast::CONFIG.protect.enable) end end end end end