Class: Contrast::Agent::Assess::Policy::PolicyNode

Inherits:
Patching::Policy::PolicyNode show all
Includes:
PolicyNodeUtils, Components::Logger::InstanceMethods
Defined in:
lib/contrast/agent/assess/policy/policy_node.rb

Overview

This class functions to translate our policy.json into an actionable Ruby object, allowing for dynamic patching over hardcoded patching.

Direct Known Subclasses

PropagationNode, SourceNode, TriggerNode

Constant Summary collapse

JSON_TAGS =
'tags'
JSON_DATAFLOW =
'dataflow'
JSON_SOURCE =

The keys used to read from policy.json to create the individual policy nodes. These are common across node types

'source'
ALL_TYPE =
'A'
JSON_TARGET =
'target'
TO_MARKER =
'2'
ORIGINAL_OBJECT_METHODS =

Here are all methods that can use original objects without mutation the source. For methods with REMOVE action, their (!) bang alternative is not listed, since this methods tends to mutate the original (shortens it’s length, remove special symbols..) and causes mismatch in tags range representation of the target. In short preshift is needed.

%i[
  capitalize
  capitalize!
  chomp
  to_s
  to_str
  downcase
  downcase!
  lstrip
  strip
  upcase!
  upcase
].cs__freeze
TO_S =
%w[to_s to_str].cs__freeze
RESPONSE_SOURCES =

Here are all Responses that will be tracked as sources, or methods they use, like body.

%w[Net::HTTPResponse Rack::Response Sinatra::Response].cs__freeze

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 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 Patching::Policy::PolicyNode

#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(policy_hash = {}) ⇒ PolicyNode

Returns a new instance of PolicyNode.



55
56
57
58
59
60
61
62
63
64
65
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 55

def initialize policy_hash = {}
  super(policy_hash)
  @source_string = policy_hash[JSON_SOURCE]
  @target_string = policy_hash[JSON_TARGET]
  @tags = Set.new(policy_hash[JSON_TAGS])
  @sources = convert_policy_markers(source_string)
  @targets = convert_policy_markers(target_string)
  @_use_original_object = ORIGINAL_OBJECT_METHODS.include?(@method_name)
  @_use_original_on_bang_method = assign_on_bang_check(policy_hash)
  @_use_response_as_source = RESPONSE_SOURCES.include?(@class_name)
end

Instance Attribute Details

#source_stringObject (readonly)

Returns the value of attribute source_string.



30
31
32
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 30

def source_string
  @source_string
end

#sourcesObject (readonly)

Returns the value of attribute sources.



30
31
32
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 30

def sources
  @sources
end

#tagsObject

Returns the value of attribute tags.



29
30
31
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 29

def tags
  @tags
end

#target_stringObject

Returns the value of attribute target_string.



30
31
32
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 30

def target_string
  @target_string
end

#targetsObject (readonly)

Returns the value of attribute targets.



30
31
32
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 30

def targets
  @targets
end

#typeObject

Returns the value of attribute type.



29
30
31
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 29

def type
  @type
end

Instance Method Details

#add_property(name, value) ⇒ Object

Sometimes we need to tie information to an event. We’ll add a properties section to the patch node, which can pass them along to the pre-dtm event



99
100
101
102
103
104
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 99

def add_property name, value
  return unless name && value

  @properties ||= {}
  @properties[name] = value
end

#assign_on_bang_check(policy_hash) ⇒ Object

If we have KEEP action on String, and the method is to_s, that method would return self: String#to_s => self or string. This method is included here to cover the situations such as String.to_s.html_safe, where normally the dynamic sources properties get lost. To solve this we will simply return the original object here.



71
72
73
74
75
76
77
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 71

def assign_on_bang_check policy_hash
  return true if @_use_original_object && TO_S.include?(policy_hash[JSON_METHOD_NAME])

  @_use_original_object &&
      # Check if method name ends with a (!) bang unless is the to_s method:
      policy_hash[JSON_METHOD_NAME].end_with?(Contrast::Utils::ObjectShare::BANG)
end

#build_actionObject

Convert our action, built from our source and target, into the TS appropriate action. That’s a single source to single target marker (A,O,P,R)

Creation (source nodes) don’t have sources (they are the source) Trigger (trigger nodes) don’t have targets (they are the target) Everything else (propagation nodes) are Source2Target



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 146

def build_action
  @event_action ||= case node_class
                    when Contrast::Agent::Assess::Policy::SourceNode::SOURCE
                      :CREATION
                    when Contrast::Agent::Assess::Policy::TriggerNode::TRIGGER
                      :TRIGGER
                    else
                      if source_string.nil?
                        :CREATION
                      elsif target_string.nil?
                        :TRIGGER
                      else
                        # TeamServer can't handle the multi-source or multi-target values. Give it some help
                        # by changing them to 'A'
                        source = all_type?(source_string) ? ALL_TYPE : source_string
                        target = all_type?(target_string) ? ALL_TYPE : target_string
                        str = source[0] + TO_MARKER + target[0]
                        str.to_sym
                      end
                    end

  @event_action
end

#featureObject



79
80
81
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 79

def feature
  'Assess'
end

#get_property(name) ⇒ Object



106
107
108
109
110
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 106

def get_property name
  return unless @properties

  @properties[name]
end

#node_classObject



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

def node_class
  'Node'
end

#node_typeObject



87
88
89
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 87

def node_type
  :TYPE_METHOD
end

#response_source_node?Boolean

This method will check if the policy node is for response method.

Returns:

  • (Boolean)


198
199
200
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 198

def response_source_node?
  @_use_response_as_source
end

#use_original_object?Boolean

This method will check if a method is fit to use it’s original object and that the method is without bang - it does not change the source, but rather creates a copy of it.

Returns:

  • (Boolean)


175
176
177
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 175

def use_original_object?
  @_use_original_object && Contrast::ASSESS.track_original_object?
end

#use_original_on_bang_method?Boolean

This method will check if a method is fit to use it’s original object and that the target return is the same as object - a bang method modifying the source.

Returns:

  • (Boolean)


184
185
186
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 184

def use_original_on_bang_method?
  @_use_original_on_bang_method && Contrast::ASSESS.track_original_object?
end

#use_response_as_source?Boolean

This method will check if policy is fit to use response as source.

Returns:

  • (Boolean)


191
192
193
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 191

def use_response_as_source?
  Contrast::ASSESS.track_response_as_source?
end

#validateObject

Don’t let nodes be created that will be missing things we need later on. Really, if they don’t have these things, they couldn’t have done their jobs anyway.



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

def validate
  super
  validate_tags
rescue ArgumentError => e
  logger.debug('Validation of policy node failed with: ', e)
  nil
end

#validate_tagsObject

TeamServer is picky. The tags here match to ENUMs there. If there isn’t a matching ENUM in TS land, the database gets got. We really don’t want to get them, so we’re going to prevent the node from being made. @raise raises if any of the tags is invalid



128
129
130
131
132
133
134
135
136
137
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 128

def validate_tags
  return unless tags

  tags.each do |tag|
    next if Contrast::Agent::Reporting::FindingEventTaintRangeTags::VALID_TAGS.include?(tag) ||
        Contrast::Agent::Reporting::FindingEventTaintRangeTags::VALID_SOURCE_TAGS.include?(tag)

    raise(ArgumentError, "#{ node_class } #{ id } had an invalid tag. #{ tag } is not a known value.")
  end
end