contrast-agent 4.2.0 → 4.3.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/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/assess/contrast_event.rb +49 -130
- data/lib/contrast/agent/assess/contrast_object.rb +51 -0
- data/lib/contrast/agent/assess/events/source_event.rb +4 -9
- data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
- data/lib/contrast/agent/assess/policy/policy_node.rb +31 -59
- 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 +1 -2
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
- 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 +3 -4
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -4
- data/lib/contrast/agent/assess/policy/propagator/split.rb +73 -117
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +11 -11
- 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 +5 -8
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
- data/lib/contrast/agent/assess/property/tagged.rb +21 -15
- data/lib/contrast/agent/assess/rule/redos.rb +1 -1
- data/lib/contrast/agent/assess/tracker.rb +16 -18
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
- data/lib/contrast/agent/middleware.rb +50 -1
- data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
- data/lib/contrast/agent/patching/policy/patch.rb +4 -4
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -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 +3 -3
- 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/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/response.rb +5 -5
- data/lib/contrast/agent/rewriter.rb +3 -3
- data/lib/contrast/agent/scope.rb +33 -13
- data/lib/contrast/agent/static_analysis.rb +13 -7
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/decorators/library.rb +1 -0
- data/lib/contrast/api/decorators/library_usage_update.rb +1 -0
- data/lib/contrast/api/decorators/trace_event.rb +19 -31
- 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 +1 -1
- data/lib/contrast/components/assess.rb +36 -0
- data/lib/contrast/components/interface.rb +5 -3
- data/lib/contrast/components/scope.rb +23 -0
- data/lib/contrast/components/settings.rb +3 -3
- data/lib/contrast/config/assess_configuration.rb +2 -1
- data/lib/contrast/extension/assess/array.rb +1 -2
- data/lib/contrast/extension/assess/erb.rb +1 -3
- data/lib/contrast/extension/assess/exec_trigger.rb +1 -1
- data/lib/contrast/extension/assess/fiber.rb +2 -3
- data/lib/contrast/extension/assess/hash.rb +4 -2
- data/lib/contrast/extension/assess/kernel.rb +1 -2
- data/lib/contrast/extension/assess/marshal.rb +34 -26
- data/lib/contrast/extension/assess/regexp.rb +3 -8
- data/lib/contrast/extension/assess/string.rb +1 -2
- data/lib/contrast/framework/base_support.rb +51 -53
- data/lib/contrast/framework/manager.rb +3 -2
- data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
- data/lib/contrast/framework/rack/support.rb +2 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
- data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +1 -1
- data/lib/contrast/framework/rails/support.rb +2 -1
- data/lib/contrast/framework/sinatra/support.rb +3 -2
- data/lib/contrast/logger/application.rb +0 -3
- data/lib/contrast/utils/duck_utils.rb +1 -1
- data/lib/contrast/utils/heap_dump_util.rb +1 -1
- 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 -2
- data/lib/contrast/utils/string_utils.rb +1 -1
- data/lib/contrast/utils/tag_util.rb +9 -13
- data/resources/assess/policy.json +9 -9
- data/resources/deadzone/policy.json +156 -0
- data/resources/protect/policy.json +12 -0
- data/ruby-agent.gemspec +9 -6
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +68 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 772706612c29bc99cb9861c736056218e830881daba5ea2f9a4a2e86ecaf83ab
|
|
4
|
+
data.tar.gz: 85d07cd9104e2b6f0c1e04e5ee4d1562ad8de62a7155dd761cdcaa554f4e68fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e80a24fd38c3f88377abc59e146c755ec6e0272a64fd3af6d840888e33672513fc62539d9d9eae6de49d5c46bcce34ce656a8f8afe3fa71c0e80a3215b58753
|
|
7
|
+
data.tar.gz: 8bf6282052071f42dba4e40b0cd3abf9f15531a4e0fdff208d047d5a679c5268a903be5d93ec178d2ace254b5784548753c795088fdd6edac56bcd38ac1d2997
|
data/Rakefile
CHANGED
|
@@ -5,28 +5,39 @@
|
|
|
5
5
|
#include "../cs__common/cs__common.h"
|
|
6
6
|
#include <ruby.h>
|
|
7
7
|
|
|
8
|
-
static VALUE
|
|
8
|
+
static VALUE contrast_marshal_module_load(const int argc,
|
|
9
9
|
const VALUE *argv) {
|
|
10
10
|
VALUE result;
|
|
11
11
|
VALUE source_string;
|
|
12
|
-
result = rb_call_super(argc, argv);
|
|
13
12
|
|
|
13
|
+
// Our patches only need only apply in the case where there was valid input.
|
|
14
14
|
if (argc >= 1) {
|
|
15
15
|
source_string = argv[0];
|
|
16
|
+
} else {
|
|
17
|
+
source_string = Qnil;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Run our protect code ahead of the original method
|
|
21
|
+
if (source_string != Qnil) {
|
|
22
|
+
rb_funcall(marshal_propagator, rb_sym_protect_marshal_load, 1, source_string);
|
|
23
|
+
}
|
|
16
24
|
|
|
25
|
+
// Invoke the original method
|
|
26
|
+
result = rb_call_super(argc, argv);
|
|
27
|
+
|
|
28
|
+
// Run our assess code after the original method
|
|
29
|
+
if (source_string != Qnil) {
|
|
17
30
|
VALUE tracked =
|
|
18
31
|
rb_funcall(properties_hash, rb_sym_hash_tracked, 1, source_string);
|
|
19
32
|
|
|
33
|
+
// Assuming the source is tracked and needs assess checks
|
|
20
34
|
if (tracked == Qtrue) {
|
|
21
35
|
VALUE skip =
|
|
22
36
|
rb_funcall(contrast_patcher(), rb_sym_skip_assess_analysis, 0);
|
|
23
|
-
|
|
37
|
+
// And Assess is enabled and applies to this request
|
|
24
38
|
if (skip == Qfalse) {
|
|
25
|
-
|
|
26
|
-
rb_funcall(contrast_patcher(), rb_sym_enter_scope, 0);
|
|
27
|
-
rb_funcall(marshal_module, rb_sym_assess_load_trigger_check, 2,
|
|
39
|
+
rb_funcall(marshal_propagator, rb_sym_assess_marshal_load, 2,
|
|
28
40
|
source_string, result);
|
|
29
|
-
rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
|
|
30
41
|
}
|
|
31
42
|
}
|
|
32
43
|
}
|
|
@@ -37,10 +48,11 @@ void Init_cs__assess_marshal_module(void) {
|
|
|
37
48
|
// Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
|
|
38
49
|
VALUE tracker = rb_define_class_under(assess, "Tracker", rb_cObject);
|
|
39
50
|
properties_hash = rb_const_get(tracker, rb_intern("PROPERTIES_HASH"));
|
|
40
|
-
|
|
51
|
+
marshal_propagator =
|
|
41
52
|
rb_define_class_under(core_assess, "MarshalPropagator", rb_cObject);
|
|
42
|
-
|
|
53
|
+
rb_sym_assess_marshal_load = rb_intern("cs__load_assess");
|
|
54
|
+
rb_sym_protect_marshal_load = rb_intern("cs__load_protect");
|
|
43
55
|
|
|
44
56
|
contrast_register_singleton_prepend_patch(
|
|
45
|
-
"Marshal", "load", &
|
|
57
|
+
"Marshal", "load", &contrast_marshal_module_load);
|
|
46
58
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#include <ruby.h>
|
|
2
2
|
|
|
3
|
-
static VALUE
|
|
3
|
+
static VALUE marshal_propagator;
|
|
4
4
|
|
|
5
|
-
static VALUE
|
|
5
|
+
static VALUE rb_sym_assess_marshal_load;
|
|
6
|
+
static VALUE rb_sym_protect_marshal_load;
|
|
6
7
|
static VALUE properties_hash;
|
|
7
8
|
|
|
8
9
|
/*
|
|
@@ -13,7 +14,7 @@ static VALUE properties_hash;
|
|
|
13
14
|
* special case this for now.
|
|
14
15
|
* -HM (shamelessly commenting on DP's work)
|
|
15
16
|
*/
|
|
16
|
-
static VALUE
|
|
17
|
+
static VALUE contrast_marshal_module_load(const int argc,
|
|
17
18
|
const VALUE *argv);
|
|
18
19
|
|
|
19
20
|
void Init_cs__assess_marshal_module(void);
|
|
@@ -9,6 +9,8 @@ require 'contrast/utils/prevent_serialization'
|
|
|
9
9
|
require 'contrast/utils/stack_trace_utils'
|
|
10
10
|
require 'contrast/utils/string_utils'
|
|
11
11
|
require 'contrast/utils/timer'
|
|
12
|
+
require 'contrast/components/interface'
|
|
13
|
+
require 'contrast/agent/assess/contrast_object'
|
|
12
14
|
|
|
13
15
|
module Contrast
|
|
14
16
|
module Agent
|
|
@@ -26,63 +28,22 @@ module Contrast
|
|
|
26
28
|
# created
|
|
27
29
|
# @attr_reader thread [Integer] the object id of the thread on which this
|
|
28
30
|
# event was generated
|
|
29
|
-
# @attr_reader object [
|
|
30
|
-
# which the method was invoked
|
|
31
|
-
# @attr_reader ret [
|
|
32
|
-
# invoked method
|
|
33
|
-
# @attr_reader args [Array<
|
|
34
|
-
# Arguments with which the method was invoked
|
|
31
|
+
# @attr_reader object [Contrast::Agent::Assess::ContrastObject] the safe
|
|
32
|
+
# representation of the Object on which the method was invoked
|
|
33
|
+
# @attr_reader ret [Contrast::Agent::Assess::ContrastObject] the safe
|
|
34
|
+
# representation of the Return of the invoked method
|
|
35
|
+
# @attr_reader args [Array<Contrast::Agent::Assess::ContrastObject>] the
|
|
36
|
+
# safe representation of the Arguments with which the method was invoked
|
|
35
37
|
class ContrastEvent
|
|
36
38
|
include Contrast::Utils::PreventSerialization
|
|
39
|
+
include Contrast::Components::Interface
|
|
40
|
+
access_component :analysis
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# @return [Array<String>] the String forms of those Objects, as
|
|
44
|
-
# determined by Contrast::Utils::ClassUtil.to_contrast_string
|
|
45
|
-
def safe_args_representation args
|
|
46
|
-
return nil unless args
|
|
47
|
-
return Contrast::Utils::ObjectShare::EMPTY_ARRAY if args.empty?
|
|
48
|
-
|
|
49
|
-
rep = []
|
|
50
|
-
args.each do |arg|
|
|
51
|
-
# We have to handle named args
|
|
52
|
-
rep << if arg.is_a?(Hash)
|
|
53
|
-
safe_arg_hash_representation(arg)
|
|
54
|
-
else
|
|
55
|
-
Contrast::Utils::ClassUtil.to_contrast_string(arg)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
rep
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# if given an object that can be duped, duplicate it. otherwise just
|
|
62
|
-
# return the original object. swallow all exceptions from
|
|
63
|
-
# non-duplicable things.
|
|
64
|
-
#
|
|
65
|
-
# we can't just check respond_to? though b/c dup exists on the
|
|
66
|
-
# base Object class
|
|
67
|
-
#
|
|
68
|
-
# @param original [Object, nil] the thing to duplicate
|
|
69
|
-
# @return [Object, nil] a copy of that thing
|
|
70
|
-
def safe_dup original
|
|
71
|
-
return nil unless original
|
|
72
|
-
|
|
73
|
-
Contrast::Agent::Assess::Tracker.duplicate(original)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
private
|
|
77
|
-
|
|
78
|
-
def safe_arg_hash_representation hash
|
|
79
|
-
# since this is the named hash for arguments, only the value is
|
|
80
|
-
# suspect here
|
|
81
|
-
hash.transform_values { |v| Contrast::Utils::ClassUtil.to_contrast_string(v) }
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
attr_reader :event_id, :policy_node, :stack_trace, :time, :thread, :object, :ret, :args
|
|
42
|
+
attr_reader :event_id, :policy_node, :stack_trace, :time, :thread,
|
|
43
|
+
:object,
|
|
44
|
+
:ret,
|
|
45
|
+
:args,
|
|
46
|
+
:tags
|
|
86
47
|
|
|
87
48
|
# We need this to track the parent id's of events to build up a flow
|
|
88
49
|
# chart of the finding
|
|
@@ -106,16 +67,25 @@ module Contrast
|
|
|
106
67
|
# was invoked
|
|
107
68
|
def initialize policy_node, tagged, object, ret, args
|
|
108
69
|
@policy_node = policy_node
|
|
109
|
-
|
|
70
|
+
|
|
71
|
+
# Capture stacktraces only as configured.
|
|
72
|
+
#
|
|
73
|
+
# So long as this event is built in a factory, we know Contrast Code
|
|
110
74
|
# will be the first three events
|
|
111
|
-
@stack_trace =
|
|
75
|
+
@stack_trace = if ASSESS.capture_stacktrace?(policy_node)
|
|
76
|
+
caller(3, 20)
|
|
77
|
+
else
|
|
78
|
+
Contrast::Utils::ObjectShare::EMPTY_ARRAY
|
|
79
|
+
end
|
|
80
|
+
|
|
112
81
|
@time = Contrast::Utils::Timer.now_ms
|
|
113
82
|
@thread = Thread.current.object_id
|
|
114
83
|
|
|
115
84
|
# These methods rely on the above being set. Don't move them!
|
|
116
85
|
@event_id = Contrast::Agent::Assess::ContrastEvent.next_atomic_id
|
|
86
|
+
@tags = Contrast::Agent::Assess::Tracker.properties(tagged)&.tags
|
|
117
87
|
find_parent_events!(policy_node, object, ret, args)
|
|
118
|
-
snapshot!(
|
|
88
|
+
snapshot!(object, ret, args)
|
|
119
89
|
end
|
|
120
90
|
|
|
121
91
|
def parent_events
|
|
@@ -128,22 +98,17 @@ module Contrast
|
|
|
128
98
|
# Per TS law, each policy_node must have at least a source or a target.
|
|
129
99
|
# The only type of policy_node w/o targets is a Trigger, but that may
|
|
130
100
|
# change.
|
|
131
|
-
# 2)
|
|
132
|
-
#
|
|
133
|
-
# to find the index). I need to address this so that TS can process
|
|
134
|
-
# it.
|
|
135
|
-
# 3) I'll set the event's source and target to TS values.
|
|
136
|
-
# 4) Return the highlight or the first source/target as the taint
|
|
137
|
-
# target.
|
|
101
|
+
# 2) I'll set the event's source and target to TS values.
|
|
102
|
+
# 3) Return the first source/target as the taint target.
|
|
138
103
|
def determine_taint_target event_dtm
|
|
139
104
|
if @policy_node&.targets&.any?
|
|
140
105
|
event_dtm.source = @policy_node.source_string if @policy_node.source_string
|
|
141
|
-
event_dtm.target = @
|
|
142
|
-
@
|
|
106
|
+
event_dtm.target = @policy_node.target_string
|
|
107
|
+
@policy_node.targets[0]
|
|
143
108
|
elsif policy_node&.sources&.any?
|
|
144
|
-
event_dtm.source = @
|
|
109
|
+
event_dtm.source = @policy_node.source_string
|
|
145
110
|
event_dtm.target = @policy_node.target_string if @policy_node.target_string
|
|
146
|
-
@
|
|
111
|
+
@policy_node.sources[0]
|
|
147
112
|
end
|
|
148
113
|
end
|
|
149
114
|
|
|
@@ -193,16 +158,7 @@ module Contrast
|
|
|
193
158
|
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
194
159
|
ret
|
|
195
160
|
else
|
|
196
|
-
|
|
197
|
-
args[source]
|
|
198
|
-
else
|
|
199
|
-
args.each do |search|
|
|
200
|
-
next unless search.is_a?(Hash)
|
|
201
|
-
|
|
202
|
-
s = search[source]
|
|
203
|
-
return s if s
|
|
204
|
-
end
|
|
205
|
-
end
|
|
161
|
+
args[source]
|
|
206
162
|
end
|
|
207
163
|
end
|
|
208
164
|
|
|
@@ -212,65 +168,28 @@ module Contrast
|
|
|
212
168
|
# them for our later use. We set those safe values to this event's
|
|
213
169
|
# instance variables.
|
|
214
170
|
#
|
|
215
|
-
# @param tagged [Object] the Target to which this event pertains.
|
|
216
171
|
# @param object [Object] the Object on which the method was invoked
|
|
217
172
|
# @param ret [Object] the Return of the invoked method
|
|
218
173
|
# @param args [Array<Object>] the Arguments with which the method
|
|
219
174
|
# was invoked
|
|
220
|
-
def snapshot!
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
# marked up, as nothing need be tracked
|
|
226
|
-
when nil
|
|
227
|
-
@object = Contrast::Utils::ClassUtil.to_contrast_string(object)
|
|
228
|
-
@args = cs__class.safe_args_representation(args)
|
|
229
|
-
@ret = Contrast::Utils::ClassUtil.to_contrast_string(ret)
|
|
230
|
-
# If the target is O, then we dup the O and safely represent the rest
|
|
231
|
-
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
232
|
-
@object = cs__class.safe_dup(tagged)
|
|
233
|
-
@args = cs__class.safe_args_representation(args)
|
|
234
|
-
@ret = Contrast::Utils::ClassUtil.to_contrast_string(ret)
|
|
235
|
-
# If the target is R, then we dup the R and safely represent the rest
|
|
236
|
-
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
237
|
-
@object = Contrast::Utils::ClassUtil.to_contrast_string(object)
|
|
238
|
-
@args = cs__class.safe_args_representation(args)
|
|
239
|
-
@ret = cs__class.safe_dup(tagged)
|
|
240
|
-
# If the target is P*, then we need to dup things a differently. We
|
|
241
|
-
# need to find the true target inside so that we can mark it up
|
|
242
|
-
# later, but the other args should be represented as their safe form.
|
|
243
|
-
else
|
|
244
|
-
@object = Contrast::Utils::ClassUtil.to_contrast_string(object)
|
|
245
|
-
@args = cs__class.safe_args_representation(args)
|
|
246
|
-
@ret = Contrast::Utils::ClassUtil.to_contrast_string(ret)
|
|
247
|
-
save_target_arg(target, tagged)
|
|
248
|
-
end
|
|
175
|
+
def snapshot! object, ret, args
|
|
176
|
+
@object = Contrast::Agent::Assess::ContrastObject.new(object) if object
|
|
177
|
+
@ret = Contrast::Agent::Assess::ContrastObject.new(ret) if ret
|
|
178
|
+
@args = safe_args_representation(args)
|
|
179
|
+
self
|
|
249
180
|
end
|
|
250
181
|
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
# complicated. - HM 8/8/19
|
|
182
|
+
# Given an array of arguments, copy them into a safe, meaning String,
|
|
183
|
+
# format that we can use to send to SR and TS for rendering.
|
|
254
184
|
#
|
|
255
|
-
# @param
|
|
256
|
-
# @
|
|
257
|
-
#
|
|
258
|
-
def
|
|
259
|
-
return
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
@args.each_with_index do |search, index|
|
|
267
|
-
next unless search.is_a?(Hash)
|
|
268
|
-
next unless search[target]
|
|
269
|
-
|
|
270
|
-
search[target] = cs__class.safe_dup(tagged)
|
|
271
|
-
@highlight = index
|
|
272
|
-
break
|
|
273
|
-
end
|
|
185
|
+
# @param args [Array<Object>] the arguments to translate
|
|
186
|
+
# @return [Array<Contrast::Agent::Assess::ContrastObject>] the String forms of those Objects, as
|
|
187
|
+
# determined by Contrast::Utils::ClassUtil.to_contrast_string
|
|
188
|
+
def safe_args_representation args
|
|
189
|
+
return unless args
|
|
190
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY if args.empty?
|
|
191
|
+
|
|
192
|
+
args.map { |arg| arg ? Contrast::Agent::Assess::ContrastObject.new(arg) : nil }
|
|
274
193
|
end
|
|
275
194
|
end
|
|
276
195
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
require 'contrast/utils/class_util'
|
|
5
|
+
|
|
6
|
+
module Contrast
|
|
7
|
+
module Agent
|
|
8
|
+
module Assess
|
|
9
|
+
# This class is a convenient holder of our version of an Object. It
|
|
10
|
+
# creates a String version of the Object from the original provided
|
|
11
|
+
# and keeps reference to the original's Tags, letting us determine if it
|
|
12
|
+
# was tracked when we try to report to TeamServer.
|
|
13
|
+
#
|
|
14
|
+
# @attr_reader object [String, nil] the Contrast string representing the
|
|
15
|
+
# object.
|
|
16
|
+
# @attr_reader object_type [String] the name of the object's module.
|
|
17
|
+
# @attr_reader tags [Hash{String => Contrast::Agent::Assess::Tag}, nil]
|
|
18
|
+
# the tags on the object before it was captured.
|
|
19
|
+
#
|
|
20
|
+
# TODO: RUBY-1083 determine if this is expensive and/or worth not storing
|
|
21
|
+
# these values directly on ContrastEvent and passing them around. Args
|
|
22
|
+
# probably make the argument for wrapping them b/c otherwise we'll have
|
|
23
|
+
# to keep two arrays in synch or make an array of arrays, at which
|
|
24
|
+
# point, we may as well make this.
|
|
25
|
+
class ContrastObject
|
|
26
|
+
attr_reader :object, :object_type, :tags
|
|
27
|
+
|
|
28
|
+
# Capture the details about the object which we need to render it in
|
|
29
|
+
# TeamServer.
|
|
30
|
+
#
|
|
31
|
+
# @param object [Object] the thing to keep a Contrast String of
|
|
32
|
+
def initialize object
|
|
33
|
+
if object
|
|
34
|
+
@object = Contrast::Utils::ClassUtil.to_contrast_string(object)
|
|
35
|
+
@object_type = object.cs__class.name
|
|
36
|
+
# TODO: RUBY-1084 determine if we need to copy these tags to
|
|
37
|
+
# restore immutability. For instance, if these tags were on a
|
|
38
|
+
# String that was then #reverse!'d, would our tags be wrong?
|
|
39
|
+
@tags = Contrast::Agent::Assess::Tracker.properties(object)&.tags
|
|
40
|
+
else
|
|
41
|
+
@object_type = Contrast::Utils::ObjectShare::NIL_STRING
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def tracked?
|
|
46
|
+
tags&.any?
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -58,19 +58,14 @@ module Contrast
|
|
|
58
58
|
|
|
59
59
|
# We have to do a little work to figure out what our TS appropriate
|
|
60
60
|
# target is. To break this down, the logic is as follows:
|
|
61
|
-
# 1)
|
|
62
|
-
#
|
|
63
|
-
# to find the index). I need to address this so that TS can process
|
|
64
|
-
# it.
|
|
65
|
-
# 2) I'll set the event's source and target to TS values.
|
|
66
|
-
# 3) Return the highlight or the first source/target as the taint
|
|
67
|
-
# target.
|
|
61
|
+
# 1) I'll set the event's source and target to TS values.
|
|
62
|
+
# 2) Return the first source/target as the taint target.
|
|
68
63
|
def determine_taint_target event_dtm
|
|
69
64
|
return unless @policy_node&.targets&.any?
|
|
70
65
|
|
|
71
66
|
event_dtm.source = @policy_node.source_string if @policy_node.source_string
|
|
72
|
-
event_dtm.target = @
|
|
73
|
-
@
|
|
67
|
+
event_dtm.target = @policy_node.target_string
|
|
68
|
+
@policy_node.targets[0]
|
|
74
69
|
end
|
|
75
70
|
end
|
|
76
71
|
end
|
|
@@ -52,12 +52,13 @@ module Contrast
|
|
|
52
52
|
Contrast::Utils::ObjectShare::CLASS,
|
|
53
53
|
Contrast::Utils::ObjectShare::MODULE
|
|
54
54
|
].cs__freeze
|
|
55
|
-
def patch_assess_method
|
|
55
|
+
def patch_assess_method mod, method_name
|
|
56
56
|
# Module.define_method is called a lot in Class and Module. We
|
|
57
57
|
# currently do not expect these define_methods to result in methods
|
|
58
58
|
# that require patching, so for the sake of performance, we're going
|
|
59
59
|
# to skip evaluating them
|
|
60
|
-
|
|
60
|
+
mod = mod.cs__class unless mod.cs__is_a?(Module)
|
|
61
|
+
class_name = mod.cs__class
|
|
61
62
|
return if CLASS_TYPES.include?(class_name)
|
|
62
63
|
return unless ASSESS.enabled?
|
|
63
64
|
|
|
@@ -73,7 +74,7 @@ module Contrast
|
|
|
73
74
|
method_name: source_node.method_name,
|
|
74
75
|
method_visibility: source_node.method_visibility,
|
|
75
76
|
instance_method: true)
|
|
76
|
-
patcher.patch_method(
|
|
77
|
+
patcher.patch_method(mod, method_array, method_policy)
|
|
77
78
|
end
|
|
78
79
|
rescue StandardError => e
|
|
79
80
|
logger.warn(
|
|
@@ -19,8 +19,8 @@ module Contrast
|
|
|
19
19
|
@source_string = policy_hash[JSON_SOURCE]
|
|
20
20
|
@target_string = policy_hash[JSON_TARGET]
|
|
21
21
|
@tags = Set.new(policy_hash[JSON_TAGS])
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
@sources = convert_policy_markers(source_string)
|
|
23
|
+
@targets = convert_policy_markers(target_string)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def feature
|
|
@@ -47,7 +47,7 @@ module Contrast
|
|
|
47
47
|
|
|
48
48
|
def target_string= value
|
|
49
49
|
@target_string = value
|
|
50
|
-
|
|
50
|
+
@targets = convert_policy_markers(value)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Sometimes we need to tie information to an event. We'll add a
|
|
@@ -66,62 +66,6 @@ module Contrast
|
|
|
66
66
|
@properties[name]
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
# Given a source in the format A,B,C, populate the sources of this node
|
|
70
|
-
# 1) Split on ','
|
|
71
|
-
# 2) If 'O', add the source, else it's P (we don't have R sources) and
|
|
72
|
-
# needs to be converted. P type will either be P:name or P# where #
|
|
73
|
-
# is the index of the parameter. Drop the P and store the int as int
|
|
74
|
-
# or name as symbol
|
|
75
|
-
def generate_sources
|
|
76
|
-
if source_string
|
|
77
|
-
@sources = []
|
|
78
|
-
source_string.split(Contrast::Utils::ObjectShare::COMMA).each do |s|
|
|
79
|
-
is_object = (s == Contrast::Utils::ObjectShare::OBJECT_KEY)
|
|
80
|
-
if is_object
|
|
81
|
-
@sources << s
|
|
82
|
-
else
|
|
83
|
-
parameter_source = s[1..-1]
|
|
84
|
-
@sources << if parameter_source.start_with?(Contrast::Utils::ObjectShare::COLON)
|
|
85
|
-
parameter_source[1..-1].to_sym
|
|
86
|
-
else
|
|
87
|
-
parameter_source.to_i
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
else
|
|
92
|
-
@sources = Contrast::Utils::ObjectShare::EMPTY_ARRAY
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Given a target in the format A,B,C, populate the targets of this node
|
|
97
|
-
# 1) Split on ','
|
|
98
|
-
# 2) If 'O' or 'R', add the target, else it's P and needs to be
|
|
99
|
-
# converted. P type will either be P:name or P# where # is the index
|
|
100
|
-
# of the paramter. Drop the P and store the int as int or name as
|
|
101
|
-
# symbol
|
|
102
|
-
def generate_targets
|
|
103
|
-
if target_string
|
|
104
|
-
@targets = []
|
|
105
|
-
target_string.split(Contrast::Utils::ObjectShare::COMMA).each do |t|
|
|
106
|
-
case t
|
|
107
|
-
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
108
|
-
@targets << t
|
|
109
|
-
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
110
|
-
@targets << t
|
|
111
|
-
else
|
|
112
|
-
parameter_target = t[1..-1]
|
|
113
|
-
@targets << if parameter_target.start_with?(Contrast::Utils::ObjectShare::COLON)
|
|
114
|
-
parameter_target[1..-1].to_sym
|
|
115
|
-
else
|
|
116
|
-
parameter_target.to_i
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
else
|
|
121
|
-
@targets = Contrast::Utils::ObjectShare::EMPTY_ARRAY
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
69
|
# Don't let nodes be created that will be missing things we need
|
|
126
70
|
# later on. Really, if they don't have these things, they couldn't have
|
|
127
71
|
# done their jobs anyway.
|
|
@@ -186,6 +130,34 @@ module Contrast
|
|
|
186
130
|
JSON_TARGET = 'target'
|
|
187
131
|
JSON_TAGS = 'tags'
|
|
188
132
|
JSON_DATAFLOW = 'dataflow'
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
# Given a policy string in the format A,B,C, populate the given array
|
|
137
|
+
# 1) Split on ','
|
|
138
|
+
# 2) If 'O' or 'R', add the array, else it's P and needs to be
|
|
139
|
+
# converted. P type will either be P# where # is the index
|
|
140
|
+
# of the parameter. Drop the P and store the # as an int.
|
|
141
|
+
#
|
|
142
|
+
# @param markers [String] the String from the policy to parse
|
|
143
|
+
# @return [Array] the array generated by converting the marker string
|
|
144
|
+
def convert_policy_markers markers
|
|
145
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless markers
|
|
146
|
+
return Contrast::Utils::ObjectShare::EMPTY_ARRAY if markers.empty?
|
|
147
|
+
|
|
148
|
+
converted = []
|
|
149
|
+
markers.split(Contrast::Utils::ObjectShare::COMMA).each do |t|
|
|
150
|
+
case t
|
|
151
|
+
when Contrast::Utils::ObjectShare::OBJECT_KEY,
|
|
152
|
+
Contrast::Utils::ObjectShare::RETURN_KEY
|
|
153
|
+
|
|
154
|
+
converted << t
|
|
155
|
+
else
|
|
156
|
+
converted << Integer(t[1..-1])
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
converted
|
|
160
|
+
end
|
|
189
161
|
end
|
|
190
162
|
end
|
|
191
163
|
end
|