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.

<Steps>

### Step 1

Copy the source below and place it in a file named `sandbox.luau`:

<details>
<summary>Click to expand</summary>

```lua copy
local fs = require("@lune/fs")
local luau = require("@lune/luau")
local process = require("@lune/process")
local stdio = require("@lune/stdio")
local processArgs = table.clone(process.args)
local filePath: string = table.remove(processArgs, 1) or error("usage: lune run sandbox [SCRIPT_PATH] -- [ARGS]")

local DEFAULT_REQUIRE = require
local DEFAULT_PRINT = print
local SANDBOXED_ENV = {
	debugName = filePath,
	environment = {
		require = nil,
		getfenv = nil,
		setfenv = nil,
		print = nil,
		warn = nil,
	},
}

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(): {}
	return table.freeze(SANDBOXED_ENV)
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(...)
	DEFAULT_PRINT(`---------------------------------------`)
end

local function sandboxedRequire(path: string)
	if path:find("@lune") then
		local module = path:split("/")[2]:gsub("%s", "")

		if module == "net" or module == "fs" or module == "process" or module == "roblox" then
			local allow: boolean =
				stdio.prompt("confirm", `allow {SANDBOXED_ENV.debugName} to access {module}?`)

			if allow then
				local moduleRequire = DEFAULT_REQUIRE(path)

				return setmetatable({}, {
					__index = function(_, key)
						local value = moduleRequire[key]

						if typeof(value) == "function" then
							if module == "roblox" and key == "getAuthCookie" then
								local allowAuthCookie: boolean = stdio.prompt(
									"confirm",
									`allow {SANDBOXED_ENV.debugName} to access your .ROBLOSECURITY token?`
								)

								if allowAuthCookie then
									return value
								end
							end

							return function(...)
								warn(`{SANDBOXED_ENV.debugName} invoked {key} with args {...}`)
								return value(...)
							end
						end

						if module == "process" and key == "args" then
							return processArgs
						end

						return value
					end,

					__tostring = function()
						return stdio.format(moduleRequire)
					end,
				})
			end

			error(`{SANDBOXED_ENV.debugName} tried to access disallowed library {module}`)
		end

		local otherModule = DEFAULT_REQUIRE(path)

		if module == "stdio" then
			return setmetatable({
				write = sandboxPrint,
				ewrite = sandboxPrint,
			}, {
				__index = otherModule,
			})
		end

		return otherModule
	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))()
```

</details>

### 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.

</Steps>


