From 2c0067dfcfa743dcfd8c6d141fd4466681bbe5d0 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 4 Jun 2025 09:25:49 -0500 Subject: [PATCH] fix env, add additional type support, use coroutines for endpoint exec --- runner/lua/env.lua | 31 +++++++++++------------ runner/lua/sandbox.lua | 45 +++++++++++++++++++-------------- runner/lualibs/env.go | 56 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 90 insertions(+), 42 deletions(-) diff --git a/runner/lua/env.lua b/runner/lua/env.lua index 068d207..047e4a5 100644 --- a/runner/lua/env.lua +++ b/runner/lua/env.lua @@ -1,5 +1,4 @@ --- Environment variable module for Moonshark --- Provides access to persistent environment variables stored in .env file +-- env.lua -- Get an environment variable with a default value function env_get(key, default_value) @@ -8,8 +7,8 @@ function env_get(key, default_value) end -- Check context for environment variables - if __ctx and __ctx._env and __ctx._env[key] ~= nil then - return __ctx._env[key] + if __ctx and __ctx.env and __ctx.env[key] ~= nil then + return __ctx.env[key] end return default_value @@ -23,8 +22,8 @@ function env_set(key, value) -- Update context immediately for future reads if __ctx then - __ctx._env = __ctx._env or {} - __ctx._env[key] = value + __ctx.env = __ctx.env or {} + __ctx.env[key] = value end -- Persist to Go backend @@ -34,9 +33,9 @@ end -- Get all environment variables as a table function env_get_all() -- Return context table directly if available - if __ctx and __ctx._env then + if __ctx and __ctx.env then local copy = {} - for k, v in pairs(__ctx._env) do + for k, v in pairs(__ctx.env) do copy[k] = v end return copy @@ -53,8 +52,8 @@ function env_exists(key) end -- Check context first - if __ctx and __ctx._env then - return __ctx._env[key] ~= nil + if __ctx and __ctx.env then + return __ctx.env[key] ~= nil end return false @@ -67,24 +66,24 @@ function env_set_many(vars) end if __ctx then - __ctx._env = __ctx._env or {} + __ctx.env = __ctx.env or {} end local success = true for key, value in pairs(vars) do - if type(key) == "string" and type(value) == "string" then + if type(key) == "string" then -- Update context - if __ctx and __ctx._env then - __ctx._env[key] = value + if __ctx and __ctx.env then + __ctx.env[key] = value end -- Persist to Go if not __env_set(key, value) then success = false end else - error("env_set_many: all keys and values must be strings") + error("env_set_many: all keys must be strings") end end return success -end \ No newline at end of file +end diff --git a/runner/lua/sandbox.lua b/runner/lua/sandbox.lua index 608d2ab..fb65982 100644 --- a/runner/lua/sandbox.lua +++ b/runner/lua/sandbox.lua @@ -1,31 +1,38 @@ --- Simplified sandbox.lua - Global function approach +-- sandbox.lua function __execute(script_func, ctx, response) -- Store context and response globally for function access __ctx = ctx __response = response _G.ctx = ctx - - -- Execute script in global environment - local ok, result = pcall(script_func) - + + -- Create a coroutine for script execution to handle early exits + local co = coroutine.create(function() + return script_func() + end) + + local ok, result = coroutine.resume(co) + -- Clean up __ctx = nil __response = nil - + if not ok then - if result == "__EXIT__" then - return {nil, response} - end + -- Real error during script execution error(result, 0) end - + + -- Check if exit was requested + if result == "__EXIT__" then + return {nil, response} + end + return {result, response} end --- Exit sentinel +-- Exit sentinel using coroutine yield instead of error function exit() - error("__EXIT__") + coroutine.yield("__EXIT__") end -- ====================================================================== @@ -55,7 +62,7 @@ function http_redirect(url, status) __response.status = status or 302 __response.headers = __response.headers or {} __response.headers["Location"] = url - error("__EXIT__") + coroutine.yield("__EXIT__") end -- ====================================================================== @@ -158,15 +165,15 @@ function csrf_validate() local token = __ctx.session and __ctx.session.data and __ctx.session.data["_csrf_token"] if not token then __response.status = 403 - error("__EXIT__") + coroutine.yield("__EXIT__") end - - local request_token = (__ctx._request_form and __ctx._request_form._csrf_token) or + + local request_token = (__ctx._request_form and __ctx._request_form._csrf_token) or (__ctx._request_headers and (__ctx._request_headers["x-csrf-token"] or __ctx._request_headers["csrf-token"])) - + if not request_token or request_token ~= token then __response.status = 403 - error("__EXIT__") + coroutine.yield("__EXIT__") end return true end @@ -420,4 +427,4 @@ function password_verify(plain_password, hash_string) end return __password_verify(plain_password, hash_string) -end \ No newline at end of file +end diff --git a/runner/lualibs/env.go b/runner/lualibs/env.go index e2c220e..9557984 100644 --- a/runner/lualibs/env.go +++ b/runner/lualibs/env.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "strings" "sync" @@ -65,6 +66,29 @@ func GetGlobalEnvManager() *EnvManager { return globalEnvManager } +// parseValue attempts to parse a string value into the appropriate type +func parseValue(value string) any { + // Try boolean first + if value == "true" { + return true + } + if value == "false" { + return false + } + + // Try number + if num, err := strconv.ParseFloat(value, 64); err == nil { + // Check if it's actually an integer + if num == float64(int64(num)) { + return int64(num) + } + return num + } + + // Default to string + return value +} + // load reads the .env file and populates the vars map func (e *EnvManager) load() error { file, err := os.Open(e.envPath) @@ -110,7 +134,7 @@ func (e *EnvManager) load() error { } } - e.vars[key] = value + e.vars[key] = parseValue(value) } return scanner.Err() @@ -139,8 +163,7 @@ func (e *EnvManager) Save() error { sort.Strings(keys) // Write header comment - fmt.Fprintln(file, "# Environment variables for Moonshark") - fmt.Fprintln(file, "# Generated automatically - you can edit this file") + fmt.Fprintln(file, "# env variables - generated automatically - you can edit this file") fmt.Fprintln(file) // Write each variable @@ -152,6 +175,12 @@ func (e *EnvManager) Save() error { switch v := value.(type) { case string: strValue = v + case bool: + strValue = strconv.FormatBool(v) + case int64: + strValue = strconv.FormatInt(v, 10) + case float64: + strValue = strconv.FormatFloat(v, 'g', -1, 64) case nil: continue // Skip nil values default: @@ -255,10 +284,23 @@ func envSet(state *luajit.State) int { return 1 } - value, err := state.SafeToString(2) - if err != nil { - state.PushBoolean(false) - return 1 + // Handle different value types from Lua + var value any + switch state.GetType(2) { + case luajit.TypeBoolean: + value = state.ToBoolean(2) + case luajit.TypeNumber: + value = state.ToNumber(2) + case luajit.TypeString: + value = state.ToString(2) + default: + // Try to convert to string as fallback + if str, err := state.SafeToString(2); err == nil { + value = str + } else { + state.PushBoolean(false) + return 1 + } } globalEnvManager.Set(key, value)