@ai-ecoverse/slicc-kernel 1.173.2 → 1.173.4
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/index.js +11 -1
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +51 -10
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.cjs +4 -3
- package/dist/node-process-worker.cjs.map +2 -2
- package/dist/node-process-worker.js +4 -3
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +51 -10
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +4 -3
- package/dist/process-worker.js.map +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/node.d.ts +1 -1
- package/dist/types/sudo.d.ts +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1082,7 +1082,7 @@ With `users: 'enforce'` the kernel checks every file call that reaches it agains
|
|
|
1082
1082
|
- **The embedders' recipe** puts cones in `sudo` (`groups: ['sudo']`) and scoops not, so a cone may ask for any command and a scoop for none.
|
|
1083
1083
|
- **The command** is found once, on `/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` for a bare name or against the caller's directory for a path, and its real path (symlinks resolved) is what runs and what the policy sees. That file, and every file that will really run for it (a catalog command's glue, module and imports module, and the `package.json` that defines it, a script and the interpreter its `#!` line or `binfmt` suffix names), must be root's and writable by root only, and so must every directory above each, or `sudo` refuses (`EPERM`), whatever the policy says. A command that wouldn't start as the target user (a `#!` line naming an interpreter that is only the caller's, say) fails with `command not found` before anything is asked. Other files a program loads at run time (a package's libraries) are the admin's to keep safe, as on Linux. The caller's directory, which the program passes on, must be a directory the caller may enter, or `sudo` refuses (`EACCES`). The kernel resolves it to its real path once, shows that in the request, and runs the command there; if it no longer resolves the same, or the caller can no longer enter it, when the answer comes or when the command starts, nothing runs.
|
|
1084
1084
|
- **The decision:** a caller whose effective uid is 0 runs anything (the check on the command's path still applies). Otherwise a matching `NOPASSWD` line runs the command at once; any other matching line asks `approve`; nothing matching is refused (`sudo: cone is not allowed to run /usr/bin/ls as root`).
|
|
1085
|
-
- **Approval:** `approve(req, { signal })` gets `{ id, kind: 'sudo', user, uid, target, argv, path, cwd, pid, tag?, tty?, createdAt, expiresAt, always: true }`: the caller's name and real uid, the target user, the argv as typed, the resolved `path`, the caller's directory, pid, tag and terminal, and the times in ms. It answers `'once'` (run it), `'always'` (run it, and append `NOPASSWD <user> (<target>) Cwd <dir> Cmnd <path> <args…>` to `/etc/sudoers.d/<user>`, each argument a literal (a `*` written as `\*`) and `""` for a call without arguments, root's `0440`, so it's never asked again for this command, these arguments, in this directory; if that can't be written, or the command line has a control character, nothing runs and `sudo` fails with `sudo: the rule could not be recorded`) or `'deny'
|
|
1085
|
+
- **Approval:** `approve(req, { signal })` gets `{ id, kind: 'sudo', user, uid, target, argv, path, cwd, pid, tag?, tty?, createdAt, expiresAt, always: true }`: the caller's name and real uid, the target user, the argv as typed, the resolved `path`, the caller's directory, pid, tag and terminal, and the times in ms. It answers `'once'` (run it), `'always'` (run it, and append `NOPASSWD <user> (<target>) Cwd <dir> Cmnd <path> <args…>` to `/etc/sudoers.d/<user>`, each argument a literal (a `*` written as `\*`) and `""` for a call without arguments, root's `0440`, so it's never asked again for this command, these arguments, in this directory; if that can't be written, or the command line has a control character, nothing runs and `sudo` fails with `sudo: the rule could not be recorded`) or `'deny'`, or a denial that says who refused and why, `{ answer: 'deny', message: 'sudo: denied by the approval judge: <reason>' }` (`SudoDenial`): `sudo` prints the message (`EACCES`) on one line, with line breaks and tabs turned into spaces, other control characters removed, and cut to 500 characters (the last one `…`), when it starts with `sudo: ` and says something after it; otherwise, and for a throw, any other answer or no hook at all, it prints `sudo: approval denied`. After 5 minutes the request expires (`signal` aborts with `'expired'`, and `sudo` fails with `sudo: the request expired`); when the requester ends or gets a signal while it waits, `^C` included, `signal` aborts with `'withdrawn'` and `sudo` ends with that signal's status. `sudo: the request was withdrawn` means it was interrupted before any answer came: an answer the hook gave before the abort (its promise had settled) is reported, so a denial still prints its message, while `'once'` or `'always'` runs nothing once the requester has gone, and reads withdrawn (or expired); an answer after the abort is ignored. A user has at most one request waiting; another fails with `sudo: another request is pending`. On the page, `createKernel` relays the request from the kernel worker and back (a denial as `{ answer, message }`, without any other field), and the abort too, and what counts as first is what reaches the kernel worker first; `terminate()` or a failed worker aborts every request still waiting with `'withdrawn'`.
|
|
1086
1086
|
- **What `'always'` covers:** the same command with the same arguments in the same directory, so a relative argument (`tee config.toml`) can't be pointed elsewhere by running it from another directory. It doesn't make a command that writes, or an interpreter (`bash -c`, `tee`, `cp`), safe to allow: what such a command touches depends on the files it is given, which the caller may control (a symlink, a file it owns). Keep `'always'` to commands that are safe in any state of the files they're given. Lines that kernels before `Cwd` recorded have no `Cwd`, so they match in any directory; to drop them, rewrite `/etc/sudoers.d/<user>` as root.
|
|
1087
1087
|
- **Running:** the command runs as the caller's child, with all six ids and the groups of the target user, in the caller's directory, on its terminal, standard input, output and error, in a **process group of its own** in the caller's session, as sudo(8) runs it without a pty. When `sudo` has the terminal's foreground, it hands the terminal to the command's group, so `^C`, `^\` and `^Z` reach the command, and takes it back when the command stops or ends. `sudo` exits with the command's status, and passes `SIGTERM`, `SIGHUP`, `SIGINT`, `SIGQUIT`, `SIGUSR1`, `SIGUSR2` and `SIGALRM` on to the command's whole group, so its children get them too; it installs these relays before it asks, so one that comes while the command starts is passed on once it runs (the kernel lets `sudo` signal the group of the command it started, which its own user may not). When the command stops (`^Z`), `sudo` stops by the same signal, so the shell's `jobs`, `fg` and `bg` work on it; when it is continued, it hands the terminal back to the command if it is in the foreground (`fg`) and continues the command's group. A `sudo` killed with `-9` leaves the command running in its group, as sudo(8) does, and the shell takes the terminal back as for any job;
|
|
1088
1088
|
- **Symlinks:** a command `sudo` started, and every process it starts in turn, follows a symlink only when root or the target user owns it: Linux's `protected_symlinks`, applied in every directory rather than only in sticky world-writable ones. Any other link is `EACCES`, and the denial names the link. That holds in every runtime: in the kernel's own path walk (JS programs' `ctx.fs` and `ctx.open`, WASI paths, `exec` and `cd` by path), in an Emscripten program's lookups (where the denial's op is `follow`), and where a WASI program resolves `..` itself. So a link the caller put where a rule's file or directory is expected (`/tmp/k4l` → `/etc/fnox`, or `config.toml` → `/etc/fnox/config.toml` in the caller's own directory) leads nowhere. `readlink`, `ls -l`, `lstat`, and removing or renaming a link still work, since they don't follow it.
|
package/dist/index.js
CHANGED
|
@@ -2622,6 +2622,16 @@ function fileWatches(request, call) {
|
|
|
2622
2622
|
}
|
|
2623
2623
|
};
|
|
2624
2624
|
}
|
|
2625
|
+
function relayable(answer) {
|
|
2626
|
+
if (typeof answer === "symbol" || typeof answer === "function") return "deny";
|
|
2627
|
+
if (typeof answer !== "object" || answer === null) return answer;
|
|
2628
|
+
try {
|
|
2629
|
+
const { answer: kind, message } = answer;
|
|
2630
|
+
return kind === "deny" && typeof message === "string" ? { answer: kind, message } : "deny";
|
|
2631
|
+
} catch {
|
|
2632
|
+
return "deny";
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2625
2635
|
function approver(approve, worker) {
|
|
2626
2636
|
const asked = /* @__PURE__ */ new Map();
|
|
2627
2637
|
const handlers = {
|
|
@@ -2641,7 +2651,7 @@ function approver(approve, worker) {
|
|
|
2641
2651
|
}
|
|
2642
2652
|
asked.delete(req.id);
|
|
2643
2653
|
if (control.signal.aborted) return;
|
|
2644
|
-
worker.postMessage({ sudoAnswer: { id: req.id, answer } });
|
|
2654
|
+
worker.postMessage({ sudoAnswer: { id: req.id, answer: relayable(answer) } });
|
|
2645
2655
|
},
|
|
2646
2656
|
abort({ id, reason }) {
|
|
2647
2657
|
asked.get(id)?.abort(reason);
|