contrast-agent 6.6.2 → 6.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) 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 +16 -12
  6. data/lib/contrast/agent/inventory/policy/datastores.rb +1 -2
  7. data/lib/contrast/agent/middleware.rb +0 -1
  8. data/lib/contrast/agent/protect/rule/base.rb +16 -20
  9. data/lib/contrast/agent/protect/rule/cmd_injection.rb +5 -4
  10. data/lib/contrast/agent/protect/rule/deserialization.rb +5 -4
  11. data/lib/contrast/agent/protect/rule/path_traversal.rb +9 -7
  12. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +16 -14
  13. data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
  14. data/lib/contrast/agent/protect/rule/xxe.rb +9 -6
  15. data/lib/contrast/agent/reporting/attack_result/attack_result.rb +8 -0
  16. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +85 -36
  17. data/lib/contrast/agent/reporting/attack_result/user_input.rb +11 -0
  18. data/lib/contrast/agent/reporting/details/bot_blocker_details.rb +29 -0
  19. data/lib/contrast/agent/reporting/details/cmd_injection_details.rb +30 -0
  20. data/lib/contrast/agent/reporting/details/details.rb +18 -0
  21. data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +27 -0
  22. data/lib/contrast/agent/reporting/details/ip_denylist_details.rb +27 -0
  23. data/lib/contrast/agent/reporting/details/no_sqli_details.rb +36 -0
  24. data/lib/contrast/agent/reporting/details/path_traversal_details.rb +24 -0
  25. data/lib/contrast/agent/reporting/details/path_traversal_semantic_analysis_details.rb +32 -0
  26. data/lib/contrast/agent/reporting/details/protect_rule_details.rb +17 -0
  27. data/lib/contrast/agent/reporting/details/sqli_details.rb +36 -0
  28. data/lib/contrast/agent/reporting/details/untrusted_deserialization_details.rb +27 -0
  29. data/lib/contrast/agent/reporting/details/virtual_patch_details.rb +24 -0
  30. data/lib/contrast/agent/reporting/details/xss_details.rb +33 -0
  31. data/lib/contrast/agent/reporting/details/xss_match.rb +30 -0
  32. data/lib/contrast/agent/reporting/details/xxe_details.rb +36 -0
  33. data/lib/contrast/agent/reporting/details/xxe_match.rb +25 -0
  34. data/lib/contrast/agent/reporting/details/xxe_wrapper.rb +25 -0
  35. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +1 -1
  36. data/lib/contrast/agent/reporting/masker/masker.rb +78 -65
  37. data/lib/contrast/agent/reporting/masker/masker_utils.rb +1 -30
  38. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +84 -15
  39. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +13 -25
  40. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +17 -22
  41. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +46 -125
  42. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -16
  43. data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -18
  44. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -14
  45. data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +29 -20
  46. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +45 -10
  47. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -2
  48. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -7
  49. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +1 -1
  50. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +2 -2
  51. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +2 -1
  52. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +1 -1
  53. data/lib/contrast/agent/request.rb +2 -0
  54. data/lib/contrast/agent/request_context.rb +13 -4
  55. data/lib/contrast/agent/request_context_extend.rb +59 -40
  56. data/lib/contrast/agent/request_handler.rb +7 -9
  57. data/lib/contrast/agent/service_heartbeat.rb +1 -1
  58. data/lib/contrast/agent/version.rb +1 -1
  59. data/lib/contrast/api/decorators/message.rb +1 -1
  60. data/lib/contrast/components/app_context.rb +62 -8
  61. data/lib/contrast/components/app_context_extend.rb +8 -8
  62. data/lib/contrast/config/assess_configuration.rb +1 -1
  63. data/lib/contrast/config/root_configuration.rb +6 -4
  64. data/lib/contrast/config.rb +0 -1
  65. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -6
  66. data/lib/contrast/utils/assess/event_limit_utils.rb +26 -7
  67. data/lib/contrast/utils/log_utils.rb +16 -10
  68. data/lib/contrast/utils/net_http_base.rb +5 -6
  69. data/lib/contrast/utils/string_utils.rb +2 -6
  70. data/lib/contrast.rb +1 -1
  71. metadata +30 -14
  72. data/lib/contrast/config/application_configuration.rb +0 -57
@@ -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
@@ -27,8 +27,8 @@ module Contrast
27
27
 
28
28
  def initialize
29
29
  @app_language = Contrast::Utils::ObjectShare::RUBY
30
- @app_name = ::Contrast::APP_CONTEXT.app_name
31
- @app_version = ::Contrast::APP_CONTEXT.app_version
30
+ @app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
31
+ @app_version = ::Contrast::APP_CONTEXT.version
32
32
  @routes = []
33
33
  end
34
34
 
@@ -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
@@ -153,7 +153,7 @@ module Contrast
153
153
  def app_name
154
154
  return @_app_name unless @_app_name.nil?
155
155
 
156
- @_app_name = ::Contrast::APP_CONTEXT.app_name
156
+ @_app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
157
157
  end
158
158
 
159
159
  # @return [String,nil]
@@ -18,12 +18,12 @@ module Contrast
18
18
  CONTENT_TYPE = 'application/json'
19
19
 
20
20
  def initialize
21
- @app_name = Base64.strict_encode64(Contrast::APP_CONTEXT.app_name)
21
+ @app_name = Base64.strict_encode64(Contrast::APP_CONTEXT.name) # rubocop:disable Security/Module/Name
22
22
  @api_key = Contrast::API.api_key
23
23
  @agent_version = [RUBY, Contrast::Agent::VERSION].join(SPACE)
24
24
  @app_language = RUBY
25
25
  @app_path = Base64.strict_encode64(Contrast::APP_CONTEXT.path)
26
- @app_version = Contrast::APP_CONTEXT.app_version
26
+ @app_version = Contrast::APP_CONTEXT.version
27
27
  @authorization = Base64.strict_encode64("#{ Contrast::API.user_name }:#{ Contrast::API.service_key }")
28
28
  @server_name = Base64.strict_encode64(Contrast::APP_CONTEXT.server_name)
29
29
  @server_path = Base64.strict_encode64(Contrast::APP_CONTEXT.server_path)
@@ -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
@@ -107,7 +107,7 @@ module Contrast
107
107
  suspend_reporting(message, ready_after, error_message) if mode == @_mode.resending
108
108
  return unless mode == @_mode.disabled
109
109
 
110
- stop_reporting(message, application: Contrast::APP_CONTEXT.app_name, error_message: error_message)
110
+ stop_reporting(message, application: Contrast::APP_CONTEXT.name, error_message: error_message) # rubocop:disable Security/Module/Name
111
111
  rescue StandardError => e
112
112
  logger.debug('Could not handle Response error information', error: e)
113
113
  end
@@ -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