contrast-agent 6.6.2 → 6.6.5

Sign up to get free protection for your applications and to get access to all the features.
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