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}