contrast-agent 6.6.2 → 6.6.5
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/assess/policy/trigger_method.rb +21 -6
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +2 -0
- data/lib/contrast/agent/at_exit_hook.rb +1 -7
- data/lib/contrast/agent/inventory/database_config.rb +16 -12
- data/lib/contrast/agent/inventory/policy/datastores.rb +1 -2
- data/lib/contrast/agent/middleware.rb +0 -1
- data/lib/contrast/agent/protect/rule/base.rb +16 -20
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +5 -4
- data/lib/contrast/agent/protect/rule/deserialization.rb +5 -4
- data/lib/contrast/agent/protect/rule/path_traversal.rb +9 -7
- data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +16 -14
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/protect/rule/xxe.rb +9 -6
- data/lib/contrast/agent/reporting/attack_result/attack_result.rb +8 -0
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +85 -36
- data/lib/contrast/agent/reporting/attack_result/user_input.rb +11 -0
- data/lib/contrast/agent/reporting/details/bot_blocker_details.rb +29 -0
- data/lib/contrast/agent/reporting/details/cmd_injection_details.rb +30 -0
- data/lib/contrast/agent/reporting/details/details.rb +18 -0
- data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/ip_denylist_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/no_sqli_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/path_traversal_details.rb +24 -0
- data/lib/contrast/agent/reporting/details/path_traversal_semantic_analysis_details.rb +32 -0
- data/lib/contrast/agent/reporting/details/protect_rule_details.rb +17 -0
- data/lib/contrast/agent/reporting/details/sqli_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/untrusted_deserialization_details.rb +27 -0
- data/lib/contrast/agent/reporting/details/virtual_patch_details.rb +24 -0
- data/lib/contrast/agent/reporting/details/xss_details.rb +33 -0
- data/lib/contrast/agent/reporting/details/xss_match.rb +30 -0
- data/lib/contrast/agent/reporting/details/xxe_details.rb +36 -0
- data/lib/contrast/agent/reporting/details/xxe_match.rb +25 -0
- data/lib/contrast/agent/reporting/details/xxe_wrapper.rb +25 -0
- data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +1 -1
- data/lib/contrast/agent/reporting/masker/masker.rb +78 -65
- data/lib/contrast/agent/reporting/masker/masker_utils.rb +1 -30
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +84 -15
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +13 -25
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +17 -22
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +46 -125
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -16
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -18
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -14
- data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +29 -20
- data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +45 -10
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -7
- data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +2 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
- data/lib/contrast/agent/request.rb +2 -0
- data/lib/contrast/agent/request_context.rb +13 -4
- data/lib/contrast/agent/request_context_extend.rb +59 -40
- data/lib/contrast/agent/request_handler.rb +7 -9
- data/lib/contrast/agent/service_heartbeat.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/decorators/message.rb +1 -1
- data/lib/contrast/components/app_context.rb +62 -8
- data/lib/contrast/components/app_context_extend.rb +8 -8
- data/lib/contrast/config/assess_configuration.rb +1 -1
- data/lib/contrast/config/root_configuration.rb +6 -4
- data/lib/contrast/config.rb +0 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -6
- data/lib/contrast/utils/assess/event_limit_utils.rb +26 -7
- data/lib/contrast/utils/log_utils.rb +16 -10
- data/lib/contrast/utils/net_http_base.rb +5 -6
- data/lib/contrast/utils/string_utils.rb +2 -6
- data/lib/contrast.rb +1 -1
- metadata +30 -14
- data/lib/contrast/config/application_configuration.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a7491020083af4de63e878defb8a57583d22258ec534fba4b6d7ad923861c55
|
4
|
+
data.tar.gz: 9c784b84420c8caa8f3123e8dc053b21a7a8964058f8e5b073cbf794275e9793
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8896ddef1bb19c81b279f54c3ec1f5458474d7f6a9b5b7910670c2e87e00a1bdfab2a6ab422bb585eb125ba707839b9967490b2001951b360b211d3e41f2b15e
|
7
|
+
data.tar.gz: 2a6e933028aaf9b899cc107e565a112344408563035c20fec1cd3625d02bbd1de5ca6d02566a9379a07b532bda16d64b0868f2a30b0183aaafcfc0d2d3dc1a6d
|
@@ -9,6 +9,7 @@ require 'contrast/utils/sha256_builder'
|
|
9
9
|
require 'contrast/utils/assess/trigger_method_utils'
|
10
10
|
require 'contrast/agent/assess/events/event_data'
|
11
11
|
require 'contrast/agent/reporting/reporting_events/preflight'
|
12
|
+
require 'contrast/agent/reporting/reporting_events/application_activity'
|
12
13
|
require 'contrast/agent/reporting/reporting_events/preflight_message'
|
13
14
|
require 'contrast/agent/reporting/reporting_events/route_discovery'
|
14
15
|
require 'contrast/agent/reporting/reporting_utilities/reporting_storage'
|
@@ -118,7 +119,8 @@ module Contrast
|
|
118
119
|
append_to_finding(finding, event_data, request)
|
119
120
|
logger.trace('Finding created', node_id: trigger_node.id, source_id: source.__id__,
|
120
121
|
rule: trigger_node.rule_id)
|
121
|
-
|
122
|
+
# check here if we need to add that finding
|
123
|
+
report_finding(finding, request) unless not_reported?(finding)
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
@@ -133,22 +135,21 @@ module Contrast
|
|
133
135
|
def report_finding finding, request = nil
|
134
136
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
135
137
|
if context
|
136
|
-
|
138
|
+
# REMOVE_DTM_ACTIVITY
|
139
|
+
context.dtm_activity.findings << finding
|
137
140
|
return
|
138
141
|
end
|
139
142
|
|
140
143
|
return unless ::Contrast::ASSESS.non_request_tracking? || NON_REQUEST_RULES.include?(finding.rule_id)
|
141
144
|
|
145
|
+
# REMOVE_DTM_ACTIVITY Findings still using dtm - REMOVE
|
142
146
|
activity = Contrast::Api::Dtm::Activity.new
|
143
|
-
activity.findings << finding
|
144
147
|
if request
|
145
|
-
activity.
|
148
|
+
activity.request = request.dtm
|
146
149
|
else
|
147
150
|
logger.debug('Attempted to report finding without request', finding: finding)
|
148
151
|
end
|
149
152
|
|
150
|
-
# If we're out of request context, then we need to report this finding ourselves,
|
151
|
-
# so we'll send it in the one-off activity we created.
|
152
153
|
Contrast::Agent.messaging_queue&.send_event_eventually(activity)
|
153
154
|
end
|
154
155
|
|
@@ -269,6 +270,20 @@ module Contrast
|
|
269
270
|
|
270
271
|
MINIMUM_FINDING_VERSION
|
271
272
|
end
|
273
|
+
|
274
|
+
# Check if the following finding was already added to context.activity
|
275
|
+
# We could use this after we get rid of the MessagingQueue.
|
276
|
+
#
|
277
|
+
# @param finding [Contrast::Api::Dtm::Finding] Finding we're about to report
|
278
|
+
# @return Boolean
|
279
|
+
def not_reported? finding
|
280
|
+
return false unless (current = Contrast::Agent::REQUEST_TRACKER.current)
|
281
|
+
|
282
|
+
currently_added_findings = current.dtm_activity.findings
|
283
|
+
return false if currently_added_findings.empty?
|
284
|
+
|
285
|
+
currently_added_findings.any? { |f| f.rule_id == finding.rule_id && f.hash_code == finding.hash_code }
|
286
|
+
end
|
272
287
|
end
|
273
288
|
end
|
274
289
|
end
|
@@ -113,6 +113,8 @@ module Contrast
|
|
113
113
|
class_name = clazz.cs__name
|
114
114
|
|
115
115
|
finding = assign_finding(class_name, constant_string)
|
116
|
+
# TODO: RUBY-1705
|
117
|
+
# The only place we still use dtm activity
|
116
118
|
activity = Contrast::Api::Dtm::Activity.new
|
117
119
|
activity.findings << finding
|
118
120
|
Contrast::Agent.messaging_queue.send_event_eventually(activity, force: true)
|
@@ -37,13 +37,7 @@ module Contrast
|
|
37
37
|
].compact.each do |event|
|
38
38
|
Contrast::Agent.reporter&.send_event_immediately(event)
|
39
39
|
end
|
40
|
-
|
41
|
-
if Contrast::Agent::Reporter.enabled?
|
42
|
-
event = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
|
43
|
-
Contrast::Agent.reporter&.send_event_immediately(event)
|
44
|
-
else
|
45
|
-
Contrast::Agent.messaging_queue&.send_event_immediately(context.activity)
|
46
|
-
end
|
40
|
+
Contrast::Agent.reporter&.send_event_immediately(context.activity)
|
47
41
|
end
|
48
42
|
end
|
49
43
|
end
|
@@ -30,20 +30,19 @@ module Contrast
|
|
30
30
|
# Both report the same
|
31
31
|
# Contrast::Api::Dtm::ArchitectureComponent, but have different names for their fields.
|
32
32
|
#
|
33
|
-
# @param activity_or_update [Contrast::
|
33
|
+
# @param activity_or_update [Contrast::Agent::Reporting::ApplicationUpdate]
|
34
34
|
# @param hash_or_str [Hash, String] the database connection information
|
35
35
|
def append_db_config activity_or_update, hash_or_str = active_record_config
|
36
36
|
arr = build_from_db_config(hash_or_str)
|
37
37
|
return unless arr&.any?
|
38
38
|
|
39
|
-
arr.each do |
|
40
|
-
next unless
|
39
|
+
arr.each do |component|
|
40
|
+
next unless component
|
41
41
|
|
42
|
-
if activity_or_update.
|
43
|
-
activity_or_update.
|
42
|
+
if activity_or_update.cs__is_a?(Contrast::Agent::Reporting::ApplicationUpdate)
|
43
|
+
activity_or_update.components << component
|
44
44
|
else
|
45
|
-
|
46
|
-
activity_or_update.components << converted_comp
|
45
|
+
activity_or_update.attach_inventory(component)
|
47
46
|
end
|
48
47
|
end
|
49
48
|
rescue StandardError => e
|
@@ -78,10 +77,15 @@ module Contrast
|
|
78
77
|
# reporting.
|
79
78
|
#
|
80
79
|
# @param hash_or_str [Hash, String]
|
81
|
-
# @return [Array<Contrast::
|
80
|
+
# @return [Array<Contrast::Agent::Reporting::ArchitectureComponent>, nil]
|
82
81
|
def build_from_db_config hash_or_str
|
83
82
|
return unless hash_or_str
|
84
83
|
|
84
|
+
# we need to handle types of HashConfig, which != Hash
|
85
|
+
# for example ActiveRecord::DatabaseConfigurations::HashConfig is type of active_record config
|
86
|
+
# but the method is not handling it properly
|
87
|
+
# so we need to handle it here and extract the hash
|
88
|
+
hash_or_str = hash_or_str.configuration_hash if hash_or_str.cs__respond_to?(:configuration_hash)
|
85
89
|
if hash_or_str.is_a?(Hash)
|
86
90
|
build_from_db_hash(hash_or_str)
|
87
91
|
else
|
@@ -93,9 +97,9 @@ module Contrast
|
|
93
97
|
# understandable by TeamServer.
|
94
98
|
#
|
95
99
|
# @param hash [Hash] the information used to open a database connection
|
96
|
-
# @return [Array<Contrast::
|
100
|
+
# @return [Array<Contrast::Agent::Reporting::ArchitectureComponent>]
|
97
101
|
def build_from_db_hash hash
|
98
|
-
ac = Contrast::
|
102
|
+
ac = Contrast::Agent::Reporting::ArchitectureComponent.build_database
|
99
103
|
ac.vendor = hash[:adapter] || hash[ADAPTER] || Contrast::Utils::ObjectShare::EMPTY_STRING
|
100
104
|
ac.remote_host = host_from_hash(hash)
|
101
105
|
ac.remote_port = port_from_hash(hash)
|
@@ -126,7 +130,7 @@ module Contrast
|
|
126
130
|
# mysql+mysqlconnector://scott:tiger@localhost/foo # pragma: allowlist secret
|
127
131
|
#
|
128
132
|
# @param str [String] the DB connection string
|
129
|
-
# @return [Array<Contrast::
|
133
|
+
# @return [Array<Contrast::Agent::Reporting::ArchitectureComponent>, nil]
|
130
134
|
def build_from_db_string str
|
131
135
|
adapter, hosts, database = split_connection_str(str)
|
132
136
|
return unless adapter && hosts && database
|
@@ -135,7 +139,7 @@ module Contrast
|
|
135
139
|
hosts.split(Contrast::Utils::ObjectShare::COMMA).map do |s|
|
136
140
|
host, port = s.split(Contrast::Utils::ObjectShare::COLON)
|
137
141
|
|
138
|
-
ac = Contrast::
|
142
|
+
ac = Contrast::Agent::Reporting::ArchitectureComponent.build_database
|
139
143
|
ac.vendor = Contrast::Utils::StringUtils.force_utf8(adapter)
|
140
144
|
ac.remote_host = Contrast::Utils::StringUtils.force_utf8(host)
|
141
145
|
ac.remote_port = port.to_i
|
@@ -13,9 +13,8 @@ module Contrast
|
|
13
13
|
# in which database operations occur. It is responsible for deciding if
|
14
14
|
# the given invocation is worth reporting or not.
|
15
15
|
module DataStores
|
16
|
+
extend Contrast::Components::Logger::InstanceMethods
|
16
17
|
class << self
|
17
|
-
extend Contrast::Components::Logger::InstanceMethods
|
18
|
-
|
19
18
|
# The key used in policy.json to indicate the database type to
|
20
19
|
# report.
|
21
20
|
DATA_STORE_MARKER = 'data_store'
|
@@ -169,9 +169,9 @@ module Contrast
|
|
169
169
|
#
|
170
170
|
# @param context [Contrast::Agent::RequestContext] the context of the
|
171
171
|
# request in which this input is evaluated.
|
172
|
-
# @param result [Contrast::
|
172
|
+
# @param result [Contrast::Agent::Reporting::AttackResult]
|
173
173
|
def append_to_activity context, result
|
174
|
-
context.activity.
|
174
|
+
context.activity.attach_defend(result) if result
|
175
175
|
end
|
176
176
|
|
177
177
|
# With this we log to CEF
|
@@ -179,15 +179,11 @@ module Contrast
|
|
179
179
|
# @param result [Contrast::Api::Dtm::AttackResult]
|
180
180
|
# @param attack [Symbol] the type of message we want to send
|
181
181
|
# @param value [String] the input value we want to log
|
182
|
-
def cef_logging result, attack = :ineffective_attack, value
|
183
|
-
|
184
|
-
outcome =
|
185
|
-
|
186
|
-
|
187
|
-
Contrast::Api::Dtm::AttackResult::ResponseType.get_name_by_tag(result.response)
|
188
|
-
end
|
189
|
-
input_type = extract_input_type(sample_to_json[:user_input].input_type)
|
190
|
-
input_value = value || sample_to_json[:user_input].value
|
182
|
+
def cef_logging result, attack = :ineffective_attack, value: nil
|
183
|
+
sample = result.samples[0]
|
184
|
+
outcome = result.response.to_s
|
185
|
+
input_type = sample.user_input.input_type.to_s
|
186
|
+
input_value = sample.user_input.value || value
|
191
187
|
cef_logger.send(attack, result.rule_id, outcome, input_type, input_value)
|
192
188
|
end
|
193
189
|
|
@@ -238,9 +234,9 @@ module Contrast
|
|
238
234
|
def update_successful_attack_response context, ia_result, result, attack_string = nil
|
239
235
|
case mode
|
240
236
|
when Contrast::Api::Settings::ProtectionRule::Mode::MONITOR
|
241
|
-
result.response = Contrast::
|
237
|
+
result.response = Contrast::Agent::Reporting::ResponseType::MONITORED
|
242
238
|
when Contrast::Api::Settings::ProtectionRule::Mode::BLOCK
|
243
|
-
result.response = Contrast::
|
239
|
+
result.response = Contrast::Agent::Reporting::ResponseType::BLOCKED
|
244
240
|
end
|
245
241
|
|
246
242
|
ia_result.attack_count = ia_result.attack_count + 1 if ia_result
|
@@ -259,9 +255,9 @@ module Contrast
|
|
259
255
|
def update_perimeter_attack_response context, ia_result, result
|
260
256
|
if mode == Contrast::Api::Settings::ProtectionRule::Mode::BLOCK_AT_PERIMETER
|
261
257
|
result.response = if blocked_rule?(ia_result)
|
262
|
-
Contrast::
|
258
|
+
Contrast::Agent::Reporting::ResponseType::BLOCKED
|
263
259
|
else
|
264
|
-
Contrast::
|
260
|
+
Contrast::Agent::Reporting::ResponseType::BLOCKED_AT_PERIMETER
|
265
261
|
end
|
266
262
|
log_rule_matched(context, ia_result, result.response)
|
267
263
|
elsif ia_result.nil? || ia_result.attack_count.zero?
|
@@ -276,9 +272,9 @@ module Contrast
|
|
276
272
|
#
|
277
273
|
# @param _context [Contrast::Agent::RequestContext] the context of
|
278
274
|
# the current request
|
279
|
-
# @return [Contrast::
|
275
|
+
# @return [Contrast::Agent::Reporting::AttackResult]
|
280
276
|
def build_attack_result _context
|
281
|
-
result = Contrast::
|
277
|
+
result = Contrast::Agent::Reporting::AttackResult.new
|
282
278
|
result.rule_id = rule_name
|
283
279
|
result
|
284
280
|
end
|
@@ -311,7 +307,7 @@ module Contrast
|
|
311
307
|
end
|
312
308
|
|
313
309
|
def build_base_sample context, ia_result
|
314
|
-
Contrast::
|
310
|
+
Contrast::Agent::Reporting::RaspRuleSample.build(context, ia_result)
|
315
311
|
end
|
316
312
|
|
317
313
|
def log_rule_matched _context, ia_result, response, _matched_string = nil
|
@@ -366,9 +362,9 @@ module Contrast
|
|
366
362
|
# determined to be an attack
|
367
363
|
def assign_reporter_response_type ia_result
|
368
364
|
if suspicious_rule?(ia_result) && Contrast::CONTRAST_SERVICE.use_agent_communication?
|
369
|
-
Contrast::
|
365
|
+
Contrast::Agent::Reporting::ResponseType::SUSPICIOUS
|
370
366
|
else
|
371
|
-
Contrast::
|
367
|
+
Contrast::Agent::Reporting::ResponseType::PROBED
|
372
368
|
end
|
373
369
|
end
|
374
370
|
end
|
@@ -6,6 +6,7 @@ require 'contrast/utils/stack_trace_utils'
|
|
6
6
|
require 'contrast/utils/object_share'
|
7
7
|
require 'contrast/components/logger'
|
8
8
|
require 'contrast/agent/reporting/input_analysis/input_type'
|
9
|
+
require 'contrast/agent/reporting/details/cmd_injection_details'
|
9
10
|
|
10
11
|
module Contrast
|
11
12
|
module Agent
|
@@ -97,17 +98,17 @@ module Contrast
|
|
97
98
|
# evaluation
|
98
99
|
def build_sample context, input_analysis_result, candidate_string, **_kwargs
|
99
100
|
sample = build_base_sample(context, input_analysis_result)
|
100
|
-
sample.
|
101
|
+
sample.details = Contrast::Agent::Reporting::Details::CmdInjectionDetails.new
|
101
102
|
|
102
103
|
command = candidate_string || input_analysis_result.value
|
103
104
|
command = Contrast::Utils::StringUtils.protobuf_safe_string(command)
|
104
|
-
sample.
|
105
|
-
sample.
|
105
|
+
sample.details.cmd = command
|
106
|
+
sample.details.end_idx = command.length
|
106
107
|
|
107
108
|
# This is a special case where the user input is UNKNOWN_USER_INPUT but
|
108
109
|
# we want to send the attack value
|
109
110
|
if input_analysis_result.nil?
|
110
|
-
ui = Contrast::
|
111
|
+
ui = Contrast::Agent::Reporting::UserInput.new
|
111
112
|
ui.input_type = :UNKNOWN
|
112
113
|
ui.value = command
|
113
114
|
sample.user_input = ui
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base'
|
5
|
+
require 'contrast/agent/reporting/details/untrusted_deserialization_details'
|
5
6
|
require 'contrast/components/logger'
|
6
7
|
|
7
8
|
module Contrast
|
@@ -116,7 +117,7 @@ module Contrast
|
|
116
117
|
ia_result = build_evaluation(gadget_command)
|
117
118
|
result = build_attack_with_match(context, ia_result, nil, gadget_command, **kwargs)
|
118
119
|
append_to_activity(context, result)
|
119
|
-
cef_logging(result, :successful_attack, gadget_command)
|
120
|
+
cef_logging(result, :successful_attack, value: gadget_command)
|
120
121
|
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
|
121
122
|
end
|
122
123
|
|
@@ -135,13 +136,13 @@ module Contrast
|
|
135
136
|
# to render this attack event in TeamServer.
|
136
137
|
def build_sample context, input_analysis_result, _candidate_string, **kwargs
|
137
138
|
sample = build_base_sample(context, input_analysis_result)
|
138
|
-
sample.
|
139
|
+
sample.details = Contrast::Agent::Reporting::Details::UntrustedDeserializationDetails.new
|
139
140
|
|
140
141
|
deserializer = Contrast::Utils::StringUtils.protobuf_safe_string(kwargs[:GADGET_TYPE])
|
141
|
-
sample.
|
142
|
+
sample.details.deserializer = deserializer
|
142
143
|
|
143
144
|
command = !!kwargs[:COMMAND_SCOPE]
|
144
|
-
sample.
|
145
|
+
sample.details.cmd = command
|
145
146
|
|
146
147
|
sample
|
147
148
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base_service'
|
5
5
|
require 'contrast/utils/stack_trace_utils'
|
6
|
+
require 'contrast/agent/reporting/details/path_traversal_details'
|
7
|
+
require 'contrast/agent/reporting/details/path_traversal_semantic_analysis_details'
|
6
8
|
|
7
9
|
module Contrast
|
8
10
|
module Agent
|
@@ -48,7 +50,7 @@ module Contrast
|
|
48
50
|
append_to_activity(context, result)
|
49
51
|
return unless blocked?
|
50
52
|
|
51
|
-
cef_logging(result, :successful_attack
|
53
|
+
cef_logging(result, :successful_attack)
|
52
54
|
raise(Contrast::SecurityException.new(self,
|
53
55
|
"Path Traversal rule triggered. Call to File.#{ method } blocked."))
|
54
56
|
end
|
@@ -65,9 +67,9 @@ module Contrast
|
|
65
67
|
# evaluation
|
66
68
|
def build_sample context, input_analysis_result, path, **_kwargs
|
67
69
|
sample = build_base_sample(context, input_analysis_result)
|
68
|
-
sample.
|
70
|
+
sample.details = Contrast::Agent::Reporting::Details::PathTraversalDetails.new
|
69
71
|
path ||= input_analysis_result.value
|
70
|
-
sample.
|
72
|
+
sample.details.path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
|
71
73
|
sample
|
72
74
|
end
|
73
75
|
|
@@ -76,17 +78,17 @@ module Contrast
|
|
76
78
|
# Build a subclass of the RaspRuleSample if the sample matches
|
77
79
|
def build_rep_sample context, path
|
78
80
|
sample = build_base_sample(context, nil)
|
79
|
-
sample.
|
81
|
+
sample.details = Contrast::Agent::Reporting::Details::PathTraversalSemanticAnalysisDetails.new
|
80
82
|
path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
|
81
|
-
sample.
|
83
|
+
sample.details.path = path
|
82
84
|
|
83
85
|
if custom_code_access_sysfile_enabled? && custom_code_accessing_system_file?(path)
|
84
|
-
sample.
|
86
|
+
sample.details.findings << :CUSTOM_CODE_ACCESSING_SYSTEM_FILES
|
85
87
|
return sample
|
86
88
|
end
|
87
89
|
|
88
90
|
if common_file_exploits_enabled? && contains_known_attack_signatures?(path)
|
89
|
-
sample.
|
91
|
+
sample.details.findings << :COMMON_FILE_EXPLOITS
|
90
92
|
return sample
|
91
93
|
end
|
92
94
|
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base'
|
5
5
|
require 'contrast/agent/protect/rule/base_service'
|
6
|
+
require 'contrast/agent/reporting/details/sqli_details'
|
7
|
+
require 'contrast/agent/reporting/details/no_sqli_details'
|
6
8
|
|
7
9
|
module Contrast
|
8
10
|
module Agent
|
@@ -18,16 +20,16 @@ module Contrast
|
|
18
20
|
# @candidate_string [String] the value of the input which may be an attack
|
19
21
|
# @kwargs [Hash] key - value pairs of context individual rules need to build out details
|
20
22
|
# to send to the Service to tell the story of the attack
|
21
|
-
# @return [Contrast::
|
23
|
+
# @return [Contrast::Agent::Reporting::RaspRuleSample] the sample from this attack
|
22
24
|
module SqliSample
|
23
25
|
def build_sample context, input_analysis_result, candidate_string, **kwargs
|
24
26
|
sqli_sample = build_base_sample(context, input_analysis_result)
|
25
|
-
sqli_sample.
|
26
|
-
sqli_sample.
|
27
|
-
sqli_sample.
|
28
|
-
sqli_sample.
|
29
|
-
sqli_sample.
|
30
|
-
sqli_sample.
|
27
|
+
sqli_sample.details = Contrast::Agent::Reporting::Details::SqliDetails.new
|
28
|
+
sqli_sample.details.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
|
29
|
+
sqli_sample.details.start_idx = kwargs[:start_idx]
|
30
|
+
sqli_sample.details.end_idx = kwargs[:end_idx]
|
31
|
+
sqli_sample.details.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
|
32
|
+
sqli_sample.details.input_boundary_idx = kwargs[:input_boundary_idx].to_i
|
31
33
|
sqli_sample
|
32
34
|
end
|
33
35
|
end
|
@@ -41,16 +43,16 @@ module Contrast
|
|
41
43
|
# @candidate_string [String] the value of the input which may be an attack
|
42
44
|
# @kwargs [Hash] key - value pairs of context individual rules need to build out details
|
43
45
|
# to send to the Service to tell the story of the attack
|
44
|
-
# @return [Contrast::
|
46
|
+
# @return [Contrast::Agent::Reporting::RaspRuleSample] the sample from this attack
|
45
47
|
module NoSqliSample
|
46
48
|
def build_sample context, input_analysis_result, candidate_string, **kwargs
|
47
49
|
no_sqli_sample = build_base_sample(context, input_analysis_result)
|
48
|
-
no_sqli_sample.
|
49
|
-
no_sqli_sample.
|
50
|
-
no_sqli_sample.
|
51
|
-
no_sqli_sample.
|
52
|
-
no_sqli_sample.
|
53
|
-
no_sqli_sample.
|
50
|
+
no_sqli_sample.details = Contrast::Agent::Reporting::Details::NoSqliDetails.new
|
51
|
+
no_sqli_sample.details.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
|
52
|
+
no_sqli_sample.details.start_idx = kwargs[:start_idx].to_i
|
53
|
+
no_sqli_sample.details.end_idx = kwargs[:end_idx].to_i
|
54
|
+
no_sqli_sample.details.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
|
55
|
+
no_sqli_sample.details.input_boundary_idx = kwargs[:input_boundary_idx].to_i
|
54
56
|
no_sqli_sample
|
55
57
|
end
|
56
58
|
end
|
@@ -61,7 +61,7 @@ module Contrast
|
|
61
61
|
|
62
62
|
append_to_activity(context, result)
|
63
63
|
|
64
|
-
cef_logging(result, :successful_attack, query_string)
|
64
|
+
cef_logging(result, :successful_attack, value: query_string)
|
65
65
|
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
|
66
66
|
end
|
67
67
|
end
|
@@ -2,6 +2,9 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/agent/protect/rule/base'
|
5
|
+
require 'contrast/agent/reporting/details/xxe_details'
|
6
|
+
require 'contrast/agent/reporting/details/xxe_match'
|
7
|
+
require 'contrast/agent/reporting/details/xxe_wrapper'
|
5
8
|
require 'contrast/utils/timer'
|
6
9
|
require 'contrast/components/logger'
|
7
10
|
|
@@ -62,7 +65,7 @@ module Contrast
|
|
62
65
|
append_to_activity(context, result)
|
63
66
|
return unless blocked?
|
64
67
|
|
65
|
-
cef_logging(result, :successful_attack, xml)
|
68
|
+
cef_logging(result, :successful_attack, value: xml)
|
66
69
|
raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE))
|
67
70
|
end
|
68
71
|
|
@@ -105,7 +108,7 @@ module Contrast
|
|
105
108
|
entity_wrapper = Contrast::Agent::Protect::Rule::Xxe::EntityWrapper.new(ss.matched)
|
106
109
|
next unless entity_wrapper.external_entity?
|
107
110
|
|
108
|
-
xxe_details ||= Contrast::
|
111
|
+
xxe_details ||= Contrast::Agent::Reporting::Details::XxeDetails.new
|
109
112
|
xxe_details.declared_entities << build_match(ss)
|
110
113
|
xxe_details.entities_resolved << build_wrapper(entity_wrapper)
|
111
114
|
end
|
@@ -119,12 +122,12 @@ module Contrast
|
|
119
122
|
def build_sample context, ia_result, _url, **kwargs
|
120
123
|
sample = build_base_sample(context, ia_result)
|
121
124
|
sample.user_input = build_user_input(ia_result)
|
122
|
-
sample.
|
125
|
+
sample.details = kwargs[:details]
|
123
126
|
sample
|
124
127
|
end
|
125
128
|
|
126
129
|
def build_user_input ia_result
|
127
|
-
input = Contrast::
|
130
|
+
input = Contrast::Agent::Reporting::UserInput.new
|
128
131
|
input.key = INPUT_NAME
|
129
132
|
input.input_type = :UNKNOWN
|
130
133
|
input.document_type = :XML
|
@@ -146,14 +149,14 @@ module Contrast
|
|
146
149
|
end
|
147
150
|
|
148
151
|
def build_match string_scanner
|
149
|
-
match = Contrast::
|
152
|
+
match = Contrast::Agent::Reporting::Details::XxeMatch.new
|
150
153
|
match.end_idx = string_scanner.pos.to_i
|
151
154
|
match.start_idx = match.end_idx - string_scanner.matched_size
|
152
155
|
match
|
153
156
|
end
|
154
157
|
|
155
158
|
def build_wrapper entity_wrapper
|
156
|
-
wrapper = Contrast::
|
159
|
+
wrapper = Contrast::Agent::Reporting::Details::XxeWrapper.new
|
157
160
|
wrapper.system_id = Contrast::Utils::StringUtils.protobuf_safe_string(entity_wrapper.system_id)
|
158
161
|
wrapper.public_id = Contrast::Utils::StringUtils.protobuf_safe_string(entity_wrapper.public_id)
|
159
162
|
wrapper
|
@@ -57,6 +57,14 @@ module Contrast
|
|
57
57
|
def tags= tags
|
58
58
|
@_tags = tags if tags.is_a?(String)
|
59
59
|
end
|
60
|
+
|
61
|
+
def details
|
62
|
+
@_details ||= {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def details= protect_details
|
66
|
+
@_details = protect_details if protect_details.is_a?(Contrast::Agent::Reporting::Details::ProtectRuleDetails)
|
67
|
+
end
|
60
68
|
end
|
61
69
|
end
|
62
70
|
end
|