aikido-zen 0.2.0-x86_64-mingw-64 → 1.0.1.beta.2-x86_64-mingw-64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.aikido +6 -0
  3. data/.simplecov +6 -0
  4. data/README.md +67 -83
  5. data/benchmarks/README.md +8 -12
  6. data/docs/rails.md +1 -1
  7. data/lib/aikido/zen/agent.rb +10 -8
  8. data/lib/aikido/zen/api_client.rb +14 -4
  9. data/lib/aikido/zen/background_worker.rb +52 -0
  10. data/lib/aikido/zen/collector.rb +12 -1
  11. data/lib/aikido/zen/config.rb +20 -0
  12. data/lib/aikido/zen/context.rb +4 -0
  13. data/lib/aikido/zen/detached_agent/agent.rb +78 -0
  14. data/lib/aikido/zen/detached_agent/front_object.rb +37 -0
  15. data/lib/aikido/zen/detached_agent/server.rb +41 -0
  16. data/lib/aikido/zen/detached_agent.rb +2 -0
  17. data/lib/aikido/zen/errors.rb +8 -0
  18. data/lib/aikido/zen/internals.rb +41 -7
  19. data/lib/aikido/zen/libzen-v0.1.39-x86_64-mingw-64.dll +0 -0
  20. data/lib/aikido/zen/middleware/rack_throttler.rb +9 -3
  21. data/lib/aikido/zen/middleware/request_tracker.rb +6 -4
  22. data/lib/aikido/zen/outbound_connection_monitor.rb +4 -0
  23. data/lib/aikido/zen/rails_engine.rb +8 -8
  24. data/lib/aikido/zen/rate_limiter/breaker.rb +3 -3
  25. data/lib/aikido/zen/rate_limiter.rb +6 -11
  26. data/lib/aikido/zen/request/heuristic_router.rb +6 -0
  27. data/lib/aikido/zen/request/rails_router.rb +6 -18
  28. data/lib/aikido/zen/request/schema/auth_schemas.rb +14 -0
  29. data/lib/aikido/zen/request/schema.rb +18 -0
  30. data/lib/aikido/zen/runtime_settings.rb +2 -2
  31. data/lib/aikido/zen/scanners/path_traversal_scanner.rb +4 -2
  32. data/lib/aikido/zen/scanners/shell_injection_scanner.rb +4 -2
  33. data/lib/aikido/zen/scanners/sql_injection_scanner.rb +4 -2
  34. data/lib/aikido/zen/scanners/ssrf/private_ip_checker.rb +33 -21
  35. data/lib/aikido/zen/scanners/ssrf_scanner.rb +6 -1
  36. data/lib/aikido/zen/scanners/stored_ssrf_scanner.rb +6 -0
  37. data/lib/aikido/zen/sink.rb +11 -1
  38. data/lib/aikido/zen/sinks/action_controller.rb +9 -4
  39. data/lib/aikido/zen/sinks/async_http.rb +35 -16
  40. data/lib/aikido/zen/sinks/curb.rb +52 -26
  41. data/lib/aikido/zen/sinks/em_http.rb +39 -25
  42. data/lib/aikido/zen/sinks/excon.rb +63 -45
  43. data/lib/aikido/zen/sinks/file.rb +67 -71
  44. data/lib/aikido/zen/sinks/http.rb +38 -19
  45. data/lib/aikido/zen/sinks/httpclient.rb +51 -22
  46. data/lib/aikido/zen/sinks/httpx.rb +37 -18
  47. data/lib/aikido/zen/sinks/kernel.rb +18 -57
  48. data/lib/aikido/zen/sinks/mysql2.rb +19 -7
  49. data/lib/aikido/zen/sinks/net_http.rb +37 -19
  50. data/lib/aikido/zen/sinks/patron.rb +41 -24
  51. data/lib/aikido/zen/sinks/pg.rb +50 -27
  52. data/lib/aikido/zen/sinks/resolv.rb +37 -16
  53. data/lib/aikido/zen/sinks/socket.rb +46 -17
  54. data/lib/aikido/zen/sinks/sqlite3.rb +31 -12
  55. data/lib/aikido/zen/sinks/trilogy.rb +19 -7
  56. data/lib/aikido/zen/sinks.rb +29 -20
  57. data/lib/aikido/zen/sinks_dsl.rb +226 -0
  58. data/lib/aikido/zen/version.rb +2 -2
  59. data/lib/aikido/zen/worker.rb +5 -0
  60. data/lib/aikido/zen.rb +59 -9
  61. data/placeholder/.gitignore +4 -0
  62. data/placeholder/README.md +11 -0
  63. data/placeholder/Rakefile +75 -0
  64. data/placeholder/lib/placeholder.rb.template +3 -0
  65. data/placeholder/placeholder.gemspec.template +20 -0
  66. data/tasklib/bench.rake +29 -6
  67. data/tasklib/libzen.rake +70 -66
  68. data/tasklib/wrk.rb +88 -0
  69. metadata +23 -13
  70. data/CHANGELOG.md +0 -25
  71. data/lib/aikido/zen/libzen-v0.1.37.x86_64.dll +0 -0
  72. data/lib/aikido.rb +0 -3
@@ -12,11 +12,11 @@ module Aikido::Zen
12
12
  def initialize(
13
13
  config: Aikido::Zen.config,
14
14
  settings: Aikido::Zen.runtime_settings,
15
- rate_limiter: Aikido::Zen::RateLimiter.new
15
+ detached_agent: Aikido::Zen.detached_agent
16
16
  )
17
17
  @config = config
18
18
  @settings = settings
19
- @rate_limiter = rate_limiter
19
+ @detached_agent = detached_agent
20
20
  end
21
21
 
22
22
  def block?(controller)
@@ -43,16 +43,21 @@ module Aikido::Zen
43
43
  end
44
44
 
45
45
  private def should_throttle?(request)
46
+ return false unless @settings.endpoints[request.route].rate_limiting.enabled?
46
47
  return false if @settings.skip_protection_for_ips.include?(request.ip)
47
48
 
48
- @rate_limiter.throttle?(request)
49
+ result = @detached_agent.calculate_rate_limits(request)
50
+ return false unless result
51
+
52
+ request.env["aikido.rate_limiting"] = result
53
+ request.env["aikido.rate_limiting"].throttled?
49
54
  end
50
55
 
51
56
  # @param request [Aikido::Zen::Request]
52
57
  private def should_block_user?(request)
53
58
  return false if request.actor.nil?
54
59
 
55
- @settings.blocked_user_ids&.include?(request.actor.id)
60
+ Aikido::Zen.runtime_settings.blocked_user_ids&.include?(request.actor.id)
56
61
  end
57
62
  end
58
63
 
@@ -1,26 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../sink"
3
+ require_relative "../scanners/ssrf_scanner"
4
4
  require_relative "../outbound_connection_monitor"
5
5
 
6
6
  module Aikido::Zen
7
7
  module Sinks
8
8
  module Async
9
9
  module HTTP
10
+ def self.load_sinks!
11
+ if Gem.loaded_specs["async-http"]
12
+ require "async/http"
13
+
14
+ ::Async::HTTP::Client.prepend(Async::HTTP::ClientExtensions)
15
+ end
16
+ end
17
+
10
18
  SINK = Sinks.add("async-http", scanners: [
11
- Aikido::Zen::Scanners::SSRFScanner,
12
- Aikido::Zen::OutboundConnectionMonitor
19
+ Scanners::SSRFScanner,
20
+ OutboundConnectionMonitor
13
21
  ])
14
22
 
15
- module Extensions
16
- def call(request)
23
+ module Helpers
24
+ def self.scan(request, connection, operation)
25
+ SINK.scan(
26
+ request: request,
27
+ connection: connection,
28
+ operation: operation
29
+ )
30
+ end
31
+ end
32
+
33
+ module ClientExtensions
34
+ extend Sinks::DSL
35
+
36
+ sink_around :call do |super_call, request|
17
37
  uri = URI(format("%<scheme>s://%<authority>s%<path>s", {
18
38
  scheme: request.scheme || scheme,
19
39
  authority: request.authority || authority,
20
40
  path: request.path
21
41
  }))
22
42
 
23
- wrapped_request = Aikido::Zen::Scanners::SSRFScanner::Request.new(
43
+ wrapped_request = Scanners::SSRFScanner::Request.new(
24
44
  verb: request.method,
25
45
  uri: uri,
26
46
  headers: request.headers.to_h,
@@ -28,22 +48,21 @@ module Aikido::Zen
28
48
  )
29
49
 
30
50
  # Store the request information so the DNS sinks can pick it up.
31
- if (context = Aikido::Zen.current_context)
51
+ context = Aikido::Zen.current_context
52
+ if context
32
53
  prev_request = context["ssrf.request"]
33
54
  context["ssrf.request"] = wrapped_request
34
55
  end
35
56
 
36
- SINK.scan(
37
- connection: Aikido::Zen::OutboundConnection.from_uri(uri),
38
- request: wrapped_request,
39
- operation: "request"
40
- )
57
+ connection = OutboundConnection.from_uri(uri)
58
+
59
+ Helpers.scan(wrapped_request, connection, "request")
41
60
 
42
- response = super
61
+ response = super_call.call
43
62
 
44
- Aikido::Zen::Scanners::SSRFScanner.track_redirects(
63
+ Scanners::SSRFScanner.track_redirects(
45
64
  request: wrapped_request,
46
- response: Aikido::Zen::Scanners::SSRFScanner::Response.new(
65
+ response: Scanners::SSRFScanner::Response.new(
47
66
  status: response.status,
48
67
  headers: response.headers.to_h,
49
68
  header_normalizer: ->(value) { Array(value).join(", ") }
@@ -60,4 +79,4 @@ module Aikido::Zen
60
79
  end
61
80
  end
62
81
 
63
- ::Async::HTTP::Client.prepend(Aikido::Zen::Sinks::Async::HTTP::Extensions)
82
+ Aikido::Zen::Sinks::Async::HTTP.load_sinks!
@@ -1,19 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../sink"
3
+ require_relative "../scanners/ssrf_scanner"
4
4
  require_relative "../outbound_connection_monitor"
5
5
 
6
6
  module Aikido::Zen
7
7
  module Sinks
8
8
  module Curl
9
+ def self.load_sinks!
10
+ if Gem.loaded_specs["curb"]
11
+ require "curb"
12
+
13
+ ::Curl::Easy.prepend(Curl::EasyExtensions)
14
+ end
15
+ end
16
+
9
17
  SINK = Sinks.add("curb", scanners: [
10
- Aikido::Zen::Scanners::SSRFScanner,
11
- Aikido::Zen::OutboundConnectionMonitor
18
+ Scanners::SSRFScanner,
19
+ OutboundConnectionMonitor
12
20
  ])
13
21
 
14
- module Extensions
22
+ module Helpers
15
23
  def self.wrap_request(curl, url: curl.url)
16
- Aikido::Zen::Scanners::SSRFScanner::Request.new(
24
+ Scanners::SSRFScanner::Request.new(
17
25
  verb: nil, # Curb hides this by directly setting an option in C
18
26
  uri: URI(url),
19
27
  headers: curl.headers
@@ -33,29 +41,40 @@ module Aikido::Zen
33
41
  status = curl.status.to_i
34
42
  end
35
43
 
36
- Aikido::Zen::Scanners::SSRFScanner::Response.new(status: status, headers: headers)
44
+ Scanners::SSRFScanner::Response.new(status: status, headers: headers)
37
45
  end
38
46
 
39
- def perform
40
- wrapped_request = Extensions.wrap_request(self)
47
+ def self.scan(request, connection, operation)
48
+ SINK.scan(
49
+ request: request,
50
+ connection: connection,
51
+ operation: operation
52
+ )
53
+ end
54
+ end
55
+
56
+ module EasyExtensions
57
+ extend Sinks::DSL
58
+
59
+ sink_around :perform do |super_call|
60
+ wrapped_request = Helpers.wrap_request(self)
41
61
 
42
62
  # Store the request information so the DNS sinks can pick it up.
43
- if (context = Aikido::Zen.current_context)
63
+ context = Aikido::Zen.current_context
64
+ if context
44
65
  prev_request = context["ssrf.request"]
45
66
  context["ssrf.request"] = wrapped_request
46
67
  end
47
68
 
48
- SINK.scan(
49
- connection: Aikido::Zen::OutboundConnection.from_uri(URI(url)),
50
- request: wrapped_request,
51
- operation: "request"
52
- )
69
+ connection = OutboundConnection.from_uri(URI(url))
53
70
 
54
- response = super
71
+ Helpers.scan(wrapped_request, connection, "request")
55
72
 
56
- Aikido::Zen::Scanners::SSRFScanner.track_redirects(
73
+ response = super_call.call
74
+
75
+ Scanners::SSRFScanner.track_redirects(
57
76
  request: wrapped_request,
58
- response: Extensions.wrap_response(self)
77
+ response: Helpers.wrap_response(self)
59
78
  )
60
79
 
61
80
  # When libcurl has follow_location set, it will handle redirections
@@ -67,14 +86,21 @@ module Aikido::Zen
67
86
  # stop the response from being exposed to the user. This downgrades
68
87
  # the SSRF into a blind SSRF, which is better than doing nothing.
69
88
  if url != last_effective_url
70
- last_effective_request = Extensions.wrap_request(self, url: last_effective_url)
71
- context["ssrf.request"] = last_effective_request if context
72
-
73
- SINK.scan(
74
- connection: Aikido::Zen::OutboundConnection.from_uri(URI(last_effective_url)),
75
- request: last_effective_request,
76
- operation: "request"
77
- )
89
+ last_effective_request = Helpers.wrap_request(self, url: last_effective_url)
90
+
91
+ # Code coverage is disabled here because the else clause is a no-op,
92
+ # so there is nothing to cover.
93
+ # :nocov:
94
+ if context
95
+ context["ssrf.request"] = last_effective_request
96
+ else
97
+ # empty
98
+ end
99
+ # :nocov:
100
+
101
+ connection = OutboundConnection.from_uri(URI(last_effective_url))
102
+
103
+ Helpers.scan(last_effective_request, connection, "request")
78
104
  end
79
105
 
80
106
  response
@@ -86,4 +112,4 @@ module Aikido::Zen
86
112
  end
87
113
  end
88
114
 
89
- ::Curl::Easy.prepend(Aikido::Zen::Sinks::Curl::Extensions)
115
+ Aikido::Zen::Sinks::Curl.load_sinks!
@@ -1,20 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../sink"
3
+ require_relative "../scanners/ssrf_scanner"
4
4
  require_relative "../outbound_connection_monitor"
5
5
 
6
6
  module Aikido::Zen
7
7
  module Sinks
8
8
  module EventMachine
9
9
  module HttpRequest
10
+ def self.load_sinks!
11
+ if Gem.loaded_specs["em-http-request"]
12
+ require "em-http-request"
13
+
14
+ ::EventMachine::HttpRequest.use(EventMachine::HttpRequest::Middleware)
15
+
16
+ # NOTE: We can't use middleware to intercept requests as we want to ensure any
17
+ # modifications to the request from user-supplied middleware are already applied
18
+ # before we scan the request.
19
+ ::EventMachine::HttpClient.prepend(EventMachine::HttpRequest::HttpClientExtensions)
20
+ end
21
+ end
22
+
10
23
  SINK = Sinks.add("em-http-request", scanners: [
11
- Aikido::Zen::Scanners::SSRFScanner,
12
- Aikido::Zen::OutboundConnectionMonitor
24
+ Scanners::SSRFScanner,
25
+ OutboundConnectionMonitor
13
26
  ])
14
27
 
15
- module Extensions
16
- def send_request(*)
17
- wrapped_request = Aikido::Zen::Scanners::SSRFScanner::Request.new(
28
+ module Helpers
29
+ def self.scan(request, connection, operation)
30
+ SINK.scan(
31
+ request: request,
32
+ connection: connection,
33
+ operation: operation
34
+ )
35
+ end
36
+ end
37
+
38
+ module HttpClientExtensions
39
+ extend Sinks::DSL
40
+
41
+ sink_before :send_request do
42
+ wrapped_request = Scanners::SSRFScanner::Request.new(
18
43
  verb: req.method.to_s,
19
44
  uri: URI(req.uri),
20
45
  headers: req.headers
@@ -24,16 +49,12 @@ module Aikido::Zen
24
49
  context = Aikido::Zen.current_context
25
50
  context["ssrf.request"] = wrapped_request if context
26
51
 
27
- SINK.scan(
28
- connection: Aikido::Zen::OutboundConnection.new(
29
- host: req.host,
30
- port: req.port
31
- ),
32
- request: wrapped_request,
33
- operation: "request"
52
+ connection = OutboundConnection.new(
53
+ host: req.host,
54
+ port: req.port
34
55
  )
35
56
 
36
- super
57
+ Helpers.scan(wrapped_request, connection, "request")
37
58
  end
38
59
  end
39
60
 
@@ -43,13 +64,13 @@ module Aikido::Zen
43
64
  context = Aikido::Zen.current_context
44
65
  context["ssrf.request"] = nil if context
45
66
 
46
- Aikido::Zen::Scanners::SSRFScanner.track_redirects(
47
- request: Aikido::Zen::Scanners::SSRFScanner::Request.new(
67
+ Scanners::SSRFScanner.track_redirects(
68
+ request: Scanners::SSRFScanner::Request.new(
48
69
  verb: client.req.method,
49
70
  uri: URI(client.req.uri),
50
71
  headers: client.req.headers
51
72
  ),
52
- response: Aikido::Zen::Scanners::SSRFScanner::Response.new(
73
+ response: Scanners::SSRFScanner::Response.new(
53
74
  status: client.response_header.status,
54
75
  headers: client.response_header.to_h
55
76
  )
@@ -61,11 +82,4 @@ module Aikido::Zen
61
82
  end
62
83
  end
63
84
 
64
- ::EventMachine::HttpRequest
65
- .use(Aikido::Zen::Sinks::EventMachine::HttpRequest::Middleware)
66
-
67
- # NOTE: We can't use middleware to intercept requests as we want to ensure any
68
- # modifications to the request from user-supplied middleware are already applied
69
- # before we scan the request.
70
- ::EventMachine::HttpClient
71
- .prepend(Aikido::Zen::Sinks::EventMachine::HttpRequest::Extensions)
85
+ Aikido::Zen::Sinks::EventMachine::HttpRequest.load_sinks!
@@ -1,31 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../sink"
3
+ require_relative "../scanners/ssrf_scanner"
4
4
  require_relative "../outbound_connection_monitor"
5
5
 
6
6
  module Aikido::Zen
7
7
  module Sinks
8
8
  module Excon
9
- SINK = Sinks.add("excon", scanners: [
10
- Aikido::Zen::Scanners::SSRFScanner,
11
- Aikido::Zen::OutboundConnectionMonitor
12
- ])
9
+ def self.load_sinks!
10
+ if Gem.loaded_specs["excon"]
11
+ require "excon"
13
12
 
14
- module Extensions
15
- # Maps Excon request params to an Aikido OutboundConnection.
16
- #
17
- # @param connection [Hash<Symbol, Object>] the data set in the connection.
18
- # @param request [Hash<Symbol, Object>] the data overrides sent for each
19
- # request.
20
- #
21
- # @return [Aikido::Zen::OutboundConnection]
22
- def self.build_outbound(connection, request)
23
- Aikido::Zen::OutboundConnection.new(
24
- host: request.fetch(:hostname) { connection[:hostname] },
25
- port: request.fetch(:port) { connection[:port] }
26
- )
13
+ ::Excon::Connection.prepend(ConnectionExtensions)
14
+ ::Excon::Middleware::RedirectFollower.prepend(RedirectFollowerExtensions)
27
15
  end
16
+ end
17
+
18
+ SINK = Sinks.add("excon", scanners: [
19
+ Scanners::SSRFScanner,
20
+ OutboundConnectionMonitor
21
+ ])
28
22
 
23
+ module Helpers
29
24
  def self.build_request(connection, request)
30
25
  uri = URI(format("%<scheme>s://%<host>s:%<port>i%<path>s", {
31
26
  scheme: request.fetch(:scheme) { connection[:scheme] },
@@ -35,45 +30,61 @@ module Aikido::Zen
35
30
  }))
36
31
  uri.query = request.fetch(:query) { connection[:query] }
37
32
 
38
- Aikido::Zen::Scanners::SSRFScanner::Request.new(
33
+ Scanners::SSRFScanner::Request.new(
39
34
  verb: request.fetch(:method) { connection[:method] },
40
35
  uri: uri,
41
36
  headers: connection[:headers].to_h.merge(request[:headers].to_h)
42
37
  )
43
38
  end
44
39
 
45
- def request(params = {}, *)
46
- request = Extensions.build_request(@data, params)
40
+ def self.scan(request, connection, operation)
41
+ SINK.scan(
42
+ request: request,
43
+ connection: connection,
44
+ operation: operation
45
+ )
46
+ end
47
+ end
48
+
49
+ module ConnectionExtensions
50
+ extend Sinks::DSL
51
+
52
+ sink_around :request do |super_call, params = {}|
53
+ request = Helpers.build_request(@data, params)
47
54
 
48
55
  # Store the request information so the DNS sinks can pick it up.
49
- if (context = Aikido::Zen.current_context)
56
+ context = Aikido::Zen.current_context
57
+ if context
50
58
  prev_request = context["ssrf.request"]
51
59
  context["ssrf.request"] = request
52
60
  end
53
61
 
54
- SINK.scan(
55
- connection: Aikido::Zen::OutboundConnection.from_uri(request.uri),
56
- request: request,
57
- operation: "request"
58
- )
62
+ connection = OutboundConnection.from_uri(request.uri)
59
63
 
60
- response = super
64
+ Helpers.scan(request, connection, "request")
61
65
 
62
- Aikido::Zen::Scanners::SSRFScanner.track_redirects(
66
+ response = super_call.call
67
+
68
+ Scanners::SSRFScanner.track_redirects(
63
69
  request: request,
64
- response: Aikido::Zen::Scanners::SSRFScanner::Response.new(
70
+ response: Scanners::SSRFScanner::Response.new(
65
71
  status: response.status,
66
72
  headers: response.headers.to_h
67
73
  )
68
74
  )
69
75
 
70
76
  response
71
- rescue ::Excon::Error::Socket => err
72
- # Excon wraps errors inside the lower level layer. This only happens
73
- # to our scanning exceptions when a request is using RedirectFollower,
74
- # so we unwrap them when it happens so host apps can handle errors
75
- # consistently.
76
- raise err.cause if err.cause.is_a?(Aikido::Zen::UnderAttackError)
77
+ rescue Sinks::DSL::PresafeError => err
78
+ outer_cause = err.cause
79
+ case outer_cause
80
+ when ::Excon::Error::Socket
81
+ inner_cause = outer_cause.cause
82
+ # Excon wraps errors inside the lower level layer. This only happens
83
+ # to our scanning exceptions when a request is using RedirectFollower,
84
+ # so we unwrap them when it happens so host apps can handle errors
85
+ # consistently.
86
+ raise inner_cause if inner_cause.is_a?(Aikido::Zen::UnderAttackError)
87
+ end
77
88
  raise
78
89
  ensure
79
90
  context["ssrf.request"] = prev_request if context
@@ -81,23 +92,30 @@ module Aikido::Zen
81
92
  end
82
93
 
83
94
  module RedirectFollowerExtensions
84
- def response_call(data)
85
- if (response = data[:response])
86
- Aikido::Zen::Scanners::SSRFScanner.track_redirects(
87
- request: Extensions.build_request(data, {}),
88
- response: Aikido::Zen::Scanners::SSRFScanner::Response.new(
95
+ extend Sinks::DSL
96
+
97
+ sink_before :response_call do |datum|
98
+ response = datum[:response]
99
+
100
+ # Code coverage is disabled here because the else clause is a no-op,
101
+ # so there is nothing to cover.
102
+ # :nocov:
103
+ if !response.nil?
104
+ Scanners::SSRFScanner.track_redirects(
105
+ request: Helpers.build_request(datum, {}),
106
+ response: Scanners::SSRFScanner::Response.new(
89
107
  status: response[:status],
90
108
  headers: response[:headers]
91
109
  )
92
110
  )
111
+ else
112
+ # empty
93
113
  end
94
-
95
- super
114
+ # :nocov:
96
115
  end
97
116
  end
98
117
  end
99
118
  end
100
119
  end
101
120
 
102
- ::Excon::Connection.prepend(Aikido::Zen::Sinks::Excon::Extensions)
103
- ::Excon::Middleware::RedirectFollower.prepend(Aikido::Zen::Sinks::Excon::RedirectFollowerExtensions)
121
+ Aikido::Zen::Sinks::Excon.load_sinks!