contrast-agent 3.16.0 → 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) 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 +2 -3
  6. data/lib/contrast/agent/assess/contrast_event.rb +49 -130
  7. data/lib/contrast/agent/assess/contrast_object.rb +51 -0
  8. data/lib/contrast/agent/assess/events/source_event.rb +4 -9
  9. data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
  10. data/lib/contrast/agent/assess/policy/policy_node.rb +31 -59
  11. data/lib/contrast/agent/assess/policy/policy_scanner.rb +17 -6
  12. data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
  13. data/lib/contrast/agent/assess/policy/propagation_method.rb +13 -19
  14. data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
  15. data/lib/contrast/agent/assess/policy/propagator/append.rb +1 -2
  16. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
  17. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  18. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -3
  19. data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -2
  20. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
  21. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
  22. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
  23. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
  24. data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
  25. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
  26. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
  27. data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -4
  28. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -4
  29. data/lib/contrast/agent/assess/policy/propagator/split.rb +73 -117
  30. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +11 -11
  31. data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
  32. data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
  33. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -8
  34. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  35. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
  36. data/lib/contrast/agent/assess/property/tagged.rb +21 -15
  37. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +3 -2
  38. data/lib/contrast/agent/assess/rule/redos.rb +1 -1
  39. data/lib/contrast/agent/assess/tracker.rb +16 -18
  40. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
  41. data/lib/contrast/agent/inventory.rb +15 -0
  42. data/lib/contrast/agent/inventory/dependencies.rb +50 -0
  43. data/lib/contrast/agent/inventory/dependency_analysis.rb +37 -0
  44. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +104 -0
  45. data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +38 -0
  46. data/lib/contrast/agent/middleware.rb +51 -3
  47. data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
  48. data/lib/contrast/agent/patching/policy/patch.rb +6 -0
  49. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
  50. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +4 -3
  51. data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
  52. data/lib/contrast/agent/protect/rule/base.rb +63 -14
  53. data/lib/contrast/agent/protect/rule/cmd_injection.rb +12 -28
  54. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
  55. data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
  56. data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
  57. data/lib/contrast/agent/protect/rule/sqli.rb +3 -3
  58. data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
  59. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
  60. data/lib/contrast/agent/reaction_processor.rb +1 -1
  61. data/lib/contrast/agent/request_handler.rb +1 -1
  62. data/lib/contrast/agent/response.rb +5 -5
  63. data/lib/contrast/agent/rewriter.rb +3 -3
  64. data/lib/contrast/agent/scope.rb +81 -55
  65. data/lib/contrast/agent/static_analysis.rb +14 -8
  66. data/lib/contrast/agent/tracepoint_hook.rb +1 -1
  67. data/lib/contrast/agent/version.rb +1 -1
  68. data/lib/contrast/api/decorators.rb +3 -0
  69. data/lib/contrast/api/decorators/address.rb +0 -1
  70. data/lib/contrast/api/decorators/application_update.rb +1 -1
  71. data/lib/contrast/api/decorators/library.rb +54 -0
  72. data/lib/contrast/api/decorators/library_usage_update.rb +31 -0
  73. data/lib/contrast/api/decorators/trace_event.rb +19 -31
  74. data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
  75. data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
  76. data/lib/contrast/api/decorators/user_input.rb +2 -1
  77. data/lib/contrast/common_agent_configuration.rb +2 -1
  78. data/lib/contrast/components/agent.rb +6 -5
  79. data/lib/contrast/components/assess.rb +36 -0
  80. data/lib/contrast/components/config.rb +29 -37
  81. data/lib/contrast/components/interface.rb +30 -6
  82. data/lib/contrast/components/inventory.rb +6 -1
  83. data/lib/contrast/components/scope.rb +72 -6
  84. data/lib/contrast/components/settings.rb +6 -3
  85. data/lib/contrast/config/assess_configuration.rb +2 -1
  86. data/lib/contrast/config/inventory_configuration.rb +2 -2
  87. data/lib/contrast/extension/assess/array.rb +2 -3
  88. data/lib/contrast/extension/assess/erb.rb +1 -3
  89. data/lib/contrast/extension/assess/exec_trigger.rb +1 -4
  90. data/lib/contrast/extension/assess/fiber.rb +2 -3
  91. data/lib/contrast/extension/assess/hash.rb +4 -2
  92. data/lib/contrast/extension/assess/kernel.rb +1 -2
  93. data/lib/contrast/extension/assess/marshal.rb +34 -26
  94. data/lib/contrast/extension/assess/regexp.rb +3 -8
  95. data/lib/contrast/extension/assess/string.rb +1 -2
  96. data/lib/contrast/framework/base_support.rb +51 -53
  97. data/lib/contrast/framework/manager.rb +3 -2
  98. data/lib/contrast/framework/rack/patch/session_cookie.rb +2 -2
  99. data/lib/contrast/framework/rack/support.rb +2 -1
  100. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
  101. data/lib/contrast/framework/rails/patch/assess_configuration.rb +1 -1
  102. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
  103. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +1 -1
  104. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +1 -1
  105. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +1 -1
  106. data/lib/contrast/framework/rails/support.rb +5 -1
  107. data/lib/contrast/framework/sinatra/support.rb +3 -2
  108. data/lib/contrast/logger/application.rb +1 -4
  109. data/lib/contrast/utils/duck_utils.rb +1 -1
  110. data/lib/contrast/utils/heap_dump_util.rb +1 -1
  111. data/lib/contrast/utils/inventory_util.rb +0 -7
  112. data/lib/contrast/utils/object_share.rb +3 -3
  113. data/lib/contrast/utils/preflight_util.rb +1 -1
  114. data/lib/contrast/utils/prevent_serialization.rb +1 -1
  115. data/lib/contrast/utils/resource_loader.rb +1 -1
  116. data/lib/contrast/utils/sha256_builder.rb +2 -14
  117. data/lib/contrast/utils/string_utils.rb +1 -1
  118. data/lib/contrast/utils/tag_util.rb +9 -13
  119. data/resources/assess/policy.json +9 -9
  120. data/resources/deadzone/policy.json +150 -0
  121. data/resources/protect/policy.json +12 -0
  122. data/ruby-agent.gemspec +10 -6
  123. data/service_executables/VERSION +1 -1
  124. data/service_executables/linux/contrast-service +0 -0
  125. data/service_executables/mac/contrast-service +0 -0
  126. metadata +76 -27
  127. data/lib/contrast/utils/boolean_util.rb +0 -30
  128. data/lib/contrast/utils/gemfile_reader.rb +0 -193
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2172066d6736b55c6754bb6913ec9fb9962ac1b818a85b4faa7ef822bb5df97
4
- data.tar.gz: 209286f4ef6ce8b688e3849a502ece6cfc914f795fae25b5cb417b3fa3998b50
3
+ metadata.gz: 678a3040ff60cf9b6222cab4a57115dc1c9d0df11c5238da4a6eaae0406d72a0
4
+ data.tar.gz: 792a68efe04d8c44f518a6d326b7b1c4a5377fce03b203752348c73352dc4a5b
5
5
  SHA512:
6
- metadata.gz: e6c19a309c1d7c7e2600d2f90d5da2664c315550be00475720165dde741d821d3ceb391282831aeb8ddcbe8e86b50b48d741d5c63d85c7a92c38ef0e54b7b0cd
7
- data.tar.gz: f4c1a92e5272730285b467c63768e31b1d6d7cb4266cbd6133c6de312603fcabc9c3bc814bc7f20d48fa444651fb040713ed1438b70076e9be9be396dab6603b
6
+ metadata.gz: 2bfca960a571eb7b9df1ae5017938be4de75c4b337ab4d35c33101a719c68a66b90fdfc0f777cadcd1ce1861d4c7b4e48cbc56f3e6d9b81cb02728c2718011d6
7
+ data.tar.gz: 250cae84e05ea4ea686de38c72ed1760ecd8a2d1787fa33d62a929c9869ed31e4445ef6cd30fcc95b99a57adb9d70b229767f8c9ed337f8f2312f09d5584ec4a
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);
@@ -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,8 +87,8 @@ 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'
@@ -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(