plugin-security-audit

Guide for plugin-security-audit

Plugin Security Audit

Analysis of the current Lua plugin sandbox security in loh-game.

Current Implementation

File: loh-game/src/plugin_api/lua_loader.rs

Memory Limits ✅

// Line 94
lua.set_memory_limit(32 * 1024 * 1024)?; // 32MB

Instruction Hooks ✅

// Lines 98-101
lua.set_hook(mlua::HookTriggers::new().every_nth_instruction(100_000), move |_lua, _debug| {
    Ok(())
});

Standard Library Restrictions 🔶 PARTIAL

// Lines 84-88
let std_libs = if mode.allows_automation() {
    mlua::StdLib::ALL  // ⚠️ DANGER: Includes io, os, debug
} else {
    mlua::StdLib::STRING | mlua::StdLib::TABLE | mlua::StdLib::MATH | mlua::StdLib::UTF8 | mlua::StdLib::COROUTINE
};
Issue: In automation mode (PluginMode::Development), all Lua standard libraries are available, including:
  • io - File system access
  • os - Operating system commands
  • debug - Runtime manipulation

Gaps Identified

RiskSeverityCurrent StateRecommendation
io library access🔴 CriticalEnabled in dev modeDisable in ALL modes
os library access🔴 CriticalEnabled in dev modeDisable in ALL modes
debug library access🟠 HighEnabled in dev modeDisable in ALL modes
require function🟠 HighAvailableSandbox or disable
loadstring🟠 HighAvailableDisable
Plugin signature bypass🟡 MediumWarning only in devEnforce in production

1. Remove Dangerous Libraries (Priority: Critical)

// NEVER allow io, os, or debug
let std_libs = mlua::StdLib::STRING 
    | mlua::StdLib::TABLE 
    | mlua::StdLib::MATH 
    | mlua::StdLib::UTF8 
    | mlua::StdLib::COROUTINE;

// Create Lua with restricted libs
let lua = Lua::new_with(std_libs, mlua::LuaOptions::default())?;

2. Disable Dangerous Globals

// After Lua creation
lua.globals().set("loadstring", mlua::Value::Nil)?;
lua.globals().set("dofile", mlua::Value::Nil)?;
lua.globals().set("loadfile", mlua::Value::Nil)?;
lua.globals().set("require", mlua::Value::Nil)?;  // Or sandbox

3. Enforce Plugin Signatures in Production

// Currently (lines 126-134) - Warning only in non-production
if let Err(e) = self.verify_signature(&plugin_dir, &manifest.plugin.entry_point) {
    if mode == PluginMode::Production {
        return Err(anyhow!("Signature verification failed: {}", e));
    }
    // Should also fail in QA/Staging
}

4. Implement CPU Time Limits

// Current hook doesn't interrupt - just logs
lua.set_hook(
    mlua::HookTriggers::new().every_nth_instruction(100_000), 
    move |_lua, _debug| {
        // TODO: Add actual timeout check
        // if start_time.elapsed() > Duration::from_secs(5) {
        //     return Err(mlua::Error::RuntimeError("CPU limit exceeded".into()));
        // }
        Ok(())
    }
);

Follow-Up Mission

Create Mission PLUGIN-SANDBOX to:
  1. Remove io/os/debug libraries from all modes
  2. Nil out loadstring/dofile/loadfile/require
  3. Implement CPU time limits with actual interruption
  4. Enforce signatures in production AND staging
  5. Add integration tests for sandbox escapes