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, kind, scanners:, operation: name, reporter: DEFAULT_REPORTER) ⇒ Sink

Returns a new instance of Sink.

Raises:

  • (ArgumentError)


60
61
62
63
64
65
66
67
68
# File 'lib/aikido/zen/sink.rb', line 60

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

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

Instance Attribute Details

#kindString (readonly)



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

def kind
  @kind
end

#nameString (readonly)



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

def name
  @name
end

#operationString (readonly)



56
57
58
# File 'lib/aikido/zen/sink.rb', line 56

def operation
  @operation
end

#scannersArray<#call> (readonly)



50
51
52
# File 'lib/aikido/zen/sink.rb', line 50

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.

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.

Raises:

  • (Aikido::UnderAttackError)

    if an attack is detected and blocking_mode is enabled.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/aikido/zen/sink.rb', line 87

def scan(context: Aikido::Zen.current_context, **scan_params)
  return if context&.scanning
  context&.scanning = true

  return if context&.protection_disabled?

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

  scans_performed = 0
  scan.perform do
    result = nil

    scanners.each do |scanner|
      next if scanner.skips_on_nil_context? && context.nil?

      result = scanner.call(scan: scan, sink: self, context: context, **scan_params)
      scans_performed += 1

      break result if result
    rescue => error
      scan.track_error(error, scanner)
    end

    result
  end

  @reporter.call(scan) if scans_performed > 0

  scan
ensure
  context&.scanning = false
end