Module: Contrast::Agent::Assess::Policy::TriggerMethod

Extended by:
Components::Logger::InstanceMethods, Utils::Assess::EventLimitUtils, Utils::Assess::TriggerMethodUtils
Included in:
Patching::Policy::Patch
Defined in:
lib/contrast/agent/assess/policy/trigger_method.rb

Overview

A trigger method is one which can perform a dangerous action, as described by the Contrast::Agent::Assess::Policy::TriggerNode class. Each such method will call to this module just after invocation in order to determine if the call was done safely. In those cases where it was not, a Finding report is issued to TeamServer.

Constant Summary collapse

NON_REQUEST_RULES =

Rules that always exists outside of Request Context

Returns:

[
  'hardcoded-password', # Contrast::Agent::Assess::Rule::Provider::HardcodedPassword.NAME,
  'hardcoded-key' # Contrast::Agent::Assess::Rule::Provider::HardcodedKey.NAME
].cs__freeze

Class Method Summary collapse

Methods included from Components::Logger::InstanceMethods

cef_logger, logger

Methods included from Utils::Assess::TriggerMethodUtils

apply_dataflow_rule, apply_regexp_rule, apply_trigger, find_event_request, find_request, reportable?

Methods included from Utils::Assess::EventLimitUtils

event_limit?, event_limit_for_rule?, increment_event_count

Class Method Details

.apply_eval_trigger(trigger_node, source, object, ret, *args) ⇒ Object



73
74
75
# File 'lib/contrast/agent/assess/policy/trigger_method.rb', line 73

def apply_eval_trigger trigger_node, source, object, ret, *args
  apply_trigger(trigger_node, source, object, ret, *args)
end

.apply_trigger_rule(trigger_node, object, ret, args) ⇒ Object

This is called from within our woven proc. It will be called as if it were inline in the Rack application.

Parameters:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/contrast/agent/assess/policy/trigger_method.rb', line 49

def apply_trigger_rule trigger_node, object, ret, args
  return if trigger_node.nil?
  return if event_limit_for_rule?(trigger_node.rule_id)

  context = Contrast::Agent::REQUEST_TRACKER.current
  # return if there is no context and the flag is set to default => false
  # we need to have request if the flag is default
  # else proceed, if the flag is true we don't need to check for context we
  # go as currently.
  # When outside of a request, only track when the feature is enabled
  return unless context ||
      Contrast::ASSESS.non_request_tracking? ||
      NON_REQUEST_RULES.include?(trigger_node.rule_id)

  if trigger_node.sources&.any?
    trigger_node.sources.each do |marker|
      source = determine_source(marker, object, ret, args)
      apply_trigger(trigger_node, source, object, ret, *args)
    end
  else
    apply_trigger(trigger_node, nil, object, ret, *args)
  end
end

.build_finding(trigger_node, source, object, ret, *args) ⇒ Contrast::Agent::Reporting::Finding?

This converts the source of the finding, and the events leading up to it into a Finding

Parameters:

  • trigger_node (Contrast::Agent::Assess::Policy::TriggerNode)

    the node to direct applying this trigger event

  • source (Object)

    the source of the Trigger Event

  • object (Object)

    the Object on which the method was invoked

  • ret (Object, nil)

    the Return of the invoked method, or nil if void method

  • args (Array<Object>)

    the Arguments with which the method was invoked

Returns:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/contrast/agent/assess/policy/trigger_method.rb', line 86

def build_finding trigger_node, source, object, ret, *args
  content_type = Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type
  if content_type.nil? && trigger_node.collectable?
    Contrast::Agent::FINDINGS.collect_finding(trigger_node, source, object, ret, *args)
    return
  end
  return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)

  request = find_request(source)
  return unless reportable?(request&.env)
  return if excluded_by_url_and_rule?(trigger_node.rule_id)

  finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
  finding.attach_data(trigger_node, source, object, ret, request, *args)
  return if excluded_by_input_and_rule?(finding, trigger_node.rule_id)

  finding.hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source, request)
  check_for_stored_xss(finding)
  finding
rescue StandardError => e
  logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
  nil
end

.report_finding(finding) ⇒ Object

Given a finding, append it to an activity message and send it to the TeamServer for processing. If an activity message does not exist, b/c we’re invoked outside of a request context, build an activity and immediately report it with the finding.

Parameters:



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/contrast/agent/assess/policy/trigger_method.rb', line 115

def report_finding finding
  return unless finding

  preflight = Contrast::Agent::Reporting::BuildPreflight.generate(finding)
  return unless preflight

  preflight_data = preflight.messages[0].data
  return if Contrast::Agent::REQUEST_TRACKER.current&.reported_findings&.include?(preflight_data)

  Contrast::Agent::Reporting::ReportingStorage[preflight.messages[0].data] = finding
  if Contrast::Agent::REQUEST_TRACKER.current
    Contrast::Agent::REQUEST_TRACKER.current.reported_findings << preflight_data
  end
  Contrast::Agent.reporter&.send_event(preflight)
end