docs/pages/getting-started/5-security.mdx

200 lines
5.3 KiB
Text
Raw Normal View History

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 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<T>(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))()
```
</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>