aikido-zen 0.1.0.alpha4-x86_64-darwin → 0.1.1-x86_64-darwin
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.simplecov +19 -0
- data/CHANGELOG.md +16 -0
- data/README.md +136 -23
- data/Rakefile +4 -0
- data/benchmarks/README.md +27 -0
- data/benchmarks/rails7.1_sql_injection.js +74 -0
- data/docs/banner.svg +203 -0
- data/docs/config.md +123 -0
- data/docs/rails.md +70 -0
- data/lib/aikido/zen/actor.rb +1 -1
- data/lib/aikido/zen/agent/heartbeats_manager.rb +66 -0
- data/lib/aikido/zen/agent.rb +100 -112
- data/lib/aikido/zen/collector/hosts.rb +15 -0
- data/lib/aikido/zen/collector/routes.rb +64 -0
- data/lib/aikido/zen/{stats → collector}/sink_stats.rb +1 -1
- data/lib/aikido/zen/collector/stats.rb +111 -0
- data/lib/aikido/zen/{stats → collector}/users.rb +6 -2
- data/lib/aikido/zen/collector.rb +117 -0
- data/lib/aikido/zen/config.rb +17 -11
- data/lib/aikido/zen/context.rb +8 -1
- data/lib/aikido/zen/errors.rb +3 -1
- data/lib/aikido/zen/event.rb +7 -4
- data/lib/aikido/zen/internals.rb +4 -0
- data/lib/aikido/zen/{libzen-v0.1.26.x86_64.dylib → libzen-v0.1.31.x86_64.dylib} +0 -0
- data/lib/aikido/zen/middleware/set_context.rb +4 -1
- data/lib/aikido/zen/rails_engine.rb +27 -18
- data/lib/aikido/zen/request/schema/builder.rb +0 -2
- data/lib/aikido/zen/request.rb +6 -0
- data/lib/aikido/zen/runtime_settings.rb +6 -11
- data/lib/aikido/zen/scanners/ssrf_scanner.rb +12 -6
- data/lib/aikido/zen/sinks/action_controller.rb +64 -0
- data/lib/aikido/zen/sinks/http.rb +1 -1
- data/lib/aikido/zen/sinks/pg.rb +13 -12
- data/lib/aikido/zen/sinks/typhoeus.rb +1 -1
- data/lib/aikido/zen/sinks.rb +1 -0
- data/lib/aikido/zen/version.rb +2 -2
- data/lib/aikido/zen/worker.rb +82 -0
- data/lib/aikido/zen.rb +55 -50
- data/tasklib/bench.rake +70 -0
- metadata +20 -9
- data/CODE_OF_CONDUCT.md +0 -132
- data/lib/aikido/zen/stats/routes.rb +0 -53
- data/lib/aikido/zen/stats.rb +0 -171
data/lib/aikido/zen.rb
CHANGED
@@ -4,7 +4,9 @@ require_relative "zen/version"
|
|
4
4
|
require_relative "zen/errors"
|
5
5
|
require_relative "zen/actor"
|
6
6
|
require_relative "zen/config"
|
7
|
+
require_relative "zen/collector"
|
7
8
|
require_relative "zen/system_info"
|
9
|
+
require_relative "zen/worker"
|
8
10
|
require_relative "zen/agent"
|
9
11
|
require_relative "zen/api_client"
|
10
12
|
require_relative "zen/context"
|
@@ -24,12 +26,24 @@ module Aikido
|
|
24
26
|
@config ||= Config.new
|
25
27
|
end
|
26
28
|
|
29
|
+
# @return [Aikido::Zen::RuntimeSettings] the firewall configuration sourced
|
30
|
+
# from your Aikido dashboard. This is periodically polled for updates.
|
31
|
+
def self.runtime_settings
|
32
|
+
@runtime_settings ||= RuntimeSettings.new
|
33
|
+
end
|
34
|
+
|
27
35
|
# Gets information about the current system configuration, which is sent to
|
28
36
|
# the server along with any events.
|
29
37
|
def self.system_info
|
30
38
|
@system_info ||= SystemInfo.new
|
31
39
|
end
|
32
40
|
|
41
|
+
# Manages runtime metrics extracted from your app, which are uploaded to the
|
42
|
+
# Aikido servers if configured to do so.
|
43
|
+
def self.collector
|
44
|
+
@collector ||= Collector.new
|
45
|
+
end
|
46
|
+
|
33
47
|
# Gets the current context object that holds all information about the
|
34
48
|
# current request.
|
35
49
|
#
|
@@ -47,24 +61,13 @@ module Aikido
|
|
47
61
|
Thread.current[:_aikido_current_context_] = context
|
48
62
|
end
|
49
63
|
|
50
|
-
# Track statistics about the result of a Sink's scan, and report it as an
|
51
|
-
# Attack if one is detected.
|
52
|
-
#
|
53
|
-
# @param scan [Aikido::Zen::Scan]
|
54
|
-
# @return [void]
|
55
|
-
# @raise [Aikido::Zen::UnderAttackError] if the scan detected an Attack
|
56
|
-
# and blocking_mode is enabled.
|
57
|
-
def self.track_scan(scan)
|
58
|
-
agent.stats.add_scan(scan)
|
59
|
-
agent.handle_attack(scan.attack) if scan.attack?
|
60
|
-
end
|
61
|
-
|
62
64
|
# Track statistics about an HTTP request the app is handling.
|
63
65
|
#
|
64
|
-
# @param
|
66
|
+
# @param request [Aikido::Zen::Request]
|
65
67
|
# @return [void]
|
66
68
|
def self.track_request(request)
|
67
|
-
|
69
|
+
autostart
|
70
|
+
collector.track_request(request)
|
68
71
|
end
|
69
72
|
|
70
73
|
# Tracks a network connection made to an external service.
|
@@ -72,7 +75,21 @@ module Aikido
|
|
72
75
|
# @param connection [Aikido::Zen::OutboundConnection]
|
73
76
|
# @return [void]
|
74
77
|
def self.track_outbound(connection)
|
75
|
-
|
78
|
+
autostart
|
79
|
+
collector.track_outbound(connection)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Track statistics about the result of a Sink's scan, and report it as
|
83
|
+
# an Attack if one is detected.
|
84
|
+
#
|
85
|
+
# @param scan [Aikido::Zen::Scan]
|
86
|
+
# @return [void]
|
87
|
+
# @raise [Aikido::Zen::UnderAttackError] if the scan detected an Attack
|
88
|
+
# and blocking_mode is enabled.
|
89
|
+
def self.track_scan(scan)
|
90
|
+
autostart
|
91
|
+
collector.track_scan(scan)
|
92
|
+
agent.handle_attack(scan.attack) if scan.attack?
|
76
93
|
end
|
77
94
|
|
78
95
|
# Track the user making the current request.
|
@@ -80,43 +97,22 @@ module Aikido
|
|
80
97
|
# @param (see Aikido::Zen.Actor)
|
81
98
|
# @return [void]
|
82
99
|
def self.track_user(user)
|
83
|
-
|
100
|
+
return if config.disabled?
|
84
101
|
|
85
|
-
if actor
|
86
|
-
|
102
|
+
if (actor = Aikido::Zen::Actor(user))
|
103
|
+
autostart
|
104
|
+
collector.track_user(actor)
|
105
|
+
current_context.request.actor = actor if current_context
|
87
106
|
else
|
88
|
-
|
89
|
-
|
90
|
-
Incompatible object sent to Aikido::Zen.track_user: %<obj>p
|
91
|
-
|
92
|
-
The object must satisfy one of the following:
|
107
|
+
config.logger.warn(format(<<~LOG, obj: user))
|
108
|
+
Incompatible object sent to track_user: %<obj>p
|
93
109
|
|
94
|
-
|
95
|
-
|
96
|
-
* Be a Hash with :id (or "id") and, optionally, :name (or "name") keys
|
110
|
+
The object must either implement #to_aikido_actor, or be a Hash with
|
111
|
+
an :id (or "id") and, optionally, a :name (or "name") key.
|
97
112
|
LOG
|
98
113
|
end
|
99
114
|
end
|
100
115
|
|
101
|
-
# Starts the background threads that keep the agent running.
|
102
|
-
#
|
103
|
-
# @return [void]
|
104
|
-
def self.initialize!
|
105
|
-
@agent ||= Agent.new
|
106
|
-
@agent.start!
|
107
|
-
end
|
108
|
-
|
109
|
-
# Stop any background threads.
|
110
|
-
def self.stop!
|
111
|
-
@agent&.stop!
|
112
|
-
end
|
113
|
-
|
114
|
-
# @return [Aikido::Zen::RuntimeSettings] the firewall configuration sourced
|
115
|
-
# from your Aikido dashboard. This is periodically polled for updates.
|
116
|
-
def self.runtime_settings
|
117
|
-
@runtime_settings ||= RuntimeSettings.new
|
118
|
-
end
|
119
|
-
|
120
116
|
# Load all sinks matching libraries loaded into memory. This method should
|
121
117
|
# be called after all other dependencies have been loaded into memory (i.e.
|
122
118
|
# at the end of the initialization process).
|
@@ -128,11 +124,20 @@ module Aikido
|
|
128
124
|
require_relative "zen/sinks"
|
129
125
|
end
|
130
126
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
127
|
+
# @!visibility private
|
128
|
+
# Stop any background threads.
|
129
|
+
def self.stop!
|
130
|
+
agent&.stop!
|
131
|
+
end
|
132
|
+
|
133
|
+
# @!visibility private
|
134
|
+
# Starts the background agent if it has not been started yet.
|
135
|
+
def self.agent
|
136
|
+
@agent ||= Agent.start
|
137
|
+
end
|
138
|
+
|
139
|
+
class << self
|
140
|
+
alias_method :autostart, :agent
|
136
141
|
end
|
137
142
|
end
|
138
143
|
end
|
data/tasklib/bench.rake
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "socket"
|
4
|
+
require "timeout"
|
5
|
+
|
6
|
+
SERVER_PIDS = {}
|
7
|
+
|
8
|
+
def stop_servers
|
9
|
+
SERVER_PIDS.each { |_, pid| Process.kill("TERM", pid) }
|
10
|
+
SERVER_PIDS.clear
|
11
|
+
end
|
12
|
+
|
13
|
+
def boot_server(dir, port:, env: {})
|
14
|
+
env["PORT"] = port.to_s
|
15
|
+
|
16
|
+
Dir.chdir(dir) do
|
17
|
+
SERVER_PIDS[port] = Process.spawn(
|
18
|
+
env,
|
19
|
+
"rails", "server", "--pid", "#{Dir.pwd}/tmp/pids/server.#{port}.pid",
|
20
|
+
out: "/dev/null"
|
21
|
+
)
|
22
|
+
rescue
|
23
|
+
SERVER_PIDS.delete(port)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def port_open?(port, timeout: 1)
|
28
|
+
Timeout.timeout(timeout) do
|
29
|
+
TCPSocket.new("127.0.0.1", port).close
|
30
|
+
true
|
31
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
|
32
|
+
false
|
33
|
+
end
|
34
|
+
rescue Timeout::Error
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def wait_for_servers
|
39
|
+
ports = SERVER_PIDS.keys
|
40
|
+
|
41
|
+
Timeout.timeout(10) do
|
42
|
+
ports.reject! { |port| port_open?(port) } while ports.any?
|
43
|
+
end
|
44
|
+
rescue Timeout::Error
|
45
|
+
raise "Could not reach ports: #{ports.join(", ")}"
|
46
|
+
end
|
47
|
+
|
48
|
+
Pathname.glob("sample_apps/*").select(&:directory?).each do |dir|
|
49
|
+
namespace :bench do
|
50
|
+
namespace dir.basename.to_s do
|
51
|
+
desc "Run benchmarks for the #{dir.basename} sample app"
|
52
|
+
task run: [:boot_protected_app, :boot_unprotected_app] do
|
53
|
+
wait_for_servers
|
54
|
+
Dir.chdir("benchmarks") { sh "k6 run #{dir.basename}.js" }
|
55
|
+
ensure
|
56
|
+
stop_servers
|
57
|
+
end
|
58
|
+
|
59
|
+
task :boot_protected_app do
|
60
|
+
boot_server(dir, port: 3001)
|
61
|
+
end
|
62
|
+
|
63
|
+
task :boot_unprotected_app do
|
64
|
+
boot_server(dir, port: 3002, env: {"AIKIDO_DISABLE" => "true"})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
task default: "#{dir.basename}:run"
|
69
|
+
end
|
70
|
+
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.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: x86_64-darwin
|
6
6
|
authors:
|
7
7
|
- Nicolas Sanguinetti
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -63,20 +63,32 @@ extensions: []
|
|
63
63
|
extra_rdoc_files: []
|
64
64
|
files:
|
65
65
|
- ".ruby-version"
|
66
|
+
- ".simplecov"
|
66
67
|
- ".standard.yml"
|
67
68
|
- CHANGELOG.md
|
68
|
-
- CODE_OF_CONDUCT.md
|
69
69
|
- LICENSE
|
70
70
|
- README.md
|
71
71
|
- Rakefile
|
72
|
+
- benchmarks/README.md
|
73
|
+
- benchmarks/rails7.1_sql_injection.js
|
74
|
+
- docs/banner.svg
|
75
|
+
- docs/config.md
|
76
|
+
- docs/rails.md
|
72
77
|
- lib/aikido-zen.rb
|
73
78
|
- lib/aikido.rb
|
74
79
|
- lib/aikido/zen.rb
|
75
80
|
- lib/aikido/zen/actor.rb
|
76
81
|
- lib/aikido/zen/agent.rb
|
82
|
+
- lib/aikido/zen/agent/heartbeats_manager.rb
|
77
83
|
- lib/aikido/zen/api_client.rb
|
78
84
|
- lib/aikido/zen/attack.rb
|
79
85
|
- lib/aikido/zen/capped_collections.rb
|
86
|
+
- lib/aikido/zen/collector.rb
|
87
|
+
- lib/aikido/zen/collector/hosts.rb
|
88
|
+
- lib/aikido/zen/collector/routes.rb
|
89
|
+
- lib/aikido/zen/collector/sink_stats.rb
|
90
|
+
- lib/aikido/zen/collector/stats.rb
|
91
|
+
- lib/aikido/zen/collector/users.rb
|
80
92
|
- lib/aikido/zen/config.rb
|
81
93
|
- lib/aikido/zen/context.rb
|
82
94
|
- lib/aikido/zen/context/rack_request.rb
|
@@ -84,7 +96,7 @@ files:
|
|
84
96
|
- lib/aikido/zen/errors.rb
|
85
97
|
- lib/aikido/zen/event.rb
|
86
98
|
- lib/aikido/zen/internals.rb
|
87
|
-
- lib/aikido/zen/libzen-v0.1.
|
99
|
+
- lib/aikido/zen/libzen-v0.1.31.x86_64.dylib
|
88
100
|
- lib/aikido/zen/middleware/check_allowed_addresses.rb
|
89
101
|
- lib/aikido/zen/middleware/set_context.rb
|
90
102
|
- lib/aikido/zen/middleware/throttler.rb
|
@@ -121,6 +133,7 @@ files:
|
|
121
133
|
- lib/aikido/zen/scanners/stored_ssrf_scanner.rb
|
122
134
|
- lib/aikido/zen/sink.rb
|
123
135
|
- lib/aikido/zen/sinks.rb
|
136
|
+
- lib/aikido/zen/sinks/action_controller.rb
|
124
137
|
- lib/aikido/zen/sinks/async_http.rb
|
125
138
|
- lib/aikido/zen/sinks/curb.rb
|
126
139
|
- lib/aikido/zen/sinks/em_http.rb
|
@@ -137,13 +150,11 @@ files:
|
|
137
150
|
- lib/aikido/zen/sinks/sqlite3.rb
|
138
151
|
- lib/aikido/zen/sinks/trilogy.rb
|
139
152
|
- lib/aikido/zen/sinks/typhoeus.rb
|
140
|
-
- lib/aikido/zen/stats.rb
|
141
|
-
- lib/aikido/zen/stats/routes.rb
|
142
|
-
- lib/aikido/zen/stats/sink_stats.rb
|
143
|
-
- lib/aikido/zen/stats/users.rb
|
144
153
|
- lib/aikido/zen/synchronizable.rb
|
145
154
|
- lib/aikido/zen/system_info.rb
|
146
155
|
- lib/aikido/zen/version.rb
|
156
|
+
- lib/aikido/zen/worker.rb
|
157
|
+
- tasklib/bench.rake
|
147
158
|
- tasklib/libzen.rake
|
148
159
|
homepage: https://aikido.dev
|
149
160
|
licenses:
|
@@ -167,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
178
|
- !ruby/object:Gem::Version
|
168
179
|
version: '0'
|
169
180
|
requirements: []
|
170
|
-
rubygems_version: 3.5.
|
181
|
+
rubygems_version: 3.5.22
|
171
182
|
signing_key:
|
172
183
|
specification_version: 4
|
173
184
|
summary: Embedded Web Application Firewall that autonomously protects Ruby apps against
|
data/CODE_OF_CONDUCT.md
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
2
|
-
|
3
|
-
## Our Pledge
|
4
|
-
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our
|
6
|
-
community a harassment-free experience for everyone, regardless of age, body
|
7
|
-
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
8
|
-
identity and expression, level of experience, education, socio-economic status,
|
9
|
-
nationality, personal appearance, race, caste, color, religion, or sexual
|
10
|
-
identity and orientation.
|
11
|
-
|
12
|
-
We pledge to act and interact in ways that contribute to an open, welcoming,
|
13
|
-
diverse, inclusive, and healthy community.
|
14
|
-
|
15
|
-
## Our Standards
|
16
|
-
|
17
|
-
Examples of behavior that contributes to a positive environment for our
|
18
|
-
community include:
|
19
|
-
|
20
|
-
* Demonstrating empathy and kindness toward other people
|
21
|
-
* Being respectful of differing opinions, viewpoints, and experiences
|
22
|
-
* Giving and gracefully accepting constructive feedback
|
23
|
-
* Accepting responsibility and apologizing to those affected by our mistakes,
|
24
|
-
and learning from the experience
|
25
|
-
* Focusing on what is best not just for us as individuals, but for the overall
|
26
|
-
community
|
27
|
-
|
28
|
-
Examples of unacceptable behavior include:
|
29
|
-
|
30
|
-
* The use of sexualized language or imagery, and sexual attention or advances of
|
31
|
-
any kind
|
32
|
-
* Trolling, insulting or derogatory comments, and personal or political attacks
|
33
|
-
* Public or private harassment
|
34
|
-
* Publishing others' private information, such as a physical or email address,
|
35
|
-
without their explicit permission
|
36
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
37
|
-
professional setting
|
38
|
-
|
39
|
-
## Enforcement Responsibilities
|
40
|
-
|
41
|
-
Community leaders are responsible for clarifying and enforcing our standards of
|
42
|
-
acceptable behavior and will take appropriate and fair corrective action in
|
43
|
-
response to any behavior that they deem inappropriate, threatening, offensive,
|
44
|
-
or harmful.
|
45
|
-
|
46
|
-
Community leaders have the right and responsibility to remove, edit, or reject
|
47
|
-
comments, commits, code, wiki edits, issues, and other contributions that are
|
48
|
-
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
49
|
-
decisions when appropriate.
|
50
|
-
|
51
|
-
## Scope
|
52
|
-
|
53
|
-
This Code of Conduct applies within all community spaces, and also applies when
|
54
|
-
an individual is officially representing the community in public spaces.
|
55
|
-
Examples of representing our community include using an official email address,
|
56
|
-
posting via an official social media account, or acting as an appointed
|
57
|
-
representative at an online or offline event.
|
58
|
-
|
59
|
-
## Enforcement
|
60
|
-
|
61
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
62
|
-
reported to the community leaders responsible for enforcement at
|
63
|
-
[hello@aikido.dev](mailto:hello@aikido.dev).
|
64
|
-
All complaints will be reviewed and investigated promptly and fairly.
|
65
|
-
|
66
|
-
All community leaders are obligated to respect the privacy and security of the
|
67
|
-
reporter of any incident.
|
68
|
-
|
69
|
-
## Enforcement Guidelines
|
70
|
-
|
71
|
-
Community leaders will follow these Community Impact Guidelines in determining
|
72
|
-
the consequences for any action they deem in violation of this Code of Conduct:
|
73
|
-
|
74
|
-
### 1. Correction
|
75
|
-
|
76
|
-
**Community Impact**: Use of inappropriate language or other behavior deemed
|
77
|
-
unprofessional or unwelcome in the community.
|
78
|
-
|
79
|
-
**Consequence**: A private, written warning from community leaders, providing
|
80
|
-
clarity around the nature of the violation and an explanation of why the
|
81
|
-
behavior was inappropriate. A public apology may be requested.
|
82
|
-
|
83
|
-
### 2. Warning
|
84
|
-
|
85
|
-
**Community Impact**: A violation through a single incident or series of
|
86
|
-
actions.
|
87
|
-
|
88
|
-
**Consequence**: A warning with consequences for continued behavior. No
|
89
|
-
interaction with the people involved, including unsolicited interaction with
|
90
|
-
those enforcing the Code of Conduct, for a specified period of time. This
|
91
|
-
includes avoiding interactions in community spaces as well as external channels
|
92
|
-
like social media. Violating these terms may lead to a temporary or permanent
|
93
|
-
ban.
|
94
|
-
|
95
|
-
### 3. Temporary Ban
|
96
|
-
|
97
|
-
**Community Impact**: A serious violation of community standards, including
|
98
|
-
sustained inappropriate behavior.
|
99
|
-
|
100
|
-
**Consequence**: A temporary ban from any sort of interaction or public
|
101
|
-
communication with the community for a specified period of time. No public or
|
102
|
-
private interaction with the people involved, including unsolicited interaction
|
103
|
-
with those enforcing the Code of Conduct, is allowed during this period.
|
104
|
-
Violating these terms may lead to a permanent ban.
|
105
|
-
|
106
|
-
### 4. Permanent Ban
|
107
|
-
|
108
|
-
**Community Impact**: Demonstrating a pattern of violation of community
|
109
|
-
standards, including sustained inappropriate behavior, harassment of an
|
110
|
-
individual, or aggression toward or disparagement of classes of individuals.
|
111
|
-
|
112
|
-
**Consequence**: A permanent ban from any sort of public interaction within the
|
113
|
-
community.
|
114
|
-
|
115
|
-
## Attribution
|
116
|
-
|
117
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
118
|
-
version 2.1, available at
|
119
|
-
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
120
|
-
|
121
|
-
Community Impact Guidelines were inspired by
|
122
|
-
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
123
|
-
|
124
|
-
For answers to common questions about this code of conduct, see the FAQ at
|
125
|
-
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
126
|
-
[https://www.contributor-covenant.org/translations][translations].
|
127
|
-
|
128
|
-
[homepage]: https://www.contributor-covenant.org
|
129
|
-
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
130
|
-
[Mozilla CoC]: https://github.com/mozilla/diversity
|
131
|
-
[FAQ]: https://www.contributor-covenant.org/faq
|
132
|
-
[translations]: https://www.contributor-covenant.org/translations
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../request/schema/empty_schema"
|
4
|
-
|
5
|
-
class Aikido::Zen::Stats
|
6
|
-
# @api private
|
7
|
-
#
|
8
|
-
# Keeps track of the visited routes.
|
9
|
-
class Routes
|
10
|
-
def initialize
|
11
|
-
@routes = Hash.new do |h, k|
|
12
|
-
h[k] = Record.new(0, Aikido::Zen::Request::Schema::EMPTY_SCHEMA)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# @param route [Aikido::Zen::Route, nil] tracks the visit, if given.
|
17
|
-
# @param schema [Aikido::Zen::Request::Schema, nil] the schema of the
|
18
|
-
# request, if the feature is enabled.
|
19
|
-
# @return [void]
|
20
|
-
def add(route, schema = nil)
|
21
|
-
@routes[route].add(schema) if route
|
22
|
-
end
|
23
|
-
|
24
|
-
# @!visibility private
|
25
|
-
def [](route)
|
26
|
-
@routes[route]
|
27
|
-
end
|
28
|
-
|
29
|
-
# @!visibility private
|
30
|
-
def empty?
|
31
|
-
@routes.empty?
|
32
|
-
end
|
33
|
-
|
34
|
-
def as_json
|
35
|
-
@routes.map do |route, record|
|
36
|
-
{
|
37
|
-
method: route.verb,
|
38
|
-
path: route.path,
|
39
|
-
hits: record.hits,
|
40
|
-
apispec: record.schema&.as_json
|
41
|
-
}.compact
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# @!visibility private
|
46
|
-
Record = Struct.new(:hits, :schema) do
|
47
|
-
def add(new_schema = nil)
|
48
|
-
self.hits += 1
|
49
|
-
self.schema |= new_schema
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/lib/aikido/zen/stats.rb
DELETED
@@ -1,171 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "concurrent"
|
4
|
-
|
5
|
-
require_relative "capped_collections"
|
6
|
-
|
7
|
-
module Aikido::Zen
|
8
|
-
# Tracks information about how the Aikido Agent is used in the app.
|
9
|
-
class Stats < Concurrent::Synchronization::LockableObject
|
10
|
-
# @!visibility private
|
11
|
-
attr_reader :started_at, :ended_at, :requests, :aborted_requests, :sinks
|
12
|
-
|
13
|
-
# @!visibility private
|
14
|
-
attr_writer :ended_at
|
15
|
-
|
16
|
-
def initialize(config = Aikido::Zen.config)
|
17
|
-
super()
|
18
|
-
@config = config
|
19
|
-
reset_state
|
20
|
-
end
|
21
|
-
|
22
|
-
# @return [Boolean]
|
23
|
-
def empty?
|
24
|
-
synchronize { @requests.zero? && @sinks.empty? }
|
25
|
-
end
|
26
|
-
|
27
|
-
# @return [Boolean]
|
28
|
-
def any?
|
29
|
-
!empty?
|
30
|
-
end
|
31
|
-
|
32
|
-
# Track the timestamp we start tracking this series of stats.
|
33
|
-
#
|
34
|
-
# @return [void]
|
35
|
-
def start(at = Time.now.utc)
|
36
|
-
synchronize { @started_at = at }
|
37
|
-
end
|
38
|
-
|
39
|
-
# Atomically copies the stats and resets them to their initial values, so
|
40
|
-
# you can start gathering a new set while being able to read the old ones
|
41
|
-
# without fear that a thread might modify them.
|
42
|
-
#
|
43
|
-
# @param at [Time] the time at which we're resetting, which is set as the
|
44
|
-
# ending time for the returned copy.
|
45
|
-
#
|
46
|
-
# @return [Aikido::Zen::Stats] a frozen copy of the object before
|
47
|
-
# resetting, so you can access the data there, with its +ended_at+
|
48
|
-
# set to the given timestamp.
|
49
|
-
def reset(at: Time.now.utc)
|
50
|
-
synchronize {
|
51
|
-
# Make sure the timing stats are compressed before copying, since we
|
52
|
-
# need these compressed when we serialize this for the API.
|
53
|
-
@sinks.each_value(&:compress_timings)
|
54
|
-
|
55
|
-
copy = clone
|
56
|
-
copy.ended_at = at
|
57
|
-
|
58
|
-
reset_state
|
59
|
-
start(at)
|
60
|
-
|
61
|
-
copy
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param request [Aikido::Zen::Request]
|
66
|
-
# @return [void]
|
67
|
-
def add_request(request)
|
68
|
-
synchronize {
|
69
|
-
@requests += 1
|
70
|
-
@routes.add(request.route, request.schema)
|
71
|
-
}
|
72
|
-
self
|
73
|
-
end
|
74
|
-
|
75
|
-
# @param connection [Aikido::Zen::OutboundConnection]
|
76
|
-
# @return [void]
|
77
|
-
def add_outbound(connection)
|
78
|
-
synchronize { @outbound_connections << connection }
|
79
|
-
self
|
80
|
-
end
|
81
|
-
|
82
|
-
# @param [Aikido::Zen::Scan]
|
83
|
-
# @return [void]
|
84
|
-
def add_scan(scan)
|
85
|
-
synchronize {
|
86
|
-
stats = @sinks[scan.sink.name]
|
87
|
-
stats.scans += 1
|
88
|
-
stats.errors += 1 if scan.errors?
|
89
|
-
stats.add_timing(scan.duration)
|
90
|
-
}
|
91
|
-
self
|
92
|
-
end
|
93
|
-
|
94
|
-
# @param attack [Aikido::Zen::Attack]
|
95
|
-
# @param being_blocked [Boolean] whether the Agent blocked the
|
96
|
-
# request where this Attack happened or not.
|
97
|
-
#
|
98
|
-
# @return [void]
|
99
|
-
def add_attack(attack, being_blocked:)
|
100
|
-
synchronize {
|
101
|
-
stats = @sinks[attack.sink.name]
|
102
|
-
stats.attacks += 1
|
103
|
-
stats.blocked_attacks += 1 if being_blocked
|
104
|
-
}
|
105
|
-
self
|
106
|
-
end
|
107
|
-
|
108
|
-
# @param actor [Aikido::Zen::Actor]
|
109
|
-
# @return [void]
|
110
|
-
def add_user(actor)
|
111
|
-
synchronize { @users.add(actor) }
|
112
|
-
end
|
113
|
-
|
114
|
-
# @return [#as_json] the set of routes visited during this stats-gathering
|
115
|
-
# period.
|
116
|
-
def routes
|
117
|
-
synchronize { @routes }
|
118
|
-
end
|
119
|
-
|
120
|
-
# @return [Enumerable<#as_json>] the set of users that had an active session
|
121
|
-
# during this stats-gathering period.
|
122
|
-
def users
|
123
|
-
synchronize { @users }
|
124
|
-
end
|
125
|
-
|
126
|
-
# @return [#as_json] the set of connections to outbound hosts that were
|
127
|
-
# established during this stats-gathering period.
|
128
|
-
def outbound_connections
|
129
|
-
synchronize { @outbound_connections }
|
130
|
-
end
|
131
|
-
|
132
|
-
def as_json
|
133
|
-
synchronize {
|
134
|
-
total_attacks, total_blocked = aggregate_attacks_from_sinks
|
135
|
-
{
|
136
|
-
startedAt: @started_at.to_i * 1000,
|
137
|
-
endedAt: (@ended_at.to_i * 1000 if @ended_at),
|
138
|
-
sinks: @sinks.transform_values(&:as_json),
|
139
|
-
requests: {
|
140
|
-
total: @requests,
|
141
|
-
aborted: @aborted_requests,
|
142
|
-
attacksDetected: {
|
143
|
-
total: total_attacks,
|
144
|
-
blocked: total_blocked
|
145
|
-
}
|
146
|
-
}
|
147
|
-
}
|
148
|
-
}
|
149
|
-
end
|
150
|
-
|
151
|
-
private def reset_state
|
152
|
-
@sinks = Hash.new { |h, k| h[k] = SinkStats.new(k, @config) }
|
153
|
-
@started_at = @ended_at = nil
|
154
|
-
@requests = 0
|
155
|
-
@routes = Routes.new
|
156
|
-
@users = Users.new(@config.max_users_tracked)
|
157
|
-
@outbound_connections = CappedSet.new(@config.max_outbound_connections)
|
158
|
-
@aborted_requests = 0
|
159
|
-
end
|
160
|
-
|
161
|
-
private def aggregate_attacks_from_sinks
|
162
|
-
@sinks.each_value.reduce([0, 0]) { |(attacks, blocked), stats|
|
163
|
-
[attacks + stats.attacks, blocked + stats.blocked_attacks]
|
164
|
-
}
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
require_relative "stats/users"
|
170
|
-
require_relative "stats/routes"
|
171
|
-
require_relative "stats/sink_stats"
|