contrast-agent 3.8.5 → 3.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/cs__assess_array/cs__assess_array.c +1 -1
- data/ext/cs__assess_module/cs__assess_module.c +0 -1
- data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
- data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
- data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
- data/ext/cs__common/cs__common.c +6 -6
- data/ext/cs__common/cs__common.h +3 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
- data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
- data/funchook/autom4te.cache/requests +48 -48
- data/funchook/config.log +2 -2
- data/lib/contrast/agent.rb +15 -5
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/assess/contrast_event.rb +9 -8
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
- data/lib/contrast/agent/assess/policy/policy.rb +0 -14
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
- data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
- data/lib/contrast/agent/assess/properties.rb +29 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
- data/lib/contrast/agent/class_reopener.rb +98 -55
- data/lib/contrast/agent/feature_state.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/logger_manager.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
- data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
- data/lib/contrast/agent/patching/policy/patch.rb +54 -23
- data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
- data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
- data/lib/contrast/agent/patching/policy/policy.rb +4 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
- data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
- data/lib/contrast/agent/protect/policy/policy.rb +6 -6
- data/lib/contrast/agent/protect/rule/base.rb +1 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/railtie.rb +11 -5
- data/lib/contrast/agent/request.rb +1 -19
- data/lib/contrast/agent/request_context.rb +1 -1
- data/lib/contrast/agent/rewriter.rb +4 -3
- data/lib/contrast/agent/scope.rb +116 -19
- data/lib/contrast/agent/service_heartbeat.rb +5 -2
- data/lib/contrast/agent/settings_state.rb +12 -8
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api.rb +1 -0
- data/lib/contrast/api/speedracer.rb +2 -2
- data/lib/contrast/components/agent.rb +26 -7
- data/lib/contrast/components/app_context.rb +8 -45
- data/lib/contrast/components/contrast_service.rb +3 -4
- data/lib/contrast/components/interface.rb +1 -1
- data/lib/contrast/components/scope.rb +56 -26
- data/lib/contrast/config/ruby_configuration.rb +8 -3
- data/lib/contrast/delegators.rb +9 -0
- data/lib/contrast/delegators/application_update.rb +32 -0
- data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
- data/lib/contrast/extensions/framework/rack/request.rb +24 -0
- data/lib/contrast/extensions/framework/rack/response.rb +23 -0
- data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
- data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
- data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
- data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
- data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
- data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
- data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
- data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
- data/lib/contrast/extensions/ruby_core/module.rb +17 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
- data/lib/contrast/framework/base_support.rb +63 -0
- data/lib/contrast/framework/manager.rb +109 -0
- data/lib/contrast/framework/platform_version.rb +21 -0
- data/lib/contrast/framework/rails_support.rb +88 -0
- data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
- data/lib/contrast/framework/sinatra_support.rb +94 -0
- data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
- data/lib/contrast/utils/assess/tracking_util.rb +2 -4
- data/lib/contrast/utils/class_util.rb +92 -37
- data/lib/contrast/utils/duck_utils.rb +59 -39
- data/lib/contrast/utils/environment_util.rb +5 -75
- data/lib/contrast/utils/freeze_util.rb +3 -7
- data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
- data/lib/contrast/utils/job_servers_running.rb +39 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
- data/lib/contrast/utils/service_response_util.rb +0 -6
- data/lib/contrast/utils/sinatra_helper.rb +6 -0
- data/lib/contrast/utils/stack_trace_utils.rb +1 -1
- data/resources/assess/policy.json +74 -23
- data/resources/inventory/policy.json +1 -1
- data/resources/protect/policy.json +11 -9
- data/resources/rubocops/object/frozen_cop.rb +1 -1
- data/ruby-agent.gemspec +2 -0
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +94 -57
- data/ext/cs__scope/cs__scope.c +0 -96
- data/ext/cs__scope/cs__scope.h +0 -33
- data/lib/contrast/agent/assess/class_reverter.rb +0 -82
- data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
- data/lib/contrast/core_extensions/module.rb +0 -42
- data/lib/contrast/core_extensions/object.rb +0 -27
- data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
- data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
- data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
- data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
- data/lib/contrast/rails_extensions/buffer.rb +0 -30
- data/lib/contrast/rails_extensions/rack.rb +0 -45
- data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
- data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
- data/lib/contrast/utils/operating_environment.rb +0 -38
- data/lib/contrast/utils/path_util.rb +0 -151
- data/lib/contrast/utils/scope_util.rb +0 -99
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
cs__scoped_require 'contrast/agent/assess/policy/source_method'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Assess
|
9
|
+
module Policy
|
10
|
+
module SourceValidation
|
11
|
+
# Validator used to assert a CROSS_SITE tag is actually applicable to
|
12
|
+
# the given method before applying the tag to its target
|
13
|
+
module CrossSiteValidator
|
14
|
+
# prevent the application of a tag if it is from a source known to
|
15
|
+
# not apply a tag in a provided context.
|
16
|
+
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/reflected_xss.md
|
17
|
+
def self.valid? tag, source_type, source_name
|
18
|
+
return true unless tag == 'CROSS_SITE'
|
19
|
+
return false if source_type == Contrast::Agent::Assess::Policy::SourceMethod::HEADER_KEY_TYPE
|
20
|
+
return true unless source_type == Contrast::Agent::Assess::Policy::SourceMethod::HEADER_TYPE
|
21
|
+
return false unless source_name
|
22
|
+
|
23
|
+
source_name.casecmp?('referer')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
cs__scoped_require 'contrast/agent/assess/policy/source_validation/cross_site_validator'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Agent
|
8
|
+
module Assess
|
9
|
+
module Policy
|
10
|
+
# Some of our sources require validation to prevent them from applying
|
11
|
+
# certain tags in a given context. This provides a single place from
|
12
|
+
# which those validations can be called.
|
13
|
+
module SourceValidation
|
14
|
+
VALIDATORS = [
|
15
|
+
Contrast::Agent::Assess::Policy::SourceValidation::CrossSiteValidator
|
16
|
+
].cs__freeze
|
17
|
+
|
18
|
+
# Determines if the conditions in which this source was called are
|
19
|
+
# valid and should result in the application of a tag
|
20
|
+
#
|
21
|
+
# @param tag [String] the tag which the Source is attempting to apply
|
22
|
+
# @param source_type [String] the type of the source
|
23
|
+
# @param source_name [String] the name of the source
|
24
|
+
# @return [Boolean] if the conditions are valid for the application
|
25
|
+
# of a tag
|
26
|
+
def self.valid? tag, source_type, source_name
|
27
|
+
VALIDATORS.each do |validator|
|
28
|
+
return false unless validator.valid?(tag, source_type, source_name)
|
29
|
+
end
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -23,184 +23,269 @@ module Contrast
|
|
23
23
|
# The level of TeamServer compliance our traces meet
|
24
24
|
CURRENT_FINDING_VERSION = 2
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
class << self
|
27
|
+
# This is called from within our woven proc. It will be called as if it
|
28
|
+
# were inline in the Rack application.
|
29
|
+
#
|
30
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
31
|
+
# the node that applies to the method being called
|
32
|
+
# @param object [Object] the Object on which the method was invoked
|
33
|
+
# @param ret [Object] the Return of the invoked method
|
34
|
+
# @param args [Array<Object>] the Arguments with which the method
|
35
|
+
# was invoked
|
36
|
+
def apply_trigger_rule trigger_node, object, ret, args
|
37
|
+
return if trigger_node.nil?
|
38
|
+
|
39
|
+
current_context = Contrast::Agent::REQUEST_TRACKER.current
|
40
|
+
return unless current_context&.analyze_request? && ASSESS.enabled?
|
29
41
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
if trigger_node.sources&.any?
|
43
|
+
trigger_node.sources.each do |marker|
|
44
|
+
source = determine_source(marker, object, ret, args)
|
45
|
+
apply_trigger(current_context,
|
46
|
+
trigger_node,
|
47
|
+
source,
|
48
|
+
object,
|
49
|
+
ret,
|
50
|
+
1,
|
51
|
+
*args)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
apply_trigger(current_context,
|
55
|
+
trigger_node,
|
56
|
+
nil,
|
57
|
+
object,
|
58
|
+
ret,
|
59
|
+
1,
|
60
|
+
*args)
|
46
61
|
end
|
47
|
-
else
|
48
|
-
TriggerMethod.cs__apply_trigger(current_context,
|
49
|
-
trigger_node,
|
50
|
-
nil,
|
51
|
-
object,
|
52
|
-
ret,
|
53
|
-
1,
|
54
|
-
*args)
|
55
62
|
end
|
56
|
-
end
|
57
63
|
|
58
|
-
|
59
|
-
|
60
|
-
return if trigger_node.rule_disabled?
|
61
|
-
return if trigger_node.dataflow? && source.nil?
|
62
|
-
|
63
|
-
invoked += 1
|
64
|
-
if trigger_node.regexp_rule?
|
65
|
-
apply_regexp_rule(context, trigger_node, source, object, ret, invoked, *args)
|
66
|
-
elsif trigger_node.custom_trigger?
|
67
|
-
trigger_node.apply_custom_trigger(context, trigger_node, source, object, ret, invoked, *args)
|
68
|
-
elsif trigger_node.dataflow?
|
69
|
-
apply_dataflow_rule(context, trigger_node, source, object, ret, invoked, *args)
|
70
|
-
else # trigger rule - just calling the method is dangerous
|
71
|
-
build_finding(context, trigger_node, source, object, ret, invoked, *args)
|
64
|
+
def apply_eval_trigger context, trigger_node, source, object, ret, invoked, *args
|
65
|
+
apply_trigger(context, trigger_node, source, object, ret, invoked, *args)
|
72
66
|
end
|
73
|
-
rescue StandardError => e
|
74
|
-
logger.warn(e, 'Unable to apply trigger.')
|
75
|
-
end
|
76
67
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
68
|
+
# This converts the source of the finding, and the events leading
|
69
|
+
# up to it into a Finding
|
70
|
+
#
|
71
|
+
# @param context [Contrast::Utils::ThreadTracker] the current request
|
72
|
+
# context
|
73
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
74
|
+
# the node to direct applying this trigger event
|
75
|
+
# @param source [Object] the source of the Trigger Event
|
76
|
+
# @param object [Object] the Object on which the method was invoked
|
77
|
+
# @param ret [Object] the Return of the invoked method
|
78
|
+
# @param invoked [Integer] the depth of this invocation from
|
79
|
+
# application code; often a lie.
|
80
|
+
# @param args [Array<Object>] the Arguments with which the method
|
81
|
+
# was invoked
|
82
|
+
# @return [Contrast::Api::Dtm::Finding, nil] the
|
83
|
+
# Contrast::Api::Dtm::Finding to send to TeamServer or nil if
|
84
|
+
# conditions were not met
|
85
|
+
def build_finding context, trigger_node, source, object, ret, invoked, *args
|
86
|
+
return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
|
87
|
+
|
88
|
+
request = context.request
|
89
|
+
env = request.env
|
90
|
+
return if defined?(ActionController::Live) &&
|
91
|
+
env &&
|
92
|
+
env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live)
|
93
|
+
|
94
|
+
finding = Contrast::Api::Dtm::Finding.new
|
95
|
+
finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
|
96
|
+
finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
|
97
|
+
finding.version = CURRENT_FINDING_VERSION
|
98
|
+
|
99
|
+
build_from_source(finding, source)
|
100
|
+
trigger_event = Contrast::Agent::Assess::ContrastEvent.new(trigger_node, source, object, ret, args, invoked + 1).to_dtm_event
|
101
|
+
finding.events << trigger_event
|
102
|
+
build_hash(finding, source)
|
103
|
+
build_tags(context)
|
104
|
+
finding.routes << context.route if context.route
|
105
|
+
context.activity.findings << finding
|
106
|
+
logger.debug(nil, "Trigger #{ trigger_node.id } detected: #{ source.__id__ } triggered #{ trigger_node.rule_id }")
|
107
|
+
rescue StandardError => e
|
108
|
+
logger.error(e, "Unable to build a finding for #{ trigger_node.id }")
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def settings
|
114
|
+
Contrast::Agent::FeatureState.instance
|
115
|
+
end
|
116
|
+
|
117
|
+
# This is our method that actually checks the taint on the object
|
118
|
+
# our trigger_node targets.
|
119
|
+
#
|
120
|
+
# @param context [Contrast::Utils::ThreadTracker] the current request
|
121
|
+
# context
|
122
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
123
|
+
# the node to direct applying this trigger event
|
124
|
+
# @param source [Object] the source of the Trigger Event
|
125
|
+
# @param object [Object] the Object on which the method was invoked
|
126
|
+
# @param ret [Object] the Return of the invoked method
|
127
|
+
# @param invoked [Integer] the depth of this invocation from
|
128
|
+
# application code; often a lie.
|
129
|
+
# @param args [Array<Object>] the Arguments with which the method
|
130
|
+
# was invoked
|
131
|
+
def apply_trigger context, trigger_node, source, object, ret, invoked, *args
|
132
|
+
return unless context && trigger_node
|
133
|
+
return if trigger_node.rule_disabled?
|
134
|
+
return if trigger_node.dataflow? && source.nil?
|
135
|
+
|
136
|
+
invoked += 1
|
137
|
+
if trigger_node.regexp_rule?
|
138
|
+
apply_regexp_rule(context, trigger_node, source, object, ret, invoked, *args)
|
139
|
+
elsif trigger_node.custom_trigger?
|
140
|
+
trigger_node.apply_custom_trigger(context, trigger_node, source, object, ret, invoked, *args)
|
141
|
+
elsif trigger_node.dataflow?
|
142
|
+
apply_dataflow_rule(context, trigger_node, source, object, ret, invoked, *args)
|
143
|
+
else # trigger rule - just calling the method is dangerous
|
144
|
+
build_finding(context, trigger_node, source, object, ret, invoked, *args)
|
145
|
+
end
|
146
|
+
rescue StandardError => e
|
147
|
+
logger.warn(e, 'Unable to apply trigger.')
|
148
|
+
end
|
98
149
|
|
99
|
-
|
100
|
-
|
150
|
+
# Given the marker from the trigger_node (the pointer indicating
|
151
|
+
# the entity from which the taint originated), return the entity on
|
152
|
+
# which this trigger needs to operate.
|
153
|
+
#
|
154
|
+
# In an effort to speed up this lookup, we've changed the marker
|
155
|
+
# for parameters to be implicit - if it is not a return or an
|
156
|
+
# object, it must be a parameter, which we can reference either by
|
157
|
+
# index or by name.
|
158
|
+
#
|
159
|
+
# @param marker [String] the source marker that indicates which
|
160
|
+
# Object
|
161
|
+
# @param object [Object] the Object on which the method was invoked
|
162
|
+
# @param ret [Object] the Return of the invoked method
|
163
|
+
# @param args [Array<Object>] the Arguments with which the method
|
164
|
+
# was invoked
|
165
|
+
# @return [Object] the literal object that this Trigger Event
|
166
|
+
# verifies
|
167
|
+
def determine_source marker, object, ret, args
|
168
|
+
case marker
|
169
|
+
when Contrast::Utils::ObjectShare::RETURN_KEY
|
170
|
+
ret
|
171
|
+
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
172
|
+
object
|
173
|
+
else # 'P'
|
174
|
+
if marker.is_a?(Integer)
|
175
|
+
args[marker]
|
176
|
+
else
|
177
|
+
arg = nil
|
178
|
+
args.each do |search|
|
179
|
+
next unless search.is_a?(Hash)
|
180
|
+
|
181
|
+
arg = search[marker]
|
182
|
+
break if arg
|
183
|
+
end
|
184
|
+
arg
|
101
185
|
end
|
102
|
-
arg
|
103
186
|
end
|
104
187
|
end
|
105
|
-
end
|
106
188
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
189
|
+
# This is our method that actually checks the taint on the object
|
190
|
+
# our trigger_node targets for our Regexp based rules.
|
191
|
+
#
|
192
|
+
# @param context [Contrast::Utils::ThreadTracker] the current request
|
193
|
+
# context
|
194
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
195
|
+
# the node to direct applying this trigger event
|
196
|
+
# @param source [Object] the source of the Trigger Event
|
197
|
+
# @param object [Object] the Object on which the method was invoked
|
198
|
+
# @param ret [Object] the Return of the invoked method
|
199
|
+
# @param invoked [Integer] the depth of this invocation from
|
200
|
+
# application code; often a lie.
|
201
|
+
# @param args [Array<Object>] the Arguments with which the method
|
202
|
+
# was invoked
|
203
|
+
def apply_regexp_rule context, trigger_node, source, object, ret, invoked, *args
|
204
|
+
return unless source.is_a?(String)
|
205
|
+
return if trigger_node.good_value && source.match?(trigger_node.good_value)
|
206
|
+
return if trigger_node.bad_value && source !~ trigger_node.bad_value
|
111
207
|
|
112
|
-
|
113
|
-
|
114
|
-
|
208
|
+
invoked += 1
|
209
|
+
build_finding(context, trigger_node, source, object, ret, invoked, *args)
|
210
|
+
end
|
115
211
|
|
116
|
-
|
117
|
-
|
212
|
+
# This is our method that actually checks the taint on the object
|
213
|
+
# our trigger_node targets for our Dataflow based rules.
|
214
|
+
#
|
215
|
+
# @param context [Contrast::Utils::ThreadTracker] the current request
|
216
|
+
# context
|
217
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
|
218
|
+
# the node to direct applying this trigger event
|
219
|
+
# @param source [Object] the source of the Trigger Event
|
220
|
+
# @param object [Object] the Object on which the method was invoked
|
221
|
+
# @param ret [Object] the Return of the invoked method
|
222
|
+
# @param invoked [Integer] the depth of this invocation from
|
223
|
+
# application code; often a lie.
|
224
|
+
# @param args [Array<Object>] the Arguments with which the method
|
225
|
+
# was invoked
|
226
|
+
def apply_dataflow_rule context, trigger_node, source, object, ret, invoked, *args
|
227
|
+
return unless source
|
118
228
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
229
|
+
invoked += 1
|
230
|
+
if Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties)
|
231
|
+
return unless source.cs__tracked?
|
232
|
+
return unless trigger_node.violated?(source)
|
123
233
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
234
|
+
build_finding(context, trigger_node, source, object, ret, invoked, *args)
|
235
|
+
elsif Contrast::Utils::DuckUtils.iterable_hash?(source)
|
236
|
+
invoked += 2 # the each & the block
|
237
|
+
source.each_pair do |key, value|
|
238
|
+
apply_dataflow_rule(context, trigger_node, key, object, ret, invoked, *args)
|
239
|
+
apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
|
240
|
+
end
|
241
|
+
elsif Contrast::Utils::DuckUtils.iterable_enumerable?(source)
|
242
|
+
invoked += 2 # the each & the block
|
243
|
+
source.each do |value|
|
244
|
+
apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
|
245
|
+
end
|
246
|
+
else
|
247
|
+
logger.warn(
|
248
|
+
nil,
|
249
|
+
"Target is a #{ source.cs__class.name } -- not sure how to inspect: #{ trigger_node.inspect }")
|
250
|
+
logger.debug(nil, source.to_s[0..99])
|
135
251
|
end
|
136
|
-
else
|
137
|
-
logger.warn(
|
138
|
-
nil,
|
139
|
-
"Target is a #{ source.cs__class.name } -- not sure how to inspect: #{ trigger_node.inspect }")
|
140
|
-
logger.debug(nil, source.to_s[0..99])
|
141
252
|
end
|
142
|
-
end
|
143
253
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
env &&
|
151
|
-
env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live)
|
152
|
-
|
153
|
-
finding = Contrast::Api::Dtm::Finding.new
|
154
|
-
finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
|
155
|
-
finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
|
156
|
-
finding.version = CURRENT_FINDING_VERSION
|
157
|
-
|
158
|
-
build_from_source(finding, source)
|
159
|
-
trigger_event = Contrast::Agent::Assess::ContrastEvent.new(trigger_node, source, object, ret, args, invoked + 1).to_dtm_event
|
160
|
-
finding.events << trigger_event
|
161
|
-
build_hash(finding, source)
|
162
|
-
build_tags(context)
|
163
|
-
finding.routes << context.route if context.route
|
164
|
-
context.activity.findings << finding
|
165
|
-
logger.debug(nil, "Trigger #{ trigger_node.id } detected: #{ source.__id__ } triggered #{ trigger_node.rule_id }")
|
166
|
-
rescue StandardError => e
|
167
|
-
logger.error(e, "Unable to build a finding for #{ trigger_node.id }")
|
168
|
-
end
|
254
|
+
def build_from_source finding, source
|
255
|
+
return unless source
|
256
|
+
return unless Contrast::Utils::DuckUtils.quacks_to?(
|
257
|
+
source,
|
258
|
+
:cs__properties)
|
259
|
+
return unless source.cs__properties
|
169
260
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
return unless source.cs__properties
|
176
|
-
|
177
|
-
# events could technically be nil, but we would have failed
|
178
|
-
# the rule check before getting here. not worth the nil check
|
179
|
-
source.cs__properties.events.each do |event|
|
180
|
-
finding.events << event.to_dtm_event
|
181
|
-
end
|
261
|
+
# events could technically be nil, but we would have failed
|
262
|
+
# the rule check before getting here. not worth the nil check
|
263
|
+
source.cs__properties.events.each do |event|
|
264
|
+
finding.events << event.to_dtm_event
|
265
|
+
end
|
182
266
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
267
|
+
# Google::Protobuf::Map doesn't support merge!, so we have to do this
|
268
|
+
# long form
|
269
|
+
source_props = source.cs__properties.properties
|
270
|
+
return unless source_props
|
187
271
|
|
188
|
-
|
189
|
-
|
190
|
-
|
272
|
+
source_props.each_pair do |key, value|
|
273
|
+
key = Contrast::Utils::StringUtils.force_utf8(key)
|
274
|
+
finding.properties[key] = Contrast::Utils::StringUtils.force_utf8(value)
|
275
|
+
end
|
191
276
|
end
|
192
|
-
end
|
193
277
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
278
|
+
def build_hash finding, source
|
279
|
+
hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
|
280
|
+
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
|
281
|
+
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|
282
|
+
end
|
199
283
|
|
200
|
-
|
201
|
-
|
284
|
+
def build_tags context
|
285
|
+
return unless ASSESS.tags
|
202
286
|
|
203
|
-
|
287
|
+
context.activity.finding_tags = Contrast::Utils::StringUtils.force_utf8(ASSESS.tags)
|
288
|
+
end
|
204
289
|
end
|
205
290
|
end
|
206
291
|
end
|