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:

  1. String Safety Check: The input first passes through CheckStringSafe (provided in src/core/scripting/LuaUtils.cpp) to ensure safe string handling.
  2. Sandbox Resolution: The ResolveInSandbox function canonicalizes the path using std::filesystem::weakly_canonical and computes its lexical relationship to ScriptsRoot().

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 ./scripts folder returned by ScriptsRoot().
  • Validation Layer: CheckSandboxedPath and ResolveInSandbox in FileMgr.cpp enforce the boundary using std::filesystem canonicalization.
  • Traversal Prevention: Any path resolving outside the root—whether via ../, absolute paths, or empty strings—triggers an immediate Lua error.
  • Universal Coverage: Every exposed FileMgr function (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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →