@ai-ecoverse/slicc-kernel 1.63.0 → 1.64.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/dist/node.js CHANGED
@@ -410,6 +410,11 @@ var Created = class {
410
410
  function deny(g, op, path, errno = "EACCES") {
411
411
  g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
412
412
  }
413
+ var TTY_MODE = 400;
414
+ function ttyOpenable(g, tty) {
415
+ if (!g || !tty.owner || permits(g.cred, tty.owner, R_OK | W_OK, false)) return;
416
+ deny(g, "open", tty.name ?? "/dev/tty");
417
+ }
413
418
  async function need(g, op, path, want, options = {}, shown = path) {
414
419
  const walked = await access(g.fs, g.cred, path, want, options);
415
420
  if (walked.denied !== void 0) deny(g, op, shown);
@@ -2307,10 +2312,18 @@ var UserDb = class {
2307
2312
  };
2308
2313
  }
2309
2314
  async lookup(user) {
2315
+ return (await this.lookupTerminal(user))?.account;
2316
+ }
2317
+ async ttyGroup() {
2318
+ return parsed(parseGroup(await this.read(GROUP, DEFAULT_GROUP))).find((g) => g.name === "tty")?.gid;
2319
+ }
2320
+ async lookupTerminal(user) {
2310
2321
  const { passwd, group } = await this.tables();
2311
2322
  const key = typeof user === "number" ? "uid" : "name";
2312
2323
  const row = parsed(passwd).find((r) => r[key] === user);
2313
- return row && this.account(row, parsed(group));
2324
+ const groups = parsed(group);
2325
+ const tty = groups.find((g) => g.name === "tty")?.gid;
2326
+ return row && { account: this.account(row, groups), tty };
2314
2327
  }
2315
2328
  async list() {
2316
2329
  const { passwd, group } = await this.tables();
@@ -3030,6 +3043,9 @@ var OpfsFs = class {
3030
3043
  trackOwners() {
3031
3044
  this.owners = true;
3032
3045
  }
3046
+ async hasOwner(path) {
3047
+ return ownOf((await this.meta.get([path]))[0]) !== void 0;
3048
+ }
3033
3049
  async ancestry(path) {
3034
3050
  const entries = await this.meta.get(chain(path));
3035
3051
  const out = [];
@@ -3801,8 +3817,10 @@ var UnlinkedKeeper = class {
3801
3817
  count = 0;
3802
3818
  links = /* @__PURE__ */ new Map();
3803
3819
  linked = /* @__PURE__ */ new Map();
3820
+ dirOwner;
3804
3821
  constructor(fs, holders, rootOf, options = {}) {
3805
3822
  const { locks, owner } = options;
3823
+ this.dirOwner = options.dirOwner;
3806
3824
  this.fs = fs;
3807
3825
  this.holders = holders;
3808
3826
  this.rootOf = rootOf;
@@ -3913,7 +3931,8 @@ var UnlinkedKeeper = class {
3913
3931
  const dir = dirOf(this.rootOf(path));
3914
3932
  const hidden = `${dir}/${this.instance}.${++this.count}`;
3915
3933
  try {
3916
- await this.fs.mkdir(dir, { recursive: true });
3934
+ const owner = this.dirOwner;
3935
+ await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
3917
3936
  await this.fs.rename(path, hidden);
3918
3937
  await this.linkedName(hidden);
3919
3938
  return hidden;
@@ -5071,6 +5090,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
5071
5090
  ...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
5072
5091
  ...fs.chown ? { chown: fs.chown.bind(fs) } : {},
5073
5092
  ...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
5093
+ ...fs.hasOwner ? { hasOwner: fs.hasOwner.bind(fs) } : {},
5074
5094
  chmod: (path, mode) => fs.chmod(path, mode),
5075
5095
  utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
5076
5096
  lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
@@ -6442,6 +6462,10 @@ var MountFs = class {
6442
6462
  if (this.at(real)) throw errnoError("ENOSYS", path);
6443
6463
  return real;
6444
6464
  }
6465
+ async hasOwner(path) {
6466
+ if (this.at(path)) return true;
6467
+ return await this.base.hasOwner?.(path) ?? true;
6468
+ }
6445
6469
  async ancestry(path) {
6446
6470
  const names = chain(normalizePath(path));
6447
6471
  const first = names.findIndex((name2) => this.at(name2));
@@ -9578,6 +9602,7 @@ var KernelTty = class {
9578
9602
  waiters = [];
9579
9603
  hungUp = false;
9580
9604
  name;
9605
+ owner;
9581
9606
  onRead;
9582
9607
  readers = 0;
9583
9608
  screen;
@@ -9921,10 +9946,11 @@ var PtyTable = class {
9921
9946
  this.signal = signal;
9922
9947
  this.onRead = onRead;
9923
9948
  }
9924
- open() {
9949
+ open(owner) {
9925
9950
  let index = 0;
9926
9951
  while (this.pairs.has(index)) index += 1;
9927
9952
  const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index), this.onRead);
9953
+ if (owner) pair.slave.owner = { uid: owner.uid, gid: owner.gid, mode: TTY_MODE };
9928
9954
  this.pairs.set(index, pair);
9929
9955
  return { pair, master: pair.master() };
9930
9956
  }
@@ -9937,6 +9963,10 @@ var PtyTable = class {
9937
9963
  return [...this.pairs.keys()].sort((a, b) => a - b);
9938
9964
  }
9939
9965
  };
9966
+ function ptsNumber(path) {
9967
+ const m = /^\/dev\/pts\/(\d+)$/.exec(path);
9968
+ return m ? Number(m[1]) : void 0;
9969
+ }
9940
9970
  var PTY_OPS = [
9941
9971
  "pty-open",
9942
9972
  "pty-slave-open",
@@ -9964,11 +9994,12 @@ function ptySyscall(req, ctx) {
9964
9994
  switch (req.op) {
9965
9995
  case "pty-open": {
9966
9996
  if (!ctx.ptys) throw new KernelError("ENOENT");
9967
- return json4(ctx.fds.install(ctx.ptys.open().master, 3));
9997
+ return json4(ctx.fds.install(ctx.ptys.open(ctx.owner).master, 3));
9968
9998
  }
9969
9999
  case "pty-slave-open": {
9970
10000
  if (!ctx.ptys) throw new KernelError("ENOENT");
9971
10001
  const pair = ctx.ptys.get(req.n);
10002
+ ttyOpenable(ctx.guard, pair.slave);
9972
10003
  const fd = ctx.fds.install(pair.openSlave(), 3);
9973
10004
  if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
9974
10005
  return json4(fd);
@@ -10784,6 +10815,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
10784
10815
  "fd-promote",
10785
10816
  "fd-open-tty",
10786
10817
  "fd-tty-names",
10818
+ "fd-tty-owner",
10787
10819
  "tty-get",
10788
10820
  "tty-set",
10789
10821
  "tty-winsz",
@@ -10985,7 +11017,9 @@ var WasmProcess = class {
10985
11017
  }
10986
11018
  if (isPtySyscall(req)) {
10987
11019
  const { ptys, jobs } = this.options;
10988
- return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs });
11020
+ const owner = this.options.owners ? this.ttyOwner() : void 0;
11021
+ const guard2 = this.guarded();
11022
+ return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs, guard: guard2, owner });
10989
11023
  }
10990
11024
  return await this.procSyscall(req, cancelled);
10991
11025
  } catch (e) {
@@ -11117,6 +11151,25 @@ var WasmProcess = class {
11117
11151
  const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
11118
11152
  await openable(g, "open", req.path, want, req.create === true);
11119
11153
  }
11154
+ openTty(name2) {
11155
+ const tty = name2 === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(name2);
11156
+ if (!tty) throw new KernelError("ENXIO");
11157
+ if (name2 !== void 0) ttyOpenable(this.guarded(), tty);
11158
+ return this.fds.install(tty.file(), 3);
11159
+ }
11160
+ guarded() {
11161
+ const { guard: guard2 } = this.options;
11162
+ return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
11163
+ }
11164
+ ttyOwner() {
11165
+ return { uid: this.cred.euid, gid: this.cred.egid, mode: TTY_MODE };
11166
+ }
11167
+ terminalByName(name2) {
11168
+ const n = ptsNumber(name2);
11169
+ const tty = n === void 0 ? this.options.jobs?.terminalNamed(name2) : this.options.ptys?.get(n).slave;
11170
+ if (!tty) throw new KernelError("ENOENT");
11171
+ return tty;
11172
+ }
11120
11173
  newOwner() {
11121
11174
  if (!this.options.owners) return void 0;
11122
11175
  return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
@@ -11220,13 +11273,12 @@ var WasmProcess = class {
11220
11273
  if (!req.keep) await Promise.resolve(this.fds.close(req.from));
11221
11274
  }
11222
11275
  return { ok: true, kind: "void" };
11223
- case "fd-open-tty": {
11224
- const tty = req.name === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(req.name);
11225
- if (!tty) throw new KernelError("ENXIO");
11226
- return { ok: true, kind: "json", json: this.fds.install(tty.file(), 3) };
11227
- }
11276
+ case "fd-open-tty":
11277
+ return { ok: true, kind: "json", json: this.openTty(req.name) };
11228
11278
  case "fd-tty-names":
11229
11279
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
11280
+ case "fd-tty-owner":
11281
+ return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
11230
11282
  case "fd-select": {
11231
11283
  const { read, write, timeoutMs } = req;
11232
11284
  if (this.owed && timeoutMs !== 0) {
@@ -14002,6 +14054,16 @@ var NOT_FOUND = 127;
14002
14054
  var DECIDE_MS = 100;
14003
14055
  var INIT_PID = 1;
14004
14056
  var SHARED_DIRS = ["/tmp", "/home"];
14057
+ var LAYOUT = [
14058
+ ["/", 493],
14059
+ ["/root", 448],
14060
+ ["/home", 493],
14061
+ ["/etc", 493],
14062
+ [PASSWD, 420],
14063
+ [GROUP, 420],
14064
+ ["/usr", 493],
14065
+ ["/usr/local", 493]
14066
+ ];
14005
14067
  var encoder3 = new TextEncoder();
14006
14068
  var USERS_MODES = ["off", "report", "enforce"];
14007
14069
  function usersMode(mode = "off") {
@@ -14195,7 +14257,8 @@ var Launcher = class {
14195
14257
  {
14196
14258
  locks: webLocks(),
14197
14259
  busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
14198
- owner: options.unlinkOwner
14260
+ owner: options.unlinkOwner,
14261
+ ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
14199
14262
  }
14200
14263
  );
14201
14264
  this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
@@ -14820,6 +14883,24 @@ var Launcher = class {
14820
14883
  await this.provisionPnpmHome();
14821
14884
  return entry;
14822
14885
  }
14886
+ async stampLayout() {
14887
+ const layout = [
14888
+ ...LAYOUT,
14889
+ [this.modulesDir, 493],
14890
+ [this.pnpmHome, 493],
14891
+ [`/${UNLINKED_DIR}`, 448]
14892
+ ];
14893
+ for (const [path, mode] of layout) {
14894
+ const real = await this.realLayoutPath(path);
14895
+ const fresh = real !== void 0 && !await this.base.hasOwner?.(real);
14896
+ if (fresh) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
14897
+ }
14898
+ }
14899
+ async realLayoutPath(path) {
14900
+ if (!await this.base.exists(path)) return void 0;
14901
+ if (path === "/") return path;
14902
+ return joinName(await realDir(this.base, parentOf(path)), baseName(path));
14903
+ }
14823
14904
  async provisionPnpmHome() {
14824
14905
  await this.base.mkdir(this.pnpmHome, { recursive: true }).catch(() => void 0);
14825
14906
  }
@@ -14893,6 +14974,7 @@ var Launcher = class {
14893
14974
  if (this.owners) await this.base.chmod("/tmp", 1023);
14894
14975
  await this.provisionPnpmHome();
14895
14976
  await this.users.ensure();
14977
+ if (this.owners) await this.stampLayout();
14896
14978
  await writeCaFile(this.base, this.ca).catch(() => void 0);
14897
14979
  this.fstab = this.mountFstab();
14898
14980
  }
@@ -14921,7 +15003,12 @@ var Launcher = class {
14921
15003
  }
14922
15004
  async account(user) {
14923
15005
  if (typeof user === "object") return user;
14924
- const found = await this.users.lookup(user ?? 0);
15006
+ return (await this.terminalAccount(user)).account;
15007
+ }
15008
+ async terminalAccount(user) {
15009
+ if (typeof user === "object")
15010
+ return { account: user, tty: this.owners ? await this.users.ttyGroup() : void 0 };
15011
+ const found = await this.users.lookupTerminal(user ?? 0);
14925
15012
  if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
14926
15013
  return found;
14927
15014
  }
@@ -14943,7 +15030,7 @@ var Launcher = class {
14943
15030
  return { cwd, file, planned };
14944
15031
  }
14945
15032
  async openTerminal(argv, options) {
14946
- const account = await this.account(options.user);
15033
+ const { account, tty: ttyGroup } = await this.terminalAccount(options.user);
14947
15034
  const { cwd, file, planned } = await this.starting(argv, options.cwd, account);
14948
15035
  if (!planned) throw new Error(`${file}: command not found`);
14949
15036
  let leader = 0;
@@ -14953,6 +15040,7 @@ var Launcher = class {
14953
15040
  );
14954
15041
  tty.onRead = () => this.settling.reading(this.jobs.tcgetpgrp(tty, leader));
14955
15042
  tty.name = `/dev/tty${++this.terminals}`;
15043
+ if (this.owners) tty.owner = { uid: account.uid, gid: ttyGroup ?? account.gid, mode: TTY_MODE };
14956
15044
  tty.setSize(options.cols ?? 80, options.rows ?? 24);
14957
15045
  const fds = new FdTable();
14958
15046
  const stdio = tty.file();