@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/kernel-worker.js
CHANGED
|
@@ -2796,8 +2796,8 @@ var OpfsFs = class {
|
|
|
2796
2796
|
const [entry] = await this.meta.get([target]);
|
|
2797
2797
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
|
|
2798
2798
|
const kept = fresh3 ? cached : this.files.get(target);
|
|
2799
|
-
const
|
|
2800
|
-
return [
|
|
2799
|
+
const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
|
|
2800
|
+
return [usable3 ? kept : void 0, target, true];
|
|
2801
2801
|
}
|
|
2802
2802
|
async writeAt(handle, target, path, offset, bytes) {
|
|
2803
2803
|
await this.counting(target);
|
|
@@ -8774,7 +8774,7 @@ function satisfies(version, range2) {
|
|
|
8774
8774
|
}
|
|
8775
8775
|
|
|
8776
8776
|
// src/version.ts
|
|
8777
|
-
var KERNEL_VERSION = true ? "1.
|
|
8777
|
+
var KERNEL_VERSION = true ? "1.177.0" : "0.0.0-dev";
|
|
8778
8778
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8779
8779
|
function procVersion() {
|
|
8780
8780
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -18801,8 +18801,8 @@ var SCRUB_IDLE_MS = 50;
|
|
|
18801
18801
|
var SCRUB_NONBLOCK_MAX = 64 * 1024;
|
|
18802
18802
|
var encoder5 = new TextEncoder();
|
|
18803
18803
|
function scrubberOf(secrets, sameLength = false) {
|
|
18804
|
-
const
|
|
18805
|
-
return
|
|
18804
|
+
const usable3 = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
|
|
18805
|
+
return usable3.length > 0 ? compile(usable3, { idleMs: SCRUB_IDLE_MS, sameLength }) : void 0;
|
|
18806
18806
|
}
|
|
18807
18807
|
async function writeAll2(file, bytes) {
|
|
18808
18808
|
const write = file.write;
|
|
@@ -19441,6 +19441,56 @@ var SealedBroker = class {
|
|
|
19441
19441
|
}
|
|
19442
19442
|
};
|
|
19443
19443
|
|
|
19444
|
+
// src/sudo-timestamp.ts
|
|
19445
|
+
var TIMESTAMPS_MAX = 64;
|
|
19446
|
+
var MINUTE_MS2 = 6e4;
|
|
19447
|
+
function timestampKey(owner, type) {
|
|
19448
|
+
if (type === "global") return "global";
|
|
19449
|
+
const { terminal, parent } = owner;
|
|
19450
|
+
if (type === "tty" && terminal) {
|
|
19451
|
+
return JSON.stringify(["tty", terminal.tty, terminal.sid, terminal.started]);
|
|
19452
|
+
}
|
|
19453
|
+
return parent ? JSON.stringify(["ppid", parent.pid, parent.started]) : void 0;
|
|
19454
|
+
}
|
|
19455
|
+
var Timestamps = class {
|
|
19456
|
+
users = /* @__PURE__ */ new Map();
|
|
19457
|
+
now;
|
|
19458
|
+
constructor(now) {
|
|
19459
|
+
this.now = now;
|
|
19460
|
+
}
|
|
19461
|
+
current(owner, policy) {
|
|
19462
|
+
if (policy.minutes === 0) return false;
|
|
19463
|
+
const key = timestampKey(owner, policy.type);
|
|
19464
|
+
const at = key === void 0 ? void 0 : this.users.get(owner.uid)?.get(key);
|
|
19465
|
+
if (at === void 0) return false;
|
|
19466
|
+
const now = this.now();
|
|
19467
|
+
return at <= now && (policy.minutes < 0 || now - at < policy.minutes * MINUTE_MS2);
|
|
19468
|
+
}
|
|
19469
|
+
stamp(owner, policy) {
|
|
19470
|
+
const key = policy.minutes === 0 ? void 0 : timestampKey(owner, policy.type);
|
|
19471
|
+
if (key === void 0) return false;
|
|
19472
|
+
const now = this.now();
|
|
19473
|
+
const records = this.users.get(owner.uid) ?? /* @__PURE__ */ new Map();
|
|
19474
|
+
this.users.set(owner.uid, records);
|
|
19475
|
+
records.delete(key);
|
|
19476
|
+
records.set(key, now);
|
|
19477
|
+
for (const [old, at] of records) {
|
|
19478
|
+
const stale = policy.minutes > 0 && (at > now || now - at >= policy.minutes * MINUTE_MS2);
|
|
19479
|
+
if (stale || records.size > TIMESTAMPS_MAX) records.delete(old);
|
|
19480
|
+
}
|
|
19481
|
+
return true;
|
|
19482
|
+
}
|
|
19483
|
+
invalidate(owner, type) {
|
|
19484
|
+
const key = timestampKey(owner, type);
|
|
19485
|
+
const records = this.users.get(owner.uid);
|
|
19486
|
+
if (key !== void 0) records?.delete(key);
|
|
19487
|
+
if (records?.size === 0) this.users.delete(owner.uid);
|
|
19488
|
+
}
|
|
19489
|
+
remove(uid) {
|
|
19490
|
+
this.users.delete(uid);
|
|
19491
|
+
}
|
|
19492
|
+
};
|
|
19493
|
+
|
|
19444
19494
|
// src/sudoers.ts
|
|
19445
19495
|
var SUDOERS = "/etc/sudoers";
|
|
19446
19496
|
var SUDOERS_D = "/etc/sudoers.d";
|
|
@@ -19460,6 +19510,8 @@ var SHOWN_MAX = 60;
|
|
|
19460
19510
|
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] Cmnd <command\u2026|*>";
|
|
19461
19511
|
var SUDOERS_SYNTAX = "<user|%group|ALL> ALL=[(<target>[,<target>\u2026])] [NOPASSWD:] <command\u2026|ALL>";
|
|
19462
19512
|
var EXPECTED = `expected '${KERNEL_SYNTAX}' or '${SUDOERS_SYNTAX}'`;
|
|
19513
|
+
var TIMESTAMP_TIMEOUT = 5;
|
|
19514
|
+
var TIMESTAMP_TYPE = "tty";
|
|
19463
19515
|
function hasControl(text) {
|
|
19464
19516
|
return CONTROL.test(text);
|
|
19465
19517
|
}
|
|
@@ -19742,9 +19794,6 @@ function directiveError(text) {
|
|
|
19742
19794
|
return /^#-?\d+\s+\S+\s*=/.test(text) ? "a #uid isn't supported: name the user, %group or ALL" : null;
|
|
19743
19795
|
}
|
|
19744
19796
|
const first = /^\S+/.exec(text)?.[0] ?? "";
|
|
19745
|
-
if (/^Defaults([:@>!]|$)/.test(first)) {
|
|
19746
|
-
return "Defaults aren't supported: the kernel's sudo has no settings, so this changes nothing";
|
|
19747
|
-
}
|
|
19748
19797
|
if (/^[A-Z][A-Za-z]*_Alias$/.test(first)) {
|
|
19749
19798
|
return "aliases aren't supported: name the users, targets and commands in each line";
|
|
19750
19799
|
}
|
|
@@ -19770,9 +19819,90 @@ function kernelLine(m, line, file) {
|
|
|
19770
19819
|
)
|
|
19771
19820
|
};
|
|
19772
19821
|
}
|
|
19822
|
+
var DEFAULTS = /^Defaults(?=[:@>!\s]|$)/;
|
|
19823
|
+
var DEFAULTS_ONLY = `Defaults aren't supported, except timestamp_timeout and timestamp_type for everyone, a user or a %group: this line changes nothing`;
|
|
19824
|
+
var PARAM = /^(!*)([A-Za-z_]+)(?:\s*([-+]?=)\s*(.*))?$/s;
|
|
19825
|
+
var MINUTES = /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$/;
|
|
19826
|
+
var TYPES = ["tty", "ppid", "global"];
|
|
19827
|
+
function defaultsValue(raw2) {
|
|
19828
|
+
const quoted = /^"(.*)"$/s.exec(raw2);
|
|
19829
|
+
return quoted ? quoted[1] : raw2;
|
|
19830
|
+
}
|
|
19831
|
+
function timeoutParam({ text, negated, op, value: value2 }) {
|
|
19832
|
+
if (negated && op === void 0) return { timestampTimeout: 0 };
|
|
19833
|
+
if (negated || op !== "=" || !MINUTES.test(value2) || !Number.isFinite(Number(value2))) {
|
|
19834
|
+
return `timestamp_timeout takes minutes, such as =5 or =2.5 (0 always asks, below 0 never expires), not ${shown(text)}`;
|
|
19835
|
+
}
|
|
19836
|
+
return { timestampTimeout: Number(value2) };
|
|
19837
|
+
}
|
|
19838
|
+
function typeParam({ text, negated, op, value: value2 }) {
|
|
19839
|
+
if (!negated && op === "=" && TYPES.includes(value2)) {
|
|
19840
|
+
return { timestampType: value2 };
|
|
19841
|
+
}
|
|
19842
|
+
if (value2 === "kernel") return "timestamp_type=kernel isn't supported: use tty, ppid or global";
|
|
19843
|
+
return `timestamp_type takes =tty, =ppid or =global, not ${shown(text)}`;
|
|
19844
|
+
}
|
|
19845
|
+
function defaultsParam(param) {
|
|
19846
|
+
const m = PARAM.exec(param);
|
|
19847
|
+
if (!m) return DEFAULTS_ONLY;
|
|
19848
|
+
const [, bangs = "", name2, op, raw2 = ""] = m;
|
|
19849
|
+
const read = { text: param, negated: bangs !== "", op, value: defaultsValue(raw2.trim()) };
|
|
19850
|
+
const settings = name2 === "timestamp_timeout" ? timeoutParam(read) : name2 === "timestamp_type" ? typeParam(read) : DEFAULTS_ONLY;
|
|
19851
|
+
if (typeof settings === "string") return settings;
|
|
19852
|
+
const value2 = settings.timestampTimeout ?? settings.timestampType;
|
|
19853
|
+
return { settings, text: `${name2}=${value2}` };
|
|
19854
|
+
}
|
|
19855
|
+
function defaultsWho(binding) {
|
|
19856
|
+
if (binding === "") return null;
|
|
19857
|
+
if (!binding.startsWith(":")) return DEFAULTS_ONLY;
|
|
19858
|
+
const who = binding.slice(1).split(",");
|
|
19859
|
+
for (const name2 of who) {
|
|
19860
|
+
if (!validWho(name2))
|
|
19861
|
+
return `expected users, %groups or ALL after Defaults:, not ${shown(name2)}`;
|
|
19862
|
+
}
|
|
19863
|
+
return who;
|
|
19864
|
+
}
|
|
19865
|
+
function defaultsLine(text) {
|
|
19866
|
+
const m = /^Defaults(\S*)(?:\s+(.*))?$/s.exec(text);
|
|
19867
|
+
const who = defaultsWho(m?.[1] ?? "");
|
|
19868
|
+
if (typeof who === "string") return { error: who };
|
|
19869
|
+
const list = (m?.[2] ?? "").trim();
|
|
19870
|
+
if (list === "")
|
|
19871
|
+
return {
|
|
19872
|
+
error: "expected timestamp_timeout=<minutes> or timestamp_type=<type> after Defaults"
|
|
19873
|
+
};
|
|
19874
|
+
const settings = {};
|
|
19875
|
+
const params = [];
|
|
19876
|
+
for (const param of list.split(",")) {
|
|
19877
|
+
const read = defaultsParam(param.trim());
|
|
19878
|
+
if (typeof read === "string") return { error: read };
|
|
19879
|
+
Object.assign(settings, read.settings);
|
|
19880
|
+
params.push(read.text);
|
|
19881
|
+
}
|
|
19882
|
+
return { defaults: { who, settings, params } };
|
|
19883
|
+
}
|
|
19884
|
+
function resolveDefaults(entries, applies) {
|
|
19885
|
+
const everyone = entries.filter((e) => e.who === null);
|
|
19886
|
+
const mine = entries.filter((e) => e.who?.some(applies));
|
|
19887
|
+
const matching = [...everyone, ...mine];
|
|
19888
|
+
const settings = Object.assign({}, ...matching.map((e) => e.settings));
|
|
19889
|
+
return {
|
|
19890
|
+
timestampTimeout: settings.timestampTimeout ?? TIMESTAMP_TIMEOUT,
|
|
19891
|
+
timestampType: settings.timestampType ?? TIMESTAMP_TYPE,
|
|
19892
|
+
matching
|
|
19893
|
+
};
|
|
19894
|
+
}
|
|
19895
|
+
function defaultsText(entries) {
|
|
19896
|
+
const all = resolveDefaults(entries, () => false);
|
|
19897
|
+
return [
|
|
19898
|
+
`Defaults timestamp_timeout=${all.timestampTimeout}, timestamp_type=${all.timestampType}`,
|
|
19899
|
+
...entries.filter((e) => e.who !== null).map((e) => `Defaults:${e.who.join(",")} ${e.params.join(", ")}`)
|
|
19900
|
+
];
|
|
19901
|
+
}
|
|
19773
19902
|
function parseLine(line, file) {
|
|
19774
19903
|
const text = line.trim();
|
|
19775
19904
|
if (text === "") return null;
|
|
19905
|
+
if (DEFAULTS.test(text)) return defaultsLine(text);
|
|
19776
19906
|
const directive = directiveError(text);
|
|
19777
19907
|
if (directive !== void 0) return directive === null ? null : { error: directive };
|
|
19778
19908
|
const kernel = KERNEL_FORM.exec(text);
|
|
@@ -19784,14 +19914,16 @@ function parseLine(line, file) {
|
|
|
19784
19914
|
}
|
|
19785
19915
|
function parsePolicy(text, file) {
|
|
19786
19916
|
const rules = [];
|
|
19917
|
+
const defaults2 = [];
|
|
19787
19918
|
const errors = [];
|
|
19788
19919
|
for (const [n, line] of text.split("\n").entries()) {
|
|
19789
19920
|
const parsed2 = parseLine(line, file);
|
|
19790
19921
|
if (parsed2 === null) continue;
|
|
19791
19922
|
if ("error" in parsed2) errors.push({ line: n + 1, message: parsed2.error });
|
|
19923
|
+
else if ("defaults" in parsed2) defaults2.push(parsed2.defaults);
|
|
19792
19924
|
else rules.push(...parsed2.rules);
|
|
19793
19925
|
}
|
|
19794
|
-
return { rules, errors };
|
|
19926
|
+
return { rules, defaults: defaults2, errors };
|
|
19795
19927
|
}
|
|
19796
19928
|
|
|
19797
19929
|
// src/sudo.ts
|
|
@@ -19799,7 +19931,14 @@ var SECURE_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
|
19799
19931
|
var DEFAULT_SUDOERS = "%sudo Cmnd *\n";
|
|
19800
19932
|
var SUDO_TIMEOUT_MS = 3e5;
|
|
19801
19933
|
var TERM = /^[A-Za-z0-9._+-]+$/;
|
|
19802
|
-
var USAGE =
|
|
19934
|
+
var USAGE = [
|
|
19935
|
+
"usage: sudo -K | -k",
|
|
19936
|
+
" sudo -v [-kn] [-u user]",
|
|
19937
|
+
" sudo -l [-kn]",
|
|
19938
|
+
" sudo [-kn] [-u user] [--] command [args...]"
|
|
19939
|
+
].join("\n");
|
|
19940
|
+
var DONE = { done: true };
|
|
19941
|
+
var NOT_STAMPED = "sudo: approved, but the approver didn't say a person decided, so no timestamp was recorded";
|
|
19803
19942
|
var DENIED2 = "sudo: approval denied";
|
|
19804
19943
|
var DENIAL_MAX = 500;
|
|
19805
19944
|
var ABORTED = /* @__PURE__ */ Symbol("aborted");
|
|
@@ -19873,6 +20012,17 @@ async function answerOf(approve, req, signal) {
|
|
|
19873
20012
|
return cut === void 0 ? "deny" : await cut;
|
|
19874
20013
|
}
|
|
19875
20014
|
}
|
|
20015
|
+
function approvalOf(answer2) {
|
|
20016
|
+
if (answer2 === "once" || answer2 === "always") return { answer: answer2, person: false };
|
|
20017
|
+
if (typeof answer2 !== "object" || answer2 === null) return void 0;
|
|
20018
|
+
try {
|
|
20019
|
+
const { answer: kind, timestamp } = answer2;
|
|
20020
|
+
if (kind !== "once" && kind !== "always" || timestamp !== true) return void 0;
|
|
20021
|
+
return { answer: kind, person: true };
|
|
20022
|
+
} catch {
|
|
20023
|
+
return void 0;
|
|
20024
|
+
}
|
|
20025
|
+
}
|
|
19876
20026
|
function recordedCommand(argv) {
|
|
19877
20027
|
const [path = "", ...args] = argv;
|
|
19878
20028
|
return commandText({ path, args: args.map((a) => [a]) });
|
|
@@ -19882,6 +20032,9 @@ function flags(parsed2, arg, next3) {
|
|
|
19882
20032
|
const flag = arg[j];
|
|
19883
20033
|
if (flag === "n") parsed2.nonInteractive = true;
|
|
19884
20034
|
else if (flag === "l") parsed2.list = true;
|
|
20035
|
+
else if (flag === "v") parsed2.validate = true;
|
|
20036
|
+
else if (flag === "k") parsed2.reset = true;
|
|
20037
|
+
else if (flag === "K") parsed2.removeAll = true;
|
|
19885
20038
|
else if (flag !== "u") return `sudo: invalid option -- '${flag}'
|
|
19886
20039
|
${USAGE}`;
|
|
19887
20040
|
else {
|
|
@@ -19895,7 +20048,15 @@ ${USAGE}`;
|
|
|
19895
20048
|
return 0;
|
|
19896
20049
|
}
|
|
19897
20050
|
function parseArgs(args) {
|
|
19898
|
-
const parsed2 = {
|
|
20051
|
+
const parsed2 = {
|
|
20052
|
+
list: false,
|
|
20053
|
+
nonInteractive: false,
|
|
20054
|
+
validate: false,
|
|
20055
|
+
reset: false,
|
|
20056
|
+
removeAll: false,
|
|
20057
|
+
user: "root",
|
|
20058
|
+
command: []
|
|
20059
|
+
};
|
|
19899
20060
|
let i = 0;
|
|
19900
20061
|
for (; i < args.length; i++) {
|
|
19901
20062
|
const arg = args[i];
|
|
@@ -19909,8 +20070,17 @@ function parseArgs(args) {
|
|
|
19909
20070
|
i += used2;
|
|
19910
20071
|
}
|
|
19911
20072
|
parsed2.command = args.slice(i);
|
|
19912
|
-
|
|
19913
|
-
|
|
20073
|
+
return usable(parsed2) ? parsed2 : USAGE;
|
|
20074
|
+
}
|
|
20075
|
+
function usable(parsed2) {
|
|
20076
|
+
const command = parsed2.command.length > 0;
|
|
20077
|
+
const modes = [parsed2.list, parsed2.validate, parsed2.removeAll].filter(Boolean).length;
|
|
20078
|
+
if (modes > 1 || parsed2.removeAll && parsed2.reset) return false;
|
|
20079
|
+
if (modes === 1) return !command;
|
|
20080
|
+
return command || parsed2.reset;
|
|
20081
|
+
}
|
|
20082
|
+
function policyOf(settings) {
|
|
20083
|
+
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
19914
20084
|
}
|
|
19915
20085
|
function described(rule) {
|
|
19916
20086
|
const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
|
|
@@ -19922,9 +20092,11 @@ var Sudo = class {
|
|
|
19922
20092
|
o;
|
|
19923
20093
|
warned = /* @__PURE__ */ new Set();
|
|
19924
20094
|
pending = /* @__PURE__ */ new Set();
|
|
20095
|
+
timestamps;
|
|
19925
20096
|
next = 0;
|
|
19926
20097
|
constructor(options) {
|
|
19927
20098
|
this.o = options;
|
|
20099
|
+
this.timestamps = new Timestamps(options.now ?? (() => performance.now()));
|
|
19928
20100
|
}
|
|
19929
20101
|
warn(message3) {
|
|
19930
20102
|
if (this.warned.has(message3)) return;
|
|
@@ -20011,8 +20183,8 @@ var Sudo = class {
|
|
|
20011
20183
|
const code = err.code;
|
|
20012
20184
|
return skipped(`it can't be read (${typeof code === "string" ? code : "error"})`);
|
|
20013
20185
|
}
|
|
20014
|
-
const { rules, errors } = parsePolicy(text, path);
|
|
20015
|
-
return { path, rules, problems: errors, ok: true };
|
|
20186
|
+
const { rules, defaults: defaults2, errors } = parsePolicy(text, path);
|
|
20187
|
+
return { path, rules, defaults: defaults2, problems: errors, ok: true };
|
|
20016
20188
|
}
|
|
20017
20189
|
async policy() {
|
|
20018
20190
|
const files = [await this.policyFile(SUDOERS)];
|
|
@@ -20041,7 +20213,10 @@ var Sudo = class {
|
|
|
20041
20213
|
}
|
|
20042
20214
|
}
|
|
20043
20215
|
if (!detailed && skipped > 0) await caller.report?.(skippedNotice(skipped));
|
|
20044
|
-
return
|
|
20216
|
+
return {
|
|
20217
|
+
rules: files.flatMap((f) => f.rules),
|
|
20218
|
+
defaults: files.flatMap((f) => f.defaults ?? [])
|
|
20219
|
+
};
|
|
20045
20220
|
}
|
|
20046
20221
|
async check(cred, call) {
|
|
20047
20222
|
const lines2 = [];
|
|
@@ -20054,17 +20229,26 @@ var Sudo = class {
|
|
|
20054
20229
|
if (read && problems.length === 0) lines2.push([1, `${path}: parsed OK`]);
|
|
20055
20230
|
if (problems.length > 0) ok4 = false;
|
|
20056
20231
|
};
|
|
20232
|
+
const values = (defaults2) => {
|
|
20233
|
+
for (const line of defaultsText(defaults2)) lines2.push([1, line]);
|
|
20234
|
+
};
|
|
20057
20235
|
if (call.files !== void 0) {
|
|
20236
|
+
const defaults2 = [];
|
|
20058
20237
|
for (const { name: name2, text } of call.files) {
|
|
20059
|
-
|
|
20238
|
+
const parsed2 = parsePolicy(text);
|
|
20239
|
+
defaults2.push(...parsed2.defaults);
|
|
20240
|
+
report({ path: name2, rules: [], problems: parsed2.errors, ok: true });
|
|
20060
20241
|
}
|
|
20242
|
+
if (defaults2.length > 0) values(defaults2);
|
|
20061
20243
|
return { lines: lines2, ok: ok4 };
|
|
20062
20244
|
}
|
|
20063
20245
|
if (this.o.mode === "enforce" && cred.euid !== 0) {
|
|
20064
20246
|
const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
|
|
20065
20247
|
return { lines: [[2, `visudo: ${SUDOERS}: Permission denied: ${hint}`]], ok: false };
|
|
20066
20248
|
}
|
|
20067
|
-
|
|
20249
|
+
const files = await this.policy();
|
|
20250
|
+
for (const file of files) report(file);
|
|
20251
|
+
values(files.flatMap((f) => f.defaults ?? []));
|
|
20068
20252
|
return { lines: lines2, ok: ok4 };
|
|
20069
20253
|
}
|
|
20070
20254
|
async groupIds() {
|
|
@@ -20073,9 +20257,9 @@ var Sudo = class {
|
|
|
20073
20257
|
async applies(caller, cred) {
|
|
20074
20258
|
const gids = await this.groupIds();
|
|
20075
20259
|
const mine = /* @__PURE__ */ new Set([cred.rgid, cred.egid, ...cred.groups]);
|
|
20076
|
-
return (
|
|
20077
|
-
if (
|
|
20078
|
-
const gid =
|
|
20260
|
+
return (who) => {
|
|
20261
|
+
if (who === "ALL" || who === caller.name) return true;
|
|
20262
|
+
const gid = who.startsWith("%") ? gids.get(who.slice(1)) : void 0;
|
|
20079
20263
|
return gid !== void 0 && mine.has(gid);
|
|
20080
20264
|
};
|
|
20081
20265
|
}
|
|
@@ -20120,18 +20304,40 @@ var Sudo = class {
|
|
|
20120
20304
|
const mode = this.o.mode;
|
|
20121
20305
|
if (parsed2.list) return { list: `sudo: users are '${mode}': commands run as the caller
|
|
20122
20306
|
` };
|
|
20123
|
-
return { run: parsed2.command };
|
|
20307
|
+
return parsed2.command.length > 0 ? { run: parsed2.command } : DONE;
|
|
20124
20308
|
}
|
|
20125
20309
|
const { cred } = caller;
|
|
20126
20310
|
const policy = await this.policyRules(caller);
|
|
20127
20311
|
const self = await this.o.users.lookup(cred.ruid);
|
|
20128
20312
|
if (!self) return failed2("EPERM", `sudo: uid ${cred.ruid} is not in the passwd database`);
|
|
20313
|
+
const owner = {
|
|
20314
|
+
uid: cred.ruid,
|
|
20315
|
+
terminal: caller.terminal,
|
|
20316
|
+
parent: caller.parent
|
|
20317
|
+
};
|
|
20318
|
+
const applies = await this.applies(self, cred);
|
|
20319
|
+
const settings = resolveDefaults(policy.defaults, applies);
|
|
20320
|
+
if (parsed2.removeAll) {
|
|
20321
|
+
this.timestamps.remove(owner.uid);
|
|
20322
|
+
return DONE;
|
|
20323
|
+
}
|
|
20324
|
+
if (parsed2.reset && !parsed2.validate && !parsed2.list && parsed2.command.length === 0) {
|
|
20325
|
+
this.timestamps.invalidate(owner, settings.timestampType);
|
|
20326
|
+
return DONE;
|
|
20327
|
+
}
|
|
20129
20328
|
const target = await this.lookup(parsed2.user);
|
|
20130
20329
|
if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed2.user}`);
|
|
20131
20330
|
const root = cred.euid === 0;
|
|
20132
|
-
const rules = root ? [] : policy;
|
|
20133
|
-
const
|
|
20134
|
-
if (parsed2.list) return this.listed(self, root,
|
|
20331
|
+
const rules = root ? [] : policy.rules;
|
|
20332
|
+
const mine = rules.filter((rule) => applies(rule.who));
|
|
20333
|
+
if (parsed2.list) return this.listed(self, root, mine, settings.matching);
|
|
20334
|
+
const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
|
|
20335
|
+
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
20336
|
+
return this.command(session, root, mine, call);
|
|
20337
|
+
}
|
|
20338
|
+
async command(s, root, mine, call) {
|
|
20339
|
+
const { caller, self, target, parsed: parsed2 } = s;
|
|
20340
|
+
const { cred } = caller;
|
|
20135
20341
|
const [word = "", ...args] = parsed2.command;
|
|
20136
20342
|
const cwd = await this.canonical(call.cwd);
|
|
20137
20343
|
if (cwd === void 0 || !await this.reachable(cred, cwd)) {
|
|
@@ -20145,8 +20351,8 @@ var Sudo = class {
|
|
|
20145
20351
|
const untrusted2 = await this.untrusted(path, cwd, word);
|
|
20146
20352
|
if (untrusted2) return untrusted2;
|
|
20147
20353
|
const argv = [path, ...args];
|
|
20148
|
-
const matching =
|
|
20149
|
-
(rule) =>
|
|
20354
|
+
const matching = mine.filter(
|
|
20355
|
+
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv)
|
|
20150
20356
|
);
|
|
20151
20357
|
const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
|
|
20152
20358
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
@@ -20157,23 +20363,67 @@ var Sudo = class {
|
|
|
20157
20363
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
20158
20364
|
);
|
|
20159
20365
|
}
|
|
20366
|
+
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
20367
|
+
this.timestamps.stamp(s.owner, s.policy);
|
|
20368
|
+
this.logged(s, cwd, argv);
|
|
20369
|
+
return grant();
|
|
20370
|
+
}
|
|
20160
20371
|
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
const answer2 = await this.ask(caller, self, target, parsed2.command, path, cwd, signal);
|
|
20165
|
-
if (typeof answer2 !== "string") return answer2;
|
|
20372
|
+
return this.pendingFor(cred.ruid, async () => {
|
|
20373
|
+
const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd });
|
|
20374
|
+
if ("error" in answer2) return answer2;
|
|
20166
20375
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
20167
20376
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
20168
20377
|
}
|
|
20169
20378
|
const changed = await this.untrusted(path, cwd, word);
|
|
20170
20379
|
if (changed) return changed;
|
|
20171
|
-
|
|
20172
|
-
|
|
20380
|
+
const result = answer2.answer === "once" ? grant() : await this.always(self, target, cwd, argv, recordable(cwd, parsed2.command), grant);
|
|
20381
|
+
if (!("error" in result)) this.stamped(s, answer2);
|
|
20382
|
+
return result;
|
|
20383
|
+
});
|
|
20384
|
+
}
|
|
20385
|
+
async validate(s, root, mine, dir) {
|
|
20386
|
+
const { self, parsed: parsed2 } = s;
|
|
20387
|
+
if (root) return DONE;
|
|
20388
|
+
if (mine.length === 0) {
|
|
20389
|
+
return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
|
|
20390
|
+
}
|
|
20391
|
+
if (mine.every((rule) => rule.nopasswd)) return DONE;
|
|
20392
|
+
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
20393
|
+
this.timestamps.stamp(s.owner, s.policy);
|
|
20394
|
+
return DONE;
|
|
20395
|
+
}
|
|
20396
|
+
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20397
|
+
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
20398
|
+
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
20399
|
+
const answer2 = await this.ask(s, { argv: [], path: "", cwd, validate: true });
|
|
20400
|
+
if ("error" in answer2) return answer2;
|
|
20401
|
+
if (!answer2.person) await s.caller.report?.(NOT_STAMPED);
|
|
20402
|
+
this.stamped(s, answer2);
|
|
20403
|
+
return DONE;
|
|
20404
|
+
});
|
|
20405
|
+
}
|
|
20406
|
+
async pendingFor(uid, work) {
|
|
20407
|
+
if (this.pending.has(uid)) return failed2("EBUSY", "sudo: another request is pending");
|
|
20408
|
+
this.pending.add(uid);
|
|
20409
|
+
try {
|
|
20410
|
+
return await work();
|
|
20173
20411
|
} finally {
|
|
20174
|
-
this.pending.delete(
|
|
20412
|
+
this.pending.delete(uid);
|
|
20175
20413
|
}
|
|
20176
20414
|
}
|
|
20415
|
+
stamped(s, answer2) {
|
|
20416
|
+
if (answer2.person && !s.parsed.reset) this.timestamps.stamp(s.owner, s.policy);
|
|
20417
|
+
}
|
|
20418
|
+
logged(s, cwd, argv) {
|
|
20419
|
+
const { caller, self, target, owner, policy } = s;
|
|
20420
|
+
const type = policy.type === "tty" && !owner.terminal ? "ppid" : policy.type;
|
|
20421
|
+
const tty = owner.terminal?.tty ?? caller.tty ?? "unknown";
|
|
20422
|
+
const command = argv.map(quoteWord).join(" ");
|
|
20423
|
+
(this.o.warn ?? console.warn)(
|
|
20424
|
+
`sudo: ${self.name} : TTY=${tty} ; PWD=${quoteWord(cwd)} ; USER=${target.name} ; TIMESTAMP=${type} ; COMMAND=${command}`
|
|
20425
|
+
);
|
|
20426
|
+
}
|
|
20177
20427
|
async always(self, target, cwd, argv, shown2, grant) {
|
|
20178
20428
|
if (!shown2 || !recordable(cwd, argv)) return { ...grant(), notice: RAN_ONCE };
|
|
20179
20429
|
if (!await this.remember(self, target, cwd, argv)) {
|
|
@@ -20181,7 +20431,7 @@ var Sudo = class {
|
|
|
20181
20431
|
}
|
|
20182
20432
|
return grant();
|
|
20183
20433
|
}
|
|
20184
|
-
listed(self, root, rules) {
|
|
20434
|
+
listed(self, root, rules, defaults2) {
|
|
20185
20435
|
const host = this.o.hostname;
|
|
20186
20436
|
if (root) {
|
|
20187
20437
|
return {
|
|
@@ -20194,11 +20444,20 @@ var Sudo = class {
|
|
|
20194
20444
|
return failed2("EPERM", `User ${self.name} is not allowed to run sudo on ${host}.`);
|
|
20195
20445
|
}
|
|
20196
20446
|
const lines2 = rules.map(described).join("\n");
|
|
20197
|
-
|
|
20447
|
+
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
20448
|
+
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
20449
|
+
${params}
|
|
20450
|
+
|
|
20451
|
+
`;
|
|
20452
|
+
return {
|
|
20453
|
+
list: `${matching}User ${self.name} may run the following commands on ${host}:
|
|
20198
20454
|
${lines2}
|
|
20199
|
-
`
|
|
20455
|
+
`
|
|
20456
|
+
};
|
|
20200
20457
|
}
|
|
20201
|
-
async ask(
|
|
20458
|
+
async ask(s, asked) {
|
|
20459
|
+
const { caller, self, target, signal: requester } = s;
|
|
20460
|
+
const { argv, path, cwd } = asked;
|
|
20202
20461
|
const { approve } = this.o;
|
|
20203
20462
|
if (!approve || requester.aborted) return failed2("EACCES", DENIED2);
|
|
20204
20463
|
const createdAt = Date.now();
|
|
@@ -20217,9 +20476,11 @@ ${lines2}
|
|
|
20217
20476
|
...caller.tty ? { tty: caller.tty } : {},
|
|
20218
20477
|
createdAt,
|
|
20219
20478
|
expiresAt: createdAt + timeout,
|
|
20220
|
-
always: recordable(cwd, [path, ...argv]),
|
|
20479
|
+
always: asked.validate ? false : recordable(cwd, [path, ...argv]),
|
|
20221
20480
|
stdin: caller.stdin,
|
|
20222
|
-
rootOnly: await this.rootOnly(cwd, argv.slice(1))
|
|
20481
|
+
rootOnly: asked.validate ? {} : await this.rootOnly(cwd, argv.slice(1)),
|
|
20482
|
+
...asked.validate ? { validate: true } : {},
|
|
20483
|
+
timestamp: true
|
|
20223
20484
|
};
|
|
20224
20485
|
const control = new AbortController();
|
|
20225
20486
|
const timer = setTimeout(() => control.abort("expired"), timeout);
|
|
@@ -20229,9 +20490,8 @@ ${lines2}
|
|
|
20229
20490
|
try {
|
|
20230
20491
|
const answer2 = await answerOf(approve, req, control.signal);
|
|
20231
20492
|
if (answer2 === ABORTED) return ended();
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
}
|
|
20493
|
+
const approved = approvalOf(answer2);
|
|
20494
|
+
if (approved) return control.signal.aborted ? ended() : approved;
|
|
20235
20495
|
return failed2("EACCES", denialText(answer2));
|
|
20236
20496
|
} finally {
|
|
20237
20497
|
clearTimeout(timer);
|
|
@@ -23948,7 +24208,7 @@ function replaced(text, values) {
|
|
|
23948
24208
|
for (const [ph, value2] of Object.entries(values)) out = out.split(ph).join(binary(value2));
|
|
23949
24209
|
return out;
|
|
23950
24210
|
}
|
|
23951
|
-
function
|
|
24211
|
+
function usable2(values) {
|
|
23952
24212
|
const out = {};
|
|
23953
24213
|
for (const [ph, value2] of Object.entries(values ?? {}))
|
|
23954
24214
|
if (encoder8.encode(value2).length >= MIN_VALUE) out[ph] = value2;
|
|
@@ -23963,7 +24223,7 @@ async function substituteHead(head, ask) {
|
|
|
23963
24223
|
if (!request3 || !found || found.length !== 1) return void 0;
|
|
23964
24224
|
const at = found[0];
|
|
23965
24225
|
if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
23966
|
-
const values =
|
|
24226
|
+
const values = usable2(
|
|
23967
24227
|
await ask(`header:${at.name}`, at.placeholders, {
|
|
23968
24228
|
method: request3[1],
|
|
23969
24229
|
path: request3[2]
|
|
@@ -24273,7 +24533,7 @@ async function cleartext(message3, asks) {
|
|
|
24273
24533
|
const placeholders = placeholdersIn(password);
|
|
24274
24534
|
if (placeholders.length === 0 || placeholders.length > SESSION_PLACEHOLDERS)
|
|
24275
24535
|
return { bytes: message3, done: true };
|
|
24276
|
-
const values =
|
|
24536
|
+
const values = usable2(await asks.cleartext(placeholders));
|
|
24277
24537
|
if (Object.keys(values).length === 0) return { bytes: message3, done: true };
|
|
24278
24538
|
return { bytes: frame3(112, `${replaced(password, values)}\0`), values, done: true };
|
|
24279
24539
|
}
|
|
@@ -24338,7 +24598,7 @@ var PgClient = class {
|
|
|
24338
24598
|
const x = got.exchange;
|
|
24339
24599
|
const binding = this.header === END_POINT ? this.server.binding : void 0;
|
|
24340
24600
|
const serverFinal = `c=${channelOf(this.header, binding)},r=${got.nonce}`;
|
|
24341
|
-
const values =
|
|
24601
|
+
const values = usable2(
|
|
24342
24602
|
await this.asks.scram({
|
|
24343
24603
|
clientFirstBare: x.clientFirstBare,
|
|
24344
24604
|
serverFirst: x.serverFirst,
|
|
@@ -24387,7 +24647,7 @@ function decoded(b642) {
|
|
|
24387
24647
|
async function swapped2(text, position, ask) {
|
|
24388
24648
|
const placeholders = placeholdersIn(text);
|
|
24389
24649
|
if (placeholders.length === 0 || placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
24390
|
-
const values =
|
|
24650
|
+
const values = usable2(await ask(position, placeholders));
|
|
24391
24651
|
if (Object.keys(values).length === 0) return void 0;
|
|
24392
24652
|
return { text: replaced(text, values), values };
|
|
24393
24653
|
}
|
|
@@ -33034,6 +33294,7 @@ var SUDO_SOURCE = [
|
|
|
33034
33294
|
" await say(1, answer.list);",
|
|
33035
33295
|
" return 0;",
|
|
33036
33296
|
" }",
|
|
33297
|
+
" if (answer.done === true) return 0;",
|
|
33037
33298
|
" if (answer.run !== undefined) {",
|
|
33038
33299
|
" try {",
|
|
33039
33300
|
" pid = (await ctx.spawn({ argv: answer.run, pgid: 0 })).pid;",
|
|
@@ -33673,6 +33934,7 @@ var Launcher = class {
|
|
|
33673
33934
|
);
|
|
33674
33935
|
}
|
|
33675
33936
|
this.users = new UserDb(this.fs, this.owners);
|
|
33937
|
+
const { sudoClock } = options;
|
|
33676
33938
|
this.sudoer = new Sudo({
|
|
33677
33939
|
fs: this.fs,
|
|
33678
33940
|
users: this.users,
|
|
@@ -33682,7 +33944,8 @@ var Launcher = class {
|
|
|
33682
33944
|
pnpmHome: this.pnpmHome,
|
|
33683
33945
|
backing: (path, cwd) => this.backing(path, cwd),
|
|
33684
33946
|
approve: options.approve,
|
|
33685
|
-
timeoutMs: options.sudoTimeoutMs
|
|
33947
|
+
timeoutMs: options.sudoTimeoutMs,
|
|
33948
|
+
now: sudoClock ? () => sudoClock.now() : void 0
|
|
33686
33949
|
});
|
|
33687
33950
|
this.cron = this.cronService(options.cron);
|
|
33688
33951
|
void this.unlinked.sweep("/");
|
|
@@ -35088,11 +35351,26 @@ var Launcher = class {
|
|
|
35088
35351
|
pid: this.jobs.shown(pid),
|
|
35089
35352
|
tags: this.tagsOf(pid),
|
|
35090
35353
|
tty: this.described.get(pid)?.tty,
|
|
35354
|
+
...this.timestampOwner(pid),
|
|
35091
35355
|
stdin,
|
|
35092
35356
|
report
|
|
35093
35357
|
};
|
|
35094
35358
|
return this.sudoer.request(caller, call, signal);
|
|
35095
35359
|
}
|
|
35360
|
+
timestampOwner(pid) {
|
|
35361
|
+
const sid = this.jobs.sidOf(pid);
|
|
35362
|
+
const tty = this.jobs.controllingTerminal(pid)?.name;
|
|
35363
|
+
const leader = sid === void 0 ? void 0 : this.startedOf(sid);
|
|
35364
|
+
const host = this.jobs.shownParent(pid) ?? this.jobs.shown(pid);
|
|
35365
|
+
const started = this.startedOf(host);
|
|
35366
|
+
return {
|
|
35367
|
+
...tty && sid !== void 0 && leader !== void 0 ? { terminal: { tty, sid, started: leader } } : {},
|
|
35368
|
+
...started === void 0 ? {} : { parent: { pid: host, started } }
|
|
35369
|
+
};
|
|
35370
|
+
}
|
|
35371
|
+
startedOf(shown2) {
|
|
35372
|
+
return this.running(shown2) ? this.described.get(shown2)?.started : void 0;
|
|
35373
|
+
}
|
|
35096
35374
|
visudo(pid, call) {
|
|
35097
35375
|
return this.sudoer.check(this.credOf(pid), call);
|
|
35098
35376
|
}
|