contrast-agent 4.6.0 → 4.7.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/.gitmodules +1 -1
- data/Rakefile +1 -2
- data/ext/build_funchook.rb +3 -3
- data/ext/extconf_common.rb +1 -5
- data/lib/contrast/agent/assess.rb +1 -1
- data/lib/contrast/agent/assess/contrast_object.rb +2 -2
- data/lib/contrast/agent/assess/events/event_factory.rb +2 -1
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +6 -3
- data/lib/contrast/agent/assess/policy/patcher.rb +10 -17
- data/lib/contrast/agent/assess/policy/policy_node.rb +25 -33
- data/lib/contrast/agent/assess/policy/preshift.rb +3 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +6 -15
- data/lib/contrast/agent/assess/policy/propagation_node.rb +19 -8
- data/lib/contrast/agent/assess/policy/propagator/center.rb +2 -1
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +3 -1
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +2 -1
- data/lib/contrast/agent/assess/policy/propagator/select.rb +2 -12
- data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -7
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -7
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -15
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +6 -3
- data/lib/contrast/agent/assess/policy/source_method.rb +6 -6
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +1 -3
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +6 -15
- data/lib/contrast/agent/assess/policy/trigger_node.rb +2 -1
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +4 -3
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -2
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +1 -8
- data/lib/contrast/agent/assess/property/evented.rb +8 -5
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +11 -5
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +4 -1
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +3 -5
- data/lib/contrast/agent/class_reopener.rb +3 -1
- data/lib/contrast/agent/disable_reaction.rb +1 -3
- data/lib/contrast/agent/exclusion_matcher.rb +5 -11
- data/lib/contrast/agent/inventory/dependencies.rb +2 -0
- data/lib/contrast/agent/middleware.rb +3 -5
- data/lib/contrast/agent/module_data.rb +3 -3
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +6 -5
- data/lib/contrast/agent/patching/policy/method_policy.rb +6 -2
- data/lib/contrast/agent/patching/policy/module_policy.rb +14 -7
- data/lib/contrast/agent/patching/policy/patch.rb +11 -16
- data/lib/contrast/agent/patching/policy/patch_status.rb +6 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +15 -12
- data/lib/contrast/agent/patching/policy/policy_node.rb +14 -4
- data/lib/contrast/agent/patching/policy/trigger_node.rb +21 -8
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +2 -3
- data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +5 -9
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +5 -5
- data/lib/contrast/agent/protect/rule/base.rb +13 -16
- data/lib/contrast/agent/protect/rule/base_service.rb +9 -5
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +14 -18
- data/lib/contrast/agent/protect/rule/deserialization.rb +6 -13
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +3 -14
- data/lib/contrast/agent/protect/rule/no_sqli.rb +6 -2
- data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -3
- data/lib/contrast/agent/protect/rule/path_traversal.rb +5 -5
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +1 -1
- data/lib/contrast/agent/protect/rule/xss.rb +1 -1
- data/lib/contrast/agent/protect/rule/xxe.rb +5 -12
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +1 -2
- data/lib/contrast/agent/reaction_processor.rb +11 -10
- data/lib/contrast/agent/request.rb +23 -23
- data/lib/contrast/agent/request_context.rb +9 -14
- data/lib/contrast/agent/rewriter.rb +5 -3
- data/lib/contrast/agent/service_heartbeat.rb +2 -3
- data/lib/contrast/agent/tracepoint_hook.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/response_processor.rb +2 -4
- data/lib/contrast/api/communication/service_lifecycle.rb +4 -2
- data/lib/contrast/api/communication/socket_client.rb +8 -21
- data/lib/contrast/api/communication/speedracer.rb +3 -7
- data/lib/contrast/api/decorators/application_startup.rb +6 -2
- data/lib/contrast/api/decorators/library.rb +8 -6
- data/lib/contrast/api/decorators/message.rb +3 -3
- data/lib/contrast/api/decorators/trace_event.rb +3 -1
- data/lib/contrast/api/decorators/trace_event_object.rb +1 -3
- data/lib/contrast/api/decorators/trace_taint_range_tags.rb +1 -6
- data/lib/contrast/components/agent.rb +9 -4
- data/lib/contrast/components/app_context.rb +6 -6
- data/lib/contrast/components/config.rb +2 -1
- data/lib/contrast/components/contrast_service.rb +7 -8
- data/lib/contrast/components/interface.rb +3 -2
- data/lib/contrast/components/sampling.rb +8 -2
- data/lib/contrast/components/settings.rb +4 -2
- data/lib/contrast/config/assess_rules_configuration.rb +1 -3
- data/lib/contrast/config/base_configuration.rb +4 -5
- data/lib/contrast/config/exception_configuration.rb +1 -5
- data/lib/contrast/config/heap_dump_configuration.rb +12 -6
- data/lib/contrast/config/logger_configuration.rb +1 -5
- data/lib/contrast/configuration.rb +3 -14
- data/lib/contrast/extension/assess/array.rb +1 -6
- data/lib/contrast/extension/assess/erb.rb +1 -7
- data/lib/contrast/extension/assess/eval_trigger.rb +2 -6
- data/lib/contrast/extension/assess/exec_trigger.rb +3 -9
- data/lib/contrast/extension/assess/fiber.rb +2 -12
- data/lib/contrast/extension/assess/kernel.rb +2 -9
- data/lib/contrast/extension/assess/marshal.rb +2 -6
- data/lib/contrast/extension/assess/regexp.rb +1 -6
- data/lib/contrast/extension/assess/string.rb +3 -1
- data/lib/contrast/extension/kernel.rb +4 -2
- data/lib/contrast/framework/manager.rb +1 -2
- data/lib/contrast/framework/rack/patch/session_cookie.rb +5 -18
- data/lib/contrast/framework/rack/patch/support.rb +6 -4
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +7 -2
- data/lib/contrast/framework/rails/patch/support.rb +4 -2
- data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +4 -1
- data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +2 -0
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +2 -0
- data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +2 -0
- data/lib/contrast/framework/rails/support.rb +2 -2
- data/lib/contrast/framework/sinatra/support.rb +3 -1
- data/lib/contrast/funchook/funchook.rb +1 -5
- data/lib/contrast/logger/application.rb +12 -9
- data/lib/contrast/logger/format.rb +2 -5
- data/lib/contrast/logger/log.rb +4 -3
- data/lib/contrast/logger/request.rb +1 -2
- data/lib/contrast/security_exception.rb +1 -1
- data/lib/contrast/tasks/service.rb +5 -1
- data/lib/contrast/utils/assess/tracking_util.rb +1 -2
- data/lib/contrast/utils/class_util.rb +0 -8
- data/lib/contrast/utils/hash_digest.rb +2 -5
- data/lib/contrast/utils/io_util.rb +1 -1
- data/lib/contrast/utils/job_servers_running.rb +9 -4
- data/lib/contrast/utils/os.rb +2 -1
- data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -1
- data/ruby-agent.gemspec +13 -14
- data/sonar-project.properties +9 -0
- metadata +37 -36
|
@@ -186,13 +186,7 @@ module Contrast
|
|
|
186
186
|
|
|
187
187
|
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
|
188
188
|
args = preshift.args
|
|
189
|
-
properties.build_event(
|
|
190
|
-
patcher,
|
|
191
|
-
ret,
|
|
192
|
-
preshift.object,
|
|
193
|
-
ret,
|
|
194
|
-
args,
|
|
195
|
-
2)
|
|
189
|
+
properties.build_event(patcher, ret, preshift.object, ret, args, 2)
|
|
196
190
|
properties.event.instance_variable_set(:@_parent_events, parent_events)
|
|
197
191
|
end
|
|
198
192
|
end
|
|
@@ -12,7 +12,6 @@ module Contrast
|
|
|
12
12
|
# right' state soon.
|
|
13
13
|
module Trim
|
|
14
14
|
class << self
|
|
15
|
-
|
|
16
15
|
# @param policy_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
17
16
|
# propagation event.
|
|
18
17
|
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
@@ -26,13 +25,7 @@ module Contrast
|
|
|
26
25
|
properties.copy_from(preshift.object, ret)
|
|
27
26
|
handle_tr(policy_node, preshift, ret, properties)
|
|
28
27
|
|
|
29
|
-
properties.build_event(
|
|
30
|
-
policy_node,
|
|
31
|
-
ret,
|
|
32
|
-
preshift.object,
|
|
33
|
-
ret,
|
|
34
|
-
preshift.args,
|
|
35
|
-
1)
|
|
28
|
+
properties.build_event(policy_node, ret, preshift.object, ret, preshift.args, 1)
|
|
36
29
|
ret
|
|
37
30
|
end
|
|
38
31
|
|
|
@@ -43,12 +36,7 @@ module Contrast
|
|
|
43
36
|
source = preshift.object
|
|
44
37
|
args = preshift.args
|
|
45
38
|
properties.splat_from(source, ret)
|
|
46
|
-
properties.build_event(
|
|
47
|
-
patcher,
|
|
48
|
-
ret,
|
|
49
|
-
source,
|
|
50
|
-
ret,
|
|
51
|
-
args)
|
|
39
|
+
properties.build_event(patcher, ret, source, ret, args)
|
|
52
40
|
ret
|
|
53
41
|
end
|
|
54
42
|
|
|
@@ -72,7 +60,7 @@ module Contrast
|
|
|
72
60
|
end
|
|
73
61
|
|
|
74
62
|
# Otherwise, we need to target each insertion point. Based on the spec for #tr & #tr_s, the find is
|
|
75
|
-
# treated as a
|
|
63
|
+
# treated as a regexp range, excepting the `\` character, which we'll need to escape. This converts to
|
|
76
64
|
# that form, wrapping the input in `[]`.
|
|
77
65
|
find_string = preshift.args[0]
|
|
78
66
|
find_string += '\\' if find_string.end_with?('\\')
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
return unless RUBY_VERSION < '2.6.0' # TODO: RUBY-714 remove guard w/ EOL of 2.5
|
|
5
|
+
|
|
4
6
|
require 'contrast/agent/patching/policy/patch_status'
|
|
5
7
|
require 'contrast/agent/module_data'
|
|
6
8
|
require 'contrast/agent/rewriter'
|
|
@@ -41,7 +43,9 @@ module Contrast
|
|
|
41
43
|
module_name = mod.cs__name
|
|
42
44
|
return unless module_name
|
|
43
45
|
|
|
44
|
-
if module_name.start_with?(Contrast::Utils::ObjectShare::CONTRAST_MODULE_START,
|
|
46
|
+
if module_name.start_with?(Contrast::Utils::ObjectShare::CONTRAST_MODULE_START,
|
|
47
|
+
Contrast::Utils::ObjectShare::ANONYMOUS_CLASS_MARKER)
|
|
48
|
+
|
|
45
49
|
status.no_rewrite!
|
|
46
50
|
return
|
|
47
51
|
|
|
@@ -64,8 +68,7 @@ module Contrast
|
|
|
64
68
|
# To get around this, we have those methods tell us the class
|
|
65
69
|
# isn't ready
|
|
66
70
|
def mid_defining? mod
|
|
67
|
-
mod.instance_variable_defined?(:@cs__defining_class) &&
|
|
68
|
-
mod.instance_variable_get(:@cs__defining_class)
|
|
71
|
+
mod.instance_variable_defined?(:@cs__defining_class) && mod.instance_variable_get(:@cs__defining_class)
|
|
69
72
|
end
|
|
70
73
|
|
|
71
74
|
def agent_should_rewrite?
|
|
@@ -56,7 +56,8 @@ module Contrast
|
|
|
56
56
|
# double check that we were able to finalize the replaced return
|
|
57
57
|
return unless Contrast::Agent::Assess::Tracker.trackable?(target)
|
|
58
58
|
end
|
|
59
|
-
apply_source(Contrast::Agent::REQUEST_TRACKER.current, source_node, target, object, ret,
|
|
59
|
+
apply_source(Contrast::Agent::REQUEST_TRACKER.current, source_node, target, object, ret,
|
|
60
|
+
source_node.type, nil, *args)
|
|
60
61
|
restore_frozen_state ? ret : nil
|
|
61
62
|
end
|
|
62
63
|
|
|
@@ -88,7 +89,9 @@ module Contrast
|
|
|
88
89
|
# While we don't taint arrays themselves, we may taint the things they hold. Let's pass their keys and
|
|
89
90
|
# values back to ourselves and try again
|
|
90
91
|
elsif Contrast::Utils::DuckUtils.iterable_enumerable?(target)
|
|
91
|
-
target.each
|
|
92
|
+
target.each do |value|
|
|
93
|
+
apply_source(context, source_node, value, object, ret, source_type, source_name, *args)
|
|
94
|
+
end
|
|
92
95
|
end
|
|
93
96
|
rescue StandardError => e
|
|
94
97
|
logger.warn('Unable to apply source', e, node_id: source_node.id)
|
|
@@ -169,10 +172,7 @@ module Contrast
|
|
|
169
172
|
length = Contrast::Utils::StringUtils.ret_length(target)
|
|
170
173
|
properties.add_tag(tag, 0...length)
|
|
171
174
|
properties.add_properties(source_node.properties)
|
|
172
|
-
logger.trace('Source detected',
|
|
173
|
-
node_id: source_node.id,
|
|
174
|
-
target_id: target.__id__,
|
|
175
|
-
tag: tag)
|
|
175
|
+
logger.trace('Source detected', node_id: source_node.id, target_id: target.__id__, tag: tag)
|
|
176
176
|
end
|
|
177
177
|
# make a representation of this method that TeamServer can render
|
|
178
178
|
properties.build_event(source_node, target, object, ret, args, source_type, source_name)
|
|
@@ -11,9 +11,7 @@ module Contrast
|
|
|
11
11
|
# certain tags in a given context. This provides a single place from
|
|
12
12
|
# which those validations can be called.
|
|
13
13
|
module SourceValidation
|
|
14
|
-
VALIDATORS = [
|
|
15
|
-
Contrast::Agent::Assess::Policy::SourceValidation::CrossSiteValidator
|
|
16
|
-
].cs__freeze
|
|
14
|
+
VALIDATORS = [Contrast::Agent::Assess::Policy::SourceValidation::CrossSiteValidator].cs__freeze
|
|
17
15
|
|
|
18
16
|
# Determines if the conditions in which this source was called are
|
|
19
17
|
# valid and should result in the application of a tag
|
|
@@ -44,7 +44,11 @@ module Contrast
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
if Contrast::Agent::Assess::Tracker.tracked?(ret)
|
|
47
|
-
Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(trigger_node,
|
|
47
|
+
Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(trigger_node,
|
|
48
|
+
ret,
|
|
49
|
+
erb_template_prerender,
|
|
50
|
+
ret,
|
|
51
|
+
interpolated_inputs)
|
|
48
52
|
end
|
|
49
53
|
|
|
50
54
|
ret
|
|
@@ -42,18 +42,10 @@ module Contrast
|
|
|
42
42
|
if trigger_node.sources&.any?
|
|
43
43
|
trigger_node.sources.each do |marker|
|
|
44
44
|
source = determine_source(marker, object, ret, args)
|
|
45
|
-
apply_trigger(trigger_node,
|
|
46
|
-
source,
|
|
47
|
-
object,
|
|
48
|
-
ret,
|
|
49
|
-
*args)
|
|
45
|
+
apply_trigger(trigger_node, source, object, ret, *args)
|
|
50
46
|
end
|
|
51
47
|
else
|
|
52
|
-
apply_trigger(trigger_node,
|
|
53
|
-
nil,
|
|
54
|
-
object,
|
|
55
|
-
ret,
|
|
56
|
-
*args)
|
|
48
|
+
apply_trigger(trigger_node, nil, object, ret, *args)
|
|
57
49
|
end
|
|
58
50
|
end
|
|
59
51
|
|
|
@@ -87,10 +79,8 @@ module Contrast
|
|
|
87
79
|
append_route(finding, request)
|
|
88
80
|
append_hash(finding, source, request)
|
|
89
81
|
finding.version = determine_compliance_version(finding)
|
|
90
|
-
logger.trace('Finding created',
|
|
91
|
-
|
|
92
|
-
source_id: source.__id__,
|
|
93
|
-
rule: trigger_node.rule_id)
|
|
82
|
+
logger.trace('Finding created', node_id: trigger_node.id, source_id: source.__id__,
|
|
83
|
+
rule: trigger_node.rule_id)
|
|
94
84
|
report_finding(finding, request)
|
|
95
85
|
rescue StandardError => e
|
|
96
86
|
logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
|
|
@@ -285,7 +275,8 @@ module Contrast
|
|
|
285
275
|
|
|
286
276
|
def append_events finding, trigger_node, source, object, ret, args
|
|
287
277
|
append_from_source(finding, source)
|
|
288
|
-
finding.events << Contrast::Agent::Assess::Events::EventFactory.build(trigger_node, source, object, ret,
|
|
278
|
+
finding.events << Contrast::Agent::Assess::Events::EventFactory.build(trigger_node, source, object, ret,
|
|
279
|
+
args).to_dtm_event
|
|
289
280
|
end
|
|
290
281
|
|
|
291
282
|
def append_from_source finding, source
|
|
@@ -104,7 +104,8 @@ 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 = ranges_with_all_tags(Contrast::Utils::StringUtils.ret_length(source), properties,
|
|
107
|
+
vulnerable_ranges = ranges_with_all_tags(Contrast::Utils::StringUtils.ret_length(source), properties,
|
|
108
|
+
required_tags)
|
|
108
109
|
# if there aren't any vulnerable ranges, nope out
|
|
109
110
|
return false if vulnerable_ranges.empty?
|
|
110
111
|
|
|
@@ -35,9 +35,10 @@ module Contrast
|
|
|
35
35
|
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/redos.md
|
|
36
36
|
def regexp_vulnerable? regexp
|
|
37
37
|
# A pattern is considered vulnerable if it has 2 or more levels of nested multi-matching.
|
|
38
|
-
# A level is defined as any set of opening and closing control characters immediately followed by a
|
|
39
|
-
#
|
|
40
|
-
#
|
|
38
|
+
# A level is defined as any set of opening and closing control characters immediately followed by a
|
|
39
|
+
# multi match control character.
|
|
40
|
+
# A control character is defined as one of the OPENING_CHARS, CLOSING_CHARS, or MULTI_MATCH_CHARS that
|
|
41
|
+
# is not immediately preceded by an escaping \ character.
|
|
41
42
|
# OPENING_CHARS are ( and [ CLOSING_CHARS are ) and ] MULTI_MATCH_CHARS are +, *, and ?
|
|
42
43
|
|
|
43
44
|
# Nota bene about Regexp#to_s: it doesn't necessarily give you the original Regexp back
|
|
@@ -10,8 +10,7 @@ module Contrast
|
|
|
10
10
|
# before serializing that finding as a DTM to report to the service.
|
|
11
11
|
module SSRFValidator
|
|
12
12
|
RULE_NAME = 'ssrf'
|
|
13
|
-
URL_PATTERN =
|
|
14
|
-
%r{(?<protocol>http|https|ftp|sftp|telnet|gopher|rtsp|rtsps|ssh|svn)://(?<host>[^/?]+)(?<path>/?[^?]*)(?<query_string>\?.*)?}i.cs__freeze
|
|
13
|
+
URL_PATTERN = %r{(?<protocol>http|https|ftp|sftp|telnet|gopher|rtsp|rtsps|ssh|svn)://(?<host>[^/?]+)(?<path>/?[^?]*)(?<query_string>\?.*)?}i.cs__freeze # rubocop:disable Layout/LineLength
|
|
15
14
|
# The Net::HTTP class validates host format on instantiation. Since
|
|
16
15
|
# our triggers for that class are on the instance, they already
|
|
17
16
|
# have this validation done for them. We do not need to apply the
|
|
@@ -11,14 +11,7 @@ module Contrast
|
|
|
11
11
|
# the service.
|
|
12
12
|
module XSSValidator
|
|
13
13
|
RULE_NAME = 'reflected-xss'
|
|
14
|
-
SAFE_CONTENT_TYPES = %w[
|
|
15
|
-
/csv
|
|
16
|
-
/javascript
|
|
17
|
-
/json
|
|
18
|
-
/pdf
|
|
19
|
-
/x-javascript
|
|
20
|
-
/x-json
|
|
21
|
-
].cs__freeze
|
|
14
|
+
SAFE_CONTENT_TYPES = %w[/csv /javascript /json /pdf /x-javascript /x-json].cs__freeze
|
|
22
15
|
|
|
23
16
|
# A finding is valid for XSS if the response type is not one of
|
|
24
17
|
# those assumed to be safe
|
|
@@ -14,7 +14,7 @@ module Contrast
|
|
|
14
14
|
# @attr_reader event [Contrast::Agent::Assess::ContrastEvent] the
|
|
15
15
|
# latest event to track
|
|
16
16
|
module Evented
|
|
17
|
-
|
|
17
|
+
attr_reader :event
|
|
18
18
|
|
|
19
19
|
# Create a new event and add it to the event set.
|
|
20
20
|
#
|
|
@@ -31,7 +31,8 @@ module Contrast
|
|
|
31
31
|
# the key used to accessed if from a map or nil if a type like
|
|
32
32
|
# BODY
|
|
33
33
|
def build_event policy_node, tagged, object, ret, args, source_type = nil, source_name = nil
|
|
34
|
-
@event = Contrast::Agent::Assess::Events::EventFactory.build(policy_node, tagged, object, ret, args,
|
|
34
|
+
@event = Contrast::Agent::Assess::Events::EventFactory.build(policy_node, tagged, object, ret, args,
|
|
35
|
+
source_type, source_name)
|
|
35
36
|
report_sources(tagged, event)
|
|
36
37
|
end
|
|
37
38
|
|
|
@@ -46,10 +47,12 @@ module Contrast
|
|
|
46
47
|
return unless tagged && !tagged.to_s.empty?
|
|
47
48
|
return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
|
|
48
49
|
return unless event.source_type
|
|
50
|
+
return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
|
|
51
|
+
|
|
52
|
+
if current_request.observed_route.sources.any? do |source|
|
|
53
|
+
source.type == event.forced_source_type && source.name == event.forced_source_name
|
|
54
|
+
end
|
|
49
55
|
|
|
50
|
-
current_request = Contrast::Agent::REQUEST_TRACKER.current
|
|
51
|
-
return unless current_request
|
|
52
|
-
if current_request.observed_route.sources.any? { |source| source.type == event.forced_source_type && source.name == event.forced_source_name }
|
|
53
56
|
return
|
|
54
57
|
end
|
|
55
58
|
|
|
@@ -55,9 +55,12 @@ module Contrast
|
|
|
55
55
|
value_node.children.each do |child|
|
|
56
56
|
next unless child
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
unless child.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) &&
|
|
59
|
+
child.type == :LIT &&
|
|
60
|
+
child.children[0]&.cs__is_a?(Integer)
|
|
61
|
+
|
|
62
|
+
return false
|
|
63
|
+
end
|
|
61
64
|
end
|
|
62
65
|
|
|
63
66
|
true
|
|
@@ -84,8 +87,11 @@ module Contrast
|
|
|
84
87
|
return false unless children.length >= 2
|
|
85
88
|
|
|
86
89
|
potential_string_node = children[0]
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
unless potential_string_node.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) &&
|
|
91
|
+
potential_string_node.type == :STR
|
|
92
|
+
|
|
93
|
+
return false
|
|
94
|
+
end
|
|
89
95
|
|
|
90
96
|
children[1] == :bytes
|
|
91
97
|
end
|
|
@@ -29,7 +29,10 @@ module Contrast
|
|
|
29
29
|
# These are markers whose presence indicates that a field is more
|
|
30
30
|
# likely to be a descriptor or requirement than an actual password.
|
|
31
31
|
# We should ignore fields that contain them.
|
|
32
|
-
NON_PASSWORD_PARTIAL_NAMES = %w[
|
|
32
|
+
NON_PASSWORD_PARTIAL_NAMES = %w[
|
|
33
|
+
DATE FORGOT FORM ENCODE PATTERN PREFIX PROP SUFFIX URL BASE FILE
|
|
34
|
+
URI
|
|
35
|
+
].cs__freeze
|
|
33
36
|
|
|
34
37
|
# If the constant looks like a password and it doesn't look like a
|
|
35
38
|
# password descriptor, it passes for this rule
|
|
@@ -26,10 +26,7 @@ module Contrast
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# TODO: RUBY-1014 - remove `#analyze`
|
|
29
|
-
COMMON_CONSTANTS = %i[
|
|
30
|
-
CONTRAST_ASSESS_POLICY_STATUS
|
|
31
|
-
VERSION
|
|
32
|
-
].cs__freeze
|
|
29
|
+
COMMON_CONSTANTS = %i[CONTRAST_ASSESS_POLICY_STATUS VERSION].cs__freeze
|
|
33
30
|
def analyze clazz
|
|
34
31
|
return if disabled?
|
|
35
32
|
|
|
@@ -171,7 +168,8 @@ module Contrast
|
|
|
171
168
|
|
|
172
169
|
finding.properties[SOURCE_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(class_name)
|
|
173
170
|
finding.properties[CONSTANT_NAME_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(constant_string)
|
|
174
|
-
finding.properties[CODE_SOURCE_KEY] =
|
|
171
|
+
finding.properties[CODE_SOURCE_KEY] =
|
|
172
|
+
Contrast::Utils::StringUtils.protobuf_safe_string(constant_string + redacted_marker)
|
|
175
173
|
|
|
176
174
|
hash = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
|
|
177
175
|
finding.hash_code = Contrast::Utils::StringUtils.protobuf_safe_string(hash)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
return unless RUBY_VERSION < '2.6.0' # TODO: RUBY-714 remove guard w/ EOL of 2.5
|
|
5
|
+
|
|
4
6
|
require 'ripper'
|
|
5
7
|
require 'contrast/extension/module'
|
|
6
8
|
require 'contrast/components/interface'
|
|
@@ -48,7 +50,7 @@ module Contrast
|
|
|
48
50
|
:public_instance_methods, :protected_instance_methods, :private_instance_methods, :locations
|
|
49
51
|
|
|
50
52
|
def initialize module_data
|
|
51
|
-
@class_module_path = module_data.
|
|
53
|
+
@class_module_path = module_data.mod_name
|
|
52
54
|
clazz = module_data.mod
|
|
53
55
|
@is_class = clazz.is_a?(Class)
|
|
54
56
|
@public_instance_methods = []
|
|
@@ -13,9 +13,7 @@ module Contrast
|
|
|
13
13
|
access_component :agent, :logging
|
|
14
14
|
|
|
15
15
|
def self.run _reaction, level
|
|
16
|
-
logger.with_level(
|
|
17
|
-
level,
|
|
18
|
-
'Contrast received instructions to disable itself - Disabling now')
|
|
16
|
+
logger.with_level(level, 'Contrast received instructions to disable itself - Disabling now')
|
|
19
17
|
AGENT.disable!
|
|
20
18
|
end
|
|
21
19
|
end
|
|
@@ -47,7 +47,7 @@ module Contrast
|
|
|
47
47
|
return if @wildcard_url
|
|
48
48
|
return unless @exclusion.urls&.any?
|
|
49
49
|
|
|
50
|
-
@wildcard_url ||= @exclusion.urls.any?
|
|
50
|
+
@wildcard_url ||= @exclusion.urls.any?('/.*')
|
|
51
51
|
return if @wildcard_url
|
|
52
52
|
|
|
53
53
|
@urls = []
|
|
@@ -95,8 +95,8 @@ module Contrast
|
|
|
95
95
|
@exclusion.type == Contrast::Api::Settings::Exclusion::ExclusionType::CODE
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
def
|
|
99
|
-
@exclusion.name
|
|
98
|
+
def exc_name
|
|
99
|
+
@exclusion.name # rubocop:disable Security/Module/Name -- part of the API.
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def match_all?
|
|
@@ -109,10 +109,7 @@ module Contrast
|
|
|
109
109
|
#
|
|
110
110
|
# @param rule - the id of the rule which we're checking for exclusion
|
|
111
111
|
def protection_rule? rule
|
|
112
|
-
protect? &&
|
|
113
|
-
(@exclusion.protection_rules.empty? ||
|
|
114
|
-
@exclusion.protection_rules.include?(rule)
|
|
115
|
-
)
|
|
112
|
+
protect? && (@exclusion.protection_rules.empty? || @exclusion.protection_rules.include?(rule))
|
|
116
113
|
end
|
|
117
114
|
|
|
118
115
|
# Determine if the given rule is excluded by this exclusion.
|
|
@@ -121,10 +118,7 @@ module Contrast
|
|
|
121
118
|
#
|
|
122
119
|
# @param rule - the id of the rule which we're checking for exclusion
|
|
123
120
|
def assess_rule? rule
|
|
124
|
-
assess? &&
|
|
125
|
-
(@exclusion.assessment_rules.empty? ||
|
|
126
|
-
@exclusion.assessment_rules.include?(rule)
|
|
127
|
-
)
|
|
121
|
+
assess? && (@exclusion.assessment_rules.empty? || @exclusion.assessment_rules.include?(rule))
|
|
128
122
|
end
|
|
129
123
|
|
|
130
124
|
def match_code? stack_trace
|
|
@@ -33,6 +33,7 @@ module Contrast
|
|
|
33
33
|
# then contrast depends on it. If its array of dependents is 1, then contrast is the
|
|
34
34
|
# only dependency in that list. Since only contrast depends on it, we should ignore it.
|
|
35
35
|
def find_contrast_gems
|
|
36
|
+
# rubocop:disable Security/Module/Name -- here name is part of Ruby Gems.
|
|
36
37
|
ignore = Set.new([CONTRAST_AGENT])
|
|
37
38
|
contrast_specs = Gem::DependencyList.from_specs.specs.find do |dependency|
|
|
38
39
|
dependency.name == CONTRAST_AGENT
|
|
@@ -43,6 +44,7 @@ module Contrast
|
|
|
43
44
|
ignore.add(dependency.name) if contrast_dep_set.include?(dependency.name) && dependents.length == 1
|
|
44
45
|
end
|
|
45
46
|
ignore
|
|
47
|
+
# rubocop:enable Security/Module/Name
|
|
46
48
|
end
|
|
47
49
|
end
|
|
48
50
|
end
|
|
@@ -37,9 +37,8 @@ module Contrast
|
|
|
37
37
|
@app = app # THIS MUST BE FIRST AND ALWAYS SET!
|
|
38
38
|
setup_agent # THIS MUST BE SECOND AND ALWAYS CALLED!
|
|
39
39
|
unless AGENT.enabled?
|
|
40
|
-
logger.error(
|
|
41
|
-
|
|
42
|
-
'Disabling permanently.')
|
|
40
|
+
logger.error('The Agent was unable to initialize before the application middleware was initialized. '\
|
|
41
|
+
'Disabling permanently.')
|
|
43
42
|
AGENT.disable! # ensure the agent is disabled (probably redundant)
|
|
44
43
|
return
|
|
45
44
|
end
|
|
@@ -207,8 +206,7 @@ module Contrast
|
|
|
207
206
|
# @param exception [Exception]
|
|
208
207
|
# @return [Boolean]
|
|
209
208
|
def security_exception? exception
|
|
210
|
-
exception.is_a?(Contrast::SecurityException) ||
|
|
211
|
-
exception.message&.include?(SECURITY_EXCEPTION_MARKER)
|
|
209
|
+
exception.is_a?(Contrast::SecurityException) || exception.message&.include?(SECURITY_EXCEPTION_MARKER)
|
|
212
210
|
end
|
|
213
211
|
|
|
214
212
|
# As we deprecate support to prepare to remove dead code, we need to inform our users still relying on the now
|