contrast-agent 7.3.2 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/contrast/agent/protect/rule/base.rb +5 -1
- data/lib/contrast/agent/protect/rule/cmdi/cmd_injection.rb +17 -5
- data/lib/contrast/agent/protect/rule/input_classification/base.rb +7 -2
- data/lib/contrast/agent/protect/rule/input_classification/encoding.rb +1 -1
- data/lib/contrast/agent/protect/rule/path_traversal/path_traversal.rb +8 -1
- data/lib/contrast/agent/protect/rule/sqli/sqli.rb +8 -1
- data/lib/contrast/agent/protect/state.rb +110 -0
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +2 -0
- data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +2 -0
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +1 -0
- data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +4 -0
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +2 -0
- data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +2 -0
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +9 -5
- data/lib/contrast/agent/reporting/reporting_events/reportable_hash.rb +30 -6
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/resend.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -5
- data/lib/contrast/agent/reporting/settings/protect.rb +3 -3
- data/lib/contrast/agent/reporting/settings/sampling.rb +5 -4
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/components/agent.rb +3 -5
- data/lib/contrast/components/api.rb +3 -3
- data/lib/contrast/components/assess_rules.rb +1 -2
- data/lib/contrast/components/base.rb +1 -2
- data/lib/contrast/components/config/sources.rb +23 -0
- data/lib/contrast/components/logger.rb +19 -0
- data/lib/contrast/components/protect.rb +55 -14
- data/lib/contrast/components/sampling.rb +5 -12
- data/lib/contrast/components/security_logger.rb +17 -0
- data/lib/contrast/components/settings.rb +110 -76
- data/lib/contrast/config/certification_configuration.rb +1 -1
- data/lib/contrast/config/configuration_files.rb +0 -2
- data/lib/contrast/config/diagnostics/config.rb +3 -3
- data/lib/contrast/config/diagnostics/effective_config.rb +1 -1
- data/lib/contrast/config/diagnostics/environment_variables.rb +21 -11
- data/lib/contrast/config/diagnostics/monitor.rb +1 -1
- data/lib/contrast/config/diagnostics/singleton_tools.rb +170 -0
- data/lib/contrast/config/diagnostics/source_config_value.rb +14 -9
- data/lib/contrast/config/diagnostics/tools.rb +23 -84
- data/lib/contrast/config/request_audit_configuration.rb +1 -1
- data/lib/contrast/config/server_configuration.rb +3 -15
- data/lib/contrast/configuration.rb +5 -2
- data/lib/contrast/framework/manager.rb +4 -3
- data/lib/contrast/framework/manager_extend.rb +3 -1
- data/lib/contrast/framework/rack/support.rb +11 -2
- data/lib/contrast/utils/log_utils.rb +1 -1
- data/lib/contrast/utils/request_utils.rb +1 -1
- data/lib/contrast/utils/timer.rb +1 -1
- metadata +4 -2
@@ -4,6 +4,8 @@
|
|
4
4
|
require 'contrast/components/base'
|
5
5
|
require 'contrast/config/exception_configuration'
|
6
6
|
require 'contrast/config/protect_rule_configuration'
|
7
|
+
require 'contrast/utils/object_share'
|
8
|
+
require 'contrast/agent/protect/state'
|
7
9
|
|
8
10
|
module Contrast
|
9
11
|
module Components
|
@@ -68,7 +70,7 @@ module Contrast
|
|
68
70
|
return false if forcibly_disabled?
|
69
71
|
return true if forcibly_enabled?
|
70
72
|
|
71
|
-
|
73
|
+
state.enabled?
|
72
74
|
end
|
73
75
|
|
74
76
|
# Check to determine if the base64 decoding is required for user inputs.
|
@@ -85,12 +87,19 @@ module Contrast
|
|
85
87
|
::Contrast::CONFIG.protect.rules
|
86
88
|
end
|
87
89
|
|
90
|
+
# Current Active Protect rules and the state/mode they are in.
|
91
|
+
#
|
92
|
+
# @return [Contrast::Agent::Protect::State]
|
93
|
+
def state
|
94
|
+
@_state ||= Contrast::Agent::Protect::State.new
|
95
|
+
end
|
96
|
+
|
88
97
|
# Returns Protect array of all initialized
|
89
98
|
# protect rules.
|
90
99
|
#
|
91
100
|
# @return defend_rules[Hash<Contrast::SETTINGS.protect_state.rules>]
|
92
101
|
def defend_rules
|
93
|
-
|
102
|
+
state.rules
|
94
103
|
end
|
95
104
|
|
96
105
|
# The Contrast::CONFIG.protect.rules is object so we need to check it's
|
@@ -102,16 +111,27 @@ module Contrast
|
|
102
111
|
# @return mode [Symbol]
|
103
112
|
def rule_mode rule_id
|
104
113
|
str = rule_id.tr('-', '_')
|
105
|
-
|
106
|
-
|
107
|
-
|
114
|
+
config_mode = Contrast::CONFIG.protect.rules[str]&.applicable_mode
|
115
|
+
settings_mode = ::Contrast::SETTINGS.application_state.modes_by_id[rule_id]
|
116
|
+
|
117
|
+
if config_mode
|
118
|
+
update_config_for_rule(rule_id, config_mode)
|
119
|
+
return config_mode
|
120
|
+
end
|
121
|
+
|
122
|
+
if settings_mode
|
123
|
+
update_config_for_rule(rule_id, settings_mode, ui_source: true)
|
124
|
+
return settings_mode
|
125
|
+
end
|
126
|
+
:NO_ACTION
|
108
127
|
end
|
109
128
|
|
110
129
|
# Name of the protect rule
|
111
130
|
#
|
112
|
-
# @
|
131
|
+
# @param name [String]
|
132
|
+
# @return [Contrast::Agent::Protect::Rule::Base]
|
113
133
|
def rule name
|
114
|
-
|
134
|
+
state.rules[name]
|
115
135
|
end
|
116
136
|
|
117
137
|
def report_any_command_execution?
|
@@ -124,7 +144,8 @@ module Contrast
|
|
124
144
|
|
125
145
|
def report_custom_code_sysfile_access?
|
126
146
|
if @_report_custom_code_sysfile_access.nil?
|
127
|
-
name_changed = Contrast::Agent::Protect::Rule::PathTraversal::NAME.
|
147
|
+
name_changed = Contrast::Agent::Protect::Rule::PathTraversal::NAME.
|
148
|
+
tr(Contrast::Utils::ObjectShare::DASH, Contrast::Utils::ObjectShare::UNDERSCORE)
|
128
149
|
ctrl = rule_config[name_changed]
|
129
150
|
@_report_custom_code_sysfile_access = ctrl && true?(ctrl.detect_custom_code_accessing_system_files)
|
130
151
|
end
|
@@ -150,16 +171,15 @@ module Contrast
|
|
150
171
|
|
151
172
|
# @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
|
152
173
|
def protect_rules_to_effective_config effective_config
|
153
|
-
|
154
|
-
|
155
|
-
defend_rules.each do |key, value|
|
174
|
+
state.rules.each do |key, value|
|
156
175
|
next unless key && value
|
157
176
|
|
158
177
|
config_prefix = "#{ CANON_NAME }.#{ RULES }.#{ key }"
|
159
|
-
|
160
|
-
add_single_effective_value(effective_config, ENABLE, value.enabled?, config_prefix, name_prefix)
|
178
|
+
add_single_effective_value(effective_config, ENABLE, value.enabled?, config_prefix)
|
161
179
|
# Get the mode by checking Current Configs or Settings received:
|
162
|
-
|
180
|
+
mode = rule_mode(key)
|
181
|
+
mode = :OFF if mode == :NO_ACTION
|
182
|
+
add_single_effective_value(effective_config, MODE, mode, config_prefix)
|
163
183
|
end
|
164
184
|
end
|
165
185
|
|
@@ -181,6 +201,27 @@ module Contrast
|
|
181
201
|
def report_custom_code_sysfile_access
|
182
202
|
report_custom_code_sysfile_access?
|
183
203
|
end
|
204
|
+
|
205
|
+
# @param rule_id [String] the canonical name for a config entry (such as api.proxy.enable).
|
206
|
+
# @param mode [Symbol] to set the value of the config entry.
|
207
|
+
# @param ui_source [Boolean] whether to se the source as contrast ui or not.
|
208
|
+
def update_config_for_rule rule_id, mode, ui_source: false
|
209
|
+
str = rule_id.tr('-', '_').to_sym
|
210
|
+
config = Contrast::CONFIG.config.loaded_config
|
211
|
+
return unless config
|
212
|
+
|
213
|
+
config[:protect] ||= {}
|
214
|
+
config[:protect][:rules] ||= {}
|
215
|
+
config[:protect][:rules][str] ||= {}
|
216
|
+
config[:protect][:rules][str][:mode] = mode
|
217
|
+
config[:protect][:rules][str][:enable] = (mode != :NO_ACTION)
|
218
|
+
return unless ui_source
|
219
|
+
|
220
|
+
Contrast::CONFIG.sources.set("#{ CANON_NAME }.#{ RULES }.#{ str }.mode",
|
221
|
+
Contrast::Components::Config::Sources::CONTRAST_UI)
|
222
|
+
Contrast::CONFIG.sources.set("#{ CANON_NAME }.#{ RULES }.#{ str }.enable",
|
223
|
+
Contrast::Components::Config::Sources::CONTRAST_UI)
|
224
|
+
end
|
184
225
|
end
|
185
226
|
end
|
186
227
|
end
|
@@ -111,7 +111,6 @@ module Contrast
|
|
111
111
|
include Contrast::Config::BaseConfiguration
|
112
112
|
|
113
113
|
CANON_NAME = 'assess.sampling'
|
114
|
-
NAME_PREFIX = "#{ CONTRAST }.#{ CANON_NAME }".cs__freeze
|
115
114
|
CONFIG_VALUES = %w[enable baseline request_frequency response_frequency window_ms].cs__freeze
|
116
115
|
|
117
116
|
# @return [Boolean, nil]
|
@@ -145,19 +144,13 @@ module Contrast
|
|
145
144
|
def to_effective_config effective_config
|
146
145
|
confirm_sources
|
147
146
|
|
148
|
-
add_single_effective_value(effective_config, 'enable', sampling_control[:enabled], canon_name
|
149
|
-
add_single_effective_value(effective_config, 'baseline', sampling_control[:baseline], canon_name
|
150
|
-
add_single_effective_value(effective_config, 'window_ms', sampling_control[:window], canon_name
|
147
|
+
add_single_effective_value(effective_config, 'enable', sampling_control[:enabled], canon_name)
|
148
|
+
add_single_effective_value(effective_config, 'baseline', sampling_control[:baseline], canon_name)
|
149
|
+
add_single_effective_value(effective_config, 'window_ms', sampling_control[:window], canon_name)
|
151
150
|
add_single_effective_value(effective_config,
|
152
|
-
'request_frequency',
|
153
|
-
sampling_control[:request_frequency],
|
154
|
-
canon_name,
|
155
|
-
NAME_PREFIX)
|
151
|
+
'request_frequency', sampling_control[:request_frequency], canon_name)
|
156
152
|
add_single_effective_value(effective_config,
|
157
|
-
'response_frequency',
|
158
|
-
sampling_control[:response_frequency],
|
159
|
-
canon_name,
|
160
|
-
NAME_PREFIX)
|
153
|
+
'response_frequency', sampling_control[:response_frequency], canon_name)
|
161
154
|
end
|
162
155
|
|
163
156
|
private
|
@@ -10,6 +10,7 @@ module Contrast
|
|
10
10
|
class Interface
|
11
11
|
include Contrast::Components::ComponentBase
|
12
12
|
|
13
|
+
DEFAULT_CEF_NAME = 'security.log'
|
13
14
|
CANON_NAME = 'agent.security_logger'
|
14
15
|
CONFIG_VALUES = %w[path level].cs__freeze
|
15
16
|
|
@@ -30,6 +31,22 @@ module Contrast
|
|
30
31
|
@path = hsh[:path]
|
31
32
|
@level = hsh[:level]
|
32
33
|
end
|
34
|
+
|
35
|
+
def to_effective_config effective_config
|
36
|
+
path_setting = nil
|
37
|
+
level_setting = nil
|
38
|
+
|
39
|
+
if defined?(Contrast::SETTINGS)
|
40
|
+
path_setting = Contrast::SETTINGS.agent_state.cef_logger_path
|
41
|
+
level_setting = Contrast::SETTINGS.agent_state.cef_logger_level
|
42
|
+
end
|
43
|
+
|
44
|
+
path_setting ||= DEFAULT_CEF_NAME
|
45
|
+
level_setting ||= Contrast::CONFIG.agent.logger.level
|
46
|
+
|
47
|
+
add_single_effective_value(effective_config, config_values[0], path || path_setting, CANON_NAME)
|
48
|
+
add_single_effective_value(effective_config, config_values[1], level || level_setting, CANON_NAME)
|
49
|
+
end
|
33
50
|
end
|
34
51
|
end
|
35
52
|
end
|
@@ -14,10 +14,12 @@ module Contrast
|
|
14
14
|
# directives (likely provided by TeamServer) about product operation.
|
15
15
|
# 'Settings' is not a generic term for 'configurable stuff'.
|
16
16
|
module Settings
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
AGENT_STATE_BASE = Struct.new(:logger_path, :logger_level, :cef_logger_path, :cef_logger_level).
|
18
|
+
new(nil, nil, nil, nil)
|
19
|
+
APPLICATION_STATE_BASE = Struct.new(:modes_by_id).new({})
|
20
|
+
PROTECT_STATE_BASE = Struct.new(:enabled).new(false)
|
21
|
+
ASSESS_STATE_BASE = Struct.new(:enabled, :sampling_settings, :disabled_assess_rules, :session_id).
|
22
|
+
new(false, nil, [], nil) do
|
21
23
|
def sampling_settings= new_val
|
22
24
|
@sampling_settings = new_val
|
23
25
|
Contrast::Utils::Assess::SamplingUtil.instance.update
|
@@ -32,6 +34,13 @@ module Contrast
|
|
32
34
|
|
33
35
|
# tainted_columns are database columns that receive unsanitized input.
|
34
36
|
attr_reader :tainted_columns # This can probably go into assess_state?
|
37
|
+
# Agent state. Used for extracting Agent level settings.
|
38
|
+
#
|
39
|
+
# logger_path[String] Path to the log file.
|
40
|
+
# logger_level[String] Log level for the logger.
|
41
|
+
# cef_logger_path[String] Path to the log file.
|
42
|
+
# cef_logger_level[String] Log level for the logger.
|
43
|
+
attr_reader :agent_state
|
35
44
|
# Current state for Assess.
|
36
45
|
# enabled [Boolean] Indicate if the assess feature set is enabled for this server or not.
|
37
46
|
#
|
@@ -87,26 +96,17 @@ module Contrast
|
|
87
96
|
end
|
88
97
|
|
89
98
|
# @param features_response [Contrast::Agent::Reporting::Response]
|
90
|
-
def update_from_server_features features_response
|
99
|
+
def update_from_server_features features_response
|
91
100
|
return unless (server_features = features_response&.server_features)
|
92
101
|
|
93
|
-
|
94
|
-
log_level = server_features.log_level
|
95
|
-
# Update logger:
|
96
|
-
Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
|
97
|
-
# Update AgentLib Logger
|
98
|
-
update_agent_lib_log(log_level.to_s)
|
99
|
-
# Update CEFlogger:
|
100
|
-
unless server_features.security_logger.settings_blank?
|
101
|
-
cef_logger.build_logger(server_features.security_logger.log_level, server_features.security_logger.log_file)
|
102
|
-
end
|
102
|
+
update_loggers(server_features)
|
103
103
|
# TODO: RUBY-99999 Update Bot-Blocker from server settings - check enable value.
|
104
104
|
# For now all protection rules are rebuild on Application update. Bot blocker uses the default
|
105
105
|
# enable from the base rule, and update it's mode on app settings update.
|
106
106
|
# Here we receive also bots for that rule.
|
107
107
|
unless settings_empty?(server_features.protect.enabled?)
|
108
108
|
@protect_state.enabled = server_features.protect.enabled?
|
109
|
-
|
109
|
+
update_config_from_settings(%i[protect enable], server_features.protect.enabled?)
|
110
110
|
end
|
111
111
|
update_assess_server_features(server_features.assess)
|
112
112
|
@last_server_update_ms = Contrast::Utils::Timer.now_ms
|
@@ -118,6 +118,54 @@ module Contrast
|
|
118
118
|
logger.warn('The following error occurred from server update: ', e: e)
|
119
119
|
end
|
120
120
|
|
121
|
+
# Update Assess server features
|
122
|
+
#
|
123
|
+
# @param assess [Contrast::Agent::Reporting::Settings::AssessServerFeature]
|
124
|
+
def update_assess_server_features assess
|
125
|
+
return if settings_empty?(assess.enabled?)
|
126
|
+
|
127
|
+
@assess_state.enabled = assess.enabled?
|
128
|
+
update_config_from_settings(%i[assess enable], assess.enabled?)
|
129
|
+
@assess_state.sampling_settings = assess.sampling
|
130
|
+
|
131
|
+
samplings_path = Contrast::Components::Sampling::Interface::CANON_NAME.split('.').map(&:to_sym)
|
132
|
+
Contrast::Components::Sampling::Interface::CONFIG_VALUES.each do |field|
|
133
|
+
lookup_field = field == 'enable' ? :enabled : field.to_sym
|
134
|
+
update_config_from_settings(samplings_path + [field.to_sym], assess.sampling.send(lookup_field))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Updates logging settings
|
139
|
+
# @param [Contrast::Agent::Reporting::Settings::ServerFeatures]
|
140
|
+
def update_loggers server_features
|
141
|
+
log_file = server_features.log_file
|
142
|
+
log_level = server_features.log_level
|
143
|
+
# Update logger:
|
144
|
+
Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
|
145
|
+
unless settings_empty?(log_file)
|
146
|
+
update_config_from_settings(%i[agent logger path], log_file)
|
147
|
+
@agent_state.logger_path = log_file
|
148
|
+
end
|
149
|
+
unless settings_empty?(log_level)
|
150
|
+
update_config_from_settings(%i[agent logger level], log_level)
|
151
|
+
@agent_state.logger_level = log_level
|
152
|
+
end
|
153
|
+
# Update AgentLib Logger
|
154
|
+
update_agent_lib_log(log_level.to_s)
|
155
|
+
# Update CEFlogger:
|
156
|
+
return if server_features.security_logger.settings_blank?
|
157
|
+
|
158
|
+
cef_logger.build_logger(server_features.security_logger.log_level, server_features.security_logger.log_file)
|
159
|
+
unless settings_empty?(log_file)
|
160
|
+
update_config_from_settings(%i[agent security_logger path], log_file)
|
161
|
+
@agent_state.cef_logger_level = log_file
|
162
|
+
end
|
163
|
+
return unless settings_empty?(log_level)
|
164
|
+
|
165
|
+
update_config_from_settings(%i[agent security_logger level], log_level)
|
166
|
+
@agent_state.cef_logger_level = log_level
|
167
|
+
end
|
168
|
+
|
121
169
|
# Update AgentLib log level
|
122
170
|
def update_agent_lib_log new_log_level
|
123
171
|
agent_lib_log_level = Contrast::AgentLib::InterfaceBase::LOG_LEVEL[0] if new_log_level.empty?
|
@@ -135,42 +183,46 @@ module Contrast
|
|
135
183
|
Contrast::AGENT_LIB.change_log_options(true, agent_lib_log_level)
|
136
184
|
end
|
137
185
|
|
138
|
-
# Update Assess server features
|
139
|
-
#
|
140
|
-
# @param assess [Contrast::Agent::Reporting::Settings::AssessServerFeature]
|
141
|
-
def update_assess_server_features assess
|
142
|
-
return if settings_empty?(assess.enabled?)
|
143
|
-
|
144
|
-
@assess_state.enabled = assess.enabled?
|
145
|
-
store_in_config(%i[assess enable], assess.enabled?)
|
146
|
-
@assess_state.sampling_settings = assess.sampling
|
147
|
-
|
148
|
-
Contrast::Components::Sampling::Interface::CONFIG_VALUES.each do |field|
|
149
|
-
lookup_field = field == 'enable' ? :enabled : field.to_sym
|
150
|
-
store_in_config(Contrast::Components::Sampling::Interface::CANON_NAME.split('.') + [field.to_sym],
|
151
|
-
assess.sampling.send(lookup_field))
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
186
|
# @param settings_response [Contrast::Agent::Reporting::Response]
|
156
187
|
def update_from_application_settings settings_response
|
157
188
|
return unless (app_settings = settings_response&.application_settings)
|
158
189
|
|
159
190
|
extract_protect_app_settings(app_settings)
|
160
|
-
|
161
|
-
app_settings.protect.virtual_patches = app_settings.protect.virtual_patches unless
|
162
|
-
settings_empty?(app_settings.protect.virtual_patches)
|
163
|
-
update_sensitive_data_policy(app_settings.sensitive_data_masking)
|
191
|
+
update_matchers_and_sensitive_data(app_settings)
|
164
192
|
@assess_state.disabled_assess_rules = app_settings.assess.disabled_rules
|
193
|
+
update_config_from_settings(%i[assess rules disabled_rules], app_settings.assess.disabled_rules)
|
165
194
|
new_session_id = app_settings.assess.session_id
|
166
|
-
|
195
|
+
unless settings_empty?(new_session_id)
|
196
|
+
@assess_state.session_id = new_session_id
|
197
|
+
# TODO: RUBY-99999 Update the default values and effective config update from TS.
|
198
|
+
# Using the session_id from the settings response to update the config.
|
199
|
+
# The Effective Config sources values are fetched from the
|
200
|
+
# Contrast::CONFIG.config.loaded_config. Some values are displayed from
|
201
|
+
# their components, however not updated here. Using this may cause some
|
202
|
+
# specs to fails check the update of all values from TS.
|
203
|
+
# Contrast::CONFIG.application.session_id = new_session_id
|
204
|
+
update_config_from_settings(%i[application session_id], new_session_id)
|
205
|
+
end
|
167
206
|
@last_app_update_ms = Contrast::Utils::Timer.now_ms
|
168
207
|
@app_settings_last_httpdate = header_application_last_update
|
169
208
|
end
|
170
209
|
|
210
|
+
# @param app_settings [Contrast::Agent::Reporting::Settings::ApplicationSettings]
|
211
|
+
def update_matchers_and_sensitive_data app_settings
|
212
|
+
update_exclusion_matchers(app_settings.exclusions)
|
213
|
+
app_settings.protect.virtual_patches = app_settings.protect.virtual_patches unless
|
214
|
+
settings_empty?(app_settings.protect.virtual_patches)
|
215
|
+
update_sensitive_data_policy(app_settings.sensitive_data_masking)
|
216
|
+
end
|
217
|
+
|
171
218
|
# Wipe state to zero.
|
172
|
-
|
173
|
-
|
219
|
+
#
|
220
|
+
# @param purge [Boolean] If true, also purge the persistent states.
|
221
|
+
def reset_state purge: false
|
222
|
+
@agent_state = AGENT_STATE_BASE.dup
|
223
|
+
# Keep the protect state, since once set the rules depend ont it.
|
224
|
+
# The state will be update on first settings response from TS.
|
225
|
+
@protect_state = PROTECT_STATE_BASE.dup if purge || @protect_state.nil?
|
174
226
|
update_assess_state
|
175
227
|
@application_state = APPLICATION_STATE_BASE.dup
|
176
228
|
@tainted_columns = {}
|
@@ -193,24 +245,6 @@ module Contrast
|
|
193
245
|
@assess_state
|
194
246
|
end
|
195
247
|
|
196
|
-
def build_protect_rules
|
197
|
-
@protect_state.rules = {}
|
198
|
-
|
199
|
-
# Rules. They add themselves on initialize.
|
200
|
-
Contrast::Agent::Protect::Rule::BotBlocker.new
|
201
|
-
cmdi = Contrast::Agent::Protect::Rule::CmdInjection.new
|
202
|
-
cmdi.sub_rules
|
203
|
-
Contrast::Agent::Protect::Rule::Deserialization.new
|
204
|
-
Contrast::Agent::Protect::Rule::NoSqli.new
|
205
|
-
path = Contrast::Agent::Protect::Rule::PathTraversal.new
|
206
|
-
path.sub_rules
|
207
|
-
sqli = Contrast::Agent::Protect::Rule::Sqli.new
|
208
|
-
sqli.sub_rules
|
209
|
-
Contrast::Agent::Protect::Rule::UnsafeFileUpload.new
|
210
|
-
Contrast::Agent::Protect::Rule::Xss.new
|
211
|
-
Contrast::Agent::Protect::Rule::Xxe.new
|
212
|
-
end
|
213
|
-
|
214
248
|
# @param exclusions [Contrast::Agent::Reporting::Settings::Exclusions]
|
215
249
|
def update_exclusion_matchers exclusions
|
216
250
|
matchers = []
|
@@ -278,23 +312,6 @@ module Contrast
|
|
278
312
|
Contrast::Agent.reporter.client.response_handler.last_application_modified
|
279
313
|
end
|
280
314
|
|
281
|
-
# Update the stored config values to ensure that we know about the correct values,
|
282
|
-
# and that the sources are correct for entries updated from the UI.
|
283
|
-
#
|
284
|
-
# @param parts [Array] the path to the setting in config
|
285
|
-
# @param value [String, Integer, Array, nil] the value for the configuration setting
|
286
|
-
def store_in_config parts, value
|
287
|
-
level = Contrast::CONFIG.config.loaded_config
|
288
|
-
parts[0...-1].each do |segment|
|
289
|
-
level[segment] ||= {}
|
290
|
-
level = level[segment]
|
291
|
-
end
|
292
|
-
return unless level.cs__is_a?(Hash)
|
293
|
-
|
294
|
-
level[parts[-1]] = value
|
295
|
-
Contrast::CONFIG.sources.set(parts.join('.'), Contrast::Components::Config::Sources::CONTRAST_UI)
|
296
|
-
end
|
297
|
-
|
298
315
|
# Extract the rules modes from protection_rules or rules_settings fields.
|
299
316
|
#
|
300
317
|
# @param app_settings [Contrast::Agent::Reporting::Settings::ApplicationSettings]
|
@@ -302,7 +319,24 @@ module Contrast
|
|
302
319
|
modes_by_id = app_settings.protect.protection_rules_to_settings_hash
|
303
320
|
modes_by_id = app_settings.protect.rules_settings_to_settings_hash if settings_empty?(modes_by_id)
|
304
321
|
# Preserve previous state if no new settings are extracted:
|
305
|
-
|
322
|
+
return if settings_empty?(modes_by_id)
|
323
|
+
|
324
|
+
@application_state.modes_by_id = modes_by_id
|
325
|
+
end
|
326
|
+
|
327
|
+
# Update the stored config values to ensure that we know about the correct values,
|
328
|
+
# and that the sources are correct for entries updated from the UI.
|
329
|
+
#
|
330
|
+
# NOTE: For each passed component path here, there should br implemented a check for the value from
|
331
|
+
# Config and Settings. For example if enable from CONFIG is nil, check the SETTINGS.
|
332
|
+
# This will keep the value not empty and be reflected in effective config reporting.
|
333
|
+
#
|
334
|
+
# @param path [String] the canonical name for the config entry (such as api.proxy.enable)
|
335
|
+
# @param value [String, Integer, Array, nil] the value for the configuration setting
|
336
|
+
def update_config_from_settings path, value
|
337
|
+
Contrast::Config::Diagnostics::Tools.update_config(path,
|
338
|
+
value,
|
339
|
+
Contrast::Components::Config::Sources::CONTRAST_UI)
|
306
340
|
end
|
307
341
|
end
|
308
342
|
end
|
@@ -40,7 +40,7 @@ module Contrast
|
|
40
40
|
#
|
41
41
|
# @param effective_config [Contrast::Config::Diagnostics::EffectiveConfig]
|
42
42
|
def to_effective_config effective_config
|
43
|
-
add_effective_config_values(effective_config, CONFIG_VALUES, CANON_NAME
|
43
|
+
add_effective_config_values(effective_config, CONFIG_VALUES, CANON_NAME)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -52,9 +52,9 @@ module Contrast
|
|
52
52
|
effective_config: effective_config.to_controlled_hash,
|
53
53
|
user_configuration_file: user_configuration_file.to_controlled_hash,
|
54
54
|
environment_variable: Contrast::Config::Diagnostics::EnvironmentVariables.environment_settings(ENV).
|
55
|
-
map(&:
|
56
|
-
command_line: Contrast::Config::Diagnostics::CommandLine.command_line_settings.map(&:
|
57
|
-
contrast_ui: Contrast::Config::Diagnostics::ContrastUI.contrast_ui_settings.map(&:
|
55
|
+
map(&:to_source_hash),
|
56
|
+
command_line: Contrast::Config::Diagnostics::CommandLine.command_line_settings.map(&:to_source_hash),
|
57
|
+
contrast_ui: Contrast::Config::Diagnostics::ContrastUI.contrast_ui_settings.map(&:to_source_hash)
|
58
58
|
}.compact
|
59
59
|
end
|
60
60
|
end
|
@@ -14,36 +14,46 @@ module Contrast
|
|
14
14
|
NON_COMMON_ENV = %w[
|
15
15
|
CONTRAST_CONFIG_PATH CONTRAST_AGENT_TELEMETRY_OPTOUT CONTRAST_AGENT_TELEMETRY_TEST
|
16
16
|
].cs__freeze
|
17
|
+
MASKED_ENV = %w[CONTRAST__API__API_KEY CONTRAST__API__SERVICE_KEY].cs__freeze
|
17
18
|
|
18
19
|
# This method will fill the canonical name for each env var and will check for any uncommon ones.
|
19
20
|
#
|
20
21
|
# @param env [Hash]
|
21
22
|
# @return [Array] array of all the values needed to be written.
|
22
|
-
def environment_settings env
|
23
|
+
def environment_settings env # rubocop:disable Metrics/MethodLength
|
23
24
|
env_hash = env.select do |e|
|
24
25
|
e.to_s.start_with?(Contrast::Configuration::CONTRAST_ENV_MARKER) || NON_COMMON_ENV.include?(e.to_s)
|
25
26
|
end
|
26
27
|
environment_settings = []
|
27
|
-
env_hash.each do |key, value|
|
28
|
+
env_hash.each do |key, value| # rubocop:disable Metrics/BlockLength
|
28
29
|
efc_value = Contrast::Config::Diagnostics::EffectiveConfigValue.new.tap do |effective_value|
|
29
30
|
next unless value
|
30
31
|
|
31
32
|
effective_value.canonical_name = if NON_COMMON_ENV.include?(key)
|
32
33
|
key.gsub(Contrast::Utils::ObjectShare::UNDERSCORE,
|
33
|
-
Contrast::Utils::ObjectShare::PERIOD).downcase
|
34
|
+
Contrast::Utils::ObjectShare::PERIOD).downcase.
|
35
|
+
gsub(Contrast::Utils::ObjectShare::CONTRAST_DOT,
|
36
|
+
Contrast::Utils::ObjectShare::EMPTY_STRING)
|
34
37
|
else
|
35
38
|
key.gsub(Contrast::Utils::ObjectShare::DOUBLE_UNDERSCORE,
|
36
|
-
Contrast::Utils::ObjectShare::PERIOD).downcase
|
39
|
+
Contrast::Utils::ObjectShare::PERIOD).downcase.
|
40
|
+
gsub(Contrast::Utils::ObjectShare::CONTRAST_DOT,
|
41
|
+
Contrast::Utils::ObjectShare::EMPTY_STRING)
|
37
42
|
end
|
38
|
-
if
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
effective_value.value = Contrast::Config::Diagnostics::Tools.value_to_s(value)
|
43
|
+
effective_value.value = if MASKED_ENV.include?(key)
|
44
|
+
Contrast::Configuration::EFFECTIVE_REDACTED
|
45
|
+
else
|
46
|
+
Contrast::Config::Diagnostics::Tools.value_to_s(value)
|
47
|
+
end
|
44
48
|
effective_value.key = key
|
45
49
|
end
|
46
|
-
|
50
|
+
next unless efc_value
|
51
|
+
|
52
|
+
environment_settings << efc_value
|
53
|
+
Contrast::Config::Diagnostics::Tools.
|
54
|
+
update_config(efc_value.key,
|
55
|
+
efc_value.value,
|
56
|
+
Contrast::Components::Config::Sources::ENVIRONMENT_VARIABLE)
|
47
57
|
end
|
48
58
|
environment_settings
|
49
59
|
end
|
@@ -102,7 +102,7 @@ module Contrast
|
|
102
102
|
extract_settings
|
103
103
|
File.open(File.join(dir_name, FILE_NAME), WRITE) do |file|
|
104
104
|
file.truncate(0)
|
105
|
-
file.write(JSON.pretty_generate(to_controlled_hash, { space: Contrast::Utils::ObjectShare::
|
105
|
+
file.write(JSON.pretty_generate(to_controlled_hash, { space: Contrast::Utils::ObjectShare::SPACE }))
|
106
106
|
status = true if file
|
107
107
|
file.close
|
108
108
|
end
|