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

Link to this function

eval_quoted(ast, opts \\ [])

View Source
@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}
Link to this function

eval_string(string, opts \\ [])

View Source
@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.

Link to this function

string_to_quoted(string, opts \\ [])

View Source
@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.