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

Inherits:
PolicyNode show all
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::TO_MARKER

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 inherited from PolicyNode

#add_property, #build_action, #feature, #generate_sources, #generate_targets, #get_property, #target, #validate_tags

Methods inherited from Patching::Policy::PolicyNode

#feature, #id, #instance_method?

Methods included from Components::Interface

included

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



34
35
36
37
38
39
40
41
42
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 34

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
end

Instance Attribute Details

#actionObject

Returns the value of attribute action.



23
24
25
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 23

def action
  @action
end

#patch_classObject

Returns the value of attribute patch_class.



23
24
25
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 23

def patch_class
  @patch_class
end

#patch_methodObject (readonly)

Returns the value of attribute patch_method.



22
23
24
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 22

def patch_method
  @patch_method
end

#untagsObject (readonly)

Returns the value of attribute untags.



22
23
24
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 22

def untags
  @untags
end

Instance Method Details

#needs_args?Boolean

Returns:

  • (Boolean)


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

def needs_args?
  @_needs_args ||= begin
    if action == Contrast::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION
      true
    elsif action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION
      true
    elsif sources.any? { |source| source.is_a?(Integer) || source.is_a?(Symbol) }
      true
    elsif targets.any? { |target| target.is_a?(Integer) || target.is_a?(Symbol) }
      true
    else
      false
    end
  end
end

#needs_object?Boolean

Returns:

  • (Boolean)


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

def needs_object?
  @_needs_object ||= begin
    if action == Contrast::Agent::Assess::Policy::PropagationMethod::CUSTOM_ACTION
      true
    elsif action == Contrast::Agent::Assess::Policy::PropagationMethod::DB_WRITE_ACTION
      true
    elsif sources.any? { |source| source == Contrast::Utils::ObjectShare::OBJECT_KEY }
      true
    elsif targets.any? { |target| target == Contrast::Utils::ObjectShare::OBJECT_KEY }
      true
    else
      false
    end
  end
end

#node_classObject



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

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



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

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)


121
122
123
124
125
126
127
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 121

def tagger?
  @_tagger ||= begin
    has_tags = tags&.any?
    has_untags = untags&.any?
    has_tags || has_untags
  end
end

#validateObject

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

Raises:

  • (ArgumentError)


60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 60

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

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

#validate_untagsObject



74
75
76
77
78
79
80
81
82
83
# File 'lib/contrast/agent/assess/policy/propagation_node.rb', line 74

def validate_untags
  return unless untags

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