Class: SecurityBox::Sandbox

Inherits:
Object
  • Object
show all
Defined in:
lib/security_box/sandbox.rb

Overview

:oneshot mode sandbox — one wasm instance per #eval.

The sandbox itself is stateless and cheap: it holds references to the shared runtime artifacts (Engine + compiled Module, both memoized in Runtime) and a WASI Linker. Each #eval runs the shared EvalRun core: an exclusive tmpdir mounted as /work, a per-eval token, fuel + epoch deadlines, and a validated result envelope.

Thread safety: Engine/Module/Linker are thread-safe and the eval core is self-contained, so a single Sandbox can be used from multiple threads (evals serialize on the GVL — see RactorPool for real parallelism).

Instance Method Summary collapse

Constructor Details

#initialize(configuration = Configuration.build) ⇒ Sandbox

Returns a new instance of Sandbox.



18
19
20
21
22
23
# File 'lib/security_box/sandbox.rb', line 18

def initialize(configuration = Configuration.build)
  @config = configuration
  @engine = Runtime.engine(epoch_interval_ms: @config.epoch_interval_ms)
  @module = Runtime.module_for(@engine, @config.image_path)
  @linker_mutex = Mutex.new
end

Instance Method Details

#eval(code, **overrides) ⇒ Object

Runs code in the sandbox and returns a Result. Per-call options (derived from the configuration without mutating it):

timeout_ms:, fuel:, fuel_ms:, memory_size:, stdout_limit:, stderr_limit:,
mounts: (replaces the configuration's mounts, like env:)

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
# File 'lib/security_box/sandbox.rb', line 29

def eval(code, **overrides)
  raise ArgumentError, "code is required" if code.nil? || code.empty?

  config = overrides.empty? ? @config : @config.with(**overrides)
  # SecureRandom is not guaranteed Ractor-safe; EvalRun callers generate
  # the token in the main Ractor/thread.
  token = SecureRandom.hex(16)
  EvalRun.run(engine: @engine, module_: @module, linker: linker,
              config: config, code: code, token: token)
end