contrast-agent 7.4.0 → 7.4.1

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/middleware/middleware.rb +1 -1
  3. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +9 -11
  4. data/lib/contrast/agent/protect/input_analyzer/worth_watching_analyzer.rb +55 -20
  5. data/lib/contrast/agent/protect/policy/rule_applicator.rb +1 -4
  6. data/lib/contrast/agent/protect/rule/base.rb +56 -25
  7. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker.rb +12 -4
  8. data/lib/contrast/agent/protect/rule/cmdi/cmd_injection.rb +2 -10
  9. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +2 -4
  10. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +2 -1
  11. data/lib/contrast/agent/protect/rule/deserialization/deserialization.rb +4 -4
  12. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli.rb +5 -2
  13. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal.rb +12 -7
  14. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_semantic_security_bypass.rb +2 -2
  15. data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +2 -3
  16. data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +3 -4
  17. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload.rb +3 -0
  18. data/lib/contrast/agent/protect/rule/utils/builders.rb +3 -4
  19. data/lib/contrast/agent/protect/rule/utils/filters.rb +32 -16
  20. data/lib/contrast/agent/protect/rule/xss/xss.rb +80 -0
  21. data/lib/contrast/agent/protect/rule/xxe/xxe.rb +9 -2
  22. data/lib/contrast/agent/reporting/details/xss_match.rb +17 -0
  23. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +32 -0
  24. data/lib/contrast/agent/reporting/reporting_events/finding.rb +1 -5
  25. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +1 -4
  26. data/lib/contrast/agent/request/request_context_extend.rb +0 -2
  27. data/lib/contrast/agent/version.rb +1 -1
  28. data/lib/contrast/components/assess.rb +4 -0
  29. data/lib/contrast/framework/rails/support.rb +2 -2
  30. data/lib/contrast/logger/cef_log.rb +30 -4
  31. data/lib/contrast/utils/io_util.rb +3 -0
  32. data/lib/contrast/utils/log_utils.rb +21 -10
  33. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1266effb11fb78123e8461ce7295f9b4a67a88b7dda632bc57da5d70cb39f87d
4
- data.tar.gz: e553aa33bd73ab712585b774578c10ff6f19ae925fdea8adb89c3b6ac253e399
3
+ metadata.gz: 2b62e21b5f8d2c3d786aaaac33005487eab07fd07438e9fe20532742f926bcb0
4
+ data.tar.gz: 37ff12d328d1a58bddb75e4f0e9c799b44df881daaa78bea62f565f95ae715ff
5
5
  SHA512:
6
- metadata.gz: 7abce257d4092010babc21cff242307343ea2fc83bdbd040b8cd95e64be9b2e640963a06ae017053989be17e98442bbc528987c2da5b8f7bc8688b776022c783
7
- data.tar.gz: f85de50b08e0eaa5f5d8c6a571fe4d04a03cbbcaab1f9c7f2431dac6b4373b00e04f63be94b5f02d56e222248c5fe256b61fb43ba123d041e1ce585b80e63a5f
6
+ metadata.gz: 46f9f576d3a28befa4681e73a250af98602c100d50ace13a1e3fcaa5a22ca2a0d0695b4b19492677d57183ff56203d2f57a5470b7c6fa0fc9c1b15bbbd49dc16
7
+ data.tar.gz: 427a3dafa3d8108a4aa2130ff02c8c4d52539f7d1c7b265ba78d60dc10f86f142a2b4f1132ae0be02c0c9a41c043ab1459c0a0e3a836dbced2f0db4b550aa970
@@ -191,7 +191,7 @@ module Contrast
191
191
  # Now we can build the ia_results only for postfilter rules.
192
192
  context.protect_postfilter_ia
193
193
  # Process Worth Watching Inputs for v2 rules
194
- Contrast::Agent.worth_watching_analyzer&.add_to_queue(context.agent_input_analysis)
194
+ Contrast::Agent.worth_watching_analyzer&.add_to_queue(context)
195
195
 
196
196
  if Contrast::Agent.framework_manager.streaming?(env)
197
197
  context.reset_activity
@@ -33,10 +33,10 @@ module Contrast
33
33
  DISPOSITION_FILENAME = 'filename'
34
34
  PREFILTER_RULES = %w[bot-blocker unsafe-file-upload reflected-xss].cs__freeze
35
35
  INFILTER_RULES = %w[
36
- sql-injection cmd-injection reflected-xss bot-blocker unsafe-file-upload path-traversal
36
+ sql-injection cmd-injection bot-blocker unsafe-file-upload path-traversal
37
37
  nosql-injection
38
38
  ].cs__freeze
39
- POSTFILTER_RULES = %w[sql-injection cmd-injection reflected-xss path-traversal nosql-injection].cs__freeze
39
+ POSTFILTER_RULES = %w[sql-injection cmd-injection path-traversal nosql-injection].cs__freeze
40
40
  AGENTLIB_TIMEOUT = 5.cs__freeze
41
41
  TIMEOUT_ERROR_MESSAGE = '[AgentLib] Timed out when processing InputAnalysisResult'
42
42
  STANDARD_ERROR_MESSAGE = '[InputAnalyzer] Exception raise while doing input analysis:'
@@ -100,10 +100,15 @@ module Contrast
100
100
  # @param input_analysis [Contrast::Agent::Reporting::InputAnalysis] from analyze method.
101
101
  # @param interval [Integer] The timeout determined for the AgentLib analysis to be performed.
102
102
  def input_classification_for rule_id, input_analysis, interval: AGENTLIB_TIMEOUT
103
- return unless input_analysis&.inputs
103
+ return if input_analysis.analysed_rules.include?(rule_id)
104
+ return if input_analysis.no_inputs?
104
105
  return unless (protect_rule = Contrast::PROTECT.rule(rule_id)) && protect_rule.enabled?
105
106
 
106
107
  input_analysis.inputs.each do |input_type, value|
108
+ # TODO: RUBY-2110 Update the HEADER handling if possible.
109
+ # Analyze only Header values:
110
+ # This may break bot blocker rule:
111
+ # value = value.values if input_type == HEADER
107
112
  next if value.nil? || value.empty?
108
113
 
109
114
  Timeout.timeout(interval) do
@@ -128,14 +133,12 @@ module Contrast
128
133
  # for each protect rule.
129
134
  # @param prefilter [Boolean] flag to set input analysis for prefilter rules only
130
135
  # @param postfilter [Boolean] flag to set input analysis for postfilter rules.
131
- # @param infilter [Boolean]
132
136
  # @param interval [Integer] The timeout determined for the AgentLib analysis to be performed
133
137
  # @return input_analysis [Contrast::Agent::Reporting::InputAnalysis, nil]
134
138
  # @raise [Timeout::Error] If timeout is met.
135
139
  def input_classification(input_analysis,
136
140
  prefilter: false,
137
141
  postfilter: false,
138
- infilter: false,
139
142
  interval: AGENTLIB_TIMEOUT)
140
143
  return unless input_analysis
141
144
 
@@ -147,12 +150,7 @@ module Contrast
147
150
  INFILTER_RULES
148
151
  end
149
152
 
150
- rules.each do |rule_id|
151
- # Check to see if rules is already triggered only for infilter:
152
- next if input_analysis.triggered_rules.include?(rule_id) && infilter
153
-
154
- input_classification_for(rule_id, input_analysis, interval: interval)
155
- end
153
+ rules.each { |rule_id| input_classification_for(rule_id, input_analysis, interval: interval) }
156
154
  input_analysis
157
155
  end
158
156
 
@@ -42,16 +42,15 @@ module Contrast
42
42
  next if queue.empty?
43
43
 
44
44
  report = false
45
- # build attack_results for all infilter active protect rules.
46
- stored_ia = queue.pop
47
- results = build_results(stored_ia)
48
- activity = Contrast::Agent::Reporting::ApplicationActivity.new(ia_request: stored_ia.request)
45
+ stored_context, stored_ia, results, activity = extract_from_context
46
+
49
47
  results.each do |result|
50
- next unless (attack_result = eval_input(result))
48
+ next unless (attack_result = eval_input(stored_context, result, stored_ia))
51
49
 
52
50
  activity.attach_defend(attack_result)
53
51
  report = true
54
52
  end
53
+
55
54
  report_activity(activity) if report
56
55
  # Handle reporting of IA Cache statistics:
57
56
  enqueue_cache_event(stored_ia.request)
@@ -62,9 +61,22 @@ module Contrast
62
61
  end
63
62
  end
64
63
 
65
- # @param input_analysis [Contrast::Agent::Reporting::InputAnalysis]
66
- def add_to_queue input_analysis
67
- return unless input_analysis
64
+ # build attack_results for all infilter active protect rules.
65
+ # Stored Context will update the logger context and build attack results for protect rules.
66
+ # Note: call only in thread loop as it extracts from the queue.
67
+ #
68
+ # @return [Array<stored_context, stored_ia, results, activity>]
69
+ def extract_from_context
70
+ stored_context = queue.pop
71
+ stored_ia = stored_context.agent_input_analysis
72
+ results = build_results(stored_ia)
73
+ activity = Contrast::Agent::Reporting::ApplicationActivity.new(ia_request: stored_ia.request)
74
+ [stored_context, stored_ia, results, activity]
75
+ end
76
+
77
+ # @param context [Contrast::Agent::RequestContext]
78
+ def add_to_queue context
79
+ return unless context
68
80
 
69
81
  if queue.size >= QUEUE_SIZE
70
82
  logger.debug('[WorthWatchingAnalyzer] queue at max size, skip input_result')
@@ -74,7 +86,7 @@ module Contrast
74
86
  # we need to save the ia which contains the request and saved extracted user inputs to
75
87
  # be evaluated on the thread rather than building results here. This way we allow the
76
88
  # request to continue and will build the attack results later.
77
- queue << input_analysis.dup
89
+ queue << context.dup
78
90
  end
79
91
 
80
92
  private
@@ -91,6 +103,9 @@ module Contrast
91
103
  Contrast::Agent::Protect::InputAnalyzer.lru_cache.clear_statistics
92
104
  end
93
105
 
106
+ # Enqueue for Telemetry reporting all base64 related events.
107
+ #
108
+ # @param request [Contrast::Agent::Request] stored request.
94
109
  def enqueue_encoding_event request
95
110
  return unless Contrast::Agent::Telemetry::Base.enabled?
96
111
  return unless Contrast::PROTECT.normalize_base64?
@@ -102,16 +117,16 @@ module Contrast
102
117
 
103
118
  # This method will build the attack results from the saved ia.
104
119
  #
105
- # @param input_analysis [Contrast::Agent::Reporting::InputAnalysis]
120
+ # @param stored_ia [Contrast::Agent::Reporting::InputAnalysis]
106
121
  # @return attack_results [array<Contrast::Agent::Reporting::InputAnalysisResult>] all the results
107
122
  # from the input analysis.
108
- def build_results input_analysis
123
+ def build_results stored_ia
109
124
  # Construct the input analysis for the all the infilter rules that were not triggered.
110
125
  # There is a set timeout for each rule to be analyzed in. The infilter flag will make
111
126
  # sure that if a rule is already triggered during the infilter phase it will not be analyzed
112
127
  # now, making sure we don't report same rule twice.
113
- Contrast::Agent::Protect::InputAnalyzer.input_classification(input_analysis, infilter: true)
114
- results = input_analysis.results.reject do |val|
128
+ Contrast::Agent::Protect::InputAnalyzer.input_classification(stored_ia)
129
+ results = stored_ia.results.reject do |val|
115
130
  val.score_level == Contrast::Agent::Reporting::InputAnalysisResult::SCORE_LEVEL::IGNORE
116
131
  end
117
132
  return results if results
@@ -119,39 +134,59 @@ module Contrast
119
134
  []
120
135
  end
121
136
 
137
+ # Evaluates the stored ia results and builds attack results if any.
138
+ #
139
+ # @param stored_context [Contrast::Agent::RequestContext]
122
140
  # @param ia_result Contrast::Agent::Reporting::InputAnalysisResult the WorthWatching InputAnalysisResult
141
+ # @param stored_ia [Contrast::Agent::Reporting::InputAnalysis] the stored InputAnalysis
123
142
  # @return [Contrast::Agent::Reporting::AttackResult, nil] InputAnalysisResult updated Result or nil
124
- def eval_input ia_result
125
- return build_attack_result(ia_result) unless ia_result.value.to_s.bytesize >= INPUT_BYTESIZE_THRESHOLD
143
+ def eval_input stored_context, ia_result, stored_ia
144
+ return skip_log if ia_result.value.to_s.bytesize >= INPUT_BYTESIZE_THRESHOLD
126
145
 
127
- logger.debug("[WorthWatchingAnalyzer] Skipping analysis: Input size is larger than
128
- #{ INPUT_BYTESIZE_THRESHOLD / 1024 }KB")
129
- nil
146
+ build_attack_result(stored_context, ia_result, stored_ia)
130
147
  end
131
148
 
149
+ # Creates new Attack Event per rule that will be triggered or probed.
150
+ #
151
+ # @param stored_context [Contrast::Agent::RequestContext]
132
152
  # @param ia_result Contrast::Agent::Reporting::InputAnalysisResult the updated InputAnalysisResult
133
153
  # with a score of :DEFINITEATTACK
154
+ # @param stored_ia [Contrast::Agent::Reporting::InputAnalysis] the stored InputAnalysis
134
155
  # @return [Contrast::Agent::Reporting::AttackResult] the attack result from
135
156
  # this input
136
- def build_attack_result ia_result
137
- Contrast::PROTECT.rule(ia_result.rule_id).build_attack_without_match(nil, ia_result, nil)
157
+ def build_attack_result stored_context, ia_result, stored_ia
158
+ return if stored_ia.triggered_rules.include?(ia_result.rule_id)
159
+
160
+ Contrast::PROTECT.rule(ia_result.rule_id).build_attack_without_match(stored_context, ia_result, nil)
138
161
  end
139
162
 
163
+ # @return [Queue]
140
164
  def queue
141
165
  @_queue ||= Queue.new
142
166
  end
143
167
 
168
+ # Reports all gather activities to batch.
169
+ #
170
+ # @param activity [Contrast::Agent::Reporting::ApplicationActivity]
144
171
  def report_activity activity
145
172
  logger.debug('[WorthWatchingAnalyzer] preparing to send activity batch')
146
173
  add_activity_to_batch(activity)
147
174
  report_batch
148
175
  end
149
176
 
177
+ # Deletes Queue and closes it.
150
178
  def delete_queue!
151
179
  @_queue&.clear
152
180
  @_queue&.close
153
181
  @_queue = nil
154
182
  end
183
+
184
+ # Logs a message that the input was skipped because it was too large.
185
+ def skip_log
186
+ logger.debug("[WorthWatchingAnalyzer] Skipping analysis: Input size is larger than
187
+ #{ INPUT_BYTESIZE_THRESHOLD / 1024 }KB")
188
+ nil
189
+ end
155
190
  end
156
191
  end
157
192
  end
@@ -55,10 +55,7 @@ module Contrast
55
55
  return unless (ia = context.agent_input_analysis)
56
56
 
57
57
  Contrast::Agent::Protect::InputAnalyzer.input_classification_for(rule_id, ia)
58
- # We add the triggered rule to the list. After request analysis will skip this rule
59
- # as already it's input applicable types has been analysed.
60
- ia.triggered_rules << rule_name
61
- ia
58
+ context.agent_input_analysis.record_analysed_rule(rule_id)
62
59
  end
63
60
 
64
61
  protected
@@ -141,20 +141,30 @@ module Contrast
141
141
  end
142
142
 
143
143
  # With this we log to CEF
144
- #
145
- # @param result [Contrast::Agent::Reporting::AttackResult]
144
+ # @param result [Contrast::Agent::Reporting::InputAnalysisResult]
146
145
  # @param attack [Symbol] the type of message we want to send
147
146
  # @param value [String] the input value we want to log
148
- def cef_logging result, attack = :ineffective_attack, value: nil
147
+ # @param input_type [String] the input type we want to log
148
+ # @param context [Contrast::Agent::RequestContext]
149
+ def cef_logging result, attack = :ineffective_attack, value: nil, input_type: nil, context: nil
149
150
  sample = result.samples[0]
150
151
  outcome = result.response.to_s
151
- input_type = sample.user_input.input_type.to_s
152
- input_value = sample.user_input.value || value
153
- cef_logger.send(attack, result.rule_id, outcome, input_type, input_value)
152
+ input_type = sample&.user_input&.input_type&.to_s || input_type
153
+ input_value = sample&.user_input&.value || value
154
+ cef_logger.send(attack, result.rule_id, outcome, input_type, input_value, context)
154
155
  end
155
156
 
156
157
  protected
157
158
 
159
+ # Records the rule being triggered at sink.
160
+ #
161
+ # @param context [Contrast::Agent::RequestContext]
162
+ def record_triggered context
163
+ return unless context
164
+
165
+ context.agent_input_analysis.record_rule_triggered(rule_name)
166
+ end
167
+
158
168
  # Assign the mode from active settings.
159
169
  #
160
170
  # @return mode [Symbol]
@@ -191,22 +201,20 @@ module Contrast
191
201
  # @param potential_attack_string [String, nil]
192
202
  # @param ia_results [Array<Contrast::Agent::Reporting::InputAnalysis>]
193
203
  # @param **kwargs
194
- # @return [Contrast::Agent::Reporting, nil]
204
+ # @return [Contrast::Agent::Reporting::AttackResult, nil]
195
205
  def find_attacker_with_results context, potential_attack_string, ia_results, **kwargs
196
206
  logger.trace('Checking vectors for attacks', rule: rule_name, input: potential_attack_string)
207
+ return unless ia_results&.any?
208
+ return build_attack_without_match(context, ia_results[0], nil, **kwargs) unless potential_attack_string
197
209
 
198
- result = nil
199
210
  ia_results.each do |ia_result|
200
- if potential_attack_string
201
- idx = potential_attack_string.index(ia_result.value)
202
- next unless idx
203
-
204
- result = build_attack_with_match(context, ia_result, result, potential_attack_string, **kwargs)
205
- else
206
- result = build_attack_without_match(context, ia_result, result, **kwargs)
207
- end
211
+ idx = potential_attack_string.index(ia_result.value)
212
+ next unless idx
213
+
214
+ result = build_attack_with_match(context, ia_result, result || nil, potential_attack_string, **kwargs)
215
+ return result if result
208
216
  end
209
- result
217
+ nil
210
218
  end
211
219
 
212
220
  # By default, rules do not have to find attackers as they do not have
@@ -231,15 +239,17 @@ module Contrast
231
239
  #
232
240
  # @param context [Contrast::Agent::RequestContext] the context for
233
241
  # the current request
234
- # @param ia_result [Contrast::Agent::Reporting::InputAnalysis]
242
+ # @param ia_result [Contrast::Agent::Reporting::Settings::InputAnalysisResult]
235
243
  # @param result [Contrast::Agent::Reporting::AttackResult]
236
244
  # @param attack_string [String] Potential attack vector
237
245
  # @return [Contrast::Agent::Reporting::AttackResult]
238
246
  def update_successful_attack_response context, ia_result, result, attack_string = nil
247
+ cef_outcome = :successful_attack
239
248
  case mode
240
249
  when :MONITOR
241
250
  # We are checking the result as the ia_result would not contain the sub-rules.
242
251
  result.response = if SUSPICIOUS_REPORTING_RULES.include?(result&.rule_id)
252
+ cef_outcome = :suspicious_attack
243
253
  Contrast::Agent::Reporting::ResponseType::SUSPICIOUS
244
254
  else
245
255
  Contrast::Agent::Reporting::ResponseType::MONITORED
@@ -250,7 +260,11 @@ module Contrast
250
260
 
251
261
  ia_result.attack_count = ia_result.attack_count + 1 if ia_result
252
262
  log_rule_matched(context, ia_result, result.response, attack_string)
253
-
263
+ cef_logging(result,
264
+ cef_outcome,
265
+ value: ia_result&.value || attack_string,
266
+ input_type: ia_result&.input_type,
267
+ context: context)
254
268
  result
255
269
  end
256
270
 
@@ -265,17 +279,32 @@ module Contrast
265
279
  # multiple inputs being found to violate the protection criteria
266
280
  # @return [Contrast::Agent::Reporting::AttackResult]
267
281
  def update_perimeter_attack_response context, ia_result, result
268
- if mode == :BLOCK_AT_PERIMETER
282
+ cef_outcome = :successful_attack
283
+ case mode
284
+ when :BLOCK_AT_PERIMETER
269
285
  result.response = if blocked_rule?(ia_result)
270
286
  Contrast::Agent::Reporting::ResponseType::BLOCKED
271
287
  else
272
288
  Contrast::Agent::Reporting::ResponseType::BLOCKED_AT_PERIMETER
273
289
  end
274
290
  log_rule_matched(context, ia_result, result.response)
275
- elsif ia_result.nil? || ia_result.attack_count.zero?
291
+ when :BLOCK && rule_name == Contrast::Agent::Protect::Rule::Xss::NAME
292
+ # Handle cases like reflected-xss:
293
+ result.response = Contrast::Agent::Reporting::ResponseType::BLOCKED
294
+ log_rule_matched(context, ia_result, result.response)
295
+ else
296
+ # Handles all other cases including Reflected-xss in MONITOR mode.
297
+ return unless ia_result.nil? || ia_result.attack_count.zero?
298
+
276
299
  result.response = assign_reporter_response_type(ia_result)
300
+ cef_outcome = suspicious_rule?(ia_result) ? :suspicious_attack : :ineffective_attack
277
301
  log_rule_probed(context, ia_result)
278
302
  end
303
+ cef_logging(result,
304
+ cef_outcome,
305
+ value: ia_result&.value,
306
+ input_type: ia_result&.input_type,
307
+ context: context)
279
308
 
280
309
  result
281
310
  end
@@ -334,7 +363,7 @@ module Contrast
334
363
  # the rule id.
335
364
  #
336
365
  # @param context [Contrast::Agent::RequestContext]
337
- # @return [Array<Contrast::Agent::Reporting::InputAnalysis>]
366
+ # @return [Array<Contrast::Agent::Reporting::InputAnalysisResult>]
338
367
  def gather_ia_results context
339
368
  return [] unless context&.agent_input_analysis&.results
340
369
 
@@ -349,7 +378,8 @@ module Contrast
349
378
  def blocked_violation? result
350
379
  return false unless result
351
380
 
352
- result.response == Contrast::Agent::Reporting::ResponseType::BLOCKED
381
+ blocked? && (result.response == Contrast::Agent::Reporting::ResponseType::BLOCKED ||
382
+ result.response == Contrast::Agent::Reporting::ResponseType::BLOCKED_AT_PERIMETER)
353
383
  end
354
384
 
355
385
  private
@@ -359,7 +389,8 @@ module Contrast
359
389
  def blocked_rule? ia_result
360
390
  [
361
391
  Contrast::Agent::Protect::Rule::Sqli::NAME,
362
- Contrast::Agent::Protect::Rule::NoSqli::NAME
392
+ Contrast::Agent::Protect::Rule::NoSqli::NAME,
393
+ Contrast::Agent::Protect::Rule::Xss::NAME
363
394
  ].include?(ia_result&.rule_id)
364
395
  end
365
396
 
@@ -392,7 +423,7 @@ module Contrast
392
423
 
393
424
  # @param context [Contrast::Agent::RequestContext]
394
425
  # @param potential_attack_string [String, nil]
395
- # @return [Contrast::Agent::Reporting, nil]
426
+ # @return [Contrast::Agent::Reporting::AttackResult, nil]
396
427
  def find_postfilter_attacker context, potential_attack_string, **kwargs
397
428
  ia_results = gather_ia_results(context)
398
429
  ia_results.select! do |ia_result|
@@ -19,6 +19,7 @@ module Contrast
19
19
 
20
20
  NAME = 'bot-blocker'
21
21
  APPLICABLE_USER_INPUTS = [HEADER].cs__freeze
22
+ BLOCK_MESSAGE = 'Bot Blocker rule triggered. Unsafe Bot blocked.'
22
23
 
23
24
  def rule_name
24
25
  NAME
@@ -28,6 +29,13 @@ module Contrast
28
29
  APPLICABLE_USER_INPUTS
29
30
  end
30
31
 
32
+ # Return the specific blocking message for this rule.
33
+ #
34
+ # @return [String] the reason for the raised security exception.
35
+ def block_message
36
+ BLOCK_MESSAGE
37
+ end
38
+
31
39
  # Bot blocker input classification
32
40
  #
33
41
  # @return [module<Contrast::Agent::Protect::Rule::BotBlockerInputClassification>]
@@ -49,13 +57,13 @@ module Contrast
49
57
  ia_result.score_level == Contrast::Agent::Reporting::ScoreLevel::DEFINITEATTACK
50
58
 
51
59
  result = build_attack_without_match(context, ia_result, nil)
52
- append_to_activity(context, result) if result
53
- cef_logging(result, :successful_attack) if result
54
- return unless blocked?
60
+ return unless result
55
61
 
62
+ append_to_activity(context, result)
63
+ record_triggered(context)
56
64
  # Raise BotBlocker error
57
65
  exception_message = "#{ rule_name } rule triggered. Unsafe Bot blocked."
58
- raise(Contrast::SecurityException.new(self, exception_message))
66
+ raise(Contrast::SecurityException.new(self, exception_message)) if blocked_violation?(result)
59
67
  end
60
68
 
61
69
  # @param context [Contrast::Agent::RequestContext]
@@ -21,11 +21,6 @@ module Contrast
21
21
  include Contrast::Components::Logger::InstanceMethods
22
22
  include Contrast::Agent::Reporting::InputType
23
23
  NAME = 'cmd-injection'
24
- APPLICABLE_USER_INPUTS = [
25
- BODY, COOKIE_VALUE, HEADER, PARAMETER_NAME,
26
- PARAMETER_VALUE, JSON_VALUE, MULTIPART_VALUE,
27
- MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE
28
- ].cs__freeze
29
24
 
30
25
  def rule_name
31
26
  NAME
@@ -84,12 +79,9 @@ module Contrast
84
79
  return unless result
85
80
 
86
81
  append_to_activity(context, result)
87
- cef_logging(result, :successful_attack)
88
-
89
- return unless blocked?
90
-
82
+ record_triggered(context)
91
83
  # Raise cmdi error
92
- raise_error(classname, method)
84
+ raise_error(classname, method) if blocked_violation?(result)
93
85
  end
94
86
  end
95
87
  end
@@ -43,10 +43,8 @@ module Contrast
43
43
  **{ classname: classname, method: method }))
44
44
 
45
45
  append_to_activity(context, result)
46
- cef_logging(result, :successful_attack)
47
- return unless blocked?
48
-
49
- raise_error(classname, method)
46
+ record_triggered(context)
47
+ raise_error(classname, method) if blocked_violation?(result)
50
48
  end
51
49
 
52
50
  private
@@ -20,7 +20,7 @@ module Contrast
20
20
  APPLICABLE_USER_INPUTS = [
21
21
  BODY, COOKIE_VALUE, HEADER, PARAMETER_NAME,
22
22
  PARAMETER_VALUE, JSON_VALUE, MULTIPART_VALUE,
23
- MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE
23
+ MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE, UNKNOWN
24
24
  ].cs__freeze
25
25
 
26
26
  # CMDI input classification
@@ -46,6 +46,7 @@ module Contrast
46
46
  return unless (result = build_violation(context, command))
47
47
 
48
48
  append_to_activity(context, result)
49
+ record_triggered(context)
49
50
  raise_error(classname, method) if blocked_violation?(result)
50
51
  end
51
52
 
@@ -50,6 +50,7 @@ module Contrast
50
50
  end
51
51
 
52
52
  # Return the specific blocking message for this rule.
53
+ #
53
54
  # @return [String] the reason for the raised security exception.
54
55
  def block_message
55
56
  BLOCK_MESSAGE
@@ -84,10 +85,9 @@ module Contrast
84
85
  kwargs = { GADGET_TYPE: gadget }
85
86
  result = build_attack_with_match(context, ia_result, nil, serialized_input, **kwargs)
86
87
  append_to_activity(context, result)
88
+ record_triggered(context)
87
89
 
88
- cef_logging(result, :successful_attack)
89
-
90
- raise(Contrast::SecurityException.new(self, block_message)) if blocked?
90
+ raise(Contrast::SecurityException.new(self, block_message)) if blocked_violation?(result)
91
91
  end
92
92
 
93
93
  # Determine if the issued command was called while we're
@@ -106,13 +106,13 @@ module Contrast
106
106
  ia_result = build_evaluation(gadget_command)
107
107
  result = build_attack_with_match(context, ia_result, nil, gadget_command, **kwargs)
108
108
  append_to_activity(context, result)
109
- cef_logging(result, :successful_attack, value: gadget_command)
110
109
  raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
111
110
  end
112
111
 
113
112
  protected
114
113
 
115
114
  # Build the RaspRuleSample for the detected Deserialization attack.
115
+ #
116
116
  # @param context [Contrast::Agent::RequestContext] the request
117
117
  # context in which this attack is occurring.
118
118
  # @param input_analysis_result [Contrast::Agent::Reporting::InputAnalysis]
@@ -32,6 +32,9 @@ module Contrast
32
32
  NAME
33
33
  end
34
34
 
35
+ # Return the specific blocking message for this rule.
36
+ #
37
+ # @return [String] the reason for the raised security exception.
35
38
  def block_message
36
39
  BLOCK_MESSAGE
37
40
  end
@@ -56,9 +59,9 @@ module Contrast
56
59
  return unless result
57
60
 
58
61
  append_to_activity(context, result)
59
-
62
+ record_triggered(context)
60
63
  cef_logging(result, :successful_attack)
61
- raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
64
+ raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked_violation?(result)
62
65
  end
63
66
 
64
67
  def build_attack_with_match context, input_analysis_result, result, candidate_string, **kwargs
@@ -26,6 +26,8 @@ module Contrast
26
26
  JSON_VALUE, MULTIPART_VALUE, MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE, URI
27
27
  ].cs__freeze
28
28
 
29
+ BLOCK_MESSAGE = 'Path Traversal rule triggered. Request blocked.'
30
+
29
31
  def rule_name
30
32
  NAME
31
33
  end
@@ -48,6 +50,13 @@ module Contrast
48
50
  APPLICABLE_USER_INPUTS
49
51
  end
50
52
 
53
+ # Return the specific blocking message for this rule.
54
+ #
55
+ # @return [String] the reason for the raised security exception.
56
+ def block_message
57
+ BLOCK_MESSAGE
58
+ end
59
+
51
60
  # Path Traversal input classification
52
61
  #
53
62
  # @return [module<Contrast::Agent::Protect::Rule::PathTraversalInputClassification>]
@@ -55,19 +64,15 @@ module Contrast
55
64
  @_classification ||= Contrast::Agent::Protect::Rule::PathTraversalInputClassification.cs__freeze
56
65
  end
57
66
 
58
- def infilter context, method, path
67
+ def infilter context, _method, path
59
68
  return unless infilter?(context)
60
69
 
61
70
  result = find_attacker(context, path)
62
71
  return unless result
63
72
 
64
73
  append_to_activity(context, result)
65
- return unless blocked?
66
-
67
- result_rule_name = Contrast::Utils::StringUtils.transform_string(result.rule_id)
68
- cef_logging(result, :successful_attack, value: path)
69
- exception_messasge = "#{ result_rule_name } rule triggered. Call to File.#{ method } blocked."
70
- raise(Contrast::SecurityException.new(self, exception_messasge))
74
+ record_triggered(context)
75
+ raise(Contrast::SecurityException.new(self, block_message)) if blocked_violation?(result)
71
76
  end
72
77
 
73
78
  protected
@@ -53,10 +53,10 @@ module Contrast
53
53
  return unless result
54
54
 
55
55
  append_to_activity(context, result)
56
- return unless blocked?
56
+ record_triggered(context)
57
+ return unless blocked_violation?(result)
57
58
 
58
59
  result_rule_name = Contrast::Utils::StringUtils.transform_string(result.rule_id)
59
- cef_logging(result, :successful_attack, value: path)
60
60
  exception_messasge = "#{ result_rule_name } rule triggered. Call to File.#{ method } blocked."
61
61
  raise(Contrast::SecurityException.new(self, exception_messasge))
62
62
  end
@@ -26,9 +26,8 @@ module Contrast
26
26
  return unless result
27
27
 
28
28
  append_to_activity(context, result)
29
-
30
- cef_logging(result, :successful_attack)
31
- raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
29
+ record_triggered(context)
30
+ raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked_violation?(result)
32
31
  end
33
32
  end
34
33
  end