aikido-zen 1.0.1.beta.4-arm64-darwin → 1.0.2.beta.1-arm64-darwin

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.
@@ -7,13 +7,6 @@ module Aikido::Zen
7
7
  module Sinks
8
8
  module Net
9
9
  module HTTP
10
- def self.load_sinks!
11
- # In stdlib but not always required
12
- require "net/http"
13
-
14
- ::Net::HTTP.prepend(Net::HTTP::HTTPExtensions)
15
- end
16
-
17
10
  SINK = Sinks.add("net-http", scanners: [
18
11
  Scanners::SSRFScanner,
19
12
  OutboundConnectionMonitor
@@ -66,33 +59,38 @@ module Aikido::Zen
66
59
  end
67
60
  end
68
61
 
69
- module HTTPExtensions
70
- extend Sinks::DSL
62
+ def self.load_sinks!
63
+ # In stdlib but not always required
64
+ require "net/http"
71
65
 
72
- sink_around :request do |super_call, req|
73
- wrapped_request = Helpers.wrap_request(req, self)
66
+ ::Net::HTTP.class_eval do
67
+ extend Sinks::DSL
74
68
 
75
- # Store the request information so the DNS sinks can pick it up.
76
- context = Aikido::Zen.current_context
77
- if context
78
- prev_request = context["ssrf.request"]
79
- context["ssrf.request"] = wrapped_request
80
- end
69
+ sink_around :request do |original_call, req|
70
+ wrapped_request = Helpers.wrap_request(req, self)
81
71
 
82
- connection = Helpers.build_outbound(self)
72
+ # Store the request information so the DNS sinks can pick it up.
73
+ context = Aikido::Zen.current_context
74
+ if context
75
+ prev_request = context["ssrf.request"]
76
+ context["ssrf.request"] = wrapped_request
77
+ end
83
78
 
84
- Helpers.scan(wrapped_request, connection, "request")
79
+ connection = Helpers.build_outbound(self)
85
80
 
86
- response = super_call.call
81
+ Helpers.scan(wrapped_request, connection, "request")
87
82
 
88
- Scanners::SSRFScanner.track_redirects(
89
- request: wrapped_request,
90
- response: Helpers.wrap_response(response)
91
- )
83
+ response = original_call.call
84
+
85
+ Scanners::SSRFScanner.track_redirects(
86
+ request: wrapped_request,
87
+ response: Helpers.wrap_response(response)
88
+ )
92
89
 
93
- response
94
- ensure
95
- context["ssrf.request"] = prev_request if context
90
+ response
91
+ ensure
92
+ context["ssrf.request"] = prev_request if context
93
+ end
96
94
  end
97
95
  end
98
96
  end
@@ -6,14 +6,6 @@ require_relative "../outbound_connection_monitor"
6
6
  module Aikido::Zen
7
7
  module Sinks
8
8
  module Patron
9
- def self.load_sinks!
10
- if Aikido::Zen.satisfy "patron", ">= 0.6.4"
11
- require "patron"
12
-
13
- ::Patron::Session.prepend(SessionExtensions)
14
- end
15
- end
16
-
17
9
  SINK = Sinks.add("patron", scanners: [
18
10
  Scanners::SSRFScanner,
19
11
  OutboundConnectionMonitor
@@ -44,58 +36,64 @@ module Aikido::Zen
44
36
  end
45
37
  end
46
38
 
47
- module SessionExtensions
48
- extend Sinks::DSL
49
-
50
- sink_around :handle_request do |super_call, request|
51
- wrapped_request = Scanners::SSRFScanner::Request.new(
52
- verb: request.action,
53
- uri: URI(request.url),
54
- headers: request.headers
55
- )
56
-
57
- # Store the request information so the DNS sinks can pick it up.
58
- context = Aikido::Zen.current_context
59
- if context
60
- prev_request = context["ssrf.request"]
61
- context["ssrf.request"] = wrapped_request
62
- end
63
-
64
- connection = OutboundConnection.from_uri(URI(request.url))
65
-
66
- Helpers.scan(wrapped_request, connection, "request")
67
-
68
- response = super_call.call
69
-
70
- Scanners::SSRFScanner.track_redirects(
71
- request: wrapped_request,
72
- response: Helpers.wrap_response(request, response)
73
- )
74
-
75
- # When libcurl has follow_location set, it will handle redirections
76
- # internally, and expose the response.url as the URI that was last
77
- # requested in the redirect chain.
78
- #
79
- # In this case, we can't actually stop the request from happening, but
80
- # we can scan again (now that we know another request happened), to
81
- # stop the response from being exposed to the user. This downgrades
82
- # the SSRF into a blind SSRF, which is better than doing nothing.
83
- if request.url != response.url && !response.url.to_s.empty?
84
- last_effective_request = Scanners::SSRFScanner::Request.new(
85
- verb: request.action,
86
- uri: URI(response.url),
87
- headers: request.headers
88
- )
89
- context["ssrf.request"] = last_effective_request if context
90
-
91
- connection = OutboundConnection.from_uri(URI(response.url))
39
+ def self.load_sinks!
40
+ if Aikido::Zen.satisfy "patron", ">= 0.6.4"
41
+ require "patron"
92
42
 
93
- Helpers.scan(last_effective_request, connection, "request")
43
+ ::Patron::Session.class_eval do
44
+ extend Sinks::DSL
45
+
46
+ sink_around :handle_request do |original_call, request|
47
+ wrapped_request = Scanners::SSRFScanner::Request.new(
48
+ verb: request.action,
49
+ uri: URI(request.url),
50
+ headers: request.headers
51
+ )
52
+
53
+ # Store the request information so the DNS sinks can pick it up.
54
+ context = Aikido::Zen.current_context
55
+ if context
56
+ prev_request = context["ssrf.request"]
57
+ context["ssrf.request"] = wrapped_request
58
+ end
59
+
60
+ connection = OutboundConnection.from_uri(URI(request.url))
61
+
62
+ Helpers.scan(wrapped_request, connection, "request")
63
+
64
+ response = original_call.call
65
+
66
+ Scanners::SSRFScanner.track_redirects(
67
+ request: wrapped_request,
68
+ response: Helpers.wrap_response(request, response)
69
+ )
70
+
71
+ # When libcurl has follow_location set, it will handle redirections
72
+ # internally, and expose the response.url as the URI that was last
73
+ # requested in the redirect chain.
74
+ #
75
+ # In this case, we can't actually stop the request from happening, but
76
+ # we can scan again (now that we know another request happened), to
77
+ # stop the response from being exposed to the user. This downgrades
78
+ # the SSRF into a blind SSRF, which is better than doing nothing.
79
+ if request.url != response.url && !response.url.to_s.empty?
80
+ last_effective_request = Scanners::SSRFScanner::Request.new(
81
+ verb: request.action,
82
+ uri: URI(response.url),
83
+ headers: request.headers
84
+ )
85
+ context["ssrf.request"] = last_effective_request if context
86
+
87
+ connection = OutboundConnection.from_uri(URI(response.url))
88
+
89
+ Helpers.scan(last_effective_request, connection, "request")
90
+ end
91
+
92
+ response
93
+ ensure
94
+ context["ssrf.request"] = prev_request if context
95
+ end
94
96
  end
95
-
96
- response
97
- ensure
98
- context["ssrf.request"] = prev_request if context
99
97
  end
100
98
  end
101
99
  end
@@ -3,14 +3,6 @@
3
3
  module Aikido::Zen
4
4
  module Sinks
5
5
  module PG
6
- def self.load_sinks!
7
- if Aikido::Zen.satisfy "pg", ">= 1.0"
8
- require "pg"
9
-
10
- ::PG::Connection.prepend(PG::ConnectionExtensions)
11
- end
12
- end
13
-
14
6
  SINK = Sinks.add("pg", scanners: [Scanners::SQLInjectionScanner])
15
7
 
16
8
  module Helpers
@@ -43,26 +35,32 @@ module Aikido::Zen
43
35
  end
44
36
  end
45
37
 
46
- module ConnectionExtensions
47
- extend Sinks::DSL
38
+ def self.load_sinks!
39
+ if Aikido::Zen.satisfy "pg", ">= 1.0"
40
+ require "pg"
48
41
 
49
- %i[
50
- send_query exec sync_exec async_exec
51
- send_query_params exec_params sync_exec_params async_exec_params
52
- ].each do |method_name|
53
- presafe_sink_before method_name do |query|
54
- Helpers.safe do
55
- Helpers.scan(query, method_name)
42
+ ::PG::Connection.class_eval do
43
+ extend Sinks::DSL
44
+
45
+ %i[
46
+ send_query exec sync_exec async_exec
47
+ send_query_params exec_params sync_exec_params async_exec_params
48
+ ].each do |method_name|
49
+ presafe_sink_before method_name do |query|
50
+ Helpers.safe do
51
+ Helpers.scan(query, method_name)
52
+ end
53
+ end
56
54
  end
57
- end
58
- end
59
55
 
60
- %i[
61
- send_prepare prepare async_prepare sync_prepare
62
- ].each do |method_name|
63
- presafe_sink_before method_name do |_, query|
64
- Helpers.safe do
65
- Helpers.scan(query, method_name)
56
+ %i[
57
+ send_prepare prepare async_prepare sync_prepare
58
+ ].each do |method_name|
59
+ presafe_sink_before method_name do |_, query|
60
+ Helpers.safe do
61
+ Helpers.scan(query, method_name)
62
+ end
63
+ end
66
64
  end
67
65
  end
68
66
  end
@@ -6,13 +6,6 @@ require_relative "../scanners/ssrf_scanner"
6
6
  module Aikido::Zen
7
7
  module Sinks
8
8
  module Resolv
9
- def self.load_sinks!
10
- # In stdlib but not always required
11
- require "resolv"
12
-
13
- ::Resolv.prepend(ResolvExtensions)
14
- end
15
-
16
9
  SINK = Sinks.add("resolv", scanners: [
17
10
  Scanners::StoredSSRFScanner,
18
11
  Scanners::SSRFScanner
@@ -35,23 +28,30 @@ module Aikido::Zen
35
28
  end
36
29
  end
37
30
 
38
- module ResolvExtensions
39
- def each_address(*args, **kwargs, &blk)
40
- # each_address is defined "manually" because no sink method pattern
41
- # is applicable.
31
+ def self.load_sinks!
32
+ # In stdlib but not always required
33
+ require "resolv"
42
34
 
43
- name, = args
35
+ ::Resolv.class_eval do
36
+ alias_method :each_address__internal_for_aikido_zen, :each_address
44
37
 
45
- addresses = []
46
- super(*args, **kwargs) do |address| # rubocop:disable Style/SuperArguments
47
- addresses << address
48
- blk.call(address)
49
- end
50
- ensure
51
- # Ensure partial results are scanned.
38
+ def each_address(*args, **kwargs, &blk)
39
+ # each_address is defined "manually" because no sink method pattern
40
+ # is applicable.
41
+
42
+ name, = args
43
+
44
+ addresses = []
45
+ each_address__internal_for_aikido_zen(*args, **kwargs) do |address|
46
+ addresses << address
47
+ blk.call(address)
48
+ end
49
+ ensure
50
+ # Ensure partial results are scanned.
52
51
 
53
- Sinks::DSL.safe do
54
- Helpers.scan(name, addresses, "lookup")
52
+ Sinks::DSL.safe do
53
+ Helpers.scan(name, addresses, "lookup")
54
+ end
55
55
  end
56
56
  end
57
57
  end
@@ -10,10 +10,6 @@ module Aikido::Zen
10
10
  # there's no way to access the internal DNS resolution that happens in C
11
11
  # when using the socket primitives.
12
12
  module Socket
13
- def self.load_sinks!
14
- ::IPSocket.singleton_class.prepend(Socket::IPSocketExtensions)
15
- end
16
-
17
13
  SINK = Sinks.add("socket", scanners: [
18
14
  Scanners::StoredSSRFScanner,
19
15
  Scanners::SSRFScanner
@@ -62,15 +58,17 @@ module Aikido::Zen
62
58
  end
63
59
  end
64
60
 
65
- module IPSocketExtensions
66
- extend Sinks::DSL
61
+ def self.load_sinks!
62
+ ::IPSocket.singleton_class.class_eval do
63
+ extend Sinks::DSL
67
64
 
68
- sink_after :open do |socket, remote_host|
69
- # Code coverage is disabled here because the tests are contrived and
70
- # intentionally do not call open.
71
- # :nocov:
72
- Helpers.scan(remote_host, socket, "open")
73
- # :nocov:
65
+ sink_after :open do |socket, remote_host|
66
+ # Code coverage is disabled here because the tests are contrived and
67
+ # intentionally do not call open.
68
+ # :nocov:
69
+ Helpers.scan(remote_host, socket, "open")
70
+ # :nocov:
71
+ end
74
72
  end
75
73
  end
76
74
  end
@@ -3,15 +3,6 @@
3
3
  module Aikido::Zen
4
4
  module Sinks
5
5
  module SQLite3
6
- def self.load_sinks!
7
- if Aikido::Zen.satisfy "sqlite3", ">= 1.0"
8
- require "sqlite3"
9
-
10
- ::SQLite3::Database.prepend(DatabaseExtensions)
11
- ::SQLite3::Statement.prepend(StatementExtensions)
12
- end
13
- end
14
-
15
6
  SINK = Sinks.add("sqlite3", scanners: [Scanners::SQLInjectionScanner])
16
7
 
17
8
  module Helpers
@@ -24,22 +15,28 @@ module Aikido::Zen
24
15
  end
25
16
  end
26
17
 
27
- module DatabaseExtensions
28
- extend Sinks::DSL
18
+ def self.load_sinks!
19
+ if Aikido::Zen.satisfy "sqlite3", ">= 1.0"
20
+ require "sqlite3"
29
21
 
30
- private
22
+ ::SQLite3::Database.class_eval do
23
+ extend Sinks::DSL
31
24
 
32
- # SQLite3::Database#exec_batch is an internal native private method.
33
- sink_before :exec_batch do |sql|
34
- Helpers.scan(sql, "exec_batch")
35
- end
36
- end
25
+ private
26
+
27
+ # SQLite3::Database#exec_batch is an internal native private method.
28
+ sink_before :exec_batch do |sql|
29
+ Helpers.scan(sql, "exec_batch")
30
+ end
31
+ end
37
32
 
38
- module StatementExtensions
39
- extend Sinks::DSL
33
+ ::SQLite3::Statement.class_eval do
34
+ extend Sinks::DSL
40
35
 
41
- sink_before :initialize do |_db, sql|
42
- Helpers.scan(sql, "statement.execute")
36
+ sink_before :initialize do |_db, sql|
37
+ Helpers.scan(sql, "statement.execute")
38
+ end
39
+ end
43
40
  end
44
41
  end
45
42
  end
@@ -3,14 +3,6 @@
3
3
  module Aikido::Zen
4
4
  module Sinks
5
5
  module Trilogy
6
- def self.load_sinks!
7
- if Aikido::Zen.satisfy "trilogy", ">= 2.0"
8
- require "trilogy"
9
-
10
- ::Trilogy.prepend(TrilogyExtensions)
11
- end
12
- end
13
-
14
6
  SINK = Sinks.add("trilogy", scanners: [Scanners::SQLInjectionScanner])
15
7
 
16
8
  module Helpers
@@ -19,11 +11,17 @@ module Aikido::Zen
19
11
  end
20
12
  end
21
13
 
22
- module TrilogyExtensions
23
- extend Sinks::DSL
14
+ def self.load_sinks!
15
+ if Aikido::Zen.satisfy "trilogy", ">= 2.0"
16
+ require "trilogy"
17
+
18
+ ::Trilogy.class_eval do
19
+ extend Sinks::DSL
24
20
 
25
- sink_before :query do |query|
26
- Helpers.scan(query, "query")
21
+ sink_before :query do |query|
22
+ Helpers.scan(query, "query")
23
+ end
24
+ end
27
25
  end
28
26
  end
29
27
  end
@@ -9,10 +9,7 @@ require_relative "sinks_dsl"
9
9
 
10
10
  require_relative "sinks/action_controller" if defined?(::ActionController)
11
11
 
12
- # Sadly, in ruby versions lower than 3.0, it's not possible to patch the
13
- # Kernel module because how the `prepend` method is applied
14
- # (https://stackoverflow.com/questions/78110397/prepend-kernel-module-function-globally#comment137713906_78112924)
15
- require_relative "sinks/kernel" if RUBY_VERSION >= "3.0"
12
+ require_relative "sinks/kernel"
16
13
 
17
14
  require_relative "sinks/file"
18
15
  require_relative "sinks/socket"
@@ -109,10 +109,16 @@ module Aikido::Zen
109
109
  #
110
110
  # @return [void]
111
111
  def presafe_sink_before(method_name, &block)
112
+ raise ArgumentError, "block required" unless block
113
+
114
+ original = instance_method(method_name)
115
+
112
116
  define_method(method_name) do |*args, **kwargs, &blk|
113
117
  instance_exec(*args, **kwargs, &block)
114
- super(*args, **kwargs, &blk)
118
+ original.bind_call(self, *args, **kwargs, &blk)
115
119
  end
120
+ rescue NameError
121
+ Aikido::Zen.config.logger.warn("cannot wrap method `#{method_name}' for class `#{self}'")
116
122
  end
117
123
 
118
124
  # Define a method `method_name` that safely executes the given block before
@@ -127,6 +133,8 @@ module Aikido::Zen
127
133
  #
128
134
  # @note the block is executed within `safe` to handle errors safely; the original method is executed outside of `safe` to preserve the original behavior
129
135
  def sink_before(method_name, &block)
136
+ raise ArgumentError, "block required" unless block
137
+
130
138
  presafe_sink_before(method_name) do |*args, **kwargs|
131
139
  DSL.safe do
132
140
  instance_exec(*args, **kwargs, &block)
@@ -145,11 +153,17 @@ module Aikido::Zen
145
153
  #
146
154
  # @return [void]
147
155
  def presafe_sink_after(method_name, &block)
156
+ raise ArgumentError, "block required" unless block
157
+
158
+ original = instance_method(method_name)
159
+
148
160
  define_method(method_name) do |*args, **kwargs, &blk|
149
- result = super(*args, **kwargs, &blk)
161
+ result = original.bind_call(self, *args, **kwargs, &blk)
150
162
  instance_exec(result, *args, **kwargs, &block)
151
163
  result
152
164
  end
165
+ rescue NameError
166
+ Aikido::Zen.config.logger.warn("cannot wrap method `#{method_name}' for class `#{self}'")
153
167
  end
154
168
 
155
169
  # Define a method `method_name` that safely executes the given block after
@@ -165,6 +179,8 @@ module Aikido::Zen
165
179
  #
166
180
  # @note the block is executed within `safe` to handle errors safely; the original method is executed outside of `safe` to preserve the original behavior
167
181
  def sink_after(method_name, &block)
182
+ raise ArgumentError, "block required" unless block
183
+
168
184
  presafe_sink_after(method_name) do |result, *args, **kwargs|
169
185
  DSL.safe do
170
186
  instance_exec(result, *args, **kwargs, &block)
@@ -177,20 +193,26 @@ module Aikido::Zen
177
193
  #
178
194
  # @param method_name [Symbol, String] the name of the method to define
179
195
  # @yield the block to execute around the original method
180
- # @yieldparam super_call [Proc] the proc that calls the original method
196
+ # @yieldparam original_call [Proc] the proc that calls the original method
181
197
  # @yieldparam args [Array] the positional arguments passed to the original method
182
198
  # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
183
199
  #
184
200
  # @return [void]
185
201
  def presafe_sink_around(method_name, &block)
202
+ raise ArgumentError, "block required" unless block
203
+
204
+ original = instance_method(method_name)
205
+
186
206
  define_method(method_name) do |*args, **kwargs, &blk|
187
207
  result = nil
188
- super_call = proc do
189
- result = super(*args, **kwargs, &blk)
208
+ original_call = proc do
209
+ result = original.bind_call(self, *args, **kwargs, &blk)
190
210
  end
191
- instance_exec(super_call, *args, **kwargs, &block)
211
+ instance_exec(original_call, *args, **kwargs, &block)
192
212
  result
193
213
  end
214
+ rescue NameError
215
+ Aikido::Zen.config.logger.warn("cannot wrap method `#{method_name}' for class `#{self}'")
194
216
  end
195
217
 
196
218
  # Define a method `method_name` that safely executes the given block around
@@ -198,27 +220,29 @@ module Aikido::Zen
198
220
  #
199
221
  # @param method_name [Symbol, String] the name of the method to define
200
222
  # @yield the block to execute around the original method
201
- # @yieldparam super_call [Proc] the proc that calls the original method
223
+ # @yieldparam original_call [Proc] the proc that calls the original method
202
224
  # @yieldparam args [Array] the positional arguments passed to the original method
203
225
  # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
204
226
  #
205
227
  # @return [void]
206
228
  #
207
229
  # @note the block is executed within `safe` to handle errors safely; the original method is executed within `presafe` to preserve the original behavior
208
- # @note if the block does not call `super_call`, the original method is called automatically after the block is executed
230
+ # @note if the block does not call `original_call`, the original method is called automatically after the block is executed
209
231
  def sink_around(method_name, &block)
210
- presafe_sink_around(method_name) do |presafe_super_call, *args, **kwargs|
211
- super_called = false
212
- super_call = proc do
213
- super_called = true
232
+ raise ArgumentError, "block required" unless block
233
+
234
+ presafe_sink_around(method_name) do |presafe_original_call, *args, **kwargs|
235
+ original_called = false
236
+ original_call = proc do
237
+ original_called = true
214
238
  DSL.presafe do
215
- presafe_super_call.call
239
+ presafe_original_call.call
216
240
  end
217
241
  end
218
242
  DSL.safe do
219
- instance_exec(super_call, *args, **kwargs, &block)
243
+ instance_exec(original_call, *args, **kwargs, &block)
220
244
  end
221
- presafe_super_call.call unless super_called
245
+ presafe_original_call.call unless original_called
222
246
  end
223
247
  end
224
248
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Aikido
4
4
  module Zen
5
- VERSION = "1.0.1.beta.4"
5
+ VERSION = "1.0.2.beta.1"
6
6
 
7
7
  # The version of libzen_internals that we build against.
8
8
  LIBZEN_VERSION = "0.1.39"
data/lib/aikido/zen.rb CHANGED
@@ -39,7 +39,7 @@ module Aikido
39
39
  return unless config.protect?
40
40
 
41
41
  unless load_sources! && load_sinks!
42
- config.logger.warn "Zen could not find any supported libraries or frameworks. Visit https://github.com/AikidoSec/firewall-ruby for more information."
42
+ config.logger.warn("Zen could not find any supported libraries or frameworks. Visit https://github.com/AikidoSec/firewall-ruby for more information.")
43
43
  return
44
44
  end
45
45
 
@@ -165,6 +165,11 @@ module Aikido
165
165
  end
166
166
  end
167
167
 
168
+ # Align with other Zen implementations, while keeping internal consistency.
169
+ class << self
170
+ alias_method :set_user, :track_user
171
+ end
172
+
168
173
  # Marks that the Zen middleware was installed properly
169
174
  # @return void
170
175
  def self.middleware_installed!
@@ -209,7 +214,7 @@ module Aikido
209
214
  end
210
215
 
211
216
  def self.detached_agent_server
212
- @detached_agent_server ||= DetachedAgent::Server.start!
217
+ @detached_agent_server ||= DetachedAgent::Server.start
213
218
  end
214
219
 
215
220
  class << self
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aikido-zen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1.beta.4
4
+ version: 1.0.2.beta.1
5
5
  platform: arm64-darwin
6
6
  authors:
7
7
  - Aikido Security
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-08-11 00:00:00.000000000 Z
11
+ date: 2025-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby