# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/api/settings.pb' module Contrast module Components # This component encapsulates the statefulness of settings. # When we say 'settings', we're referring specifically to external # directives (likely provided by TeamServer) about product operation. # 'Settings' is not a generic term for 'configurable stuff'. module Settings APPLICATION_STATE_BASE = Struct.new(:modes_by_id, :exclusion_matchers). new(Hash.new(Contrast::Api::Settings::ProtectionRule::Mode::NO_ACTION), []) PROTECT_STATE_BASE = Struct.new(:enabled, :rules).new(false, {}) ASSESS_STATE_BASE = Struct.new(:enabled, :sampling_settings, :disabled_assess_rules).new(false, nil, []) do def sampling_settings= new_val @sampling_settings = new_val Contrast::Utils::Assess::SamplingUtil.instance.update end end # This is a class. class Interface extend Contrast::Components::Config # tainted_columns are database columns that receive unsanitized input. attr_reader :tainted_columns # This can probably go into assess_state? attr_reader :assess_state, :protect_state, :application_state def initialize reset_state end def code_exclusions @application_state.exclusion_matchers.select(&:code?) end # @param features [Contrast::Api::Settings::ServerFeatures, Contrast::Agent::Reporting::Response] def update_from_server_features features if features&.class == Contrast::Agent::Reporting::Response @protect_state.enabled = features.server_features.protect.enabled? @assess_state.enabled = features.server_features.assess.enabled? @assess_state.sampling_settings = features.server_features.assess.sampling else @protect_state.enabled = features.protect_enabled? @assess_state.enabled = features.assess_enabled? @assess_state.sampling_settings = features.assess.sampling end end # @param features [Contrast::Api::Settings::ApplicationSettings, Contrast::Agent::Reporting::Response] def update_from_application_settings features if features&.class == Contrast::Agent::Reporting::Response @application_state.modes_by_id = features.application_settings.protect.protection_rules_to_settings_hash # TODO: RUBY-1438 this needs to be translated # @application_state.exclusion_matchers = new_vals[:exclusion_matchers] @assess_state.disabled_assess_rules = features.application_settings.assess.disabled_rules else new_vals = features.application_state_translation @application_state.modes_by_id = new_vals[:modes_by_id] @application_state.exclusion_matchers = new_vals[:exclusion_matchers] @assess_state.disabled_assess_rules = new_vals[:disabled_assess_rules] end end # Wipe state to zero. def reset_state @protect_state = PROTECT_STATE_BASE.dup @assess_state = ASSESS_STATE_BASE.dup @application_state = APPLICATION_STATE_BASE.dup @tainted_columns = {} end def build_protect_rules @protect_state.rules = {} # Rules. They add themselves on initialize. Contrast::Agent::Protect::Rule::CmdInjection.new Contrast::Agent::Protect::Rule::Deserialization.new Contrast::Agent::Protect::Rule::HttpMethodTampering.new Contrast::Agent::Protect::Rule::NoSqli.new Contrast::Agent::Protect::Rule::PathTraversal.new Contrast::Agent::Protect::Rule::Sqli.new Contrast::Agent::Protect::Rule::UnsafeFileUpload.new Contrast::Agent::Protect::Rule::Xss.new Contrast::Agent::Protect::Rule::Xxe.new end end end end end