From bf900cdea97173ac738258b9b5ec5cca5aa4c58a Mon Sep 17 00:00:00 2001 From: James Hall Date: Mon, 12 Jan 2026 00:32:57 +0000 Subject: [PATCH 1/2] wip security fix --- .../lib/script/AbstractScriptManager.java | 7 +- .../dev/amble/lib/script/lua/LuaBinder.java | 1 + .../lib/script/lua/SandboxedGlobals.java | 77 +++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java diff --git a/src/main/java/dev/amble/lib/script/AbstractScriptManager.java b/src/main/java/dev/amble/lib/script/AbstractScriptManager.java index a683a66..953ee11 100644 --- a/src/main/java/dev/amble/lib/script/AbstractScriptManager.java +++ b/src/main/java/dev/amble/lib/script/AbstractScriptManager.java @@ -3,13 +3,13 @@ import dev.amble.lib.AmbleKit; import dev.amble.lib.script.lua.LuaBinder; import dev.amble.lib.script.lua.MinecraftData; +import dev.amble.lib.script.lua.SandboxedGlobals; import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.util.Identifier; import org.luaj.vm2.Globals; import org.luaj.vm2.LuaValue; -import org.luaj.vm2.lib.jse.JsePlatform; import java.io.InputStreamReader; import java.util.HashMap; @@ -72,7 +72,8 @@ public LuaScript load(Identifier id, ResourceManager manager) { return cache.computeIfAbsent(id, key -> { try { Resource res = manager.getResource(key).orElseThrow(); - Globals globals = JsePlatform.standardGlobals(); + // Use sandboxed globals to prevent access to dangerous APIs like luajava + Globals globals = SandboxedGlobals.create(); // Create and cache the minecraft data for this script MinecraftData data = createMinecraftData(); @@ -89,7 +90,7 @@ public LuaScript load(Identifier id, ResourceManager manager) { ); chunk.call(); - LuaScript script = new LuaScript(globals); + LuaScript script = new LuaScript(globals); // Call onRegister when script is first loaded into the manager if (script.onRegister() != null && !script.onRegister().isnil()) { diff --git a/src/main/java/dev/amble/lib/script/lua/LuaBinder.java b/src/main/java/dev/amble/lib/script/lua/LuaBinder.java index daa93e4..58d4e10 100644 --- a/src/main/java/dev/amble/lib/script/lua/LuaBinder.java +++ b/src/main/java/dev/amble/lib/script/lua/LuaBinder.java @@ -343,6 +343,7 @@ public Varargs invoke(Varargs args) { } meta.set("__index", index); + return meta; } } diff --git a/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java b/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java new file mode 100644 index 0000000..df216d5 --- /dev/null +++ b/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java @@ -0,0 +1,77 @@ +package dev.amble.lib.script.lua; + +import org.luaj.vm2.Globals; +import org.luaj.vm2.LoadState; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.compiler.LuaC; +import org.luaj.vm2.lib.*; +import org.luaj.vm2.lib.jse.JseBaseLib; +import org.luaj.vm2.lib.jse.JseMathLib; + +/** + * Creates a sandboxed Lua environment that prevents access to dangerous APIs. + *

+ * This specifically excludes: + *

+ */ +public final class SandboxedGlobals { + + private SandboxedGlobals() { + // Utility class + } + + /** + * Creates a new sandboxed Lua globals environment. + * This environment is safe to use with untrusted scripts. + * + * @return A new sandboxed Globals instance + */ + public static Globals create() { + Globals globals = new Globals(); + + // Install safe base libraries + globals.load(new JseBaseLib()); // Basic functions (print, type, etc.) - but we'll remove dangerous ones + globals.load(new PackageLib()); // Package/module system + globals.load(new Bit32Lib()); // Bit operations + globals.load(new TableLib()); // Table manipulation + globals.load(new StringLib()); // String manipulation + globals.load(new JseMathLib()); // Math functions + + // Install the compiler so scripts can be loaded + LoadState.install(globals); + LuaC.install(globals); + + // Remove dangerous functions from base library + removeDangerousFunctions(globals); + + return globals; + } + + /** + * Removes dangerous functions that could be used to escape the sandbox. + */ + private static void removeDangerousFunctions(Globals globals) { + // Remove functions that can load arbitrary code + globals.set("dofile", LuaValue.NIL); // Can load files from disk + globals.set("loadfile", LuaValue.NIL); // Can load files from disk + + // Note: We keep 'load' and 'loadstring' since they can only load Lua source code + // (not bytecode) when LuaC is the only compiler installed, making them relatively safe. + // However, if you want maximum security, uncomment these: + // globals.set("load", LuaValue.NIL); + // globals.set("loadstring", LuaValue.NIL); + + // Remove the package.loadlib function which can load native libraries + LuaValue pkg = globals.get("package"); + if (pkg.istable()) { + pkg.set("loadlib", LuaValue.NIL); + } + } +} + From c229e2fa47a82a48d008b86c035932236b39b2fd Mon Sep 17 00:00:00 2001 From: James Hall Date: Mon, 12 Jan 2026 00:50:53 +0000 Subject: [PATCH 2/2] wip security fix pt2 --- .../lib/script/lua/SandboxedGlobals.java | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java b/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java index df216d5..c8617fe 100644 --- a/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java +++ b/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java @@ -4,8 +4,10 @@ import org.luaj.vm2.LoadState; import org.luaj.vm2.LuaValue; import org.luaj.vm2.compiler.LuaC; -import org.luaj.vm2.lib.*; -import org.luaj.vm2.lib.jse.JseBaseLib; +import org.luaj.vm2.lib.BaseLib; +import org.luaj.vm2.lib.Bit32Lib; +import org.luaj.vm2.lib.StringLib; +import org.luaj.vm2.lib.TableLib; import org.luaj.vm2.lib.jse.JseMathLib; /** @@ -17,7 +19,8 @@ *
  • os library - Prevents system command execution and file operations
  • *
  • io library - Prevents file system access
  • *
  • debug library - Prevents environment manipulation and introspection attacks
  • - *
  • load/loadfile/loadstring with bytecode - Prevents bytecode injection
  • + *
  • package library - Prevents loading modules from disk
  • + *
  • load/loadfile/dofile - Prevents loading code from files
  • * */ public final class SandboxedGlobals { @@ -35,15 +38,22 @@ private SandboxedGlobals() { public static Globals create() { Globals globals = new Globals(); - // Install safe base libraries - globals.load(new JseBaseLib()); // Basic functions (print, type, etc.) - but we'll remove dangerous ones - globals.load(new PackageLib()); // Package/module system + // Install safe base libraries only + // Using BaseLib instead of JseBaseLib to avoid any file system access + globals.load(new BaseLib()); // Basic functions (print, type, tostring, etc.) globals.load(new Bit32Lib()); // Bit operations globals.load(new TableLib()); // Table manipulation globals.load(new StringLib()); // String manipulation globals.load(new JseMathLib()); // Math functions - // Install the compiler so scripts can be loaded + // NOTE: We intentionally do NOT load: + // - PackageLib (can search/load files from disk) + // - IoLib / JseIoLib (file system access) + // - OsLib / JseOsLib (system commands, file operations) + // - DebugLib (can manipulate environments) + // - LuajavaLib (arbitrary Java class access) + + // Install the compiler so scripts can be loaded from strings LoadState.install(globals); LuaC.install(globals); @@ -57,21 +67,13 @@ public static Globals create() { * Removes dangerous functions that could be used to escape the sandbox. */ private static void removeDangerousFunctions(Globals globals) { - // Remove functions that can load arbitrary code - globals.set("dofile", LuaValue.NIL); // Can load files from disk - globals.set("loadfile", LuaValue.NIL); // Can load files from disk - - // Note: We keep 'load' and 'loadstring' since they can only load Lua source code - // (not bytecode) when LuaC is the only compiler installed, making them relatively safe. - // However, if you want maximum security, uncomment these: - // globals.set("load", LuaValue.NIL); - // globals.set("loadstring", LuaValue.NIL); + // Remove functions that can load code from files + globals.set("dofile", LuaValue.NIL); // Loads and executes files from disk + globals.set("loadfile", LuaValue.NIL); // Loads files from disk - // Remove the package.loadlib function which can load native libraries - LuaValue pkg = globals.get("package"); - if (pkg.istable()) { - pkg.set("loadlib", LuaValue.NIL); - } + // Remove load/loadstring to prevent any dynamic code execution + // This is the safest option as it prevents all forms of dynamic code loading + globals.set("load", LuaValue.NIL); + globals.set("loadstring", LuaValue.NIL); } } -