contrast-agent 5.3.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/cs__assess_array/cs__assess_array.c +7 -0
- data/ext/cs__assess_basic_object/cs__assess_basic_object.c +19 -5
- data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +1 -1
- data/ext/cs__assess_hash/cs__assess_hash.c +3 -4
- data/ext/cs__assess_kernel/cs__assess_kernel.c +1 -2
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +26 -12
- data/ext/cs__assess_module/cs__assess_module.c +1 -1
- data/ext/cs__assess_string/cs__assess_string.c +13 -1
- data/ext/cs__common/cs__common.c +12 -11
- data/ext/cs__contrast_patch/cs__contrast_patch.c +54 -43
- data/ext/cs__os_information/cs__os_information.c +13 -10
- data/ext/cs__scope/cs__scope.c +146 -97
- data/lib/contrast/agent/assess/finalizers/hash.rb +2 -0
- data/lib/contrast/agent/assess/policy/policy_node.rb +50 -27
- data/lib/contrast/agent/assess/policy/policy_node_utils.rb +51 -0
- data/lib/contrast/agent/assess/policy/preshift.rb +8 -2
- data/lib/contrast/agent/assess/policy/propagation_method.rb +47 -13
- data/lib/contrast/agent/assess/policy/propagator/buffer.rb +118 -0
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +19 -4
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +18 -2
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +17 -3
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/substitution_utils.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator.rb +1 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +7 -7
- data/lib/contrast/agent/assess/policy/trigger_method.rb +3 -1
- data/lib/contrast/agent/assess/property/tagged.rb +1 -1
- data/lib/contrast/agent/assess/rule/response/auto_complete_rule.rb +1 -1
- data/lib/contrast/agent/assess/rule/response/body_rule.rb +3 -3
- data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +1 -1
- data/lib/contrast/agent/assess/rule/response/parameters_pollution_rule.rb +1 -1
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +0 -7
- data/lib/contrast/agent/deadzone/policy/policy.rb +0 -6
- data/lib/contrast/agent/exclusion_matcher.rb +3 -3
- data/lib/contrast/agent/middleware.rb +3 -1
- data/lib/contrast/agent/patching/policy/patcher.rb +3 -3
- data/lib/contrast/agent/patching/policy/policy_node.rb +15 -2
- data/lib/contrast/agent/protect/exploitable_collection.rb +38 -0
- data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +61 -8
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -1
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +2 -2
- data/lib/contrast/agent/protect/rule/base.rb +37 -5
- data/lib/contrast/agent/protect/rule/base_service.rb +3 -1
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +13 -0
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_input_classification.rb +83 -0
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_worth_watching.rb +64 -0
- data/lib/contrast/agent/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +96 -0
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +8 -0
- data/lib/contrast/agent/protect/rule/no_sqli/no_sqli_input_classification.rb +231 -0
- data/lib/contrast/agent/protect/rule/no_sqli.rb +27 -0
- data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +18 -54
- data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +1 -4
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +82 -0
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_matcher.rb +45 -0
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +42 -0
- data/lib/contrast/agent/reporting/attack_result/attack_result.rb +63 -0
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +52 -0
- data/lib/contrast/agent/reporting/attack_result/response_type.rb +29 -0
- data/lib/contrast/agent/reporting/attack_result/user_input.rb +87 -0
- data/lib/contrast/agent/reporting/masker/masker.rb +246 -0
- data/lib/contrast/agent/reporting/masker/masker_utils.rb +58 -0
- data/lib/contrast/agent/reporting/report.rb +2 -0
- data/lib/contrast/agent/reporting/reporter.rb +23 -11
- data/lib/contrast/agent/reporting/reporting_events/agent_startup.rb +30 -0
- data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +7 -3
- data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +40 -0
- data/lib/contrast/agent/reporting/reporting_events/application_startup_instrumentation.rb +27 -0
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +5 -5
- data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +9 -9
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -1
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +2 -1
- data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +8 -6
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +16 -5
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +27 -26
- data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +97 -0
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +68 -6
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_mode.rb +63 -0
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +122 -96
- data/lib/contrast/agent/reporting/settings/application_settings.rb +9 -0
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +5 -33
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +1 -1
- data/lib/contrast/agent/reporting/settings/sampling.rb +36 -0
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +110 -0
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +58 -0
- data/lib/contrast/agent/request_context.rb +1 -1
- data/lib/contrast/agent/request_context_extend.rb +1 -1
- data/lib/contrast/agent/request_handler.rb +4 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +51 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +36 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message.rb +97 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +65 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +47 -0
- data/lib/contrast/agent/{metric_telemetry_event.rb → telemetry/events/metric_telemetry_event.rb} +1 -1
- data/lib/contrast/agent/{startup_metrics_telemetry_event.rb → telemetry/events/startup_metrics_telemetry_event.rb} +3 -3
- data/lib/contrast/agent/{telemetry_event.rb → telemetry/events/telemetry_event.rb} +1 -1
- data/lib/contrast/agent/{telemetry.rb → telemetry/telemetry.rb} +32 -19
- data/lib/contrast/agent/thread_watcher.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +3 -0
- data/lib/contrast/api/communication/speedracer.rb +1 -1
- data/lib/contrast/api/decorators/address.rb +1 -1
- data/lib/contrast/api/decorators/response_type.rb +30 -0
- data/lib/contrast/api/decorators.rb +1 -0
- data/lib/contrast/components/app_context.rb +0 -4
- data/lib/contrast/components/assess.rb +14 -0
- data/lib/contrast/components/protect.rb +2 -2
- data/lib/contrast/components/sampling.rb +7 -11
- data/lib/contrast/components/settings.rb +106 -8
- data/lib/contrast/config/agent_configuration.rb +13 -30
- data/lib/contrast/config/api_configuration.rb +4 -67
- data/lib/contrast/config/api_proxy_configuration.rb +2 -45
- data/lib/contrast/config/application_configuration.rb +9 -84
- data/lib/contrast/config/assess_configuration.rb +10 -69
- data/lib/contrast/config/assess_rules_configuration.rb +9 -38
- data/lib/contrast/config/base_configuration.rb +17 -50
- data/lib/contrast/config/certification_configuration.rb +6 -63
- data/lib/contrast/config/exception_configuration.rb +5 -52
- data/lib/contrast/config/heap_dump_configuration.rb +6 -64
- data/lib/contrast/config/inventory_configuration.rb +2 -49
- data/lib/contrast/config/logger_configuration.rb +0 -36
- data/lib/contrast/config/protect_configuration.rb +17 -7
- data/lib/contrast/config/protect_rule_configuration.rb +12 -30
- data/lib/contrast/config/protect_rules_configuration.rb +21 -26
- data/lib/contrast/config/request_audit_configuration.rb +6 -48
- data/lib/contrast/config/root_configuration.rb +52 -12
- data/lib/contrast/config/ruby_configuration.rb +0 -36
- data/lib/contrast/config/sampling_configuration.rb +1 -57
- data/lib/contrast/config/server_configuration.rb +0 -36
- data/lib/contrast/config/service_configuration.rb +5 -44
- data/lib/contrast/configuration.rb +2 -3
- data/lib/contrast/extension/assess/string.rb +20 -1
- data/lib/contrast/extension/module.rb +0 -1
- data/lib/contrast/framework/manager.rb +2 -2
- data/lib/contrast/logger/application.rb +1 -1
- data/lib/contrast/logger/cef_log.rb +1 -1
- data/lib/contrast/tasks/config.rb +90 -3
- data/lib/contrast/utils/assess/object_store.rb +36 -0
- data/lib/contrast/utils/assess/propagation_method_utils.rb +6 -0
- data/lib/contrast/utils/class_util.rb +3 -12
- data/lib/contrast/utils/input_classification.rb +73 -0
- data/lib/contrast/utils/log_utils.rb +1 -1
- data/lib/contrast/utils/middleware_utils.rb +9 -8
- data/lib/contrast/utils/net_http_base.rb +1 -1
- data/lib/contrast/utils/object_share.rb +2 -1
- data/lib/contrast/utils/os.rb +0 -5
- data/lib/contrast/utils/patching/policy/patch_utils.rb +4 -5
- data/lib/contrast/utils/response_utils.rb +18 -33
- data/lib/contrast/utils/telemetry.rb +1 -1
- data/lib/contrast/utils/telemetry_client.rb +1 -1
- data/lib/contrast/utils/telemetry_identifier.rb +1 -1
- data/resources/assess/policy.json +98 -0
- data/resources/deadzone/policy.json +0 -86
- data/ruby-agent.gemspec +9 -8
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +73 -28
@@ -1,50 +1,148 @@
|
|
1
1
|
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'contrast/agent/reporting/reporting_utilities/response_extractor'
|
5
|
+
|
4
6
|
module Contrast
|
5
7
|
module Agent
|
6
8
|
module Reporting
|
7
9
|
# This module holds utilities required by the reporting service response handler
|
8
10
|
module ResponseHandlerUtils
|
9
|
-
|
10
|
-
|
11
|
+
include Contrast::Agent::Reporting::ResponseExtractor
|
12
|
+
|
13
|
+
ANALYZE_WHEN = %w[200 204].cs__freeze
|
14
|
+
ERROR_CODES = {
|
15
|
+
message_not_sent: '400',
|
16
|
+
access_forbidden: '401',
|
17
|
+
access_forbidden_no_action: '403',
|
18
|
+
application_do_not_exist: '404',
|
19
|
+
unprocessable_entity: '422',
|
20
|
+
too_many_requests: '429'
|
21
|
+
}.cs__freeze
|
22
|
+
APP_NON_EXISTENT_MSG = 'Application does not exist! Either it has not been created or has '\
|
23
|
+
'been deleted or archived. '\
|
24
|
+
'Disabling permanently.'
|
25
|
+
SUSPEND_MSG = 'Reporter is temporarily suspended.'
|
26
|
+
UNSUCCESSFULLY_RECEIVED_MSG = 'The Reporter was unable to send message.'
|
27
|
+
FORBIDDEN_MSG = 'Access was forbidden for current Report because the request authentication '\
|
28
|
+
'information was not provided'
|
29
|
+
FORBIDDEN_NO_ACTION_MSG = 'Report access was forbidden because the supplied credentials failed '\
|
30
|
+
'to authenticate the Agent'
|
31
|
+
UNPROCESSABLE_ENTITY_MSG = 'Reporter received Unprocessable Entity response. Disabling permanently.'
|
32
|
+
RETRY_AFTER_MSG = "There are too many requests of this type being sent by this Agent. #{ SUSPEND_MSG }"
|
11
33
|
|
12
34
|
private
|
13
35
|
|
14
36
|
# check if response code is valid before analyze it
|
15
37
|
#
|
16
|
-
# @param
|
38
|
+
# @param response [Net::HTTP::Response, nil]
|
17
39
|
# @return [Boolean]
|
18
|
-
def
|
19
|
-
#
|
40
|
+
def analyze_response? response
|
41
|
+
# Code descriptions:
|
42
|
+
# 200:
|
43
|
+
# Message successfully received and there are new settings
|
44
|
+
# 204:
|
45
|
+
# Message successfully received and it's up to Contrast Server to decide what is done with the data.
|
46
|
+
# 304:
|
47
|
+
# Message successfully received and there are no new settings. Use your current ones.
|
48
|
+
#
|
49
|
+
# ERRORS:
|
50
|
+
# 400:
|
51
|
+
# Message unsuccessfully received. The Contrast Server was unable to process the message properly.
|
20
52
|
# 401:
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
|
53
|
+
# Access was forbidden because the request authentication information was not provided.
|
54
|
+
# headers:
|
55
|
+
# www-Authenticate => Indicate that the API-Key and Authorization header are both required,
|
56
|
+
# in the standard format per RFC 2616
|
57
|
+
# 403:
|
58
|
+
# Access was forbidden because the supplied credentials failed to authenticate the Agent.
|
59
|
+
# 404:
|
60
|
+
# The application does not exist - either it has not been created or has been deleted or archived.
|
61
|
+
# If possible, the Agent should no longer analyze this application. For those Agents with multiple
|
62
|
+
# applications in a single process, at a minimum, cease reporting about this application.
|
63
|
+
# 422:
|
64
|
+
# Unprocessable Entity - The application startup is rejected because some piece of data is incorrect.
|
65
|
+
# The session_id could reference a non-existent session or the metadata (not session_metadata) could
|
66
|
+
# fail a constraint check. TeamServer should indicate this is an error message which the Agent should
|
67
|
+
# log. The Agent should no longer analyze this application.
|
68
|
+
# {
|
69
|
+
# "error": "string"
|
70
|
+
# }
|
71
|
+
# 429:
|
72
|
+
# There are too many requests of this type being sent by this Agent. Back off for the time listed in
|
73
|
+
# the Retry-After header. In this case, it is on the Agent to determine if it is safe to hold onto the
|
74
|
+
# data to attempt to send again or if it needs to be dropped. The Contrast Server can choose what to do
|
75
|
+
# with message from improperly throttled Agents, including dropping them.
|
76
|
+
# header:
|
77
|
+
# Retry-After => how long, in seconds, to wait before attempting to send another request to this endpoint,
|
78
|
+
# in the standard format per RFC 2616
|
79
|
+
# used for in observed routes message.
|
80
|
+
return false unless response && (response_code = response&.code)
|
33
81
|
return true if ANALYZE_WHEN.include? response_code
|
34
82
|
|
83
|
+
handle_error response if ERROR_CODES.value?(response_code)
|
84
|
+
# There was error, so analyze the Error and nothing more.
|
35
85
|
false
|
36
86
|
end
|
37
87
|
|
88
|
+
# Analyze the headers of the response code. They have information about the
|
89
|
+
# retry timeout and some response bodies contains error messages.
|
90
|
+
#
|
91
|
+
# @param response [String] the response code from Net::HTTPResponse, which is obnoxiousy a String, not an
|
92
|
+
# Integer
|
93
|
+
# @param message [String] Message to log.
|
94
|
+
# @param mode [Symbol, nil]
|
95
|
+
def handle_response_errors response, message, mode
|
96
|
+
# Set the current mode status.
|
97
|
+
@_mode.status = mode
|
98
|
+
ready_after, error_message, auth_error = extract_response_info response
|
99
|
+
# log, suspend, disable:
|
100
|
+
if mode == @_mode.running
|
101
|
+
log_debug_msg message,
|
102
|
+
response: response.__id__,
|
103
|
+
request: Contrast::Agent::REQUEST_TRACKER.current&.request&.type,
|
104
|
+
error_message: error_message || 'none',
|
105
|
+
auth_error: auth_error || 'none'
|
106
|
+
end
|
107
|
+
suspend_reporting message, ready_after, error_message if mode == @_mode.resending
|
108
|
+
return unless mode == @_mode.disabled
|
109
|
+
|
110
|
+
stop_reporting(message, application: Contrast::APP_CONTEXT.app_name, error_message: error_message)
|
111
|
+
rescue StandardError => e
|
112
|
+
logger.debug('Could not handle Response error information', error: e)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Extract what we've received.
|
116
|
+
#
|
117
|
+
# @param response [Net::HTTP::Response, nil]
|
118
|
+
# @return [Array<String, Integer>] all collected error info.
|
119
|
+
def extract_response_info response
|
120
|
+
# Extract what we got from the response:
|
121
|
+
ready_after = response['Ready-After'] if response.to_hash.keys.map(&:downcase).include?('ready-after')
|
122
|
+
if response.to_hash.keys.map(&:downcase).include?('www-authenticate')
|
123
|
+
auth_error = response['www-Authenticate']
|
124
|
+
end
|
125
|
+
error_message = response.message
|
126
|
+
[ready_after.to_i, error_message, auth_error]
|
127
|
+
end
|
128
|
+
|
129
|
+
# Cease reporting about this application
|
130
|
+
#
|
131
|
+
# @param message [String] Message to log
|
132
|
+
# @param info_hash [Hash] information about the context to log.
|
133
|
+
def stop_reporting message, info_hash
|
134
|
+
Contrast::Agent.reporter&.stop!
|
135
|
+
log_debug_msg(message, info_hash)
|
136
|
+
::Contrast::AGENT.disable!
|
137
|
+
end
|
138
|
+
|
38
139
|
# Applies the settings from the TS response
|
39
140
|
#
|
40
141
|
# @param response [Contrast::Agent::Reporting::Response]
|
41
142
|
def update_agent_settings response
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Contrast::Logger::Log.instance.update(log_file, log_level) if log_file && log_level
|
46
|
-
Contrast::SETTINGS.update_from_server_features(response)
|
47
|
-
end
|
143
|
+
return unless response
|
144
|
+
|
145
|
+
Contrast::SETTINGS.update_from_server_features(response) if response.server_features
|
48
146
|
Contrast::SETTINGS.update_from_application_settings(response) if response.application_settings
|
49
147
|
end
|
50
148
|
|
@@ -114,6 +212,7 @@ module Contrast
|
|
114
212
|
extract_protect response_data, res
|
115
213
|
extract_exclusions response_data, res
|
116
214
|
extract_reactions response_data, res
|
215
|
+
extract_sensitive_data_policy response_data, res
|
117
216
|
res
|
118
217
|
end
|
119
218
|
|
@@ -129,79 +228,6 @@ module Contrast
|
|
129
228
|
res.server_features.telemetry = response_data[:telemetry]
|
130
229
|
res
|
131
230
|
end
|
132
|
-
|
133
|
-
# @param response_data [Hash]
|
134
|
-
# @return res [Contrast::Agent::Reporting::Response]
|
135
|
-
def extract_assess response_data, res
|
136
|
-
assessments = response_data[:settings][:assessment]
|
137
|
-
return unless assessments
|
138
|
-
|
139
|
-
res.application_settings.assess.disabled_rules = assessments[:disabledRules]
|
140
|
-
res.application_settings.assess.session_id = assessments[:session_id]
|
141
|
-
end
|
142
|
-
|
143
|
-
# @param response_data [Hash]
|
144
|
-
# @return res [Contrast::Agent::Reporting::Response]
|
145
|
-
def extract_protect response_data, res
|
146
|
-
protect = response_data[:settings][:defend]
|
147
|
-
return unless protect
|
148
|
-
|
149
|
-
res.application_settings.protect.protection_rules = protect[:protectionRules]
|
150
|
-
res.application_settings.protect.virtual_patches = protect[:virtualPatches]
|
151
|
-
end
|
152
|
-
|
153
|
-
# @param response_data [Hash]
|
154
|
-
# @return res [Contrast::Agent::Reporting::Response]
|
155
|
-
def extract_exclusions response_data, res
|
156
|
-
exclusions = response_data[:settings][:exceptions]
|
157
|
-
return unless exclusions
|
158
|
-
|
159
|
-
res.application_settings.exclusions.code_exclusions = exclusions[:codeExceptions]
|
160
|
-
res.application_settings.exclusions.input_exclusions = exclusions[:inputExceptions]
|
161
|
-
res.application_settings.exclusions.url_exclusions = exclusions[:urlExceptions]
|
162
|
-
end
|
163
|
-
|
164
|
-
# @param response_data [Hash]
|
165
|
-
# @return res [Contrast::Agent::Reporting::Response]
|
166
|
-
def extract_reactions response_data, res
|
167
|
-
res.application_settings.reactions = response_data[:settings][:reactions]
|
168
|
-
end
|
169
|
-
|
170
|
-
# @param response_data [Hash]
|
171
|
-
# @return res [Contrast::Agent::Reporting::Response]
|
172
|
-
def extract_assess_server_features response_data, res
|
173
|
-
assess = response_data[:assessment]
|
174
|
-
return unless assess
|
175
|
-
|
176
|
-
res.server_features.assess.enabled = assess[:enabled]
|
177
|
-
res.server_features.assess.disabled_rules = assess[:disabledRules]
|
178
|
-
res.server_features.assess.sampling = assess[:sampling]
|
179
|
-
res.server_features.assess.sanitizers = assess[:sanitizers]
|
180
|
-
res.server_features.assess.validators = assess[:validators]
|
181
|
-
end
|
182
|
-
|
183
|
-
# @param response_data [Hash]
|
184
|
-
# @return res [Contrast::Agent::Reporting::Response]
|
185
|
-
def extract_protect_server_features response_data, res
|
186
|
-
protect = response_data[:defend]
|
187
|
-
return unless protect
|
188
|
-
|
189
|
-
res.server_features.protect.enabled = protect[:enabled]
|
190
|
-
res.server_features.protect.bot_blocker = protect[:bot_blocker]
|
191
|
-
res.server_features.protect.syslog = protect[:syslog]
|
192
|
-
end
|
193
|
-
|
194
|
-
# @param response_data [Hash]
|
195
|
-
# @return res [Contrast::Agent::Reporting::Response]
|
196
|
-
def extract_protect_lists response_data, res
|
197
|
-
protect = response_data[:defend]
|
198
|
-
return unless protect
|
199
|
-
|
200
|
-
res.server_features.protect.ip_allowlist = protect[:ipAllowlist]
|
201
|
-
res.server_features.protect.ip_denylist = protect[:ipDenyList]
|
202
|
-
res.server_features.protect.log_enchancers = protect[:logEnhancers]
|
203
|
-
res.server_features.protect.rule_definition_list = protect[:ruleDefinitionList]
|
204
|
-
end
|
205
231
|
end
|
206
232
|
end
|
207
233
|
end
|
@@ -5,6 +5,7 @@ require 'contrast/agent/reporting/settings/assess'
|
|
5
5
|
require 'contrast/agent/reporting/settings/protect'
|
6
6
|
require 'contrast/agent/reporting/settings/exclusions'
|
7
7
|
require 'contrast/agent/reporting/settings/reaction'
|
8
|
+
require 'contrast/agent/reporting/settings/sensitive_data_masking'
|
8
9
|
|
9
10
|
module Contrast
|
10
11
|
module Agent
|
@@ -43,6 +44,14 @@ module Contrast
|
|
43
44
|
@_reactions
|
44
45
|
end
|
45
46
|
|
47
|
+
# This object will hold the masking rules send from TS.
|
48
|
+
#
|
49
|
+
# @return sensitive_data_masking [Contrast::Agent::Reporting::Settings::SensitiveDataMasking] this object
|
50
|
+
# includes mask_http_body flag and Array set of rules with keywords in them.
|
51
|
+
def sensitive_data_masking
|
52
|
+
@_sensitive_data_masking ||= Contrast::Agent::Reporting::Settings::SensitiveDataMasking.new
|
53
|
+
end
|
54
|
+
|
46
55
|
# Set the reaction
|
47
56
|
#
|
48
57
|
# @param reactions [Array<Reaction>] {
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/utils/object_share'
|
5
|
+
require 'contrast/agent/reporting/settings/sampling'
|
5
6
|
|
6
7
|
module Contrast
|
7
8
|
module Agent
|
@@ -11,19 +12,6 @@ module Contrast
|
|
11
12
|
# Application level settings for the Assess featureset.
|
12
13
|
# Used for the FeatureSet TS response
|
13
14
|
class AssessServerFeature
|
14
|
-
# Assess rules to disable for this application.
|
15
|
-
#
|
16
|
-
# @return disabled_rules [Array<AssessRuleID>] Array with string rule_ids
|
17
|
-
def disabled_rules
|
18
|
-
@_disabled_rules ||= []
|
19
|
-
end
|
20
|
-
|
21
|
-
# @param disabled_rules [Array<AssessRuleID>] with AssessRuleID strings
|
22
|
-
# @return disabled_rules [Array<AssessRuleID>] Array with string rule_ids
|
23
|
-
def disabled_rules= disabled_rules
|
24
|
-
@_disabled_rules = disabled_rules if disabled_rules.is_a? Array
|
25
|
-
end
|
26
|
-
|
27
15
|
# Indicate if the assess feature set is enabled for this server or not.
|
28
16
|
#
|
29
17
|
# @return enabled [Boolean]
|
@@ -41,17 +29,9 @@ module Contrast
|
|
41
29
|
|
42
30
|
# Used to control the sampling feature in the agent.
|
43
31
|
#
|
44
|
-
# @return
|
45
|
-
# baseline [Integer] The number of baseline requests to take before switching to sampling
|
46
|
-
# for the window.
|
47
|
-
# enabled [Boolean] If the sampling feature should be used or not.
|
48
|
-
# frequency [Integer] The number of requests to skip before observing during the sampling
|
49
|
-
# window after the baseline.
|
50
|
-
# responseFrequency [Integer]
|
51
|
-
# window [Integer]
|
52
|
-
# }
|
32
|
+
# @return [Contrast::Agent::Reporting::Settings::Sampling]
|
53
33
|
def sampling
|
54
|
-
@_sampling ||= {}
|
34
|
+
@_sampling ||= Contrast::Agent::Reporting::Settings::Sampling.new({})
|
55
35
|
end
|
56
36
|
|
57
37
|
# set sampling
|
@@ -65,17 +45,9 @@ module Contrast
|
|
65
45
|
# responseFrequency [Integer]
|
66
46
|
# window [Integer]
|
67
47
|
# }
|
68
|
-
# @return
|
69
|
-
# baseline [Integer] The number of baseline requests to take before switching to sampling
|
70
|
-
# for the window.
|
71
|
-
# enabled [Boolean] If the sampling feature should be used or not.
|
72
|
-
# frequency [Integer] The number of requests to skip before observing during the sampling
|
73
|
-
# window after the baseline.
|
74
|
-
# responseFrequency [Integer]
|
75
|
-
# window [Integer]
|
76
|
-
# }
|
48
|
+
# @return [Contrast::Agent::Reporting::Settings::Sampling]
|
77
49
|
def sampling= sampling
|
78
|
-
@_sampling = sampling if sampling.is_a? Hash
|
50
|
+
@_sampling = Contrast::Agent::Reporting::Settings::Sampling.new(sampling) if sampling.is_a? Hash
|
79
51
|
end
|
80
52
|
|
81
53
|
# The sanitizers defined by the user for use by the agent on this server for this organization.
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/utils/object_share'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Reporting
|
9
|
+
module Settings
|
10
|
+
# Sampling controls for the Assess Features provided by TeamServer
|
11
|
+
class Sampling
|
12
|
+
# @return [Integer] The number of baseline requests to take before switching to sampling for the window.
|
13
|
+
attr_reader :baseline
|
14
|
+
# @return [Boolean] If the sampling feature should be used or not.
|
15
|
+
attr_reader :enabled
|
16
|
+
# @return [Integer] The number of requests to skip before observing during the sampling window after the
|
17
|
+
# baseline.
|
18
|
+
attr_reader :request_frequency
|
19
|
+
# @return [Integer] The number of responses to skip before observing during the sampling window after the
|
20
|
+
# baseline.
|
21
|
+
attr_reader :response_frequency
|
22
|
+
# @return [Integer] The length of time for which the sample period is valid, in ms.
|
23
|
+
attr_reader :window_ms
|
24
|
+
|
25
|
+
def initialize hsh
|
26
|
+
@baseline = hsh[:baseline]
|
27
|
+
@enabled = hsh[:enabled]
|
28
|
+
@request_frequency = hsh[:frequency]
|
29
|
+
@response_frequency = hsh[:responseFrequency]
|
30
|
+
@window_ms = hsh[:window]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/agent/reporting/settings/sensitive_data_masking_rule'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Reporting
|
9
|
+
# This module will hold all the settings from the TS responce
|
10
|
+
module Settings
|
11
|
+
# Protect level settings for the sensitive_data_masking_policy.
|
12
|
+
# Configuration of the masking parameters will be delivered dynamically from TeamServer
|
13
|
+
# during an ApplicationSettings response from requests to TS endpoints that return that
|
14
|
+
# structure.
|
15
|
+
# https://contrast.atlassian.net/wiki/spaces/~699189087/pages/807960614/Sensitive+Data+Masking+Design
|
16
|
+
class SensitiveDataMasking
|
17
|
+
# Policy flag to enable the use of masking on request body
|
18
|
+
# Here is set to defaults to true.
|
19
|
+
#
|
20
|
+
# @return true | false
|
21
|
+
def mask_http_body?
|
22
|
+
@_mask_http_body
|
23
|
+
end
|
24
|
+
|
25
|
+
# Set the flag for request body masking
|
26
|
+
#
|
27
|
+
# @param bool [Boolean]
|
28
|
+
# @return true | false
|
29
|
+
def mask_http_body= bool
|
30
|
+
@_mask_http_body = bool.nil? ? true : !!bool
|
31
|
+
end
|
32
|
+
|
33
|
+
# Policy flag to enable the use of masking on attack vector.
|
34
|
+
# Here is set to defaults to false.
|
35
|
+
#
|
36
|
+
# @return true | false
|
37
|
+
def mask_attack_vector?
|
38
|
+
@_mask_attack_vector
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set the flag for using masking on attack vector
|
42
|
+
#
|
43
|
+
# @param bool [Boolean]
|
44
|
+
# @return true | false
|
45
|
+
def mask_attack_vector= bool
|
46
|
+
@_mask_attack_vector = !!bool
|
47
|
+
end
|
48
|
+
|
49
|
+
# Rules to follow when using the masking
|
50
|
+
#
|
51
|
+
# @return rules [Array<Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule>, []]
|
52
|
+
def rules
|
53
|
+
@_rules ||= Contrast::Agent::Reporting::Settings::RulesArray.new
|
54
|
+
end
|
55
|
+
|
56
|
+
# Assign rules array
|
57
|
+
#
|
58
|
+
# @param rules [Array<Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule>]
|
59
|
+
# @return rules [Array<Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule>, []]
|
60
|
+
def rules= rules
|
61
|
+
@_rules = rules if rules_array? rules
|
62
|
+
end
|
63
|
+
|
64
|
+
# Build rules from hash
|
65
|
+
#
|
66
|
+
# @param settings_rules [Hash] Response settings under Settings/sensitive_data_masking_policy/rules
|
67
|
+
# @return rules [Array<Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule>, nil
|
68
|
+
def build_rules_form_settings settings_rules
|
69
|
+
return unless settings_rules || settings_rules.empty?
|
70
|
+
|
71
|
+
settings_rules.each do |rule|
|
72
|
+
instance = Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule.new
|
73
|
+
instance.rule_id = rule[:id]
|
74
|
+
instance.keywords = rule[:keywords]
|
75
|
+
rules << instance
|
76
|
+
end
|
77
|
+
rules
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Determine if parameter is array of Rules.
|
83
|
+
#
|
84
|
+
# @param array [Array<Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule>] Array of keywords.
|
85
|
+
# @return true | false
|
86
|
+
def rules_array? array
|
87
|
+
return false unless array.is_a?(Array)
|
88
|
+
|
89
|
+
array.all?(Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Simple validation class for Rules Array.
|
94
|
+
class RulesArray < Array
|
95
|
+
# Do not push anything except Rules instances.
|
96
|
+
# :<< method is called on object directly so this
|
97
|
+
# will make sure the data pushed is as expected.
|
98
|
+
#
|
99
|
+
# @param item [Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule]
|
100
|
+
# @return itself [Array<Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule>]
|
101
|
+
def << item
|
102
|
+
return itself unless item.instance_of? Contrast::Agent::Reporting::Settings::SensitiveDataMaskingRule
|
103
|
+
|
104
|
+
super
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/utils/object_share'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Reporting
|
9
|
+
# This module will hold all the settings from the TS responce
|
10
|
+
module Settings
|
11
|
+
# Protect level settings for the sensitive_data_masking_policy Rule entry.
|
12
|
+
class SensitiveDataMaskingRule
|
13
|
+
# Id for this rule.
|
14
|
+
#
|
15
|
+
# @return rule_id [String] Name of parameter to mask.
|
16
|
+
def rule_id
|
17
|
+
@_rule_id ||= Contrast::Utils::ObjectShare::EMPTY_STRING
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set the rule name.
|
21
|
+
#
|
22
|
+
# @param id [String] Name of parameter to mask.
|
23
|
+
# @return rule_id [String]
|
24
|
+
def rule_id= id
|
25
|
+
@_rule_id = id.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
# Array of keywords to mask values for the rule_id.
|
29
|
+
#
|
30
|
+
# @return keywords [Array<String>] set of keywords
|
31
|
+
def keywords
|
32
|
+
@_keywords ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set keywords array.
|
36
|
+
#
|
37
|
+
# @param words_array [Array<String>]
|
38
|
+
# @return keywords [Array<String>] set of keywords
|
39
|
+
def keywords= words_array
|
40
|
+
@_keywords = words_array if string_array? words_array
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Determine if a array is array of strings.
|
46
|
+
#
|
47
|
+
# @param array [Array<String] Array of keywords.
|
48
|
+
# @return true | false
|
49
|
+
def string_array? array
|
50
|
+
return false unless array.is_a?(Array) && array.any?
|
51
|
+
|
52
|
+
array.all? String
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -42,7 +42,7 @@ module Contrast
|
|
42
42
|
attr_reader :activity, :logging_hash, :observed_route, :new_observed_route, :request, :response, :route,
|
43
43
|
:speedracer_input_analysis, :agent_input_analysis, :server_activity, :timer
|
44
44
|
|
45
|
-
def initialize rack_request, app_loaded
|
45
|
+
def initialize rack_request, app_loaded: true
|
46
46
|
with_contrast_scope do
|
47
47
|
# all requests get a timer and hash
|
48
48
|
@timer = Contrast::Utils::Timer.new
|
@@ -126,6 +126,7 @@ module Contrast
|
|
126
126
|
@response = Contrast::Agent::Response.new(rack_response)
|
127
127
|
return unless @sample_res
|
128
128
|
|
129
|
+
#
|
129
130
|
# TODO: RUBY-1376 once all rules translated, move this to if/else w/ the enabled
|
130
131
|
if Contrast::Agent::Reporter.enabled?
|
131
132
|
Contrast::Agent::Assess::Rule::Response::AutoComplete.new.analyze(@response)
|
@@ -209,7 +210,6 @@ module Contrast
|
|
209
210
|
@agent_input_analysis = ia
|
210
211
|
else
|
211
212
|
logger.trace('Analysis from Agent was empty.')
|
212
|
-
|
213
213
|
end
|
214
214
|
end
|
215
215
|
|
@@ -5,6 +5,7 @@ require 'contrast/components/logger'
|
|
5
5
|
require 'contrast/components/scope'
|
6
6
|
require 'contrast/agent/reporting/reporter'
|
7
7
|
require 'contrast/agent/reporting/reporting_utilities/dtm_message'
|
8
|
+
require 'contrast/agent/reporting/masker/masker'
|
8
9
|
|
9
10
|
module Contrast
|
10
11
|
module Agent
|
@@ -53,6 +54,9 @@ module Contrast
|
|
53
54
|
reporter = Contrast::Agent.reporter
|
54
55
|
return unless reporter
|
55
56
|
|
57
|
+
# Mask Sensitive Data
|
58
|
+
Contrast::Agent::Reporting::Masker.mask context.activity
|
59
|
+
|
56
60
|
Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage(context.activity)
|
57
61
|
[
|
58
62
|
context.new_observed_route,
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Contrast
|
5
|
+
module Agent
|
6
|
+
# This class will hold the all the mutual information for the Telemetry Exception
|
7
|
+
class TelemetryExceptionBase
|
8
|
+
def to_controlled_hash; end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
# Validate required and option fields
|
13
|
+
#
|
14
|
+
# @param validations [Hash] Validation hash to use
|
15
|
+
# @return [nil]
|
16
|
+
def validate validations
|
17
|
+
validations.each do |k, v|
|
18
|
+
next if v[:required] == false
|
19
|
+
|
20
|
+
validate_field validations[k], k
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# This method will validate every single field passed from validate
|
26
|
+
#
|
27
|
+
# @param validation_pair [Hash] Validation hash to use
|
28
|
+
# @param key[String] The key to check in VALIDATIONS
|
29
|
+
def validate_field validation_pair, key
|
30
|
+
value_to_validate = send(key.to_sym)
|
31
|
+
validate_class value_to_validate, validation_pair[:class], key if validation_pair.key?(:class)
|
32
|
+
value_length = value_to_validate.cs__is_a?(String) ? value_to_validate.length : value_to_validate.entries.length
|
33
|
+
unless validation_pair[:range].include?(value_length)
|
34
|
+
raise ArgumentError, "The provided value for #{ key } is invalid"
|
35
|
+
end
|
36
|
+
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# With the all nested classes, we still need to double check if everything passed along the way
|
41
|
+
# is right
|
42
|
+
# @param message [Object] The message we want to check the class of
|
43
|
+
# @param klass [Class] The klass we want to check the message with
|
44
|
+
# @param field [Object] The field with the error
|
45
|
+
def validate_class message, klass, field
|
46
|
+
message = message[0] if message.cs__is_a?(Array)
|
47
|
+
raise ArgumentError, "The provided value for #{ field } is invalid" unless message.cs__is_a? klass
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|