@ai-ecoverse/slicc-kernel 1.85.0 → 1.85.1
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 +1 -1
- package/dist/kernel-worker.js +9 -8
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node.js +9 -8
- package/dist/node.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -648,7 +648,7 @@ The kernel then asks the hook `{ v: 1, op: 'materializeConsume', ticket, stamp,
|
|
|
648
648
|
- `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;
|
|
649
649
|
- `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.
|
|
650
650
|
|
|
651
|
-
**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.
|
|
651
|
+
**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`) or [`/dev/clipboard`](#devclipboard) it opens, and a `/dev/clipboard` descriptor it inherited is wrapped at the redeem like fd 1 and 2, so the page's clipboard gets placeholders, never the values. 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.
|
|
652
652
|
|
|
653
653
|
### `/dev/clipboard`
|
|
654
654
|
|
package/dist/kernel-worker.js
CHANGED
|
@@ -11843,7 +11843,7 @@ function ptySyscall(req, ctx) {
|
|
|
11843
11843
|
const pair = ctx.ptys.get(req.n);
|
|
11844
11844
|
ttyOpenable(ctx.guard, pair.slave);
|
|
11845
11845
|
const slave = pair.openSlave();
|
|
11846
|
-
const fd = ctx.fds.install(ctx.
|
|
11846
|
+
const fd = ctx.fds.install(ctx.outputFilter?.(slave) ?? slave, 3);
|
|
11847
11847
|
if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
|
|
11848
11848
|
return json4(fd);
|
|
11849
11849
|
}
|
|
@@ -13009,8 +13009,8 @@ var WasmProcess = class {
|
|
|
13009
13009
|
const { ptys, jobs } = this.options;
|
|
13010
13010
|
const owner = this.options.owners ? this.ttyOwner() : void 0;
|
|
13011
13011
|
const guard2 = this.guarded();
|
|
13012
|
-
const {
|
|
13013
|
-
const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner,
|
|
13012
|
+
const { outputFilter } = this.options;
|
|
13013
|
+
const ctx = { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner, outputFilter };
|
|
13014
13014
|
return ptySyscall(req, ctx);
|
|
13015
13015
|
}
|
|
13016
13016
|
return await this.procSyscall(req, cancelled);
|
|
@@ -13187,7 +13187,7 @@ var WasmProcess = class {
|
|
|
13187
13187
|
if (!tty) throw new KernelError("ENXIO");
|
|
13188
13188
|
if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
|
|
13189
13189
|
const file = tty.file();
|
|
13190
|
-
return this.fds.install(this.options.
|
|
13190
|
+
return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
|
|
13191
13191
|
}
|
|
13192
13192
|
openSealed() {
|
|
13193
13193
|
const broker = this.options.sealed;
|
|
@@ -13199,7 +13199,8 @@ var WasmProcess = class {
|
|
|
13199
13199
|
if (!broker) throw new KernelError("ENOENT");
|
|
13200
13200
|
const mode = flags2 & WRITE_ACCESS;
|
|
13201
13201
|
const access2 = mode === 0 ? "read" : mode === 1 ? "write" : "both";
|
|
13202
|
-
|
|
13202
|
+
const file = broker.open(access2, await this.clipboardStamp());
|
|
13203
|
+
return this.fds.install(this.options.outputFilter?.(file) ?? file, 3);
|
|
13203
13204
|
}
|
|
13204
13205
|
async clipboardStamp() {
|
|
13205
13206
|
const shown = await this.options.identity?.();
|
|
@@ -13998,7 +13999,7 @@ function processOptions(opts, header3, hooks, created3) {
|
|
|
13998
13999
|
...inherited(opts),
|
|
13999
14000
|
...opts.owners ? { owners: true } : {},
|
|
14000
14001
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
14001
|
-
...opts.
|
|
14002
|
+
...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
|
|
14002
14003
|
...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
|
|
14003
14004
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
14004
14005
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
|
|
@@ -15000,7 +15001,7 @@ function scrubStdio(fds, scrubber) {
|
|
|
15000
15001
|
const stdio = stdioFiles(fds);
|
|
15001
15002
|
for (const fd of fds.numbers()) {
|
|
15002
15003
|
const original = fds.get(fd);
|
|
15003
|
-
if (!stdio.has(original) && !original.file.tty) continue;
|
|
15004
|
+
if (!stdio.has(original) && !original.file.tty && !original.file.clipboard) continue;
|
|
15004
15005
|
const known = wrapped.get(original);
|
|
15005
15006
|
const flags2 = fds.statusFlags(fd);
|
|
15006
15007
|
const wrapper = known ? known.retain() : scrubbed(original.retain(), scrubber);
|
|
@@ -18079,7 +18080,7 @@ var Launcher = class {
|
|
|
18079
18080
|
...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
|
|
18080
18081
|
...this.owners ? { owners: true } : {},
|
|
18081
18082
|
sealed: this.sealed,
|
|
18082
|
-
...scrubs ? {
|
|
18083
|
+
...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
|
|
18083
18084
|
clipboard: this.clipboard,
|
|
18084
18085
|
...this.dac ? { dac: this.dac } : {},
|
|
18085
18086
|
program: req.program,
|