aikido-zen 1.0.2.beta.10-x86_64-mingw-64 → 1.0.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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/docs/config.md +1 -1
- data/docs/troubleshooting.md +62 -0
- data/lib/aikido/zen/agent.rb +2 -2
- data/lib/aikido/zen/attack.rb +8 -6
- data/lib/aikido/zen/attack_wave/helpers.rb +457 -0
- data/lib/aikido/zen/attack_wave.rb +88 -0
- data/lib/aikido/zen/cache.rb +91 -0
- data/lib/aikido/zen/capped_collections.rb +22 -4
- data/lib/aikido/zen/collector/event.rb +29 -0
- data/lib/aikido/zen/collector/hosts.rb +16 -1
- data/lib/aikido/zen/collector/stats.rb +17 -3
- data/lib/aikido/zen/collector/users.rb +2 -2
- data/lib/aikido/zen/collector.rb +14 -0
- data/lib/aikido/zen/config.rb +29 -6
- data/lib/aikido/zen/context/rack_request.rb +3 -0
- data/lib/aikido/zen/context/rails_request.rb +3 -0
- data/lib/aikido/zen/context.rb +2 -2
- data/lib/aikido/zen/event.rb +47 -2
- data/lib/aikido/zen/helpers.rb +24 -0
- data/lib/aikido/zen/middleware/{check_allowed_addresses.rb → allowed_address_checker.rb} +1 -1
- data/lib/aikido/zen/middleware/attack_wave_protector.rb +46 -0
- data/lib/aikido/zen/middleware/{set_context.rb → context_setter.rb} +1 -1
- data/lib/aikido/zen/middleware/rack_throttler.rb +3 -1
- data/lib/aikido/zen/middleware/request_tracker.rb +8 -3
- data/lib/aikido/zen/outbound_connection.rb +11 -1
- data/lib/aikido/zen/rails_engine.rb +3 -2
- data/lib/aikido/zen/request/rails_router.rb +17 -2
- data/lib/aikido/zen/request.rb +2 -36
- data/lib/aikido/zen/route.rb +50 -0
- data/lib/aikido/zen/runtime_settings/endpoints.rb +37 -8
- data/lib/aikido/zen/runtime_settings.rb +5 -4
- data/lib/aikido/zen/scanners/path_traversal_scanner.rb +3 -2
- data/lib/aikido/zen/scanners/shell_injection_scanner.rb +3 -2
- data/lib/aikido/zen/scanners/sql_injection_scanner.rb +3 -2
- data/lib/aikido/zen/scanners/ssrf_scanner.rb +2 -1
- data/lib/aikido/zen/scanners/stored_ssrf_scanner.rb +5 -1
- data/lib/aikido/zen/sinks/action_controller.rb +3 -1
- data/lib/aikido/zen/sinks/socket.rb +7 -0
- data/lib/aikido/zen/system_info.rb +1 -5
- data/lib/aikido/zen/version.rb +1 -1
- data/lib/aikido/zen.rb +55 -6
- data/tasklib/bench.rake +1 -1
- metadata +10 -4
data/lib/aikido/zen/request.rb
CHANGED
|
@@ -22,13 +22,11 @@ module Aikido::Zen
|
|
|
22
22
|
@config = config
|
|
23
23
|
@framework = framework
|
|
24
24
|
@router = router
|
|
25
|
-
@body_read = false
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
def __setobj__(delegate) # :nodoc:
|
|
29
28
|
super
|
|
30
|
-
@
|
|
31
|
-
@route = @normalized_header = @truncated_body = nil
|
|
29
|
+
@route = @normalized_header = nil
|
|
32
30
|
end
|
|
33
31
|
|
|
34
32
|
# @return [Aikido::Zen::Route] the framework route being requested.
|
|
@@ -41,8 +39,6 @@ module Aikido::Zen
|
|
|
41
39
|
@schema ||= Aikido::Zen::Request::Schema.build
|
|
42
40
|
end
|
|
43
41
|
|
|
44
|
-
# @api private
|
|
45
|
-
#
|
|
46
42
|
# @return [String] the IP address of the client making the request.
|
|
47
43
|
def client_ip
|
|
48
44
|
return @client_ip if @client_ip
|
|
@@ -74,42 +70,12 @@ module Aikido::Zen
|
|
|
74
70
|
}
|
|
75
71
|
end
|
|
76
72
|
|
|
77
|
-
# @api private
|
|
78
|
-
#
|
|
79
|
-
# Reads the first 16KiB of the request body, to include in attack reports
|
|
80
|
-
# back to the Aikido server. This method should only be called if an attack
|
|
81
|
-
# is detected during the current request.
|
|
82
|
-
#
|
|
83
|
-
# If the underlying IO object has been partially (or fully) read before,
|
|
84
|
-
# this will attempt to restore the previous cursor position after reading it
|
|
85
|
-
# if possible, or leave if rewund if not.
|
|
86
|
-
#
|
|
87
|
-
# @param max_size [Integer] number of bytes to read at most.
|
|
88
|
-
#
|
|
89
|
-
# @return [String]
|
|
90
|
-
def truncated_body(max_size: 16384)
|
|
91
|
-
return @truncated_body if @body_read
|
|
92
|
-
return nil if body.nil?
|
|
93
|
-
|
|
94
|
-
begin
|
|
95
|
-
initial_pos = body.pos if body.respond_to?(:pos)
|
|
96
|
-
body.rewind
|
|
97
|
-
@truncated_body = body.read(max_size)
|
|
98
|
-
ensure
|
|
99
|
-
@body_read = true
|
|
100
|
-
body.rewind
|
|
101
|
-
body.seek(initial_pos) if initial_pos && body.respond_to?(:seek)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
73
|
def as_json
|
|
106
74
|
{
|
|
107
|
-
method: request_method.
|
|
75
|
+
method: request_method.upcase,
|
|
108
76
|
url: url,
|
|
109
77
|
ipAddress: client_ip,
|
|
110
78
|
userAgent: user_agent,
|
|
111
|
-
headers: normalized_headers.reject { |_, val| val.to_s.empty? },
|
|
112
|
-
body: truncated_body,
|
|
113
79
|
source: framework,
|
|
114
80
|
route: route&.path
|
|
115
81
|
}
|
data/lib/aikido/zen/route.rb
CHANGED
|
@@ -39,8 +39,58 @@ module Aikido::Zen
|
|
|
39
39
|
[verb, path].hash
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# Sort routes by wildcard matching order deterministically:
|
|
43
|
+
#
|
|
44
|
+
# 1. Exact path before wildcard path
|
|
45
|
+
# 2. Fewer wildcards in path relative to path length
|
|
46
|
+
# 3. Earliest wildcard position in path
|
|
47
|
+
# 4. Exact verb before wildcard verb
|
|
48
|
+
# 5. Lexicographic path (tie-break)
|
|
49
|
+
# 6. Lexicographic verb (tie-break)
|
|
50
|
+
#
|
|
51
|
+
# @return [Array] the sort key
|
|
52
|
+
def sort_key
|
|
53
|
+
@sort_key ||= begin
|
|
54
|
+
stars = []
|
|
55
|
+
i = -1
|
|
56
|
+
while (i = path.index("*", i + 1))
|
|
57
|
+
stars << i
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
[
|
|
61
|
+
stars.empty? ? 0 : 1,
|
|
62
|
+
stars.length - path.length,
|
|
63
|
+
stars,
|
|
64
|
+
(verb == "*") ? 1 : 0,
|
|
65
|
+
path,
|
|
66
|
+
verb
|
|
67
|
+
].freeze
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def match?(other)
|
|
72
|
+
other.is_a?(Route) &&
|
|
73
|
+
pattern(verb).match?(other.verb) &&
|
|
74
|
+
pattern(path).match?(other.path)
|
|
75
|
+
end
|
|
76
|
+
|
|
42
77
|
def inspect
|
|
43
78
|
"#<#{self.class.name} #{verb} #{path.inspect}>"
|
|
44
79
|
end
|
|
80
|
+
|
|
81
|
+
# Construct a regular expression equivalent to the wildcard string,
|
|
82
|
+
# where '*' is the wildcard operator.
|
|
83
|
+
#
|
|
84
|
+
# The resulting pattern matches the entire input, allows an optional
|
|
85
|
+
# trailing slash, and is case-insensitive.
|
|
86
|
+
#
|
|
87
|
+
# All other special characters in the regular expression are escaped
|
|
88
|
+
# so that they are treated literally.
|
|
89
|
+
#
|
|
90
|
+
# @param string [String] wildcard string
|
|
91
|
+
# @return [Regexp] regular expression matching the wildcard string
|
|
92
|
+
private def pattern(string)
|
|
93
|
+
/^#{Regexp.escape(string).gsub("\\*", ".*")}\/?$/i
|
|
94
|
+
end
|
|
45
95
|
end
|
|
46
96
|
end
|
|
@@ -16,24 +16,53 @@ module Aikido::Zen
|
|
|
16
16
|
# @param data [Array<Hash>]
|
|
17
17
|
# @return [Aikido::Zen::RuntimeSettings::Endpoints]
|
|
18
18
|
def self.from_json(data)
|
|
19
|
-
|
|
20
|
-
route = Route.new(verb:
|
|
21
|
-
settings = RuntimeSettings::ProtectionSettings.from_json(
|
|
19
|
+
endpoint_pairs = Array(data).map do |value|
|
|
20
|
+
route = Route.new(verb: value["method"], path: value["route"])
|
|
21
|
+
settings = RuntimeSettings::ProtectionSettings.from_json(value)
|
|
22
22
|
[route, settings]
|
|
23
|
-
|
|
23
|
+
end
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
# Sort endpoints by wildcard matching order
|
|
26
|
+
endpoint_pairs.sort_by! do |route, settings|
|
|
27
|
+
route.sort_key
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
new(endpoint_pairs.to_h)
|
|
26
31
|
end
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
# @param endpoints [Hash] the endpoints in wildcard matching order
|
|
34
|
+
# @return [Aikido::Zen::RuntimeSettings::Endpoints]
|
|
35
|
+
def initialize(endpoints = {})
|
|
36
|
+
@endpoints = endpoints
|
|
30
37
|
@endpoints.default = RuntimeSettings::ProtectionSettings.none
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
# @param route [Aikido::Zen::Route]
|
|
34
41
|
# @return [Aikido::Zen::RuntimeSettings::ProtectionSettings]
|
|
35
42
|
def [](route)
|
|
36
|
-
@endpoints[route]
|
|
43
|
+
return @endpoints[route] if @endpoints.key?(route)
|
|
44
|
+
|
|
45
|
+
# Wildcard endpoint matching
|
|
46
|
+
|
|
47
|
+
@endpoints.each do |pattern, settings|
|
|
48
|
+
return settings if pattern.match?(route)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@endpoints.default
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param route [Aikido::Zen::Route]
|
|
55
|
+
# @return [Array<Aikido::Zen::RuntimeSettings::ProtectionSettings>]
|
|
56
|
+
def match(route)
|
|
57
|
+
matches = []
|
|
58
|
+
|
|
59
|
+
@endpoints.each do |pattern, settings|
|
|
60
|
+
matches << settings if pattern.match?(route)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
matches << @endpoints.default if matches.empty?
|
|
64
|
+
|
|
65
|
+
matches
|
|
37
66
|
end
|
|
38
67
|
|
|
39
68
|
# @!visibility private
|
|
@@ -11,11 +11,11 @@ module Aikido::Zen
|
|
|
11
11
|
#
|
|
12
12
|
# You can subscribe to changes with +#add_observer(object, func_name)+, which
|
|
13
13
|
# will call the function passing the settings as an argument.
|
|
14
|
-
RuntimeSettings = Struct.new(:updated_at, :heartbeat_interval, :endpoints, :blocked_user_ids, :
|
|
14
|
+
RuntimeSettings = Struct.new(:updated_at, :heartbeat_interval, :endpoints, :blocked_user_ids, :allowed_ips, :received_any_stats, :blocking_mode) do
|
|
15
15
|
def initialize(*)
|
|
16
16
|
super
|
|
17
17
|
self.endpoints ||= RuntimeSettings::Endpoints.new
|
|
18
|
-
self.
|
|
18
|
+
self.allowed_ips ||= RuntimeSettings::IPSet.new
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @!attribute [rw] updated_at
|
|
@@ -35,7 +35,7 @@ module Aikido::Zen
|
|
|
35
35
|
# @!attribute [rw] blocked_user_ids
|
|
36
36
|
# @return [Array]
|
|
37
37
|
|
|
38
|
-
# @!attribute [rw]
|
|
38
|
+
# @!attribute [rw] allowed_ips
|
|
39
39
|
# @return [Aikido::Zen::RuntimeSettings::IPSet]
|
|
40
40
|
|
|
41
41
|
# Parse and interpret the JSON response from the core API with updated
|
|
@@ -53,8 +53,9 @@ module Aikido::Zen
|
|
|
53
53
|
self.heartbeat_interval = data["heartbeatIntervalInMS"].to_i / 1000
|
|
54
54
|
self.endpoints = RuntimeSettings::Endpoints.from_json(data["endpoints"])
|
|
55
55
|
self.blocked_user_ids = data["blockedUserIds"]
|
|
56
|
-
self.
|
|
56
|
+
self.allowed_ips = RuntimeSettings::IPSet.from_json(data["allowedIPAddresses"])
|
|
57
57
|
self.received_any_stats = data["receivedAnyStats"]
|
|
58
|
+
self.blocking_mode = data["block"]
|
|
58
59
|
|
|
59
60
|
updated_at != last_updated_at
|
|
60
61
|
end
|
|
@@ -21,14 +21,15 @@ module Aikido::Zen
|
|
|
21
21
|
# user input is detected to be attempting a Path Traversal Attack, or +nil+ if not.
|
|
22
22
|
def self.call(filepath:, sink:, context:, operation:)
|
|
23
23
|
context.payloads.each do |payload|
|
|
24
|
-
next unless new(filepath, payload.value).attack?
|
|
24
|
+
next unless new(filepath, payload.value.to_s).attack?
|
|
25
25
|
|
|
26
26
|
return Attacks::PathTraversalAttack.new(
|
|
27
27
|
sink: sink,
|
|
28
28
|
input: payload,
|
|
29
29
|
filepath: filepath,
|
|
30
30
|
context: context,
|
|
31
|
-
operation: "#{sink.operation}.#{operation}"
|
|
31
|
+
operation: "#{sink.operation}.#{operation}",
|
|
32
|
+
stack: Aikido::Zen.clean_stack_trace
|
|
32
33
|
)
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -16,14 +16,15 @@ module Aikido::Zen
|
|
|
16
16
|
#
|
|
17
17
|
def self.call(command:, sink:, context:, operation:)
|
|
18
18
|
context.payloads.each do |payload|
|
|
19
|
-
next unless new(command, payload.value).attack?
|
|
19
|
+
next unless new(command, payload.value.to_s).attack?
|
|
20
20
|
|
|
21
21
|
return Attacks::ShellInjectionAttack.new(
|
|
22
22
|
sink: sink,
|
|
23
23
|
input: payload,
|
|
24
24
|
command: command,
|
|
25
25
|
context: context,
|
|
26
|
-
operation: "#{sink.operation}.#{operation}"
|
|
26
|
+
operation: "#{sink.operation}.#{operation}",
|
|
27
|
+
stack: Aikido::Zen.clean_stack_trace
|
|
27
28
|
)
|
|
28
29
|
end
|
|
29
30
|
|
|
@@ -32,7 +32,7 @@ module Aikido::Zen
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
context.payloads.each do |payload|
|
|
35
|
-
next unless new(query, payload.value, dialect).attack?
|
|
35
|
+
next unless new(query, payload.value.to_s, dialect).attack?
|
|
36
36
|
|
|
37
37
|
return Attacks::SQLInjectionAttack.new(
|
|
38
38
|
sink: sink,
|
|
@@ -40,7 +40,8 @@ module Aikido::Zen
|
|
|
40
40
|
input: payload,
|
|
41
41
|
dialect: dialect,
|
|
42
42
|
context: context,
|
|
43
|
-
operation: "#{sink.operation}.#{operation}"
|
|
43
|
+
operation: "#{sink.operation}.#{operation}",
|
|
44
|
+
stack: Aikido::Zen.clean_stack_trace
|
|
44
45
|
)
|
|
45
46
|
end
|
|
46
47
|
|
|
@@ -20,7 +20,8 @@ module Aikido::Zen
|
|
|
20
20
|
address: offending_address,
|
|
21
21
|
sink: sink,
|
|
22
22
|
context: context,
|
|
23
|
-
operation: "#{sink.operation}.#{operation}"
|
|
23
|
+
operation: "#{sink.operation}.#{operation}",
|
|
24
|
+
stack: Aikido::Zen.clean_stack_trace
|
|
24
25
|
)
|
|
25
26
|
end
|
|
26
27
|
|
|
@@ -44,6 +45,9 @@ module Aikido::Zen
|
|
|
44
45
|
|
|
45
46
|
DANGEROUS_ADDRESSES = [
|
|
46
47
|
IPAddr.new("169.254.169.254"),
|
|
48
|
+
IPAddr.new("100.100.100.200"),
|
|
49
|
+
IPAddr.new("::ffff:169.254.169.254"),
|
|
50
|
+
IPAddr.new("::ffff:100.100.100.200"),
|
|
47
51
|
IPAddr.new("fd00:ec2::254")
|
|
48
52
|
]
|
|
49
53
|
end
|
|
@@ -43,8 +43,10 @@ module Aikido::Zen
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
private def should_throttle?(request)
|
|
46
|
+
# Bypass rate limiting for allowed IPs
|
|
47
|
+
return false if @settings.allowed_ips.include?(request.ip)
|
|
48
|
+
|
|
46
49
|
return false unless @settings.endpoints[request.route].rate_limiting.enabled?
|
|
47
|
-
return false if @settings.skip_protection_for_ips.include?(request.ip)
|
|
48
50
|
|
|
49
51
|
result = @detached_agent.calculate_rate_limits(request)
|
|
50
52
|
return false unless result
|
|
@@ -68,6 +68,13 @@ module Aikido::Zen
|
|
|
68
68
|
# :nocov:
|
|
69
69
|
Helpers.scan(remote_host, socket, "open")
|
|
70
70
|
# :nocov:
|
|
71
|
+
rescue Aikido::Zen::UnderAttackError, Aikido::Zen::Sinks::DSL::PresafeError
|
|
72
|
+
# If the scan raises an exception that will escape the safe block,
|
|
73
|
+
# the open socket must be closed because it will not be returned,
|
|
74
|
+
# so the user cannot close it.
|
|
75
|
+
socket.close
|
|
76
|
+
|
|
77
|
+
raise
|
|
71
78
|
end
|
|
72
79
|
end
|
|
73
80
|
end
|
|
@@ -8,12 +8,8 @@ require_relative "package"
|
|
|
8
8
|
module Aikido::Zen
|
|
9
9
|
# Provides information about the currently running Agent.
|
|
10
10
|
class SystemInfo
|
|
11
|
-
def initialize(config = Aikido::Zen.config)
|
|
12
|
-
@config = config
|
|
13
|
-
end
|
|
14
|
-
|
|
15
11
|
def attacks_block_requests?
|
|
16
|
-
|
|
12
|
+
!!Aikido::Zen.blocking_mode?
|
|
17
13
|
end
|
|
18
14
|
|
|
19
15
|
def attacks_are_only_reported?
|
data/lib/aikido/zen/version.rb
CHANGED
data/lib/aikido/zen.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "zen/helpers"
|
|
3
4
|
require_relative "zen/version"
|
|
4
5
|
require_relative "zen/errors"
|
|
5
6
|
require_relative "zen/actor"
|
|
@@ -13,13 +14,15 @@ require_relative "zen/context"
|
|
|
13
14
|
require_relative "zen/detached_agent"
|
|
14
15
|
require_relative "zen/middleware/middleware"
|
|
15
16
|
require_relative "zen/middleware/fork_detector"
|
|
16
|
-
require_relative "zen/middleware/
|
|
17
|
-
require_relative "zen/middleware/
|
|
17
|
+
require_relative "zen/middleware/context_setter"
|
|
18
|
+
require_relative "zen/middleware/allowed_address_checker"
|
|
19
|
+
require_relative "zen/middleware/attack_wave_protector"
|
|
18
20
|
require_relative "zen/middleware/request_tracker"
|
|
19
21
|
require_relative "zen/outbound_connection"
|
|
20
22
|
require_relative "zen/outbound_connection_monitor"
|
|
21
23
|
require_relative "zen/runtime_settings"
|
|
22
24
|
require_relative "zen/rate_limiter"
|
|
25
|
+
require_relative "zen/attack_wave"
|
|
23
26
|
require_relative "zen/scanners"
|
|
24
27
|
|
|
25
28
|
module Aikido
|
|
@@ -77,6 +80,16 @@ module Aikido
|
|
|
77
80
|
@runtime_settings = settings
|
|
78
81
|
end
|
|
79
82
|
|
|
83
|
+
# @return [Boolean] whether the Aikido agent is currently blocking requests.
|
|
84
|
+
# Blocking mode is configured at startup and can be controlled through the
|
|
85
|
+
# Aikido dashboard at runtime.
|
|
86
|
+
def self.blocking_mode?
|
|
87
|
+
blocking_mode = runtime_settings.blocking_mode
|
|
88
|
+
return blocking_mode unless blocking_mode.nil?
|
|
89
|
+
|
|
90
|
+
config.blocking_mode
|
|
91
|
+
end
|
|
92
|
+
|
|
80
93
|
# Gets information about the current system configuration, which is sent to
|
|
81
94
|
# the server along with any events.
|
|
82
95
|
def self.system_info
|
|
@@ -89,10 +102,6 @@ module Aikido
|
|
|
89
102
|
@collector ||= Collector.new
|
|
90
103
|
end
|
|
91
104
|
|
|
92
|
-
def self.detached_agent
|
|
93
|
-
@detached_agent ||= DetachedAgent::Agent.new
|
|
94
|
-
end
|
|
95
|
-
|
|
96
105
|
# Gets the current context object that holds all information about the
|
|
97
106
|
# current request.
|
|
98
107
|
#
|
|
@@ -118,6 +127,18 @@ module Aikido
|
|
|
118
127
|
collector.track_request
|
|
119
128
|
end
|
|
120
129
|
|
|
130
|
+
# Track statistics about an attack wave the app is handling.
|
|
131
|
+
#
|
|
132
|
+
# @param attack_wave [Aikido::Zen::Events::AttackWave]
|
|
133
|
+
# @return [void]
|
|
134
|
+
def self.track_attack_wave(attack_wave)
|
|
135
|
+
collector.track_attack_wave(being_blocked: false)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Track statistics about a route that the app has discovered.
|
|
139
|
+
#
|
|
140
|
+
# @param request [Aikido::Zen::Request]
|
|
141
|
+
# @return [void]
|
|
121
142
|
def self.track_discovered_route(request)
|
|
122
143
|
collector.track_route(request)
|
|
123
144
|
end
|
|
@@ -173,6 +194,11 @@ module Aikido
|
|
|
173
194
|
collector.middleware_installed!
|
|
174
195
|
end
|
|
175
196
|
|
|
197
|
+
# @return [Aikido::Zen::AttackWave::Detector] the attack wave detector.
|
|
198
|
+
def self.attack_wave_detector
|
|
199
|
+
@attack_wave_detector ||= AttackWave::Detector.new
|
|
200
|
+
end
|
|
201
|
+
|
|
176
202
|
# @!visibility private
|
|
177
203
|
# Load all sources.
|
|
178
204
|
#
|
|
@@ -210,6 +236,10 @@ module Aikido
|
|
|
210
236
|
@agent ||= Agent.start
|
|
211
237
|
end
|
|
212
238
|
|
|
239
|
+
def self.detached_agent
|
|
240
|
+
@detached_agent ||= DetachedAgent::Agent.new
|
|
241
|
+
end
|
|
242
|
+
|
|
213
243
|
def self.detached_agent_server
|
|
214
244
|
@detached_agent_server ||= DetachedAgent::Server.start
|
|
215
245
|
end
|
|
@@ -250,5 +280,24 @@ module Aikido
|
|
|
250
280
|
@detached_agent&.handle_fork
|
|
251
281
|
end
|
|
252
282
|
end
|
|
283
|
+
|
|
284
|
+
# @!visibility private
|
|
285
|
+
# Returns the stack trace trimmed to where execution last entered Zen.
|
|
286
|
+
#
|
|
287
|
+
# @return [String]
|
|
288
|
+
def self.clean_stack_trace
|
|
289
|
+
stack_trace = caller_locations
|
|
290
|
+
|
|
291
|
+
spec = Gem.loaded_specs["aikido-zen"]
|
|
292
|
+
|
|
293
|
+
# Only trim stack frames from .../lib/aikido/zen/ in the aikido-zen gem,
|
|
294
|
+
# so calls in sample apps are preserved.
|
|
295
|
+
lib_path_start = File.expand_path(File.join(spec.full_gem_path, "lib", "aikido", "zen")) + File::SEPARATOR
|
|
296
|
+
|
|
297
|
+
index = stack_trace.index { |frame| !File.expand_path(frame.path).start_with?(lib_path_start) }
|
|
298
|
+
stack_trace = stack_trace[index..] if index
|
|
299
|
+
|
|
300
|
+
stack_trace.map(&:to_s).join("\n")
|
|
301
|
+
end
|
|
253
302
|
end
|
|
254
303
|
end
|
data/tasklib/bench.rake
CHANGED
|
@@ -87,7 +87,7 @@ Pathname.glob("sample_apps/*").select(&:directory?).each do |dir|
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
task :boot_unprotected_app do
|
|
90
|
-
boot_server(dir, port: PORT_UNPROTECTED, env: {"
|
|
90
|
+
boot_server(dir, port: PORT_UNPROTECTED, env: {"AIKIDO_DISABLE" => "true"})
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
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: 1.0.2
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: x86_64-mingw-64
|
|
6
6
|
authors:
|
|
7
7
|
- Aikido Security
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-12-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -77,6 +77,7 @@ files:
|
|
|
77
77
|
- docs/config.md
|
|
78
78
|
- docs/proxy.md
|
|
79
79
|
- docs/rails.md
|
|
80
|
+
- docs/troubleshooting.md
|
|
80
81
|
- lib/aikido-zen.rb
|
|
81
82
|
- lib/aikido/zen.rb
|
|
82
83
|
- lib/aikido/zen/actor.rb
|
|
@@ -84,7 +85,10 @@ files:
|
|
|
84
85
|
- lib/aikido/zen/agent/heartbeats_manager.rb
|
|
85
86
|
- lib/aikido/zen/api_client.rb
|
|
86
87
|
- lib/aikido/zen/attack.rb
|
|
88
|
+
- lib/aikido/zen/attack_wave.rb
|
|
89
|
+
- lib/aikido/zen/attack_wave/helpers.rb
|
|
87
90
|
- lib/aikido/zen/background_worker.rb
|
|
91
|
+
- lib/aikido/zen/cache.rb
|
|
88
92
|
- lib/aikido/zen/capped_collections.rb
|
|
89
93
|
- lib/aikido/zen/collector.rb
|
|
90
94
|
- lib/aikido/zen/collector/event.rb
|
|
@@ -103,14 +107,16 @@ files:
|
|
|
103
107
|
- lib/aikido/zen/detached_agent/server.rb
|
|
104
108
|
- lib/aikido/zen/errors.rb
|
|
105
109
|
- lib/aikido/zen/event.rb
|
|
110
|
+
- lib/aikido/zen/helpers.rb
|
|
106
111
|
- lib/aikido/zen/internals.rb
|
|
107
112
|
- lib/aikido/zen/libzen-v0.1.48-x86_64-mingw-64.dll
|
|
108
|
-
- lib/aikido/zen/middleware/
|
|
113
|
+
- lib/aikido/zen/middleware/allowed_address_checker.rb
|
|
114
|
+
- lib/aikido/zen/middleware/attack_wave_protector.rb
|
|
115
|
+
- lib/aikido/zen/middleware/context_setter.rb
|
|
109
116
|
- lib/aikido/zen/middleware/fork_detector.rb
|
|
110
117
|
- lib/aikido/zen/middleware/middleware.rb
|
|
111
118
|
- lib/aikido/zen/middleware/rack_throttler.rb
|
|
112
119
|
- lib/aikido/zen/middleware/request_tracker.rb
|
|
113
|
-
- lib/aikido/zen/middleware/set_context.rb
|
|
114
120
|
- lib/aikido/zen/outbound_connection.rb
|
|
115
121
|
- lib/aikido/zen/outbound_connection_monitor.rb
|
|
116
122
|
- lib/aikido/zen/package.rb
|