@ai-ecoverse/slicc-kernel 1.180.0 → 1.181.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/README.md +25 -1
- package/dist/driver-worker.js +15 -1
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js +26 -1
- package/dist/index.js.map +3 -3
- package/dist/kernel-worker.js +351 -88
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-driver-worker.js +15 -1
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.cjs +1 -1
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +367 -87
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +1 -1
- package/dist/types/client/attach.d.ts +12 -0
- package/dist/types/client/protocol.d.ts +9 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/kernel/children.d.ts +2 -1
- package/dist/types/launcher.d.ts +22 -0
- package/dist/types/node.d.ts +5 -3
- package/dist/types/serve.d.ts +11 -1
- package/dist/types/sudo.d.ts +15 -1
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -652,8 +652,8 @@ function dirsChannel(metadata) {
|
|
|
652
652
|
function noticeOf(data) {
|
|
653
653
|
if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
|
|
654
654
|
const notice = data ?? {};
|
|
655
|
-
const
|
|
656
|
-
return { paths:
|
|
655
|
+
const strings2 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
|
|
656
|
+
return { paths: strings2(notice.paths), trees: strings2(notice.trees), outside: true };
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
// src/fs/types.ts
|
|
@@ -8206,7 +8206,7 @@ var UserDb = class {
|
|
|
8206
8206
|
};
|
|
8207
8207
|
|
|
8208
8208
|
// src/client/protocol.ts
|
|
8209
|
-
var PROTOCOL = [1,
|
|
8209
|
+
var PROTOCOL = [1, 16];
|
|
8210
8210
|
var FS_METHODS = [
|
|
8211
8211
|
"readFile",
|
|
8212
8212
|
"writeFile",
|
|
@@ -8362,9 +8362,9 @@ async function accountOf(l, user) {
|
|
|
8362
8362
|
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
8363
8363
|
return found;
|
|
8364
8364
|
}
|
|
8365
|
-
async function runAs(l, self,
|
|
8366
|
-
if (
|
|
8367
|
-
const target = await accountOf(l,
|
|
8365
|
+
async function runAs(l, self, requested2) {
|
|
8366
|
+
if (requested2 === void 0) return self;
|
|
8367
|
+
const target = await accountOf(l, requested2);
|
|
8368
8368
|
if (self.uid !== 0 && target.uid !== self.uid) {
|
|
8369
8369
|
throw fsError("EPERM", `${self.name} may not run as ${target.name}`);
|
|
8370
8370
|
}
|
|
@@ -8373,21 +8373,9 @@ async function runAs(l, self, requested) {
|
|
|
8373
8373
|
function clientCred(self) {
|
|
8374
8374
|
return self ? credOf(self) : copyCred(ROOT);
|
|
8375
8375
|
}
|
|
8376
|
-
var MAX_SPAWN_TAGS = 8;
|
|
8377
|
-
function spawnTags(value2) {
|
|
8378
|
-
if (!Array.isArray(value2)) {
|
|
8379
|
-
tagOption(value2);
|
|
8380
|
-
return value2 === void 0 ? [] : [value2];
|
|
8381
|
-
}
|
|
8382
|
-
if (value2.length > MAX_SPAWN_TAGS) {
|
|
8383
|
-
throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
|
|
8384
|
-
}
|
|
8385
|
-
for (const each of value2) tagOption(each ?? 0);
|
|
8386
|
-
return value2;
|
|
8387
|
-
}
|
|
8388
8376
|
async function runTagged(l, self, options, tag, session = -1) {
|
|
8389
8377
|
let tags = withTag(void 0, tag);
|
|
8390
|
-
for (const added of
|
|
8378
|
+
for (const added of tagList(options.tag)) tags = withTag(tags, added);
|
|
8391
8379
|
return {
|
|
8392
8380
|
user: await runAs(l, self, options.user),
|
|
8393
8381
|
tag,
|
|
@@ -8572,6 +8560,20 @@ function requestBinding(send2, port) {
|
|
|
8572
8560
|
close: () => unbind?.()
|
|
8573
8561
|
};
|
|
8574
8562
|
}
|
|
8563
|
+
async function sudoRunAs(l, self, host, req, reply) {
|
|
8564
|
+
if (self.uid !== 0) throw sudoFailure("EPERM", "sudo: only a root client may run a sudo request");
|
|
8565
|
+
const { stdout, stderr, tag } = req.options;
|
|
8566
|
+
const running = await l.sudoRun(req.replay, {
|
|
8567
|
+
stdout,
|
|
8568
|
+
stderr,
|
|
8569
|
+
tag,
|
|
8570
|
+
base: host.tag,
|
|
8571
|
+
session: host.session
|
|
8572
|
+
});
|
|
8573
|
+
reply(req.id, { started: running.pid });
|
|
8574
|
+
const { status, signal } = await running.ended;
|
|
8575
|
+
return { result: status, ...signalField(signal) };
|
|
8576
|
+
}
|
|
8575
8577
|
function answered(pending, req, reply, detach) {
|
|
8576
8578
|
pending.then(
|
|
8577
8579
|
({ result, signal, transfer }) => {
|
|
@@ -8721,6 +8723,8 @@ function serveClient(port, host) {
|
|
|
8721
8723
|
case "trace":
|
|
8722
8724
|
case "untrace":
|
|
8723
8725
|
return traces.answer(l, self, req);
|
|
8726
|
+
case "sudo-run":
|
|
8727
|
+
return sudoRunAs(l, self, host, req, reply);
|
|
8724
8728
|
case "detach":
|
|
8725
8729
|
return { result: true };
|
|
8726
8730
|
default:
|
|
@@ -8904,7 +8908,7 @@ function satisfies(version, range2) {
|
|
|
8904
8908
|
}
|
|
8905
8909
|
|
|
8906
8910
|
// src/version.ts
|
|
8907
|
-
var KERNEL_VERSION = true ? "1.
|
|
8911
|
+
var KERNEL_VERSION = true ? "1.181.0" : "0.0.0-dev";
|
|
8908
8912
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8909
8913
|
function procVersion() {
|
|
8910
8914
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -9769,27 +9773,27 @@ function hashId(text) {
|
|
|
9769
9773
|
return hex4(a) + hex4(b);
|
|
9770
9774
|
}
|
|
9771
9775
|
function split2(text, count2) {
|
|
9772
|
-
const
|
|
9776
|
+
const words3 = [];
|
|
9773
9777
|
let rest2 = text;
|
|
9774
9778
|
for (let i = 0; i < count2; i++) {
|
|
9775
9779
|
const match = /^(\S+)\s+/.exec(rest2);
|
|
9776
9780
|
if (!match) return void 0;
|
|
9777
|
-
|
|
9781
|
+
words3.push(match[1]);
|
|
9778
9782
|
rest2 = rest2.slice(match[0].length);
|
|
9779
9783
|
}
|
|
9780
|
-
return [
|
|
9784
|
+
return [words3, rest2.trim()];
|
|
9781
9785
|
}
|
|
9782
9786
|
function jobOf(content, pending, ctx, env) {
|
|
9783
9787
|
const fields = content.startsWith("@") ? 1 : 5;
|
|
9784
9788
|
const parts2 = split2(content, fields + (ctx.system ? 1 : 0));
|
|
9785
9789
|
if (!parts2 || parts2[1] === "") throw new Error("no command");
|
|
9786
|
-
const [
|
|
9787
|
-
const schedule =
|
|
9790
|
+
const [words3, command] = parts2;
|
|
9791
|
+
const schedule = words3.slice(0, fields).join(" ");
|
|
9788
9792
|
const when = parseWhen(schedule);
|
|
9789
9793
|
const zone = env.CRON_TZ;
|
|
9790
9794
|
if (zone && !when.reboot && canonicalZone(zone) === void 0)
|
|
9791
9795
|
throw new Error(`CRON_TZ: unknown time zone "${zone}"`);
|
|
9792
|
-
const user = ctx.system ?
|
|
9796
|
+
const user = ctx.system ? words3[fields] : void 0;
|
|
9793
9797
|
if (user !== void 0 && !USER_NAME.test(user)) throw new Error(`bad user name ${user}`);
|
|
9794
9798
|
if (pending !== void 0 && !JOB_NAME.test(pending))
|
|
9795
9799
|
throw new Error(`bad job name "${pending}"`);
|
|
@@ -12770,9 +12774,11 @@ var SpawnError = class extends Error {
|
|
|
12770
12774
|
};
|
|
12771
12775
|
var ChangedError = class extends SpawnError {
|
|
12772
12776
|
notice;
|
|
12773
|
-
|
|
12777
|
+
file;
|
|
12778
|
+
constructor(notice, file) {
|
|
12774
12779
|
super("EPERM");
|
|
12775
12780
|
this.notice = notice;
|
|
12781
|
+
this.file = file;
|
|
12776
12782
|
}
|
|
12777
12783
|
};
|
|
12778
12784
|
function interrupted(signal) {
|
|
@@ -19818,21 +19824,21 @@ function unescaped(double) {
|
|
|
19818
19824
|
}
|
|
19819
19825
|
function splitWords(text) {
|
|
19820
19826
|
const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
|
|
19821
|
-
const
|
|
19827
|
+
const words3 = [];
|
|
19822
19828
|
let word;
|
|
19823
19829
|
while (piece.lastIndex < text.length) {
|
|
19824
19830
|
const m = piece.exec(text);
|
|
19825
19831
|
if (!m) return void 0;
|
|
19826
19832
|
const [, single, double, escaped, plain3, space] = m;
|
|
19827
19833
|
if (space !== void 0) {
|
|
19828
|
-
if (word !== void 0)
|
|
19834
|
+
if (word !== void 0) words3.push(word);
|
|
19829
19835
|
word = void 0;
|
|
19830
19836
|
continue;
|
|
19831
19837
|
}
|
|
19832
19838
|
word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
|
|
19833
19839
|
}
|
|
19834
|
-
if (word !== void 0)
|
|
19835
|
-
return
|
|
19840
|
+
if (word !== void 0) words3.push(word);
|
|
19841
|
+
return words3;
|
|
19836
19842
|
}
|
|
19837
19843
|
function quoteWord(word) {
|
|
19838
19844
|
const text = hasControl(word) ? withoutControl(word) : word;
|
|
@@ -19853,14 +19859,14 @@ function addPlain(word, plain3) {
|
|
|
19853
19859
|
}
|
|
19854
19860
|
function splitPatterns(text) {
|
|
19855
19861
|
const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
|
|
19856
|
-
const
|
|
19862
|
+
const words3 = [];
|
|
19857
19863
|
let word;
|
|
19858
19864
|
while (piece.lastIndex < text.length) {
|
|
19859
19865
|
const m = piece.exec(text);
|
|
19860
19866
|
if (!m) return void 0;
|
|
19861
19867
|
const [, single, double, escaped, plain3, space] = m;
|
|
19862
19868
|
if (space !== void 0) {
|
|
19863
|
-
if (word !== void 0)
|
|
19869
|
+
if (word !== void 0) words3.push(word);
|
|
19864
19870
|
word = void 0;
|
|
19865
19871
|
continue;
|
|
19866
19872
|
}
|
|
@@ -19868,8 +19874,8 @@ function splitPatterns(text) {
|
|
|
19868
19874
|
if (plain3 !== void 0) addPlain(word, plain3);
|
|
19869
19875
|
else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
|
|
19870
19876
|
}
|
|
19871
|
-
if (word !== void 0)
|
|
19872
|
-
return
|
|
19877
|
+
if (word !== void 0) words3.push(word);
|
|
19878
|
+
return words3;
|
|
19873
19879
|
}
|
|
19874
19880
|
var literal = (pattern) => pattern.map((p) => p ?? "*").join("");
|
|
19875
19881
|
var isAny = (pattern) => pattern?.length === 1 && pattern[0] === null;
|
|
@@ -19942,14 +19948,14 @@ function whoError(who) {
|
|
|
19942
19948
|
return `expected a user name, %group or ALL first, not ${shown(who)}`;
|
|
19943
19949
|
}
|
|
19944
19950
|
function cwdOf(word) {
|
|
19945
|
-
const
|
|
19946
|
-
const path =
|
|
19951
|
+
const words3 = splitWords(word);
|
|
19952
|
+
const path = words3?.length === 1 ? words3[0] : "";
|
|
19947
19953
|
return path.startsWith("/") ? normalizePath(path) : "";
|
|
19948
19954
|
}
|
|
19949
19955
|
function commandOf2(text, after) {
|
|
19950
|
-
const
|
|
19951
|
-
if (
|
|
19952
|
-
const [first, ...args] =
|
|
19956
|
+
const words3 = splitPatterns(text);
|
|
19957
|
+
if (words3 === void 0) return "a quote in the command is not closed, or it ends in a \\";
|
|
19958
|
+
const [first, ...args] = words3;
|
|
19953
19959
|
const path = first ? literal(first) : "";
|
|
19954
19960
|
if (path === "") return `expected a command after ${after}`;
|
|
19955
19961
|
if (!path.startsWith("/")) return `expected an absolute command path, not ${shown(path)}`;
|
|
@@ -20262,6 +20268,9 @@ function recordable(cwd, argv) {
|
|
|
20262
20268
|
function changedText(file) {
|
|
20263
20269
|
return `sudo: ${file} changed since it was approved`;
|
|
20264
20270
|
}
|
|
20271
|
+
function requestedText(file) {
|
|
20272
|
+
return `sudo: ${file} changed since it was requested`;
|
|
20273
|
+
}
|
|
20265
20274
|
function changedFile(approved, now) {
|
|
20266
20275
|
const files = /* @__PURE__ */ new Set([...Object.keys(approved), ...Object.keys(now)]);
|
|
20267
20276
|
return [...files].find((file) => approved[file] !== now[file]);
|
|
@@ -20269,6 +20278,67 @@ function changedFile(approved, now) {
|
|
|
20269
20278
|
function pinned(rule, bound3) {
|
|
20270
20279
|
return rule.sha256 === void 0 || changedFile(rule.sha256, bound3) === void 0;
|
|
20271
20280
|
}
|
|
20281
|
+
var REPLAY_ID = /^[!-~]{1,256}$/;
|
|
20282
|
+
var REPLAY_TAGS_MAX = 64;
|
|
20283
|
+
var REPLAY_ARGV_MAX = 4096;
|
|
20284
|
+
var REPLAY_BYTES_MAX = 1024 * 1024;
|
|
20285
|
+
var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
|
|
20286
|
+
function strings(value2, max) {
|
|
20287
|
+
return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
|
|
20288
|
+
}
|
|
20289
|
+
function words(value2) {
|
|
20290
|
+
if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
|
|
20291
|
+
const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
|
|
20292
|
+
return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
|
|
20293
|
+
}
|
|
20294
|
+
function filesOf(value2) {
|
|
20295
|
+
if (value2 === void 0) return {};
|
|
20296
|
+
if (!Array.isArray(value2) || value2.length > OPERANDS_MAX) return void 0;
|
|
20297
|
+
const files = {};
|
|
20298
|
+
for (const file of value2) {
|
|
20299
|
+
const { path, sha256: hex4 } = file ?? {};
|
|
20300
|
+
if (typeof path !== "string" || typeof hex4 !== "string") return void 0;
|
|
20301
|
+
files[path] = hex4;
|
|
20302
|
+
}
|
|
20303
|
+
return files;
|
|
20304
|
+
}
|
|
20305
|
+
function flagsOf(value2) {
|
|
20306
|
+
if (typeof value2 !== "object" || value2 === null || Array.isArray(value2)) return void 0;
|
|
20307
|
+
const entries = Object.entries(value2);
|
|
20308
|
+
if (entries.length > ROOT_ONLY_MAX || entries.some(([, v]) => typeof v !== "boolean")) {
|
|
20309
|
+
return void 0;
|
|
20310
|
+
}
|
|
20311
|
+
return Object.fromEntries(entries);
|
|
20312
|
+
}
|
|
20313
|
+
function replayOf(value2) {
|
|
20314
|
+
if (typeof value2 !== "object" || value2 === null) return failed2("EINVAL", NOT_A_REQUEST);
|
|
20315
|
+
const r = value2;
|
|
20316
|
+
if (r.validate === true) return failed2("EINVAL", "sudo: a sudo -v request runs no command");
|
|
20317
|
+
if (r.stdin === "pipe" || r.stdin === "file") {
|
|
20318
|
+
return failed2("EINVAL", `sudo: the request read a ${r.stdin}, and that input is gone`);
|
|
20319
|
+
}
|
|
20320
|
+
const { id: id2, user, uid, target, argv, path, cwd, stdin, tag, tags } = r;
|
|
20321
|
+
const rootOnly2 = flagsOf(r.rootOnly);
|
|
20322
|
+
const bound3 = filesOf(r.userWritable);
|
|
20323
|
+
const named = (s) => typeof s === "string" && s !== "";
|
|
20324
|
+
const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
|
|
20325
|
+
const valid = typeof id2 === "string" && REPLAY_ID.test(id2) && named(user) && named(target) && Number.isInteger(uid) && uid >= 0 && words(argv) && absolute2(path) && absolute2(cwd) && (stdin === "tty" || stdin === "none") && (tag === void 0 || typeof tag === "string") && (tags === void 0 || strings(tags, REPLAY_TAGS_MAX)) && rootOnly2 !== void 0 && bound3 !== void 0;
|
|
20326
|
+
if (!valid) return failed2("EINVAL", NOT_A_REQUEST);
|
|
20327
|
+
const all = tags ?? (tag === void 0 ? [] : [tag]);
|
|
20328
|
+
return {
|
|
20329
|
+
id: id2,
|
|
20330
|
+
user,
|
|
20331
|
+
uid,
|
|
20332
|
+
target,
|
|
20333
|
+
argv,
|
|
20334
|
+
path,
|
|
20335
|
+
cwd,
|
|
20336
|
+
stdin,
|
|
20337
|
+
rootOnly: rootOnly2,
|
|
20338
|
+
bound: bound3,
|
|
20339
|
+
tags: [...new Set(all)]
|
|
20340
|
+
};
|
|
20341
|
+
}
|
|
20272
20342
|
var VISUDO_FILES_MAX = 64;
|
|
20273
20343
|
var VISUDO_TEXT_MAX = 1024 * 1024;
|
|
20274
20344
|
function visudoCall(req) {
|
|
@@ -20670,6 +20740,61 @@ var Sudo = class {
|
|
|
20670
20740
|
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
20671
20741
|
return this.command(session, root, mine, call);
|
|
20672
20742
|
}
|
|
20743
|
+
async replay(value2) {
|
|
20744
|
+
const r = replayOf(value2);
|
|
20745
|
+
if ("error" in r) return r;
|
|
20746
|
+
if (this.o.mode !== "enforce") {
|
|
20747
|
+
return failed2("EINVAL", `sudo: users are '${this.o.mode}', so no request needed approval`);
|
|
20748
|
+
}
|
|
20749
|
+
const self = await this.o.users.lookup(r.user);
|
|
20750
|
+
if (!self) return failed2("ENOENT", `sudo: unknown user ${r.user}`);
|
|
20751
|
+
if (self.uid !== r.uid) return failed2("EPERM", `sudo: ${self.name} is no longer uid ${r.uid}`);
|
|
20752
|
+
const target = await this.lookup(r.target);
|
|
20753
|
+
if (!target) return failed2("ENOENT", `sudo: unknown user ${r.target}`);
|
|
20754
|
+
const cred = credOf(self);
|
|
20755
|
+
const [word = "", ...args] = r.argv;
|
|
20756
|
+
const argv = [r.path, ...args];
|
|
20757
|
+
if (!await this.mayAsk(self, target, r, argv)) {
|
|
20758
|
+
const quoted = argv.map(quoteWord).join(" ");
|
|
20759
|
+
return failed2(
|
|
20760
|
+
"EACCES",
|
|
20761
|
+
`sudo: ${self.name} may no longer ask to run ${quoted} as ${target.name}`
|
|
20762
|
+
);
|
|
20763
|
+
}
|
|
20764
|
+
if (await this.canonical(r.cwd) !== r.cwd) {
|
|
20765
|
+
return failed2("EACCES", `sudo: ${r.cwd} changed since it was requested`);
|
|
20766
|
+
}
|
|
20767
|
+
if (!await this.reachable(cred, r.cwd)) {
|
|
20768
|
+
return failed2(
|
|
20769
|
+
"EACCES",
|
|
20770
|
+
`sudo: cannot run in ${r.cwd}: it isn't a directory ${self.name} can enter`
|
|
20771
|
+
);
|
|
20772
|
+
}
|
|
20773
|
+
const path = await this.locate(word, r.cwd);
|
|
20774
|
+
if (!path) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20775
|
+
if (path !== r.path) return failed2("EPERM", `sudo: ${word} now runs ${path}, not ${r.path}`);
|
|
20776
|
+
const bound3 = await this.bound(path, r.cwd);
|
|
20777
|
+
if (!bound3) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20778
|
+
const changed = changedFile(r.bound, bound3);
|
|
20779
|
+
if (changed !== void 0) return failed2("EPERM", requestedText(changed));
|
|
20780
|
+
const now = await this.rootOnly(r.cwd, args);
|
|
20781
|
+
const flipped = Object.keys(r.rootOnly).find((at) => r.rootOnly[at] && now[at] !== true);
|
|
20782
|
+
if (flipped !== void 0) return failed2("EPERM", `sudo: ${flipped} is no longer root-only`);
|
|
20783
|
+
const { grant } = this.grant(self, target, cred, r.argv, path, r.cwd, void 0, bound3);
|
|
20784
|
+
(this.o.warn ?? console.warn)(
|
|
20785
|
+
`sudo: ${self.name} : TTY=unknown ; PWD=${quoteWord(r.cwd)} ; USER=${target.name} ; REPLAY=${r.id} ; COMMAND=${argv.map(quoteWord).join(" ")}`
|
|
20786
|
+
);
|
|
20787
|
+
return { grant, caller: self, target, tags: r.tags };
|
|
20788
|
+
}
|
|
20789
|
+
async mayAsk(self, target, r, argv) {
|
|
20790
|
+
if (self.uid === 0) return true;
|
|
20791
|
+
const cred = credOf(self);
|
|
20792
|
+
const { rules } = await this.policyRules({ cred, pid: 0, stdin: r.stdin });
|
|
20793
|
+
const applies = await this.applies(self, cred);
|
|
20794
|
+
return rules.some(
|
|
20795
|
+
(rule) => applies(rule.who) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === r.cwd) && stdinMatches(rule, r.stdin) && commandMatches(rule.cmnd, argv) && pinned(rule, r.bound)
|
|
20796
|
+
);
|
|
20797
|
+
}
|
|
20673
20798
|
async command(s, root, mine, call) {
|
|
20674
20799
|
const { caller, self, target, parsed: parsed2 } = s;
|
|
20675
20800
|
const { cred } = caller;
|
|
@@ -20849,8 +20974,8 @@ ${lines2}
|
|
|
20849
20974
|
const st = await fs.lstat(path).catch(() => void 0);
|
|
20850
20975
|
if (st && !await this.trusted(path)) return false;
|
|
20851
20976
|
const old = st ? await fs.readFile(path) : "";
|
|
20852
|
-
const
|
|
20853
|
-
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${
|
|
20977
|
+
const words3 = recordedCommand(argv);
|
|
20978
|
+
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${words3}
|
|
20854
20979
|
`;
|
|
20855
20980
|
const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
|
|
20856
20981
|
${line}`;
|
|
@@ -22155,8 +22280,8 @@ function ipv6(host) {
|
|
|
22155
22280
|
const head = groups(halves[0]);
|
|
22156
22281
|
const back = halves.length === 2 ? groups(halves[1]) : [];
|
|
22157
22282
|
if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
|
|
22158
|
-
const
|
|
22159
|
-
const missing6 = 8 -
|
|
22283
|
+
const words3 = [...head, ...back].map((g) => Number.parseInt(g, 16));
|
|
22284
|
+
const missing6 = 8 - words3.length - tail2.length;
|
|
22160
22285
|
if (halves.length === 2 ? missing6 < 1 : missing6 !== 0) return void 0;
|
|
22161
22286
|
const all = [
|
|
22162
22287
|
...head.map((g) => Number.parseInt(g, 16)),
|
|
@@ -25797,7 +25922,7 @@ function selectFlag(req) {
|
|
|
25797
25922
|
throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS: alpn with select");
|
|
25798
25923
|
return TLS_SERVER_SELECT;
|
|
25799
25924
|
}
|
|
25800
|
-
function
|
|
25925
|
+
function flagsOf2(req) {
|
|
25801
25926
|
return certFlags(req) | selectFlag(req) | (req.keylog ? TLS_KEYLOG : 0);
|
|
25802
25927
|
}
|
|
25803
25928
|
async function engineOf(proc, { select, keylog }) {
|
|
@@ -25848,7 +25973,7 @@ async function serverOf(proc, req) {
|
|
|
25848
25973
|
let mine;
|
|
25849
25974
|
try {
|
|
25850
25975
|
mine = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
|
|
25851
|
-
const flags2 =
|
|
25976
|
+
const flags2 = flagsOf2(req);
|
|
25852
25977
|
const store = mine ?? (flags2 & CERT_FLAGS ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
|
|
25853
25978
|
return {
|
|
25854
25979
|
engine,
|
|
@@ -27737,7 +27862,7 @@ var WRITE_ACCESS = 3;
|
|
|
27737
27862
|
var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
|
|
27738
27863
|
var STUCK_ESCAPES = /* @__PURE__ */ new Set([SIG.INT, SIG.TERM, SIG.QUIT, SIG.HUP]);
|
|
27739
27864
|
var DEFAULT_UMASK2 = 18;
|
|
27740
|
-
function
|
|
27865
|
+
function words2(value2) {
|
|
27741
27866
|
if (!Array.isArray(value2) || !value2.every((w) => typeof w === "string")) {
|
|
27742
27867
|
throw new KernelError("EINVAL");
|
|
27743
27868
|
}
|
|
@@ -29190,7 +29315,7 @@ var WasmProcess = class {
|
|
|
29190
29315
|
if (!sudo) throw new KernelError("ENOSYS");
|
|
29191
29316
|
const call = {
|
|
29192
29317
|
op: "proc-sudo",
|
|
29193
|
-
args:
|
|
29318
|
+
args: words2(req.args),
|
|
29194
29319
|
cwd: typeof req.cwd === "string" && req.cwd.startsWith("/") ? req.cwd : "/",
|
|
29195
29320
|
...typeof req.term === "string" ? { term: req.term } : {}
|
|
29196
29321
|
};
|
|
@@ -33800,7 +33925,40 @@ function describe(req, tty) {
|
|
|
33800
33925
|
}
|
|
33801
33926
|
function refuseChanged(approved, now) {
|
|
33802
33927
|
const changed = changedFile(approved, now);
|
|
33803
|
-
if (changed !== void 0) throw new ChangedError(changedText(changed));
|
|
33928
|
+
if (changed !== void 0) throw new ChangedError(changedText(changed), changed);
|
|
33929
|
+
}
|
|
33930
|
+
var O_WRONLY2 = 1;
|
|
33931
|
+
var OUTPUT_MODE = 384;
|
|
33932
|
+
var ERRNO_TEXT = {
|
|
33933
|
+
EACCES: "Permission denied",
|
|
33934
|
+
EISDIR: "Is a directory",
|
|
33935
|
+
ENOENT: "No such file or directory",
|
|
33936
|
+
ENOTDIR: "Not a directory",
|
|
33937
|
+
EROFS: "Read-only file system"
|
|
33938
|
+
};
|
|
33939
|
+
function sudoFailure(errno3, message3) {
|
|
33940
|
+
return Object.assign(new Error(message3), { code: errno3 });
|
|
33941
|
+
}
|
|
33942
|
+
function sudoCause(err) {
|
|
33943
|
+
const code = codeOf8(err);
|
|
33944
|
+
if (typeof code !== "string") throw err;
|
|
33945
|
+
return code;
|
|
33946
|
+
}
|
|
33947
|
+
function requested(err) {
|
|
33948
|
+
if (err instanceof ChangedError) throw sudoFailure(err.code, requestedText(err.file));
|
|
33949
|
+
throw err;
|
|
33950
|
+
}
|
|
33951
|
+
var MAX_SPAWN_TAGS = 8;
|
|
33952
|
+
function tagList(value2) {
|
|
33953
|
+
if (!Array.isArray(value2)) {
|
|
33954
|
+
tagOption(value2);
|
|
33955
|
+
return value2 === void 0 ? [] : [value2];
|
|
33956
|
+
}
|
|
33957
|
+
if (value2.length > MAX_SPAWN_TAGS) {
|
|
33958
|
+
throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
|
|
33959
|
+
}
|
|
33960
|
+
for (const each of value2) tagOption(each ?? 0);
|
|
33961
|
+
return value2;
|
|
33804
33962
|
}
|
|
33805
33963
|
function linksOf2(req) {
|
|
33806
33964
|
return req.links === void 0 ? {} : { links: req.links };
|
|
@@ -33926,27 +34084,27 @@ function appendedRoots(given) {
|
|
|
33926
34084
|
}
|
|
33927
34085
|
return [...roots];
|
|
33928
34086
|
}
|
|
33929
|
-
function plainEnv(
|
|
33930
|
-
const next3 =
|
|
33931
|
-
if (baseName2(
|
|
34087
|
+
function plainEnv(words3) {
|
|
34088
|
+
const next3 = words3[1];
|
|
34089
|
+
if (baseName2(words3[0] ?? "") !== "env" || next3 === void 0) return false;
|
|
33932
34090
|
return next3 === "-S" || !next3.startsWith("-") && !next3.includes("=");
|
|
33933
34091
|
}
|
|
33934
|
-
function shebangCommand(
|
|
33935
|
-
if (!plainEnv(
|
|
33936
|
-
const arg2 =
|
|
33937
|
-
return { interp:
|
|
34092
|
+
function shebangCommand(words3) {
|
|
34093
|
+
if (!plainEnv(words3)) {
|
|
34094
|
+
const arg2 = words3.slice(1).join(" ");
|
|
34095
|
+
return { interp: words3[0], args: arg2 ? [arg2] : [] };
|
|
33938
34096
|
}
|
|
33939
|
-
const split3 =
|
|
33940
|
-
const [interp, ...rest2] =
|
|
34097
|
+
const split3 = words3[1] === "-S";
|
|
34098
|
+
const [interp, ...rest2] = words3.slice(split3 ? 2 : 1);
|
|
33941
34099
|
const arg = rest2.join(" ");
|
|
33942
34100
|
return { interp, args: split3 ? rest2 : arg ? [arg] : [] };
|
|
33943
34101
|
}
|
|
33944
34102
|
function interpreter(head) {
|
|
33945
|
-
const
|
|
33946
|
-
if (!
|
|
33947
|
-
if (baseName2(
|
|
33948
|
-
if (
|
|
33949
|
-
return baseName2(
|
|
34103
|
+
const words3 = shebang(head);
|
|
34104
|
+
if (!words3) return "node";
|
|
34105
|
+
if (baseName2(words3[0]) === "env") words3.shift();
|
|
34106
|
+
if (words3[0] === "-S") words3.shift();
|
|
34107
|
+
return baseName2(words3[0] ?? "");
|
|
33950
34108
|
}
|
|
33951
34109
|
function expandDefaults(defaults2, env) {
|
|
33952
34110
|
const out = {};
|
|
@@ -34721,9 +34879,9 @@ var Launcher = class {
|
|
|
34721
34879
|
const head = await this.head(script);
|
|
34722
34880
|
if (!head) return void 0;
|
|
34723
34881
|
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
|
|
34724
|
-
const
|
|
34725
|
-
if (!
|
|
34726
|
-
const { interp, args } = shebangCommand(
|
|
34882
|
+
const words3 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
|
|
34883
|
+
if (!words3) return void 0;
|
|
34884
|
+
const { interp, args } = shebangCommand(words3);
|
|
34727
34885
|
const passed = [...args, command ? script : file, ...argv.slice(1)];
|
|
34728
34886
|
if (!interp) return void 0;
|
|
34729
34887
|
const found = await this.resolve(interp, interp, cwd, scope);
|
|
@@ -34954,7 +35112,7 @@ var Launcher = class {
|
|
|
34954
35112
|
const hex4 = pinned2[real];
|
|
34955
35113
|
if (hex4 !== void 0) return { [real]: hex4 };
|
|
34956
35114
|
if (await this.sudoer.trusted(real)) return {};
|
|
34957
|
-
throw new ChangedError(changedText(real));
|
|
35115
|
+
throw new ChangedError(changedText(real), real);
|
|
34958
35116
|
}
|
|
34959
35117
|
async pinnedBytes(pin, bytes) {
|
|
34960
35118
|
const [real = ""] = Object.keys(pin);
|
|
@@ -35233,6 +35391,10 @@ var Launcher = class {
|
|
|
35233
35391
|
}
|
|
35234
35392
|
async launch(asked, req) {
|
|
35235
35393
|
const cwd = await physicalPathAsync(this.base, req.cwd);
|
|
35394
|
+
if (req.exactCwd && cwd !== req.cwd) {
|
|
35395
|
+
await req.fds.closeAll();
|
|
35396
|
+
throw sudoFailure("EACCES", `sudo: ${req.cwd} changed since it was requested`);
|
|
35397
|
+
}
|
|
35236
35398
|
const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
|
|
35237
35399
|
await this.pnpmReady();
|
|
35238
35400
|
let loaded;
|
|
@@ -36928,6 +37090,96 @@ var Launcher = class {
|
|
|
36928
37090
|
const signal = handle.termsig();
|
|
36929
37091
|
return { status, ...signal ? { signal } : {}, stdout: concat7(out), stderr: concat7(err) };
|
|
36930
37092
|
}
|
|
37093
|
+
async sudoRun(replay, options = {}) {
|
|
37094
|
+
const added = tagList(options.tag);
|
|
37095
|
+
const replayed = await this.sudoer.replay(replay);
|
|
37096
|
+
if ("error" in replayed) throw sudoFailure(replayed.errno, replayed.error);
|
|
37097
|
+
const { grant, caller, target } = replayed;
|
|
37098
|
+
let tags = withTag(void 0, options.base);
|
|
37099
|
+
for (const tag of [...replayed.tags, ...added]) tags = withTag(tags, tag);
|
|
37100
|
+
const tagged = { tags, tagBase: options.base === void 0 ? 0 : 1 };
|
|
37101
|
+
await this.swaps.settled();
|
|
37102
|
+
this.resetCatalogs();
|
|
37103
|
+
const req = { file: grant.file, argv: grant.argv, env: grant.env, cwd: grant.cwd };
|
|
37104
|
+
const ctx = { ppid: 0, cred: grant.cred, scope: "" };
|
|
37105
|
+
const planned = await this.planOne(req, ctx).catch((err2) => {
|
|
37106
|
+
const code = sudoCause(err2);
|
|
37107
|
+
const word = grant.argv[0] ?? grant.file;
|
|
37108
|
+
if (code === "ENOENT") throw sudoFailure(code, `sudo: ${word}: command not found`);
|
|
37109
|
+
throw sudoFailure(code, `sudo: ${grant.file}: ${ERRNO_TEXT[code] ?? code}`);
|
|
37110
|
+
});
|
|
37111
|
+
const pin = await this.unchanged(planned, grant.file, grant.cwd, grant).catch(requested);
|
|
37112
|
+
const fds = await this.sudoOutputs(options, caller, this.stackFor(tags, tagged.tagBase));
|
|
37113
|
+
const err = fds.get(2).file;
|
|
37114
|
+
const report = (message3) => void Promise.resolve(err.write?.(encoder10.encode(`${message3}
|
|
37115
|
+
`))).catch(() => void 0);
|
|
37116
|
+
const replan = () => this.planOne(req, ctx);
|
|
37117
|
+
if (!await this.grantedCwd(grant.caller, grant.cwd)) {
|
|
37118
|
+
await fds.closeAll();
|
|
37119
|
+
throw sudoFailure("EACCES", `sudo: ${grant.cwd} changed since it was requested`);
|
|
37120
|
+
}
|
|
37121
|
+
const handle = await this.launch(
|
|
37122
|
+
{ ...planned, replan },
|
|
37123
|
+
{
|
|
37124
|
+
...pin,
|
|
37125
|
+
env: grant.env,
|
|
37126
|
+
cwd: grant.cwd,
|
|
37127
|
+
fds,
|
|
37128
|
+
report,
|
|
37129
|
+
...this.identity(target, tagged, options.session),
|
|
37130
|
+
...caller.umask !== void 0 ? { umask: caller.umask } : {},
|
|
37131
|
+
links: grant.cred.euid,
|
|
37132
|
+
exactCwd: true
|
|
37133
|
+
}
|
|
37134
|
+
).catch(requested);
|
|
37135
|
+
const ended = handle.exited.then((status) => {
|
|
37136
|
+
const signal = handle.termsig();
|
|
37137
|
+
return { status, ...signal ? { signal } : {} };
|
|
37138
|
+
});
|
|
37139
|
+
return { pid: handle.pid, ended };
|
|
37140
|
+
}
|
|
37141
|
+
async sudoOutputs(options, caller, stack) {
|
|
37142
|
+
const fds = new FdTable();
|
|
37143
|
+
const opened2 = /* @__PURE__ */ new Map();
|
|
37144
|
+
fds.installAt(0, deviceFile("null"));
|
|
37145
|
+
try {
|
|
37146
|
+
fds.installAt(1, await this.sudoOutput(options.stdout, caller, stack, opened2));
|
|
37147
|
+
fds.installAt(2, await this.sudoOutput(options.stderr, caller, stack, opened2));
|
|
37148
|
+
} catch (err) {
|
|
37149
|
+
await fds.closeAll();
|
|
37150
|
+
throw err;
|
|
37151
|
+
}
|
|
37152
|
+
return fds;
|
|
37153
|
+
}
|
|
37154
|
+
async sudoOutput(path, caller, stack, opened2) {
|
|
37155
|
+
if (path === void 0) return deviceFile("null");
|
|
37156
|
+
if (typeof path !== "string" || !path.startsWith("/")) {
|
|
37157
|
+
throw sudoFailure("EINVAL", `sudo: ${String(path)} is not an absolute path`);
|
|
37158
|
+
}
|
|
37159
|
+
const cred = credOf(caller);
|
|
37160
|
+
const walked = await walk(this.fs, cred, normalizePath(path), true, caller.uid);
|
|
37161
|
+
const real = walked.path;
|
|
37162
|
+
const same3 = opened2.get(real);
|
|
37163
|
+
if (same3) return same3.retain();
|
|
37164
|
+
try {
|
|
37165
|
+
if (walked.denied !== void 0) throw fsError("EACCES", path);
|
|
37166
|
+
await writable({ ...this.execGuard(cred, 0), links: caller.uid }, "open", real);
|
|
37167
|
+
const owner = { uid: caller.uid, gid: caller.gid, mode: OUTPUT_MODE };
|
|
37168
|
+
await stack.noLinks.writeFile(real, new Uint8Array(0), owner);
|
|
37169
|
+
} catch (err) {
|
|
37170
|
+
const code = sudoCause(err);
|
|
37171
|
+
throw sudoFailure(code, `sudo: ${path}: ${ERRNO_TEXT[code] ?? code}`);
|
|
37172
|
+
}
|
|
37173
|
+
const file = vfsFile(stack.fs, { path: real, flags: O_WRONLY2, position: 0 }, stack.nodes);
|
|
37174
|
+
const again = await walk(this.fs, cred, real, true, caller.uid);
|
|
37175
|
+
const st = await stack.noLinks.lstat(real).catch(() => void 0);
|
|
37176
|
+
if (again.path !== real || again.denied !== void 0 || !st?.isFile) {
|
|
37177
|
+
await file.release();
|
|
37178
|
+
throw sudoFailure("EACCES", `sudo: ${path} changed while it was opened`);
|
|
37179
|
+
}
|
|
37180
|
+
opened2.set(real, file);
|
|
37181
|
+
return file;
|
|
37182
|
+
}
|
|
36931
37183
|
};
|
|
36932
37184
|
function cronLocks() {
|
|
36933
37185
|
return globalThis.navigator?.locks;
|
|
@@ -37393,11 +37645,38 @@ function policyAsker(port) {
|
|
|
37393
37645
|
}
|
|
37394
37646
|
};
|
|
37395
37647
|
}
|
|
37648
|
+
function processOps(ready2, reply) {
|
|
37649
|
+
const terminals = /* @__PURE__ */ new Map();
|
|
37650
|
+
async function terminal(req) {
|
|
37651
|
+
const onData = (bytes) => reply(req.id, { fd: 1, bytes });
|
|
37652
|
+
const session = await (await ready2()).openTerminal(req.argv, { ...req.options, onData });
|
|
37653
|
+
terminals.set(req.id, session);
|
|
37654
|
+
reply(req.id, { started: session.pid });
|
|
37655
|
+
try {
|
|
37656
|
+
const status = await session.exited;
|
|
37657
|
+
const signal = session.termsig();
|
|
37658
|
+
return signal ? { status, signal } : status;
|
|
37659
|
+
} finally {
|
|
37660
|
+
terminals.delete(req.id);
|
|
37661
|
+
}
|
|
37662
|
+
}
|
|
37663
|
+
async function sudoRun(req) {
|
|
37664
|
+
const running = await (await ready2()).sudoRun(req.replay, req.options);
|
|
37665
|
+
reply(req.id, { started: running.pid });
|
|
37666
|
+
return running.ended;
|
|
37667
|
+
}
|
|
37668
|
+
function onTerminal(req) {
|
|
37669
|
+
const session = terminals.get(req.terminal);
|
|
37670
|
+
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
37671
|
+
act2(session, req);
|
|
37672
|
+
return true;
|
|
37673
|
+
}
|
|
37674
|
+
return { terminal, sudoRun, act: onTerminal };
|
|
37675
|
+
}
|
|
37396
37676
|
function serveKernel(port, deps) {
|
|
37397
37677
|
let launcher;
|
|
37398
37678
|
let remote;
|
|
37399
37679
|
let uplink;
|
|
37400
|
-
const terminals = /* @__PURE__ */ new Map();
|
|
37401
37680
|
const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
|
|
37402
37681
|
const clients = /* @__PURE__ */ new Set();
|
|
37403
37682
|
const sealed = sealedAsker(port);
|
|
@@ -37426,19 +37705,7 @@ function serveKernel(port, deps) {
|
|
|
37426
37705
|
if (!launcher) throw new Error("the kernel is not initialized");
|
|
37427
37706
|
return launcher;
|
|
37428
37707
|
}
|
|
37429
|
-
|
|
37430
|
-
const onData = (bytes) => reply(req.id, { fd: 1, bytes });
|
|
37431
|
-
const session = await (await ready2()).openTerminal(req.argv, { ...req.options, onData });
|
|
37432
|
-
terminals.set(req.id, session);
|
|
37433
|
-
reply(req.id, { started: session.pid });
|
|
37434
|
-
try {
|
|
37435
|
-
const status = await session.exited;
|
|
37436
|
-
const signal = session.termsig();
|
|
37437
|
-
return signal ? { status, signal } : status;
|
|
37438
|
-
} finally {
|
|
37439
|
-
terminals.delete(req.id);
|
|
37440
|
-
}
|
|
37441
|
-
}
|
|
37708
|
+
const processes2 = processOps(ready2, reply);
|
|
37442
37709
|
async function init(req) {
|
|
37443
37710
|
launcher = rootOf(deps, req).then(async (root) => {
|
|
37444
37711
|
req = await rootNamed(deps, req, root);
|
|
@@ -37493,7 +37760,8 @@ function serveKernel(port, deps) {
|
|
|
37493
37760
|
if (req.op === "init") return init(req);
|
|
37494
37761
|
if (isAttestRequest(req)) return attestOp(await ready2(), req);
|
|
37495
37762
|
if (req.op === "sealed-placeholders") return (await ready2()).setPlaceholders(req.list);
|
|
37496
|
-
if (req.op === "open-terminal") return terminal(req);
|
|
37763
|
+
if (req.op === "open-terminal") return processes2.terminal(req);
|
|
37764
|
+
if (req.op === "sudo-run") return processes2.sudoRun(req);
|
|
37497
37765
|
if (req.op === "connect") return connect2(req.user, req.tag, req.requests);
|
|
37498
37766
|
if (isUsersRequest(req)) return usersOp(await ready2(), req);
|
|
37499
37767
|
if (req.op === "routes" || req.op === "transport-traits") {
|
|
@@ -37502,12 +37770,7 @@ function serveKernel(port, deps) {
|
|
|
37502
37770
|
if (req.op === "dial") return dial(await ready2(), req);
|
|
37503
37771
|
if (req.op === "mount" || req.op === "umount" || req.op === "mounts" || req.op === "insert")
|
|
37504
37772
|
return mountOp(req);
|
|
37505
|
-
if (req.op === "terminal")
|
|
37506
|
-
const session = terminals.get(req.terminal);
|
|
37507
|
-
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
37508
|
-
act2(session, req);
|
|
37509
|
-
return true;
|
|
37510
|
-
}
|
|
37773
|
+
if (req.op === "terminal") return processes2.act(req);
|
|
37511
37774
|
return (await ready2()).run(req.argv, {
|
|
37512
37775
|
...req.options,
|
|
37513
37776
|
onStdout: (bytes) => reply(req.id, { fd: 1, bytes }),
|