contrast-agent 3.15.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) 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 +4 -12
  6. data/lib/contrast/agent/assess/contrast_event.rb +121 -130
  7. data/lib/contrast/agent/assess/contrast_object.rb +51 -0
  8. data/lib/contrast/agent/assess/events/source_event.rb +5 -10
  9. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +10 -3
  10. data/lib/contrast/agent/assess/policy/patcher.rb +4 -3
  11. data/lib/contrast/agent/assess/policy/policy_node.rb +46 -69
  12. data/lib/contrast/agent/assess/policy/policy_scanner.rb +19 -2
  13. data/lib/contrast/agent/assess/policy/preshift.rb +3 -3
  14. data/lib/contrast/agent/assess/policy/propagation_method.rb +13 -19
  15. data/lib/contrast/agent/assess/policy/propagation_node.rb +12 -24
  16. data/lib/contrast/agent/assess/policy/propagator/append.rb +1 -2
  17. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -2
  18. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  19. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -3
  20. data/lib/contrast/agent/assess/policy/propagator/insert.rb +2 -3
  21. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -2
  22. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -5
  23. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -2
  24. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -2
  25. data/lib/contrast/agent/assess/policy/propagator/remove.rb +2 -4
  26. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -2
  27. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -2
  28. data/lib/contrast/agent/assess/policy/propagator/select.rb +4 -7
  29. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -9
  30. data/lib/contrast/agent/assess/policy/propagator/split.rb +77 -122
  31. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +32 -25
  32. data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -7
  33. data/lib/contrast/agent/assess/policy/source_method.rb +2 -14
  34. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +9 -13
  35. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +1 -1
  36. data/lib/contrast/agent/assess/policy/trigger_method.rb +39 -14
  37. data/lib/contrast/agent/assess/policy/trigger_node.rb +31 -37
  38. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -1
  39. data/lib/contrast/agent/assess/property/evented.rb +5 -18
  40. data/lib/contrast/agent/assess/property/tagged.rb +28 -16
  41. data/lib/contrast/agent/assess/property/updated.rb +0 -5
  42. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
  43. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +23 -8
  44. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +83 -14
  45. data/lib/contrast/agent/assess/rule/redos.rb +1 -1
  46. data/lib/contrast/agent/assess/tag.rb +1 -1
  47. data/lib/contrast/agent/assess/tracker.rb +16 -18
  48. data/lib/contrast/agent/at_exit_hook.rb +5 -5
  49. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +7 -0
  50. data/lib/contrast/agent/inventory.rb +15 -0
  51. data/lib/contrast/agent/inventory/dependencies.rb +50 -0
  52. data/lib/contrast/agent/inventory/dependency_analysis.rb +37 -0
  53. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +104 -0
  54. data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +38 -0
  55. data/lib/contrast/agent/middleware.rb +51 -3
  56. data/lib/contrast/agent/patching/policy/after_load_patch.rb +5 -5
  57. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +20 -20
  58. data/lib/contrast/agent/patching/policy/method_policy.rb +1 -1
  59. data/lib/contrast/agent/patching/policy/module_policy.rb +10 -10
  60. data/lib/contrast/agent/patching/policy/patch.rb +6 -0
  61. data/lib/contrast/agent/patching/policy/policy.rb +16 -2
  62. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +3 -5
  63. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +47 -1
  64. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +4 -3
  65. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
  66. data/lib/contrast/agent/protect/policy/rule_applicator.rb +53 -0
  67. data/lib/contrast/agent/protect/rule/base.rb +63 -14
  68. data/lib/contrast/agent/protect/rule/cmd_injection.rb +12 -28
  69. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -4
  70. data/lib/contrast/agent/protect/rule/deserialization.rb +4 -1
  71. data/lib/contrast/agent/protect/rule/no_sqli.rb +3 -3
  72. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
  73. data/lib/contrast/agent/protect/rule/sqli.rb +3 -3
  74. data/lib/contrast/agent/protect/rule/xxe.rb +32 -11
  75. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +10 -6
  76. data/lib/contrast/agent/reaction_processor.rb +1 -1
  77. data/lib/contrast/agent/request.rb +34 -34
  78. data/lib/contrast/agent/request_handler.rb +1 -1
  79. data/lib/contrast/agent/response.rb +5 -5
  80. data/lib/contrast/agent/rewriter.rb +3 -3
  81. data/lib/contrast/agent/scope.rb +81 -55
  82. data/lib/contrast/agent/static_analysis.rb +15 -9
  83. data/lib/contrast/agent/tracepoint_hook.rb +1 -1
  84. data/lib/contrast/agent/version.rb +1 -1
  85. data/lib/contrast/api/communication/socket_client.rb +36 -1
  86. data/lib/contrast/api/decorators.rb +3 -0
  87. data/lib/contrast/api/decorators/address.rb +13 -14
  88. data/lib/contrast/api/decorators/application_update.rb +1 -1
  89. data/lib/contrast/api/decorators/library.rb +54 -0
  90. data/lib/contrast/api/decorators/library_usage_update.rb +31 -0
  91. data/lib/contrast/api/decorators/message.rb +1 -0
  92. data/lib/contrast/api/decorators/trace_event.rb +31 -41
  93. data/lib/contrast/api/decorators/trace_event_object.rb +11 -3
  94. data/lib/contrast/api/decorators/trace_event_signature.rb +27 -5
  95. data/lib/contrast/api/decorators/user_input.rb +2 -1
  96. data/lib/contrast/common_agent_configuration.rb +2 -1
  97. data/lib/contrast/components/agent.rb +6 -5
  98. data/lib/contrast/components/app_context.rb +39 -30
  99. data/lib/contrast/components/assess.rb +36 -0
  100. data/lib/contrast/components/config.rb +29 -37
  101. data/lib/contrast/components/contrast_service.rb +9 -9
  102. data/lib/contrast/components/interface.rb +30 -6
  103. data/lib/contrast/components/inventory.rb +6 -1
  104. data/lib/contrast/components/scope.rb +72 -6
  105. data/lib/contrast/components/settings.rb +23 -23
  106. data/lib/contrast/config/assess_configuration.rb +2 -1
  107. data/lib/contrast/config/inventory_configuration.rb +2 -2
  108. data/lib/contrast/config/service_configuration.rb +4 -2
  109. data/lib/contrast/configuration.rb +1 -1
  110. data/lib/contrast/extension/assess/array.rb +9 -6
  111. data/lib/contrast/extension/assess/erb.rb +6 -3
  112. data/lib/contrast/extension/assess/eval_trigger.rb +6 -6
  113. data/lib/contrast/extension/assess/exec_trigger.rb +0 -3
  114. data/lib/contrast/extension/assess/fiber.rb +5 -6
  115. data/lib/contrast/extension/assess/hash.rb +7 -5
  116. data/lib/contrast/extension/assess/kernel.rb +19 -22
  117. data/lib/contrast/extension/assess/marshal.rb +40 -28
  118. data/lib/contrast/extension/assess/regexp.rb +6 -11
  119. data/lib/contrast/extension/assess/string.rb +14 -13
  120. data/lib/contrast/extension/protect/kernel.rb +3 -3
  121. data/lib/contrast/framework/base_support.rb +51 -53
  122. data/lib/contrast/framework/manager.rb +6 -5
  123. data/lib/contrast/framework/rack/patch/session_cookie.rb +10 -10
  124. data/lib/contrast/framework/rack/support.rb +2 -1
  125. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +14 -14
  126. data/lib/contrast/framework/rails/patch/assess_configuration.rb +1 -1
  127. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +11 -11
  128. data/lib/contrast/framework/rails/patch/support.rb +1 -1
  129. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +12 -12
  130. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +13 -13
  131. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -3
  132. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +13 -13
  133. data/lib/contrast/framework/rails/support.rb +5 -1
  134. data/lib/contrast/framework/sinatra/patch/base.rb +11 -11
  135. data/lib/contrast/framework/sinatra/support.rb +7 -6
  136. data/lib/contrast/logger/application.rb +1 -4
  137. data/lib/contrast/logger/log.rb +7 -2
  138. data/lib/contrast/utils/duck_utils.rb +1 -1
  139. data/lib/contrast/utils/heap_dump_util.rb +1 -1
  140. data/lib/contrast/utils/invalid_configuration_util.rb +2 -5
  141. data/lib/contrast/utils/inventory_util.rb +0 -7
  142. data/lib/contrast/utils/object_share.rb +3 -3
  143. data/lib/contrast/utils/preflight_util.rb +1 -1
  144. data/lib/contrast/utils/prevent_serialization.rb +1 -1
  145. data/lib/contrast/utils/resource_loader.rb +1 -1
  146. data/lib/contrast/utils/sha256_builder.rb +2 -14
  147. data/lib/contrast/utils/string_utils.rb +1 -1
  148. data/lib/contrast/utils/tag_util.rb +9 -13
  149. data/resources/assess/policy.json +31 -12
  150. data/resources/deadzone/policy.json +156 -0
  151. data/resources/protect/policy.json +12 -0
  152. data/ruby-agent.gemspec +11 -6
  153. data/service_executables/VERSION +1 -1
  154. data/service_executables/linux/contrast-service +0 -0
  155. data/service_executables/mac/contrast-service +0 -0
  156. metadata +91 -28
  157. data/lib/contrast/utils/boolean_util.rb +0 -30
  158. data/lib/contrast/utils/gemfile_reader.rb +0 -193
@@ -21,7 +21,7 @@ module Contrast
21
21
  @request = Contrast::Agent::REQUEST_TRACKER.current&.request
22
22
  end
23
23
 
24
- def find_parent_ids _policy_node, _object, _ret, _args
24
+ def parent_events
25
25
  nil
26
26
  end
27
27
 
@@ -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
@@ -110,16 +110,23 @@ module Contrast
110
110
  dynamic_source.method_name = Contrast::Utils::StringUtils.force_utf8(field)
111
111
  dynamic_source.instance_method = source_node.instance_method?
112
112
  dynamic_source.target = Contrast::Utils::StringUtils.force_utf8(source_node.target_string)
113
- properties.events.each do |event|
114
- dynamic_source.events << event.to_dtm_event
115
- end
116
113
  dynamic_source.properties[READ_TABLE] = Contrast::Utils::StringUtils.force_utf8(source_node.class_name)
117
114
  dynamic_source.properties[READ_COLUMN] = Contrast::Utils::StringUtils.force_utf8(field)
118
115
  dynamic_source.properties[WRITE_QUERY_TIME] = Contrast::Utils::StringUtils.force_utf8(Contrast::Utils::Timer.now_ms)
119
116
  url = current_context.request.normalized_uri
120
117
  dynamic_source.properties[WRITE_QUERY_URL] = Contrast::Utils::StringUtils.force_utf8(url)
118
+ append_events(dynamic_source, properties.event)
121
119
  dynamic_source
122
120
  end
121
+
122
+ def append_events dynamic_source, event
123
+ return unless event
124
+
125
+ event.parent_events&.each do |parent_event|
126
+ append_events(dynamic_source, parent_event)
127
+ end
128
+ dynamic_source.events << event.to_dtm_event
129
+ end
123
130
  end
124
131
  end
125
132
  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.
@@ -157,19 +101,24 @@ module Contrast
157
101
  # Everything else (propagation nodes) are Source2Target
158
102
  def build_action
159
103
  @event_action ||= begin
160
- source = source_string
161
- target = target_string
162
- if source.nil?
104
+ case node_class
105
+ when Contrast::Agent::Assess::Policy::SourceNode::SOURCE
163
106
  :CREATION
164
- elsif target.nil?
107
+ when Contrast::Agent::Assess::Policy::TriggerNode::TRIGGER
165
108
  :TRIGGER
166
109
  else
167
- # TeamServer can't handle the multi-source or multi-target
168
- # values. Give it some help by changing them to 'A'
169
- source = ALL_TYPE if source.include?(Contrast::Utils::ObjectShare::COMMA)
170
- target = ALL_TYPE if target.include?(Contrast::Utils::ObjectShare::COMMA)
171
- str = source[0] + TO_MARKER + target[0]
172
- str.to_sym
110
+ if source_string.nil?
111
+ :CREATION
112
+ elsif target_string.nil?
113
+ :TRIGGER
114
+ else
115
+ # TeamServer can't handle the multi-source or multi-target
116
+ # values. Give it some help by changing them to 'A'
117
+ source = source_string.include?(Contrast::Utils::ObjectShare::COMMA) ? ALL_TYPE : source_string
118
+ target = target_string.include?(Contrast::Utils::ObjectShare::COMMA) ? ALL_TYPE : target_string
119
+ str = source[0] + TO_MARKER + target[0]
120
+ str.to_sym
121
+ end
173
122
  end
174
123
  end
175
124
  @event_action
@@ -181,6 +130,34 @@ module Contrast
181
130
  JSON_TARGET = 'target'
182
131
  JSON_TAGS = 'tags'
183
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
184
161
  end
185
162
  end
186
163
  end
@@ -17,18 +17,35 @@ module Contrast
17
17
  access_component :analysis
18
18
 
19
19
  class << self
20
+ # Use the given trace_point, built from an :end event, to determine
21
+ # where the loaded code lives and scan that code for policy
22
+ # violations.
23
+ #
24
+ # @param trace_point [TracePoint] the TracePoint generated by an
25
+ # :end event at the end of a Module definition.
20
26
  def scan trace_point
21
27
  return unless ASSESS.enabled?
22
28
  return unless ASSESS.require_scan?
23
29
 
30
+ provider_values = policy.providers.values
31
+ return if provider_values.all?(&:disabled?)
32
+
24
33
  return unless trace_point.path
25
34
  return if trace_point.path.start_with?(Gem.dir)
26
35
 
27
36
  mod = trace_point.self
28
37
  return if mod.cs__frozen? || mod.singleton_class?
29
38
 
30
- policy.providers.each_value do |provider|
31
- provider.analyze mod
39
+ # TODO: RUBY-1014 - remove non-AST approach
40
+ if RUBY_VERSION >= '2.6.0'
41
+ ast = RubyVM::AbstractSyntaxTree.parse_file(trace_point.path)
42
+ provider_values.each do |provider|
43
+ provider.parse(trace_point, ast)
44
+ end
45
+ else
46
+ provider_values.each do |provider|
47
+ provider.analyze(mod)
48
+ end
32
49
  end
33
50
  end
34
51
 
@@ -36,11 +36,11 @@ module Contrast
36
36
  # the state of the object and arguments just prior to the method
37
37
  # being called or nil if one is not required.
38
38
  def build_preshift propagation_node, object, args
39
- return nil unless propagation_node
40
- return nil unless ASSESS.enabled?
39
+ return unless propagation_node
40
+ return unless ASSESS.enabled?
41
41
 
42
42
  initializing = propagation_node.method_name == :initialize
43
- return nil if unsafe_io_object?(object, initializing)
43
+ return if unsafe_io_object?(object, initializing)
44
44
 
45
45
  needs_object = propagation_node.needs_object?
46
46
  needs_args = propagation_node.needs_args?
@@ -37,20 +37,15 @@ module Contrast
37
37
 
38
38
  class << self
39
39
  def determine_target propagation_node, ret, object, args
40
- target_key = propagation_node.targets[0]
41
- return ret if target_key == Contrast::Utils::ObjectShare::RETURN_KEY
42
- return object if target_key == Contrast::Utils::ObjectShare::OBJECT_KEY
43
-
44
- return args[target_key] if target_key.is_a?(Integer)
45
-
46
- arg = nil
47
- args.each do |search|
48
- next unless search.is_a?(Hash)
49
-
50
- arg = search[target_key]
51
- break if arg
40
+ target = propagation_node.targets[0]
41
+ case target
42
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
43
+ object
44
+ when Contrast::Utils::ObjectShare::RETURN_KEY
45
+ ret
46
+ else
47
+ args[target]
52
48
  end
53
- arg
54
49
  end
55
50
 
56
51
  # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy]
@@ -208,8 +203,8 @@ module Contrast
208
203
  # If this patcher has tags, apply them to the entire target
209
204
  def apply_tags propagation_node, target
210
205
  return unless propagation_node.tags
206
+ return unless (properties = Contrast::Agent::Assess::Tracker.properties(target))
211
207
 
212
- properties = Contrast::Agent::Assess::Tracker.properties(target)
213
208
  length = Contrast::Utils::StringUtils.ret_length(target)
214
209
  propagation_node.tags.each do |tag|
215
210
  properties.add_tag(tag, 0...length)
@@ -219,9 +214,7 @@ module Contrast
219
214
  # If this patcher has tags, remove them from the entire target
220
215
  def apply_untags propagation_node, target
221
216
  return unless propagation_node.untags
222
-
223
- properties = Contrast::Agent::Assess::Tracker.properties(target)
224
- return unless properties
217
+ return unless (properties = Contrast::Agent::Assess::Tracker.properties(target))
225
218
 
226
219
  propagation_node.untags.each do |tag|
227
220
  properties.delete_tags(tag)
@@ -256,7 +249,7 @@ module Contrast
256
249
 
257
250
  def handle_enumerable_propagation propagation_node, preshift, target, object, ret, args, block
258
251
  target.each do |value|
259
- next if target == value # Some Enumerable#each are overriden to return self the first time which leads to infinite propagation
252
+ next if target == value # Some Enumerable#each are overridden to return self the first time which leads to infinite propagation
260
253
 
261
254
  apply_propagator(propagation_node, preshift, value, object, ret, args, block)
262
255
  end
@@ -300,7 +293,8 @@ module Contrast
300
293
  # both and there should never be a propagator that has a tag in
301
294
  # its untag.
302
295
  apply_untags(propagation_node, target)
303
- properties = Contrast::Agent::Assess::Tracker.properties(target)
296
+ return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
297
+
304
298
  properties.add_properties(propagation_node.properties)
305
299
  properties.build_event(propagation_node, target, object, ret, args)
306
300
  logger.trace('Propagation detected',
@@ -83,35 +83,23 @@ module Contrast
83
83
  end
84
84
 
85
85
  def needs_object?
86
- @_needs_object ||= begin
87
- if action == Contrast::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION
88
- true
89
- elsif action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION
90
- true
91
- elsif sources.any? { |source| source == Contrast::Utils::ObjectShare::OBJECT_KEY }
92
- true
93
- elsif targets.any? { |target| target == Contrast::Utils::ObjectShare::OBJECT_KEY }
94
- true
95
- else
96
- false
97
- end
86
+ if @_needs_object.nil?
87
+ @_needs_object = action == Contrast::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION ||
88
+ action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION ||
89
+ sources.any? { |source| source == Contrast::Utils::ObjectShare::OBJECT_KEY } ||
90
+ targets.any? { |target| target == Contrast::Utils::ObjectShare::OBJECT_KEY }
98
91
  end
92
+ @_needs_object
99
93
  end
100
94
 
101
95
  def needs_args?
102
- @_needs_args ||= begin
103
- if action == Contrast::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION
104
- true
105
- elsif action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION
106
- true
107
- elsif sources.any? { |source| source.is_a?(Integer) || source.is_a?(Symbol) }
108
- true
109
- elsif targets.any? { |target| target.is_a?(Integer) || target.is_a?(Symbol) }
110
- true
111
- else
112
- false
113
- end
96
+ if @_needs_args.nil?
97
+ @_needs_args = action == Contrast::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION ||
98
+ action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION ||
99
+ sources.any? { |source| source.is_a?(Integer) || source.is_a?(Symbol) } ||
100
+ targets.any? { |target| target.is_a?(Integer) || target.is_a?(Symbol) }
114
101
  end
102
+ @_needs_args
115
103
  end
116
104
 
117
105
  # This is a tagger if it has a tag or an untag.
@@ -16,8 +16,7 @@ module Contrast
16
16
  # copy tags from the param to the target in chunks of param size or less
17
17
  # if param is appended in space less than param length
18
18
  def propagate propagation_node, preshift, target
19
- properties = Contrast::Agent::Assess::Tracker.properties(target)
20
- return unless properties
19
+ return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
21
20
 
22
21
  sources = propagation_node.sources
23
22
  source1 = find_source(sources[0], preshift)
@@ -12,8 +12,7 @@ module Contrast
12
12
  class Center < Contrast::Agent::Assess::Policy::Propagator::Base
13
13
  class << self
14
14
  def propagate propagation_node, preshift, target
15
- properties = Contrast::Agent::Assess::Tracker.properties(target)
16
- return unless properties
15
+ return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
17
16
 
18
17
  sources = propagation_node.sources
19
18
  source1 = find_source(sources[0], preshift)
@@ -12,7 +12,7 @@ module Contrast
12
12
  # of tags from the source to the target. Each node with the CUSTOM
13
13
  # action knows the class and method it should call to preform this
14
14
  # action.
15
- class Custom
15
+ module Custom
16
16
  class << self
17
17
  def propagate propagation_node, preshift, ret, block
18
18
  clazz = propagation_node.patch_class
@@ -30,9 +30,7 @@ module Contrast
30
30
  arg.each_pair do |key, value|
31
31
  next unless value
32
32
  next if known_tainted&.include?(key)
33
-
34
- properties = Contrast::Agent::Assess::Tracker.properties(value)
35
- next unless properties
33
+ next unless (properties = Contrast::Agent::Assess::Tracker.properties!(value))
36
34
 
37
35
  # TODO: RUBY-540 handle sanitization, handle nested objects
38
36
  Contrast::Agent::Assess::Policy::PropagationMethod.apply_tags(propagation_node, value)