aikido-zen 0.2.0-arm64-linux → 1.0.1.beta.2-arm64-linux

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 (71) 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.37.aarch64.so → libzen-v0.1.39-arm64-linux.so} +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.rb +0 -3
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aikido::Zen
4
+ module Sinks
5
+ module DSL
6
+ extend self
7
+
8
+ # In the context of `Aikido::Zen::Sinks::DSL`, the terms safe and presafe
9
+ # are defined as follows:
10
+ #
11
+ # safe: the desired state for a sink, particularly with respect to rescue.
12
+ #
13
+ # A sink is considered safe when unintended errors in the sink are handled,
14
+ # and-so are prevented from disrupting the operation of the original method
15
+ # (by raising unintended errors).
16
+ #
17
+ # presafe: the default state of a sink, particularly with respect to rescue.
18
+ #
19
+ # A sink is in the presafe state before and while unintended errors in the
20
+ # sink are not handled.
21
+ #
22
+ # Sink methods (like all methods) are in the presafe state when defined and
23
+ # become safe when unexpected errors cannot cause harm. The `safe` method
24
+ # is used to establish a safe state for the duration of the block executed.
25
+ # It is sometimes useful to be able to reestablish the presafe safe while
26
+ # inside a `safe` block; the `presafe` method allows this.
27
+ #
28
+ # Methods that contain the term presafe in their name should be used with
29
+ # appropriate care and understanding.
30
+ #
31
+ # IMPORTANT: All sinks should be safe!
32
+ #
33
+ # While this DSL proves useful for defining safe sink methods that follow
34
+ # common patterns, there are exceptions. It is always possible to define
35
+ # sink methods without using this DSL, but this should only be done when
36
+ # absolutely necessary. The sink methods defined using this DSL are safe,
37
+ # unless explicitly declared presafe.
38
+ #
39
+ # IMPORTANT: No sinks should be presafe in production!
40
+ #
41
+ # We are all responsible for ensuring that the sinks we implement are safe
42
+ # for production use. This DSL is only here to assist, by taking care of
43
+ # delicate edge cases and reducing the space for errors.
44
+ #
45
+ # When writing sink methods manually, some principles should be considered,
46
+ # to ensure safety for production:
47
+ #
48
+ # 1. Sink methods should ensure that the original method is always called,
49
+ # passing all parameters (positional, keyword, and block) exactly as they
50
+ # were passed to the original method, and return the result returned by
51
+ # the original method exactly as it was returned by the original method.
52
+ # (Unless intervention is required.)
53
+ #
54
+ # 2. Sink methods should not predict the signature of the original method,
55
+ # and-so restrict it from varying. The original method implementation is
56
+ # the sole and ultimate reference for its own behavior. We are observers
57
+ # (unless intervention is required).
58
+ #
59
+ # 3. Unexpected errors that are encountered in sink methods should not be
60
+ # capable of interfering with or preventing the normal operation of the
61
+ # original method. This includes but is not limited to exceptions that
62
+ # that may be raised when the sink method is called. Safe sink methods
63
+ # should return control to their caller (unless intervention is required).
64
+ #
65
+ # These are the guidelines adhered to by the `Aikido::Zen::Sinks::DSL`.
66
+
67
+ # The error with an original error as its cause to re-raise in `safe`.
68
+ class PresafeError < StandardError
69
+ end
70
+
71
+ # Safely execute the given block
72
+ #
73
+ # All standard errors are suppressed except `Aikido::Zen::UnderAttackError`s.
74
+ # This ensures that unexpected errors do not interrupt the execution of the
75
+ # original method, while all detected attacks are raised.
76
+ #
77
+ # When an error is wrapped in `PresafeError` the original error is reraised.
78
+ #
79
+ # @yield the block to execute
80
+ def safe
81
+ yield
82
+ rescue Aikido::Zen::UnderAttackError
83
+ raise
84
+ rescue PresafeError => err
85
+ raise err.cause
86
+ rescue => err
87
+ Aikido::Zen.config.logger.debug("[safe] #{err.class}: #{err.message}")
88
+ end
89
+
90
+ # Presafely execute the given block
91
+ #
92
+ # Safely wrap standard errors in `PresafeError` so that the original error is
93
+ # reraised when rescued in `safe`.
94
+ #
95
+ # @yield the block to execute
96
+ def presafe
97
+ yield
98
+ rescue => err
99
+ raise PresafeError, cause: err
100
+ end
101
+
102
+ # Define a method `method_name` that presafely executes the given block before
103
+ # the original method.
104
+ #
105
+ # @param method_name [Symbol, String] the name of the method to define
106
+ # @yield the block to execute before the original method
107
+ # @yieldparam args [Array] the positional arguments passed to the original method
108
+ # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
109
+ #
110
+ # @return [void]
111
+ def presafe_sink_before(method_name, &block)
112
+ define_method(method_name) do |*args, **kwargs, &blk|
113
+ instance_exec(*args, **kwargs, &block)
114
+ super(*args, **kwargs, &blk)
115
+ end
116
+ end
117
+
118
+ # Define a method `method_name` that safely executes the given block before
119
+ # the original method.
120
+ #
121
+ # @param method_name [Symbol, String] the name of the method to define
122
+ # @yield the block to execute before the original method
123
+ # @yieldparam args [Array] the positional arguments passed to the original method
124
+ # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
125
+ #
126
+ # @return [void]
127
+ #
128
+ # @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
+ def sink_before(method_name, &block)
130
+ presafe_sink_before(method_name) do |*args, **kwargs|
131
+ DSL.safe do
132
+ instance_exec(*args, **kwargs, &block)
133
+ end
134
+ end
135
+ end
136
+
137
+ # Define a method `method_name` that presafely executes the given block after
138
+ # the original method.
139
+ #
140
+ # @param method_name [Symbol, String] the name of the method to define
141
+ # @yield the block to execute after the original method
142
+ # @yieldparam result [Object] the result returned by the original method
143
+ # @yieldparam args [Array] the positional arguments passed to the original method
144
+ # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
145
+ #
146
+ # @return [void]
147
+ def presafe_sink_after(method_name, &block)
148
+ define_method(method_name) do |*args, **kwargs, &blk|
149
+ result = super(*args, **kwargs, &blk)
150
+ instance_exec(result, *args, **kwargs, &block)
151
+ result
152
+ end
153
+ end
154
+
155
+ # Define a method `method_name` that safely executes the given block after
156
+ # the original method.
157
+ #
158
+ # @param method_name [Symbol, String] the name of the method to define
159
+ # @yield the block to execute after the original method
160
+ # @yieldparam result [Object] the result returned by the original method
161
+ # @yieldparam args [Array] the positional arguments passed to the original method
162
+ # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
163
+ #
164
+ # @return [void]
165
+ #
166
+ # @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
+ def sink_after(method_name, &block)
168
+ presafe_sink_after(method_name) do |result, *args, **kwargs|
169
+ DSL.safe do
170
+ instance_exec(result, *args, **kwargs, &block)
171
+ end
172
+ end
173
+ end
174
+
175
+ # Define a method `method_name` that presafely executes the given block around
176
+ # the original method.
177
+ #
178
+ # @param method_name [Symbol, String] the name of the method to define
179
+ # @yield the block to execute around the original method
180
+ # @yieldparam super_call [Proc] the proc that calls the original method
181
+ # @yieldparam args [Array] the positional arguments passed to the original method
182
+ # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
183
+ #
184
+ # @return [void]
185
+ def presafe_sink_around(method_name, &block)
186
+ define_method(method_name) do |*args, **kwargs, &blk|
187
+ result = nil
188
+ super_call = proc do
189
+ result = super(*args, **kwargs, &blk)
190
+ end
191
+ instance_exec(super_call, *args, **kwargs, &block)
192
+ result
193
+ end
194
+ end
195
+
196
+ # Define a method `method_name` that safely executes the given block around
197
+ # the original method.
198
+ #
199
+ # @param method_name [Symbol, String] the name of the method to define
200
+ # @yield the block to execute around the original method
201
+ # @yieldparam super_call [Proc] the proc that calls the original method
202
+ # @yieldparam args [Array] the positional arguments passed to the original method
203
+ # @yieldparam kwargs [Hash] the keyword arguments passed to the original method
204
+ #
205
+ # @return [void]
206
+ #
207
+ # @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
209
+ 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
214
+ DSL.presafe do
215
+ presafe_super_call.call
216
+ end
217
+ end
218
+ DSL.safe do
219
+ instance_exec(super_call, *args, **kwargs, &block)
220
+ end
221
+ presafe_super_call.call unless super_called
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Aikido
4
4
  module Zen
5
- VERSION = "0.2.0"
5
+ VERSION = "1.0.1.beta.2"
6
6
 
7
7
  # The version of libzen_internals that we build against.
8
- LIBZEN_VERSION = "0.1.37"
8
+ LIBZEN_VERSION = "0.1.39"
9
9
  end
10
10
  end
@@ -78,5 +78,10 @@ module Aikido::Zen
78
78
  @executor.shutdown
79
79
  @executor.wait_for_termination(30)
80
80
  end
81
+
82
+ def restart
83
+ shutdown
84
+ @executor = Concurrent::SingleThreadExecutor.new
85
+ end
81
86
  end
82
87
  end
data/lib/aikido/zen.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # IMPORTANT: Any files that load sinks or start the Aikido Agent should
4
+ # be required in `Aikido::Zen.protect!`.
5
+
3
6
  require_relative "zen/version"
4
7
  require_relative "zen/errors"
5
8
  require_relative "zen/actor"
@@ -10,6 +13,7 @@ require_relative "zen/worker"
10
13
  require_relative "zen/agent"
11
14
  require_relative "zen/api_client"
12
15
  require_relative "zen/context"
16
+ require_relative "zen/detached_agent"
13
17
  require_relative "zen/middleware/check_allowed_addresses"
14
18
  require_relative "zen/middleware/middleware"
15
19
  require_relative "zen/middleware/request_tracker"
@@ -19,10 +23,24 @@ require_relative "zen/outbound_connection_monitor"
19
23
  require_relative "zen/runtime_settings"
20
24
  require_relative "zen/rate_limiter"
21
25
  require_relative "zen/scanners"
22
- require_relative "zen/rails_engine" if defined?(::Rails)
23
26
 
24
27
  module Aikido
25
28
  module Zen
29
+ # Enable protection. Until this method is called no sinks are loaded
30
+ # and the Aikido Agent does not start.
31
+ #
32
+ # @return [void]
33
+ def self.protect!
34
+ if config.disabled?
35
+ config.logger.warn("Zen has been disabled and will not run.")
36
+ return
37
+ end
38
+
39
+ # IMPORTANT: Any files that load sinks or start the Aikido Agent
40
+ # should be required here only.
41
+ require_relative "zen/rails_engine" if defined?(::Rails)
42
+ end
43
+
26
44
  # @return [Aikido::Zen::Config] the agent configuration.
27
45
  def self.config
28
46
  @config ||= Config.new
@@ -34,6 +52,10 @@ module Aikido
34
52
  @runtime_settings ||= RuntimeSettings.new
35
53
  end
36
54
 
55
+ def self.runtime_settings=(settings)
56
+ @runtime_settings = settings
57
+ end
58
+
37
59
  # Gets information about the current system configuration, which is sent to
38
60
  # the server along with any events.
39
61
  def self.system_info
@@ -43,9 +65,15 @@ module Aikido
43
65
  # Manages runtime metrics extracted from your app, which are uploaded to the
44
66
  # Aikido servers if configured to do so.
45
67
  def self.collector
68
+ check_and_handle_fork
46
69
  @collector ||= Collector.new
47
70
  end
48
71
 
72
+ def self.detached_agent
73
+ check_and_handle_fork
74
+ @detached_agent ||= DetachedAgent::Agent.new
75
+ end
76
+
49
77
  # Gets the current context object that holds all information about the
50
78
  # current request.
51
79
  #
@@ -68,12 +96,10 @@ module Aikido
68
96
  # @param request [Aikido::Zen::Request]
69
97
  # @return [void]
70
98
  def self.track_request(request)
71
- autostart
72
- collector.track_request(request)
99
+ collector.track_request
73
100
  end
74
101
 
75
102
  def self.track_discovered_route(request)
76
- autostart
77
103
  collector.track_route(request)
78
104
  end
79
105
 
@@ -82,7 +108,6 @@ module Aikido
82
108
  # @param connection [Aikido::Zen::OutboundConnection]
83
109
  # @return [void]
84
110
  def self.track_outbound(connection)
85
- autostart
86
111
  collector.track_outbound(connection)
87
112
  end
88
113
 
@@ -94,7 +119,6 @@ module Aikido
94
119
  # @raise [Aikido::Zen::UnderAttackError] if the scan detected an Attack
95
120
  # and blocking_mode is enabled.
96
121
  def self.track_scan(scan)
97
- autostart
98
122
  collector.track_scan(scan)
99
123
  agent.handle_attack(scan.attack) if scan.attack?
100
124
  end
@@ -107,7 +131,6 @@ module Aikido
107
131
  return if config.disabled?
108
132
 
109
133
  if (actor = Aikido::Zen::Actor(user))
110
- autostart
111
134
  collector.track_user(actor)
112
135
  current_context.request.actor = actor if current_context
113
136
  else
@@ -140,7 +163,8 @@ module Aikido
140
163
  # @!visibility private
141
164
  # Stop any background threads.
142
165
  def self.stop!
143
- agent&.stop!
166
+ @agent&.stop!
167
+ @detached_agent_server&.stop!
144
168
  end
145
169
 
146
170
  # @!visibility private
@@ -149,8 +173,34 @@ module Aikido
149
173
  @agent ||= Agent.start
150
174
  end
151
175
 
176
+ def self.detached_agent_server
177
+ @detached_agent_server ||= DetachedAgent::Server.start!
178
+ end
179
+
152
180
  class << self
153
- alias_method :autostart, :agent
181
+ # `agent` and `detached_agent` are started on the first method call.
182
+ # A mutex controls thread execution to prevent multiple attempts.
183
+ LOCK = Mutex.new
184
+
185
+ def start!
186
+ @pid = Process.pid
187
+ LOCK.synchronize do
188
+ agent
189
+ detached_agent_server
190
+ end
191
+ end
192
+
193
+ def check_and_handle_fork
194
+ if has_forked
195
+ @detached_agent&.handle_fork
196
+ end
197
+ end
198
+
199
+ def has_forked
200
+ pid_changed = Process.pid != @pid
201
+ @pid = Process.pid
202
+ pid_changed
203
+ end
154
204
  end
155
205
  end
156
206
  end
@@ -0,0 +1,4 @@
1
+ LICENSE
2
+ *.gemspec
3
+ /lib/*.rb
4
+ *.gem
@@ -0,0 +1,11 @@
1
+ # Security Placeholder
2
+
3
+ This gem has been published by [Aikido Security](https://aikido.dev) to help prevent supply chain attacks and protect the integrity of the Ruby ecosystem.
4
+
5
+ It is **not intended for direct use** and contains no functional code.
6
+
7
+ If you are looking for the actual library, please use [`aikido-zen`](https://rubygems.org/gems/aikido-zen).
8
+
9
+ ---
10
+
11
+ This package exists solely for security purposes. For more information, visit [aikido.dev](https://aikido.dev).
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/clean"
5
+ require "rubygems/package"
6
+ require "fileutils"
7
+
8
+ GEM_NAMES = %w[aikido]
9
+
10
+ # Clean up created files
11
+ CLEAN.include("LICENSE")
12
+ CLEAN.include(*GEM_NAMES.map { |name| "#{name}.gemspec" })
13
+ CLEAN.include(*GEM_NAMES.map { |name| "lib/#{name}.rb" })
14
+ CLOBBER.include(*GEM_NAMES.map { |name| "#{name}-*.gem" })
15
+
16
+ namespace :build do
17
+ GEM_NAMES.each do |gem_name|
18
+ file "LICENSE" => ["../LICENSE"] do
19
+ FileUtils.cp("../LICENSE", "LICENSE")
20
+ puts "Copied LICENSE"
21
+ end
22
+
23
+ entry_point_path = "lib/#{gem_name}.rb"
24
+
25
+ # Generate the entry point file from template if needed
26
+ file entry_point_path => ["lib/placeholder.rb.template"] do
27
+ template = File.read("lib/placeholder.rb.template")
28
+ content = template.gsub("@GEM_NAME", gem_name)
29
+ File.write(entry_point_path, content)
30
+ puts "Generated #{entry_point_path}"
31
+ end
32
+
33
+ gemspec_path = "#{gem_name}.gemspec"
34
+
35
+ # Generate gemspec file from template if needed
36
+ file gemspec_path => ["placeholder.gemspec.template"] do
37
+ template = File.read("placeholder.gemspec.template")
38
+ content = template.gsub("@GEM_NAME", gem_name)
39
+ File.write(gemspec_path, content)
40
+ puts "Generated #{gemspec_path}"
41
+ end
42
+
43
+ desc "Build the #{gem_name} gem"
44
+ task gem_name => [entry_point_path, gemspec_path, "LICENSE"] do
45
+ gemspec = Gem::Specification.load(gemspec_path)
46
+ raise "Failed to load gemspec: #{gemspec_path}" unless gemspec
47
+
48
+ gem_path = Gem::Package.build(gemspec)
49
+ puts "Built #{gem_path}"
50
+ end
51
+ end
52
+
53
+ desc "Build all gems"
54
+ task all: GEM_NAMES.map { |gem_name| "build:#{gem_name}" }
55
+ end
56
+
57
+ namespace :release do
58
+ GEM_NAMES.each do |gem_name|
59
+ gemspec_path = "#{gem_name}.gemspec"
60
+
61
+ desc "Build and publish the #{gem_name} to RubyGems"
62
+ task gem_name => ["build:#{gem_name}"] do
63
+ gemspec = Gem::Specification.load(gemspec_path)
64
+ raise "Failed to load gemspec: #{gemspec_path}" unless gemspec
65
+
66
+ gem_path = "#{gemspec.name}-#{gemspec.version}.gem"
67
+
68
+ puts "Publishing #{gem_path} to RubyGem..."
69
+ sh "gem push #{gem_path}"
70
+ end
71
+ end
72
+
73
+ desc "Build and publish all gems to RubyGems"
74
+ task all: GEM_NAMES.map { |gem_name| "release:#{gem_name}" }
75
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ raise LoadError, "This gem has been published by Aikido Security to help prevent supply chain attacks. It is not intended for direct use. Please use 'aikido-zen' instead."
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "@GEM_NAME"
5
+ spec.version = "0.0.2"
6
+ spec.authors = ["Aikido Security"]
7
+ spec.email = ["dev-admin@aikido.dev"]
8
+ spec.summary = "Security placeholder for 'aikido-zen'."
9
+ spec.description = "This gem has been published by Aikido Security to help prevent supply chain attacks. It is not intended for direct use. Please use 'aikido-zen' instead."
10
+ spec.homepage = "https://aikido.dev/zen"
11
+ spec.license = "AGPL-3.0-or-later"
12
+
13
+ spec.required_ruby_version = ">= 2.3"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/aikidosec/firewall-ruby"
17
+
18
+ spec.files = ["lib/@GEM_NAME.rb", "README.md", "LICENSE"]
19
+ spec.require_paths = ["lib"]
20
+ end
data/tasklib/bench.rake CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  require "socket"
4
4
  require "timeout"
5
+ require_relative "wrk"
5
6
 
6
7
  SERVER_PIDS = {}
8
+ PORT_PROTECTED = 3001
9
+ PORT_UNPROTECTED = 3002
7
10
 
8
11
  def stop_servers
9
12
  SERVER_PIDS.each { |_, pid| Process.kill("TERM", pid) }
@@ -11,6 +14,8 @@ def stop_servers
11
14
  end
12
15
 
13
16
  def boot_server(dir, port:, env: {})
17
+ env["RAILS_MIN_THREADS"] = NUMBER_OF_THREADS
18
+ env["RAILS_MAX_THREADS"] = NUMBER_OF_THREADS
14
19
  env["PORT"] = port.to_s
15
20
  env["SECRET_KEY_BASE"] = rand(36**64).to_s(36)
16
21
 
@@ -49,8 +54,28 @@ end
49
54
  Pathname.glob("sample_apps/*").select(&:directory?).each do |dir|
50
55
  namespace :bench do
51
56
  namespace dir.basename.to_s do
52
- desc "Run benchmarks for the #{dir.basename} sample app"
53
- task run: [:boot_protected_app, :boot_unprotected_app] do
57
+ desc "Run WRK benchmarks for the #{dir.basename} sample app"
58
+ task wrk_run: [:boot_protected_app, :boot_unprotected_app] do
59
+ throughput_decrease_limit_perc = 25
60
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0.0") && Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.1.0")
61
+ # add higher limit for ruby 3.0
62
+ throughput_decrease_limit_perc = 35
63
+ end
64
+
65
+ wait_for_servers
66
+ run_benchmark(
67
+ route_zen: "http://localhost:#{PORT_PROTECTED}/benchmark", # Application with Zen
68
+ route_no_zen: "http://localhost:#{PORT_UNPROTECTED}/benchmark", # Application without Zen
69
+ description: "An empty route (1ms simulated delay)",
70
+ throughput_decrease_limit_perc: throughput_decrease_limit_perc,
71
+ latency_increase_limit_ms: 200
72
+ )
73
+ ensure
74
+ stop_servers
75
+ end
76
+
77
+ desc "Run K6 benchmarks for the #{dir.basename} sample app"
78
+ task k6_run: [:boot_protected_app, :boot_unprotected_app] do
54
79
  wait_for_servers
55
80
  Dir.chdir("benchmarks") { sh "k6 run #{dir.basename}.js" }
56
81
  ensure
@@ -58,14 +83,12 @@ Pathname.glob("sample_apps/*").select(&:directory?).each do |dir|
58
83
  end
59
84
 
60
85
  task :boot_protected_app do
61
- boot_server(dir, port: 3001)
86
+ boot_server(dir, port: PORT_PROTECTED)
62
87
  end
63
88
 
64
89
  task :boot_unprotected_app do
65
- boot_server(dir, port: 3002, env: {"AIKIDO_DISABLED" => "true"})
90
+ boot_server(dir, port: PORT_UNPROTECTED, env: {"AIKIDO_DISABLED" => "true"})
66
91
  end
67
92
  end
68
-
69
- task default: "#{dir.basename}:run"
70
93
  end
71
94
  end