contrast-agent 4.12.0 → 4.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +1 -0
  3. data/ext/cs__assess_module/cs__assess_module.c +48 -0
  4. data/ext/cs__assess_module/cs__assess_module.h +7 -0
  5. data/ext/cs__common/cs__common.c +5 -0
  6. data/ext/cs__common/cs__common.h +8 -0
  7. data/ext/cs__contrast_patch/cs__contrast_patch.c +16 -1
  8. data/ext/cs__os_information/cs__os_information.c +31 -0
  9. data/ext/cs__os_information/cs__os_information.h +7 -0
  10. data/ext/cs__os_information/extconf.rb +5 -0
  11. data/lib/contrast/agent/assess/policy/policy_node.rb +6 -6
  12. data/lib/contrast/agent/assess/policy/policy_scanner.rb +5 -0
  13. data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
  14. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  15. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -1
  16. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +2 -154
  17. data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
  18. data/lib/contrast/agent/assess/policy/trigger_method.rb +45 -110
  19. data/lib/contrast/agent/assess/policy/trigger_node.rb +14 -6
  20. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +1 -1
  21. data/lib/contrast/agent/assess/property/tagged.rb +53 -185
  22. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +40 -6
  23. data/lib/contrast/agent/deadzone/policy/policy.rb +1 -1
  24. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -0
  25. data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
  26. data/lib/contrast/agent/middleware.rb +14 -62
  27. data/lib/contrast/agent/patching/policy/method_policy.rb +3 -89
  28. data/lib/contrast/agent/patching/policy/method_policy_extend.rb +111 -0
  29. data/lib/contrast/agent/patching/policy/patch.rb +28 -235
  30. data/lib/contrast/agent/patching/policy/patcher.rb +14 -49
  31. data/lib/contrast/agent/reporting/report.rb +21 -0
  32. data/lib/contrast/agent/reporting/reporter.rb +142 -0
  33. data/lib/contrast/agent/reporting/reporting_events/finding.rb +90 -0
  34. data/lib/contrast/agent/reporting/reporting_events/preflight.rb +25 -0
  35. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +56 -0
  36. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +37 -0
  37. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +127 -0
  38. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +168 -0
  39. data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +66 -0
  40. data/lib/contrast/agent/request.rb +2 -81
  41. data/lib/contrast/agent/request_context.rb +4 -128
  42. data/lib/contrast/agent/request_context_extend.rb +138 -0
  43. data/lib/contrast/agent/request_handler.rb +7 -3
  44. data/lib/contrast/agent/response.rb +2 -73
  45. data/lib/contrast/agent/startup_metrics_telemetry_event.rb +94 -0
  46. data/lib/contrast/agent/static_analysis.rb +5 -3
  47. data/lib/contrast/agent/telemetry.rb +137 -0
  48. data/lib/contrast/agent/telemetry_event.rb +33 -0
  49. data/lib/contrast/agent/thread_watcher.rb +66 -11
  50. data/lib/contrast/agent/version.rb +1 -1
  51. data/lib/contrast/agent.rb +21 -0
  52. data/lib/contrast/api/communication/connection_status.rb +10 -7
  53. data/lib/contrast/api/communication/messaging_queue.rb +37 -3
  54. data/lib/contrast/api/communication/response_processor.rb +15 -8
  55. data/lib/contrast/api/communication/service_lifecycle.rb +13 -3
  56. data/lib/contrast/api/communication/socket.rb +6 -8
  57. data/lib/contrast/api/communication/socket_client.rb +29 -12
  58. data/lib/contrast/api/communication/speedracer.rb +37 -1
  59. data/lib/contrast/api/communication/tcp_socket.rb +4 -3
  60. data/lib/contrast/api/communication/unix_socket.rb +1 -0
  61. data/lib/contrast/api/decorators/finding.rb +45 -0
  62. data/lib/contrast/components/api.rb +90 -0
  63. data/lib/contrast/components/app_context.rb +10 -41
  64. data/lib/contrast/components/app_context_extend.rb +78 -0
  65. data/lib/contrast/components/base.rb +23 -0
  66. data/lib/contrast/components/config.rb +92 -13
  67. data/lib/contrast/components/contrast_service.rb +11 -0
  68. data/lib/contrast/components/sampling.rb +2 -2
  69. data/lib/contrast/config/agent_configuration.rb +1 -1
  70. data/lib/contrast/config/api_configuration.rb +27 -0
  71. data/lib/contrast/config/api_proxy_configuration.rb +14 -0
  72. data/lib/contrast/config/application_configuration.rb +2 -3
  73. data/lib/contrast/config/assess_configuration.rb +3 -3
  74. data/lib/contrast/config/base_configuration.rb +17 -28
  75. data/lib/contrast/config/certification_configuration.rb +15 -0
  76. data/lib/contrast/config/env_variables.rb +18 -0
  77. data/lib/contrast/config/heap_dump_configuration.rb +6 -6
  78. data/lib/contrast/config/inventory_configuration.rb +1 -5
  79. data/lib/contrast/config/protect_rule_configuration.rb +1 -1
  80. data/lib/contrast/config/request_audit_configuration.rb +18 -0
  81. data/lib/contrast/config/root_configuration.rb +1 -0
  82. data/lib/contrast/config/ruby_configuration.rb +6 -6
  83. data/lib/contrast/config/service_configuration.rb +2 -2
  84. data/lib/contrast/config.rb +1 -1
  85. data/lib/contrast/configuration.rb +4 -2
  86. data/lib/contrast/extension/assess/array.rb +5 -7
  87. data/lib/contrast/extension/thread.rb +31 -12
  88. data/lib/contrast/framework/manager.rb +22 -44
  89. data/lib/contrast/framework/manager_extend.rb +50 -0
  90. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
  91. data/lib/contrast/framework/rails/patch/support.rb +31 -29
  92. data/lib/contrast/framework/rails/railtie.rb +1 -1
  93. data/lib/contrast/framework/sinatra/support.rb +2 -1
  94. data/lib/contrast/logger/application.rb +4 -0
  95. data/lib/contrast/logger/log.rb +8 -103
  96. data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
  97. data/lib/contrast/utils/assess/property/tagged_utils.rb +165 -0
  98. data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
  99. data/lib/contrast/utils/assess/tracking_util.rb +20 -15
  100. data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
  101. data/lib/contrast/utils/class_util.rb +18 -14
  102. data/lib/contrast/utils/exclude_key.rb +20 -0
  103. data/lib/contrast/utils/findings.rb +62 -0
  104. data/lib/contrast/utils/hash_digest.rb +10 -73
  105. data/lib/contrast/utils/hash_digest_extend.rb +86 -0
  106. data/lib/contrast/utils/head_dump_utils_extend.rb +74 -0
  107. data/lib/contrast/utils/heap_dump_util.rb +2 -65
  108. data/lib/contrast/utils/invalid_configuration_util.rb +29 -0
  109. data/lib/contrast/utils/io_util.rb +1 -1
  110. data/lib/contrast/utils/log_utils.rb +108 -0
  111. data/lib/contrast/utils/metrics_hash.rb +59 -0
  112. data/lib/contrast/utils/middleware_utils.rb +87 -0
  113. data/lib/contrast/utils/net_http_base.rb +158 -0
  114. data/lib/contrast/utils/object_share.rb +1 -0
  115. data/lib/contrast/utils/os.rb +23 -0
  116. data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
  117. data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
  118. data/lib/contrast/utils/request_utils.rb +88 -0
  119. data/lib/contrast/utils/response_utils.rb +97 -0
  120. data/lib/contrast/utils/substitution_utils.rb +167 -0
  121. data/lib/contrast/utils/tag_util.rb +9 -9
  122. data/lib/contrast/utils/telemetry.rb +79 -0
  123. data/lib/contrast/utils/telemetry_client.rb +90 -0
  124. data/lib/contrast/utils/telemetry_identifier.rb +130 -0
  125. data/lib/contrast.rb +18 -0
  126. data/ruby-agent.gemspec +7 -6
  127. data/service_executables/VERSION +1 -1
  128. data/service_executables/linux/contrast-service +0 -0
  129. data/service_executables/mac/contrast-service +0 -0
  130. metadata +69 -22
  131. data/lib/contrast/config/default_value.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a0a50913c5c680462f9afd79070ca1f3d46d09d66ffdb943b1dc778f84830d6
4
- data.tar.gz: bb3254c6a2cdc2b4add4e09b4bf1ed63c6a640f0dd82824db2769b911aacf96b
3
+ metadata.gz: 8ab9eae65aeefbeb3c12102f71287c60856783238067a7c461fcf534aaa65712
4
+ data.tar.gz: 1fe74d85374374f40a11c47e44ab0da1042a3f99d607fdfd697a45cc321b45b0
5
5
  SHA512:
6
- metadata.gz: 78d5e66ee3ab7408e94348f6c2998e73cd78227a8b75cffa7add261b771092d470431ce8e44357dfb7d1a178e03e5033efc9f7a4c974a979f9dea88b2c55a3ec
7
- data.tar.gz: d20f41b563778d84e63e4db7a9399e666a85fe1a2343772258f5248cd139f02e6a6bea33e51d9e5a0f954b1e2b4683ec08f3db7a52ab5a6990f7d725d2bc3dea
6
+ metadata.gz: df020b9d0ff09e61107e280f6e76411f9a9b75c1c94f7082ef52d487e5f88b940e75996dd869d648ac0af654f5f84e48b1f691fdcc9e5f294a565d2bb1a39b7d
7
+ data.tar.gz: 8bd869bfacbb82aea7b0cf55ff806fded4bae5c6b0454185c4c422bdd14ee07fd1edb3f4b843e702765a4693b51697a0497129d73d3d3652f32cc50056ed783b
data/.simplecov CHANGED
@@ -4,5 +4,6 @@
4
4
  SimpleCov.minimum_coverage line: 94.75
5
5
  SimpleCov.start do
6
6
  add_filter '/spec/'
7
+ add_filter '/lib/contrast/extension/assess/erb.rb'
7
8
  enable_coverage :branch
8
9
  end
@@ -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);
@@ -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"));
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ #include <ruby.h>
2
+
3
+ extern VALUE contrast, utils, os;
4
+
5
+ VALUE contrast_get_system_information();
6
+
7
+ void Init_cs__os_information(void);
@@ -0,0 +1,5 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ $TO_MAKE = File.basename(__dir__)
5
+ require_relative '../extconf_common'
@@ -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
- when Contrast::Utils::ObjectShare::OBJECT_KEY,
138
+ converted << case t
139
+ when Contrast::Utils::ObjectShare::OBJECT_KEY,
140
140
  Contrast::Utils::ObjectShare::RETURN_KEY
141
141
 
142
- converted << t
143
- else
144
- converted << Integer(t[1..-1])
145
- end
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::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION ||
99
- action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION ||
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::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION ||
109
- action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION ||
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