Module: Aikido::Zen::Sinks::Excon::Extensions

Defined in:
lib/aikido/zen/sinks/excon.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build_outbound(connection, request) ⇒ Aikido::Zen::OutboundConnection

Maps Excon request params to an Aikido OutboundConnection.

Parameters:

  • connection (Hash<Symbol, Object>)

    the data set in the connection.

  • request (Hash<Symbol, Object>)

    the data overrides sent for each request.

Returns:



22
23
24
25
26
27
# File 'lib/aikido/zen/sinks/excon.rb', line 22

def self.build_outbound(connection, request)
  Aikido::Zen::OutboundConnection.new(
    host: request.fetch(:hostname) { connection[:hostname] },
    port: request.fetch(:port) { connection[:port] }
  )
end

.build_request(connection, request) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/aikido/zen/sinks/excon.rb', line 29

def self.build_request(connection, request)
  uri = URI(format("%<scheme>s://%<host>s:%<port>i%<path>s", {
    scheme: request.fetch(:scheme) { connection[:scheme] },
    host: request.fetch(:hostname) { connection[:hostname] },
    port: request.fetch(:port) { connection[:port] },
    path: request.fetch(:path) { connection[:path] }
  }))
  uri.query = request.fetch(:query) { connection[:query] }

  Aikido::Zen::Scanners::SSRFScanner::Request.new(
    verb: request.fetch(:method) { connection[:method] },
    uri: uri,
    headers: connection[:headers].to_h.merge(request[:headers].to_h)
  )
end

Instance Method Details

#request(params = {}) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/aikido/zen/sinks/excon.rb', line 45

def request(params = {}, *)
  request = Extensions.build_request(@data, params)

  # Store the request information so the DNS sinks can pick it up.
  if (context = Aikido::Zen.current_context)
    prev_request = context["ssrf.request"]
    context["ssrf.request"] = request
  end

  SINK.scan(
    connection: Aikido::Zen::OutboundConnection.from_uri(request.uri),
    request: request,
    operation: "request"
  )

  response = super

  Aikido::Zen::Scanners::SSRFScanner.track_redirects(
    request: request,
    response: Aikido::Zen::Scanners::SSRFScanner::Response.new(
      status: response.status,
      headers: response.headers.to_h
    )
  )

  response
rescue ::Excon::Error::Socket => err
  # Excon wraps errors inside the lower level layer. This only happens
  # to our scanning exceptions when a request is using RedirectFollower,
  # so we unwrap them when it happens so host apps can handle errors
  # consistently.
  raise err.cause if err.cause.is_a?(Aikido::Zen::UnderAttackError)
  raise
ensure
  context["ssrf.request"] = prev_request if context
end