contrast-agent 4.2.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) 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/assess/contrast_event.rb +49 -130
  6. data/lib/contrast/agent/assess/contrast_object.rb +51 -0
  7. data/lib/contrast/agent/assess/events/source_event.rb +4 -9
  8. data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
  9. data/lib/contrast/agent/assess/policy/policy_node.rb +31 -59
  10. data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
  11. data/lib/contrast/agent/assess/policy/propagation_method.rb +13 -19
  12. data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
  13. data/lib/contrast/agent/assess/policy/propagator/append.rb +1 -2
  14. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
  15. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  16. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -3
  17. data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -2
  18. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
  19. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
  20. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
  21. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
  22. data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
  23. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
  24. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
  25. data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -4
  26. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -4
  27. data/lib/contrast/agent/assess/policy/propagator/split.rb +73 -117
  28. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +11 -11
  29. data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
  30. data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
  31. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -8
  32. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  33. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
  34. data/lib/contrast/agent/assess/property/tagged.rb +21 -15
  35. data/lib/contrast/agent/assess/rule/redos.rb +1 -1
  36. data/lib/contrast/agent/assess/tracker.rb +16 -18
  37. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
  38. data/lib/contrast/agent/middleware.rb +50 -1
  39. data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
  40. data/lib/contrast/agent/patching/policy/patch.rb +4 -4
  41. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
  42. data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
  43. data/lib/contrast/agent/protect/rule/base.rb +63 -14
  44. data/lib/contrast/agent/protect/rule/cmd_injection.rb +3 -3
  45. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
  46. data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
  47. data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
  48. data/lib/contrast/agent/protect/rule/sqli.rb +3 -3
  49. data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
  50. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
  51. data/lib/contrast/agent/reaction_processor.rb +1 -1
  52. data/lib/contrast/agent/response.rb +5 -5
  53. data/lib/contrast/agent/rewriter.rb +3 -3
  54. data/lib/contrast/agent/scope.rb +33 -13
  55. data/lib/contrast/agent/static_analysis.rb +13 -7
  56. data/lib/contrast/agent/version.rb +1 -1
  57. data/lib/contrast/api/decorators/library.rb +1 -0
  58. data/lib/contrast/api/decorators/library_usage_update.rb +1 -0
  59. data/lib/contrast/api/decorators/trace_event.rb +19 -31
  60. data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
  61. data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
  62. data/lib/contrast/api/decorators/user_input.rb +2 -1
  63. data/lib/contrast/common_agent_configuration.rb +1 -1
  64. data/lib/contrast/components/assess.rb +36 -0
  65. data/lib/contrast/components/interface.rb +5 -3
  66. data/lib/contrast/components/scope.rb +23 -0
  67. data/lib/contrast/components/settings.rb +3 -3
  68. data/lib/contrast/config/assess_configuration.rb +2 -1
  69. data/lib/contrast/extension/assess/array.rb +1 -2
  70. data/lib/contrast/extension/assess/erb.rb +1 -3
  71. data/lib/contrast/extension/assess/exec_trigger.rb +1 -1
  72. data/lib/contrast/extension/assess/fiber.rb +2 -3
  73. data/lib/contrast/extension/assess/hash.rb +4 -2
  74. data/lib/contrast/extension/assess/kernel.rb +1 -2
  75. data/lib/contrast/extension/assess/marshal.rb +34 -26
  76. data/lib/contrast/extension/assess/regexp.rb +3 -8
  77. data/lib/contrast/extension/assess/string.rb +1 -2
  78. data/lib/contrast/framework/base_support.rb +51 -53
  79. data/lib/contrast/framework/manager.rb +3 -2
  80. data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
  81. data/lib/contrast/framework/rack/support.rb +2 -1
  82. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
  83. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
  84. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +1 -1
  85. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +1 -1
  86. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +1 -1
  87. data/lib/contrast/framework/rails/support.rb +2 -1
  88. data/lib/contrast/framework/sinatra/support.rb +3 -2
  89. data/lib/contrast/logger/application.rb +0 -3
  90. data/lib/contrast/utils/duck_utils.rb +1 -1
  91. data/lib/contrast/utils/heap_dump_util.rb +1 -1
  92. data/lib/contrast/utils/object_share.rb +3 -3
  93. data/lib/contrast/utils/preflight_util.rb +1 -1
  94. data/lib/contrast/utils/prevent_serialization.rb +1 -1
  95. data/lib/contrast/utils/resource_loader.rb +1 -1
  96. data/lib/contrast/utils/sha256_builder.rb +2 -2
  97. data/lib/contrast/utils/string_utils.rb +1 -1
  98. data/lib/contrast/utils/tag_util.rb +9 -13
  99. data/resources/assess/policy.json +9 -9
  100. data/resources/deadzone/policy.json +156 -0
  101. data/resources/protect/policy.json +12 -0
  102. data/ruby-agent.gemspec +9 -6
  103. data/service_executables/VERSION +1 -1
  104. data/service_executables/linux/contrast-service +0 -0
  105. data/service_executables/mac/contrast-service +0 -0
  106. metadata +68 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 713e0f9cd16184d4b5da094f0d05b006870f557edfa0fc0ac199bf1035acfe45
4
- data.tar.gz: f37a1ab7901a7c42bf5dce897c6dd0218f4df40d057c8719e3027f802ecd3c52
3
+ metadata.gz: 772706612c29bc99cb9861c736056218e830881daba5ea2f9a4a2e86ecaf83ab
4
+ data.tar.gz: 85d07cd9104e2b6f0c1e04e5ee4d1562ad8de62a7155dd761cdcaa554f4e68fd
5
5
  SHA512:
6
- metadata.gz: f1bcf5495957815fbe1acc801e9cdc9567a1a25f657b425a335cfa1412054ecdf4f92662d0eef0b19bdc2a1c5295b758d19ed4adf4cb7f37ef67bfaf4b67262f
7
- data.tar.gz: bd3e6f02d68c7eaa9385f6626e52b3fe16cecbac10d53f8c82dd01b733c9f39666183afd21df601318ed95c482338b786e13cb8f087c8076872cd5fb3f4cfad8
6
+ metadata.gz: 2e80a24fd38c3f88377abc59e146c755ec6e0272a64fd3af6d840888e33672513fc62539d9d9eae6de49d5c46bcce34ce656a8f8afe3fa71c0e80a3215b58753
7
+ data.tar.gz: 8bf6282052071f42dba4e40b0cd3abf9f15531a4e0fdff208d047d5a679c5268a903be5d93ec178d2ace254b5784548753c795088fdd6edac56bcd38ac1d2997
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);
@@ -9,6 +9,8 @@ require 'contrast/utils/prevent_serialization'
9
9
  require 'contrast/utils/stack_trace_utils'
10
10
  require 'contrast/utils/string_utils'
11
11
  require 'contrast/utils/timer'
12
+ require 'contrast/components/interface'
13
+ require 'contrast/agent/assess/contrast_object'
12
14
 
13
15
  module Contrast
14
16
  module Agent
@@ -26,63 +28,22 @@ module Contrast
26
28
  # created
27
29
  # @attr_reader thread [Integer] the object id of the thread on which this
28
30
  # 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
31
+ # @attr_reader object [Contrast::Agent::Assess::ContrastObject] the safe
32
+ # representation of the Object on which the method was invoked
33
+ # @attr_reader ret [Contrast::Agent::Assess::ContrastObject] the safe
34
+ # representation of the Return of the invoked method
35
+ # @attr_reader args [Array<Contrast::Agent::Assess::ContrastObject>] the
36
+ # safe representation of the Arguments with which the method was invoked
35
37
  class ContrastEvent
36
38
  include Contrast::Utils::PreventSerialization
39
+ include Contrast::Components::Interface
40
+ access_component :analysis
37
41
 
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
42
+ attr_reader :event_id, :policy_node, :stack_trace, :time, :thread,
43
+ :object,
44
+ :ret,
45
+ :args,
46
+ :tags
86
47
 
87
48
  # We need this to track the parent id's of events to build up a flow
88
49
  # chart of the finding
@@ -106,16 +67,25 @@ module Contrast
106
67
  # was invoked
107
68
  def initialize policy_node, tagged, object, ret, args
108
69
  @policy_node = policy_node
109
- # so long as this event is built in a factory, we know Contrast Code
70
+
71
+ # Capture stacktraces only as configured.
72
+ #
73
+ # So long as this event is built in a factory, we know Contrast Code
110
74
  # will be the first three events
111
- @stack_trace = caller(3, 20)
75
+ @stack_trace = if ASSESS.capture_stacktrace?(policy_node)
76
+ caller(3, 20)
77
+ else
78
+ Contrast::Utils::ObjectShare::EMPTY_ARRAY
79
+ end
80
+
112
81
  @time = Contrast::Utils::Timer.now_ms
113
82
  @thread = Thread.current.object_id
114
83
 
115
84
  # These methods rely on the above being set. Don't move them!
116
85
  @event_id = Contrast::Agent::Assess::ContrastEvent.next_atomic_id
86
+ @tags = Contrast::Agent::Assess::Tracker.properties(tagged)&.tags
117
87
  find_parent_events!(policy_node, object, ret, args)
118
- snapshot!(tagged, object, ret, args)
88
+ snapshot!(object, ret, args)
119
89
  end
120
90
 
121
91
  def parent_events
@@ -128,22 +98,17 @@ module Contrast
128
98
  # Per TS law, each policy_node must have at least a source or a target.
129
99
  # The only type of policy_node w/o targets is a Trigger, but that may
130
100
  # 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.
101
+ # 2) I'll set the event's source and target to TS values.
102
+ # 3) Return the first source/target as the taint target.
138
103
  def determine_taint_target event_dtm
139
104
  if @policy_node&.targets&.any?
140
105
  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]
106
+ event_dtm.target = @policy_node.target_string
107
+ @policy_node.targets[0]
143
108
  elsif policy_node&.sources&.any?
144
- event_dtm.source = @highlight ? "P#{ @highlight }" : @policy_node.source_string
109
+ event_dtm.source = @policy_node.source_string
145
110
  event_dtm.target = @policy_node.target_string if @policy_node.target_string
146
- @highlight || @policy_node.sources[0]
111
+ @policy_node.sources[0]
147
112
  end
148
113
  end
149
114
 
@@ -193,16 +158,7 @@ module Contrast
193
158
  when Contrast::Utils::ObjectShare::RETURN_KEY
194
159
  ret
195
160
  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
161
+ args[source]
206
162
  end
207
163
  end
208
164
 
@@ -212,65 +168,28 @@ module Contrast
212
168
  # them for our later use. We set those safe values to this event's
213
169
  # instance variables.
214
170
  #
215
- # @param tagged [Object] the Target to which this event pertains.
216
171
  # @param object [Object] the Object on which the method was invoked
217
172
  # @param ret [Object] the Return of the invoked method
218
173
  # @param args [Array<Object>] the Arguments with which the method
219
174
  # 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
175
+ def snapshot! object, ret, args
176
+ @object = Contrast::Agent::Assess::ContrastObject.new(object) if object
177
+ @ret = Contrast::Agent::Assess::ContrastObject.new(ret) if ret
178
+ @args = safe_args_representation(args)
179
+ self
249
180
  end
250
181
 
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
182
+ # Given an array of arguments, copy them into a safe, meaning String,
183
+ # format that we can use to send to SR and TS for rendering.
254
184
  #
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
185
+ # @param args [Array<Object>] the arguments to translate
186
+ # @return [Array<Contrast::Agent::Assess::ContrastObject>] the String forms of those Objects, as
187
+ # determined by Contrast::Utils::ClassUtil.to_contrast_string
188
+ def safe_args_representation args
189
+ return unless args
190
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY if args.empty?
191
+
192
+ args.map { |arg| arg ? Contrast::Agent::Assess::ContrastObject.new(arg) : nil }
274
193
  end
275
194
  end
276
195
  end
@@ -0,0 +1,51 @@
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
+
6
+ module Contrast
7
+ module Agent
8
+ module Assess
9
+ # This class is a convenient holder of our version of an Object. It
10
+ # creates a String version of the Object from the original provided
11
+ # and keeps reference to the original's Tags, letting us determine if it
12
+ # was tracked when we try to report to TeamServer.
13
+ #
14
+ # @attr_reader object [String, nil] the Contrast string representing the
15
+ # object.
16
+ # @attr_reader object_type [String] the name of the object's module.
17
+ # @attr_reader tags [Hash{String => Contrast::Agent::Assess::Tag}, nil]
18
+ # the tags on the object before it was captured.
19
+ #
20
+ # TODO: RUBY-1083 determine if this is expensive and/or worth not storing
21
+ # these values directly on ContrastEvent and passing them around. Args
22
+ # probably make the argument for wrapping them b/c otherwise we'll have
23
+ # to keep two arrays in synch or make an array of arrays, at which
24
+ # point, we may as well make this.
25
+ class ContrastObject
26
+ attr_reader :object, :object_type, :tags
27
+
28
+ # Capture the details about the object which we need to render it in
29
+ # TeamServer.
30
+ #
31
+ # @param object [Object] the thing to keep a Contrast String of
32
+ def initialize object
33
+ if object
34
+ @object = Contrast::Utils::ClassUtil.to_contrast_string(object)
35
+ @object_type = object.cs__class.name
36
+ # TODO: RUBY-1084 determine if we need to copy these tags to
37
+ # restore immutability. For instance, if these tags were on a
38
+ # String that was then #reverse!'d, would our tags be wrong?
39
+ @tags = Contrast::Agent::Assess::Tracker.properties(object)&.tags
40
+ else
41
+ @object_type = Contrast::Utils::ObjectShare::NIL_STRING
42
+ end
43
+ end
44
+
45
+ def tracked?
46
+ tags&.any?
47
+ end
48
+ end
49
+ end
50
+ end
51
+ 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
@@ -52,12 +52,13 @@ module Contrast
52
52
  Contrast::Utils::ObjectShare::CLASS,
53
53
  Contrast::Utils::ObjectShare::MODULE
54
54
  ].cs__freeze
55
- def patch_assess_method clazz, method_name
55
+ def patch_assess_method mod, method_name
56
56
  # Module.define_method is called a lot in Class and Module. We
57
57
  # currently do not expect these define_methods to result in methods
58
58
  # that require patching, so for the sake of performance, we're going
59
59
  # to skip evaluating them
60
- class_name = clazz.cs__name
60
+ mod = mod.cs__class unless mod.cs__is_a?(Module)
61
+ class_name = mod.cs__class
61
62
  return if CLASS_TYPES.include?(class_name)
62
63
  return unless ASSESS.enabled?
63
64
 
@@ -73,7 +74,7 @@ module Contrast
73
74
  method_name: source_node.method_name,
74
75
  method_visibility: source_node.method_visibility,
75
76
  instance_method: true)
76
- patcher.patch_method(clazz, method_array, method_policy)
77
+ patcher.patch_method(mod, method_array, method_policy)
77
78
  end
78
79
  rescue StandardError => e
79
80
  logger.warn(
@@ -19,8 +19,8 @@ module Contrast
19
19
  @source_string = policy_hash[JSON_SOURCE]
20
20
  @target_string = policy_hash[JSON_TARGET]
21
21
  @tags = Set.new(policy_hash[JSON_TAGS])
22
- generate_sources
23
- generate_targets
22
+ @sources = convert_policy_markers(source_string)
23
+ @targets = convert_policy_markers(target_string)
24
24
  end
25
25
 
26
26
  def feature
@@ -47,7 +47,7 @@ module Contrast
47
47
 
48
48
  def target_string= value
49
49
  @target_string = value
50
- generate_targets
50
+ @targets = convert_policy_markers(value)
51
51
  end
52
52
 
53
53
  # Sometimes we need to tie information to an event. We'll add a
@@ -66,62 +66,6 @@ module Contrast
66
66
  @properties[name]
67
67
  end
68
68
 
69
- # Given a source in the format A,B,C, populate the sources of this node
70
- # 1) Split on ','
71
- # 2) If 'O', add the source, else it's P (we don't have R sources) and
72
- # needs to be converted. P type will either be P:name or P# where #
73
- # is the index of the parameter. Drop the P and store the int as int
74
- # or name as symbol
75
- def generate_sources
76
- if source_string
77
- @sources = []
78
- source_string.split(Contrast::Utils::ObjectShare::COMMA).each do |s|
79
- is_object = (s == Contrast::Utils::ObjectShare::OBJECT_KEY)
80
- if is_object
81
- @sources << s
82
- else
83
- parameter_source = s[1..-1]
84
- @sources << if parameter_source.start_with?(Contrast::Utils::ObjectShare::COLON)
85
- parameter_source[1..-1].to_sym
86
- else
87
- parameter_source.to_i
88
- end
89
- end
90
- end
91
- else
92
- @sources = Contrast::Utils::ObjectShare::EMPTY_ARRAY
93
- end
94
- end
95
-
96
- # Given a target in the format A,B,C, populate the targets of this node
97
- # 1) Split on ','
98
- # 2) If 'O' or 'R', add the target, else it's P and needs to be
99
- # converted. P type will either be P:name or P# where # is the index
100
- # of the paramter. Drop the P and store the int as int or name as
101
- # symbol
102
- def generate_targets
103
- if target_string
104
- @targets = []
105
- target_string.split(Contrast::Utils::ObjectShare::COMMA).each do |t|
106
- case t
107
- when Contrast::Utils::ObjectShare::OBJECT_KEY
108
- @targets << t
109
- when Contrast::Utils::ObjectShare::RETURN_KEY
110
- @targets << t
111
- else
112
- parameter_target = t[1..-1]
113
- @targets << if parameter_target.start_with?(Contrast::Utils::ObjectShare::COLON)
114
- parameter_target[1..-1].to_sym
115
- else
116
- parameter_target.to_i
117
- end
118
- end
119
- end
120
- else
121
- @targets = Contrast::Utils::ObjectShare::EMPTY_ARRAY
122
- end
123
- end
124
-
125
69
  # Don't let nodes be created that will be missing things we need
126
70
  # later on. Really, if they don't have these things, they couldn't have
127
71
  # done their jobs anyway.
@@ -186,6 +130,34 @@ module Contrast
186
130
  JSON_TARGET = 'target'
187
131
  JSON_TAGS = 'tags'
188
132
  JSON_DATAFLOW = 'dataflow'
133
+
134
+ private
135
+
136
+ # Given a policy string in the format A,B,C, populate the given array
137
+ # 1) Split on ','
138
+ # 2) If 'O' or 'R', add the array, else it's P and needs to be
139
+ # converted. P type will either be P# where # is the index
140
+ # of the parameter. Drop the P and store the # as an int.
141
+ #
142
+ # @param markers [String] the String from the policy to parse
143
+ # @return [Array] the array generated by converting the marker string
144
+ def convert_policy_markers markers
145
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless markers
146
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY if markers.empty?
147
+
148
+ converted = []
149
+ markers.split(Contrast::Utils::ObjectShare::COMMA).each do |t|
150
+ case t
151
+ when Contrast::Utils::ObjectShare::OBJECT_KEY,
152
+ Contrast::Utils::ObjectShare::RETURN_KEY
153
+
154
+ converted << t
155
+ else
156
+ converted << Integer(t[1..-1])
157
+ end
158
+ end
159
+ converted
160
+ end
189
161
  end
190
162
  end
191
163
  end