contrast-agent 6.15.1 → 6.15.2
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.
- checksums.yaml +4 -4
- data/lib/contrast/agent/telemetry/base.rb +13 -4
- data/lib/contrast/agent/telemetry/client.rb +3 -1
- data/lib/contrast/agent/telemetry/telemetry.rb +6 -0
- data/lib/contrast/agent/thread/thread_watcher.rb +7 -1
- data/lib/contrast/agent/thread/worker_thread.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/components/agent.rb +3 -0
- data/lib/contrast/configuration.rb +9 -0
- data/lib/contrast/framework/manager.rb +5 -2
- data/lib/contrast/funchook/funchook.rb +0 -1
- data/lib/contrast/logger/aliased_logging.rb +26 -0
- data/lib/contrast/utils/job_servers_running.rb +8 -6
- data/lib/contrast/utils/log_utils.rb +1 -1
- data/lib/contrast/utils/middleware_utils.rb +4 -0
- data/lib/contrast/utils/net_http_base.rb +9 -3
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2f94b8a7a87febf8c10b58cf5133081a6dc3183c158ad59c202921efc23753e
|
4
|
+
data.tar.gz: 2e3fff601596655f725a4bea7a8b6f309a5f702557cfb009ad82b1d424da8d40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd0a28ee1a7331401a4709e1aec63a44b272d9d93ba9e6dcebd47270078165b42e3955fe8d938311b10cae4ec60cfaa92d9bee088b3a6056a2461fb00c6a4ab8
|
7
|
+
data.tar.gz: ba7ab7bebd769fd3057533da348a260e208bb622fe50aab3d0e68f8709413579bac258588680f04a65aa68ff4f5dbf0a2038d56fde7fd2ef2892a095e0e8e388
|
@@ -7,12 +7,13 @@ require 'contrast/agent/telemetry/client'
|
|
7
7
|
require 'contrast/agent/thread/worker_thread'
|
8
8
|
require 'contrast/agent/telemetry/telemetry'
|
9
9
|
require 'contrast/agent/telemetry/exception'
|
10
|
+
require 'contrast/utils/job_servers_running'
|
10
11
|
|
11
12
|
module Contrast
|
12
13
|
module Agent
|
13
14
|
module Telemetry
|
14
15
|
# This class will initialize and hold everything needed for the telemetry
|
15
|
-
class Base < WorkerThread
|
16
|
+
class Base < WorkerThread # rubocop:disable Metrics/ClassLength
|
16
17
|
include Contrast::Components::Logger::InstanceMethods
|
17
18
|
# this is where we will send the data from the agents
|
18
19
|
URL = 'https://telemetry.ruby.contrastsecurity.com/'
|
@@ -48,6 +49,8 @@ module Contrast
|
|
48
49
|
private
|
49
50
|
|
50
51
|
def telemetry_enabled?
|
52
|
+
return false if Contrast::AGENT.disabled? || Contrast::Utils::JobServersRunning.job_servers_running?
|
53
|
+
|
51
54
|
opt_out_telemetry = return_value(:telemetry_opt_outs).to_s
|
52
55
|
return false if opt_out_telemetry.casecmp?('true') || opt_out_telemetry == '1'
|
53
56
|
|
@@ -56,7 +59,9 @@ module Contrast
|
|
56
59
|
@_client = Contrast::Agent::Telemetry::Client.new
|
57
60
|
ip_opt_out_telemetry = @_client.initialize_connection(URL)
|
58
61
|
if ip_opt_out_telemetry.nil?
|
59
|
-
|
62
|
+
# TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
|
63
|
+
# an infinite loop w/ telemetry
|
64
|
+
logger.debug("[Telemetry] Connection was not established properly!!! \n
|
60
65
|
Telemetry reporting will be disabled!")
|
61
66
|
return false
|
62
67
|
end
|
@@ -74,6 +79,8 @@ module Contrast
|
|
74
79
|
end
|
75
80
|
|
76
81
|
def attempt_to_start?
|
82
|
+
return unless super
|
83
|
+
|
77
84
|
unless cs__class.enabled?
|
78
85
|
logger.info('[Telemetry] Telemetry service is disabled!')
|
79
86
|
return false
|
@@ -92,7 +99,7 @@ module Contrast
|
|
92
99
|
|
93
100
|
def send_event event
|
94
101
|
if ::Contrast::AGENT.disabled?
|
95
|
-
logger.
|
102
|
+
logger.debug('[Telemetry] Attempted to queue event with Agent disabled', caller: caller, event: event)
|
96
103
|
return
|
97
104
|
end
|
98
105
|
|
@@ -148,7 +155,9 @@ module Contrast
|
|
148
155
|
sleep(retry_sleep_time) unless retry_sleep_time.nil?
|
149
156
|
end
|
150
157
|
rescue StandardError => e
|
151
|
-
|
158
|
+
# TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
|
159
|
+
# an infinite loop w/ telemetry
|
160
|
+
logger.debug('[Telemetry] Could not send message to service from telemetry queue.', e)
|
152
161
|
stop!
|
153
162
|
end
|
154
163
|
end
|
@@ -100,7 +100,9 @@ module Contrast
|
|
100
100
|
def get_event_json event
|
101
101
|
Array(event.to_controlled_hash).to_json
|
102
102
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
103
|
-
|
103
|
+
# TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
|
104
|
+
# an infinite loop w/ telemetry
|
105
|
+
logger.debug('[Telemetry] Unable to convert TelemetryEvent to JSON string', e, hsh)
|
104
106
|
raise(e)
|
105
107
|
end
|
106
108
|
end
|
@@ -94,6 +94,12 @@ module Contrast
|
|
94
94
|
def telemetry_exceptions_enabled?
|
95
95
|
opts_out_telemetry = return_value(:telemetry_opt_outs).to_s
|
96
96
|
return false if opts_out_telemetry.casecmp?('true') || opts_out_telemetry == '1'
|
97
|
+
# Double check if telemetry is enabled, this includes a check for Agent enable setting in the
|
98
|
+
# config. In case of disabled Agent the queue won't be created and the Telemetry would not
|
99
|
+
# be enabled. This check is here to prevent a loop of error creations that could no be added
|
100
|
+
# to a queue ( because the client cannot be initialized if the Agent is disabled or settings are
|
101
|
+
# missing).
|
102
|
+
return false unless Contrast::Agent::Telemetry::Base.enabled?
|
97
103
|
|
98
104
|
true
|
99
105
|
end
|
@@ -7,6 +7,7 @@ require 'contrast/agent/reporting/reporting_workers/reporting_workers'
|
|
7
7
|
require 'contrast/agent/telemetry/base'
|
8
8
|
require 'contrast/agent/protect/input_analyzer/worth_watching_analyzer'
|
9
9
|
require 'contrast/config/diagnostics'
|
10
|
+
require 'contrast/utils/job_servers_running'
|
10
11
|
|
11
12
|
module Contrast
|
12
13
|
module Agent
|
@@ -28,6 +29,11 @@ module Contrast
|
|
28
29
|
attr_reader :reporter_app_settings_worker
|
29
30
|
|
30
31
|
def initialize
|
32
|
+
if Contrast::AGENT.disabled? || Contrast::Utils::JobServersRunning.job_servers_running?
|
33
|
+
logger.info('Agent Disabled, shutting down all threads...')
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
31
37
|
@heapdump_util = Contrast::Utils::HeapDumpUtil.new
|
32
38
|
@reporter = Contrast::Agent::Reporter.new
|
33
39
|
@reporter_heartbeat = Contrast::Agent::ReportingWorkers::ReporterHeartbeat.new
|
@@ -37,7 +43,7 @@ module Contrast
|
|
37
43
|
@worth_watching_analyzer = Contrast::Agent::Protect::WorthWatchingInputAnalyzer.new unless protect_disabled?
|
38
44
|
end
|
39
45
|
|
40
|
-
# @return [
|
46
|
+
# @return [Array<Contrast::Agent::WorkerThread>] map of process to thread startup status
|
41
47
|
def startup!
|
42
48
|
check_before_start
|
43
49
|
|
@@ -9,6 +9,7 @@ require 'contrast/components/security_logger'
|
|
9
9
|
require 'contrast/components/heap_dump'
|
10
10
|
require 'contrast/components/ruby_component'
|
11
11
|
require 'contrast/components/polling'
|
12
|
+
require 'contrast/agent/hooks/tracepoint_hook'
|
12
13
|
|
13
14
|
module Contrast
|
14
15
|
module Components
|
@@ -23,6 +24,8 @@ module Contrast
|
|
23
24
|
attr_reader :canon_name
|
24
25
|
# @return [Array]
|
25
26
|
attr_reader :config_values
|
27
|
+
# @return [Boolean]
|
28
|
+
attr_accessor :enable
|
26
29
|
|
27
30
|
CANON_NAME = 'agent'
|
28
31
|
CONFIG_VALUES = %w[enabled? omit_body?].cs__freeze
|
@@ -53,6 +53,7 @@ module Contrast
|
|
53
53
|
# @return [String,nil]
|
54
54
|
attr_reader :config_file
|
55
55
|
|
56
|
+
CONTRAST_ENV_MARKER = 'CONTRAST__'
|
56
57
|
DEFAULT_YAML_PATH = 'contrast_security.yaml'
|
57
58
|
MILLISECOND_MARKER = '_ms'
|
58
59
|
CONVERSION = {}.cs__freeze
|
@@ -67,6 +68,14 @@ module Contrast
|
|
67
68
|
config_kv = deep_symbolize_all_keys(load_config)
|
68
69
|
config_sources = assign_source_to(config_kv, Contrast::Components::Config::Sources::YAML)
|
69
70
|
|
71
|
+
unless cli_options
|
72
|
+
cli_options = {}
|
73
|
+
ENV.each do |key, value|
|
74
|
+
next unless key.to_s.start_with?(CONTRAST_ENV_MARKER)
|
75
|
+
|
76
|
+
cli_options[key] = value
|
77
|
+
end
|
78
|
+
end
|
70
79
|
# Overlay CLI options - they take precedence over config file
|
71
80
|
cli_options = deep_symbolize_all_keys(cli_options)
|
72
81
|
if cli_options
|
@@ -11,6 +11,7 @@ require 'contrast/framework/rack/support'
|
|
11
11
|
require 'contrast/framework/rails/support'
|
12
12
|
require 'contrast/framework/sinatra/support'
|
13
13
|
require 'contrast/utils/class_util'
|
14
|
+
require 'contrast/utils/job_servers_running'
|
14
15
|
|
15
16
|
module Contrast
|
16
17
|
module Framework
|
@@ -28,6 +29,8 @@ module Contrast
|
|
28
29
|
].cs__freeze
|
29
30
|
|
30
31
|
def initialize
|
32
|
+
return if Contrast::AGENT.disabled? || Contrast::Utils::JobServersRunning.job_servers_running?
|
33
|
+
|
31
34
|
@_frameworks = SUPPORTED_FRAMEWORKS.map do |framework_klass|
|
32
35
|
next unless enable_framework_support?(framework_klass.detection_class)
|
33
36
|
|
@@ -116,8 +119,8 @@ module Contrast
|
|
116
119
|
# @param request [Contrast::Agent::Request] the current request.
|
117
120
|
# @return [Contrast::Agent::Reporting::RouteCoverage] the current route as a Dtm.
|
118
121
|
def get_route_information request
|
119
|
-
@_frameworks
|
120
|
-
reject(&:nil?)
|
122
|
+
@_frameworks&.lazy&.map { |framework_support| framework_support.current_route_coverage(request) }&.
|
123
|
+
reject(&:nil?)&.first
|
121
124
|
end
|
122
125
|
|
123
126
|
# Sometimes the framework we want to instrument is loaded after our agent code. To catch that case, we'll detect
|
@@ -40,12 +40,38 @@ module Contrast
|
|
40
40
|
|
41
41
|
private
|
42
42
|
|
43
|
+
# There's a weird circular import in our code that we don't have time to untangle. This is 100% bad code and
|
44
|
+
# I'm sorry to the future team, but this is all we got.
|
45
|
+
#
|
46
|
+
# If the configuration we need is not available, we'll skip out for now - it means the agent isn't ready. We
|
47
|
+
# won't get telemetry exceptions at this point, but that means the agent hasn't initialized and there's a
|
48
|
+
# chance we're not supposed to. Essentially, we fail closed.
|
49
|
+
#
|
50
|
+
# Once we have the agent initialized, we'll use the value to check. Since it cannot change once set, we'll use
|
51
|
+
# the saved value.
|
52
|
+
#
|
53
|
+
# - HM
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
56
|
+
def buildable?
|
57
|
+
if @_buildable.nil?
|
58
|
+
return false unless defined?(Contrast) &&
|
59
|
+
defined?(Contrast::Agent) &&
|
60
|
+
defined?(Contrast::Agent::Telemetry)
|
61
|
+
|
62
|
+
@_buildable = Contrast::Agent::Telemetry.exceptions_enabled?
|
63
|
+
end
|
64
|
+
@_buildable
|
65
|
+
end
|
66
|
+
|
43
67
|
# @param type [ALIASED_FATAL, ALIASED_ERROR, ALIASED_WARN] the type of error, used to indicate the function used
|
44
68
|
# for logging
|
45
69
|
# @param message [String] the exception message
|
46
70
|
# @param exception [Exception] The exception or error
|
47
71
|
# @param data [Object] Any structured data
|
48
72
|
def build_exception type, message = nil, exception = nil, data = nil
|
73
|
+
return unless buildable?
|
74
|
+
|
49
75
|
stack_trace = wrapped_caller_locations
|
50
76
|
caller_idx = stack_trace&.find_index { |stack| stack.to_s.include?(type) } || 0
|
51
77
|
# The caller_stack is the method in which the error occurred, so has to be above this method
|
@@ -1,15 +1,13 @@
|
|
1
1
|
# Copyright (c) 2023 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/
|
4
|
+
require 'contrast/components/ruby_component'
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Utils
|
8
8
|
# A module that detects whether any job servers attached to
|
9
9
|
# the application are running
|
10
10
|
module JobServersRunning
|
11
|
-
extend Contrast::Components::Logger::InstanceMethods
|
12
|
-
|
13
11
|
class << self
|
14
12
|
def job_servers_running?
|
15
13
|
sidekiq_running? || rake_running?
|
@@ -20,7 +18,6 @@ module Contrast
|
|
20
18
|
def sidekiq_running?
|
21
19
|
return unless defined?(Sidekiq) && Sidekiq.cs__respond_to?(:server?) && Sidekiq.server?
|
22
20
|
|
23
|
-
logger.trace('Detected the spawn of a Sidekiq process')
|
24
21
|
true
|
25
22
|
end
|
26
23
|
|
@@ -32,13 +29,18 @@ module Contrast
|
|
32
29
|
return
|
33
30
|
end
|
34
31
|
|
35
|
-
|
32
|
+
# This might be called before component even exist, so we backup to
|
33
|
+
# default disabled rake tasks.
|
34
|
+
disabled_rake_tasks = if Contrast.const_defined?(:APP_CONTEXT) # rubocop:disable Security/Module/ConstDefined
|
35
|
+
Contrast::APP_CONTEXT.disabled_agent_rake_tasks
|
36
|
+
else
|
37
|
+
Contrast::Components::Ruby::Interface::DISABLED_RAKE_TASK_LIST
|
38
|
+
end
|
36
39
|
has_disabled_task = Rake.application.top_level_tasks.any? do |top_level_task|
|
37
40
|
disabled_rake_tasks.include?(top_level_task)
|
38
41
|
end
|
39
42
|
return false unless has_disabled_task
|
40
43
|
|
41
|
-
logger.trace('Detected startup within Rake task')
|
42
44
|
true
|
43
45
|
end
|
44
46
|
end
|
@@ -51,7 +51,7 @@ module Contrast
|
|
51
51
|
logger.extend(Contrast::Logger::Application)
|
52
52
|
logger.extend(Contrast::Logger::Request)
|
53
53
|
logger.extend(Contrast::Logger::Time)
|
54
|
-
logger.extend(Contrast::Logger::AliasedLogging)
|
54
|
+
logger.extend(Contrast::Logger::AliasedLogging)
|
55
55
|
end
|
56
56
|
|
57
57
|
# Determine the valid path to which to log, given the precedence of config > settings > default.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/config/yaml_file'
|
5
|
+
require 'contrast/utils/job_servers_running'
|
5
6
|
|
6
7
|
module Contrast
|
7
8
|
module Utils
|
@@ -30,6 +31,9 @@ module Contrast
|
|
30
31
|
logger.error('!!! CONFIG FILE IS INVALID - DISABLING CONTRAST AGENT !!!')
|
31
32
|
elsif ::Contrast::AGENT.disabled?
|
32
33
|
logger.warn('Contrast disabled by configuration. Continuing without instrumentation.')
|
34
|
+
elsif Contrast::Utils::JobServersRunning.job_servers_running?
|
35
|
+
logger.info('Server job detected disabling Agent...')
|
36
|
+
::Contrast::AGENT.disable!
|
33
37
|
else
|
34
38
|
::Contrast::AGENT.enable!
|
35
39
|
end
|
@@ -43,7 +43,9 @@ module Contrast
|
|
43
43
|
logger.debug('Client verified', service: service_name, url: url)
|
44
44
|
net_http_client
|
45
45
|
rescue StandardError => e
|
46
|
-
|
46
|
+
# TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
|
47
|
+
# an infinite loop w/ telemetry
|
48
|
+
logger.debug('Connection failed', e, service: service_name, url: url)
|
47
49
|
nil
|
48
50
|
end
|
49
51
|
|
@@ -72,7 +74,9 @@ module Contrast
|
|
72
74
|
Errno::ETIMEDOUT, Errno::ESHUTDOWN, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::EISCONN,
|
73
75
|
Errno::ECONNABORTED, Errno::ENETRESET, Errno::ENETUNREACH => e
|
74
76
|
|
75
|
-
|
77
|
+
# TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
|
78
|
+
# an infinite loop w/ telemetry
|
79
|
+
logger.debug("#{ service_name } connection failed", e.message)
|
76
80
|
false
|
77
81
|
end
|
78
82
|
|
@@ -110,7 +114,9 @@ module Contrast
|
|
110
114
|
client.key = OpenSSL::PKey::RSA.new(File.read(Contrast::API.certification_key_file)).to_s
|
111
115
|
end
|
112
116
|
rescue Errno::ENOENT => e
|
113
|
-
|
117
|
+
# TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
|
118
|
+
# an infinite loop w/ telemetry
|
119
|
+
logger.debug('Custom certificates failed', e.message)
|
114
120
|
end
|
115
121
|
|
116
122
|
# sets default setting for client validation of certificates and
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contrast-agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.15.
|
4
|
+
version: 6.15.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- galen.palmer@contrastsecurity.com
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: exe
|
15
15
|
cert_chain: []
|
16
|
-
date: 2023-02-
|
16
|
+
date: 2023-02-22 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: bundler
|
@@ -678,22 +678,22 @@ email:
|
|
678
678
|
executables: []
|
679
679
|
extensions:
|
680
680
|
- ext/cs__common/extconf.rb
|
681
|
-
- ext/
|
682
|
-
- ext/
|
681
|
+
- ext/cs__assess_marshal_module/extconf.rb
|
682
|
+
- ext/cs__assess_yield_track/extconf.rb
|
683
|
+
- ext/cs__scope/extconf.rb
|
683
684
|
- ext/cs__assess_kernel/extconf.rb
|
684
|
-
- ext/
|
685
|
-
- ext/
|
685
|
+
- ext/cs__assess_array/extconf.rb
|
686
|
+
- ext/cs__os_information/extconf.rb
|
686
687
|
- ext/cs__assess_string/extconf.rb
|
688
|
+
- ext/cs__assess_hash/extconf.rb
|
687
689
|
- ext/cs__assess_regexp/extconf.rb
|
688
|
-
- ext/cs__tests/extconf.rb
|
689
690
|
- ext/cs__assess_module/extconf.rb
|
690
|
-
- ext/
|
691
|
-
- ext/
|
692
|
-
- ext/cs__scope/extconf.rb
|
691
|
+
- ext/cs__assess_string_interpolation/extconf.rb
|
692
|
+
- ext/cs__tests/extconf.rb
|
693
693
|
- ext/cs__assess_test/extconf.rb
|
694
|
-
- ext/
|
695
|
-
- ext/
|
696
|
-
- ext/
|
694
|
+
- ext/cs__assess_fiber_track/extconf.rb
|
695
|
+
- ext/cs__assess_basic_object/extconf.rb
|
696
|
+
- ext/cs__contrast_patch/extconf.rb
|
697
697
|
extra_rdoc_files: []
|
698
698
|
files:
|
699
699
|
- ".clang-format"
|