Class: Contrast::Agent::Assess::Policy::PropagationNode

Inherits:
PolicyNode show all
Includes:
Components::Logger::InstanceMethods
Defined in:
lib/contrast/agent/assess/policy/propagation_node.rb

Overview

This class functions to translate our policy.json into an actionable Ruby object, allowing for dynamic patching over hardcoded patching, specifically for those methods which result in the transformation of untrusted data (indicate points in the application where user controlled input is modified).

Constant Summary collapse

JSON_ACTION =
'action'
JSON_UNTAGS =
'untags'
JSON_PATCH_CLASS =
'patch_class'
JSON_PATCH_METHOD =
'patch_method'
TAGGER =
'Tagger'
PROPAGATOR =
'Propagator'

Constants inherited from PolicyNode

Contrast::Agent::Assess::Policy::PolicyNode::ALL_TYPE, Contrast::Agent::Assess::Policy::PolicyNode::JSON_DATAFLOW, Contrast::Agent::Assess::Policy::PolicyNode::JSON_SOURCE, Contrast::Agent::Assess::Policy::PolicyNode::JSON_TAGS, Contrast::Agent::Assess::Policy::PolicyNode::JSON_TARGET, Contrast::Agent::Assess::Policy::PolicyNode::ORIGINAL_OBJECT_METHODS, Contrast::Agent::Assess::Policy::PolicyNode::RESPONSE_SOURCES, Contrast::Agent::Assess::Policy::PolicyNode::TO_MARKER, Contrast::Agent::Assess::Policy::PolicyNode::TO_S

Constants inherited from Patching::Policy::PolicyNode

Patching::Policy::PolicyNode::JSON_CLASS_NAME, Patching::Policy::PolicyNode::JSON_INSTANCE_METHOD, Patching::Policy::PolicyNode::JSON_METHOD_NAME, Patching::Policy::PolicyNode::JSON_METHOD_SCOPE, Patching::Policy::PolicyNode::JSON_METHOD_VISIBILITY, Patching::Policy::PolicyNode::JSON_PROPERTIES

Instance Attribute Summary collapse

Attributes inherited from PolicyNode

#source_string, #sources, #tags, #target_string, #targets, #type

Attributes inherited from Patching::Policy::PolicyNode

#class_name, #instance_method, #method_name, #method_scope, #method_visibility, #properties

Instance Method Summary collapse

Methods included from Components::Logger::InstanceMethods

#cef_logger, #logger

Methods inherited from PolicyNode

#add_property, #assign_on_bang_check, #build_action, #feature, #get_property, #response_source_node?, #use_original_object?, #use_original_on_bang_method?, #use_response_as_source?, #validate_tags

Methods inherited from Patching::Policy::PolicyNode

#feature, #id, #instance_method?

Methods included from Components::Scope::InstanceMethods

#contrast_enter_method_scopes!, #contrast_exit_method_scopes!, #with_app_scope, #with_contrast_scope, #with_deserialization_scope, #with_split_scope

Constructor Details

#initialize(propagation_hash = {}) ⇒ PropagationNode

Most things here carry over from PolicyNode. A couple things are new / have new rules

Source - from where the tainted data flows, cannot be nil Target - to where the tainted data flows, cannot be nil Action - how the tainted data flows from source to target, should not be nil Tags - array of tags to apply to the target, can be nil if no tags are added Untags - array of tags to remove from the target, can be nil if not tags are removed id, class_name, instance_method, method_name, source, target, action, tags = nil, untags = nil

Parameters:

  • propagation_hash (Hash) (defaults to: {})

    the hash from which to build the propagation node.



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 69

def initialize propagation_hash = {}
  super(propagation_hash)
  @action = propagation_hash[JSON_ACTION]
  @untags = Set.new(propagation_hash[JSON_UNTAGS])
  @patch_class = propagation_hash[JSON_PATCH_CLASS]
  @patch_method = propagation_hash[JSON_PATCH_METHOD]
  @patch_method = @patch_method.to_sym if @patch_method
  validate
rescue ArgumentError => e
  logger.error('Propagation Node Initialization failed with: ', e)
  nil
end

Instance Attribute Details

#actionObject (readonly)

  • ‘CUSTOM’ - the propagation node is a custom propagation node

  • ‘DB_WRITE’ - the propagation node is a database write propagation

node
  • ‘APPEND’ - the propagation node is an append propagation node

  • ‘CENTER’ - the propagation node is a center propagation node

  • ‘INSERT’ - the propagation node is an insert propagation node

  • ‘KEEP’ - the propagation node is a keep propagation node

  • ‘NEXT’ - the propagation node is a next propagation node

  • ‘BUFFER’ - the propagation node is a buffer propagation node

  • ‘NOOP’ - the propagation node is a noop propagation node

  • ‘PREPEND’ - the propagation node is a prepend propagation node

  • ‘REPLACE’ - the propagation node is a replace propagation node

  • ‘REMOVE’ - the propagation node is a remove propagation node

  • ‘REVERSE’ - the propagation node is a reverse propagation node

  • ‘RESPONSE’ - the propagation node is a response propagation node

  • ‘SPLAT’ - the propagation node is a splat propagation node

  • ‘SPLIT’ - the propagation node is a split propagation node



44
45
46
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 44

def action
  @action
end

#patch_classObject

for this propagation node.



53
54
55
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 53

def patch_class
  @patch_class
end

#patch_methodObject (readonly)

contains the method to be called for this propagation node.



50
51
52
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 50

def patch_method
  @patch_method
end

#untagsObject (readonly)

target of this propagation node.



47
48
49
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 47

def untags
  @untags
end

Instance Method Details

#needs_args?Boolean

Returns:

  • (Boolean)


150
151
152
153
154
155
156
157
158
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 150

def needs_args?
  if @_needs_args.nil?
    @_needs_args = action == Contrast::Utils::Assess::PropagationMethodUtils::CUSTOM_ACTION ||
        action == Contrast::Utils::Assess::PropagationMethodUtils::DB_WRITE_ACTION ||
        sources.any? { |source| source.is_a?(Integer) || source.is_a?(Symbol) } ||
        targets.any? { |target| target.is_a?(Integer) || target.is_a?(Symbol) }
  end
  @_needs_args
end

#needs_object?Boolean

Returns propagation node.

Returns:

  • (Boolean)

    propagation node.



139
140
141
142
143
144
145
146
147
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 139

def needs_object?
  if @_needs_object.nil?
    @_needs_object = action == Contrast::Utils::Assess::PropagationMethodUtils::CUSTOM_ACTION ||
        action == Contrast::Utils::Assess::PropagationMethodUtils::DB_WRITE_ACTION ||
        sources.any?(Contrast::Utils::ObjectShare::OBJECT_KEY) ||
        targets.any?(Contrast::Utils::ObjectShare::OBJECT_KEY)
  end
  @_needs_object
end

#node_classString

Returns class name.

Returns:



83
84
85
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 83

def node_class
  @_node_class ||= tagger? ? TAGGER : PROPAGATOR
end

#node_typeSymbol

Unlike the other agents, we don’t have separate tag & propagation events. To make TS happy, we need to have different types though. Pretty straight forward: if there’s a tag, this is a tagger

Returns:

  • (Symbol)

    the type of node.



92
93
94
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 92

def node_type
  tagger? ? :TYPE_TAG : :TYPE_PROPAGATION
end

#tagger?Boolean

This is a tagger if it has a tag or an untag. It indicates this method is more than just a transformation, it is an interesting security event that has a meaningful change.

Returns:

  • (Boolean)


166
167
168
169
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 166

def tagger?
  @_tagger = tags&.any? || untags&.any? if @_tagger.nil?
  @_tagger
end

#validateObject

Standard validation + TS trace version two rules: Must have source, target, and action

@raise raises if any of the required propagation node field is not valid, or is missing

Raises:

  • (ArgumentError)


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 100

def validate
  super
  raise(ArgumentError, "Propagator #{ id } did not have a proper action. Unable to create.") unless action

  if @action == 'CUSTOM'
    unless patch_class
      raise(ArgumentError, "Propagator #{ id } did not have a proper patch_class. Unable to create.")
    end
    unless patch_method.is_a?(Symbol)
      raise(ArgumentError, "Propagator #{ id } did not have a proper patch_method. Unable to create.")
    end
  else
    unless targets&.any?
      raise(ArgumentError, "Propagator #{ id } did not have a proper target. Unable to create.")
    end
    unless sources&.any?
      raise(ArgumentError, "Propagator #{ id } did not have a proper source. Unable to create.")
    end
  end
  validate_untags
end

#validate_untagsObject

@raise raises if any of the tags is invalid



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 123

def validate_untags
  return unless untags

  untags.each do |tag|
    unless Contrast::Agent::Reporting::FindingEventTaintRangeTags::VALID_TAGS.include?(tag)
      raise(ArgumentError,
            "#{ node_type } #{ id } did not have a valid untag. #{ tag } is not a known value.")
    end
    if tags&.include?(tag)
      raise(ArgumentError, "#{ node_type } #{ id } had the same tag and untag, #{ tag }.")
    end
  end
end