@ai-ecoverse/slicc-kernel 1.178.1 → 1.178.2
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 +8 -7
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +172 -54
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.cjs +1 -1
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +172 -54
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/kernel/children.d.ts +5 -0
- package/dist/types/launcher.d.ts +3 -0
- package/dist/types/node.d.ts +1 -1
- package/dist/types/sudo.d.ts +12 -1
- package/dist/types/sudoers.d.ts +3 -1
- package/package.json +3 -3
package/dist/kernel-worker.js
CHANGED
|
@@ -8777,7 +8777,7 @@ function satisfies(version, range2) {
|
|
|
8777
8777
|
}
|
|
8778
8778
|
|
|
8779
8779
|
// src/version.ts
|
|
8780
|
-
var KERNEL_VERSION = true ? "1.178.
|
|
8780
|
+
var KERNEL_VERSION = true ? "1.178.2" : "0.0.0-dev";
|
|
8781
8781
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8782
8782
|
function procVersion() {
|
|
8783
8783
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10059,9 +10059,9 @@ var UnlinkedKeeper = class {
|
|
|
10059
10059
|
if (kept.whole) return kept.undoing(async () => void 0);
|
|
10060
10060
|
await kept.undoing(
|
|
10061
10061
|
() => replace(target, async (gated, sealed) => {
|
|
10062
|
-
const
|
|
10063
|
-
if (
|
|
10064
|
-
else await keeper.settle(
|
|
10062
|
+
const pinned2 = await keeper.pin(target, gated, sealed);
|
|
10063
|
+
if (pinned2.moved.has(target)) pinned2.commit();
|
|
10064
|
+
else await keeper.settle(pinned2, () => fs.rm(path, options));
|
|
10065
10065
|
})
|
|
10066
10066
|
);
|
|
10067
10067
|
},
|
|
@@ -10074,8 +10074,8 @@ var UnlinkedKeeper = class {
|
|
|
10074
10074
|
() => keeper.marks.around(
|
|
10075
10075
|
[source],
|
|
10076
10076
|
() => replace(target, async (gated, sealed) => {
|
|
10077
|
-
const
|
|
10078
|
-
await keeper.settle(
|
|
10077
|
+
const pinned2 = await keeper.pin(target, gated, sealed);
|
|
10078
|
+
await keeper.settle(pinned2, () => fs.rename(from, to));
|
|
10079
10079
|
})
|
|
10080
10080
|
)
|
|
10081
10081
|
);
|
|
@@ -10110,14 +10110,14 @@ var UnlinkedKeeper = class {
|
|
|
10110
10110
|
}
|
|
10111
10111
|
return false;
|
|
10112
10112
|
}
|
|
10113
|
-
async settle(
|
|
10113
|
+
async settle(pinned2, op) {
|
|
10114
10114
|
try {
|
|
10115
10115
|
await op();
|
|
10116
10116
|
} catch (err) {
|
|
10117
|
-
await
|
|
10117
|
+
await pinned2.rollback();
|
|
10118
10118
|
throw err;
|
|
10119
10119
|
}
|
|
10120
|
-
|
|
10120
|
+
pinned2.commit();
|
|
10121
10121
|
}
|
|
10122
10122
|
heldWithin(target) {
|
|
10123
10123
|
const held = /* @__PURE__ */ new Map();
|
|
@@ -12641,6 +12641,13 @@ var SpawnError = class extends Error {
|
|
|
12641
12641
|
this.code = code;
|
|
12642
12642
|
}
|
|
12643
12643
|
};
|
|
12644
|
+
var ChangedError = class extends SpawnError {
|
|
12645
|
+
notice;
|
|
12646
|
+
constructor(notice) {
|
|
12647
|
+
super("EPERM");
|
|
12648
|
+
this.notice = notice;
|
|
12649
|
+
}
|
|
12650
|
+
};
|
|
12644
12651
|
function interrupted(signal) {
|
|
12645
12652
|
const { promise, reject } = Promise.withResolvers();
|
|
12646
12653
|
const fail2 = () => reject(new KernelError("EINTR"));
|
|
@@ -19620,8 +19627,10 @@ var SUDOERS_D = "/etc/sudoers.d";
|
|
|
19620
19627
|
var NAME4 = /^[a-z_][a-z0-9_.-]*$/;
|
|
19621
19628
|
var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
|
|
19622
19629
|
var KERNEL_FORM = new RegExp(
|
|
19623
|
-
String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()]*)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(\S+)\s+)?Cmnd(?:\s+(.*))?$`
|
|
19630
|
+
String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()]*)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(\S+)\s+)?((?:Sha256\s+${WORD}\s+)*)Cmnd(?:\s+(.*))?$`
|
|
19624
19631
|
);
|
|
19632
|
+
var SHA256_CLAUSE = new RegExp(String.raw`Sha256\s+(${WORD})`, "g");
|
|
19633
|
+
var HEX = /^[0-9a-f]{64}$/;
|
|
19625
19634
|
var SUDOERS_FORM = /^(\S+)\s+([^\s=]+)\s*=\s*(.*)$/;
|
|
19626
19635
|
var RUNAS = /^\(([^()]*)\)\s*/;
|
|
19627
19636
|
var TAG = /^([A-Z_]+)\s*:\s*/;
|
|
@@ -19630,7 +19639,7 @@ var PLAIN2 = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
|
19630
19639
|
var CONTROL2 = /[\p{Cc}\u2028\u2029]/u;
|
|
19631
19640
|
var CONTROLS = /[\p{Cc}\u2028\u2029]/gu;
|
|
19632
19641
|
var SHOWN_MAX = 60;
|
|
19633
|
-
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] Cmnd <command\u2026|*>";
|
|
19642
|
+
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] [Sha256 <file>=<sha256>\u2026] Cmnd <command\u2026|*>";
|
|
19634
19643
|
var SUDOERS_SYNTAX = "<user|%group|ALL> ALL=[(<target>[,<target>\u2026])] [NOPASSWD:] <command\u2026|ALL>";
|
|
19635
19644
|
var EXPECTED = `expected '${KERNEL_SYNTAX}' or '${SUDOERS_SYNTAX}'`;
|
|
19636
19645
|
var TIMESTAMP_TIMEOUT = 5;
|
|
@@ -19797,6 +19806,23 @@ function commandOf2(text, after) {
|
|
|
19797
19806
|
if (only && /\s""$/.test(text)) return { path, args: [] };
|
|
19798
19807
|
return args.length === 0 ? { path } : { path, args };
|
|
19799
19808
|
}
|
|
19809
|
+
function sha256Of(clauses) {
|
|
19810
|
+
const files = {};
|
|
19811
|
+
for (const [, word = ""] of clauses.matchAll(SHA256_CLAUSE)) {
|
|
19812
|
+
const [text = ""] = splitWords(word) ?? [];
|
|
19813
|
+
const at = text.lastIndexOf("=");
|
|
19814
|
+
const path = text.slice(0, at);
|
|
19815
|
+
const hex4 = text.slice(at + 1).toLowerCase();
|
|
19816
|
+
if (!path.startsWith("/") || !HEX.test(hex4)) {
|
|
19817
|
+
return `Sha256 expects <absolute file>=<sha256 in hex>, not ${shown(word)}`;
|
|
19818
|
+
}
|
|
19819
|
+
files[normalizePath(path)] = hex4;
|
|
19820
|
+
}
|
|
19821
|
+
return files;
|
|
19822
|
+
}
|
|
19823
|
+
function sha256Text(files) {
|
|
19824
|
+
return Object.entries(files).map(([path, hex4]) => `Sha256 ${quoteWord(`${path}=${hex4}`)} `).join("");
|
|
19825
|
+
}
|
|
19800
19826
|
function targetError(list) {
|
|
19801
19827
|
if (list.includes(",")) {
|
|
19802
19828
|
return `(${list}) names one user in this form: write one line per user, or use '${SUDOERS_SYNTAX}'`;
|
|
@@ -19804,19 +19830,22 @@ function targetError(list) {
|
|
|
19804
19830
|
return `expected one user name or ALL in (\u2026), not ${shown(`(${list})`)}`;
|
|
19805
19831
|
}
|
|
19806
19832
|
function kernelRules(m) {
|
|
19807
|
-
const [, nopasswd, who = "", target = "root", dir, stdin, rest2 = ""] = m;
|
|
19833
|
+
const [, nopasswd, who = "", target = "root", dir, stdin, sums = "", rest2 = ""] = m;
|
|
19808
19834
|
const bad = whoError(who);
|
|
19809
19835
|
if (bad !== void 0) return bad;
|
|
19810
19836
|
if (target !== "ALL" && !NAME4.test(target)) return targetError(target);
|
|
19811
19837
|
const cwd = dir === void 0 ? void 0 : cwdOf(dir);
|
|
19812
19838
|
if (cwd === "") return `Cwd expects one absolute directory, not ${shown(dir)}`;
|
|
19813
19839
|
if (stdin !== void 0 && stdin !== "none") return `Stdin takes none, not ${shown(stdin)}`;
|
|
19840
|
+
const sha2564 = sha256Of(sums);
|
|
19841
|
+
if (typeof sha2564 === "string") return sha2564;
|
|
19814
19842
|
const rule = {
|
|
19815
19843
|
nopasswd: !!nopasswd,
|
|
19816
19844
|
who,
|
|
19817
19845
|
target,
|
|
19818
19846
|
...cwd !== void 0 ? { cwd } : {},
|
|
19819
|
-
...stdin === "none" ? { stdin: "none" } : {}
|
|
19847
|
+
...stdin === "none" ? { stdin: "none" } : {},
|
|
19848
|
+
...sums === "" ? {} : { sha256: sha2564 }
|
|
19820
19849
|
};
|
|
19821
19850
|
const text = rest2.trim();
|
|
19822
19851
|
if (text === "*") return [{ ...rule, cmnd: "*" }];
|
|
@@ -20069,12 +20098,26 @@ var RAN_ONCE = "sudo: ran once; this command line can't be recorded as always";
|
|
|
20069
20098
|
var ROOT_ONLY_MAX = 64;
|
|
20070
20099
|
var OPERANDS_MAX = 256;
|
|
20071
20100
|
var PATH_MAX = 4096;
|
|
20101
|
+
function userWritable(bound3) {
|
|
20102
|
+
const files = Object.entries(bound3).map(([path, hex4]) => ({ path, sha256: hex4 }));
|
|
20103
|
+
return files.length > 0 ? { userWritable: files } : {};
|
|
20104
|
+
}
|
|
20072
20105
|
function stdinMatches(rule, stdin) {
|
|
20073
20106
|
return rule.stdin === void 0 || stdin === "tty" || stdin === "none";
|
|
20074
20107
|
}
|
|
20075
20108
|
function recordable(cwd, argv) {
|
|
20076
20109
|
return !hasControl(cwd) && !argv.some(hasControl);
|
|
20077
20110
|
}
|
|
20111
|
+
function changedText(file) {
|
|
20112
|
+
return `sudo: ${file} changed since it was approved`;
|
|
20113
|
+
}
|
|
20114
|
+
function changedFile(approved, now) {
|
|
20115
|
+
const files = /* @__PURE__ */ new Set([...Object.keys(approved), ...Object.keys(now)]);
|
|
20116
|
+
return [...files].find((file) => approved[file] !== now[file]);
|
|
20117
|
+
}
|
|
20118
|
+
function pinned(rule, bound3) {
|
|
20119
|
+
return rule.sha256 === void 0 || changedFile(rule.sha256, bound3) === void 0;
|
|
20120
|
+
}
|
|
20078
20121
|
var VISUDO_FILES_MAX = 64;
|
|
20079
20122
|
var VISUDO_TEXT_MAX = 1024 * 1024;
|
|
20080
20123
|
function visudoCall(req) {
|
|
@@ -20205,11 +20248,13 @@ function usable(parsed2) {
|
|
|
20205
20248
|
function policyOf(settings) {
|
|
20206
20249
|
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
20207
20250
|
}
|
|
20208
|
-
function described(rule) {
|
|
20251
|
+
function described(rule, writable2) {
|
|
20209
20252
|
const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
|
|
20210
20253
|
const cwd = rule.cwd === void 0 ? "" : `CWD=${quoteWord(rule.cwd)} `;
|
|
20211
20254
|
const stdin = rule.stdin === void 0 ? "" : "STDIN=none ";
|
|
20212
|
-
|
|
20255
|
+
const sums = Object.entries(rule.sha256 ?? {}).map(([path, hex4]) => `SHA256=${quoteWord(path)}:${hex4} `).join("");
|
|
20256
|
+
const flag = writable2 ? " # user-writable command" : "";
|
|
20257
|
+
return ` (${rule.target}) ${cwd}${stdin}${sums}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}${flag}`;
|
|
20213
20258
|
}
|
|
20214
20259
|
var Sudo = class {
|
|
20215
20260
|
o;
|
|
@@ -20400,15 +20445,31 @@ var Sudo = class {
|
|
|
20400
20445
|
if (walked.denied !== void 0 || walked.missing) return false;
|
|
20401
20446
|
return (await this.o.fs.stat(cwd).catch(() => void 0))?.isDirectory === true;
|
|
20402
20447
|
}
|
|
20403
|
-
async
|
|
20404
|
-
const
|
|
20405
|
-
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
|
|
20448
|
+
async real(file) {
|
|
20449
|
+
const { fs } = this.o;
|
|
20450
|
+
return await fs.realpath?.(file, true).catch(() => file) ?? file;
|
|
20451
|
+
}
|
|
20452
|
+
async digests(files) {
|
|
20453
|
+
const { fs } = this.o;
|
|
20454
|
+
const found = {};
|
|
20455
|
+
for (const file of new Set(files)) {
|
|
20456
|
+
if (await this.trusted(file)) continue;
|
|
20457
|
+
const real = await this.real(file);
|
|
20458
|
+
found[real] = await fs.readFileBuffer(real).then(sha256, () => "");
|
|
20410
20459
|
}
|
|
20411
|
-
return
|
|
20460
|
+
return found;
|
|
20461
|
+
}
|
|
20462
|
+
async bound(path, cwd) {
|
|
20463
|
+
const files = this.o.backing ? await this.o.backing(path, cwd) : [];
|
|
20464
|
+
const bound3 = files && await this.digests([path, ...files]);
|
|
20465
|
+
return bound3 && !Object.values(bound3).includes("") ? bound3 : void 0;
|
|
20466
|
+
}
|
|
20467
|
+
async writable(rule) {
|
|
20468
|
+
if (rule.cmnd === "*") return false;
|
|
20469
|
+
const { path } = rule.cmnd;
|
|
20470
|
+
const bound3 = await this.bound(path, "/");
|
|
20471
|
+
if (bound3) return Object.keys(bound3).length > 0;
|
|
20472
|
+
return !await this.trusted(await this.existing(path));
|
|
20412
20473
|
}
|
|
20413
20474
|
async locate(word, cwd) {
|
|
20414
20475
|
const { fs } = this.o;
|
|
@@ -20453,7 +20514,7 @@ var Sudo = class {
|
|
|
20453
20514
|
const root = cred.euid === 0;
|
|
20454
20515
|
const rules = root ? [] : policy.rules;
|
|
20455
20516
|
const mine = rules.filter((rule) => applies(rule.who));
|
|
20456
|
-
if (parsed2.list) return this.listed(self, root, mine, settings.matching);
|
|
20517
|
+
if (parsed2.list) return await this.listed(self, root, mine, settings.matching);
|
|
20457
20518
|
const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
|
|
20458
20519
|
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
20459
20520
|
return this.command(session, root, mine, call);
|
|
@@ -20470,14 +20531,13 @@ var Sudo = class {
|
|
|
20470
20531
|
);
|
|
20471
20532
|
}
|
|
20472
20533
|
const path = await this.locate(word, cwd);
|
|
20473
|
-
|
|
20474
|
-
|
|
20475
|
-
if (untrusted2) return untrusted2;
|
|
20534
|
+
const bound3 = path && await this.bound(path, cwd);
|
|
20535
|
+
if (!path || !bound3) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20476
20536
|
const argv = [path, ...args];
|
|
20477
20537
|
const matching = mine.filter(
|
|
20478
|
-
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv)
|
|
20538
|
+
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv) && pinned(rule, bound3)
|
|
20479
20539
|
);
|
|
20480
|
-
const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
|
|
20540
|
+
const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term, bound3);
|
|
20481
20541
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
20482
20542
|
const quoted = argv.map(quoteWord).join(" ");
|
|
20483
20543
|
if (matching.length === 0) {
|
|
@@ -20493,14 +20553,15 @@ var Sudo = class {
|
|
|
20493
20553
|
}
|
|
20494
20554
|
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20495
20555
|
return this.pendingFor(cred.ruid, async () => {
|
|
20496
|
-
const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd });
|
|
20556
|
+
const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd, bound: bound3 });
|
|
20497
20557
|
if ("error" in answer2) return answer2;
|
|
20498
20558
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
20499
20559
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
20500
20560
|
}
|
|
20501
|
-
const
|
|
20502
|
-
if (
|
|
20503
|
-
const
|
|
20561
|
+
const now = await this.bound(path, cwd);
|
|
20562
|
+
if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20563
|
+
const shown2 = recordable(cwd, [...parsed2.command, ...Object.keys(bound3)]);
|
|
20564
|
+
const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
|
|
20504
20565
|
if (!("error" in result)) this.stamped(s, answer2);
|
|
20505
20566
|
return result;
|
|
20506
20567
|
});
|
|
@@ -20519,7 +20580,7 @@ var Sudo = class {
|
|
|
20519
20580
|
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20520
20581
|
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
20521
20582
|
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
20522
|
-
const answer2 = await this.ask(s, { argv: [], path: "", cwd, validate: true });
|
|
20583
|
+
const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
|
|
20523
20584
|
if ("error" in answer2) return answer2;
|
|
20524
20585
|
if (!answer2.person) await s.caller.report?.(NOT_STAMPED);
|
|
20525
20586
|
this.stamped(s, answer2);
|
|
@@ -20547,14 +20608,17 @@ var Sudo = class {
|
|
|
20547
20608
|
`sudo: ${self.name} : TTY=${tty} ; PWD=${quoteWord(cwd)} ; USER=${target.name} ; TIMESTAMP=${type} ; COMMAND=${command}`
|
|
20548
20609
|
);
|
|
20549
20610
|
}
|
|
20550
|
-
async always(self, target,
|
|
20611
|
+
async always(self, target, line, now, shown2, grant) {
|
|
20612
|
+
const { cwd, argv, bound: bound3 } = line;
|
|
20551
20613
|
if (!shown2 || !recordable(cwd, argv)) return { ...grant(), notice: RAN_ONCE };
|
|
20552
|
-
|
|
20614
|
+
const changed = changedFile(bound3, now);
|
|
20615
|
+
if (changed !== void 0) return failed2("EPERM", changedText(changed));
|
|
20616
|
+
if (!await this.remember(self, target, line)) {
|
|
20553
20617
|
return failed2("EIO", "sudo: the rule could not be recorded");
|
|
20554
20618
|
}
|
|
20555
20619
|
return grant();
|
|
20556
20620
|
}
|
|
20557
|
-
listed(self, root, rules, defaults2) {
|
|
20621
|
+
async listed(self, root, rules, defaults2) {
|
|
20558
20622
|
const host = this.o.hostname;
|
|
20559
20623
|
if (root) {
|
|
20560
20624
|
return {
|
|
@@ -20566,7 +20630,9 @@ var Sudo = class {
|
|
|
20566
20630
|
if (rules.length === 0) {
|
|
20567
20631
|
return failed2("EPERM", `User ${self.name} is not allowed to run sudo on ${host}.`);
|
|
20568
20632
|
}
|
|
20569
|
-
const
|
|
20633
|
+
const shown2 = [];
|
|
20634
|
+
for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
|
|
20635
|
+
const lines2 = shown2.join("\n");
|
|
20570
20636
|
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
20571
20637
|
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
20572
20638
|
${params}
|
|
@@ -20599,9 +20665,10 @@ ${lines2}
|
|
|
20599
20665
|
...caller.tty ? { tty: caller.tty } : {},
|
|
20600
20666
|
createdAt,
|
|
20601
20667
|
expiresAt: createdAt + timeout,
|
|
20602
|
-
always: asked.validate ? false : recordable(cwd, [path, ...argv]),
|
|
20668
|
+
always: asked.validate ? false : recordable(cwd, [path, ...argv, ...Object.keys(asked.bound)]),
|
|
20603
20669
|
stdin: caller.stdin,
|
|
20604
20670
|
rootOnly: asked.validate ? {} : await this.rootOnly(cwd, argv.slice(1)),
|
|
20671
|
+
...userWritable(asked.bound),
|
|
20605
20672
|
...asked.validate ? { validate: true } : {},
|
|
20606
20673
|
timestamp: true
|
|
20607
20674
|
};
|
|
@@ -20621,7 +20688,7 @@ ${lines2}
|
|
|
20621
20688
|
requester.removeEventListener("abort", withdraw);
|
|
20622
20689
|
}
|
|
20623
20690
|
}
|
|
20624
|
-
async remember(self, target, cwd, argv) {
|
|
20691
|
+
async remember(self, target, { cwd, argv, bound: bound3 }) {
|
|
20625
20692
|
const { fs } = this.o;
|
|
20626
20693
|
const path = `${SUDOERS_D}/${self.name}`;
|
|
20627
20694
|
if (!NAME4.test(self.name)) return false;
|
|
@@ -20632,7 +20699,7 @@ ${lines2}
|
|
|
20632
20699
|
if (st && !await this.trusted(path)) return false;
|
|
20633
20700
|
const old = st ? await fs.readFile(path) : "";
|
|
20634
20701
|
const words2 = recordedCommand(argv);
|
|
20635
|
-
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none Cmnd ${words2}
|
|
20702
|
+
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${words2}
|
|
20636
20703
|
`;
|
|
20637
20704
|
const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
|
|
20638
20705
|
${line}`;
|
|
@@ -20643,7 +20710,7 @@ ${line}`;
|
|
|
20643
20710
|
return false;
|
|
20644
20711
|
}
|
|
20645
20712
|
}
|
|
20646
|
-
grant(self, target, cred, argv, file, cwd, term) {
|
|
20713
|
+
grant(self, target, cred, argv, file, cwd, term, files) {
|
|
20647
20714
|
const env = {
|
|
20648
20715
|
...this.o.env(),
|
|
20649
20716
|
...userEnv(target, this.o.pnpmHome),
|
|
@@ -20656,7 +20723,9 @@ ${line}`;
|
|
|
20656
20723
|
SUDO_GID: String(cred.rgid),
|
|
20657
20724
|
SUDO_COMMAND: [file, ...argv.slice(1)].join(" ")
|
|
20658
20725
|
};
|
|
20659
|
-
return {
|
|
20726
|
+
return {
|
|
20727
|
+
grant: { file, argv: [...argv], env, cwd, cred: credOf(target), caller: cred, files }
|
|
20728
|
+
};
|
|
20660
20729
|
}
|
|
20661
20730
|
};
|
|
20662
20731
|
|
|
@@ -28983,14 +29052,20 @@ var WasmProcess = class {
|
|
|
28983
29052
|
if (!("grant" in answer2)) return answer2;
|
|
28984
29053
|
if (this.dying) throw new KernelError("EINTR");
|
|
28985
29054
|
if (answer2.notice !== void 0) await this.complainLine(answer2.notice);
|
|
28986
|
-
const { file, argv, env, cwd, cred, caller } = answer2.grant;
|
|
29055
|
+
const { file, argv, env, cwd, cred, caller, files } = answer2.grant;
|
|
28987
29056
|
const stdio = [{ fd: 0 }, { fd: 1 }, { fd: 2 }];
|
|
28988
|
-
|
|
28989
|
-
|
|
28990
|
-
|
|
28991
|
-
|
|
28992
|
-
|
|
28993
|
-
|
|
29057
|
+
try {
|
|
29058
|
+
return {
|
|
29059
|
+
pid: await this.children.spawn({ file, argv, env, cwd, pgid: 0 }, stdio, [], {
|
|
29060
|
+
cred,
|
|
29061
|
+
caller,
|
|
29062
|
+
files
|
|
29063
|
+
})
|
|
29064
|
+
};
|
|
29065
|
+
} catch (err) {
|
|
29066
|
+
if (err instanceof ChangedError) return { error: err.notice, errno: err.code };
|
|
29067
|
+
throw err;
|
|
29068
|
+
}
|
|
28994
29069
|
}
|
|
28995
29070
|
stdinKind() {
|
|
28996
29071
|
if (!this.fds.has(0)) return "none";
|
|
@@ -33569,6 +33644,10 @@ function describe(req, tty) {
|
|
|
33569
33644
|
};
|
|
33570
33645
|
return { argv: [req.argv0, ...req.args], tty, started: Date.now(), ...exe };
|
|
33571
33646
|
}
|
|
33647
|
+
function refuseChanged(approved, now) {
|
|
33648
|
+
const changed = changedFile(approved, now);
|
|
33649
|
+
if (changed !== void 0) throw new ChangedError(changedText(changed));
|
|
33650
|
+
}
|
|
33572
33651
|
function linksOf2(req) {
|
|
33573
33652
|
return req.links === void 0 ? {} : { links: req.links };
|
|
33574
33653
|
}
|
|
@@ -34702,6 +34781,27 @@ var Launcher = class {
|
|
|
34702
34781
|
}
|
|
34703
34782
|
return true;
|
|
34704
34783
|
}
|
|
34784
|
+
async unchanged(planned, file, cwd, grant) {
|
|
34785
|
+
const approved = grant?.files;
|
|
34786
|
+
if (!approved) return {};
|
|
34787
|
+
const files = await this.backingOf(planned, file, cwd, "");
|
|
34788
|
+
refuseChanged(approved, await this.sudoer.digests([file, ...files]));
|
|
34789
|
+
return { pinned: approved };
|
|
34790
|
+
}
|
|
34791
|
+
async pinOf(path, caller) {
|
|
34792
|
+
const { pinned: pinned2 } = caller;
|
|
34793
|
+
if (!pinned2) return {};
|
|
34794
|
+
const real = await this.sudoer.real(path);
|
|
34795
|
+
const hex4 = pinned2[real];
|
|
34796
|
+
if (hex4 !== void 0) return { [real]: hex4 };
|
|
34797
|
+
if (await this.sudoer.trusted(real)) return {};
|
|
34798
|
+
throw new ChangedError(changedText(real));
|
|
34799
|
+
}
|
|
34800
|
+
async pinnedBytes(pin, bytes) {
|
|
34801
|
+
const [real = ""] = Object.keys(pin);
|
|
34802
|
+
refuseChanged(pin, { [real]: await sha256(bytes) });
|
|
34803
|
+
return bytes;
|
|
34804
|
+
}
|
|
34705
34805
|
async grantedCwd(caller, cwd) {
|
|
34706
34806
|
const real = await physicalPathAsync(this.base, cwd).catch(() => void 0);
|
|
34707
34807
|
return real === cwd && await this.sudoer.reachable(caller, cwd);
|
|
@@ -34790,13 +34890,19 @@ var Launcher = class {
|
|
|
34790
34890
|
return found;
|
|
34791
34891
|
}
|
|
34792
34892
|
async module(path, caller, asked) {
|
|
34793
|
-
const
|
|
34893
|
+
const pin = await this.pinOf(path, caller);
|
|
34894
|
+
const sum = Object.values(pin)[0];
|
|
34895
|
+
const key = sum === void 0 ? await this.cacheKey(path, asked) : `${path}:sha256:${sum}`;
|
|
34794
34896
|
return this.cached(this.compiled, key, async () => {
|
|
34795
|
-
const { bytes, key:
|
|
34897
|
+
const { bytes: read, key: readKey } = await this.keyed(path, caller, asked);
|
|
34898
|
+
const bytes = sum === void 0 ? read : await this.pinnedBytes(pin, read);
|
|
34796
34899
|
const memory = importedMemory(bytes);
|
|
34797
34900
|
const foreign = foreignImports(bytes);
|
|
34798
34901
|
const compiled = await WebAssembly.compile(bytes);
|
|
34799
|
-
return {
|
|
34902
|
+
return {
|
|
34903
|
+
value: { module: compiled, ...memory ? { memory } : {}, foreign },
|
|
34904
|
+
key: sum === void 0 ? readKey : key
|
|
34905
|
+
};
|
|
34800
34906
|
});
|
|
34801
34907
|
}
|
|
34802
34908
|
async checkedSources(planned, caller) {
|
|
@@ -34821,6 +34927,11 @@ var Launcher = class {
|
|
|
34821
34927
|
}
|
|
34822
34928
|
}
|
|
34823
34929
|
async text(path, caller, asked) {
|
|
34930
|
+
const pin = await this.pinOf(path, caller);
|
|
34931
|
+
if (Object.keys(pin).length > 0) {
|
|
34932
|
+
const { bytes } = await this.keyed(path, caller, asked);
|
|
34933
|
+
return new TextDecoder().decode(await this.pinnedBytes(pin, bytes));
|
|
34934
|
+
}
|
|
34824
34935
|
if (!this.integrity) return this.base.readFile(path);
|
|
34825
34936
|
const key = await this.cacheKey(path, asked);
|
|
34826
34937
|
return this.cached(this.texts, key, async () => {
|
|
@@ -35009,7 +35120,12 @@ var Launcher = class {
|
|
|
35009
35120
|
const own = env.HOSTNAME;
|
|
35010
35121
|
if (asked.target.abi === "wasi") env.HOSTNAME = this.hostname;
|
|
35011
35122
|
const planned = chosen2(asked, env);
|
|
35012
|
-
const caller = {
|
|
35123
|
+
const caller = {
|
|
35124
|
+
pid: req.ppid ?? 0,
|
|
35125
|
+
uid: req.cred.euid,
|
|
35126
|
+
tag: req.tags?.[0],
|
|
35127
|
+
pinned: req.pinned
|
|
35128
|
+
};
|
|
35013
35129
|
if (this.integrity) await this.checkedSources(planned, caller);
|
|
35014
35130
|
const [program, exeId] = await this.loaded(planned.target, env, caller);
|
|
35015
35131
|
if (readsEtcHostname(program)) setOrDrop(env, "HOSTNAME", own);
|
|
@@ -35524,6 +35640,7 @@ var Launcher = class {
|
|
|
35524
35640
|
const scope = grant ? "" : await this.scopeOf(cred.euid);
|
|
35525
35641
|
const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
|
|
35526
35642
|
const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
|
|
35643
|
+
const pin = await this.unchanged(planned, file, req.cwd, grant);
|
|
35527
35644
|
const { recheck, ...redeemed } = await this.redeemed(
|
|
35528
35645
|
{ ...req, file },
|
|
35529
35646
|
{ ppid, cred, scope, booked },
|
|
@@ -35535,6 +35652,7 @@ var Launcher = class {
|
|
|
35535
35652
|
const replan = () => this.planOrSearch({ ...req, file }, { ppid, cred, scope });
|
|
35536
35653
|
const again = { ...planned, replan, recheck };
|
|
35537
35654
|
const handle = await this.launch(again, {
|
|
35655
|
+
...pin,
|
|
35538
35656
|
...deciding ? { decided: deciding.promise } : {},
|
|
35539
35657
|
cwd: req.cwd,
|
|
35540
35658
|
cwdFrom: inheritedCwd(req.chdir, this.cwdSlots.get(ppid)),
|