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.



64
65
66
67
68
69
70
71
72
73
74
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 64

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_stringString (readonly)

Returns:



37
38
39
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 37

def source_string
  @source_string
end

#sourcesObject (readonly)

Returns the value of attribute sources.



33
34
35
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 33

def sources
  @sources
end

#tagsSet<String>

Returns:



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

def tags
  @tags
end

#target_stringString

Returns:



39
40
41
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 39

def target_string
  @target_string
end

#targetsArray<String> (readonly)

Returns:



35
36
37
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 35

def targets
  @targets
end

#typeSymbol

Returns:

  • (Symbol)


32
33
34
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 32

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

Parameters:



119
120
121
122
123
124
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 119

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.

Parameters:

  • policy_hash (Hash)


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

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



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 169

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
                        (source[0] + TO_MARKER + target[0]).to_sym
                      end
                    end

  @event_action
end

#featureString

Returns:



91
92
93
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 91

def feature
  'Assess'
end

#get_property(name) ⇒ String

Parameters:

Returns:



128
129
130
131
132
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 128

def get_property name
  return unless @properties

  @properties[name]
end

#node_classString

Returns:



96
97
98
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 96

def node_class
  'Node'
end

#node_typeSymbol

Returns:

  • (Symbol)


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

def node_type
  :TYPE_METHOD
end

#response_source_node?Boolean

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

Returns:

  • (Boolean)


220
221
222
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 220

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)


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

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)


206
207
208
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 206

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)


213
214
215
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 213

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.

Raises:

  • (ArgumentError)

    raises if the node is invalid.



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

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



153
154
155
156
157
158
159
160
# File 'lib/contrast/agent/assess/policy/policy_node.rb', line 153

def validate_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