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
@@ -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
 
@@ -23,6 +23,7 @@ module Contrast
23
23
  include Contrast::Utils::CEFLogUtils
24
24
  include Contrast::Components::Logger::InstanceMethods
25
25
  BUILD_ATTACK_LOGGER_MESSAGE = 'Building attack result from Contrast Service input analysis result'
26
+ CEF_LOGGING_RULES = %w[bot-blocker virtual-patch ip-denylist].cs__freeze
26
27
  # Convert the discovered route for this request to appropriate forms and disseminate it to those locations
27
28
  # where it is necessary for our route coverage and finding vulnerability discovery features to function.
28
29
  #
@@ -34,8 +35,10 @@ module Contrast
34
35
  # For our findings
35
36
  @route = route
36
37
 
38
+ # REMOVE_DTM_ACTIVITY
39
+ #
37
40
  # For SR findings
38
- @activity.routes << route
41
+ @dtm_activity.routes << route
39
42
 
40
43
  # For TS routes
41
44
  @request.route = route
@@ -54,27 +57,14 @@ module Contrast
54
57
  @request.observed_route = @observed_route
55
58
  end
56
59
 
57
- # Collect the results for the given rule with the given action
58
- #
59
- # @param rule [String] the id of the rule to which the results apply
60
- # @param response_type [Symbol] the result of the response, matching a value of
61
- # Contrast::Api::Dtm::AttackResult::ResponseType
62
- # @return [Array<Contrast::Api::Dtm::AttackResult>]
63
- def results_for rule, response_type = nil
64
- if response_type.nil?
65
- activity.results.select { |r| r.rule_id == rule }
66
- else
67
- activity.results.select { |r| r.rule_id == rule && r.response == response_type }
68
- end
69
- end
70
-
71
60
  # @raise [Contrast::SecurityException]
72
61
  def service_extract_request
73
62
  return false unless ::Contrast::AGENT.enabled?
74
63
  return false unless ::Contrast::PROTECT.enabled?
75
64
  return false if @do_not_track
76
65
 
77
- service_response = Contrast::Agent&.messaging_queue&.send_event_immediately(@activity.http_request)
66
+ # REMOVE_DTM_ACTIVITY
67
+ service_response = Contrast::Agent&.messaging_queue&.send_event_immediately(@activity.request.dtm)
78
68
  return false unless service_response
79
69
 
80
70
  handle_protect_state(service_response)
@@ -112,6 +102,7 @@ module Contrast
112
102
  @do_not_track = true unless state.track_request
113
103
  return unless state.security_exception
114
104
 
105
+ # make sure the activity get send before the error
115
106
  # If Contrast Service has NOT handled the input analysis, handle them here
116
107
  build_attack_results(agent_settings)
117
108
  logger.debug('Contrast Service said to block this request')
@@ -126,7 +117,6 @@ module Contrast
126
117
  @response = Contrast::Agent::Response.new(rack_response)
127
118
  return unless @sample_res
128
119
 
129
- #
130
120
  # TODO: RUBY-1376 once all rules translated, move this to if/else w/ the enabled
131
121
  if Contrast::Agent::Reporter.enabled?
132
122
  Contrast::Agent::Assess::Rule::Response::AutoComplete.new.analyze(@response)
@@ -139,7 +129,8 @@ module Contrast
139
129
  Contrast::Agent::Assess::Rule::Response::XContentType.new.analyze(@response)
140
130
  Contrast::Agent::Assess::Rule::Response::XXssProtection.new.analyze(@response)
141
131
  else
142
- activity.http_response = @response.dtm
132
+ # REMOVE_DTM_ACTIVITY
133
+ dtm_activity.http_response = @response.dtm
143
134
  end
144
135
  rescue StandardError => e
145
136
  logger.error('Unable to extract information after request', e)
@@ -147,7 +138,7 @@ module Contrast
147
138
 
148
139
  # This here is for things we don't have implemented
149
140
  def log_to_cef
150
- activity.results.each { |attack_result| logging_logic(attack_result, attack_result.rule_id.downcase) }
141
+ activity.defend.attackers.each { |attacker| logging_logic(attacker.protection_rules) }
151
142
  end
152
143
 
153
144
  # @param input_analysis [Contrast::Api::Settings::InputAnalysis]
@@ -177,7 +168,7 @@ module Contrast
177
168
 
178
169
  results_by_rule.each_pair do |_, attack_result|
179
170
  logger.info('Blocking attack result', rule: attack_result.rule_id)
180
- activity.results << attack_result
171
+ activity.attach_defend(attack_result)
181
172
  end
182
173
  end
183
174
 
@@ -199,28 +190,56 @@ module Contrast
199
190
  end
200
191
  end
201
192
 
202
- def logging_logic result, rule_id
203
- rules = %w[bot_blocker virtual_patch ip_denylist]
204
- return unless rules.include?(rule_id)
205
-
206
- rule_details = Contrast::Api::Dtm::RaspRuleSample.to_controlled_hash(result.samples[0]).fetch(rule_id.to_sym)
207
- outcome = Contrast::Api::Dtm::AttackResult::ResponseType.get_name_by_tag(result.response)
208
- case rule_id
209
- when /bot_blocker/i
210
- blocker_to_json = Contrast::Api::Dtm::BotBlockerDetails.to_controlled_hash(rule_details)
211
- cef_logger.bot_blocking_message(blocker_to_json, outcome)
212
- when /virtual_patch/i
213
- virtual_patch_to_json = Contrast::Api::Dtm::VirtualPatchDetails.to_controlled_hash(rule_details)
214
- cef_logger.virtual_patch_message(virtual_patch_to_json, outcome)
215
- when /ip_denylist/i
216
- sender_ip = extract_sender_ip
217
- ip_denylist_to_json = Contrast::Api::Dtm::IpDenylistDetails.to_controlled_hash(rule_details)
218
- return unless sender_ip
219
- return unless sender_ip.include?(ip_denylist_to_json[:ip])
220
-
221
- cef_logger.ip_denylisted_message(sender_ip, ip_denylist_to_json, outcome)
193
+ # @param protection_rules [Array<rule_id => Contrast::Agent::Reporting::ApplicationDefendAttackActivity>] Array
194
+ # of all protection rules per active attackers of this request life cycle.
195
+ def logging_logic protection_rules
196
+ protection_rules.any? do |rule_id, activity|
197
+ next unless CEF_LOGGING_RULES.include?(rule_id)
198
+
199
+ outcome = activity.response_type
200
+ rule_details = details_builder(outcome, activity)
201
+ case rule_id
202
+ when /bot-blocker/i
203
+ cef_logger.bot_blocking_message(rule_details.to_controlled_hash, outcome) if rule_details
204
+ when /virtual-patch/i
205
+ cef_logger.virtual_patch_message(rule_details.to_controlled_hash, outcome) if rule_details
206
+ when /ip-denylist/i
207
+ sender_ip = extract_sender_ip
208
+ next unless sender_ip
209
+ next unless rule_details && rule_details.ip == sender_ip
210
+
211
+ cef_logger.ip_denylisted_message(sender_ip, rule_details.to_controlled_hash, outcome)
212
+ end
213
+ end
214
+ end
215
+
216
+ # @param outcome [Symbol<Contrast::Agent::Reporting::ResponseType>]
217
+ # @param activity [Contrast::Agent::Reporting::ApplicationDefendAttackActivity]
218
+ def details_builder outcome, activity
219
+ case outcome
220
+ when ::Contrast::Agent::Reporting::ResponseType::BLOCKED
221
+ blocked = activity.blocked
222
+ get_details(blocked)
223
+ when ::Contrast::Agent::Reporting::ResponseType::MONITORED
224
+ exploited = activity.exploited
225
+ get_details(exploited)
226
+ when ::Contrast::Agent::Reporting::ResponseType::PROBED
227
+ ineffective = activity.ineffective
228
+ get_details(ineffective)
229
+ when ::Contrast::Agent::Reporting::ResponseType::SUSPICIOUS
230
+ activity.suspicious.samples[0].details
231
+ suspicious = activity.suspicious
232
+ get_details(suspicious)
222
233
  end
223
234
  end
235
+
236
+ # @param type [Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity]
237
+ # @return details [Contrast::Agent::Reporting::ProtectRuleDetails] depends on rule
238
+ def get_details type
239
+ sample = nil
240
+ sample = type.samples[0] unless type.samples.empty?
241
+ sample&.details
242
+ end
224
243
  end
225
244
  end
226
245
  end
@@ -22,13 +22,6 @@ module Contrast
22
22
  @ruleset = ::Contrast::AGENT.ruleset
23
23
  end
24
24
 
25
- # Send Activities messages to TS [Contrast::Api::Dtm::Activity]
26
- # TODO: RUBY-1704
27
- # TODO: RUBY-1438
28
- def send_activity_messages
29
- Contrast::Agent.messaging_queue&.send_event_eventually(context.activity)
30
- end
31
-
32
25
  # reports events[Contrast::Agent::Reporting::ReporterEvent] to TS
33
26
  # This method is used to send our JSON messages directly to TeamServer at the end of each request. As we move
34
27
  # more endpoints over, this method will take the messages originally sent by #send_actiivty_messages. At the end,
@@ -37,11 +30,16 @@ module Contrast
37
30
  return unless (reporter = Contrast::Agent.reporter)
38
31
 
39
32
  reporter.send_event(context.observed_route)
40
- return unless Contrast::Agent::Reporter.enabled?
33
+ # return unless Contrast::Agent::Reporter.enabled?
34
+
35
+ # REMOVE_DTM_ACTIVITY after reporter routes, responses, findings are implemented
36
+ #
37
+ # This reports routes, findings and traces with response dependent rules.
38
+ Contrast::Agent.messaging_queue&.send_event_eventually(context.dtm_activity)
41
39
 
42
40
  # Mask Sensitive Data
43
41
  Contrast::Agent::Reporting::Masker.mask(context.activity)
44
- event = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
42
+ event = context.activity
45
43
  reporter.send_event(event)
46
44
  end
47
45
 
@@ -19,7 +19,7 @@ module Contrast
19
19
  @_thread = Contrast::Agent::Thread.new do
20
20
  logger.info('Starting heartbeat thread.')
21
21
  loop do
22
- logger.info("Queue Size: #{ Contrast::Agent.messaging_queue.queue&.length }")
22
+ logger.info("Queue Size: #{ Contrast::Agent.messaging_queue&.queue&.length }")
23
23
  Contrast::Agent.messaging_queue&.send_event_eventually(poll_message)
24
24
  clean_properties
25
25
  sleep(REFRESH_INTERVAL_SEC)
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '6.6.2'
6
+ VERSION = '6.6.5'
7
7
  end
8
8
  end
@@ -52,7 +52,7 @@ module Contrast
52
52
 
53
53
  def build event
54
54
  msg = new
55
- msg.app_name = ::Contrast::APP_CONTEXT.app_name
55
+ msg.app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
56
56
  msg.app_path = ::Contrast::APP_CONTEXT.path
57
57
  msg.app_language = Contrast::Utils::ObjectShare::RUBY
58
58
  msg.client_id = ::Contrast::APP_CONTEXT.client_id
@@ -6,6 +6,7 @@ require 'contrast/api/decorators/agent_startup'
6
6
  require 'contrast/api/decorators/application_startup'
7
7
  require 'contrast/utils/object_share'
8
8
  require 'contrast/components/app_context_extend'
9
+ require 'contrast/config/base_configuration'
9
10
 
10
11
  module Contrast
11
12
  module Components
@@ -18,15 +19,66 @@ module Contrast
18
19
  class Interface
19
20
  include Contrast::Components::AppContextExtend
20
21
  include Contrast::Components::ComponentBase
21
- include Contrast::Components::Logger::InstanceMethods
22
+ include Contrast::Config::BaseConfiguration
22
23
 
23
24
  DEFAULT_APP_NAME = 'rails'
24
25
  DEFAULT_APP_PATH = '/'
25
26
  DEFAULT_SERVER_NAME = 'localhost'
26
27
  DEFAULT_SERVER_PATH = '/'
27
28
 
28
- def initialize
29
+ # @return [String]
30
+ attr_accessor :version
31
+ # @return [String]
32
+ attr_accessor :language
33
+ # @return [String]
34
+ attr_accessor :group
35
+ # @return [String]
36
+ attr_accessor :tags
37
+ # @return [String]
38
+ attr_accessor :code
39
+ # @return [String]
40
+ attr_accessor :metadata
41
+
42
+ def initialize hsh = {}
29
43
  original_pid
44
+ return unless hsh
45
+
46
+ @_name = hsh[:name]
47
+ @version = hsh[:version]
48
+ @language = hsh[:language]
49
+ @_path = hsh[:path]
50
+ @group = hsh[:group]
51
+ @tags = hsh[:tags]
52
+ @code = hsh[:code]
53
+ @metadata = hsh[:metadata]
54
+ @_session_id = hsh[:session_id]
55
+ @_session_metadata = hsh[:session_metadata]
56
+ end
57
+
58
+ # @return [String, Contrast::Utils::ObjectShare::EMPTY_STRING]
59
+ def session_id
60
+ @_session_id ||= Contrast::Utils::ObjectShare::EMPTY_STRING
61
+ end
62
+
63
+ # Set session_id
64
+ #
65
+ # @param id [String]
66
+ # @return [String]
67
+ def session_id= id
68
+ @_session_id = id
69
+ end
70
+
71
+ # @return [String, Contrast::Utils::ObjectShare::EMPTY_STRING]
72
+ def session_metadata
73
+ @_session_metadata ||= Contrast::Utils::ObjectShare::EMPTY_STRING
74
+ end
75
+
76
+ # Set session_metadata
77
+ #
78
+ # @param meta [String]
79
+ # @return [String]
80
+ def session_metadata= meta
81
+ @_session_metadata = meta
30
82
  end
31
83
 
32
84
  def server_type
@@ -37,9 +89,8 @@ module Contrast
37
89
  end
38
90
  end
39
91
 
40
- def app_name
41
- @_app_name ||= begin
42
- tmp = ::Contrast::CONFIG.root.application.name # rubocop:disable Security/Module/Name
92
+ def name
93
+ @_name ||= begin
43
94
  tmp = Contrast::Agent.framework_manager.app_name unless Contrast::Utils::StringUtils.present?(tmp)
44
95
  tmp = File.basename(Dir.pwd) unless Contrast::Utils::StringUtils.present?(tmp)
45
96
  Contrast::Utils::StringUtils.truncate(tmp, DEFAULT_APP_NAME)
@@ -48,13 +99,16 @@ module Contrast
48
99
  end
49
100
  end
50
101
 
51
- def app_version
52
- @_app_version ||= Contrast::CONFIG.root.application.version
102
+ # Set application name
103
+ #
104
+ # @param app_name [String] application name
105
+ # @return [String]
106
+ def name= app_name
107
+ @_name = app_name
53
108
  end
54
109
 
55
110
  def path
56
111
  @_path ||= begin
57
- tmp = ::Contrast::CONFIG.root.application.path
58
112
  tmp = Contrast::Agent.framework_manager.application_root unless Contrast::Utils::StringUtils.present?(tmp)
59
113
  Contrast::Utils::StringUtils.truncate(tmp, DEFAULT_APP_PATH)
60
114
  rescue StandardError
@@ -18,13 +18,13 @@ module Contrast
18
18
 
19
19
  def build_agent_startup_message
20
20
  msg = Contrast::Api::Dtm::AgentStartup.build(server_name, server_path, server_type)
21
- logger.info('Application context',
22
- server_name: msg.server_name,
23
- server_path: msg.server_path,
24
- server_type: msg.server_type,
25
- application_name: app_name,
26
- application_path: path,
27
- application_language: Contrast::Utils::ObjectShare::RUBY)
21
+ Contrast::CONFIG.proto_logger.info('Application context',
22
+ server_name: msg.server_name,
23
+ server_path: msg.server_path,
24
+ server_type: msg.server_type,
25
+ application_name: name, # rubocop:disable Security/Module/Name
26
+ application_path: path,
27
+ application_language: Contrast::Utils::ObjectShare::RUBY)
28
28
 
29
29
  msg
30
30
  end
@@ -42,7 +42,7 @@ module Contrast
42
42
  end
43
43
 
44
44
  def client_id
45
- @_client_id ||= [app_name, pgid].join('-')
45
+ @_client_id ||= [name, pgid].join('-') # rubocop:disable Security/Module/Name
46
46
  end
47
47
 
48
48
  def app_and_server_information
@@ -19,7 +19,7 @@ module Contrast
19
19
  DEFAULT_STACKTRACES = 'ALL'
20
20
  DEFAULT_MAX_SOURCE_EVENTS = 50_000
21
21
  DEFAULT_MAX_PROPAGATION_EVENTS = 50_000
22
- DEFAULT_MAX_RULE_REPORTED = 50_000
22
+ DEFAULT_MAX_RULE_REPORTED = 100
23
23
  DEFAULT_MAX_RULE_TIME_THRESHOLD = 300_000
24
24
 
25
25
  def initialize hsh = {}
@@ -4,6 +4,8 @@
4
4
  require 'contrast/components/agent'
5
5
  require 'contrast/components/inventory'
6
6
  require 'contrast/components/protect'
7
+ require 'contrast/components/app_context'
8
+
7
9
  module Contrast
8
10
  module Config
9
11
  # The base of the Common Configuration settings.
@@ -14,7 +16,7 @@ module Contrast
14
16
  attr_writer :api
15
17
  # @return [Contrast::Components::Agent::Interface]
16
18
  attr_writer :agent
17
- # @return [Contrast::Config::ApplicationConfiguration]
19
+ # @return [Contrast::Components::AppContext::Interface]
18
20
  attr_writer :application
19
21
  # @return [Contrast::Config::ServerConfiguration]
20
22
  attr_writer :server
@@ -36,7 +38,7 @@ module Contrast
36
38
  @api = Contrast::Components::Api::Interface.new(hsh[:api])
37
39
  @enable = hsh[:enable]
38
40
  @agent = Contrast::Components::Agent::Interface.new(hsh[:agent])
39
- @application = Contrast::Config::ApplicationConfiguration.new(hsh[:application])
41
+ @application = Contrast::Components::AppContext::Interface.new(hsh[:application])
40
42
  @server = Contrast::Config::ServerConfiguration.new(hsh[:server])
41
43
  @assess = Contrast::Config::AssessConfiguration.new(hsh[:assess])
42
44
  @inventory = Contrast::Components::Inventory::Interface.new(hsh[:inventory])
@@ -54,9 +56,9 @@ module Contrast
54
56
  @agent ||= Contrast::Components::Agent::Interface.new
55
57
  end
56
58
 
57
- # @return [Contrast::Config::ApplicationConfiguration]
59
+ # @return [Contrast::Components::AppContext::Interface]
58
60
  def application
59
- @application ||= Contrast::Config::ApplicationConfiguration.new
61
+ @application ||= Contrast::Components::AppContext::Interface.new
60
62
  end
61
63
 
62
64
  # @return [Contrast::Config::ServerConfiguration]
@@ -18,7 +18,6 @@ require 'contrast/config/protect_rule_configuration'
18
18
  require 'contrast/config/protect_rules_configuration'
19
19
 
20
20
  require 'contrast/config/ruby_configuration'
21
- require 'contrast/config/application_configuration'
22
21
  require 'contrast/config/server_configuration'
23
22
  require 'contrast/config/assess_configuration'
24
23
  require 'contrast/config/root_configuration'
@@ -20,12 +20,7 @@ module Contrast
20
20
  Contrast::Agent.reporter&.send_event_immediately(event)
21
21
  end
22
22
 
23
- if Contrast::Agent::Reporter.enabled?
24
- event = Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
25
- Contrast::Agent.reporter&.send_event_immediately(event)
26
- else
27
- Contrast::Agent.messaging_queue&.send_event_immediately(context.activity)
28
- end
23
+ Contrast::Agent.reporter&.send_event_immediately(context.activity)
29
24
  end
30
25
 
31
26
  def instrument
@@ -30,16 +30,29 @@ module Contrast
30
30
  false # policy does not have limit
31
31
  end
32
32
 
33
- def event_limit_for_rule? rule_id
34
- if Contrast::Utils::Timer.now_ms > threshold_time_limit
35
- @_rule_counts = nil
36
- @_threshold_time_limit = nil
33
+ def event_limit_for_rule? rule_id # rubocop:disable Metrics/AbcSize
34
+ return false unless (context = Contrast::Agent::REQUEST_TRACKER.current)
35
+
36
+ saved_request_ids = rule_counts.keys.map { |k| k.to_s.split('_')[1] }
37
+
38
+ # if we passed the threshold and we actually have records for that request - wipe them
39
+ if saved_request_ids.uniq.include?(context.request.__id__)
40
+ restore_defaults
37
41
  threshold_time_limit
38
42
  end
39
- rule_counts[rule_id] += 1
43
+
44
+ # if we have recorded rule counts, but none of them are for the current request_id
45
+ # eventually we can try and play with the time_limit_threshold -> DEFAULT_MAX_RULE_TIME_THRESHOLD
46
+ unless !rule_counts.empty? && saved_request_ids.include?(context.request.__id__)
47
+ restore_defaults
48
+ threshold_time_limit
49
+ end
50
+
51
+ rule_key = "#{ rule_id }_#{ context.request.__id__ }"
52
+ rule_counts[rule_key] += 1
40
53
  # TODO: RUBY-1680 remove default
41
- rule_counts[rule_id] >=
42
- (::Contrast::ASSESS.max_rule_reported || Contrast::Config::AssessConfiguration::DEFAULT_MAX_RULE_REPORTED)
54
+ # we don't need the default here because we either return from the config, or we return the default
55
+ rule_counts[rule_key] >= ::Contrast::ASSESS.max_rule_reported
43
56
  end
44
57
 
45
58
  # Increments the event count for the type of event that is being tracked
@@ -90,6 +103,12 @@ module Contrast
90
103
  def threshold_time_limit
91
104
  @_threshold_time_limit ||= Contrast::Utils::Timer.now_ms + (::Contrast::ASSESS.time_limit_threshold || 0)
92
105
  end
106
+
107
+ # @return nil
108
+ def restore_defaults
109
+ @_rule_counts = nil
110
+ @_threshold_time_limit = nil
111
+ end
93
112
  end
94
113
  end
95
114
  end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'socket'
5
5
  require 'contrast/agent/version'
6
+ require 'contrast/utils/object_share'
6
7
  require 'contrast/logger/aliased_logging'
7
8
 
8
9
  module Contrast
@@ -175,14 +176,11 @@ module Contrast
175
176
  # initially here we will use case to add it
176
177
  def extract_metadata rule_id = nil, outcome = nil
177
178
  message = []
178
- sender_info = context&.activity&.http_request&.sender
179
+ request = context&.activity&.request
180
+ sender_info = { ip: request&.ip || Contrast::Utils::ObjectShare::EMPTY_STRING, port: request&.port || 0 }
179
181
  rule_id ? message << "pri=#{ rule_id } " : 'asd'
180
- request_method = if context.request.rack_request.env['REQUEST_METHOD'].length.positive?
181
- context.request.rack_request.env['REQUEST_METHOD']
182
- else
183
- DEFAULT_METADATA
184
- end
185
- app_name = ::Contrast::APP_CONTEXT.app_name
182
+ request_method = assign_request_method(context)
183
+ app_name = ::Contrast::APP_CONTEXT.name # rubocop:disable Security/Module/Name
186
184
  attach_request_and_sender_info(message, sender_info)
187
185
  message << "request=#{ context.request.url } "
188
186
  message << "requestMethod=#{ request_method } "
@@ -198,10 +196,10 @@ module Contrast
198
196
  src = if needed_header
199
197
  needed_header
200
198
  else
201
- sender_info.ip.length > 1 ? sender_info.ip : DEFAULT_METADATA
199
+ sender_info[:ip].length > 1 ? sender_info[:ip] : DEFAULT_METADATA
202
200
  end
203
201
  message << "src=#{ src }"
204
- message << "port=#{ sender_info.port }"
202
+ message << "port=#{ sender_info[:port] }"
205
203
  end
206
204
 
207
205
  def extract_ip_address
@@ -216,9 +214,17 @@ module Contrast
216
214
  end
217
215
 
218
216
  def extract_sender_ip
219
- request_headers = context.activity.http_request.request_headers&.transform_keys(&:to_s)
217
+ request_headers = context.activity.request.headers&.transform_keys(&:to_s)
220
218
  request_headers['X-Forwarded-For']
221
219
  end
220
+
221
+ def assign_request_method context
222
+ if context.request.rack_request.env['REQUEST_METHOD'].length.positive?
223
+ context.request.rack_request.env['REQUEST_METHOD']
224
+ else
225
+ DEFAULT_METADATA
226
+ end
227
+ end
222
228
  end
223
229
  end
224
230
  end
@@ -38,7 +38,7 @@ module Contrast
38
38
  return unless net_http_client.started?
39
39
 
40
40
  logger.debug("Starting #{ service_name } connection test")
41
- return unless connection_verified?(net_http_client)
41
+ return unless connection_verified?(net_http_client, url)
42
42
 
43
43
  logger.debug('Client verified', service: service_name, url: url)
44
44
  net_http_client
@@ -49,18 +49,17 @@ module Contrast
49
49
 
50
50
  # Validates connection with assigned domain.
51
51
  # If connection is running, SSL certificate of the endpoint is valid, Ip address is resolvable
52
- # and response is received without peer's reset or refuse of connection,
53
- # then validation returns true. Error handling is in place so that the work of the agent will continue as
54
- # normal without Telemetry.
52
+ # and response is received without peer's reset or refuse of connection, then validation returns true.
55
53
  #
56
54
  # @param client [Net::HTTP]
55
+ # @param url [String]
57
56
  # @return [Boolean] true | false
58
- def connection_verified? client
57
+ def connection_verified? client, url
59
58
  return @_connection_verified unless @_connection_verified.nil?
60
59
  return false if client.nil?
61
60
 
62
61
  ipaddr = get_ipaddr(client)
63
- response = client.request(Net::HTTP::Get.new(client.address))
62
+ response = client.request(Net::HTTP::Get.new(url))
64
63
  verify_cert = client.address.to_s.include?('localhost') ||
65
64
  OpenSSL::SSL.verify_certificate_identity(client.peer_cert, client.address)
66
65
  resolved = resolved?(client.address, ipaddr)
@@ -1,15 +1,11 @@
1
1
  # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'contrast/components/logger'
5
-
6
4
  module Contrast
7
5
  module Utils
8
6
  # Utilities for encoding and normalizing strings
9
- class StringUtils
7
+ module StringUtils
10
8
  class << self
11
- include Contrast::Components::Logger::InstanceMethods
12
-
13
9
  UTF8 = 'utf-8'
14
10
  HTTP_PREFIX = 'HTTP_'
15
11
 
@@ -61,7 +57,7 @@ module Contrast
61
57
  # We were unable to switch the String to a UTF-8 format.
62
58
  # Return non-nil so as not to throw an exception later when trying
63
59
  # to do regexp or other compares on the String
64
- logger.trace('Unable to cast String to UTF-8 format', e, value: str)
60
+ Contrast::CONFIG.proto_logger.trace('Unable to cast String to UTF-8 format', e, value: str)
65
61
 
66
62
  Contrast::Utils::ObjectShare::EMPTY_STRING
67
63
  end