Dune

A sandbox for Elixir to safely evaluate untrusted code from user input.

Try it out online!

This can be useful to develop playgrounds, online REPL, coding games, or customizeable business logic.

Warning:Dune is still early stage: expect bugs and vulnerabilities. Use at your own risk and avoid running it directly on a server with any sensisitive access, e.g. to a database.

Features

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("Enum.product(1..100_000)")
%Dune.Failure{message: "Execution stopped - reductions limit exceeded", stdio: "", type: :reductions}

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:

iex> Dune.Session.new()
...> |> Dune.Session.eval_string("x = 1")
...> |> Dune.Session.eval_string("x + 2")
#Dune.Session<last_result: %Dune.Success{inspected: "3", stdio: "", value: 3}, ...>

Limitations

Dune supports a fair subset of the base language, but it cannot safely support advanced features (at least at this stage) such as:

Installation

The package can be installed by adding dune to your list of dependencies in mix.exs:

def deps do
[
{:dune, "~> 0.1.0"}
]
end

Documentation can be found at https://hexdocs.pm/dune.

FAQ

Why can I still create atoms?

Atoms are converted by the parser and mapped to always use a given set of atoms. So when you type foo = bar(:baz), the atoms :foo, :bar and :baz won't actually be created, but other atoms like :atom1, :atom2 and :atom3 are going to be used instead.

So even if many user run various codes using many different variable and function names, they will all pull from the same pool of atoms.

Why can I still create modules?

Modules are being defined globally within the VM, making it both an isolation concern and a memory concern. But modules are an important part of the Elixir language, and are especially important for learning platforms.

Dune implements an alternative defmodule, relying on maps of anonymous functions at runtime and should reproduce the basic behavior of actual modules, with support for recursive and private functions.

Why is the behavior different than Elixir when doing X?

As explained above, some parts of the language are actually being completely reimplemented because the original version could not be safely sandboxed: atoms, modules... While these alternative implementation aim to be as close as possible to the original ones, they might differ in some cases, because the code being executed is actually different.

Why can't I do X?

Some parts of the language are being restricted because they present a direct security risk, while some other would need to be reimplemented in an alternative way and therefore need a consequent amount of work that hasn't been done yet.