contrast-agent 4.3.2 → 4.4.0
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.rb +5 -1
- data/lib/contrast/agent/assess.rb +0 -9
- data/lib/contrast/agent/assess/contrast_event.rb +0 -2
- data/lib/contrast/agent/assess/contrast_object.rb +5 -2
- data/lib/contrast/agent/assess/finalizers/hash.rb +7 -0
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +17 -3
- data/lib/contrast/agent/assess/policy/propagation_method.rb +28 -13
- data/lib/contrast/agent/assess/policy/propagator/append.rb +28 -13
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +21 -16
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +23 -13
- data/lib/contrast/agent/assess/policy/propagator/split.rb +14 -7
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +30 -14
- data/lib/contrast/agent/assess/policy/trigger_method.rb +13 -8
- data/lib/contrast/agent/assess/policy/trigger_node.rb +28 -7
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +59 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -2
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +6 -4
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +2 -4
- data/lib/contrast/agent/assess/properties.rb +0 -2
- data/lib/contrast/agent/assess/property/tagged.rb +37 -19
- data/lib/contrast/agent/assess/tracker.rb +1 -1
- data/lib/contrast/agent/middleware.rb +85 -55
- data/lib/contrast/agent/patching/policy/patch_status.rb +1 -1
- data/lib/contrast/agent/patching/policy/patcher.rb +51 -44
- data/lib/contrast/agent/patching/policy/trigger_node.rb +5 -2
- data/lib/contrast/agent/protect/rule/sqli.rb +17 -11
- data/lib/contrast/agent/request_context.rb +12 -0
- data/lib/contrast/agent/thread.rb +1 -1
- data/lib/contrast/agent/thread_watcher.rb +20 -5
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +18 -21
- data/lib/contrast/api/communication/response_processor.rb +8 -1
- data/lib/contrast/api/communication/socket_client.rb +22 -14
- data/lib/contrast/api/decorators.rb +2 -0
- data/lib/contrast/api/decorators/agent_startup.rb +58 -0
- data/lib/contrast/api/decorators/application_startup.rb +51 -0
- data/lib/contrast/api/decorators/route_coverage.rb +15 -5
- data/lib/contrast/api/decorators/trace_event.rb +42 -14
- data/lib/contrast/components/agent.rb +2 -0
- data/lib/contrast/components/app_context.rb +4 -22
- data/lib/contrast/components/sampling.rb +48 -6
- data/lib/contrast/components/settings.rb +5 -4
- data/lib/contrast/framework/manager.rb +13 -12
- data/lib/contrast/framework/rails/support.rb +42 -43
- data/lib/contrast/framework/sinatra/support.rb +100 -41
- data/lib/contrast/logger/log.rb +31 -15
- data/lib/contrast/utils/class_util.rb +3 -1
- data/lib/contrast/utils/heap_dump_util.rb +103 -87
- data/lib/contrast/utils/invalid_configuration_util.rb +21 -12
- data/resources/assess/policy.json +3 -9
- data/resources/deadzone/policy.json +6 -0
- data/ruby-agent.gemspec +54 -16
- metadata +105 -136
- data/lib/contrast/agent/assess/rule.rb +0 -18
- data/lib/contrast/agent/assess/rule/base.rb +0 -52
- data/lib/contrast/agent/assess/rule/redos.rb +0 -67
- data/lib/contrast/framework/sinatra/patch/base.rb +0 -83
- data/lib/contrast/framework/sinatra/patch/support.rb +0 -27
- data/lib/contrast/utils/prevent_serialization.rb +0 -52
@@ -13,7 +13,7 @@ module Contrast
|
|
13
13
|
# one does not exist.
|
14
14
|
#
|
15
15
|
# @param mod [Module] the Module for which the status is asked
|
16
|
-
# @return [Contrast::Agent::Patching::Policy::
|
16
|
+
# @return [Contrast::Agent::Patching::Policy::PatchStatus]
|
17
17
|
def get_status mod
|
18
18
|
if mod.cs__const_defined?(status_key, false)
|
19
19
|
mod.cs__const_get(status_key, false)
|
@@ -52,7 +52,7 @@ module Contrast
|
|
52
52
|
# startup to catchup on everything we didn't see get loaded
|
53
53
|
def patch
|
54
54
|
catchup_after_load_patches
|
55
|
-
|
55
|
+
catchup_loaded_methods
|
56
56
|
Contrast::Agent::Assess::Policy::RewriterPatch.rewrite_interpolations
|
57
57
|
end
|
58
58
|
|
@@ -62,10 +62,11 @@ module Contrast
|
|
62
62
|
# where only a subset of the Assess changes are needed.
|
63
63
|
PATCH_MONITOR = Monitor.new
|
64
64
|
|
65
|
-
|
65
|
+
# Iterate over and patch those Modules and Methods which were loaded before the Agent was enabled.
|
66
|
+
def catchup_loaded_methods
|
66
67
|
PATCH_MONITOR.synchronize do
|
67
68
|
t = Contrast::Agent::Thread.new do
|
68
|
-
|
69
|
+
synchronized_catchup_loaded_methods
|
69
70
|
end
|
70
71
|
# aborting on exception makes exceptions propagate.
|
71
72
|
t.abort_on_exception = true
|
@@ -100,7 +101,7 @@ module Contrast
|
|
100
101
|
# @param mod [Module] the module in which the patch should be
|
101
102
|
# placed.
|
102
103
|
# @param methods [Array(Symbol)] all the instance or singleton
|
103
|
-
# methods in this
|
104
|
+
# methods in this mod.
|
104
105
|
# @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy]
|
105
106
|
# the policy that applies to the given method_name.
|
106
107
|
# @return [Boolean] if patched, either by this invocation or a
|
@@ -149,7 +150,7 @@ module Contrast
|
|
149
150
|
# other functions, like rewriting or scanning. This method should
|
150
151
|
# only be invoked by the patch_methods method above in order to
|
151
152
|
# ensure it is wrapped in a synchronize call
|
152
|
-
def
|
153
|
+
def synchronized_catchup_loaded_methods
|
153
154
|
logger.trace_with_time('Running patching') do
|
154
155
|
patched = []
|
155
156
|
all_module_names.each do |patchable_name|
|
@@ -166,53 +167,37 @@ module Contrast
|
|
166
167
|
end
|
167
168
|
end
|
168
169
|
|
169
|
-
# Given the patchers that apply to this class that may apply, patch
|
170
|
-
#
|
170
|
+
# Given the patchers that apply to this class that may apply, patch Contrast method calls into the methods
|
171
|
+
# for which we have rules.
|
171
172
|
#
|
172
|
-
# @param module_data [Contrast::Agent::ModuleData] the module, and
|
173
|
-
#
|
174
|
-
#
|
175
|
-
# regardless of the state of the
|
176
|
-
# Contrast::Agent::Patching::Policy::PatchStatus status on the
|
177
|
-
# Module
|
173
|
+
# @param module_data [Contrast::Agent::ModuleData] the module, and its name, that's being patched into
|
174
|
+
# @param redo_patch [Boolean] a trigger to force patching regardless of the state of the
|
175
|
+
# Contrast::Agent::Patching::Policy::PatchStatus status on the Module
|
178
176
|
def patch_into_module module_data, redo_patch = false
|
179
177
|
status = status_type.get_status(module_data.mod)
|
180
178
|
return if (status&.patched? || status&.patching?) && !redo_patch
|
181
179
|
|
182
|
-
# Begin patching our sources into the given
|
183
|
-
#
|
184
|
-
# patching.
|
185
|
-
# Find all the patchers that apply to this class, sorted by type.
|
180
|
+
# Begin patching our sources into the given module. Any patcher that has the name of the module will be
|
181
|
+
# evaluated for patching. Find all the patchers that apply to this class, sorted by type.
|
186
182
|
module_policy = Contrast::Agent::Patching::Policy::ModulePolicy.create_module_policy(module_data.name)
|
187
|
-
|
188
|
-
|
183
|
+
# If there's nothing to match, then set that status and exit
|
184
|
+
if module_policy.empty?
|
185
|
+
status.no_patch!
|
186
|
+
return
|
187
|
+
end
|
189
188
|
|
190
189
|
status.patching!
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
unless module_policy.empty?
|
196
|
-
instance_methods = all_instance_methods(clazz, true)
|
197
|
-
singleton_methods = clazz.singleton_methods(false)
|
198
|
-
counts += patch_into_methods(clazz, instance_methods, module_policy, true)
|
199
|
-
counts += patch_into_methods(clazz, singleton_methods, module_policy, false)
|
200
|
-
counts = module_policy.num_expected_patches if adjust_for_prepend(clazz)
|
201
|
-
patched = true
|
202
|
-
end
|
190
|
+
num_applied_patches = patch_into_instance_methods(module_data, module_policy)
|
191
|
+
num_applied_patches += patch_into_singleton_methods(module_data, module_policy)
|
192
|
+
if adjust_for_prepend(module_data) ||
|
193
|
+
module_policy.num_expected_patches == num_applied_patches
|
203
194
|
|
204
|
-
|
205
|
-
if module_policy.num_expected_patches == counts
|
206
|
-
status.patched!
|
207
|
-
else
|
208
|
-
status.partial_patch!
|
209
|
-
end
|
195
|
+
status.patched!
|
210
196
|
else
|
211
|
-
status.
|
197
|
+
status.partial_patch!
|
212
198
|
end
|
213
199
|
rescue StandardError => e
|
214
|
-
status
|
215
|
-
status.failed_patch!
|
200
|
+
status&.failed_patch!
|
216
201
|
logger.warn('Patching failed', e, module: module_data.name)
|
217
202
|
ensure
|
218
203
|
logger.trace('Patching complete',
|
@@ -249,6 +234,29 @@ module Contrast
|
|
249
234
|
instance_methods
|
250
235
|
end
|
251
236
|
|
237
|
+
# Patch into the Instance Methods, including private, of the given Module that match the ModulePolicy
|
238
|
+
# provided.
|
239
|
+
#
|
240
|
+
# @param module_data [Contrast::Agent::ModuleData] the module, and its name, that's being patched into
|
241
|
+
# @param module_policy [Contrast::Agent::Patching::Policy::ModulePolicy] All the patchers that apply to
|
242
|
+
# this module, sorted by type.
|
243
|
+
def patch_into_instance_methods module_data, module_policy
|
244
|
+
mod = module_data.mod
|
245
|
+
methods = all_instance_methods(mod, true)
|
246
|
+
patch_into_methods(mod, methods, module_policy, true)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Patch into the Singleton Methods of the given Module that match the ModulePolicy provided.
|
250
|
+
#
|
251
|
+
# @param module_data [Contrast::Agent::ModuleData] the module, and its name, that's being patched into
|
252
|
+
# @param module_policy [Contrast::Agent::Patching::Policy::ModulePolicy] All the patchers that apply to
|
253
|
+
# this module, sorted by type.
|
254
|
+
def patch_into_singleton_methods module_data, module_policy
|
255
|
+
mod = module_data.mod
|
256
|
+
methods = mod.singleton_methods(false)
|
257
|
+
patch_into_methods(mod, methods, module_policy, false)
|
258
|
+
end
|
259
|
+
|
252
260
|
# We've found the patchers that apply to this class (or module). Now we'll
|
253
261
|
# filter on the given method.
|
254
262
|
#
|
@@ -279,11 +287,10 @@ module Contrast
|
|
279
287
|
# it has to be reapplied.
|
280
288
|
# TODO: RUBY-620 should remove the need for this
|
281
289
|
#
|
282
|
-
# @param
|
283
|
-
# be accounted
|
290
|
+
# @param module_data [Contrast::Agent::ModuleData] the module, and its name, that's being patched into
|
284
291
|
# @return [Boolean] if an adjustment was made or not
|
285
|
-
def adjust_for_prepend
|
286
|
-
return false unless mod.cs__name == 'CGI::Util'
|
292
|
+
def adjust_for_prepend module_data
|
293
|
+
return false unless module_data.mod.cs__name == 'CGI::Util'
|
287
294
|
|
288
295
|
CGI.include(CGI::Util)
|
289
296
|
CGI.extend(CGI::Util)
|
@@ -46,6 +46,11 @@ module Contrast
|
|
46
46
|
raise(ArgumentError,
|
47
47
|
"#{ id } did not have a proper applicator method: #{ applicator } does not respond to #{ applicator_method }. Unable to create.")
|
48
48
|
end
|
49
|
+
validate_properties
|
50
|
+
validate_rule
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_properties
|
49
54
|
if (required_properties & optional_properties).any?
|
50
55
|
raise(ArgumentError, "#{ rule_id } had overlapping elements between required and optional properties. Unable to create.")
|
51
56
|
end
|
@@ -53,8 +58,6 @@ module Contrast
|
|
53
58
|
raise(ArgumentError, "#{ id } had an unexpected property. Unable to create.")
|
54
59
|
end
|
55
60
|
raise(ArgumentError, "#{ id } did not have a required property. Unable to create.") if (required_properties - properties.keys).any?
|
56
|
-
|
57
|
-
validate_rule
|
58
61
|
end
|
59
62
|
|
60
63
|
def validate_rule
|
@@ -50,16 +50,9 @@ module Contrast
|
|
50
50
|
last_boundary, boundary = scanner.crosses_boundary(query_string, idx, input_analysis_result.value)
|
51
51
|
next unless last_boundary && boundary
|
52
52
|
|
53
|
-
input_analysis_result.attack_count = input_analysis_result.attack_count + 1
|
54
|
-
|
55
|
-
kwargs[:start_idx] = idx
|
56
|
-
kwargs[:end_idx] = idx + length
|
57
|
-
kwargs[:boundary_overrun_idx] = boundary
|
58
|
-
kwargs[:input_boundary_idx] = last_boundary
|
59
|
-
|
60
53
|
result ||= build_attack_result(context)
|
61
|
-
|
62
|
-
|
54
|
+
record_match(idx, length, boundary, last_boundary, kwargs)
|
55
|
+
append_match(context, input_analysis_result, result, query_string, kwargs)
|
63
56
|
end
|
64
57
|
|
65
58
|
result
|
@@ -75,13 +68,26 @@ module Contrast
|
|
75
68
|
sample.sqli.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
|
76
69
|
sample.sqli.start_idx = sample.sqli.query.index(input).to_i
|
77
70
|
sample.sqli.end_idx = sample.sqli.start_idx + input.length
|
78
|
-
sample.sqli.boundary_overrun_idx = kwargs[:
|
79
|
-
sample.sqli.input_boundary_idx = kwargs[:
|
71
|
+
sample.sqli.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
|
72
|
+
sample.sqli.input_boundary_idx = kwargs[:input_boundary_idx].to_i
|
80
73
|
sample
|
81
74
|
end
|
82
75
|
|
83
76
|
private
|
84
77
|
|
78
|
+
def record_match idx, length, boundary, last_boundary, kwargs
|
79
|
+
kwargs[:start_idx] = idx
|
80
|
+
kwargs[:end_idx] = idx + length
|
81
|
+
kwargs[:boundary_overrun_idx] = boundary
|
82
|
+
kwargs[:input_boundary_idx] = last_boundary
|
83
|
+
end
|
84
|
+
|
85
|
+
def append_match context, input_analysis_result, result, query_string, **kwargs
|
86
|
+
input_analysis_result.attack_count = input_analysis_result.attack_count + 1
|
87
|
+
update_successful_attack_response(context, input_analysis_result, result, query_string)
|
88
|
+
append_sample(context, input_analysis_result, result, query_string, **kwargs)
|
89
|
+
end
|
90
|
+
|
85
91
|
def select_scanner database
|
86
92
|
@sql_scanners ||= {
|
87
93
|
Contrast::Agent::Protect::Policy::AppliesSqliRule::DATABASE_MYSQL =>
|
@@ -12,6 +12,18 @@ module Contrast
|
|
12
12
|
# This class acts to encapsulate information about the currently executed
|
13
13
|
# request, making it available to the Agent for the duration of the request
|
14
14
|
# in a standardized and normalized format which the Agent understands.
|
15
|
+
#
|
16
|
+
# @attr_reader timer [Contrast::Utils::Timer] when the context was created
|
17
|
+
# @attr_reader logging_hash [Hash] context used to log the request
|
18
|
+
# @attr_reader speedracer_input_analysis [Contrast::Api::Settings::InputAnalysis] the protect input analysis of
|
19
|
+
# sources on this request
|
20
|
+
# @attr_reader request [Contrast::Agent::Request] our wrapper around the Rack::Request for this context
|
21
|
+
# @attr_reader response [Contrast::Agent::Response] our wrapper aroudn the Rack::Response or Array for this context,
|
22
|
+
# only available after the application has finished its processing
|
23
|
+
# @attr_reader activity [Contrast::Api::Dtm::Activity] the application activity found in this request
|
24
|
+
# @attr_reader server_activity [Contrast::Api::Dtm::ServerActivity] the server activity found in this request
|
25
|
+
# @attr_reader route [Contrast::Api::Dtm::RouteCoverage] the route, used for findings, of this request
|
26
|
+
# @attr_reader observed_route [Contrast::Api::Dtm::ObservedRoute] the route, used for coverage, of this request
|
15
27
|
class RequestContext
|
16
28
|
include Contrast::Components::Interface
|
17
29
|
access_component :agent, :analysis, :logging, :scope
|
@@ -5,7 +5,7 @@ require 'contrast/components/interface'
|
|
5
5
|
|
6
6
|
module Contrast
|
7
7
|
module Agent
|
8
|
-
# Threads used by Contrast.
|
8
|
+
# Threads used by Contrast. Any long running thread should be created and managed by our ThreadWatcher class.
|
9
9
|
class Thread < ::Thread
|
10
10
|
include Contrast::Components::Interface
|
11
11
|
|
@@ -3,22 +3,31 @@
|
|
3
3
|
|
4
4
|
require 'contrast/components/interface'
|
5
5
|
require 'contrast/agent/service_heartbeat'
|
6
|
+
require 'contrast/api/communication/messaging_queue'
|
6
7
|
|
7
8
|
module Contrast
|
8
9
|
module Agent
|
9
10
|
# This class used to ensure that our worker threads are running in multi-process environments
|
11
|
+
#
|
12
|
+
# @attr_reader heapdump_util [Contrast::Utils::HeapDumpUtil]
|
13
|
+
# @attr_reader heartbeat [Contrast::Agent::ServiceHeartbeat]
|
14
|
+
# @attr_reader messaging_queue [Contrast::Api::Communication::MessagingQueue]
|
10
15
|
class ThreadWatcher
|
11
16
|
include Contrast::Components::Interface
|
12
|
-
access_component :logging
|
17
|
+
access_component :agent, :logging
|
13
18
|
|
14
|
-
attr_reader :heartbeat
|
19
|
+
attr_reader :heapdump_util, :heartbeat, :messaging_queue
|
15
20
|
|
16
21
|
def initialize
|
17
22
|
@pids = {}
|
23
|
+
@heapdump_util = Contrast::Utils::HeapDumpUtil.new
|
18
24
|
@heartbeat = Contrast::Agent::ServiceHeartbeat.new
|
25
|
+
@messaging_queue = Contrast::Api::Communication::MessagingQueue.new
|
19
26
|
end
|
20
27
|
|
21
28
|
def startup!
|
29
|
+
return unless AGENT.enabled?
|
30
|
+
|
22
31
|
unless heartbeat.running?
|
23
32
|
logger.debug('Attempting to start heartbeat thread')
|
24
33
|
heartbeat.start_thread!
|
@@ -26,11 +35,11 @@ module Contrast
|
|
26
35
|
heartbeat_result = heartbeat.running?
|
27
36
|
logger.debug('Heartbeat thread status', alive: heartbeat_result)
|
28
37
|
|
29
|
-
unless
|
38
|
+
unless messaging_queue.running?
|
30
39
|
logger.debug('Attempting to start messaging queue thread')
|
31
|
-
|
40
|
+
messaging_queue.start_thread!
|
32
41
|
end
|
33
|
-
messaging_result =
|
42
|
+
messaging_result = messaging_queue.running?
|
34
43
|
logger.debug('Messaging thread status', alive: messaging_result)
|
35
44
|
|
36
45
|
logger.debug('ThreadWatcher started threads')
|
@@ -44,6 +53,12 @@ module Contrast
|
|
44
53
|
logger.debug('ThreadWatcher - threads not running')
|
45
54
|
startup!
|
46
55
|
end
|
56
|
+
|
57
|
+
def shutdown!
|
58
|
+
heartbeat.stop!
|
59
|
+
messaging_queue.stop!
|
60
|
+
heapdump_util.stop!
|
61
|
+
end
|
47
62
|
end
|
48
63
|
end
|
49
64
|
end
|
@@ -10,7 +10,7 @@ module Contrast
|
|
10
10
|
# Top level gateway to messaging with speedracer
|
11
11
|
class MessagingQueue < Contrast::Agent::WorkerThread
|
12
12
|
include Contrast::Components::Interface
|
13
|
-
access_component :analysis, :logging, :settings
|
13
|
+
access_component :agent, :analysis, :logging, :settings
|
14
14
|
|
15
15
|
attr_reader :queue, :speedracer
|
16
16
|
|
@@ -22,11 +22,19 @@ module Contrast
|
|
22
22
|
|
23
23
|
# Use this to bypass the messaging queue and leave response processing to the caller
|
24
24
|
def send_event_immediately event
|
25
|
-
|
25
|
+
if AGENT.disabled?
|
26
|
+
logger.warn('Attempted to send event immediately with Agent disabled', caller: caller, event: event)
|
27
|
+
return
|
28
|
+
end
|
29
|
+
speedracer.return_response(event)
|
26
30
|
end
|
27
31
|
|
28
32
|
# Use this to add a message to the queue and process the response internally
|
29
33
|
def send_event_eventually event
|
34
|
+
if AGENT.disabled?
|
35
|
+
logger.warn('Attempted to queue event with Agent disabled', caller: caller, event: event)
|
36
|
+
return
|
37
|
+
end
|
30
38
|
logger.debug('Enqueued event for sending', event_type: event.cs__class)
|
31
39
|
queue << event if event
|
32
40
|
end
|
@@ -35,13 +43,14 @@ module Contrast
|
|
35
43
|
speedracer.ensure_startup!
|
36
44
|
return if running?
|
37
45
|
|
46
|
+
@queue ||= Queue.new
|
38
47
|
@_thread = Contrast::Agent::Thread.new do
|
39
48
|
loop do
|
40
49
|
event = queue.pop
|
41
50
|
|
42
51
|
begin
|
43
52
|
logger.debug('Dequeued event for sending', event_type: event.cs__class)
|
44
|
-
|
53
|
+
speedracer.process_internally(event)
|
45
54
|
rescue StandardError => e
|
46
55
|
logger.error('Could not send message to service from messaging queue thread.', e)
|
47
56
|
end
|
@@ -50,25 +59,13 @@ module Contrast
|
|
50
59
|
logger.debug('Started background sending thread.')
|
51
60
|
end
|
52
61
|
|
53
|
-
|
62
|
+
def stop!
|
63
|
+
return unless running?
|
54
64
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
speedracer.return_response(event)
|
60
|
-
else
|
61
|
-
speedracer.process_internally(event)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# For now this only handles appending assess tags
|
66
|
-
# eventually we could break out preprocessors for every event type
|
67
|
-
def preprocess_event event
|
68
|
-
return unless event.is_a?(Contrast::Api::Dtm::Activity)
|
69
|
-
|
70
|
-
# See if they're even enabled
|
71
|
-
event.findings.delete_if { |finding| ASSESS.rule_disabled?(finding.rule_id) }
|
65
|
+
super
|
66
|
+
@queue&.clear
|
67
|
+
@queue&.close
|
68
|
+
@queue = nil
|
72
69
|
end
|
73
70
|
end
|
74
71
|
end
|
@@ -11,6 +11,7 @@ module Contrast
|
|
11
11
|
include Contrast::Components::Interface
|
12
12
|
access_component :agent, :analysis, :logging, :settings
|
13
13
|
|
14
|
+
# @param response [Contrast::Api::Settings::AgentSettings]
|
14
15
|
def process response
|
15
16
|
logger.debug('Received a response', sent_ms: response&.sent_ms)
|
16
17
|
|
@@ -31,8 +32,10 @@ module Contrast
|
|
31
32
|
|
32
33
|
private
|
33
34
|
|
34
|
-
# Given some protobuf messages, update
|
35
|
+
# Given some protobuf messages, update server features.
|
35
36
|
# This is the bridge between Contrast Service <-> Settings.
|
37
|
+
#
|
38
|
+
# @param response [Contrast::Api::Settings::AgentSettings]
|
36
39
|
def process_server_response response
|
37
40
|
server_features = response&.server_features
|
38
41
|
return unless server_features
|
@@ -44,6 +47,10 @@ module Contrast
|
|
44
47
|
server_features
|
45
48
|
end
|
46
49
|
|
50
|
+
# Given some protobuf messages, update application settings.
|
51
|
+
# This is the bridge between Contrast Service <-> Settings.
|
52
|
+
#
|
53
|
+
# @param response [Contrast::Api::Settings::AgentSettings]
|
47
54
|
def process_application_response response
|
48
55
|
app_settings = response&.application_settings
|
49
56
|
return unless app_settings
|
@@ -26,6 +26,7 @@ module Contrast
|
|
26
26
|
#
|
27
27
|
# @param event [Contrast::Api::Dtm] One of the DTMs valid for the event field of
|
28
28
|
# Contrast::Api::Dtm::Message
|
29
|
+
# @return [Contrast::Api::Settings::AgentSettings]
|
29
30
|
def send_one event
|
30
31
|
msg = Contrast::Api::Dtm::Message.build(event)
|
31
32
|
send_message(msg)
|
@@ -58,24 +59,31 @@ module Contrast
|
|
58
59
|
end
|
59
60
|
|
60
61
|
# Or something is not set.
|
61
|
-
|
62
|
-
'Missing a required connection value to the Contrast Service. ' \
|
63
|
-
'`agent.service.port` is not set. ' \
|
64
|
-
'Falling back to default TCP socket port.'
|
65
|
-
elsif CONFIG.root.agent.service.port
|
66
|
-
'Missing a required connection value to the Contrast Service. ' \
|
67
|
-
'`agent.service.host` is not set. ' \
|
68
|
-
'Falling back to default TCP socket host.'
|
69
|
-
else
|
70
|
-
'Missing a required connection value to the Contrast Service. ' \
|
71
|
-
'Neither `agent.service.socket` nor the pair of `agent.service.host` and `agent.service.port` are set. '\
|
72
|
-
'Falling back to default TCP socket.'
|
73
|
-
end
|
74
|
-
logger.warn(msg,
|
62
|
+
logger.warn(log_connection_error_msg,
|
75
63
|
host: CONTRAST_SERVICE.host,
|
76
64
|
port: CONTRAST_SERVICE.port)
|
77
65
|
end
|
78
66
|
|
67
|
+
# If our connection isn't built properly, we need to warn the user. This builds out the context specific
|
68
|
+
# message to provide that warning
|
69
|
+
#
|
70
|
+
# @return [String]
|
71
|
+
def log_connection_error_msg
|
72
|
+
if CONFIG.root.agent.service.host
|
73
|
+
'Missing a required connection value to the Contrast Service. ' \
|
74
|
+
'`agent.service.port` is not set. ' \
|
75
|
+
'Falling back to default TCP socket port.'
|
76
|
+
elsif CONFIG.root.agent.service.port
|
77
|
+
'Missing a required connection value to the Contrast Service. ' \
|
78
|
+
'`agent.service.host` is not set. ' \
|
79
|
+
'Falling back to default TCP socket host.'
|
80
|
+
else
|
81
|
+
'Missing a required connection value to the Contrast Service. ' \
|
82
|
+
'Neither `agent.service.socket` nor the pair of `agent.service.host` and `agent.service.port` are set. '\
|
83
|
+
'Falling back to default TCP socket.'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
79
87
|
def send_message msg
|
80
88
|
return unless msg
|
81
89
|
|