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