aikido-zen 1.6.0 → 1.7.0
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/lib/aikido/zen/agent.rb +12 -10
- data/lib/aikido/zen/runtime_settings/ip_list.rb +12 -7
- data/lib/aikido/zen/runtime_settings.rb +35 -1
- data/lib/aikido/zen/sinks/httpx.rb +4 -0
- data/lib/aikido/zen/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 651c1ff033d7689d75fa83bc27e64249463e74ad5ecac534df46e2cae96fcda2
|
|
4
|
+
data.tar.gz: 8232034fd237b6b4be9cde3cbea8d35c9a796d05b6f72bb90ff4cfc63888d037
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf4940ca74224b195ddbe9447b5d14d61b6f677c152f484a5ed71b6fe100482e5bd71d917f57807b632b24e93e4b9f6d9dbb53f6cc5cdc280dcf3e3315fdbcfd
|
|
7
|
+
data.tar.gz: f7746da3d5a9bb8e4c54be18c689fc3338ba7598fe59a683921ff47a8455b6800572016a4ad1103bc42aa0f01e09f34d9e38aecf957474c8dbd18847632ac937
|
data/lib/aikido/zen/agent.rb
CHANGED
|
@@ -18,12 +18,14 @@ module Aikido::Zen
|
|
|
18
18
|
|
|
19
19
|
def initialize(
|
|
20
20
|
config: Aikido::Zen.config,
|
|
21
|
+
settings: Aikido::Zen.runtime_settings,
|
|
21
22
|
collector: Aikido::Zen.collector,
|
|
22
23
|
worker: Aikido::Zen::Worker.new(config: config),
|
|
23
24
|
api_client: Aikido::Zen::APIClient.new(config: config),
|
|
24
25
|
api_stream: Aikido::Zen::APIStream.new(config: config)
|
|
25
26
|
)
|
|
26
27
|
@config = config
|
|
28
|
+
@settings = settings
|
|
27
29
|
@collector = collector
|
|
28
30
|
@worker = worker
|
|
29
31
|
@api_client = api_client
|
|
@@ -68,6 +70,15 @@ module Aikido::Zen
|
|
|
68
70
|
report(Events::Started.new(time: @started_at)) do |response|
|
|
69
71
|
if update_settings_from_runtime_config!(response, reason: "after start")
|
|
70
72
|
updated_settings!
|
|
73
|
+
|
|
74
|
+
if @config.realtime_settings_updates_enabled? || @settings.realtime_settings_updates_enabled?
|
|
75
|
+
@api_stream.handle("config-updated") do |event|
|
|
76
|
+
@config.logger.debug("Received server-sent event: config-updated")
|
|
77
|
+
settings_updated(event)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
@api_stream.start!
|
|
81
|
+
end
|
|
71
82
|
# :nocov:
|
|
72
83
|
else
|
|
73
84
|
# empty
|
|
@@ -83,15 +94,6 @@ module Aikido::Zen
|
|
|
83
94
|
@config.logger.error(err.message)
|
|
84
95
|
end
|
|
85
96
|
|
|
86
|
-
if @config.realtime_settings_updates_enabled?
|
|
87
|
-
@api_stream.handle("config-updated") do |event|
|
|
88
|
-
@config.logger.debug("Received server-sent event: config-updated")
|
|
89
|
-
settings_updated(event)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
@api_stream.start!
|
|
93
|
-
end
|
|
94
|
-
|
|
95
97
|
poll_for_setting_updates
|
|
96
98
|
|
|
97
99
|
@config.initial_heartbeat_delays.each do |heartbeat_delay|
|
|
@@ -197,7 +199,7 @@ module Aikido::Zen
|
|
|
197
199
|
# @return [void]
|
|
198
200
|
# @see Aikido::Zen::RuntimeSettings
|
|
199
201
|
def poll_for_setting_updates
|
|
200
|
-
@worker.every(@config.polling_interval) do
|
|
202
|
+
@worker.every(@config.polling_interval, run_now: true) do
|
|
201
203
|
if @api_client.should_fetch_settings?
|
|
202
204
|
if update_settings_from_runtime_config!(@api_client.fetch_runtime_config, reason: "after polling")
|
|
203
205
|
updated_settings!
|
|
@@ -29,10 +29,13 @@ module Aikido::Zen
|
|
|
29
29
|
@ipv6_ranges = []
|
|
30
30
|
|
|
31
31
|
ips.each do |ip|
|
|
32
|
+
range = ip.to_range
|
|
33
|
+
ip_int_range = (range.begin.to_i..range.end.to_i)
|
|
34
|
+
|
|
32
35
|
if ip.ipv4?
|
|
33
|
-
@ipv4_ranges <<
|
|
36
|
+
@ipv4_ranges << ip_int_range
|
|
34
37
|
elsif ip.ipv6?
|
|
35
|
-
@ipv6_ranges <<
|
|
38
|
+
@ipv6_ranges << ip_int_range
|
|
36
39
|
else
|
|
37
40
|
raise ArgumentError, "Unsupported IP address family: #{ip.inspect}"
|
|
38
41
|
end
|
|
@@ -50,10 +53,12 @@ module Aikido::Zen
|
|
|
50
53
|
native_ip = nativize_ip(ip)
|
|
51
54
|
return false if native_ip.nil?
|
|
52
55
|
|
|
56
|
+
ip_int = native_ip.to_i
|
|
57
|
+
|
|
53
58
|
if native_ip.ipv4?
|
|
54
|
-
ranges_cover?(@ipv4_ranges,
|
|
59
|
+
ranges_cover?(@ipv4_ranges, ip_int)
|
|
55
60
|
elsif native_ip.ipv6?
|
|
56
|
-
ranges_cover?(@ipv6_ranges,
|
|
61
|
+
ranges_cover?(@ipv6_ranges, ip_int)
|
|
57
62
|
else
|
|
58
63
|
raise ArgumentError, "Unsupported IP address family: #{ip.inspect}"
|
|
59
64
|
end
|
|
@@ -78,12 +83,12 @@ module Aikido::Zen
|
|
|
78
83
|
end
|
|
79
84
|
end
|
|
80
85
|
|
|
81
|
-
def ranges_cover?(ranges,
|
|
82
|
-
index = ranges.bsearch_index { |range| range.begin >
|
|
86
|
+
def ranges_cover?(ranges, ip_int)
|
|
87
|
+
index = ranges.bsearch_index { |range| range.begin > ip_int }
|
|
83
88
|
index = index ? index - 1 : ranges.size - 1
|
|
84
89
|
return false if index < 0
|
|
85
90
|
|
|
86
|
-
ranges[index].cover?(
|
|
91
|
+
ranges[index].cover?(ip_int)
|
|
87
92
|
end
|
|
88
93
|
end
|
|
89
94
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
3
5
|
module Aikido::Zen
|
|
4
6
|
# Stores the firewall configuration sourced from the Aikido dashboard. This
|
|
5
7
|
# object is updated by the Agent regularly.
|
|
@@ -11,7 +13,25 @@ module Aikido::Zen
|
|
|
11
13
|
#
|
|
12
14
|
# You can subscribe to changes with +#add_observer(object, func_name)+, which
|
|
13
15
|
# will call the function passing the settings as an argument
|
|
14
|
-
RuntimeSettings = Struct.new(
|
|
16
|
+
RuntimeSettings = Struct.new(
|
|
17
|
+
:updated_at,
|
|
18
|
+
:heartbeat_interval,
|
|
19
|
+
:endpoints,
|
|
20
|
+
:blocked_user_ids,
|
|
21
|
+
:bypassed_ips,
|
|
22
|
+
:received_any_stats,
|
|
23
|
+
:blocking_mode,
|
|
24
|
+
:blocked_user_agent_regexp,
|
|
25
|
+
:monitored_user_agent_regexp,
|
|
26
|
+
:user_agent_details,
|
|
27
|
+
:blocked_ip_lists,
|
|
28
|
+
:allowed_ip_lists,
|
|
29
|
+
:monitored_ip_lists,
|
|
30
|
+
:block_new_outbound,
|
|
31
|
+
:domains,
|
|
32
|
+
:excluded_user_ids_from_rate_limiting,
|
|
33
|
+
:enabled_features
|
|
34
|
+
) do
|
|
15
35
|
def initialize(*)
|
|
16
36
|
super
|
|
17
37
|
self.endpoints ||= RuntimeSettings::Endpoints.new
|
|
@@ -20,6 +40,7 @@ module Aikido::Zen
|
|
|
20
40
|
self.allowed_ip_lists ||= []
|
|
21
41
|
self.monitored_ip_lists ||= []
|
|
22
42
|
self.domains ||= RuntimeSettings::Domains.new
|
|
43
|
+
self.enabled_features ||= Set.new
|
|
23
44
|
end
|
|
24
45
|
|
|
25
46
|
# @!attribute [rw] updated_at
|
|
@@ -73,6 +94,9 @@ module Aikido::Zen
|
|
|
73
94
|
# @return [Array<String>, nil] the user IDs that should be skipped from
|
|
74
95
|
# rate limiting entirely.
|
|
75
96
|
|
|
97
|
+
# @!attribute [rw] enabled_features
|
|
98
|
+
# @return [Set<String>]
|
|
99
|
+
|
|
76
100
|
# Parse and interpret the JSON response from the core API with updated
|
|
77
101
|
# runtime settings, and apply the changes.
|
|
78
102
|
#
|
|
@@ -97,6 +121,8 @@ module Aikido::Zen
|
|
|
97
121
|
|
|
98
122
|
self.excluded_user_ids_from_rate_limiting = data["excludedUserIdsFromRateLimiting"]
|
|
99
123
|
|
|
124
|
+
self.enabled_features = Set.new(data["enabledFeatures"])
|
|
125
|
+
|
|
100
126
|
updated_at != last_updated_at
|
|
101
127
|
end
|
|
102
128
|
|
|
@@ -223,6 +249,14 @@ module Aikido::Zen
|
|
|
223
249
|
|
|
224
250
|
(!domain.nil? && domain.block?) || (domain.nil? && block_new_outbound)
|
|
225
251
|
end
|
|
252
|
+
|
|
253
|
+
private def enabled_feature?(feature)
|
|
254
|
+
enabled_features.include?(feature)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def realtime_settings_updates_enabled?
|
|
258
|
+
enabled_feature?("realtime_updates")
|
|
259
|
+
end
|
|
226
260
|
end
|
|
227
261
|
end
|
|
228
262
|
|
|
@@ -70,6 +70,10 @@ module Aikido::Zen
|
|
|
70
70
|
Helpers.scan(wrapped_request, connection, "request")
|
|
71
71
|
|
|
72
72
|
request.on(:response) do |response|
|
|
73
|
+
# HTTPX emits an ErrorResponse (no status/headers) through this
|
|
74
|
+
# same callback when the request fails at the transport level.
|
|
75
|
+
next if response.is_a?(::HTTPX::ErrorResponse)
|
|
76
|
+
|
|
73
77
|
Scanners::SSRFScanner.track_redirects(
|
|
74
78
|
request: wrapped_request,
|
|
75
79
|
response: Helpers.wrap_response(response)
|
data/lib/aikido/zen/version.rb
CHANGED
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.
|
|
4
|
+
version: 1.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aikido Security
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|