@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.
package/dist/node.js CHANGED
@@ -3702,7 +3702,7 @@ function satisfies(version, range2) {
3702
3702
  }
3703
3703
 
3704
3704
  // src/version.ts
3705
- var KERNEL_VERSION = true ? "1.173.3" : "0.0.0-dev";
3705
+ var KERNEL_VERSION = true ? "1.174.0" : "0.0.0-dev";
3706
3706
  var OS_RELEASE = "6.0.0-slicc";
3707
3707
  function procVersion() {
3708
3708
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -23011,9 +23011,10 @@ var WasmProcess = class {
23011
23011
  cwd: typeof req.cwd === "string" && req.cwd.startsWith("/") ? req.cwd : "/",
23012
23012
  ...typeof req.term === "string" ? { term: req.term } : {}
23013
23013
  };
23014
- const answer2 = await sudo(call, this.blockingSignal(cancelled));
23014
+ const answer2 = await sudo(call, this.blockingSignal(cancelled), this.stdinKind());
23015
23015
  if (!("grant" in answer2)) return answer2;
23016
23016
  if (this.dying) throw new KernelError("EINTR");
23017
+ if (answer2.notice !== void 0) await this.complainLine(answer2.notice);
23017
23018
  const { file, argv, env, cwd, cred, caller } = answer2.grant;
23018
23019
  const stdio = [{ fd: 0 }, { fd: 1 }, { fd: 2 }];
23019
23020
  return {
@@ -23023,6 +23024,14 @@ var WasmProcess = class {
23023
23024
  })
23024
23025
  };
23025
23026
  }
23027
+ stdinKind() {
23028
+ if (!this.fds.has(0)) return "none";
23029
+ const { file } = this.fds.get(0);
23030
+ if (file.tty) return "tty";
23031
+ const meta = file.heldMeta;
23032
+ if (meta && "device" in meta) return meta.device === "null" ? "none" : "file";
23033
+ return file.seek || file.held ? "file" : "pipe";
23034
+ }
23026
23035
  async mountSyscall(req, cancelled) {
23027
23036
  const { mount, umount } = this.options;
23028
23037
  if (!mount || !umount) throw new KernelError("ENOSYS");
@@ -30645,24 +30654,36 @@ var SUDO_TIMEOUT_MS = 3e5;
30645
30654
  var NAME3 = /^[a-z_][a-z0-9_.-]*$/;
30646
30655
  var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
30647
30656
  var RULE = new RegExp(
30648
- String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?(?:Cwd\s+(${WORD})\s+)?Cmnd(?:\s+(.*))?$`
30657
+ String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()\s]+)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(none)\s+)?Cmnd(?:\s+(.*))?$`
30649
30658
  );
30650
30659
  var TERM = /^[A-Za-z0-9._+-]+$/;
30651
30660
  var PLAIN3 = /^[A-Za-z0-9_@%+=:,./-]+$/;
30652
- var CONTROL = /[\0-\x1f\x7f]/;
30661
+ var CONTROL = /[\p{Cc}\u2028\u2029]/u;
30662
+ var CONTROLS = /[\p{Cc}\u2028\u2029]/gu;
30653
30663
  var USAGE = "usage: sudo [-n] [-u user] [--] command [args...]\n sudo -l";
30654
30664
  var DENIED2 = "sudo: approval denied";
30655
30665
  var DENIAL_MAX = 500;
30656
30666
  var ABORTED = /* @__PURE__ */ Symbol("aborted");
30667
+ var RAN_ONCE = "sudo: ran once; this command line can't be recorded as always";
30668
+ var ROOT_ONLY_MAX = 64;
30669
+ var OPERANDS_MAX = 256;
30670
+ var PATH_MAX = 4096;
30657
30671
  function hasControl(text) {
30658
30672
  return CONTROL.test(text);
30659
30673
  }
30674
+ function stdinMatches(rule, stdin) {
30675
+ return rule.stdin === void 0 || stdin === "tty" || stdin === "none";
30676
+ }
30677
+ function recordable(cwd, argv) {
30678
+ return !hasControl(cwd) && !argv.some(hasControl);
30679
+ }
30660
30680
  function withoutControl(text) {
30661
- return text.replace(CONTROL, (ch) => {
30681
+ return text.replace(CONTROLS, (ch) => {
30662
30682
  const n = ch.charCodeAt(0);
30663
30683
  if (n === 9) return "\\t";
30664
30684
  if (n === 10) return "\\n";
30665
30685
  if (n === 13) return "\\r";
30686
+ if (n > 255) return `\\u${n.toString(16)}`;
30666
30687
  return `\\x${n.toString(16).padStart(2, "0")}`;
30667
30688
  });
30668
30689
  }
@@ -30846,11 +30867,17 @@ function parseRule(line) {
30846
30867
  if (text === "" || text.startsWith("#")) return null;
30847
30868
  const m = RULE.exec(text);
30848
30869
  if (!m) return void 0;
30849
- const [, nopasswd, who = "", target = "root", dir, rest2 = ""] = m;
30870
+ const [, nopasswd, who = "", target = "root", dir, stdin, rest2 = ""] = m;
30850
30871
  if (!validWho(who) || target !== "ALL" && !NAME3.test(target)) return void 0;
30851
30872
  const cwd = dir === void 0 ? void 0 : cwdOf(dir);
30852
30873
  if (cwd === "") return void 0;
30853
- const rule = { nopasswd: !!nopasswd, who, target, ...cwd !== void 0 ? { cwd } : {} };
30874
+ const rule = {
30875
+ nopasswd: !!nopasswd,
30876
+ who,
30877
+ target,
30878
+ ...cwd !== void 0 ? { cwd } : {},
30879
+ ...stdin === "none" ? { stdin: "none" } : {}
30880
+ };
30854
30881
  if (rest2.trim() === "*") return { ...rule, cmnd: "*" };
30855
30882
  const words2 = splitPatterns(rest2);
30856
30883
  const [first, ...args] = words2 ?? [];
@@ -30868,7 +30895,8 @@ function recordedBare(line, file, rule) {
30868
30895
  const head = `NOPASSWD ${rule.who} (${rule.target}) `;
30869
30896
  const cmnd = `Cmnd ${quoteWord(rule.cmnd.path)}`;
30870
30897
  const cwd = rule.cwd === void 0 ? "" : `Cwd ${quoteWord(rule.cwd)} `;
30871
- return line.trimEnd() === `${head}${cwd}${cmnd}`;
30898
+ const stdin = rule.stdin === void 0 ? "" : "Stdin none ";
30899
+ return line.trimEnd() === `${head}${cwd}${stdin}${cmnd}`;
30872
30900
  }
30873
30901
  function flags(parsed2, arg, next3) {
30874
30902
  for (let j = 1; j < arg.length; j++) {
@@ -30908,7 +30936,8 @@ function parseArgs(args) {
30908
30936
  function described(rule) {
30909
30937
  const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
30910
30938
  const cwd = rule.cwd === void 0 ? "" : `CWD=${quoteWord(rule.cwd)} `;
30911
- return ` (${rule.target}) ${cwd}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
30939
+ const stdin = rule.stdin === void 0 ? "" : "STDIN=none ";
30940
+ return ` (${rule.target}) ${cwd}${stdin}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
30912
30941
  }
30913
30942
  var Sudo = class {
30914
30943
  o;
@@ -30943,6 +30972,51 @@ var Sudo = class {
30943
30972
  }
30944
30973
  return true;
30945
30974
  }
30975
+ async rootOnly(cwd, args) {
30976
+ const { fs } = this.o;
30977
+ const found = {};
30978
+ const paths = [cwd];
30979
+ for (const arg of args.slice(0, OPERANDS_MAX)) {
30980
+ const value2 = /^--[^=]+=(.+)$/s.exec(arg)?.[1];
30981
+ for (const word of value2 === void 0 ? [arg] : [arg, value2]) {
30982
+ if (word === "" || word.length > PATH_MAX) continue;
30983
+ const raw2 = word.startsWith("/") ? word : `${cwd}/${word}`;
30984
+ if (word.startsWith("/") || await fs.lstat(normalizePath(raw2)).catch(() => void 0)) {
30985
+ paths.push(raw2);
30986
+ }
30987
+ }
30988
+ }
30989
+ for (const raw2 of paths) {
30990
+ const key = normalizePath(raw2);
30991
+ const known = Object.hasOwn(found, key);
30992
+ if (!known && Object.keys(found).length >= ROOT_ONLY_MAX) break;
30993
+ if (known && !found[key]) continue;
30994
+ found[key] = !await this.crossesLink(raw2) && await this.trusted(await this.existing(key));
30995
+ }
30996
+ return found;
30997
+ }
30998
+ async crossesLink(path) {
30999
+ const parts2 = [];
31000
+ for (const part of path.split("/")) {
31001
+ if (part === "" || part === ".") continue;
31002
+ if (part !== "..") {
31003
+ parts2.push(part);
31004
+ continue;
31005
+ }
31006
+ const at = `/${parts2.join("/")}`;
31007
+ const st = parts2.length > 0 ? await this.o.fs.lstat(at).catch(() => void 0) : void 0;
31008
+ if (st?.isSymbolicLink) return true;
31009
+ parts2.pop();
31010
+ }
31011
+ return false;
31012
+ }
31013
+ async existing(path) {
31014
+ let at = normalizePath(path);
31015
+ while (at !== "/" && !await this.o.fs.lstat(at).catch(() => void 0)) {
31016
+ at = at.slice(0, at.lastIndexOf("/")) || "/";
31017
+ }
31018
+ return at;
31019
+ }
30946
31020
  async policyFile(path) {
30947
31021
  if (!await this.trusted(path)) {
30948
31022
  this.warn(`sudo: ${path} skipped: it must be root's, no symlink, and writable by root only`);
@@ -31050,7 +31124,7 @@ var Sudo = class {
31050
31124
  if (untrusted2) return untrusted2;
31051
31125
  const argv = [path, ...args];
31052
31126
  const matching = rules.filter(
31053
- (rule) => applies(rule) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && commandMatches(rule.cmnd, argv)
31127
+ (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)
31054
31128
  );
31055
31129
  const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
31056
31130
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
@@ -31072,14 +31146,19 @@ var Sudo = class {
31072
31146
  }
31073
31147
  const changed = await this.untrusted(path, cwd, word);
31074
31148
  if (changed) return changed;
31075
- if (answer2 === "always" && !await this.remember(self, target, cwd, argv)) {
31076
- return failed3("EIO", "sudo: the rule could not be recorded");
31077
- }
31078
- return grant();
31149
+ if (answer2 === "once") return grant();
31150
+ return await this.always(self, target, cwd, argv, recordable(cwd, parsed2.command), grant);
31079
31151
  } finally {
31080
31152
  this.pending.delete(cred.ruid);
31081
31153
  }
31082
31154
  }
31155
+ async always(self, target, cwd, argv, shown, grant) {
31156
+ if (!shown || !recordable(cwd, argv)) return { ...grant(), notice: RAN_ONCE };
31157
+ if (!await this.remember(self, target, cwd, argv)) {
31158
+ return failed3("EIO", "sudo: the rule could not be recorded");
31159
+ }
31160
+ return grant();
31161
+ }
31083
31162
  listed(self, root, rules) {
31084
31163
  const host = this.o.hostname;
31085
31164
  if (root) {
@@ -31116,7 +31195,9 @@ ${lines2}
31116
31195
  ...caller.tty ? { tty: caller.tty } : {},
31117
31196
  createdAt,
31118
31197
  expiresAt: createdAt + timeout,
31119
- always: true
31198
+ always: recordable(cwd, [path, ...argv]),
31199
+ stdin: caller.stdin,
31200
+ rootOnly: await this.rootOnly(cwd, argv.slice(1))
31120
31201
  };
31121
31202
  const control = new AbortController();
31122
31203
  const timer = setTimeout(() => control.abort("expired"), timeout);
@@ -31139,7 +31220,6 @@ ${lines2}
31139
31220
  const { fs } = this.o;
31140
31221
  const path = `${SUDOERS_D}/${self.name}`;
31141
31222
  if (!NAME3.test(self.name)) return false;
31142
- if (hasControl(cwd) || argv.some(hasControl)) return false;
31143
31223
  try {
31144
31224
  await this.provision();
31145
31225
  if (!await this.trusted(SUDOERS_D)) return false;
@@ -31147,7 +31227,7 @@ ${lines2}
31147
31227
  if (st && !await this.trusted(path)) return false;
31148
31228
  const old = st ? await fs.readFile(path) : "";
31149
31229
  const words2 = recordedCommand(argv);
31150
- const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Cmnd ${words2}
31230
+ const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none Cmnd ${words2}
31151
31231
  `;
31152
31232
  const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
31153
31233
  ${line}`;
@@ -32913,7 +32993,7 @@ var Launcher = class {
32913
32993
  fileIds: this.rootFs.fileIdWrites?.() ?? 0,
32914
32994
  holds: this.rootFs.holdCounts?.()
32915
32995
  }),
32916
- sudo: (call, signal) => this.sudo(pid, call, signal),
32996
+ sudo: (call, signal, stdin) => this.sudo(pid, call, signal, stdin),
32917
32997
  crontab: (call) => this.crontab(pid, call),
32918
32998
  openFiles: this.openFiles,
32919
32999
  nodes: stack.nodes,
@@ -33153,12 +33233,13 @@ var Launcher = class {
33153
33233
  base: this.tagBases.get(pid)
33154
33234
  });
33155
33235
  }
33156
- sudo(pid, call, signal) {
33236
+ sudo(pid, call, signal, stdin) {
33157
33237
  const caller = {
33158
33238
  cred: this.credOf(pid),
33159
33239
  pid: this.jobs.shown(pid),
33160
33240
  tags: this.tagsOf(pid),
33161
- tty: this.described.get(pid)?.tty
33241
+ tty: this.described.get(pid)?.tty,
33242
+ stdin
33162
33243
  };
33163
33244
  return this.sudoer.request(caller, call, signal);
33164
33245
  }