contrast-agent 6.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_regexp/cs__assess_regexp.c +15 -2
  3. data/ext/cs__assess_regexp/cs__assess_regexp.h +2 -0
  4. data/ext/cs__assess_string/cs__assess_string.c +8 -0
  5. data/ext/cs__assess_test/cs__assess_test.h +9 -0
  6. data/ext/cs__assess_test/cs__assess_tests.c +22 -0
  7. data/ext/cs__assess_test/extconf.rb +5 -0
  8. data/ext/cs__common/cs__common.c +101 -0
  9. data/ext/cs__common/cs__common.h +29 -5
  10. data/ext/cs__contrast_patch/cs__contrast_patch.c +1 -1
  11. data/ext/cs__tests/cs__tests.c +12 -0
  12. data/ext/cs__tests/cs__tests.h +3 -0
  13. data/ext/cs__tests/extconf.rb +5 -0
  14. data/lib/contrast/agent/assess/contrast_object.rb +16 -16
  15. data/lib/contrast/agent/assess/events/source_event.rb +17 -19
  16. data/lib/contrast/agent/assess/policy/policy_scanner.rb +2 -16
  17. data/lib/contrast/agent/assess/policy/propagator/split.rb +15 -19
  18. data/lib/contrast/agent/assess/policy/trigger_method.rb +3 -11
  19. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +7 -2
  20. data/lib/contrast/agent/assess/rule/response/base_rule.rb +11 -3
  21. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +60 -36
  22. data/lib/contrast/agent/at_exit_hook.rb +1 -1
  23. data/lib/contrast/agent/inventory/database_config.rb +10 -3
  24. data/lib/contrast/agent/middleware.rb +3 -3
  25. data/lib/contrast/agent/patching/policy/after_load_patch.rb +0 -2
  26. data/lib/contrast/agent/patching/policy/patch.rb +13 -12
  27. data/lib/contrast/agent/patching/policy/patcher.rb +1 -1
  28. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +6 -2
  29. data/lib/contrast/agent/reporting/masker/masker.rb +8 -11
  30. data/lib/contrast/agent/reporting/masker/masker_utils.rb +8 -4
  31. data/lib/contrast/agent/reporting/reporter.rb +11 -16
  32. data/lib/contrast/agent/reporting/reporter_heartbeat.rb +49 -0
  33. data/lib/contrast/agent/reporting/reporting_events/agent_startup.rb +6 -2
  34. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +53 -0
  35. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +48 -0
  36. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +64 -0
  37. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +70 -0
  38. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +57 -0
  39. data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +56 -0
  40. data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +5 -1
  41. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +58 -0
  42. data/lib/contrast/agent/reporting/reporting_events/application_reporting_event.rb +27 -0
  43. data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +20 -10
  44. data/lib/contrast/agent/reporting/reporting_events/application_update.rb +7 -12
  45. data/lib/contrast/agent/reporting/reporting_events/finding.rb +9 -3
  46. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +2 -4
  47. data/lib/contrast/agent/reporting/reporting_events/finding_event_object.rb +3 -3
  48. data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +6 -2
  49. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +7 -3
  50. data/lib/contrast/agent/reporting/reporting_events/poll.rb +6 -2
  51. data/lib/contrast/agent/reporting/reporting_events/preflight.rb +10 -8
  52. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +6 -10
  53. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +12 -20
  54. data/lib/contrast/agent/reporting/reporting_events/server_reporting_event.rb +27 -0
  55. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +17 -27
  56. data/lib/contrast/agent/reporting/reporting_utilities/build_preflight.rb +38 -0
  57. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +8 -0
  58. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +6 -0
  59. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +9 -4
  60. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +54 -67
  61. data/lib/contrast/agent/reporting/reporting_utilities/response.rb +17 -7
  62. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +8 -5
  63. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +10 -10
  64. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +32 -17
  65. data/lib/contrast/agent/reporting/settings/protect.rb +1 -1
  66. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +1 -1
  67. data/lib/contrast/agent/request.rb +3 -3
  68. data/lib/contrast/agent/request_context_extend.rb +1 -1
  69. data/lib/contrast/agent/request_handler.rb +3 -3
  70. data/lib/contrast/agent/response.rb +2 -0
  71. data/lib/contrast/agent/service_heartbeat.rb +6 -48
  72. data/lib/contrast/agent/static_analysis.rb +1 -1
  73. data/lib/contrast/agent/telemetry/base.rb +151 -0
  74. data/lib/contrast/agent/telemetry/events/event.rb +35 -0
  75. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +44 -36
  76. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_event.rb +29 -21
  77. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message.rb +91 -73
  78. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +62 -44
  79. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +50 -33
  80. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions.rb +20 -0
  81. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +32 -0
  82. data/lib/contrast/agent/telemetry/events/metric_event.rb +28 -0
  83. data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +123 -0
  84. data/lib/contrast/agent/thread_watcher.rb +52 -68
  85. data/lib/contrast/agent/version.rb +1 -1
  86. data/lib/contrast/agent/worker_thread.rb +8 -0
  87. data/lib/contrast/agent.rb +1 -3
  88. data/lib/contrast/api/communication/messaging_queue.rb +28 -11
  89. data/lib/contrast/api/communication/response_processor.rb +7 -10
  90. data/lib/contrast/api/communication/speedracer.rb +1 -1
  91. data/lib/contrast/api/decorators/activity.rb +33 -0
  92. data/lib/contrast/api/decorators/http_request.rb +1 -1
  93. data/lib/contrast/components/config.rb +13 -22
  94. data/lib/contrast/components/contrast_service.rb +9 -0
  95. data/lib/contrast/components/settings.rb +10 -0
  96. data/lib/contrast/config/agent_configuration.rb +21 -11
  97. data/lib/contrast/config/api_configuration.rb +12 -8
  98. data/lib/contrast/config/api_proxy_configuration.rb +7 -3
  99. data/lib/contrast/config/application_configuration.rb +15 -11
  100. data/lib/contrast/config/assess_configuration.rb +13 -9
  101. data/lib/contrast/config/assess_rules_configuration.rb +5 -1
  102. data/lib/contrast/config/base_configuration.rb +3 -35
  103. data/lib/contrast/config/certification_configuration.rb +9 -5
  104. data/lib/contrast/config/exception_configuration.rb +10 -7
  105. data/lib/contrast/config/heap_dump_configuration.rb +13 -9
  106. data/lib/contrast/config/inventory_configuration.rb +9 -6
  107. data/lib/contrast/config/logger_configuration.rb +9 -6
  108. data/lib/contrast/config/protect_configuration.rb +9 -6
  109. data/lib/contrast/config/protect_rule_configuration.rb +12 -8
  110. data/lib/contrast/config/protect_rules_configuration.rb +18 -17
  111. data/lib/contrast/config/request_audit_configuration.rb +10 -7
  112. data/lib/contrast/config/root_configuration.rb +28 -11
  113. data/lib/contrast/config/ruby_configuration.rb +14 -11
  114. data/lib/contrast/config/sampling_configuration.rb +11 -8
  115. data/lib/contrast/config/server_configuration.rb +13 -9
  116. data/lib/contrast/config/service_configuration.rb +14 -11
  117. data/lib/contrast/configuration.rb +19 -10
  118. data/lib/contrast/framework/rails/patch/support.rb +13 -45
  119. data/lib/contrast/logger/aliased_logging.rb +87 -0
  120. data/lib/contrast/logger/application.rb +0 -4
  121. data/lib/contrast/tasks/config.rb +22 -13
  122. data/lib/contrast/utils/class_util.rb +2 -6
  123. data/lib/contrast/utils/invalid_configuration_util.rb +1 -1
  124. data/lib/contrast/utils/log_utils.rb +2 -0
  125. data/lib/contrast/utils/middleware_utils.rb +1 -1
  126. data/lib/contrast/utils/object_share.rb +1 -1
  127. data/lib/contrast/utils/telemetry.rb +20 -2
  128. data/lib/contrast/utils/telemetry_client.rb +22 -10
  129. data/lib/contrast/utils/telemetry_hash.rb +41 -0
  130. data/lib/contrast/utils/telemetry_identifier.rb +16 -1
  131. data/lib/contrast.rb +9 -0
  132. data/ruby-agent.gemspec +1 -1
  133. data/service_executables/VERSION +1 -1
  134. data/service_executables/linux/contrast-service +0 -0
  135. data/service_executables/mac/contrast-service +0 -0
  136. metadata +39 -16
  137. data/lib/contrast/agent/telemetry/events/metric_telemetry_event.rb +0 -26
  138. data/lib/contrast/agent/telemetry/events/startup_metrics_telemetry_event.rb +0 -121
  139. data/lib/contrast/agent/telemetry/events/telemetry_event.rb +0 -33
  140. data/lib/contrast/agent/telemetry/telemetry.rb +0 -150
  141. data/lib/contrast/utils/exclude_key.rb +0 -20
@@ -5,46 +5,58 @@ require 'contrast/components/logger'
5
5
  require 'contrast/agent/service_heartbeat'
6
6
  require 'contrast/api/communication/messaging_queue'
7
7
  require 'contrast/agent/reporting/report'
8
- require 'contrast/agent/telemetry/telemetry'
8
+ require 'contrast/agent/reporting/reporter_heartbeat'
9
+ require 'contrast/agent/telemetry/base'
9
10
 
10
11
  module Contrast
11
12
  module Agent
12
13
  # This class used to ensure that our worker threads are running in multi-process environments
13
- #
14
- # @attr_reader heapdump_util [Contrast::Utils::HeapDumpUtil]
15
- # @attr_reader heartbeat [Contrast::Agent::ServiceHeartbeat]
16
- # @attr_reader messaging_queue [Contrast::Api::Communication::MessagingQueue]
17
14
  class ThreadWatcher
18
15
  include Contrast::Components::Logger::InstanceMethods
19
16
 
20
- attr_reader :heapdump_util, :heartbeat, :messaging_queue
17
+ # @return [Contrast::Utils::HeapDumpUtil]
18
+ attr_reader :heapdump_util
19
+ # @return [Contrast::Agent::ServiceHeartbeat, nil]
20
+ attr_reader :heartbeat
21
+ # @return [Contrast::Api::Communication::MessagingQueue, nil]
22
+ attr_reader :messaging_queue
23
+ # @return [Contrast::Agent::Reporter]
24
+ attr_reader :reporter
25
+ # @return [Contrast::Agent::ReporterHeartbeat]
26
+ attr_reader :reporter_heartbeat
21
27
 
22
28
  def initialize
23
29
  @pids = {}
24
30
  @heapdump_util = Contrast::Utils::HeapDumpUtil.new
25
- @heartbeat = Contrast::Agent::ServiceHeartbeat.new
26
- @messaging_queue = Contrast::Api::Communication::MessagingQueue.new
27
- @telemetry = Contrast::Agent::Telemetry.new if Contrast::Agent::Telemetry.enabled?
28
- @reporter = Contrast::Agent::Reporter.new if Contrast::Agent::Reporter.enabled?
31
+ unless ::Contrast::CONTRAST_SERVICE.unnecessary?
32
+ @heartbeat = Contrast::Agent::ServiceHeartbeat.new
33
+ @messaging_queue = Contrast::Api::Communication::MessagingQueue.new
34
+ end
35
+ if Contrast::Agent::Reporter.enabled?
36
+ @reporter = Contrast::Agent::Reporter.new
37
+ @reporter_heartbeat = Contrast::Agent::ReporterHeartbeat.new
38
+ end
39
+ @telemetry = Contrast::Agent::Telemetry::Base.new if Contrast::Agent::Telemetry::Base.enabled?
29
40
  end
30
41
 
42
+ # @return [Hash, nil] map of process to thread startup status
31
43
  def startup!
32
44
  return unless ::Contrast::AGENT.enabled?
33
45
 
34
- telemetry_status = telemetry_thread_init
35
- heartbeat_status = heartbeat_thread_init
36
- messaging_status = messaging_thread_init
37
- reporter_status = reporter_thread_init
38
-
39
46
  logger.debug('ThreadWatcher started threads')
40
-
47
+ heartbeat_status = init_thread(heartbeat)
48
+ messaging_status = init_thread(messaging_queue)
41
49
  @pids[Process.pid] = messaging_status && heartbeat_status
42
- return @pids unless Contrast::Agent::Telemetry.enabled?
43
-
44
- @pids[Process.pid] = @pids[Process.pid] && telemetry_status
50
+ if Contrast::Agent::Telemetry::Base.enabled?
51
+ telemetry_status = init_thread(telemetry_queue)
52
+ @pids[Process.pid] = @pids[Process.pid] && telemetry_status
53
+ end
45
54
  return @pids unless Contrast::Agent::Reporter.enabled?
46
55
 
47
- @pids[Process.pid] = @pids[Process.pid] && reporter_status
56
+ reporter_status = init_thread(reporter)
57
+ reporter_heartbeat_status = init_thread(reporter_heartbeat)
58
+ @pids[Process.pid] = @pids[Process.pid] && reporter_status && reporter_heartbeat_status
59
+ @pids
48
60
  end
49
61
 
50
62
  def ensure_running?
@@ -55,63 +67,35 @@ module Contrast
55
67
  end
56
68
 
57
69
  def shutdown!
58
- heartbeat.stop!
59
- messaging_queue.stop!
60
- heapdump_util.stop!
70
+ heartbeat&.stop!
71
+ messaging_queue&.stop!
72
+ heapdump_util&.stop!
61
73
  telemetry_queue&.stop!
62
74
  reporter&.stop!
75
+ reporter_heartbeat&.stop!
63
76
  end
64
77
 
65
- def heartbeat_thread_init
66
- unless heartbeat.running?
67
- logger.debug('Attempting to start heartbeat thread')
68
- heartbeat.start_thread!
69
- end
70
- heartbeat_result = heartbeat.running?
71
- logger.debug('Heartbeat thread status', alive: heartbeat_result)
72
- heartbeat_result
73
- end
74
-
75
- def telemetry_thread_init
76
- @telemetry.start_thread! if @telemetry&.attempt_to_start?
77
- telemetry_result = @telemetry&.running?
78
- logger.debug('Telemetry thread status', alive: telemetry_result)
79
- telemetry_result
80
- end
81
-
82
- def messaging_thread_init
83
- unless messaging_queue.running?
84
- logger.debug('Attempting to start messaging queue thread')
85
- messaging_queue.start_thread!
86
- end
87
- messaging_result = messaging_queue.running?
88
- logger.debug('Messaging thread status', alive: messaging_result)
89
- messaging_result
90
- end
91
-
92
- def reporter_thread_init
93
- @reporter.start_thread! if @reporter&.attempt_to_start?
94
- unless @reporter&.running?
95
- logger.debug('Attempting to start reporter thread')
96
- @reporter&.start_thread!
97
- end
98
- reporter_result = @reporter&.running?
99
- logger.debug('Reporter thread status', alive: reporter_result)
100
- reporter_result
101
- end
102
-
103
- # @return [Contrast::Agent::Telemetry]
78
+ # @return [Contrast::Agent::Telemetry::Base]
104
79
  def telemetry_queue
105
- return if @telemetry.nil?
106
-
107
80
  @telemetry
108
81
  end
109
82
 
110
- # @return [Contrast::Agent::Reporter]
111
- def reporter
112
- return if @reporter.nil?
83
+ private
113
84
 
114
- @reporter
85
+ # Start the thread governed by the given watcher
86
+ #
87
+ # @param watcher [Contrast::Agent::ThreadWatcher]
88
+ # @return [Boolean] if the watched thread started successfully
89
+ def init_thread watcher
90
+ return unless watcher&.attempt_to_start?
91
+
92
+ unless watcher.running?
93
+ logger.debug('Attempting to start thread', type: watcher.to_s)
94
+ watcher.start_thread!
95
+ end
96
+ result = watcher.running?
97
+ logger.debug('Thread status', type: watcher.to_s, alive: result)
98
+ result
115
99
  end
116
100
  end
117
101
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '6.0.0'
6
+ VERSION = '6.1.0'
7
7
  end
8
8
  end
@@ -15,10 +15,18 @@ module Contrast
15
15
 
16
16
  def stop!
17
17
  return unless running?
18
+ return unless @_thread
18
19
 
19
20
  Thread.kill(@_thread)
20
21
  @_thread = nil
21
22
  end
23
+
24
+ # Most threads, we always want to start. Some may be controlled by feature guards though, so we need to check.
25
+ #
26
+ # @return [Boolean]
27
+ def attempt_to_start?
28
+ true
29
+ end
22
30
  end
23
31
  end
24
32
  end
@@ -71,10 +71,8 @@ module Contrast
71
71
  thread_watcher.messaging_queue
72
72
  end
73
73
 
74
- # @return [nil, Contrast::Agent::Telemetry]
74
+ # @return [nil, Contrast::Agent::Telemetry::Base]
75
75
  def self.telemetry_queue
76
- return unless thread_watcher.telemetry_queue
77
-
78
76
  thread_watcher.telemetry_queue
79
77
  end
80
78
 
@@ -2,8 +2,10 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/components/logger'
5
+ require 'contrast/agent/assess/policy/trigger_method'
5
6
  require 'contrast/agent/worker_thread'
6
7
  require 'contrast/agent/reporting/reporting_utilities/audit'
8
+ require 'contrast/api/dtm.pb'
7
9
 
8
10
  module Contrast
9
11
  module Api
@@ -12,11 +14,13 @@ module Contrast
12
14
  class MessagingQueue < Contrast::Agent::WorkerThread
13
15
  include Contrast::Components::Logger::InstanceMethods
14
16
 
17
+ # @return [Contrast::Api::Communication::Speedracer, nil]
15
18
  attr_reader :speedracer
16
19
 
17
20
  def initialize
21
+ return if ::Contrast::CONTRAST_SERVICE.unnecessary?
22
+
18
23
  @speedracer = Contrast::Api::Communication::Speedracer.new
19
- @audit = Contrast::Agent::Reporting::Audit.new if ::Contrast::API.request_audit_enable?
20
24
  super
21
25
  end
22
26
 
@@ -33,18 +37,18 @@ module Contrast
33
37
  logger.warn('Attempted to send event immediately with Agent disabled', caller: caller, event: event)
34
38
  return
35
39
  end
36
- response_data = speedracer.return_response(event)
37
- return response_data unless ::Contrast::API.request_audit_enable?
40
+ return if ::Contrast::CONTRAST_SERVICE.unnecessary?
38
41
 
39
- @audit&.audit_event(event, response_data)
40
- response_data
42
+ speedracer.return_response(event)
41
43
  end
42
44
 
43
45
  # A simple Queue used to hold messages that are ready to be sent to SpeedRacer but for which we do not need an
44
46
  # immediate response
45
47
  #
46
- # @return [Queue]
48
+ # @return [Queue, nil]
47
49
  def queue
50
+ return if ::Contrast::CONTRAST_SERVICE.unnecessary?
51
+
48
52
  @_queue ||= Queue.new
49
53
  end
50
54
 
@@ -56,29 +60,42 @@ module Contrast
56
60
  #
57
61
  # @param event [Contrast::Api::Dtm] One of the DTMs valid for the event field of
58
62
  # Contrast::Api::Dtm::Message|Contrast::Api::Dtm::Activity
59
- def send_event_eventually event
63
+ # @param force [Boolean] if we should always queue this event, even if the service isn't running, in case the
64
+ # message may be ready before the service has initiated. usually for those events which happen in a separate
65
+ # thread or which may occur during initialization.
66
+ def send_event_eventually event, force: false
60
67
  if ::Contrast::AGENT.disabled?
61
68
  logger.warn('Attempted to queue event with Agent disabled', caller: caller, event: event)
62
69
  return
63
70
  end
71
+ return if ::Contrast::CONTRAST_SERVICE.unnecessary?
72
+ # If we're unable to start the Service, then we cannot afford to queue messages as this creates the potential
73
+ # for a memory leak, especially if the thread responsible for de-queueing the messages is dead.
74
+ return if !force && !(speedracer.status.connected? && running?)
75
+
76
+ # If we're in direct communication mode, the only message we should queue is the heartbeat to keep the
77
+ # service alive during Protect activities. All other messages should go through direct reporting.
78
+ if ::Contrast::CONTRAST_SERVICE.use_agent_communication?
79
+ return unless ::Contrast::PROTECT.enabled?
80
+ return unless event.cs__is_a?(Contrast::Api::Dtm::Poll)
81
+ end
82
+
64
83
  logger.debug('Enqueued event for sending', event_type: event.cs__class)
65
84
  queue << event if event
66
- return unless ::Contrast::API.request_audit_enable?
67
-
68
- @audit&.audit_event(event)
69
85
  end
70
86
 
71
87
  # Create the reporting thread, which will pull from the queue in order to send messages to SpeedRacer. If
72
88
  # SpeedRacer is not running and should be, meaning the Agent is configured to control it, then we will also
73
89
  # try to start that process.
74
90
  def start_thread!
91
+ return if ::Contrast::CONTRAST_SERVICE.unnecessary?
92
+
75
93
  speedracer.ensure_startup!
76
94
  return if running?
77
95
 
78
96
  @_thread = Contrast::Agent::Thread.new do
79
97
  loop do
80
98
  event = queue.pop
81
-
82
99
  begin
83
100
  logger.debug('Dequeued event for sending', event_type: event.cs__class)
84
101
  speedracer.process_internally(event)
@@ -8,12 +8,8 @@ module Contrast
8
8
  module Api
9
9
  module Communication
10
10
  # Handles processing deferred messages sent to SpeedRacer.
11
- #
12
- # @attr_reader last_app_update_ms [Integer] the time, in ms, that application settings last changed
13
- # @attr_reader last_server_update_ms [Integer] the time, in ms, that server settings last changed
14
11
  class ResponseProcessor
15
12
  include Contrast::Components::Logger::InstanceMethods
16
- attr_reader :last_app_update_ms, :last_server_update_ms
17
13
 
18
14
  # Use the given response to update the Agent's server features and application settings, allowing it to reflect
19
15
  # the latest options configured by the user in TeamServer
@@ -48,7 +44,6 @@ module Contrast
48
44
  logger.trace('Agent: Received updated server features')
49
45
 
50
46
  ::Contrast::SETTINGS.update_from_server_features(server_features)
51
- @last_server_update_ms = Contrast::Utils::Timer.now_ms
52
47
  server_features
53
48
  end
54
49
 
@@ -58,22 +53,24 @@ module Contrast
58
53
  # @param response [Contrast::Api::Settings::AgentSettings]
59
54
  # @return [Contrast::Api::Settings::ApplicationSettings]
60
55
  def process_application_response response
56
+ logger.debug('[!] process_application_response', response: response)
61
57
  app_settings = response&.application_settings
62
58
  return unless app_settings
63
59
 
64
- logger.debug('Agent: Received updated application settings')
65
-
60
+ logger.debug('[!] Agent: Received updated application settings', settings: app_settings)
66
61
  ::Contrast::SETTINGS.update_from_application_settings(app_settings)
67
- @last_app_update_ms = Contrast::Utils::Timer.now_ms
68
62
  app_settings
69
63
  end
70
64
 
71
65
  # This can't go in the Settings component because protect and assess depend on settings
72
66
  # I don't think it should go into contrast_service because that only handles connection specific data.
73
67
  #
74
- # @param server_features [Contrast::Api::Settings::AgentSettings, Array]
75
- # @param app_settings [Contrast::Api::Settings::ApplicationSettings, Array]
68
+ # @param server_features
69
+ # [Contrast::Api::Settings::AgentSettings, Contrast::Agent::Reporting::Settings::FeatureSettings]
70
+ # @param app_settings
71
+ # [Contrast::Api::Settings::ApplicationSettings, Contrast::Agent::Reporting::Settings::ApplicationSettings]
76
72
  def update_features server_features, app_settings
73
+ logger.info('Updating features')
77
74
  return unless !!(server_features || app_settings)
78
75
  return unless ::Contrast::AGENT.enabled?
79
76
 
@@ -42,7 +42,7 @@ module Contrast
42
42
  return
43
43
  end
44
44
  end
45
- unless status.startup_messages_sent? || Contrast::Agent::Reporter.enabled?
45
+ unless status.startup_messages_sent? || Contrast::CONTRAST_SERVICE.unnecessary?
46
46
  startup_responses = send_initialization_messages
47
47
  return false unless startup_responses
48
48
 
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/string_utils'
5
+
6
+ module Contrast
7
+ module Api
8
+ module Decorators
9
+ # Used to decorate the {Contrast::Api::Dtm::Activity} protobuf model
10
+ # to handle conversion between framework route classes and the dtm.
11
+ module Activity
12
+ def self.included klass
13
+ klass.extend(ClassMethods)
14
+ end
15
+
16
+ # Class methods for Activity
17
+ module ClassMethods
18
+ def source_or_string obj
19
+ if obj.cs__is_a?(Regexp)
20
+ obj.source
21
+ elsif obj.cs__respond_to?(:safe_string)
22
+ obj.safe_string
23
+ else
24
+ obj.to_s
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Contrast::Api::Dtm::Activity.include(Contrast::Api::Decorators::Activity)
@@ -94,7 +94,7 @@ module Contrast
94
94
  return true if ::Contrast::AGENT.omit_body?
95
95
  return false if request.document_type != :NORMAL
96
96
 
97
- request.content_type&.include?('multipart/form-data')
97
+ request.media_type&.include?('multipart/form-data')
98
98
  end
99
99
 
100
100
  def append_pair map, key, value
@@ -72,6 +72,19 @@ module Contrast
72
72
  @config.loggable
73
73
  end
74
74
 
75
+ # Typically, this would be accessed through Contrast::SETTINGS, but we're specifically checking for the user
76
+ # provided value here rather than that echoed back by TeamServer.
77
+ #
78
+ # @return [String,nil] the value of the session id set in the configuration, or nil if unset
79
+ def session_id
80
+ root.application.session_id
81
+ end
82
+
83
+ # @return [String,nil] the value of the session metadata set in the configuration, or nil if unset
84
+ def session_metadata
85
+ root.application.session_metadata
86
+ end
87
+
75
88
  private
76
89
 
77
90
  SESSION_VARIABLES = 'Invalid configuration. '\
@@ -127,28 +140,6 @@ module Contrast
127
140
  end
128
141
  end
129
142
 
130
- # Typically, this would be accessed through
131
- # Contrast::Components::AppContext, but we're too early in the
132
- # initialization of the Agent to use that mechanism, so we look it up
133
- # directly for ourselves
134
- #
135
- # @return [String,nil] the value of the session id set in the
136
- # configuration, or nil if unset
137
- def session_id
138
- root.application.session_id
139
- end
140
-
141
- # Typically, this would be accessed through
142
- # Contrast::Components::AppContext, but we're too early in the
143
- # initialization of the Agent to use that mechanism, so we look it up
144
- # directly for ourselves
145
- #
146
- # @return [String,nil] the value of the session metadata set in the
147
- # configuration, or nil if unset
148
- def session_metadata
149
- root.application.session_metadata
150
- end
151
-
152
143
  # Typically, the following values would be accessed through Contrast::Components::AppContext
153
144
  # and Contrast::Components::API, but we're too early in the initialization of the Agent to use
154
145
  # that mechanism, so we look it up directly for ourselves.
@@ -38,6 +38,15 @@ module Contrast
38
38
  @_use_agent_communication = true?(::Contrast::CONFIG.root.agent.service.bypass)
39
39
  end
40
40
 
41
+ # If we're using the agent directly and not using protect, then there is no need to start the service. Because
42
+ # we only know this at startup when hardcoded as such (b/c TS could turn protect on otherwise), we can only do
43
+ # so when bypass is on and protect is off in local config
44
+ #
45
+ # @return [Boolean]
46
+ def unnecessary?
47
+ ::Contrast::CONTRAST_SERVICE.use_agent_communication? && ::Contrast::PROTECT.forcibly_disabled?
48
+ end
49
+
41
50
  def host
42
51
  @_host ||=
43
52
  (::Contrast::CONFIG.root.agent.service.host || Contrast::Config::ServiceConfiguration::DEFAULT_HOST).to_s
@@ -86,6 +86,10 @@ module Contrast
86
86
  # Rules to follow when using the masking. Each rules contains Id [String]
87
87
  # and Keywords [Array<String>].
88
88
  attr_reader :sensitive_data_masking
89
+ # @return [Integer] the time, in ms, that application settings last changed
90
+ attr_reader :last_app_update_ms
91
+ # @return [Integer] the time, in ms, that server settings last changed
92
+ attr_reader :last_server_update_ms
89
93
 
90
94
  def initialize
91
95
  reset_state
@@ -99,6 +103,8 @@ module Contrast
99
103
  def update_from_server_features features
100
104
  if features.cs__is_a?(Contrast::Agent::Reporting::Response)
101
105
  server_features = features.server_features
106
+ return unless server_features
107
+
102
108
  log_file = server_features.log_file
103
109
  log_level = server_features.log_level
104
110
  Contrast::Logger::Log.instance.update(log_file, log_level) if log_file || log_level
@@ -110,12 +116,15 @@ module Contrast
110
116
  @assess_state.enabled = features.assess_enabled?
111
117
  @assess_state.sampling_settings = features.assess.sampling
112
118
  end
119
+ @last_server_update_ms = Contrast::Utils::Timer.now_ms
113
120
  end
114
121
 
115
122
  # @param features [Contrast::Api::Settings::ApplicationSettings, Contrast::Agent::Reporting::Response]
116
123
  def update_from_application_settings features
117
124
  if features&.class == Contrast::Agent::Reporting::Response
118
125
  settings = features.application_settings
126
+ return unless settings
127
+
119
128
  @application_state.modes_by_id = settings.protect.protection_rules_to_settings_hash
120
129
  # TODO: RUBY-1438 this needs to be translated
121
130
  # @application_state.exclusion_matchers = new_vals[:exclusion_matchers]
@@ -128,6 +137,7 @@ module Contrast
128
137
  @application_state.exclusion_matchers = new_vals[:exclusion_matchers]
129
138
  @assess_state.disabled_assess_rules = new_vals[:disabled_assess_rules]
130
139
  end
140
+ @last_app_update_ms = Contrast::Utils::Timer.now_ms
131
141
  end
132
142
 
133
143
  # Wipe state to zero.
@@ -9,23 +9,33 @@ require 'contrast/config/api_configuration'
9
9
 
10
10
  module Contrast
11
11
  module Config
12
- # Common Configuration settings. Those in this section pertain to the
13
- # core functionality of the Agent.
14
- class AgentConfiguration < BaseConfiguration
12
+ # Common Configuration settings. Those in this section pertain to the core functionality of the Agent.
13
+ class AgentConfiguration
14
+ include Contrast::Config::BaseConfiguration
15
+
15
16
  # @return [Boolean, nil]
16
17
  attr_accessor :enable
17
18
  # @return [Boolean, nil]
18
19
  attr_accessor :omit_body
19
- attr_writer :ruby, :service, :logger, :heap_dump
20
+ # @return [Contrast::Config::RubyConfiguration]
21
+ attr_writer :ruby
22
+ # @return [Contrast::Config::ServiceConfiguration]
23
+ attr_writer :service
24
+ # @return [ Contrast::Config::LoggerConfiguration]
25
+ attr_writer :logger
26
+ # @return [Contrast::Config::HeapDumpConfiguration]
27
+ attr_writer :heap_dump
20
28
 
21
29
  def initialize hsh = {}
22
- @enable = traverse_config(hsh, :enable)
23
- @start_bundled_service = traverse_config(hsh, :start_bundled_service)
24
- @omit_body = traverse_config(hsh, :omit_body)
25
- @service = Contrast::Config::ServiceConfiguration.new(traverse_config(hsh, :service))
26
- @logger = Contrast::Config::LoggerConfiguration.new(traverse_config(hsh, :logger))
27
- @ruby = Contrast::Config::RubyConfiguration.new(traverse_config(hsh, :ruby))
28
- @heap_dump = Contrast::Config::HeapDumpConfiguration.new(traverse_config(hsh, :heap_dump))
30
+ return unless hsh
31
+
32
+ @enable = hsh[:enable]
33
+ @start_bundled_service = hsh[:start_bundled_service]
34
+ @omit_body = hsh[:omit_body]
35
+ @service = Contrast::Config::ServiceConfiguration.new(hsh[:service])
36
+ @logger = Contrast::Config::LoggerConfiguration.new(hsh[:logger])
37
+ @ruby = Contrast::Config::RubyConfiguration.new(hsh[:ruby])
38
+ @heap_dump = Contrast::Config::HeapDumpConfiguration.new(hsh[:heap_dump])
29
39
  end
30
40
 
31
41
  # @return [Boolean, true]
@@ -8,7 +8,9 @@ require 'contrast/config/request_audit_configuration'
8
8
  module Contrast
9
9
  module Config
10
10
  # Api keys configuration
11
- class ApiConfiguration < BaseConfiguration
11
+ class ApiConfiguration
12
+ include Contrast::Config::BaseConfiguration
13
+
12
14
  # @return [String]
13
15
  attr_accessor :api_key
14
16
  # @return [String]
@@ -20,13 +22,15 @@ module Contrast
20
22
  DEFAULT_URL = 'https://app.contrastsecurity.com/Contrast'
21
23
 
22
24
  def initialize hsh = {}
23
- @api_key = traverse_config(hsh, :api_key)
24
- @url = traverse_config(hsh, :url)
25
- @user_name = traverse_config(hsh, :user_name)
26
- @service_key = traverse_config(hsh, :service_key)
27
- @proxy = Contrast::Config::ApiProxyConfiguration.new(traverse_config(hsh, :proxy))
28
- @request_audit = Contrast::Config::RequestAuditConfiguration.new(traverse_config(hsh, :request_audit))
29
- @certificate = Contrast::Config::CertificationConfiguration.new(traverse_config(hsh, :certificate))
25
+ return unless hsh
26
+
27
+ @api_key = hsh[:api_key]
28
+ @url = hsh[:url]
29
+ @user_name = hsh[:user_name]
30
+ @service_key = hsh[:service_key]
31
+ @proxy = Contrast::Config::ApiProxyConfiguration.new(hsh[:proxy])
32
+ @request_audit = Contrast::Config::RequestAuditConfiguration.new(hsh[:request_audit])
33
+ @certificate = Contrast::Config::CertificationConfiguration.new(hsh[:certificate])
30
34
  end
31
35
 
32
36
  def url
@@ -4,14 +4,18 @@
4
4
  module Contrast
5
5
  module Config
6
6
  # Api Proxy keys configuration
7
- class ApiProxyConfiguration < BaseConfiguration
7
+ class ApiProxyConfiguration
8
+ include Contrast::Config::BaseConfiguration
9
+
8
10
  # @return [String] proxy url
9
11
  attr_accessor :url
10
12
  attr_writer :enable
11
13
 
12
14
  def initialize hsh = {}
13
- @enable = traverse_config(hsh, :enable)
14
- @url = traverse_config(hsh, :url)
15
+ return unless hsh
16
+
17
+ @enable = hsh[:enable]
18
+ @url = hsh[:url]
15
19
  end
16
20
 
17
21
  # @return [Boolean, false]
@@ -7,7 +7,9 @@ module Contrast
7
7
  module Config
8
8
  # Common Configuration settings. Those in this section pertain to the
9
9
  # application identification functionality of the Agent.
10
- class ApplicationConfiguration < BaseConfiguration
10
+ class ApplicationConfiguration
11
+ include Contrast::Config::BaseConfiguration
12
+
11
13
  # @return [String]
12
14
  attr_accessor :name
13
15
  # @return [String]
@@ -27,16 +29,18 @@ module Contrast
27
29
  attr_writer :session_id, :session_metadata
28
30
 
29
31
  def initialize hsh = {}
30
- @name = traverse_config(hsh, :name)
31
- @version = traverse_config(hsh, :version)
32
- @language = traverse_config(hsh, :language)
33
- @path = traverse_config(hsh, :path)
34
- @group = traverse_config(hsh, :group)
35
- @tags = traverse_config(hsh, :tags)
36
- @code = traverse_config(hsh, :code)
37
- @metadata = traverse_config(hsh, :metadata)
38
- @session_id = traverse_config(hsh, :session_id)
39
- @session_metadata = traverse_config(hsh, :session_metadata)
32
+ return unless hsh
33
+
34
+ @name = hsh[:name]
35
+ @version = hsh[:version]
36
+ @language = hsh[:language]
37
+ @path = hsh[:path]
38
+ @group = hsh[:group]
39
+ @tags = hsh[:tags]
40
+ @code = hsh[:code]
41
+ @metadata = hsh[:metadata]
42
+ @session_id = hsh[:session_id]
43
+ @session_metadata = hsh[:session_metadata]
40
44
  end
41
45
 
42
46
  # @return [String, Contrast::Utils::ObjectShare::EMPTY_STRING]