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
@@ -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