contrast-agent 3.14.0 → 4.2.0

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 (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