Powered by AppSignal & Oban Pro

Sandboxing

guides/examples/sandboxing.livemd

Sandboxing

Mix.install([
  {:lua, "~> 1.0.0-rc.0"}
])

The default sandbox

Lua.new/0 sandboxes a curated list of functions (os.execute, os.exit, os.getenv, require, loadfile, …). Calling a sandboxed function raises, which pcall turns into a false, message pair so the script can recover in-band.

{[ok?, message], _lua} =
  Lua.eval!(Lua.new(), ~S[return pcall(function() return os.getenv("HOME") end)])

{ok?, message}
{false, "Lua runtime error: os.getenv(_) is sandboxed"}

Allowing specific operations

Lua.new(exclude: [...]) lifts the sandbox for specific paths while keeping everything else locked down. Here we allow os.getenv only.

allowed = Lua.new(exclude: [[:os, :getenv]])

{[kind], _lua} = Lua.eval!(allowed, ~S[return type(os.getenv("HOME"))])
kind
"string"

os.execute is still blocked on the allowed VM — we only lifted getenv.

{[exec_ok?, _message], _lua} =
  Lua.eval!(allowed, ~S[return pcall(function() return os.execute("echo hi") end)])

exec_ok?
false

Replacing or disabling the sandbox

Lua.new(sandboxed: [...]) replaces the whole sandbox list, and Lua.new(sandboxed: []) disables sandboxing entirely. Reach for these only when you fully trust the script you are running.