# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true cs__scoped_require 'securerandom' cs__scoped_require 'set' cs__scoped_require 'contrast/utils/assess/sampling_util' cs__scoped_require 'contrast/utils/assess/tracking_util' cs__scoped_require 'contrast/utils/environment_util' cs__scoped_require 'contrast/utils/env_configuration_item' cs__scoped_require 'contrast/utils/gemfile_reader' cs__scoped_require 'contrast/utils/object_share' cs__scoped_require 'contrast/utils/performs_logging' cs__scoped_require 'contrast/utils/string_utils' cs__scoped_require 'contrast/agent/reaction_processor' cs__scoped_require 'contrast/agent/require_state' cs__scoped_require 'contrast/agent/assess/policy/patcher' cs__scoped_require 'contrast/agent/patching/policy/patcher' cs__scoped_require 'contrast/components/interface' module Contrast module Agent # This class functions as a way to query the Agent for its current # configuration without having to expose other sections of code to the # decision tree needed to make that determination. # It should not be accessed directly, but should instead be inherited from # # @abstract Use the methods in FeatureState to access this data class SettingsState include Contrast::Utils::PerformsLogging include Contrast::Components::Interface INVALID_CONFIG = '!!! CONFIG FILE IS INVALID - DISABLING CONTRAST AGENT !!!' access_component :agent, :analysis, :settings, :config, :contrast_service MODE_WHITELIST = %i[NO_ACTION BLOCK_AT_PERIMETER MONITOR BLOCK].cs__freeze # These are components. attr_accessor :last_update, :log_file, :log_level # These represent process-level attributes, # not directly related to Contrast function itself. attr_reader :pid, :ppid def initialize @instrumentation_mutex = Mutex.new @instrumented_packages = {} # Last we heard from the Contrast Service @last_update = nil # keep track of which process instantiated this instance @pid = Process.pid.to_i @ppid = Process.ppid.to_i init_log_queueing check_config flush_log_queues end def check_config SETTINGS.reset_state if CONFIG.invalid? AGENT.disable! self.class.log_error(INVALID_CONFIG) abort_log_queues elseif CONFIG.disabled? AGENT.disable! self.class.log_warn(LOG_CONFIGURATION_DISABLED) else AGENT.enable! end end # workaround to mixed use of class/instance def self.logger instance.logger end def logger logger_manager.current end def logger_manager (@_logger_manager ||= Contrast::Agent::LoggerManager.new.tap(&:update)) end def assess_rule name ASSESS.rule name end def instrument package return if @instrumented_packages[package] # double check is intentional @instrumentation_mutex.synchronize do return if @instrumented_packages[package] @instrumented_packages[package] = true end self.class.debug_with_time("instrumenting #{ package }") do cs__scoped_require(package) end rescue LoadError, StandardError => e @instrumented_packages[package] = false self.class.log_error("[e.class: #{ e.class }] unable to instrument: #{ package }", e) end def protect_rule name PROTECT.rule name end def send_inventory_message return unless INVENTORY.enabled? msg = Contrast::Api::Dtm::ApplicationUpdate.new msg.platform = Contrast::Api::Dtm::Platform.new msg.platform.major, msg.platform.minor, msg.platform.build = *Contrast::Utils::EnvironmentUtil.platform Contrast::Utils::EnvironmentUtil.add_library_to_app_update(msg, protobuf_format(CONFIG.root.inventory.tags)) Contrast::Utils::EnvironmentUtil.scan_views(msg) Contrast::Utils::EnvironmentUtil.scan_routes(msg) Contrast::Utils::InventoryUtil.append_db_config(msg) CONTRAST_SERVICE.queue_message msg end def present? str Contrast::Utils::EnvironmentUtil.present?(str) end # CONTRAST-35326, move this responsibility toward the protobuf object def protobuf_format param, truncate: true param = param&.to_s param = Contrast::Utils::StringUtils.force_utf8(param) param = Contrast::Utils::StringUtils.truncate(param) if truncate param end end end end