contrast-agent 3.15.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -0
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +22 -10
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +4 -3
- data/lib/contrast/agent.rb +4 -12
- data/lib/contrast/agent/assess/contrast_event.rb +121 -130
- data/lib/contrast/agent/assess/contrast_object.rb +51 -0
- data/lib/contrast/agent/assess/events/source_event.rb +5 -10
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +10 -3
- data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
- data/lib/contrast/agent/assess/policy/policy_node.rb +46 -69
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +19 -2
- data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
- data/lib/contrast/agent/assess/policy/propagation_method.rb +13 -19
- data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
- data/lib/contrast/agent/assess/policy/propagator/append.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -3
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +2 -3
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -5
- data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/select.rb +4 -7
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -9
- data/lib/contrast/agent/assess/policy/propagator/split.rb +77 -122
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +32 -25
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
- data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +9 -13
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +39 -14
- data/lib/contrast/agent/assess/policy/trigger_node.rb +31 -37
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
- data/lib/contrast/agent/assess/property/evented.rb +5 -18
- data/lib/contrast/agent/assess/property/tagged.rb +28 -16
- data/lib/contrast/agent/assess/property/updated.rb +0 -5
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +23 -8
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +83 -14
- data/lib/contrast/agent/assess/rule/redos.rb +1 -1
- data/lib/contrast/agent/assess/tag.rb +1 -1
- data/lib/contrast/agent/assess/tracker.rb +16 -18
- data/lib/contrast/agent/at_exit_hook.rb +5 -5
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
- data/lib/contrast/agent/inventory.rb +15 -0
- data/lib/contrast/agent/inventory/dependencies.rb +50 -0
- data/lib/contrast/agent/inventory/dependency_analysis.rb +37 -0
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +104 -0
- data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +38 -0
- data/lib/contrast/agent/middleware.rb +51 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +5 -5
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +20 -20
- data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
- data/lib/contrast/agent/patching/policy/module_policy.rb +10 -10
- data/lib/contrast/agent/patching/policy/patch.rb +6 -0
- data/lib/contrast/agent/patching/policy/policy.rb +16 -2
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +3 -5
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +4 -3
- data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
- data/lib/contrast/agent/protect/rule/base.rb +63 -14
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +12 -28
- data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
- data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
- data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
- data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +3 -3
- data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
- data/lib/contrast/agent/reaction_processor.rb +1 -1
- data/lib/contrast/agent/request.rb +34 -34
- data/lib/contrast/agent/request_handler.rb +1 -1
- data/lib/contrast/agent/response.rb +5 -5
- data/lib/contrast/agent/rewriter.rb +3 -3
- data/lib/contrast/agent/scope.rb +81 -55
- data/lib/contrast/agent/static_analysis.rb +15 -9
- data/lib/contrast/agent/tracepoint_hook.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/socket_client.rb +36 -1
- data/lib/contrast/api/decorators.rb +3 -0
- data/lib/contrast/api/decorators/address.rb +13 -14
- data/lib/contrast/api/decorators/application_update.rb +1 -1
- data/lib/contrast/api/decorators/library.rb +54 -0
- data/lib/contrast/api/decorators/library_usage_update.rb +31 -0
- data/lib/contrast/api/decorators/message.rb +1 -0
- data/lib/contrast/api/decorators/trace_event.rb +31 -41
- data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
- data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
- data/lib/contrast/api/decorators/user_input.rb +2 -1
- data/lib/contrast/common_agent_configuration.rb +2 -1
- data/lib/contrast/components/agent.rb +6 -5
- data/lib/contrast/components/app_context.rb +39 -30
- data/lib/contrast/components/assess.rb +36 -0
- data/lib/contrast/components/config.rb +29 -37
- data/lib/contrast/components/contrast_service.rb +9 -9
- data/lib/contrast/components/interface.rb +30 -6
- data/lib/contrast/components/inventory.rb +6 -1
- data/lib/contrast/components/scope.rb +72 -6
- data/lib/contrast/components/settings.rb +23 -23
- data/lib/contrast/config/assess_configuration.rb +2 -1
- data/lib/contrast/config/inventory_configuration.rb +2 -2
- data/lib/contrast/config/service_configuration.rb +4 -2
- data/lib/contrast/configuration.rb +1 -1
- data/lib/contrast/extension/assess/array.rb +9 -6
- data/lib/contrast/extension/assess/erb.rb +6 -3
- data/lib/contrast/extension/assess/eval_trigger.rb +6 -6
- data/lib/contrast/extension/assess/exec_trigger.rb +0 -3
- data/lib/contrast/extension/assess/fiber.rb +5 -6
- data/lib/contrast/extension/assess/hash.rb +7 -5
- data/lib/contrast/extension/assess/kernel.rb +19 -22
- data/lib/contrast/extension/assess/marshal.rb +40 -28
- data/lib/contrast/extension/assess/regexp.rb +6 -11
- data/lib/contrast/extension/assess/string.rb +14 -13
- data/lib/contrast/extension/protect/kernel.rb +3 -3
- data/lib/contrast/framework/base_support.rb +51 -53
- data/lib/contrast/framework/manager.rb +6 -5
- data/lib/contrast/framework/rack/patch/session_cookie.rb +10 -10
- data/lib/contrast/framework/rack/support.rb +2 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +14 -14
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +1 -1
- data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +11 -11
- data/lib/contrast/framework/rails/patch/support.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +12 -12
- data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +13 -13
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -3
- data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +13 -13
- data/lib/contrast/framework/rails/support.rb +5 -1
- data/lib/contrast/framework/sinatra/patch/base.rb +11 -11
- data/lib/contrast/framework/sinatra/support.rb +7 -6
- data/lib/contrast/logger/application.rb +1 -4
- data/lib/contrast/logger/log.rb +7 -2
- data/lib/contrast/utils/duck_utils.rb +1 -1
- data/lib/contrast/utils/heap_dump_util.rb +1 -1
- data/lib/contrast/utils/invalid_configuration_util.rb +2 -5
- data/lib/contrast/utils/inventory_util.rb +0 -7
- data/lib/contrast/utils/object_share.rb +3 -3
- data/lib/contrast/utils/preflight_util.rb +1 -1
- data/lib/contrast/utils/prevent_serialization.rb +1 -1
- data/lib/contrast/utils/resource_loader.rb +1 -1
- data/lib/contrast/utils/sha256_builder.rb +2 -14
- data/lib/contrast/utils/string_utils.rb +1 -1
- data/lib/contrast/utils/tag_util.rb +9 -13
- data/resources/assess/policy.json +31 -12
- data/resources/deadzone/policy.json +156 -0
- data/resources/protect/policy.json +12 -0
- data/ruby-agent.gemspec +11 -6
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +91 -28
- data/lib/contrast/utils/boolean_util.rb +0 -30
- data/lib/contrast/utils/gemfile_reader.rb +0 -193
@@ -51,16 +51,23 @@ module Contrast
|
|
51
51
|
incoming_tracked = args && determine_tracked(args)
|
52
52
|
return ret unless self_tracked || incoming_tracked
|
53
53
|
|
54
|
+
parent_events = []
|
54
55
|
if block
|
55
56
|
block_sub(self_tracked, source, ret)
|
56
57
|
elsif args.is_a?(String)
|
57
|
-
string_sub(self_tracked, preshift, ret, args, incoming_tracked, global)
|
58
|
+
string_sub(parent_events, self_tracked, preshift, ret, args, incoming_tracked, global)
|
58
59
|
elsif args.is_a?(Hash)
|
59
60
|
hash_sub(self_tracked, source, ret)
|
60
61
|
else # Enumerator, only for gsub
|
61
|
-
pattern_gsub(preshift, ret)
|
62
|
+
pattern_gsub(parent_events, preshift, ret)
|
62
63
|
end
|
63
|
-
|
64
|
+
|
65
|
+
if self_tracked
|
66
|
+
source_properties = Contrast::Agent::Assess::Tracker.properties(source)
|
67
|
+
parent_event = source_properties&.event
|
68
|
+
parent_events.prepend(parent_event) if parent_event
|
69
|
+
end
|
70
|
+
string_build_event(parent_events, patcher, preshift, ret)
|
64
71
|
rescue StandardError => e
|
65
72
|
logger.error('Unable to apply gsub propagator', e)
|
66
73
|
end
|
@@ -84,9 +91,12 @@ module Contrast
|
|
84
91
|
end
|
85
92
|
end
|
86
93
|
|
87
|
-
def string_sub self_tracked, preshift, ret, incoming, incoming_tracked, global
|
88
|
-
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
89
|
-
|
94
|
+
def string_sub parent_events, self_tracked, preshift, ret, incoming, incoming_tracked, global
|
95
|
+
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
|
96
|
+
|
97
|
+
incoming_properties = Contrast::Agent::Assess::Tracker.properties(incoming)
|
98
|
+
parent_event = incoming_properties&.event
|
99
|
+
parent_events << parent_event if parent_event
|
90
100
|
|
91
101
|
pattern = preshift.args[0]
|
92
102
|
source = preshift.object
|
@@ -119,8 +129,8 @@ module Contrast
|
|
119
129
|
properties.delete_tags_at_ranges(ranges)
|
120
130
|
properties.shift_tags(ranges)
|
121
131
|
return unless incoming_tracked
|
132
|
+
return unless incoming_properties
|
122
133
|
|
123
|
-
incoming_properties = Contrast::Agent::Assess::Tracker.properties(incoming)
|
124
134
|
tags = incoming_properties.tag_keys
|
125
135
|
ranges.each do |range|
|
126
136
|
tags.each do |tag|
|
@@ -130,40 +140,36 @@ module Contrast
|
|
130
140
|
end
|
131
141
|
|
132
142
|
def block_sub self_tracked, source, ret
|
133
|
-
|
134
|
-
|
143
|
+
return unless self_tracked
|
144
|
+
|
145
|
+
properties = Contrast::Agent::Assess::Tracker.properties!(ret)
|
146
|
+
properties&.splat_from(source, ret)
|
135
147
|
end
|
136
148
|
|
137
149
|
def hash_sub self_tracked, source, ret
|
138
|
-
|
139
|
-
properties&.splat_from(source, ret) if self_tracked
|
140
|
-
end
|
150
|
+
return unless self_tracked
|
141
151
|
|
142
|
-
|
143
|
-
properties
|
144
|
-
|
152
|
+
properties = Contrast::Agent::Assess::Tracker.properties!(ret)
|
153
|
+
properties&.splat_from(source, ret)
|
154
|
+
end
|
145
155
|
|
156
|
+
def pattern_gsub parent_events, preshift, ret
|
146
157
|
source = preshift.object
|
147
|
-
source_properties = Contrast::Agent::Assess::Tracker.properties(source)
|
148
|
-
return unless
|
158
|
+
return unless (source_properties = Contrast::Agent::Assess::Tracker.properties(source))
|
159
|
+
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
|
149
160
|
|
150
161
|
source_properties.tag_keys.each do |key|
|
151
162
|
properties.add_tag(key, 0...1)
|
152
163
|
end
|
164
|
+
parent_event = source_properties.event
|
165
|
+
parent_events << parent_event if parent_event
|
153
166
|
end
|
154
167
|
|
155
|
-
def string_build_event patcher, preshift, ret
|
168
|
+
def string_build_event parent_events, patcher, preshift, ret
|
156
169
|
return unless Contrast::Agent::Assess::Tracker.tracked?(ret)
|
157
170
|
|
158
171
|
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
159
172
|
args = preshift.args
|
160
|
-
if args.length > 1
|
161
|
-
arg = args[1]
|
162
|
-
arg_properties = Contrast::Agent::Assess::Tracker.properties(arg)
|
163
|
-
arg_properties&.events&.each do |event|
|
164
|
-
properties.events << event
|
165
|
-
end
|
166
|
-
end
|
167
173
|
properties.build_event(
|
168
174
|
patcher,
|
169
175
|
ret,
|
@@ -171,6 +177,7 @@ module Contrast
|
|
171
177
|
ret,
|
172
178
|
args,
|
173
179
|
2)
|
180
|
+
properties.event.instance_variable_set(:@_parent_events, parent_events)
|
174
181
|
end
|
175
182
|
end
|
176
183
|
end
|
@@ -11,13 +11,11 @@ module Contrast
|
|
11
11
|
# Disclaimer: there may be a better way, but we're
|
12
12
|
# in a 'get it work' state. hopefully, we'll be in
|
13
13
|
# a 'get it right' state soon.
|
14
|
-
|
14
|
+
module Trim
|
15
15
|
class << self
|
16
16
|
def tr_tagger patcher, preshift, ret, _block
|
17
17
|
return ret unless ret && !ret.empty?
|
18
|
-
|
19
|
-
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
20
|
-
return unless properties
|
18
|
+
return ret unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
|
21
19
|
|
22
20
|
source = preshift.object
|
23
21
|
args = preshift.args
|
@@ -59,9 +57,7 @@ module Contrast
|
|
59
57
|
|
60
58
|
def tr_s_tagger patcher, preshift, ret, _block
|
61
59
|
return unless ret && !ret.empty?
|
62
|
-
|
63
|
-
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
64
|
-
return unless properties
|
60
|
+
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
|
65
61
|
|
66
62
|
source = preshift.object
|
67
63
|
args = preshift.args
|
@@ -178,8 +178,8 @@ module Contrast
|
|
178
178
|
# don't apply second source -- probably needs tuning later if we
|
179
179
|
# use more than 'UNTRUSTED' in our sources
|
180
180
|
return if Contrast::Agent::Assess::Tracker.tracked?(target)
|
181
|
+
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
|
181
182
|
|
182
|
-
properties = Contrast::Agent::Assess::Tracker.properties(target)
|
183
183
|
# otherwise for each tag this source_node applies, create a tag range
|
184
184
|
# on the target object
|
185
185
|
# I realize this looping is counter-intuitive from the above
|
@@ -243,19 +243,7 @@ module Contrast
|
|
243
243
|
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
244
244
|
object
|
245
245
|
else
|
246
|
-
|
247
|
-
args[source_target]
|
248
|
-
# If this isn't an index param, it's a named one. R.I.P.
|
249
|
-
else
|
250
|
-
arg = nil
|
251
|
-
args.each do |search|
|
252
|
-
next unless search.is_a?(Hash)
|
253
|
-
|
254
|
-
arg = search[source_target]
|
255
|
-
break if arg
|
256
|
-
end
|
257
|
-
arg
|
258
|
-
end
|
246
|
+
args[source_target]
|
259
247
|
end
|
260
248
|
end
|
261
249
|
|
@@ -25,24 +25,22 @@ module Contrast
|
|
25
25
|
TEMPLATE_PROPAGATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH)
|
26
26
|
|
27
27
|
def xss_tilt_trigger context, trigger_node, _source, object, ret, *args
|
28
|
-
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
29
|
-
return unless properties
|
28
|
+
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
|
30
29
|
|
31
30
|
scope = args[0]
|
32
31
|
erb_template_prerender = object.instance_variable_get(:@data)
|
33
32
|
interpolated_inputs = []
|
34
|
-
handle_binding_variables(scope, erb_template_prerender, ret, interpolated_inputs)
|
35
|
-
handle_local_variables(args, erb_template_prerender, ret, interpolated_inputs)
|
33
|
+
handle_binding_variables(scope, erb_template_prerender, ret, properties, interpolated_inputs)
|
34
|
+
handle_local_variables(args, erb_template_prerender, ret, properties, interpolated_inputs)
|
35
|
+
properties.build_event(TEMPLATE_PROPAGATION_NODE, ret, erb_template_prerender, ret, interpolated_inputs)
|
36
36
|
unless interpolated_inputs.empty?
|
37
|
+
current_event = properties.event
|
37
38
|
interpolated_inputs.each do |input|
|
38
39
|
input_properties = Contrast::Agent::Assess::Tracker.properties(input)
|
39
|
-
next unless input_properties
|
40
|
+
next unless input_properties&.event
|
40
41
|
|
41
|
-
|
42
|
-
properties.events << event
|
43
|
-
end
|
42
|
+
current_event.parent_events << input_properties.event
|
44
43
|
end
|
45
|
-
properties.build_event(TEMPLATE_PROPAGATION_NODE, ret, erb_template_prerender, ret, interpolated_inputs)
|
46
44
|
end
|
47
45
|
|
48
46
|
if Contrast::Agent::Assess::Tracker.tracked?(ret)
|
@@ -54,8 +52,7 @@ module Contrast
|
|
54
52
|
|
55
53
|
private
|
56
54
|
|
57
|
-
def handle_binding_variables scope, erb_template_prerender, ret, interpolated_inputs
|
58
|
-
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
55
|
+
def handle_binding_variables scope, erb_template_prerender, ret, properties, interpolated_inputs
|
59
56
|
binding_variables = scope.instance_variables
|
60
57
|
|
61
58
|
binding_variables.each do |bound_variable_sym|
|
@@ -72,8 +69,7 @@ module Contrast
|
|
72
69
|
end
|
73
70
|
end
|
74
71
|
|
75
|
-
def handle_local_variables args, erb_template_prerender, ret, interpolated_inputs
|
76
|
-
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
72
|
+
def handle_local_variables args, erb_template_prerender, ret, properties, interpolated_inputs
|
77
73
|
locals = args[1]
|
78
74
|
locals.each do |local_name, local_value|
|
79
75
|
next unless Contrast::Agent::Assess::Tracker.tracked?(local_value)
|
@@ -27,6 +27,24 @@ module Contrast
|
|
27
27
|
CURRENT_FINDING_VERSION = 4
|
28
28
|
|
29
29
|
class << self
|
30
|
+
# Append the given finding to the given context to be reported when
|
31
|
+
# the Context's activity is sent to the Service or, in the absence
|
32
|
+
# of that Context, generate an Activity and queue it manually
|
33
|
+
# @param finding [Contrast::Api::Dtm::Finding]
|
34
|
+
def report_finding finding
|
35
|
+
context = Contrast::Agent::REQUEST_TRACKER.current
|
36
|
+
if context
|
37
|
+
context.activity.findings << finding
|
38
|
+
else
|
39
|
+
activity = Contrast::Api::Dtm::Activity.new
|
40
|
+
activity.findings << finding
|
41
|
+
|
42
|
+
Contrast::Agent.messaging_queue.send_event_eventually(activity)
|
43
|
+
end
|
44
|
+
logger.debug('Finding reported',
|
45
|
+
rule: finding.rule_id)
|
46
|
+
end
|
47
|
+
|
30
48
|
# This is called from within our woven proc. It will be called as if it
|
31
49
|
# were inline in the Rack application.
|
32
50
|
#
|
@@ -69,8 +87,8 @@ module Contrast
|
|
69
87
|
# This converts the source of the finding, and the events leading
|
70
88
|
# up to it into a Finding
|
71
89
|
#
|
72
|
-
# @param context [Contrast::
|
73
|
-
# context
|
90
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
91
|
+
# request context
|
74
92
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
75
93
|
# the node to direct applying this trigger event
|
76
94
|
# @param source [Object] the source of the Trigger Event
|
@@ -98,11 +116,11 @@ module Contrast
|
|
98
116
|
build_hash(finding, source)
|
99
117
|
finding.routes << context.route if context.route
|
100
118
|
finding.version = determine_compliance_version(finding)
|
101
|
-
context.activity.findings << finding
|
102
119
|
logger.trace('Finding created',
|
103
120
|
node_id: trigger_node.id,
|
104
121
|
source_id: source.__id__,
|
105
122
|
rule: trigger_node.rule_id)
|
123
|
+
report_finding(finding)
|
106
124
|
rescue StandardError => e
|
107
125
|
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
|
108
126
|
end
|
@@ -112,8 +130,8 @@ module Contrast
|
|
112
130
|
# This is our method that actually checks the taint on the object
|
113
131
|
# our trigger_node targets.
|
114
132
|
#
|
115
|
-
# @param context [Contrast::
|
116
|
-
# context
|
133
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
134
|
+
# request context
|
117
135
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
118
136
|
# the node to direct applying this trigger event
|
119
137
|
# @param source [Object] the source of the Trigger Event
|
@@ -181,8 +199,8 @@ module Contrast
|
|
181
199
|
# This is our method that actually checks the taint on the object
|
182
200
|
# our trigger_node targets for our Regexp based rules.
|
183
201
|
#
|
184
|
-
# @param context [Contrast::
|
185
|
-
# context
|
202
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
203
|
+
# request context
|
186
204
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
187
205
|
# the node to direct applying this trigger event
|
188
206
|
# @param source [Object] the source of the Trigger Event
|
@@ -201,8 +219,8 @@ module Contrast
|
|
201
219
|
# This is our method that actually checks the taint on the object
|
202
220
|
# our trigger_node targets for our Dataflow based rules.
|
203
221
|
#
|
204
|
-
# @param context [Contrast::
|
205
|
-
# context
|
222
|
+
# @param context [Contrast::Agent::RequestContext] the current
|
223
|
+
# request context
|
206
224
|
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
207
225
|
# the node to direct applying this trigger event
|
208
226
|
# @param source [Object] the source of the Trigger Event
|
@@ -244,11 +262,7 @@ module Contrast
|
|
244
262
|
properties = Contrast::Agent::Assess::Tracker.properties(source)
|
245
263
|
return unless properties
|
246
264
|
|
247
|
-
|
248
|
-
# the rule check before getting here. not worth the nil check
|
249
|
-
properties.events.each do |event|
|
250
|
-
finding.events << event.to_dtm_event
|
251
|
-
end
|
265
|
+
build_events finding, properties.event if properties.event
|
252
266
|
|
253
267
|
# Google::Protobuf::Map doesn't support merge!, so we have to do this
|
254
268
|
# long form
|
@@ -261,6 +275,17 @@ module Contrast
|
|
261
275
|
end
|
262
276
|
end
|
263
277
|
|
278
|
+
def build_events finding, event
|
279
|
+
return unless event
|
280
|
+
|
281
|
+
event.parent_events&.each do |parent_event|
|
282
|
+
build_events(finding, parent_event)
|
283
|
+
end
|
284
|
+
# events could technically be nil, but we would have failed
|
285
|
+
# the rule check before getting here. not worth the nil check
|
286
|
+
finding.events << event.to_dtm_event
|
287
|
+
end
|
288
|
+
|
264
289
|
def build_hash finding, source
|
265
290
|
hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
|
266
291
|
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
|
@@ -104,13 +104,13 @@ module Contrast
|
|
104
104
|
|
105
105
|
properties = Contrast::Agent::Assess::Tracker.properties(source)
|
106
106
|
# find the ranges that violate the rule (untrusted, etc)
|
107
|
-
vulnerable_ranges =
|
107
|
+
vulnerable_ranges = ranges_with_all_tags(Contrast::Utils::StringUtils.ret_length(source), properties, required_tags)
|
108
108
|
# if there aren't any vulnerable ranges, nope out
|
109
109
|
return false if vulnerable_ranges.empty?
|
110
110
|
|
111
111
|
# find the ranges that are exempt from the rule
|
112
112
|
# (validated, sanitized, etc)
|
113
|
-
secure_ranges =
|
113
|
+
secure_ranges = ranges_with_any_tag(properties, disallowed_tags)
|
114
114
|
# if there are vulnerable ranges and no secure, report
|
115
115
|
return true if secure_ranges.empty?
|
116
116
|
|
@@ -181,49 +181,43 @@ module Contrast
|
|
181
181
|
# tags.
|
182
182
|
# @param properties [Contrast::Agent::Assess::Properties] the
|
183
183
|
# properties to check for the tags
|
184
|
-
# @param
|
184
|
+
# @param required_tags [Set<String>] the list of tags on which to match
|
185
185
|
# @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
|
186
186
|
# by the given conditions
|
187
|
-
def
|
188
|
-
# if there
|
187
|
+
def ranges_with_all_tags length, properties, required_tags
|
188
|
+
# if there are no tags, not required tags, or the tags don't have
|
189
|
+
# all the required tags, we can just return here.
|
189
190
|
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless properties.tracked?
|
190
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless
|
191
|
-
|
192
|
-
# :zap: faster to treat all as any if there's only one tag
|
193
|
-
return find_ranges_by_any_tag(properties, tags) if tags.length == 1
|
191
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless required_tags&.any?
|
192
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless required_tags.all? { |tag| properties.tag_keys.include?(tag) }
|
194
193
|
|
195
194
|
ranges = []
|
196
|
-
|
197
|
-
# tags.each { |tag| applicable << properties.fetch_tag(tag) }
|
198
|
-
# return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless applicable.length == tags.length
|
199
|
-
# ...
|
195
|
+
chunking = false
|
200
196
|
# find all the indicies on the source that have all the given tags
|
201
197
|
(0..length).each do |idx|
|
202
|
-
tags_at = properties.tags_at(idx)
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
198
|
+
tags_at = properties.tags_at(idx).to_a
|
199
|
+
# only those that have all the required tags in the tags_at
|
200
|
+
# satisfy the requirement
|
201
|
+
satisfied = tags_at.any? && required_tags.all? { |tag| tags_at.any? { |found| found.label == tag } }
|
202
|
+
# if this range matches all the required tags and we're already
|
203
|
+
# chunking, meaning the previous range matched, do nothing
|
204
|
+
next if satisfied && chunking
|
205
|
+
|
206
|
+
# if we are satisfied and we were not chunking, this represents
|
207
|
+
# the start of the next range, so create a new entry.
|
208
|
+
if satisfied
|
209
|
+
ranges << Contrast::Agent::Assess::Tag.new('required', 0, idx)
|
210
|
+
chunking = true
|
211
|
+
# if we are chunking and not satisfied, this represents the end
|
212
|
+
# of the range, meaning the last index is what satisfied the
|
213
|
+
# range. Because the range is exclusive end, we can just use this
|
214
|
+
# index.
|
215
|
+
elsif chunking
|
216
|
+
ranges[-1]&.update_end(idx)
|
217
|
+
chunking = false
|
210
218
|
end
|
211
219
|
end
|
212
|
-
|
213
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY if ranges.empty?
|
214
|
-
|
215
|
-
# chunk all the adjacent ranges
|
216
|
-
chunked = ranges.chunk_while { |i, j| i + 1 == j }
|
217
|
-
tag_ranges = []
|
218
|
-
# and convert them into Tags
|
219
|
-
chunked.each do |join|
|
220
|
-
start = join[0]
|
221
|
-
stop = join[-1]
|
222
|
-
# add the 1 to account for end index being exclusive
|
223
|
-
tag_length = stop - start + 1
|
224
|
-
tag_ranges = Contrast::Utils::TagUtil.ordered_merge(tag_ranges, Tag.new(tag_length, start))
|
225
|
-
end
|
226
|
-
tag_ranges
|
220
|
+
ranges
|
227
221
|
end
|
228
222
|
|
229
223
|
# Find the ranges that satisfy any of the given tags.
|
@@ -233,7 +227,7 @@ module Contrast
|
|
233
227
|
# @param tags [Set<String>] the list of tags on which to match
|
234
228
|
# @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
|
235
229
|
# by the given conditions
|
236
|
-
def
|
230
|
+
def ranges_with_any_tag properties, tags
|
237
231
|
# if there aren't any all_tags or tags, break early
|
238
232
|
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless properties.tracked?
|
239
233
|
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tags&.any?
|