aikido-zen 0.1.0 → 0.2.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +20 -0
  3. data/CHANGELOG.md +20 -0
  4. data/README.md +11 -2
  5. data/benchmarks/rails7.1_sql_injection.js +30 -34
  6. data/docs/banner.svg +128 -129
  7. data/docs/config.md +8 -6
  8. data/docs/rails.md +2 -2
  9. data/lib/aikido/zen/agent.rb +6 -2
  10. data/lib/aikido/zen/api_client.rb +3 -3
  11. data/lib/aikido/zen/attack.rb +105 -36
  12. data/lib/aikido/zen/collector/routes.rb +2 -0
  13. data/lib/aikido/zen/collector.rb +19 -3
  14. data/lib/aikido/zen/config.rb +44 -20
  15. data/lib/aikido/zen/errors.rb +10 -1
  16. data/lib/aikido/zen/event.rb +4 -2
  17. data/lib/aikido/zen/internals.rb +4 -0
  18. data/lib/aikido/zen/middleware/check_allowed_addresses.rb +2 -14
  19. data/lib/aikido/zen/middleware/middleware.rb +11 -0
  20. data/lib/aikido/zen/middleware/{throttler.rb → rack_throttler.rb} +3 -11
  21. data/lib/aikido/zen/middleware/request_tracker.rb +190 -0
  22. data/lib/aikido/zen/middleware/set_context.rb +1 -4
  23. data/lib/aikido/zen/payload.rb +2 -0
  24. data/lib/aikido/zen/rails_engine.rb +8 -0
  25. data/lib/aikido/zen/rate_limiter.rb +1 -1
  26. data/lib/aikido/zen/request/schema/builder.rb +0 -2
  27. data/lib/aikido/zen/request/schema/definition.rb +0 -5
  28. data/lib/aikido/zen/request/schema.rb +0 -3
  29. data/lib/aikido/zen/scanners/path_traversal/helpers.rb +65 -0
  30. data/lib/aikido/zen/scanners/path_traversal_scanner.rb +61 -0
  31. data/lib/aikido/zen/scanners/shell_injection/helpers.rb +159 -0
  32. data/lib/aikido/zen/scanners/shell_injection_scanner.rb +62 -0
  33. data/lib/aikido/zen/scanners/sql_injection_scanner.rb +0 -4
  34. data/lib/aikido/zen/scanners/ssrf_scanner.rb +21 -12
  35. data/lib/aikido/zen/scanners.rb +2 -0
  36. data/lib/aikido/zen/sinks/action_controller.rb +26 -12
  37. data/lib/aikido/zen/sinks/file.rb +120 -0
  38. data/lib/aikido/zen/sinks/http.rb +1 -1
  39. data/lib/aikido/zen/sinks/kernel.rb +73 -0
  40. data/lib/aikido/zen/sinks/pg.rb +13 -12
  41. data/lib/aikido/zen/sinks/typhoeus.rb +1 -1
  42. data/lib/aikido/zen/sinks.rb +8 -0
  43. data/lib/aikido/zen/system_info.rb +1 -1
  44. data/lib/aikido/zen/version.rb +2 -2
  45. data/lib/aikido/zen.rb +14 -1
  46. data/tasklib/bench.rake +3 -2
  47. metadata +16 -7
@@ -112,7 +112,8 @@ module Aikido::Zen
112
112
  is_port_relevant = input_uri.port != input_uri.default_port
113
113
  return false if is_port_relevant && input_uri.port != conn_uri.port
114
114
 
115
- conn_uri.hostname == input_uri.hostname
115
+ conn_uri.hostname == input_uri.hostname &&
116
+ conn_uri.port == input_uri.port
116
117
  end
117
118
 
118
119
  def private_ip?(hostname)
@@ -128,8 +129,11 @@ module Aikido::Zen
128
129
  # * The input itself, if it already looks like a URI.
129
130
  # * The input prefixed with http://
130
131
  # * The input prefixed with https://
132
+ # * The input prefixed with the scheme of the request's URI, to consider
133
+ # things like an FTP request (to "ftp://localhost") with a plain host
134
+ # as a user-input ("localhost").
131
135
  #
132
- # @return [Set<URI>]
136
+ # @return [Array<URI>] a list of unique URIs based on the above criteria.
133
137
  def uris_from_input
134
138
  input = @input.to_s
135
139
 
@@ -138,10 +142,12 @@ module Aikido::Zen
138
142
  # valid hostname. We should do the same for the input.
139
143
  input = format("[%s]", input) if unescaped_ipv6?(input)
140
144
 
141
- [input, "http://#{input}", "https://#{input}"]
142
- .map { |candidate| as_uri(candidate) }
143
- .compact
144
- .uniq
145
+ [
146
+ input,
147
+ "http://#{input}",
148
+ "https://#{input}",
149
+ "#{@request_uri.scheme}://#{input}"
150
+ ].map { |candidate| as_uri(candidate) }.compact.uniq
145
151
  end
146
152
 
147
153
  def as_uri(string)
@@ -222,11 +228,11 @@ module Aikido::Zen
222
228
  # @api private
223
229
  class RedirectChains
224
230
  def initialize
225
- @redirects = {}
231
+ @redirects = Hash.new { |h, k| h[k] = [] }
226
232
  end
227
233
 
228
234
  def add(source:, destination:)
229
- @redirects[destination] = source
235
+ @redirects[destination].push(source)
230
236
  self
231
237
  end
232
238
 
@@ -236,11 +242,14 @@ module Aikido::Zen
236
242
  #
237
243
  # @param uri [URI]
238
244
  # @return [URI, nil]
239
- def origin(uri)
240
- source = @redirects[uri]
245
+ def origin(uri, visited = Set.new)
246
+ source = @redirects[uri].first
241
247
 
242
- if @redirects[source]
243
- origin(source)
248
+ return source if visited.include?(source)
249
+ visited << source
250
+
251
+ if !@redirects[source].empty?
252
+ origin(source, visited)
244
253
  else
245
254
  source
246
255
  end
@@ -3,3 +3,5 @@
3
3
  require_relative "scanners/sql_injection_scanner"
4
4
  require_relative "scanners/stored_ssrf_scanner"
5
5
  require_relative "scanners/ssrf_scanner"
6
+ require_relative "scanners/path_traversal_scanner"
7
+ require_relative "scanners/shell_injection_scanner"
@@ -3,12 +3,12 @@
3
3
  module Aikido::Zen
4
4
  module Sinks
5
5
  module ActionController
6
- # Implements the "middleware" for rate limiting in Rails apps, where we
7
- # need to check at the end of the `before_action` chain, rather than in
8
- # an actual Rack middleware, to allow for calls to Zen.track_user being
9
- # made from before_actions in the host app, thus allowing rate-limiting
10
- # by user ID rather than solely by IP.
11
- class Throttler
6
+ # Implements the "middleware" for blocking requests (i.e.: rate-limiting or blocking
7
+ # user/bots) in Rails apps, where we need to check at the end of the `before_action`
8
+ # chain, rather than in an actual Rack middleware, to allow for calls to
9
+ # `Zen.track_user` being made from before_actions in the host app, thus allowing
10
+ # block/rate-limit by user ID rather than solely by IP.
11
+ class BlockRequestChecker
12
12
  def initialize(
13
13
  config: Aikido::Zen.config,
14
14
  settings: Aikido::Zen.runtime_settings,
@@ -19,10 +19,18 @@ module Aikido::Zen
19
19
  @rate_limiter = rate_limiter
20
20
  end
21
21
 
22
- def throttle(controller)
22
+ def block?(controller)
23
23
  context = controller.request.env[Aikido::Zen::ENV_KEY]
24
24
  request = context.request
25
25
 
26
+ if should_block_user?(request)
27
+ status, headers, body = @config.blocked_responder.call(request, :user)
28
+ controller.headers.update(headers)
29
+ controller.render plain: Array(body).join, status: status
30
+
31
+ return true
32
+ end
33
+
26
34
  if should_throttle?(request)
27
35
  status, headers, body = @config.rate_limited_responder.call(request)
28
36
  controller.headers.update(headers)
@@ -39,10 +47,17 @@ module Aikido::Zen
39
47
 
40
48
  @rate_limiter.throttle?(request)
41
49
  end
50
+
51
+ # @param request [Aikido::Zen::Request]
52
+ private def should_block_user?(request)
53
+ return false if request.actor.nil?
54
+
55
+ @settings.blocked_user_ids&.include?(request.actor.id)
56
+ end
42
57
  end
43
58
 
44
- def self.throttler
45
- @throttler ||= Aikido::Zen::Sinks::ActionController::Throttler.new
59
+ def self.block_request_checker
60
+ @block_request_checker ||= Aikido::Zen::Sinks::ActionController::BlockRequestChecker.new
46
61
  end
47
62
 
48
63
  module Extensions
@@ -50,10 +65,9 @@ module Aikido::Zen
50
65
  return super unless kind == :process_action
51
66
 
52
67
  super do
53
- rate_limiter = Aikido::Zen::Sinks::ActionController.throttler
54
- throttled = rate_limiter.throttle(self)
68
+ checker = Aikido::Zen::Sinks::ActionController.block_request_checker
55
69
 
56
- yield if block_given? && !throttled
70
+ yield if block_given? && !checker.block?(self)
57
71
  end
58
72
  end
59
73
  end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aikido::Zen
4
+ module Sinks
5
+ module File
6
+ SINK = Sinks.add("File", scanners: [
7
+ Aikido::Zen::Scanners::PathTraversalScanner
8
+ ])
9
+
10
+ module Extensions
11
+ def self.scan_path(filepath, operation)
12
+ SINK.scan(
13
+ filepath: filepath,
14
+ operation: operation
15
+ )
16
+ end
17
+
18
+ # Module to extend only the initializer method of `File` (`File.new`)
19
+ module Initiliazer
20
+ def initialize(filename, *, **)
21
+ Extensions.scan_path(filename, "new")
22
+ super
23
+ end
24
+ end
25
+
26
+ def open(filename, *, **)
27
+ Extensions.scan_path(filename, "open")
28
+ super
29
+ end
30
+
31
+ def read(filename, *)
32
+ Extensions.scan_path(filename, "read")
33
+ super
34
+ end
35
+
36
+ def write(filename, *, **)
37
+ Extensions.scan_path(filename, "write")
38
+ super
39
+ end
40
+
41
+ def join(*)
42
+ joined = super
43
+ Extensions.scan_path(joined, "join")
44
+ joined
45
+ end
46
+
47
+ def chmod(mode, *paths)
48
+ paths.each { |path| Extensions.scan_path(path, "chmod") }
49
+ super
50
+ end
51
+
52
+ def chown(user, group, *paths)
53
+ paths.each { |path| Extensions.scan_path(path, "chown") }
54
+ super
55
+ end
56
+
57
+ def rename(from, to)
58
+ Extensions.scan_path(from, "rename")
59
+ Extensions.scan_path(to, "rename")
60
+ super
61
+ end
62
+
63
+ def symlink(from, to)
64
+ Extensions.scan_path(from, "symlink")
65
+ Extensions.scan_path(to, "symlink")
66
+ super
67
+ end
68
+
69
+ def truncate(file_name, *)
70
+ Extensions.scan_path(file_name, "truncate")
71
+ super
72
+ end
73
+
74
+ def unlink(*args)
75
+ args.each do |arg|
76
+ Extensions.scan_path(arg, "unlink")
77
+ end
78
+ super
79
+ end
80
+
81
+ def delete(*args)
82
+ args.each do |arg|
83
+ Extensions.scan_path(arg, "delete")
84
+ end
85
+ super
86
+ end
87
+
88
+ def utime(atime, mtime, *args)
89
+ args.each do |arg|
90
+ Extensions.scan_path(arg, "utime")
91
+ end
92
+ super
93
+ end
94
+
95
+ def expand_path(filename, *)
96
+ Extensions.scan_path(filename, "expand_path")
97
+ super
98
+ end
99
+
100
+ def realpath(filename, *)
101
+ Extensions.scan_path(filename, "realpath")
102
+ super
103
+ end
104
+
105
+ def realdirpath(filename, *)
106
+ Extensions.scan_path(filename, "realdirpath")
107
+ super
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ # Internally, Path Traversal's scanner logic uses `expand_path`, in order to avoid recursion issues we keep
115
+ # a copy of the original method, only to be used internally.
116
+ # It's important to keep this line before prepend the Extensions module, otherwise the alias will call
117
+ # the extended method.
118
+ ::File.singleton_class.alias_method :expand_path__internal_for_aikido_zen, :expand_path
119
+ ::File.singleton_class.prepend(Aikido::Zen::Sinks::File::Extensions)
120
+ ::File.prepend Aikido::Zen::Sinks::File::Extensions::Initiliazer
@@ -66,7 +66,7 @@ module Aikido::Zen
66
66
 
67
67
  response
68
68
  ensure
69
- context["ssrf.request"] = prev_request
69
+ context["ssrf.request"] = prev_request if context
70
70
  end
71
71
  end
72
72
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aikido::Zen
4
+ module Sinks
5
+ module Kernel
6
+ SINK = Sinks.add("Kernel", scanners: [
7
+ Aikido::Zen::Scanners::ShellInjectionScanner
8
+ ])
9
+
10
+ module Extensions
11
+ # Checks if the user introduced input is trying to execute other commands
12
+ # using Shell Injection kind of attacks.
13
+ #
14
+ # @param command [String] the _full command_ that will be executed.
15
+ # @param context [Aikido::Zen::Context]
16
+ # @param sink [Aikido::Zen::Sink] the Sink that is running the scan.
17
+ # @param operation [Symbol, String] name of the method being scanned.
18
+ #
19
+ # @return [Aikido::Zen::Attacks::ShellInjectionAttack, nil] an Attack if any
20
+ # user input is detected as part of a Shell Injection Attack, or +nil+ if it's safe.
21
+ def self.scan_command(command, operation)
22
+ SINK.scan(
23
+ command: command,
24
+ operation: operation
25
+ )
26
+ end
27
+
28
+ # `system, spawn` functions can be invoked in several ways. For more details,
29
+ # see [the documentation](https://ruby-doc.org/3.4.1/Kernel.html#method-i-spawn)
30
+ #
31
+ # In our context, we care primarily about two common scenarios:
32
+ # - one argument (String)
33
+ # e.g.: system("ls"), system("echo something")
34
+ # - two arguments (Hash, String)
35
+ # e.g.: system({"foo" => "bar"}, "ls"), system({"foo" => "bar"}, "echo something")
36
+ #
37
+ # In all other cases, we do not protect against shell argument injections. Specifically:
38
+ #
39
+ # If a user input contains something like $(whoami) and is passed as part of the command
40
+ # arguments (e.g., user_input = "$(whoami)"):
41
+ #
42
+ # system("echo", user_input) This is safe because Ruby automatically escapes arguments
43
+ # passed to system/spawn in this form.
44
+ #
45
+ # system("echo #{user_input}") This is not safe because Ruby interpolates the user_input
46
+ # into the command string, resulting in a potentially harmful
47
+ # command like `echo $(whoami)`.
48
+ def send_arg_to_scan(args, operation)
49
+ if args.size == 1 && args[0].is_a?(String)
50
+ Extensions.scan_command(args[0], operation)
51
+ end
52
+
53
+ if args.size == 2 && args[0].is_a?(Hash)
54
+ Extensions.scan_command(args[1], operation)
55
+ end
56
+ end
57
+
58
+ def system(*args, **)
59
+ send_arg_to_scan(args, "system")
60
+ super
61
+ end
62
+
63
+ def spawn(*args, **)
64
+ send_arg_to_scan(args, "spawn")
65
+ super
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ ::Kernel.singleton_class.prepend Aikido::Zen::Sinks::Kernel::Extensions
73
+ ::Kernel.prepend Aikido::Zen::Sinks::Kernel::Extensions
@@ -7,6 +7,17 @@ module Aikido::Zen
7
7
  module PG
8
8
  SINK = Sinks.add("pg", scanners: [Scanners::SQLInjectionScanner])
9
9
 
10
+ # For some reason, the ActiveRecord pg adapter does not wrap exceptions in
11
+ # StatementInvalid, which leads to inconsistent handling. This guarantees
12
+ # that all Zen errors are wrapped in a StatementInvalid, so documentation
13
+ # can be consistent.
14
+ WRAP_EXCEPTIONS = if defined?(ActiveRecord::StatementInvalid)
15
+ <<~RUBY
16
+ rescue Aikido::Zen::SQLInjectionError
17
+ raise ActiveRecord::StatementInvalid
18
+ RUBY
19
+ end
20
+
10
21
  module Extensions
11
22
  %i[
12
23
  send_query exec sync_exec async_exec
@@ -16,12 +27,7 @@ module Aikido::Zen
16
27
  def #{method}(query, *)
17
28
  SINK.scan(query: query, dialect: :postgresql, operation: :#{method})
18
29
  super
19
- rescue Aikido::Zen::SQLInjectionError
20
- # The pg adapter does not wrap exceptions in StatementInvalid, which
21
- # leads to inconsistent handling. This guarantees that all Aikido
22
- # errors are wrapped in a StatementInvalid, so documentation can be
23
- # consistent.
24
- raise ActiveRecord::StatementInvalid
30
+ #{WRAP_EXCEPTIONS}
25
31
  end
26
32
  RUBY
27
33
  end
@@ -33,12 +39,7 @@ module Aikido::Zen
33
39
  def #{method}(_, query, *)
34
40
  SINK.scan(query: query, dialect: :postgresql, operation: :#{method})
35
41
  super
36
- rescue Aikido::Zen::SQLInjectionError
37
- # The pg adapter does not wrap exceptions in StatementInvalid, which
38
- # leads to inconsistent handling. This guarantees that all Aikido
39
- # errors are wrapped in a StatementInvalid, so documentation can be
40
- # consistent.
41
- raise ActiveRecord::StatementInvalid
42
+ #{WRAP_EXCEPTIONS}
42
43
  end
43
44
  RUBY
44
45
  end
@@ -66,7 +66,7 @@ module Aikido::Zen
66
66
  operation: "request"
67
67
  )
68
68
  ensure
69
- context["ssrf.request"] = nil
69
+ context["ssrf.request"] = nil if context
70
70
  end
71
71
 
72
72
  true
@@ -5,6 +5,14 @@ require_relative "sink"
5
5
  require_relative "sinks/socket"
6
6
 
7
7
  require_relative "sinks/action_controller" if defined?(::ActionController)
8
+ require_relative "sinks/file" if defined?(::File)
9
+
10
+ # Sadly, in ruby versions lower than 3.0, it's not possible to patch the
11
+ # Kernel module because how the `prepend` method is applied
12
+ # (https://stackoverflow.com/questions/78110397/prepend-kernel-module-function-globally#comment137713906_78112924)
13
+ if RUBY_VERSION >= "3.0"
14
+ require_relative "sinks/kernel" if defined?(::Kernel)
15
+ end
8
16
  require_relative "sinks/resolv" if defined?(::Resolv)
9
17
  require_relative "sinks/net_http" if defined?(::Net::HTTP)
10
18
  require_relative "sinks/http" if defined?(::HTTP)
@@ -60,7 +60,7 @@ module Aikido::Zen
60
60
  end
61
61
 
62
62
  def os_version
63
- Gem::Platform.local.version
63
+ Gem::Platform.local.version || "unknown"
64
64
  end
65
65
 
66
66
  def as_json
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Aikido
4
4
  module Zen
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
 
7
7
  # The version of libzen_internals that we build against.
8
- LIBZEN_VERSION = "0.1.30"
8
+ LIBZEN_VERSION = "0.1.37"
9
9
  end
10
10
  end
data/lib/aikido/zen.rb CHANGED
@@ -10,13 +10,15 @@ require_relative "zen/worker"
10
10
  require_relative "zen/agent"
11
11
  require_relative "zen/api_client"
12
12
  require_relative "zen/context"
13
+ require_relative "zen/middleware/check_allowed_addresses"
14
+ require_relative "zen/middleware/middleware"
15
+ require_relative "zen/middleware/request_tracker"
13
16
  require_relative "zen/middleware/set_context"
14
17
  require_relative "zen/outbound_connection"
15
18
  require_relative "zen/outbound_connection_monitor"
16
19
  require_relative "zen/runtime_settings"
17
20
  require_relative "zen/rate_limiter"
18
21
  require_relative "zen/scanners"
19
- require_relative "zen/middleware/check_allowed_addresses"
20
22
  require_relative "zen/rails_engine" if defined?(::Rails)
21
23
 
22
24
  module Aikido
@@ -70,6 +72,11 @@ module Aikido
70
72
  collector.track_request(request)
71
73
  end
72
74
 
75
+ def self.track_discovered_route(request)
76
+ autostart
77
+ collector.track_route(request)
78
+ end
79
+
73
80
  # Tracks a network connection made to an external service.
74
81
  #
75
82
  # @param connection [Aikido::Zen::OutboundConnection]
@@ -113,6 +120,12 @@ module Aikido
113
120
  end
114
121
  end
115
122
 
123
+ # Marks that the Zen middleware was installed properly
124
+ # @return void
125
+ def self.middleware_installed!
126
+ collector.middleware_installed!
127
+ end
128
+
116
129
  # Load all sinks matching libraries loaded into memory. This method should
117
130
  # be called after all other dependencies have been loaded into memory (i.e.
118
131
  # at the end of the initialization process).
data/tasklib/bench.rake CHANGED
@@ -12,11 +12,12 @@ end
12
12
 
13
13
  def boot_server(dir, port:, env: {})
14
14
  env["PORT"] = port.to_s
15
+ env["SECRET_KEY_BASE"] = rand(36**64).to_s(36)
15
16
 
16
17
  Dir.chdir(dir) do
17
18
  SERVER_PIDS[port] = Process.spawn(
18
19
  env,
19
- "rails", "server", "--pid", "#{Dir.pwd}/tmp/pids/server.#{port}.pid",
20
+ "rails", "server", "--pid", "#{Dir.pwd}/tmp/pids/server.#{port}.pid", "-e", "production",
20
21
  out: "/dev/null"
21
22
  )
22
23
  rescue
@@ -61,7 +62,7 @@ Pathname.glob("sample_apps/*").select(&:directory?).each do |dir|
61
62
  end
62
63
 
63
64
  task :boot_unprotected_app do
64
- boot_server(dir, port: 3002, env: {"AIKIDO_DISABLE" => "true"})
65
+ boot_server(dir, port: 3002, env: {"AIKIDO_DISABLED" => "true"})
65
66
  end
66
67
  end
67
68
 
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: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Sanguinetti
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-15 00:00:00.000000000 Z
11
+ date: 2025-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -52,7 +52,7 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- description:
55
+ description:
56
56
  email:
57
57
  - foca@foca.io
58
58
  executables: []
@@ -60,6 +60,7 @@ extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
62
  - ".ruby-version"
63
+ - ".simplecov"
63
64
  - ".standard.yml"
64
65
  - CHANGELOG.md
65
66
  - LICENSE
@@ -93,8 +94,10 @@ files:
93
94
  - lib/aikido/zen/event.rb
94
95
  - lib/aikido/zen/internals.rb
95
96
  - lib/aikido/zen/middleware/check_allowed_addresses.rb
97
+ - lib/aikido/zen/middleware/middleware.rb
98
+ - lib/aikido/zen/middleware/rack_throttler.rb
99
+ - lib/aikido/zen/middleware/request_tracker.rb
96
100
  - lib/aikido/zen/middleware/set_context.rb
97
- - lib/aikido/zen/middleware/throttler.rb
98
101
  - lib/aikido/zen/outbound_connection.rb
99
102
  - lib/aikido/zen/outbound_connection_monitor.rb
100
103
  - lib/aikido/zen/package.rb
@@ -121,6 +124,10 @@ files:
121
124
  - lib/aikido/zen/runtime_settings/rate_limit_settings.rb
122
125
  - lib/aikido/zen/scan.rb
123
126
  - lib/aikido/zen/scanners.rb
127
+ - lib/aikido/zen/scanners/path_traversal/helpers.rb
128
+ - lib/aikido/zen/scanners/path_traversal_scanner.rb
129
+ - lib/aikido/zen/scanners/shell_injection/helpers.rb
130
+ - lib/aikido/zen/scanners/shell_injection_scanner.rb
124
131
  - lib/aikido/zen/scanners/sql_injection_scanner.rb
125
132
  - lib/aikido/zen/scanners/ssrf/dns_lookups.rb
126
133
  - lib/aikido/zen/scanners/ssrf/private_ip_checker.rb
@@ -133,9 +140,11 @@ files:
133
140
  - lib/aikido/zen/sinks/curb.rb
134
141
  - lib/aikido/zen/sinks/em_http.rb
135
142
  - lib/aikido/zen/sinks/excon.rb
143
+ - lib/aikido/zen/sinks/file.rb
136
144
  - lib/aikido/zen/sinks/http.rb
137
145
  - lib/aikido/zen/sinks/httpclient.rb
138
146
  - lib/aikido/zen/sinks/httpx.rb
147
+ - lib/aikido/zen/sinks/kernel.rb
139
148
  - lib/aikido/zen/sinks/mysql2.rb
140
149
  - lib/aikido/zen/sinks/net_http.rb
141
150
  - lib/aikido/zen/sinks/patron.rb
@@ -158,7 +167,7 @@ metadata:
158
167
  homepage_uri: https://aikido.dev
159
168
  source_code_uri: https://github.com/aikidosec/firewall-ruby
160
169
  changelog_uri: https://github.com/aikidosec/firewall-ruby/blob/main/CHANGELOG.md
161
- post_install_message:
170
+ post_install_message:
162
171
  rdoc_options: []
163
172
  require_paths:
164
173
  - lib
@@ -174,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
183
  version: '0'
175
184
  requirements: []
176
185
  rubygems_version: 3.5.22
177
- signing_key:
186
+ signing_key:
178
187
  specification_version: 4
179
188
  summary: Embedded Web Application Firewall that autonomously protects Ruby apps against
180
189
  common and critical attacks.