contrast-agent 4.10.0 → 4.13.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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_module/cs__assess_module.c +48 -0
  3. data/ext/cs__assess_module/cs__assess_module.h +7 -0
  4. data/ext/cs__common/cs__common.c +24 -7
  5. data/ext/cs__common/cs__common.h +12 -2
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +48 -11
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -2
  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__protect_kernel → cs__os_information}/extconf.rb +0 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +1 -1
  12. data/lib/contrast/agent/assess/contrast_object.rb +1 -4
  13. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
  14. data/lib/contrast/agent/assess/policy/preshift.rb +25 -11
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
  16. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  17. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -0
  18. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +4 -4
  19. data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
  20. data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
  21. data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -107
  22. data/lib/contrast/agent/assess/policy/trigger_node.rb +52 -19
  23. data/lib/contrast/agent/assess/property/tagged.rb +15 -132
  24. data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
  25. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +2 -1
  26. data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
  27. data/lib/contrast/agent/middleware.rb +22 -0
  28. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +0 -1
  29. data/lib/contrast/agent/patching/policy/method_policy.rb +54 -9
  30. data/lib/contrast/agent/patching/policy/patch.rb +37 -238
  31. data/lib/contrast/agent/patching/policy/patcher.rb +3 -42
  32. data/lib/contrast/agent/request.rb +5 -3
  33. data/lib/contrast/agent/request_context.rb +32 -11
  34. data/lib/contrast/agent/request_handler.rb +7 -3
  35. data/lib/contrast/agent/rule_set.rb +2 -4
  36. data/lib/contrast/agent/scope.rb +32 -20
  37. data/lib/contrast/agent/startup_metrics_telemetry_event.rb +71 -0
  38. data/lib/contrast/agent/static_analysis.rb +4 -2
  39. data/lib/contrast/agent/telemetry.rb +129 -0
  40. data/lib/contrast/agent/telemetry_event.rb +34 -0
  41. data/lib/contrast/agent/thread_watcher.rb +43 -14
  42. data/lib/contrast/agent/tracepoint_hook.rb +11 -3
  43. data/lib/contrast/agent/version.rb +1 -1
  44. data/lib/contrast/agent.rb +6 -1
  45. data/lib/contrast/components/api.rb +34 -0
  46. data/lib/contrast/components/app_context.rb +24 -0
  47. data/lib/contrast/components/assess.rb +7 -0
  48. data/lib/contrast/components/config.rb +90 -11
  49. data/lib/contrast/components/contrast_service.rb +6 -0
  50. data/lib/contrast/config/api_configuration.rb +22 -0
  51. data/lib/contrast/config/assess_configuration.rb +1 -0
  52. data/lib/contrast/config/env_variables.rb +25 -0
  53. data/lib/contrast/config/root_configuration.rb +1 -0
  54. data/lib/contrast/config/service_configuration.rb +2 -1
  55. data/lib/contrast/config.rb +1 -0
  56. data/lib/contrast/configuration.rb +3 -0
  57. data/lib/contrast/framework/manager.rb +14 -12
  58. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
  59. data/lib/contrast/framework/rails/patch/support.rb +31 -29
  60. data/lib/contrast/logger/application.rb +4 -0
  61. data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
  62. data/lib/contrast/utils/assess/property/tagged_utils.rb +142 -0
  63. data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
  64. data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
  65. data/lib/contrast/utils/class_util.rb +58 -44
  66. data/lib/contrast/utils/exclude_key.rb +20 -0
  67. data/lib/contrast/utils/io_util.rb +42 -34
  68. data/lib/contrast/utils/lru_cache.rb +45 -0
  69. data/lib/contrast/utils/metrics_hash.rb +59 -0
  70. data/lib/contrast/utils/os.rb +23 -0
  71. data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
  72. data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
  73. data/lib/contrast/utils/requests_client.rb +150 -0
  74. data/lib/contrast/utils/ruby_ast_rewriter.rb +1 -1
  75. data/lib/contrast/utils/telemetry.rb +77 -0
  76. data/lib/contrast/utils/telemetry_identifier.rb +137 -0
  77. data/lib/contrast.rb +19 -1
  78. data/resources/assess/policy.json +12 -6
  79. data/resources/deadzone/policy.json +86 -5
  80. data/ruby-agent.gemspec +2 -1
  81. data/service_executables/VERSION +1 -1
  82. data/service_executables/linux/contrast-service +0 -0
  83. data/service_executables/mac/contrast-service +0 -0
  84. metadata +32 -14
  85. data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
  86. data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
  87. data/lib/contrast/extension/protect/kernel.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab65fd574ef84fbe339e4f4d4bf340623235a442ecc11d7ba28c6af3c6f04d4d
4
- data.tar.gz: 0fa9cc44fe85713ee924101139e615d361de6d906c8f5ff4fdd345a930eee9d2
3
+ metadata.gz: 96cff410085a542c1003dda242268f349bc20f738b82bde70725b695661a79bc
4
+ data.tar.gz: 2a5d703693f697785034df1f602ef0f61241ae16edf11dbd174ad9a8da01b72c
5
5
  SHA512:
6
- metadata.gz: cb21ebcad0e772649d16a25d2f00cc057ee10df71638b7bd7cc6a4710a1ded28ce4dca749fa040ba2fb78e992727f4cc5d5f38e1187c363a8ea33a12333fc289
7
- data.tar.gz: c3ad4c82a9e25ecb58c0ea906f1170cffdd6811952f9c1d71a6c75166358a37e0ebba40e55e9a7433156a51122b9ca1450bb5a4a2a87f9081a4a4268d9abfdb4
6
+ metadata.gz: 409632acc3352b7c172a1aa12c9f5de482b5e5c3c0307b0732c48c7b8cf8d56c5ef5a4c039a0c866dd0c1ea31340b21ee8e4bcc0f2c4c4fdea9cd4fffcbaa212
7
+ data.tar.gz: 17b56ee4355a472b637cf1f9059f1bfce01319351713fc3b35e744bc3afad3d4f068cf77dd474032193249cb8f178141d7d7da2aa7b4d2f3a0cc2158e1c55d0c
@@ -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);
@@ -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)) {
79
80
  return _contrast_register_patch(module_name, method_name, c_fn,
80
- IMPL_PREPEND);
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)) {
88
+ return _contrast_register_patch(module_name, method_name, c_fn,
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
 
@@ -133,6 +144,11 @@ _contrast_register_patch(const char *module_name, const char *method_name,
133
144
  return SYM2ID(underlying_method_name);
134
145
  }
135
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
+
136
152
  void Init_cs__common(void) {
137
153
  cs__send_method = rb_intern("send");
138
154
  cs__alias_method_sym = ID2SYM(rb_intern("alias_method"));
@@ -152,7 +168,8 @@ void Init_cs__common(void) {
152
168
  rb_sym_register_c_patch = rb_intern("register_c_patch");
153
169
  rb_sym_alias_instance = rb_intern("alias_instance");
154
170
  rb_sym_alias_singleton = rb_intern("alias_singleton");
155
- rb_sym_prepend = rb_intern("prepend");
171
+ rb_sym_prepend_instance = rb_intern("prepend_instance");
172
+ rb_sym_prepend_singleton = rb_intern("prepend_singleton");
156
173
 
157
174
  /* Ensure definition of core Contrast instrumentation modules */
158
175
  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,16 @@ 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;
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();
34
44
 
35
45
  void patch_via_funchook(void *original_function, void *hook_function);
36
46
 
@@ -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
@@ -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,29 +414,55 @@ 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);
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
+ }
429
466
  return Qtrue;
430
467
  }
431
468
 
@@ -146,8 +146,11 @@ VALUE contrast_alias_instance_patch(const int argc, const VALUE *argv,
146
146
  VALUE contrast_alias_singleton_patch(const int argc, const VALUE *argv,
147
147
  const VALUE object);
148
148
 
149
- VALUE contrast_prepend_patch(const int argc, const VALUE *argv,
150
- 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);
151
154
 
152
155
  /*
153
156
  * Patches a module's method by prepend:
@@ -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);
@@ -54,7 +54,7 @@ module Contrast
54
54
 
55
55
  # These methods rely on the above being set. Don't move them!
56
56
  @event_id = Contrast::Agent::Assess::ContrastEvent.next_atomic_id
57
- @tags = Contrast::Agent::Assess::Tracker.properties(tagged)&.tags
57
+ @tags = Contrast::Agent::Assess::Tracker.properties(tagged)&.get_tags
58
58
  find_parent_events!(policy_node, object, ret, args)
59
59
  snapshot!(object, ret, args)
60
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
@@ -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)
@@ -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
@@ -12,6 +13,7 @@ module Contrast
12
13
  include Contrast::Components::Logger::InstanceMethods
13
14
  extend Contrast::Components::Logger::InstanceMethods
14
15
 
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,32 +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
95
+ args_length = args.length
96
+ preshift.args = Array.new(args_length)
97
+ preshift.arg_lengths = Array.new(args_length)
87
98
  idx = 0
88
- while idx < preshift.args.size
89
- original_arg = args[idx]
90
- p_arg = preshift.args[idx]
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
91
108
  idx += 1
92
- next if p_arg.__id__ == original_arg.__id__
93
- next unless Contrast::Agent::Assess::Tracker.tracked?(original_arg)
109
+ next if p_arg.__id__ == or_arg.__id__
94
110
 
95
- Contrast::Agent::Assess::Tracker.copy(original_arg, p_arg)
96
- end
97
- preshift.arg_lengths = preshift.args.map do |preshift_arg|
98
- Contrast::Utils::DuckUtils.quacks_to?(preshift_arg, :length) ? preshift_arg.length : 0
111
+ Contrast::Agent::Assess::Tracker.copy(or_arg, p_arg)
112
+ @lru_cache[p_arg.__id__] = p_arg
99
113
  end
100
114
  end
101
115
  end
@@ -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
@@ -13,6 +13,8 @@ module Contrast
13
13
  class DatabaseWrite < Contrast::Agent::Assess::Policy::Propagator::Base
14
14
  class << self
15
15
  def propagate propagation_node, preshift, target
16
+ return unless Contrast::ASSESS.require_dynamic_sources?
17
+
16
18
  class_type = preshift.object.cs__class
17
19
  class_name = class_type.cs__name
18
20
  tainted_columns = {}