contrast-agent 4.8.0 → 4.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -1
- data/.rspec +0 -1
- data/.rspec_parallel +6 -0
- data/.simplecov +1 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.c +0 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.h +0 -2
- data/lib/contrast/agent/assess/contrast_event.rb +1 -5
- data/lib/contrast/agent/assess/contrast_object.rb +0 -3
- data/lib/contrast/agent/assess/finalizers/hash.rb +2 -5
- data/lib/contrast/agent/assess/policy/patcher.rb +5 -4
- data/lib/contrast/agent/assess/policy/policy.rb +1 -1
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +2 -6
- data/lib/contrast/agent/assess/policy/preshift.rb +16 -12
- data/lib/contrast/agent/assess/policy/propagation_method.rb +102 -59
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -7
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
- data/lib/contrast/agent/assess/policy/propagator/split.rb +10 -6
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +3 -3
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +6 -7
- data/lib/contrast/agent/assess/policy/source_method.rb +18 -22
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -4
- data/lib/contrast/agent/assess/policy/trigger_method.rb +62 -88
- data/lib/contrast/agent/assess/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/assess/property/evented.rb +2 -1
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +3 -4
- data/lib/contrast/agent/at_exit_hook.rb +3 -3
- data/lib/contrast/agent/class_reopener.rb +6 -5
- data/lib/contrast/agent/disable_reaction.rb +4 -5
- data/lib/contrast/agent/exclusion_matcher.rb +2 -7
- data/lib/contrast/agent/inventory/database_config.rb +117 -0
- data/lib/contrast/agent/inventory/dependency_analysis.rb +2 -6
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +9 -10
- data/lib/contrast/agent/inventory/policy/datastores.rb +5 -6
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/middleware.rb +15 -13
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +6 -3
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +21 -16
- data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/patch.rb +13 -8
- data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +14 -14
- data/lib/contrast/agent/patching/policy/policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/policy_node.rb +2 -3
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/policy.rb +1 -1
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +3 -5
- data/lib/contrast/agent/protect/rule/base.rb +10 -10
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +4 -5
- data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
- data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -5
- data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
- data/lib/contrast/agent/reaction_processor.rb +3 -4
- data/lib/contrast/agent/request.rb +13 -7
- data/lib/contrast/agent/request_context.rb +36 -34
- data/lib/contrast/agent/request_handler.rb +5 -3
- data/lib/contrast/agent/response.rb +2 -3
- data/lib/contrast/agent/rewriter.rb +4 -3
- data/lib/contrast/agent/rule_set.rb +5 -4
- data/lib/contrast/agent/scope.rb +32 -20
- data/lib/contrast/agent/service_heartbeat.rb +2 -3
- data/lib/contrast/agent/static_analysis.rb +7 -6
- data/lib/contrast/agent/thread.rb +2 -4
- data/lib/contrast/agent/thread_watcher.rb +3 -4
- data/lib/contrast/agent/tracepoint_hook.rb +20 -7
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +16 -11
- data/lib/contrast/api/communication/response_processor.rb +11 -11
- data/lib/contrast/api/communication/service_lifecycle.rb +9 -5
- data/lib/contrast/api/communication/socket_client.rb +18 -14
- data/lib/contrast/api/communication/speedracer.rb +5 -6
- data/lib/contrast/api/decorators/address.rb +2 -3
- data/lib/contrast/api/decorators/agent_startup.rb +7 -9
- data/lib/contrast/api/decorators/application_startup.rb +9 -10
- data/lib/contrast/api/decorators/application_update.rb +0 -4
- data/lib/contrast/api/decorators/http_request.rb +3 -7
- data/lib/contrast/api/decorators/instrumentation_mode.rb +3 -5
- data/lib/contrast/api/decorators/message.rb +7 -7
- data/lib/contrast/api/decorators/route_coverage.rb +24 -1
- data/lib/contrast/api/decorators/trace_event_object.rb +2 -3
- data/lib/contrast/components/agent.rb +13 -15
- data/lib/contrast/components/app_context.rb +7 -11
- data/lib/contrast/components/assess.rb +19 -16
- data/lib/contrast/components/base.rb +40 -0
- data/lib/contrast/components/config.rb +1 -2
- data/lib/contrast/components/contrast_service.rb +8 -11
- data/lib/contrast/components/heap_dump.rb +5 -4
- data/lib/contrast/components/inventory.rb +2 -7
- data/lib/contrast/components/logger.rb +14 -10
- data/lib/contrast/components/protect.rb +10 -13
- data/lib/contrast/components/sampling.rb +5 -5
- data/lib/contrast/components/scope.rb +9 -32
- data/lib/contrast/components/settings.rb +1 -5
- data/lib/contrast/config/base_configuration.rb +14 -6
- data/lib/contrast/configuration.rb +22 -19
- data/lib/contrast/extension/assess/array.rb +3 -15
- data/lib/contrast/extension/assess/eval_trigger.rb +2 -23
- data/lib/contrast/extension/assess/fiber.rb +6 -16
- data/lib/contrast/extension/assess/hash.rb +3 -13
- data/lib/contrast/extension/assess/kernel.rb +3 -14
- data/lib/contrast/extension/assess/marshal.rb +6 -14
- data/lib/contrast/extension/assess/regexp.rb +5 -15
- data/lib/contrast/extension/assess/string.rb +6 -31
- data/lib/contrast/extension/extension.rb +61 -0
- data/lib/contrast/extension/kernel.rb +2 -4
- data/lib/contrast/extension/protect/kernel.rb +0 -15
- data/lib/contrast/framework/grape/support.rb +174 -0
- data/lib/contrast/framework/manager.rb +44 -9
- data/lib/contrast/framework/rack/patch/session_cookie.rb +6 -6
- data/lib/contrast/framework/rack/support.rb +1 -1
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +5 -8
- data/lib/contrast/framework/rails/patch/support.rb +43 -36
- data/lib/contrast/framework/rails/railtie.rb +8 -6
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +4 -4
- data/lib/contrast/framework/rails/support.rb +60 -13
- data/lib/contrast/framework/sinatra/support.rb +1 -1
- data/lib/contrast/funchook/funchook.rb +4 -3
- data/lib/contrast/logger/application.rb +1 -6
- data/lib/contrast/logger/log.rb +103 -13
- data/lib/contrast/logger/request.rb +0 -4
- data/lib/contrast/tasks/config.rb +0 -1
- data/lib/contrast/tasks/service.rb +1 -6
- data/lib/contrast/utils/assess/sampling_util.rb +2 -3
- data/lib/contrast/utils/assess/tracking_util.rb +2 -4
- data/lib/contrast/utils/class_util.rb +26 -19
- data/lib/contrast/utils/heap_dump_util.rb +5 -3
- data/lib/contrast/utils/invalid_configuration_util.rb +4 -3
- data/lib/contrast/utils/io_util.rb +46 -40
- data/lib/contrast/utils/job_servers_running.rb +4 -3
- data/lib/contrast/utils/lru_cache.rb +43 -0
- data/lib/contrast/utils/os.rb +2 -3
- data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
- data/lib/contrast/utils/string_utils.rb +2 -3
- data/lib/contrast/utils/tag_util.rb +26 -19
- data/lib/contrast.rb +24 -14
- data/resources/assess/policy.json +197 -2
- data/resources/deadzone/policy.json +10 -0
- data/ruby-agent.gemspec +13 -3
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +91 -25
- data/lib/contrast/components/interface.rb +0 -196
- data/lib/contrast/delegators/input_analysis.rb +0 -12
- data/lib/contrast/utils/inventory_util.rb +0 -114
data/lib/contrast/logger/log.rb
CHANGED
@@ -5,21 +5,106 @@ require 'logger'
|
|
5
5
|
require 'ougai'
|
6
6
|
require 'singleton'
|
7
7
|
|
8
|
-
require 'contrast/components/interface'
|
9
8
|
require 'contrast/extension/module'
|
10
9
|
require 'contrast/logger/application'
|
11
10
|
require 'contrast/logger/format'
|
12
11
|
require 'contrast/logger/request'
|
13
12
|
require 'contrast/logger/time'
|
13
|
+
require 'contrast/components/config'
|
14
14
|
|
15
15
|
module Contrast
|
16
|
+
# This module allows us to dynamically weave timing into our code, so that only when the time is actually needed do
|
17
|
+
# we pay the penalty for that timing block
|
18
|
+
module TraceTiming
|
19
|
+
def methods_to_time
|
20
|
+
@_methods_to_time ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
# Store info about methods for later patching.
|
24
|
+
METHOD_INFO = Struct.new(:clazz, :method_name, :custom_msg, :aliased)
|
25
|
+
|
26
|
+
# Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
|
27
|
+
# logger set to TRACE.
|
28
|
+
#
|
29
|
+
# @params: clazz [Class] the class of the method to time.
|
30
|
+
# @params: method [Symbol] the method to time.
|
31
|
+
# @params: method [String] optional custom logging message.
|
32
|
+
def add_method_to_trace_timing clazz, method, msg = nil
|
33
|
+
methods_to_time.append(METHOD_INFO.new(clazz, method, msg, false))
|
34
|
+
enable_trace_timing if logger.level == ::Ougai::Logging::TRACE
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
|
38
|
+
# logger set to TRACE.
|
39
|
+
#
|
40
|
+
# @params: method_spec [METHOD_INFO] specs about the method to be timed.
|
41
|
+
# @params: class_method [Boolean] whether this is or isn't a class/module method.
|
42
|
+
def trace_time_class_method meth_spec, class_method
|
43
|
+
untimed_func_symbol = "untimed_#{ meth_spec.method_name }".to_sym
|
44
|
+
send_to = class_method ? meth_spec.clazz.cs__singleton_class : meth_spec.clazz
|
45
|
+
meth_spec.clazz.class_eval do
|
46
|
+
include Contrast::Components::Logger::InstanceMethods
|
47
|
+
extend Contrast::Components::Logger::InstanceMethods
|
48
|
+
|
49
|
+
send_to.send(:alias_method, untimed_func_symbol, meth_spec.method_name)
|
50
|
+
meth_spec.aliased = true
|
51
|
+
|
52
|
+
log_message = "Elapsed time for #{ meth_spec.method_name }."
|
53
|
+
log_message = meth_spec.custom_message if meth_spec.custom_msg
|
54
|
+
|
55
|
+
send_to.send(:define_method, meth_spec.method_name) do |*args, **kwargs, &block| # rubocop:disable Performance/Kernel/DefineMethod
|
56
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
57
|
+
rv = if kwargs.empty?
|
58
|
+
send(untimed_func_symbol, *args, &block)
|
59
|
+
else
|
60
|
+
send(untimed_func_symbol, *args, **kwargs, &block)
|
61
|
+
end
|
62
|
+
delta = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
63
|
+
logger.trace(log_message, elapsed: delta * 1000)
|
64
|
+
rv
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Enable trace timing of methods specified in @_methods_to_time via aliasing.
|
70
|
+
def enable_trace_timing
|
71
|
+
methods_to_time.each do |meth_spec|
|
72
|
+
next if meth_spec.aliased
|
73
|
+
|
74
|
+
is_class_method = meth_spec.clazz.singleton_methods(false).include?(meth_spec.method_name)
|
75
|
+
trace_time_class_method meth_spec, is_class_method
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Contrast
|
82
|
+
# Used as a wrapper around our logging. The module option specifically adds in a new method for error that raises the
|
83
|
+
# logged exception, used in testing so that we can see if anything unexpected happens without it being swallowed
|
84
|
+
# while still providing safe options for customers.
|
16
85
|
module Logger
|
17
|
-
#
|
18
|
-
|
86
|
+
# For development set following env var to raise logged exceptions instead of just logging.
|
87
|
+
if ENV['CONTRAST__AGENT__RUBY_MORE_COWBELL']
|
88
|
+
Ougai::Logger.class_eval do
|
89
|
+
alias_method :cs__error, :error
|
90
|
+
alias_method :cs__warn, :warn
|
91
|
+
|
92
|
+
def error *args, **kwargs
|
93
|
+
if kwargs.empty?
|
94
|
+
cs__error(*args)
|
95
|
+
else
|
96
|
+
cs__error(*args, **kwargs)
|
97
|
+
end
|
98
|
+
args.each { |arg| raise arg if arg && arg.cs__class < Exception }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# This class functions to serve as a wrapper around our logging, as we need to be able to dynamically update
|
104
|
+
# level based on updates to TeamServer.
|
19
105
|
class Log
|
20
106
|
include Singleton
|
21
|
-
include Contrast::
|
22
|
-
access_component :config
|
107
|
+
include ::Contrast::TraceTiming
|
23
108
|
|
24
109
|
DEFAULT_NAME = 'contrast.log'
|
25
110
|
DEFAULT_LEVEL = ::Ougai::Logging::Severity::INFO
|
@@ -33,8 +118,8 @@ module Contrast
|
|
33
118
|
update
|
34
119
|
end
|
35
120
|
|
36
|
-
# Given new settings from TeamServer, update our logging to use the new
|
37
|
-
#
|
121
|
+
# Given new settings from TeamServer, update our logging to use the new file and level, assuming they weren't
|
122
|
+
# set by local configuration.
|
38
123
|
#
|
39
124
|
# @param log_file [String] the file to which to log, as provided by TeamServer settings
|
40
125
|
# @param log_level [String] the level at which to log, as provided by TeamServer settings
|
@@ -51,6 +136,8 @@ module Contrast
|
|
51
136
|
@previous_path = current_path
|
52
137
|
@previous_level = current_level_const
|
53
138
|
|
139
|
+
enable_trace_timing if current_level_const == ::Ougai::Logging::TRACE
|
140
|
+
|
54
141
|
@_logger = build(path: current_path, level_const: current_level_const)
|
55
142
|
# If we're logging to a new path, then let's start it w/ our helpful
|
56
143
|
# data gathering messages
|
@@ -60,6 +147,9 @@ module Contrast
|
|
60
147
|
logger.error('Unable to process update to LoggerManager.', e)
|
61
148
|
else
|
62
149
|
puts 'Unable to process update to LoggerManager.'
|
150
|
+
raise e if ENV['CONTRAST__AGENT__RUBY_MORE_COWBELL']
|
151
|
+
|
152
|
+
puts e.message
|
63
153
|
puts e.backtrace.join("\n")
|
64
154
|
end
|
65
155
|
end
|
@@ -107,8 +197,8 @@ module Contrast
|
|
107
197
|
# TeamServer.
|
108
198
|
# @return [String] the path to which to log or STDOUT / STDERR if one of those values provided.
|
109
199
|
def find_valid_path log_file
|
110
|
-
config = CONFIG.root.agent.logger
|
111
|
-
config_path = config
|
200
|
+
config = ::Contrast::CONFIG.root.agent.logger
|
201
|
+
config_path = config&.path&.length.to_i.positive? ? config.path : nil
|
112
202
|
valid_path(config_path || log_file)
|
113
203
|
end
|
114
204
|
|
@@ -141,8 +231,9 @@ module Contrast
|
|
141
231
|
# TeamServer.
|
142
232
|
# @return [::Ougai::Logging::Severity] the level at which to log
|
143
233
|
def find_valid_level log_level
|
144
|
-
config = CONFIG.root.agent.logger
|
145
|
-
config_level = config
|
234
|
+
config = ::Contrast::CONFIG.root.agent.logger
|
235
|
+
config_level = config&.level&.length&.positive? ? config.level : nil
|
236
|
+
|
146
237
|
valid_level(config_level || log_level)
|
147
238
|
end
|
148
239
|
|
@@ -158,8 +249,7 @@ module Contrast
|
|
158
249
|
DEFAULT_LEVEL
|
159
250
|
end
|
160
251
|
|
161
|
-
# Log that the Agent log has changed and include some default information
|
162
|
-
# at the start of the log.
|
252
|
+
# Log that the Agent log has changed and include some default information at the start of the log.
|
163
253
|
def log_update
|
164
254
|
logger.debug('Initialized new contrast agent logger')
|
165
255
|
logger.debug_with_time('middleware: log environment') do
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# Copyright (c) 2021 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/interface'
|
5
4
|
require 'contrast/utils/timer'
|
6
5
|
|
7
6
|
module Contrast
|
@@ -9,9 +8,6 @@ module Contrast
|
|
9
8
|
# Our decorator for the Ougai logger allowing for the logging of the
|
10
9
|
# request lifecycle, used to provide context during troubleshooting.
|
11
10
|
module Request
|
12
|
-
include Contrast::Components::Interface
|
13
|
-
access_component :config
|
14
|
-
|
15
11
|
# Utility method to log the start of a request
|
16
12
|
def request_start
|
17
13
|
debug('Beginning request analysis')
|
@@ -7,7 +7,6 @@ module Contrast
|
|
7
7
|
# A Rake task to generate a contrast_security.yaml file with some basic settings
|
8
8
|
module Config
|
9
9
|
extend Rake::DSL
|
10
|
-
include Contrast::Components::Interface
|
11
10
|
DEFAULT_CONFIG = {
|
12
11
|
'api' => {
|
13
12
|
'url' => 'Enter your Contrast URL ex: https://app.contrastsecurity.com/Contrast',
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# Copyright (c) 2021 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/interface'
|
5
4
|
require 'contrast/utils/os'
|
6
5
|
|
7
6
|
module Contrast
|
@@ -10,14 +9,10 @@ module Contrast
|
|
10
9
|
# forked from the application
|
11
10
|
module Service
|
12
11
|
extend Rake::DSL
|
13
|
-
include Contrast::Components::Interface
|
14
|
-
|
15
|
-
access_component :contrast_service
|
16
|
-
|
17
12
|
# Start the service if it is not already running
|
18
13
|
def self.start_service
|
19
14
|
puts 'Starting Contrast Service'
|
20
|
-
service_log = CONTRAST_SERVICE.logger_path
|
15
|
+
service_log = ::Contrast::CONTRAST_SERVICE.logger_path
|
21
16
|
if File.writable?(service_log)
|
22
17
|
spawn('contrast_service', out: File::NULL, err: service_log)
|
23
18
|
else
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'singleton'
|
5
|
-
require 'contrast/components/
|
5
|
+
require 'contrast/components/sampling'
|
6
6
|
|
7
7
|
module Contrast
|
8
8
|
module Utils
|
@@ -11,8 +11,7 @@ module Contrast
|
|
11
11
|
class SamplingUtil
|
12
12
|
include Singleton
|
13
13
|
|
14
|
-
|
15
|
-
access_component :sampling
|
14
|
+
extend Contrast::Components::Sampling::InstanceMethods
|
16
15
|
|
17
16
|
def initialize
|
18
17
|
@requests = {}
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/agent/assess/tracker'
|
5
|
-
require 'contrast/components/
|
5
|
+
require 'contrast/components/logger'
|
6
6
|
require 'contrast/utils/duck_utils'
|
7
7
|
|
8
8
|
module Contrast
|
@@ -10,9 +10,7 @@ module Contrast
|
|
10
10
|
module Assess
|
11
11
|
# TrackingUtil has methods for determining if a object is being tracked
|
12
12
|
class TrackingUtil
|
13
|
-
|
14
|
-
|
15
|
-
access_component :logging
|
13
|
+
extend Contrast::Components::Logger::InstanceMethods
|
16
14
|
|
17
15
|
class << self
|
18
16
|
# Public interface to our tracking check, isolating the internals
|
@@ -3,11 +3,13 @@
|
|
3
3
|
|
4
4
|
require 'contrast/extension/module'
|
5
5
|
require 'contrast/utils/object_share'
|
6
|
+
require 'contrast/utils/lru_cache'
|
6
7
|
|
7
8
|
module Contrast
|
8
9
|
module Utils
|
9
10
|
# Utility methods for exploring the complete space of Objects
|
10
11
|
class ClassUtil
|
12
|
+
@lru_cache = LRUCache.new
|
11
13
|
class << self
|
12
14
|
# some classes have had things prepended to them, like Marshal in Rails
|
13
15
|
# 5 and higher. Their ActiveSupport::MarshalWithAutoloading will break
|
@@ -47,27 +49,32 @@ module Contrast
|
|
47
49
|
# @param object [Object, nil] the entity to convert to a String
|
48
50
|
# @return [String] the human readable form of the String, as defined by
|
49
51
|
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/vulnerability/capture-snapshot.md
|
52
|
+
|
50
53
|
def to_contrast_string object
|
51
|
-
#
|
54
|
+
# After implementing the LRU Cache, we firstly need to check if already had that object cached
|
55
|
+
# and if we have it - we can return it directly
|
56
|
+
return @lru_cache[object.__id__] if @lru_cache.key? object.__id__
|
57
|
+
|
58
|
+
# Only treat object like a string if it actually is a string+
|
52
59
|
# some subclasses of String override string methods we depend on
|
53
|
-
if object.cs__class == String
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
60
|
+
@lru_cache[object.__id__] = if object.cs__class == String
|
61
|
+
cached = to_cached_string(object)
|
62
|
+
return cached if cached
|
63
|
+
|
64
|
+
object.dup
|
65
|
+
elsif object.nil?
|
66
|
+
Contrast::Utils::ObjectShare::NIL_STRING
|
67
|
+
elsif object.cs__is_a?(Symbol)
|
68
|
+
":#{ object }"
|
69
|
+
elsif object.cs__is_a?(Module) || object.cs__is_a?(Class)
|
70
|
+
"#{ object.cs__name }@#{ object.__id__ }"
|
71
|
+
elsif object.cs__is_a?(Regexp)
|
72
|
+
object.source
|
73
|
+
elsif use_to_s?(object)
|
74
|
+
object.to_s
|
75
|
+
else
|
76
|
+
"#{ object.cs__class.cs__name }@#{ object.__id__ }"
|
77
|
+
end
|
71
78
|
end
|
72
79
|
|
73
80
|
# The method const_defined? can cause autoload, which is bad for us.
|
@@ -3,14 +3,16 @@
|
|
3
3
|
|
4
4
|
require 'objspace'
|
5
5
|
require 'singleton'
|
6
|
-
require 'contrast/components/
|
6
|
+
require 'contrast/components/heap_dump'
|
7
|
+
require 'contrast/components/logger'
|
7
8
|
|
8
9
|
module Contrast
|
9
10
|
module Utils
|
10
11
|
# Implementation of a heap dump util to automate generation
|
11
12
|
class HeapDumpUtil < Contrast::Agent::WorkerThread
|
12
|
-
|
13
|
-
|
13
|
+
extend Contrast::Components::Logger::InstanceMethods
|
14
|
+
include Contrast::Components::Logger::InstanceMethods
|
15
|
+
extend Contrast::Components::HeapDump::InstanceMethods
|
14
16
|
|
15
17
|
LOG_ERROR_DUMPS = 'Unable to generate heap dumps'
|
16
18
|
FILE_WRITE_FLAGS = 'w'
|
@@ -2,15 +2,16 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/agent/assess/policy/trigger_method'
|
5
|
-
require 'contrast/components/
|
5
|
+
require 'contrast/components/logger'
|
6
|
+
require 'contrast/components/scope'
|
6
7
|
|
7
8
|
module Contrast
|
8
9
|
module Utils
|
9
10
|
# This utility allows us to report invalid configurations detected in
|
10
11
|
# customer applications, as determined by Configuration Rules at runtime.
|
11
12
|
module InvalidConfigurationUtil
|
12
|
-
include Contrast::Components::
|
13
|
-
|
13
|
+
include Contrast::Components::Logger::InstanceMethods
|
14
|
+
include Contrast::Components::Scope::InstanceMethods
|
14
15
|
|
15
16
|
CS__PATH = 'path'
|
16
17
|
CS__SESSION_ID = 'sessionId'
|
@@ -1,50 +1,56 @@
|
|
1
1
|
# Copyright (c) 2021 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/logger'
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Utils
|
8
8
|
# Util for information about an IO
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
9
|
+
module IOUtil
|
10
|
+
extend Contrast::Components::Logger::InstanceMethods
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# We're only going to call rewind on things that we believe are safe to
|
14
|
+
# call it on. This method white lists those cases and returns false in
|
15
|
+
# all others.
|
16
|
+
def should_rewind? potential_io
|
17
|
+
return true if potential_io.is_a?(StringIO)
|
18
|
+
return false unless io?(potential_io)
|
19
|
+
|
20
|
+
should_rewind_io?(potential_io)
|
21
|
+
rescue StandardError => e
|
22
|
+
logger.debug('Encountered an issue determining if rewindable', e, module: potential_io.cs__class.cs__name)
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
# A class is IO if it is a decedent or delegate of IO
|
27
|
+
def io? object
|
28
|
+
return true if object.is_a?(IO)
|
29
|
+
|
30
|
+
# DelegateClass, which is a Delegator, defines __getobj__ as a way to
|
31
|
+
# get the object that the class wraps around (delegates to)
|
32
|
+
return false unless object.is_a?(Delegator)
|
33
|
+
|
34
|
+
object.__getobj__.is_a?(IO)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# IO rewind cannot be used with streams such as pipes, ttys, and sockets or for ones which have been closed.
|
40
|
+
#
|
41
|
+
# @param io [IO] the input to check for the ability to rewind
|
42
|
+
# @return [Boolean] if the given IO can be rewound
|
43
|
+
def should_rewind_io? io
|
44
|
+
return false if io.closed?
|
45
|
+
return false if io.tty?
|
46
|
+
|
47
|
+
status = io.stat
|
48
|
+
return false unless status
|
49
|
+
return false if status.pipe?
|
50
|
+
return false if status.socket?
|
51
|
+
|
52
|
+
true
|
53
|
+
end
|
48
54
|
end
|
49
55
|
end
|
50
56
|
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
# Copyright (c) 2021 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/logger'
|
5
|
+
|
4
6
|
module Contrast
|
5
7
|
module Utils
|
6
8
|
# A module that detects whether any job servers attached to
|
7
9
|
# the application are running
|
8
10
|
module JobServersRunning
|
9
|
-
|
10
|
-
access_component :app_context, :logging
|
11
|
+
extend Contrast::Components::Logger::InstanceMethods
|
11
12
|
|
12
13
|
class << self
|
13
14
|
def job_servers_running?
|
@@ -31,7 +32,7 @@ module Contrast
|
|
31
32
|
return
|
32
33
|
end
|
33
34
|
|
34
|
-
disabled_rake_tasks = APP_CONTEXT.disabled_agent_rake_tasks
|
35
|
+
disabled_rake_tasks = ::Contrast::APP_CONTEXT.disabled_agent_rake_tasks
|
35
36
|
has_disabled_task = Rake.application.top_level_tasks.any? do |top_level_task|
|
36
37
|
disabled_rake_tasks.include?(top_level_task)
|
37
38
|
end
|
@@ -0,0 +1,43 @@
|
|
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/logger'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Utils
|
8
|
+
# A LRU(Least Recently Used) Cache store.
|
9
|
+
class LRUCache
|
10
|
+
def initialize capacity = 500
|
11
|
+
raise StandardError 'Capacity must be bigger than 0' if capacity <= 0
|
12
|
+
|
13
|
+
@capacity = capacity
|
14
|
+
@cache = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def [] key
|
18
|
+
val = @cache.delete(key)
|
19
|
+
@cache[key] = val if val
|
20
|
+
val
|
21
|
+
end
|
22
|
+
|
23
|
+
def []= key, value
|
24
|
+
@cache.delete(key)
|
25
|
+
@cache[key] = value
|
26
|
+
@cache.shift if @cache.size > @capacity
|
27
|
+
value # rubocop:disable Lint/Void
|
28
|
+
end
|
29
|
+
|
30
|
+
def keys
|
31
|
+
@cache.keys
|
32
|
+
end
|
33
|
+
|
34
|
+
def key? key
|
35
|
+
@cache.key?(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def values
|
39
|
+
@cache.values
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/contrast/utils/os.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright (c) 2021 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/scope'
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Utils
|
@@ -9,8 +9,7 @@ module Contrast
|
|
9
9
|
# which will not change at runtime, such as the operating system, the
|
10
10
|
# Utility memozies to avoid multiple lookups.
|
11
11
|
module OS
|
12
|
-
|
13
|
-
access_component :scope
|
12
|
+
extend Contrast::Components::Scope::InstanceMethods
|
14
13
|
|
15
14
|
class << self
|
16
15
|
def running?
|
@@ -36,36 +36,39 @@ module Contrast
|
|
36
36
|
# the replace within the given node.
|
37
37
|
def on_dstr node
|
38
38
|
return if node.children.all? { |child_node| child_node.type == :str }
|
39
|
-
|
40
39
|
new_content = +'('
|
41
|
-
|
40
|
+
idx = 0
|
41
|
+
while idx < node.children.length
|
42
|
+
#node.children.each_with_index do |child_node, index|
|
43
|
+
child_node = node.children[idx]
|
42
44
|
# A begin node looks like #{some_code} in ruby, we do a substring
|
43
45
|
# from [2...-1] to get rid of the #{ & trailing }.
|
44
46
|
if child_node.type == :begin
|
45
47
|
code = child_node.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
location.
|
49
|
+
expression.
|
50
|
+
source_buffer.
|
51
|
+
source[child_node.location.begin.begin_pos...child_node.location.end.end_pos]
|
50
52
|
code = code[2...-1]
|
51
53
|
new_content += "((#{ code }).to_s)"
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
# For interpolations that use class, instance, or global variables,
|
56
|
+
# those are NOT within a begin block, but instead are a ivar, cvar,
|
57
|
+
# or gvar node, not stripping of interpolation markers required.
|
56
58
|
elsif VARIABLES.include?(child_node.type)
|
57
59
|
variable = child_node.children.first
|
58
60
|
new_content << "((#{ variable }).to_s)"
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
62
|
+
# When interpolation includes strings before or after an
|
63
|
+
# interpolation they are simple :str nodes, in order to preserve
|
64
|
+
# escaping we need to do a dump of the string value.
|
63
65
|
elsif child_node.type == :str
|
64
66
|
literal_value = child_node.children.first
|
65
67
|
literal_value = literal_value.dump[1...-1]
|
66
68
|
new_content << "\"#{ literal_value }\""
|
67
69
|
end
|
68
|
-
new_content << ' + ' unless
|
70
|
+
new_content << ' + ' unless idx == node.children.length - 1
|
71
|
+
idx += 1
|
69
72
|
end
|
70
73
|
new_content << ')'
|
71
74
|
if node.location.cs__respond_to?(:heredoc_body)
|
@@ -1,14 +1,13 @@
|
|
1
1
|
# Copyright (c) 2021 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/logger'
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Utils
|
8
8
|
# Utilities for encoding and normalizing strings
|
9
9
|
class StringUtils
|
10
|
-
include Contrast::Components::
|
11
|
-
access_component :logging
|
10
|
+
include Contrast::Components::Logger::InstanceMethods
|
12
11
|
|
13
12
|
UTF8 = 'utf-8'
|
14
13
|
HTTP_PREFIX = 'HTTP_'
|