contrast-agent 4.10.0 → 4.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_module/cs__assess_module.c +48 -0
  3. data/ext/cs__assess_module/cs__assess_module.h +7 -0
  4. data/ext/cs__common/cs__common.c +24 -7
  5. data/ext/cs__common/cs__common.h +12 -2
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +48 -11
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -2
  8. data/ext/cs__os_information/cs__os_information.c +31 -0
  9. data/ext/cs__os_information/cs__os_information.h +7 -0
  10. data/ext/{cs__protect_kernel → cs__os_information}/extconf.rb +0 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +1 -1
  12. data/lib/contrast/agent/assess/contrast_object.rb +1 -4
  13. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
  14. data/lib/contrast/agent/assess/policy/preshift.rb +25 -11
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
  16. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  17. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -0
  18. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +4 -4
  19. data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
  20. data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
  21. data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -107
  22. data/lib/contrast/agent/assess/policy/trigger_node.rb +52 -19
  23. data/lib/contrast/agent/assess/property/tagged.rb +15 -132
  24. data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
  25. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +2 -1
  26. data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
  27. data/lib/contrast/agent/middleware.rb +22 -0
  28. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +0 -1
  29. data/lib/contrast/agent/patching/policy/method_policy.rb +54 -9
  30. data/lib/contrast/agent/patching/policy/patch.rb +37 -238
  31. data/lib/contrast/agent/patching/policy/patcher.rb +3 -42
  32. data/lib/contrast/agent/request.rb +5 -3
  33. data/lib/contrast/agent/request_context.rb +32 -11
  34. data/lib/contrast/agent/request_handler.rb +7 -3
  35. data/lib/contrast/agent/rule_set.rb +2 -4
  36. data/lib/contrast/agent/scope.rb +32 -20
  37. data/lib/contrast/agent/startup_metrics_telemetry_event.rb +71 -0
  38. data/lib/contrast/agent/static_analysis.rb +4 -2
  39. data/lib/contrast/agent/telemetry.rb +129 -0
  40. data/lib/contrast/agent/telemetry_event.rb +34 -0
  41. data/lib/contrast/agent/thread_watcher.rb +43 -14
  42. data/lib/contrast/agent/tracepoint_hook.rb +11 -3
  43. data/lib/contrast/agent/version.rb +1 -1
  44. data/lib/contrast/agent.rb +6 -1
  45. data/lib/contrast/components/api.rb +34 -0
  46. data/lib/contrast/components/app_context.rb +24 -0
  47. data/lib/contrast/components/assess.rb +7 -0
  48. data/lib/contrast/components/config.rb +90 -11
  49. data/lib/contrast/components/contrast_service.rb +6 -0
  50. data/lib/contrast/config/api_configuration.rb +22 -0
  51. data/lib/contrast/config/assess_configuration.rb +1 -0
  52. data/lib/contrast/config/env_variables.rb +25 -0
  53. data/lib/contrast/config/root_configuration.rb +1 -0
  54. data/lib/contrast/config/service_configuration.rb +2 -1
  55. data/lib/contrast/config.rb +1 -0
  56. data/lib/contrast/configuration.rb +3 -0
  57. data/lib/contrast/framework/manager.rb +14 -12
  58. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
  59. data/lib/contrast/framework/rails/patch/support.rb +31 -29
  60. data/lib/contrast/logger/application.rb +4 -0
  61. data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
  62. data/lib/contrast/utils/assess/property/tagged_utils.rb +142 -0
  63. data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
  64. data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
  65. data/lib/contrast/utils/class_util.rb +58 -44
  66. data/lib/contrast/utils/exclude_key.rb +20 -0
  67. data/lib/contrast/utils/io_util.rb +42 -34
  68. data/lib/contrast/utils/lru_cache.rb +45 -0
  69. data/lib/contrast/utils/metrics_hash.rb +59 -0
  70. data/lib/contrast/utils/os.rb +23 -0
  71. data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
  72. data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
  73. data/lib/contrast/utils/requests_client.rb +150 -0
  74. data/lib/contrast/utils/ruby_ast_rewriter.rb +1 -1
  75. data/lib/contrast/utils/telemetry.rb +77 -0
  76. data/lib/contrast/utils/telemetry_identifier.rb +137 -0
  77. data/lib/contrast.rb +19 -1
  78. data/resources/assess/policy.json +12 -6
  79. data/resources/deadzone/policy.json +86 -5
  80. data/ruby-agent.gemspec +2 -1
  81. data/service_executables/VERSION +1 -1
  82. data/service_executables/linux/contrast-service +0 -0
  83. data/service_executables/mac/contrast-service +0 -0
  84. metadata +32 -14
  85. data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
  86. data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
  87. data/lib/contrast/extension/protect/kernel.rb +0 -29
@@ -6,19 +6,23 @@ require 'contrast/components/scope'
6
6
 
7
7
  module Contrast
8
8
  module Agent
9
- # This class is instantiated when we receive a request and the agent is enabled to process
10
- # that request. It holds the ruleset that we perform filtering operations on (currently
11
- # prefilter and postfilter).
9
+ # This class is instantiated when we receive a request and the agent is enabled to process that request. It holds
10
+ # the ruleset that we perform filtering operations on (currently prefilter and postfilter).
12
11
  class RequestHandler
13
12
  include Contrast::Components::Logger::InstanceMethods
14
13
 
15
14
  attr_reader :ruleset, :context
16
15
 
16
+ # @param context [Contrast::Agent::RequestContext] the context of the request for which this handler applies
17
17
  def initialize context
18
18
  @context = context
19
19
  @ruleset = ::Contrast::AGENT.ruleset
20
20
  end
21
21
 
22
+ # TODO: RUBY-1353
23
+ # TODO: RUBY-1355
24
+ # TODO: RUBY-1357
25
+ # TODO: RUBY-1358
22
26
  def send_activity_messages
23
27
  Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.generate_library_usage(context.activity)
24
28
  [context.server_activity, context.activity, context.observed_route].each do |message|
@@ -16,8 +16,7 @@ module Contrast
16
16
  # terminate requests on attack detection if set to block at perimeter
17
17
  def prefilter
18
18
  context = Contrast::Agent::REQUEST_TRACKER.current
19
- # TODO: RUBY-801 We shouldn't be responsible for knowing what modes are enabled
20
- return unless context&.analyze_request? || ::Contrast::PROTECT.enabled?
19
+ return unless context&.analyze_request?
21
20
 
22
21
  logger.trace_with_time('Running prefilter...') do
23
22
  map { |rule| rule.prefilter(context) }
@@ -33,8 +32,7 @@ module Contrast
33
32
  # has been created. The main actions here are analyzing the response for unsafe state or actions.
34
33
  def postfilter
35
34
  context = Contrast::Agent::REQUEST_TRACKER.current
36
- # TODO: RUBY-801 We shouldn't be responsible for knowing what modes are enabled
37
- return unless context&.analyze_response? || ::Contrast::PROTECT.enabled?
35
+ return unless context&.analyze_response?
38
36
 
39
37
  logger.trace_with_time('Running postfilter...') do
40
38
  map { |rule| rule.postfilter(context) }
@@ -104,39 +104,51 @@ module Contrast
104
104
  exit_split_scope!
105
105
  end
106
106
 
107
- # Dynamic versions of the above.
108
- # These are equivalent, but they're slower and riskier.
109
- # Prefer the static methods if you know what scope you need at the call site.
107
+ # Static methods to be used, the cases are defined by the usage from the above methods
108
+ # if more methods are added - please extend the case statements as they are no longed dynamic
110
109
  def in_scope? name
111
- cs__class.ensure_valid_scope! name
112
- call = with_contrast_scope { :"in_#{ name }_scope?" }
113
- send(call)
110
+ case name
111
+ when :contrast
112
+ in_contrast_scope?
113
+ when :deserialization
114
+ in_deserialization_scope?
115
+ when :split
116
+ in_split_scope?
117
+ else
118
+ raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a scope."
119
+ end
114
120
  end
115
121
 
116
122
  def enter_scope! name
117
- cs__class.ensure_valid_scope! name
118
- call = with_contrast_scope { :"enter_#{ name }_scope!" }
119
- send(call)
123
+ case name
124
+ when :contrast
125
+ enter_contrast_scope!
126
+ when :deserialization
127
+ enter_deserialization_scope!
128
+ when :split
129
+ enter_split_scope!
130
+ else
131
+ raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a scope."
132
+ end
120
133
  end
121
134
 
122
135
  def exit_scope! name
123
- cs__class.ensure_valid_scope! name
124
- call = with_contrast_scope { :"exit_#{ name }_scope!" }
125
- send(call)
136
+ case name
137
+ when :contrast
138
+ exit_contrast_scope!
139
+ when :deserialization
140
+ exit_deserialization_scope!
141
+ when :split
142
+ exit_split_scope!
143
+ else
144
+ raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a scope."
145
+ end
126
146
  end
127
147
 
128
148
  class << self
129
149
  def valid_scope? scope_sym
130
150
  Contrast::Agent::Scope::SCOPE_LIST.include? scope_sym
131
151
  end
132
-
133
- def ensure_valid_scope! scope_sym
134
- unless valid_scope? scope_sym # rubocop:disable Style/GuardClause
135
- with_contrast_scope do
136
- raise NoMethodError, "Scope '#{ scope_sym.inspect }' is not registered as a scope."
137
- end
138
- end
139
- end
140
152
  end
141
153
  end
142
154
  end
@@ -0,0 +1,71 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/metrics_hash'
5
+ require 'contrast/agent/metric_telemetry_event'
6
+ require 'contrast/agent/version'
7
+ require 'contrast/utils/os'
8
+
9
+ module Contrast
10
+ module Agent
11
+ # This class will hold the Startup Metrics Telemetry Event
12
+ # The class will include initialization of the agent version, language version
13
+ # os type, arch and version
14
+ # application framework and version and server framework
15
+ # It will be initialized and send in Middleware#agent_startup_routine
16
+ class StartupMetricsTelemetryEvent < Contrast::Agent::MetricTelemetryEvent
17
+ include Contrast::Utils::OS
18
+
19
+ APP_AND_SERVER_DATA = ::Contrast::APP_CONTEXT.app_and_server_information.cs__freeze
20
+ SAAS_DEFAULT = { addr: 'app.contrastsecuirty.com', type: 'SAAS_DEFAULT' }.cs__freeze
21
+ SAAS_CE = { addr: 'ce.contrastsecurity.com', type: 'SAAS_CE' }.cs__freeze
22
+ SAAS_CUSTOM = { addr: 'contrastsecurite.com', type: 'SAAS_CUSTOM' }.cs__freeze
23
+ SAAS_POV = { addr: 'eval.contrastsecuirty.com', type: 'SAAS_POV' }.cs__freeze
24
+ EOP = 'EOP'
25
+
26
+ def initialize
27
+ super
28
+ add_tags
29
+ end
30
+
31
+ def path
32
+ '/startup'
33
+ end
34
+
35
+ def add_tags
36
+ @tags['teamserver'] = teamserver_type
37
+ @tags['agent_version'] = VERSION
38
+ @tags['ruby_version'] = RUBY_VERSION
39
+ @tags['os_type'] = sys_info['os_type'] == 'Darwin' ? 'MacOS' : 'Linux'
40
+ @tags['os_arch'] = sys_info['os_arch']
41
+ @tags['os_version'] = sys_info['os_version']
42
+ @tags['app_framework_and_version'] = APP_AND_SERVER_DATA[:application_info].to_s
43
+ @tags['server_framework_and_version'] = APP_AND_SERVER_DATA[:server_info].to_s
44
+ end
45
+
46
+ def sys_info
47
+ @sys_info ||= get_system_information if @sys_info.nil?
48
+ @sys_info
49
+ end
50
+
51
+ private
52
+
53
+ # Here we extract the Teamserver url type
54
+ #
55
+ # @return[String] type, it could be SAAS_DEFAULT, SAAS_POV, SAAS_CE, SAAS_CUSTOM, or EOP
56
+ def teamserver_type
57
+ @_teamserver_type ||= if Contrast::API.api_url.include?(SAAS_DEFAULT[:addr])
58
+ SAAS_DEFAULT[:type]
59
+ elsif Contrast::API.api_url.include?(SAAS_POV[:addr])
60
+ SAAS_POV[:type]
61
+ elsif Contrast::API.api_url.include?(SAAS_CE[:addr])
62
+ SAAS_CE[:type]
63
+ elsif Contrast::API.api_url.end_with? SAAS_CUSTOM[:addr]
64
+ SAAS_CUSTOM[:type]
65
+ else
66
+ EOP
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -14,8 +14,8 @@ module Contrast
14
14
  include Contrast::Components::Scope::InstanceMethods
15
15
 
16
16
  class << self
17
- # After the first request is complete, we do a one-time manual catchup to review and
18
- # report the already-loaded gems.
17
+ # After the first request is complete, we do a one-time manual catchup to review and report the already-loaded
18
+ # gems.
19
19
  def catchup
20
20
  @_catchup ||= begin
21
21
  threaded_analysis!
@@ -23,6 +23,8 @@ module Contrast
23
23
  end
24
24
  end
25
25
 
26
+ # TODO: RUBY-1354
27
+ # TODO: RUBY-1356
26
28
  def send_inventory_message
27
29
  return unless ::Contrast::INVENTORY.enabled?
28
30
 
@@ -0,0 +1,129 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/config/env_variables'
5
+ require 'contrast/components/logger'
6
+ require 'contrast/utils/requests_client'
7
+ require 'contrast/agent/worker_thread'
8
+ require 'contrast/utils/telemetry'
9
+
10
+ module Contrast
11
+ module Agent
12
+ # This class will initialize and hold everything needed for the telemetry
13
+ class Telemetry < WorkerThread
14
+ include Contrast::Utils::RequestsClient
15
+ include Contrast::Components::Logger::InstanceMethods
16
+ # this is where we will send the data from the agents
17
+ URL = 'https://telemetry.ruby.contrastsecurity.com/'
18
+ # Suggested timeout after each send is to be 3 hours (10800 seconds)
19
+ SUGGESTED_TIMEOUT = 10_800
20
+
21
+ class << self
22
+ include Contrast::Components::Logger::InstanceMethods
23
+ include Contrast::Config::EnvVariables
24
+
25
+ def application_id
26
+ @_application_id ||= begin
27
+ id = nil
28
+ mac = Contrast::Utils::Telemetry::Identifier.mac
29
+ app_name = Contrast::Utils::Telemetry::Identifier.app_name
30
+ id = mac + app_name if mac && app_name
31
+ Digest::SHA2.new(256).hexdigest(id || '_' + SecureRandom.uuid)
32
+ end
33
+ end
34
+
35
+ def instance_id
36
+ @_instance_id ||= Digest::SHA2.new(256).hexdigest(Contrast::Utils::Telemetry::Identifier.mac ||
37
+ '_' + SecureRandom.uuid)
38
+ end
39
+
40
+ def enabled?
41
+ @_enabled = telemetry_enabled? if @_enabled.nil?
42
+ @_enabled
43
+ end
44
+
45
+ private
46
+
47
+ def telemetry_enabled?
48
+ opt_out_telemetry = return_value(:telemetry_opt_outs)
49
+ return false if opt_out_telemetry.to_s.casecmp('true').zero?
50
+ return false if opt_out_telemetry.to_s.casecmp('1').zero?
51
+
52
+ # In case of connection error, do not create the background thread or queue,
53
+ # as if the opt-out env var was set
54
+ ip_opt_out_telemetry = Contrast::Utils::RequestsClient.initialize_connection(URL)
55
+ if ip_opt_out_telemetry.nil?
56
+ logger.warn('Connection was not established properly!!!')
57
+ logger.warn('THE SERVICE IS GOING TO BE TERMINATED!!')
58
+ return false
59
+ end
60
+
61
+ true
62
+ end
63
+ end
64
+
65
+ def attempt_to_start?
66
+ unless cs__class.enabled?
67
+ logger.warn('Telemetry service is disabled!')
68
+ return false
69
+ end
70
+
71
+ logger.debug('Attempting to start telemetry thread') unless running?
72
+ true
73
+ end
74
+
75
+ def start_thread!
76
+ return if running?
77
+
78
+ # It is recommended that implementations send a single payload of
79
+ # general metrics every 3 hours, starting from implementation startup.
80
+ @_thread = Contrast::Agent::Thread.new do
81
+ logger.debug('Starting background telemetry thread.')
82
+ event = queue.pop
83
+
84
+ begin
85
+ logger.debug('This is the current processed event', event)
86
+ res = Contrast::Utils::RequestsClient.send_request event, connection
87
+ sleep_time = Contrast::Utils::RequestsClient.handle_response res
88
+ sleep(sleep_time) unless sleep_time.nil?
89
+ rescue StandardError => e
90
+ logger.error('Could not send message to service from telemetry queue.', e)
91
+ end
92
+ sleep(SUGGESTED_TIMEOUT)
93
+ end
94
+ end
95
+
96
+ def send_event event
97
+ if ::Contrast::AGENT.disabled?
98
+ logger.warn('Attempted to queue event with Agent disabled', caller: caller, event: event)
99
+ return
100
+ end
101
+
102
+ return unless cs__class.enabled?
103
+
104
+ logger.debug('Enqueued event for sending', event_type: event.cs__class)
105
+
106
+ queue << event if event
107
+ end
108
+
109
+ def delete_queue!
110
+ @_queue&.clear
111
+ @_queue&.close
112
+ @_queue = nil
113
+ end
114
+
115
+ def stop!
116
+ return unless running?
117
+
118
+ super
119
+ delete_queue!
120
+ end
121
+
122
+ private
123
+
124
+ def queue
125
+ @_queue ||= Queue.new
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/metrics_hash'
5
+
6
+ module Contrast
7
+ module Agent
8
+ # This class will hold the basic information for a Telemetry Event
9
+ class TelemetryEvent
10
+ include Contrast::Utils
11
+
12
+ attr_reader :timestamp, :tags
13
+
14
+ # The instance_id comes from the Telemetry Instance
15
+ def initialize
16
+ @tags = MetricsHash.new(String)
17
+ @timestamp = format_date
18
+ @instance_id = Contrast::Agent.telemetry_queue.__id__
19
+ end
20
+
21
+ def path
22
+ ''
23
+ end
24
+
25
+ def to_json **_args
26
+ { tags: @tags, timestamp: @timestamp, instance_id: @instance_id }
27
+ end
28
+
29
+ def format_date
30
+ Time.now.strftime('%Y-%m-%d %H:%M:%S.%L')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -4,6 +4,7 @@
4
4
  require 'contrast/components/logger'
5
5
  require 'contrast/agent/service_heartbeat'
6
6
  require 'contrast/api/communication/messaging_queue'
7
+ require 'contrast/agent/telemetry'
7
8
 
8
9
  module Contrast
9
10
  module Agent
@@ -22,28 +23,22 @@ module Contrast
22
23
  @heapdump_util = Contrast::Utils::HeapDumpUtil.new
23
24
  @heartbeat = Contrast::Agent::ServiceHeartbeat.new
24
25
  @messaging_queue = Contrast::Api::Communication::MessagingQueue.new
26
+ @telemetry = Contrast::Agent::Telemetry.new if Contrast::Agent::Telemetry.enabled?
25
27
  end
26
28
 
27
29
  def startup!
28
30
  return unless ::Contrast::AGENT.enabled?
29
31
 
30
- unless heartbeat.running?
31
- logger.debug('Attempting to start heartbeat thread')
32
- heartbeat.start_thread!
33
- end
34
- heartbeat_result = heartbeat.running?
35
- logger.debug('Heartbeat thread status', alive: heartbeat_result)
36
-
37
- unless messaging_queue.running?
38
- logger.debug('Attempting to start messaging queue thread')
39
- messaging_queue.start_thread!
40
- end
41
- messaging_result = messaging_queue.running?
42
- logger.debug('Messaging thread status', alive: messaging_result)
32
+ telemetry_thread_status = telemetry_thread_init
33
+ heartbeat_thread_status = heartbeat_thread_init
34
+ messaging_thread_status = messaging_thread_init
43
35
 
44
36
  logger.debug('ThreadWatcher started threads')
45
37
 
46
- @pids[Process.pid] = messaging_result && heartbeat_result
38
+ @pids[Process.pid] = messaging_thread_status && heartbeat_thread_status
39
+ return @pids unless Contrast::Agent::Telemetry.enabled?
40
+
41
+ @pids[Process.pid] = messaging_thread_status && heartbeat_thread_status && telemetry_thread_status
47
42
  end
48
43
 
49
44
  def ensure_running?
@@ -57,6 +52,40 @@ module Contrast
57
52
  heartbeat.stop!
58
53
  messaging_queue.stop!
59
54
  heapdump_util.stop!
55
+ telemetry_queue&.stop!
56
+ end
57
+
58
+ def heartbeat_thread_init
59
+ unless heartbeat.running?
60
+ logger.debug('Attempting to start heartbeat thread')
61
+ heartbeat.start_thread!
62
+ end
63
+ heartbeat_result = heartbeat.running?
64
+ logger.debug('Heartbeat thread status', alive: heartbeat_result)
65
+ heartbeat_result
66
+ end
67
+
68
+ def telemetry_thread_init
69
+ @telemetry.start_thread! if @telemetry&.attempt_to_start?
70
+ telemetry_result = @telemetry&.running?
71
+ logger.debug('Telemetry thread status', alive: telemetry_result)
72
+ telemetry_result
73
+ end
74
+
75
+ def messaging_thread_init
76
+ unless messaging_queue.running?
77
+ logger.debug('Attempting to start messaging queue thread')
78
+ messaging_queue.start_thread!
79
+ end
80
+ messaging_result = messaging_queue.running?
81
+ logger.debug('Messaging thread status', alive: messaging_result)
82
+ messaging_result
83
+ end
84
+
85
+ def telemetry_queue
86
+ return if @telemetry.nil?
87
+
88
+ @telemetry
60
89
  end
61
90
  end
62
91
  end
@@ -27,14 +27,22 @@ module Contrast
27
27
 
28
28
  private
29
29
 
30
+ # Use the TracePoint from the :end event, meaning the completion of a definition of a Class or Module (or
31
+ # really the completion of that piece of a definition, as determined by an `end` statement since there could be
32
+ # definitions across multiple files) to carry out actions required on definition. This typically involves
33
+ # patching and usage analysis
34
+ #
35
+ # @param tracepoint_event [TracePoint] the TracePoint from the :end
30
36
  def process tracepoint_event
31
37
  with_contrast_scope do
32
- logger.trace('Received TracePoint end event', module: tracepoint_event.self.to_s)
33
-
38
+ # the Module or Class that was loaded during this event
34
39
  loaded_module = tracepoint_event.self
40
+ # the file being loaded that contained this definition
35
41
  path = tracepoint_event.path
36
42
  return if path&.include?('contrast')
37
43
 
44
+ logger.trace('Received TracePoint end event', module: loaded_module, path: path)
45
+
38
46
  Contrast::Agent.framework_manager.register_late_framework(loaded_module)
39
47
  Contrast::Agent::Inventory::DependencyUsageAnalysis.instance.associate_file(path) if path
40
48
  Contrast::Agent::Patching::Policy::Patcher.patch_specific_module(loaded_module)
@@ -43,7 +51,7 @@ module Contrast
43
51
  end
44
52
  Contrast::Agent::Assess::Policy::PolicyScanner.scan(tracepoint_event)
45
53
  rescue StandardError => e
46
- logger.error('Unable to complete TracePoint analysis', e, module: loaded_module)
54
+ logger.error('Unable to complete TracePoint analysis', e, module: loaded_module, path: path)
47
55
  end
48
56
  end
49
57
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '4.10.0'
6
+ VERSION = '4.13.1'
7
7
  end
8
8
  end
@@ -20,7 +20,6 @@ require 'contrast/extension/delegator'
20
20
  require 'contrast/extension/inventory'
21
21
  require 'contrast/extension/module'
22
22
  require 'contrast/extension/protect'
23
- require 'contrast/extension/protect/kernel'
24
23
 
25
24
  require 'contrast/utils/object_share'
26
25
  require 'contrast/utils/string_utils'
@@ -62,6 +61,12 @@ module Contrast
62
61
  thread_watcher.messaging_queue
63
62
  end
64
63
 
64
+ def self.telemetry_queue
65
+ return unless thread_watcher.telemetry_queue
66
+
67
+ thread_watcher.telemetry_queue
68
+ end
69
+
65
70
  def self.thread_watcher
66
71
  @_thread_watcher ||= Contrast::Agent::ThreadWatcher.new
67
72
  end
@@ -0,0 +1,34 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/components/base'
5
+ require 'contrast/components/config'
6
+
7
+ module Contrast
8
+ module Components
9
+ module Api
10
+ # A wrapper build around the Common Agent Configuration project to allow
11
+ # for Api keys to be mapped with their values contained in their
12
+ # parent_configuration_spec.yaml.
13
+ class Interface
14
+ include Contrast::Components::ComponentBase
15
+
16
+ def api_url
17
+ @_api_url ||= ::Contrast::CONFIG.root.api.url
18
+ end
19
+
20
+ def api_key
21
+ @_api_key ||= ::Contrast::CONFIG.root.api.api_key
22
+ end
23
+
24
+ def service_key
25
+ @_service_key ||= ::Contrast::CONFIG.root.api.service_key
26
+ end
27
+
28
+ def username
29
+ @_username ||= ::Contrast::CONFIG.root.api.user_name
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -23,6 +23,10 @@ module Contrast
23
23
  DEFAULT_SERVER_NAME = 'localhost'
24
24
  DEFAULT_SERVER_PATH = '/'
25
25
 
26
+ SUPPORTED_FRAMEWORKS = %w[rails sinatra grape rack].cs__freeze
27
+
28
+ SUPPORTED_SERVERS = %w[passenger puma thin unicorn].cs__freeze
29
+
26
30
  def initialize
27
31
  original_pid
28
32
  end
@@ -109,6 +113,26 @@ module Contrast
109
113
  @_client_id ||= [app_name, pgid].join('-')
110
114
  end
111
115
 
116
+ def app_and_server_information
117
+ {
118
+ application_info: find_gem_information(SUPPORTED_FRAMEWORKS),
119
+ server_info: find_gem_information(SUPPORTED_SERVERS)
120
+ }
121
+ end
122
+
123
+ def find_gem_information arr
124
+ arr.each do |framework|
125
+ next unless Gem.loaded_specs.key?(framework)
126
+
127
+ loaded = Gem.loaded_specs[framework]
128
+ next unless loaded
129
+
130
+ name = loaded.instance_variable_get(:@name)
131
+ version = loaded.instance_variable_get(:@version).to_s
132
+ return [name, version].join(' ')
133
+ end
134
+ end
135
+
112
136
  def instrument_middleware_stack?
113
137
  !Contrast::Utils::JobServersRunning.job_servers_running?
114
138
  end
@@ -88,6 +88,13 @@ module Contrast
88
88
  @_require_scan
89
89
  end
90
90
 
91
+ def require_dynamic_sources?
92
+ if @_require_dynamic_sources.nil?
93
+ @_require_dynamic_sources = !false?(::Contrast::CONFIG.root.assess.enable_dynamic_sources)
94
+ end
95
+ @_require_dynamic_sources
96
+ end
97
+
91
98
  def tags
92
99
  ::Contrast::CONFIG.root.assess&.tags
93
100
  end