aikido-zen 1.0.0.pre.beta.1-x86_64-mingw-64 → 1.0.1.beta.3-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/.aikido +6 -0
- data/README.md +67 -83
- data/lib/aikido/zen/api_client.rb +6 -3
- data/lib/aikido/zen/config.rb +11 -2
- data/lib/aikido/zen/context/rails_request.rb +3 -1
- data/lib/aikido/zen/context.rb +4 -0
- data/lib/aikido/zen/internals.rb +48 -13
- data/lib/aikido/zen/libzen-v0.1.39-x86_64-mingw-64.dll +0 -0
- data/lib/aikido/zen/middleware/request_tracker.rb +6 -4
- data/lib/aikido/zen/rails_engine.rb +5 -9
- data/lib/aikido/zen/request/heuristic_router.rb +6 -0
- data/lib/aikido/zen/request/rails_router.rb +5 -0
- data/lib/aikido/zen/sink.rb +5 -0
- data/lib/aikido/zen/sinks/async_http.rb +35 -16
- data/lib/aikido/zen/sinks/curb.rb +52 -26
- data/lib/aikido/zen/sinks/em_http.rb +39 -25
- data/lib/aikido/zen/sinks/excon.rb +63 -45
- data/lib/aikido/zen/sinks/file.rb +67 -71
- data/lib/aikido/zen/sinks/http.rb +38 -19
- data/lib/aikido/zen/sinks/httpclient.rb +51 -22
- data/lib/aikido/zen/sinks/httpx.rb +37 -18
- data/lib/aikido/zen/sinks/kernel.rb +18 -57
- data/lib/aikido/zen/sinks/mysql2.rb +19 -7
- data/lib/aikido/zen/sinks/net_http.rb +37 -19
- data/lib/aikido/zen/sinks/patron.rb +41 -24
- data/lib/aikido/zen/sinks/pg.rb +50 -27
- data/lib/aikido/zen/sinks/resolv.rb +37 -16
- data/lib/aikido/zen/sinks/socket.rb +33 -17
- data/lib/aikido/zen/sinks/sqlite3.rb +31 -12
- data/lib/aikido/zen/sinks/trilogy.rb +19 -7
- data/lib/aikido/zen/sinks.rb +29 -20
- data/lib/aikido/zen/sinks_dsl.rb +226 -0
- data/lib/aikido/zen/version.rb +2 -2
- data/lib/aikido/zen.rb +42 -1
- data/placeholder/.gitignore +4 -0
- data/placeholder/README.md +11 -0
- data/placeholder/Rakefile +75 -0
- data/placeholder/lib/placeholder.rb.template +3 -0
- data/placeholder/placeholder.gemspec.template +20 -0
- data/tasklib/libzen.rake +70 -66
- metadata +17 -13
- data/CHANGELOG.md +0 -25
- data/lib/aikido/zen/libzen-v0.1.37.x86_64.dll +0 -0
- data/lib/aikido.rb +0 -3
@@ -1,28 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "../sink"
|
4
3
|
require_relative "../scanners/stored_ssrf_scanner"
|
5
4
|
require_relative "../scanners/ssrf_scanner"
|
6
5
|
|
7
6
|
module Aikido::Zen
|
8
7
|
module Sinks
|
9
8
|
module Resolv
|
9
|
+
def self.load_sinks!
|
10
|
+
# In stdlib but not always required
|
11
|
+
require "resolv"
|
12
|
+
|
13
|
+
::Resolv.prepend(ResolvExtensions)
|
14
|
+
end
|
15
|
+
|
10
16
|
SINK = Sinks.add("resolv", scanners: [
|
11
|
-
|
12
|
-
|
17
|
+
Scanners::StoredSSRFScanner,
|
18
|
+
Scanners::SSRFScanner
|
13
19
|
])
|
14
20
|
|
15
|
-
module
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
addresses << address
|
21
|
-
yield address
|
22
|
-
end
|
23
|
-
ensure
|
24
|
-
if (context = Aikido::Zen.current_context)
|
25
|
-
context["dns.lookups"] ||= Aikido::Zen::Scanners::SSRF::DNSLookups.new
|
21
|
+
module Helpers
|
22
|
+
def self.scan(name, addresses, operation)
|
23
|
+
context = Aikido::Zen.current_context
|
24
|
+
if context
|
25
|
+
context["dns.lookups"] ||= Scanners::SSRF::DNSLookups.new
|
26
26
|
context["dns.lookups"].add(name, addresses)
|
27
27
|
end
|
28
28
|
|
@@ -30,12 +30,33 @@ module Aikido::Zen
|
|
30
30
|
hostname: name,
|
31
31
|
addresses: addresses,
|
32
32
|
request: context && context["ssrf.request"],
|
33
|
-
operation:
|
33
|
+
operation: operation
|
34
34
|
)
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
module ResolvExtensions
|
39
|
+
def each_address(*args, **kwargs, &blk)
|
40
|
+
# each_address is defined "manually" because no sink method pattern
|
41
|
+
# is applicable.
|
42
|
+
|
43
|
+
name, = args
|
44
|
+
|
45
|
+
addresses = []
|
46
|
+
super(*args, **kwargs) do |address| # rubocop:disable Style/SuperArguments
|
47
|
+
addresses << address
|
48
|
+
blk.call(address)
|
49
|
+
end
|
50
|
+
ensure
|
51
|
+
# Ensure partial results are scanned.
|
52
|
+
|
53
|
+
Sinks::DSL.safe do
|
54
|
+
Helpers.scan(name, addresses, "lookup")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
37
58
|
end
|
38
59
|
end
|
39
60
|
end
|
40
61
|
|
41
|
-
|
62
|
+
Aikido::Zen::Sinks::Resolv.load_sinks!
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "socket"
|
4
|
+
require_relative "../scanners/stored_ssrf_scanner"
|
5
|
+
require_relative "../scanners/ssrf_scanner"
|
4
6
|
|
5
7
|
module Aikido::Zen
|
6
8
|
module Sinks
|
@@ -8,13 +10,17 @@ module Aikido::Zen
|
|
8
10
|
# there's no way to access the internal DNS resolution that happens in C
|
9
11
|
# when using the socket primitives.
|
10
12
|
module Socket
|
13
|
+
def self.load_sinks!
|
14
|
+
::IPSocket.singleton_class.prepend(Socket::IPSocketExtensions)
|
15
|
+
end
|
16
|
+
|
11
17
|
SINK = Sinks.add("socket", scanners: [
|
12
|
-
|
13
|
-
|
18
|
+
Scanners::StoredSSRFScanner,
|
19
|
+
Scanners::SSRFScanner
|
14
20
|
])
|
15
21
|
|
16
|
-
module
|
17
|
-
def self.
|
22
|
+
module Helpers
|
23
|
+
def self.scan(hostname, socket, operation)
|
18
24
|
# We're patching IPSocket.open(..) method.
|
19
25
|
# The IPSocket class hierarchy is:
|
20
26
|
# IPSocket
|
@@ -29,36 +35,46 @@ module Aikido::Zen
|
|
29
35
|
return unless socket.instance_of?(TCPSocket)
|
30
36
|
|
31
37
|
# ["AF_INET", 80, "10.0.0.1", "10.0.0.1"]
|
32
|
-
|
38
|
+
address_family, _port, _hostname, numeric_address = socket.peeraddr(:numeric)
|
33
39
|
|
34
40
|
# We only care about IPv4 (AF_INET) or IPv6 (AF_INET6) sockets
|
35
41
|
# This might be overcautious, since this is _IP_Socket, so you
|
36
42
|
# would expect it's only used for IP connections?
|
37
|
-
return unless addr_family.start_with?("AF_INET")
|
38
43
|
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
# Code coverage is disabled here because the then clause is a no-op,
|
45
|
+
# so there is nothing to cover.
|
46
|
+
# :nocov:
|
47
|
+
return unless address_family.start_with?("AF_INET")
|
48
|
+
# :nocov:
|
49
|
+
|
50
|
+
context = Aikido::Zen.current_context
|
51
|
+
if context
|
52
|
+
context["dns.lookups"] ||= Scanners::SSRF::DNSLookups.new
|
53
|
+
context["dns.lookups"].add(hostname, numeric_address)
|
42
54
|
end
|
43
55
|
|
44
56
|
SINK.scan(
|
45
57
|
hostname: hostname,
|
46
|
-
addresses: [
|
58
|
+
addresses: [numeric_address],
|
47
59
|
request: context && context["ssrf.request"],
|
48
|
-
operation:
|
60
|
+
operation: operation
|
49
61
|
)
|
50
62
|
end
|
63
|
+
end
|
51
64
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
IPSocketExtensions.scan_socket(name, socket)
|
65
|
+
module IPSocketExtensions
|
66
|
+
extend Sinks::DSL
|
56
67
|
|
57
|
-
|
68
|
+
sink_after :open do |socket, remote_host|
|
69
|
+
# Code coverage is disabled here because the tests are contrived and
|
70
|
+
# intentionally do not call open.
|
71
|
+
# :nocov:
|
72
|
+
Helpers.scan(remote_host, socket, "open")
|
73
|
+
# :nocov:
|
58
74
|
end
|
59
75
|
end
|
60
76
|
end
|
61
77
|
end
|
62
78
|
end
|
63
79
|
|
64
|
-
|
80
|
+
Aikido::Zen::Sinks::Socket.load_sinks!
|
@@ -1,30 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "../sink"
|
4
|
-
|
5
3
|
module Aikido::Zen
|
6
4
|
module Sinks
|
7
5
|
module SQLite3
|
6
|
+
def self.load_sinks!
|
7
|
+
if Aikido::Zen.satisfy "sqlite3", ">= 1.0"
|
8
|
+
require "sqlite3"
|
9
|
+
|
10
|
+
::SQLite3::Database.prepend(DatabaseExtensions)
|
11
|
+
::SQLite3::Statement.prepend(StatementExtensions)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
8
15
|
SINK = Sinks.add("sqlite3", scanners: [Scanners::SQLInjectionScanner])
|
9
16
|
|
10
|
-
module
|
11
|
-
def
|
12
|
-
SINK.scan(
|
17
|
+
module Helpers
|
18
|
+
def self.scan(query, operation)
|
19
|
+
SINK.scan(
|
20
|
+
query: query,
|
21
|
+
dialect: :sqlite,
|
22
|
+
operation: operation
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module DatabaseExtensions
|
28
|
+
extend Sinks::DSL
|
29
|
+
|
30
|
+
private
|
13
31
|
|
14
|
-
|
32
|
+
# SQLite3::Database#exec_batch is an internal native private method.
|
33
|
+
sink_before :exec_batch do |sql|
|
34
|
+
Helpers.scan(sql, "exec_batch")
|
15
35
|
end
|
16
36
|
end
|
17
37
|
|
18
|
-
module
|
19
|
-
|
20
|
-
SINK.scan(query: sql, dialect: :sqlite, operation: "statement.execute")
|
38
|
+
module StatementExtensions
|
39
|
+
extend Sinks::DSL
|
21
40
|
|
22
|
-
|
41
|
+
sink_before :initialize do |_db, sql|
|
42
|
+
Helpers.scan(sql, "statement.execute")
|
23
43
|
end
|
24
44
|
end
|
25
45
|
end
|
26
46
|
end
|
27
47
|
end
|
28
48
|
|
29
|
-
|
30
|
-
::SQLite3::Statement.prepend(Aikido::Zen::Sinks::SQLite3::StatementExt)
|
49
|
+
Aikido::Zen::Sinks::SQLite3.load_sinks!
|
@@ -1,21 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "../sink"
|
4
|
-
|
5
3
|
module Aikido::Zen
|
6
4
|
module Sinks
|
7
5
|
module Trilogy
|
6
|
+
def self.load_sinks!
|
7
|
+
if Aikido::Zen.satisfy "trilogy", ">= 2.0"
|
8
|
+
require "trilogy"
|
9
|
+
|
10
|
+
::Trilogy.prepend(TrilogyExtensions)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
8
14
|
SINK = Sinks.add("trilogy", scanners: [Scanners::SQLInjectionScanner])
|
9
15
|
|
10
|
-
module
|
11
|
-
def
|
12
|
-
SINK.scan(query: query, dialect: :mysql, operation:
|
16
|
+
module Helpers
|
17
|
+
def self.scan(query, operation)
|
18
|
+
SINK.scan(query: query, dialect: :mysql, operation: operation)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module TrilogyExtensions
|
23
|
+
extend Sinks::DSL
|
13
24
|
|
14
|
-
|
25
|
+
sink_before :query do |query|
|
26
|
+
Helpers.scan(query, "query")
|
15
27
|
end
|
16
28
|
end
|
17
29
|
end
|
18
30
|
end
|
19
31
|
end
|
20
32
|
|
21
|
-
|
33
|
+
Aikido::Zen::Sinks::Trilogy.load_sinks!
|
data/lib/aikido/zen/sinks.rb
CHANGED
@@ -1,30 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
# Code coverage is disabled in this file because it is environment-specific and
|
4
|
+
# not intended to be tested directly.
|
5
|
+
# :nocov:
|
4
6
|
|
5
|
-
require_relative "
|
7
|
+
require_relative "sink"
|
8
|
+
require_relative "sinks_dsl"
|
6
9
|
|
7
10
|
require_relative "sinks/action_controller" if defined?(::ActionController)
|
8
|
-
require_relative "sinks/file" if defined?(::File)
|
9
11
|
|
10
12
|
# Sadly, in ruby versions lower than 3.0, it's not possible to patch the
|
11
13
|
# Kernel module because how the `prepend` method is applied
|
12
14
|
# (https://stackoverflow.com/questions/78110397/prepend-kernel-module-function-globally#comment137713906_78112924)
|
13
|
-
if RUBY_VERSION >= "3.0"
|
14
|
-
|
15
|
-
|
16
|
-
require_relative "sinks/
|
17
|
-
require_relative "sinks/
|
18
|
-
require_relative "sinks/
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
require_relative "sinks/
|
23
|
-
|
15
|
+
require_relative "sinks/kernel" if RUBY_VERSION >= "3.0"
|
16
|
+
|
17
|
+
require_relative "sinks/file"
|
18
|
+
require_relative "sinks/socket"
|
19
|
+
require_relative "sinks/resolv"
|
20
|
+
require_relative "sinks/net_http"
|
21
|
+
|
22
|
+
# http.rb aims to support and is tested against Ruby 3.0+:
|
23
|
+
# https://github.com/httprb/http?tab=readme-ov-file#supported-ruby-versions
|
24
|
+
require_relative "sinks/http" if RUBY_VERSION >= "3.0"
|
25
|
+
|
26
|
+
require_relative "sinks/httpx"
|
27
|
+
require_relative "sinks/httpclient"
|
28
|
+
require_relative "sinks/excon"
|
29
|
+
require_relative "sinks/curb"
|
30
|
+
require_relative "sinks/patron"
|
24
31
|
require_relative "sinks/typhoeus" if defined?(::Typhoeus)
|
25
|
-
require_relative "sinks/async_http"
|
26
|
-
require_relative "sinks/em_http"
|
27
|
-
require_relative "sinks/mysql2"
|
28
|
-
require_relative "sinks/pg"
|
29
|
-
require_relative "sinks/sqlite3"
|
30
|
-
require_relative "sinks/trilogy"
|
32
|
+
require_relative "sinks/async_http"
|
33
|
+
require_relative "sinks/em_http"
|
34
|
+
require_relative "sinks/mysql2"
|
35
|
+
require_relative "sinks/pg"
|
36
|
+
require_relative "sinks/sqlite3"
|
37
|
+
require_relative "sinks/trilogy"
|
38
|
+
|
39
|
+
# :nocov:
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aikido::Zen
|
4
|
+
module Sinks
|
5
|
+
module DSL
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# In the context of `Aikido::Zen::Sinks::DSL`, the terms safe and presafe
|
9
|
+
# are defined as follows:
|
10
|
+
#
|
11
|
+
# safe: the desired state for a sink, particularly with respect to rescue.
|
12
|
+
#
|
13
|
+
# A sink is considered safe when unintended errors in the sink are handled,
|
14
|
+
# and-so are prevented from disrupting the operation of the original method
|
15
|
+
# (by raising unintended errors).
|
16
|
+
#
|
17
|
+
# presafe: the default state of a sink, particularly with respect to rescue.
|
18
|
+
#
|
19
|
+
# A sink is in the presafe state before and while unintended errors in the
|
20
|
+
# sink are not handled.
|
21
|
+
#
|
22
|
+
# Sink methods (like all methods) are in the presafe state when defined and
|
23
|
+
# become safe when unexpected errors cannot cause harm. The `safe` method
|
24
|
+
# is used to establish a safe state for the duration of the block executed.
|
25
|
+
# It is sometimes useful to be able to reestablish the presafe safe while
|
26
|
+
# inside a `safe` block; the `presafe` method allows this.
|
27
|
+
#
|
28
|
+
# Methods that contain the term presafe in their name should be used with
|
29
|
+
# appropriate care and understanding.
|
30
|
+
#
|
31
|
+
# IMPORTANT: All sinks should be safe!
|
32
|
+
#
|
33
|
+
# While this DSL proves useful for defining safe sink methods that follow
|
34
|
+
# common patterns, there are exceptions. It is always possible to define
|
35
|
+
# sink methods without using this DSL, but this should only be done when
|
36
|
+
# absolutely necessary. The sink methods defined using this DSL are safe,
|
37
|
+
# unless explicitly declared presafe.
|
38
|
+
#
|
39
|
+
# IMPORTANT: No sinks should be presafe in production!
|
40
|
+
#
|
41
|
+
# We are all responsible for ensuring that the sinks we implement are safe
|
42
|
+
# for production use. This DSL is only here to assist, by taking care of
|
43
|
+
# delicate edge cases and reducing the space for errors.
|
44
|
+
#
|
45
|
+
# When writing sink methods manually, some principles should be considered,
|
46
|
+
# to ensure safety for production:
|
47
|
+
#
|
48
|
+
# 1. Sink methods should ensure that the original method is always called,
|
49
|
+
# passing all parameters (positional, keyword, and block) exactly as they
|
50
|
+
# were passed to the original method, and return the result returned by
|
51
|
+
# the original method exactly as it was returned by the original method.
|
52
|
+
# (Unless intervention is required.)
|
53
|
+
#
|
54
|
+
# 2. Sink methods should not predict the signature of the original method,
|
55
|
+
# and-so restrict it from varying. The original method implementation is
|
56
|
+
# the sole and ultimate reference for its own behavior. We are observers
|
57
|
+
# (unless intervention is required).
|
58
|
+
#
|
59
|
+
# 3. Unexpected errors that are encountered in sink methods should not be
|
60
|
+
# capable of interfering with or preventing the normal operation of the
|
61
|
+
# original method. This includes but is not limited to exceptions that
|
62
|
+
# that may be raised when the sink method is called. Safe sink methods
|
63
|
+
# should return control to their caller (unless intervention is required).
|
64
|
+
#
|
65
|
+
# These are the guidelines adhered to by the `Aikido::Zen::Sinks::DSL`.
|
66
|
+
|
67
|
+
# The error with an original error as its cause to re-raise in `safe`.
|
68
|
+
class PresafeError < StandardError
|
69
|
+
end
|
70
|
+
|
71
|
+
# Safely execute the given block
|
72
|
+
#
|
73
|
+
# All standard errors are suppressed except `Aikido::Zen::UnderAttackError`s.
|
74
|
+
# This ensures that unexpected errors do not interrupt the execution of the
|
75
|
+
# original method, while all detected attacks are raised.
|
76
|
+
#
|
77
|
+
# When an error is wrapped in `PresafeError` the original error is reraised.
|
78
|
+
#
|
79
|
+
# @yield the block to execute
|
80
|
+
def safe
|
81
|
+
yield
|
82
|
+
rescue Aikido::Zen::UnderAttackError
|
83
|
+
raise
|
84
|
+
rescue PresafeError => err
|
85
|
+
raise err.cause
|
86
|
+
rescue => err
|
87
|
+
Aikido::Zen.config.logger.debug("[safe] #{err.class}: #{err.message}")
|
88
|
+
end
|
89
|
+
|
90
|
+
# Presafely execute the given block
|
91
|
+
#
|
92
|
+
# Safely wrap standard errors in `PresafeError` so that the original error is
|
93
|
+
# reraised when rescued in `safe`.
|
94
|
+
#
|
95
|
+
# @yield the block to execute
|
96
|
+
def presafe
|
97
|
+
yield
|
98
|
+
rescue => err
|
99
|
+
raise PresafeError, cause: err
|
100
|
+
end
|
101
|
+
|
102
|
+
# Define a method `method_name` that presafely executes the given block before
|
103
|
+
# the original method.
|
104
|
+
#
|
105
|
+
# @param method_name [Symbol, String] the name of the method to define
|
106
|
+
# @yield the block to execute before the original method
|
107
|
+
# @yieldparam args [Array] the positional arguments passed to the original method
|
108
|
+
# @yieldparam kwargs [Hash] the keyword arguments passed to the original method
|
109
|
+
#
|
110
|
+
# @return [void]
|
111
|
+
def presafe_sink_before(method_name, &block)
|
112
|
+
define_method(method_name) do |*args, **kwargs, &blk|
|
113
|
+
instance_exec(*args, **kwargs, &block)
|
114
|
+
super(*args, **kwargs, &blk)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Define a method `method_name` that safely executes the given block before
|
119
|
+
# the original method.
|
120
|
+
#
|
121
|
+
# @param method_name [Symbol, String] the name of the method to define
|
122
|
+
# @yield the block to execute before the original method
|
123
|
+
# @yieldparam args [Array] the positional arguments passed to the original method
|
124
|
+
# @yieldparam kwargs [Hash] the keyword arguments passed to the original method
|
125
|
+
#
|
126
|
+
# @return [void]
|
127
|
+
#
|
128
|
+
# @note the block is executed within `safe` to handle errors safely; the original method is executed outside of `safe` to preserve the original behavior
|
129
|
+
def sink_before(method_name, &block)
|
130
|
+
presafe_sink_before(method_name) do |*args, **kwargs|
|
131
|
+
DSL.safe do
|
132
|
+
instance_exec(*args, **kwargs, &block)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Define a method `method_name` that presafely executes the given block after
|
138
|
+
# the original method.
|
139
|
+
#
|
140
|
+
# @param method_name [Symbol, String] the name of the method to define
|
141
|
+
# @yield the block to execute after the original method
|
142
|
+
# @yieldparam result [Object] the result returned by the original method
|
143
|
+
# @yieldparam args [Array] the positional arguments passed to the original method
|
144
|
+
# @yieldparam kwargs [Hash] the keyword arguments passed to the original method
|
145
|
+
#
|
146
|
+
# @return [void]
|
147
|
+
def presafe_sink_after(method_name, &block)
|
148
|
+
define_method(method_name) do |*args, **kwargs, &blk|
|
149
|
+
result = super(*args, **kwargs, &blk)
|
150
|
+
instance_exec(result, *args, **kwargs, &block)
|
151
|
+
result
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Define a method `method_name` that safely executes the given block after
|
156
|
+
# the original method.
|
157
|
+
#
|
158
|
+
# @param method_name [Symbol, String] the name of the method to define
|
159
|
+
# @yield the block to execute after the original method
|
160
|
+
# @yieldparam result [Object] the result returned by the original method
|
161
|
+
# @yieldparam args [Array] the positional arguments passed to the original method
|
162
|
+
# @yieldparam kwargs [Hash] the keyword arguments passed to the original method
|
163
|
+
#
|
164
|
+
# @return [void]
|
165
|
+
#
|
166
|
+
# @note the block is executed within `safe` to handle errors safely; the original method is executed outside of `safe` to preserve the original behavior
|
167
|
+
def sink_after(method_name, &block)
|
168
|
+
presafe_sink_after(method_name) do |result, *args, **kwargs|
|
169
|
+
DSL.safe do
|
170
|
+
instance_exec(result, *args, **kwargs, &block)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Define a method `method_name` that presafely executes the given block around
|
176
|
+
# the original method.
|
177
|
+
#
|
178
|
+
# @param method_name [Symbol, String] the name of the method to define
|
179
|
+
# @yield the block to execute around the original method
|
180
|
+
# @yieldparam super_call [Proc] the proc that calls the original method
|
181
|
+
# @yieldparam args [Array] the positional arguments passed to the original method
|
182
|
+
# @yieldparam kwargs [Hash] the keyword arguments passed to the original method
|
183
|
+
#
|
184
|
+
# @return [void]
|
185
|
+
def presafe_sink_around(method_name, &block)
|
186
|
+
define_method(method_name) do |*args, **kwargs, &blk|
|
187
|
+
result = nil
|
188
|
+
super_call = proc do
|
189
|
+
result = super(*args, **kwargs, &blk)
|
190
|
+
end
|
191
|
+
instance_exec(super_call, *args, **kwargs, &block)
|
192
|
+
result
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Define a method `method_name` that safely executes the given block around
|
197
|
+
# the original method.
|
198
|
+
#
|
199
|
+
# @param method_name [Symbol, String] the name of the method to define
|
200
|
+
# @yield the block to execute around the original method
|
201
|
+
# @yieldparam super_call [Proc] the proc that calls the original method
|
202
|
+
# @yieldparam args [Array] the positional arguments passed to the original method
|
203
|
+
# @yieldparam kwargs [Hash] the keyword arguments passed to the original method
|
204
|
+
#
|
205
|
+
# @return [void]
|
206
|
+
#
|
207
|
+
# @note the block is executed within `safe` to handle errors safely; the original method is executed within `presafe` to preserve the original behavior
|
208
|
+
# @note if the block does not call `super_call`, the original method is called automatically after the block is executed
|
209
|
+
def sink_around(method_name, &block)
|
210
|
+
presafe_sink_around(method_name) do |presafe_super_call, *args, **kwargs|
|
211
|
+
super_called = false
|
212
|
+
super_call = proc do
|
213
|
+
super_called = true
|
214
|
+
DSL.presafe do
|
215
|
+
presafe_super_call.call
|
216
|
+
end
|
217
|
+
end
|
218
|
+
DSL.safe do
|
219
|
+
instance_exec(super_call, *args, **kwargs, &block)
|
220
|
+
end
|
221
|
+
presafe_super_call.call unless super_called
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/lib/aikido/zen/version.rb
CHANGED
data/lib/aikido/zen.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# IMPORTANT: Any files that load sinks or start the Aikido Agent should
|
4
|
+
# be required in `Aikido::Zen.protect!`.
|
5
|
+
|
3
6
|
require_relative "zen/version"
|
4
7
|
require_relative "zen/errors"
|
5
8
|
require_relative "zen/actor"
|
@@ -20,10 +23,48 @@ require_relative "zen/outbound_connection_monitor"
|
|
20
23
|
require_relative "zen/runtime_settings"
|
21
24
|
require_relative "zen/rate_limiter"
|
22
25
|
require_relative "zen/scanners"
|
23
|
-
require_relative "zen/rails_engine" if defined?(::Rails)
|
24
26
|
|
25
27
|
module Aikido
|
26
28
|
module Zen
|
29
|
+
# Enable protection. Until this method is called no sinks are loaded
|
30
|
+
# and the Aikido Agent does not start.
|
31
|
+
#
|
32
|
+
# @return [void]
|
33
|
+
def self.protect!
|
34
|
+
if config.disabled?
|
35
|
+
config.logger.warn("Zen has been disabled and will not run.")
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
# IMPORTANT: Any files that load sinks or start the Aikido Agent
|
40
|
+
# should be required here only.
|
41
|
+
|
42
|
+
if Aikido::Zen.satisfy "rails", ">= 7.0"
|
43
|
+
require_relative "zen/rails_engine"
|
44
|
+
end
|
45
|
+
|
46
|
+
if Aikido::Zen::Sinks.registry.empty?
|
47
|
+
warn "Zen could not find any supported libraries or frameworks. Visit https://github.com/AikidoSec/firewall-ruby for more information."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @!visibility private
|
52
|
+
# Returns whether the loaded gem specification satisfies the listed requirements.
|
53
|
+
#
|
54
|
+
# Returns false if the gem specification is not loaded.
|
55
|
+
#
|
56
|
+
# @param name [String] the gem name
|
57
|
+
# @param requirements [Array<String>] a variable number of gem requirement strings
|
58
|
+
#
|
59
|
+
# @return [Boolean] true if the gem specification is loaded and all gem requirements are satisfied
|
60
|
+
def self.satisfy(name, *requirements)
|
61
|
+
spec = Gem.loaded_specs[name]
|
62
|
+
|
63
|
+
return false if spec.nil?
|
64
|
+
|
65
|
+
Gem::Requirement.new(*requirements).satisfied_by?(spec.version)
|
66
|
+
end
|
67
|
+
|
27
68
|
# @return [Aikido::Zen::Config] the agent configuration.
|
28
69
|
def self.config
|
29
70
|
@config ||= Config.new
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Security Placeholder
|
2
|
+
|
3
|
+
This gem has been published by [Aikido Security](https://aikido.dev) to help prevent supply chain attacks and protect the integrity of the Ruby ecosystem.
|
4
|
+
|
5
|
+
It is **not intended for direct use** and contains no functional code.
|
6
|
+
|
7
|
+
If you are looking for the actual library, please use [`aikido-zen`](https://rubygems.org/gems/aikido-zen).
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
This package exists solely for security purposes. For more information, visit [aikido.dev](https://aikido.dev).
|