View Source Dune (dune v0.3.10)
A sandbox for Elixir to safely evaluate untrusted code from user input.
Features
- only authorized modules and functions can be executed (see
Dune.Allowlist.Default
) - no access to environment variables, file system, network...
- code executed in an isolated process
- execution within configurable limits: timeout, maximum reductions and memory (inspired by Luerl)
- captured standard output
- atoms, without atom leaks: parsing and runtime do not leak atoms (i.e. does not keep filling the atom table until the VM crashes)
- modules, without actual module creation: Dune does not let users define any
actual module (would leak memory and modify the state of the VM globally), but
defmodule
simulates the basic behavior of a module, including private and recursive functions
The list of modules and functions authorized by default is defined by the
Dune.Allowlist.Default
module, but this list can be extended and customized
(at your own risk!) using Dune.Allowlist
.
If you need to keep the state between evaluations, you might consider
Dune.Session
.
Summary
Functions
Evaluates the quoted ast
in the sandbox.
Evaluates the string
in the sandbox.
Returns the AST corresponding to the provided string
, without leaking atoms.
Functions
@spec eval_quoted(Macro.t(), Keyword.t()) :: Dune.Success.t() | Dune.Failure.t()
Evaluates the quoted ast
in the sandbox.
Available options are detailed in Dune.Opts
(parsing restrictions have no effect)..
Returns a Dune.Success
struct if the execution went successfully,
a Dune.Failure
else.
Examples
iex> Dune.eval_quoted(quote do: [1, 2] ++ [3, 4])
%Dune.Success{inspected: "[1, 2, 3, 4]", stdio: "", value: [1, 2, 3, 4]}
iex> Dune.eval_quoted(quote do: System.get_env())
%Dune.Failure{message: "** (DuneRestrictedError) function System.get_env/0 is restricted", type: :restricted}
iex> Dune.eval_quoted(quote do: Process.sleep(500))
%Dune.Failure{message: "Execution timeout - 50ms", type: :timeout}
@spec eval_string(String.t(), Keyword.t()) :: Dune.Success.t() | Dune.Failure.t()
Evaluates the string
in the sandbox.
Available options are detailed in Dune.Opts
.
Returns a Dune.Success
struct if the execution went successfully,
a Dune.Failure
else.
Examples
iex> Dune.eval_string("IO.puts(\"Hello world!\")")
%Dune.Success{inspected: ":ok", stdio: "Hello world!\n", value: :ok}
iex> Dune.eval_string("File.cwd!()")
%Dune.Failure{message: "** (DuneRestrictedError) function File.cwd!/0 is restricted", type: :restricted}
iex> Dune.eval_string("List.duplicate(:spam, 100_000)")
%Dune.Failure{message: "Execution stopped - memory limit exceeded", stdio: "", type: :memory}
iex> Dune.eval_string("Foo.bar()")
%Dune.Failure{message: "** (UndefinedFunctionError) function Foo.bar/0 is undefined (module Foo is not available)", type: :exception}
iex> Dune.eval_string("][")
%Dune.Failure{message: "unexpected token: ]", type: :parsing}
Atoms used during parsing and execution might be transformed to prevent atom leaks:
iex> Dune.eval_string("some_variable = IO.inspect(:some_atom)")
%Dune.Success{inspected: ":some_atom", stdio: ":some_atom\n", value: :a__Dune_atom_2__}
The value
field shows the actual runtime value, but inspected
and stdio
are safe to display to the user.
@spec string_to_quoted(String.t(), Keyword.t()) :: Dune.Success.t() | Dune.Failure.t()
Returns the AST corresponding to the provided string
, without leaking atoms.
Available options are detailed in Dune.Opts
(runtime restrictions have no effect).
Returns a Dune.Success
struct if the execution went successfully,
a Dune.Failure
else.
Examples
iex> Dune.string_to_quoted("1 + 2")
%Dune.Success{inspected: "{:+, [line: 1], [1, 2]}", stdio: "", value: {:+, [line: 1], [1, 2]}}
iex> Dune.string_to_quoted("[invalid")
%Dune.Failure{stdio: "", message: "missing terminator: ]", type: :parsing}
The pretty
option can make the AST more readable by adding newlines to inspected
:
iex> Dune.string_to_quoted("IO.puts(:hello)", pretty: true).inspected
"{{:., [line: 1], [{:__aliases__, [line: 1], [:IO]}, :puts]}, [line: 1],\n [:hello]}"
iex> Dune.string_to_quoted("IO.puts(:hello)").inspected
"{{:., [line: 1], [{:__aliases__, [line: 1], [:IO]}, :puts]}, [line: 1], [:hello]}"
Since the code isn't executed, there is no allowlist restriction:
iex> Dune.string_to_quoted("System.halt()")
%Dune.Success{
inspected: "{{:., [line: 1], [{:__aliases__, [line: 1], [:System]}, :halt]}, [line: 1], []}",
stdio: "",
value: {{:., [line: 1], [{:__aliases__, [line: 1], [:System]}, :halt]}, [line: 1], []}
}
Atoms might be transformed during parsing to prevent atom leaks:
iex> Dune.string_to_quoted("some_variable = :some_atom")
%Dune.Success{
inspected: "{:=, [line: 1], [{:some_variable, [line: 1], nil}, :some_atom]}",
stdio: "",
value: {:=, [line: 1], [{:a__Dune_atom_1__, [line: 1], nil}, :a__Dune_atom_2__]}
}
The value
field shows the actual runtime value, but inspected
is safe to display to the user.