@ai-ecoverse/slicc-kernel 1.173.3 → 1.174.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.
@@ -8740,7 +8740,7 @@ function satisfies(version, range2) {
8740
8740
  }
8741
8741
 
8742
8742
  // src/version.ts
8743
- var KERNEL_VERSION = true ? "1.173.3" : "0.0.0-dev";
8743
+ var KERNEL_VERSION = true ? "1.174.0" : "0.0.0-dev";
8744
8744
  var OS_RELEASE = "6.0.0-slicc";
8745
8745
  function procVersion() {
8746
8746
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -27458,9 +27458,10 @@ var WasmProcess = class {
27458
27458
  cwd: typeof req.cwd === "string" && req.cwd.startsWith("/") ? req.cwd : "/",
27459
27459
  ...typeof req.term === "string" ? { term: req.term } : {}
27460
27460
  };
27461
- const answer2 = await sudo(call, this.blockingSignal(cancelled));
27461
+ const answer2 = await sudo(call, this.blockingSignal(cancelled), this.stdinKind());
27462
27462
  if (!("grant" in answer2)) return answer2;
27463
27463
  if (this.dying) throw new KernelError("EINTR");
27464
+ if (answer2.notice !== void 0) await this.complainLine(answer2.notice);
27464
27465
  const { file, argv, env, cwd, cred, caller } = answer2.grant;
27465
27466
  const stdio = [{ fd: 0 }, { fd: 1 }, { fd: 2 }];
27466
27467
  return {
@@ -27470,6 +27471,14 @@ var WasmProcess = class {
27470
27471
  })
27471
27472
  };
27472
27473
  }
27474
+ stdinKind() {
27475
+ if (!this.fds.has(0)) return "none";
27476
+ const { file } = this.fds.get(0);
27477
+ if (file.tty) return "tty";
27478
+ const meta = file.heldMeta;
27479
+ if (meta && "device" in meta) return meta.device === "null" ? "none" : "file";
27480
+ return file.seek || file.held ? "file" : "pipe";
27481
+ }
27473
27482
  async mountSyscall(req, cancelled) {
27474
27483
  const { mount, umount } = this.options;
27475
27484
  if (!mount || !umount) throw new KernelError("ENOSYS");
@@ -31745,24 +31754,36 @@ var SUDO_TIMEOUT_MS = 3e5;
31745
31754
  var NAME3 = /^[a-z_][a-z0-9_.-]*$/;
31746
31755
  var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
31747
31756
  var RULE = new RegExp(
31748
- String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?(?:Cwd\s+(${WORD})\s+)?Cmnd(?:\s+(.*))?$`
31757
+ String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(none)\s+)?Cmnd(?:\s+(.*))?$`
31749
31758
  );
31750
31759
  var TERM = /^[A-Za-z0-9._+-]+$/;
31751
31760
  var PLAIN3 = /^[A-Za-z0-9_@%+=:,./-]+$/;
31752
- var CONTROL = /[\0-\x1f\x7f]/;
31761
+ var CONTROL = /[\p{Cc}\u2028\u2029]/u;
31762
+ var CONTROLS = /[\p{Cc}\u2028\u2029]/gu;
31753
31763
  var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
31754
31764
  var DENIED2 = "sudo: approval denied";
31755
31765
  var DENIAL_MAX = 500;
31756
31766
  var ABORTED = /* @__PURE__ */ Symbol("aborted");
31767
+ var RAN_ONCE = "sudo: ran once; this command line can't be recorded as always";
31768
+ var ROOT_ONLY_MAX = 64;
31769
+ var OPERANDS_MAX = 256;
31770
+ var PATH_MAX = 4096;
31757
31771
  function hasControl(text) {
31758
31772
  return CONTROL.test(text);
31759
31773
  }
31774
+ function stdinMatches(rule, stdin) {
31775
+ return rule.stdin === void 0 || stdin === "tty" || stdin === "none";
31776
+ }
31777
+ function recordable(cwd, argv) {
31778
+ return !hasControl(cwd) && !argv.some(hasControl);
31779
+ }
31760
31780
  function withoutControl(text) {
31761
- return text.replace(CONTROL, (ch) => {
31781
+ return text.replace(CONTROLS, (ch) => {
31762
31782
  const n = ch.charCodeAt(0);
31763
31783
  if (n === 9) return "\\t";
31764
31784
  if (n === 10) return "\\n";
31765
31785
  if (n === 13) return "\\r";
31786
+ if (n > 255) return `\\u${n.toString(16)}`;
31766
31787
  return `\\x${n.toString(16).padStart(2, "0")}`;
31767
31788
  });
31768
31789
  }
@@ -31946,11 +31967,17 @@ function parseRule(line) {
31946
31967
  if (text === "" || text.startsWith("#")) return null;
31947
31968
  const m = RULE.exec(text);
31948
31969
  if (!m) return void 0;
31949
- const [, nopasswd, who = "", target = "root", dir, rest2 = ""] = m;
31970
+ const [, nopasswd, who = "", target = "root", dir, stdin, rest2 = ""] = m;
31950
31971
  if (!validWho(who) || target !== "ALL" && !NAME3.test(target)) return void 0;
31951
31972
  const cwd = dir === void 0 ? void 0 : cwdOf(dir);
31952
31973
  if (cwd === "") return void 0;
31953
- const rule = { nopasswd: !!nopasswd, who, target, ...cwd !== void 0 ? { cwd } : {} };
31974
+ const rule = {
31975
+ nopasswd: !!nopasswd,
31976
+ who,
31977
+ target,
31978
+ ...cwd !== void 0 ? { cwd } : {},
31979
+ ...stdin === "none" ? { stdin: "none" } : {}
31980
+ };
31954
31981
  if (rest2.trim() === "*") return { ...rule, cmnd: "*" };
31955
31982
  const words2 = splitPatterns(rest2);
31956
31983
  const [first, ...args] = words2 ?? [];
@@ -31968,7 +31995,8 @@ function recordedBare(line, file, rule) {
31968
31995
  const head = `NOPASSWD ${rule.who} (${rule.target}) `;
31969
31996
  const cmnd = `Cmnd ${quoteWord(rule.cmnd.path)}`;
31970
31997
  const cwd = rule.cwd === void 0 ? "" : `Cwd ${quoteWord(rule.cwd)} `;
31971
- return line.trimEnd() === `${head}${cwd}${cmnd}`;
31998
+ const stdin = rule.stdin === void 0 ? "" : "Stdin none ";
31999
+ return line.trimEnd() === `${head}${cwd}${stdin}${cmnd}`;
31972
32000
  }
31973
32001
  function flags(parsed2, arg, next3) {
31974
32002
  for (let j = 1; j < arg.length; j++) {
@@ -32008,7 +32036,8 @@ function parseArgs(args) {
32008
32036
  function described(rule) {
32009
32037
  const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
32010
32038
  const cwd = rule.cwd === void 0 ? "" : `CWD=${quoteWord(rule.cwd)} `;
32011
- return ` (${rule.target}) ${cwd}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
32039
+ const stdin = rule.stdin === void 0 ? "" : "STDIN=none ";
32040
+ return ` (${rule.target}) ${cwd}${stdin}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
32012
32041
  }
32013
32042
  var Sudo = class {
32014
32043
  o;
@@ -32043,6 +32072,51 @@ var Sudo = class {
32043
32072
  }
32044
32073
  return true;
32045
32074
  }
32075
+ async rootOnly(cwd, args) {
32076
+ const { fs } = this.o;
32077
+ const found = {};
32078
+ const paths = [cwd];
32079
+ for (const arg of args.slice(0, OPERANDS_MAX)) {
32080
+ const value2 = /^--[^=]+=(.+)$/s.exec(arg)?.[1];
32081
+ for (const word of value2 === void 0 ? [arg] : [arg, value2]) {
32082
+ if (word === "" || word.length > PATH_MAX) continue;
32083
+ const raw2 = word.startsWith("/") ? word : `${cwd}/${word}`;
32084
+ if (word.startsWith("/") || await fs.lstat(normalizePath(raw2)).catch(() => void 0)) {
32085
+ paths.push(raw2);
32086
+ }
32087
+ }
32088
+ }
32089
+ for (const raw2 of paths) {
32090
+ const key = normalizePath(raw2);
32091
+ const known = Object.hasOwn(found, key);
32092
+ if (!known && Object.keys(found).length >= ROOT_ONLY_MAX) break;
32093
+ if (known && !found[key]) continue;
32094
+ found[key] = !await this.crossesLink(raw2) && await this.trusted(await this.existing(key));
32095
+ }
32096
+ return found;
32097
+ }
32098
+ async crossesLink(path) {
32099
+ const parts2 = [];
32100
+ for (const part of path.split("/")) {
32101
+ if (part === "" || part === ".") continue;
32102
+ if (part !== "..") {
32103
+ parts2.push(part);
32104
+ continue;
32105
+ }
32106
+ const at = `/${parts2.join("/")}`;
32107
+ const st = parts2.length > 0 ? await this.o.fs.lstat(at).catch(() => void 0) : void 0;
32108
+ if (st?.isSymbolicLink) return true;
32109
+ parts2.pop();
32110
+ }
32111
+ return false;
32112
+ }
32113
+ async existing(path) {
32114
+ let at = normalizePath(path);
32115
+ while (at !== "/" && !await this.o.fs.lstat(at).catch(() => void 0)) {
32116
+ at = at.slice(0, at.lastIndexOf("/")) || "/";
32117
+ }
32118
+ return at;
32119
+ }
32046
32120
  async policyFile(path) {
32047
32121
  if (!await this.trusted(path)) {
32048
32122
  this.warn(`sudo: ${path} skipped: it must be root's, no symlink, and writable by root only`);
@@ -32150,7 +32224,7 @@ var Sudo = class {
32150
32224
  if (untrusted2) return untrusted2;
32151
32225
  const argv = [path, ...args];
32152
32226
  const matching = rules.filter(
32153
- (rule) => applies(rule) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && commandMatches(rule.cmnd, argv)
32227
+ (rule) => applies(rule) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv)
32154
32228
  );
32155
32229
  const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
32156
32230
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
@@ -32172,14 +32246,19 @@ var Sudo = class {
32172
32246
  }
32173
32247
  const changed = await this.untrusted(path, cwd, word);
32174
32248
  if (changed) return changed;
32175
- if (answer2 === "always" && !await this.remember(self, target, cwd, argv)) {
32176
- return failed3("EIO", "sudo: the rule could not be recorded");
32177
- }
32178
- return grant();
32249
+ if (answer2 === "once") return grant();
32250
+ return await this.always(self, target, cwd, argv, recordable(cwd, parsed2.command), grant);
32179
32251
  } finally {
32180
32252
  this.pending.delete(cred.ruid);
32181
32253
  }
32182
32254
  }
32255
+ async always(self, target, cwd, argv, shown, grant) {
32256
+ if (!shown || !recordable(cwd, argv)) return { ...grant(), notice: RAN_ONCE };
32257
+ if (!await this.remember(self, target, cwd, argv)) {
32258
+ return failed3("EIO", "sudo: the rule could not be recorded");
32259
+ }
32260
+ return grant();
32261
+ }
32183
32262
  listed(self, root, rules) {
32184
32263
  const host = this.o.hostname;
32185
32264
  if (root) {
@@ -32216,7 +32295,9 @@ ${lines2}
32216
32295
  ...caller.tty ? { tty: caller.tty } : {},
32217
32296
  createdAt,
32218
32297
  expiresAt: createdAt + timeout,
32219
- always: true
32298
+ always: recordable(cwd, [path, ...argv]),
32299
+ stdin: caller.stdin,
32300
+ rootOnly: await this.rootOnly(cwd, argv.slice(1))
32220
32301
  };
32221
32302
  const control = new AbortController();
32222
32303
  const timer = setTimeout(() => control.abort("expired"), timeout);
@@ -32239,7 +32320,6 @@ ${lines2}
32239
32320
  const { fs } = this.o;
32240
32321
  const path = `${SUDOERS_D}/${self.name}`;
32241
32322
  if (!NAME3.test(self.name)) return false;
32242
- if (hasControl(cwd) || argv.some(hasControl)) return false;
32243
32323
  try {
32244
32324
  await this.provision();
32245
32325
  if (!await this.trusted(SUDOERS_D)) return false;
@@ -32247,7 +32327,7 @@ ${lines2}
32247
32327
  if (st && !await this.trusted(path)) return false;
32248
32328
  const old = st ? await fs.readFile(path) : "";
32249
32329
  const words2 = recordedCommand(argv);
32250
- const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Cmnd ${words2}
32330
+ const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none Cmnd ${words2}
32251
32331
  `;
32252
32332
  const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
32253
32333
  ${line}`;
@@ -34017,7 +34097,7 @@ var Launcher = class {
34017
34097
  fileIds: this.rootFs.fileIdWrites?.() ?? 0,
34018
34098
  holds: this.rootFs.holdCounts?.()
34019
34099
  }),
34020
- sudo: (call, signal) => this.sudo(pid, call, signal),
34100
+ sudo: (call, signal, stdin) => this.sudo(pid, call, signal, stdin),
34021
34101
  crontab: (call) => this.crontab(pid, call),
34022
34102
  openFiles: this.openFiles,
34023
34103
  nodes: stack.nodes,
@@ -34257,12 +34337,13 @@ var Launcher = class {
34257
34337
  base: this.tagBases.get(pid)
34258
34338
  });
34259
34339
  }
34260
- sudo(pid, call, signal) {
34340
+ sudo(pid, call, signal, stdin) {
34261
34341
  const caller = {
34262
34342
  cred: this.credOf(pid),
34263
34343
  pid: this.jobs.shown(pid),
34264
34344
  tags: this.tagsOf(pid),
34265
- tty: this.described.get(pid)?.tty
34345
+ tty: this.described.get(pid)?.tty,
34346
+ stdin
34266
34347
  };
34267
34348
  return this.sudoer.request(caller, call, signal);
34268
34349
  }