How FileMgr Sandboxes File Access for Lua Scripts in YimMenuV2
YimMenuV2 confines all Lua file operations to a dedicated ./scripts directory by validating every path through the FileMgr library, rejecting traversal attempts using ../ or absolute paths with a clear Lua error.
The FileMgr system in YimMenuV2 provides Lua scripts with essential filesystem capabilities while enforcing strict security boundaries. Implemented in src/core/scripting/libraries/FileMgr.cpp, this sandbox ensures user scripts interact only with files inside a designated scripts folder, preventing unauthorized access to sensitive host system locations.
Defining the Sandbox Boundary
The Scripts Root Directory
The sandbox establishes a single canonical root for all file activity. The ScriptsRoot() function returns an absolute path pointing to ./scripts within the project folder, obtained via YimMenu::FileMgr::GetProjectFolder("./scripts"). This location represents the exclusive zone where Lua scripts may read, write, or modify files.
Path Validation Pipeline
Before any filesystem operation executes, the engine subjects the requested path to a rigorous two-stage validation:
- String Safety Check: The input first passes through
CheckStringSafe(provided insrc/core/scripting/LuaUtils.cpp) to ensure safe string handling. - Sandbox Resolution: The
ResolveInSandboxfunction canonicalizes the path usingstd::filesystem::weakly_canonicaland computes its lexical relationship toScriptsRoot().
Core Validation Implementation
ResolveInSandbox Function
Located in src/core/scripting/libraries/FileMgr.cpp between lines 22-47, the ResolveInSandbox function implements the core security logic:
static bool ResolveInSandbox(std::string_view input, std::filesystem::path& out) {
if (input.empty()) return false;
std::filesystem::path p(std::string{input});
std::error_code ec;
auto canon = std::filesystem::weakly_canonical(p, ec);
if (ec) return false;
auto rel = canon.lexically_relative(ScriptsRoot());
std::string rel_str = rel.generic_string();
if (rel_str.empty() || rel_str == "." || rel_str.starts_with(".."))
return false; // outside sandbox
out = canon;
return true;
}
This function rejects paths where canonicalization fails or where the resulting relative path indicates traversal above the scripts root.
CheckSandboxedPath Wrapper
The CheckSandboxedPath function (lines 49-58) serves as the uniform entry point for all file operations exposed to Lua. It invokes ResolveInSandbox and, upon validation failure, aborts execution with luaL_argerror(state, idx, "path is outside the script sandbox"), immediately terminating the offending script.
Sandboxed API Methods
Every public function in the FileMgr Lua library applies CheckSandboxedPath before accessing the filesystem:
- CreateDir(path): Creates directories (including parent directories) after sandbox verification.
- DeleteFile(path): Removes files only after confirming the target lies within the scripts folder.
- DoesFileExist(path): Returns existence status restricted to the sandbox boundary.
- FindFiles(dir, ext, recursive): Searches exclusively within the allowed directory tree.
- ReadFileContent(path): Reads file contents post-validation.
- WriteFileContent(path, data, append?): Writes or appends data only following successful path verification.
Practical Usage Examples
The following Lua code demonstrates successful sandboxed operations and failed escape attempts:
-- Successful operations inside the sandbox
FileMgr.CreateDir("my_mod/data") -- creates ./scripts/my_mod/data
local ok = FileMgr.WriteFileContent("my_mod/data/config.txt", "enabled=true")
print("Write succeeded:", ok)
-- Attempt to escape the sandbox (raises a Lua error)
-- This triggers: path is outside the script sandbox
FileMgr.ReadFileContent("../outside.txt")
Summary
- Isolation Root: All Lua file operations are restricted to the
./scriptsfolder returned byScriptsRoot(). - Validation Layer:
CheckSandboxedPathandResolveInSandboxinFileMgr.cppenforce the boundary usingstd::filesystemcanonicalization. - Traversal Prevention: Any path resolving outside the root—whether via
../, absolute paths, or empty strings—triggers an immediate Lua error. - Universal Coverage: Every exposed
FileMgrfunction (read, write, delete, search) applies identical sandbox checks before execution.
Frequently Asked Questions
What happens if a Lua script tries to access a file outside the scripts folder?
The CheckSandboxedPath function detects the traversal attempt during the ResolveInSandbox check and calls luaL_argerror with the message "path is outside the script sandbox", immediately halting the script execution and preventing the file operation.
Can Lua scripts use absolute paths within the FileMgr sandbox?
No. Even absolute paths are canonicalized and checked against the ScriptsRoot(). If the canonicalized absolute path does not resolve to a location inside the ./scripts directory, the operation is rejected regardless of whether an absolute or relative path was provided.
Where is the sandbox root directory located in YimMenuV2?
The sandbox root is located at ./scripts relative to the YimMenuV2 project folder, obtained programmatically via YimMenu::FileMgr::GetProjectFolder("./scripts"). This path is calculated once and serves as the authoritative boundary for all subsequent file access validation.
Does the FileMgr sandbox allow recursive directory creation?
Yes. The CreateDir function supports creating nested directory structures (similar to mkdir -p) as long as the resulting path remains within the ./scripts folder. Parent directories are created automatically if they do not exist, provided the entire path chain stays inside the sandbox boundary.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →