aikido-zen 0.1.0.alpha4 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +19 -0
  3. data/CHANGELOG.md +16 -0
  4. data/README.md +136 -23
  5. data/Rakefile +4 -0
  6. data/benchmarks/README.md +27 -0
  7. data/benchmarks/rails7.1_sql_injection.js +74 -0
  8. data/docs/banner.svg +203 -0
  9. data/docs/config.md +123 -0
  10. data/docs/rails.md +70 -0
  11. data/lib/aikido/zen/actor.rb +1 -1
  12. data/lib/aikido/zen/agent/heartbeats_manager.rb +66 -0
  13. data/lib/aikido/zen/agent.rb +100 -112
  14. data/lib/aikido/zen/collector/hosts.rb +15 -0
  15. data/lib/aikido/zen/collector/routes.rb +64 -0
  16. data/lib/aikido/zen/{stats → collector}/sink_stats.rb +1 -1
  17. data/lib/aikido/zen/collector/stats.rb +111 -0
  18. data/lib/aikido/zen/{stats → collector}/users.rb +6 -2
  19. data/lib/aikido/zen/collector.rb +117 -0
  20. data/lib/aikido/zen/config.rb +17 -11
  21. data/lib/aikido/zen/context.rb +8 -1
  22. data/lib/aikido/zen/errors.rb +3 -1
  23. data/lib/aikido/zen/event.rb +7 -4
  24. data/lib/aikido/zen/internals.rb +4 -0
  25. data/lib/aikido/zen/middleware/set_context.rb +4 -1
  26. data/lib/aikido/zen/rails_engine.rb +27 -18
  27. data/lib/aikido/zen/request/schema/builder.rb +0 -2
  28. data/lib/aikido/zen/request.rb +6 -0
  29. data/lib/aikido/zen/runtime_settings.rb +6 -11
  30. data/lib/aikido/zen/scanners/ssrf_scanner.rb +12 -6
  31. data/lib/aikido/zen/sinks/action_controller.rb +64 -0
  32. data/lib/aikido/zen/sinks/http.rb +1 -1
  33. data/lib/aikido/zen/sinks/pg.rb +13 -12
  34. data/lib/aikido/zen/sinks/typhoeus.rb +1 -1
  35. data/lib/aikido/zen/sinks.rb +1 -0
  36. data/lib/aikido/zen/version.rb +2 -2
  37. data/lib/aikido/zen/worker.rb +82 -0
  38. data/lib/aikido/zen.rb +55 -50
  39. data/tasklib/bench.rake +70 -0
  40. metadata +19 -8
  41. data/CODE_OF_CONDUCT.md +0 -132
  42. data/lib/aikido/zen/stats/routes.rb +0 -53
  43. data/lib/aikido/zen/stats.rb +0 -171
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: be7c4a6ff2bcd0499a293948d68e64d114c8172090c7bf2ad1c5b3529055fa0b
4
- data.tar.gz: 5e9de98ce8bc0e38af347f375ef6f6c4c1a02fc035e7a7e4ba1ce1e9220b9b02
3
+ metadata.gz: 51eaea8541cd36bd28a72265eb65c03d603ca04200e4445c1ce9ef5f1eda161e
4
+ data.tar.gz: dbb1085f145533d7c432950e0960b35eae89f9b5c389d17971273fd16ae02da6
5
5
  SHA512:
6
- metadata.gz: 55b00a55772cca9173e55e43c0f94f1c76ebb9055e791cd2d688cd8ab2fc2bb759a5d6c9b5afe90d407cfa67cde37bab3fec651e56c7eec704e24b0bbfd2b35b
7
- data.tar.gz: 6c45daec9a7b1f7c5e463e359b23dc6205d8c02b735c79a34dad24264c260802201b7f60659063629af98d23e1e3bd1e6c8bd313446f5d4c26fee2da66040b39
6
+ metadata.gz: 298cd2aee853100f077036d8982c04cbcaafafda4554e279f855586bf379d68227005c7eb1869a7c64941e0fe2c06b7d0fb9cdf0f22232d1c9c4ad6b98b5d383
7
+ data.tar.gz: 4f8fa891bc625d17ed21e0c78ecee44af8fb6784202243982748fa6df86dbdb333f3242cc7890e28bef7cb9ce700b6567600e6ba4a5448af002915852db0c7e8
data/.simplecov ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Due to dependency resolution, on Ruby 2.x we're stuck with a _very_ old
4
+ # SimpleCov version, and it doesn't really give us any benefit to run coverage
5
+ # in separate ruby versions since we don't branch on ruby version in the code.
6
+ return if RUBY_VERSION < "3.0"
7
+
8
+ SimpleCov.start do
9
+ # Make sure SimpleCov waits until after the tests
10
+ # are finished to generate the coverage reports.
11
+ self.external_at_exit = true
12
+
13
+ enable_coverage :branch
14
+ minimum_coverage line: 95, branch: 85
15
+
16
+ add_filter "/test/"
17
+ end
18
+
19
+ # vim: ft=ruby
data/CHANGELOG.md CHANGED
@@ -2,4 +2,20 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## 0.1.1
6
+
7
+ ### Fixed
8
+
9
+ - Avoid an error when sending the initial heartbeat if the Aikido server hasn't
10
+ received stats yet.
11
+ - Fix the SSRF scanner to ensure the port in the user-supplied payload matches
12
+ the port in the request.
13
+ - Don't break the HTTP.rb sink when a Zen context isn't set.
14
+ - Don't break the Typhoeus sink when a Zen context isn't set.
15
+ - Don't break the PG sink outside of Rails.
16
+ - Updated [libzen](https://github.com/AikidoSec/zen-internals) to v0.1.31 to
17
+ prevent flagging false positives in SQL queries with comments.
18
+
19
+ ## 0.1.0
20
+
5
21
  - Initial version
data/README.md CHANGED
@@ -1,40 +1,153 @@
1
- # Aikido::Firewall
1
+ ![Zen by Aikido for Ruby](./docs/banner.svg)
2
2
 
3
- TODO: Write me :)
3
+ # Zen, in-app firewall for Ruby | by Aikido
4
+
5
+ Zen, your in-app firewall for peace of mind—at runtime.
6
+
7
+ Zen by Aikido is an embedded Web Application Firewall that autonomously protects
8
+ Ruby on Rails apps against common and critical attacks.
9
+
10
+ It protects your Rails apps by preventing user input containing dangerous
11
+ strings, preventing SQL injection and SSRF attacks. It runs embedded on your
12
+ Rails application, for simple installation and zero maintenance.
13
+
14
+ * 🛡️ [SQL injection attacks](https://www.aikido.dev/blog/the-state-of-sql-injections)
15
+ * 🛡️ [Server-side request forgery (SSRF)](https://github.com/AikidoSec/firewall-node/blob/main/docs/ssrf.md)
16
+ * 🛡️ [Command injection attacks](https://owasp.org/www-community/attacks/Command_Injection) (coming soon)
17
+ * 🛡️ [Path traversal attacks](https://owasp.org/www-community/attacks/Path_Traversal)
18
+ * 🛡️ [NoSQL injection attacks](https://www.aikido.dev/blog/web-application-security-vulnerabilities) (coming soon)
19
+
20
+ Zen operates autonomously on the same server as your Rails app to:
21
+
22
+ * ✅ Secure your app like a classic web application firewall (WAF), but with none of the infrastructure or cost.
23
+ * ✅ Rate limit specific API endpoints by IP or by user.
24
+ * ✅ Allow you to block specific users manually.
25
+
26
+ ## Supported libraries and frameworks
27
+
28
+ Zen for Ruby 2.7+ is compatible with:
29
+
30
+ ### Database drivers
31
+
32
+ * ✅ [sqlite3](https://github.com/sparklemotion/sqlite3-ruby) 1.x, 2.x
33
+ * ✅ [pg](https://github.com/ged/ruby-pg) 1.x
34
+ * ✅ [trilogy](https://github.com/trilogy-libraries/trilogy) 2.x
35
+ * ✅ [mysql2](https://github.com/brianmario/mysql2) 0.x
36
+
37
+ ### ORMs and Query Builders
38
+
39
+ See list above for supported database drivers.
40
+
41
+ * ✅ [ActiveRecord](https://github.com/rails/rails)
42
+ * ✅ [Sequel](https://github.com/jeremyevans/sequel)
43
+
44
+ ### HTTP Clients
45
+
46
+ * ✅ [net-http](https://github.com/ruby/net-http)
47
+ * ✅ [http.rb](https://github.com/httprb/http) 1.x, 2.x, 3.x, 4.x, 5.x
48
+ * ✅ [httpx](https://gitlab.com/os85/httpx) 1.x (1.1.3+)
49
+ * ✅ [HttpClient](https://github.com/nahi/httpclient) 2.x, 3.x
50
+ * ✅ [excon](https://github.com/excon/excon) 0.x (0.50.0+), 1.x
51
+ * ✅ [patron](https://github.com/toland/patron) 0.x (0.6.4+)
52
+ * ✅ [typhoeus](https://github.com/typhoeus/typhoeus) 0.x (0.5.0+), 1.x
53
+ * ✅ [curb](https://github.com/taf2/curb) 0.x (0.2.3+), 1.x
54
+ * ✅ [em-http-request](https://github.com/igrigorik/em-http-request) 1.x
55
+ * ✅ [async-http](https://github.com/igrigorik/em-http-request) 0.x (0.70.0+)
4
56
 
5
57
  ## Installation
6
58
 
7
- Install the gem and add to the application's Gemfile by executing:
59
+ We recommend testing Zen locally or on staging before deploying to production.
60
+
61
+ ```
62
+ bundle add aikido-zen
63
+ ```
64
+
65
+ or, if not using bundler:
66
+
67
+ ```
68
+ gem install aikido-zen
69
+ ```
8
70
 
9
- $ bundle add aikido-firewall
71
+ For framework specific instructions, check out our docs:
10
72
 
11
- If bundler is not being used to manage dependencies, install the gem by executing:
73
+ * [Ruby on Rails apps](docs/rails.md)
12
74
 
13
- $ gem install aikido-firewall
75
+ ## Running in production (blocking) mode
14
76
 
15
- ## Development
77
+ By default, Zen will only detect and report attacks to Aikido.
16
78
 
17
- After checking out the repo, run `bin/setup` to install dependencies. Then, run
18
- `rake test` to run the tests. You can also run `bin/console` for an interactive
19
- prompt that will allow you to experiment.
79
+ To block requests, set the `AIKIDO_BLOCK` environment variable to `true`.
20
80
 
21
- To install this gem onto your local machine, run `bundle exec rake install`. To
22
- release a new version, update the version number in `version.rb`, and then run
23
- `bundle exec rake release`, which will create a git tag for the version, push
24
- git commits and the created tag, and push the `.gem` file to
25
- [rubygems.org](https://rubygems.org).
81
+ See [Reporting to Aikido](#reporting-to-your-aikido-security-dashboard) to learn
82
+ how to send events to Aikido.
83
+
84
+ ## Reporting to your Aikido Security dashboard
85
+
86
+ > Aikido is your no nonsense application security platform. One central system
87
+ > that scans your source code & cloud, shows you what vulnerabilities matter,
88
+ > and how to fix them - fast. So you can get back to building.
89
+
90
+ Zen is a new product by Aikido. Built for developers to level up their security.
91
+ While Aikido scans, get Zen for always-on protection.
92
+
93
+ You can use some of Zen’s features without Aikido, of course. Peace of mind is
94
+ just a few lines of code away.
95
+
96
+ But you will get the most value by reporting your data to Aikido.
97
+
98
+ You will need an Aikido account and a token to report events to Aikido. If you
99
+ don't have an account, you can sign up for free.
100
+
101
+ Here's how:
102
+
103
+ * Log in to your Aikido account.
104
+ * Go to "Zen" on the sidebar.
105
+ * Click on "Add App".
106
+ * Choose a name for your App.
107
+ * Click "Continue to Install"
108
+ * Click "Generate Token".
109
+ * Copy the token.
110
+ * Set the token as an environment variable, `AIKIDO_TOKEN`, using
111
+ [dotenv](https://github.com/bkeepers/dotenv) or another method
112
+ of your choosing.
113
+
114
+ ## Performance
115
+
116
+ We run a benchmark on every commit to ensure Zen has a minimal impact on your
117
+ application's performance.
118
+
119
+ For example, here's a benchmark that runs a single GET request to a Rails
120
+ endpoint that performs a single SQL SELECT query:
121
+
122
+ | Without Zen | With Zen | Difference |
123
+ |------------------|---------------|---------------|
124
+ | 3.527ms | 3.583ms | +0.056ms |
125
+
126
+ Using Ruby 3.3, Rails 7.1, SQLite 1.7, running on a MacBook Pro M1 Pro. Results
127
+ will vary based on hardware.
128
+
129
+ See [benchmarks](benchmarks) for more information.
130
+
131
+ ## Bug bounty program
132
+
133
+ Our bug bounty program is public and can be found by all registered Intigriti
134
+ users at: https://app.intigriti.com/researcher/programs/aikido/aikidoruntime
26
135
 
27
136
  ## Contributing
28
137
 
29
- Bug reports and pull requests are welcome [on GitHub][repo]. This project is
30
- intended to be a safe, welcoming space for collaboration, and contributors are
31
- expected to adhere to the [code of conduct][coc].
138
+ See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for more information.
32
139
 
33
140
  ## Code of Conduct
34
141
 
35
- Everyone interacting in the `Aikido::Firewall` project's codebases, issue
36
- trackers, chat rooms and mailing lists is expected to follow the [code of
37
- conduct][coc].
142
+ See [CODE_OF_CONDUCT.md](.github/CODE_OF_CONDUCT.md) for more information.
143
+
144
+ ## License
145
+
146
+ This program is offered under a commercial and under the AGPL license. You can
147
+ be released from the requirements of the AGPL license by purchasing a commercial
148
+ license. Buying such a license is mandatory as soon as you develop commercial
149
+ activities involving the Zen software without disclosing the source code of your
150
+ own applications.
38
151
 
39
- [repo]: https://github.com/aikidosec/firewall-ruby
40
- [coc]: https://github.com/aikidosec/firewall-ruby/blob/main/CODE_OF_CONDUCT.md
152
+ For more information, please contact Aikido Security at this address:
153
+ support@aikido.dev or create an account at https://app.aikido.dev.
data/Rakefile CHANGED
@@ -6,6 +6,10 @@ require "standard/rake"
6
6
  require "rake/clean"
7
7
 
8
8
  load "tasklib/libzen.rake"
9
+ load "tasklib/bench.rake"
10
+
11
+ desc "Run all benchmarks"
12
+ task bench: "bench:default"
9
13
 
10
14
  namespace :build do
11
15
  desc "Ensure Gemfile.lock is up-to-date"
@@ -0,0 +1,27 @@
1
+ # Benchmarking Zen for Ruby
2
+
3
+ This directory contains the benchmarking scripts that we use to ensure adding
4
+ Zen to your application does not impact performance significantly.
5
+
6
+ We use [Grafana K6](https://k6.io) for these. For each sample application we
7
+ include in this repo under [sample_apps](../sample_apps), you should find
8
+ a script here that runs certain benchmarks against that app.
9
+
10
+ To run all the benchmarks, run the following from the root of the project:
11
+
12
+ ```
13
+ $ bundle exec rake bench
14
+ ```
15
+
16
+ In order to run a benchmarks against a single application, run the following
17
+ from the root of the project:
18
+
19
+ ```
20
+ $ bundle exec rake bench:{app}:run
21
+ ```
22
+
23
+ For example, for the `rails7.1_sql_injection` application:
24
+
25
+ ```
26
+ $ bundle exec rake bench:rails7.1_sql_injection:run
27
+ ```
@@ -0,0 +1,74 @@
1
+ import http from 'k6/http';
2
+ import { textSummary } from 'https://jslib.k6.io/k6-summary/0.0.2/index.js';
3
+ import { check, sleep, fail } from 'k6';
4
+ import exec from 'k6/execution';
5
+ import { Trend } from 'k6/metrics';
6
+
7
+ const HTTP = {
8
+ withZen: {
9
+ get: (path, ...args) => http.get("http://localhost:3001" + path, ...args),
10
+ post: (path, ...args) => http.post("http://localhost:3001" + path, ...args)
11
+ },
12
+ withoutZen: {
13
+ get: (path, ...args) => http.get("http://localhost:3002" + path, ...args),
14
+ post: (path, ...args) => http.post("http://localhost:3002" + path, ...args)
15
+ }
16
+ }
17
+
18
+ function test(name, fn) {
19
+ const duration = tests[name].duration;
20
+ const overhead = tests[name].overhead;
21
+
22
+ const withZen = fn(HTTP.withZen);
23
+ const withoutZen = fn(HTTP.withoutZen);
24
+
25
+ const timeWithZen = withZen.timings.duration,
26
+ timeWithoutZen = withoutZen.timings.duration;
27
+
28
+ duration.add(timeWithZen - timeWithoutZen);
29
+
30
+ const ratio = withZen.timings.duration / withoutZen.timings.duration;
31
+ overhead.add(100 * (timeWithZen - timeWithoutZen) / timeWithoutZen)
32
+ }
33
+
34
+ const defaultHeaders = {
35
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
36
+ };
37
+
38
+ const tests = {
39
+ test_post_page_with_json_body: {
40
+ duration: new Trend("test_post_page_with_json_body"),
41
+ overhead: new Trend("test_overhead_with_json_body")
42
+ },
43
+ test_get_page_without_attack: {
44
+ duration: new Trend("test_get_page_without_attack"),
45
+ overhead: new Trend("test_overhead_without_attack")
46
+ },
47
+ test_get_page_with_sql_injection: {
48
+ duration: new Trend("test_get_page_with_sql_injection"),
49
+ overhead: new Trend("test_overhead_with_sql_injection"),
50
+ }
51
+ }
52
+ export const options = {
53
+ vus: 1, // Number of virtual users
54
+ iterations: 200,
55
+ thresholds: {
56
+ test_post_page_with_json_body: ["med<10"],
57
+ test_get_page_without_attack: ["med<10"],
58
+ test_get_page_with_sql_injection: ["med<10"],
59
+ }
60
+ };
61
+
62
+ const expectAttack = http.expectedStatuses(500);
63
+
64
+ export default function () {
65
+ test("test_post_page_with_json_body",
66
+ (http) => http.post("/cats", JSON.stringify({cat: {name: "Féline Dion"}}), {
67
+ headers: {"Content-Type": "application/json"}
68
+ })
69
+ )
70
+ test("test_get_page_without_attack", (http) => http.get("/cats"))
71
+ test("test_get_page_with_sql_injection", (http) =>
72
+ http.get("/cats/1'%20OR%20''='", {responseCallback: expectAttack})
73
+ )
74
+ }