contrast-agent 6.6.4 → 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.
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
@@ -6,6 +6,8 @@ require 'contrast/agent/protect/rule/deserialization'
6
6
  require 'contrast/agent/protect/rule/http_method_tampering'
7
7
  require 'contrast/agent/protect/rule/no_sqli'
8
8
  require 'contrast/api/dtm.pb'
9
+ require 'contrast/agent/reporting/attack_result/user_input'
10
+ require 'contrast/agent/reporting/attack_result/response_type'
9
11
  require 'contrast/components/logger'
10
12
 
11
13
  module Contrast
@@ -16,12 +18,20 @@ module Contrast
16
18
  class ApplicationDefendAttackSample
17
19
  include Contrast::Agent::Reporting::InputType
18
20
 
19
- # @return [Hash] => start: ms, elapsed: ms
20
- attr_reader :time_stamp
21
+ # @return [Contrast::Agent::Reporting::UserInput]
22
+ attr_accessor :user_input
23
+ # @return [Array<Contrast::Agent::Reporting::ApplicationDefendAttackSampleStack>]
24
+ attr_reader :stack
25
+ # Details are per rule specific and should be set when the sample is build
26
+ #
27
+ # @return [Contrast::Agent::Reporting::Details::ProtectRuleDetails, {}]
28
+ attr_accessor :details
29
+ # @return [Integer] time in ms
30
+ attr_accessor :time_stamp
21
31
 
22
32
  class << self
23
- # @param attack_result [Contrast::Api::Dtm::AttackResult]
24
- # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
33
+ # @param attack_result [Contrast::Agent::Reporting::AttackResult]
34
+ # @param attack_sample [Contrast::Agent::Reporting::ApplicationDefendAttackSample]
25
35
  def convert attack_result, attack_sample
26
36
  activity = new
27
37
  activity.attach_data(attack_result, attack_sample)
@@ -30,151 +40,62 @@ module Contrast
30
40
  end
31
41
 
32
42
  def initialize
43
+ @time_stamp = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0 # in ms
33
44
  @blocked = false
34
45
  @event_type = :application_defend_attack_sample
46
+ @user_input = Contrast::Agent::Reporting::UserInput.new
47
+ @request = Contrast::Agent::REQUEST_TRACKER.current&.activity&.request
48
+ @stack = Contrast::Utils::StackTraceUtils.build_protect_report_stack_array
49
+ @details = {}
35
50
  end
36
51
 
37
52
  def to_controlled_hash
38
53
  {
39
54
  blocked: @blocked,
40
- input: @input,
41
- details: @details,
55
+ input: build_input(@user_input),
56
+ details: @details&.to_controlled_hash,
42
57
  request: @request&.to_controlled_hash,
43
58
  stack: @stack&.map(&:to_controlled_hash),
44
- timestamp: time_stamp
59
+ timestamp: build_time_stamp
45
60
  }
46
61
  end
47
62
 
48
- # @param attack_result [Contrast::Api::Dtm::AttackResult]
49
- # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
50
- def attach_data attack_result, attack_sample
51
- @blocked = attack_result.response == ::Contrast::Api::Dtm::AttackResult::ResponseType::BLOCKED
52
- @input = build_input(attack_sample)
53
- @details = build_details(attack_result.rule_id, attack_sample)
54
- @time_stamp = build_time_stamp(attack_sample.timestamp_ms)
55
- @request = FindingRequest.convert(Contrast::Agent::REQUEST_TRACKER.current&.request)
56
- @stack = Contrast::Utils::StackTraceUtils.build_protect_report_stack_array
57
- end
58
-
59
- # @param start [Integer]
60
- # @return [Hash]
61
- def build_time_stamp start
62
- {
63
- start: start,
64
- elapsed: Contrast::Utils::Timer.now_ms - start
65
- }
63
+ # @param response_type [Contrast::Api::Dtm::AttackResult::ResponseType]
64
+ # @return [Boolean] check if response type is blocked
65
+ def blocked? response_type
66
+ @blocked = response_type == Contrast::Agent::Reporting::ResponseType::BLOCKED
66
67
  end
67
68
 
68
- # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
69
- def build_input attack_sample
70
- user_input = attack_sample.user_input
69
+ # @param user_input [Contrast::Agent::Reporting::UserInput]
70
+ def build_input user_input
71
71
  {
72
72
  documentPath: user_input.path,
73
- documentType: build_document_type(user_input.document_type),
73
+ documentType: user_input.document_type.to_s,
74
74
  filters: user_input.matcher_ids,
75
75
  name: user_input.key,
76
- time: attack_sample.timestamp_ms,
77
- type: build_input_type(user_input.input_type),
76
+ time: time_stamp,
77
+ type: user_input.input_type.to_s,
78
78
  value: user_input.value
79
79
  }
80
80
  end
81
81
 
82
- # Convert the document type api enum to a String constant TeamServer can understand.
83
- #
84
- # @param type_enum [::Contrast::Api::Dtm::HttpRequest::DocumentType]
85
- # @return [String]
86
- def build_document_type type_enum
87
- case type_enum
88
- when ::Contrast::Api::Dtm::HttpRequest::DocumentType::JSON
89
- 'JSON'
90
- when ::Contrast::Api::Dtm::HttpRequest::DocumentType::XML
91
- 'XML'
92
- else
93
- 'NORMAL'
94
- end
95
- end
96
-
97
- # Convert the input type api enum to a String constant TeamServer can understand.
98
- #
99
- # @param type_enum [when ::Contrast::Api::Dtm::UserInput::InputType]
100
- # @return [String]
101
- def build_input_type type_enum
102
- case type_enum
103
- when ::Contrast::Api::Dtm::UserInput::InputType::UNDEFINED_TYPE
104
- 'UNDEFINED_TYPE'
105
- when ::Contrast::Api::Dtm::UserInput::InputType::BODY
106
- 'BODY'
107
- when ::Contrast::Api::Dtm::UserInput::InputType::COOKIE_NAME
108
- 'COOKIE_NAME'
109
- when ::Contrast::Api::Dtm::UserInput::InputType::COOKIE_VALUE
110
- 'COOKIE_VALUE'
111
- when ::Contrast::Api::Dtm::UserInput::InputType::HEADER
112
- 'HEADER'
113
- when ::Contrast::Api::Dtm::UserInput::InputType::PARAMETER_NAME
114
- 'PARAMETER_NAME'
115
- when ::Contrast::Api::Dtm::UserInput::InputType::PARAMETER_VALUE
116
- 'PARAMETER_VALUE'
117
- when ::Contrast::Api::Dtm::UserInput::InputType::QUERYSTRING
118
- 'QUERYSTRING'
119
- when ::Contrast::Api::Dtm::UserInput::InputType::URI
120
- 'URI'
121
- when ::Contrast::Api::Dtm::UserInput::InputType::SOCKET
122
- 'SOCKET'
123
- when ::Contrast::Api::Dtm::UserInput::InputType::JSON_VALUE
124
- 'JSON_VALUE'
125
- when ::Contrast::Api::Dtm::UserInput::InputType::JSON_ARRAYED_VALUE
126
- 'JSON_ARRAYED_VALUE'
127
- when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_CONTENT_TYPE
128
- 'MULTIPART_CONTENT_TYPE'
129
- when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_VALUE
130
- 'MULTIPART_VALUE'
131
- when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_FIELD_NAME
132
- 'MULTIPART_FIELD_NAME'
133
- when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_NAME
134
- 'MULTIPART_NAME'
135
- when ::Contrast::Api::Dtm::UserInput::InputType::XML_VALUE
136
- 'XML_VALUE'
137
- when ::Contrast::Api::Dtm::UserInput::InputType::DWR_VALUE
138
- 'DWR_VALUE'
139
- when ::Contrast::Api::Dtm::UserInput::InputType::METHOD
140
- 'METHOD'
141
- when ::Contrast::Api::Dtm::UserInput::InputType::REQUEST
142
- 'REQUEST'
143
- when ::Contrast::Api::Dtm::UserInput::InputType::URL_PARAMETER
144
- 'URL_PARAMETER'
145
- else # ::Contrast::Api::Dtm::UserInput::InputType::UNKNOWN
146
- 'UNKNOWN'
147
- end
82
+ # @param attack_result [Contrast::Agent::Reporting::AttackResult]
83
+ # @param attack_sample [Contrast::Agent::Reporting::RaspRuleSample]
84
+ def attach_data attack_result, attack_sample
85
+ @blocked = attack_result.response == ::Contrast::Agent::Reporting::ResponseType::BLOCKED
86
+ @user_input = attack_sample.user_input
87
+ @details = attack_sample.details
88
+ @time_stamp = attack_sample.time_stamp
89
+ @request = FindingRequest.convert(Contrast::Agent::REQUEST_TRACKER.current&.request)
90
+ @stack = Contrast::Utils::StackTraceUtils.build_protect_report_stack_array
148
91
  end
149
92
 
150
- # This is a temporary method to facilitate the translation of API details to Reporting details. As we move off
151
- # of protobuf, this responsibility should shift from this class to the individual rules, as they do today when
152
- # they populate their details in Contrast::Api::Dtm::RaspRuleSample
153
- #
154
- # @param rule_id [String]
155
- # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
156
- # @return [Hash] the details for this specific rule
157
- def build_details rule_id, attack_sample
158
- case rule_id
159
- when Contrast::Agent::Protect::Rule::CmdInjection::NAME
160
- Contrast::Agent::Protect::Rule::CmdInjection.extract_details(attack_sample)
161
- when Contrast::Agent::Protect::Rule::Deserialization::NAME
162
- Contrast::Agent::Protect::Rule::Deserialization.extract_details(attack_sample)
163
- when Contrast::Agent::Protect::Rule::HttpMethodTampering::NAME
164
- Contrast::Agent::Protect::Rule::HttpMethodTampering.extract_details(attack_sample)
165
- when Contrast::Agent::Protect::Rule::NoSqli::NAME
166
- Contrast::Agent::Protect::Rule::NoSqli.extract_details(attack_sample)
167
- when Contrast::Agent::Protect::Rule::PathTraversal::NAME
168
- Contrast::Agent::Protect::Rule::PathTraversal.extract_details(attack_sample)
169
- when Contrast::Agent::Protect::Rule::Sqli::NAME
170
- Contrast::Agent::Protect::Rule::Sqli.extract_details(attack_sample)
171
- when Contrast::Agent::Protect::Rule::Xss::NAME
172
- Contrast::Agent::Protect::Rule::Xss.extract_details(attack_sample)
173
- when Contrast::Agent::Protect::Rule::Xxe::NAME
174
- Contrast::Agent::Protect::Rule::Xxe.extract_details(attack_sample)
175
- else # something unknown or Contrast::Agent::Protect::Rule::UnsafeFileUpload::NAME
176
- Contrast::Utils::ObjectShare::EMPTY_HASH
177
- end
93
+ # @return [Hash]
94
+ def build_time_stamp
95
+ {
96
+ start: time_stamp,
97
+ elapsed: Contrast::Utils::Timer.now_ms - time_stamp
98
+ }
178
99
  end
179
100
  end
180
101
  end
@@ -16,21 +16,11 @@ module Contrast
16
16
  # @return [Hash<Integer,Integer>] map of time from start in seconds to number of attacks in that second
17
17
  attr_reader :time_map
18
18
 
19
- class << self
20
- # @param attack_result [Contrast::Api::Dtm::AttackResult]
21
- def convert attack_result
22
- activity = new
23
- activity.attach_data(attack_result)
24
- activity
25
- end
26
- end
27
-
28
19
  def initialize
29
20
  @samples = []
30
21
  @start_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0 # in ms
31
22
  @event_type = :application_defend_attack_sample_activity
32
23
  @time_map = Hash.new { |h, k| h[k] = 0 }
33
- super
34
24
  end
35
25
 
36
26
  def to_controlled_hash
@@ -42,17 +32,16 @@ module Contrast
42
32
  }
43
33
  end
44
34
 
45
- # @param attack_result [Contrast::Api::Dtm::AttackResult]
35
+ # @param attack_result [Contrast::Agent::Reporting::AttackResult]
46
36
  def attach_data attack_result
47
37
  attack_result.samples.each do |attack_sample|
48
38
  base_time = Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0
49
- dmt_time = attack_sample.timestamp_ms.to_i
50
- converted = Contrast::Agent::Reporting::ApplicationDefendAttackSample.convert(attack_result, attack_sample)
51
- samples << converted
52
- @start_time = if dmt_time.zero?
39
+ sample_time = attack_sample.time_stamp.to_i
40
+ samples << Contrast::Agent::Reporting::ApplicationDefendAttackSample.convert(attack_result, attack_sample)
41
+ @start_time = if sample_time.zero?
53
42
  @start_time
54
43
  else
55
- dmt_time
44
+ sample_time
56
45
  end
57
46
  attack_second = (@start_time - base_time) / 1000 # in seconds
58
47
  time_map[attack_second] += 1
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/components/logger'
5
+ require 'contrast/utils/object_share'
5
6
  require 'contrast/agent/reporting/reporting_events/application_defend_attack_activity'
6
7
 
7
8
  module Contrast
@@ -12,7 +13,7 @@ module Contrast
12
13
  class ApplicationDefendAttackerActivity
13
14
  # @return [Hash<String,Contrast::Agent::Reporting::ApplicationDefendAttackActivity>] map of rule-id to violated
14
15
  # samples for that rule
15
- attr_reader :protection_rules
16
+ attr_accessor :protection_rules
16
17
  # @return [String, nil] the IP address of the request from which the attack originated; used to identify unique
17
18
  # attackers
18
19
  attr_reader :source_ip
@@ -20,25 +21,14 @@ module Contrast
20
21
  # identify unique attackers
21
22
  attr_reader :source_forwarded_for
22
23
 
23
- class << self
24
- # @param attack_result_dtm [Contrast::Api::Dtm::AttackResult]
25
- # @return [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
26
- def convert attack_result_dtm
27
- activity = new
28
- activity.attach_data(attack_result_dtm)
29
- activity
30
- end
31
- end
32
-
33
24
  def initialize
34
25
  @protection_rules = {}
35
- req = Contrast::Agent::REQUEST_TRACKER.current.activity.http_request
26
+ req = Contrast::Agent::REQUEST_TRACKER.current&.request
36
27
  if req
37
- @source_ip = req.sender.ip
38
- @source_forwarded_for = req.request_headers['X-Forwarded-For']
28
+ @source_ip = req.ip || Contrast::Utils::ObjectShare::EMPTY_STRING
29
+ @source_forwarded_for = req.headers['X-Forwarded-For']
39
30
  end
40
31
  @event_type = :application_defend_attacker_activity
41
- super
42
32
  end
43
33
 
44
34
  def to_controlled_hash
@@ -51,10 +41,12 @@ module Contrast
51
41
  }
52
42
  end
53
43
 
54
- # @param attack_result [Contrast::Api::Dtm::AttackResult]
44
+ # @param attack_result [Contrast::Agent::Reporting::AttackResult]
55
45
  def attach_data attack_result
56
- @protection_rules[attack_result.rule_id] =
57
- Contrast::Agent::Reporting::ApplicationDefendAttackActivity.convert(attack_result)
46
+ @protection_rules[attack_result.rule_id] = Contrast::Agent::Reporting::ApplicationDefendAttackActivity.new.
47
+ tap do |activity|
48
+ activity.attach_data(attack_result)
49
+ end
58
50
  end
59
51
 
60
52
  def process_protection_rules
@@ -18,16 +18,6 @@ module Contrast
18
18
  # @ return [Array<String>, nil] - User-Agent Header value
19
19
  attr_reader :browsers
20
20
 
21
- class << self
22
- # @param activity_dtm [Contrast::Api::Dtm::ApplicationActivity]
23
- # @return [Contrast::Agent::Reporting::ApplicationInventoryActivity]
24
- def convert activity_dtm
25
- inventory = new
26
- inventory.attach_data(activity_dtm)
27
- inventory
28
- end
29
- end
30
-
31
21
  def initialize
32
22
  @event_type = :application_inventory_activity
33
23
  @browsers = []
@@ -43,11 +33,13 @@ module Contrast
43
33
  }
44
34
  end
45
35
 
46
- def attach_data activity
47
- activity.architectures.each do |architecture|
48
- @components << Contrast::Agent::Reporting::ArchitectureComponent.convert(architecture)
36
+ # @param architectures [Array<Contrast::Agent::Reporting::ArchitectureComponent>,
37
+ # Contrast::Agent::Reporting::ArchitectureComponent]
38
+ def attach_data architectures
39
+ Array(architectures).each do |architecture|
40
+ @components << architecture
49
41
  end
50
- request_headers = activity.http_request&.request_headers
42
+ request_headers = Contrast::Agent::REQUEST_TRACKER.current&.request&.headers
51
43
  @browsers << request_headers['USER_AGENT'] if request_headers
52
44
  end
53
45
 
@@ -20,36 +20,45 @@ module Contrast
20
20
  class ArchitectureComponent
21
21
  include Contrast::Components::Logger::InstanceMethods
22
22
  # required attributes
23
- attr_reader :type, :url
23
+ attr_accessor :type, :url
24
24
  # optional attributes
25
- attr_reader :remote_host, :remote_port, :vendor
25
+ attr_accessor :remote_host, :remote_port, :vendor
26
26
 
27
27
  # TeamServer only treats these specific values as valid for Architecture Components. It does not know how to
28
28
  # process a message with a different type.
29
+ AC_TYPE_DB = 'db'
29
30
  VALID_TYPES = %w[db ldap ws].cs__freeze
30
31
 
32
+ # class << self
33
+ # # Convert a DTM for SpeedRacer to an Event for TeamServer.
34
+ # #
35
+ # # @param component_dtm [Contrast::Api::Dtm::ArchitectureComponent]
36
+ # # @return [Contrast::Agent::Reporting::ArchitectureComponent]
37
+ # def convert component_dtm
38
+ # report = new
39
+ # report.attach_data(component_dtm)
40
+ # report
41
+ # end
42
+ # end
43
+
31
44
  class << self
32
- # Convert a DTM for SpeedRacer to an Event for TeamServer.
33
- #
34
- # @param component_dtm [Contrast::Api::Dtm::ArchitectureComponent]
35
- # @return [Contrast::Agent::Reporting::ArchitectureComponent]
36
- def convert component_dtm
37
- report = new
38
- report.attach_data(component_dtm)
39
- report
45
+ def build_database
46
+ msg = new
47
+ msg.type = AC_TYPE_DB
48
+ msg
40
49
  end
41
50
  end
42
51
 
43
- # Attach the data from the protobuf models to this reporter so that it can be sent to TeamServer directly.
44
- #
45
- # @param component_dtm [Contrast::Api::Dtm::ArchitectureComponent]
46
- def attach_data component_dtm
47
- @remote_host = component_dtm.remote_host
48
- @remote_port = component_dtm.remote_port
49
- @type = component_dtm.type
50
- @url = component_dtm.url
51
- @vendor = component_dtm.vendor
52
- end
52
+ # # Attach the data from the protobuf models to this reporter so that it can be sent to TeamServer directly.
53
+ # #
54
+ # # @param component_dtm [Contrast::Api::Dtm::ArchitectureComponent]
55
+ # def attach_data component_dtm
56
+ # @remote_host = component_dtm.remote_host
57
+ # @remote_port = component_dtm.remote_port
58
+ # @type = component_dtm.type
59
+ # @url = component_dtm.url
60
+ # @vendor = component_dtm.vendor
61
+ # end
53
62
 
54
63
  # Convert the instance variables on the class, and other information, into the identifiers required for
55
64
  # TeamServer to process the JSON form of this message.
@@ -13,10 +13,12 @@ module Contrast
13
13
  class FindingRequest
14
14
  include Contrast::Components::Logger::InstanceMethods
15
15
 
16
+ OMITTED_BODY = '{{body-omitted-by-contrast}}'
17
+
16
18
  # @return [String] the body of this request
17
- attr_reader :body
19
+ attr_accessor :body
18
20
  # @return [Hash<String,Array<String>>] the headers of this request
19
- attr_reader :headers
21
+ attr_accessor :headers
20
22
  # @return [String] the HTTP verb of this request
21
23
  attr_reader :method
22
24
  # @return [Hash<String,Array<String>>] the parameters of this request
@@ -26,16 +28,27 @@ module Contrast
26
28
  # @return [String] the HTTP(S) protocol of this request
27
29
  attr_reader :protocol
28
30
  # @return [String] the query string of this request
29
- attr_reader :query_string
31
+ attr_accessor :query_string
30
32
  # @return [String] the url, including path and script, of this request
31
33
  attr_reader :uri
32
34
  # @return [String] the HTTP version of this request
33
35
  attr_reader :version
36
+ # @return [Integer]
37
+ attr_reader :ip
38
+ # @return [String] Byte representation of the body
39
+ attr_accessor :body_binary
40
+ # @return [Hash]
41
+ attr_reader :cookies
42
+ # REMOVE_DTM_REQUEST
43
+ # @return [Contrast::Api::Dtm::RawRequest]
44
+ attr_reader :dtm
34
45
 
35
46
  class << self
36
47
  # @param request [Contrast::Agent::Request]
37
48
  # @return [Contrast::Agent::Reporting::FindingRequest]
38
49
  def convert request
50
+ return unless request
51
+
39
52
  report = new
40
53
  report.attach_data(request)
41
54
  report
@@ -47,15 +60,11 @@ module Contrast
47
60
  #
48
61
  # @param request [Contrast::Agent::Request]
49
62
  def attach_data request
63
+ # REMOVE_DTM_REQUEST
64
+ @dtm = request.dtm
50
65
  @body = request.body
51
66
  @headers = {}
52
- request.headers.each_pair do |key, value|
53
- # We need to change from the uppercase _ format to capitalized - format.
54
- header = key.split('_')
55
- header.each(&:capitalize!)
56
- header = header.join('-')
57
- headers[header] = value.split
58
- end
67
+ extract_headers(request)
59
68
  @method = request.request_method
60
69
  @parameters = {}
61
70
  request.parameters.each_pair { |key, value| @parameters[key] = Array(value) }
@@ -64,6 +73,14 @@ module Contrast
64
73
  @query_string = request.query_string
65
74
  @uri = request.normalized_uri
66
75
  @version = request.version
76
+ @ip = request.ip || ''
77
+ @body_binary = if omit_body?(request)
78
+ OMITTED_BODY
79
+ else
80
+ Contrast::Utils::StringUtils.force_utf8(request.body)
81
+ end
82
+ @cookies = {}
83
+ @cookies = request.cookies unless request.cookies.empty?
67
84
  end
68
85
 
69
86
  # Convert the instance variables on the class, and other information, into the identifiers required for
@@ -92,12 +109,30 @@ module Contrast
92
109
  }
93
110
  end
94
111
 
112
+ def omit_body? request
113
+ return true if ::Contrast::AGENT.omit_body?
114
+ return false if request.document_type != :NORMAL
115
+
116
+ request.media_type&.include?('multipart/form-data')
117
+ end
118
+
95
119
  def validate
96
120
  unless method && !method.empty? # rubocop:disable Security/Object/Method
97
121
  raise(ArgumentError, "#{ self } did not have a proper method. Unable to continue.")
98
122
  end
99
123
  raise(ArgumentError, "#{ self } did not have a proper uri. Unable to continue.") unless uri && !uri.empty?
100
124
  end
125
+
126
+ # @param request [Contrast::Agent::Request]
127
+ def extract_headers request
128
+ request.headers.each_pair do |key, value|
129
+ # We need to change from the uppercase _ format to capitalized - format.
130
+ header = key.split('_')
131
+ header.each(&:capitalize!)
132
+ header = header.join('-')
133
+ headers[header] = value.split
134
+ end
135
+ end
101
136
  end
102
137
  end
103
138
  end
@@ -18,12 +18,6 @@ module Contrast
18
18
  dtm.cs__is_a?(Contrast::Api::Dtm::Finding)
19
19
  end
20
20
 
21
- # @param dtm [Contrast::Api::Dtm::Finding,Object]
22
- # @return [Boolean]
23
- def activity? dtm
24
- dtm.cs__is_a?(Contrast::Api::Dtm::Activity)
25
- end
26
-
27
21
  # Converts DTM message to Reporting Event for those messages that have conversion methods crated. We use this
28
22
  # as we work to move away from requiring the Service.
29
23
  #
@@ -32,7 +26,6 @@ module Contrast
32
26
  def dtm_to_event dtm
33
27
  # For the others, we convert them.
34
28
  return Contrast::Agent::Reporting::Finding.convert(dtm) if finding?(dtm)
35
- return Contrast::Agent::Reporting::ApplicationActivity.convert(dtm) if activity?(dtm)
36
29
 
37
30
  nil
38
31
  end
@@ -43,7 +43,6 @@ module Contrast
43
43
  # @param connection [Net::HTTP] open connection
44
44
  def startup! connection
45
45
  return if status.startup_messages_sent?
46
- return unless Contrast::Agent::Reporter.enabled?
47
46
 
48
47
  send_agent_startup(connection)
49
48
  end
@@ -57,6 +56,8 @@ module Contrast
57
56
  def send_event event, connection
58
57
  return unless connection
59
58
 
59
+ logger.debug('Preparing to send reporting event', event_class: event.cs__class)
60
+
60
61
  request = build_request(event)
61
62
  response = connection.request(request)
62
63
  audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable
@@ -142,6 +142,8 @@ module Contrast
142
142
  @_body
143
143
  end
144
144
 
145
+ # REMOVE_DTM_REQUEST
146
+ #
145
147
  # Unlike most of our translation, which is called where needed for each
146
148
  # message and forgotten, we'll leave this method to call the build as we
147
149
  # don't want to pay to reconstruct the DTM for this Request multiple
@@ -12,6 +12,7 @@ require 'contrast/utils/request_utils'
12
12
  require 'contrast/agent/request_context_extend'
13
13
  require 'contrast/agent/reporting/reporting_events/observed_route'
14
14
  require 'contrast/agent/reporting/input_analysis/input_analysis'
15
+ require 'contrast/agent/reporting/reporting_events/application_activity'
15
16
 
16
17
  module Contrast
17
18
  module Agent
@@ -25,8 +26,12 @@ module Contrast
25
26
 
26
27
  EMPTY_INPUT_ANALYSIS_PB = Contrast::Api::Settings::InputAnalysis.new
27
28
 
28
- # @return [Contrast::Api::Dtm::Activity] the application activity found in this request
29
+ # @return [Contrast::Agent::Reporting:ApplicationActivity] the application activity found in this request
29
30
  attr_reader :activity
31
+ # REMOVE once findings and routes are done.
32
+ #
33
+ # @return [Contrast::Api::Dtm::Activity] the application activity found in this request
34
+ attr_reader :dtm_activity
30
35
  # @return [Hash] context used to log the request
31
36
  attr_reader :logging_hash
32
37
  # @return [Contrast::Agent::Reporting::ObservedRoute] the route, used for coverage, of this request
@@ -53,9 +58,13 @@ module Contrast
53
58
 
54
59
  # instantiate helper for request and response
55
60
  @request = Contrast::Agent::Request.new(rack_request)
61
+ @activity = Contrast::Agent::Reporting::ApplicationActivity.new
56
62
 
57
- @activity = Contrast::Api::Dtm::Activity.new
58
- @activity.http_request = request.dtm
63
+ # REMOVE once findings and routes are done.
64
+ #
65
+ # We still need the activity to report routes and findings
66
+ @dtm_activity = Contrast::Api::Dtm::Activity.new
67
+ @dtm_activity.http_request = request.dtm
59
68
 
60
69
  # build analyzer
61
70
  @do_not_track = false
@@ -125,7 +134,7 @@ module Contrast
125
134
  end
126
135
 
127
136
  def reset_activity
128
- @activity = Contrast::Api::Dtm::Activity.new(http_request: request.dtm)
137
+ @activity = Contrast::Agent::Reporting::ApplicationActivity.new
129
138
  @observed_route = Contrast::Agent::Reporting::ObservedRoute.new
130
139
  end
131
140