aikido-zen 0.2.0-arm64-darwin → 1.0.1.beta.2-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.
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-arm64-darwin.dylib +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.aarch64.dylib +0 -0
  72. data/lib/aikido.rb +0 -3
data/tasklib/libzen.rake CHANGED
@@ -1,23 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ffi"
3
4
  require "open-uri"
4
5
  require "rubygems/package_task"
5
6
 
6
7
  require_relative "../lib/aikido/zen/version"
7
8
 
8
- LibZenDL = Struct.new(:os, :arch, :artifact) do
9
- def download
10
- puts "Downloading #{path}"
11
- File.open(path, "wb") { |file| FileUtils.copy_stream(URI(url).open("rb"), file) }
12
- end
13
-
14
- def verify
15
- expected = URI(url + ".sha256sum").read.split(/\s+/).first
16
- actual = Digest::SHA256.file(path).to_s
9
+ class LibZen
10
+ attr_reader :platform, :suffix, :artifact
17
11
 
18
- if expected != actual
19
- abort "Checksum mismatch on #{path}: Expected #{expected}, got #{actual}."
20
- end
12
+ def initialize(platform_suffix, artifact = nil)
13
+ platform, suffix = platform_suffix.split(".", 2)
14
+ @platform = Gem::Platform.new(platform)
15
+ @suffix = suffix
16
+ @artifact = artifact
21
17
  end
22
18
 
23
19
  def version
@@ -25,79 +21,90 @@ LibZenDL = Struct.new(:os, :arch, :artifact) do
25
21
  end
26
22
 
27
23
  def path
28
- [prefix, arch, ext].join(".")
24
+ "lib/aikido/zen/libzen-#{version}-#{platform}.#{suffix}"
29
25
  end
30
26
 
31
- def gem_path
32
- platform = "-#{gemspec.platform}" unless gemspec.platform.to_s == "ruby"
33
- "pkg/#{gemspec.name}-#{gemspec.version}#{platform}.gem"
27
+ def url
28
+ File.join("https://github.com/AikidoSec/zen-internals/releases/download", version, artifact)
34
29
  end
35
30
 
36
- def pkg_dir
37
- File.dirname(gem_path)
31
+ def gemspec(source = Bundler.load_gemspec("aikido-zen.gemspec"))
32
+ return @spec if defined?(@spec)
33
+
34
+ @spec = source.dup
35
+ @spec.platform = platform
36
+ @spec.files << path
37
+ @spec
38
38
  end
39
39
 
40
- def prefix
41
- "lib/aikido/zen/libzen-#{version}"
40
+ def gem_path
41
+ "pkg/#{gemspec.name}-#{gemspec.version}-#{gemspec.platform}.gem"
42
42
  end
43
43
 
44
- def ext
45
- case os
46
- when :darwin then "dylib"
47
- when :linux then "so"
48
- when :windows then "dll"
49
- end
44
+ def resolvable?
45
+ downloadable? || File.exist?(path)
50
46
  end
51
47
 
52
- def url
53
- File.join("https://github.com/AikidoSec/zen-internals/releases/download", version, artifact)
48
+ def downloadable?
49
+ !artifact.nil?
54
50
  end
55
51
 
56
- def gem_platform
57
- gem_os = (os == :windows) ? "mingw64" : os
58
- platform = (arch == "aarch64") ? "arm64" : arch
59
- Gem::Platform.new("#{platform}-#{gem_os}")
52
+ def download
53
+ puts "Downloading #{path}"
54
+ File.open(path, "wb") { |file| FileUtils.copy_stream(URI(url).open("rb"), file) }
60
55
  end
61
56
 
62
- def gemspec(source = Bundler.load_gemspec("aikido-zen.gemspec"))
63
- return @spec if defined?(@spec)
57
+ def verify
58
+ expected = URI(url + ".sha256sum").read.split(/\s+/).first
59
+ actual = Digest::SHA256.file(path).to_s
64
60
 
65
- @spec = source.dup
66
- @spec.platform = gem_platform
67
- @spec.files << path
68
- @spec
61
+ if expected != actual
62
+ abort "Checksum verification failed for #{path}: expected #{expected}, but got #{actual}"
63
+ end
69
64
  end
70
65
 
71
66
  def namespace
72
- "#{os}:#{arch}"
67
+ platform.to_s
68
+ end
69
+
70
+ def pkg_dir
71
+ File.dirname(gem_path)
73
72
  end
74
73
  end
75
74
 
76
- LIBZEN = [
77
- LibZenDL.new(:darwin, "aarch64", "libzen_internals_aarch64-apple-darwin.dylib"),
78
- LibZenDL.new(:darwin, "x86_64", "libzen_internals_x86_64-apple-darwin.dylib"),
79
- LibZenDL.new(:linux, "aarch64", "libzen_internals_aarch64-unknown-linux-gnu.so"),
80
- LibZenDL.new(:linux, "x86_64", "libzen_internals_x86_64-unknown-linux-gnu.so"),
81
- LibZenDL.new(:windows, "x86_64", "libzen_internals_x86_64-pc-windows-gnu.dll")
82
- ]
75
+ LIBZENS = [
76
+ LibZen.new("arm64-darwin.dylib", "libzen_internals_aarch64-apple-darwin.dylib"),
77
+ LibZen.new("arm64-linux.so", "libzen_internals_aarch64-unknown-linux-gnu.so"),
78
+ LibZen.new("arm64-linux-musl.so", "libzen_internals_aarch64-unknown-linux-musl.so"),
79
+ LibZen.new("x86_64-darwin.dylib", "libzen_internals_x86_64-apple-darwin.dylib"),
80
+ LibZen.new("x86_64-linux.so", "libzen_internals_x86_64-unknown-linux-gnu.so"),
81
+ LibZen.new("x86_64-linux-musl.so", "libzen_internals_x86_64-unknown-linux-musl.so"),
82
+ LibZen.new("x86_64-mingw64.dll", "libzen_internals_x86_64-pc-windows-gnu.dll"),
83
+ # Not officially supported, but used during testing:
84
+ LibZen.new("x86_64-freebsd.so"),
85
+ LibZen.new("x86_64-solaris.so")
86
+ ].filter(&:resolvable?)
87
+
83
88
  namespace :libzen do
84
- LIBZEN.each do |lib|
85
- desc "Download libzen for #{lib.os}-#{lib.arch} if necessary"
89
+ LIBZENS.each do |lib|
90
+ desc "Download libzen for #{lib.platform} if necessary"
86
91
  task(lib.namespace => lib.path)
87
92
 
88
- file(lib.path) {
89
- lib.download
90
- lib.verify
91
- }
92
- CLEAN.include(lib.path)
93
+ if lib.downloadable?
94
+ file(lib.path) do
95
+ lib.download
96
+ lib.verify
97
+ end
98
+ CLEAN.include(lib.path)
99
+ end
93
100
 
94
101
  directory lib.pkg_dir
95
102
  CLOBBER.include(lib.pkg_dir)
96
103
 
97
- file(lib.gem_path => [lib.path, lib.pkg_dir]) {
104
+ file(lib.gem_path => [lib.path, lib.pkg_dir]) do
98
105
  path = Gem::Package.build(lib.gemspec)
99
106
  mv path, lib.pkg_dir
100
- }
107
+ end
101
108
  CLOBBER.include(lib.pkg_dir)
102
109
 
103
110
  task "#{lib.namespace}:release" => [lib.gem_path, "release:guard_clean"] do
@@ -105,24 +112,21 @@ namespace :libzen do
105
112
  end
106
113
  end
107
114
 
108
- desc "Build all the native gems for the different libzen versions"
109
- task gems: LIBZEN.map(&:gem_path)
115
+ desc "Build all the native gems"
116
+ task gems: LIBZENS.map(&:gem_path)
110
117
 
111
118
  desc "Push all the native gems to RubyGems"
112
- task release: LIBZEN.map { |lib| "#{lib.namespace}:release" }
119
+ task release: LIBZENS.map { |lib| "#{lib.namespace}:release" }
113
120
 
114
121
  desc "Download the libzen pre-built library for all platforms"
115
- task "download:all" => LIBZEN.map(&:path)
122
+ task "download:all" => LIBZENS.map(&:path)
116
123
 
117
124
  desc "Downloads the libzen library for the current platform"
118
125
  task "download:current" do
119
- require "rbconfig"
120
- os = case RbConfig::CONFIG["host_os"]
121
- when /darwin/ then :darwin
122
- when /mingw|cygwin|mswin/ then :windows
123
- else :linux
124
- end
126
+ platform = Gem::Platform.local.dup
127
+ platform.version = nil unless Rake::Task.task_defined?("libzen:#{platform}")
125
128
 
126
- Rake::Task["libzen:#{os}:#{RbConfig::CONFIG["build_cpu"]}"].invoke
129
+ # Invoke the most specific task
130
+ Rake::Task["libzen:#{platform}"].invoke
127
131
  end
128
132
  end
data/tasklib/wrk.rb ADDED
@@ -0,0 +1,88 @@
1
+ require "open3"
2
+ require "time"
3
+
4
+ NUMBER_OF_THREADS = ENV.fetch("BENCHMARK_NUMBER_OF_THREADS") { 12 }.to_s
5
+ CONNECTIONS = ENV.fetch("BENCHMARK_WRK_CONNECTIONS") { 400 }
6
+
7
+ def generate_wrk_command_for_url(url)
8
+ # Define the command with wrk included
9
+ "wrk --threads #{NUMBER_OF_THREADS} --connections #{CONNECTIONS} --duration 15s --timeout 5s --latency #{url}"
10
+ end
11
+
12
+ def cold_start(url)
13
+ 10.times do
14
+ _, err, status = Open3.capture3("curl #{url}")
15
+
16
+ if status != 0
17
+ puts err
18
+ exit(-1)
19
+ end
20
+ end
21
+ end
22
+
23
+ def extract_requests_and_latency_tuple(out, err, status)
24
+ if status == 0
25
+ # Extracting requests/sec
26
+ requests_sec_match = out.match(/Requests\/sec:\s+([\d.]+)/)
27
+ requests_sec = requests_sec_match[1].to_f if requests_sec_match
28
+
29
+ # Extracting latency
30
+ latency_match = out.match(/Latency\s+([\d.]+)(ms|s)/)
31
+ latency = latency_match[1].to_f if latency_match
32
+ latency_unit = latency_match[2] if latency_match
33
+
34
+ if latency_unit == "s"
35
+ latency *= 1000
36
+ end
37
+
38
+ {requests_sec: requests_sec, latency: latency}
39
+ else
40
+ puts "Error occurred running benchmark command:"
41
+ puts err.strip
42
+ exit(1)
43
+ end
44
+ end
45
+
46
+ def run_benchmark(route_no_zen:, route_zen:, description:, throughput_decrease_limit_perc:, latency_increase_limit_ms:)
47
+ # Cold start
48
+ cold_start(route_no_zen)
49
+ cold_start(route_zen)
50
+
51
+ out, err, status = Open3.capture3(generate_wrk_command_for_url(route_zen))
52
+ puts <<~MSG
53
+ WRK OUTPUT
54
+ ================
55
+ FIREWALL ENABLED:
56
+ #{out}
57
+ ----------------
58
+ MSG
59
+ result_zen_enabled = extract_requests_and_latency_tuple(out, err, status)
60
+
61
+ out, err, status = Open3.capture3(generate_wrk_command_for_url(route_no_zen))
62
+ puts <<~MSG
63
+ FIREWALL DISABLED:
64
+ #{out}
65
+ ================
66
+ MSG
67
+ result_zen_disabled = extract_requests_and_latency_tuple(out, err, status)
68
+
69
+ # Check if the command was successful
70
+ if result_zen_enabled && result_zen_disabled
71
+ # Print the output, which should be the Requests/sec value
72
+ puts "[ZEN ENABLED ] Requests/sec: #{result_zen_enabled[:requests_sec]} | Latency in ms: #{result_zen_enabled[:latency]}"
73
+ puts "[ZEN DISABLED] Requests/sec: #{result_zen_disabled[:requests_sec]} | Latency in ms: #{result_zen_disabled[:latency]}"
74
+
75
+ latency_increase_ms = (result_zen_enabled[:latency] - result_zen_disabled[:latency]).round(2)
76
+ puts "-> Delta in ms: #{latency_increase_ms}ms after running load test on #{description}"
77
+
78
+ throughput_decrease_perc = ((result_zen_disabled[:requests_sec] - result_zen_enabled[:requests_sec]) / result_zen_disabled[:requests_sec] * 100).round
79
+ puts "-> #{throughput_decrease_perc}% decrease in throughput after running load test on #{description}\n"
80
+
81
+ if latency_increase_ms >= latency_increase_limit_ms
82
+ exit(1)
83
+ end
84
+ if throughput_decrease_perc >= throughput_decrease_limit_perc
85
+ exit(1)
86
+ end
87
+ end
88
+ end
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.2.0
4
+ version: 1.0.1.beta.2
5
5
  platform: arm64-darwin
6
6
  authors:
7
- - Nicolas Sanguinetti
7
+ - Aikido Security
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-10 00:00:00.000000000 Z
11
+ date: 2025-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -55,17 +55,18 @@ dependencies:
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
57
  force_ruby_platform: false
58
- description:
58
+ description: Zen by Aikido is an embedded Web Application Firewall that autonomously
59
+ protects Ruby apps against common and critical attacks.
59
60
  email:
60
- - foca@foca.io
61
+ - dev-admin@aikido.dev
61
62
  executables: []
62
63
  extensions: []
63
64
  extra_rdoc_files: []
64
65
  files:
66
+ - ".aikido"
65
67
  - ".ruby-version"
66
68
  - ".simplecov"
67
69
  - ".standard.yml"
68
- - CHANGELOG.md
69
70
  - LICENSE
70
71
  - README.md
71
72
  - Rakefile
@@ -75,13 +76,13 @@ files:
75
76
  - docs/config.md
76
77
  - docs/rails.md
77
78
  - lib/aikido-zen.rb
78
- - lib/aikido.rb
79
79
  - lib/aikido/zen.rb
80
80
  - lib/aikido/zen/actor.rb
81
81
  - lib/aikido/zen/agent.rb
82
82
  - lib/aikido/zen/agent/heartbeats_manager.rb
83
83
  - lib/aikido/zen/api_client.rb
84
84
  - lib/aikido/zen/attack.rb
85
+ - lib/aikido/zen/background_worker.rb
85
86
  - lib/aikido/zen/capped_collections.rb
86
87
  - lib/aikido/zen/collector.rb
87
88
  - lib/aikido/zen/collector/hosts.rb
@@ -93,10 +94,14 @@ files:
93
94
  - lib/aikido/zen/context.rb
94
95
  - lib/aikido/zen/context/rack_request.rb
95
96
  - lib/aikido/zen/context/rails_request.rb
97
+ - lib/aikido/zen/detached_agent.rb
98
+ - lib/aikido/zen/detached_agent/agent.rb
99
+ - lib/aikido/zen/detached_agent/front_object.rb
100
+ - lib/aikido/zen/detached_agent/server.rb
96
101
  - lib/aikido/zen/errors.rb
97
102
  - lib/aikido/zen/event.rb
98
103
  - lib/aikido/zen/internals.rb
99
- - lib/aikido/zen/libzen-v0.1.37.aarch64.dylib
104
+ - lib/aikido/zen/libzen-v0.1.39-arm64-darwin.dylib
100
105
  - lib/aikido/zen/middleware/check_allowed_addresses.rb
101
106
  - lib/aikido/zen/middleware/middleware.rb
102
107
  - lib/aikido/zen/middleware/rack_throttler.rb
@@ -158,19 +163,25 @@ files:
158
163
  - lib/aikido/zen/sinks/sqlite3.rb
159
164
  - lib/aikido/zen/sinks/trilogy.rb
160
165
  - lib/aikido/zen/sinks/typhoeus.rb
166
+ - lib/aikido/zen/sinks_dsl.rb
161
167
  - lib/aikido/zen/synchronizable.rb
162
168
  - lib/aikido/zen/system_info.rb
163
169
  - lib/aikido/zen/version.rb
164
170
  - lib/aikido/zen/worker.rb
171
+ - placeholder/.gitignore
172
+ - placeholder/README.md
173
+ - placeholder/Rakefile
174
+ - placeholder/lib/placeholder.rb.template
175
+ - placeholder/placeholder.gemspec.template
165
176
  - tasklib/bench.rake
166
177
  - tasklib/libzen.rake
167
- homepage: https://aikido.dev
178
+ - tasklib/wrk.rb
179
+ homepage: https://aikido.dev/zen
168
180
  licenses:
169
181
  - AGPL-3.0-or-later
170
182
  metadata:
171
- homepage_uri: https://aikido.dev
183
+ homepage_uri: https://aikido.dev/zen
172
184
  source_code_uri: https://github.com/aikidosec/firewall-ruby
173
- changelog_uri: https://github.com/aikidosec/firewall-ruby/blob/main/CHANGELOG.md
174
185
  post_install_message:
175
186
  rdoc_options: []
176
187
  require_paths:
@@ -189,6 +200,5 @@ requirements: []
189
200
  rubygems_version: 3.5.22
190
201
  signing_key:
191
202
  specification_version: 4
192
- summary: Embedded Web Application Firewall that autonomously protects Ruby apps against
193
- common and critical attacks.
203
+ summary: Embedded Web Application Firewall.
194
204
  test_files: []
data/CHANGELOG.md DELETED
@@ -1,25 +0,0 @@
1
- # Changelog
2
-
3
- ## [Unreleased]
4
-
5
- ### Fixed
6
-
7
- - Avoid an infinite loop when checking for SSRFs in a circular redirects loop.
8
-
9
- ## 0.1.1
10
-
11
- ### Fixed
12
-
13
- - Avoid an error when sending the initial heartbeat if the Aikido server hasn't
14
- received stats yet.
15
- - Fix the SSRF scanner to ensure the port in the user-supplied payload matches
16
- the port in the request.
17
- - Don't break the HTTP.rb sink when a Zen context isn't set.
18
- - Don't break the Typhoeus sink when a Zen context isn't set.
19
- - Don't break the PG sink outside of Rails.
20
- - Updated [libzen](https://github.com/AikidoSec/zen-internals) to v0.1.31 to
21
- prevent flagging false positives in SQL queries with comments.
22
-
23
- ## 0.1.0
24
-
25
- - Initial version
data/lib/aikido.rb DELETED
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "aikido/zen"