Module: Contrast::Utils::Patching::PatchUtils

Included in:
Agent::Patching::Policy::Patch
Defined in:
lib/contrast/utils/patching/policy/patch_utils.rb

Overview

This module will include all methods for different patch applies from Patch module and some other module methods from the same place, so we can ease the main module

Instance Method Summary collapse

Instance Method Details

#apply_assess(method_policy, preshift, object, ret, args, block) ⇒ Object

Apply the Assess patches which apply to the given method.

Parameters:

  • method_policy (Contrast::Agent::Patching::Policy::MethodPolicy)

    Mapping of the triggers on the given method.

  • preshift (Contrast::Agent::Assess::PreShift)

    The capture of the state of the code just prior to the invocation of the patched method.

  • object (Object)

    The object on which the method was invoked, typically what would be returned by self.

  • ret (Object)

    The return of the method that was invoked.

  • args (Array<Object>)

    The arguments passed to the method being invoked.

  • block (Proc)

    The block passed to the method that was invoked.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 113

def apply_assess method_policy, preshift, object, ret, args, block
  return ret unless method_policy && ::Contrast::ASSESS.enabled?

  current_context = Contrast::Agent::REQUEST_TRACKER.current
  return ret if current_context && !current_context.analyze_request?

  trigger_node = method_policy.trigger_node

  if trigger_node && !trigger_node.nil?
    Contrast::Agent::Assess::Policy::TriggerMethod.apply_trigger_rule(trigger_node, object, ret, args)
  end
  if method_policy.source_node
    Contrast::Agent::Assess::Policy::SourceMethod.apply_source(method_policy, object, ret, args)
  end
  if method_policy.propagation_node
    Contrast::Agent::Assess::Policy::PropagationMethod.apply_propagation(
        method_policy,
        preshift,
        object,
        ret,
        args,
        block)
  end
rescue StandardError => e
  logger.error('Unable to assess method call.', e)
rescue Exception => e # rubocop:disable Lint/RescueException
  logger.error('Unable to assess method call due to exception.', e)
  raise(e)
ensure
  ret.rewind if Contrast::Utils::IOUtil.should_rewind?(ret)
end

#apply_inventory(method_policy, method, exception, object, args) ⇒ Object

Apply the Inventory patch which applies to the given method.

Parameters:

  • method_policy (Contrast::Agent::Patching::Policy::MethodPolicy)

    Mapping of the triggers on the given method.

  • method (Symbol)

    The method into which we’re patching

  • exception (StandardError)

    Any exception raised during the call of the patched method.

  • object (Object)

    The object on which the method is invoked, typically what would be returned by self.

  • args (Array<Object>)

    The arguments passed to the method being invoked.



97
98
99
100
101
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 97

def apply_inventory method_policy, method, exception, object, args
  return unless ::Contrast::INVENTORY.enable

  apply_trigger_only(method_policy&.inventory_node, method, exception, object, args)
end

#apply_post_patch(method_policy, preshift, object, ret, args, block) ⇒ Object

THIS IS CALLED FROM C. Do not change the signature lightly.

This method functions to call the infilter methods from our patches, allowing for analysis and reporting at the point just after the patched code is invoked

Parameters:

  • method_policy (Contrast::Agent::Patching::Policy::MethodPolicy)

    Mapping of the triggers on the given method.

  • preshift (Contrast::Agent::Assess::PreShift)

    The capture of the state of the code just prior to the invocation of the patched method.

  • object (Object)

    The object on which the method was invoked, typically what would be returned by self.

  • ret (Object)

    The return of the method that was invoked.

  • args (Array<Object>)

    The arguments passed to the method being invoked.

  • block (Proc)

    The block passed to the method that was invoked.



68
69
70
71
72
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 68

def apply_post_patch method_policy, preshift, object, ret, args, block
  apply_assess(method_policy, preshift, object, ret, args, block)
rescue StandardError => e
  logger.error('Unable to apply post patch to method.', e)
end

#apply_pre_patch(method_policy, method, exception, object, args) ⇒ Object

PATCH APPLIERS =====

THIS IS CALLED FROM C. Do not change the signature lightly.

This method functions to call the infilter methods from our patches, allowing for analysis and reporting at the point just before the patched code is invoked.

Parameters:

  • method_policy (Contrast::Agent::Patching::Policy::MethodPolicy)

    Mapping of the triggers on the given method.

  • method (Symbol)

    The method into which we’re patching

  • exception (StandardError)

    Any exception raised during the call of the patched method.

  • object (Object)

    The object on which the method is invoked, typically what would be returned by self.

  • args (Array<Object>)

    The arguments passed to the method being invoked.



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 43

def apply_pre_patch method_policy, method, exception, object, args
  apply_protect(method_policy, method, exception, object, args)
  apply_inventory(method_policy, method, exception, object, args)
rescue Contrast::SecurityException => e
  # We were told to block something, so we gotta. Don't catch this one, let it get back to our Middleware or
  # even all the way out to the framework
  raise(e)
rescue StandardError => e
  # Anything else was our bad and we gotta catch that to allow for normal application flow
  logger.error('Unable to apply pre patch to method.', e)
end

#apply_protect(method_policy, method, exception, object, args) ⇒ Object

Apply the Protect patch which applies to the given method.

Parameters:

  • method_policy (Contrast::Agent::Patching::Policy::MethodPolicy)

    Mapping of the triggers on the given method.

  • method (Symbol)

    The method into which we’re patching

  • exception (StandardError)

    Any exception raised during the call of the patched method.

  • object (Object)

    The object on which the method is invoked, typically what would be returned by self.

  • args (Array<Object>)

    The arguments passed to the method being invoked.



82
83
84
85
86
87
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 82

def apply_protect method_policy, method, exception, object, args
  return unless ::Contrast::AGENT.enabled?
  return unless ::Contrast::PROTECT.enabled?

  apply_trigger_only(method_policy&.protect_node, method, exception, object, args)
end

#apply_trigger_only(trigger_node, method, exception, object, args) ⇒ Object

Generic invocation of the Inventory or Protect patch which apply to the given method.

Parameters:

  • trigger_node (Contrast::Agent::Inventory::Policy::TriggerNode)

    Mapping of the specific trigger on the given method.

  • method (Symbol)

    The method into which we’re patching

  • exception (StandardError)

    Any exception raised during the call of the patched method.

  • object (Object)

    The object on which the method is invoked, typically what would be returned by self.

  • args (Array<Object>)

    The arguments passed to the method being invoked.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 153

def apply_trigger_only trigger_node, method, exception, object, args
  return unless trigger_node

  # If that rule only applies in the case of an exception being thrown and there's no exception here, move
  # along, or vice versa
  return if trigger_node.on_exception && !exception
  return if !trigger_node.on_exception && exception

  # Each patch has an applicator that handles logic for it. Think of this as being similar to propagator
  # actions, most closely resembling CUSTOM - they all have a common interface but their own logic based on
  # what's in the method(s) they've been patched into.
  # Each patch also knows the method of its applicator. Some things, like AppliesXxeRule, have different
  # methods depending on the library patched. This lets us handle the boilerplate of patching while still
  # allowing for custom handling of the methods.
  applicator_method = trigger_node.applicator_method
  # By calling send like this, we can reuse all the patching. We `send` to the given method of the given class
  # (applicator) since they all accept the same inputs
  trigger_node.applicator.send(applicator_method, method, exception, trigger_node.properties, object, args)
end

#build_method_name(patched_class, patched_method) ⇒ Symbol

Given a module and method, construct an expected name for the alias by which Contrast will reference the original.

Parameters:

  • patched_class (Module)

    the module being patched

  • patched_method (Symbol)

    the method being patched

Returns:

  • (Symbol)


16
17
18
19
20
21
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 16

def build_method_name patched_class, patched_method
  (Contrast::Utils::ObjectShare::CONTRAST_PATCHED_METHOD_START +
    patched_class.cs__name.gsub('::', '_').downcase +
    Contrast::Utils::ObjectShare::UNDERSCORE +
    patched_method.to_s).to_sym
end

#build_unbound_method_name(patcher_method) ⇒ Object

Given a method, return a symbol in the format <method_start>unbound<method_name>



25
26
27
28
29
# File 'lib/contrast/utils/patching/policy/patch_utils.rb', line 25

def build_unbound_method_name patcher_method
  "#{ Contrast::Utils::ObjectShare::CONTRAST_PATCHED_METHOD_START }unbound" \
  "#{ Contrast::Utils::ObjectShare::UNDERSCORE }" \
  "#{ patcher_method }".to_sym
end