aikido-zen 1.0.0.pre.beta.1-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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.aikido +6 -0
  3. data/README.md +67 -83
  4. data/lib/aikido/zen/config.rb +11 -2
  5. data/lib/aikido/zen/context.rb +4 -0
  6. data/lib/aikido/zen/internals.rb +41 -7
  7. data/lib/aikido/zen/libzen-v0.1.39-x86_64-mingw-64.dll +0 -0
  8. data/lib/aikido/zen/middleware/request_tracker.rb +6 -4
  9. data/lib/aikido/zen/rails_engine.rb +5 -9
  10. data/lib/aikido/zen/request/heuristic_router.rb +6 -0
  11. data/lib/aikido/zen/sink.rb +5 -0
  12. data/lib/aikido/zen/sinks/async_http.rb +35 -16
  13. data/lib/aikido/zen/sinks/curb.rb +52 -26
  14. data/lib/aikido/zen/sinks/em_http.rb +39 -25
  15. data/lib/aikido/zen/sinks/excon.rb +63 -45
  16. data/lib/aikido/zen/sinks/file.rb +67 -71
  17. data/lib/aikido/zen/sinks/http.rb +38 -19
  18. data/lib/aikido/zen/sinks/httpclient.rb +51 -22
  19. data/lib/aikido/zen/sinks/httpx.rb +37 -18
  20. data/lib/aikido/zen/sinks/kernel.rb +18 -57
  21. data/lib/aikido/zen/sinks/mysql2.rb +19 -7
  22. data/lib/aikido/zen/sinks/net_http.rb +37 -19
  23. data/lib/aikido/zen/sinks/patron.rb +41 -24
  24. data/lib/aikido/zen/sinks/pg.rb +50 -27
  25. data/lib/aikido/zen/sinks/resolv.rb +37 -16
  26. data/lib/aikido/zen/sinks/socket.rb +33 -17
  27. data/lib/aikido/zen/sinks/sqlite3.rb +31 -12
  28. data/lib/aikido/zen/sinks/trilogy.rb +19 -7
  29. data/lib/aikido/zen/sinks.rb +29 -20
  30. data/lib/aikido/zen/sinks_dsl.rb +226 -0
  31. data/lib/aikido/zen/version.rb +2 -2
  32. data/lib/aikido/zen.rb +18 -1
  33. data/placeholder/.gitignore +4 -0
  34. data/placeholder/README.md +11 -0
  35. data/placeholder/Rakefile +75 -0
  36. data/placeholder/lib/placeholder.rb.template +3 -0
  37. data/placeholder/placeholder.gemspec.template +20 -0
  38. data/tasklib/libzen.rake +70 -66
  39. metadata +17 -13
  40. data/CHANGELOG.md +0 -25
  41. data/lib/aikido/zen/libzen-v0.1.37.x86_64.dll +0 -0
  42. data/lib/aikido.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 612c8be80af943e42d497eef7621c500acce361d8a269f35042df322b97f44ca
4
- data.tar.gz: 50ead136c69a3c8a90896bf430b7632b3771ea0a2c2891cdb57abda83fb3220f
3
+ metadata.gz: 6870d1d0a0a5f98ddaa519ea4d4629d5f188651767d14e7d73e7f8f5f768dce5
4
+ data.tar.gz: 8fe0c47688f64f8a735b192159339ff19499f4da99451045ad0e3fad95bccdde
5
5
  SHA512:
6
- metadata.gz: 97315d03785d9dc8b4dda1d31ff7ccb299812d5da789b2a5473883c975b07fac9c0c1d5336030f27bb9e21f162c2726d51dd39ffb00071e9d550f111aa098092
7
- data.tar.gz: a83eb6028d858b19dbb3514d9a7565b804fcfa9d812d8670dce2a0abf41c750b7b9328208b44f60192018652d0dc5c8dec144d2caea6d920ec45362e1fb09ea4
6
+ metadata.gz: 516d0a01b52bc249bb5ed730322442c6885e76beb561b49e2dfac7dae399952ba55d7a9485c2f14ff17cdeea2e71ffdd4df94d25d106039aa4114f4327287d45
7
+ data.tar.gz: 3eb82a1a977b37c4cfba905a22ab071fd1f3ca68529c282b7ddb70659f6b52acefeba80d25d95d72db48bff6ce0dc43dc45e03cb04d691ec09781d4c237da64b
data/.aikido ADDED
@@ -0,0 +1,6 @@
1
+ exclude:
2
+ paths:
3
+ - benchmarks/
4
+ - docs/
5
+ - sample_apps/
6
+ - tasklib/wrk.rb
data/README.md CHANGED
@@ -1,146 +1,137 @@
1
1
  ![Zen by Aikido for Ruby](./docs/banner.svg)
2
2
 
3
+ # Zen, in-app firewall for Ruby | by Aikido
4
+
3
5
  [![Gem Version](https://badge.fury.io/rb/aikido-zen.svg?icon=si%3Arubygems&style=flat)](https://badge.fury.io/rb/aikido-zen)
4
6
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
5
7
  [![Unit tests](https://github.com/AikidoSec/firewall-ruby/actions/workflows/main.yml/badge.svg)](https://github.com/AikidoSec/firewall-ruby/actions/workflows/main.yml)
6
8
  [![Release](https://github.com/AikidoSec/firewall-ruby/actions/workflows/release.yml/badge.svg)](https://github.com/AikidoSec/firewall-ruby/actions/workflows/release.yml)
7
9
 
8
- # Zen, in-app firewall for Ruby | by Aikido
10
+ Zen, your in-app firewall for peace of mind - at runtime.
11
+
12
+ Zen by Aikido is an embedded Web Application Firewall that autonomously protects Ruby apps against common and critical attacks.
9
13
 
10
- Zen, your in-app firewall for peace of mind—at runtime.
14
+ It protects your Ruby apps by preventing user input containing dangerous strings, which allow injection, pollution, and path traversal attacks. It runs on the same server as your Ruby app for simple [installation](#installation) and zero maintenance.
11
15
 
12
- Zen by Aikido is an embedded Web Application Firewall that autonomously protects
13
- Ruby on Rails apps against common and critical attacks.
16
+ ## Features
14
17
 
15
- It protects your Rails apps by preventing user input containing dangerous
16
- strings, preventing SQL injection and SSRF attacks. It runs embedded on your
17
- Rails application, for simple installation and zero maintenance.
18
+ Zen will autonomously protect your Ruby applications against:
18
19
 
19
20
  * 🛡️ [SQL injection attacks](https://www.aikido.dev/blog/the-state-of-sql-injections)
20
21
  * 🛡️ [Server-side request forgery (SSRF)](https://github.com/AikidoSec/firewall-node/blob/main/docs/ssrf.md)
21
- * 🛡️ [Command injection attacks](https://www.aikido.dev/blog/command-injection-in-2024-unpacked) (coming soon)
22
- * 🛡️ [Path traversal attacks](https://www.aikido.dev/blog/path-traversal-in-2024-the-year-unpacked) (coming soon)
22
+ * 🛡️ [Command injection attacks](https://www.aikido.dev/blog/command-injection-in-2024-unpacked)
23
+ * 🛡️ [Path traversal attacks](https://www.aikido.dev/blog/path-traversal-in-2024-the-year-unpacked)
23
24
  * 🛡️ [NoSQL injection attacks](https://www.aikido.dev/blog/web-application-security-vulnerabilities) (coming soon)
24
25
 
25
- Zen operates autonomously on the same server as your Rails app to:
26
+ Zen operates autonomously on the same server as your Ruby app to:
26
27
 
27
- * ✅ Secure your app like a classic web application firewall (WAF), but with none of the infrastructure or cost.
28
- * ✅ Rate limit specific API endpoints by IP or by user.
29
- * ✅ Allow you to block specific users manually.
28
+ * ✅ Secure your app like a classic web application firewall (WAF), but with none of the infrastructure or cost
29
+ * ✅ Rate limit specific API endpoints by IP or by user
30
+ * ✅ Allow you to block specific users manually
30
31
 
31
32
  ## Supported libraries and frameworks
32
33
 
33
34
  Zen for Ruby 2.7+ is compatible with:
34
35
 
36
+ ### Web frameworks
37
+
38
+ * ✅ [Ruby on Rails](docs/rails.md) 7.x, 8.x
39
+
35
40
  ### Database drivers
36
41
 
37
- * ✅ [sqlite3](https://github.com/sparklemotion/sqlite3-ruby) 1.x, 2.x
38
- * ✅ [pg](https://github.com/ged/ruby-pg) 1.x
39
- * ✅ [trilogy](https://github.com/trilogy-libraries/trilogy) 2.x
40
- * ✅ [mysql2](https://github.com/brianmario/mysql2) 0.x
42
+ * ✅ [`sqlite3`](https://github.com/sparklemotion/sqlite3-ruby) 1.x, 2.x
43
+ * ✅ [`pg`](https://github.com/ged/ruby-pg) 1.x
44
+ * ✅ [`mysql2`](https://github.com/brianmario/mysql2) 0.x
45
+ * ✅ [`trilogy`](https://github.com/trilogy-libraries/trilogy) 2.x
41
46
 
42
- ### ORMs and Query Builders
47
+ ### ORMs and query builders
43
48
 
44
49
  See list above for supported database drivers.
45
50
 
46
51
  * ✅ [ActiveRecord](https://github.com/rails/rails)
47
52
  * ✅ [Sequel](https://github.com/jeremyevans/sequel)
48
53
 
49
- ### HTTP Clients
54
+ ### HTTP clients
50
55
 
51
- * ✅ [net-http](https://github.com/ruby/net-http)
52
- * ✅ [http.rb](https://github.com/httprb/http) 1.x, 2.x, 3.x, 4.x, 5.x
53
- * ✅ [httpx](https://gitlab.com/os85/httpx) 1.x (1.1.3+)
54
- * ✅ [HttpClient](https://github.com/nahi/httpclient) 2.x, 3.x
55
- * ✅ [excon](https://github.com/excon/excon) 0.x (0.50.0+), 1.x
56
- * ✅ [patron](https://github.com/toland/patron) 0.x (0.6.4+)
57
- * ✅ [typhoeus](https://github.com/typhoeus/typhoeus) 0.x (0.5.0+), 1.x
58
- * ✅ [curb](https://github.com/taf2/curb) 0.x (0.2.3+), 1.x
59
- * ✅ [em-http-request](https://github.com/igrigorik/em-http-request) 1.x
60
- * ✅ [async-http](https://github.com/igrigorik/em-http-request) 0.x (0.70.0+)
56
+ * ✅ [`net-http`](https://github.com/ruby/net-http)
57
+ * ✅ [`http.rb`](https://github.com/httprb/http) 1.x, 2.x, 3.x, 4.x, 5.x
58
+ * ✅ [`httpx`](https://gitlab.com/os85/httpx) 1.x (1.1.3+)
59
+ * ✅ [`httpclient`](https://github.com/nahi/httpclient) 2.x, 3.x
60
+ * ✅ [`excon`](https://github.com/excon/excon) 0.x (0.50.0+), 1.x
61
+ * ✅ [`curb`](https://github.com/taf2/curb) 0.x (0.2.3+), 1.x
62
+ * ✅ [`patron`](https://github.com/toland/patron) 0.x (0.6.4+)
63
+ * ✅ [`typhoeus`](https://github.com/typhoeus/typhoeus) 0.x (0.5.0+), 1.x
64
+ * ✅ [`async-http`](https://github.com/igrigorik/em-http-request) 0.x (0.70.0+)
65
+ * ✅ [`em-http-request`](https://github.com/igrigorik/em-http-request) 1.x
61
66
 
62
67
  ## Installation
63
68
 
64
69
  We recommend testing Zen locally or on staging before deploying to production.
65
70
 
66
- ```
71
+ ```sh
67
72
  bundle add aikido-zen
68
73
  ```
69
74
 
70
75
  or, if not using bundler:
71
76
 
72
- ```
77
+ ```sh
73
78
  gem install aikido-zen
74
79
  ```
75
80
 
76
81
  For framework specific instructions, check out our docs:
77
82
 
78
- * [Ruby on Rails apps](docs/rails.md)
83
+ * [Ruby on Rails](docs/rails.md)
79
84
 
80
- ## Running in production (blocking) mode
85
+ ## Reporting to your Aikido Security dashboard
81
86
 
82
- By default, Zen will only detect and report attacks to Aikido.
87
+ > Aikido is your no nonsense application security platform. One central system that scans your source code & cloud, shows you what vulnerabilities matter, and how to fix them - fast. So you can get back to building.
83
88
 
84
- To block requests, set the `AIKIDO_BLOCK` environment variable to `true`.
89
+ Zen is a new product by Aikido. Built for developers to level up their security. While Aikido scans, get Zen for always-on protection.
85
90
 
86
- See [Reporting to Aikido](#reporting-to-your-aikido-security-dashboard) to learn
87
- how to send events to Aikido.
91
+ You can use some of Zen's features without Aikido, of course. Peace of mind is just a few lines of code away.
88
92
 
89
- ## Additional configuration
93
+ But you will get the most value by reporting your data to Aikido.
90
94
 
91
- [Configure Zen using environment variables for authentication, mode settings, debugging, and more.](https://help.aikido.dev/doc/configuration-via-env-vars/docrSItUkeR9)
95
+ You will need an Aikido account and a token to report events to Aikido. If you don't have an account, you can [sign up for free](https://app.aikido.dev/login).
92
96
 
93
- ## Reporting to your Aikido Security dashboard
97
+ Here's how:
94
98
 
95
- > Aikido is your no nonsense application security platform. One central system
96
- > that scans your source code & cloud, shows you what vulnerabilities matter,
97
- > and how to fix them - fast. So you can get back to building.
99
+ * [Log in to your Aikido account](https://app.aikido.dev/login).
100
+ * Go to [Zen](https://app.aikido.dev/runtime/services).
101
+ * Go to apps.
102
+ * Click on **Add app**.
103
+ * Choose a name for your app.
104
+ * Click **Generate token**.
105
+ * Copy the token.
106
+ * Set the token as an environment variable, `AIKIDO_TOKEN`, using [dotenv](https://github.com/bkeepers/dotenv) or another method of your choosing.
98
107
 
99
- Zen is a new product by Aikido. Built for developers to level up their security.
100
- While Aikido scans, get Zen for always-on protection.
108
+ ## Running in production (blocking) mode
101
109
 
102
- You can use some of Zen’s features without Aikido, of course. Peace of mind is
103
- just a few lines of code away.
110
+ By default, Zen will only detect and report attacks to Aikido.
104
111
 
105
- But you will get the most value by reporting your data to Aikido.
112
+ To block requests, set the `AIKIDO_BLOCK` environment variable to `true`.
106
113
 
107
- You will need an Aikido account and a token to report events to Aikido. If you
108
- don't have an account, you can sign up for free.
114
+ See [Reporting to Aikido](#reporting-to-your-aikido-security-dashboard) to learn how to send events to Aikido.
109
115
 
110
- Here's how:
116
+ ## Additional configuration
111
117
 
112
- * Log in to your Aikido account.
113
- * Go to "Zen" on the sidebar.
114
- * Click on "Add App".
115
- * Choose a name for your App.
116
- * Click "Continue to Install"
117
- * Click "Generate Token".
118
- * Copy the token.
119
- * Set the token as an environment variable, `AIKIDO_TOKEN`, using
120
- [dotenv](https://github.com/bkeepers/dotenv) or another method
121
- of your choosing.
118
+ [Configure Zen using environment variables for authentication, mode settings, debugging, and more.](https://help.aikido.dev/doc/configuration-via-env-vars/docrSItUkeR9)
122
119
 
123
- ## Performance
120
+ ## License
124
121
 
125
- We run a benchmark on every commit to ensure Zen has a minimal impact on your
126
- application's performance.
122
+ This program is offered under a commercial and under the AGPL license. You can be released from the requirements of the AGPL license by purchasing a commercial license. Buying such a license is mandatory as soon as you develop commercial activities involving the Zen software without disclosing the source code of your own applications.
127
123
 
128
- For example, here's a benchmark that runs a single GET request to a Rails
129
- endpoint that performs a single SQL SELECT query:
124
+ For more information, please contact Aikido Security at this address: support@aikido.dev or create an account at https://app.aikido.dev.
130
125
 
131
- | Without Zen | With Zen | Difference |
132
- |------------------|---------------|---------------|
133
- | 3.527ms | 3.583ms | +0.056ms |
126
+ ## Benchmarks
134
127
 
135
- Using Ruby 3.3, Rails 7.1, SQLite 1.7, running on a MacBook Pro M1 Pro. Results
136
- will vary based on hardware.
128
+ We run a benchmark on every commit to ensure Zen has a minimal impact on your application's performance.
137
129
 
138
- See [benchmarks](benchmarks) for more information.
130
+ See [benchmarks](benchmarks)
139
131
 
140
132
  ## Bug bounty program
141
133
 
142
- Our bug bounty program is public and can be found by all registered Intigriti
143
- users at: https://app.intigriti.com/researcher/programs/aikido/aikidoruntime
134
+ Our bug bounty program is public and can be found by all registered Intigriti users at: https://app.intigriti.com/researcher/programs/aikido/aikidozenbeta
144
135
 
145
136
  ## Contributing
146
137
 
@@ -150,13 +141,6 @@ See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for more information.
150
141
 
151
142
  See [CODE_OF_CONDUCT.md](.github/CODE_OF_CONDUCT.md) for more information.
152
143
 
153
- ## License
154
-
155
- This program is offered under a commercial and under the AGPL license. You can
156
- be released from the requirements of the AGPL license by purchasing a commercial
157
- license. Buying such a license is mandatory as soon as you develop commercial
158
- activities involving the Zen software without disclosing the source code of your
159
- own applications.
144
+ ## Security
160
145
 
161
- For more information, please contact Aikido Security at this address:
162
- support@aikido.dev or create an account at https://app.aikido.dev.
146
+ See [SECURITY.md](.github/SECURITY.md) for more information.
@@ -8,6 +8,12 @@ require_relative "context"
8
8
 
9
9
  module Aikido::Zen
10
10
  class Config
11
+ # @api private
12
+ # @return [Boolean] whether Aikido should protect.
13
+ def protect?
14
+ !api_token.nil? || blocking_mode? || debugging?
15
+ end
16
+
11
17
  # @return [Boolean] whether Aikido should be turned completely off (no
12
18
  # intercepting calls to protect the app, no agent process running, no
13
19
  # middleware installed). Defaults to false (so, enabled). Can be set
@@ -57,7 +63,7 @@ module Aikido::Zen
57
63
 
58
64
  # @return [string] Path of the socket where the detached agent will listen.
59
65
  # By default, is stored under the root application path with file name
60
- # `aikido-detached-agent.socket`
66
+ # `aikido-detached-agent.sock`
61
67
  attr_reader :detached_agent_socket_path
62
68
 
63
69
  # @return [Boolean] is the agent in debugging mode?
@@ -158,7 +164,7 @@ module Aikido::Zen
158
164
  self.debugging = read_boolean_from_env(ENV.fetch("AIKIDO_DEBUG", false))
159
165
  self.logger = Logger.new($stdout, progname: "aikido", level: debugging ? Logger::DEBUG : Logger::INFO)
160
166
  self.max_performance_samples = 5000
161
- self.detached_agent_socket_path = "aikido-detached-agent.socket"
167
+ self.detached_agent_socket_path = ENV.fetch("AIKIDO_DETACHED_AGENT_SOCKET_PATH", DEFAULT_DETACHED_AGENT_SOCKET_PATH)
162
168
  self.max_compressed_stats = 100
163
169
  self.max_outbound_connections = 200
164
170
  self.max_users_tracked = 1000
@@ -246,6 +252,9 @@ module Aikido::Zen
246
252
  # @!visibility private
247
253
  DEFAULT_JSON_DECODER = JSON.method(:parse)
248
254
 
255
+ # @!visibility private
256
+ DEFAULT_DETACHED_AGENT_SOCKET_PATH = "aikido-detached-agent.sock"
257
+
249
258
  # @!visibility private
250
259
  DEFAULT_BLOCKED_RESPONDER = ->(request, blocking_type) do
251
260
  message = case blocking_type
@@ -20,6 +20,9 @@ module Aikido::Zen
20
20
  # @return [Aikido::Zen::Request]
21
21
  attr_reader :request
22
22
 
23
+ # @return [Boolean]
24
+ attr_accessor :scanning
25
+
23
26
  # @param request [Rack::Request] a Request object that implements the
24
27
  # Rack::Request API, to which we will delegate behavior.
25
28
  # @param settings [Aikido::Zen::RuntimeSettings]
@@ -32,6 +35,7 @@ module Aikido::Zen
32
35
  @settings = settings
33
36
  @payload_sources = sources
34
37
  @metadata = {}
38
+ @scanning = false
35
39
  end
36
40
 
37
41
  # Fetch some metadata stored in the Context.
@@ -13,14 +13,48 @@ module Aikido::Zen
13
13
  attr_accessor :libzen_name
14
14
  end
15
15
 
16
- self.libzen_name = [
17
- "libzen-v#{LIBZEN_VERSION}",
18
- FFI::Platform::ARCH,
19
- FFI::Platform::LIBSUFFIX
20
- ].join(".")
16
+ def self.libzen_names
17
+ lib_name = "libzen-v#{LIBZEN_VERSION}"
18
+ lib_ext = FFI::Platform::LIBSUFFIX
19
+
20
+ # Gem::Platform#version should be understood as an arbitrary Ruby defined
21
+ # OS specific string. A platform with a version string is considered more
22
+ # specific than a platform without a version string.
23
+ # https://docs.ruby-lang.org/en/3.3/Gem/Platform.html
24
+
25
+ platform = Gem::Platform.local.dup
26
+
27
+ # Library names in preferred order.
28
+ #
29
+ # If two library names are added, the specific platform library names is
30
+ # first and the generic platform library name is second.
31
+ names = []
32
+
33
+ names << "#{lib_name}-#{platform}.#{lib_ext}"
34
+
35
+ unless platform.version.nil?
36
+ platform.version = nil
37
+ names << "#{lib_name}-#{platform}.#{lib_ext}"
38
+ end
39
+
40
+ names
41
+ end
42
+
43
+ # Load the most specific library
44
+ def self.load_libzen
45
+ libzen_names.each do |libzen_name|
46
+ libzen_path = File.expand_path(libzen_name, __dir__)
47
+ begin
48
+ return ffi_lib(libzen_path)
49
+ rescue LoadError
50
+ # empty
51
+ end
52
+ end
53
+ raise LoadError, "Could not load libzen"
54
+ end
21
55
 
22
56
  begin
23
- ffi_lib File.expand_path(libzen_name, __dir__)
57
+ load_libzen
24
58
 
25
59
  # @!method self.detect_sql_injection_native(query, input, dialect)
26
60
  # @param (see .detect_sql_injection)
@@ -30,7 +64,7 @@ module Aikido::Zen
30
64
  # calling libzen.
31
65
  attach_function :detect_sql_injection_native, :detect_sql_injection,
32
66
  [:string, :string, :int], :int
33
- rescue LoadError, FFI::NotFoundError => err
67
+ rescue LoadError, FFI::NotFoundError => err # rubocop:disable Lint/ShadowedException
34
68
  # :nocov:
35
69
 
36
70
  # Emit an $stderr warning at startup.
@@ -13,14 +13,16 @@ module Aikido::Zen
13
13
  request = Aikido::Zen::Middleware.request_from(env)
14
14
  response = @app.call(env)
15
15
 
16
- Aikido::Zen.track_request request
17
-
18
- if Aikido::Zen.config.collect_api_schema? && request.route && track?(
16
+ if request.route && track?(
19
17
  status_code: response[0],
20
18
  route: request.route.path,
21
19
  http_method: request.request_method
22
20
  )
23
- Aikido::Zen.track_discovered_route(request)
21
+ Aikido::Zen.track_request request
22
+
23
+ if Aikido::Zen.config.collect_api_schema?
24
+ Aikido::Zen.track_discovered_route(request)
25
+ end
24
26
  end
25
27
 
26
28
  response
@@ -10,7 +10,7 @@ module Aikido::Zen
10
10
  end
11
11
 
12
12
  initializer "aikido.add_middleware" do |app|
13
- next if config.zen.disabled?
13
+ next unless config.zen.protect?
14
14
 
15
15
  app.middleware.use Aikido::Zen::Middleware::SetContext
16
16
  app.middleware.use Aikido::Zen::Middleware::CheckAllowedAddresses
@@ -33,9 +33,9 @@ module Aikido::Zen
33
33
  initializer "aikido.configuration" do |app|
34
34
  # Allow the logger to be configured before checking if disabled? so we can
35
35
  # let the user know that the agent is disabled.
36
- app.config.zen.logger = ::Rails.logger.tagged("aikido")
37
-
38
- next if config.zen.disabled?
36
+ logger = ::Rails.logger
37
+ logger = ActiveSupport::TaggedLogging.new(logger) unless logger.respond_to?(:tagged)
38
+ app.config.zen.logger = logger.tagged("aikido")
39
39
 
40
40
  app.config.zen.request_builder = Aikido::Zen::Context::RAILS_REQUEST_BUILDER
41
41
 
@@ -51,10 +51,7 @@ module Aikido::Zen
51
51
  end
52
52
 
53
53
  config.after_initialize do
54
- if config.zen.disabled?
55
- config.zen.logger.warn("Zen has been disabled and will not run.")
56
- next
57
- end
54
+ next unless config.zen.protect?
58
55
 
59
56
  # Make sure this is run at the end of the initialization process, so
60
57
  # that any gems required after aikido-zen are detected and patched
@@ -63,7 +60,6 @@ module Aikido::Zen
63
60
 
64
61
  # It's important we start after loading sinks, so we can report the installed packages
65
62
  Aikido::Zen.start!
66
- Aikido::Zen.start!
67
63
 
68
64
  # Agent's bootstrap process has finished —Controllers are patched to block
69
65
  # unwanted requests, sinks are loaded, scanners are running—, so we mark
@@ -30,6 +30,10 @@ module Aikido::Zen
30
30
 
31
31
  private def parameterize_segment(segment)
32
32
  case segment
33
+ when ULID
34
+ ":ulid"
35
+ when OBJECT_ID
36
+ ":objectId"
33
37
  when NUMBER
34
38
  ":number"
35
39
  when UUID
@@ -57,6 +61,8 @@ module Aikido::Zen
57
61
  | 00000000-0000-0000-0000-000000000000
58
62
  | ffffffff-ffff-ffff-ffff-ffffffffffff
59
63
  )\z/ix
64
+ ULID = /\A[0-9A-HJKMNP-TV-Z]{26}\z/i
65
+ OBJECT_ID = /\A[0-9a-f]{24}\z/i
60
66
  EMAIL = /\A
61
67
  [a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+
62
68
  @
@@ -80,6 +80,9 @@ module Aikido::Zen
80
80
  # @raise [Aikido::UnderAttackError] if an attack is detected and
81
81
  # blocking_mode is enabled.
82
82
  def scan(context: Aikido::Zen.current_context, **scan_params)
83
+ return if context&.scanning
84
+ context&.scanning = true
85
+
83
86
  return if context&.protection_disabled?
84
87
 
85
88
  scan = Scan.new(sink: self, context: context)
@@ -108,6 +111,8 @@ module Aikido::Zen
108
111
  @reporter.call(scan) if scans_performed > 0
109
112
 
110
113
  scan
114
+ ensure
115
+ context&.scanning = false
111
116
  end
112
117
  end
113
118
  end
@@ -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!