std/sandbox

Sandbox and Script, which load a .trb file as a type-checked, capability-limited receiver closure.

Diese Seite gibt es noch nicht auf Deutsch, deshalb steht sie hier auf Englisch. Zur englischen Seite

Die generierte Referenz von std/sandbox zeigt jede öffentliche Deklaration mit Signatur, Doc-Kommentar und Beispielen.

Only the VM runs a script. A script is always interpreted (Receiver Scripts and the Sandbox): torb run runs a program that loads one in the VM; a native binary torb build compiled links the script host - the front end and the VM, built once per toolchain - and hands it the value as text, through the derived Encode and Decode of the receiver, which is therefore Encode & Decode there; torb build --embed-vm builds a native binary that runs the whole program in the VM.

std/sandbox loads .trb files as sandboxed receiver closures - the mechanism behind project.trb and every configuration script. What a script may do is granted at the call site of Sandbox.load, never in the script or its own project file, because a script that could grant itself capabilities would not be a sandbox. Left at the defaults, a script has no IO, no network, no clock, no environment and no foreign functions.

Import#

use Sandbox, Script, SandboxCapabilities, SandboxError from "std/sandbox"
use Sandbox from "std/sandbox"

type ServerConfig {
  var port: Int = 8080
}

const script = Sandbox.load<ServerConfig>("./config.trb") {
  limits steps: 1_000_000
}
print script.isOk()

Declarations#

Sandbox#

native type Sandbox {
  static fn load<Value>(path: String, capabilities: (var self: SandboxCapabilities) => Void = {}): Result<Script<Value>, SandboxError>
  static fn loadText<Value>(text: String, name: String, capabilities: (var self: SandboxCapabilities) => Void = {}): Result<Script<Value>, SandboxError>
}

Type checks path against Value and answers a Script, ready to run against a Value of the caller's own. What is wrong with the file (syntax, types, a module the script may not import) is reported here; what goes wrong while it runs is reported by Script.apply instead. The trailing block grants capabilities beyond the defaults.

The path is relative to the directory the program runs in. A literal path names a file the checker checked against the type argument with the program, which carries that script. Any other path - one the program computes - is read, checked and lowered when the program asks for it, by the torb that runs it, and costs a check of the program the first time; Value is then a type without type arguments. A file that is nowhere or does not check is the SandboxError of load, with the line of the first error, and so is an import of the script that the grant does not name, with the line of the use.

loadText is load for a script the program holds as text, which it fetched through a Storage (readText) or built itself: text is checked against Value with the same grants, limits and errors, and name is only what a SandboxError calls it - the line of an error counts the lines of text. Nothing is read from or written to a file, the same text loaded twice is the same script whatever it is called, and std/sandbox knows no Storage: the program fetches the text and hands it over. Value is a type without type arguments, as for a computed path, and a native binary runs it through the script host it links, like load.

Script#

type Script<Value> {
  fn apply(var value: Value): Result<Void, SandboxError>
}

A loaded script, ready to run against a Value of the caller's own. It is not a closure, because its body runs inside the sandbox and can still fail there - a step, memory or time limit, or a panic. This is the one panic in the language that is recoverable, because the sandbox interprets the script and the script has a heap of its own.

SandboxCapabilities#

type SandboxCapabilities {
  protected var moduleNames: List<String> = []
  protected var readOnlyRoots: List<String> = []
  protected var readWriteRoots: List<String> = []
  protected var variablePatterns: List<String> = []
  protected var connectPatterns: List<String> = []
  protected var listenPatterns: List<String> = []
  protected var stepLimit: Int = 1_000_000
  protected var memoryLimit: Int = 64_000_000
  protected var timeLimit: Int = 2000
  protected var workerLimit: Int = 1
  var fn modules(...names: String)
  var fn files(readOnly: String = "", readWrite: String = "")
  var fn network(connect: String = "", listen: String = "")
  var fn environment(...patterns: String)
  var fn limits(steps: Int = 1_000_000, memory: ByteSize = 64.megabytes(), time: Duration = 2.seconds(), workers: Int = 1)
}

modules names additional parts of the standard library the script may use. files names the file system roots the script may reach; a side left out stays closed. environment names the environment variable patterns the script may read. network names a host:port pattern the script may resolve and connect to, and one it may listen on or bind (network connect: "api.example.test:443", listen: "127.0.0.1:8080"); each call adds one per side, a side left out stays closed, and the script also needs modules "std/network". The host is a name, *. and a name for every name below it, *, localhost or an address (IPv6 in brackets, "[::1]:8080"), the port a number or *; a listen pattern names an address, * or localhost. Anything outside a pattern - a resolve, connect, listen, UDP bind or datagram, or the system's name servers - stops the script with a SandboxError, and a malformed pattern fails the load (NETWORK.md, section 10). limits guards against a runaway script (loop {}) - workers is the most workers its tasks may run on at once, and the VM runs a script's tasks on the thread of its sandbox, so it never uses more than one - and memory is a ByteSize of std/number (64.megabytes(), 16.mebibytes()), which needs no import.

SandboxError#

type SandboxError with Show, Error {
  line: Int
  message: String
  fn show(): String
}

What went wrong loading a script: a syntax or type error against the receiver type, or a capability it does not have. line is 0 when the problem is not about one place in the script, such as the file not being readable at all.

  • std/project - Project, the receiver type project.trb is loaded against.
  • std/os - Environment, gated by SandboxCapabilities.environment.
  • std/network - the sockets and the resolver, gated by SandboxCapabilities.network.
  • The standard library - the other packages.
Esc