--[[ Moonshark Lua Sandbox Environment This file contains all the Lua code needed for the sandbox environment, including core modules and utilities. It's designed to be embedded in the Go binary at build time. ]]-- __http_response = {} __module_paths = {} __module_bytecode = {} __ready_modules = {} -- ====================================================================== -- CORE SANDBOX FUNCTIONALITY -- ====================================================================== -- Create environment inheriting from _G function __create_env(ctx) local env = setmetatable({}, {__index = _G}) if ctx then env.ctx = ctx end if __setup_require then __setup_require(env) end return env end -- Execute script with clean environment function __execute_script(fn, ctx) __http_response = nil local env = __create_env(ctx) setfenv(fn, env) local ok, result = pcall(fn) if not ok then error(result, 0) end return result end -- Ensure __http_response exists, then return it function __ensure_response() if not __http_response then __http_response = {} end return __http_response end -- ====================================================================== -- HTTP MODULE -- ====================================================================== local http = { -- Set HTTP status code set_status = function(code) if type(code) ~= "number" then error("http.set_status: status code must be a number", 2) end local resp = __ensure_response() resp.status = code end, -- Set HTTP header set_header = function(name, value) if type(name) ~= "string" or type(value) ~= "string" then error("http.set_header: name and value must be strings", 2) end local resp = __ensure_response() resp.headers = resp.headers or {} resp.headers[name] = value end, -- Set content type; set_header helper set_content_type = function(content_type) http.set_header("Content-Type", content_type) end, -- Set metadata (arbitrary data to be returned with response) set_metadata = function(key, value) if type(key) ~= "string" then error("http.set_metadata: key must be a string", 2) end local resp = __ensure_response() resp.metadata = resp.metadata or {} resp.metadata[key] = value end, -- HTTP client submodule client = { -- Generic request function request = function(method, url, body, options) if type(method) ~= "string" then error("http.client.request: method must be a string", 2) end if type(url) ~= "string" then error("http.client.request: url must be a string", 2) end -- Call native implementation local result = __http_request(method, url, body, options) return result end, -- Shorthand function to directly get JSON get_json = function(url, options) options = options or {} local response = http.client.get(url, options) if response.ok and response.json then return response.json end return nil, response end, -- Utility to build a URL with query parameters build_url = function(base_url, params) if not params or type(params) ~= "table" then return base_url end local query = {} for k, v in pairs(params) do if type(v) == "table" then for _, item in ipairs(v) do table.insert(query, k .. "=" .. tostring(item)) end else table.insert(query, k .. "=" .. tostring(v)) end end if #query > 0 then if base_url:find("?") then return base_url .. "&" .. table.concat(query, "&") else return base_url .. "?" .. table.concat(query, "&") end end return base_url end } } local function make_method(method, needs_body) return function(url, body_or_options, options) if needs_body then options = options or {} return http.client.request(method, url, body_or_options, options) else body_or_options = body_or_options or {} return http.client.request(method, url, nil, body_or_options) end end end http.client.get = make_method("GET", false) http.client.delete = make_method("DELETE", false) http.client.head = make_method("HEAD", false) http.client.options = make_method("OPTIONS", false) http.client.post = make_method("POST", true) http.client.put = make_method("PUT", true) http.client.patch = make_method("PATCH", true) -- ====================================================================== -- COOKIE MODULE -- ====================================================================== local cookie = { -- Set a cookie set = function(name, value, options) if type(name) ~= "string" then error("cookie.set: name must be a string", 2) end local resp = __ensure_response() resp.cookies = resp.cookies or {} local opts = options or {} local cookie = { name = name, value = value or "", path = opts.path or "/", domain = opts.domain } if opts.expires then if type(opts.expires) == "number" then if opts.expires > 0 then cookie.max_age = opts.expires local now = os.time() cookie.expires = now + opts.expires elseif opts.expires < 0 then cookie.expires = 1 cookie.max_age = 0 end -- opts.expires == 0: Session cookie (omitting both expires and max-age) end end cookie.secure = (opts.secure ~= false) cookie.http_only = (opts.http_only ~= false) if opts.same_site then local valid_values = {none = true, lax = true, strict = true} local same_site = string.lower(opts.same_site) if not valid_values[same_site] then error("cookie.set: same_site must be one of 'None', 'Lax', or 'Strict'", 2) end -- If SameSite=None, the cookie must be secure if same_site == "none" and not cookie.secure then cookie.secure = true end cookie.same_site = opts.same_site else cookie.same_site = "Lax" end table.insert(resp.cookies, cookie) return true end, -- Get a cookie value get = function(name) if type(name) ~= "string" then error("cookie.get: name must be a string", 2) end local env = getfenv(2) if env.ctx and env.ctx.cookies then return env.ctx.cookies[name] end if env.ctx and env.ctx._request_cookies then return env.ctx._request_cookies[name] end return nil end, -- Remove a cookie remove = function(name, path, domain) if type(name) ~= "string" then error("cookie.remove: name must be a string", 2) end return cookie.set(name, "", {expires = 0, path = path or "/", domain = domain}) end } -- ====================================================================== -- UTIL MODULE -- ====================================================================== -- Utility module implementation local util = { -- Generate a token (wrapper around __generate_token) generate_token = function(length) return __generate_token(length or 32) end, -- Simple JSON stringify (for when you just need a quick string) json_encode = function(value) if type(value) == "table" then local json = "{" local sep = "" for k, v in pairs(value) do json = json .. sep if type(k) == "number" then -- Array-like json = json .. util.json_encode(v) else -- Object-like json = json .. '"' .. k .. '":' .. util.json_encode(v) end sep = "," end return json .. "}" elseif type(value) == "string" then return '"' .. value:gsub('"', '\\"'):gsub('\n', '\\n') .. '"' elseif type(value) == "number" then return tostring(value) elseif type(value) == "boolean" then return value and "true" or "false" elseif value == nil then return "null" end return '"' .. tostring(value) .. '"' end, -- Deep copy of tables deep_copy = function(obj) if type(obj) ~= 'table' then return obj end local res = {} for k, v in pairs(obj) do res[k] = util.deep_copy(v) end return res end, -- Merge tables merge_tables = function(t1, t2) if type(t1) ~= 'table' or type(t2) ~= 'table' then error("Both arguments must be tables", 2) end local result = util.deep_copy(t1) for k, v in pairs(t2) do if type(v) == 'table' and type(result[k]) == 'table' then result[k] = util.merge_tables(result[k], v) else result[k] = v end end return result end, -- String utilities string = { -- Trim whitespace trim = function(s) return (s:gsub("^%s*(.-)%s*$", "%1")) end, -- Split string split = function(s, delimiter) delimiter = delimiter or "," local result = {} for match in (s..delimiter):gmatch("(.-)"..delimiter) do table.insert(result, match) end return result end } } -- ====================================================================== -- REGISTER MODULES GLOBALLY -- ====================================================================== _G.http = http _G.cookie = cookie _G.util = util