Skip to main content

lua_stdlib/
sandbox.rs

1//! Shared sandbox helpers: the canonical strict capability-strip list and a
2//! global-stripping routine, used by both the CLI `--sandbox` flag and
3//! `lua-rs-runtime`'s `SandboxConfig::strict()` so the dangerous-globals list
4//! has a single source of truth.
5//!
6//! The instruction and memory budget itself is installed separately via
7//! [`lua_vm::state::LuaState::install_sandbox_limits`]; capability stripping is
8//! defense-in-depth on top of the host-hook gating.
9
10use lua_types::value::LuaValue;
11use lua_types::LuaError;
12
13use crate::state_stub::LuaState;
14
15/// Globals removed by the strict sandbox preset: the code-loading and
16/// host-access surfaces. A `.`-separated entry nils a field of a sub-table
17/// (e.g. `os.execute`); a bare name nils a top-level global.
18pub const STRICT_REMOVED_GLOBALS: &[&[u8]] = &[
19    b"dofile",
20    b"loadfile",
21    b"load",
22    b"loadstring",
23    b"require",
24    b"package",
25    b"io",
26    b"debug",
27    b"os.execute",
28    b"os.exit",
29    b"os.remove",
30    b"os.rename",
31    b"os.tmpname",
32    b"os.getenv",
33    b"os.setlocale",
34];
35
36/// Delete the named globals from `_G`. Each entry is either a bare global name
37/// or a `head.tail` path naming a field of a sub-table.
38pub fn strip_globals(state: &mut LuaState, names: &[&[u8]]) -> Result<(), LuaError> {
39    let globals = match state.global().globals.clone() {
40        LuaValue::Table(t) => t,
41        _ => return Ok(()),
42    };
43    for name in names {
44        match name.iter().position(|&b| b == b'.') {
45            Some(dot) => {
46                let head = &name[..dot];
47                let tail = &name[dot + 1..];
48                if let LuaValue::Table(sub) = globals.get_str_bytes(head) {
49                    let key = LuaValue::Str(state.new_string(tail)?);
50                    sub.raw_set(key, LuaValue::Nil);
51                }
52            }
53            None => {
54                let key = LuaValue::Str(state.new_string(name)?);
55                globals.raw_set(key, LuaValue::Nil);
56            }
57        }
58    }
59    Ok(())
60}