@ai-ecoverse/slicc-kernel 1.179.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 +41 -2
- package/dist/driver-worker.js +67 -1
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js +78 -1
- package/dist/index.js.map +3 -3
- package/dist/kernel-worker.js +553 -93
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +67 -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 +619 -90
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +1 -1
- package/dist/types/client/attach.d.ts +27 -3
- package/dist/types/client/protocol.d.ts +29 -4
- package/dist/types/index.d.ts +5 -3
- package/dist/types/kernel/children.d.ts +2 -1
- package/dist/types/kernel/jobs.d.ts +2 -1
- package/dist/types/kernel/trace.d.ts +59 -0
- package/dist/types/launcher.d.ts +31 -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 +2 -2
package/dist/node.js
CHANGED
|
@@ -2751,6 +2751,82 @@ var KERNEL_SIGINFO = Object.freeze({
|
|
|
2751
2751
|
status: 0
|
|
2752
2752
|
});
|
|
2753
2753
|
|
|
2754
|
+
// src/kernel/trace.ts
|
|
2755
|
+
var TRACE_LIMIT = 4096;
|
|
2756
|
+
var TRACE_LIMIT_MAX = 65536;
|
|
2757
|
+
function traceLimit(value2) {
|
|
2758
|
+
if (value2 === void 0) return TRACE_LIMIT;
|
|
2759
|
+
if (!Number.isInteger(value2) || value2 < 1 || value2 > TRACE_LIMIT_MAX) {
|
|
2760
|
+
throw Object.assign(new Error(`a trace limit is an integer from 1 to ${TRACE_LIMIT_MAX}`), {
|
|
2761
|
+
code: "EINVAL"
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
return value2;
|
|
2765
|
+
}
|
|
2766
|
+
var TraceHub = class {
|
|
2767
|
+
subs = /* @__PURE__ */ new Set();
|
|
2768
|
+
seq = 0;
|
|
2769
|
+
get idle() {
|
|
2770
|
+
return this.subs.size === 0;
|
|
2771
|
+
}
|
|
2772
|
+
subscribe(tag, viewer, deliver, limit = TRACE_LIMIT) {
|
|
2773
|
+
const sub = {
|
|
2774
|
+
tag,
|
|
2775
|
+
viewer: copyCred(viewer),
|
|
2776
|
+
limit,
|
|
2777
|
+
queue: [],
|
|
2778
|
+
dropped: 0,
|
|
2779
|
+
waiting: false,
|
|
2780
|
+
scheduled: false,
|
|
2781
|
+
deliver
|
|
2782
|
+
};
|
|
2783
|
+
this.subs.add(sub);
|
|
2784
|
+
return {
|
|
2785
|
+
ack: () => {
|
|
2786
|
+
sub.waiting = false;
|
|
2787
|
+
this.schedule(sub);
|
|
2788
|
+
},
|
|
2789
|
+
close: () => {
|
|
2790
|
+
this.subs.delete(sub);
|
|
2791
|
+
sub.queue = [];
|
|
2792
|
+
}
|
|
2793
|
+
};
|
|
2794
|
+
}
|
|
2795
|
+
wants(tags, cred) {
|
|
2796
|
+
if (!tags?.length || this.subs.size === 0) return false;
|
|
2797
|
+
for (const sub of this.subs) if (this.matches(sub, tags, cred)) return true;
|
|
2798
|
+
return false;
|
|
2799
|
+
}
|
|
2800
|
+
publish(tags, cred, draft) {
|
|
2801
|
+
if (!tags?.length) return;
|
|
2802
|
+
let event;
|
|
2803
|
+
for (const sub of this.subs) {
|
|
2804
|
+
if (!this.matches(sub, tags, cred)) continue;
|
|
2805
|
+
event ??= { ...draft, seq: ++this.seq };
|
|
2806
|
+
if (sub.queue.length >= sub.limit) sub.dropped++;
|
|
2807
|
+
else sub.queue.push(event);
|
|
2808
|
+
this.schedule(sub);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
matches(sub, tags, cred) {
|
|
2812
|
+
return tags.includes(sub.tag) && maySee(sub.viewer, cred);
|
|
2813
|
+
}
|
|
2814
|
+
schedule(sub) {
|
|
2815
|
+
if (sub.scheduled || sub.waiting) return;
|
|
2816
|
+
if (sub.queue.length === 0 && sub.dropped === 0) return;
|
|
2817
|
+
sub.scheduled = true;
|
|
2818
|
+
queueMicrotask(() => {
|
|
2819
|
+
sub.scheduled = false;
|
|
2820
|
+
if (!this.subs.has(sub)) return;
|
|
2821
|
+
const batch = { events: sub.queue, dropped: sub.dropped };
|
|
2822
|
+
sub.queue = [];
|
|
2823
|
+
sub.dropped = 0;
|
|
2824
|
+
sub.waiting = true;
|
|
2825
|
+
sub.deliver(batch);
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
};
|
|
2829
|
+
|
|
2754
2830
|
// src/kernel/net/http1.ts
|
|
2755
2831
|
var HttpError = class extends Error {
|
|
2756
2832
|
status;
|
|
@@ -3711,7 +3787,7 @@ function satisfies(version, range2) {
|
|
|
3711
3787
|
}
|
|
3712
3788
|
|
|
3713
3789
|
// src/version.ts
|
|
3714
|
-
var KERNEL_VERSION = true ? "1.
|
|
3790
|
+
var KERNEL_VERSION = true ? "1.181.0" : "0.0.0-dev";
|
|
3715
3791
|
var OS_RELEASE = "6.0.0-slicc";
|
|
3716
3792
|
function procVersion() {
|
|
3717
3793
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -4576,27 +4652,27 @@ function hashId(text) {
|
|
|
4576
4652
|
return hex4(a) + hex4(b);
|
|
4577
4653
|
}
|
|
4578
4654
|
function split(text, count2) {
|
|
4579
|
-
const
|
|
4655
|
+
const words3 = [];
|
|
4580
4656
|
let rest2 = text;
|
|
4581
4657
|
for (let i = 0; i < count2; i++) {
|
|
4582
4658
|
const match = /^(\S+)\s+/.exec(rest2);
|
|
4583
4659
|
if (!match) return void 0;
|
|
4584
|
-
|
|
4660
|
+
words3.push(match[1]);
|
|
4585
4661
|
rest2 = rest2.slice(match[0].length);
|
|
4586
4662
|
}
|
|
4587
|
-
return [
|
|
4663
|
+
return [words3, rest2.trim()];
|
|
4588
4664
|
}
|
|
4589
4665
|
function jobOf(content, pending, ctx, env) {
|
|
4590
4666
|
const fields = content.startsWith("@") ? 1 : 5;
|
|
4591
4667
|
const parts2 = split(content, fields + (ctx.system ? 1 : 0));
|
|
4592
4668
|
if (!parts2 || parts2[1] === "") throw new Error("no command");
|
|
4593
|
-
const [
|
|
4594
|
-
const schedule =
|
|
4669
|
+
const [words3, command] = parts2;
|
|
4670
|
+
const schedule = words3.slice(0, fields).join(" ");
|
|
4595
4671
|
const when = parseWhen(schedule);
|
|
4596
4672
|
const zone = env.CRON_TZ;
|
|
4597
4673
|
if (zone && !when.reboot && canonicalZone(zone) === void 0)
|
|
4598
4674
|
throw new Error(`CRON_TZ: unknown time zone "${zone}"`);
|
|
4599
|
-
const user = ctx.system ?
|
|
4675
|
+
const user = ctx.system ? words3[fields] : void 0;
|
|
4600
4676
|
if (user !== void 0 && !USER_NAME.test(user)) throw new Error(`bad user name ${user}`);
|
|
4601
4677
|
if (pending !== void 0 && !JOB_NAME.test(pending))
|
|
4602
4678
|
throw new Error(`bad job name "${pending}"`);
|
|
@@ -8298,9 +8374,11 @@ var SpawnError = class extends Error {
|
|
|
8298
8374
|
};
|
|
8299
8375
|
var ChangedError = class extends SpawnError {
|
|
8300
8376
|
notice;
|
|
8301
|
-
|
|
8377
|
+
file;
|
|
8378
|
+
constructor(notice, file) {
|
|
8302
8379
|
super("EPERM");
|
|
8303
8380
|
this.notice = notice;
|
|
8381
|
+
this.file = file;
|
|
8304
8382
|
}
|
|
8305
8383
|
};
|
|
8306
8384
|
function interrupted(signal) {
|
|
@@ -15377,21 +15455,21 @@ function unescaped(double) {
|
|
|
15377
15455
|
}
|
|
15378
15456
|
function splitWords(text) {
|
|
15379
15457
|
const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
|
|
15380
|
-
const
|
|
15458
|
+
const words3 = [];
|
|
15381
15459
|
let word;
|
|
15382
15460
|
while (piece.lastIndex < text.length) {
|
|
15383
15461
|
const m = piece.exec(text);
|
|
15384
15462
|
if (!m) return void 0;
|
|
15385
15463
|
const [, single, double, escaped, plain3, space] = m;
|
|
15386
15464
|
if (space !== void 0) {
|
|
15387
|
-
if (word !== void 0)
|
|
15465
|
+
if (word !== void 0) words3.push(word);
|
|
15388
15466
|
word = void 0;
|
|
15389
15467
|
continue;
|
|
15390
15468
|
}
|
|
15391
15469
|
word = (word ?? "") + (single ?? (double !== void 0 ? unescaped(double) : escaped ?? plain3));
|
|
15392
15470
|
}
|
|
15393
|
-
if (word !== void 0)
|
|
15394
|
-
return
|
|
15471
|
+
if (word !== void 0) words3.push(word);
|
|
15472
|
+
return words3;
|
|
15395
15473
|
}
|
|
15396
15474
|
function quoteWord(word) {
|
|
15397
15475
|
const text = hasControl(word) ? withoutControl(word) : word;
|
|
@@ -15412,14 +15490,14 @@ function addPlain(word, plain3) {
|
|
|
15412
15490
|
}
|
|
15413
15491
|
function splitPatterns(text) {
|
|
15414
15492
|
const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
|
|
15415
|
-
const
|
|
15493
|
+
const words3 = [];
|
|
15416
15494
|
let word;
|
|
15417
15495
|
while (piece.lastIndex < text.length) {
|
|
15418
15496
|
const m = piece.exec(text);
|
|
15419
15497
|
if (!m) return void 0;
|
|
15420
15498
|
const [, single, double, escaped, plain3, space] = m;
|
|
15421
15499
|
if (space !== void 0) {
|
|
15422
|
-
if (word !== void 0)
|
|
15500
|
+
if (word !== void 0) words3.push(word);
|
|
15423
15501
|
word = void 0;
|
|
15424
15502
|
continue;
|
|
15425
15503
|
}
|
|
@@ -15427,8 +15505,8 @@ function splitPatterns(text) {
|
|
|
15427
15505
|
if (plain3 !== void 0) addPlain(word, plain3);
|
|
15428
15506
|
else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
|
|
15429
15507
|
}
|
|
15430
|
-
if (word !== void 0)
|
|
15431
|
-
return
|
|
15508
|
+
if (word !== void 0) words3.push(word);
|
|
15509
|
+
return words3;
|
|
15432
15510
|
}
|
|
15433
15511
|
var literal = (pattern) => pattern.map((p) => p ?? "*").join("");
|
|
15434
15512
|
var isAny = (pattern) => pattern?.length === 1 && pattern[0] === null;
|
|
@@ -15501,14 +15579,14 @@ function whoError(who) {
|
|
|
15501
15579
|
return `expected a user name, %group or ALL first, not ${shown(who)}`;
|
|
15502
15580
|
}
|
|
15503
15581
|
function cwdOf(word) {
|
|
15504
|
-
const
|
|
15505
|
-
const path =
|
|
15582
|
+
const words3 = splitWords(word);
|
|
15583
|
+
const path = words3?.length === 1 ? words3[0] : "";
|
|
15506
15584
|
return path.startsWith("/") ? normalizePath(path) : "";
|
|
15507
15585
|
}
|
|
15508
15586
|
function commandOf2(text, after) {
|
|
15509
|
-
const
|
|
15510
|
-
if (
|
|
15511
|
-
const [first, ...args] =
|
|
15587
|
+
const words3 = splitPatterns(text);
|
|
15588
|
+
if (words3 === void 0) return "a quote in the command is not closed, or it ends in a \\";
|
|
15589
|
+
const [first, ...args] = words3;
|
|
15512
15590
|
const path = first ? literal(first) : "";
|
|
15513
15591
|
if (path === "") return `expected a command after ${after}`;
|
|
15514
15592
|
if (!path.startsWith("/")) return `expected an absolute command path, not ${shown(path)}`;
|
|
@@ -16198,6 +16276,9 @@ function recordable(cwd, argv) {
|
|
|
16198
16276
|
function changedText(file) {
|
|
16199
16277
|
return `sudo: ${file} changed since it was approved`;
|
|
16200
16278
|
}
|
|
16279
|
+
function requestedText(file) {
|
|
16280
|
+
return `sudo: ${file} changed since it was requested`;
|
|
16281
|
+
}
|
|
16201
16282
|
function changedFile(approved, now) {
|
|
16202
16283
|
const files = /* @__PURE__ */ new Set([...Object.keys(approved), ...Object.keys(now)]);
|
|
16203
16284
|
return [...files].find((file) => approved[file] !== now[file]);
|
|
@@ -16205,6 +16286,67 @@ function changedFile(approved, now) {
|
|
|
16205
16286
|
function pinned(rule, bound3) {
|
|
16206
16287
|
return rule.sha256 === void 0 || changedFile(rule.sha256, bound3) === void 0;
|
|
16207
16288
|
}
|
|
16289
|
+
var REPLAY_ID = /^[!-~]{1,256}$/;
|
|
16290
|
+
var REPLAY_TAGS_MAX = 64;
|
|
16291
|
+
var REPLAY_ARGV_MAX = 4096;
|
|
16292
|
+
var REPLAY_BYTES_MAX = 1024 * 1024;
|
|
16293
|
+
var NOT_A_REQUEST = "sudo: that isn't a sudo request this kernel can run";
|
|
16294
|
+
function strings(value2, max) {
|
|
16295
|
+
return Array.isArray(value2) && value2.length <= max && value2.every((s) => typeof s === "string");
|
|
16296
|
+
}
|
|
16297
|
+
function words(value2) {
|
|
16298
|
+
if (!strings(value2, REPLAY_ARGV_MAX) || value2.length === 0) return false;
|
|
16299
|
+
const bytes = value2.reduce((n, s) => n + s.length + 1, 0);
|
|
16300
|
+
return bytes <= REPLAY_BYTES_MAX && !value2.some((s) => s.includes("\0"));
|
|
16301
|
+
}
|
|
16302
|
+
function filesOf(value2) {
|
|
16303
|
+
if (value2 === void 0) return {};
|
|
16304
|
+
if (!Array.isArray(value2) || value2.length > OPERANDS_MAX) return void 0;
|
|
16305
|
+
const files = {};
|
|
16306
|
+
for (const file of value2) {
|
|
16307
|
+
const { path, sha256: hex4 } = file ?? {};
|
|
16308
|
+
if (typeof path !== "string" || typeof hex4 !== "string") return void 0;
|
|
16309
|
+
files[path] = hex4;
|
|
16310
|
+
}
|
|
16311
|
+
return files;
|
|
16312
|
+
}
|
|
16313
|
+
function flagsOf(value2) {
|
|
16314
|
+
if (typeof value2 !== "object" || value2 === null || Array.isArray(value2)) return void 0;
|
|
16315
|
+
const entries = Object.entries(value2);
|
|
16316
|
+
if (entries.length > ROOT_ONLY_MAX || entries.some(([, v]) => typeof v !== "boolean")) {
|
|
16317
|
+
return void 0;
|
|
16318
|
+
}
|
|
16319
|
+
return Object.fromEntries(entries);
|
|
16320
|
+
}
|
|
16321
|
+
function replayOf(value2) {
|
|
16322
|
+
if (typeof value2 !== "object" || value2 === null) return failed2("EINVAL", NOT_A_REQUEST);
|
|
16323
|
+
const r = value2;
|
|
16324
|
+
if (r.validate === true) return failed2("EINVAL", "sudo: a sudo -v request runs no command");
|
|
16325
|
+
if (r.stdin === "pipe" || r.stdin === "file") {
|
|
16326
|
+
return failed2("EINVAL", `sudo: the request read a ${r.stdin}, and that input is gone`);
|
|
16327
|
+
}
|
|
16328
|
+
const { id: id2, user, uid, target, argv, path, cwd, stdin, tag, tags } = r;
|
|
16329
|
+
const rootOnly2 = flagsOf(r.rootOnly);
|
|
16330
|
+
const bound3 = filesOf(r.userWritable);
|
|
16331
|
+
const named = (s) => typeof s === "string" && s !== "";
|
|
16332
|
+
const absolute2 = (s) => typeof s === "string" && s.startsWith("/") && s.length <= PATH_MAX && !s.includes("\0");
|
|
16333
|
+
const valid2 = 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;
|
|
16334
|
+
if (!valid2) return failed2("EINVAL", NOT_A_REQUEST);
|
|
16335
|
+
const all = tags ?? (tag === void 0 ? [] : [tag]);
|
|
16336
|
+
return {
|
|
16337
|
+
id: id2,
|
|
16338
|
+
user,
|
|
16339
|
+
uid,
|
|
16340
|
+
target,
|
|
16341
|
+
argv,
|
|
16342
|
+
path,
|
|
16343
|
+
cwd,
|
|
16344
|
+
stdin,
|
|
16345
|
+
rootOnly: rootOnly2,
|
|
16346
|
+
bound: bound3,
|
|
16347
|
+
tags: [...new Set(all)]
|
|
16348
|
+
};
|
|
16349
|
+
}
|
|
16208
16350
|
var VISUDO_FILES_MAX = 64;
|
|
16209
16351
|
var VISUDO_TEXT_MAX = 1024 * 1024;
|
|
16210
16352
|
function visudoCall(req) {
|
|
@@ -16606,6 +16748,61 @@ var Sudo = class {
|
|
|
16606
16748
|
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
16607
16749
|
return this.command(session, root, mine, call);
|
|
16608
16750
|
}
|
|
16751
|
+
async replay(value2) {
|
|
16752
|
+
const r = replayOf(value2);
|
|
16753
|
+
if ("error" in r) return r;
|
|
16754
|
+
if (this.o.mode !== "enforce") {
|
|
16755
|
+
return failed2("EINVAL", `sudo: users are '${this.o.mode}', so no request needed approval`);
|
|
16756
|
+
}
|
|
16757
|
+
const self = await this.o.users.lookup(r.user);
|
|
16758
|
+
if (!self) return failed2("ENOENT", `sudo: unknown user ${r.user}`);
|
|
16759
|
+
if (self.uid !== r.uid) return failed2("EPERM", `sudo: ${self.name} is no longer uid ${r.uid}`);
|
|
16760
|
+
const target = await this.lookup(r.target);
|
|
16761
|
+
if (!target) return failed2("ENOENT", `sudo: unknown user ${r.target}`);
|
|
16762
|
+
const cred = credOf(self);
|
|
16763
|
+
const [word = "", ...args] = r.argv;
|
|
16764
|
+
const argv = [r.path, ...args];
|
|
16765
|
+
if (!await this.mayAsk(self, target, r, argv)) {
|
|
16766
|
+
const quoted = argv.map(quoteWord).join(" ");
|
|
16767
|
+
return failed2(
|
|
16768
|
+
"EACCES",
|
|
16769
|
+
`sudo: ${self.name} may no longer ask to run ${quoted} as ${target.name}`
|
|
16770
|
+
);
|
|
16771
|
+
}
|
|
16772
|
+
if (await this.canonical(r.cwd) !== r.cwd) {
|
|
16773
|
+
return failed2("EACCES", `sudo: ${r.cwd} changed since it was requested`);
|
|
16774
|
+
}
|
|
16775
|
+
if (!await this.reachable(cred, r.cwd)) {
|
|
16776
|
+
return failed2(
|
|
16777
|
+
"EACCES",
|
|
16778
|
+
`sudo: cannot run in ${r.cwd}: it isn't a directory ${self.name} can enter`
|
|
16779
|
+
);
|
|
16780
|
+
}
|
|
16781
|
+
const path = await this.locate(word, r.cwd);
|
|
16782
|
+
if (!path) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
16783
|
+
if (path !== r.path) return failed2("EPERM", `sudo: ${word} now runs ${path}, not ${r.path}`);
|
|
16784
|
+
const bound3 = await this.bound(path, r.cwd);
|
|
16785
|
+
if (!bound3) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
16786
|
+
const changed = changedFile(r.bound, bound3);
|
|
16787
|
+
if (changed !== void 0) return failed2("EPERM", requestedText(changed));
|
|
16788
|
+
const now = await this.rootOnly(r.cwd, args);
|
|
16789
|
+
const flipped = Object.keys(r.rootOnly).find((at) => r.rootOnly[at] && now[at] !== true);
|
|
16790
|
+
if (flipped !== void 0) return failed2("EPERM", `sudo: ${flipped} is no longer root-only`);
|
|
16791
|
+
const { grant } = this.grant(self, target, cred, r.argv, path, r.cwd, void 0, bound3);
|
|
16792
|
+
(this.o.warn ?? console.warn)(
|
|
16793
|
+
`sudo: ${self.name} : TTY=unknown ; PWD=${quoteWord(r.cwd)} ; USER=${target.name} ; REPLAY=${r.id} ; COMMAND=${argv.map(quoteWord).join(" ")}`
|
|
16794
|
+
);
|
|
16795
|
+
return { grant, caller: self, target, tags: r.tags };
|
|
16796
|
+
}
|
|
16797
|
+
async mayAsk(self, target, r, argv) {
|
|
16798
|
+
if (self.uid === 0) return true;
|
|
16799
|
+
const cred = credOf(self);
|
|
16800
|
+
const { rules } = await this.policyRules({ cred, pid: 0, stdin: r.stdin });
|
|
16801
|
+
const applies = await this.applies(self, cred);
|
|
16802
|
+
return rules.some(
|
|
16803
|
+
(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)
|
|
16804
|
+
);
|
|
16805
|
+
}
|
|
16609
16806
|
async command(s, root, mine, call) {
|
|
16610
16807
|
const { caller, self, target, parsed: parsed2 } = s;
|
|
16611
16808
|
const { cred } = caller;
|
|
@@ -16785,8 +16982,8 @@ ${lines2}
|
|
|
16785
16982
|
const st = await fs.lstat(path).catch(() => void 0);
|
|
16786
16983
|
if (st && !await this.trusted(path)) return false;
|
|
16787
16984
|
const old = st ? await fs.readFile(path) : "";
|
|
16788
|
-
const
|
|
16789
|
-
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${
|
|
16985
|
+
const words3 = recordedCommand(argv);
|
|
16986
|
+
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${words3}
|
|
16790
16987
|
`;
|
|
16791
16988
|
const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
|
|
16792
16989
|
${line}`;
|
|
@@ -18091,8 +18288,8 @@ function ipv6(host) {
|
|
|
18091
18288
|
const head = groups(halves[0]);
|
|
18092
18289
|
const back = halves.length === 2 ? groups(halves[1]) : [];
|
|
18093
18290
|
if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
|
|
18094
|
-
const
|
|
18095
|
-
const missing6 = 8 -
|
|
18291
|
+
const words3 = [...head, ...back].map((g) => Number.parseInt(g, 16));
|
|
18292
|
+
const missing6 = 8 - words3.length - tail2.length;
|
|
18096
18293
|
if (halves.length === 2 ? missing6 < 1 : missing6 !== 0) return void 0;
|
|
18097
18294
|
const all = [
|
|
18098
18295
|
...head.map((g) => Number.parseInt(g, 16)),
|
|
@@ -21576,7 +21773,7 @@ function selectFlag(req) {
|
|
|
21576
21773
|
throw new KernelError("EINVAL", "ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS: alpn with select");
|
|
21577
21774
|
return TLS_SERVER_SELECT;
|
|
21578
21775
|
}
|
|
21579
|
-
function
|
|
21776
|
+
function flagsOf2(req) {
|
|
21580
21777
|
return certFlags(req) | selectFlag(req) | (req.keylog ? TLS_KEYLOG : 0);
|
|
21581
21778
|
}
|
|
21582
21779
|
async function engineOf(proc, { select, keylog }) {
|
|
@@ -21627,7 +21824,7 @@ async function serverOf(proc, req) {
|
|
|
21627
21824
|
let mine;
|
|
21628
21825
|
try {
|
|
21629
21826
|
mine = req.ca && req.ca.length > 0 ? storeOf(engine, req.ca) : void 0;
|
|
21630
|
-
const flags2 =
|
|
21827
|
+
const flags2 = flagsOf2(req);
|
|
21631
21828
|
const store = mine ?? (flags2 & CERT_FLAGS ? await defaultStore(engine, supportOf(proc.net).roots) : void 0);
|
|
21632
21829
|
return {
|
|
21633
21830
|
engine,
|
|
@@ -23516,7 +23713,7 @@ var WRITE_ACCESS = 3;
|
|
|
23516
23713
|
var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
|
|
23517
23714
|
var STUCK_ESCAPES = /* @__PURE__ */ new Set([SIG.INT, SIG.TERM, SIG.QUIT, SIG.HUP]);
|
|
23518
23715
|
var DEFAULT_UMASK2 = 18;
|
|
23519
|
-
function
|
|
23716
|
+
function words2(value2) {
|
|
23520
23717
|
if (!Array.isArray(value2) || !value2.every((w) => typeof w === "string")) {
|
|
23521
23718
|
throw new KernelError("EINVAL");
|
|
23522
23719
|
}
|
|
@@ -24969,7 +25166,7 @@ var WasmProcess = class {
|
|
|
24969
25166
|
if (!sudo) throw new KernelError("ENOSYS");
|
|
24970
25167
|
const call = {
|
|
24971
25168
|
op: "proc-sudo",
|
|
24972
|
-
args:
|
|
25169
|
+
args: words2(req.args),
|
|
24973
25170
|
cwd: typeof req.cwd === "string" && req.cwd.startsWith("/") ? req.cwd : "/",
|
|
24974
25171
|
...typeof req.term === "string" ? { term: req.term } : {}
|
|
24975
25172
|
};
|
|
@@ -25973,6 +26170,9 @@ var JobTable = class {
|
|
|
25973
26170
|
idOf(pid) {
|
|
25974
26171
|
return this.members.get(pid)?.shownAs ?? pid;
|
|
25975
26172
|
}
|
|
26173
|
+
execParentOf(pid) {
|
|
26174
|
+
return this.members.get(pid)?.execParent;
|
|
26175
|
+
}
|
|
25976
26176
|
byId(id2) {
|
|
25977
26177
|
for (const member of this.members.values()) if (member.shownAs === id2) return member;
|
|
25978
26178
|
return this.member(id2);
|
|
@@ -28255,8 +28455,8 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
|
28255
28455
|
function noticeOf(data) {
|
|
28256
28456
|
if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
|
|
28257
28457
|
const notice = data ?? {};
|
|
28258
|
-
const
|
|
28259
|
-
return { paths:
|
|
28458
|
+
const strings2 = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
|
|
28459
|
+
return { paths: strings2(notice.paths), trees: strings2(notice.trees), outside: true };
|
|
28260
28460
|
}
|
|
28261
28461
|
|
|
28262
28462
|
// src/fs/opfs.ts
|
|
@@ -32569,7 +32769,40 @@ function describe(req, tty) {
|
|
|
32569
32769
|
}
|
|
32570
32770
|
function refuseChanged(approved, now) {
|
|
32571
32771
|
const changed = changedFile(approved, now);
|
|
32572
|
-
if (changed !== void 0) throw new ChangedError(changedText(changed));
|
|
32772
|
+
if (changed !== void 0) throw new ChangedError(changedText(changed), changed);
|
|
32773
|
+
}
|
|
32774
|
+
var O_WRONLY2 = 1;
|
|
32775
|
+
var OUTPUT_MODE = 384;
|
|
32776
|
+
var ERRNO_TEXT = {
|
|
32777
|
+
EACCES: "Permission denied",
|
|
32778
|
+
EISDIR: "Is a directory",
|
|
32779
|
+
ENOENT: "No such file or directory",
|
|
32780
|
+
ENOTDIR: "Not a directory",
|
|
32781
|
+
EROFS: "Read-only file system"
|
|
32782
|
+
};
|
|
32783
|
+
function sudoFailure(errno3, message3) {
|
|
32784
|
+
return Object.assign(new Error(message3), { code: errno3 });
|
|
32785
|
+
}
|
|
32786
|
+
function sudoCause(err) {
|
|
32787
|
+
const code = codeOf8(err);
|
|
32788
|
+
if (typeof code !== "string") throw err;
|
|
32789
|
+
return code;
|
|
32790
|
+
}
|
|
32791
|
+
function requested(err) {
|
|
32792
|
+
if (err instanceof ChangedError) throw sudoFailure(err.code, requestedText(err.file));
|
|
32793
|
+
throw err;
|
|
32794
|
+
}
|
|
32795
|
+
var MAX_SPAWN_TAGS = 8;
|
|
32796
|
+
function tagList(value2) {
|
|
32797
|
+
if (!Array.isArray(value2)) {
|
|
32798
|
+
tagOption(value2);
|
|
32799
|
+
return value2 === void 0 ? [] : [value2];
|
|
32800
|
+
}
|
|
32801
|
+
if (value2.length > MAX_SPAWN_TAGS) {
|
|
32802
|
+
throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
|
|
32803
|
+
}
|
|
32804
|
+
for (const each of value2) tagOption(each ?? 0);
|
|
32805
|
+
return value2;
|
|
32573
32806
|
}
|
|
32574
32807
|
function linksOf2(req) {
|
|
32575
32808
|
return req.links === void 0 ? {} : { links: req.links };
|
|
@@ -32695,27 +32928,27 @@ function appendedRoots(given) {
|
|
|
32695
32928
|
}
|
|
32696
32929
|
return [...roots];
|
|
32697
32930
|
}
|
|
32698
|
-
function plainEnv(
|
|
32699
|
-
const next3 =
|
|
32700
|
-
if (baseName2(
|
|
32931
|
+
function plainEnv(words3) {
|
|
32932
|
+
const next3 = words3[1];
|
|
32933
|
+
if (baseName2(words3[0] ?? "") !== "env" || next3 === void 0) return false;
|
|
32701
32934
|
return next3 === "-S" || !next3.startsWith("-") && !next3.includes("=");
|
|
32702
32935
|
}
|
|
32703
|
-
function shebangCommand(
|
|
32704
|
-
if (!plainEnv(
|
|
32705
|
-
const arg2 =
|
|
32706
|
-
return { interp:
|
|
32936
|
+
function shebangCommand(words3) {
|
|
32937
|
+
if (!plainEnv(words3)) {
|
|
32938
|
+
const arg2 = words3.slice(1).join(" ");
|
|
32939
|
+
return { interp: words3[0], args: arg2 ? [arg2] : [] };
|
|
32707
32940
|
}
|
|
32708
|
-
const split3 =
|
|
32709
|
-
const [interp, ...rest2] =
|
|
32941
|
+
const split3 = words3[1] === "-S";
|
|
32942
|
+
const [interp, ...rest2] = words3.slice(split3 ? 2 : 1);
|
|
32710
32943
|
const arg = rest2.join(" ");
|
|
32711
32944
|
return { interp, args: split3 ? rest2 : arg ? [arg] : [] };
|
|
32712
32945
|
}
|
|
32713
32946
|
function interpreter(head) {
|
|
32714
|
-
const
|
|
32715
|
-
if (!
|
|
32716
|
-
if (baseName2(
|
|
32717
|
-
if (
|
|
32718
|
-
return baseName2(
|
|
32947
|
+
const words3 = shebang(head);
|
|
32948
|
+
if (!words3) return "node";
|
|
32949
|
+
if (baseName2(words3[0]) === "env") words3.shift();
|
|
32950
|
+
if (words3[0] === "-S") words3.shift();
|
|
32951
|
+
return baseName2(words3[0] ?? "");
|
|
32719
32952
|
}
|
|
32720
32953
|
function expandDefaults(defaults2, env) {
|
|
32721
32954
|
const out = {};
|
|
@@ -33490,9 +33723,9 @@ var Launcher = class {
|
|
|
33490
33723
|
const head = await this.head(script);
|
|
33491
33724
|
if (!head) return void 0;
|
|
33492
33725
|
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
|
|
33493
|
-
const
|
|
33494
|
-
if (!
|
|
33495
|
-
const { interp, args } = shebangCommand(
|
|
33726
|
+
const words3 = shebang(head) ?? (binfmt ? [binfmt] : void 0);
|
|
33727
|
+
if (!words3) return void 0;
|
|
33728
|
+
const { interp, args } = shebangCommand(words3);
|
|
33496
33729
|
const passed = [...args, command ? script : file, ...argv.slice(1)];
|
|
33497
33730
|
if (!interp) return void 0;
|
|
33498
33731
|
const found = await this.resolve(interp, interp, cwd, scope);
|
|
@@ -33723,7 +33956,7 @@ var Launcher = class {
|
|
|
33723
33956
|
const hex4 = pinned2[real];
|
|
33724
33957
|
if (hex4 !== void 0) return { [real]: hex4 };
|
|
33725
33958
|
if (await this.sudoer.trusted(real)) return {};
|
|
33726
|
-
throw new ChangedError(changedText(real));
|
|
33959
|
+
throw new ChangedError(changedText(real), real);
|
|
33727
33960
|
}
|
|
33728
33961
|
async pinnedBytes(pin, bytes) {
|
|
33729
33962
|
const [real = ""] = Object.keys(pin);
|
|
@@ -34002,6 +34235,10 @@ var Launcher = class {
|
|
|
34002
34235
|
}
|
|
34003
34236
|
async launch(asked, req) {
|
|
34004
34237
|
const cwd = await physicalPathAsync(this.base, req.cwd);
|
|
34238
|
+
if (req.exactCwd && cwd !== req.cwd) {
|
|
34239
|
+
await req.fds.closeAll();
|
|
34240
|
+
throw sudoFailure("EACCES", `sudo: ${req.cwd} changed since it was requested`);
|
|
34241
|
+
}
|
|
34005
34242
|
const seen = { ...req.env, HOSTNAME: this.hostname, cwd };
|
|
34006
34243
|
await this.pnpmReady();
|
|
34007
34244
|
let loaded;
|
|
@@ -34180,10 +34417,12 @@ var Launcher = class {
|
|
|
34180
34417
|
});
|
|
34181
34418
|
const fit = await (handle.ready ?? true);
|
|
34182
34419
|
if (fit) {
|
|
34420
|
+
this.traceAdmitted(handle.pid, true);
|
|
34183
34421
|
await fds.closeAll();
|
|
34184
34422
|
return handle;
|
|
34185
34423
|
}
|
|
34186
34424
|
await handle.exited;
|
|
34425
|
+
this.traceAdmitted(handle.pid, false);
|
|
34187
34426
|
await this.memories.refused().catch(async (err) => {
|
|
34188
34427
|
await fds.closeAll();
|
|
34189
34428
|
throw err;
|
|
@@ -34314,10 +34553,12 @@ var Launcher = class {
|
|
|
34314
34553
|
if (req.group === "lead") this.jobs.lead(pid);
|
|
34315
34554
|
if (req.group === "session") this.jobs.setsid(pid);
|
|
34316
34555
|
this.described.set(pid, describe(req, terminal?.name ?? null));
|
|
34556
|
+
this.traceStarted(pid, req);
|
|
34317
34557
|
handle.onState(
|
|
34318
34558
|
(state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
|
|
34319
34559
|
);
|
|
34320
|
-
void handle.exited.then(() => {
|
|
34560
|
+
void handle.exited.then((status) => {
|
|
34561
|
+
this.traceEnded(pid, handle, status);
|
|
34321
34562
|
this.cwds.release(this.cwdSlots.get(pid));
|
|
34322
34563
|
this.cwdSlots.delete(pid);
|
|
34323
34564
|
this.cwdPaths.delete(pid);
|
|
@@ -34432,6 +34673,71 @@ var Launcher = class {
|
|
|
34432
34673
|
imageTimes = /* @__PURE__ */ new Map();
|
|
34433
34674
|
childTimes = /* @__PURE__ */ new Map();
|
|
34434
34675
|
stopped = /* @__PURE__ */ new Set();
|
|
34676
|
+
traces = new TraceHub();
|
|
34677
|
+
traceHeld = /* @__PURE__ */ new Map();
|
|
34678
|
+
replacedImages = /* @__PURE__ */ new Set();
|
|
34679
|
+
traceStarted(pid, req) {
|
|
34680
|
+
const replaces = req.exec ? req.ppid : void 0;
|
|
34681
|
+
if (req.quietOom) this.traceHeld.set(pid, { replaces, events: [] });
|
|
34682
|
+
else if (replaces !== void 0) this.replacedImages.add(replaces);
|
|
34683
|
+
this.traceEmit(pid, () => {
|
|
34684
|
+
const { pid: shown2, ppid } = this.identityOf(pid);
|
|
34685
|
+
const described2 = this.described.get(pid);
|
|
34686
|
+
const { argv, started, tty: _tty, exeId: _exeId, ...exe } = described2;
|
|
34687
|
+
return {
|
|
34688
|
+
type: req.fork ? "fork" : "exec",
|
|
34689
|
+
pid: shown2,
|
|
34690
|
+
ppid,
|
|
34691
|
+
pgid: this.jobs.pgidOf(pid),
|
|
34692
|
+
argv: [...argv],
|
|
34693
|
+
...exe,
|
|
34694
|
+
uid: this.credOf(pid).euid,
|
|
34695
|
+
at: started,
|
|
34696
|
+
...req.exec ? { replaced: true } : {}
|
|
34697
|
+
};
|
|
34698
|
+
});
|
|
34699
|
+
}
|
|
34700
|
+
traceEnded(pid, handle, status) {
|
|
34701
|
+
if (this.replacedImages.delete(pid)) return;
|
|
34702
|
+
this.traceEmit(pid, () => ({
|
|
34703
|
+
type: "exit",
|
|
34704
|
+
pid: this.jobs.idOf(pid),
|
|
34705
|
+
status,
|
|
34706
|
+
...signalField(handle.termsig()),
|
|
34707
|
+
...this.chainTimes(pid),
|
|
34708
|
+
at: Date.now()
|
|
34709
|
+
}));
|
|
34710
|
+
}
|
|
34711
|
+
traceEmit(pid, build) {
|
|
34712
|
+
if (this.traces.idle) return;
|
|
34713
|
+
const tags = this.tagsOf(pid);
|
|
34714
|
+
const cred = this.credOf(pid);
|
|
34715
|
+
if (!this.traces.wants(tags, cred)) return;
|
|
34716
|
+
const held = this.traceHeld.get(pid);
|
|
34717
|
+
if (held) held.events.push({ tags, cred: copyCred(cred), draft: build() });
|
|
34718
|
+
else this.traces.publish(tags, cred, build());
|
|
34719
|
+
}
|
|
34720
|
+
traceAdmitted(pid, fit) {
|
|
34721
|
+
const held = this.traceHeld.get(pid);
|
|
34722
|
+
if (!held) return;
|
|
34723
|
+
this.traceHeld.delete(pid);
|
|
34724
|
+
if (!fit) return;
|
|
34725
|
+
if (held.replaces !== void 0) this.replacedImages.add(held.replaces);
|
|
34726
|
+
for (const { tags, cred, draft } of held.events) this.traces.publish(tags, cred, draft);
|
|
34727
|
+
}
|
|
34728
|
+
chainTimes(pid) {
|
|
34729
|
+
let utime = 0;
|
|
34730
|
+
let stime = 0;
|
|
34731
|
+
let timed2 = false;
|
|
34732
|
+
for (let at = pid; at !== void 0; at = this.jobs.execParentOf(at)) {
|
|
34733
|
+
const t = this.timesAt(at);
|
|
34734
|
+
if (!t) continue;
|
|
34735
|
+
timed2 = true;
|
|
34736
|
+
utime += t.utime;
|
|
34737
|
+
stime += t.stime;
|
|
34738
|
+
}
|
|
34739
|
+
return timed2 ? { utime, stime } : {};
|
|
34740
|
+
}
|
|
34435
34741
|
stateOf(pid) {
|
|
34436
34742
|
if (!this.processes.has(pid)) return "Z";
|
|
34437
34743
|
return this.stopped.has(pid) ? "T" : "S";
|
|
@@ -35628,6 +35934,96 @@ var Launcher = class {
|
|
|
35628
35934
|
const signal = handle.termsig();
|
|
35629
35935
|
return { status, ...signal ? { signal } : {}, stdout: concat7(out), stderr: concat7(err) };
|
|
35630
35936
|
}
|
|
35937
|
+
async sudoRun(replay, options = {}) {
|
|
35938
|
+
const added = tagList(options.tag);
|
|
35939
|
+
const replayed = await this.sudoer.replay(replay);
|
|
35940
|
+
if ("error" in replayed) throw sudoFailure(replayed.errno, replayed.error);
|
|
35941
|
+
const { grant, caller, target } = replayed;
|
|
35942
|
+
let tags = withTag(void 0, options.base);
|
|
35943
|
+
for (const tag of [...replayed.tags, ...added]) tags = withTag(tags, tag);
|
|
35944
|
+
const tagged = { tags, tagBase: options.base === void 0 ? 0 : 1 };
|
|
35945
|
+
await this.swaps.settled();
|
|
35946
|
+
this.resetCatalogs();
|
|
35947
|
+
const req = { file: grant.file, argv: grant.argv, env: grant.env, cwd: grant.cwd };
|
|
35948
|
+
const ctx = { ppid: 0, cred: grant.cred, scope: "" };
|
|
35949
|
+
const planned = await this.planOne(req, ctx).catch((err2) => {
|
|
35950
|
+
const code = sudoCause(err2);
|
|
35951
|
+
const word = grant.argv[0] ?? grant.file;
|
|
35952
|
+
if (code === "ENOENT") throw sudoFailure(code, `sudo: ${word}: command not found`);
|
|
35953
|
+
throw sudoFailure(code, `sudo: ${grant.file}: ${ERRNO_TEXT[code] ?? code}`);
|
|
35954
|
+
});
|
|
35955
|
+
const pin = await this.unchanged(planned, grant.file, grant.cwd, grant).catch(requested);
|
|
35956
|
+
const fds = await this.sudoOutputs(options, caller, this.stackFor(tags, tagged.tagBase));
|
|
35957
|
+
const err = fds.get(2).file;
|
|
35958
|
+
const report = (message3) => void Promise.resolve(err.write?.(encoder10.encode(`${message3}
|
|
35959
|
+
`))).catch(() => void 0);
|
|
35960
|
+
const replan = () => this.planOne(req, ctx);
|
|
35961
|
+
if (!await this.grantedCwd(grant.caller, grant.cwd)) {
|
|
35962
|
+
await fds.closeAll();
|
|
35963
|
+
throw sudoFailure("EACCES", `sudo: ${grant.cwd} changed since it was requested`);
|
|
35964
|
+
}
|
|
35965
|
+
const handle = await this.launch(
|
|
35966
|
+
{ ...planned, replan },
|
|
35967
|
+
{
|
|
35968
|
+
...pin,
|
|
35969
|
+
env: grant.env,
|
|
35970
|
+
cwd: grant.cwd,
|
|
35971
|
+
fds,
|
|
35972
|
+
report,
|
|
35973
|
+
...this.identity(target, tagged, options.session),
|
|
35974
|
+
...caller.umask !== void 0 ? { umask: caller.umask } : {},
|
|
35975
|
+
links: grant.cred.euid,
|
|
35976
|
+
exactCwd: true
|
|
35977
|
+
}
|
|
35978
|
+
).catch(requested);
|
|
35979
|
+
const ended = handle.exited.then((status) => {
|
|
35980
|
+
const signal = handle.termsig();
|
|
35981
|
+
return { status, ...signal ? { signal } : {} };
|
|
35982
|
+
});
|
|
35983
|
+
return { pid: handle.pid, ended };
|
|
35984
|
+
}
|
|
35985
|
+
async sudoOutputs(options, caller, stack) {
|
|
35986
|
+
const fds = new FdTable();
|
|
35987
|
+
const opened2 = /* @__PURE__ */ new Map();
|
|
35988
|
+
fds.installAt(0, deviceFile("null"));
|
|
35989
|
+
try {
|
|
35990
|
+
fds.installAt(1, await this.sudoOutput(options.stdout, caller, stack, opened2));
|
|
35991
|
+
fds.installAt(2, await this.sudoOutput(options.stderr, caller, stack, opened2));
|
|
35992
|
+
} catch (err) {
|
|
35993
|
+
await fds.closeAll();
|
|
35994
|
+
throw err;
|
|
35995
|
+
}
|
|
35996
|
+
return fds;
|
|
35997
|
+
}
|
|
35998
|
+
async sudoOutput(path, caller, stack, opened2) {
|
|
35999
|
+
if (path === void 0) return deviceFile("null");
|
|
36000
|
+
if (typeof path !== "string" || !path.startsWith("/")) {
|
|
36001
|
+
throw sudoFailure("EINVAL", `sudo: ${String(path)} is not an absolute path`);
|
|
36002
|
+
}
|
|
36003
|
+
const cred = credOf(caller);
|
|
36004
|
+
const walked = await walk(this.fs, cred, normalizePath(path), true, caller.uid);
|
|
36005
|
+
const real = walked.path;
|
|
36006
|
+
const same2 = opened2.get(real);
|
|
36007
|
+
if (same2) return same2.retain();
|
|
36008
|
+
try {
|
|
36009
|
+
if (walked.denied !== void 0) throw fsError("EACCES", path);
|
|
36010
|
+
await writable({ ...this.execGuard(cred, 0), links: caller.uid }, "open", real);
|
|
36011
|
+
const owner = { uid: caller.uid, gid: caller.gid, mode: OUTPUT_MODE };
|
|
36012
|
+
await stack.noLinks.writeFile(real, new Uint8Array(0), owner);
|
|
36013
|
+
} catch (err) {
|
|
36014
|
+
const code = sudoCause(err);
|
|
36015
|
+
throw sudoFailure(code, `sudo: ${path}: ${ERRNO_TEXT[code] ?? code}`);
|
|
36016
|
+
}
|
|
36017
|
+
const file = vfsFile(stack.fs, { path: real, flags: O_WRONLY2, position: 0 }, stack.nodes);
|
|
36018
|
+
const again = await walk(this.fs, cred, real, true, caller.uid);
|
|
36019
|
+
const st = await stack.noLinks.lstat(real).catch(() => void 0);
|
|
36020
|
+
if (again.path !== real || again.denied !== void 0 || !st?.isFile) {
|
|
36021
|
+
await file.release();
|
|
36022
|
+
throw sudoFailure("EACCES", `sudo: ${path} changed while it was opened`);
|
|
36023
|
+
}
|
|
36024
|
+
opened2.set(real, file);
|
|
36025
|
+
return file;
|
|
36026
|
+
}
|
|
35631
36027
|
};
|
|
35632
36028
|
function cronLocks() {
|
|
35633
36029
|
return globalThis.navigator?.locks;
|
|
@@ -36112,7 +36508,7 @@ function serveTransport(peer, transport, {
|
|
|
36112
36508
|
}
|
|
36113
36509
|
|
|
36114
36510
|
// src/client/protocol.ts
|
|
36115
|
-
var PROTOCOL = [1,
|
|
36511
|
+
var PROTOCOL = [1, 16];
|
|
36116
36512
|
var FS_METHODS = [
|
|
36117
36513
|
"readFile",
|
|
36118
36514
|
"writeFile",
|
|
@@ -36268,9 +36664,9 @@ async function accountOf(l, user) {
|
|
|
36268
36664
|
if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
36269
36665
|
return found;
|
|
36270
36666
|
}
|
|
36271
|
-
async function runAs(l, self,
|
|
36272
|
-
if (
|
|
36273
|
-
const target = await accountOf(l,
|
|
36667
|
+
async function runAs(l, self, requested2) {
|
|
36668
|
+
if (requested2 === void 0) return self;
|
|
36669
|
+
const target = await accountOf(l, requested2);
|
|
36274
36670
|
if (self.uid !== 0 && target.uid !== self.uid) {
|
|
36275
36671
|
throw fsError("EPERM", `${self.name} may not run as ${target.name}`);
|
|
36276
36672
|
}
|
|
@@ -36280,8 +36676,8 @@ function clientCred(self) {
|
|
|
36280
36676
|
return self ? credOf(self) : copyCred(ROOT);
|
|
36281
36677
|
}
|
|
36282
36678
|
async function runTagged(l, self, options, tag, session = -1) {
|
|
36283
|
-
|
|
36284
|
-
const tags = withTag(
|
|
36679
|
+
let tags = withTag(void 0, tag);
|
|
36680
|
+
for (const added of tagList(options.tag)) tags = withTag(tags, added);
|
|
36285
36681
|
return {
|
|
36286
36682
|
user: await runAs(l, self, options.user),
|
|
36287
36683
|
tag,
|
|
@@ -36412,6 +36808,34 @@ async function watchOp(l, req, watches, send2, g) {
|
|
|
36412
36808
|
watches.set(req.id, unwatch);
|
|
36413
36809
|
return { result: req.id };
|
|
36414
36810
|
}
|
|
36811
|
+
function traceRegistry(send2) {
|
|
36812
|
+
const traces = /* @__PURE__ */ new Map();
|
|
36813
|
+
return {
|
|
36814
|
+
answer(l, self, req) {
|
|
36815
|
+
if (req.op === "untrace") {
|
|
36816
|
+
traces.get(req.trace)?.close();
|
|
36817
|
+
return { result: traces.delete(req.trace) };
|
|
36818
|
+
}
|
|
36819
|
+
if (typeof req.tag !== "string" || req.tag === "") {
|
|
36820
|
+
throw fsError("EINVAL", "trace: a tag is a non-empty string");
|
|
36821
|
+
}
|
|
36822
|
+
const limit = traceLimit(req.limit);
|
|
36823
|
+
const id2 = req.id;
|
|
36824
|
+
const deliver = ({ events, dropped: dropped2 }) => send2({ trace: id2, events, ...dropped2 > 0 ? { dropped: dropped2 } : {} });
|
|
36825
|
+
traces.set(id2, l.traces.subscribe(req.tag, credOf(self), deliver, limit));
|
|
36826
|
+
return { result: id2 };
|
|
36827
|
+
},
|
|
36828
|
+
ack(data) {
|
|
36829
|
+
if (typeof data.traceAck !== "number") return false;
|
|
36830
|
+
traces.get(data.traceAck)?.ack();
|
|
36831
|
+
return true;
|
|
36832
|
+
},
|
|
36833
|
+
close() {
|
|
36834
|
+
for (const trace of traces.values()) trace.close();
|
|
36835
|
+
traces.clear();
|
|
36836
|
+
}
|
|
36837
|
+
};
|
|
36838
|
+
}
|
|
36415
36839
|
var TRAITS_WATCH = -1;
|
|
36416
36840
|
function traitsWatch(launcher, transport, send2) {
|
|
36417
36841
|
return launcher.onTraits(() => send2({ traits: transport.traits }));
|
|
@@ -36438,6 +36862,35 @@ function requestBinding(send2, port) {
|
|
|
36438
36862
|
close: () => unbind?.()
|
|
36439
36863
|
};
|
|
36440
36864
|
}
|
|
36865
|
+
async function sudoRunAs(l, self, host, req, reply) {
|
|
36866
|
+
if (self.uid !== 0) throw sudoFailure("EPERM", "sudo: only a root client may run a sudo request");
|
|
36867
|
+
const { stdout, stderr, tag } = req.options;
|
|
36868
|
+
const running = await l.sudoRun(req.replay, {
|
|
36869
|
+
stdout,
|
|
36870
|
+
stderr,
|
|
36871
|
+
tag,
|
|
36872
|
+
base: host.tag,
|
|
36873
|
+
session: host.session
|
|
36874
|
+
});
|
|
36875
|
+
reply(req.id, { started: running.pid });
|
|
36876
|
+
const { status, signal } = await running.ended;
|
|
36877
|
+
return { result: status, ...signalField(signal) };
|
|
36878
|
+
}
|
|
36879
|
+
function answered(pending, req, reply, detach) {
|
|
36880
|
+
pending.then(
|
|
36881
|
+
({ result, signal, transfer }) => {
|
|
36882
|
+
reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
|
|
36883
|
+
if (req.op === "detach") detach(req.kill === true);
|
|
36884
|
+
},
|
|
36885
|
+
(err) => {
|
|
36886
|
+
const code = err?.code;
|
|
36887
|
+
reply(req.id, {
|
|
36888
|
+
error: err instanceof Error ? err.message : String(err),
|
|
36889
|
+
...typeof code === "string" ? { code } : {}
|
|
36890
|
+
});
|
|
36891
|
+
}
|
|
36892
|
+
);
|
|
36893
|
+
}
|
|
36441
36894
|
function serveClient(port, host) {
|
|
36442
36895
|
let state = "new";
|
|
36443
36896
|
let self;
|
|
@@ -36453,6 +36906,7 @@ function serveClient(port, host) {
|
|
|
36453
36906
|
};
|
|
36454
36907
|
const reply = (id2, body, transfer) => send2({ id: id2, ...body }, transfer);
|
|
36455
36908
|
const hosts = hostRegistries(send2, ended.promise, port);
|
|
36909
|
+
const traces = traceRegistry(send2);
|
|
36456
36910
|
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
36457
36911
|
if (state === "closed") return;
|
|
36458
36912
|
send2({ bye });
|
|
@@ -36460,6 +36914,7 @@ function serveClient(port, host) {
|
|
|
36460
36914
|
net?.close();
|
|
36461
36915
|
for (const unwatch of watches.values()) unwatch();
|
|
36462
36916
|
watches.clear();
|
|
36917
|
+
traces.close();
|
|
36463
36918
|
hosts.close();
|
|
36464
36919
|
for (const session of terminals.values()) session.close();
|
|
36465
36920
|
terminals.clear();
|
|
@@ -36567,6 +37022,11 @@ function serveClient(port, host) {
|
|
|
36567
37022
|
case "serve-facade":
|
|
36568
37023
|
case "unserve-facade":
|
|
36569
37024
|
return { result: hosts.answer(l, req) };
|
|
37025
|
+
case "trace":
|
|
37026
|
+
case "untrace":
|
|
37027
|
+
return traces.answer(l, self, req);
|
|
37028
|
+
case "sudo-run":
|
|
37029
|
+
return sudoRunAs(l, self, host, req, reply);
|
|
36570
37030
|
case "detach":
|
|
36571
37031
|
return { result: true };
|
|
36572
37032
|
default:
|
|
@@ -36590,20 +37050,9 @@ function serveClient(port, host) {
|
|
|
36590
37050
|
net?.answer(data);
|
|
36591
37051
|
return;
|
|
36592
37052
|
}
|
|
37053
|
+
if (traces.ack(data)) return;
|
|
36593
37054
|
const req = data;
|
|
36594
|
-
handle(req)
|
|
36595
|
-
({ result, signal, transfer }) => {
|
|
36596
|
-
reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
|
|
36597
|
-
if (req.op === "detach") detach(req.kill === true);
|
|
36598
|
-
},
|
|
36599
|
-
(err) => {
|
|
36600
|
-
const code = err?.code;
|
|
36601
|
-
reply(req.id, {
|
|
36602
|
-
error: err instanceof Error ? err.message : String(err),
|
|
36603
|
-
...typeof code === "string" ? { code } : {}
|
|
36604
|
-
});
|
|
36605
|
-
}
|
|
36606
|
-
);
|
|
37055
|
+
answered(handle(req), req, reply, detach);
|
|
36607
37056
|
});
|
|
36608
37057
|
port.addEventListener("close", () => detach());
|
|
36609
37058
|
port.start?.();
|
|
@@ -36683,16 +37132,16 @@ function placeOf(address) {
|
|
|
36683
37132
|
const kind = isIP3(plain3);
|
|
36684
37133
|
if (kind === 4) return ipv4Place(plain3);
|
|
36685
37134
|
if (kind !== 6) return void 0;
|
|
36686
|
-
const
|
|
36687
|
-
const embedded2 =
|
|
36688
|
-
if (embedded2 && (
|
|
36689
|
-
if (
|
|
36690
|
-
if (((
|
|
36691
|
-
if (
|
|
37135
|
+
const words3 = hextets2(plain3);
|
|
37136
|
+
const embedded2 = words3.slice(0, 5).every((w) => w === 0) && (words3[5] === 65535 || words3[5] === 0);
|
|
37137
|
+
if (embedded2 && (words3[5] === 65535 || words3[6] !== 0)) return ipv4Place(ipv4Of(words3));
|
|
37138
|
+
if (words3.slice(0, 7).every((w) => w === 0) && (words3[7] ?? 0) <= 1) return "loopback";
|
|
37139
|
+
if (((words3[0] ?? 0) & 65472) === 65152) return "link-local";
|
|
37140
|
+
if (words3.join(":") === METADATA_V6) return "link-local";
|
|
36692
37141
|
return void 0;
|
|
36693
37142
|
}
|
|
36694
|
-
function ipv4Of(
|
|
36695
|
-
const [high = 0, low = 0] =
|
|
37143
|
+
function ipv4Of(words3) {
|
|
37144
|
+
const [high = 0, low = 0] = words3.slice(6);
|
|
36696
37145
|
return [high >> 8, high & 255, low >> 8, low & 255].join(".");
|
|
36697
37146
|
}
|
|
36698
37147
|
function hextets2(address) {
|
|
@@ -37532,6 +37981,7 @@ var Channel = class {
|
|
|
37532
37981
|
failure;
|
|
37533
37982
|
pending = /* @__PURE__ */ new Map();
|
|
37534
37983
|
watching = /* @__PURE__ */ new Map();
|
|
37984
|
+
tracing = /* @__PURE__ */ new Map();
|
|
37535
37985
|
cdpHooks = /* @__PURE__ */ new Map();
|
|
37536
37986
|
cdpOpen = /* @__PURE__ */ new Set();
|
|
37537
37987
|
facadeHooks = /* @__PURE__ */ new Map();
|
|
@@ -37599,6 +38049,10 @@ var Channel = class {
|
|
|
37599
38049
|
this.watching.get(data.watch)?.(data.change);
|
|
37600
38050
|
return;
|
|
37601
38051
|
}
|
|
38052
|
+
if (data.trace !== void 0 && data.id === void 0) {
|
|
38053
|
+
this.traced(data.trace, data.events ?? [], data.dropped ?? 0);
|
|
38054
|
+
return;
|
|
38055
|
+
}
|
|
37602
38056
|
if (data.bye !== void 0) {
|
|
37603
38057
|
this.end(new KernelGoneError(data.bye));
|
|
37604
38058
|
return;
|
|
@@ -37627,6 +38081,23 @@ var Channel = class {
|
|
|
37627
38081
|
if (reply.error !== void 0) call.reject(new KernelCallError(reply.error, reply.code));
|
|
37628
38082
|
else call.resolve(reply.result);
|
|
37629
38083
|
}
|
|
38084
|
+
traced(trace, events, dropped2) {
|
|
38085
|
+
const listener = this.tracing.get(trace);
|
|
38086
|
+
if (!listener) return;
|
|
38087
|
+
const ack = () => {
|
|
38088
|
+
if (this.tracing.has(trace)) this.post({ traceAck: trace });
|
|
38089
|
+
};
|
|
38090
|
+
let consumed;
|
|
38091
|
+
try {
|
|
38092
|
+
consumed = listener({ events, dropped: dropped2 });
|
|
38093
|
+
} finally {
|
|
38094
|
+
if (typeof consumed?.then === "function") {
|
|
38095
|
+
void Promise.resolve(consumed).then(ack, ack);
|
|
38096
|
+
} else {
|
|
38097
|
+
ack();
|
|
38098
|
+
}
|
|
38099
|
+
}
|
|
38100
|
+
}
|
|
37630
38101
|
openCdp({ cdpOpen: { registration, ...request }, port }) {
|
|
37631
38102
|
const hook = registration === void 0 ? void 0 : this.cdpHooks.get(registration);
|
|
37632
38103
|
if (!hook || this.failure) {
|
|
@@ -37689,6 +38160,29 @@ async function serveCdpOn(channel, hook, options) {
|
|
|
37689
38160
|
}
|
|
37690
38161
|
};
|
|
37691
38162
|
}
|
|
38163
|
+
async function traceOn(channel, tag, onEvents, options) {
|
|
38164
|
+
if (typeof onEvents !== "function") throw new TypeError("trace: onEvents must be a function");
|
|
38165
|
+
await since(channel, 15, "trace", async () => {
|
|
38166
|
+
});
|
|
38167
|
+
const { id: id2, done: done4 } = channel.request({
|
|
38168
|
+
op: "trace",
|
|
38169
|
+
tag,
|
|
38170
|
+
...options.limit !== void 0 ? { limit: options.limit } : {}
|
|
38171
|
+
});
|
|
38172
|
+
channel.tracing.set(id2, onEvents);
|
|
38173
|
+
try {
|
|
38174
|
+
await done4;
|
|
38175
|
+
} catch (error) {
|
|
38176
|
+
channel.tracing.delete(id2);
|
|
38177
|
+
throw error;
|
|
38178
|
+
}
|
|
38179
|
+
return {
|
|
38180
|
+
close: () => {
|
|
38181
|
+
if (!channel.tracing.delete(id2)) return;
|
|
38182
|
+
void channel.call({ op: "untrace", trace: id2 }).catch(() => void 0);
|
|
38183
|
+
}
|
|
38184
|
+
};
|
|
38185
|
+
}
|
|
37692
38186
|
async function dialOn(channel, { port, host }) {
|
|
37693
38187
|
const req = { op: "dial", port, ...host ? { host } : {} };
|
|
37694
38188
|
return dialStream(await since(channel, 4, "dial", () => channel.call(req)));
|
|
@@ -37702,6 +38196,8 @@ async function spawnOn(channel, argv, opts) {
|
|
|
37702
38196
|
});
|
|
37703
38197
|
if (opts.tag !== void 0) await since(channel, 12, "a tag per spawn", async () => {
|
|
37704
38198
|
});
|
|
38199
|
+
if (Array.isArray(opts.tag)) await since(channel, 15, "several tags per spawn", async () => {
|
|
38200
|
+
});
|
|
37705
38201
|
if (opts.cwdMissing === "error") await since(channel, 14, "cwdMissing: 'error'", async () => {
|
|
37706
38202
|
});
|
|
37707
38203
|
const started = Promise.withResolvers();
|
|
@@ -37726,6 +38222,19 @@ async function spawnOn(channel, argv, opts) {
|
|
|
37726
38222
|
signal: async (name2 = "SIGTERM") => void await channel.call({ op: "kill", pid: -pgid, signal: name2 })
|
|
37727
38223
|
};
|
|
37728
38224
|
}
|
|
38225
|
+
async function sudoRunOn(channel, replay, { stdout, stderr, tag }) {
|
|
38226
|
+
await since(channel, 16, "sudoRun", async () => {
|
|
38227
|
+
});
|
|
38228
|
+
const started = Promise.withResolvers();
|
|
38229
|
+
let endedBy;
|
|
38230
|
+
const { done: done4 } = channel.request(
|
|
38231
|
+
{ op: "sudo-run", replay, options: { stdout, stderr, tag } },
|
|
38232
|
+
{ started: started.resolve, ended: (signal) => endedBy = signal }
|
|
38233
|
+
);
|
|
38234
|
+
done4.catch(started.reject);
|
|
38235
|
+
const pid = await started.promise;
|
|
38236
|
+
return { pid, pgid: pid, ended: endedOf(done4, () => endedBy) };
|
|
38237
|
+
}
|
|
37729
38238
|
function endedOf(done4, signal) {
|
|
37730
38239
|
return quiet(
|
|
37731
38240
|
done4.then((status) => {
|
|
@@ -37763,6 +38272,10 @@ async function terminalOn(channel, argv, opts) {
|
|
|
37763
38272
|
});
|
|
37764
38273
|
if (opts.tag !== void 0) await since(channel, 12, "a tag per terminal", async () => {
|
|
37765
38274
|
});
|
|
38275
|
+
if (Array.isArray(opts.tag)) {
|
|
38276
|
+
await since(channel, 15, "several tags per terminal", async () => {
|
|
38277
|
+
});
|
|
38278
|
+
}
|
|
37766
38279
|
if (opts.cwdMissing === "error") await since(channel, 14, "cwdMissing: 'error'", async () => {
|
|
37767
38280
|
});
|
|
37768
38281
|
let listener = onData ?? null;
|
|
@@ -37922,6 +38435,8 @@ async function attachKernel(port, options = {}) {
|
|
|
37922
38435
|
channel.traitsListeners.add(listener);
|
|
37923
38436
|
return () => channel.traitsListeners.delete(listener);
|
|
37924
38437
|
},
|
|
38438
|
+
trace: (tag, onEvents, options2 = {}) => traceOn(channel, tag, onEvents, options2),
|
|
38439
|
+
sudoRun: (replay, options2 = {}) => sudoRunOn(channel, replay, options2),
|
|
37925
38440
|
async close(o = {}) {
|
|
37926
38441
|
if (channel.failure) return;
|
|
37927
38442
|
await channel.call({ op: "detach", ...o.kill ? { kill: true } : {} }).catch(() => void 0);
|
|
@@ -38236,6 +38751,30 @@ function checkedRequests(hook) {
|
|
|
38236
38751
|
const kinds = kindsOf(hook);
|
|
38237
38752
|
return { kinds, handle: (request) => hook.handle(request) };
|
|
38238
38753
|
}
|
|
38754
|
+
function nodeAccounts(launcher, guard2) {
|
|
38755
|
+
return {
|
|
38756
|
+
users: {
|
|
38757
|
+
mode: launcher.usersMode,
|
|
38758
|
+
add: (user) => guard2(() => launcher.users.add(user)),
|
|
38759
|
+
remove: (name2, options) => guard2(() => launcher.users.remove(name2, options)),
|
|
38760
|
+
list: () => guard2(() => launcher.users.list())
|
|
38761
|
+
},
|
|
38762
|
+
groups: {
|
|
38763
|
+
list: () => guard2(() => launcher.users.groups()),
|
|
38764
|
+
add: (name2, options) => guard2(() => launcher.users.addGroup(name2, options?.gid)),
|
|
38765
|
+
addMember: (group, user) => guard2(() => launcher.users.addMember(group, user)),
|
|
38766
|
+
removeMember: (group, user) => guard2(() => launcher.users.removeMember(group, user))
|
|
38767
|
+
}
|
|
38768
|
+
};
|
|
38769
|
+
}
|
|
38770
|
+
async function nodeSudoRun(launcher, guard2, replay, options) {
|
|
38771
|
+
const running = await guard2(() => launcher.sudoRun(replay, options));
|
|
38772
|
+
const ended = guard2(async () => {
|
|
38773
|
+
const { status, signal } = await running.ended;
|
|
38774
|
+
return { status, ...signalField(signal) };
|
|
38775
|
+
});
|
|
38776
|
+
return { pid: running.pid, pgid: running.pid, ended: quiet(ended) };
|
|
38777
|
+
}
|
|
38239
38778
|
async function createNodeKernel(options = {}) {
|
|
38240
38779
|
const hooks = sealedHooks(options.sealed);
|
|
38241
38780
|
const timeZone = systemTimeZone(options.timeZone, clockZone(options.cron?.clock));
|
|
@@ -38364,18 +38903,8 @@ async function createNodeKernel(options = {}) {
|
|
|
38364
38903
|
mounts: async () => launcher.mounts.listed(),
|
|
38365
38904
|
setRoutes: (routes) => guard2(async () => launcher.setRoutes(routes)),
|
|
38366
38905
|
updateTransportTraits: () => guard2(async () => launcher.updateTransportTraits()),
|
|
38367
|
-
|
|
38368
|
-
|
|
38369
|
-
add: (user) => guard2(() => launcher.users.add(user)),
|
|
38370
|
-
remove: (name2, options2) => guard2(() => launcher.users.remove(name2, options2)),
|
|
38371
|
-
list: () => guard2(() => launcher.users.list())
|
|
38372
|
-
},
|
|
38373
|
-
groups: {
|
|
38374
|
-
list: () => guard2(() => launcher.users.groups()),
|
|
38375
|
-
add: (name2, options2) => guard2(() => launcher.users.addGroup(name2, options2?.gid)),
|
|
38376
|
-
addMember: (group, user) => guard2(() => launcher.users.addMember(group, user)),
|
|
38377
|
-
removeMember: (group, user) => guard2(() => launcher.users.removeMember(group, user))
|
|
38378
|
-
},
|
|
38906
|
+
...nodeAccounts(launcher, guard2),
|
|
38907
|
+
sudoRun: (replay, sudoOptions = {}) => nodeSudoRun(launcher, guard2, replay, sudoOptions),
|
|
38379
38908
|
async connect(connectOptions = {}) {
|
|
38380
38909
|
if (terminated) throw new Error(TERMINATED);
|
|
38381
38910
|
const { user } = connectOptions;
|