lib/contrast/components/settings.rb in contrast-agent-5.3.0 vs lib/contrast/components/settings.rb in contrast-agent-6.0.0
- old
+ new
@@ -1,9 +1,10 @@
# 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'
+require 'contrast/agent/reporting/settings/sensitive_data_masking'
module Contrast
module Components
# This component encapsulates the statefulness of settings.
# When we say 'settings', we're referring specifically to external
@@ -11,24 +12,82 @@
# '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
+ ASSESS_STATE_BASE = Struct.new(:enabled, :sampling_settings, :disabled_assess_rules, :session_id).new(false, nil,
+ [], nil) do
def sampling_settings= new_val
@sampling_settings = new_val
Contrast::Utils::Assess::SamplingUtil.instance.update
end
end
+ SENSITIVE_DATA_MASKING_BASE = Contrast::Agent::Reporting::Settings::SensitiveDataMasking.new
# 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
+ # Current state for Assess.
+ # enabled [Boolean] Indicate if the assess feature set is enabled for this server or not.
+ #
+ # sampling [Hash<AssessSampling>] Hash of AssessSampling Used to control the sampling feature in the agent: {
+ # baseline [Integer] The number of baseline requests to take before switching to sampling
+ # for the window.
+ # enabled [Boolean] If the sampling feature should be used or not.
+ # frequency [Integer] The number of requests to skip before observing during the sampling
+ # window after the baseline.
+ # responseFrequency [Integer]
+ # window [Integer]
+ # }
+ #
+ # disabled_assess_rules [array<AssessRuleID(String)>] Assess rules to disable for this application.
+ attr_reader :assess_state
+ # Current State for Protect.
+ # enabled [Boolean] Indicate if the protect feature set is enabled for this server or not.
+ #
+ # Protection rules are returned as:
+ # rules [Hash<RULE_ID => MODE>, nil] Hash with rule_id as key and mode as value
+ attr_reader :protect_state
+ # Current Application State.
+ #
+ # modes_by_id [Hash<Rule_id => Mode] Returns Hash with rules and their current mode.
+ # exclusion_matchers [Array] Array of all the exclusions.
+ # code_exclusions [Array<CodeExclusion>] Array of CodeExclusion: {
+ # name [String] The name of the exclusion as defined by the user in TS.
+ # modes [String] If this exclusion applies to assess or protect. [assess, defend]
+ # assess_rules [Array] Array of assess rules to which this exclusion applies. AssessRuleID [String]
+ # denylist [String] The call, if in the stack, should result in the agent not taking action.
+ # input_exclusions [Array<InputExclusions>] Array of InputExclusions: {
+ # name [String] The name of the input.
+ # modes [String] If this exclusion applies to assess or protect. [assess, defend]
+ # assess_rules [Array] Array of assess rules to which this exclusion applies. AssessRuleID [String]
+ # protect_rules [Array] Array of ProtectRuleID [String] The protect rules to which this exclusion applies.
+ # urls [Array] Array of URLs to which the exclusions apply. URL [String]
+ # match_strategy [String] If this exclusion applies to all URLs or only those specified. [ALL, ONLY]
+ # type [String] The type of the input [COOKIE, PARAMETER, HEADER, BODY, QUERYSTRING]
+ # }
+ # url_exclusions [Array<UrlExclusions>] Array of UrlExclusions: {
+ # name [String] The name of the input.
+ # modes [String] If this exclusion applies to assess or protect. [assess, defend]
+ # assess_rules [Array] Array of assess rules to which this exclusion applies. AssessRuleID [String]
+ # protect_rules [Array] Array of ProtectRuleID [String] The protect rules to which this exclusion applies.
+ # urls [Array] Array of URLs to which the exclusions apply. URL [String]
+ # match_strategy [String] If this exclusion applies to all URLs or only those specified. [ALL, ONLY]
+ # type [String] The type of the input [COOKIE, PARAMETER, HEADER, BODY, QUERYSTRING]
+ # }
+ attr_reader :application_state
+ # This the structure that will hold the masking rules send from TS.
+ #
+ # @return [Contrast::Agent::Reporting::Settings::SensitiveDataMasking]:
+ # mask_http_body [Boolean] Policy flag to enable the use of masking on request body.
+ # rules [Array<Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule>]
+ # Rules to follow when using the masking. Each rules contains Id [String]
+ # and Keywords [Array<String>].
+ attr_reader :sensitive_data_masking
def initialize
reset_state
end
@@ -36,28 +95,35 @@
@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
+ if features.cs__is_a?(Contrast::Agent::Reporting::Response)
+ server_features = features.server_features
+ log_file = server_features.log_file
+ log_level = server_features.log_level
+ Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
+ @protect_state.enabled = server_features.protect.enabled?
+ @assess_state.enabled = server_features.assess.enabled?
+ @assess_state.sampling_settings = 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
+ settings = features.application_settings
+ @application_state.modes_by_id = 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
+ update_sensitive_data_policy(settings.sensitive_data_masking)
+ @assess_state.disabled_assess_rules = settings.assess.disabled_rules
+ @assess_state.session_id = settings.assess.session_id if settings.assess.session_id
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]
@@ -68,10 +134,11 @@
def reset_state
@protect_state = PROTECT_STATE_BASE.dup
@assess_state = ASSESS_STATE_BASE.dup
@application_state = APPLICATION_STATE_BASE.dup
@tainted_columns = {}
+ @sensitive_data_masking = SENSITIVE_DATA_MASKING_BASE.dup
end
def build_protect_rules
@protect_state.rules = {}
@@ -83,9 +150,40 @@
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
+
+ # Update the sensitive data masking policy from settings,
+ # received from TS. In case the settings are empty,
+ # keep current ones.
+ #
+ # @param sensitive_data_masking [Contrast::Agent::Reporting::Settings::SensitiveDataMasking]
+ # Ts Response settings for sensitive data masking policy
+ def update_sensitive_data_policy sensitive_data_masking
+ @sensitive_data_masking.mask_http_body = sensitive_data_masking.mask_http_body? unless
+ settings_empty?(sensitive_data_masking.mask_http_body?)
+ @sensitive_data_masking.mask_attack_vector = sensitive_data_masking.mask_attack_vector? unless
+ settings_empty?(sensitive_data_masking.mask_attack_vector?)
+ return if settings_empty?(sensitive_data_masking.rules)
+
+ @sensitive_data_masking.rules = sensitive_data_masking.rules
+ # update with the newly received rules.
+ Contrast::Agent::Reporting::Masker.send(:update_dictionary)
+ end
+
+ private
+
+ # check if settings are empty and return true if so.
+ #
+ # @param settings [String, Boolean, Array, Hash]
+ # @return true | false
+ def settings_empty? settings
+ return false if !!settings == settings
+ return true if settings.nil? || settings.empty?
+
+ false
end
end
end
end
end