#moonrockz/extism/wasi

    A WASI preview 1 sandbox for Extism plugins (design WASI-1). It links every function of wasi_snapshot_preview1 and gives no ambient authority: no preopens, empty arguments, environment and stdin, clocks and random bytes from sources that the embedder gives, and stdout and stderr to sinks that the embedder gives. It works on any GuestMemory, so the native and the js engine give the same answers.

    The bound of a sink applies to each fd_write. A guest that writes in a loop makes many calls, so the sink bounds the total itself, for example with a tail of the last bytes; the deadline of the call ends the loop.

    FunctionsAnswer
    args_get, args_sizes_get, environ_get, environ_sizes_getZero entries; success
    fd_readfd 0 reads 0 bytes (end of file); fd 1 and 2: ENOTCAPABLE
    fd_writefd 1 and 2: the embedder's sinks, one iovec at a time, at most the sink's SinkBound per call (1 MiB by default; the first bytes are written and reported); discarded without a sink. More than 1024 iovecs: EINVAL; a range outside memory: EFAULT, before a byte goes to the sink. fd 0: ENOTCAPABLE
    clock_res_get, clock_time_get, random_getThe embedder's sources; ENOTCAPABLE (76) without one. random_get checks its range before it calls the source
    fd_fdstat_get, fd_filestat_getA character device for fds 0, 1 and 2
    proc_exitEnds the call with the exit code
    Any function with an fd argument other than 0, 1 and 2EBADF (8)
    Every other functionENOTCAPABLE (76)

    The test below checks that this table is the function table of the package:

    ///|
    test "the README table is the function table" {
    let table : Array[(Array[String], @wasi.Answer)] = [
    (
    ["args_get", "args_sizes_get", "environ_get", "environ_sizes_get"],
    NoEntries,
    ),
    (["fd_read"], EndOfFile),
    (["fd_write"], Sinks),
    (["clock_res_get", "clock_time_get", "random_get"], Sources),
    (["fd_fdstat_get", "fd_filestat_get"], StreamStatus),
    (["proc_exit"], Ends),
    ]
    for function in @wasi.Function::all() {
    let documented = table
    .iter()
    .find_first(row => row.0.contains(function.name()))
    .map(row => row.1)
    .unwrap_or(NotCapable)
    assert_eq(function.answer(), documented)
    }
    }

    A sandbox with a clock and a stdout sink:

    ///|
    test "a sandbox with a clock and a sink" {
    let printed = []
    let sandbox = @wasi.Sandbox::new()
    .clock({ time: _ => 42UL, resolution: _ => 1UL, })
    .stdout(bytes => printed.push(bytes.to_owned()))
    assert_eq(@wasi.Function::all().length(), 46)
    ignore(sandbox)
    assert_eq(printed.length(), 0)
    }

    GuestMemory

    pub(open) trait GuestMemory {
    fn size(Self) -> Int64
    fn read(Self, Int64, Int) -> Bytes raise GuestMemoryError
    fn write(Self, Int64, BytesView) -> Unit raise GuestMemoryError
    }

    The linear memory of a guest, as the sandbox sees it. The engine implements it for the memory of an instance; a test can implement it over plain bytes. An access outside the memory raises OutOfBounds and reads or writes nothing.

    GuestMemoryError

    pub(all) suberror GuestMemoryError {
    OutOfBounds(Int64, Int)
    } derive(Eq,
    Debug
    )

    A guest memory access outside the memory: the offset and the length.

    GuestMemoryError::equal

    GuestMemoryError::not_equal

    fn GuestMemoryError::not_equal(x : GuestMemoryError, y : GuestMemoryError) -> Bool

    SinkBoundError

    pub(all) suberror SinkBoundError {
    SinkBoundError(Int, String)
    } derive(Eq,
    Debug
    )

    A sink bound that breaks its rule: the bytes and the rule.

    SinkBoundError::equal

    SinkBoundError::not_equal

    fn SinkBoundError::not_equal(x : SinkBoundError, y : SinkBoundError) -> Bool

    Answer

    pub(all) enum Answer {
    NoEntries
    EndOfFile
    Sinks
    Sources
    StreamStatus
    Ends
    NotCapable
    } derive(Eq,
    Debug
    )

    The answer of the sandbox to a function, as README.mbt.md documents it. A function with an fd argument other than 0, 1 and 2 returns EBADF first.

    Answer::equal

    fn Answer::equal(Answer, Answer) -> Bool

    Answer::not_equal

    fn Answer::not_equal(x : Answer, y : Answer) -> Bool

    Answer::to_repr

    Clock

    pub(all) struct Clock {
    time : (ClockId) -> UInt64
    resolution : (ClockId) -> UInt64
    }

    A clock source of the embedder: the time and the resolution of each clock, in nanoseconds.

    ClockId

    pub(all) enum ClockId {
    Realtime
    Monotonic
    ProcessCputime
    ThreadCputime
    } derive(Eq,
    Debug
    )

    A WASI clock.

    ClockId::equal

    fn ClockId::equal(ClockId, ClockId) -> Bool

    ClockId::not_equal

    fn ClockId::not_equal(x : ClockId, y : ClockId) -> Bool

    ClockId::to_repr

    Errno

    pub(all) enum Errno {
    Success
    Badf
    Fault
    Inval
    Notcapable
    } derive(Eq,
    Debug
    )

    A WASI errno that the sandbox gives.

    Errno::code

    fn Errno::code(self : Errno) -> Int

    The number of the errno.

    Errno::equal

    fn Errno::equal(Errno, Errno) -> Bool

    Errno::not_equal

    fn Errno::not_equal(x : Errno, y : Errno) -> Bool

    Errno::to_repr

    Function

    pub(all) enum Function {
    ArgsGet
    ArgsSizesGet
    EnvironGet
    EnvironSizesGet
    ClockResGet
    ClockTimeGet
    FdAdvise
    FdAllocate
    FdClose
    FdDatasync
    FdFdstatGet
    FdFdstatSetFlags
    FdFdstatSetRights
    FdFilestatGet
    FdFilestatSetSize
    FdFilestatSetTimes
    FdPread
    FdPrestatGet
    FdPrestatDirName
    FdPwrite
    FdRead
    FdReaddir
    FdRenumber
    FdSeek
    FdSync
    FdTell
    FdWrite
    PathCreateDirectory
    PathFilestatGet
    PathFilestatSetTimes
    PathLink
    PathOpen
    PathReadlink
    PathRemoveDirectory
    PathRename
    PathSymlink
    PathUnlinkFile
    PollOneoff
    ProcExit
    ProcRaise
    SchedYield
    RandomGet
    SockAccept
    SockRecv
    SockSend
    SockShutdown
    } derive(Eq,
    Debug
    )

    The 46 functions of wasi_snapshot_preview1, in the order of the witx.

    Function::all

    fn Function::all() -> Array[Function]

    Every preview 1 function, in the order of the witx.

    Function::answer

    fn Function::answer(self : Function) -> Answer

    The answer of the sandbox to the function.

    Function::equal

    fn Function::equal(Function, Function) -> Bool

    Function::from_name

    fn Function::from_name(name : StringView) -> Function?

    The function with the import name name, or None for a name that preview 1 does not have.

    Function::name

    fn Function::name(self : Function) -> String

    The import name of the function, for example fd_write.

    Function::not_equal

    fn Function::not_equal(x : Function, y : Function) -> Bool

    Function::params

    fn Function::params(self : Function) -> Array[WasiType]

    The core WASM parameter types of the function.

    Function::results

    fn Function::results(self : Function) -> Array[WasiType]

    The core WASM result types of the function: one i32 errno, or none for proc_exit.

    Function::to_repr

    Outcome

    pub(all) enum Outcome {
    Return(Errno)
    Exit(Int)
    } derive(Eq,
    Debug
    )

    The result of a sandbox call.

    Outcome::equal

    fn Outcome::equal(Outcome, Outcome) -> Bool

    Outcome::not_equal

    fn Outcome::not_equal(x : Outcome, y : Outcome) -> Bool

    Outcome::to_repr

    Sandbox

    pub struct Sandbox {
    // private fields
    }

    The WASI preview 1 sandbox of a plugin: no preopens, empty arguments, environment and stdin, clocks and random bytes from sources that the embedder gives, and stdout and stderr to sinks that the embedder gives. Every other function returns ENOTCAPABLE or EBADF. The builder steps give a new sandbox.

    Sandbox::call

    fn[M : GuestMemory] Sandbox::call(self : Sandbox, function : Function, memory : M, args : Array[Int64]) -> Outcome

    Runs the preview 1 function function with the arguments args (an i32 as its value, an i64 as its value) on the guest memory memory.

    Sandbox::clock

    fn Sandbox::clock(self : Sandbox, clock : Clock) -> Sandbox

    The sandbox with clock as its clock source.

    Sandbox::new

    fn Sandbox::new() -> Sandbox

    A sandbox with no clock, no random source and no sinks.

    Sandbox::random

    fn Sandbox::random(self : Sandbox, source : (Int) -> Bytes) -> Sandbox

    The sandbox with a random source, which gives the number of bytes that it is asked for.

    Sandbox::stderr

    fn Sandbox::stderr(self : Sandbox, sink : (BytesView) -> Unit, bound? : SinkBound) -> Sandbox

    The sandbox with a sink for the bytes that the guest writes to fd 2, with the rules of stdout.

    Sandbox::stdout

    fn Sandbox::stdout(self : Sandbox, sink : (BytesView) -> Unit, bound? : SinkBound) -> Sandbox

    The sandbox with a sink for the bytes that the guest writes to fd 1. One fd_write gives the sink at most bound bytes, one iovec at a time; it writes the first bound bytes and reports them, as a POSIX write that writes part of its bytes.

    SinkBound

    pub struct SinkBound {
    // private fields
    } derive(Eq,
    Debug
    )

    The most bytes that one fd_write gives a sink: 1 byte to 16 MiB. The bound applies to each call; a guest that writes in a loop makes many calls, so the embedder's sink bounds the total itself, for example with a tail of the last bytes.

    SinkBound::bytes

    fn SinkBound::bytes(self : SinkBound) -> Int

    SinkBound::default

    fn SinkBound::default() -> SinkBound

    The default sink bound: 1 MiB.

    SinkBound::equal

    fn SinkBound::equal(SinkBound, SinkBound) -> Bool

    SinkBound::not_equal

    fn SinkBound::not_equal(x : SinkBound, y : SinkBound) -> Bool

    SinkBound::parse

    fn SinkBound::parse(bytes : Int) -> SinkBound raise SinkBoundError

    A sink bound of bytes bytes, from 1 to 16 MiB.

    WasiType

    pub(all) enum WasiType {
    I32
    I64
    } derive(Eq,
    Debug
    )

    A core WASM type of a preview 1 function.

    WasiType::equal

    fn WasiType::equal(WasiType, WasiType) -> Bool

    WasiType::not_equal

    fn WasiType::not_equal(x : WasiType, y : WasiType) -> Bool

    WasiType::to_repr

    import_namespace

    let import_namespace : String

    The import namespace of WASI preview 1.

    max_iovecs

    let max_iovecs : Int

    The most iovecs that one call takes.