contrast-agent 4.1.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
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