The sandbox a virtual source's function runs in (spec/08 §10.5, D-194).
A virtual source is a source computed from other sources by an Elixir function, rows in
and rows out (#643). A function stored in a design as source text is untrusted, so it is
checked against an allowlist before anything runs (check/2), compiled once into
a closure (compile/2, cached by Visualize.VirtualSource.Sandbox.Cache), and called
in a bounded process (run/3) — as is a host's own trusted function.
iex> alias Visualize.VirtualSource.Sandbox
iex> {:ok, compiled} = Sandbox.compile("fn rows -> Enum.map(rows, & &1.v * 2) end")
iex> Sandbox.run(compiled, [[%{v: 1}, %{v: 2}]])
{:ok, [2, 4]}
iex> {:error, [{1, reason}]} = Sandbox.check("fn _ -> File.read(\"/etc/passwd\") end")
iex> reason
{:restricted, {"File", "read", 1}}Nothing is rescued: a raise, a heap overrun and a timeout are read from the process the call ran in, through its monitor.
Summary
Types
A checker fault: the source line, or nil, and the reason (spec/08 §10.5.1).
What a call can end in besides a value (spec/08 §10.5.4).
Functions
As check/2 with no options.
A stored function checked against the allowlist (spec/08 §10.5.1): {:ok, quoted}, or
{:error, faults} with every fault, each {line, reason}, ordered by line. Nothing is
evaluated.
As compile/2 with no options.
A stored function checked, then compiled once into a closure (spec/08 §10.5.3):
{:ok, %Compiled{}}, or {:error, faults} — the checker's, or {nil, {:compile, message}} for a checked source the compiler refuses. The compilation runs in the
bounded runner. Options: the checker's and the runner's.
One checker fault as a line a person reads (spec/08 §10.5.1).
A source's SHA-256 as 64 lowercase hexadecimal digits: its key in the cache (spec/08 §10.5.3).
As run/3 with no options.
A function called with args in a process of its own (spec/08 §10.5.4): {:ok, value},
or {:error, kind, detail} — {:error, :timeout, ms} when it did not answer within
timeout: (1_000 ms), {:error, :memory, words} when its heap passed max_heap:
(10_000_000 words) and the VM killed it, {:error, :raised, module} with the
exception's module when it raised. The process is spawned with its heap limit set and
monitored; a killed process is waited for before the call returns.
Types
@type fault() :: {pos_integer() | nil, term()}
A checker fault: the source line, or nil, and the reason (spec/08 §10.5.1).
@type kind() :: :timeout | :memory | :raised
What a call can end in besides a value (spec/08 §10.5.4).
Functions
As check/2 with no options.
A stored function checked against the allowlist (spec/08 §10.5.1): {:ok, quoted}, or
{:error, faults} with every fault, each {line, reason}, ordered by line. Nothing is
evaluated.
Options: arity: (1), fields: (the names a row.field may read; nil reads any)
and max_source: (bytes, 10_000).
@spec compile(String.t()) :: {:ok, Visualize.VirtualSource.Sandbox.Compiled.t()} | {:error, [fault(), ...]}
As compile/2 with no options.
@spec compile(String.t(), keyword()) :: {:ok, Visualize.VirtualSource.Sandbox.Compiled.t()} | {:error, [fault(), ...]}
A stored function checked, then compiled once into a closure (spec/08 §10.5.3):
{:ok, %Compiled{}}, or {:error, faults} — the checker's, or {nil, {:compile, message}} for a checked source the compiler refuses. The compilation runs in the
bounded runner. Options: the checker's and the runner's.
One checker fault as a line a person reads (spec/08 §10.5.1).
iex> Visualize.VirtualSource.Sandbox.format_error({3, {:restricted, {"Process", :send, 3}}})
"line 3: Process.send/3 is not allowed in a virtual source"
A source's SHA-256 as 64 lowercase hexadecimal digits: its key in the cache (spec/08 §10.5.3).
iex> Visualize.VirtualSource.Sandbox.hash("fn rows -> rows end")
"a07f4aab6263d19d009830e7ad7b37931d654ff286700db61a32f064ea6d98d8"
@spec run(Visualize.VirtualSource.Sandbox.Compiled.t() | function(), list()) :: {:ok, term()} | {:error, kind(), term()}
As run/3 with no options.
@spec run( Visualize.VirtualSource.Sandbox.Compiled.t() | function(), list(), keyword() ) :: {:ok, term()} | {:error, kind(), term()}
A function called with args in a process of its own (spec/08 §10.5.4): {:ok, value},
or {:error, kind, detail} — {:error, :timeout, ms} when it did not answer within
timeout: (1_000 ms), {:error, :memory, words} when its heap passed max_heap:
(10_000_000 words) and the VM killed it, {:error, :raised, module} with the
exception's module when it raised. The process is spawned with its heap limit set and
monitored; a killed process is waited for before the call returns.