Class: Aikido::Zen::Sink

Inherits:
Object
  • Object
show all
Defined in:
lib/aikido/zen/sink.rb

Overview

Sinks serve as the proxies between a given library that we protect (such as a database adapter that we patch to prevent SQL injections) and the reporting agent.

When a library is patched to track and potentially block attacks, we rely on a sink to run any scans required, and report any attacks to our agent.

See Also:

  • for a reference implementation.

Constant Summary collapse

DEFAULT_REPORTER =
->(scan) { Aikido::Zen.track_scan(scan) }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, scanners:, operation: name, reporter: DEFAULT_REPORTER) ⇒ Sink

Returns a new instance of Sink.

Raises:

  • (ArgumentError)


56
57
58
59
60
61
62
63
# File 'lib/aikido/zen/sink.rb', line 56

def initialize(name, scanners:, operation: name, reporter: DEFAULT_REPORTER)
  raise ArgumentError, "scanners cannot be empty" if scanners.empty?

  @name = name
  @operation = operation
  @scanners = scanners
  @reporter = reporter
end

Instance Attribute Details

#nameString (readonly)

Returns name of the patched library (e.g. “mysql2”).

Returns:

  • (String)

    name of the patched library (e.g. “mysql2”).



43
44
45
# File 'lib/aikido/zen/sink.rb', line 43

def name
  @name
end

#operationString (readonly)

Returns descriptor of the module / method being scanned for attacks. This is fed into Attacks when instantiated. In certain cases, some scanners allow you to specialize this by using an operation param of their own.

Returns:

  • (String)

    descriptor of the module / method being scanned for attacks. This is fed into Attacks when instantiated. In certain cases, some scanners allow you to specialize this by using an operation param of their own.



52
53
54
# File 'lib/aikido/zen/sink.rb', line 52

def operation
  @operation
end

#scannersArray<#call> (readonly)

Returns list of registered scanners for this sink.

Returns:

  • (Array<#call>)

    list of registered scanners for this sink.



46
47
48
# File 'lib/aikido/zen/sink.rb', line 46

def scanners
  @scanners
end

Instance Method Details

#scan(context: Aikido::Zen.current_context, **scan_params) ⇒ Aikido::Zen::Scan?

Run the given arguments through all the registered scanners, until one of them returns an Attack or all return nil, and report the findings back to the Sink’s reporter to track statistics and potentially handle the Attack, if anything.

This checks if runtime protection has been turned off for the current route first, and if so skips the scanning altogether, returning nil.

Parameters:

  • scan_params (Hash)

    data to pass to all registered scanners.

Options Hash (**scan_params):

  • :context (Aikido::Zen::Context, nil)

    The current Context, including the HTTP request being inspected, or nil if we’re scanning outside of an HTTP request.

Returns:

  • (Aikido::Zen::Scan, nil)

    the result of the scan, or nil if the scan was skipped due to protection being disabled for the current route.

Raises:

  • (Aikido::UnderAttackError)

    if an attack is detected and blocking_mode is enabled.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/aikido/zen/sink.rb', line 82

def scan(context: Aikido::Zen.current_context, **scan_params)
  return if context&.protection_disabled?

  scan = Scan.new(sink: self, context: context)

  scan.perform do
    result = nil

    scanners.each do |scanner|
      result = scanner.call(sink: self, context: context, **scan_params)
      break result if result
    rescue Aikido::Zen::InternalsError => error
      Aikido::Zen.config.logger.warn(error.message)
      scan.track_error(error, scanner)
    rescue => error
      scan.track_error(error, scanner)
    end

    result
  end

  @reporter.call(scan)

  scan
end