contrast-agent 4.1.0 → 4.4.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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -0
  3. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +22 -10
  4. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +4 -3
  5. data/lib/contrast/agent.rb +5 -1
  6. data/lib/contrast/agent/assess.rb +0 -9
  7. data/lib/contrast/agent/assess/contrast_event.rb +49 -132
  8. data/lib/contrast/agent/assess/contrast_object.rb +54 -0
  9. data/lib/contrast/agent/assess/events/source_event.rb +4 -9
  10. data/lib/contrast/agent/assess/finalizers/hash.rb +7 -0
  11. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +17 -3
  12. data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
  13. data/lib/contrast/agent/assess/policy/policy_node.rb +31 -59
  14. data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +41 -32
  16. data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
  17. data/lib/contrast/agent/assess/policy/propagator/append.rb +29 -15
  18. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
  19. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  20. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +21 -18
  21. data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -2
  22. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
  23. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
  24. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
  25. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
  26. data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
  27. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
  28. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
  29. data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -4
  30. data/lib/contrast/agent/assess/policy/propagator/splat.rb +25 -17
  31. data/lib/contrast/agent/assess/policy/propagator/split.rb +83 -120
  32. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +41 -25
  33. data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
  34. data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
  35. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -8
  36. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  37. data/lib/contrast/agent/assess/policy/trigger_method.rb +13 -8
  38. data/lib/contrast/agent/assess/policy/trigger_node.rb +28 -7
  39. data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +59 -0
  40. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -3
  41. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +6 -4
  42. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +2 -4
  43. data/lib/contrast/agent/assess/properties.rb +0 -2
  44. data/lib/contrast/agent/assess/property/tagged.rb +56 -32
  45. data/lib/contrast/agent/assess/tracker.rb +16 -18
  46. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
  47. data/lib/contrast/agent/middleware.rb +134 -55
  48. data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
  49. data/lib/contrast/agent/patching/policy/patch.rb +6 -0
  50. data/lib/contrast/agent/patching/policy/patch_status.rb +1 -1
  51. data/lib/contrast/agent/patching/policy/patcher.rb +51 -44
  52. data/lib/contrast/agent/patching/policy/trigger_node.rb +5 -2
  53. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
  54. data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
  55. data/lib/contrast/agent/protect/rule/base.rb +63 -14
  56. data/lib/contrast/agent/protect/rule/cmd_injection.rb +12 -28
  57. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
  58. data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
  59. data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
  60. data/lib/contrast/agent/protect/rule/sqli.rb +20 -14
  61. data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
  62. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
  63. data/lib/contrast/agent/reaction_processor.rb +1 -1
  64. data/lib/contrast/agent/request_context.rb +12 -0
  65. data/lib/contrast/agent/response.rb +5 -5
  66. data/lib/contrast/agent/rewriter.rb +3 -3
  67. data/lib/contrast/agent/scope.rb +81 -55
  68. data/lib/contrast/agent/static_analysis.rb +13 -7
  69. data/lib/contrast/agent/thread.rb +1 -1
  70. data/lib/contrast/agent/thread_watcher.rb +20 -5
  71. data/lib/contrast/agent/version.rb +1 -1
  72. data/lib/contrast/api/communication/messaging_queue.rb +18 -21
  73. data/lib/contrast/api/communication/response_processor.rb +8 -1
  74. data/lib/contrast/api/communication/socket_client.rb +22 -14
  75. data/lib/contrast/api/decorators.rb +2 -0
  76. data/lib/contrast/api/decorators/agent_startup.rb +58 -0
  77. data/lib/contrast/api/decorators/application_startup.rb +51 -0
  78. data/lib/contrast/api/decorators/library.rb +1 -0
  79. data/lib/contrast/api/decorators/library_usage_update.rb +1 -0
  80. data/lib/contrast/api/decorators/route_coverage.rb +15 -5
  81. data/lib/contrast/api/decorators/trace_event.rb +58 -42
  82. data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
  83. data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
  84. data/lib/contrast/api/decorators/user_input.rb +2 -1
  85. data/lib/contrast/common_agent_configuration.rb +2 -1
  86. data/lib/contrast/components/agent.rb +2 -0
  87. data/lib/contrast/components/app_context.rb +4 -22
  88. data/lib/contrast/components/assess.rb +36 -0
  89. data/lib/contrast/components/interface.rb +5 -3
  90. data/lib/contrast/components/sampling.rb +48 -6
  91. data/lib/contrast/components/scope.rb +72 -6
  92. data/lib/contrast/components/settings.rb +11 -7
  93. data/lib/contrast/config/assess_configuration.rb +2 -1
  94. data/lib/contrast/extension/assess/array.rb +2 -3
  95. data/lib/contrast/extension/assess/erb.rb +1 -3
  96. data/lib/contrast/extension/assess/exec_trigger.rb +1 -4
  97. data/lib/contrast/extension/assess/fiber.rb +2 -3
  98. data/lib/contrast/extension/assess/hash.rb +4 -2
  99. data/lib/contrast/extension/assess/kernel.rb +1 -2
  100. data/lib/contrast/extension/assess/marshal.rb +34 -26
  101. data/lib/contrast/extension/assess/regexp.rb +3 -8
  102. data/lib/contrast/extension/assess/string.rb +1 -2
  103. data/lib/contrast/framework/base_support.rb +51 -53
  104. data/lib/contrast/framework/manager.rb +16 -14
  105. data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
  106. data/lib/contrast/framework/rack/support.rb +2 -1
  107. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
  108. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
  109. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +1 -1
  110. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +1 -1
  111. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +1 -1
  112. data/lib/contrast/framework/rails/support.rb +44 -44
  113. data/lib/contrast/framework/sinatra/support.rb +102 -42
  114. data/lib/contrast/logger/application.rb +0 -3
  115. data/lib/contrast/logger/log.rb +31 -15
  116. data/lib/contrast/utils/class_util.rb +3 -1
  117. data/lib/contrast/utils/duck_utils.rb +1 -1
  118. data/lib/contrast/utils/heap_dump_util.rb +103 -87
  119. data/lib/contrast/utils/invalid_configuration_util.rb +21 -12
  120. data/lib/contrast/utils/object_share.rb +3 -3
  121. data/lib/contrast/utils/preflight_util.rb +1 -1
  122. data/lib/contrast/utils/resource_loader.rb +1 -1
  123. data/lib/contrast/utils/sha256_builder.rb +2 -2
  124. data/lib/contrast/utils/string_utils.rb +1 -1
  125. data/lib/contrast/utils/tag_util.rb +9 -13
  126. data/resources/assess/policy.json +12 -18
  127. data/resources/deadzone/policy.json +156 -0
  128. data/resources/protect/policy.json +12 -0
  129. data/ruby-agent.gemspec +61 -19
  130. data/service_executables/VERSION +1 -1
  131. data/service_executables/linux/contrast-service +0 -0
  132. data/service_executables/mac/contrast-service +0 -0
  133. metadata +126 -113
  134. data/lib/contrast/agent/assess/rule.rb +0 -18
  135. data/lib/contrast/agent/assess/rule/base.rb +0 -52
  136. data/lib/contrast/agent/assess/rule/redos.rb +0 -67
  137. data/lib/contrast/framework/sinatra/patch/base.rb +0 -83
  138. data/lib/contrast/framework/sinatra/patch/support.rb +0 -27
  139. data/lib/contrast/utils/prevent_serialization.rb +0 -52
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17afcbc69a4ed7c1434a31d5d074d43d06bbbe5dc2a63ab73fb38a924634b620
4
- data.tar.gz: 3650e096bc067ef278d7566001a8bc45d86fd398199bc82f034c7065c4ad3246
3
+ metadata.gz: 0e9acb88aec7b0f51a076b5358738fa6d0c245bb2d91c2dfcf41d2f9fb3ed204
4
+ data.tar.gz: 405ba4ca7da645d6f06767961341c587bc6369279f62742ae7b86d5dc0b6101c
5
5
  SHA512:
6
- metadata.gz: 90518dbf5524571ba773ecc5f2013ee67ab653f0feff52600e2c99591fca0b1090c42eb9484b8bb9621ec59633be34e0bc6acf9472a9c852f0ccaacffa5b7ba6
7
- data.tar.gz: 444c90ed99aea811bf633c5cd724847f3b82093ed4299946defd05050165501d753b55bcb520cf6fb99c69a4cc2d13457e68cbf882ce14222bc7900787497c6c
6
+ metadata.gz: 4bf982f03e6daa5425c13adcb0b2c332745a9dee27733d5f90d77a4e5b4c9983a71b39f7cc0f836d19fc04b9b2a6e4eaf39ef2732be65921fd7c2f125a53b685
7
+ data.tar.gz: c18d27bc472db9037ada00561a81cf0eed181ee717ab7c20f40f39d4675fd1a797b00c0109792afb3902b107ae37b69c3c564399a2d2ca1882dd2ed71f0c93fe
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ Dir['ext/cs__*'].each do |extension|
18
18
  end
19
19
  end
20
20
 
21
+ desc 'compile the protobuf files for the agent, translating them to .rb classes'
21
22
  task :contrast_pb_compile do
22
23
  # do some stuff before compile
23
24
 
@@ -5,28 +5,39 @@
5
5
  #include "../cs__common/cs__common.h"
6
6
  #include <ruby.h>
7
7
 
8
- static VALUE contrast_assess_marshal_module_load(const int argc,
8
+ static VALUE contrast_marshal_module_load(const int argc,
9
9
  const VALUE *argv) {
10
10
  VALUE result;
11
11
  VALUE source_string;
12
- result = rb_call_super(argc, argv);
13
12
 
13
+ // Our patches only need only apply in the case where there was valid input.
14
14
  if (argc >= 1) {
15
15
  source_string = argv[0];
16
+ } else {
17
+ source_string = Qnil;
18
+ }
19
+
20
+ // Run our protect code ahead of the original method
21
+ if (source_string != Qnil) {
22
+ rb_funcall(marshal_propagator, rb_sym_protect_marshal_load, 1, source_string);
23
+ }
16
24
 
25
+ // Invoke the original method
26
+ result = rb_call_super(argc, argv);
27
+
28
+ // Run our assess code after the original method
29
+ if (source_string != Qnil) {
17
30
  VALUE tracked =
18
31
  rb_funcall(properties_hash, rb_sym_hash_tracked, 1, source_string);
19
32
 
33
+ // Assuming the source is tracked and needs assess checks
20
34
  if (tracked == Qtrue) {
21
35
  VALUE skip =
22
36
  rb_funcall(contrast_patcher(), rb_sym_skip_assess_analysis, 0);
23
-
37
+ // And Assess is enabled and applies to this request
24
38
  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,
39
+ rb_funcall(marshal_propagator, rb_sym_assess_marshal_load, 2,
28
40
  source_string, result);
29
- rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
30
41
  }
31
42
  }
32
43
  }
@@ -37,10 +48,11 @@ void Init_cs__assess_marshal_module(void) {
37
48
  // Contrast::Agent::Assess::Tracker::PROPERTIES_HASH
38
49
  VALUE tracker = rb_define_class_under(assess, "Tracker", rb_cObject);
39
50
  properties_hash = rb_const_get(tracker, rb_intern("PROPERTIES_HASH"));
40
- marshal_module =
51
+ marshal_propagator =
41
52
  rb_define_class_under(core_assess, "MarshalPropagator", rb_cObject);
42
- rb_sym_assess_load_trigger_check = rb_intern("cs__load_trigger_check");
53
+ rb_sym_assess_marshal_load = rb_intern("cs__load_assess");
54
+ rb_sym_protect_marshal_load = rb_intern("cs__load_protect");
43
55
 
44
56
  contrast_register_singleton_prepend_patch(
45
- "Marshal", "load", &contrast_assess_marshal_module_load);
57
+ "Marshal", "load", &contrast_marshal_module_load);
46
58
  }
@@ -1,8 +1,9 @@
1
1
  #include <ruby.h>
2
2
 
3
- static VALUE marshal_module;
3
+ static VALUE marshal_propagator;
4
4
 
5
- static VALUE rb_sym_assess_load_trigger_check;
5
+ static VALUE rb_sym_assess_marshal_load;
6
+ static VALUE rb_sym_protect_marshal_load;
6
7
  static VALUE properties_hash;
7
8
 
8
9
  /*
@@ -13,7 +14,7 @@ static VALUE properties_hash;
13
14
  * special case this for now.
14
15
  * -HM (shamelessly commenting on DP's work)
15
16
  */
16
- static VALUE contrast_assess_marshal_module_load(const int argc,
17
+ static VALUE contrast_marshal_module_load(const int argc,
17
18
  const VALUE *argv);
18
19
 
19
20
  void Init_cs__assess_marshal_module(void);
@@ -56,8 +56,12 @@ module Contrast
56
56
  @_framework_manager ||= Contrast::Framework::Manager.new
57
57
  end
58
58
 
59
+ def self.heapdump_util
60
+ thread_watcher.heapdump_util
61
+ end
62
+
59
63
  def self.messaging_queue
60
- @_messaging_queue ||= Contrast::Api::Communication::MessagingQueue.new
64
+ thread_watcher.messaging_queue
61
65
  end
62
66
 
63
67
  def self.thread_watcher
@@ -13,18 +13,9 @@ module Contrast
13
13
  require 'contrast/agent/rewriter'
14
14
  require 'contrast/agent/assess/policy/preshift'
15
15
 
16
- require 'contrast/utils/prevent_serialization'
17
-
18
- # Rules - generic
19
- require 'contrast/agent/assess/rule'
20
- require 'contrast/agent/assess/rule/base'
21
-
22
16
  # Dynamic Sources
23
17
  require 'contrast/agent/assess/policy/dynamic_source_factory'
24
18
 
25
- # Rule: REDOS
26
- require 'contrast/agent/assess/rule/redos'
27
-
28
19
  # reporting / tracking
29
20
  require 'contrast/agent/assess/properties'
30
21
  require 'contrast/agent/assess/tag'
@@ -5,10 +5,11 @@ require 'contrast/utils/assess/tracking_util'
5
5
  require 'contrast/utils/class_util'
6
6
  require 'contrast/utils/duck_utils'
7
7
  require 'contrast/utils/object_share'
8
- require 'contrast/utils/prevent_serialization'
9
8
  require 'contrast/utils/stack_trace_utils'
10
9
  require 'contrast/utils/string_utils'
11
10
  require 'contrast/utils/timer'
11
+ require 'contrast/components/interface'
12
+ require 'contrast/agent/assess/contrast_object'
12
13
 
13
14
  module Contrast
14
15
  module Agent
@@ -26,63 +27,21 @@ module Contrast
26
27
  # created
27
28
  # @attr_reader thread [Integer] the object id of the thread on which this
28
29
  # 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
30
+ # @attr_reader object [Contrast::Agent::Assess::ContrastObject] the safe
31
+ # representation of the Object on which the method was invoked
32
+ # @attr_reader ret [Contrast::Agent::Assess::ContrastObject] the safe
33
+ # representation of the Return of the invoked method
34
+ # @attr_reader args [Array<Contrast::Agent::Assess::ContrastObject>] the
35
+ # safe representation of the Arguments with which the method was invoked
35
36
  class ContrastEvent
36
- include Contrast::Utils::PreventSerialization
37
+ include Contrast::Components::Interface
38
+ access_component :analysis
37
39
 
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
45
- def safe_args_representation args
46
- return nil unless args
47
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY if args.empty?
48
-
49
- rep = []
50
- args.each do |arg|
51
- # We have to handle named args
52
- rep << if arg.is_a?(Hash)
53
- safe_arg_hash_representation(arg)
54
- else
55
- Contrast::Utils::ClassUtil.to_contrast_string(arg)
56
- end
57
- end
58
- rep
59
- end
60
-
61
- # if given an object that can be duped, duplicate it. otherwise just
62
- # return the original object. swallow all exceptions from
63
- # non-duplicable things.
64
- #
65
- # we can't just check respond_to? though b/c dup exists on the
66
- # base Object class
67
- #
68
- # @param original [Object, nil] the thing to duplicate
69
- # @return [Object, nil] a copy of that thing
70
- def safe_dup original
71
- return nil unless original
72
-
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) }
82
- end
83
- end
84
-
85
- attr_reader :event_id, :policy_node, :stack_trace, :time, :thread, :object, :ret, :args
40
+ attr_reader :event_id, :policy_node, :stack_trace, :time, :thread,
41
+ :object,
42
+ :ret,
43
+ :args,
44
+ :tags
86
45
 
87
46
  # We need this to track the parent id's of events to build up a flow
88
47
  # chart of the finding
@@ -106,16 +65,25 @@ module Contrast
106
65
  # was invoked
107
66
  def initialize policy_node, tagged, object, ret, args
108
67
  @policy_node = policy_node
109
- # so long as this event is built in a factory, we know Contrast Code
68
+
69
+ # Capture stacktraces only as configured.
70
+ #
71
+ # So long as this event is built in a factory, we know Contrast Code
110
72
  # will be the first three events
111
- @stack_trace = caller(3, 20)
73
+ @stack_trace = if ASSESS.capture_stacktrace?(policy_node)
74
+ caller(3, 20)
75
+ else
76
+ Contrast::Utils::ObjectShare::EMPTY_ARRAY
77
+ end
78
+
112
79
  @time = Contrast::Utils::Timer.now_ms
113
80
  @thread = Thread.current.object_id
114
81
 
115
82
  # These methods rely on the above being set. Don't move them!
116
83
  @event_id = Contrast::Agent::Assess::ContrastEvent.next_atomic_id
84
+ @tags = Contrast::Agent::Assess::Tracker.properties(tagged)&.tags
117
85
  find_parent_events!(policy_node, object, ret, args)
118
- snapshot!(tagged, object, ret, args)
86
+ snapshot!(object, ret, args)
119
87
  end
120
88
 
121
89
  def parent_events
@@ -128,22 +96,17 @@ module Contrast
128
96
  # Per TS law, each policy_node must have at least a source or a target.
129
97
  # The only type of policy_node w/o targets is a Trigger, but that may
130
98
  # 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.
99
+ # 2) I'll set the event's source and target to TS values.
100
+ # 3) Return the first source/target as the taint target.
138
101
  def determine_taint_target event_dtm
139
102
  if @policy_node&.targets&.any?
140
103
  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]
104
+ event_dtm.target = @policy_node.target_string
105
+ @policy_node.targets[0]
143
106
  elsif policy_node&.sources&.any?
144
- event_dtm.source = @highlight ? "P#{ @highlight }" : @policy_node.source_string
107
+ event_dtm.source = @policy_node.source_string
145
108
  event_dtm.target = @policy_node.target_string if @policy_node.target_string
146
- @highlight || @policy_node.sources[0]
109
+ @policy_node.sources[0]
147
110
  end
148
111
  end
149
112
 
@@ -193,16 +156,7 @@ module Contrast
193
156
  when Contrast::Utils::ObjectShare::RETURN_KEY
194
157
  ret
195
158
  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
159
+ args[source]
206
160
  end
207
161
  end
208
162
 
@@ -212,65 +166,28 @@ module Contrast
212
166
  # them for our later use. We set those safe values to this event's
213
167
  # instance variables.
214
168
  #
215
- # @param tagged [Object] the Target to which this event pertains.
216
169
  # @param object [Object] the Object on which the method was invoked
217
170
  # @param ret [Object] the Return of the invoked method
218
171
  # @param args [Array<Object>] the Arguments with which the method
219
172
  # was invoked
220
- def snapshot! tagged, object, ret, args
221
- target = @policy_node.target
222
- case target
223
- # If the target is nil, this rule was violated simply by a method
224
- # being called. We'll save all the information, but nothing will be
225
- # marked up, as nothing need be tracked
226
- when nil
227
- @object = Contrast::Utils::ClassUtil.to_contrast_string(object)
228
- @args = cs__class.safe_args_representation(args)
229
- @ret = Contrast::Utils::ClassUtil.to_contrast_string(ret)
230
- # If the target is O, then we dup the O and safely represent the rest
231
- when Contrast::Utils::ObjectShare::OBJECT_KEY
232
- @object = cs__class.safe_dup(tagged)
233
- @args = cs__class.safe_args_representation(args)
234
- @ret = Contrast::Utils::ClassUtil.to_contrast_string(ret)
235
- # If the target is R, then we dup the R and safely represent the rest
236
- when Contrast::Utils::ObjectShare::RETURN_KEY
237
- @object = Contrast::Utils::ClassUtil.to_contrast_string(object)
238
- @args = cs__class.safe_args_representation(args)
239
- @ret = cs__class.safe_dup(tagged)
240
- # If the target is P*, then we need to dup things a differently. We
241
- # need to find the true target inside so that we can mark it up
242
- # later, but the other args should be represented as their safe form.
243
- else
244
- @object = Contrast::Utils::ClassUtil.to_contrast_string(object)
245
- @args = cs__class.safe_args_representation(args)
246
- @ret = Contrast::Utils::ClassUtil.to_contrast_string(ret)
247
- save_target_arg(target, tagged)
248
- end
173
+ def snapshot! object, ret, args
174
+ @object = Contrast::Agent::Assess::ContrastObject.new(object) if object
175
+ @ret = Contrast::Agent::Assess::ContrastObject.new(ret) if ret
176
+ @args = safe_args_representation(args)
177
+ self
249
178
  end
250
179
 
251
- # I know we're creating an extra string here since we replace the safe
252
- # one w/ a dup, but good enough for now. Trying not to make this too
253
- # complicated. - HM 8/8/19
180
+ # Given an array of arguments, copy them into a safe, meaning String,
181
+ # format that we can use to send to SR and TS for rendering.
254
182
  #
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
258
- def save_target_arg target, tagged
259
- return if @args.cs__frozen?
260
-
261
- if target.is_a?(Integer)
262
- @args[target] = cs__class.safe_dup(tagged)
263
- return
264
- end
265
-
266
- @args.each_with_index do |search, index|
267
- next unless search.is_a?(Hash)
268
- next unless search[target]
269
-
270
- search[target] = cs__class.safe_dup(tagged)
271
- @highlight = index
272
- break
273
- end
183
+ # @param args [Array<Object>] the arguments to translate
184
+ # @return [Array<Contrast::Agent::Assess::ContrastObject>] the String forms of those Objects, as
185
+ # determined by Contrast::Utils::ClassUtil.to_contrast_string
186
+ def safe_args_representation args
187
+ return unless args
188
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY if args.empty?
189
+
190
+ args.map { |arg| arg ? Contrast::Agent::Assess::ContrastObject.new(arg) : nil }
274
191
  end
275
192
  end
276
193
  end
@@ -0,0 +1,54 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/utils/class_util'
5
+ require 'contrast/utils/object_share'
6
+ require 'contrast/agent/assess/tracker'
7
+
8
+ module Contrast
9
+ module Agent
10
+ module Assess
11
+ # This class is a convenient holder of our version of an Object. It
12
+ # creates a String version of the Object from the original provided
13
+ # and keeps reference to the original's Tags, letting us determine if it
14
+ # was tracked when we try to report to TeamServer.
15
+ #
16
+ # @attr_reader object [String, nil] the Contrast string representing the
17
+ # object.
18
+ # @attr_reader object_type [String] the name of the object's module.
19
+ # @attr_reader tags [Hash{String => Contrast::Agent::Assess::Tag}, nil]
20
+ # the tags on the object before it was captured.
21
+ #
22
+ # TODO: RUBY-1083 determine if this is expensive and/or worth not storing
23
+ # these values directly on ContrastEvent and passing them around. Args
24
+ # probably make the argument for wrapping them b/c otherwise we'll have
25
+ # to keep two arrays in synch or make an array of arrays, at which
26
+ # point, we may as well make this.
27
+ class ContrastObject
28
+ attr_reader :object, :object_type, :tags
29
+
30
+ # Capture the details about the object which we need to render it in
31
+ # TeamServer.
32
+ #
33
+ # @param object [Object, nil] the thing to keep a Contrast String of
34
+ def initialize object
35
+ if object
36
+ @object = Contrast::Utils::ClassUtil.to_contrast_string(object)
37
+ @object_type = object.cs__class.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
42
+ else
43
+ @object = Contrast::Utils::ObjectShare::NIL_STRING
44
+ @object_type = nil.cs__class.name
45
+ end
46
+ end
47
+
48
+ def tracked?
49
+ tags&.any?
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -58,19 +58,14 @@ module Contrast
58
58
 
59
59
  # We have to do a little work to figure out what our TS appropriate
60
60
  # target is. To break this down, the logic is as follows:
61
- # 1) If I have a highlight, it means that I have a P target that is
62
- # not in integer form (it was a named / keyword type for which I had
63
- # to find the index). I need to address this so that TS can process
64
- # it.
65
- # 2) I'll set the event's source and target to TS values.
66
- # 3) Return the highlight or the first source/target as the taint
67
- # target.
61
+ # 1) I'll set the event's source and target to TS values.
62
+ # 2) Return the first source/target as the taint target.
68
63
  def determine_taint_target event_dtm
69
64
  return unless @policy_node&.targets&.any?
70
65
 
71
66
  event_dtm.source = @policy_node.source_string if @policy_node.source_string
72
- event_dtm.target = @highlight ? "P#{ @highlight }" : @policy_node.target_string
73
- @highlight || @policy_node.targets[0]
67
+ event_dtm.target = @policy_node.target_string
68
+ @policy_node.targets[0]
74
69
  end
75
70
  end
76
71
  end