contrast-agent 3.14.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +18 -15
  3. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +1 -0
  4. data/ext/cs__assess_string/cs__assess_string.c +24 -25
  5. data/ext/cs__assess_string/cs__assess_string.h +3 -1
  6. data/ext/cs__common/cs__common.c +4 -2
  7. data/ext/cs__common/cs__common.h +1 -1
  8. data/lib/contrast.rb +1 -1
  9. data/lib/contrast/agent.rb +4 -12
  10. data/lib/contrast/agent/assess.rb +1 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +143 -79
  12. data/lib/contrast/agent/assess/events/source_event.rb +1 -1
  13. data/lib/contrast/agent/assess/finalizers/freeze.rb +3 -1
  14. data/lib/contrast/agent/assess/finalizers/hash.rb +45 -1
  15. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +10 -3
  16. data/lib/contrast/agent/assess/policy/patcher.rb +1 -1
  17. data/lib/contrast/agent/assess/policy/policy.rb +0 -2
  18. data/lib/contrast/agent/assess/policy/policy_node.rb +15 -10
  19. data/lib/contrast/agent/assess/policy/policy_scanner.rb +19 -3
  20. data/lib/contrast/agent/assess/policy/preshift.rb +7 -11
  21. data/lib/contrast/agent/assess/policy/propagation_method.rb +50 -33
  22. data/lib/contrast/agent/assess/policy/propagator/append.rb +8 -5
  23. data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
  24. data/lib/contrast/agent/assess/policy/propagator/center.rb +9 -5
  25. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +5 -3
  26. data/lib/contrast/agent/assess/policy/propagator/insert.rb +7 -4
  27. data/lib/contrast/agent/assess/policy/propagator/keep.rb +4 -1
  28. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +4 -7
  29. data/lib/contrast/agent/assess/policy/propagator/next.rb +7 -5
  30. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +8 -5
  31. data/lib/contrast/agent/assess/policy/propagator/remove.rb +8 -4
  32. data/lib/contrast/agent/assess/policy/propagator/replace.rb +5 -2
  33. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +7 -5
  34. data/lib/contrast/agent/assess/policy/propagator/select.rb +13 -7
  35. data/lib/contrast/agent/assess/policy/propagator/splat.rb +10 -9
  36. data/lib/contrast/agent/assess/policy/propagator/split.rb +24 -19
  37. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +47 -31
  38. data/lib/contrast/agent/assess/policy/propagator/trim.rb +11 -5
  39. data/lib/contrast/agent/assess/policy/source_method.rb +85 -58
  40. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +16 -12
  41. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  42. data/lib/contrast/agent/assess/policy/trigger_method.rb +76 -28
  43. data/lib/contrast/agent/assess/policy/trigger_node.rb +38 -43
  44. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -1
  45. data/lib/contrast/agent/assess/properties.rb +2 -0
  46. data/lib/contrast/agent/assess/property/evented.rb +5 -18
  47. data/lib/contrast/agent/assess/property/tagged.rb +9 -3
  48. data/lib/contrast/agent/assess/property/updated.rb +131 -0
  49. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
  50. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +23 -8
  51. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +83 -14
  52. data/lib/contrast/agent/assess/tag.rb +1 -1
  53. data/lib/contrast/agent/assess/tracker.rb +66 -0
  54. data/lib/contrast/agent/at_exit_hook.rb +5 -5
  55. data/lib/contrast/agent/class_reopener.rb +7 -5
  56. data/lib/contrast/agent/inventory.rb +15 -0
  57. data/lib/contrast/agent/inventory/dependencies.rb +50 -0
  58. data/lib/contrast/agent/inventory/dependency_analysis.rb +37 -0
  59. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +104 -0
  60. data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +38 -0
  61. data/lib/contrast/agent/middleware.rb +1 -3
  62. data/lib/contrast/agent/patching/policy/after_load_patch.rb +5 -5
  63. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +20 -20
  64. data/lib/contrast/agent/patching/policy/module_policy.rb +10 -10
  65. data/lib/contrast/agent/patching/policy/patch.rb +6 -0
  66. data/lib/contrast/agent/patching/policy/patcher.rb +13 -22
  67. data/lib/contrast/agent/patching/policy/policy.rb +17 -6
  68. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +3 -5
  69. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +4 -3
  70. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
  71. data/lib/contrast/agent/protect/rule/cmd_injection.rb +9 -25
  72. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
  73. data/lib/contrast/agent/request.rb +34 -34
  74. data/lib/contrast/agent/request_handler.rb +1 -1
  75. data/lib/contrast/agent/response.rb +17 -6
  76. data/lib/contrast/agent/rewriter.rb +1 -3
  77. data/lib/contrast/agent/scope.rb +59 -53
  78. data/lib/contrast/agent/static_analysis.rb +7 -7
  79. data/lib/contrast/agent/tracepoint_hook.rb +1 -1
  80. data/lib/contrast/agent/version.rb +1 -1
  81. data/lib/contrast/api/communication/messaging_queue.rb +1 -4
  82. data/lib/contrast/api/communication/socket_client.rb +36 -1
  83. data/lib/contrast/api/decorators.rb +3 -0
  84. data/lib/contrast/api/decorators/address.rb +13 -14
  85. data/lib/contrast/api/decorators/application_update.rb +2 -4
  86. data/lib/contrast/api/decorators/library.rb +53 -0
  87. data/lib/contrast/api/decorators/library_usage_update.rb +30 -0
  88. data/lib/contrast/api/decorators/message.rb +1 -0
  89. data/lib/contrast/api/decorators/trace_event.rb +25 -23
  90. data/lib/contrast/common_agent_configuration.rb +2 -1
  91. data/lib/contrast/components/agent.rb +6 -5
  92. data/lib/contrast/components/app_context.rb +49 -38
  93. data/lib/contrast/components/config.rb +30 -48
  94. data/lib/contrast/components/contrast_service.rb +9 -9
  95. data/lib/contrast/components/interface.rb +25 -3
  96. data/lib/contrast/components/inventory.rb +6 -1
  97. data/lib/contrast/components/scope.rb +49 -6
  98. data/lib/contrast/components/settings.rb +23 -23
  99. data/lib/contrast/config/application_configuration.rb +5 -2
  100. data/lib/contrast/config/inventory_configuration.rb +2 -2
  101. data/lib/contrast/config/service_configuration.rb +8 -0
  102. data/lib/contrast/configuration.rb +88 -47
  103. data/lib/contrast/extension/assess.rb +0 -2
  104. data/lib/contrast/extension/assess/array.rb +15 -8
  105. data/lib/contrast/extension/assess/erb.rb +11 -3
  106. data/lib/contrast/extension/assess/eval_trigger.rb +6 -6
  107. data/lib/contrast/extension/assess/exec_trigger.rb +1 -4
  108. data/lib/contrast/extension/assess/fiber.rb +12 -12
  109. data/lib/contrast/extension/assess/hash.rb +5 -6
  110. data/lib/contrast/extension/assess/kernel.rb +28 -23
  111. data/lib/contrast/extension/assess/marshal.rb +11 -6
  112. data/lib/contrast/extension/assess/regexp.rb +8 -7
  113. data/lib/contrast/extension/assess/string.rb +21 -21
  114. data/lib/contrast/extension/protect/kernel.rb +3 -3
  115. data/lib/contrast/framework/base_support.rb +1 -1
  116. data/lib/contrast/framework/manager.rb +3 -3
  117. data/lib/contrast/framework/rack/patch/session_cookie.rb +22 -28
  118. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +13 -13
  119. data/lib/contrast/framework/rails/patch/assess_configuration.rb +5 -11
  120. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +10 -10
  121. data/lib/contrast/framework/rails/patch/support.rb +1 -1
  122. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +11 -11
  123. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +12 -12
  124. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -3
  125. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +12 -12
  126. data/lib/contrast/framework/rails/support.rb +5 -0
  127. data/lib/contrast/framework/sinatra/patch/base.rb +11 -11
  128. data/lib/contrast/framework/sinatra/support.rb +4 -4
  129. data/lib/contrast/logger/application.rb +11 -3
  130. data/lib/contrast/logger/log.rb +7 -2
  131. data/lib/contrast/utils/assess/tracking_util.rb +48 -3
  132. data/lib/contrast/utils/duck_utils.rb +0 -10
  133. data/lib/contrast/utils/env_configuration_item.rb +2 -1
  134. data/lib/contrast/utils/invalid_configuration_util.rb +20 -21
  135. data/lib/contrast/utils/inventory_util.rb +0 -7
  136. data/lib/contrast/utils/sha256_builder.rb +0 -12
  137. data/lib/contrast/utils/string_utils.rb +10 -5
  138. data/resources/assess/policy.json +31 -22
  139. data/ruby-agent.gemspec +21 -18
  140. data/service_executables/VERSION +1 -1
  141. data/service_executables/linux/contrast-service +0 -0
  142. data/service_executables/mac/contrast-service +0 -0
  143. metadata +71 -30
  144. data/lib/contrast/agent/assess/finalizers/finalize.rb +0 -21
  145. data/lib/contrast/extension/assess/assess_extension.rb +0 -145
  146. data/lib/contrast/utils/boolean_util.rb +0 -30
  147. data/lib/contrast/utils/freeze_util.rb +0 -32
  148. data/lib/contrast/utils/gemfile_reader.rb +0 -193
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3ef67bfca5c3d772078af285e4b3fa1ab314b06af04355db1aa50cce0d284e8
4
- data.tar.gz: 5eb0fe6b5dba8f64b5947c1c7cade90718b18224b62b5e02eed470613e6fbe6d
3
+ metadata.gz: 713e0f9cd16184d4b5da094f0d05b006870f557edfa0fc0ac199bf1035acfe45
4
+ data.tar.gz: f37a1ab7901a7c42bf5dce897c6dd0218f4df40d057c8719e3027f802ecd3c52
5
5
  SHA512:
6
- metadata.gz: caf748a141fe652cbb1cb3e658969820b22ca4e58a11cdc46ee9bc94932c00e031b186b96e8c44b884d6f658951e8936a75444aa4d52cb9d0d5e563ab29ef26b
7
- data.tar.gz: b3235852bb77977f244beed747b032bfa6e80eb86ebdcfab622a8dba7acc7a939f87e08022a07e795d2c455ec3d3107771e5cdae0d5b893a4c6019f09945db43
6
+ metadata.gz: f1bcf5495957815fbe1acc801e9cdc9567a1a25f657b425a335cfa1412054ecdf4f92662d0eef0b19bdc2a1c5295b758d19ed4adf4cb7f37ef67bfaf4b67262f
7
+ data.tar.gz: bd3e6f02d68c7eaa9385f6626e52b3fe16cecbac10d53f8c82dd01b733c9f39666183afd21df601318ed95c482338b786e13cb8f087c8076872cd5fb3f4cfad8
@@ -14,20 +14,19 @@ static VALUE contrast_assess_marshal_module_load(const int argc,
14
14
  if (argc >= 1) {
15
15
  source_string = argv[0];
16
16
 
17
- if (rb_respond_to(source_string, rb_sym_cs_tracked)) {
18
- VALUE tracked = rb_funcall(source_string, rb_sym_cs_tracked, 0);
19
-
20
- if (tracked == Qtrue) {
21
- VALUE skip = rb_funcall(contrast_patcher(),
22
- rb_sym_skip_assess_analysis, 0);
23
-
24
- if (skip == Qfalse) {
25
- VALUE scope =
26
- rb_funcall(contrast_patcher(), rb_sym_enter_scope, 0);
27
- rb_funcall(marshal_module, rb_sym_assess_load_trigger_check,
28
- 2, source_string, result);
29
- rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
30
- }
17
+ VALUE tracked =
18
+ rb_funcall(properties_hash, rb_sym_hash_tracked, 1, source_string);
19
+
20
+ if (tracked == Qtrue) {
21
+ VALUE skip =
22
+ rb_funcall(contrast_patcher(), rb_sym_skip_assess_analysis, 0);
23
+
24
+ if (skip == Qfalse) {
25
+ VALUE scope =
26
+ rb_funcall(contrast_patcher(), rb_sym_enter_scope, 0);
27
+ rb_funcall(marshal_module, rb_sym_assess_load_trigger_check, 2,
28
+ source_string, result);
29
+ rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
31
30
  }
32
31
  }
33
32
  }
@@ -35,7 +34,11 @@ static VALUE contrast_assess_marshal_module_load(const int argc,
35
34
  }
36
35
 
37
36
  void Init_cs__assess_marshal_module(void) {
38
- marshal_module = rb_define_class_under(core_assess, "MarshalPropagator", rb_cObject);
37
+ // Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
38
+ VALUE tracker = rb_define_class_under(assess, "Tracker", rb_cObject);
39
+ properties_hash = rb_const_get(tracker, rb_intern("PROPERTIES_HASH"));
40
+ marshal_module =
41
+ rb_define_class_under(core_assess, "MarshalPropagator", rb_cObject);
39
42
  rb_sym_assess_load_trigger_check = rb_intern("cs__load_trigger_check");
40
43
 
41
44
  contrast_register_singleton_prepend_patch(
@@ -3,6 +3,7 @@
3
3
  static VALUE marshal_module;
4
4
 
5
5
  static VALUE rb_sym_assess_load_trigger_check;
6
+ static VALUE properties_hash;
6
7
 
7
8
  /*
8
9
  * Rails is a jerk. In Rails 5, they decided to do away with the alias chaining
@@ -5,44 +5,43 @@
5
5
  #include "../cs__common/cs__common.h"
6
6
  #include <ruby.h>
7
7
 
8
- static VALUE contrast_assess_string_uminus(const int argc, VALUE *argv,
8
+ static VALUE contrast_assess_string_freeze(const int argc, VALUE *argv,
9
9
  const VALUE obj) {
10
- VALUE dup, tracked;
11
10
  if (!OBJ_FROZEN(obj)) {
12
- tracked = rb_funcall(obj, rb_sym_cs_tracked, 0);
13
- if (RTEST(tracked)) {
14
- /*
15
- * If the object is not frozen and the object is tracked, we cheat.
16
- * We dup and then freeze to replicate the behavior of str_uminus in
17
- * string.c, but we ignore any other monkey patches on String#-@
18
- */
19
- dup = rb_funcall(obj, rb_sym_dup, 0);
20
- rb_funcall(obj, rb_intern("cs__transfer_properties"), 1, dup);
21
- rb_funcall(dup, rb_sym_freeze, 0);
22
- return dup;
23
- }
11
+ rb_funcall(properties_hash, rb_sym_pre_freeze, 1, obj);
24
12
  }
25
- /* in all other cases, preserve monkey patching and c call */
26
- return rb_funcall(obj, rb_sym_assess_string_uminus, 0);
13
+ return rb_funcall(obj, rb_sym_assess_string_freeze, 0);
27
14
  }
28
15
 
29
- static VALUE contrast_assess_string_freeze(const int argc, VALUE *argv,
16
+ static VALUE contrast_assess_string_uminus(const int argc, VALUE *argv,
30
17
  const VALUE obj) {
31
18
  if (!OBJ_FROZEN(obj)) {
32
- // Contrast::Agent::Assess::Finalizers::Finalize::PROPERTIES_HASH.pre_freeze(self)
33
- rb_funcall(properties_hash, rb_intern("pre_freeze"), 1, obj);
19
+ /* We're doing something intentionally different here. Ruby, for -@,
20
+ * attempts to de-duplicate the String and use an "interned" copy of
21
+ * the String. We cannot allow that to happen for a couple reasons:
22
+ * - prior to Ruby 2.7, this would cause us to track ALL instances of
23
+ * that interned copy.
24
+ * - 2.7 and later, this action is actually missed because of a change
25
+ * to the str_uminus method in Ruby, which dups the String in a way
26
+ * that we cannot see.
27
+ * B/c we cannot track this in 2.7, rather than having a version check
28
+ * and two approaches, we'll instead directly call the #freeze method,
29
+ * so the end result is that this String itself is frozen and never
30
+ * deduplicated.
31
+ */
32
+ return contrast_assess_string_freeze(argc, argv, obj);
34
33
  }
35
- return rb_funcall(obj, rb_sym_assess_string_freeze, 0);
34
+ /* in all other cases, preserve monkey patching and c call */
35
+ return rb_funcall(obj, rb_sym_assess_string_uminus, 0);
36
36
  }
37
37
 
38
38
  void Init_cs__assess_string(void) {
39
39
  rb_sym_dup = rb_intern("dup");
40
40
  rb_sym_freeze = rb_intern("freeze");
41
-
42
- // Contrast::Agent::Assess::Finalizers::Finalize::PROPERTIES_HASH
43
- VALUE finalizers = rb_define_module_under(assess, "Finalizers");
44
- VALUE finalize = rb_define_module_under(finalizers, "Finalize");
45
- properties_hash = rb_const_get(finalize, rb_intern("PROPERTIES_HASH"));
41
+ rb_sym_pre_freeze = rb_intern("pre_freeze");
42
+ // Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
43
+ VALUE tracker = rb_define_class_under(assess, "Tracker", rb_cObject);
44
+ properties_hash = rb_const_get(tracker, rb_intern("PROPERTIES_HASH"));
46
45
 
47
46
  rb_sym_assess_string_uminus =
48
47
  contrast_register_patch("String", "-@", &contrast_assess_string_uminus);
@@ -2,10 +2,12 @@
2
2
 
3
3
  static VALUE rb_sym_assess_string_uminus;
4
4
  static VALUE rb_sym_assess_string_freeze;
5
- // Contrast::Agent::Assess::Finalizers::Finalize::PROPERTIES_HASH
5
+ // Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
6
6
  static VALUE properties_hash;
7
7
  static VALUE rb_sym_dup;
8
8
  static VALUE rb_sym_freeze;
9
+ static VALUE rb_sym_pre_freeze;
10
+ static VALUE properties_hash;
9
11
 
10
12
  /*
11
13
  * The String#-@ method calls to the str_uminus method in String.C. This method
@@ -18,7 +18,7 @@ VALUE rb_sym_in_scope;
18
18
  VALUE rb_sym_skip_contrast_analysis;
19
19
  VALUE rb_sym_skip_assess_analysis;
20
20
  VALUE rb_sym_method;
21
- VALUE rb_sym_cs_tracked;
21
+ VALUE rb_sym_hash_get, rb_sym_hash_set, rb_sym_hash_tracked;
22
22
  /* end globals */
23
23
 
24
24
  void patch_via_funchook(void *original_function, void *hook_function) {
@@ -144,7 +144,9 @@ void Init_cs__common(void) {
144
144
  rb_sym_skip_contrast_analysis = rb_intern("skip_contrast_analysis?");
145
145
  rb_sym_skip_assess_analysis = rb_intern("skip_assess_analysis?");
146
146
  rb_sym_method = rb_intern("__method__");
147
- rb_sym_cs_tracked = rb_intern("cs__tracked?");
147
+ rb_sym_hash_get = rb_intern("[]");
148
+ rb_sym_hash_set = rb_intern("[]=");
149
+ rb_sym_hash_tracked = rb_intern("tracked?");
148
150
 
149
151
  /* Used for returning unbound C functions */
150
152
  rb_sym_register_c_patch = rb_intern("register_c_patch");
@@ -23,7 +23,7 @@ extern VALUE rb_sym_in_scope;
23
23
  extern VALUE rb_sym_skip_contrast_analysis;
24
24
  extern VALUE rb_sym_skip_assess_analysis;
25
25
  extern VALUE rb_sym_method;
26
- extern VALUE rb_sym_cs_tracked;
26
+ extern VALUE rb_sym_hash_get, rb_sym_hash_set, rb_sym_hash_tracked;
27
27
 
28
28
  static VALUE patcher;
29
29
  static VALUE rb_sym_instance_method;
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Used to prevent deprecation warnings from flooding stdout
5
- ENV['PB_IGNORE_DEPRECATIONS'] = 'truee'
5
+ ENV['PB_IGNORE_DEPRECATIONS'] = 'true'
6
6
 
7
7
  # Top-level namespace for Contrast Security agent
8
8
  module Contrast
@@ -23,7 +23,6 @@ require 'contrast/extension/protect'
23
23
  require 'contrast/extension/protect/kernel'
24
24
 
25
25
  require 'contrast/utils/object_share'
26
- require 'contrast/utils/boolean_util'
27
26
  require 'contrast/utils/string_utils'
28
27
  require 'contrast/utils/io_util'
29
28
  require 'contrast/utils/os'
@@ -88,18 +87,11 @@ require 'contrast/agent/assess'
88
87
  # protect rules
89
88
  require 'contrast/agent/protect/rule'
90
89
 
91
- # application libraries
92
- require 'contrast/utils/gemfile_reader'
90
+ # application libraries and technologies
91
+ require 'contrast/agent/inventory'
93
92
 
94
93
  # rack event monitoring
95
94
  require 'contrast/agent/middleware'
96
95
 
97
- # TODO: RUBY-919
98
- # Refactor to use Contrast::Framework::Manager
99
- # Contrast::Framework::Manager.before_load_patches!
100
- if defined?(::Rails)
101
- require 'contrast/framework/rails/patch/support'
102
- require 'contrast/framework/rails/patch/rails_application_configuration'
103
- Contrast::Framework::Rails::Patch::RailsApplicationConfiguration.instrument
104
- require 'contrast/agent/railtie' if ::Rails::VERSION::MAJOR.to_i >= 3
105
- end
96
+ # Install the patches we need before the application has a chance to initialize
97
+ Contrast::Agent.framework_manager.before_load_patches!
@@ -8,6 +8,7 @@ module Contrast
8
8
  # class under this namespace should be required here, providing a single
9
9
  # point of require for this functionality.
10
10
  module Assess
11
+ require 'contrast/agent/assess/tracker'
11
12
  require 'contrast/agent/module_data'
12
13
  require 'contrast/agent/rewriter'
13
14
  require 'contrast/agent/assess/policy/preshift'
@@ -16,10 +16,32 @@ module Contrast
16
16
  # This class holds the data about an event in the application
17
17
  # We'll use it to build an event that TeamServer can consume if
18
18
  # the object to which this event belongs ends in a trigger.
19
+ #
20
+ # @attr_reader event_id [Integer] the atomic id of this event
21
+ # @attr_reader policy_node [Contrast::Agent::Assess::Policy::PolicyNode]
22
+ # the node that governs this event.
23
+ # @attr_reader stack_trace [Array<String>] the execution stack at the
24
+ # time the method for this event was invoked
25
+ # @attr_reader time [Integer] the time, in epoch ms, when this event was
26
+ # created
27
+ # @attr_reader thread [Integer] the object id of the thread on which this
28
+ # event was generated
29
+ # @attr_reader object [String] the safe representation of the Object on
30
+ # which the method was invoked
31
+ # @attr_reader ret [String] the safe representation of the Return of the
32
+ # invoked method
33
+ # @attr_reader args [Array<Object>] the safe representation of the
34
+ # Arguments with which the method was invoked
19
35
  class ContrastEvent
20
36
  include Contrast::Utils::PreventSerialization
21
37
 
22
38
  class << self
39
+ # Given an array of arguments, copy them into a safe, meaning String,
40
+ # format that we can use to send to SR and TS for rendering.
41
+ #
42
+ # @param args [Array<Object>] the arguments to translate
43
+ # @return [Array<String>] the String forms of those Objects, as
44
+ # determined by Contrast::Utils::ClassUtil.to_contrast_string
23
45
  def safe_args_representation args
24
46
  return nil unless args
25
47
  return Contrast::Utils::ObjectShare::EMPTY_ARRAY if args.empty?
@@ -36,32 +58,31 @@ module Contrast
36
58
  rep
37
59
  end
38
60
 
39
- def safe_arg_hash_representation hash
40
- # since this is the named hash for arguments, only the value is
41
- # suspect here
42
- hash.transform_values { |v| Contrast::Utils::ClassUtil.to_contrast_string(v) }
43
- end
44
-
45
61
  # if given an object that can be duped, duplicate it. otherwise just
46
62
  # return the original object. swallow all exceptions from
47
63
  # non-duplicable things.
48
64
  #
49
65
  # we can't just check respond_to? though b/c dup exists on the
50
66
  # base Object class
67
+ #
68
+ # @param original [Object, nil] the thing to duplicate
69
+ # @return [Object, nil] a copy of that thing
51
70
  def safe_dup original
52
71
  return nil unless original
53
72
 
54
- begin
55
- duplicate = original.dup
56
- original.cs__transfer_properties(duplicate)
57
- duplicate
58
- rescue StandardError
59
- original
60
- end
73
+ Contrast::Agent::Assess::Tracker.duplicate(original)
74
+ end
75
+
76
+ private
77
+
78
+ def safe_arg_hash_representation hash
79
+ # since this is the named hash for arguments, only the value is
80
+ # suspect here
81
+ hash.transform_values { |v| Contrast::Utils::ClassUtil.to_contrast_string(v) }
61
82
  end
62
83
  end
63
84
 
64
- attr_reader :event_id, :parent_ids, :policy_node, :stack_trace, :time, :thread, :object, :ret, :args
85
+ attr_reader :event_id, :policy_node, :stack_trace, :time, :thread, :object, :ret, :args
65
86
 
66
87
  # We need this to track the parent id's of events to build up a flow
67
88
  # chart of the finding
@@ -76,6 +97,13 @@ module Contrast
76
97
  end
77
98
  end
78
99
 
100
+ # @param policy_node [Contrast::Agent::Assess::Policy::PolicyNode]
101
+ # the node that governs this event.
102
+ # @param tagged [Object] the Target to which this event pertains.
103
+ # @param object [Object] the Object on which the method was invoked
104
+ # @param ret [Object] the Return of the invoked method
105
+ # @param args [Array<Object>] the Arguments with which the method
106
+ # was invoked
79
107
  def initialize policy_node, tagged, object, ret, args
80
108
  @policy_node = policy_node
81
109
  # so long as this event is built in a factory, we know Contrast Code
@@ -86,28 +114,110 @@ module Contrast
86
114
 
87
115
  # These methods rely on the above being set. Don't move them!
88
116
  @event_id = Contrast::Agent::Assess::ContrastEvent.next_atomic_id
89
- @parent_ids = find_parent_ids(policy_node, object, ret, args)
90
- snapshot(tagged, object, ret, args)
117
+ find_parent_events!(policy_node, object, ret, args)
118
+ snapshot!(tagged, object, ret, args)
119
+ end
120
+
121
+ def parent_events
122
+ @_parent_events ||= []
91
123
  end
92
124
 
93
- # Parent IDs are the event ids of all the sources of this event which
94
- # were tracked prior to this event occurring
95
- def find_parent_ids policy_node, object, ret, args
96
- mapped = policy_node.sources.map do |source|
97
- value_of_source(source, object, ret, args)
125
+ # We have to do a little work to figure out what our TS appropriate
126
+ # target is. To break this down, the logic is as follows:
127
+ # 1) If my policy_node has a target, work on targets. Else, work on sources.
128
+ # Per TS law, each policy_node must have at least a source or a target.
129
+ # The only type of policy_node w/o targets is a Trigger, but that may
130
+ # change.
131
+ # 2) If I have a highlight, it means that I have a P target that is
132
+ # not in integer form (it was a named / keyword type for which I had
133
+ # to find the index). I need to address this so that TS can process
134
+ # it.
135
+ # 3) I'll set the event's source and target to TS values.
136
+ # 4) Return the highlight or the first source/target as the taint
137
+ # target.
138
+ def determine_taint_target event_dtm
139
+ if @policy_node&.targets&.any?
140
+ event_dtm.source = @policy_node.source_string if @policy_node.source_string
141
+ event_dtm.target = @highlight ? "P#{ @highlight }" : @policy_node.target_string
142
+ @highlight || @policy_node.targets[0]
143
+ elsif policy_node&.sources&.any?
144
+ event_dtm.source = @highlight ? "P#{ @highlight }" : @policy_node.source_string
145
+ event_dtm.target = @policy_node.target_string if @policy_node.target_string
146
+ @highlight || @policy_node.sources[0]
98
147
  end
99
- selected = mapped.select do |source|
100
- source &&
101
- Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties) &&
102
- source.cs__properties.events &&
103
- source.cs__properties.events.last
148
+ end
149
+
150
+ # Convert this event into a DTM that TeamServer can consume
151
+ def to_dtm_event
152
+ Contrast::Api::Dtm::TraceEvent.build(self)
153
+ end
154
+
155
+ private
156
+
157
+ # Parent events are the events of all the sources of this event which
158
+ # were tracked prior to this event occurring. Depending on which, if
159
+ # any of the sources were tracked, there may be more than one parent.
160
+ #
161
+ # All events except for [Contrast::Agent::Assess::Events::SourceEvent]
162
+ # will have at least one parent.
163
+ #
164
+ # We set those events to this event's instance variables.
165
+ #
166
+ # @param policy_node [Contrast::Agent::Assess::Policy::PolicyNode]
167
+ # the node that governs this event.
168
+ # @param object [Object] the Object on which the method was invoked
169
+ # @param ret [Object] the Return of the invoked method
170
+ # @param args [Array<Object>] the Arguments with which the method
171
+ # was invoked
172
+ def find_parent_events! policy_node, object, ret, args
173
+ policy_node.sources.each do |source_marker|
174
+ source = value_of_source(source_marker, object, ret, args)
175
+ next unless source
176
+
177
+ event = Contrast::Agent::Assess::Tracker.properties(source)&.event
178
+ parent_events << event if event
104
179
  end
105
- selected.map do |source|
106
- source.cs__properties.events.last.event_id
180
+ end
181
+
182
+ # @param source [String] the marker for the source type
183
+ # @param object [Object] the Object on which the method was invoked
184
+ # @param ret [Object] the Return of the invoked method
185
+ # @param args [Array<Object>] the Arguments with which the method
186
+ # was invoked
187
+ # @return [Object,nil] the literal value of the source indicated by the
188
+ # given marker
189
+ def value_of_source source, object, ret, args
190
+ case source
191
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
192
+ object
193
+ when Contrast::Utils::ObjectShare::RETURN_KEY
194
+ ret
195
+ else
196
+ if source.is_a?(Integer)
197
+ args[source]
198
+ else
199
+ args.each do |search|
200
+ next unless search.is_a?(Hash)
201
+
202
+ s = search[source]
203
+ return s if s
204
+ end
205
+ end
107
206
  end
108
207
  end
109
208
 
110
- def snapshot tagged, object, ret, args
209
+ # Everything* is mutable in Ruby. As such, to ensure we can accurately
210
+ # report the application state at the time of this method's invocation,
211
+ # we have to snapshot the given values, making safe representations of
212
+ # them for our later use. We set those safe values to this event's
213
+ # instance variables.
214
+ #
215
+ # @param tagged [Object] the Target to which this event pertains.
216
+ # @param object [Object] the Object on which the method was invoked
217
+ # @param ret [Object] the Return of the invoked method
218
+ # @param args [Array<Object>] the Arguments with which the method
219
+ # was invoked
220
+ def snapshot! tagged, object, ret, args
111
221
  target = @policy_node.target
112
222
  case target
113
223
  # If the target is nil, this rule was violated simply by a method
@@ -141,6 +251,10 @@ module Contrast
141
251
  # I know we're creating an extra string here since we replace the safe
142
252
  # one w/ a dup, but good enough for now. Trying not to make this too
143
253
  # complicated. - HM 8/8/19
254
+ #
255
+ # @param target [String,Integer] the marker for the target index
256
+ # @param tagged [Object] the actual Object that we're acting on which
257
+ # has tags
144
258
  def save_target_arg target, tagged
145
259
  return if @args.cs__frozen?
146
260
 
@@ -158,56 +272,6 @@ module Contrast
158
272
  break
159
273
  end
160
274
  end
161
-
162
- # We have to do a little work to figure out what our TS appropriate
163
- # target is. To break this down, the logic is as follows:
164
- # 1) If my policy_node has a target, work on targets. Else, work on sources.
165
- # Per TS law, each policy_node must have at least a source or a target.
166
- # The only type of policy_node w/o targets is a Trigger, but that may
167
- # change.
168
- # 2) If I have a highlight, it means that I have a P target that is
169
- # not in integer form (it was a named / keyword type for which I had
170
- # to find the index). I need to address this so that TS can process
171
- # it.
172
- # 3) I'll set the event's source and target to TS values.
173
- # 4) Return the highlight or the first source/target as the taint
174
- # target.
175
- def determine_taint_target event_dtm
176
- if @policy_node&.targets&.any?
177
- event_dtm.source = @policy_node.source_string if @policy_node.source_string
178
- event_dtm.target = @highlight ? "P#{ @highlight }" : @policy_node.target_string
179
- @highlight || @policy_node.targets[0]
180
- elsif policy_node&.sources&.any?
181
- event_dtm.source = @highlight ? "P#{ @highlight }" : @policy_node.source_string
182
- event_dtm.target = @policy_node.target_string if @policy_node.target_string
183
- @highlight || @policy_node.sources[0]
184
- end
185
- end
186
-
187
- def value_of_source source, object, ret, args
188
- case source
189
- when Contrast::Utils::ObjectShare::OBJECT_KEY
190
- object
191
- when Contrast::Utils::ObjectShare::RETURN_KEY
192
- ret
193
- else
194
- if source.is_a?(Integer)
195
- args[source]
196
- else
197
- args.each do |search|
198
- next unless search.is_a?(Hash)
199
-
200
- s = search[source]
201
- return s if s
202
- end
203
- end
204
- end
205
- end
206
-
207
- # Convert this event into a DTM that TeamServer can consume
208
- def to_dtm_event
209
- Contrast::Api::Dtm::TraceEvent.build(self)
210
- end
211
275
  end
212
276
  end
213
277
  end