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..c8617fe --- /dev/null +++ b/src/main/java/dev/amble/lib/script/lua/SandboxedGlobals.java @@ -0,0 +1,79 @@ +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.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; + +/** + * 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 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 + + // 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); + + // 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 code from files + globals.set("dofile", LuaValue.NIL); // Loads and executes files from disk + globals.set("loadfile", LuaValue.NIL); // Loads files from disk + + // 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); + } +}