contrast-agent 4.9.0 → 4.12.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/.rspec +0 -1
- data/.rspec_parallel +6 -0
- data/ext/cs__common/cs__common.c +19 -7
- data/ext/cs__common/cs__common.h +4 -2
- data/ext/cs__contrast_patch/cs__contrast_patch.c +32 -11
- data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -4
- data/lib/contrast/agent/assess/contrast_event.rb +1 -2
- data/lib/contrast/agent/assess/contrast_object.rb +1 -4
- data/lib/contrast/agent/assess/finalizers/hash.rb +0 -1
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
- data/lib/contrast/agent/assess/policy/patcher.rb +0 -1
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +0 -2
- data/lib/contrast/agent/assess/policy/preshift.rb +29 -12
- data/lib/contrast/agent/assess/policy/propagation_method.rb +100 -57
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
- data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -2
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -0
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +0 -1
- data/lib/contrast/agent/assess/policy/source_method.rb +13 -17
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +60 -85
- data/lib/contrast/agent/assess/policy/trigger_node.rb +52 -19
- data/lib/contrast/agent/assess/property/evented.rb +2 -1
- data/lib/contrast/agent/assess/property/tagged.rb +34 -25
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +0 -1
- data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
- data/lib/contrast/agent/disable_reaction.rb +1 -1
- data/lib/contrast/agent/exclusion_matcher.rb +0 -4
- data/lib/contrast/agent/inventory/database_config.rb +117 -0
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +6 -5
- data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -0
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +17 -12
- data/lib/contrast/agent/patching/policy/method_policy.rb +54 -9
- data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/patch.rb +17 -6
- data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +9 -9
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
- data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
- data/lib/contrast/agent/reaction_processor.rb +1 -1
- data/lib/contrast/agent/request.rb +9 -4
- data/lib/contrast/agent/request_context.rb +51 -33
- data/lib/contrast/agent/rule_set.rb +2 -4
- data/lib/contrast/agent/scope.rb +32 -20
- data/lib/contrast/agent/static_analysis.rb +1 -1
- data/lib/contrast/agent/tracepoint_hook.rb +16 -3
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +0 -1
- data/lib/contrast/api/communication/messaging_queue.rb +12 -6
- data/lib/contrast/api/communication/service_lifecycle.rb +4 -1
- data/lib/contrast/api/communication/socket_client.rb +4 -4
- data/lib/contrast/api/decorators/agent_startup.rb +4 -4
- data/lib/contrast/api/decorators/application_startup.rb +6 -5
- data/lib/contrast/api/decorators/route_coverage.rb +24 -1
- data/lib/contrast/components/agent.rb +5 -2
- data/lib/contrast/components/assess.rb +13 -3
- data/lib/contrast/components/base.rb +2 -2
- data/lib/contrast/components/config.rb +1 -0
- data/lib/contrast/components/contrast_service.rb +4 -2
- data/lib/contrast/components/logger.rb +13 -8
- data/lib/contrast/components/scope.rb +9 -28
- data/lib/contrast/config/assess_configuration.rb +1 -0
- data/lib/contrast/config/base_configuration.rb +14 -6
- data/lib/contrast/configuration.rb +19 -15
- data/lib/contrast/extension/assess/array.rb +1 -11
- data/lib/contrast/extension/assess/eval_trigger.rb +0 -20
- data/lib/contrast/extension/assess/fiber.rb +0 -11
- data/lib/contrast/extension/assess/hash.rb +0 -10
- data/lib/contrast/extension/assess/kernel.rb +1 -10
- data/lib/contrast/extension/assess/marshal.rb +3 -11
- data/lib/contrast/extension/assess/regexp.rb +0 -11
- data/lib/contrast/extension/assess/string.rb +1 -26
- data/lib/contrast/extension/extension.rb +61 -0
- data/lib/contrast/framework/grape/support.rb +174 -0
- data/lib/contrast/framework/manager.rb +42 -6
- data/lib/contrast/framework/rack/support.rb +1 -1
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +0 -1
- data/lib/contrast/framework/rails/patch/support.rb +6 -3
- data/lib/contrast/framework/rails/railtie.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +1 -0
- data/lib/contrast/framework/rails/support.rb +60 -13
- data/lib/contrast/framework/sinatra/support.rb +1 -1
- data/lib/contrast/logger/log.rb +89 -15
- data/lib/contrast/tasks/config.rb +0 -1
- data/lib/contrast/utils/class_util.rb +58 -44
- data/lib/contrast/utils/io_util.rb +43 -35
- data/lib/contrast/utils/lru_cache.rb +45 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
- data/lib/contrast/utils/tag_util.rb +2 -1
- data/lib/contrast.rb +1 -1
- data/resources/assess/policy.json +208 -7
- data/resources/deadzone/policy.json +91 -0
- data/ruby-agent.gemspec +10 -2
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +74 -26
- data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
- data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
- data/ext/cs__protect_kernel/extconf.rb +0 -5
- data/lib/contrast/extension/protect/kernel.rb +0 -39
- data/lib/contrast/utils/inventory_util.rb +0 -113
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a0a50913c5c680462f9afd79070ca1f3d46d09d66ffdb943b1dc778f84830d6
|
|
4
|
+
data.tar.gz: bb3254c6a2cdc2b4add4e09b4bf1ed63c6a640f0dd82824db2769b911aacf96b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 78d5e66ee3ab7408e94348f6c2998e73cd78227a8b75cffa7add261b771092d470431ce8e44357dfb7d1a178e03e5033efc9f7a4c974a979f9dea88b2c55a3ec
|
|
7
|
+
data.tar.gz: d20f41b563778d84e63e4db7a9399e666a85fe1a2343772258f5248cd139f02e6a6bea33e51d9e5a0f954b1e2b4683ec08f3db7a52ab5a6990f7d725d2bc3dea
|
data/.rspec
CHANGED
data/.rspec_parallel
ADDED
data/ext/cs__common/cs__common.c
CHANGED
|
@@ -73,11 +73,20 @@ VALUE contrast_register_singleton_patch(const char *module_name,
|
|
|
73
73
|
IMPL_ALIAS_SINGLETON);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
VALUE
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
VALUE contrast_register_prepend_patch(const char *module_name,
|
|
77
|
+
const char *method_name,
|
|
78
|
+
VALUE(c_fn)(const int, VALUE *,
|
|
79
|
+
const VALUE)) {
|
|
80
|
+
return _contrast_register_patch(module_name, method_name, c_fn,
|
|
81
|
+
IMPL_PREPEND_INSTANCE);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
VALUE contrast_register_singleton_prepend_patch(const char *module_name,
|
|
85
|
+
const char *method_name,
|
|
86
|
+
VALUE(c_fn)(const int, VALUE *,
|
|
87
|
+
const VALUE)) {
|
|
79
88
|
return _contrast_register_patch(module_name, method_name, c_fn,
|
|
80
|
-
|
|
89
|
+
IMPL_PREPEND_SINGLETON);
|
|
81
90
|
}
|
|
82
91
|
|
|
83
92
|
static VALUE
|
|
@@ -122,8 +131,10 @@ _contrast_register_patch(const char *module_name, const char *method_name,
|
|
|
122
131
|
case IMPL_ALIAS_SINGLETON:
|
|
123
132
|
impl = ID2SYM(rb_sym_alias_singleton);
|
|
124
133
|
break;
|
|
125
|
-
case
|
|
126
|
-
impl = ID2SYM(
|
|
134
|
+
case IMPL_PREPEND_INSTANCE:
|
|
135
|
+
impl = ID2SYM(rb_sym_prepend_instance);
|
|
136
|
+
case IMPL_PREPEND_SINGLETON:
|
|
137
|
+
impl = ID2SYM(rb_sym_prepend_singleton);
|
|
127
138
|
break;
|
|
128
139
|
}
|
|
129
140
|
|
|
@@ -152,7 +163,8 @@ void Init_cs__common(void) {
|
|
|
152
163
|
rb_sym_register_c_patch = rb_intern("register_c_patch");
|
|
153
164
|
rb_sym_alias_instance = rb_intern("alias_instance");
|
|
154
165
|
rb_sym_alias_singleton = rb_intern("alias_singleton");
|
|
155
|
-
|
|
166
|
+
rb_sym_prepend_instance = rb_intern("prepend_instance");
|
|
167
|
+
rb_sym_prepend_singleton = rb_intern("prepend_singleton");
|
|
156
168
|
|
|
157
169
|
/* Ensure definition of core Contrast instrumentation modules */
|
|
158
170
|
contrast = rb_define_module("Contrast");
|
data/ext/cs__common/cs__common.h
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
typedef enum {
|
|
7
7
|
IMPL_ALIAS_INSTANCE,
|
|
8
8
|
IMPL_ALIAS_SINGLETON,
|
|
9
|
-
|
|
9
|
+
IMPL_PREPEND_INSTANCE,
|
|
10
|
+
IMPL_PREPEND_SINGLETON,
|
|
10
11
|
} patch_impl;
|
|
11
12
|
|
|
12
13
|
static VALUE cs__send_method;
|
|
@@ -30,7 +31,8 @@ static VALUE rb_sym_instance_method;
|
|
|
30
31
|
static VALUE rb_sym_register_c_patch;
|
|
31
32
|
static VALUE rb_sym_alias_instance;
|
|
32
33
|
static VALUE rb_sym_alias_singleton;
|
|
33
|
-
static VALUE
|
|
34
|
+
static VALUE rb_sym_prepend_instance;
|
|
35
|
+
static VALUE rb_sym_prepend_singleton;
|
|
34
36
|
|
|
35
37
|
void patch_via_funchook(void *original_function, void *hook_function);
|
|
36
38
|
|
|
@@ -182,7 +182,8 @@ VALUE contrast_run_patches(const VALUE *wrapped_args) {
|
|
|
182
182
|
contrast_patch_call_rescue,
|
|
183
183
|
(VALUE)rescue_args, rb_eException, 0);
|
|
184
184
|
break;
|
|
185
|
-
case
|
|
185
|
+
case IMPL_PREPEND_INSTANCE:
|
|
186
|
+
case IMPL_PREPEND_SINGLETON:
|
|
186
187
|
original_ret = rb_rescue2(contrast_call_super, original_args,
|
|
187
188
|
contrast_patch_call_rescue,
|
|
188
189
|
(VALUE)rescue_args, rb_eException, 0);
|
|
@@ -247,10 +248,14 @@ VALUE contrast_patch_dispatch(const int argc, const VALUE *argv,
|
|
|
247
248
|
*/
|
|
248
249
|
switch (impl) {
|
|
249
250
|
case IMPL_ALIAS_INSTANCE:
|
|
250
|
-
case
|
|
251
|
+
case IMPL_PREPEND_INSTANCE:
|
|
251
252
|
known =
|
|
252
253
|
rb_funcall(patch_status, rb_sym_info_for, 3, object, method, Qtrue);
|
|
253
254
|
break;
|
|
255
|
+
case IMPL_PREPEND_SINGLETON:
|
|
256
|
+
known =
|
|
257
|
+
rb_funcall(patch_status, rb_sym_info_for, 3, object, method, Qfalse);
|
|
258
|
+
break;
|
|
254
259
|
case IMPL_ALIAS_SINGLETON:
|
|
255
260
|
known = rb_funcall(patch_status, rb_sym_info_for, 3, object, method,
|
|
256
261
|
Qfalse);
|
|
@@ -323,7 +328,8 @@ call_original:
|
|
|
323
328
|
case IMPL_ALIAS_INSTANCE:
|
|
324
329
|
case IMPL_ALIAS_SINGLETON:
|
|
325
330
|
return contrast_patch_call_original(original_args);
|
|
326
|
-
case
|
|
331
|
+
case IMPL_PREPEND_INSTANCE:
|
|
332
|
+
case IMPL_PREPEND_SINGLETON:
|
|
327
333
|
return contrast_call_super(original_args);
|
|
328
334
|
};
|
|
329
335
|
}
|
|
@@ -338,9 +344,14 @@ VALUE contrast_alias_singleton_patch(const int argc, const VALUE *argv,
|
|
|
338
344
|
return contrast_patch_dispatch(argc, argv, IMPL_ALIAS_SINGLETON, object);
|
|
339
345
|
}
|
|
340
346
|
|
|
341
|
-
VALUE
|
|
347
|
+
VALUE contrast_prepend_instance_patch(const int argc, const VALUE *argv,
|
|
342
348
|
const VALUE object) {
|
|
343
|
-
return contrast_patch_dispatch(argc, argv,
|
|
349
|
+
return contrast_patch_dispatch(argc, argv, IMPL_PREPEND_INSTANCE, object);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
VALUE contrast_prepend_singleton_patch(const int argc, const VALUE *argv,
|
|
353
|
+
const VALUE object) {
|
|
354
|
+
return contrast_patch_dispatch(argc, argv, IMPL_PREPEND_SINGLETON, object);
|
|
344
355
|
}
|
|
345
356
|
|
|
346
357
|
VALUE contrast_patch_define_method(const VALUE self, const VALUE clazz, const VALUE method_policy,
|
|
@@ -403,26 +414,37 @@ VALUE contrast_patch_define_method(const VALUE self, const VALUE clazz, const VA
|
|
|
403
414
|
VALUE contrast_patch_prepend(const VALUE self, const VALUE originalModule,
|
|
404
415
|
const VALUE method_policy) {
|
|
405
416
|
|
|
417
|
+
const VALUE instance = Qtrue;
|
|
418
|
+
const VALUE singleton = Qfalse;
|
|
406
419
|
const VALUE original_method_name =
|
|
407
420
|
rb_funcall(method_policy, rb_sym_method_name, 0);
|
|
408
421
|
const VALUE is_private =
|
|
409
422
|
rb_funcall(method_policy, rb_sym_private_method, 0);
|
|
410
423
|
const VALUE is_instance_method =
|
|
411
424
|
rb_funcall(method_policy, rb_sym_instance_method, 0);
|
|
412
|
-
|
|
413
|
-
|
|
425
|
+
|
|
426
|
+
// Set the value for instance or singleton method
|
|
427
|
+
if (RTEST(is_instance_method)){
|
|
428
|
+
rb_funcall(patch_status, rb_sym_set_info_for, 5, originalModule,
|
|
429
|
+
original_method_name, method_policy, instance, Qnil);
|
|
430
|
+
|
|
431
|
+
} else {
|
|
432
|
+
rb_funcall(patch_status, rb_sym_set_info_for, 5, originalModule,
|
|
433
|
+
original_method_name, method_policy, singleton, Qnil);
|
|
434
|
+
}
|
|
435
|
+
|
|
414
436
|
VALUE module = rb_define_module_under(originalModule, "ContrastPrepend");
|
|
415
437
|
VALUE str = rb_funcall(original_method_name, rb_sym_cs_to_s, 0);
|
|
416
438
|
char *cMethodName = StringValueCStr(str);
|
|
417
439
|
if (RTEST(is_instance_method)) {
|
|
418
440
|
if (RTEST(is_private)) {
|
|
419
441
|
rb_define_private_method(module, cMethodName,
|
|
420
|
-
|
|
442
|
+
contrast_prepend_instance_patch, -1);
|
|
421
443
|
} else {
|
|
422
|
-
rb_define_method(module, cMethodName,
|
|
444
|
+
rb_define_method(module, cMethodName, contrast_prepend_instance_patch, -1);
|
|
423
445
|
}
|
|
424
446
|
} else {
|
|
425
|
-
rb_define_singleton_method(module, cMethodName,
|
|
447
|
+
rb_define_singleton_method(module, cMethodName, contrast_prepend_singleton_patch,
|
|
426
448
|
-1);
|
|
427
449
|
}
|
|
428
450
|
rb_prepend_module(originalModule, module);
|
|
@@ -437,7 +459,6 @@ void Init_cs__contrast_patch(void) {
|
|
|
437
459
|
rb_sym_contrast_apply_pre_patch = rb_intern("apply_pre_patch");
|
|
438
460
|
rb_sym_cs_to_s = rb_intern("to_s");
|
|
439
461
|
rb_sym_custom_patch = rb_intern("requires_custom_patch?");
|
|
440
|
-
rb_sym_in_request_context = rb_intern("in_request_context?");
|
|
441
462
|
rb_sym_info_for = rb_intern("info_for");
|
|
442
463
|
rb_sym_propagation_node = rb_intern("propagation_node");
|
|
443
464
|
rb_sym_set_info_for = rb_intern("set_info_for");
|
|
@@ -16,8 +16,6 @@ static VALUE rb_sym_contrast_apply_pre_patch;
|
|
|
16
16
|
static VALUE rb_sym_custom_patch;
|
|
17
17
|
static VALUE rb_sym_cs_to_s;
|
|
18
18
|
|
|
19
|
-
static VALUE rb_sym_in_request_context;
|
|
20
|
-
|
|
21
19
|
static VALUE rb_sym_enter_method_scope;
|
|
22
20
|
static VALUE rb_sym_exit_method_scope;
|
|
23
21
|
|
|
@@ -148,8 +146,11 @@ VALUE contrast_alias_instance_patch(const int argc, const VALUE *argv,
|
|
|
148
146
|
VALUE contrast_alias_singleton_patch(const int argc, const VALUE *argv,
|
|
149
147
|
const VALUE object);
|
|
150
148
|
|
|
151
|
-
VALUE
|
|
152
|
-
|
|
149
|
+
VALUE contrast_prepend_instance_patch(const int argc, const VALUE *argv,
|
|
150
|
+
const VALUE object);
|
|
151
|
+
|
|
152
|
+
VALUE contrast_prepend_singleton_patch(const int argc, const VALUE *argv,
|
|
153
|
+
const VALUE object);
|
|
153
154
|
|
|
154
155
|
/*
|
|
155
156
|
* Patches a module's method by prepend:
|
|
@@ -28,7 +28,6 @@ module Contrast
|
|
|
28
28
|
# @attr_reader args [Array<Contrast::Agent::Assess::ContrastObject>] the safe representation of the Arguments
|
|
29
29
|
# with which the method was invoked
|
|
30
30
|
class ContrastEvent
|
|
31
|
-
|
|
32
31
|
attr_reader :event_id, :policy_node, :stack_trace, :time, :thread, :object, :ret, :args, :tags
|
|
33
32
|
|
|
34
33
|
# We need this to track the parent id's of events to build up a flow chart of the finding
|
|
@@ -55,7 +54,7 @@ module Contrast
|
|
|
55
54
|
|
|
56
55
|
# These methods rely on the above being set. Don't move them!
|
|
57
56
|
@event_id = Contrast::Agent::Assess::ContrastEvent.next_atomic_id
|
|
58
|
-
@tags = Contrast::Agent::Assess::Tracker.properties(tagged)&.
|
|
57
|
+
@tags = Contrast::Agent::Assess::Tracker.properties(tagged)&.get_tags
|
|
59
58
|
find_parent_events!(policy_node, object, ret, args)
|
|
60
59
|
snapshot!(object, ret, args)
|
|
61
60
|
capture_stacktrace!
|
|
@@ -35,10 +35,7 @@ module Contrast
|
|
|
35
35
|
if object
|
|
36
36
|
@object = Contrast::Utils::ClassUtil.to_contrast_string(object)
|
|
37
37
|
@object_type = object.cs__class.cs__name
|
|
38
|
-
|
|
39
|
-
# restore immutability. For instance, if these tags were on a
|
|
40
|
-
# String that was then #reverse!'d, would our tags be wrong?
|
|
41
|
-
@tags = Contrast::Agent::Assess::Tracker.properties(object)&.tags
|
|
38
|
+
@tags = Contrast::Agent::Assess::Tracker.properties(object)&.get_tags
|
|
42
39
|
else
|
|
43
40
|
@object = Contrast::Utils::ObjectShare::NIL_STRING
|
|
44
41
|
@object_type = nil.cs__class.cs__name
|
|
@@ -10,7 +10,6 @@ module Contrast
|
|
|
10
10
|
# An extension of Hash that doesn't impact GC of the object being stored by storing its ID as a Key to lookup
|
|
11
11
|
# and registering a finalizer on the object to remove its entry from the Hash immediately after it's GC'd.
|
|
12
12
|
class Hash < Hash
|
|
13
|
-
|
|
14
13
|
FROZEN_FINALIZED_IDS = Set.new
|
|
15
14
|
|
|
16
15
|
def []= key, obj
|
|
@@ -24,6 +24,8 @@ module Contrast
|
|
|
24
24
|
# the name of the method to taint, mapped to the properties it
|
|
25
25
|
# should apply
|
|
26
26
|
def create_sources klass, tainted_columns
|
|
27
|
+
return unless Contrast::ASSESS.require_dynamic_sources?
|
|
28
|
+
|
|
27
29
|
class_name = klass.cs__name
|
|
28
30
|
instance_methods = klass.instance_methods
|
|
29
31
|
instance_methods.concat(klass.private_instance_methods)
|
|
@@ -12,8 +12,6 @@ module Contrast
|
|
|
12
12
|
# of a file vs data flow, such as the detection of Hardcoded Passwords
|
|
13
13
|
# or Keys.
|
|
14
14
|
module PolicyScanner
|
|
15
|
-
|
|
16
|
-
|
|
17
15
|
class << self
|
|
18
16
|
# Use the given trace_point, built from an :end event, to determine
|
|
19
17
|
# where the loaded code lives and scan that code for policy
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/components/logger'
|
|
5
|
+
require 'contrast/utils/lru_cache'
|
|
5
6
|
|
|
6
7
|
module Contrast
|
|
7
8
|
module Agent
|
|
@@ -10,8 +11,9 @@ module Contrast
|
|
|
10
11
|
# caused, we'll need to store the state before the change occurred.
|
|
11
12
|
class PreShift
|
|
12
13
|
include Contrast::Components::Logger::InstanceMethods
|
|
14
|
+
extend Contrast::Components::Logger::InstanceMethods
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
@lru_cache = Contrast::Utils::LRUCache.new
|
|
15
17
|
UNDUPLICABLE_MODULES = [
|
|
16
18
|
Enumerator # dup'ing results in 'can't copy execution context'
|
|
17
19
|
].cs__freeze
|
|
@@ -70,29 +72,44 @@ module Contrast
|
|
|
70
72
|
|
|
71
73
|
def append_object_details preshift, initializing, object
|
|
72
74
|
can = can_dup?(initializing, object)
|
|
73
|
-
preshift.object =
|
|
75
|
+
preshift.object = if @lru_cache.key?(object.__id__) && !Contrast::Agent::Assess::Tracker.tracked?(object)
|
|
76
|
+
@lru_cache[object.__id__]
|
|
77
|
+
else
|
|
78
|
+
can ? object.dup : object
|
|
79
|
+
end
|
|
74
80
|
preshift.object_length = if Contrast::Utils::DuckUtils.quacks_to?(preshift.object, :length)
|
|
75
81
|
object.length
|
|
76
82
|
else
|
|
77
83
|
0
|
|
78
84
|
end
|
|
85
|
+
|
|
79
86
|
return unless can
|
|
87
|
+
|
|
80
88
|
return unless Contrast::Agent::Assess::Tracker.tracked?(object)
|
|
81
89
|
|
|
82
90
|
Contrast::Agent::Assess::Tracker.copy(object, preshift.object)
|
|
91
|
+
@lru_cache[object.__id__] = object
|
|
83
92
|
end
|
|
84
93
|
|
|
85
94
|
def append_arg_details preshift, args
|
|
86
|
-
|
|
87
|
-
preshift.args
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
args_length = args.length
|
|
96
|
+
preshift.args = Array.new(args_length)
|
|
97
|
+
preshift.arg_lengths = Array.new(args_length)
|
|
98
|
+
idx = 0
|
|
99
|
+
while idx < args_length
|
|
100
|
+
or_arg = args[idx]
|
|
101
|
+
p_arg = if @lru_cache.key?(or_arg.__id__)
|
|
102
|
+
@lru_cache[or_arg.__id__]
|
|
103
|
+
else
|
|
104
|
+
can_dup?(false, or_arg) ? or_arg.dup : or_arg
|
|
105
|
+
end
|
|
106
|
+
preshift.args[idx] = p_arg
|
|
107
|
+
preshift.arg_lengths[idx] = Contrast::Utils::DuckUtils.quacks_to?(p_arg, :length) ? p_arg.length : 0
|
|
108
|
+
idx += 1
|
|
109
|
+
next if p_arg.__id__ == or_arg.__id__
|
|
110
|
+
|
|
111
|
+
Contrast::Agent::Assess::Tracker.copy(or_arg, p_arg)
|
|
112
|
+
@lru_cache[p_arg.__id__] = p_arg
|
|
96
113
|
end
|
|
97
114
|
end
|
|
98
115
|
end
|
|
@@ -12,14 +12,11 @@ module Contrast
|
|
|
12
12
|
module Agent
|
|
13
13
|
module Assess
|
|
14
14
|
module Policy
|
|
15
|
-
# This class is responsible for the continuation of traces. A
|
|
16
|
-
#
|
|
17
|
-
# general, these methods work on the String class or a holder of
|
|
18
|
-
# Strings
|
|
15
|
+
# This class is responsible for the continuation of traces. A Propagator is any method that transforms an
|
|
16
|
+
# untrusted value. In general, these methods work on the String class or a holder of Strings.
|
|
19
17
|
module PropagationMethod
|
|
20
18
|
extend Contrast::Components::Logger::InstanceMethods
|
|
21
19
|
|
|
22
|
-
|
|
23
20
|
APPEND_ACTION = 'APPEND'
|
|
24
21
|
CENTER_ACTION = 'CENTER'
|
|
25
22
|
INSERT_ACTION = 'INSERT'
|
|
@@ -48,19 +45,16 @@ module Contrast
|
|
|
48
45
|
end
|
|
49
46
|
end
|
|
50
47
|
|
|
51
|
-
# @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy]
|
|
52
|
-
#
|
|
53
|
-
# @param preshift [Contrast::Agent::Assess::PreShift] The capture
|
|
54
|
-
#
|
|
55
|
-
# patched method.
|
|
48
|
+
# @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] the policy that governs the
|
|
49
|
+
# patches to this method
|
|
50
|
+
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
51
|
+
# the invocation of the patched method.
|
|
56
52
|
# @param object [Object] the Object on which the method was invoked
|
|
57
53
|
# @param ret [Object] the Return of the invoked method
|
|
58
|
-
# @param args [Array<Object>] the Arguments with which the method
|
|
59
|
-
# was invoked
|
|
54
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
60
55
|
# @param block [Block] the Block passed to the original method
|
|
61
|
-
# @return [Object, nil] the tracked Return or nil if no changes
|
|
62
|
-
#
|
|
63
|
-
# not nil
|
|
56
|
+
# @return [Object, nil] the tracked Return or nil if no changes were made; will replace the return of the
|
|
57
|
+
# original function if not nil
|
|
64
58
|
def apply_propagation method_policy, preshift, object, ret, args, block
|
|
65
59
|
return unless method_policy.propagation_node
|
|
66
60
|
return unless preshift
|
|
@@ -86,26 +80,21 @@ module Contrast
|
|
|
86
80
|
SPLIT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Split
|
|
87
81
|
}.cs__freeze
|
|
88
82
|
|
|
89
|
-
# I lied above. We had to figure out what the target of the
|
|
90
|
-
#
|
|
91
|
-
# it
|
|
92
|
-
# return of the patched function unless it is nil, so be sure
|
|
93
|
-
# you're returning what you intend.
|
|
83
|
+
# I lied above. We had to figure out what the target of the propagation was. Now that we know, we'll
|
|
84
|
+
# actually do things to it. Note that the return of this method will replace the original return of the
|
|
85
|
+
# patched function unless it is nil, so be sure you're returning what you intend.
|
|
94
86
|
#
|
|
95
|
-
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode]
|
|
96
|
-
#
|
|
97
|
-
# @param preshift [Contrast::Agent::Assess::PreShift] The capture
|
|
98
|
-
#
|
|
99
|
-
# patched method.
|
|
87
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
88
|
+
# propagation event.
|
|
89
|
+
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
90
|
+
# the invocation of the patched method.
|
|
100
91
|
# @param target [Object] the Target to which to propagate.
|
|
101
92
|
# @param object [Object] the Object on which the method was invoked
|
|
102
93
|
# @param ret [Object] the Return of the invoked method
|
|
103
|
-
# @param args [Array<Object>] the Arguments with which the method
|
|
104
|
-
# was invoked
|
|
94
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
105
95
|
# @param block [Block] the Block passed to the original method
|
|
106
|
-
# @return [Object, nil] the tracked Return or nil if no changes
|
|
107
|
-
#
|
|
108
|
-
# not nil
|
|
96
|
+
# @return [Object, nil] the tracked Return or nil if no changes were made; will replace the return of the
|
|
97
|
+
# original function if not nil
|
|
109
98
|
def apply_propagator propagation_node, preshift, target, object, ret, args, block
|
|
110
99
|
return unless propagation_possible?(propagation_node, target)
|
|
111
100
|
|
|
@@ -127,13 +116,16 @@ module Contrast
|
|
|
127
116
|
nil
|
|
128
117
|
end
|
|
129
118
|
|
|
130
|
-
# Custom actions tend to be the more complex of our propagations.
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
119
|
+
# Custom actions tend to be the more complex of our propagations. Often, the method has to make decisions
|
|
120
|
+
# about the target based on the context with which the method was called. As such, defer determining if the
|
|
121
|
+
# target is valid to that method.
|
|
122
|
+
#
|
|
123
|
+
# In all other cases, a target is valid for propagation if it is not nil
|
|
134
124
|
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
125
|
+
# @param target [Object] the thing to which to propagate
|
|
126
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
127
|
+
# propagation event.
|
|
128
|
+
# @return [Boolean]
|
|
137
129
|
def valid_target? target, propagation_node
|
|
138
130
|
return true if propagation_node.action == CUSTOM_ACTION
|
|
139
131
|
|
|
@@ -141,8 +133,11 @@ module Contrast
|
|
|
141
133
|
end
|
|
142
134
|
|
|
143
135
|
ZERO_LENGTH_ACTIONS = [DB_WRITE_ACTION, CUSTOM_ACTION, KEEP_ACTION, REPLACE_ACTION, SPLAT_ACTION].cs__freeze
|
|
144
|
-
# If the action required needs a length and the target does not have
|
|
145
|
-
#
|
|
136
|
+
# If the action required needs a length and the target does not have one, the length is not valid
|
|
137
|
+
#
|
|
138
|
+
# @param target [Object] the thing to which to propagate
|
|
139
|
+
# @param action [String] the name of the action taken during this propagation
|
|
140
|
+
# @return [Boolean]
|
|
146
141
|
def valid_length? target, action
|
|
147
142
|
return true if ZERO_LENGTH_ACTIONS.include?(action)
|
|
148
143
|
|
|
@@ -153,9 +148,15 @@ module Contrast
|
|
|
153
148
|
end
|
|
154
149
|
end
|
|
155
150
|
|
|
156
|
-
# Before we do any work, we should check if we even need to.
|
|
157
|
-
#
|
|
158
|
-
#
|
|
151
|
+
# Before we do any work, we should check if we even need to. If the source and target of this patcher are
|
|
152
|
+
# not tracked, there's no need to do anything. A copy of nothing is still nothing.
|
|
153
|
+
#
|
|
154
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
155
|
+
# propagation event.
|
|
156
|
+
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
157
|
+
# the invocation of the patched method.
|
|
158
|
+
# @param target [Object] the thing to which to propagate
|
|
159
|
+
# @return [Boolean]
|
|
159
160
|
def can_propagate? propagation_node, preshift, target
|
|
160
161
|
return false unless appropriate_target?(propagation_node, target)
|
|
161
162
|
return true if Contrast::Utils::Assess::TrackingUtil.tracked?(target)
|
|
@@ -165,22 +166,21 @@ module Contrast
|
|
|
165
166
|
case source
|
|
166
167
|
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
167
168
|
return true if Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.object)
|
|
168
|
-
else
|
|
169
|
+
else
|
|
170
|
+
# has to be P, there's no ret source type (yet? ever?)
|
|
169
171
|
return true if preshift.args && Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.args[source])
|
|
170
172
|
end
|
|
171
173
|
end
|
|
172
174
|
false
|
|
173
175
|
end
|
|
174
176
|
|
|
175
|
-
# We cannot propagate to frozen things that have not been updated
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
# they're supposed to be immutable, but third parties do jenky
|
|
180
|
-
# things, so allow it as long as it is safe to do.
|
|
177
|
+
# We cannot propagate to frozen things that have not been updated to work with our property tracking,
|
|
178
|
+
# unless they're duplicable and the return. We probably shouldn't propagate to frozen things at all, as
|
|
179
|
+
# they're supposed to be immutable, but third parties do jenky things, so allow it as long as it is safe to
|
|
180
|
+
# do.
|
|
181
181
|
#
|
|
182
|
-
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode]
|
|
183
|
-
#
|
|
182
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
183
|
+
# propagation event.
|
|
184
184
|
# @param target [Object] the Target to which to propagate.
|
|
185
185
|
# @return [Boolean] if the target can be propagated to
|
|
186
186
|
def appropriate_target? propagation_node, target
|
|
@@ -191,6 +191,9 @@ module Contrast
|
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
# If this patcher has tags, apply them to the entire target
|
|
194
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
195
|
+
# propagation event.
|
|
196
|
+
# @param target [Object] the Target to which to propagate.
|
|
194
197
|
def apply_tags propagation_node, target
|
|
195
198
|
return unless propagation_node.tags
|
|
196
199
|
return unless (properties = Contrast::Agent::Assess::Tracker.properties(target))
|
|
@@ -202,6 +205,10 @@ module Contrast
|
|
|
202
205
|
end
|
|
203
206
|
|
|
204
207
|
# If this patcher has tags, remove them from the entire target
|
|
208
|
+
#
|
|
209
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
210
|
+
# propagation event.
|
|
211
|
+
# @param target [Object] the Target to which to propagate.
|
|
205
212
|
def apply_untags propagation_node, target
|
|
206
213
|
return unless propagation_node.untags
|
|
207
214
|
return unless (properties = Contrast::Agent::Assess::Tracker.properties(target))
|
|
@@ -214,6 +221,10 @@ module Contrast
|
|
|
214
221
|
private
|
|
215
222
|
|
|
216
223
|
# This is checked right before actual propagation
|
|
224
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
225
|
+
# propagation event.
|
|
226
|
+
# @param target [Object] the Target to which to propagate.
|
|
227
|
+
# @return [Boolean]
|
|
217
228
|
def propagation_possible? propagation_node, target
|
|
218
229
|
return false unless propagation_node && valid_target?(target, propagation_node)
|
|
219
230
|
return false unless valid_length?(target, propagation_node.action)
|
|
@@ -224,12 +235,24 @@ module Contrast
|
|
|
224
235
|
# Safely duplicate the target, or return nil
|
|
225
236
|
#
|
|
226
237
|
# @param target [Object] the thing to check for duplication
|
|
238
|
+
# @return [Object, nil]
|
|
227
239
|
def safe_dup target
|
|
228
240
|
target.dup
|
|
229
241
|
rescue StandardError => _e
|
|
230
242
|
nil
|
|
231
243
|
end
|
|
232
244
|
|
|
245
|
+
# Iterate over each key and value in a hash to allow for propagation to each.
|
|
246
|
+
#
|
|
247
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
248
|
+
# propagation event.
|
|
249
|
+
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
250
|
+
# the invocation of the patched method.
|
|
251
|
+
# @param target [Object] the Target to which to propagate.
|
|
252
|
+
# @param object [Object] the Object on which the method was invoked
|
|
253
|
+
# @param ret [Object] the Return of the invoked method
|
|
254
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
255
|
+
# @param block [Block] the Block passed to the original method
|
|
233
256
|
def handle_hash_propagation propagation_node, preshift, target, object, ret, args, block
|
|
234
257
|
target.each_pair do |key, value|
|
|
235
258
|
apply_propagator(propagation_node, preshift, key, object, ret, args, block)
|
|
@@ -237,6 +260,17 @@ module Contrast
|
|
|
237
260
|
end
|
|
238
261
|
end
|
|
239
262
|
|
|
263
|
+
# Iterate over each value in an enumerable to allow for propagation to each.
|
|
264
|
+
#
|
|
265
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
266
|
+
# propagation event.
|
|
267
|
+
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
268
|
+
# the invocation of the patched method.
|
|
269
|
+
# @param target [Object] the Target to which to propagate.
|
|
270
|
+
# @param object [Object] the Object on which the method was invoked
|
|
271
|
+
# @param ret [Object] the Return of the invoked method
|
|
272
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
273
|
+
# @param block [Block] the Block passed to the original method
|
|
240
274
|
def handle_enumerable_propagation propagation_node, preshift, target, object, ret, args, block
|
|
241
275
|
target.each do |value|
|
|
242
276
|
next if target == value
|
|
@@ -245,6 +279,17 @@ module Contrast
|
|
|
245
279
|
end
|
|
246
280
|
end
|
|
247
281
|
|
|
282
|
+
# Move the properties from the source(s) to the target of the propagation.
|
|
283
|
+
#
|
|
284
|
+
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
285
|
+
# propagation event.
|
|
286
|
+
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
287
|
+
# the invocation of the patched method.
|
|
288
|
+
# @param target [Object] the Target to which to propagate.
|
|
289
|
+
# @param object [Object] the Object on which the method was invoked
|
|
290
|
+
# @param ret [Object] the Return of the invoked method
|
|
291
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
|
292
|
+
# @param _block [Block] the Block passed to the original method
|
|
248
293
|
def handle_cs_properties_propagation propagation_node, preshift, target, object, ret, args, _block
|
|
249
294
|
return if propagation_node.action == NOOP_ACTION
|
|
250
295
|
return unless can_propagate?(propagation_node, preshift, target)
|
|
@@ -266,12 +311,9 @@ module Contrast
|
|
|
266
311
|
propagation_class.propagate(propagation_node, preshift, target)
|
|
267
312
|
# Once we've propagated, attempt to tag the target if there is a tag(s) to be applied
|
|
268
313
|
apply_tags(propagation_node, target)
|
|
269
|
-
# Even though we skipped propagating tags from the source if they
|
|
270
|
-
#
|
|
271
|
-
#
|
|
272
|
-
# In this order, untags takes precedent over tags; but we control
|
|
273
|
-
# both and there should never be a propagator that has a tag in
|
|
274
|
-
# its untag.
|
|
314
|
+
# Even though we skipped propagating tags from the source if they were included in untags, the target may
|
|
315
|
+
# have already had some on it. Let's go ahead and remove them. In this order, untags takes precedent over
|
|
316
|
+
# tags; but we control both and there should never be a propagator that has a tag in its untag.
|
|
275
317
|
apply_untags(propagation_node, target)
|
|
276
318
|
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
|
|
277
319
|
|
|
@@ -302,7 +344,8 @@ module Contrast
|
|
|
302
344
|
# propagation event.
|
|
303
345
|
# @return [Boolean]
|
|
304
346
|
def can_handle_frozen? propagation_node
|
|
305
|
-
::Contrast::ASSESS.track_frozen_sources? &&
|
|
347
|
+
::Contrast::ASSESS.track_frozen_sources? &&
|
|
348
|
+
propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
|
|
306
349
|
end
|
|
307
350
|
end
|
|
308
351
|
end
|