@ai-ecoverse/slicc-kernel 1.176.2 → 1.177.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 +28 -7
- package/dist/index.js +8 -1
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +328 -50
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.cjs +1 -1
- package/dist/node-process-worker.cjs.map +2 -2
- package/dist/node-process-worker.js +1 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +329 -50
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +1 -1
- package/dist/process-worker.js.map +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/launcher.d.ts +5 -0
- package/dist/types/node.d.ts +6 -1
- package/dist/types/process/js/js-context.d.ts +2 -0
- package/dist/types/sudo-timestamp.d.ts +28 -0
- package/dist/types/sudo.d.ts +20 -1
- package/dist/types/sudoers.d.ts +25 -2
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -3709,7 +3709,7 @@ function satisfies(version, range2) {
|
|
|
3709
3709
|
}
|
|
3710
3710
|
|
|
3711
3711
|
// src/version.ts
|
|
3712
|
-
var KERNEL_VERSION = true ? "1.
|
|
3712
|
+
var KERNEL_VERSION = true ? "1.177.0" : "0.0.0-dev";
|
|
3713
3713
|
var OS_RELEASE = "6.0.0-slicc";
|
|
3714
3714
|
function procVersion() {
|
|
3715
3715
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -14488,8 +14488,8 @@ var SCRUB_IDLE_MS = 50;
|
|
|
14488
14488
|
var SCRUB_NONBLOCK_MAX = 64 * 1024;
|
|
14489
14489
|
var encoder5 = new TextEncoder();
|
|
14490
14490
|
function scrubberOf(secrets, sameLength = false) {
|
|
14491
|
-
const
|
|
14492
|
-
return
|
|
14491
|
+
const usable3 = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
|
|
14492
|
+
return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
|
|
14493
14493
|
}
|
|
14494
14494
|
async function writeAll(file, bytes) {
|
|
14495
14495
|
const write = file.write;
|
|
@@ -15128,6 +15128,56 @@ var SealedBroker = class {
|
|
|
15128
15128
|
}
|
|
15129
15129
|
};
|
|
15130
15130
|
|
|
15131
|
+
// src/sudo-timestamp.ts
|
|
15132
|
+
var TIMESTAMPS_MAX = 64;
|
|
15133
|
+
var MINUTE_MS2 = 6e4;
|
|
15134
|
+
function timestampKey(owner, type) {
|
|
15135
|
+
if (type === "global") return "global";
|
|
15136
|
+
const { terminal, parent } = owner;
|
|
15137
|
+
if (type === "tty" && terminal) {
|
|
15138
|
+
return JSON.stringify(["tty", terminal.tty, terminal.sid, terminal.started]);
|
|
15139
|
+
}
|
|
15140
|
+
return parent ? JSON.stringify(["ppid", parent.pid, parent.started]) : void 0;
|
|
15141
|
+
}
|
|
15142
|
+
var Timestamps = class {
|
|
15143
|
+
users = /* @__PURE__ */ new Map();
|
|
15144
|
+
now;
|
|
15145
|
+
constructor(now) {
|
|
15146
|
+
this.now = now;
|
|
15147
|
+
}
|
|
15148
|
+
current(owner, policy) {
|
|
15149
|
+
if (policy.minutes === 0) return false;
|
|
15150
|
+
const key = timestampKey(owner, policy.type);
|
|
15151
|
+
const at = key === void 0 ? void 0 : this.users.get(owner.uid)?.get(key);
|
|
15152
|
+
if (at === void 0) return false;
|
|
15153
|
+
const now = this.now();
|
|
15154
|
+
return at <= now && (policy.minutes < 0 || now - at < policy.minutes * MINUTE_MS2);
|
|
15155
|
+
}
|
|
15156
|
+
stamp(owner, policy) {
|
|
15157
|
+
const key = policy.minutes === 0 ? void 0 : timestampKey(owner, policy.type);
|
|
15158
|
+
if (key === void 0) return false;
|
|
15159
|
+
const now = this.now();
|
|
15160
|
+
const records = this.users.get(owner.uid) ?? /* @__PURE__ */ new Map();
|
|
15161
|
+
this.users.set(owner.uid, records);
|
|
15162
|
+
records.delete(key);
|
|
15163
|
+
records.set(key, now);
|
|
15164
|
+
for (const [old, at] of records) {
|
|
15165
|
+
const stale = policy.minutes > 0 && (at > now || now - at >= policy.minutes * MINUTE_MS2);
|
|
15166
|
+
if (stale || records.size > TIMESTAMPS_MAX) records.delete(old);
|
|
15167
|
+
}
|
|
15168
|
+
return true;
|
|
15169
|
+
}
|
|
15170
|
+
invalidate(owner, type) {
|
|
15171
|
+
const key = timestampKey(owner, type);
|
|
15172
|
+
const records = this.users.get(owner.uid);
|
|
15173
|
+
if (key !== void 0) records?.delete(key);
|
|
15174
|
+
if (records?.size === 0) this.users.delete(owner.uid);
|
|
15175
|
+
}
|
|
15176
|
+
remove(uid) {
|
|
15177
|
+
this.users.delete(uid);
|
|
15178
|
+
}
|
|
15179
|
+
};
|
|
15180
|
+
|
|
15131
15181
|
// src/sudoers.ts
|
|
15132
15182
|
var SUDOERS = "/etc/sudoers";
|
|
15133
15183
|
var SUDOERS_D = "/etc/sudoers.d";
|
|
@@ -15147,6 +15197,8 @@ var SHOWN_MAX = 60;
|
|
|
15147
15197
|
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] Cmnd <command\u2026|*>";
|
|
15148
15198
|
var SUDOERS_SYNTAX = "<user|%group|ALL> ALL=[(<target>[,<target>\u2026])] [NOPASSWD:] <command\u2026|ALL>";
|
|
15149
15199
|
var EXPECTED = `expected '${KERNEL_SYNTAX}' or '${SUDOERS_SYNTAX}'`;
|
|
15200
|
+
var TIMESTAMP_TIMEOUT = 5;
|
|
15201
|
+
var TIMESTAMP_TYPE = "tty";
|
|
15150
15202
|
function hasControl(text) {
|
|
15151
15203
|
return CONTROL.test(text);
|
|
15152
15204
|
}
|
|
@@ -15429,9 +15481,6 @@ function directiveError(text) {
|
|
|
15429
15481
|
return /^#-?\d+\s+\S+\s*=/.test(text) ? "a #uid isn't supported: name the user, %group or ALL" : null;
|
|
15430
15482
|
}
|
|
15431
15483
|
const first = /^\S+/.exec(text)?.[0] ?? "";
|
|
15432
|
-
if (/^Defaults([:@>!]|$)/.test(first)) {
|
|
15433
|
-
return "Defaults aren't supported: the kernel's sudo has no settings, so this changes nothing";
|
|
15434
|
-
}
|
|
15435
15484
|
if (/^[A-Z][A-Za-z]*_Alias$/.test(first)) {
|
|
15436
15485
|
return "aliases aren't supported: name the users, targets and commands in each line";
|
|
15437
15486
|
}
|
|
@@ -15457,9 +15506,90 @@ function kernelLine(m, line, file) {
|
|
|
15457
15506
|
)
|
|
15458
15507
|
};
|
|
15459
15508
|
}
|
|
15509
|
+
var DEFAULTS = /^Defaults(?=[:@>!\s]|$)/;
|
|
15510
|
+
var DEFAULTS_ONLY = `Defaults aren't supported, except timestamp_timeout and timestamp_type for everyone, a user or a %group: this line changes nothing`;
|
|
15511
|
+
var PARAM = /^(!*)([A-Za-z_]+)(?:\s*([-+]?=)\s*(.*))?$/s;
|
|
15512
|
+
var MINUTES = /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$/;
|
|
15513
|
+
var TYPES = ["tty", "ppid", "global"];
|
|
15514
|
+
function defaultsValue(raw2) {
|
|
15515
|
+
const quoted = /^"(.*)"$/s.exec(raw2);
|
|
15516
|
+
return quoted ? quoted[1] : raw2;
|
|
15517
|
+
}
|
|
15518
|
+
function timeoutParam({ text, negated, op, value: value2 }) {
|
|
15519
|
+
if (negated && op === void 0) return { timestampTimeout: 0 };
|
|
15520
|
+
if (negated || op !== "=" || !MINUTES.test(value2) || !Number.isFinite(Number(value2))) {
|
|
15521
|
+
return `timestamp_timeout takes minutes, such as =5 or =2.5 (0 always asks, below 0 never expires), not ${shown(text)}`;
|
|
15522
|
+
}
|
|
15523
|
+
return { timestampTimeout: Number(value2) };
|
|
15524
|
+
}
|
|
15525
|
+
function typeParam({ text, negated, op, value: value2 }) {
|
|
15526
|
+
if (!negated && op === "=" && TYPES.includes(value2)) {
|
|
15527
|
+
return { timestampType: value2 };
|
|
15528
|
+
}
|
|
15529
|
+
if (value2 === "kernel") return "timestamp_type=kernel isn't supported: use tty, ppid or global";
|
|
15530
|
+
return `timestamp_type takes =tty, =ppid or =global, not ${shown(text)}`;
|
|
15531
|
+
}
|
|
15532
|
+
function defaultsParam(param) {
|
|
15533
|
+
const m = PARAM.exec(param);
|
|
15534
|
+
if (!m) return DEFAULTS_ONLY;
|
|
15535
|
+
const [, bangs = "", name2, op, raw2 = ""] = m;
|
|
15536
|
+
const read = { text: param, negated: bangs !== "", op, value: defaultsValue(raw2.trim()) };
|
|
15537
|
+
const settings = name2 === "timestamp_timeout" ? timeoutParam(read) : name2 === "timestamp_type" ? typeParam(read) : DEFAULTS_ONLY;
|
|
15538
|
+
if (typeof settings === "string") return settings;
|
|
15539
|
+
const value2 = settings.timestampTimeout ?? settings.timestampType;
|
|
15540
|
+
return { settings, text: `${name2}=${value2}` };
|
|
15541
|
+
}
|
|
15542
|
+
function defaultsWho(binding) {
|
|
15543
|
+
if (binding === "") return null;
|
|
15544
|
+
if (!binding.startsWith(":")) return DEFAULTS_ONLY;
|
|
15545
|
+
const who = binding.slice(1).split(",");
|
|
15546
|
+
for (const name2 of who) {
|
|
15547
|
+
if (!validWho(name2))
|
|
15548
|
+
return `expected users, %groups or ALL after Defaults:, not ${shown(name2)}`;
|
|
15549
|
+
}
|
|
15550
|
+
return who;
|
|
15551
|
+
}
|
|
15552
|
+
function defaultsLine(text) {
|
|
15553
|
+
const m = /^Defaults(\S*)(?:\s+(.*))?$/s.exec(text);
|
|
15554
|
+
const who = defaultsWho(m?.[1] ?? "");
|
|
15555
|
+
if (typeof who === "string") return { error: who };
|
|
15556
|
+
const list = (m?.[2] ?? "").trim();
|
|
15557
|
+
if (list === "")
|
|
15558
|
+
return {
|
|
15559
|
+
error: "expected timestamp_timeout=<minutes> or timestamp_type=<type> after Defaults"
|
|
15560
|
+
};
|
|
15561
|
+
const settings = {};
|
|
15562
|
+
const params = [];
|
|
15563
|
+
for (const param of list.split(",")) {
|
|
15564
|
+
const read = defaultsParam(param.trim());
|
|
15565
|
+
if (typeof read === "string") return { error: read };
|
|
15566
|
+
Object.assign(settings, read.settings);
|
|
15567
|
+
params.push(read.text);
|
|
15568
|
+
}
|
|
15569
|
+
return { defaults: { who, settings, params } };
|
|
15570
|
+
}
|
|
15571
|
+
function resolveDefaults(entries, applies) {
|
|
15572
|
+
const everyone = entries.filter((e) => e.who === null);
|
|
15573
|
+
const mine = entries.filter((e) => e.who?.some(applies));
|
|
15574
|
+
const matching = [...everyone, ...mine];
|
|
15575
|
+
const settings = Object.assign({}, ...matching.map((e) => e.settings));
|
|
15576
|
+
return {
|
|
15577
|
+
timestampTimeout: settings.timestampTimeout ?? TIMESTAMP_TIMEOUT,
|
|
15578
|
+
timestampType: settings.timestampType ?? TIMESTAMP_TYPE,
|
|
15579
|
+
matching
|
|
15580
|
+
};
|
|
15581
|
+
}
|
|
15582
|
+
function defaultsText(entries) {
|
|
15583
|
+
const all = resolveDefaults(entries, () => false);
|
|
15584
|
+
return [
|
|
15585
|
+
`Defaults timestamp_timeout=${all.timestampTimeout}, timestamp_type=${all.timestampType}`,
|
|
15586
|
+
...entries.filter((e) => e.who !== null).map((e) => `Defaults:${e.who.join(",")} ${e.params.join(", ")}`)
|
|
15587
|
+
];
|
|
15588
|
+
}
|
|
15460
15589
|
function parseLine(line, file) {
|
|
15461
15590
|
const text = line.trim();
|
|
15462
15591
|
if (text === "") return null;
|
|
15592
|
+
if (DEFAULTS.test(text)) return defaultsLine(text);
|
|
15463
15593
|
const directive = directiveError(text);
|
|
15464
15594
|
if (directive !== void 0) return directive === null ? null : { error: directive };
|
|
15465
15595
|
const kernel = KERNEL_FORM.exec(text);
|
|
@@ -15471,14 +15601,16 @@ function parseLine(line, file) {
|
|
|
15471
15601
|
}
|
|
15472
15602
|
function parsePolicy(text, file) {
|
|
15473
15603
|
const rules = [];
|
|
15604
|
+
const defaults2 = [];
|
|
15474
15605
|
const errors = [];
|
|
15475
15606
|
for (const [n, line] of text.split("\n").entries()) {
|
|
15476
15607
|
const parsed2 = parseLine(line, file);
|
|
15477
15608
|
if (parsed2 === null) continue;
|
|
15478
15609
|
if ("error" in parsed2) errors.push({ line: n + 1, message: parsed2.error });
|
|
15610
|
+
else if ("defaults" in parsed2) defaults2.push(parsed2.defaults);
|
|
15479
15611
|
else rules.push(...parsed2.rules);
|
|
15480
15612
|
}
|
|
15481
|
-
return { rules, errors };
|
|
15613
|
+
return { rules, defaults: defaults2, errors };
|
|
15482
15614
|
}
|
|
15483
15615
|
|
|
15484
15616
|
// src/users.ts
|
|
@@ -15863,7 +15995,14 @@ var SECURE_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
|
15863
15995
|
var DEFAULT_SUDOERS = "%sudo Cmnd *\n";
|
|
15864
15996
|
var SUDO_TIMEOUT_MS = 3e5;
|
|
15865
15997
|
var TERM = /^[A-Za-z0-9._+-]+$/;
|
|
15866
|
-
var USAGE =
|
|
15998
|
+
var USAGE = [
|
|
15999
|
+
"usage: sudo -K | -k",
|
|
16000
|
+
" sudo -v [-kn] [-u user]",
|
|
16001
|
+
" sudo -l [-kn]",
|
|
16002
|
+
" sudo [-kn] [-u user] [--] command [args...]"
|
|
16003
|
+
].join("\n");
|
|
16004
|
+
var DONE = { done: true };
|
|
16005
|
+
var NOT_STAMPED = "sudo: approved, but the approver didn't say a person decided, so no timestamp was recorded";
|
|
15867
16006
|
var DENIED2 = "sudo: approval denied";
|
|
15868
16007
|
var DENIAL_MAX = 500;
|
|
15869
16008
|
var ABORTED = /* @__PURE__ */ Symbol("aborted");
|
|
@@ -15937,6 +16076,17 @@ async function answerOf(approve, req, signal) {
|
|
|
15937
16076
|
return cut === void 0 ? "deny" : await cut;
|
|
15938
16077
|
}
|
|
15939
16078
|
}
|
|
16079
|
+
function approvalOf(answer2) {
|
|
16080
|
+
if (answer2 === "once" || answer2 === "always") return { answer: answer2, person: false };
|
|
16081
|
+
if (typeof answer2 !== "object" || answer2 === null) return void 0;
|
|
16082
|
+
try {
|
|
16083
|
+
const { answer: kind, timestamp } = answer2;
|
|
16084
|
+
if (kind !== "once" && kind !== "always" || timestamp !== true) return void 0;
|
|
16085
|
+
return { answer: kind, person: true };
|
|
16086
|
+
} catch {
|
|
16087
|
+
return void 0;
|
|
16088
|
+
}
|
|
16089
|
+
}
|
|
15940
16090
|
function recordedCommand(argv) {
|
|
15941
16091
|
const [path = "", ...args] = argv;
|
|
15942
16092
|
return commandText({ path, args: args.map((a) => [a]) });
|
|
@@ -15946,6 +16096,9 @@ function flags(parsed2, arg, next3) {
|
|
|
15946
16096
|
const flag = arg[j];
|
|
15947
16097
|
if (flag === "n") parsed2.nonInteractive = true;
|
|
15948
16098
|
else if (flag === "l") parsed2.list = true;
|
|
16099
|
+
else if (flag === "v") parsed2.validate = true;
|
|
16100
|
+
else if (flag === "k") parsed2.reset = true;
|
|
16101
|
+
else if (flag === "K") parsed2.removeAll = true;
|
|
15949
16102
|
else if (flag !== "u") return `sudo: invalid option -- '${flag}'
|
|
15950
16103
|
${USAGE}`;
|
|
15951
16104
|
else {
|
|
@@ -15959,7 +16112,15 @@ ${USAGE}`;
|
|
|
15959
16112
|
return 0;
|
|
15960
16113
|
}
|
|
15961
16114
|
function parseArgs(args) {
|
|
15962
|
-
const parsed2 = {
|
|
16115
|
+
const parsed2 = {
|
|
16116
|
+
list: false,
|
|
16117
|
+
nonInteractive: false,
|
|
16118
|
+
validate: false,
|
|
16119
|
+
reset: false,
|
|
16120
|
+
removeAll: false,
|
|
16121
|
+
user: "root",
|
|
16122
|
+
command: []
|
|
16123
|
+
};
|
|
15963
16124
|
let i = 0;
|
|
15964
16125
|
for (; i < args.length; i++) {
|
|
15965
16126
|
const arg = args[i];
|
|
@@ -15973,8 +16134,17 @@ function parseArgs(args) {
|
|
|
15973
16134
|
i += used;
|
|
15974
16135
|
}
|
|
15975
16136
|
parsed2.command = args.slice(i);
|
|
15976
|
-
|
|
15977
|
-
|
|
16137
|
+
return usable(parsed2) ? parsed2 : USAGE;
|
|
16138
|
+
}
|
|
16139
|
+
function usable(parsed2) {
|
|
16140
|
+
const command = parsed2.command.length > 0;
|
|
16141
|
+
const modes = [parsed2.list, parsed2.validate, parsed2.removeAll].filter(Boolean).length;
|
|
16142
|
+
if (modes > 1 || parsed2.removeAll && parsed2.reset) return false;
|
|
16143
|
+
if (modes === 1) return !command;
|
|
16144
|
+
return command || parsed2.reset;
|
|
16145
|
+
}
|
|
16146
|
+
function policyOf(settings) {
|
|
16147
|
+
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
15978
16148
|
}
|
|
15979
16149
|
function described(rule) {
|
|
15980
16150
|
const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
|
|
@@ -15986,9 +16156,11 @@ var Sudo = class {
|
|
|
15986
16156
|
o;
|
|
15987
16157
|
warned = /* @__PURE__ */ new Set();
|
|
15988
16158
|
pending = /* @__PURE__ */ new Set();
|
|
16159
|
+
timestamps;
|
|
15989
16160
|
next = 0;
|
|
15990
16161
|
constructor(options) {
|
|
15991
16162
|
this.o = options;
|
|
16163
|
+
this.timestamps = new Timestamps(options.now ?? (() => performance.now()));
|
|
15992
16164
|
}
|
|
15993
16165
|
warn(message3) {
|
|
15994
16166
|
if (this.warned.has(message3)) return;
|
|
@@ -16075,8 +16247,8 @@ var Sudo = class {
|
|
|
16075
16247
|
const code = err.code;
|
|
16076
16248
|
return skipped(`it can't be read (${typeof code === "string" ? code : "error"})`);
|
|
16077
16249
|
}
|
|
16078
|
-
const { rules, errors } = parsePolicy(text, path);
|
|
16079
|
-
return { path, rules, problems: errors, ok: true };
|
|
16250
|
+
const { rules, defaults: defaults2, errors } = parsePolicy(text, path);
|
|
16251
|
+
return { path, rules, defaults: defaults2, problems: errors, ok: true };
|
|
16080
16252
|
}
|
|
16081
16253
|
async policy() {
|
|
16082
16254
|
const files = [await this.policyFile(SUDOERS)];
|
|
@@ -16105,7 +16277,10 @@ var Sudo = class {
|
|
|
16105
16277
|
}
|
|
16106
16278
|
}
|
|
16107
16279
|
if (!detailed && skipped > 0) await caller.report?.(skippedNotice(skipped));
|
|
16108
|
-
return
|
|
16280
|
+
return {
|
|
16281
|
+
rules: files.flatMap((f) => f.rules),
|
|
16282
|
+
defaults: files.flatMap((f) => f.defaults ?? [])
|
|
16283
|
+
};
|
|
16109
16284
|
}
|
|
16110
16285
|
async check(cred, call) {
|
|
16111
16286
|
const lines2 = [];
|
|
@@ -16118,17 +16293,26 @@ var Sudo = class {
|
|
|
16118
16293
|
if (read && problems.length === 0) lines2.push([1, `${path}: parsed OK`]);
|
|
16119
16294
|
if (problems.length > 0) ok4 = false;
|
|
16120
16295
|
};
|
|
16296
|
+
const values = (defaults2) => {
|
|
16297
|
+
for (const line of defaultsText(defaults2)) lines2.push([1, line]);
|
|
16298
|
+
};
|
|
16121
16299
|
if (call.files !== void 0) {
|
|
16300
|
+
const defaults2 = [];
|
|
16122
16301
|
for (const { name: name2, text } of call.files) {
|
|
16123
|
-
|
|
16302
|
+
const parsed2 = parsePolicy(text);
|
|
16303
|
+
defaults2.push(...parsed2.defaults);
|
|
16304
|
+
report({ path: name2, rules: [], problems: parsed2.errors, ok: true });
|
|
16124
16305
|
}
|
|
16306
|
+
if (defaults2.length > 0) values(defaults2);
|
|
16125
16307
|
return { lines: lines2, ok: ok4 };
|
|
16126
16308
|
}
|
|
16127
16309
|
if (this.o.mode === "enforce" && cred.euid !== 0) {
|
|
16128
16310
|
const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
|
|
16129
16311
|
return { lines: [[2, `visudo: ${SUDOERS}: Permission denied: ${hint}`]], ok: false };
|
|
16130
16312
|
}
|
|
16131
|
-
|
|
16313
|
+
const files = await this.policy();
|
|
16314
|
+
for (const file of files) report(file);
|
|
16315
|
+
values(files.flatMap((f) => f.defaults ?? []));
|
|
16132
16316
|
return { lines: lines2, ok: ok4 };
|
|
16133
16317
|
}
|
|
16134
16318
|
async groupIds() {
|
|
@@ -16137,9 +16321,9 @@ var Sudo = class {
|
|
|
16137
16321
|
async applies(caller, cred) {
|
|
16138
16322
|
const gids = await this.groupIds();
|
|
16139
16323
|
const mine = /* @__PURE__ */ new Set([cred.rgid, cred.egid, ...cred.groups]);
|
|
16140
|
-
return (
|
|
16141
|
-
if (
|
|
16142
|
-
const gid =
|
|
16324
|
+
return (who) => {
|
|
16325
|
+
if (who === "ALL" || who === caller.name) return true;
|
|
16326
|
+
const gid = who.startsWith("%") ? gids.get(who.slice(1)) : void 0;
|
|
16143
16327
|
return gid !== void 0 && mine.has(gid);
|
|
16144
16328
|
};
|
|
16145
16329
|
}
|
|
@@ -16184,18 +16368,40 @@ var Sudo = class {
|
|
|
16184
16368
|
const mode = this.o.mode;
|
|
16185
16369
|
if (parsed2.list) return { list: `sudo: users are '${mode}': commands run as the caller
|
|
16186
16370
|
` };
|
|
16187
|
-
return { run: parsed2.command };
|
|
16371
|
+
return parsed2.command.length > 0 ? { run: parsed2.command } : DONE;
|
|
16188
16372
|
}
|
|
16189
16373
|
const { cred } = caller;
|
|
16190
16374
|
const policy = await this.policyRules(caller);
|
|
16191
16375
|
const self = await this.o.users.lookup(cred.ruid);
|
|
16192
16376
|
if (!self) return failed2("EPERM", `sudo: uid ${cred.ruid} is not in the passwd database`);
|
|
16377
|
+
const owner = {
|
|
16378
|
+
uid: cred.ruid,
|
|
16379
|
+
terminal: caller.terminal,
|
|
16380
|
+
parent: caller.parent
|
|
16381
|
+
};
|
|
16382
|
+
const applies = await this.applies(self, cred);
|
|
16383
|
+
const settings = resolveDefaults(policy.defaults, applies);
|
|
16384
|
+
if (parsed2.removeAll) {
|
|
16385
|
+
this.timestamps.remove(owner.uid);
|
|
16386
|
+
return DONE;
|
|
16387
|
+
}
|
|
16388
|
+
if (parsed2.reset && !parsed2.validate && !parsed2.list && parsed2.command.length === 0) {
|
|
16389
|
+
this.timestamps.invalidate(owner, settings.timestampType);
|
|
16390
|
+
return DONE;
|
|
16391
|
+
}
|
|
16193
16392
|
const target = await this.lookup(parsed2.user);
|
|
16194
16393
|
if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed2.user}`);
|
|
16195
16394
|
const root = cred.euid === 0;
|
|
16196
|
-
const rules = root ? [] : policy;
|
|
16197
|
-
const
|
|
16198
|
-
if (parsed2.list) return this.listed(self, root,
|
|
16395
|
+
const rules = root ? [] : policy.rules;
|
|
16396
|
+
const mine = rules.filter((rule) => applies(rule.who));
|
|
16397
|
+
if (parsed2.list) return this.listed(self, root, mine, settings.matching);
|
|
16398
|
+
const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
|
|
16399
|
+
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
16400
|
+
return this.command(session, root, mine, call);
|
|
16401
|
+
}
|
|
16402
|
+
async command(s, root, mine, call) {
|
|
16403
|
+
const { caller, self, target, parsed: parsed2 } = s;
|
|
16404
|
+
const { cred } = caller;
|
|
16199
16405
|
const [word = "", ...args] = parsed2.command;
|
|
16200
16406
|
const cwd = await this.canonical(call.cwd);
|
|
16201
16407
|
if (cwd === void 0 || !await this.reachable(cred, cwd)) {
|
|
@@ -16209,8 +16415,8 @@ var Sudo = class {
|
|
|
16209
16415
|
const untrusted2 = await this.untrusted(path, cwd, word);
|
|
16210
16416
|
if (untrusted2) return untrusted2;
|
|
16211
16417
|
const argv = [path, ...args];
|
|
16212
|
-
const matching =
|
|
16213
|
-
(rule) =>
|
|
16418
|
+
const matching = mine.filter(
|
|
16419
|
+
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv)
|
|
16214
16420
|
);
|
|
16215
16421
|
const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
|
|
16216
16422
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
@@ -16221,23 +16427,67 @@ var Sudo = class {
|
|
|
16221
16427
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
16222
16428
|
);
|
|
16223
16429
|
}
|
|
16430
|
+
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
16431
|
+
this.timestamps.stamp(s.owner, s.policy);
|
|
16432
|
+
this.logged(s, cwd, argv);
|
|
16433
|
+
return grant();
|
|
16434
|
+
}
|
|
16224
16435
|
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
const answer2 = await this.ask(caller, self, target, parsed2.command, path, cwd, signal);
|
|
16229
|
-
if (typeof answer2 !== "string") return answer2;
|
|
16436
|
+
return this.pendingFor(cred.ruid, async () => {
|
|
16437
|
+
const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd });
|
|
16438
|
+
if ("error" in answer2) return answer2;
|
|
16230
16439
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
16231
16440
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
16232
16441
|
}
|
|
16233
16442
|
const changed = await this.untrusted(path, cwd, word);
|
|
16234
16443
|
if (changed) return changed;
|
|
16235
|
-
|
|
16236
|
-
|
|
16444
|
+
const result = answer2.answer === "once" ? grant() : await this.always(self, target, cwd, argv, recordable(cwd, parsed2.command), grant);
|
|
16445
|
+
if (!("error" in result)) this.stamped(s, answer2);
|
|
16446
|
+
return result;
|
|
16447
|
+
});
|
|
16448
|
+
}
|
|
16449
|
+
async validate(s, root, mine, dir) {
|
|
16450
|
+
const { self, parsed: parsed2 } = s;
|
|
16451
|
+
if (root) return DONE;
|
|
16452
|
+
if (mine.length === 0) {
|
|
16453
|
+
return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
|
|
16454
|
+
}
|
|
16455
|
+
if (mine.every((rule) => rule.nopasswd)) return DONE;
|
|
16456
|
+
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
16457
|
+
this.timestamps.stamp(s.owner, s.policy);
|
|
16458
|
+
return DONE;
|
|
16459
|
+
}
|
|
16460
|
+
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
16461
|
+
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
16462
|
+
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
16463
|
+
const answer2 = await this.ask(s, { argv: [], path: "", cwd, validate: true });
|
|
16464
|
+
if ("error" in answer2) return answer2;
|
|
16465
|
+
if (!answer2.person) await s.caller.report?.(NOT_STAMPED);
|
|
16466
|
+
this.stamped(s, answer2);
|
|
16467
|
+
return DONE;
|
|
16468
|
+
});
|
|
16469
|
+
}
|
|
16470
|
+
async pendingFor(uid, work) {
|
|
16471
|
+
if (this.pending.has(uid)) return failed2("EBUSY", "sudo: another request is pending");
|
|
16472
|
+
this.pending.add(uid);
|
|
16473
|
+
try {
|
|
16474
|
+
return await work();
|
|
16237
16475
|
} finally {
|
|
16238
|
-
this.pending.delete(
|
|
16476
|
+
this.pending.delete(uid);
|
|
16239
16477
|
}
|
|
16240
16478
|
}
|
|
16479
|
+
stamped(s, answer2) {
|
|
16480
|
+
if (answer2.person && !s.parsed.reset) this.timestamps.stamp(s.owner, s.policy);
|
|
16481
|
+
}
|
|
16482
|
+
logged(s, cwd, argv) {
|
|
16483
|
+
const { caller, self, target, owner, policy } = s;
|
|
16484
|
+
const type = policy.type === "tty" && !owner.terminal ? "ppid" : policy.type;
|
|
16485
|
+
const tty = owner.terminal?.tty ?? caller.tty ?? "unknown";
|
|
16486
|
+
const command = argv.map(quoteWord).join(" ");
|
|
16487
|
+
(this.o.warn ?? console.warn)(
|
|
16488
|
+
`sudo: ${self.name} : TTY=${tty} ; PWD=${quoteWord(cwd)} ; USER=${target.name} ; TIMESTAMP=${type} ; COMMAND=${command}`
|
|
16489
|
+
);
|
|
16490
|
+
}
|
|
16241
16491
|
async always(self, target, cwd, argv, shown2, grant) {
|
|
16242
16492
|
if (!shown2 || !recordable(cwd, argv)) return { ...grant(), notice: RAN_ONCE };
|
|
16243
16493
|
if (!await this.remember(self, target, cwd, argv)) {
|
|
@@ -16245,7 +16495,7 @@ var Sudo = class {
|
|
|
16245
16495
|
}
|
|
16246
16496
|
return grant();
|
|
16247
16497
|
}
|
|
16248
|
-
listed(self, root, rules) {
|
|
16498
|
+
listed(self, root, rules, defaults2) {
|
|
16249
16499
|
const host = this.o.hostname;
|
|
16250
16500
|
if (root) {
|
|
16251
16501
|
return {
|
|
@@ -16258,11 +16508,20 @@ var Sudo = class {
|
|
|
16258
16508
|
return failed2("EPERM", `User ${self.name} is not allowed to run sudo on ${host}.`);
|
|
16259
16509
|
}
|
|
16260
16510
|
const lines2 = rules.map(described).join("\n");
|
|
16261
|
-
|
|
16511
|
+
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
16512
|
+
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
16513
|
+
${params}
|
|
16514
|
+
|
|
16515
|
+
`;
|
|
16516
|
+
return {
|
|
16517
|
+
list: `${matching}User ${self.name} may run the following commands on ${host}:
|
|
16262
16518
|
${lines2}
|
|
16263
|
-
`
|
|
16519
|
+
`
|
|
16520
|
+
};
|
|
16264
16521
|
}
|
|
16265
|
-
async ask(
|
|
16522
|
+
async ask(s, asked) {
|
|
16523
|
+
const { caller, self, target, signal: requester } = s;
|
|
16524
|
+
const { argv, path, cwd } = asked;
|
|
16266
16525
|
const { approve } = this.o;
|
|
16267
16526
|
if (!approve || requester.aborted) return failed2("EACCES", DENIED2);
|
|
16268
16527
|
const createdAt = Date.now();
|
|
@@ -16281,9 +16540,11 @@ ${lines2}
|
|
|
16281
16540
|
...caller.tty ? { tty: caller.tty } : {},
|
|
16282
16541
|
createdAt,
|
|
16283
16542
|
expiresAt: createdAt + timeout,
|
|
16284
|
-
always: recordable(cwd, [path, ...argv]),
|
|
16543
|
+
always: asked.validate ? false : recordable(cwd, [path, ...argv]),
|
|
16285
16544
|
stdin: caller.stdin,
|
|
16286
|
-
rootOnly: await this.rootOnly(cwd, argv.slice(1))
|
|
16545
|
+
rootOnly: asked.validate ? {} : await this.rootOnly(cwd, argv.slice(1)),
|
|
16546
|
+
...asked.validate ? { validate: true } : {},
|
|
16547
|
+
timestamp: true
|
|
16287
16548
|
};
|
|
16288
16549
|
const control = new AbortController();
|
|
16289
16550
|
const timer = setTimeout(() => control.abort("expired"), timeout);
|
|
@@ -16293,9 +16554,8 @@ ${lines2}
|
|
|
16293
16554
|
try {
|
|
16294
16555
|
const answer2 = await answerOf(approve, req, control.signal);
|
|
16295
16556
|
if (answer2 === ABORTED) return ended();
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
}
|
|
16557
|
+
const approved = approvalOf(answer2);
|
|
16558
|
+
if (approved) return control.signal.aborted ? ended() : approved;
|
|
16299
16559
|
return failed2("EACCES", denialText(answer2));
|
|
16300
16560
|
} finally {
|
|
16301
16561
|
clearTimeout(timer);
|
|
@@ -19852,7 +20112,7 @@ function replaced(text, values) {
|
|
|
19852
20112
|
for (const [ph, value2] of Object.entries(values)) out = out.split(ph).join(binary(value2));
|
|
19853
20113
|
return out;
|
|
19854
20114
|
}
|
|
19855
|
-
function
|
|
20115
|
+
function usable2(values) {
|
|
19856
20116
|
const out = {};
|
|
19857
20117
|
for (const [ph, value2] of Object.entries(values ?? {}))
|
|
19858
20118
|
if (encoder8.encode(value2).length >= MIN_VALUE) out[ph] = value2;
|
|
@@ -19867,7 +20127,7 @@ async function substituteHead(head, ask) {
|
|
|
19867
20127
|
if (!request || !found || found.length !== 1) return void 0;
|
|
19868
20128
|
const at = found[0];
|
|
19869
20129
|
if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
19870
|
-
const values =
|
|
20130
|
+
const values = usable2(
|
|
19871
20131
|
await ask(`header:${at.name}`, at.placeholders, {
|
|
19872
20132
|
method: request[1],
|
|
19873
20133
|
path: request[2]
|
|
@@ -20177,7 +20437,7 @@ async function cleartext(message3, asks) {
|
|
|
20177
20437
|
const placeholders = placeholdersIn(password);
|
|
20178
20438
|
if (placeholders.length === 0 || placeholders.length > SESSION_PLACEHOLDERS)
|
|
20179
20439
|
return { bytes: message3, done: true };
|
|
20180
|
-
const values =
|
|
20440
|
+
const values = usable2(await asks.cleartext(placeholders));
|
|
20181
20441
|
if (Object.keys(values).length === 0) return { bytes: message3, done: true };
|
|
20182
20442
|
return { bytes: frame3(112, `${replaced(password, values)}\0`), values, done: true };
|
|
20183
20443
|
}
|
|
@@ -20242,7 +20502,7 @@ var PgClient = class {
|
|
|
20242
20502
|
const x = got.exchange;
|
|
20243
20503
|
const binding = this.header === END_POINT ? this.server.binding : void 0;
|
|
20244
20504
|
const serverFinal = `c=${channelOf(this.header, binding)},r=${got.nonce}`;
|
|
20245
|
-
const values =
|
|
20505
|
+
const values = usable2(
|
|
20246
20506
|
await this.asks.scram({
|
|
20247
20507
|
clientFirstBare: x.clientFirstBare,
|
|
20248
20508
|
serverFirst: x.serverFirst,
|
|
@@ -20291,7 +20551,7 @@ function decoded(b642) {
|
|
|
20291
20551
|
async function swapped(text, position, ask) {
|
|
20292
20552
|
const placeholders = placeholdersIn(text);
|
|
20293
20553
|
if (placeholders.length === 0 || placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
20294
|
-
const values =
|
|
20554
|
+
const values = usable2(await ask(position, placeholders));
|
|
20295
20555
|
if (Object.keys(values).length === 0) return void 0;
|
|
20296
20556
|
return { text: replaced(text, values), values };
|
|
20297
20557
|
}
|
|
@@ -28938,8 +29198,8 @@ var OpfsFs = class {
|
|
|
28938
29198
|
const [entry] = await this.meta.get([target]);
|
|
28939
29199
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
|
|
28940
29200
|
const kept = fresh3 ? cached : this.files.get(target);
|
|
28941
|
-
const
|
|
28942
|
-
return [
|
|
29201
|
+
const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
|
|
29202
|
+
return [usable3 ? kept : void 0, target, true];
|
|
28943
29203
|
}
|
|
28944
29204
|
async writeAt(handle, target, path, offset, bytes) {
|
|
28945
29205
|
await this.counting(target);
|
|
@@ -31935,6 +32195,7 @@ var SUDO_SOURCE = [
|
|
|
31935
32195
|
" await say(1, answer.list);",
|
|
31936
32196
|
" return 0;",
|
|
31937
32197
|
" }",
|
|
32198
|
+
" if (answer.done === true) return 0;",
|
|
31938
32199
|
" if (answer.run !== undefined) {",
|
|
31939
32200
|
" try {",
|
|
31940
32201
|
" pid = (await ctx.spawn({ argv: answer.run, pgid: 0 })).pid;",
|
|
@@ -32570,6 +32831,7 @@ var Launcher = class {
|
|
|
32570
32831
|
);
|
|
32571
32832
|
}
|
|
32572
32833
|
this.users = new UserDb(this.fs, this.owners);
|
|
32834
|
+
const { sudoClock } = options;
|
|
32573
32835
|
this.sudoer = new Sudo({
|
|
32574
32836
|
fs: this.fs,
|
|
32575
32837
|
users: this.users,
|
|
@@ -32579,7 +32841,8 @@ var Launcher = class {
|
|
|
32579
32841
|
pnpmHome: this.pnpmHome,
|
|
32580
32842
|
backing: (path, cwd) => this.backing(path, cwd),
|
|
32581
32843
|
approve: options.approve,
|
|
32582
|
-
timeoutMs: options.sudoTimeoutMs
|
|
32844
|
+
timeoutMs: options.sudoTimeoutMs,
|
|
32845
|
+
now: sudoClock ? () => sudoClock.now() : void 0
|
|
32583
32846
|
});
|
|
32584
32847
|
this.cron = this.cronService(options.cron);
|
|
32585
32848
|
void this.unlinked.sweep("/");
|
|
@@ -33985,11 +34248,26 @@ var Launcher = class {
|
|
|
33985
34248
|
pid: this.jobs.shown(pid),
|
|
33986
34249
|
tags: this.tagsOf(pid),
|
|
33987
34250
|
tty: this.described.get(pid)?.tty,
|
|
34251
|
+
...this.timestampOwner(pid),
|
|
33988
34252
|
stdin,
|
|
33989
34253
|
report
|
|
33990
34254
|
};
|
|
33991
34255
|
return this.sudoer.request(caller, call, signal);
|
|
33992
34256
|
}
|
|
34257
|
+
timestampOwner(pid) {
|
|
34258
|
+
const sid = this.jobs.sidOf(pid);
|
|
34259
|
+
const tty = this.jobs.controllingTerminal(pid)?.name;
|
|
34260
|
+
const leader = sid === void 0 ? void 0 : this.startedOf(sid);
|
|
34261
|
+
const host = this.jobs.shownParent(pid) ?? this.jobs.shown(pid);
|
|
34262
|
+
const started = this.startedOf(host);
|
|
34263
|
+
return {
|
|
34264
|
+
...tty && sid !== void 0 && leader !== void 0 ? { terminal: { tty, sid, started: leader } } : {},
|
|
34265
|
+
...started === void 0 ? {} : { parent: { pid: host, started } }
|
|
34266
|
+
};
|
|
34267
|
+
}
|
|
34268
|
+
startedOf(shown2) {
|
|
34269
|
+
return this.running(shown2) ? this.described.get(shown2)?.started : void 0;
|
|
34270
|
+
}
|
|
33993
34271
|
visudo(pid, call) {
|
|
33994
34272
|
return this.sudoer.check(this.credOf(pid), call);
|
|
33995
34273
|
}
|
|
@@ -37676,6 +37954,7 @@ async function createNodeKernel(options = {}) {
|
|
|
37676
37954
|
...options.clipboard ? { clipboard: options.clipboard } : {},
|
|
37677
37955
|
...options.requests ? { embedder: checkedRequests(options.requests) } : {},
|
|
37678
37956
|
...options.approve ? { approve: options.approve } : {},
|
|
37957
|
+
...options.sudo?.clock ? { sudoClock: options.sudo.clock } : {},
|
|
37679
37958
|
...options.wasmMemories ? { wasmMemories: options.wasmMemories } : {},
|
|
37680
37959
|
cron: { lock: cronLock(root), ...options.cron?.clock ? { clock: options.cron.clock } : {} }
|
|
37681
37960
|
});
|