contrast-agent 6.1.2 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent/at_exit_hook.rb +2 -1
  3. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +9 -5
  4. data/lib/contrast/agent/protect/rule/xss.rb +4 -0
  5. data/lib/contrast/agent/reporting/reporter.rb +2 -11
  6. data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +3 -18
  7. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +75 -15
  8. data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
  9. data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +5 -19
  10. data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +6 -22
  11. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +1 -1
  12. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -3
  13. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +1 -3
  14. data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +9 -0
  15. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -2
  16. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -10
  17. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +0 -1
  18. data/lib/contrast/agent/reporting/reporting_utilities/response.rb +60 -2
  19. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +32 -10
  20. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
  21. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +58 -26
  22. data/lib/contrast/agent/reporting/settings/application_settings.rb +8 -23
  23. data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +27 -33
  24. data/lib/contrast/agent/reporting/settings/bot_blocker.rb +68 -0
  25. data/lib/contrast/agent/reporting/settings/code_exclusion.rb +27 -0
  26. data/lib/contrast/agent/reporting/settings/exclusion_base.rb +33 -0
  27. data/lib/contrast/agent/reporting/settings/exclusions.rb +39 -57
  28. data/lib/contrast/agent/reporting/settings/helpers.rb +56 -0
  29. data/lib/contrast/agent/reporting/settings/input_exclusion.rb +37 -0
  30. data/lib/contrast/agent/reporting/settings/ip_filter.rb +35 -0
  31. data/lib/contrast/agent/reporting/settings/keyword.rb +74 -0
  32. data/lib/contrast/agent/reporting/settings/log_enhancer.rb +65 -0
  33. data/lib/contrast/agent/reporting/settings/protect.rb +4 -2
  34. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +62 -115
  35. data/lib/contrast/agent/reporting/settings/reaction.rb +11 -2
  36. data/lib/contrast/agent/reporting/settings/rule_definition.rb +63 -0
  37. data/lib/contrast/agent/reporting/settings/sampling.rb +10 -0
  38. data/lib/contrast/agent/reporting/settings/sanitizer.rb +38 -0
  39. data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +9 -1
  40. data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +7 -0
  41. data/lib/contrast/agent/reporting/settings/server_features.rb +8 -0
  42. data/lib/contrast/agent/reporting/settings/syslog.rb +176 -0
  43. data/lib/contrast/agent/reporting/settings/url_exclusion.rb +42 -0
  44. data/lib/contrast/agent/reporting/settings/validator.rb +17 -0
  45. data/lib/contrast/agent/request_context.rb +4 -0
  46. data/lib/contrast/agent/request_handler.rb +8 -4
  47. data/lib/contrast/agent/static_analysis.rb +4 -8
  48. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +1 -1
  49. data/lib/contrast/agent/thread_watcher.rb +4 -5
  50. data/lib/contrast/agent/version.rb +1 -1
  51. data/lib/contrast/agent.rb +1 -3
  52. data/lib/contrast/api/decorators/application_update.rb +0 -8
  53. data/lib/contrast/api/decorators.rb +0 -1
  54. data/lib/contrast/framework/base_support.rb +5 -4
  55. data/lib/contrast/framework/grape/support.rb +6 -6
  56. data/lib/contrast/framework/manager.rb +2 -4
  57. data/lib/contrast/framework/manager_extend.rb +1 -0
  58. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +2 -1
  59. data/lib/contrast/framework/rails/support.rb +9 -2
  60. data/lib/contrast/framework/sinatra/support.rb +3 -2
  61. data/lib/contrast/logger/aliased_logging.rb +31 -26
  62. data/lib/contrast/utils/response_utils.rb +14 -1
  63. data/lib/contrast/utils/telemetry.rb +9 -0
  64. data/lib/contrast/utils/telemetry_hash.rb +36 -12
  65. data/lib/contrast/utils/telemetry_identifier.rb +8 -0
  66. data/lib/contrast/utils/thread_tracker.rb +26 -9
  67. data/lib/contrast/utils/timer.rb +6 -1
  68. data/lib/contrast.rb +1 -3
  69. metadata +26 -14
  70. data/lib/contrast/api/decorators/library_usage_update.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c901ed882ebff8176fe2f3794907e29b03cc2903b61260f138d93b2ef02a465c
4
- data.tar.gz: 72e4f01ccf5a57bbd5afa0cac58c51cddbe26183691d9495f563dfd9fb37e7e1
3
+ metadata.gz: b285299c51379c206ff8c7a64426ef5207594391d59c1ce284bae3a899d6d56f
4
+ data.tar.gz: f7847b7523600ebad30ba380cd338d36f6ed2e8136e86d2fdb71f4c7953f530a
5
5
  SHA512:
6
- metadata.gz: 1d60653e61e95443c45bb43caac325b3c72449ddea435096e2d8e49c0a0851ff9ae6486fc16a17f65f663c2f069c58f6157f4f7ef8745d0b082d4fe7c5c0b8b6
7
- data.tar.gz: e1e5dd1a542009d153fa6e77f86d889d8bb852d85d66c84400a583b63536be06a70423c4f05e547280f06368851a43720ac941efedf3aeb17a314ff3c9f61a14
6
+ metadata.gz: 3edc5557919437f5c7f76b741ed4133263bd975baebb6c81d8ac416713571c6ef871e9ab4d3b1ddf34aa0ff548242e71081443bc5c3cd04c2f4bae35b030e866
7
+ data.tar.gz: 21c33b917d5f9c0f3b8070bde64b42c8d800dd73c952b0d45830b9d13eb6135ba086c9abf43b3003168f5c3902753cf1a480444bbfca506f00d7bf51b5039d03
@@ -31,11 +31,12 @@ module Contrast
31
31
  context = Contrast::Agent::REQUEST_TRACKER.current
32
32
  return unless context
33
33
 
34
+ Contrast::Agent.reporter.send_event_immediately(context.observed_library_usage)
35
+
34
36
  if Contrast::Agent::Reporter.enabled?
35
37
  [
36
38
  context.new_observed_route,
37
39
  Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
38
- Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity.library_usages),
39
40
  Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
40
41
  ].each do |event|
41
42
  Contrast::Agent.reporter&.send_event_immediately(event)
@@ -1,6 +1,7 @@
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/agent/reporting/reporting_events/library_usage_observation'
4
5
  require 'contrast/agent/inventory/dependencies'
5
6
  require 'contrast/components/logger'
6
7
  require 'contrast/utils/object_share'
@@ -73,10 +74,11 @@ module Contrast
73
74
  #
74
75
  # TODO: RUBY-1355
75
76
  # TODO: RUBY-1438 -- change to just use EventMessage
76
- # @param activity [Contrast::Api::Dtm::Activity] the message to which to append the usage data
77
- def generate_library_usage activity
77
+ # @param observed_library_usage [Contrast::Agent::Reporting::ObservedLibraryUsage] the message to
78
+ # which to append the usage data
79
+ def generate_library_usage observed_library_usage
78
80
  return unless enabled?
79
- return unless activity
81
+ return unless observed_library_usage
80
82
 
81
83
  # Disconnect gemdigest_cache and replace it with an empty one; synch so new libs cannot be added between the
82
84
  # assignment and the replace
@@ -86,8 +88,10 @@ module Contrast
86
88
  hold
87
89
  end
88
90
  gem_spec_digest_to_files.each_pair do |digest, files|
89
- usage = Contrast::Api::Dtm::LibraryUsageUpdate.build(digest, files)
90
- activity.library_usages[usage.hash_code] = usage
91
+ usage = Contrast::Agent::Reporting::LibraryUsageObservation.new(digest, files)
92
+ next if usage.names.empty?
93
+
94
+ observed_library_usage.observations << usage
91
95
  end
92
96
  rescue StandardError => e
93
97
  logger.error('Unable to generate library usage.', e)
@@ -16,6 +16,10 @@ module Contrast
16
16
  # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
17
17
  # @return [Hash] the details for this specific rule
18
18
  def extract_details attack_sample
19
+ # TODO: RUBY-1702 - figure out why xss isn't populated when reported; probably something to do w/
20
+ # suspicious
21
+ return Contrast::Utils::ObjectShare::EMPTY_HASH unless attack_sample&.xss
22
+
19
23
  {
20
24
  input: attack_sample.xss.input,
21
25
  matches: attack_sample.xss.matches.map do |match|
@@ -31,16 +31,6 @@ module Contrast
31
31
  @_connection ||= client.initialize_connection
32
32
  end
33
33
 
34
- def attempt_to_start?
35
- unless cs__class.enabled?
36
- logger.warn('Reporter service is disabled!')
37
- return false
38
- end
39
-
40
- logger.debug('Attempting to start Reporter thread') unless running?
41
- true
42
- end
43
-
44
34
  def start_thread!
45
35
  return if running?
46
36
 
@@ -54,6 +44,8 @@ module Contrast
54
44
  next unless client && connection
55
45
 
56
46
  process_event(queue.pop)
47
+ rescue StandardError => e
48
+ logger.debug('Reporter thread could not process because of:', error: e)
57
49
  end
58
50
  end
59
51
  end
@@ -78,7 +70,6 @@ module Contrast
78
70
  return
79
71
  end
80
72
  return unless event
81
- return unless cs__class.enabled?
82
73
 
83
74
  logger.debug('Enqueued event for sending', event_type: event.cs__class)
84
75
  queue << event
@@ -17,19 +17,12 @@ module Contrast
17
17
  # discovered during first request processing.
18
18
  attr_reader :routes
19
19
 
20
- class << self
21
- def convert app_update_dtm
22
- app_inventory = new
23
- app_inventory.attach_data(app_update_dtm)
24
- app_inventory
25
- end
26
- end
27
-
28
20
  def initialize
29
- @routes = []
30
21
  @event_type = :application_inventory
31
22
  @event_method = :POST
32
23
  @event_endpoint = Contrast::Agent::Reporting::Endpoints.application_inventory
24
+ # The API spec limits us to 500 routes, so we'll enforce that here to not hold on to superfulous data.
25
+ @routes = Contrast::Agent.framework_manager.find_route_discovery_data.take(500)
33
26
  super
34
27
  end
35
28
 
@@ -39,18 +32,10 @@ module Contrast
39
32
 
40
33
  def to_controlled_hash
41
34
  {
42
- session_id: @agent_session_id_value,
35
+ session_id: ::Contrast::ASSESS.session_id,
43
36
  routes: routes.map(&:to_controlled_hash)
44
37
  }
45
38
  end
46
-
47
- # @param inventory_dtm [Contrast::Api::Dtm::ApplicationUpdate]
48
- # @return [Contrast::Agent::Reporting::ApplicationInventory]
49
- def attach_data inventory_dtm
50
- inventory_dtm.routes.each do |route|
51
- @routes << Contrast::Agent::Reporting::DiscoveredRoute.convert(route)
52
- end
53
- end
54
39
  end
55
40
  end
56
41
  end
@@ -17,15 +17,84 @@ module Contrast
17
17
  # recorded.
18
18
  class DiscoveredRoute < Contrast::Agent::Reporting::ObservedRoute
19
19
  class << self
20
- # @param dtm [Contrast::Api::Dtm::RouteCoverage]
20
+ # @param obj [Regexp, Object]
21
+ # @return [String]
22
+ def source_or_string obj
23
+ if obj.cs__is_a?(Regexp)
24
+ obj.source
25
+ elsif obj.cs__respond_to?(:safe_string)
26
+ obj.safe_string
27
+ else
28
+ obj.to_s
29
+ end
30
+ end
31
+
32
+ # Convert ActionDispatch::Journey::Route to Contrast::Agent::Reporting::DiscoveredRoute
33
+ #
34
+ # @param journey_obj [ActionDispatch::Journey::Route] a rails route
35
+ # @param url [String, nil] use url from string instead of journey object.
36
+ # @return [Contrast::Agent::Reporting::DiscoveredRoute]
37
+ def from_action_dispatch_journey journey_obj, url = nil
38
+ msg = new
39
+ msg.signature = "#{ journey_obj.defaults[:controller] }##{ journey_obj.defaults[:action] }"
40
+
41
+ verb = source_or_string(journey_obj.verb)
42
+ msg.verb = Contrast::Utils::StringUtils.force_utf8(verb)
43
+
44
+ url ||= source_or_string(journey_obj.path.spec)
45
+ msg.url = Contrast::Utils::StringUtils.force_utf8(url)
46
+ msg
47
+ end
48
+
49
+ # Convert Grape route data to discovered route.
50
+ #
51
+ # @param controller [::Grape::API] the route's final controller.
52
+ # @param method [String] GET, PUT, POST, etc...
53
+ # @param url [String, nil] use url from string instead matched pattern.
54
+ # @param pattern [String, Grape::Router::Route] the pattern that was matched in routing.
21
55
  # @return [Contrast::Agent::Reporting::DiscoveredRoute]
22
- def convert dtm
23
- discovered_route = new
24
- discovered_route.attach_data(dtm)
25
- discovered_route
56
+ def from_grape_controller controller, method, pattern, url = nil
57
+ if pattern.cs__is_a?(Grape::Router::Route)
58
+ safe_pattern = pattern.pattern&.path&.to_s
59
+ safe_url = source_or_string(url || safe_pattern)
60
+ else
61
+ safe_pattern = source_or_string(pattern)
62
+ safe_url = source_or_string(url || pattern)
63
+ end
64
+
65
+ msg = new
66
+ msg.signature = "#{ controller }##{ method } #{ safe_pattern }"
67
+ msg.verb = Contrast::Utils::StringUtils.force_utf8(method)
68
+ msg.url = Contrast::Utils::StringUtils.force_utf8(safe_url)
69
+ msg
70
+ end
71
+
72
+ # Convert Sinatra route data to discovered route.
73
+ #
74
+ # @param controller [::Sinatra::Base] the route's final controller.
75
+ # @param method [String] GET, PUT, POST, etc...
76
+ # @param pattern [::Mustermann::Sinatra] the pattern that was matched in routing.
77
+ # @param url [String, nil] use url from string instead matched pattern.
78
+ # @return [Contrast::Agent::Reporting::DiscoveredRoute]
79
+ def from_sinatra_route controller, method, pattern, url = nil
80
+ safe_pattern = source_or_string(pattern)
81
+ safe_url = source_or_string(url || pattern)
82
+
83
+ msg = new
84
+ msg.signature = "#{ controller }##{ method } #{ safe_pattern }"
85
+ msg.verb = Contrast::Utils::StringUtils.force_utf8(method)
86
+ msg.url = Contrast::Utils::StringUtils.force_utf8(safe_url)
87
+ msg
26
88
  end
27
89
  end
28
90
 
91
+ # @return [String] the controller, method, and pattern of this route
92
+ attr_accessor :signature
93
+ # @return [String] the url (or url pattern) used to access this route
94
+ attr_accessor :url
95
+ # @return [String, nil] the HTTP verb used to access this route or nil for any verb
96
+ attr_accessor :verb
97
+
29
98
  def initialize
30
99
  super
31
100
  @event_type = :discovered_route
@@ -34,18 +103,9 @@ module Contrast
34
103
  @url = Contrast::Utils::ObjectShare::EMPTY_STRING
35
104
  end
36
105
 
37
- # @param dtm[Contrast::Api::Dtm::RouteCoverage]
38
- def attach_data dtm
39
- @signature = dtm.route
40
- @verb = dtm.verb
41
- @url = dtm.url
42
- end
43
-
44
106
  def to_controlled_hash
45
107
  validate
46
- dr_hash = { session_id: @agent_session_id_value, signature: @signature, verb: @verb, url: @url }
47
- dr_hash.delete(:verb) unless @verb
48
- dr_hash
108
+ { session_id: ::Contrast::ASSESS.session_id, signature: @signature, verb: @verb, url: @url }.compact
49
109
  end
50
110
 
51
111
  def validate
@@ -135,7 +135,7 @@ module Contrast
135
135
  created: created,
136
136
  hash: hash_code.to_s,
137
137
  ruleId: rule_id,
138
- session_id: @agent_session_id_value.to_s,
138
+ session_id: ::Contrast::ASSESS.session_id,
139
139
  version: 4
140
140
  }
141
141
  hsh[:events] = events.map(&:to_controlled_hash) if event_based?
@@ -152,7 +152,7 @@ module Contrast
152
152
  # @raise [ArgumentError]
153
153
  def validate
154
154
  raise(ArgumentError, "#{ self } did not have a proper rule. Unable to continue.") unless @rule_id
155
- unless @agent_session_id_value
155
+ unless ::Contrast::ASSESS.session_id
156
156
  raise(ArgumentError, "#{ self } did not have a proper session id. Unable to continue.")
157
157
  end
158
158
  if event_based? && events.empty?
@@ -11,25 +11,11 @@ module Contrast
11
11
  # @param [Array<String>] List of file paths that have been loaded out of or executed by the library
12
12
  attr_reader :names
13
13
 
14
- class << self
15
- # Convert a DTM for SpeedRacer to an Event for TeamServer.
16
- #
17
- # @param usage_dtm [Contrast::Api::Dtm::LibraryUsageUpdate]
18
- # @return [Contrast::Agent::Reporting::LibraryUsageObservation]
19
- def convert usage_dtm
20
- observation = new
21
- observation.attach_data(usage_dtm)
22
- observation
23
- end
24
- end
25
-
26
- def initialize
27
- @names = []
28
- end
29
-
30
- def attach_data usage_dtm
31
- @id = usage_dtm.hash_code
32
- @names = usage_dtm.class_names.keys
14
+ # @param id [String] Sha256Sum of library as identified by the agent
15
+ # @param class_names [Array<String>] List of file paths that have been loaded out of or executed by the library
16
+ def initialize id, class_names
17
+ @id = id
18
+ @names = class_names
33
19
  end
34
20
 
35
21
  def to_controlled_hash
@@ -9,23 +9,11 @@ module Contrast
9
9
  module Reporting
10
10
  # List of libraries that have been observed to have something loaded or executed.
11
11
  #
12
- # @attr_reader observations - Array[Contrast::Agent::Reporting::LibraryUsageObservation]
13
- # - Hash of LibraryUsageObservations
14
12
  class ObservedLibraryUsage < Contrast::Agent::Reporting::ApplicationReportingEvent
13
+ # @attr_reader observations - Array[Contrast::Agent::Reporting::LibraryUsageObservation]
14
+ # - Hash of LibraryUsageObservations
15
15
  attr_reader :observations
16
16
 
17
- class << self
18
- # Convert a Hash of LibraryUsageUpdate DTMs for SpeedRacer to an Event for TeamServer.
19
- #
20
- # @param usages_dtm_hash Hash[Contrast::Api::Dtm::LibraryUsageUpdate]
21
- # @return [Contrast::Agent::Reporting::ObservedLibraryUsage]
22
- def convert usages_dtm_hash
23
- report = new
24
- report.attach_data(usages_dtm_hash)
25
- report
26
- end
27
- end
28
-
29
17
  def initialize
30
18
  @event_endpoint = Contrast::Agent::Reporting::Endpoints.library_usage
31
19
  @observations = []
@@ -41,17 +29,13 @@ module Contrast
41
29
  { observations: @observations.map(&:to_controlled_hash) }
42
30
  end
43
31
 
44
- def attach_data usages_dtm_hash
45
- usages_dtm_hash.each do |_key, value|
46
- next unless value.class_names.any?
47
-
48
- @observations << Contrast::Agent::Reporting::LibraryUsageObservation.convert(value)
49
- end
50
- end
51
-
52
32
  def validate
53
33
  raise(ArgumentError, "#{ self } did not have observations. Unable to continue.") if observations.empty?
54
34
  end
35
+
36
+ def clear
37
+ @observations = []
38
+ end
55
39
  end
56
40
  end
57
41
  end
@@ -47,7 +47,7 @@ module Contrast
47
47
  def to_controlled_hash
48
48
  validate
49
49
  rc_hash = {
50
- session_id: @agent_session_id_value,
50
+ session_id: ::Contrast::ASSESS.session_id,
51
51
  sources: @sources.map(&:to_controlled_hash),
52
52
  signature: @signature,
53
53
  verb: @verb,
@@ -30,7 +30,6 @@ module Contrast
30
30
  @app_name = ::Contrast::APP_CONTEXT.app_name
31
31
  @app_version = ::Contrast::APP_CONTEXT.app_version
32
32
  @routes = []
33
- @agent_session_id_value = ::Contrast::ASSESS.session_id
34
33
  end
35
34
 
36
35
  # Convert the instance variables on the class, and other information, into the identifiers required for
@@ -48,7 +47,7 @@ module Contrast
48
47
  data: '',
49
48
  key: 0,
50
49
  routes: @routes,
51
- session_id: @agent_session_id_value
50
+ session_id: ::Contrast::ASSESS.session_id
52
51
  }
53
52
  end
54
53
 
@@ -60,7 +59,7 @@ module Contrast
60
59
  unless @app_language
61
60
  raise(ArgumentError, "#{ cs__class } did not have a proper application language. Unable to continue.")
62
61
  end
63
- unless @agent_session_id_value
62
+ unless ::Contrast::ASSESS.session_id
64
63
  raise(ArgumentError, "#{ cs__class } did not have a proper session id. Unable to continue.")
65
64
  end
66
65
 
@@ -20,9 +20,7 @@ module Contrast
20
20
  attr_reader :event_method
21
21
 
22
22
  def initialize
23
- @agent_session_id_value = ::Contrast::ASSESS.session_id
24
- @event_endpoint ||= nil
25
- @event_method ||= :POST
23
+ @event_method ||= :POST # rubocop:disable Lint/DisjunctiveAssignmentInConstructor
26
24
  end
27
25
 
28
26
  # Some reports require specific additional headers to be used. To that end, we'll attach them here, letting
@@ -32,6 +32,15 @@ module Contrast
32
32
  @count = 0
33
33
  end
34
34
 
35
+ # Parse the given controller and route from a Rack based application framework in order to create an instance
36
+ # of this class
37
+ #
38
+ # @param final_controller [Grape::API, Sinatra::Base] the controller responsible for the definition of the
39
+ # entrypoint of the route actively being executed
40
+ # @param method [String] the HTTP request method of the route actively being executed
41
+ # @param route_pattern [Grape::Router::Route, Mustermann::Sinatra] the pattern to which the url maps
42
+ # @param url [String] the literal url of the route actively being executed
43
+ # @return [Contrast::Agent::Reporting::RouteCoverage]
35
44
  def attach_rack_based_data final_controller, method, route_pattern, url = nil
36
45
  if route_pattern.cs__is_a?(Grape::Router::Route)
37
46
  safe_pattern = route_pattern.pattern&.path&.to_s
@@ -16,7 +16,7 @@ module Contrast
16
16
  attr_reader :path_for_requests, :path_for_responses
17
17
 
18
18
  def initialize
19
- generate_paths if enabled? && Contrast::CONTRAST_SERVICE.use_agent_communication?
19
+ generate_paths if enabled?
20
20
  end
21
21
 
22
22
  # This method will be handling the auditing of the requests and responses we send to SpeedRacer. If the audit
@@ -45,7 +45,6 @@ module Contrast
45
45
  # @param data[String] String representation if the logged data
46
46
  def log_data type, file_name, data = nil
47
47
  return unless enabled?
48
- return unless Contrast::CONTRAST_SERVICE.use_agent_communication?
49
48
 
50
49
  logger.debug('logging to file', file_name: file_name) # TODO: RUBY-99999 DO NOT COMMIT THIS
51
50
  write_to_file(type, file_name, data)
@@ -21,15 +21,6 @@ module Contrast
21
21
  dtm.cs__is_a?(Contrast::Api::Dtm::ServerActivity)
22
22
  end
23
23
 
24
- # Checks if the message is a Hash of Contrast::Api::Dtm::LibraryUsageUpdate class
25
- #
26
- # @param message [Protobuf::Field::FieldHash<String,::Contrast::Api::Dtm::LibraryUsageUpdate>]
27
- # @return [Boolean]
28
- def library_usage? message
29
- message.cs__is_a?(Protobuf::Field::FieldHash) &&
30
- message.values[0].cs__is_a?(Contrast::Api::Dtm::LibraryUsageUpdate)
31
- end
32
-
33
24
  # Checks if the message is of Contrast::Api::Dtm::ApplicationUpdate class
34
25
  #
35
26
  # @param dtm [Contrast::Api::Dtm::ApplicationUpdate,Object]
@@ -61,7 +52,6 @@ module Contrast
61
52
  return Contrast::Agent::Reporting::ServerActivity.new if server_activity?(dtm)
62
53
 
63
54
  # For the others, we convert them.
64
- return Contrast::Agent::Reporting::ObservedLibraryUsage.convert(dtm) if library_usage?(dtm)
65
55
  return Contrast::Agent::Reporting::ApplicationUpdate.convert(dtm) if application_update?(dtm)
66
56
  return Contrast::Agent::Reporting::Finding.convert(dtm) if finding?(dtm)
67
57
  return Contrast::Agent::Reporting::ApplicationActivity.convert(dtm) if activity?(dtm)
@@ -56,7 +56,6 @@ module Contrast
56
56
  # @param send_immediately [Boolean] flag for the logger
57
57
  # @return response [Net::HTTP::Response, nil] response from TS if no response
58
58
  def send_event event, connection, send_immediately: false
59
- return unless Contrast::Agent::Reporter.enabled?
60
59
  return unless connection
61
60
 
62
61
  log_send_event(event) if send_immediately
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'contrast/agent/reporting/settings/application_settings'
5
5
  require 'contrast/agent/reporting/settings/server_features'
6
+ require 'contrast/agent/reporting/settings/reaction'
6
7
 
7
8
  module Contrast
8
9
  module Agent
@@ -21,19 +22,76 @@ module Contrast
21
22
  # @return [Contrast::Agent::Reporting::Settings::FeatureSettings, nil]
22
23
  attr_accessor :server_features
23
24
 
25
+ # Success boolean message value
26
+ #
27
+ # @return [Boolean]
28
+ attr_accessor :success
29
+
30
+ # Message with reasons for success or fail.
31
+ #
32
+ # @return [Array<String>] Messages received from TS.
33
+ attr_accessor :messages
34
+
24
35
  class << self
25
- def application_response
36
+ # All of the settings from TeamServer that apply at the application level.
37
+ #
38
+ # @return response [Contrast::Agent::Reporting::Response]
39
+ def build_application_response
26
40
  res = new
27
41
  res.application_settings = Contrast::Agent::Reporting::Settings::ApplicationSettings.new
28
42
  res
29
43
  end
30
44
 
31
- def server_response
45
+ # All of the settings from TeamServer that apply at the server level.
46
+ #
47
+ # @return response [Contrast::Agent::Reporting::Response]
48
+ def build_server_response
32
49
  res = new
33
50
  res.server_features = Contrast::Agent::Reporting::Settings::ServerFeatures.new
34
51
  res
35
52
  end
36
53
  end
54
+
55
+ # Reaction the agent should take based on a state in TS.
56
+ # This is moved one level up because the responses we
57
+ # receive for feature and settings from TS have different
58
+ # place to store these reactions:
59
+ #
60
+ # body.reactions vs body.settings.reactions
61
+ #
62
+ # @return [Array<Contrast::Agent::Reporting::Settings::Reaction>]
63
+ def reactions
64
+ @_reactions ||= []
65
+ end
66
+
67
+ # Set the reaction
68
+ #
69
+ # @param reaction_array [Array<Reaction>] {
70
+ # level [String] The level at which the agent should log this reaction.
71
+ # [ERROR, WARN, INFO, DEBUG, TRACE]
72
+ # message [String] A message to log when receiving this reaction.
73
+ # operation [String] What to do in response to this reaction.[NOOP, DISABLE] }
74
+ # @return [Array<Contrast::Agent::Reporting::Settings::Reaction>]
75
+ def reactions= reaction_array
76
+ return unless reaction_array.is_a?(Array)
77
+
78
+ reaction_array.each do |r|
79
+ reactions << Contrast::Agent::Reporting::Settings::Reaction.new(r[:level], r[:operation], r[:message])
80
+ end
81
+ end
82
+
83
+ # This method is used only for testing with golden files.
84
+ def to_controlled_hash
85
+ {
86
+ success: success,
87
+ messages: messages,
88
+ features: server_features.nil? ? nil : server_features.to_controlled_hash,
89
+ settings: application_settings.nil? ? nil : application_settings.to_controlled_hash,
90
+ logLevel: server_features&.log_level,
91
+ logFile: server_features&.log_file,
92
+ reactions: server_features.nil? ? nil : reactions.map(&:to_controlled_hash)
93
+ }.compact
94
+ end
37
95
  end
38
96
  end
39
97
  end
@@ -24,7 +24,6 @@ module Contrast
24
24
  protect = response_data[:settings][:defend]
25
25
  return unless protect
26
26
 
27
- # TODO: RUBY-1636 should this be `:rules` or `:protectionRules`
28
27
  res.application_settings.protect.protection_rules = protect[:protectionRules]
29
28
  res.application_settings.protect.virtual_patches = protect[:virtualPatches]
30
29
  end
@@ -40,10 +39,14 @@ module Contrast
40
39
  res.application_settings.exclusions.url_exclusions = exclusions[:urlExceptions]
41
40
  end
42
41
 
42
+ # The responses we receive for feature and settings from TS have different
43
+ # place to store these reactions: body.reactions vs body.settings.reactions.
44
+ #
43
45
  # @param response_data [Hash]
44
46
  # @param res [Contrast::Agent::Reporting::Response]
45
47
  def extract_reactions response_data, res
46
- res.application_settings.reactions = response_data[:settings][:reactions]
48
+ res.reactions = response_data[:settings][:reactions] if response_data[:settings]
49
+ res.reactions = response_data[:reactions] if response_data[:features]
47
50
  end
48
51
 
49
52
  # @param response_data [Hash]
@@ -65,10 +68,17 @@ module Contrast
65
68
  return unless protect
66
69
 
67
70
  res.server_features.protect.enabled = protect[:enabled]
68
- res.server_features.protect.bot_blocker = protect[:'bot-blocker']
69
- # TODO: RUBY-1636 should this be `:rules` or `:protectionRules`
70
- # process the botBlockers field
71
- res.server_features.protect.syslog = protect[:syslog]
71
+ res.server_features.protect.bot_blocker.enable = protect[:'bot-blocker']
72
+ res.server_features.protect.bot_blocker.bots = protect[:botBlockers]
73
+ extract_syslog(response_data, res)
74
+ end
75
+
76
+ # @param response_data [Hash]
77
+ # @param res [Contrast::Agent::Reporting::Response]
78
+ def extract_syslog response_data, res
79
+ return unless (syslog = response_data[:features][:defend][:syslog])
80
+
81
+ res.server_features.protect.syslog.assign_array(syslog)
72
82
  end
73
83
 
74
84
  # @param response_data [Hash]
@@ -78,22 +88,34 @@ module Contrast
78
88
  return unless protect
79
89
 
80
90
  res.server_features.protect.ip_allowlist = protect[:ipAllowlist]
81
- res.server_features.protect.ip_denylist = protect[:ipDenyList]
82
- res.server_features.protect.log_enchancers = protect[:logEnhancers]
91
+ res.server_features.protect.ip_denylist = protect[:ipDenylist]
92
+ res.server_features.protect.log_enhancers = protect[:logEnhancers]
83
93
  res.server_features.protect.rule_definition_list = protect[:ruleDefinitionList]
84
94
  end
85
95
 
86
96
  # Here we extract the rules and state for the sensitive data masking policy
87
- # Received from TS.
97
+ # received from TS.
88
98
  #
89
99
  # @param response_data [Hash]
90
100
  # @param res [Contrast::Agent::Reporting::Response]
91
101
  def extract_sensitive_data_policy response_data, res
92
- sensitive_data = response_data[:settings][:sensitive_data_masking_policy]
102
+ return unless (sensitive_data = response_data[:settings][:sensitive_data_masking_policy])
103
+
93
104
  res.application_settings.sensitive_data_masking.mask_http_body = sensitive_data[:mask_http_body]
94
105
  res.application_settings.sensitive_data_masking.mask_attack_vector = sensitive_data[:mask_attack_vector]
95
106
  res.application_settings.sensitive_data_masking.build_rules_form_settings(sensitive_data[:rules])
96
107
  end
108
+
109
+ # Here we extract the log settings received from TS.
110
+ #
111
+ # @param response_data [Hash]
112
+ # @param res [Contrast::Agent::Reporting::Response]
113
+ def extract_log_settings response_data, res
114
+ return unless (log_level = response_data[:logLevel])
115
+
116
+ res.server_features.log_level = log_level
117
+ res.server_features.log_file = response_data[:logFile] if response_data[:logFile]
118
+ end
97
119
  end
98
120
  end
99
121
  end
@@ -90,7 +90,7 @@ module Contrast
90
90
  when ERROR_CODES[:too_many_requests]
91
91
  handle_response_errors(response, RETRY_AFTER_MSG, mode.resending)
92
92
  else
93
- logger.debug('Response Error code could not be processed')
93
+ logger.error('Response Error code could not be processed')
94
94
  end
95
95
  end
96
96