From cd0fa453c27f5b10c4771f3cf96c4ebd878d1ea7 Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Thu, 1 Oct 2026 22:13:29 +0400 Subject: [PATCH] fix(script): bound how deep json.encode walks a Lua table --- internal/script/lua_json.go | 11 +++++++++++ internal/types/errors.go | 1 + 2 files changed, 12 insertions(+) diff --git a/internal/script/lua_json.go b/internal/script/lua_json.go index bdc2ba2..d9da6c9 100644 --- a/internal/script/lua_json.go +++ b/internal/script/lua_json.go @@ -45,9 +45,14 @@ func luaJSONDecode(state *lua.LState) int { return 1 } +// luaJSONMaxDepth matches the depth encoding/json accepts, so a table a script +// decoded always encodes again, and it stays far below what overflows the Go stack. +const luaJSONMaxDepth = 10000 + // luaToGo converts a Lua value to a JSON-encodable Go value. // It can return the following errors: // - types.ErrScriptJSONCycle +// - types.ErrScriptJSONDepth // - types.ScriptTypeError func luaToGo(value lua.LValue, visiting map[*lua.LTable]bool) (any, error) { switch v := value.(type) { @@ -63,6 +68,12 @@ func luaToGo(value lua.LValue, visiting map[*lua.LTable]bool) (any, error) { if visiting[v] { return nil, types.ErrScriptJSONCycle } + // visiting holds the tables on the current path, so its size is the depth. + // A table millions of levels deep overflows the Go stack, which is a fatal + // error no pcall can catch, so the descent is bounded well before that. + if len(visiting) >= luaJSONMaxDepth { + return nil, types.ErrScriptJSONDepth + } visiting[v] = true defer delete(visiting, v) diff --git a/internal/types/errors.go b/internal/types/errors.go index 9fdb522..05c2953 100644 --- a/internal/types/errors.go +++ b/internal/types/errors.go @@ -442,6 +442,7 @@ var ( ErrScriptHTTPURLInvalid = errors.New("URL must be an absolute http or https URL") ErrScriptHTTPUnknownOption = errors.New("unknown option") ErrScriptJSONCycle = errors.New("cannot encode a table that contains itself") + ErrScriptJSONDepth = errors.New("cannot encode a table nested too deeply") ) type ScriptTypeError struct {