contrast-agent 4.9.1 → 4.10.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/.rspec +0 -1
- data/.rspec_parallel +6 -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 +0 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +0 -1
- data/lib/contrast/agent/assess/policy/patcher.rb +0 -1
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +0 -2
- data/lib/contrast/agent/assess/policy/preshift.rb +8 -5
- data/lib/contrast/agent/assess/policy/propagation_method.rb +100 -57
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +0 -2
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
- data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -2
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -0
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +0 -1
- data/lib/contrast/agent/assess/policy/source_method.rb +13 -17
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +59 -83
- data/lib/contrast/agent/assess/property/evented.rb +2 -1
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +0 -1
- data/lib/contrast/agent/disable_reaction.rb +1 -1
- data/lib/contrast/agent/exclusion_matcher.rb +0 -4
- data/lib/contrast/agent/inventory/database_config.rb +117 -0
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +5 -4
- data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -0
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +18 -12
- data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/patch.rb +5 -0
- data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +8 -8
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
- 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 +1 -1
- data/lib/contrast/agent/request.rb +5 -2
- data/lib/contrast/agent/request_context.rb +19 -22
- data/lib/contrast/agent/static_analysis.rb +1 -1
- data/lib/contrast/agent/tracepoint_hook.rb +6 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +12 -6
- data/lib/contrast/api/communication/service_lifecycle.rb +4 -1
- data/lib/contrast/api/communication/socket_client.rb +4 -4
- data/lib/contrast/api/decorators/agent_startup.rb +4 -4
- data/lib/contrast/api/decorators/application_startup.rb +6 -5
- data/lib/contrast/api/decorators/route_coverage.rb +24 -1
- data/lib/contrast/components/agent.rb +5 -2
- data/lib/contrast/components/assess.rb +6 -3
- data/lib/contrast/components/base.rb +2 -2
- data/lib/contrast/components/config.rb +1 -0
- data/lib/contrast/components/contrast_service.rb +4 -2
- data/lib/contrast/components/logger.rb +13 -8
- data/lib/contrast/components/scope.rb +9 -28
- data/lib/contrast/config/base_configuration.rb +14 -6
- data/lib/contrast/configuration.rb +19 -15
- data/lib/contrast/extension/assess/array.rb +1 -11
- data/lib/contrast/extension/assess/eval_trigger.rb +0 -20
- data/lib/contrast/extension/assess/fiber.rb +0 -11
- data/lib/contrast/extension/assess/hash.rb +0 -10
- data/lib/contrast/extension/assess/kernel.rb +1 -10
- data/lib/contrast/extension/assess/marshal.rb +3 -11
- data/lib/contrast/extension/assess/regexp.rb +0 -11
- data/lib/contrast/extension/assess/string.rb +1 -26
- data/lib/contrast/extension/extension.rb +61 -0
- data/lib/contrast/extension/protect/kernel.rb +0 -10
- data/lib/contrast/framework/grape/support.rb +174 -0
- data/lib/contrast/framework/manager.rb +42 -6
- data/lib/contrast/framework/rack/support.rb +1 -1
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +0 -1
- data/lib/contrast/framework/rails/patch/support.rb +6 -3
- data/lib/contrast/framework/rails/railtie.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +1 -0
- data/lib/contrast/framework/rails/support.rb +60 -13
- data/lib/contrast/framework/sinatra/support.rb +1 -1
- data/lib/contrast/logger/log.rb +89 -15
- data/lib/contrast/utils/io_util.rb +1 -1
- data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
- data/lib/contrast/utils/tag_util.rb +2 -1
- data/resources/assess/policy.json +197 -2
- data/resources/deadzone/policy.json +10 -0
- data/ruby-agent.gemspec +10 -1
- metadata +78 -12
- data/lib/contrast/utils/inventory_util.rb +0 -113
@@ -17,7 +17,6 @@ module Contrast
|
|
17
17
|
class Split < Contrast::Agent::Assess::Policy::Propagator::Base
|
18
18
|
extend Contrast::Components::Scope::InstanceMethods
|
19
19
|
extend Contrast::Components::Logger::InstanceMethods
|
20
|
-
#cs__const_set('AGENT', Contrast::AGENT)
|
21
20
|
|
22
21
|
SPLIT_TRACKER = Contrast::Utils::ThreadTracker.new
|
23
22
|
|
@@ -111,7 +110,9 @@ module Contrast
|
|
111
110
|
# Load patch.
|
112
111
|
def instrument_string_split
|
113
112
|
@_instrument_string_split ||= begin
|
114
|
-
|
113
|
+
if ::Contrast::AGENT.patch_yield? && Funchook.available?
|
114
|
+
require 'cs__assess_yield_track/cs__assess_yield_track'
|
115
|
+
end
|
115
116
|
true
|
116
117
|
rescue StandardError => e
|
117
118
|
logger.error('Error loading split rb_yield patch', e)
|
@@ -16,6 +16,7 @@ module Contrast
|
|
16
16
|
# a 'get it right' state soon.
|
17
17
|
class Substitution
|
18
18
|
include Contrast::Components::Logger::InstanceMethods
|
19
|
+
extend Contrast::Components::Logger::InstanceMethods
|
19
20
|
|
20
21
|
CAPTURE_GROUP_REGEXP = /\\[[:digit:]]/.cs__freeze
|
21
22
|
CAPTURE_NAME_REGEXP = /\\k<[[:alpha:]]/.cs__freeze
|
@@ -17,7 +17,6 @@ module Contrast
|
|
17
17
|
module SourceMethod
|
18
18
|
extend Contrast::Components::Logger::InstanceMethods
|
19
19
|
|
20
|
-
|
21
20
|
PARAMETER_TYPE = 'PARAMETER'
|
22
21
|
PARAMETER_KEY_TYPE = 'PARAMETER_KEY'
|
23
22
|
HEADER_TYPE = 'HEADER'
|
@@ -26,8 +25,7 @@ module Contrast
|
|
26
25
|
COOKIE_KEY_TYPE = 'COOKIE_KEY'
|
27
26
|
|
28
27
|
class << self
|
29
|
-
# This is called from within our woven proc. It will be called as if it were inline in the Rack
|
30
|
-
# application.
|
28
|
+
# This is called from within our woven proc. It will be called as if it were inline in the Rack application.
|
31
29
|
#
|
32
30
|
# @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] the policy that applies to the
|
33
31
|
# method being called
|
@@ -37,29 +35,27 @@ module Contrast
|
|
37
35
|
# @return [Object, nil] the tracked Return or nil if no changes were made
|
38
36
|
def source_patchers method_policy, object, ret, args
|
39
37
|
return unless analyze?(method_policy, object, ret, args)
|
38
|
+
return unless (source_node = method_policy.source_node)
|
39
|
+
return unless (target = determine_target(source_node, object, ret, args))
|
40
40
|
|
41
|
-
|
42
|
-
target = determine_target(source_node, object, ret, args)
|
43
|
-
restore_frozen_state = false
|
41
|
+
return_val = nil
|
44
42
|
if target.cs__frozen? && !Contrast::Agent::Assess::Tracker.trackable?(target)
|
45
43
|
return unless ::Contrast::ASSESS.track_frozen_sources?
|
46
44
|
return unless source_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
|
45
|
+
return unless (dup = safe_dup(ret))
|
46
|
+
return unless Contrast::Agent::Assess::Tracker.trackable?(dup)
|
47
47
|
|
48
|
-
dup
|
49
|
-
|
50
|
-
|
51
|
-
restore_frozen_state = true
|
52
|
-
ret = dup
|
53
|
-
target = ret
|
54
|
-
Contrast::Agent::Assess::Tracker.pre_freeze(ret)
|
55
|
-
ret.cs__freeze
|
56
|
-
# double check that we were able to finalize the replaced return
|
57
|
-
return unless Contrast::Agent::Assess::Tracker.trackable?(target)
|
48
|
+
Contrast::Agent::Assess::Tracker.pre_freeze(dup)
|
49
|
+
return_val = dup.cs__freeze
|
50
|
+
target = dup
|
58
51
|
end
|
52
|
+
|
59
53
|
apply_source(Contrast::Agent::REQUEST_TRACKER.current, source_node, target, object, ret,
|
60
54
|
source_node.type, nil, *args)
|
61
|
-
|
55
|
+
|
56
|
+
return_val
|
62
57
|
end
|
58
|
+
Contrast::Components::Logger.add_trace_log_timing_for(SourceMethod, :source_patchers)
|
63
59
|
|
64
60
|
private
|
65
61
|
|
@@ -11,30 +11,28 @@ module Contrast
|
|
11
11
|
module Agent
|
12
12
|
module Assess
|
13
13
|
module Policy
|
14
|
-
# A trigger method is one which can perform a dangerous action, as
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# it was not, a Finding report is issued to the Service
|
14
|
+
# A trigger method is one which can perform a dangerous action, as described by the
|
15
|
+
# Contrast::Agent::Assess::Policy::TriggerNode class. Each such method will call to this module just after
|
16
|
+
# invocation in order to determine if the call was done safely. In those cases where it was not, a Finding
|
17
|
+
# report is issued to the Service.
|
19
18
|
module TriggerMethod
|
20
19
|
extend Contrast::Components::Logger::InstanceMethods
|
21
20
|
|
22
|
-
# The level of TeamServer compliance our traces meet when in the
|
23
|
-
#
|
21
|
+
# The level of TeamServer compliance our traces meet when in the abnormal condition of being dataflow rules
|
22
|
+
# without routes.
|
24
23
|
MINIMUM_FINDING_VERSION = 3
|
25
|
-
# The level of TeamServer compliance our traces meet
|
24
|
+
# The level of TeamServer compliance our traces meet.
|
26
25
|
CURRENT_FINDING_VERSION = 4
|
27
26
|
|
28
27
|
class << self
|
29
|
-
# This is called from within our woven proc. It will be called as if it
|
30
|
-
#
|
28
|
+
# This is called from within our woven proc. It will be called as if it were inline in the Rack
|
29
|
+
# application.
|
31
30
|
#
|
32
|
-
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
33
|
-
#
|
31
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node that applies to the method
|
32
|
+
# being called
|
34
33
|
# @param object [Object] the Object on which the method was invoked
|
35
34
|
# @param ret [Object] the Return of the invoked method
|
36
|
-
# @param args [Array<Object>] the Arguments with which the method
|
37
|
-
# was invoked
|
35
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
38
36
|
def apply_trigger_rule trigger_node, object, ret, args
|
39
37
|
return if trigger_node.nil?
|
40
38
|
|
@@ -52,19 +50,16 @@ module Contrast
|
|
52
50
|
apply_trigger(trigger_node, source, object, ret, *args)
|
53
51
|
end
|
54
52
|
|
55
|
-
# This converts the source of the finding, and the events leading
|
56
|
-
# up to it into a Finding
|
53
|
+
# This converts the source of the finding, and the events leading up to it into a Finding
|
57
54
|
#
|
58
|
-
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
59
|
-
#
|
55
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
56
|
+
# trigger event
|
60
57
|
# @param source [Object] the source of the Trigger Event
|
61
58
|
# @param object [Object] the Object on which the method was invoked
|
62
59
|
# @param ret [Object] the Return of the invoked method
|
63
|
-
# @param args [Array<Object>] the Arguments with which the method
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# Contrast::Api::Dtm::Finding to send to TeamServer or nil if
|
67
|
-
# conditions were not met
|
60
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
61
|
+
# @return [Contrast::Api::Dtm::Finding, nil] the Contrast::Api::Dtm::Finding to send to TeamServer or
|
62
|
+
# nil if conditions were not met
|
68
63
|
def build_finding trigger_node, source, object, ret, *args
|
69
64
|
return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
|
70
65
|
|
@@ -85,13 +80,12 @@ module Contrast
|
|
85
80
|
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
|
86
81
|
end
|
87
82
|
|
88
|
-
# Given a finding, append it to an activity message and send it to
|
89
|
-
#
|
83
|
+
# Given a finding, append it to an activity message and send it to the Service for processing. If an
|
84
|
+
# activity message does not exist, b/c we're invoked outside of a request context, build an activity and
|
85
|
+
# immediately report it with the finding.
|
90
86
|
#
|
91
|
-
# @param finding [Contrast::Api::Dtm::Finding] the Finding to
|
92
|
-
#
|
93
|
-
# @param request [Contrast::Agent::Request] our wrapper around the
|
94
|
-
# Rack::Request.
|
87
|
+
# @param finding [Contrast::Api::Dtm::Finding] the Finding to report.
|
88
|
+
# @param request [Contrast::Agent::Request] our wrapper around the Rack::Request.
|
95
89
|
def report_finding finding, request = nil
|
96
90
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
97
91
|
if context
|
@@ -107,9 +101,8 @@ module Contrast
|
|
107
101
|
logger.debug('Attempted to report finding without request', finding: finding)
|
108
102
|
end
|
109
103
|
|
110
|
-
# If we're out of request context, then we need to report this
|
111
|
-
#
|
112
|
-
# created.
|
104
|
+
# If we're out of request context, then we need to report this finding ourselves, so we'll send it in the
|
105
|
+
# one-off activity we created.
|
113
106
|
Contrast::Agent.messaging_queue.send_event_eventually(activity)
|
114
107
|
end
|
115
108
|
|
@@ -125,15 +118,12 @@ module Contrast
|
|
125
118
|
env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live))
|
126
119
|
end
|
127
120
|
|
128
|
-
# Find the request for this finding. This assumes, for now, that if
|
129
|
-
#
|
130
|
-
# Otherwise, we'll use the first request found in the events of the
|
121
|
+
# Find the request for this finding. This assumes, for now, that if there is an active request, then that
|
122
|
+
# is the request to report. Otherwise, we'll use the first request found in the events of the
|
131
123
|
# source_object.
|
132
124
|
#
|
133
|
-
# @param source [Object,nil] some Object used as the source of a
|
134
|
-
#
|
135
|
-
# @return [Contrast::Agent::Request,nil] the request from which the
|
136
|
-
# dataflow on the request originated.
|
125
|
+
# @param source [Object,nil] some Object used as the source of a trigger event
|
126
|
+
# @return [Contrast::Agent::Request,nil] the request from which the dataflow on the request originated.
|
137
127
|
def find_request source
|
138
128
|
return Contrast::Agent::REQUEST_TRACKER.current.request if Contrast::Agent::REQUEST_TRACKER.current
|
139
129
|
return unless (properties = Contrast::Agent::Assess::Tracker.properties(source))
|
@@ -150,16 +140,14 @@ module Contrast
|
|
150
140
|
Contrast::Agent::FeatureState.instance
|
151
141
|
end
|
152
142
|
|
153
|
-
# This is our method that actually checks the taint on the object
|
154
|
-
# our trigger_node targets.
|
143
|
+
# This is our method that actually checks the taint on the object our trigger_node targets.
|
155
144
|
#
|
156
|
-
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
157
|
-
#
|
145
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
146
|
+
# trigger event
|
158
147
|
# @param source [Object] the source of the Trigger Event
|
159
148
|
# @param object [Object] the Object on which the method was invoked
|
160
149
|
# @param ret [Object] the Return of the invoked method
|
161
|
-
# @param args [Array<Object>] the Arguments with which the method
|
162
|
-
# was invoked
|
150
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
163
151
|
def apply_trigger trigger_node, source, object, ret, *args
|
164
152
|
return unless trigger_node
|
165
153
|
return if trigger_node.rule_disabled?
|
@@ -178,23 +166,17 @@ module Contrast
|
|
178
166
|
logger.warn('Unable to apply trigger', e, node_id: trigger_node.id)
|
179
167
|
end
|
180
168
|
|
181
|
-
# Given the marker from the trigger_node (the pointer indicating
|
182
|
-
# the entity
|
183
|
-
# which this trigger needs to operate.
|
169
|
+
# Given the marker from the trigger_node (the pointer indicating the entity from which the taint
|
170
|
+
# originated), return the entity on which this trigger needs to operate.
|
184
171
|
#
|
185
|
-
# In an effort to speed up this lookup, we've changed the marker
|
186
|
-
#
|
187
|
-
# object, it must be a parameter, which we can reference either by
|
188
|
-
# index or by name.
|
172
|
+
# In an effort to speed up this lookup, we've changed the marker for parameters to be implicit - if it is
|
173
|
+
# not a return or an object, it must be a parameter, which we can reference by index.
|
189
174
|
#
|
190
|
-
# @param marker [String] the source marker that indicates which
|
191
|
-
# Object
|
175
|
+
# @param marker [String] the source marker that indicates which Object
|
192
176
|
# @param object [Object] the Object on which the method was invoked
|
193
177
|
# @param ret [Object] the Return of the invoked method
|
194
|
-
# @param args [Array<Object>] the Arguments with which the method
|
195
|
-
#
|
196
|
-
# @return [Object] the literal object that this Trigger Event
|
197
|
-
# verifies
|
178
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
179
|
+
# @return [Object] the literal object that this Trigger Event verifies
|
198
180
|
def determine_source marker, object, ret, args
|
199
181
|
case marker
|
200
182
|
when Contrast::Utils::ObjectShare::RETURN_KEY
|
@@ -217,16 +199,15 @@ module Contrast
|
|
217
199
|
end
|
218
200
|
end
|
219
201
|
|
220
|
-
# This is our method that actually checks the taint on the object
|
221
|
-
#
|
202
|
+
# This is our method that actually checks the taint on the object our trigger_node targets for our Regexp
|
203
|
+
# based rules.
|
222
204
|
#
|
223
|
-
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
224
|
-
#
|
205
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
206
|
+
# trigger event
|
225
207
|
# @param source [Object] the source of the Trigger Event
|
226
208
|
# @param object [Object] the Object on which the method was invoked
|
227
209
|
# @param ret [Object] the Return of the invoked method
|
228
|
-
# @param args [Array<Object>] the Arguments with which the method
|
229
|
-
# was invoked
|
210
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
230
211
|
def apply_regexp_rule trigger_node, source, object, ret, *args
|
231
212
|
return unless source.is_a?(String)
|
232
213
|
return if trigger_node.good_value && source.match?(trigger_node.good_value)
|
@@ -235,16 +216,15 @@ module Contrast
|
|
235
216
|
build_finding(trigger_node, source, object, ret, *args)
|
236
217
|
end
|
237
218
|
|
238
|
-
# This is our method that actually checks the taint on the object
|
239
|
-
#
|
219
|
+
# This is our method that actually checks the taint on the object our trigger_node targets for our Dataflow
|
220
|
+
# based rules.
|
240
221
|
#
|
241
|
-
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
242
|
-
#
|
222
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
223
|
+
# trigger event
|
243
224
|
# @param source [Object] the source of the Trigger Event
|
244
225
|
# @param object [Object] the Object on which the method was invoked
|
245
226
|
# @param ret [Object] the Return of the invoked method
|
246
|
-
# @param args [Array<Object>] the Arguments with which the method
|
247
|
-
# was invoked
|
227
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
248
228
|
def apply_dataflow_rule trigger_node, source, object, ret, *args
|
249
229
|
return unless source
|
250
230
|
|
@@ -287,8 +267,7 @@ module Contrast
|
|
287
267
|
|
288
268
|
build_events finding, properties.event if properties.event
|
289
269
|
|
290
|
-
# Google::Protobuf::Map doesn't support merge!, so we have to do this
|
291
|
-
# long form
|
270
|
+
# Google::Protobuf::Map doesn't support merge!, so we have to do this long form
|
292
271
|
source_props = properties.properties
|
293
272
|
return unless source_props
|
294
273
|
|
@@ -304,8 +283,8 @@ module Contrast
|
|
304
283
|
event.parent_events&.each do |parent_event|
|
305
284
|
build_events(finding, parent_event)
|
306
285
|
end
|
307
|
-
# events could technically be nil, but we would have failed
|
308
|
-
#
|
286
|
+
# events could technically be nil, but we would have failed the rule check before getting here. not
|
287
|
+
# worth the nil check
|
309
288
|
finding.events << event.to_dtm_event
|
310
289
|
end
|
311
290
|
|
@@ -324,21 +303,18 @@ module Contrast
|
|
324
303
|
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
325
304
|
end
|
326
305
|
|
327
|
-
# Because our APIs are not versioned, TeamServer relies on a field,
|
328
|
-
#
|
329
|
-
#
|
330
|
-
# level to which it conforms.
|
306
|
+
# Because our APIs are not versioned, TeamServer relies on a field, version, to tell it what, if any,
|
307
|
+
# validation it can preform on the findings we send it. Examine the finding and determine the level to
|
308
|
+
# which it conforms.
|
331
309
|
#
|
332
310
|
# @param finding [Contrast::Api::Dtm::Finding]
|
333
311
|
# @return [int] the version of compliance
|
334
312
|
def determine_compliance_version finding
|
335
313
|
return MINIMUM_FINDING_VERSION unless finding
|
336
|
-
# as routes are the only variable between findings, in the case
|
337
|
-
#
|
338
|
-
# maximum compliance
|
314
|
+
# as routes are the only variable between findings, in the case where we couldn't determine one, any
|
315
|
+
# finding with a route is at maximum compliance
|
339
316
|
return CURRENT_FINDING_VERSION if finding.routes.any?
|
340
|
-
# any finding without events is not of a dataflow type and
|
341
|
-
# therefore at maximum compliance
|
317
|
+
# any finding without events is not of a dataflow type and therefore at maximum compliance
|
342
318
|
return CURRENT_FINDING_VERSION unless finding.events.any?
|
343
319
|
|
344
320
|
MINIMUM_FINDING_VERSION
|
@@ -50,7 +50,8 @@ module Contrast
|
|
50
50
|
return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
|
51
51
|
|
52
52
|
if current_request.observed_route.sources.any? do |source|
|
53
|
-
source.type == event.forced_source_type &&
|
53
|
+
source.type == event.forced_source_type &&
|
54
|
+
source.name == event.forced_source_name # rubocop:disable Security/Module/Name
|
54
55
|
end
|
55
56
|
|
56
57
|
return
|
@@ -8,7 +8,7 @@ module Contrast
|
|
8
8
|
# A Reaction from TeamServer which indicates the Agent should be disabled,
|
9
9
|
# typically because some configuration setting did not satisfy requirements
|
10
10
|
# set by the Organization's Administrator
|
11
|
-
|
11
|
+
module DisableReaction
|
12
12
|
extend Contrast::Components::Logger::InstanceMethods
|
13
13
|
|
14
14
|
def self.run _reaction, level
|
@@ -94,10 +94,6 @@ module Contrast
|
|
94
94
|
@exclusion.type == Contrast::Api::Settings::Exclusion::ExclusionType::CODE
|
95
95
|
end
|
96
96
|
|
97
|
-
def exc_name
|
98
|
-
@exclusion.name # rubocop:disable Security/Module/Name -- part of the API.
|
99
|
-
end
|
100
|
-
|
101
97
|
def match_all?
|
102
98
|
@exclusion.urls.nil? || @exclusion.urls.empty?
|
103
99
|
end
|
@@ -0,0 +1,117 @@
|
|
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/timer'
|
5
|
+
require 'contrast/utils/object_share'
|
6
|
+
require 'contrast/components/logger'
|
7
|
+
|
8
|
+
module Contrast
|
9
|
+
module Agent
|
10
|
+
module Inventory
|
11
|
+
# Methods used for parsing database connection configurations
|
12
|
+
# for getting inventory information from the application
|
13
|
+
module DatabaseConfig
|
14
|
+
extend Contrast::Components::Logger::InstanceMethods
|
15
|
+
|
16
|
+
# TeamServer only accepts certain values for ArchitectureComponents.
|
17
|
+
# DO NOT CHANGE THIS!
|
18
|
+
AC_TYPE_DB = 'db'
|
19
|
+
# TeamServer only accepts certain values for FlowMap Services.
|
20
|
+
# DO NOT CHANGE THIS
|
21
|
+
ADAPTER = 'adapter'
|
22
|
+
HOST = 'host'
|
23
|
+
PORT = 'port'
|
24
|
+
DATABASE = 'database'
|
25
|
+
DEFAULT = 'default'
|
26
|
+
LOCALHOST = 'localhost'
|
27
|
+
|
28
|
+
def self.active_record_config
|
29
|
+
return @_active_record_config if instance_variable_defined?(:@_active_record_config)
|
30
|
+
|
31
|
+
@_active_record_config = ActiveRecord::Base.connection_config rescue nil # rubocop:disable Style/RescueModifier
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.append_db_config(activity_or_update,
|
35
|
+
hash_or_str = Contrast::Agent::Inventory::DatabaseConfig.active_record_config)
|
36
|
+
arr = build_from_db_config(hash_or_str)
|
37
|
+
return unless arr&.any?
|
38
|
+
|
39
|
+
arr.each do |a|
|
40
|
+
next unless a
|
41
|
+
|
42
|
+
if activity_or_update.is_a?(Contrast::Api::Dtm::Activity)
|
43
|
+
activity_or_update.architectures << a
|
44
|
+
else
|
45
|
+
activity_or_update.components << a
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue StandardError => e
|
49
|
+
logger.error('Unable to append db config', e)
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.build_from_db_config hash_or_str
|
54
|
+
return unless hash_or_str
|
55
|
+
|
56
|
+
if hash_or_str.is_a?(Hash)
|
57
|
+
build_from_db_hash(hash_or_str)
|
58
|
+
else
|
59
|
+
build_from_db_string(hash_or_str.to_s)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.build_from_db_hash hash
|
64
|
+
ac = Contrast::Api::Dtm::ArchitectureComponent.new
|
65
|
+
ac.vendor = hash[:adapter] || hash[ADAPTER] || Contrast::Utils::ObjectShare::EMPTY_STRING
|
66
|
+
ac.remote_host = host_from_hash(hash)
|
67
|
+
ac.remote_port = port_from_hash(hash)
|
68
|
+
ac.type = AC_TYPE_DB
|
69
|
+
ac.url = hash[:database] || hash[DATABASE] || DEFAULT
|
70
|
+
[ac]
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.host_from_hash hash
|
74
|
+
hash[:host] || hash[HOST] || Contrast::Utils::ObjectShare::EMPTY_STRING
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.port_from_hash hash
|
78
|
+
p = hash[:port] || hash[PORT] || Contrast::Utils::ObjectShare::EMPTY_STRING
|
79
|
+
p.to_i
|
80
|
+
end
|
81
|
+
|
82
|
+
# Examples:
|
83
|
+
# mongodb://[user:pass@]host1[:port1][,host2[:port2],[,hostN[:portN]]][/[database][?options]]
|
84
|
+
# postgresql://scott:tiger@localhost/mydatabase
|
85
|
+
# mysql+mysqlconnector://scott:tiger@localhost/foo
|
86
|
+
def self.build_from_db_string str
|
87
|
+
adapter, hosts, database = split_connection_str(str)
|
88
|
+
acs = []
|
89
|
+
hosts.split(Contrast::Utils::ObjectShare::COMMA).map do |s|
|
90
|
+
host, port = s.split(Contrast::Utils::ObjectShare::COLON)
|
91
|
+
|
92
|
+
ac = Contrast::Api::Dtm::ArchitectureComponent.new
|
93
|
+
ac.vendor = Contrast::Utils::StringUtils.force_utf8(adapter)
|
94
|
+
ac.remote_host = Contrast::Utils::StringUtils.force_utf8(host)
|
95
|
+
ac.remote_port = port.to_i
|
96
|
+
ac.type = AC_TYPE_DB
|
97
|
+
ac.url = Contrast::Utils::StringUtils.force_utf8(database)
|
98
|
+
acs << ac
|
99
|
+
end
|
100
|
+
acs
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.split_connection_str str
|
104
|
+
adapter, str = str.split(Contrast::Utils::ObjectShare::COLON_SLASH_SLASH)
|
105
|
+
_auth, str = str.split(Contrast::Utils::ObjectShare::AT)
|
106
|
+
# Not currently used
|
107
|
+
# user, pass = auth.split(Contrast::Utils::ObjectShare::COLON)
|
108
|
+
hosts, db_and_options = str.split(Contrast::Utils::ObjectShare::SLASH)
|
109
|
+
hosts << LOCALHOST if hosts.empty?
|
110
|
+
database, _options = db_and_options.split(Contrast::Utils::ObjectShare::QUESTION_MARK)
|
111
|
+
|
112
|
+
[adapter, hosts, database]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|