contrast-agent 3.14.0 → 3.15.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/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +18 -15
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +1 -0
- data/ext/cs__assess_string/cs__assess_string.c +24 -25
- data/ext/cs__assess_string/cs__assess_string.h +3 -1
- data/ext/cs__common/cs__common.c +4 -2
- data/ext/cs__common/cs__common.h +1 -1
- data/lib/contrast.rb +1 -1
- data/lib/contrast/agent/assess.rb +1 -0
- data/lib/contrast/agent/assess/contrast_event.rb +4 -12
- data/lib/contrast/agent/assess/finalizers/freeze.rb +3 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +45 -1
- data/lib/contrast/agent/assess/policy/patcher.rb +1 -1
- data/lib/contrast/agent/assess/policy/policy.rb +0 -2
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +0 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +7 -11
- data/lib/contrast/agent/assess/policy/propagation_method.rb +50 -33
- data/lib/contrast/agent/assess/policy/propagator/append.rb +8 -5
- data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/center.rb +9 -5
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +5 -3
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +6 -3
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +4 -1
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +6 -6
- data/lib/contrast/agent/assess/policy/propagator/next.rb +7 -5
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +8 -5
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +8 -4
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +5 -2
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +7 -5
- data/lib/contrast/agent/assess/policy/propagator/select.rb +15 -7
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +14 -8
- data/lib/contrast/agent/assess/policy/propagator/split.rb +14 -8
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +30 -21
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +11 -5
- data/lib/contrast/agent/assess/policy/source_method.rb +85 -58
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +16 -11
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +38 -15
- data/lib/contrast/agent/assess/policy/trigger_node.rb +14 -13
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -1
- data/lib/contrast/agent/assess/properties.rb +2 -0
- data/lib/contrast/agent/assess/property/updated.rb +136 -0
- data/lib/contrast/agent/assess/tracker.rb +66 -0
- data/lib/contrast/agent/class_reopener.rb +7 -5
- data/lib/contrast/agent/middleware.rb +0 -1
- data/lib/contrast/agent/patching/policy/patcher.rb +13 -22
- data/lib/contrast/agent/patching/policy/policy.rb +1 -4
- data/lib/contrast/agent/response.rb +17 -6
- data/lib/contrast/agent/rewriter.rb +1 -3
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +1 -4
- data/lib/contrast/api/decorators/application_update.rb +2 -4
- data/lib/contrast/api/decorators/trace_event.rb +5 -5
- data/lib/contrast/components/app_context.rb +11 -9
- data/lib/contrast/components/config.rb +3 -13
- data/lib/contrast/components/contrast_service.rb +2 -2
- data/lib/contrast/config/application_configuration.rb +5 -2
- data/lib/contrast/config/service_configuration.rb +8 -2
- data/lib/contrast/configuration.rb +88 -47
- data/lib/contrast/extension/assess.rb +0 -2
- data/lib/contrast/extension/assess/array.rb +8 -5
- data/lib/contrast/extension/assess/erb.rb +6 -3
- data/lib/contrast/extension/assess/fiber.rb +9 -9
- data/lib/contrast/extension/assess/hash.rb +2 -3
- data/lib/contrast/extension/assess/kernel.rb +12 -5
- data/lib/contrast/extension/assess/marshal.rb +3 -2
- data/lib/contrast/extension/assess/regexp.rb +5 -4
- data/lib/contrast/extension/assess/string.rb +8 -10
- data/lib/contrast/framework/rack/patch/session_cookie.rb +12 -18
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +4 -10
- data/lib/contrast/framework/rails/support.rb +2 -0
- data/lib/contrast/logger/application.rb +11 -3
- data/lib/contrast/utils/assess/tracking_util.rb +48 -3
- data/lib/contrast/utils/duck_utils.rb +0 -10
- data/lib/contrast/utils/env_configuration_item.rb +2 -1
- data/lib/contrast/utils/invalid_configuration_util.rb +21 -19
- data/lib/contrast/utils/string_utils.rb +10 -5
- data/resources/assess/policy.json +0 -10
- data/ruby-agent.gemspec +16 -15
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +42 -21
- data/lib/contrast/agent/assess/finalizers/finalize.rb +0 -21
- data/lib/contrast/extension/assess/assess_extension.rb +0 -145
- data/lib/contrast/utils/freeze_util.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac8fc7d0e9c127859cf7bdb149c7ac519286c4329bd81ad28db4963128e0cd63
|
4
|
+
data.tar.gz: 784afc67ef269df8dfbaed392e8ad28e2e3b679113ed8679625f95033e324929
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1a5563b34a4eba33fdf8094986362f70c88bda53102899bb01ad0096588d26883b5e7ad475e41afa15b95674b8c2810cfe6546c6779b8e3b2030bf7bb1e33d6
|
7
|
+
data.tar.gz: 1a1eb220719c1caf3f7153108bff4ac5132130d6879dc2b425e6dd31720e4c76bda9c27c0ac65fa00ccb846a31cb173e04dc7d3320ba3079ea9b785a62991f00
|
@@ -14,20 +14,19 @@ static VALUE contrast_assess_marshal_module_load(const int argc,
|
|
14
14
|
if (argc >= 1) {
|
15
15
|
source_string = argv[0];
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
}
|
17
|
+
VALUE tracked =
|
18
|
+
rb_funcall(properties_hash, rb_sym_hash_tracked, 1, source_string);
|
19
|
+
|
20
|
+
if (tracked == Qtrue) {
|
21
|
+
VALUE skip =
|
22
|
+
rb_funcall(contrast_patcher(), rb_sym_skip_assess_analysis, 0);
|
23
|
+
|
24
|
+
if (skip == Qfalse) {
|
25
|
+
VALUE scope =
|
26
|
+
rb_funcall(contrast_patcher(), rb_sym_enter_scope, 0);
|
27
|
+
rb_funcall(marshal_module, rb_sym_assess_load_trigger_check, 2,
|
28
|
+
source_string, result);
|
29
|
+
rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
|
31
30
|
}
|
32
31
|
}
|
33
32
|
}
|
@@ -35,7 +34,11 @@ static VALUE contrast_assess_marshal_module_load(const int argc,
|
|
35
34
|
}
|
36
35
|
|
37
36
|
void Init_cs__assess_marshal_module(void) {
|
38
|
-
|
37
|
+
// Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
|
38
|
+
VALUE tracker = rb_define_class_under(assess, "Tracker", rb_cObject);
|
39
|
+
properties_hash = rb_const_get(tracker, rb_intern("PROPERTIES_HASH"));
|
40
|
+
marshal_module =
|
41
|
+
rb_define_class_under(core_assess, "MarshalPropagator", rb_cObject);
|
39
42
|
rb_sym_assess_load_trigger_check = rb_intern("cs__load_trigger_check");
|
40
43
|
|
41
44
|
contrast_register_singleton_prepend_patch(
|
@@ -5,44 +5,43 @@
|
|
5
5
|
#include "../cs__common/cs__common.h"
|
6
6
|
#include <ruby.h>
|
7
7
|
|
8
|
-
static VALUE
|
8
|
+
static VALUE contrast_assess_string_freeze(const int argc, VALUE *argv,
|
9
9
|
const VALUE obj) {
|
10
|
-
VALUE dup, tracked;
|
11
10
|
if (!OBJ_FROZEN(obj)) {
|
12
|
-
|
13
|
-
if (RTEST(tracked)) {
|
14
|
-
/*
|
15
|
-
* If the object is not frozen and the object is tracked, we cheat.
|
16
|
-
* We dup and then freeze to replicate the behavior of str_uminus in
|
17
|
-
* string.c, but we ignore any other monkey patches on String#-@
|
18
|
-
*/
|
19
|
-
dup = rb_funcall(obj, rb_sym_dup, 0);
|
20
|
-
rb_funcall(obj, rb_intern("cs__transfer_properties"), 1, dup);
|
21
|
-
rb_funcall(dup, rb_sym_freeze, 0);
|
22
|
-
return dup;
|
23
|
-
}
|
11
|
+
rb_funcall(properties_hash, rb_sym_pre_freeze, 1, obj);
|
24
12
|
}
|
25
|
-
|
26
|
-
return rb_funcall(obj, rb_sym_assess_string_uminus, 0);
|
13
|
+
return rb_funcall(obj, rb_sym_assess_string_freeze, 0);
|
27
14
|
}
|
28
15
|
|
29
|
-
static VALUE
|
16
|
+
static VALUE contrast_assess_string_uminus(const int argc, VALUE *argv,
|
30
17
|
const VALUE obj) {
|
31
18
|
if (!OBJ_FROZEN(obj)) {
|
32
|
-
|
33
|
-
|
19
|
+
/* We're doing something intentionally different here. Ruby, for -@,
|
20
|
+
* attempts to de-duplicate the String and use an "interned" copy of
|
21
|
+
* the String. We cannot allow that to happen for a couple reasons:
|
22
|
+
* - prior to Ruby 2.7, this would cause us to track ALL instances of
|
23
|
+
* that interned copy.
|
24
|
+
* - 2.7 and later, this action is actually missed because of a change
|
25
|
+
* to the str_uminus method in Ruby, which dups the String in a way
|
26
|
+
* that we cannot see.
|
27
|
+
* B/c we cannot track this in 2.7, rather than having a version check
|
28
|
+
* and two approaches, we'll instead directly call the #freeze method,
|
29
|
+
* so the end result is that this String itself is frozen and never
|
30
|
+
* deduplicated.
|
31
|
+
*/
|
32
|
+
return contrast_assess_string_freeze(argc, argv, obj);
|
34
33
|
}
|
35
|
-
|
34
|
+
/* in all other cases, preserve monkey patching and c call */
|
35
|
+
return rb_funcall(obj, rb_sym_assess_string_uminus, 0);
|
36
36
|
}
|
37
37
|
|
38
38
|
void Init_cs__assess_string(void) {
|
39
39
|
rb_sym_dup = rb_intern("dup");
|
40
40
|
rb_sym_freeze = rb_intern("freeze");
|
41
|
-
|
42
|
-
// Contrast::Agent::Assess::
|
43
|
-
VALUE
|
44
|
-
|
45
|
-
properties_hash = rb_const_get(finalize, rb_intern("PROPERTIES_HASH"));
|
41
|
+
rb_sym_pre_freeze = rb_intern("pre_freeze");
|
42
|
+
// Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
|
43
|
+
VALUE tracker = rb_define_class_under(assess, "Tracker", rb_cObject);
|
44
|
+
properties_hash = rb_const_get(tracker, rb_intern("PROPERTIES_HASH"));
|
46
45
|
|
47
46
|
rb_sym_assess_string_uminus =
|
48
47
|
contrast_register_patch("String", "-@", &contrast_assess_string_uminus);
|
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
static VALUE rb_sym_assess_string_uminus;
|
4
4
|
static VALUE rb_sym_assess_string_freeze;
|
5
|
-
// Contrast::Agent::Assess::
|
5
|
+
// Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
|
6
6
|
static VALUE properties_hash;
|
7
7
|
static VALUE rb_sym_dup;
|
8
8
|
static VALUE rb_sym_freeze;
|
9
|
+
static VALUE rb_sym_pre_freeze;
|
10
|
+
static VALUE properties_hash;
|
9
11
|
|
10
12
|
/*
|
11
13
|
* The String#-@ method calls to the str_uminus method in String.C. This method
|
data/ext/cs__common/cs__common.c
CHANGED
@@ -18,7 +18,7 @@ VALUE rb_sym_in_scope;
|
|
18
18
|
VALUE rb_sym_skip_contrast_analysis;
|
19
19
|
VALUE rb_sym_skip_assess_analysis;
|
20
20
|
VALUE rb_sym_method;
|
21
|
-
VALUE
|
21
|
+
VALUE rb_sym_hash_get, rb_sym_hash_set, rb_sym_hash_tracked;
|
22
22
|
/* end globals */
|
23
23
|
|
24
24
|
void patch_via_funchook(void *original_function, void *hook_function) {
|
@@ -144,7 +144,9 @@ void Init_cs__common(void) {
|
|
144
144
|
rb_sym_skip_contrast_analysis = rb_intern("skip_contrast_analysis?");
|
145
145
|
rb_sym_skip_assess_analysis = rb_intern("skip_assess_analysis?");
|
146
146
|
rb_sym_method = rb_intern("__method__");
|
147
|
-
|
147
|
+
rb_sym_hash_get = rb_intern("[]");
|
148
|
+
rb_sym_hash_set = rb_intern("[]=");
|
149
|
+
rb_sym_hash_tracked = rb_intern("tracked?");
|
148
150
|
|
149
151
|
/* Used for returning unbound C functions */
|
150
152
|
rb_sym_register_c_patch = rb_intern("register_c_patch");
|
data/ext/cs__common/cs__common.h
CHANGED
@@ -23,7 +23,7 @@ extern VALUE rb_sym_in_scope;
|
|
23
23
|
extern VALUE rb_sym_skip_contrast_analysis;
|
24
24
|
extern VALUE rb_sym_skip_assess_analysis;
|
25
25
|
extern VALUE rb_sym_method;
|
26
|
-
extern VALUE
|
26
|
+
extern VALUE rb_sym_hash_get, rb_sym_hash_set, rb_sym_hash_tracked;
|
27
27
|
|
28
28
|
static VALUE patcher;
|
29
29
|
static VALUE rb_sym_instance_method;
|
data/lib/contrast.rb
CHANGED
@@ -8,6 +8,7 @@ module Contrast
|
|
8
8
|
# class under this namespace should be required here, providing a single
|
9
9
|
# point of require for this functionality.
|
10
10
|
module Assess
|
11
|
+
require 'contrast/agent/assess/tracker'
|
11
12
|
require 'contrast/agent/module_data'
|
12
13
|
require 'contrast/agent/rewriter'
|
13
14
|
require 'contrast/agent/assess/policy/preshift'
|
@@ -51,13 +51,7 @@ module Contrast
|
|
51
51
|
def safe_dup original
|
52
52
|
return nil unless original
|
53
53
|
|
54
|
-
|
55
|
-
duplicate = original.dup
|
56
|
-
original.cs__transfer_properties(duplicate)
|
57
|
-
duplicate
|
58
|
-
rescue StandardError
|
59
|
-
original
|
60
|
-
end
|
54
|
+
Contrast::Agent::Assess::Tracker.duplicate(original)
|
61
55
|
end
|
62
56
|
end
|
63
57
|
|
@@ -97,13 +91,11 @@ module Contrast
|
|
97
91
|
value_of_source(source, object, ret, args)
|
98
92
|
end
|
99
93
|
selected = mapped.select do |source|
|
100
|
-
source &&
|
101
|
-
Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties) &&
|
102
|
-
source.cs__properties.events &&
|
103
|
-
source.cs__properties.events.last
|
94
|
+
source && Contrast::Agent::Assess::Tracker.properties(source)&.events&.last
|
104
95
|
end
|
105
96
|
selected.map do |source|
|
106
|
-
source
|
97
|
+
properties = Contrast::Agent::Assess::Tracker.properties(source)
|
98
|
+
properties.events.last.event_id
|
107
99
|
end
|
108
100
|
end
|
109
101
|
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'contrast/agent/assess/tracker'
|
5
|
+
|
4
6
|
# Our patch of the Object#freeze method, allowing any Object we track to
|
5
7
|
# function with our Contrast::Agent::Assess::Finalizers::Hash
|
6
8
|
class Object
|
7
9
|
alias_method :cs__patched_object_freeze, :freeze
|
8
10
|
|
9
11
|
def freeze
|
10
|
-
Contrast::Agent::Assess::
|
12
|
+
Contrast::Agent::Assess::Tracker.pre_freeze(self)
|
11
13
|
cs__patched_object_freeze
|
12
14
|
end
|
13
15
|
end
|
@@ -29,6 +29,43 @@ module Contrast
|
|
29
29
|
super key.__id__
|
30
30
|
end
|
31
31
|
|
32
|
+
# Something is trackable if it is not a collection and either not
|
33
|
+
# frozen or it was frozen after we put a finalizer on it.
|
34
|
+
#
|
35
|
+
# @param key [Object] the thing to determine if trackable
|
36
|
+
# @return [Boolean]
|
37
|
+
def trackable? key
|
38
|
+
# Track things in these, not them themselves.
|
39
|
+
return false if Contrast::Utils::DuckUtils.iterable_hash?(key)
|
40
|
+
return false if Contrast::Utils::DuckUtils.iterable_enumerable?(key)
|
41
|
+
# If it's not frozen, we can finalize/ track it.
|
42
|
+
return true unless key.cs__frozen?
|
43
|
+
|
44
|
+
# Otherwise, we can only track it if we've finalized it in our
|
45
|
+
# freeze patch.
|
46
|
+
FROZEN_FINALIZED_IDS.include?(key.__id__)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Determine if the given Object is tracked, meaning it has a known
|
50
|
+
# set of properties and those properties are tracked.
|
51
|
+
#
|
52
|
+
# @param key [Object] the Object whose properties, by id, we want to
|
53
|
+
# check for tracked status
|
54
|
+
# @return [Boolean]
|
55
|
+
def tracked? key
|
56
|
+
key?(key.__id__) && fetch(key.__id__, nil)&.tracked?
|
57
|
+
end
|
58
|
+
|
59
|
+
# Remove the given key from our frozen and properties tracking during
|
60
|
+
# finalization of the Object to which the given key_id pertains.
|
61
|
+
# NOTE: by necessity, this is the only method which takes the __id__,
|
62
|
+
# not the Object itself. You CANNOT pass the Object to this as a
|
63
|
+
# finalizer cannot hold reference to the Object being finalized; that
|
64
|
+
# prevents GC, which introduces a memory leak and defeats the entire
|
65
|
+
# purpose of this.
|
66
|
+
#
|
67
|
+
# @param key_id [Integer] the Object Identifier to clean up during
|
68
|
+
# finalization.
|
32
69
|
def finalize key_id
|
33
70
|
proc do
|
34
71
|
FROZEN_FINALIZED_IDS.delete(key_id)
|
@@ -36,12 +73,19 @@ module Contrast
|
|
36
73
|
end
|
37
74
|
end
|
38
75
|
|
76
|
+
# Frozen things cannot be finalized. To avoid any issue here, we
|
77
|
+
# intercept the #freeze call and set finalizers on the Object. To
|
78
|
+
# ensure later we know it's been pre-finalized, we add it's __id__ to
|
79
|
+
# our tracking.
|
80
|
+
#
|
81
|
+
# @param key [Object] the Object on which we need to pre-define
|
82
|
+
# finalizers
|
39
83
|
def pre_freeze key
|
40
84
|
return if key.cs__frozen?
|
41
85
|
return if FROZEN_FINALIZED_IDS.include?(key.__id__)
|
42
|
-
return unless Contrast::Utils::DuckUtils.trackable?(key)
|
43
86
|
|
44
87
|
ObjectSpace.define_finalizer(key, finalize(key.__id__))
|
88
|
+
|
45
89
|
FROZEN_FINALIZED_IDS << key.__id__
|
46
90
|
rescue StandardError => _e
|
47
91
|
nil
|
@@ -37,7 +37,7 @@ module Contrast
|
|
37
37
|
return unless ASSESS.enabled?
|
38
38
|
return if in_contrast_scope?
|
39
39
|
|
40
|
-
|
40
|
+
patcher.patch_specific_module(mod)
|
41
41
|
rescue StandardError => e
|
42
42
|
logger.warn(
|
43
43
|
'Unable to patch assess during eval',
|
@@ -77,25 +77,21 @@ module Contrast
|
|
77
77
|
0
|
78
78
|
end
|
79
79
|
return unless can
|
80
|
+
return unless Contrast::Agent::Assess::Tracker.tracked?(object)
|
80
81
|
|
81
|
-
|
82
|
-
return unless props
|
83
|
-
|
84
|
-
Contrast::Agent::Assess::Finalizers::Finalize::PROPERTIES_HASH[preshift.object] ||= props.dup
|
82
|
+
Contrast::Agent::Assess::Tracker.copy(object, preshift.object)
|
85
83
|
end
|
86
84
|
|
87
85
|
def append_arg_details preshift, args
|
88
86
|
preshift.args = args.dup
|
89
|
-
preshift.args.each_with_index do |
|
87
|
+
preshift.args.each_with_index do |preshift_arg, index|
|
90
88
|
original_arg = args[index]
|
91
|
-
next if
|
92
|
-
|
93
|
-
props = Contrast::Agent::Assess::Finalizers::Finalize::PROPERTIES_HASH[original_arg]
|
94
|
-
next unless props
|
89
|
+
next if preshift_arg.__id__ == original_arg.__id__
|
90
|
+
next unless Contrast::Agent::Assess::Tracker.tracked?(original_arg)
|
95
91
|
|
96
|
-
Contrast::Agent::Assess::
|
92
|
+
Contrast::Agent::Assess::Tracker.copy(original_arg, preshift_arg)
|
97
93
|
end
|
98
|
-
preshift.arg_lengths = preshift.args.map { |
|
94
|
+
preshift.arg_lengths = preshift.args.map { |preshift_arg| Contrast::Utils::DuckUtils.quacks_to?(preshift_arg, :length) ? preshift_arg.length : 0 }
|
99
95
|
end
|
100
96
|
end
|
101
97
|
end
|
@@ -18,7 +18,7 @@ module Contrast
|
|
18
18
|
# Strings
|
19
19
|
module PropagationMethod
|
20
20
|
include Contrast::Components::Interface
|
21
|
-
access_component :logging
|
21
|
+
access_component :analysis, :logging
|
22
22
|
|
23
23
|
APPEND_ACTION = 'APPEND'
|
24
24
|
CENTER_ACTION = 'CENTER'
|
@@ -124,15 +124,16 @@ module Contrast
|
|
124
124
|
Contrast::Agent::Assess::Policy::Propagator::Custom.propagate(propagation_node, preshift, ret, block)
|
125
125
|
elsif propagation_node.action == SPLIT_ACTION
|
126
126
|
Contrast::Agent::Assess::Policy::Propagator::Split.propagate(propagation_node, preshift, target)
|
127
|
-
elsif Contrast::Utils::DuckUtils.quacks_to?(target, :cs__properties)
|
128
|
-
handle_cs_properties_propagation(propagation_node, preshift, target, object, ret, args, block)
|
129
127
|
elsif Contrast::Utils::DuckUtils.iterable_hash?(target)
|
130
128
|
handle_hash_propagation(propagation_node, preshift, target, object, ret, args, block)
|
131
129
|
elsif Contrast::Utils::DuckUtils.iterable_enumerable?(target)
|
132
130
|
handle_enumerable_propagation(propagation_node, preshift, target, object, ret, args, block)
|
131
|
+
else
|
132
|
+
handle_cs_properties_propagation(propagation_node, preshift, target, object, ret, args, block)
|
133
133
|
end
|
134
134
|
rescue StandardError => e
|
135
135
|
logger.warn('Unable to apply propagation', e, node_id: propagation_node.id)
|
136
|
+
nil
|
136
137
|
end
|
137
138
|
|
138
139
|
# Custom actions tend to be the more complex of our propagations.
|
@@ -186,28 +187,32 @@ module Contrast
|
|
186
187
|
false
|
187
188
|
end
|
188
189
|
|
190
|
+
# We cannot propagate to frozen things that have not been updated
|
191
|
+
# to work with our property tracking, unless they're duplicable and
|
192
|
+
# the return.
|
193
|
+
# We probably shouldn't propagate to frozen things at all, as
|
194
|
+
# they're supposed to be immutable, but third parties do jenky
|
195
|
+
# things, so allow it as long as it is safe to do.
|
196
|
+
#
|
197
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode]
|
198
|
+
# the node that governs this propagation event.
|
199
|
+
# @param target [Object] the Target to which to propagate.
|
200
|
+
# @return [Boolean] if the target can be propagated to
|
189
201
|
def appropriate_target? propagation_node, target
|
190
|
-
#
|
191
|
-
return
|
192
|
-
|
193
|
-
# We cannot propagate to frozen things that have not been
|
194
|
-
# previously tracked. We probably shouldn't propagate to frozen
|
195
|
-
# things at all, as they're supposed to be immutable, but third
|
196
|
-
# parties do jenky things, so allow it as long as it is safe to do.
|
197
|
-
return false if target.cs__frozen? &&
|
198
|
-
!target.cs__tracked? &&
|
199
|
-
propagation_node.targets[0] != Contrast::Utils::ObjectShare::RETURN_KEY
|
202
|
+
# special handle Returns b/c we can do unfreezing magic during propagation
|
203
|
+
return true if propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
|
200
204
|
|
201
|
-
|
205
|
+
Contrast::Agent::Assess::Tracker.trackable?(target)
|
202
206
|
end
|
203
207
|
|
204
208
|
# If this patcher has tags, apply them to the entire target
|
205
209
|
def apply_tags propagation_node, target
|
206
210
|
return unless propagation_node.tags
|
207
211
|
|
212
|
+
properties = Contrast::Agent::Assess::Tracker.properties(target)
|
208
213
|
length = Contrast::Utils::StringUtils.ret_length(target)
|
209
214
|
propagation_node.tags.each do |tag|
|
210
|
-
|
215
|
+
properties.add_tag(tag, 0...length)
|
211
216
|
end
|
212
217
|
end
|
213
218
|
|
@@ -215,8 +220,11 @@ module Contrast
|
|
215
220
|
def apply_untags propagation_node, target
|
216
221
|
return unless propagation_node.untags
|
217
222
|
|
223
|
+
properties = Contrast::Agent::Assess::Tracker.properties(target)
|
224
|
+
return unless properties
|
225
|
+
|
218
226
|
propagation_node.untags.each do |tag|
|
219
|
-
|
227
|
+
properties.delete_tags(tag)
|
220
228
|
end
|
221
229
|
end
|
222
230
|
|
@@ -230,6 +238,15 @@ module Contrast
|
|
230
238
|
true
|
231
239
|
end
|
232
240
|
|
241
|
+
# Safely duplicate the target, or return nil
|
242
|
+
#
|
243
|
+
# @param target [Object] the thing to check for duplication
|
244
|
+
def safe_dup target
|
245
|
+
target.dup
|
246
|
+
rescue StandardError => _e
|
247
|
+
nil
|
248
|
+
end
|
249
|
+
|
233
250
|
def handle_hash_propagation propagation_node, preshift, target, object, ret, args, block
|
234
251
|
target.each_pair do |key, value|
|
235
252
|
apply_propagator(propagation_node, preshift, key, object, ret, args, block)
|
@@ -249,30 +266,33 @@ module Contrast
|
|
249
266
|
return if propagation_node.action == NOOP_ACTION
|
250
267
|
return unless can_propagate?(propagation_node, preshift, target)
|
251
268
|
|
252
|
-
# propagate all the tags from the sources to the target
|
253
269
|
propagation_class = PROPAGATION_ACTIONS.fetch(propagation_node.action, nil)
|
254
270
|
unless propagation_class
|
255
271
|
logger.warn(
|
256
|
-
'Unknown
|
272
|
+
'Unknown propagation action received. Unable to propagate.',
|
257
273
|
node_id: propagation_node.id,
|
258
274
|
action: propagation_node.action)
|
259
|
-
return
|
275
|
+
return
|
260
276
|
end
|
261
|
-
|
262
277
|
restore_frozen_state = false
|
263
|
-
if target.cs__frozen?
|
264
|
-
return
|
278
|
+
if target.cs__frozen? && !Contrast::Agent::Assess::Tracker.trackable?(target)
|
279
|
+
return unless ASSESS.track_frozen_sources?
|
280
|
+
return unless propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
|
281
|
+
|
282
|
+
dup = safe_dup(ret)
|
283
|
+
return unless dup
|
265
284
|
|
266
285
|
restore_frozen_state = true
|
267
|
-
ret =
|
286
|
+
ret = dup
|
268
287
|
target = ret
|
288
|
+
Contrast::Agent::Assess::Tracker.pre_freeze(ret)
|
289
|
+
ret.cs__freeze
|
290
|
+
# double check that we were able to finalize the replaced return
|
291
|
+
return unless Contrast::Agent::Assess::Tracker.trackable?(target)
|
269
292
|
end
|
270
|
-
|
271
293
|
propagation_class.propagate(propagation_node, preshift, target)
|
272
|
-
|
273
294
|
# Once we've propagated, attempt to tag the target if there is a tag(s) to be applied
|
274
295
|
apply_tags(propagation_node, target)
|
275
|
-
|
276
296
|
# Even though we skipped propagating tags from the source if they
|
277
297
|
# were included in untags, the target may have already had some on
|
278
298
|
# it. Let's go ahead and remove them.
|
@@ -280,16 +300,13 @@ module Contrast
|
|
280
300
|
# both and there should never be a propagator that has a tag in
|
281
301
|
# its untag.
|
282
302
|
apply_untags(propagation_node, target)
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
target.cs__properties.build_event(propagation_node, target, object, ret, args)
|
287
|
-
|
303
|
+
properties = Contrast::Agent::Assess::Tracker.properties(target)
|
304
|
+
properties.add_properties(propagation_node.properties)
|
305
|
+
properties.build_event(propagation_node, target, object, ret, args)
|
288
306
|
logger.trace('Propagation detected',
|
289
307
|
node_id: propagation_node.id,
|
290
308
|
target_id: target.__id__)
|
291
|
-
ret
|
292
|
-
ret
|
309
|
+
restore_frozen_state ? ret : nil
|
293
310
|
end
|
294
311
|
end
|
295
312
|
end
|