import { Steps } from "nextra/components" # Security When running lune scripts, it is important to note that any scripts you execute have full access to your device, including access to your files, programs, and more. Therefore, it is important to stay cautious when executing a script from a source you don't trust. If you are unsure what a script does, it is recommended to run the script in a [sandboxed](https://en.wikipedia.org/wiki/Sandbox_(computer_security)) environment to evaluate what it does. Lune does not include a built-in permissions sandbox, but the following luau script, which utilizes the [@lune/luau](/api-reference/luau) library can be used. ### Step 1 Copy the source below and place it in a file named `sandbox.luau`:
Click to expand ```lua copy local datetime = require("@lune/datetime") local fs = require("@lune/fs") local luau = require("@lune/luau") local net = require("@lune/net") local process = require("@lune/process") local regex = require("@lune/regex") local roblox = require("@lune/roblox") local serde = require("@lune/serde") local stdio = require("@lune/stdio") local task = require("@lune/task") local processArgs = table.clone(process.args) local filePath: string = table.remove(processArgs, 1) or error("usage: lune run sandbox [SCRIPT_PATH] -- [ARGS]") local DEFAULT_PRINT = print local SANDBOXED_ENV = { debugName = filePath, environment = { require = nil, getfenv = nil, setfenv = nil, print = nil, warn = nil, }, } local PROMPT_MSG_TMPL = `allow {SANDBOXED_ENV.debugName} to access %s?` local DENIED_ERR_TMPL = `{SANDBOXED_ENV.debugName} tried to access disallowed library %s!` local function discoverAndReadScript(filePath: string): string local scriptContents: string if fs.isFile(filePath) then scriptContents = fs.readFile(filePath) return scriptContents end if fs.isDir(filePath) then if fs.isFile(filePath .. "/init.luau") then scriptContents = fs.readFile(filePath .. "/init.luau") end if fs.isFile(filePath .. "/init.lua") then scriptContents = fs.readFile(filePath .. "init.lua") end end if scriptContents == nil then for _, ext in { ".luau", ".lua" } do local filePathExt = filePath .. ext if fs.isFile(filePathExt) then scriptContents = fs.readFile(filePathExt) end end if scriptContents == nil then error(`No such file or directory \`{filePath}\``) end end return scriptContents end local function sandboxGetfenv(): {} if table.isfrozen(SANDBOXED_ENV) then return SANDBOXED_ENV.environment end return {} end local function sandboxSetfenv(env: {}): never error("cannot call setfenv from sandbox") end local function sandboxPrint(...: any) DEFAULT_PRINT(`---- Output from {SANDBOXED_ENV.debugName} ----`) DEFAULT_PRINT(tostring(...)) DEFAULT_PRINT(`---------------------------------------`) end local function constructProtectedMt(library: T) return { __index = library, __metatable = "Locked", __tostring = function() return stdio.format(library) end, } end local SANDBOXED_LUNE_STD_LIB = { ["@lune/fs"] = setmetatable({}, constructProtectedMt(fs)), ["@lune/luau"] = setmetatable({}, constructProtectedMt(luau)), ["@lune/process"] = setmetatable({}, constructProtectedMt(process)), ["@lune/stdio"] = setmetatable({ write = sandboxPrint, ewrite = sandboxPrint, }, constructProtectedMt(stdio)), ["@lune/net"] = setmetatable({}, constructProtectedMt(net)), ["@lune/roblox"] = setmetatable({ getAuthCookie = function(...) local allowAuthCookie: boolean = stdio.prompt( "confirm", `allow {SANDBOXED_ENV.debugName} to access your .ROBLOSECURITY token?` ) if allowAuthCookie then local getAuthCookie = require("@lune/roblox").getAuthCookie return getAuthCookie(...) end error( `{SANDBOXED_ENV.debugName} attempted to access .ROBLOSECURITY token even when denied` ) end, }, constructProtectedMt(roblox)), ["@lune/serde"] = serde, ["@lune/task"] = task, ["@lune/regex"] = regex, ["@lune/datetime"] = datetime, } local function sandboxedRequire(path: string) local module = SANDBOXED_LUNE_STD_LIB[path] if module then local allowed: boolean = stdio.prompt("confirm", string.format(PROMPT_MSG_TMPL, path)) if allowed then return module end error(string.format(DENIED_ERR_TMPL, path)) else local contents = discoverAndReadScript(path) local evalChunk = luau.load(contents, SANDBOXED_ENV) return evalChunk() end end SANDBOXED_ENV.environment.require = sandboxedRequire SANDBOXED_ENV.environment.getfenv = sandboxGetfenv SANDBOXED_ENV.environment.setfenv = sandboxSetfenv SANDBOXED_ENV.environment.print = sandboxPrint SANDBOXED_ENV.environment.warn = sandboxPrint luau.load(discoverAndReadScript(filePath), table.freeze(SANDBOXED_ENV))() ```
### Step 2 Now, place the untrusted script you want to run safely next to the `sandbox.luau` script. ```sh copy filename="Bash" lune run sandbox.luau script.luau -- [ARGUMENTS_HERE] ``` Replace `script.luau` and `[ARGUMENTS_HERE]` with the path to the script and the arguments to run it with. ### Step 3 As the script runs, any requires to potentially dangerous modules will require your approval before continuing and any invocations to methods within approved modules will be logged. Furthermore, the output of the sandbox script and the script being run will be separated.