@ai-ecoverse/slicc-kernel 1.82.0 → 1.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/dist/kernel-worker.js +940 -180
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node.js +948 -188
- package/dist/node.js.map +4 -4
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -624,10 +624,25 @@ With `users: 'enforce'` the kernel checks every file call that reaches it agains
|
|
|
624
624
|
A character device (`0666`, major 10, minor 250) through which a program talks to the embedder's `sealed` hook (`createKernel({ sealed })`), the secrets vault on the page. Emscripten and WASI programs can open it (Emscripten programs see it in `/dev`); JS programs can't yet.
|
|
625
625
|
- **State per open:** each `open()` is independent; `dup` and fork share one, as on Linux.
|
|
626
626
|
- **One write is one request:** a single UTF-8 JSON object of at most 64 KiB with `v` 1 and a known `op` (`seal`, `placeholder`, `list`, `test`, `request`, `status`, `cancel`, `materialize`). The write takes all the bytes or fails: `EMSGSIZE` over 64 KiB, `EINVAL` for anything else that isn't such an object. These are the only errnos; every other outcome is a JSON reply. The kernel zeroes the bytes it was given once it has parsed them.
|
|
627
|
-
- **The stamp:** the hook gets the request with `caller: { pid, ppid, uid, gid,
|
|
627
|
+
- **The stamp:** the hook gets the request with `caller: { pid, ppid, uid, gid, user?, groups, users, tag? }`, from the process that writes (at the write; not the one that opened it): `uid` and `gid` the effective ids, `user` the `/etc/passwd` name of `uid` read at that moment (left out when there is none), `groups` the process's supplementary groups (only root can change them), `users` the kernel's `users` mode, since a uid means more under `'enforce'`, and `tag` the process's tag (see `kernel.run`) when it has one. A `caller` in the request is replaced.
|
|
628
628
|
- **Reading:** the reply comes from the same descriptor, as JSON (`{ v: 1, … }`). A read waits for it, drains it, then returns end-of-file; with no request outstanding it returns end-of-file at once. A signal ends the wait with `EINTR` and the request stays outstanding, `O_NONBLOCK` gives `EAGAIN`, and `poll`/`select` report the descriptor readable once the reply is there. A new write drops an unread reply.
|
|
629
629
|
- **The hook:** `signal` aborts with `'timeout'` after 10 s (the reply is then `{ v: 1, error: { code: 'ETIMEDOUT' } }`), `'replaced'` when a new write on the descriptor comes first, and `'withdrawn'` when the writer exits or the descriptor is closed. A throw or a reply that isn't an object becomes an `EIO` error reply, with the first line of the message; no hook is `ENOTCONN`. On the page, `createKernel` relays the request from the kernel worker and back, and the abort too.
|
|
630
630
|
|
|
631
|
+
**Exec tickets.** A `materialize` request needs `argv` (an array of strings), `path` and `cwd` (strings), and `names` (an array of strings) or `shellGrants: true`; anything else is `EINVAL` at the write. When the hook's reply carries a `ticket` (32 lowercase hex digits, no `error`), the kernel books it for the process that wrote the request: the pid, the `path`, the sha256 of the JSON of `argv`, and the `cwd`, for 30 s, at most 256 at a time. A spawn or exec whose environment has `SLICC_SEALED_TICKET` (from WASIX `proc_spawn`/`proc_exec`, an Emscripten `exec`, a JS program's `spawn` or `sync.run`, or `sudo`) redeems it, and the kernel takes the variable out of the child's environment either way. It also takes it out of what `run`, `spawn` and `openTerminal` are given. The ticket is used up by any attempt, and the spawn fails with `EACCES` (status 126 in bash, with no message beyond the errno) unless all of these hold:
|
|
632
|
+
- the ticket is booked and under 30 s old;
|
|
633
|
+
- the process spawning or exec'ing is the one that sent the `materialize`;
|
|
634
|
+
- the program's file is absolute and, with its symlinks resolved, the same file as `path` resolved the same way when the ticket was booked (so fnox sends the path it would exec, from a `PATH` lookup or not; bash hands over the file it found, with `argv[0]` as typed);
|
|
635
|
+
- the sha256 of the `argv` the spawn or exec asked for, with the `argv[0]` the child really gets (a spawn's `argv0`, unless the command's manifest fixes its own), is the booked one; arguments a command's manifest adds in front (`args`) are the command's, and aren't part of it;
|
|
636
|
+
- the child's directory, symlinks resolved, is `cwd`, and the child starts in that resolved directory.
|
|
637
|
+
|
|
638
|
+
The kernel then asks the hook `{ v: 1, op: 'materializeConsume', ticket, stamp, path, resolved, argv, cwd, interpreters, trusted }` (slicc-vault#10):
|
|
639
|
+
- `stamp` is the child's caller stamp, as above (`pid` the child's; `ppid` the process that spawned or exec'd it, which is the one that sent the `materialize`, so for an exec both are that process's pid), also given as `caller`, as on every request;
|
|
640
|
+
- `path` and `cwd` are the ones the `materialize` named, and `resolved` the file that runs, symlinks resolved;
|
|
641
|
+
- `interpreters` is the chain of `#!` and `binfmt` interpreters the kernel will really run, outermost first, each absolute with its symlinks resolved, and empty for a program of the catalog (Emscripten, WASI or JS) and the `/usr/bin/<name>` entries for one. A plain `#!/usr/bin/env X` (or `env -S X …`) is reported as `X`: a command of the catalog as `/usr/bin/X`, a script command by its file. `env` with any other option or a `NAME=value` assignment is run and reported as `/usr/bin/env` itself;
|
|
642
|
+
- `trusted` is true when the program and every interpreter are regular files owned by root and not writable by group or others, under directories that are all the same, up to `/` (for a command of the catalog, its package files). It's false unless `users` is `'enforce'`: under `'off'` files have no owners, and under `'report'` owners don't stop a write. The reply must be `{ values, placeholders }` (string maps) within 10 s; anything else is `EACCES`. Each variable of the child's environment whose value is exactly `placeholders[NAME]` gets `values[NAME]`; nothing else changes. The kernel keeps no copy and logs nothing of either. Like any environment, the values are inherited by whatever the child itself spawns or execs, as on Linux. A ticket isn't carried across `sudo`, which builds its own environment: a `sudo` run with `SLICC_SEALED_TICKET` set fails with `EACCES`, and burns the ticket.
|
|
643
|
+
|
|
644
|
+
**The child's output is scrubbed (S2).** The kernel compiles a [slicc-scrub](https://github.com/ai-ecoverse/slicc-scrub) scrubber (1.0.0, bundled; the published package has no runtime dependency) over exactly the values it swapped in, and gives the child new open file descriptions for fd 1 and fd 2, for every other descriptor it holds on the same descriptions (a `dup` of them), and for every descriptor on a terminal (fd 0 of a terminal session is one), that pass every write through it into the originals (one wrapper per original description, so `2>&1` keeps its order). A value appears as its placeholder, and a fragment or encoded form of it (JSON, percent, HTML, base64, hex, wrapped) as `[redacted:NAME]`. Bytes go through at once unless they may still be the start of a match; such a tail is let out after 50 ms without a write when it's shorter than 16 bytes, and otherwise at the next write or at close. The child's descendants inherit the scrubbed descriptions through fork, spawn, exec and `dup`, and a descendant that opens its controlling terminal (`/dev/tty`, or a terminal by name) gets one scrubbed too, as does a pseudo-terminal slave (`/dev/pts/N`) it opens. Files, sockets and pipes the child or a descendant opens itself are not scrubbed, so `printf %s "$T" > f` writes the value. A scrubbed fd 1 or 2 is a stream: it doesn't seek. A descendant that redeems a ticket of its own adds its values: its output is scrubbed for both sets. A value under 8 bytes is swapped but can't be scrubbed. The scrubber goes when the last process and description holding it do.
|
|
645
|
+
|
|
631
646
|
### `/dev/clipboard`
|
|
632
647
|
|
|
633
648
|
A character device (`0666`, major 10, minor 251), there in every `users` mode, through which programs read and write the embedder's clipboard (`createKernel({ clipboard: { read, write } })`; on the page, bios answers with `navigator.clipboard`). Emscripten programs (`cat /dev/clipboard`, `cat > /dev/clipboard`, see it in `/dev`), WASI programs (`std::fs`) and JS programs (`ctx.fs.readFile`, `ctx.fs.writeFile`, `ctx.open`) all open it.
|