Class: Contrast::Components::Settings::Interface

Inherits:
Object
  • Object
show all
Extended by:
Config
Includes:
Logger::InstanceMethods
Defined in:
lib/contrast/components/settings.rb

Overview

This is a class.

Constant Summary

Constants included from Config

Config::CONTRAST_LOG, Config::CONTRAST_NAME, Config::DATE_TIME

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logger::InstanceMethods

#cef_logger, #logger

Constructor Details

#initializeInterface

Returns a new instance of Interface.



94
95
96
# File 'lib/contrast/components/settings.rb', line 94

def initialize
  reset_state
end

Instance Attribute Details

#agent_stateObject (readonly)

Agent state. Used for extracting Agent level settings.

logger_path Path to the log file. logger_level Log level for the logger. cef_logger_path Path to the log file. cef_logger_level Log level for the logger.



43
44
45
# File 'lib/contrast/components/settings.rb', line 43

def agent_state
  @agent_state
end

#app_settings_last_httpdateString (readonly)

This value should be sent be TS in the Last-Modified header to sync and save resources if the two dates are the same. format: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT

Returns:

  • (String)

    The last update but in string format used to build request header.



92
93
94
# File 'lib/contrast/components/settings.rb', line 92

def app_settings_last_httpdate
  @app_settings_last_httpdate
end

#application_stateObject (readonly)

Current Application State.

modes_by_id [Hash<Rule_id => Mode] Returns Hash with rules and their current mode.



68
69
70
# File 'lib/contrast/components/settings.rb', line 68

def application_state
  @application_state
end

#assess_stateObject (readonly)

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.



58
59
60
# File 'lib/contrast/components/settings.rb', line 58

def assess_state
  @assess_state
end

#excluderContrast::Agent::Excluder (readonly)

Returns a wrapper around the exclusion rules for the application.

Returns:



82
83
84
# File 'lib/contrast/components/settings.rb', line 82

def excluder
  @excluder
end

#last_app_update_msInteger (readonly)

Returns the time, in ms, that application settings last changed.

Returns:

  • (Integer)

    the time, in ms, that application settings last changed



78
79
80
# File 'lib/contrast/components/settings.rb', line 78

def last_app_update_ms
  @last_app_update_ms
end

#last_server_update_msInteger (readonly)

Returns the time, in ms, that server settings last changed.

Returns:

  • (Integer)

    the time, in ms, that server settings last changed



80
81
82
# File 'lib/contrast/components/settings.rb', line 80

def last_server_update_ms
  @last_server_update_ms
end

#protect_stateObject (readonly)

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



64
65
66
# File 'lib/contrast/components/settings.rb', line 64

def protect_state
  @protect_state
end

#sensitive_data_maskingContrast::Agent::Reporting::Settings::SensitiveDataMasking (readonly)

This the structure that will hold the masking rules send from TS.

Returns:

  • (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>].
    


76
77
78
# File 'lib/contrast/components/settings.rb', line 76

def sensitive_data_masking
  @sensitive_data_masking
end

#server_settings_last_httpdateString (readonly)

This value should be sent be TS in the Last-Modified header to sync and save resources if the two dates are the same. format: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT

Returns:

  • (String)

    The last update but in string format used to build request header.



87
88
89
# File 'lib/contrast/components/settings.rb', line 87

def server_settings_last_httpdate
  @server_settings_last_httpdate
end

#tainted_columnsObject (readonly)

tainted_columns are database columns that receive unsanitized input.



36
37
38
# File 'lib/contrast/components/settings.rb', line 36

def tainted_columns
  @tainted_columns
end

Instance Method Details

#reset_state(purge: false) ⇒ Object

Wipe state to zero.

Parameters:

  • purge (Boolean) (defaults to: false)

    If true, also purge the persistent states.



221
222
223
224
225
226
227
228
229
230
231
# File 'lib/contrast/components/settings.rb', line 221

def reset_state purge: false
  @agent_state = AGENT_STATE_BASE.dup
  # Keep the protect state, since once set the rules depend ont it.
  # The state will be update on first settings response from TS.
  @protect_state = PROTECT_STATE_BASE.dup if purge || @protect_state.nil?
  update_assess_state
  @application_state = APPLICATION_STATE_BASE.dup
  @tainted_columns = {}
  @sensitive_data_masking = SENSITIVE_DATA_MASKING_BASE.dup
  @excluder = Contrast::Agent::Excluder.new
end

#update_agent_lib_log(new_log_level) ⇒ Object

Update AgentLib log level



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/contrast/components/settings.rb', line 170

def update_agent_lib_log new_log_level
  agent_lib_log_level = Contrast::AgentLib::InterfaceBase::LOG_LEVEL[0] if new_log_level.empty?
  agent_lib_log_level ||= Contrast::AgentLib::InterfaceBase::LOG_LEVEL.key(new_log_level.upcase)

  # detect if the provided level is invalid and log if it is
  # by default if we pass invalid log level - it will leave the last active
  unless Contrast::AgentLib::InterfaceBase::LOG_LEVEL.value?(new_log_level.upcase)
    cur_active = Contrast::AGENT_LIB.log_level
    logger.debug('The provided level was invalid, so the logger stays to the last active: ',
                 active: cur_active,
                 provided_level: new_log_level)
  end

  Contrast::AGENT_LIB.change_log_options(true, agent_lib_log_level)
end

#update_assess_server_features(assess) ⇒ Object

Update Assess server features



124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/contrast/components/settings.rb', line 124

def update_assess_server_features assess
  return if settings_empty?(assess.enabled?)

  @assess_state.enabled = assess.enabled?
  update_config_from_settings(%i[assess enable], assess.enabled?)
  @assess_state.sampling_settings = assess.sampling

  samplings_path = Contrast::Components::Sampling::Interface::CANON_NAME.split('.').map(&:to_sym)
  Contrast::Components::Sampling::Interface::CONFIG_VALUES.each do |field|
    lookup_field = field == 'enable' ? :enabled : field.to_sym
    update_config_from_settings(samplings_path + [field.to_sym], assess.sampling.send(lookup_field))
  end
end

#update_assess_stateStruct

We save the session_id, reset and set it again if available. This done so that reporting between updates won’t trigger argument error for missing session_id given one is already set and used with the first application create response received from TS.

Returns:

  • (Struct)


239
240
241
242
243
244
245
246
# File 'lib/contrast/components/settings.rb', line 239

def update_assess_state
  current_session_id = @assess_state&.session_id
  @assess_state = ASSESS_STATE_BASE.dup
  # There is application settings update for the session id if new is received.
  # Here we make sure not to delete the already set one.
  @assess_state&.session_id = current_session_id unless current_session_id&.empty?
  @assess_state
end

#update_exclusion_matchers(exclusions) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/contrast/components/settings.rb', line 249

def update_exclusion_matchers exclusions
  matchers = []
  exclusions.url_exclusions.each do |exclusion|
    matchers << Contrast::Agent::ExclusionMatcher.new(exclusion)
  end
  exclusions.input_exclusions.each do |exclusion|
    matchers << Contrast::Agent::ExclusionMatcher.new(exclusion)
  end
  # Do not populate the matchers unless we have any. There are certain checks in
  # SourceMethod that will safe-guard return if there are no exclusions received.
  # The matching operation is expensive, and the excluder calls are made for each
  # source, and we do not want to check for exclusions if they are empty. This is
  # probably redundant as all exclusions default to empty, but will save useless
  # new object creation at very least.
  return if Contrast::Utils::DuckUtils.empty_duck?(matchers)

  @excluder = Contrast::Agent::Excluder.new(matchers)
end

#update_from_application_settings(settings_response) ⇒ Object

Parameters:



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/contrast/components/settings.rb', line 187

def update_from_application_settings settings_response
  return unless (app_settings = settings_response&.application_settings)

  extract_protect_app_settings(app_settings)
  update_matchers_and_sensitive_data(app_settings)
  @assess_state.disabled_assess_rules = app_settings.assess.disabled_rules
  update_config_from_settings(%i[assess rules disabled_rules], app_settings.assess.disabled_rules)
  new_session_id = app_settings.assess.session_id
  unless settings_empty?(new_session_id)
    @assess_state.session_id = new_session_id
    # TODO: RUBY-99999 Update the default values and effective config update from TS.
    # Using the session_id from the settings response to update the config.
    # The Effective Config sources values are fetched from the
    # Contrast::CONFIG.config.loaded_config. Some values are displayed from
    # their components, however not updated here. Using this may cause some
    # specs to fails check the update of all values from TS.
    # Contrast::CONFIG.application.session_id = new_session_id
    update_config_from_settings(%i[application session_id], new_session_id)
  end
  @last_app_update_ms = Contrast::Utils::Timer.now_ms
  @app_settings_last_httpdate = header_application_last_update
end

#update_from_server_features(features_response) ⇒ Object

Parameters:



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/contrast/components/settings.rb', line 99

def update_from_server_features features_response
  return unless (server_features = features_response&.server_features)

  update_loggers(server_features)
  # TODO: RUBY-99999 Update Bot-Blocker from server settings - check enable value.
  # For now all protection rules are rebuild on Application update. Bot blocker uses the default
  # enable from the base rule, and update it's mode on app settings update.
  # Here we receive also bots for that rule.
  unless settings_empty?(server_features.protect.enabled?)
    @protect_state.enabled = server_features.protect.enabled?
    update_config_from_settings(%i[protect enable], server_features.protect.enabled?)
  end
  update_assess_server_features(server_features.assess)
  @last_server_update_ms = Contrast::Utils::Timer.now_ms
  # update via response header. We receive header from TS with last update info, setting the
  # next request's header with the same time will save needless update of settings if there
  # are no new server features updates after the said time.
  @server_settings_last_httpdate = header_server_last_update
rescue StandardError => e
  logger.warn('The following error occurred from server update: ', e: e)
end

#update_loggers(server_features) ⇒ Object

Updates logging settings



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/contrast/components/settings.rb', line 140

def update_loggers server_features
  log_file = server_features.log_file
  log_level = server_features.log_level
  # Update logger:
  Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
  unless settings_empty?(log_file)
    update_config_from_settings(%i[agent logger path], log_file)
    @agent_state.logger_path = log_file
  end
  unless settings_empty?(log_level)
    update_config_from_settings(%i[agent logger level], log_level)
    @agent_state.logger_level = log_level
  end
  # Update AgentLib Logger
  update_agent_lib_log(log_level.to_s)
  # Update CEFlogger:
  return if server_features.security_logger.settings_blank?

  cef_logger.build_logger(server_features.security_logger.log_level, server_features.security_logger.log_file)
  unless settings_empty?(log_file)
    update_config_from_settings(%i[agent security_logger path], log_file)
    @agent_state.cef_logger_level = log_file
  end
  return unless settings_empty?(log_level)

  update_config_from_settings(%i[agent security_logger level], log_level)
  @agent_state.cef_logger_level = log_level
end

#update_matchers_and_sensitive_data(app_settings) ⇒ Object



211
212
213
214
215
216
# File 'lib/contrast/components/settings.rb', line 211

def update_matchers_and_sensitive_data app_settings
  update_exclusion_matchers(app_settings.exclusions)
  app_settings.protect.virtual_patches = app_settings.protect.virtual_patches unless
    settings_empty?(app_settings.protect.virtual_patches)
  update_sensitive_data_policy(app_settings.sensitive_data_masking)
end

#update_sensitive_data_policy(sensitive_data_masking) ⇒ Object

Update the sensitive data masking policy from settings, received from TS. In case the settings are empty, keep current ones.

Ts Response settings for sensitive data masking policy

Parameters:



274
275
276
277
278
279
280
281
282
283
284
# File 'lib/contrast/components/settings.rb', line 274

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