contrast-agent 6.6.4 → 6.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/assess/policy/trigger_method.rb +21 -6
  3. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +2 -0
  4. data/lib/contrast/agent/at_exit_hook.rb +1 -7
  5. data/lib/contrast/agent/inventory/database_config.rb +12 -13
  6. data/lib/contrast/agent/middleware.rb +0 -1
  7. data/lib/contrast/agent/protect/rule/base.rb +16 -20
  8. data/lib/contrast/agent/protect/rule/cmd_injection.rb +5 -4
  9. data/lib/contrast/agent/protect/rule/deserialization.rb +5 -4
  10. data/lib/contrast/agent/protect/rule/path_traversal.rb +9 -7
  11. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +16 -14
  12. data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
  13. data/lib/contrast/agent/protect/rule/xxe.rb +9 -6
  14. data/lib/contrast/agent/reporting/attack_result/attack_result.rb +8 -0
  15. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +85 -36
  16. data/lib/contrast/agent/reporting/attack_result/user_input.rb +11 -0
  17. data/lib/contrast/agent/reporting/details/bot_blocker_details.rb +29 -0
  18. data/lib/contrast/agent/reporting/details/cmd_injection_details.rb +30 -0
  19. data/lib/contrast/agent/reporting/details/details.rb +18 -0
  20. data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +27 -0
  21. data/lib/contrast/agent/reporting/details/ip_denylist_details.rb +27 -0
  22. data/lib/contrast/agent/reporting/details/no_sqli_details.rb +36 -0
  23. data/lib/contrast/agent/reporting/details/path_traversal_details.rb +24 -0
  24. data/lib/contrast/agent/reporting/details/path_traversal_semantic_analysis_details.rb +32 -0
  25. data/lib/contrast/agent/reporting/details/protect_rule_details.rb +17 -0
  26. data/lib/contrast/agent/reporting/details/sqli_details.rb +36 -0
  27. data/lib/contrast/agent/reporting/details/untrusted_deserialization_details.rb +27 -0
  28. data/lib/contrast/agent/reporting/details/virtual_patch_details.rb +24 -0
  29. data/lib/contrast/agent/reporting/details/xss_details.rb +33 -0
  30. data/lib/contrast/agent/reporting/details/xss_match.rb +30 -0
  31. data/lib/contrast/agent/reporting/details/xxe_details.rb +36 -0
  32. data/lib/contrast/agent/reporting/details/xxe_match.rb +25 -0
  33. data/lib/contrast/agent/reporting/details/xxe_wrapper.rb +25 -0
  34. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +1 -1
  35. data/lib/contrast/agent/reporting/masker/masker.rb +78 -65
  36. data/lib/contrast/agent/reporting/masker/masker_utils.rb +1 -30
  37. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +84 -15
  38. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +13 -25
  39. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +17 -22
  40. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +46 -125
  41. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -16
  42. data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -18
  43. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -14
  44. data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +29 -20
  45. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +45 -10
  46. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -7
  47. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +2 -1
  48. data/lib/contrast/agent/request.rb +2 -0
  49. data/lib/contrast/agent/request_context.rb +13 -4
  50. data/lib/contrast/agent/request_context_extend.rb +59 -40
  51. data/lib/contrast/agent/request_handler.rb +7 -9
  52. data/lib/contrast/agent/service_heartbeat.rb +1 -1
  53. data/lib/contrast/agent/version.rb +1 -1
  54. data/lib/contrast/components/app_context.rb +6 -6
  55. data/lib/contrast/config/assess_configuration.rb +1 -1
  56. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -6
  57. data/lib/contrast/utils/assess/event_limit_utils.rb +26 -7
  58. data/lib/contrast/utils/log_utils.rb +15 -9
  59. metadata +19 -2
@@ -30,21 +30,18 @@ module Contrast
30
30
 
31
31
  # Mask sensitive data according to the contrast sensitive data rules.
32
32
  #
33
- # @param [Contrast::Api::Dtm::Activity]
33
+ # @param [Contrast::Agent::Reporting::ApplicationActivity]
34
34
  def mask activity
35
- return unless Contrast::Agent::Reporter.enabled?
36
35
  return unless activity
37
36
 
38
- logger.debug('Searching for sensitive data',
39
- activity: activity.__id__,
40
- request: activity.http_request&.uuid)
37
+ logger.debug('Masker: masking sensitive data', activity: activity.__id__, request: activity.request&.__id__)
41
38
  mask_body(activity)
42
39
  mask_query_string(activity)
43
40
  mask_request_params(activity)
44
41
  mask_request_cookies(activity)
45
42
  mask_request_headers(activity)
46
43
  rescue StandardError => _e
47
- logger.debug('Could not mask activity!', activity: activity.__id__, request: activity.http_request&.uuid)
44
+ logger.debug('Could not mask activity!', activity: activity.__id__, request: activity.request&.__id__)
48
45
  end
49
46
 
50
47
  private
@@ -64,68 +61,67 @@ module Contrast
64
61
 
65
62
  # Mask request body:
66
63
  #
67
- # @param activity [Contrast::Api::Dtm::Activity]
64
+ # @param activity [Contrast::Agent::Reporting::ApplicationActivity]
68
65
  # @return masked_body [String, nil]
69
66
  def mask_body activity
70
67
  return unless mask_body?
71
68
 
72
- body = activity.http_request.request_body
69
+ body = activity.request.body
73
70
  return if body.nil? || body.empty?
74
71
 
75
- activity.http_request.request_body = BODY_MASK
76
- activity.http_request.request_body_binary = BODY_BINARY_MASK
72
+ activity.request.body = BODY_MASK
73
+ activity.request.body_binary = BODY_BINARY_MASK
77
74
  end
78
75
 
79
76
  # Mask request params.
80
77
  #
81
- # @param activity [Contrast::Api::Dtm::Activity]
78
+ # @param activity [Contrast::Agent::Reporting::ApplicationActivity]
82
79
  # @return masked_body [String, nil]
83
80
  def mask_request_params activity
84
- params = activity.http_request.normalized_request_params
81
+ params = activity.request.parameters
85
82
  return unless params
86
83
 
87
- mask_with_dictionary(activity.results, params)
84
+ mask_with_dictionary(activity.attack_results, params)
88
85
  end
89
86
 
90
87
  def mask_request_headers activity
91
- if activity.http_request.parsed_request_headers
92
- # Used normalized request_headers
93
- mask_with_dictionary(activity.results, activity.http_request.normalized_request_headers)
94
- else
95
- headers = activity.http_request.request_headers
96
- mask_field_hash(headers, activity.results)
97
- end
88
+ headers = activity.request.headers
89
+ return if headers&.empty?
90
+
91
+ # Used normalized request_headers
92
+ mask = mask_with_dictionary(activity.attack_results, headers)
93
+ activity.request.headers = mask if mask
98
94
  end
99
95
 
100
96
  # Mask Cookies.
101
97
  #
102
- # @param activity [Contrast::Api::Dtm::Activity] Activity to mask
98
+ # @param activity [Contrast::Agent::Reporting::ApplicationActivity] Activity to mask
103
99
  # @return masked_values [Hash, nil]
104
100
  def mask_request_cookies activity
105
- cookies = activity.http_request.normalized_cookies
106
- return unless cookies
101
+ cookies = activity.request.cookies
102
+ return if cookies&.empty?
107
103
 
108
- mask_with_dictionary(activity.results, cookies)
104
+ mask_with_dictionary(activity.attack_results, cookies)
109
105
  end
110
106
 
111
107
  # Mask request query string:
112
108
  # exp: password => sensitive to password => contrast-redacted-password
113
109
  #
114
- # @param activity [Contrast::Api::Dtm::Activity]
110
+ # @param activity [Contrast::Agent::Reporting::ApplicationActivity]
115
111
  # @return masked_query [String]
116
112
  def mask_query_string activity
117
- qs = activity.http_request.query_string
113
+ qs = activity.request.query_string
118
114
  return if qs.nil? || qs.empty?
119
115
 
120
- mask_field_hash(qs, activity.results) unless qs.cs__is_a?(String)
121
- mask_raw_query(qs, activity.results)
116
+ mask = mask_raw_query(qs, activity.attack_results)
117
+ activity.request.query_string = mask if mask
122
118
  end
123
119
 
124
120
  # Mask if the value in the passed hash are matched against dictionary
125
121
  # keyword. If the mask_attack_vector flag is set, this will also mask
126
122
  # any attack.
127
123
  #
128
- # @param results [Array<Contrast::Api::Dtm::AttackResults>]
124
+ # @param results [Array<Contrast::Agent::Reporting::ApplicationDefendAttackActivity>]
129
125
  # results to match against.
130
126
  # @param hash [Hash] Normalized hash representing the key/val pair from
131
127
  # the activity's http request parameters.
@@ -134,81 +130,98 @@ module Contrast
134
130
  return if hash.nil? || hash.empty?
135
131
 
136
132
  hash.each do |key, val|
137
- match = dictionary_matcher(key)
138
- next unless match
139
-
140
- # The normalized values are paired.
141
- # key => Contrast::Api::Dtm::Pair (key, val<Values>).
142
- # try one level down
143
- if val.cs__respond_to?(:values)
144
- mask_values(key, val, results)
133
+ next unless dictionary_match(key)
134
+
135
+ if val.cs__is_a?(Array)
136
+ mask_values(key, val, hash, results)
145
137
  else
146
138
  # Just assign keys.
147
139
  mask_hash(key, val, hash, results)
148
140
  end
149
141
  end
150
- hash
151
142
  end
152
143
 
153
- # Mask the values of DTM pair with attack vector condition check.
154
- # if the attack vector flag is set then mask the attack value.
144
+ # Mask the values of key value pair with array of string as input.
145
+ # If the mask_attack_vector? flag is set then the attack vector won't be
146
+ # masked.
155
147
  #
156
- # @param key [String] current iterable key from Protobuf::Field::FieldHash
157
- # pointing to Contrast::Api::Dtm::Pair<key, val>(holding the value to mask)
158
- # @param results [Array<Contrast::Api::Dtm::AttackResults>]
148
+ # @param key [String]
149
+ # @param hash [Hash] Normalized hash representing the key/val pair.
150
+ # @param results [Array<Contrast::Agent::Reporting::ApplicationDefendAttackActivity>]
159
151
  # results to match against.
160
- # @param val [Contrast::Api::Dtm::Pair<Value>]
161
- def mask_values key, val, results
162
- val.values.each.with_index do |v, idx|
152
+ # @param val [String, Array<String>]
153
+ def mask_values key, val, hash, results
154
+ val.each.with_index do |v, idx|
163
155
  # Mask the attack vector only if the flag is set.
164
- val.values[idx] = MASK + key.downcase if attack_vector?(results, v) && mask_attack_vector?
156
+ hash[key][idx] = MASK + key.downcase if attack_vector?(results, v) && mask_attack_vector?
165
157
  # It is not attack vector and we mask it as normal.
166
- val.values[idx] = MASK + key.downcase unless attack_vector?(results, v)
158
+ hash[key][idx] = MASK + key.downcase unless attack_vector?(results, v)
167
159
  end
168
- val
160
+ hash
169
161
  end
170
162
 
171
- # Handles the masking of Field hash with string values.
172
- # this case is used when called from #mask_field_hash
173
- # and #mask_raw_query helper methods. Since they dont
174
- # return values containing sub-values (key, val<Values>).
163
+ # Handles the masking of hash
175
164
  #
176
165
  # @param key [String] current iterable key from Protobuf::Field::FieldHash
177
166
  # @param val [String] normalized value to be matched against the results
178
167
  # and masked.
179
168
  # @param hash [Hash] Normalized hash representing the key/val pair.
180
- # @param results [Array<Contrast::Api::Dtm::AttackResults>]
169
+ # @param results [Array<Contrast::Agent::Reporting::ApplicationDefendAttackActivity>]
181
170
  # results to match against.
171
+ # @return [Hash]
182
172
  def mask_hash key, val, hash, results
173
+ # Mask the attack vector only if the flag is set.
183
174
  hash[key] = MASK + key.downcase if attack_vector?(results, val) && mask_attack_vector?
175
+ # It is not attack vector we mask it.
184
176
  hash[key] = MASK + key.downcase unless attack_vector?(results, val)
177
+ hash
185
178
  end
186
179
 
187
180
  # Match to see if values matches input from AttackResults array.
188
181
  # If match is found and the attack result's response is any of
189
182
  # [BAP(Block At Perimeter), BLOCKED, PROBED] the return is true.
190
183
  #
191
- # @param results [Array<Contrast::Api::Dtm::AttackResults>]
184
+ # @param results [Array<Contrast::Agent::Reporting::ApplicationDefendAttackActivity>]
192
185
  # results to match against.
193
186
  # @param value [String] Input to match.
194
- # @return true | false
187
+ # @return [Boolean]
195
188
  def attack_vector? results, value
196
189
  return false unless value && results
197
190
 
198
- results.each do |result|
199
- # Check samples Contrast::Api::Dtm::RaspRuleSample
200
- # is the value in sample and the response is valid?
201
- result.samples.any? do |sample|
202
- # Check user input Contrast::Api::Dtm::UserInput.
203
- match = sample.user_input.value == value.to_s &&
204
- result.response&.name != Contrast::Agent::Reporting::ResponseType::NO_ACTION
191
+ results.each do |attacker|
192
+ attacker.each do |activity|
193
+ blocked = iterate_attack_samples(activity.blocked, value)
194
+ return blocked if blocked
195
+
196
+ exploited = iterate_attack_samples(activity.exploited, value)
197
+ return exploited if exploited
205
198
 
206
- return match if match
199
+ ineffective = iterate_attack_samples(activity.ineffective, value)
200
+ return ineffective if ineffective
201
+
202
+ suspicious = iterate_attack_samples(activity.suspicious, value)
203
+ return suspicious if suspicious
207
204
  end
208
205
  end
209
206
  false
210
207
  end
211
208
 
209
+ # Go through activity samples and search for a matching input.
210
+ #
211
+ # @param activity [Contrast::Agent::Reporting::ApplicationDefendAttackActivity]
212
+ # @param value [String] Input to match.
213
+ # @return [Boolean]
214
+ def iterate_attack_samples activity, value
215
+ return false unless activity
216
+
217
+ activity.samples.any? do |sample|
218
+ match = sample.user_input.value == value.to_s
219
+
220
+ return true if match
221
+ end
222
+ false
223
+ end
224
+
212
225
  # Consult with our current settings state.
213
226
  #
214
227
  # @return true | false
@@ -227,7 +240,7 @@ module Contrast
227
240
  #
228
241
  # @param value [String] Value to check.
229
242
  # @return match [String, nil] from the Dictionary, or nil.
230
- def dictionary_matcher value
243
+ def dictionary_match value
231
244
  return unless @_dictionary
232
245
 
233
246
  @_dictionary.each do |rule|
@@ -9,35 +9,6 @@ module Contrast
9
9
  # helper methods used for masking
10
10
  module MaskerUtils
11
11
  include Contrast::Utils::ObjectShare
12
- # Helper to deal with Protobuf FieldHash.
13
- #
14
- # @param field_hash [Protobuf::Field::FieldHash] hash to be masked
15
- # @param results [Array<Contrast::Api::Dtm::AttackResults>]
16
- # results to match against.
17
- # @return [Hash]
18
- def mask_field_hash field_hash, results
19
- return {} unless field_hash&.any?
20
-
21
- hash = {}
22
- # Because this is the start of a built string, we have to be sure that it is not frozen.
23
- masked = +''
24
- field_hash.each do |entry|
25
- # Protobuf::Field::FieldHash produces array, with the key as first param and value as second.
26
- new_value = entry[1].delete(SEMICOLON).split(SPACE)
27
- new_value.each do |value|
28
- arr = value.split(EQUALS)
29
- # Add to new hash.
30
- hash[arr[0]] = arr[1]
31
- end
32
- # Mask the newly created hash.
33
- mask_with_dictionary(results, hash)
34
-
35
- # Restore to original form.
36
- hash.each { |k, v| masked += "#{ k }=#{ v }; " }
37
- masked.rstrip!
38
- field_hash[entry[0]] = masked
39
- end
40
- end
41
12
 
42
13
  # Mask raw query as it comes from the env.
43
14
  # exp:
@@ -45,7 +16,7 @@ module Contrast
45
16
  # 'ssn=contrast-redacted-ssn&id=contrast-redacted-id'
46
17
  #
47
18
  # @param query [String]
48
- # @param results [Array<Contrast::Api::Dtm::AttackResults>]
19
+ # @param results [Array<Contrast::Agent::Reporting::ApplicationDefendAttackActivitys>]
49
20
  # results to match against.
50
21
  def mask_raw_query query, results
51
22
  masked = EMPTY_STRING
@@ -5,6 +5,7 @@ require 'contrast/components/logger'
5
5
  require 'contrast/agent/reporting/reporting_events/application_reporting_event'
6
6
  require 'contrast/agent/reporting/reporting_events/application_defend_activity'
7
7
  require 'contrast/agent/reporting/reporting_events/application_inventory_activity'
8
+ require 'contrast/agent/reporting/attack_result/response_type'
8
9
 
9
10
  module Contrast
10
11
  module Agent
@@ -12,38 +13,106 @@ module Contrast
12
13
  # This is the new ApplicationActivity class which will include all the needed information for the new reporting
13
14
  # system to report
14
15
  class ApplicationActivity < Contrast::Agent::Reporting::ApplicationReportingEvent
15
- class << self
16
- # @param app_activity_dtm [Contrast::Api::Dtm::Activity]
17
- # @return [Contrast::Agent::Reporting::ApplicationActivity]
18
- def convert app_activity_dtm
19
- app_activity = new
20
- app_activity.attach_data(app_activity_dtm)
21
- app_activity
22
- end
23
- end
16
+ include Contrast::Agent::Reporting::ResponseType
17
+
18
+ # @return [Integer]
19
+ attr_accessor :query_count
20
+ # @return [Array]
21
+ attr_accessor :routes
22
+ # @return [Array]
23
+ attr_accessor :dynamic_sources
24
+ # @return [Contrast::Agent::Response]
25
+ attr_accessor :response
24
26
 
25
27
  def initialize
28
+ @routes = []
29
+ @dynamic_sources = {}
30
+ @query_count = 0
26
31
  @event_method = :PUT
27
32
  @event_type = :application_activity
28
33
  @event_endpoint = Contrast::Agent::Reporting::Endpoints.application_activity
29
34
  super
30
35
  end
31
36
 
37
+ # @return [Contrast::Agent::Reporting::FindingRequest] Current context's request
38
+ def request
39
+ @_request ||= FindingRequest.convert(Contrast::Agent::REQUEST_TRACKER.current&.request)
40
+ end
41
+
42
+ # @return [Contrast::Agent::Reporting::ApplicationDefendActivity] main
43
+ # activity for all protect rules.
44
+ def defend
45
+ @_defend ||= Contrast::Agent::Reporting::ApplicationDefendActivity.new
46
+ end
47
+
48
+ # @return [Contrast::Agent::Reporting::ApplicationInventoryActivity] main
49
+ # activity for all inventory activity reporting.
50
+ def inventory
51
+ @_inventory ||= Contrast::Agent::Reporting::ApplicationInventoryActivity.new
52
+ end
53
+
54
+ # @return file_name [String] used for audit
32
55
  def file_name
33
56
  'activity-application'
34
57
  end
35
58
 
36
59
  def to_controlled_hash
37
60
  hsh = { lastUpdate: since_last_update }
38
- hsh[:defend] = @defend&.to_controlled_hash if @defend
39
- hsh[:inventory] = @inventory&.to_controlled_hash if @inventory
61
+ hsh[:defend] = @_defend&.to_controlled_hash if @_defend
62
+ hsh[:inventory] = @_inventory&.to_controlled_hash if @_inventory
40
63
  hsh
41
64
  end
42
65
 
43
- # @param activity_dtm [Contrast::Api::Dtm::ApplicationActivity]
44
- def attach_data activity_dtm
45
- @defend = Contrast::Agent::Reporting::ApplicationDefendActivity.convert(activity_dtm)
46
- @inventory = Contrast::Agent::Reporting::ApplicationInventoryActivity.convert(activity_dtm)
66
+ # Look for attack results and access to samples by
67
+ # searching with rule_id and response_type
68
+ #
69
+ # @param rule_id [String] name of the protect rule
70
+ # @param response_type[Symbol<Contrast::Agent::Reporting::ResponseType]
71
+ # filter by response type
72
+ # @return [Array<Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity>, nil]
73
+ # return any matches.
74
+ def attack_results_for rule_id, response_type = nil
75
+ results = []
76
+ defend.attackers.each do |attacker|
77
+ results << case response_type
78
+ when BLOCKED, BLOCK_AT_PERIMETER
79
+ attacker.protection_rules[rule_id].blocked
80
+ when EXPLOITED
81
+ attacker.protection_rules[rule_id].exploited
82
+ when PROBED
83
+ attacker.protection_rules[rule_id].ineffective
84
+ when SUSPICIOUS
85
+ attacker.protection_rules[rule_id].suspicious
86
+ else
87
+ attacker.protection_rules[rule_id]
88
+ end
89
+ end
90
+ results
91
+ end
92
+
93
+ # By reference. List of all results only by values, no rule_ids.
94
+ #
95
+ # @return [Array<[Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]>]
96
+ def attack_results
97
+ results = []
98
+ defend.attackers.each { |a| results << a.protection_rules.values }
99
+ results
100
+ end
101
+
102
+ # This is primary used for attaching new data and merging existing
103
+ # samples per rule entry in attackers, and to make sure the attack
104
+ # time_map is updated correctly.
105
+ #
106
+ # @param attack_result [Contrast::Agent::Reporting::AttackResult]
107
+ def attach_defend attack_result
108
+ defend.attach_data(attack_result)
109
+ end
110
+
111
+ # This is primary used for attaching new inventory reporting
112
+ #
113
+ # @param architecture [Contrast::Agent::Reporting::AttackResult]
114
+ def attach_inventory architecture
115
+ inventory.attach_data(architecture)
47
116
  end
48
117
  end
49
118
  end
@@ -13,20 +13,9 @@ module Contrast
13
13
  # @return [Array<Contrast::Agent::Reporting::ApplicationDefendAttackerActivity>]
14
14
  attr_reader :attackers
15
15
 
16
- class << self
17
- # @param activity_dtm [Contrast::Api::Dtm::ApplicationActivity]
18
- # @return [Contrast::Agent::Reporting::ApplicationDefendActivity]
19
- def convert activity_dtm
20
- activity = new
21
- activity.attach_data(activity_dtm.results)
22
- activity
23
- end
24
- end
25
-
26
16
  def initialize
27
17
  @attackers = []
28
18
  @event_type = :application_defend_activity
29
- super
30
19
  end
31
20
 
32
21
  def to_controlled_hash
@@ -35,20 +24,19 @@ module Contrast
35
24
  }
36
25
  end
37
26
 
38
- # @param attack_dtms [Array<Contrast::Api::Dtm::AttackResult>]
39
- def attach_data attack_dtms
40
- attack_dtms.each do |attack_result|
41
- attacker_activity = Contrast::Agent::Reporting::ApplicationDefendAttackerActivity.convert(attack_result)
42
- existing_attacker_activity = attackers.find do |existing|
43
- existing.source_forwarded_for == attacker_activity.source_forwarded_for &&
44
- existing.source_ip == attacker_activity.source_ip
45
- end
46
- rule = attack_result.rule_id
47
- if existing_attacker_activity
48
- attach_existing(existing_attacker_activity, attacker_activity, rule)
49
- else
50
- attackers << attacker_activity
51
- end
27
+ # @param attack_result [Contrast::Agent::Reporting::AttackResult]
28
+ def attach_data attack_result
29
+ attacker_activity = Contrast::Agent::Reporting::ApplicationDefendAttackerActivity.new
30
+ attacker_activity.attach_data(attack_result)
31
+ existing_attacker_activity = attackers.find do |existing|
32
+ existing.source_forwarded_for == attacker_activity.source_forwarded_for &&
33
+ existing.source_ip == attacker_activity.source_ip
34
+ end
35
+ rule = attack_result.rule_id
36
+ if existing_attacker_activity
37
+ attach_existing(existing_attacker_activity, attacker_activity, rule)
38
+ else
39
+ attackers << attacker_activity
52
40
  end
53
41
  end
54
42
 
@@ -19,44 +19,39 @@ module Contrast
19
19
  attr_accessor :ineffective
20
20
  # @return [Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity]
21
21
  attr_accessor :suspicious
22
-
23
- class << self
24
- # @param attack_result [Contrast::Api::Dtm::AttackResult]
25
- def convert attack_result
26
- activity = new
27
- activity.attach_data(attack_result)
28
- activity
29
- end
30
- end
22
+ # Helper method to determine before hand the response type and iv needed for access
23
+ #
24
+ # @return [Contrast::Agent::Reporting::ResponseType]
25
+ attr_reader :response_type
31
26
 
32
27
  def initialize
33
28
  @start_time = start_time
34
- super
35
29
  end
36
30
 
37
31
  def to_controlled_hash
38
32
  {
39
33
  startTime: @start_time,
40
- blocked: blocked&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
41
- exploited: exploited&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
42
- ineffective: ineffective&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
43
- suspicious: suspicious&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH
34
+ blocked: @blocked&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
35
+ exploited: @exploited&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
36
+ ineffective: @ineffective&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
37
+ suspicious: @suspicious&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH
44
38
  }
45
39
  end
46
40
 
47
- # @param attack_result [Contrast::Api::Dtm::AttackResult]
41
+ # @param attack_result [Contrast::Agent::Reporting::AttackResult]
48
42
  # @return [Contrast::Agent::Reporting::Defend::AttackSampleActivity]
49
43
  def attach_data attack_result
50
- attack_sample_activity =
51
- Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.convert(attack_result)
52
- case attack_result.response
53
- when ::Contrast::Api::Dtm::AttackResult::ResponseType::BLOCKED
44
+ attack_sample_activity = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
45
+ attack_sample_activity.attach_data(attack_result)
46
+ @response_type = attack_result.response
47
+ case response_type
48
+ when ::Contrast::Agent::Reporting::ResponseType::BLOCKED
54
49
  @blocked = attack_sample_activity
55
- when ::Contrast::Api::Dtm::AttackResult::ResponseType::MONITORED
50
+ when ::Contrast::Agent::Reporting::ResponseType::MONITORED
56
51
  @exploited = attack_sample_activity
57
- when ::Contrast::Api::Dtm::AttackResult::ResponseType::PROBED
52
+ when ::Contrast::Agent::Reporting::ResponseType::PROBED
58
53
  @ineffective = attack_sample_activity
59
- when ::Contrast::Api::Dtm::AttackResult::ResponseType::SUSPICIOUS
54
+ when ::Contrast::Agent::Reporting::ResponseType::SUSPICIOUS
60
55
  @suspicious = attack_sample_activity
61
56
  end
62
57
  end