diff --git a/core/runner/Sandbox.go b/core/runner/Sandbox.go index 291ddbd..ddefd60 100644 --- a/core/runner/Sandbox.go +++ b/core/runner/Sandbox.go @@ -4,7 +4,6 @@ import ( "fmt" "sync" - "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" "Moonshark/core/utils/logger" @@ -113,78 +112,48 @@ func (s *Sandbox) registerCoreFunctions(state *luajit.State) error { // Execute runs a Lua script in the sandbox with the given context func (s *Sandbox) Execute(state *luajit.State, bytecode []byte, ctx *Context) (*Response, error) { - s.debugLog("Executing script...") - // Create a response object response := NewResponse() - // Get a buffer for string operations - buf := bytebufferpool.Get() - defer bytebufferpool.Put(buf) - // Load bytecode if err := state.LoadBytecode(bytecode, "script"); err != nil { ReleaseResponse(response) - s.debugLog("Failed to load bytecode: %v", err) return nil, fmt.Errorf("failed to load script: %w", err) } - // Initialize session data in Lua + // Add session data to context + contextWithSession := make(map[string]any) + maps.Copy(contextWithSession, ctx.Values) + + // Pass session data through context if ctx.SessionID != "" { - // Set session ID - state.PushString(ctx.SessionID) - state.SetGlobal("__session_id") - - // Set session data - if err := state.PushTable(ctx.SessionData); err != nil { - ReleaseResponse(response) - s.debugLog("Failed to push session data: %v", err) - return nil, err - } - state.SetGlobal("__session_data") - - // Reset modification flag and tracking - state.PushBoolean(false) - state.SetGlobal("__session_modified") - - // Create empty modified keys table - state.NewTable() - state.SetGlobal("__session_modified_keys") - } else { - // Initialize empty session - if err := state.DoString("__session_data = {}; __session_modified = false; __session_modified_keys = {}"); err != nil { - s.debugLog("Failed to initialize empty session data: %v", err) - } + contextWithSession["session_id"] = ctx.SessionID + contextWithSession["session_data"] = ctx.SessionData } // Set up context values for execution - if err := state.PushTable(ctx.Values); err != nil { + if err := state.PushTable(contextWithSession); err != nil { ReleaseResponse(response) - s.debugLog("Failed to push context values: %v", err) return nil, err } // Get the execution function state.GetGlobal("__execute_script") if !state.IsFunction(-1) { - state.Pop(1) // Pop non-function + state.Pop(1) ReleaseResponse(response) - s.debugLog("__execute_script is not a function") return nil, ErrSandboxNotInitialized } - // Push function and context to stack - state.PushCopy(-2) // bytecode - state.PushCopy(-2) // context - - // Remove duplicates - state.Remove(-4) - state.Remove(-3) + // Push function and bytecode + state.PushCopy(-2) // Bytecode + state.PushCopy(-2) // Context + state.Remove(-4) // Remove bytecode duplicate + state.Remove(-3) // Remove context duplicate // Execute with 2 args, 1 result if err := state.Call(2, 1); err != nil { ReleaseResponse(response) - s.debugLog("Execution failed: %v", err) return nil, fmt.Errorf("script execution failed: %w", err) } @@ -195,102 +164,84 @@ func (s *Sandbox) Execute(state *luajit.State, bytecode []byte, ctx *Context) (* } state.Pop(1) - // Extract HTTP response data from Lua state - s.extractResponseData(state, response) + extractHTTPResponseData(state, response) + + extractSessionData(state, response) return response, nil } // extractResponseData pulls response info from the Lua state -func (s *Sandbox) extractResponseData(state *luajit.State, response *Response) { - // Get HTTP response +func extractHTTPResponseData(state *luajit.State, response *Response) { state.GetGlobal("__http_responses") - if !state.IsNil(-1) && state.IsTable(-1) { - state.PushNumber(1) - state.GetTable(-2) - - if !state.IsNil(-1) && state.IsTable(-1) { - // Extract status - state.GetField(-1, "status") - if state.IsNumber(-1) { - response.Status = int(state.ToNumber(-1)) - } - state.Pop(1) - - // Extract headers - state.GetField(-1, "headers") - if state.IsTable(-1) { - state.PushNil() // Start iteration - for state.Next(-2) { - if state.IsString(-2) && state.IsString(-1) { - key := state.ToString(-2) - value := state.ToString(-1) - response.Headers[key] = value - } - state.Pop(1) - } - } - state.Pop(1) - - // Extract cookies - state.GetField(-1, "cookies") - if state.IsTable(-1) { - length := state.GetTableLength(-1) - for i := 1; i <= length; i++ { - state.PushNumber(float64(i)) - state.GetTable(-2) - - if state.IsTable(-1) { - s.extractCookie(state, response) - } - state.Pop(1) - } - } - state.Pop(1) - - // Extract metadata if present - state.GetField(-1, "metadata") - if state.IsTable(-1) { - table, err := state.ToTable(-1) - if err == nil { - for k, v := range table { - response.Metadata[k] = v - } - } - } - state.Pop(1) - } + if !state.IsTable(-1) { state.Pop(1) + return + } + + state.PushNumber(1) + state.GetTable(-2) + if !state.IsTable(-1) { + state.Pop(2) + return + } + + // Extract status + state.GetField(-1, "status") + if state.IsNumber(-1) { + response.Status = int(state.ToNumber(-1)) } state.Pop(1) - // Extract session data - state.GetGlobal("__session_modified") - if state.IsBoolean(-1) && state.ToBoolean(-1) { - response.SessionModified = true - - // Get session ID - state.GetGlobal("__session_id") - if state.IsString(-1) { - response.SessionID = state.ToString(-1) - } - state.Pop(1) - - // Get session data - state.GetGlobal("__session_data") - if state.IsTable(-1) { - sessionData, err := state.ToTable(-1) - if err == nil { - maps.Copy(response.SessionData, sessionData) + // Extract headers + state.GetField(-1, "headers") + if state.IsTable(-1) { + state.PushNil() // Start iteration + for state.Next(-2) { + if state.IsString(-2) && state.IsString(-1) { + key := state.ToString(-2) + value := state.ToString(-1) + response.Headers[key] = value } + state.Pop(1) } - state.Pop(1) } state.Pop(1) + + // Extract cookies + state.GetField(-1, "cookies") + if state.IsTable(-1) { + length := state.GetTableLength(-1) + for i := 1; i <= length; i++ { + state.PushNumber(float64(i)) + state.GetTable(-2) + + if state.IsTable(-1) { + extractCookie(state, response) + } + state.Pop(1) + } + } + state.Pop(1) + + // Extract metadata + state.GetField(-1, "metadata") + if state.IsTable(-1) { + table, err := state.ToTable(-1) + if err == nil { + for k, v := range table { + response.Metadata[k] = v + } + } + } + state.Pop(1) + + // Clean up + state.Pop(2) } // extractCookie pulls cookie data from the current table on the stack -func (s *Sandbox) extractCookie(state *luajit.State, response *Response) { +func extractCookie(state *luajit.State, response *Response) { cookie := fasthttp.AcquireCookie() // Get name (required) @@ -347,3 +298,69 @@ func (s *Sandbox) extractCookie(state *luajit.State, response *Response) { response.Cookies = append(response.Cookies, cookie) } + +// Extract session data if modified +func extractSessionData(state *luajit.State, response *Response) { + logger.Debug("extractSessionData: Starting extraction") + + // Get HTTP response table + state.GetGlobal("__http_responses") + if !state.IsTable(-1) { + logger.Debug("extractSessionData: __http_responses is not a table") + state.Pop(1) + return + } + + // Get first response + state.PushNumber(1) + state.GetTable(-2) + if !state.IsTable(-1) { + logger.Debug("extractSessionData: __http_responses[1] is not a table") + state.Pop(2) + return + } + + // Check session_modified flag + state.GetField(-1, "session_modified") + if !state.IsBoolean(-1) || !state.ToBoolean(-1) { + logger.Debug("extractSessionData: session_modified is not true") + state.Pop(3) + return + } + logger.Debug("extractSessionData: Found session_modified=true") + state.Pop(1) + + // Get session ID + state.GetField(-1, "session_id") + if state.IsString(-1) { + response.SessionID = state.ToString(-1) + logger.Debug("extractSessionData: Found session ID: %s", response.SessionID) + } else { + logger.Debug("extractSessionData: session_id not found or not a string") + } + state.Pop(1) + + // Get session data + state.GetField(-1, "session_data") + if state.IsTable(-1) { + logger.Debug("extractSessionData: Found session_data table") + sessionData, err := state.ToTable(-1) + if err == nil { + logger.Debug("extractSessionData: Converted session data, size=%d", len(sessionData)) + for k, v := range sessionData { + response.SessionData[k] = v + logger.Debug("extractSessionData: Added session key=%s, value=%v", k, v) + } + response.SessionModified = true + } else { + logger.Debug("extractSessionData: Failed to convert session data: %v", err) + } + } else { + logger.Debug("extractSessionData: session_data not found or not a table") + } + state.Pop(1) + + // Clean up stack + state.Pop(2) + logger.Debug("extractSessionData: Finished extraction, modified=%v", response.SessionModified) +} diff --git a/core/runner/sandbox.lua b/core/runner/sandbox.lua index 4f2c1f7..b96e9ba 100644 --- a/core/runner/sandbox.lua +++ b/core/runner/sandbox.lua @@ -14,7 +14,6 @@ __ready_modules = {} __session_data = {} __session_id = nil __session_modified = false -__session_modified_keys = {} -- ====================================================================== -- CORE SANDBOX FUNCTIONALITY @@ -43,11 +42,25 @@ function __execute_script(fn, ctx) -- Clear previous responses __http_responses[1] = nil - -- Reset session modification flag - __session_modified = false + -- Create environment with metatable inheriting from _G + local env = setmetatable({}, {__index = _G}) - -- Create environment - local env = __create_env(ctx) + -- Add context if provided + if ctx then + env.ctx = ctx + end + + print("INIT SESSION DATA:", util.json_encode(ctx.session_data or {})) + + -- Initialize local session variables in the environment + env.__session_data = ctx.session_data or {} + env.__session_id = ctx.session_id + env.__session_modified = false + + -- Add proper require function to this environment + if __setup_require then + __setup_require(env) + end -- Set environment for function setfenv(fn, env) @@ -58,6 +71,17 @@ function __execute_script(fn, ctx) error(result, 0) end + -- If session was modified, add to response + if env.__session_modified then + __http_responses[1] = __http_responses[1] or {} + __http_responses[1].session_data = env.__session_data + __http_responses[1].session_id = env.__session_id + __http_responses[1].session_modified = true + end + + print("SESSION MODIFIED:", env.__session_modified) + print("FINAL DATA:", util.json_encode(env.__session_data or {})) + return result end @@ -293,77 +317,79 @@ local cookie = { -- SESSION MODULE -- ====================================================================== --- Session module implementation local session = { - -- Get a session value + -- Get session value get = function(key) if type(key) ~= "string" then error("session.get: key must be a string", 2) end - return __session_data and __session_data[key] + local env = getfenv(2) + return env.__session_data and env.__session_data[key] end, - -- Set a session value + -- Set session value set = function(key, value) if type(key) ~= "string" then error("session.set: key must be a string", 2) end - __session_data = __session_data or {} - __session_data[key] = value - __session_modified = true + + local env = getfenv(2) + print("SET ENV:", tostring(env)) -- Debug the environment + + if not env.__session_data then + env.__session_data = {} + print("CREATED NEW SESSION TABLE") + end + + env.__session_data[key] = value + env.__session_modified = true + print("SET:", key, "=", tostring(value), "MODIFIED:", env.__session_modified) return true end, - -- Delete a session value + -- Delete session value delete = function(key) if type(key) ~= "string" then error("session.delete: key must be a string", 2) end - if __session_data and __session_data[key] ~= nil then - __session_data[key] = nil - __session_modified = true - __session_modified_keys[key] = true + local env = getfenv(2) + if env.__session_data and env.__session_data[key] ~= nil then + env.__session_data[key] = nil + env.__session_modified = true end - return true end, -- Clear all session data clear = function() - if __session_data and next(__session_data) then - -- Track all keys as modified - for k in pairs(__session_data) do - __session_modified_keys[k] = true - end - - __session_data = {} - __session_modified = true + local env = getfenv(2) + if env.__session_data and next(env.__session_data) then + env.__session_data = {} + env.__session_modified = true end return true end, - -- Get the session ID + -- Get session ID get_id = function() - return __session_id or nil + local env = getfenv(2) + return env.__session_id or "" end, -- Get all session data get_all = function() - local result = {} - for k, v in pairs(__session_data or {}) do - result[k] = v - end - return result + local env = getfenv(2) + return env.__session_data or {} end, - -- Check if session has a key + -- Check if session has key has = function(key) if type(key) ~= "string" then error("session.has: key must be a string", 2) end - - return __session_data and __session_data[key] ~= nil + local env = getfenv(2) + return env.__session_data ~= nil and env.__session_data[key] ~= nil end }