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



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 39

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)

Returns the value of attribute action.



25
26
27
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 25

def action
  @action
end

#patch_classObject

Returns the value of attribute patch_class.



26
27
28
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 26

def patch_class
  @patch_class
end

#patch_methodObject (readonly)

Returns the value of attribute patch_method.



25
26
27
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 25

def patch_method
  @patch_method
end

#untagsObject (readonly)

Returns the value of attribute untags.



25
26
27
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 25

def untags
  @untags
end

Instance Method Details

#needs_args?Boolean

Returns:

  • (Boolean)


113
114
115
116
117
118
119
120
121
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 113

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:

  • (Boolean)


103
104
105
106
107
108
109
110
111
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 103

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_classObject



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

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

#node_typeObject

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



59
60
61
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 59

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)


127
128
129
130
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 127

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)


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 66

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



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 89

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