contrast-agent 4.12.0 → 4.14.1
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/.simplecov +1 -0
- data/ext/cs__assess_module/cs__assess_module.c +48 -0
- data/ext/cs__assess_module/cs__assess_module.h +7 -0
- data/ext/cs__common/cs__common.c +5 -0
- data/ext/cs__common/cs__common.h +8 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.c +16 -1
- data/ext/cs__os_information/cs__os_information.c +31 -0
- data/ext/cs__os_information/cs__os_information.h +7 -0
- data/ext/cs__os_information/extconf.rb +5 -0
- data/lib/contrast/agent/assess/policy/policy_node.rb +6 -6
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +5 -0
- data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
- data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
- data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +2 -154
- data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
- data/lib/contrast/agent/assess/policy/trigger_method.rb +45 -110
- data/lib/contrast/agent/assess/policy/trigger_node.rb +14 -6
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +1 -1
- data/lib/contrast/agent/assess/property/tagged.rb +53 -185
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +40 -6
- data/lib/contrast/agent/deadzone/policy/policy.rb +1 -1
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -0
- data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
- data/lib/contrast/agent/middleware.rb +14 -62
- data/lib/contrast/agent/patching/policy/method_policy.rb +3 -89
- data/lib/contrast/agent/patching/policy/method_policy_extend.rb +111 -0
- data/lib/contrast/agent/patching/policy/patch.rb +28 -235
- data/lib/contrast/agent/patching/policy/patcher.rb +14 -49
- data/lib/contrast/agent/reporting/report.rb +21 -0
- data/lib/contrast/agent/reporting/reporter.rb +142 -0
- data/lib/contrast/agent/reporting/reporting_events/finding.rb +90 -0
- data/lib/contrast/agent/reporting/reporting_events/preflight.rb +25 -0
- data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +56 -0
- data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +37 -0
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +127 -0
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +168 -0
- data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +66 -0
- data/lib/contrast/agent/request.rb +2 -81
- data/lib/contrast/agent/request_context.rb +4 -128
- data/lib/contrast/agent/request_context_extend.rb +138 -0
- data/lib/contrast/agent/request_handler.rb +7 -3
- data/lib/contrast/agent/response.rb +2 -73
- data/lib/contrast/agent/startup_metrics_telemetry_event.rb +94 -0
- data/lib/contrast/agent/static_analysis.rb +5 -3
- data/lib/contrast/agent/telemetry.rb +137 -0
- data/lib/contrast/agent/telemetry_event.rb +33 -0
- data/lib/contrast/agent/thread_watcher.rb +66 -11
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +21 -0
- data/lib/contrast/api/communication/connection_status.rb +10 -7
- data/lib/contrast/api/communication/messaging_queue.rb +37 -3
- data/lib/contrast/api/communication/response_processor.rb +15 -8
- data/lib/contrast/api/communication/service_lifecycle.rb +13 -3
- data/lib/contrast/api/communication/socket.rb +6 -8
- data/lib/contrast/api/communication/socket_client.rb +29 -12
- data/lib/contrast/api/communication/speedracer.rb +37 -1
- data/lib/contrast/api/communication/tcp_socket.rb +4 -3
- data/lib/contrast/api/communication/unix_socket.rb +1 -0
- data/lib/contrast/api/decorators/finding.rb +45 -0
- data/lib/contrast/components/api.rb +90 -0
- data/lib/contrast/components/app_context.rb +10 -41
- data/lib/contrast/components/app_context_extend.rb +78 -0
- data/lib/contrast/components/base.rb +23 -0
- data/lib/contrast/components/config.rb +92 -13
- data/lib/contrast/components/contrast_service.rb +11 -0
- data/lib/contrast/components/sampling.rb +2 -2
- data/lib/contrast/config/agent_configuration.rb +1 -1
- data/lib/contrast/config/api_configuration.rb +27 -0
- data/lib/contrast/config/api_proxy_configuration.rb +14 -0
- data/lib/contrast/config/application_configuration.rb +2 -3
- data/lib/contrast/config/assess_configuration.rb +3 -3
- data/lib/contrast/config/base_configuration.rb +17 -28
- data/lib/contrast/config/certification_configuration.rb +15 -0
- data/lib/contrast/config/env_variables.rb +18 -0
- data/lib/contrast/config/heap_dump_configuration.rb +6 -6
- data/lib/contrast/config/inventory_configuration.rb +1 -5
- data/lib/contrast/config/protect_rule_configuration.rb +1 -1
- data/lib/contrast/config/request_audit_configuration.rb +18 -0
- data/lib/contrast/config/root_configuration.rb +1 -0
- data/lib/contrast/config/ruby_configuration.rb +6 -6
- data/lib/contrast/config/service_configuration.rb +2 -2
- data/lib/contrast/config.rb +1 -1
- data/lib/contrast/configuration.rb +4 -2
- data/lib/contrast/extension/assess/array.rb +5 -7
- data/lib/contrast/extension/thread.rb +31 -12
- data/lib/contrast/framework/manager.rb +22 -44
- data/lib/contrast/framework/manager_extend.rb +50 -0
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
- data/lib/contrast/framework/rails/patch/support.rb +31 -29
- data/lib/contrast/framework/rails/railtie.rb +1 -1
- data/lib/contrast/framework/sinatra/support.rb +2 -1
- data/lib/contrast/logger/application.rb +4 -0
- data/lib/contrast/logger/log.rb +8 -103
- data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
- data/lib/contrast/utils/assess/property/tagged_utils.rb +165 -0
- data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
- data/lib/contrast/utils/assess/tracking_util.rb +20 -15
- data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
- data/lib/contrast/utils/class_util.rb +18 -14
- data/lib/contrast/utils/exclude_key.rb +20 -0
- data/lib/contrast/utils/findings.rb +62 -0
- data/lib/contrast/utils/hash_digest.rb +10 -73
- data/lib/contrast/utils/hash_digest_extend.rb +86 -0
- data/lib/contrast/utils/head_dump_utils_extend.rb +74 -0
- data/lib/contrast/utils/heap_dump_util.rb +2 -65
- data/lib/contrast/utils/invalid_configuration_util.rb +29 -0
- data/lib/contrast/utils/io_util.rb +1 -1
- data/lib/contrast/utils/log_utils.rb +108 -0
- data/lib/contrast/utils/metrics_hash.rb +59 -0
- data/lib/contrast/utils/middleware_utils.rb +87 -0
- data/lib/contrast/utils/net_http_base.rb +158 -0
- data/lib/contrast/utils/object_share.rb +1 -0
- data/lib/contrast/utils/os.rb +23 -0
- data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
- data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
- data/lib/contrast/utils/request_utils.rb +88 -0
- data/lib/contrast/utils/response_utils.rb +97 -0
- data/lib/contrast/utils/substitution_utils.rb +167 -0
- data/lib/contrast/utils/tag_util.rb +9 -9
- data/lib/contrast/utils/telemetry.rb +79 -0
- data/lib/contrast/utils/telemetry_client.rb +90 -0
- data/lib/contrast/utils/telemetry_identifier.rb +130 -0
- data/lib/contrast.rb +18 -0
- data/ruby-agent.gemspec +7 -6
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +69 -22
- data/lib/contrast/config/default_value.rb +0 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ab9eae65aeefbeb3c12102f71287c60856783238067a7c461fcf534aaa65712
|
|
4
|
+
data.tar.gz: 1fe74d85374374f40a11c47e44ab0da1042a3f99d607fdfd697a45cc321b45b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: df020b9d0ff09e61107e280f6e76411f9a9b75c1c94f7082ef52d487e5f88b940e75996dd869d648ac0af654f5f84e48b1f691fdcc9e5f294a565d2bb1a39b7d
|
|
7
|
+
data.tar.gz: 8bd869bfacbb82aea7b0cf55ff806fded4bae5c6b0454185c4c422bdd14ee07fd1edb3f4b843e702765a4693b51697a0497129d73d3d3652f32cc50056ed783b
|
data/.simplecov
CHANGED
|
@@ -57,6 +57,45 @@ contrast_assess_module_module_eval(const int argc, const VALUE *argv,
|
|
|
57
57
|
return ret;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
VALUE
|
|
61
|
+
contrast_assess_module_prepend(const int argc, const VALUE *argv,
|
|
62
|
+
const VALUE self) {
|
|
63
|
+
|
|
64
|
+
rb_prepend_module(self, argv[0]);
|
|
65
|
+
|
|
66
|
+
VALUE module_at;
|
|
67
|
+
VALUE rb_incl_in_mod_ary = rb_funcall(self, rb_intern("included_in"), 0);
|
|
68
|
+
|
|
69
|
+
if (RB_TYPE_P(rb_incl_in_mod_ary, T_ARRAY)) {
|
|
70
|
+
int i = 0;
|
|
71
|
+
int size = rb_funcall(rb_incl_in_mod_ary, rb_intern("length"), 0);
|
|
72
|
+
for (i = 0; i < size; ++i) {
|
|
73
|
+
module_at = rb_ary_entry(rb_incl_in_mod_ary, i);
|
|
74
|
+
if (RB_TYPE_P(module_at, T_MODULE)) {
|
|
75
|
+
rb_include_module(module_at, argv[0]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return self;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
VALUE
|
|
83
|
+
contrast_assess_module_included(const int argc, const VALUE *argv,
|
|
84
|
+
const VALUE self) {
|
|
85
|
+
VALUE frozen;
|
|
86
|
+
if (RB_TYPE_P(self, T_MODULE)) {
|
|
87
|
+
// check if frozen
|
|
88
|
+
frozen = rb_funcall(self, rb_intern("cs__frozen?"), 0);
|
|
89
|
+
if (frozen == Qfalse) {
|
|
90
|
+
VALUE ary = rb_funcall(self, rb_intern("included_in"), 0);
|
|
91
|
+
if (RB_TYPE_P(ary, T_ARRAY)) {
|
|
92
|
+
rb_ary_push(ary, argv[0]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return self;
|
|
97
|
+
}
|
|
98
|
+
|
|
60
99
|
void Init_cs__assess_module(void) {
|
|
61
100
|
module_eval_trigger =
|
|
62
101
|
rb_define_class_under(core_assess, "EvalTrigger", rb_cObject);
|
|
@@ -76,4 +115,13 @@ void Init_cs__assess_module(void) {
|
|
|
76
115
|
|
|
77
116
|
contrast_register_patch("Module", "module_eval",
|
|
78
117
|
contrast_assess_module_module_eval);
|
|
118
|
+
/*
|
|
119
|
+
* We patch these for better ancestors handling, and only for older ruby versions.
|
|
120
|
+
*/
|
|
121
|
+
if (rb_ver_below_three()) {
|
|
122
|
+
contrast_register_patch("Module", "included",
|
|
123
|
+
contrast_assess_module_included);
|
|
124
|
+
contrast_register_patch("Module", "prepend",
|
|
125
|
+
contrast_assess_module_prepend);
|
|
126
|
+
}
|
|
79
127
|
}
|
|
@@ -24,5 +24,12 @@ contrast_assess_module_class_eval(const int argc, const VALUE *argv,
|
|
|
24
24
|
VALUE
|
|
25
25
|
contrast_assess_module_module_eval(const int argc, const VALUE *argv,
|
|
26
26
|
const VALUE mod);
|
|
27
|
+
VALUE
|
|
28
|
+
contrast_assess_module_prepend(const int argc, const VALUE *argv,
|
|
29
|
+
const VALUE self);
|
|
30
|
+
|
|
31
|
+
VALUE
|
|
32
|
+
contrast_assess_module_included(const int argc, const VALUE *argv,
|
|
33
|
+
const VALUE mod);
|
|
27
34
|
|
|
28
35
|
void Init_cs__assess_module(void);
|
data/ext/cs__common/cs__common.c
CHANGED
|
@@ -144,6 +144,11 @@ _contrast_register_patch(const char *module_name, const char *method_name,
|
|
|
144
144
|
return SYM2ID(underlying_method_name);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
int rb_ver_below_three() {
|
|
148
|
+
int ruby_version = FIX2INT(rb_funcall(rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")), rb_intern("to_i"), 0));
|
|
149
|
+
return ruby_version < 3;
|
|
150
|
+
}
|
|
151
|
+
|
|
147
152
|
void Init_cs__common(void) {
|
|
148
153
|
cs__send_method = rb_intern("send");
|
|
149
154
|
cs__alias_method_sym = ID2SYM(rb_intern("alias_method"));
|
data/ext/cs__common/cs__common.h
CHANGED
|
@@ -34,6 +34,14 @@ static VALUE rb_sym_alias_singleton;
|
|
|
34
34
|
static VALUE rb_sym_prepend_instance;
|
|
35
35
|
static VALUE rb_sym_prepend_singleton;
|
|
36
36
|
|
|
37
|
+
/*
|
|
38
|
+
* Check if ruby version is < 3.0.0.
|
|
39
|
+
* We are using this for handling ancestors of included modules.
|
|
40
|
+
* Since this is fixed after Ruby 3.0.0 we should remove this after
|
|
41
|
+
* dropping support for older versions, as no longer needed.
|
|
42
|
+
*/
|
|
43
|
+
int rb_ver_below_three();
|
|
44
|
+
|
|
37
45
|
void patch_via_funchook(void *original_function, void *hook_function);
|
|
38
46
|
|
|
39
47
|
void contrast_alias_method(const VALUE target, const char *to,
|
|
@@ -43,7 +43,7 @@ VALUE contrast_patch_call_original(const VALUE *args) {
|
|
|
43
43
|
if (rb_block_given_p()) {
|
|
44
44
|
return rb_funcall_with_block_kw(object, method_id, argc, params, rb_block_proc(), RB_PASS_CALLED_KEYWORDS);
|
|
45
45
|
} else {
|
|
46
|
-
return rb_funcallv_kw(object, method_id, argc, params, RB_PASS_CALLED_KEYWORDS);
|
|
46
|
+
return rb_funcallv_kw(object, method_id, argc, params, RB_PASS_CALLED_KEYWORDS);
|
|
47
47
|
}
|
|
48
48
|
/* Ruby < 2.7 */
|
|
49
49
|
#else
|
|
@@ -448,6 +448,21 @@ VALUE contrast_patch_prepend(const VALUE self, const VALUE originalModule,
|
|
|
448
448
|
-1);
|
|
449
449
|
}
|
|
450
450
|
rb_prepend_module(originalModule, module);
|
|
451
|
+
|
|
452
|
+
if (rb_ver_below_three()) {
|
|
453
|
+
VALUE module_at;
|
|
454
|
+
VALUE rb_incl_in_mod_ary = rb_funcall(originalModule, rb_intern("included_in"), 0);
|
|
455
|
+
if (RB_TYPE_P(rb_incl_in_mod_ary, T_ARRAY)) {
|
|
456
|
+
int i = 0;
|
|
457
|
+
int size = rb_funcall(rb_incl_in_mod_ary, rb_intern("length"), 0);
|
|
458
|
+
for (i = 0; i < size; ++i) {
|
|
459
|
+
module_at = rb_ary_entry(rb_incl_in_mod_ary, i);
|
|
460
|
+
if (RB_TYPE_P(module_at, T_MODULE)) {
|
|
461
|
+
rb_include_module(module_at, module);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
451
466
|
return Qtrue;
|
|
452
467
|
}
|
|
453
468
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Copyright (c) 2021 Contrast Security, Inc. See
|
|
2
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a for more details. */
|
|
3
|
+
|
|
4
|
+
#include "cs__os_information.h"
|
|
5
|
+
#include <dlfcn.h>
|
|
6
|
+
#include <ruby.h>
|
|
7
|
+
#include <sys/utsname.h>
|
|
8
|
+
|
|
9
|
+
VALUE contrast, utils, os;
|
|
10
|
+
|
|
11
|
+
VALUE contrast_get_system_information()
|
|
12
|
+
{
|
|
13
|
+
struct utsname uname_pointer;
|
|
14
|
+
|
|
15
|
+
uname (&uname_pointer);
|
|
16
|
+
|
|
17
|
+
VALUE rb_data_hash = rb_hash_new();
|
|
18
|
+
rb_hash_aset(rb_data_hash, rb_str_new2("os_type"), rb_str_new2(uname_pointer.sysname));
|
|
19
|
+
rb_hash_aset(rb_data_hash, rb_str_new2("os_version"), rb_str_new2(uname_pointer.release));
|
|
20
|
+
rb_hash_aset(rb_data_hash, rb_str_new2("os_complete_version"), rb_str_new2(uname_pointer.version));
|
|
21
|
+
rb_hash_aset(rb_data_hash, rb_str_new2("os_arch"), rb_str_new2(uname_pointer.machine));
|
|
22
|
+
return rb_data_hash;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void Init_cs__os_information(void)
|
|
26
|
+
{
|
|
27
|
+
contrast = rb_define_module("Contrast");
|
|
28
|
+
utils = rb_define_module_under(contrast, "Utils");
|
|
29
|
+
os = rb_define_module_under(utils, "OS");
|
|
30
|
+
rb_define_module_function(os, "get_system_information", contrast_get_system_information, 0);
|
|
31
|
+
}
|
|
@@ -135,14 +135,14 @@ module Contrast
|
|
|
135
135
|
|
|
136
136
|
converted = []
|
|
137
137
|
markers.split(Contrast::Utils::ObjectShare::COMMA).each do |t|
|
|
138
|
-
case t
|
|
139
|
-
|
|
138
|
+
converted << case t
|
|
139
|
+
when Contrast::Utils::ObjectShare::OBJECT_KEY,
|
|
140
140
|
Contrast::Utils::ObjectShare::RETURN_KEY
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
t
|
|
143
|
+
else
|
|
144
|
+
Integer(t[1..-1])
|
|
145
|
+
end
|
|
146
146
|
end
|
|
147
147
|
converted
|
|
148
148
|
end
|
|
@@ -32,8 +32,13 @@ module Contrast
|
|
|
32
32
|
mod = trace_point.self
|
|
33
33
|
return if mod.cs__frozen? || mod.singleton_class?
|
|
34
34
|
|
|
35
|
+
different_ruby_version trace_point, provider_values, mod
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def different_ruby_version trace_point, provider_values, mod
|
|
35
39
|
# TODO: RUBY-1014 - remove non-AST approach
|
|
36
40
|
if RUBY_VERSION >= '2.6.0'
|
|
41
|
+
# TODO: RUBY-714 EOL 2.5. That check will be removed and the code will be brought back in here.
|
|
37
42
|
ast = RubyVM::AbstractSyntaxTree.parse_file(trace_point.path)
|
|
38
43
|
provider_values.each do |provider|
|
|
39
44
|
provider.parse(trace_point, ast)
|
|
@@ -7,6 +7,7 @@ require 'contrast/agent/assess/policy/propagator'
|
|
|
7
7
|
require 'contrast/components/logger'
|
|
8
8
|
require 'contrast/utils/object_share'
|
|
9
9
|
require 'contrast/utils/sha256_builder'
|
|
10
|
+
require 'contrast/utils/assess/propagation_method_utils'
|
|
10
11
|
|
|
11
12
|
module Contrast
|
|
12
13
|
module Agent
|
|
@@ -16,35 +17,9 @@ module Contrast
|
|
|
16
17
|
# untrusted value. In general, these methods work on the String class or a holder of Strings.
|
|
17
18
|
module PropagationMethod
|
|
18
19
|
extend Contrast::Components::Logger::InstanceMethods
|
|
19
|
-
|
|
20
|
-
APPEND_ACTION = 'APPEND'
|
|
21
|
-
CENTER_ACTION = 'CENTER'
|
|
22
|
-
INSERT_ACTION = 'INSERT'
|
|
23
|
-
KEEP_ACTION = 'KEEP'
|
|
24
|
-
NEXT_ACTION = 'NEXT'
|
|
25
|
-
NOOP_ACTION = 'NOOP'
|
|
26
|
-
PREPEND_ACTION = 'PREPEND'
|
|
27
|
-
REPLACE_ACTION = 'REPLACE'
|
|
28
|
-
REMOVE_ACTION = 'REMOVE'
|
|
29
|
-
REVERSE_ACTION = 'REVERSE'
|
|
30
|
-
SPLAT_ACTION = 'SPLAT'
|
|
31
|
-
SPLIT_ACTION = 'SPLIT'
|
|
32
|
-
DB_WRITE_ACTION = 'DB_WRITE'
|
|
33
|
-
CUSTOM_ACTION = 'CUSTOM'
|
|
20
|
+
extend Contrast::Utils::Assess::PropagationMethodUtils
|
|
34
21
|
|
|
35
22
|
class << self
|
|
36
|
-
def determine_target propagation_node, ret, object, args
|
|
37
|
-
target = propagation_node.targets[0]
|
|
38
|
-
case target
|
|
39
|
-
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
40
|
-
object
|
|
41
|
-
when Contrast::Utils::ObjectShare::RETURN_KEY
|
|
42
|
-
ret
|
|
43
|
-
else
|
|
44
|
-
args[target]
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
23
|
# @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] the policy that governs the
|
|
49
24
|
# patches to this method
|
|
50
25
|
# @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
|
|
@@ -65,21 +40,6 @@ module Contrast
|
|
|
65
40
|
PropagationMethod.apply_propagator(propagation_node, preshift, target, object, ret, args, block)
|
|
66
41
|
end
|
|
67
42
|
|
|
68
|
-
PROPAGATION_ACTIONS = {
|
|
69
|
-
APPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Append,
|
|
70
|
-
CENTER_ACTION => Contrast::Agent::Assess::Policy::Propagator::Center,
|
|
71
|
-
INSERT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Insert,
|
|
72
|
-
KEEP_ACTION => Contrast::Agent::Assess::Policy::Propagator::Keep,
|
|
73
|
-
NEXT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Next,
|
|
74
|
-
NOOP_ACTION => nil,
|
|
75
|
-
PREPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Prepend,
|
|
76
|
-
REPLACE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Replace,
|
|
77
|
-
REMOVE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Remove,
|
|
78
|
-
REVERSE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Reverse,
|
|
79
|
-
SPLAT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Splat,
|
|
80
|
-
SPLIT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Split
|
|
81
|
-
}.cs__freeze
|
|
82
|
-
|
|
83
43
|
# I lied above. We had to figure out what the target of the propagation was. Now that we know, we'll
|
|
84
44
|
# actually do things to it. Note that the return of this method will replace the original return of the
|
|
85
45
|
# patched function unless it is nil, so be sure you're returning what you intend.
|
|
@@ -116,80 +76,6 @@ module Contrast
|
|
|
116
76
|
nil
|
|
117
77
|
end
|
|
118
78
|
|
|
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
|
|
124
|
-
#
|
|
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]
|
|
129
|
-
def valid_target? target, propagation_node
|
|
130
|
-
return true if propagation_node.action == CUSTOM_ACTION
|
|
131
|
-
|
|
132
|
-
!!target
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
ZERO_LENGTH_ACTIONS = [DB_WRITE_ACTION, CUSTOM_ACTION, KEEP_ACTION, REPLACE_ACTION, SPLAT_ACTION].cs__freeze
|
|
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]
|
|
141
|
-
def valid_length? target, action
|
|
142
|
-
return true if ZERO_LENGTH_ACTIONS.include?(action)
|
|
143
|
-
|
|
144
|
-
if Contrast::Utils::DuckUtils.quacks_to?(target, :length)
|
|
145
|
-
target.length != 0 # rubocop:disable Style/ZeroLengthPredicate
|
|
146
|
-
else
|
|
147
|
-
!target.to_s.empty?
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
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]
|
|
160
|
-
def can_propagate? propagation_node, preshift, target
|
|
161
|
-
return false unless appropriate_target?(propagation_node, target)
|
|
162
|
-
return true if Contrast::Utils::Assess::TrackingUtil.tracked?(target)
|
|
163
|
-
return false unless preshift
|
|
164
|
-
|
|
165
|
-
propagation_node.sources.each do |source|
|
|
166
|
-
case source
|
|
167
|
-
when Contrast::Utils::ObjectShare::OBJECT_KEY
|
|
168
|
-
return true if Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.object)
|
|
169
|
-
else
|
|
170
|
-
# has to be P, there's no ret source type (yet? ever?)
|
|
171
|
-
return true if preshift.args && Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.args[source])
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
false
|
|
175
|
-
end
|
|
176
|
-
|
|
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
|
-
#
|
|
182
|
-
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
183
|
-
# propagation event.
|
|
184
|
-
# @param target [Object] the Target to which to propagate.
|
|
185
|
-
# @return [Boolean] if the target can be propagated to
|
|
186
|
-
def appropriate_target? propagation_node, target
|
|
187
|
-
# special handle Returns b/c we can do unfreezing magic during propagation
|
|
188
|
-
return true if propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
|
|
189
|
-
|
|
190
|
-
Contrast::Agent::Assess::Tracker.trackable?(target)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
79
|
# If this patcher has tags, apply them to the entire target
|
|
194
80
|
# @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
|
|
195
81
|
# propagation event.
|
|
@@ -95,8 +95,8 @@ module Contrast
|
|
|
95
95
|
|
|
96
96
|
def needs_object?
|
|
97
97
|
if @_needs_object.nil?
|
|
98
|
-
@_needs_object = action == Contrast::
|
|
99
|
-
action == Contrast::
|
|
98
|
+
@_needs_object = action == Contrast::Utils::Assess::PropagationMethodUtils::CUSTOM_ACTION ||
|
|
99
|
+
action == Contrast::Utils::Assess::PropagationMethodUtils::DB_WRITE_ACTION ||
|
|
100
100
|
sources.any?(Contrast::Utils::ObjectShare::OBJECT_KEY) ||
|
|
101
101
|
targets.any?(Contrast::Utils::ObjectShare::OBJECT_KEY)
|
|
102
102
|
end
|
|
@@ -105,8 +105,8 @@ module Contrast
|
|
|
105
105
|
|
|
106
106
|
def needs_args?
|
|
107
107
|
if @_needs_args.nil?
|
|
108
|
-
@_needs_args = action == Contrast::
|
|
109
|
-
action == Contrast::
|
|
108
|
+
@_needs_args = action == Contrast::Utils::Assess::PropagationMethodUtils::CUSTOM_ACTION ||
|
|
109
|
+
action == Contrast::Utils::Assess::PropagationMethodUtils::DB_WRITE_ACTION ||
|
|
110
110
|
sources.any? { |source| source.is_a?(Integer) || source.is_a?(Symbol) } ||
|
|
111
111
|
targets.any? { |target| target.is_a?(Integer) || target.is_a?(Symbol) }
|
|
112
112
|
end
|
|
@@ -24,7 +24,7 @@ module Contrast
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# find original in the target, copy tags to the new position in target
|
|
27
|
-
original_start_index = target[0..target.length / 2 + 1].rindex(source1)
|
|
27
|
+
original_start_index = target[0..(target.length / 2) + 1].rindex(source1)
|
|
28
28
|
original_start_index ||= 1
|
|
29
29
|
properties.copy_from(source1, target, original_start_index, propagation_node.untags)
|
|
30
30
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require 'contrast/components/logger'
|
|
5
5
|
require 'contrast/utils/duck_utils'
|
|
6
|
+
require 'contrast/utils/substitution_utils'
|
|
6
7
|
|
|
7
8
|
module Contrast
|
|
8
9
|
module Agent
|
|
@@ -17,9 +18,7 @@ module Contrast
|
|
|
17
18
|
class Substitution
|
|
18
19
|
include Contrast::Components::Logger::InstanceMethods
|
|
19
20
|
extend Contrast::Components::Logger::InstanceMethods
|
|
20
|
-
|
|
21
|
-
CAPTURE_GROUP_REGEXP = /\\[[:digit:]]/.cs__freeze
|
|
22
|
-
CAPTURE_NAME_REGEXP = /\\k<[[:alpha:]]/.cs__freeze
|
|
21
|
+
extend Contrast::Utils::SubstitutionUtils
|
|
23
22
|
|
|
24
23
|
class << self
|
|
25
24
|
# gsub is hard. there are four versions of this method
|
|
@@ -38,157 +37,6 @@ module Contrast
|
|
|
38
37
|
def sub_tagger patcher, preshift, ret, block
|
|
39
38
|
substitution_tagger(patcher, preshift, ret, !block.nil?, false)
|
|
40
39
|
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def substitution_tagger patcher, preshift, ret, block, global = true
|
|
45
|
-
return ret unless ret
|
|
46
|
-
|
|
47
|
-
begin
|
|
48
|
-
source = preshift.object
|
|
49
|
-
self_tracked = Contrast::Agent::Assess::Tracker.tracked?(source)
|
|
50
|
-
args = preshift.args[1]
|
|
51
|
-
incoming_tracked = args && determine_tracked(args)
|
|
52
|
-
return ret unless self_tracked || incoming_tracked
|
|
53
|
-
|
|
54
|
-
parent_events = []
|
|
55
|
-
if block
|
|
56
|
-
block_sub(self_tracked, source, ret)
|
|
57
|
-
elsif args.is_a?(String)
|
|
58
|
-
string_sub(parent_events, self_tracked, preshift, ret, args, incoming_tracked, global)
|
|
59
|
-
elsif args.is_a?(Hash)
|
|
60
|
-
hash_sub(self_tracked, source, ret)
|
|
61
|
-
else # Enumerator, only for gsub
|
|
62
|
-
pattern_gsub(parent_events, preshift, ret)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
if self_tracked
|
|
66
|
-
source_properties = Contrast::Agent::Assess::Tracker.properties(source)
|
|
67
|
-
parent_event = source_properties&.event
|
|
68
|
-
parent_events.prepend(parent_event) if parent_event
|
|
69
|
-
end
|
|
70
|
-
string_build_event(parent_events, patcher, preshift, ret)
|
|
71
|
-
rescue StandardError => e
|
|
72
|
-
logger.error('Unable to apply gsub propagator', e)
|
|
73
|
-
end
|
|
74
|
-
ret
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def determine_tracked args
|
|
78
|
-
# if there's no arg, it can't be tracked
|
|
79
|
-
return false unless args
|
|
80
|
-
|
|
81
|
-
# if it's a string, just ask if it's tracked
|
|
82
|
-
case args
|
|
83
|
-
when String
|
|
84
|
-
Contrast::Agent::Assess::Tracker.tracked?(args)
|
|
85
|
-
# if it's a hash, ask if it has a tracked string
|
|
86
|
-
when Hash
|
|
87
|
-
args.values.any? { |value| value.is_a?(String) && Contrast::Agent::Assess::Tracker.tracked?(value) }
|
|
88
|
-
# this should never happen
|
|
89
|
-
else
|
|
90
|
-
false
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def string_sub parent_events, self_tracked, preshift, ret, incoming, incoming_tracked, global
|
|
95
|
-
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
|
|
96
|
-
|
|
97
|
-
incoming_properties = Contrast::Agent::Assess::Tracker.properties(incoming)
|
|
98
|
-
parent_events << incoming_properties&.event if incoming_properties&.event
|
|
99
|
-
|
|
100
|
-
source = preshift.object
|
|
101
|
-
|
|
102
|
-
# We can't efficiently find the places that things were
|
|
103
|
-
# copied from regexp / captures. Trading accuracy for
|
|
104
|
-
# performance
|
|
105
|
-
if incoming.match?(CAPTURE_GROUP_REGEXP) || incoming.match?(CAPTURE_NAME_REGEXP)
|
|
106
|
-
properties.splat_from(source, ret) if self_tracked
|
|
107
|
-
return
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# if it's just a straight insert, that we can do
|
|
111
|
-
# Copy the tags from us to the return
|
|
112
|
-
ranges = find_string_sub_insert(properties, preshift, incoming, ret, global)
|
|
113
|
-
|
|
114
|
-
properties.delete_tags_at_ranges(ranges)
|
|
115
|
-
properties.shift_tags(ranges)
|
|
116
|
-
return unless incoming_tracked
|
|
117
|
-
return unless incoming_properties
|
|
118
|
-
|
|
119
|
-
tags = incoming_properties.tag_keys
|
|
120
|
-
ranges.each do |range|
|
|
121
|
-
tags.each do |tag|
|
|
122
|
-
properties.add_tag(tag, range)
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Find the points at which the new String was placed into the original
|
|
128
|
-
#
|
|
129
|
-
# @param properties [Contrast::Agent::Assess::Properties] the Properties of the ret
|
|
130
|
-
# @param preshift [Contrast::Agent::Assess::PreShift] the capture of the state of the code just prior to
|
|
131
|
-
# the invocation of the patched method
|
|
132
|
-
# @param incoming [String] the new String going into the substitution
|
|
133
|
-
# @param ret [String] the result of the substitution
|
|
134
|
-
# @param global [Boolean] if this was a global or single substitution
|
|
135
|
-
# @return [Array<Range>] the Ranges where substitution occurred
|
|
136
|
-
def find_string_sub_insert properties, preshift, incoming, ret, global
|
|
137
|
-
pattern = preshift.args[0]
|
|
138
|
-
source = preshift.object
|
|
139
|
-
|
|
140
|
-
properties.copy_from(source, ret)
|
|
141
|
-
# Figure out where inserts occurred
|
|
142
|
-
last_idx = 0
|
|
143
|
-
ranges = []
|
|
144
|
-
# For each insert, move the tag ranges
|
|
145
|
-
while last_idx
|
|
146
|
-
idx = source.index(pattern, last_idx)
|
|
147
|
-
break unless idx
|
|
148
|
-
|
|
149
|
-
last_idx = idx ? idx + 1 : nil
|
|
150
|
-
start_index = idx
|
|
151
|
-
end_index = idx + incoming.length
|
|
152
|
-
ranges << (start_index...end_index)
|
|
153
|
-
break unless global
|
|
154
|
-
end
|
|
155
|
-
ranges
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def block_sub self_tracked, source, ret
|
|
159
|
-
return unless self_tracked
|
|
160
|
-
|
|
161
|
-
properties = Contrast::Agent::Assess::Tracker.properties!(ret)
|
|
162
|
-
properties&.splat_from(source, ret)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def hash_sub self_tracked, source, ret
|
|
166
|
-
return unless self_tracked
|
|
167
|
-
|
|
168
|
-
properties = Contrast::Agent::Assess::Tracker.properties!(ret)
|
|
169
|
-
properties&.splat_from(source, ret)
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def pattern_gsub parent_events, preshift, ret
|
|
173
|
-
source = preshift.object
|
|
174
|
-
return unless (source_properties = Contrast::Agent::Assess::Tracker.properties(source))
|
|
175
|
-
return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
|
|
176
|
-
|
|
177
|
-
source_properties.tag_keys.each do |key|
|
|
178
|
-
properties.add_tag(key, 0...1)
|
|
179
|
-
end
|
|
180
|
-
parent_event = source_properties.event
|
|
181
|
-
parent_events << parent_event if parent_event
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def string_build_event parent_events, patcher, preshift, ret
|
|
185
|
-
return unless Contrast::Agent::Assess::Tracker.tracked?(ret)
|
|
186
|
-
|
|
187
|
-
properties = Contrast::Agent::Assess::Tracker.properties(ret)
|
|
188
|
-
args = preshift.args
|
|
189
|
-
properties.build_event(patcher, ret, preshift.object, ret, args, 2)
|
|
190
|
-
properties.event.instance_variable_set(:@_parent_events, parent_events)
|
|
191
|
-
end
|
|
192
40
|
end
|
|
193
41
|
end
|
|
194
42
|
end
|