# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/agent/reaction_processor' require 'contrast/components/logger' module Contrast module Api module Communication # Handles processing deferred messages sent to SpeedRacer. # # @attr_reader last_app_update_ms [Integer] the time, in ms, that application settings last changed # @attr_reader last_server_update_ms [Integer] the time, in ms, that server settings last changed class ResponseProcessor include Contrast::Components::Logger::InstanceMethods attr_reader :last_app_update_ms, :last_server_update_ms # Use the given response to update the Agent's server features and application settings, allowing it to reflect # the latest options configured by the user in TeamServer # # @param response [Contrast::Api::Settings::AgentSettings] def process response logger.debug('Received a response', sent_ms: response&.sent_ms) server_features = process_server_response(response) app_settings = process_application_response(response) # ReactionProcessor is a design pattern from TeamServer. Right now, there's one potential reaction, which is # disabling the agent Contrast::Agent::ReactionProcessor.process(response&.application_settings) Contrast::Logger::Log.instance.update(server_features&.log_file, server_features&.log_level) update_features(server_features, app_settings) logger.trace('Agent settings updated in response to Service', protect_on: ::Contrast::PROTECT.enabled?, assess_on: ::Contrast::ASSESS.enabled?) end private # Given some protobuf messages, update server features. This is the bridge between SpeedRacer <-> Settings. # # @param response [Contrast::Api::Settings::AgentSettings] # @return [Contrast::Api::Settings::ServerFeatures] def process_server_response response server_features = response&.server_features return unless server_features logger.trace('Agent: Received updated server features') ::Contrast::SETTINGS.update_from_server_features(server_features) @last_server_update_ms = Contrast::Utils::Timer.now_ms server_features end # Given some protobuf messages, update application settings. This is the bridge between SpeedRacer <-> # Settings. # # @param response [Contrast::Api::Settings::AgentSettings] # @return [Contrast::Api::Settings::ApplicationSettings] def process_application_response response app_settings = response&.application_settings return unless app_settings logger.debug('Agent: Received updated application settings') ::Contrast::SETTINGS.update_from_application_settings(app_settings) @last_app_update_ms = Contrast::Utils::Timer.now_ms app_settings end # This can't go in the Settings component because protect and assess depend on settings # I don't think it should go into contrast_service because that only handles connection specific data. # # @param server_features [Contrast::Api::Settings::AgentSettings, Array] # @param app_settings [Contrast::Api::Settings::ApplicationSettings, Array] def update_features server_features, app_settings return unless !!(server_features || app_settings) return unless ::Contrast::AGENT.enabled? logger.trace_with_time('Rebuilding rule modes') do ::Contrast::SETTINGS.build_protect_rules if ::Contrast::PROTECT.enabled? ::Contrast::AGENT.reset_ruleset logger.info('Current rule settings:') ::Contrast::PROTECT.rules.each { |k, v| logger.info('Protect Rule mode set', rule: k, mode: v.mode) } logger.info('Disabled Assess Rules', rules: ::Contrast::ASSESS.disabled_rules) end end end end end end