contrast-agent 4.9.0 → 4.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rspec_parallel +6 -0
  4. data/ext/cs__common/cs__common.c +19 -7
  5. data/ext/cs__common/cs__common.h +4 -2
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +32 -11
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -4
  8. data/lib/contrast/agent/assess/contrast_event.rb +1 -2
  9. data/lib/contrast/agent/assess/contrast_object.rb +1 -4
  10. data/lib/contrast/agent/assess/finalizers/hash.rb +0 -1
  11. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
  12. data/lib/contrast/agent/assess/policy/patcher.rb +0 -1
  13. data/lib/contrast/agent/assess/policy/policy_scanner.rb +0 -2
  14. data/lib/contrast/agent/assess/policy/preshift.rb +29 -12
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +100 -57
  16. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -2
  17. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
  18. data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
  19. data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -2
  20. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -0
  21. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +0 -1
  22. data/lib/contrast/agent/assess/policy/source_method.rb +13 -17
  23. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -1
  24. data/lib/contrast/agent/assess/policy/trigger_method.rb +60 -85
  25. data/lib/contrast/agent/assess/policy/trigger_node.rb +52 -19
  26. data/lib/contrast/agent/assess/property/evented.rb +2 -1
  27. data/lib/contrast/agent/assess/property/tagged.rb +34 -25
  28. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +0 -1
  29. data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
  30. data/lib/contrast/agent/disable_reaction.rb +1 -1
  31. data/lib/contrast/agent/exclusion_matcher.rb +0 -4
  32. data/lib/contrast/agent/inventory/database_config.rb +117 -0
  33. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +6 -5
  34. data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
  35. data/lib/contrast/agent/middleware.rb +1 -0
  36. data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -0
  37. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +17 -12
  38. data/lib/contrast/agent/patching/policy/method_policy.rb +54 -9
  39. data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
  40. data/lib/contrast/agent/patching/policy/patch.rb +17 -6
  41. data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
  42. data/lib/contrast/agent/patching/policy/patcher.rb +9 -9
  43. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
  44. data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
  45. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
  46. data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
  47. data/lib/contrast/agent/reaction_processor.rb +1 -1
  48. data/lib/contrast/agent/request.rb +9 -4
  49. data/lib/contrast/agent/request_context.rb +51 -33
  50. data/lib/contrast/agent/rule_set.rb +2 -4
  51. data/lib/contrast/agent/scope.rb +32 -20
  52. data/lib/contrast/agent/static_analysis.rb +1 -1
  53. data/lib/contrast/agent/tracepoint_hook.rb +16 -3
  54. data/lib/contrast/agent/version.rb +1 -1
  55. data/lib/contrast/agent.rb +0 -1
  56. data/lib/contrast/api/communication/messaging_queue.rb +12 -6
  57. data/lib/contrast/api/communication/service_lifecycle.rb +4 -1
  58. data/lib/contrast/api/communication/socket_client.rb +4 -4
  59. data/lib/contrast/api/decorators/agent_startup.rb +4 -4
  60. data/lib/contrast/api/decorators/application_startup.rb +6 -5
  61. data/lib/contrast/api/decorators/route_coverage.rb +24 -1
  62. data/lib/contrast/components/agent.rb +5 -2
  63. data/lib/contrast/components/assess.rb +13 -3
  64. data/lib/contrast/components/base.rb +2 -2
  65. data/lib/contrast/components/config.rb +1 -0
  66. data/lib/contrast/components/contrast_service.rb +4 -2
  67. data/lib/contrast/components/logger.rb +13 -8
  68. data/lib/contrast/components/scope.rb +9 -28
  69. data/lib/contrast/config/assess_configuration.rb +1 -0
  70. data/lib/contrast/config/base_configuration.rb +14 -6
  71. data/lib/contrast/configuration.rb +19 -15
  72. data/lib/contrast/extension/assess/array.rb +1 -11
  73. data/lib/contrast/extension/assess/eval_trigger.rb +0 -20
  74. data/lib/contrast/extension/assess/fiber.rb +0 -11
  75. data/lib/contrast/extension/assess/hash.rb +0 -10
  76. data/lib/contrast/extension/assess/kernel.rb +1 -10
  77. data/lib/contrast/extension/assess/marshal.rb +3 -11
  78. data/lib/contrast/extension/assess/regexp.rb +0 -11
  79. data/lib/contrast/extension/assess/string.rb +1 -26
  80. data/lib/contrast/extension/extension.rb +61 -0
  81. data/lib/contrast/framework/grape/support.rb +174 -0
  82. data/lib/contrast/framework/manager.rb +42 -6
  83. data/lib/contrast/framework/rack/support.rb +1 -1
  84. data/lib/contrast/framework/rails/patch/assess_configuration.rb +0 -1
  85. data/lib/contrast/framework/rails/patch/support.rb +6 -3
  86. data/lib/contrast/framework/rails/railtie.rb +1 -1
  87. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +1 -0
  88. data/lib/contrast/framework/rails/support.rb +60 -13
  89. data/lib/contrast/framework/sinatra/support.rb +1 -1
  90. data/lib/contrast/logger/log.rb +89 -15
  91. data/lib/contrast/tasks/config.rb +0 -1
  92. data/lib/contrast/utils/class_util.rb +58 -44
  93. data/lib/contrast/utils/io_util.rb +43 -35
  94. data/lib/contrast/utils/lru_cache.rb +45 -0
  95. data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
  96. data/lib/contrast/utils/tag_util.rb +2 -1
  97. data/lib/contrast.rb +1 -1
  98. data/resources/assess/policy.json +208 -7
  99. data/resources/deadzone/policy.json +91 -0
  100. data/ruby-agent.gemspec +10 -2
  101. data/service_executables/VERSION +1 -1
  102. data/service_executables/linux/contrast-service +0 -0
  103. data/service_executables/mac/contrast-service +0 -0
  104. metadata +74 -26
  105. data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
  106. data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
  107. data/ext/cs__protect_kernel/extconf.rb +0 -5
  108. data/lib/contrast/extension/protect/kernel.rb +0 -39
  109. data/lib/contrast/utils/inventory_util.rb +0 -113
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04fe75ff7b610b12a7c4e3416830e0362dcc272346a2591dee97d36b08fe4d20
4
- data.tar.gz: 319c8eb0cf2377f735d9ba9318ec518e10c9986c7674bf652885ff73c40423fb
3
+ metadata.gz: 6a0a50913c5c680462f9afd79070ca1f3d46d09d66ffdb943b1dc778f84830d6
4
+ data.tar.gz: bb3254c6a2cdc2b4add4e09b4bf1ed63c6a640f0dd82824db2769b911aacf96b
5
5
  SHA512:
6
- metadata.gz: 66f782973a14ee44b732f3fb17d4a40ac072ca6b78627c72f343bf5a4902d5d9dcf95aa92249f8659cb3214fb765828834507196f6bcb8787eece732a7242795
7
- data.tar.gz: 5a9578db89d3d2a6f1e904fe3e248d5577d1db30a4d81049d4552fa6c0a3733638cbe9862d3f29f5979bad718add38b6d579541df40780e3746c1990bbe4f882
6
+ metadata.gz: 78d5e66ee3ab7408e94348f6c2998e73cd78227a8b75cffa7add261b771092d470431ce8e44357dfb7d1a178e03e5033efc9f7a4c974a979f9dea88b2c55a3ec
7
+ data.tar.gz: d20f41b563778d84e63e4db7a9399e666a85fe1a2343772258f5248cd139f02e6a6bea33e51d9e5a0f954b1e2b4683ec08f3db7a52ab5a6990f7d725d2bc3dea
data/.rspec CHANGED
@@ -3,4 +3,3 @@
3
3
  --format documentation
4
4
  --format RspecJunitFormatter
5
5
  --out ./test-results/results.xml
6
- --color
data/.rspec_parallel ADDED
@@ -0,0 +1,6 @@
1
+ --require spec_helper
2
+ --order rand
3
+ --format progress
4
+ --format RspecJunitFormatter
5
+ --out ./test-results/results.xml
6
+ --format ParallelTests::RSpec::FailuresLogger --out tmp/failing_specs.log
@@ -73,11 +73,20 @@ VALUE contrast_register_singleton_patch(const char *module_name,
73
73
  IMPL_ALIAS_SINGLETON);
74
74
  }
75
75
 
76
- VALUE contrast_register_singleton_prepend_patch(
77
- const char *module_name, const char *method_name,
78
- VALUE(c_fn)(const int, VALUE *, const VALUE)) {
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
- IMPL_PREPEND);
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 IMPL_PREPEND:
126
- impl = ID2SYM(rb_sym_prepend);
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
- rb_sym_prepend = rb_intern("prepend");
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");
@@ -6,7 +6,8 @@
6
6
  typedef enum {
7
7
  IMPL_ALIAS_INSTANCE,
8
8
  IMPL_ALIAS_SINGLETON,
9
- IMPL_PREPEND
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 rb_sym_prepend;
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 IMPL_PREPEND:
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 IMPL_PREPEND:
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 IMPL_PREPEND:
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 contrast_prepend_patch(const int argc, const VALUE *argv,
347
+ VALUE contrast_prepend_instance_patch(const int argc, const VALUE *argv,
342
348
  const VALUE object) {
343
- return contrast_patch_dispatch(argc, argv, IMPL_PREPEND, object);
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
- rb_funcall(patch_status, rb_sym_set_info_for, 5, originalModule,
413
- original_method_name, method_policy, Qtrue, Qnil);
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
- contrast_prepend_patch, -1);
442
+ contrast_prepend_instance_patch, -1);
421
443
  } else {
422
- rb_define_method(module, cMethodName, contrast_prepend_patch, -1);
444
+ rb_define_method(module, cMethodName, contrast_prepend_instance_patch, -1);
423
445
  }
424
446
  } else {
425
- rb_define_singleton_method(module, cMethodName, contrast_prepend_patch,
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 contrast_prepend_patch(const int argc, const VALUE *argv,
152
- const VALUE object);
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)&.tags
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
- # TODO: RUBY-1084 determine if we need to copy these tags to
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)
@@ -20,7 +20,6 @@ module Contrast
20
20
  extend Contrast::Components::Logger::InstanceMethods
21
21
  extend Contrast::Components::Scope::InstanceMethods
22
22
 
23
-
24
23
  class << self
25
24
  def policy
26
25
  Contrast::Agent::Assess::Policy::Policy.instance
@@ -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 = can ? object.dup : 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
- preshift.args = args.dup
87
- preshift.args.each_with_index do |preshift_arg, index|
88
- original_arg = args[index]
89
- next if preshift_arg.__id__ == original_arg.__id__
90
- next unless Contrast::Agent::Assess::Tracker.tracked?(original_arg)
91
-
92
- Contrast::Agent::Assess::Tracker.copy(original_arg, preshift_arg)
93
- end
94
- preshift.arg_lengths = preshift.args.map do |preshift_arg|
95
- Contrast::Utils::DuckUtils.quacks_to?(preshift_arg, :length) ? preshift_arg.length : 0
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
- # Propagator is any method that transforms an untrusted value. In
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
- # the policy that governs the patches to this method
53
- # @param preshift [Contrast::Agent::Assess::PreShift] The capture
54
- # of the state of the code just prior to the invocation of the
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
- # were made; will replace the return of the original function if
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
- # propagation was. Now that we know, we'll actually do things to
91
- # it. Note that the return of this method will replace the original
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
- # the node that governs this propagation event.
97
- # @param preshift [Contrast::Agent::Assess::PreShift] The capture
98
- # of the state of the code just prior to the invocation of the
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
- # were made; will replace the return of the original function if
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
- # Often, the method has to make decisions about the target based on
132
- # the context with which the method was called. As such, defer
133
- # determining if the target is valid to that method.
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
- # In all other cases, a target is valid for propagation if it is not
136
- # nil
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
- # one, the length is not valid
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
- # If the source of this patcher is not tracked, there's no need to do
158
- # anything. A copy of nothing is still nothing.
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 # has to be P, there's no ret source type (yet? ever?)
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
- # to work with our property tracking, unless they're duplicable and
177
- # the return.
178
- # We probably shouldn't propagate to frozen things at all, as
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
- # the node that governs this propagation event.
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
- # were included in untags, the target may have already had some on
271
- # it. Let's go ahead and remove them.
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? && propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
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