contrast-agent 6.15.1 → 6.15.2
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|