Module: Aikido::Zen

Defined in:
lib/aikido/zen.rb,
lib/aikido/zen/sql.rb,
lib/aikido/zen/scan.rb,
lib/aikido/zen/sink.rb,
lib/aikido/zen/actor.rb,
lib/aikido/zen/agent.rb,
lib/aikido/zen/cache.rb,
lib/aikido/zen/event.rb,
lib/aikido/zen/route.rb,
lib/aikido/zen/attack.rb,
lib/aikido/zen/config.rb,
lib/aikido/zen/errors.rb,
lib/aikido/zen/worker.rb,
lib/aikido/zen/context.rb,
lib/aikido/zen/helpers.rb,
lib/aikido/zen/package.rb,
lib/aikido/zen/payload.rb,
lib/aikido/zen/request.rb,
lib/aikido/zen/version.rb,
lib/aikido/zen/sinks/pg.rb,
lib/aikido/zen/collector.rb,
lib/aikido/zen/internals.rb,
lib/aikido/zen/sinks_dsl.rb,
lib/aikido/zen/api_client.rb,
lib/aikido/zen/sinks/curb.rb,
lib/aikido/zen/sinks/file.rb,
lib/aikido/zen/sinks/http.rb,
lib/aikido/zen/attack_wave.rb,
lib/aikido/zen/sinks/excon.rb,
lib/aikido/zen/sinks/httpx.rb,
lib/aikido/zen/system_info.rb,
lib/aikido/zen/rails_engine.rb,
lib/aikido/zen/rate_limiter.rb,
lib/aikido/zen/sinks/kernel.rb,
lib/aikido/zen/sinks/mysql2.rb,
lib/aikido/zen/sinks/patron.rb,
lib/aikido/zen/sinks/resolv.rb,
lib/aikido/zen/sinks/socket.rb,
lib/aikido/zen/sinks/em_http.rb,
lib/aikido/zen/sinks/sqlite3.rb,
lib/aikido/zen/sinks/trilogy.rb,
lib/aikido/zen/idor/protector.rb,
lib/aikido/zen/request/schema.rb,
lib/aikido/zen/sinks/net_http.rb,
lib/aikido/zen/sinks/typhoeus.rb,
lib/aikido/zen/synchronizable.rb,
lib/aikido/zen/collector/event.rb,
lib/aikido/zen/collector/hosts.rb,
lib/aikido/zen/collector/stats.rb,
lib/aikido/zen/collector/users.rb,
lib/aikido/zen/collector/routes.rb,
lib/aikido/zen/runtime_settings.rb,
lib/aikido/zen/sinks/async_http.rb,
lib/aikido/zen/sinks/httpclient.rb,
lib/aikido/zen/background_worker.rb,
lib/aikido/zen/capped_collections.rb,
lib/aikido/zen/attack_wave/helpers.rb,
lib/aikido/zen/outbound_connection.rb,
lib/aikido/zen/rate_limiter/bucket.rb,
lib/aikido/zen/rate_limiter/result.rb,
lib/aikido/zen/collector/sink_stats.rb,
lib/aikido/zen/context/rack_request.rb,
lib/aikido/zen/idor/analysis_result.rb,
lib/aikido/zen/rate_limiter/breaker.rb,
lib/aikido/zen/request/rails_router.rb,
lib/aikido/zen/context/rails_request.rb,
lib/aikido/zen/scanners/ssrf_scanner.rb,
lib/aikido/zen/request/schema/builder.rb,
lib/aikido/zen/runtime_settings/ip_set.rb,
lib/aikido/zen/sinks/action_controller.rb,
lib/aikido/zen/agent/heartbeats_manager.rb,
lib/aikido/zen/middleware/fork_detector.rb,
lib/aikido/zen/request/heuristic_router.rb,
lib/aikido/zen/runtime_settings/domains.rb,
lib/aikido/zen/runtime_settings/ip_list.rb,
lib/aikido/zen/middleware/context_setter.rb,
lib/aikido/zen/middleware/rack_throttler.rb,
lib/aikido/zen/request/schema/definition.rb,
lib/aikido/zen/scanners/ssrf/dns_lookups.rb,
lib/aikido/zen/middleware/ip_list_checker.rb,
lib/aikido/zen/middleware/request_tracker.rb,
lib/aikido/zen/runtime_settings/endpoints.rb,
lib/aikido/zen/middleware/attack_protector.rb,
lib/aikido/zen/request/schema/auth_schemas.rb,
lib/aikido/zen/request/schema/empty_schema.rb,
lib/aikido/zen/scanners/stored_ssrf_scanner.rb,
lib/aikido/zen/middleware/user_agent_checker.rb,
lib/aikido/zen/request/schema/auth_discovery.rb,
lib/aikido/zen/scanners/sql_injection_scanner.rb,
lib/aikido/zen/scanners/path_traversal/helpers.rb,
lib/aikido/zen/scanners/path_traversal_scanner.rb,
lib/aikido/zen/middleware/attack_wave_protector.rb,
lib/aikido/zen/runtime_settings/domain_settings.rb,
lib/aikido/zen/scanners/shell_injection_scanner.rb,
lib/aikido/zen/scanners/ssrf/private_ip_checker.rb,
lib/aikido/zen/middleware/allowed_address_checker.rb,
lib/aikido/zen/runtime_settings/protection_settings.rb,
lib/aikido/zen/runtime_settings/rate_limit_settings.rb

Defined Under Namespace

Modules: AttackWave, Attacks, DetachedAgent, Events, Helpers, IDOR, Internals, Middleware, Rails, SQL, Scanners, Sinks Classes: APIClient, APIError, Actor, Agent, Attack, BackgroundWorker, Cache, CacheEntry, CappedMap, CappedSet, Collector, Config, Context, DecodeError, DetachedAgentError, Event, InternalsError, NetworkError, OutboundConnection, OutboundConnectionBlockedError, Package, PathTraversalError, Payload, RailsEngine, RateLimitedError, RateLimiter, Request, Route, RuntimeSettings, SQLInjectionError, SSRFDetectedError, Scan, ShellInjectionError, Sink, SystemInfo, UnderAttackError, Worker

Constant Summary collapse

LOCK =

‘agent` and `detached_agent` are started on the first method call. A mutex controls thread execution to prevent multiple attempts.

Mutex.new
VERSION =
"1.4.0"
LIBZEN_VERSION =

The version of libzen_internals that we build against.

"0.1.61"

Class Method Summary collapse

Class Method Details

.Actor(actor) ⇒ Object .Actor(data) ⇒ Object

Converts an object into an Actor for reporting back to the Aikido Dashboard.

Overloads:

  • .Actor(actor) ⇒ Object

    Returns Aikido::Zen::Actor.

    Parameters:

    • actor (#to_aikido_actor)

      anything that implements #to_aikido_actor will have that method called and its value returned.

    Returns:

    • Aikido::Zen::Actor

  • .Actor(data) ⇒ Object

    Returns Aikido::Zen::Actor.

    Parameters:

    • data (Hash<Symbol, String>)

    Options Hash (data):

    • :id (String)

      a unique identifier for this user.

    • :name (String, nil)

      an optional name to display in the UI.

    Returns:

    • Aikido::Zen::Actor



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/aikido/zen/actor.rb', line 19

def self.Actor(data)
  return if data.nil?
  return data.to_aikido_actor if data.respond_to?(:to_aikido_actor)

  attrs = {}
  if data.respond_to?(:to_hash)
    attrs = data.to_hash
      .slice("id", "name", :id, :name)
      .compact
      .transform_keys(&:to_sym)
      .transform_values(&:to_s)
  else
    return nil
  end

  return nil if attrs[:id].nil? || attrs[:id].to_s.strip.empty?

  Actor.new(**attrs)
end

.attack_wave_detectorAikido::Zen::AttackWave::Detector

Returns the attack wave detector.

Returns:



219
220
221
# File 'lib/aikido/zen.rb', line 219

def self.attack_wave_detector
  @attack_wave_detector ||= AttackWave::Detector.new
end

.blocking_mode?Boolean

Returns whether the Aikido agent is currently blocking requests. Blocking mode is configured at startup and can be controlled through the Aikido dashboard at runtime.

Returns:

  • (Boolean)

    whether the Aikido agent is currently blocking requests. Blocking mode is configured at startup and can be controlled through the Aikido dashboard at runtime.



91
92
93
94
95
96
# File 'lib/aikido/zen.rb', line 91

def self.blocking_mode?
  blocking_mode = runtime_settings.blocking_mode
  return blocking_mode unless blocking_mode.nil?

  config.blocking_mode
end

.check_and_handle_forkObject



357
358
359
# File 'lib/aikido/zen.rb', line 357

def check_and_handle_fork
  handle_fork if forked?
end

.collectorObject

Manages runtime metrics extracted from your app, which are uploaded to the Aikido servers if configured to do so.



106
107
108
# File 'lib/aikido/zen.rb', line 106

def self.collector
  @collector ||= Collector.new
end

.configAikido::Zen::Config

Returns the agent configuration.

Returns:



74
75
76
# File 'lib/aikido/zen.rb', line 74

def self.config
  @config ||= Config.new
end

.current_contextAikido::Zen::Context?

Gets the current context object that holds all information about the current request.

Returns:



114
115
116
# File 'lib/aikido/zen.rb', line 114

def self.current_context
  Fiber.current.aikido_current_context
end

.current_context=(context) ⇒ Aikido::Zen::Context?

Sets the current context object that holds all information about the current request, or nil to clear the current context.

Parameters:

Returns:



123
124
125
# File 'lib/aikido/zen.rb', line 123

def self.current_context=(context)
  Fiber.current.aikido_current_context = context
end

.detached_agentObject



327
328
329
# File 'lib/aikido/zen.rb', line 327

def self.detached_agent
  @detached_agent ||= DetachedAgent::Agent.new
end

.detached_agent_serverObject



331
332
333
# File 'lib/aikido/zen.rb', line 331

def self.detached_agent_server
  @detached_agent_server ||= DetachedAgent::Server.start
end

.enable_idor_protectionvoid

This method returns an undefined value.

Enable IDOR protection for the current context.



243
244
245
246
247
248
# File 'lib/aikido/zen.rb', line 243

def self.enable_idor_protection
  context = current_context
  return unless context

  context.idor_protection_enabled = true
end

.forked?Boolean

Returns:

  • (Boolean)


361
362
363
364
365
# File 'lib/aikido/zen.rb', line 361

def forked?
  pid_changed = Process.pid != @pid
  @pid = Process.pid
  pid_changed
end

.handle_forkObject



367
368
369
# File 'lib/aikido/zen.rb', line 367

def handle_fork
  @detached_agent&.handle_fork
end

.idor_protect(sql, dialect_name, params = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • sql (String)
  • dialect (Symbol)
  • params (Array, nil) (defaults to: nil)

Raises:



233
234
235
236
237
238
# File 'lib/aikido/zen.rb', line 233

def self.idor_protect(sql, dialect_name, params = nil)
  context = current_context
  return unless context

  idor_protector.protect(sql, dialect_name, params, context)
end

.idor_protectorAikido::Zen::IDOR::Protector



224
225
226
# File 'lib/aikido/zen.rb', line 224

def self.idor_protector
  @idor_protector ||= IDOR::Protector.new
end

.middleware_installed!Object

Marks that the Zen middleware was installed properly

Returns:

  • void



286
287
288
# File 'lib/aikido/zen.rb', line 286

def self.middleware_installed!
  collector.middleware_installed!
end

.protect!void

This method returns an undefined value.

Enable protection. Until this method is called no sinks are loaded and the Aikido Agent does not start.

This method should be called only once, in the application after the initialization process is complete.



42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/aikido/zen.rb', line 42

def self.protect!
  if config.disabled?
    config.logger.warn("Zen has been disabled and will not run")
    return
  end

  unless load_sources! && load_sinks!
    config.logger.warn("Zen could not find any supported libraries or frameworks. Visit https://github.com/AikidoSec/firewall-ruby for more information.")
    return
  end

  middleware_installed!
end

.runtime_settingsAikido::Zen::RuntimeSettings

Returns the firewall configuration sourced from your Aikido dashboard. This is periodically polled for updates.

Returns:

  • (Aikido::Zen::RuntimeSettings)

    the firewall configuration sourced from your Aikido dashboard. This is periodically polled for updates.



80
81
82
# File 'lib/aikido/zen.rb', line 80

def self.runtime_settings
  @runtime_settings ||= RuntimeSettings.new
end

.runtime_settings=(settings) ⇒ Object



84
85
86
# File 'lib/aikido/zen.rb', line 84

def self.runtime_settings=(settings)
  @runtime_settings = settings
end

.set_tenant_id(tenant_id) ⇒ void

This method returns an undefined value.

Set the tenant ID for the current request.

Parameters:

  • tenant_id (Integer, String, nil)


254
255
256
257
258
259
# File 'lib/aikido/zen.rb', line 254

def self.set_tenant_id(tenant_id)
  context = current_context
  return unless context

  context.request.tenant_id = tenant_id
end

.start!Object



340
341
342
343
344
345
346
347
348
349
# File 'lib/aikido/zen.rb', line 340

def start!
  return unless start?

  @pid = Process.pid

  LOCK.synchronize do
    agent
    detached_agent_server
  end
end

.start?Boolean

Returns:

  • (Boolean)


351
352
353
354
355
# File 'lib/aikido/zen.rb', line 351

def start?
  !config.api_token.nil? ||
    config.blocking_mode? ||
    config.debugging?
end

.system_infoObject

Gets information about the current system configuration, which is sent to the server along with any events.



100
101
102
# File 'lib/aikido/zen.rb', line 100

def self.system_info
  @system_info ||= SystemInfo.new
end

.track_attack_wave(_attack_wave) ⇒ void

This method returns an undefined value.

Track statistics about an attack wave the app is handling.

Parameters:



161
162
163
# File 'lib/aikido/zen.rb', line 161

def self.track_attack_wave(_attack_wave)
  collector.track_attack_wave(being_blocked: false)
end

.track_discovered_route(request) ⇒ void

This method returns an undefined value.

Track statistics about a route that the app has discovered.

Parameters:



169
170
171
# File 'lib/aikido/zen.rb', line 169

def self.track_discovered_route(request)
  collector.track_route(request)
end

.track_ip_list(ip_list_keys) ⇒ void

This method returns an undefined value.

Track blocked and monitored IP lists.

Parameters:

  • ip_list_keys (Array<String>, nil)

    the IP list keys from matching runtime firewall list IP lists.



153
154
155
# File 'lib/aikido/zen.rb', line 153

def self.track_ip_list(ip_list_keys)
  collector.track_ip_list(ip_list_keys)
end

.track_outbound(connection) ⇒ void

This method returns an undefined value.

Tracks a network connection made to an external service.

Parameters:



177
178
179
# File 'lib/aikido/zen.rb', line 177

def self.track_outbound(connection)
  collector.track_outbound(connection)
end

.track_rate_limited_request(_request) ⇒ Object



135
136
137
# File 'lib/aikido/zen.rb', line 135

def self.track_rate_limited_request(_request)
  collector.track_rate_limited_request
end

.track_request(_request) ⇒ void

This method returns an undefined value.

Track statistics about an HTTP request the app is handling.

Parameters:



131
132
133
# File 'lib/aikido/zen.rb', line 131

def self.track_request(_request)
  collector.track_request
end

.track_scan(scan) ⇒ void

This method returns an undefined value.

Track statistics about the result of a Sink’s scan, and report it as an Attack if one is detected.

Parameters:

Raises:



188
189
190
191
# File 'lib/aikido/zen.rb', line 188

def self.track_scan(scan)
  collector.track_scan(scan)
  agent.handle_attack(scan.attack) if scan.attack?
end

.track_user(user) ⇒ void Also known as: set_user

This method returns an undefined value.

Track the user making the current request.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/aikido/zen.rb', line 197

def self.track_user(user)
  return if config.disabled?

  if (actor = Aikido::Zen::Actor(user))
    collector.track_user(actor)
    current_context.request.actor = actor if current_context
  else
    config.logger.warn(format(<<~LOG, obj: user))
      Incompatible object sent to track_user: %<obj>p

      The object must either implement #to_aikido_actor, or be a Hash with
      an :id (or "id") and, optionally, a :name (or "name") key.
    LOG
  end
end

.track_user_agent(user_agent_keys) ⇒ void

This method returns an undefined value.

Track blocked and monitored user agents.

Parameters:

  • user_agent_keys (Array<String>, nil)

    the user agent keys from matching runtime firewall list user agent details.



144
145
146
# File 'lib/aikido/zen.rb', line 144

def self.track_user_agent(user_agent_keys)
  collector.track_user_agent(user_agent_keys)
end

.without_idor_protection { ... } ⇒ Object

Execute a block with the IDOR protection disabled.

Yields:

  • the block to execute with the IDOR protection disabled.

Returns:

  • (Object)

    the result of the block

Raises:

  • (ArgumentError)

    if no block is given



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/aikido/zen.rb', line 266

def self.without_idor_protection
  raise ArgumentError, "block required" unless block_given?

  context = current_context

  if context
    begin
      original_idor_protection_enabled = context.idor_protection_enabled
      context.idor_protection_enabled = false
      yield
    ensure
      context.idor_protection_enabled = original_idor_protection_enabled
    end
  else
    yield
  end
end