# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true 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 # This is a class. class Interface include Contrast::Components::ComponentBase include Contrast::Components::Interface access_component :config attr_reader :assess_rules, :protect_rules # Other stateful information that doesn't yet cleanly fit anywhere: # tainted_columns are database columns that receive unsanitized input. # this statefulness attr_reader :tainted_columns # This can probably go into assess_state? # These three 'state' variables represent atomic config/setting state, # outside of things like rule defs. def assess_state @assess_state ||= { # rubocop:disable Naming/MemoizedInstanceVariableName enabled: false, sampling_features: nil } end def protect_state @protect_state ||= { # rubocop:disable Naming/MemoizedInstanceVariableName enabled: false } end def application_state @application_state ||= { # rubocop:disable Naming/MemoizedInstanceVariableName modes_by_id: Hash.new(:NO_ACTION), exclusion_matchers: [], disabled_assess_rules: [] } end # These are settings that we receive & store. # Rules are settings too, but they're more involved. # So, between this block and rules, that's setting state. PROTECT_STATE_ATTRS = %i[].cs__freeze ASSESS_STATE_ATTRS = %i[sampling_features].cs__freeze APPLICATION_STATE_ATTRS = %i[modes_by_id exclusion_matchers disabled_assess_rules].cs__freeze # Meta-define an accessor for each state attribute. PROTECT_STATE_ATTRS.each do |attr| # TODO: RUBY-1052 define_method(attr) do # rubocop:disable Kernel/DefineMethod protect_state[attr] end end ASSESS_STATE_ATTRS.each do |attr| # TODO: RUBY-1052 define_method(attr) do # rubocop:disable Kernel/DefineMethod assess_state[attr] end end APPLICATION_STATE_ATTRS.each do |attr| # TODO: RUBY-1052 define_method(attr) do # rubocop:disable Kernel/DefineMethod application_state[attr] end end def initialize reset_state end def protect_enabled? @_protect_enabled = !!protect_state[:enabled] if @_protect_enabled.nil? @_protect_enabled end def assess_enabled? @_assess_enabled = !!assess_state[:enabled] if @_assess_enabled.nil? @_assess_enabled end def code_exclusions exclusion_matchers.select(&:code?) end def update_from_server_features server_features # protect @_protect_enabled = nil protect_state[:enabled] = server_features.protect_enabled? # assess @_assess_enabled = nil assess_state[:enabled] = server_features.assess_enabled? assess_state[:sampling_settings] = server_features.assess.sampling Contrast::Utils::Assess::SamplingUtil.instance.update end def update_from_application_settings application_settings application_state.merge!(application_settings.application_state_translation) end # Wipe state to zero. def reset_state @assess_rules = {} @protect_rules = {} @tainted_columns = {} @assess_state = nil @protect_state = nil @application_state = nil end def build_assess_rules @assess_rules = {} Contrast::Agent::Assess::Rule::Redos.new end def build_protect_rules @protect_rules = {} # rules 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 COMPONENT_INTERFACE = Interface.new end end end