@ai-ecoverse/slicc-kernel 1.176.2 → 1.178.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 +48 -9
- package/dist/index.js +8 -1
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +519 -121
- 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 +515 -116
- 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 +2 -1
- package/dist/types/kernel/sealed-oauth.d.ts +74 -0
- package/dist/types/kernel/sealed.d.ts +1 -0
- package/dist/types/launcher.d.ts +5 -0
- package/dist/types/node.d.ts +7 -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 +3 -3
package/dist/node.js
CHANGED
|
@@ -969,9 +969,9 @@ function changeCred(cur, change) {
|
|
|
969
969
|
}
|
|
970
970
|
const privileged = cur.euid === 0;
|
|
971
971
|
const next3 = copyCred(cur);
|
|
972
|
-
for (const
|
|
973
|
-
const own =
|
|
974
|
-
for (const key of
|
|
972
|
+
for (const keys2 of [UIDS, GIDS]) {
|
|
973
|
+
const own = keys2.map((key) => cur[key]);
|
|
974
|
+
for (const key of keys2) {
|
|
975
975
|
const value2 = id(change[key]);
|
|
976
976
|
if (value2 === void 0) continue;
|
|
977
977
|
if (!privileged && !own.includes(value2)) throw fsError("EPERM", `${key} ${value2}`);
|
|
@@ -3617,12 +3617,12 @@ function next(p) {
|
|
|
3617
3617
|
var ANY = [[">=", [0, 0, 0, []]]];
|
|
3618
3618
|
var NOTHING = [["<", [0, 0, 0, ["0"]]]];
|
|
3619
3619
|
function plain(op, p) {
|
|
3620
|
-
const
|
|
3620
|
+
const exact2 = !p.parts.includes(void 0);
|
|
3621
3621
|
const low = floor(p);
|
|
3622
3622
|
const high = next(p);
|
|
3623
3623
|
if (op === ">=") return [[">=", low]];
|
|
3624
|
-
if (op === "<") return [["<",
|
|
3625
|
-
if (
|
|
3624
|
+
if (op === "<") return [["<", exact2 ? low : [low[0], low[1], low[2], ["0"]]]];
|
|
3625
|
+
if (exact2) return [[op === "" ? "=" : op, low]];
|
|
3626
3626
|
if (op === ">") return high ? [[">=", high]] : NOTHING;
|
|
3627
3627
|
if (op === "<=") return high ? [["<", high]] : ANY;
|
|
3628
3628
|
return high ? [
|
|
@@ -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.178.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
|
|
@@ -4299,7 +4299,7 @@ function intlZone(name2) {
|
|
|
4299
4299
|
minute: "numeric",
|
|
4300
4300
|
second: "numeric"
|
|
4301
4301
|
});
|
|
4302
|
-
const
|
|
4302
|
+
const exact2 = (at) => {
|
|
4303
4303
|
const parts2 = {};
|
|
4304
4304
|
for (const part of format.formatToParts(at)) parts2[part.type] = Number(part.value);
|
|
4305
4305
|
const wall = Date.UTC(
|
|
@@ -4319,12 +4319,12 @@ function intlZone(name2) {
|
|
|
4319
4319
|
const bucket = Math.floor(at / BUCKET_MS);
|
|
4320
4320
|
let known = buckets.get(bucket);
|
|
4321
4321
|
if (known === void 0) {
|
|
4322
|
-
const start2 =
|
|
4323
|
-
known = start2 ===
|
|
4322
|
+
const start2 = exact2(bucket * BUCKET_MS);
|
|
4323
|
+
known = start2 === exact2(bucket * BUCKET_MS + BUCKET_MS - MINUTE_MS) ? start2 : null;
|
|
4324
4324
|
if (buckets.size >= CACHE_MAX) buckets.clear();
|
|
4325
4325
|
buckets.set(bucket, known);
|
|
4326
4326
|
}
|
|
4327
|
-
return known ??
|
|
4327
|
+
return known ?? exact2(at);
|
|
4328
4328
|
}
|
|
4329
4329
|
};
|
|
4330
4330
|
}
|
|
@@ -5567,11 +5567,11 @@ var CronService = class {
|
|
|
5567
5567
|
const now = this.clock.now();
|
|
5568
5568
|
const system = this.host.timeZone;
|
|
5569
5569
|
const due2 = [];
|
|
5570
|
-
const
|
|
5570
|
+
const keys2 = /* @__PURE__ */ new Set();
|
|
5571
5571
|
let next3 = now + WAKE_MS;
|
|
5572
5572
|
for (const entry of loaded) {
|
|
5573
5573
|
const key = keyOf2(entry);
|
|
5574
|
-
|
|
5574
|
+
keys2.add(key);
|
|
5575
5575
|
const { when } = entry.job;
|
|
5576
5576
|
if (when.reboot) {
|
|
5577
5577
|
if (this.rebooted || stamp.reboot[key] === this.host.bootId) continue;
|
|
@@ -5597,7 +5597,7 @@ var CronService = class {
|
|
|
5597
5597
|
}
|
|
5598
5598
|
this.rebooted = true;
|
|
5599
5599
|
for (const map of [stamp.jobs, stamp.reboot, stamp.when])
|
|
5600
|
-
for (const key of Object.keys(map)) if (!
|
|
5600
|
+
for (const key of Object.keys(map)) if (!keys2.has(key)) delete map[key];
|
|
5601
5601
|
await this.writeStamp(stamp);
|
|
5602
5602
|
for (const each of due2) void this.spawn(each);
|
|
5603
5603
|
return next3;
|
|
@@ -6883,16 +6883,16 @@ function missing2(err) {
|
|
|
6883
6883
|
return err?.code === "ENOENT";
|
|
6884
6884
|
}
|
|
6885
6885
|
function synthetic(directory, path) {
|
|
6886
|
-
const
|
|
6886
|
+
const epoch2 = /* @__PURE__ */ new Date(0);
|
|
6887
6887
|
return {
|
|
6888
6888
|
isFile: !directory,
|
|
6889
6889
|
isDirectory: directory,
|
|
6890
6890
|
isSymbolicLink: false,
|
|
6891
6891
|
size: 0,
|
|
6892
6892
|
mode: (directory ? S_IFDIR : S_IFREG) | 493,
|
|
6893
|
-
mtime:
|
|
6894
|
-
atime:
|
|
6895
|
-
ctime:
|
|
6893
|
+
mtime: epoch2,
|
|
6894
|
+
atime: epoch2,
|
|
6895
|
+
ctime: epoch2,
|
|
6896
6896
|
ino: inodeOf(path)
|
|
6897
6897
|
};
|
|
6898
6898
|
}
|
|
@@ -7111,16 +7111,16 @@ function missing3(err) {
|
|
|
7111
7111
|
return err?.code === "ENOENT";
|
|
7112
7112
|
}
|
|
7113
7113
|
function statOf(path, bytes) {
|
|
7114
|
-
const
|
|
7114
|
+
const epoch2 = /* @__PURE__ */ new Date(0);
|
|
7115
7115
|
return {
|
|
7116
7116
|
isFile: true,
|
|
7117
7117
|
isDirectory: false,
|
|
7118
7118
|
isSymbolicLink: false,
|
|
7119
7119
|
size: bytes.length,
|
|
7120
7120
|
mode: S_IFREG2 | 420,
|
|
7121
|
-
mtime:
|
|
7122
|
-
atime:
|
|
7123
|
-
ctime:
|
|
7121
|
+
mtime: epoch2,
|
|
7122
|
+
atime: epoch2,
|
|
7123
|
+
ctime: epoch2,
|
|
7124
7124
|
ino: inodeOf(path),
|
|
7125
7125
|
nlink: 1,
|
|
7126
7126
|
uid: 0,
|
|
@@ -9476,10 +9476,10 @@ async function copyByBytes(fs, from, to, options) {
|
|
|
9476
9476
|
|
|
9477
9477
|
// src/fs/view.ts
|
|
9478
9478
|
function view(target, overrides) {
|
|
9479
|
-
const
|
|
9479
|
+
const keys2 = new Set(Object.keys(overrides));
|
|
9480
9480
|
return new Proxy(target, {
|
|
9481
9481
|
get(t, prop, receiver) {
|
|
9482
|
-
if (
|
|
9482
|
+
if (keys2.has(prop)) return overrides[prop];
|
|
9483
9483
|
return Reflect.get(t, prop, receiver);
|
|
9484
9484
|
},
|
|
9485
9485
|
set(t, prop, value2) {
|
|
@@ -9735,11 +9735,11 @@ var HeldPaths = class extends Map {
|
|
|
9735
9735
|
handlesAt(path) {
|
|
9736
9736
|
return [...this.handles].filter(([, key]) => key === path).map(([id2]) => id2);
|
|
9737
9737
|
}
|
|
9738
|
-
doubt(over,
|
|
9738
|
+
doubt(over, exact2 = false) {
|
|
9739
9739
|
if (this.bound.size === 0) return;
|
|
9740
9740
|
for (const [handle, key] of this.handles) {
|
|
9741
9741
|
if (!this.bound.has(handle) || this.keptHandles.has(handle)) continue;
|
|
9742
|
-
if (
|
|
9742
|
+
if (exact2 ? key === over : within5(key, over)) this.doubted.set(handle, ++this.doubts);
|
|
9743
9743
|
}
|
|
9744
9744
|
}
|
|
9745
9745
|
boundIno(handle) {
|
|
@@ -13594,8 +13594,8 @@ function refused2(engine, session, err, opening2) {
|
|
|
13594
13594
|
const code = verifyCode(flags2);
|
|
13595
13595
|
if (code && flags2 !== -1)
|
|
13596
13596
|
return new KernelError("EPROTO", `${code}: ${engine.verifyInfo(flags2).trim()}`);
|
|
13597
|
-
const
|
|
13598
|
-
if (
|
|
13597
|
+
const record3 = recordRefusal(opening2);
|
|
13598
|
+
if (record3) return record3;
|
|
13599
13599
|
const text = err instanceof TlsError ? err.message : String(err);
|
|
13600
13600
|
return new KernelError("EPROTO", `ERR_SSL_HANDSHAKE_FAILURE: ${text}${alertOf(session)}`);
|
|
13601
13601
|
}
|
|
@@ -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;
|
|
@@ -14710,6 +14710,120 @@ var SessionScrubs = class {
|
|
|
14710
14710
|
}
|
|
14711
14711
|
};
|
|
14712
14712
|
|
|
14713
|
+
// src/kernel/sealed-oauth.ts
|
|
14714
|
+
var SEALED_OAUTH_TIMEOUT_MS = 45e3;
|
|
14715
|
+
var SEALED_OAUTH_OPS = /* @__PURE__ */ new Set([
|
|
14716
|
+
"oauthGrant",
|
|
14717
|
+
"oauthStatus",
|
|
14718
|
+
"oauthRefresh",
|
|
14719
|
+
"oauthRemove"
|
|
14720
|
+
]);
|
|
14721
|
+
var MESSAGES = {
|
|
14722
|
+
EINVAL: "Invalid OAuth request",
|
|
14723
|
+
ENOENT: "OAuth credential not found",
|
|
14724
|
+
EEXIST: "OAuth credential identity conflicts",
|
|
14725
|
+
EACCES: "OAuth operation refused",
|
|
14726
|
+
EAGAIN: "OAuth credential changed; try again",
|
|
14727
|
+
ENOTSUP: "OAuth operation is not supported",
|
|
14728
|
+
ENOTCONN: "Vault is unavailable",
|
|
14729
|
+
ETIMEDOUT: "Vault request timed out",
|
|
14730
|
+
EIO: "Invalid vault reply"
|
|
14731
|
+
};
|
|
14732
|
+
var CAUSES = {
|
|
14733
|
+
"vault-locked": "Vault is locked",
|
|
14734
|
+
"oauth-reauth-required": "OAuth sign-in is required",
|
|
14735
|
+
"oauth-refresh-failed": "OAuth refresh failed"
|
|
14736
|
+
};
|
|
14737
|
+
var COMMON = ["v", "op", "caller"];
|
|
14738
|
+
var IDENTITY = [...COMMON, "name", "provider", "account"];
|
|
14739
|
+
var GRANT = [...IDENTITY, "accessToken", "refreshToken", "tokenType", "expiresAt", "oauth"];
|
|
14740
|
+
var CONFIG = ["tokenUrl", "clientId", "authMethod", "scope", "resource"];
|
|
14741
|
+
var UNSUPPORTED = ["auth", "clientSecret", "fields"];
|
|
14742
|
+
var STATES2 = /* @__PURE__ */ new Set([
|
|
14743
|
+
"ready",
|
|
14744
|
+
"refreshing",
|
|
14745
|
+
"expired",
|
|
14746
|
+
"reauth-required",
|
|
14747
|
+
"removed",
|
|
14748
|
+
"locked"
|
|
14749
|
+
]);
|
|
14750
|
+
var NAME2 = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
14751
|
+
var PROVIDER = /^[a-z0-9][a-z0-9-]{0,127}$/;
|
|
14752
|
+
var PLACEHOLDER2 = /^fnox_proxy_[0-9a-f]{24}$/;
|
|
14753
|
+
var CONTROL = /[\u0000-\u001f\u007f-\u009f]/;
|
|
14754
|
+
function record(value2) {
|
|
14755
|
+
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
14756
|
+
}
|
|
14757
|
+
function keys(value2, allowed) {
|
|
14758
|
+
return Object.keys(value2).every((key) => allowed.includes(key));
|
|
14759
|
+
}
|
|
14760
|
+
function exact(value2, fields) {
|
|
14761
|
+
return Object.keys(value2).length === fields.length && keys(value2, fields);
|
|
14762
|
+
}
|
|
14763
|
+
function nonempty(value2) {
|
|
14764
|
+
return typeof value2 === "string" && value2.length > 0;
|
|
14765
|
+
}
|
|
14766
|
+
function epoch(value2) {
|
|
14767
|
+
return typeof value2 === "number" && Number.isSafeInteger(value2) && value2 >= 0;
|
|
14768
|
+
}
|
|
14769
|
+
function sealedOAuthOperation(op) {
|
|
14770
|
+
return op === "capabilities" || SEALED_OAUTH_OPS.has(op);
|
|
14771
|
+
}
|
|
14772
|
+
function validateSealedOAuth(req) {
|
|
14773
|
+
const config = req.oauth;
|
|
14774
|
+
if (UNSUPPORTED.some((key) => Object.hasOwn(req, key)) || record(config) && UNSUPPORTED.some((key) => Object.hasOwn(config, key)))
|
|
14775
|
+
throw new KernelError("ENOTSUP");
|
|
14776
|
+
const allowed = req.op === "capabilities" ? COMMON : req.op === "oauthGrant" ? GRANT : IDENTITY;
|
|
14777
|
+
if (!keys(req, allowed)) throw new KernelError("EINVAL");
|
|
14778
|
+
if (req.op === "capabilities") return;
|
|
14779
|
+
if (typeof req.name !== "string" || !NAME2.test(req.name) || typeof req.provider !== "string" || !PROVIDER.test(req.provider) || !nonempty(req.account) || req.account.length > 256 || CONTROL.test(req.account))
|
|
14780
|
+
throw new KernelError("EINVAL");
|
|
14781
|
+
if (req.op !== "oauthGrant") return;
|
|
14782
|
+
if (!nonempty(req.accessToken) || req.refreshToken !== void 0 && !nonempty(req.refreshToken) || req.tokenType !== void 0 && typeof req.tokenType !== "string" || req.expiresAt !== void 0 && !epoch(req.expiresAt) || !record(config) || !keys(config, CONFIG) || !nonempty(config.tokenUrl) || !nonempty(config.clientId) || typeof config.authMethod !== "string" || config.scope !== void 0 && typeof config.scope !== "string" || config.resource !== void 0 && !nonempty(config.resource))
|
|
14783
|
+
throw new KernelError("EINVAL");
|
|
14784
|
+
if (config.authMethod !== "none" || typeof req.tokenType === "string" && req.tokenType.toLowerCase() !== "bearer")
|
|
14785
|
+
throw new KernelError("ENOTSUP");
|
|
14786
|
+
}
|
|
14787
|
+
function sealedOAuthError(code = "EIO", cause) {
|
|
14788
|
+
return {
|
|
14789
|
+
v: 1,
|
|
14790
|
+
error: { code, ...cause ? { cause } : {}, message: cause ? CAUSES[cause] : MESSAGES[code] }
|
|
14791
|
+
};
|
|
14792
|
+
}
|
|
14793
|
+
function sealedOAuthReply(op, value2) {
|
|
14794
|
+
try {
|
|
14795
|
+
return checkedReply(op, value2) ?? sealedOAuthError();
|
|
14796
|
+
} catch {
|
|
14797
|
+
return sealedOAuthError();
|
|
14798
|
+
}
|
|
14799
|
+
}
|
|
14800
|
+
function checkedError(value2) {
|
|
14801
|
+
const error = value2.error;
|
|
14802
|
+
if (!exact(value2, ["v", "error"]) || !record(error) || !Object.hasOwn(error, "code") || !Object.hasOwn(error, "message"))
|
|
14803
|
+
return void 0;
|
|
14804
|
+
const { code, cause, message: message3 } = error;
|
|
14805
|
+
if (!keys(error, ["code", "cause", "message"]) || typeof code !== "string" || !Object.hasOwn(MESSAGES, code) || typeof message3 !== "string" || cause !== void 0 && (typeof cause !== "string" || !Object.hasOwn(CAUSES, cause)))
|
|
14806
|
+
return void 0;
|
|
14807
|
+
return sealedOAuthError(code, cause);
|
|
14808
|
+
}
|
|
14809
|
+
function checkedReply(op, value2) {
|
|
14810
|
+
if (!record(value2) || !Object.hasOwn(value2, "v") || value2.v !== 1) return void 0;
|
|
14811
|
+
if (Object.hasOwn(value2, "error")) return checkedError(value2);
|
|
14812
|
+
if (op === "capabilities") {
|
|
14813
|
+
const caps = value2.capabilities;
|
|
14814
|
+
if (!exact(value2, ["v", "capabilities"]) || !record(caps) || !keys(caps, ["kernelOAuth"]))
|
|
14815
|
+
return void 0;
|
|
14816
|
+
if (Object.hasOwn(caps, "kernelOAuth") && caps.kernelOAuth !== 1) return void 0;
|
|
14817
|
+
return { v: 1, capabilities: Object.hasOwn(caps, "kernelOAuth") ? { kernelOAuth: 1 } : {} };
|
|
14818
|
+
}
|
|
14819
|
+
if (op === "oauthRemove")
|
|
14820
|
+
return exact(value2, ["v", "removed"]) && value2.removed === true ? { v: 1, removed: true } : void 0;
|
|
14821
|
+
const { placeholder, state, expiresAt, refreshable } = value2;
|
|
14822
|
+
if (!exact(value2, ["v", "placeholder", "state", "expiresAt", "refreshable"]) || typeof placeholder !== "string" || !PLACEHOLDER2.test(placeholder) || typeof state !== "string" || !STATES2.has(state) || expiresAt !== null && !epoch(expiresAt) || typeof refreshable !== "boolean")
|
|
14823
|
+
return void 0;
|
|
14824
|
+
return { v: 1, placeholder, state, expiresAt, refreshable };
|
|
14825
|
+
}
|
|
14826
|
+
|
|
14713
14827
|
// src/kernel/sealed-session.ts
|
|
14714
14828
|
var SESSION_WINDOW_MS = 6e4;
|
|
14715
14829
|
var SESSION_PLACEHOLDERS = 16;
|
|
@@ -14820,8 +14934,8 @@ function sessionValues(reply, asked) {
|
|
|
14820
14934
|
const { error, values } = reply;
|
|
14821
14935
|
if (error !== void 0 || typeof values !== "object" || values === null) return void 0;
|
|
14822
14936
|
const out = {};
|
|
14823
|
-
const
|
|
14824
|
-
const wanted = asked ?? (
|
|
14937
|
+
const keys2 = Object.keys(values);
|
|
14938
|
+
const wanted = asked ?? (keys2.length === 1 && SESSION_PLACEHOLDER.test(keys2[0]) ? keys2 : []);
|
|
14825
14939
|
for (const ph of wanted) {
|
|
14826
14940
|
const value2 = values[ph];
|
|
14827
14941
|
if (typeof value2 === "string" && value2.length > 0 && !UNSAFE.test(value2)) out[ph] = value2;
|
|
@@ -14845,7 +14959,9 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
|
|
|
14845
14959
|
"request",
|
|
14846
14960
|
"status",
|
|
14847
14961
|
"cancel",
|
|
14848
|
-
"materialize"
|
|
14962
|
+
"materialize",
|
|
14963
|
+
"capabilities",
|
|
14964
|
+
...SEALED_OAUTH_OPS
|
|
14849
14965
|
]);
|
|
14850
14966
|
function tagFields(tags) {
|
|
14851
14967
|
const [tag] = tags ?? [];
|
|
@@ -14880,6 +14996,7 @@ function parseSealed(bytes) {
|
|
|
14880
14996
|
throw new KernelError("EINVAL");
|
|
14881
14997
|
if (req.op === "materialize" && !materializable(req)) throw new KernelError("EINVAL");
|
|
14882
14998
|
if (req.op === "ask" && !askable(req)) throw new KernelError("EINVAL");
|
|
14999
|
+
if (sealedOAuthOperation(req.op)) validateSealedOAuth(req);
|
|
14883
15000
|
return req;
|
|
14884
15001
|
} finally {
|
|
14885
15002
|
bytes.fill(0);
|
|
@@ -14958,7 +15075,7 @@ var SealedChannel = class {
|
|
|
14958
15075
|
const ask = fields.op === "ask";
|
|
14959
15076
|
const timer = setTimeout(
|
|
14960
15077
|
() => control.abort(ask ? "expired" : "timeout"),
|
|
14961
|
-
ask ? this.broker.askTimeoutMs : this.broker.timeoutMs
|
|
15078
|
+
ask ? this.broker.askTimeoutMs : SEALED_OAUTH_OPS.has(fields.op) ? this.broker.oauthTimeoutMs : this.broker.timeoutMs
|
|
14962
15079
|
);
|
|
14963
15080
|
const req = { ...fields, v: 1, op: fields.op, caller };
|
|
14964
15081
|
const asked = ask ? this.asking(fields, caller, requester, control.signal) : void 0;
|
|
@@ -14981,24 +15098,26 @@ var SealedChannel = class {
|
|
|
14981
15098
|
if (!facts) return errorReply("EIO", "the kernel has no record of this process");
|
|
14982
15099
|
return this.answer(askRequest(fields, caller, facts), signal);
|
|
14983
15100
|
}
|
|
14984
|
-
stopped(signal) {
|
|
15101
|
+
stopped(signal, oauth = false) {
|
|
14985
15102
|
if (signal.reason === "timeout")
|
|
14986
|
-
return errorReply("ETIMEDOUT", "the vault did not answer within 10 s");
|
|
15103
|
+
return oauth ? sealedOAuthError("ETIMEDOUT") : errorReply("ETIMEDOUT", "the vault did not answer within 10 s");
|
|
14987
15104
|
return signal.reason === "expired" ? { v: 1, state: "expired" } : void 0;
|
|
14988
15105
|
}
|
|
14989
15106
|
async answer(req, signal) {
|
|
14990
15107
|
const hook = this.broker.hook;
|
|
14991
|
-
|
|
15108
|
+
const oauth = sealedOAuthOperation(req.op);
|
|
15109
|
+
if (!hook)
|
|
15110
|
+
return oauth ? sealedOAuthError("ENOTCONN") : errorReply("ENOTCONN", "no vault is connected");
|
|
14992
15111
|
const stopped = new Promise((resolve2) => {
|
|
14993
15112
|
signal.addEventListener("abort", () => resolve2(void 0), { once: true });
|
|
14994
15113
|
});
|
|
14995
15114
|
if (signal.aborted) return void 0;
|
|
14996
15115
|
const asked = Promise.resolve().then(() => hook(req, { signal })).then(
|
|
14997
|
-
(reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
|
|
14998
|
-
(err) => errorReply("EIO", lineOf3(err))
|
|
15116
|
+
(reply2) => oauth ? sealedOAuthReply(req.op, reply2) : isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
|
|
15117
|
+
(err) => oauth ? sealedOAuthError() : errorReply("EIO", lineOf3(err))
|
|
14999
15118
|
);
|
|
15000
15119
|
const reply = await Promise.race([asked, stopped]);
|
|
15001
|
-
return signal.aborted ? this.stopped(signal) : reply;
|
|
15120
|
+
return signal.aborted ? this.stopped(signal, oauth) : reply;
|
|
15002
15121
|
}
|
|
15003
15122
|
drop() {
|
|
15004
15123
|
this.reply?.fill(0);
|
|
@@ -15061,6 +15180,7 @@ var SealedBroker = class {
|
|
|
15061
15180
|
tickets = new TicketBook();
|
|
15062
15181
|
sessions = new SessionScrubs();
|
|
15063
15182
|
askTimeoutMs = SEALED_ASK_TIMEOUT_MS;
|
|
15183
|
+
oauthTimeoutMs = SEALED_OAUTH_TIMEOUT_MS;
|
|
15064
15184
|
userOf = async () => void 0;
|
|
15065
15185
|
factsOf = async () => void 0;
|
|
15066
15186
|
constructor(hook, users, timeoutMs = SEALED_TIMEOUT_MS, tagOf = () => void 0) {
|
|
@@ -15128,10 +15248,60 @@ var SealedBroker = class {
|
|
|
15128
15248
|
}
|
|
15129
15249
|
};
|
|
15130
15250
|
|
|
15251
|
+
// src/sudo-timestamp.ts
|
|
15252
|
+
var TIMESTAMPS_MAX = 64;
|
|
15253
|
+
var MINUTE_MS2 = 6e4;
|
|
15254
|
+
function timestampKey(owner, type) {
|
|
15255
|
+
if (type === "global") return "global";
|
|
15256
|
+
const { terminal, parent } = owner;
|
|
15257
|
+
if (type === "tty" && terminal) {
|
|
15258
|
+
return JSON.stringify(["tty", terminal.tty, terminal.sid, terminal.started]);
|
|
15259
|
+
}
|
|
15260
|
+
return parent ? JSON.stringify(["ppid", parent.pid, parent.started]) : void 0;
|
|
15261
|
+
}
|
|
15262
|
+
var Timestamps = class {
|
|
15263
|
+
users = /* @__PURE__ */ new Map();
|
|
15264
|
+
now;
|
|
15265
|
+
constructor(now) {
|
|
15266
|
+
this.now = now;
|
|
15267
|
+
}
|
|
15268
|
+
current(owner, policy) {
|
|
15269
|
+
if (policy.minutes === 0) return false;
|
|
15270
|
+
const key = timestampKey(owner, policy.type);
|
|
15271
|
+
const at = key === void 0 ? void 0 : this.users.get(owner.uid)?.get(key);
|
|
15272
|
+
if (at === void 0) return false;
|
|
15273
|
+
const now = this.now();
|
|
15274
|
+
return at <= now && (policy.minutes < 0 || now - at < policy.minutes * MINUTE_MS2);
|
|
15275
|
+
}
|
|
15276
|
+
stamp(owner, policy) {
|
|
15277
|
+
const key = policy.minutes === 0 ? void 0 : timestampKey(owner, policy.type);
|
|
15278
|
+
if (key === void 0) return false;
|
|
15279
|
+
const now = this.now();
|
|
15280
|
+
const records = this.users.get(owner.uid) ?? /* @__PURE__ */ new Map();
|
|
15281
|
+
this.users.set(owner.uid, records);
|
|
15282
|
+
records.delete(key);
|
|
15283
|
+
records.set(key, now);
|
|
15284
|
+
for (const [old, at] of records) {
|
|
15285
|
+
const stale = policy.minutes > 0 && (at > now || now - at >= policy.minutes * MINUTE_MS2);
|
|
15286
|
+
if (stale || records.size > TIMESTAMPS_MAX) records.delete(old);
|
|
15287
|
+
}
|
|
15288
|
+
return true;
|
|
15289
|
+
}
|
|
15290
|
+
invalidate(owner, type) {
|
|
15291
|
+
const key = timestampKey(owner, type);
|
|
15292
|
+
const records = this.users.get(owner.uid);
|
|
15293
|
+
if (key !== void 0) records?.delete(key);
|
|
15294
|
+
if (records?.size === 0) this.users.delete(owner.uid);
|
|
15295
|
+
}
|
|
15296
|
+
remove(uid) {
|
|
15297
|
+
this.users.delete(uid);
|
|
15298
|
+
}
|
|
15299
|
+
};
|
|
15300
|
+
|
|
15131
15301
|
// src/sudoers.ts
|
|
15132
15302
|
var SUDOERS = "/etc/sudoers";
|
|
15133
15303
|
var SUDOERS_D = "/etc/sudoers.d";
|
|
15134
|
-
var
|
|
15304
|
+
var NAME3 = /^[a-z_][a-z0-9_.-]*$/;
|
|
15135
15305
|
var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
|
|
15136
15306
|
var KERNEL_FORM = new RegExp(
|
|
15137
15307
|
String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()]*)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(\S+)\s+)?Cmnd(?:\s+(.*))?$`
|
|
@@ -15141,14 +15311,16 @@ var RUNAS = /^\(([^()]*)\)\s*/;
|
|
|
15141
15311
|
var TAG = /^([A-Z_]+)\s*:\s*/;
|
|
15142
15312
|
var OPTION = /^([A-Z_]+)=/;
|
|
15143
15313
|
var PLAIN = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
15144
|
-
var
|
|
15314
|
+
var CONTROL2 = /[\p{Cc}\u2028\u2029]/u;
|
|
15145
15315
|
var CONTROLS = /[\p{Cc}\u2028\u2029]/gu;
|
|
15146
15316
|
var SHOWN_MAX = 60;
|
|
15147
15317
|
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] Cmnd <command\u2026|*>";
|
|
15148
15318
|
var SUDOERS_SYNTAX = "<user|%group|ALL> ALL=[(<target>[,<target>\u2026])] [NOPASSWD:] <command\u2026|ALL>";
|
|
15149
15319
|
var EXPECTED = `expected '${KERNEL_SYNTAX}' or '${SUDOERS_SYNTAX}'`;
|
|
15320
|
+
var TIMESTAMP_TIMEOUT = 5;
|
|
15321
|
+
var TIMESTAMP_TYPE = "tty";
|
|
15150
15322
|
function hasControl(text) {
|
|
15151
|
-
return
|
|
15323
|
+
return CONTROL2.test(text);
|
|
15152
15324
|
}
|
|
15153
15325
|
function withoutControl(text) {
|
|
15154
15326
|
return text.replace(CONTROLS, (ch) => {
|
|
@@ -15286,7 +15458,7 @@ function commandText(cmnd) {
|
|
|
15286
15458
|
}
|
|
15287
15459
|
function validWho(who) {
|
|
15288
15460
|
if (who === "ALL") return true;
|
|
15289
|
-
return
|
|
15461
|
+
return NAME3.test(who.startsWith("%") ? who.slice(1) : who);
|
|
15290
15462
|
}
|
|
15291
15463
|
function whoError(who) {
|
|
15292
15464
|
if (validWho(who)) return void 0;
|
|
@@ -15319,7 +15491,7 @@ function kernelRules(m) {
|
|
|
15319
15491
|
const [, nopasswd, who = "", target = "root", dir, stdin, rest2 = ""] = m;
|
|
15320
15492
|
const bad = whoError(who);
|
|
15321
15493
|
if (bad !== void 0) return bad;
|
|
15322
|
-
if (target !== "ALL" && !
|
|
15494
|
+
if (target !== "ALL" && !NAME3.test(target)) return targetError(target);
|
|
15323
15495
|
const cwd = dir === void 0 ? void 0 : cwdOf(dir);
|
|
15324
15496
|
if (cwd === "") return `Cwd expects one absolute directory, not ${shown(dir)}`;
|
|
15325
15497
|
if (stdin !== void 0 && stdin !== "none") return `Stdin takes none, not ${shown(stdin)}`;
|
|
@@ -15343,11 +15515,11 @@ function runasOf(list) {
|
|
|
15343
15515
|
return colon < 0 ? "() names no user to run as: name one, or leave the (\u2026) out for root" : `(${list}) runs as the caller with another group, which the kernel's sudo can't do`;
|
|
15344
15516
|
}
|
|
15345
15517
|
for (const name2 of names2) {
|
|
15346
|
-
if (name2 === "ALL" ||
|
|
15518
|
+
if (name2 === "ALL" || NAME3.test(name2)) continue;
|
|
15347
15519
|
return `expected user names or ALL to run as, not ${shown(name2)} (no %group, #uid or !user)`;
|
|
15348
15520
|
}
|
|
15349
15521
|
for (const group of groups) {
|
|
15350
|
-
if (group === "" || group === "ALL" ||
|
|
15522
|
+
if (group === "" || group === "ALL" || NAME3.test(group)) continue;
|
|
15351
15523
|
return `expected group names or ALL after ':' in (\u2026), not ${shown(group)}`;
|
|
15352
15524
|
}
|
|
15353
15525
|
return names2.includes("ALL") ? ["ALL"] : [...new Set(names2)];
|
|
@@ -15429,9 +15601,6 @@ function directiveError(text) {
|
|
|
15429
15601
|
return /^#-?\d+\s+\S+\s*=/.test(text) ? "a #uid isn't supported: name the user, %group or ALL" : null;
|
|
15430
15602
|
}
|
|
15431
15603
|
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
15604
|
if (/^[A-Z][A-Za-z]*_Alias$/.test(first)) {
|
|
15436
15605
|
return "aliases aren't supported: name the users, targets and commands in each line";
|
|
15437
15606
|
}
|
|
@@ -15439,7 +15608,7 @@ function directiveError(text) {
|
|
|
15439
15608
|
}
|
|
15440
15609
|
function recordedBare(line, file, rule) {
|
|
15441
15610
|
if (rule.cmnd === "*" || rule.cmnd.args !== void 0 || !rule.nopasswd) return false;
|
|
15442
|
-
if (file !== `${SUDOERS_D}/${rule.who}` || !
|
|
15611
|
+
if (file !== `${SUDOERS_D}/${rule.who}` || !NAME3.test(rule.who) || !NAME3.test(rule.target)) {
|
|
15443
15612
|
return false;
|
|
15444
15613
|
}
|
|
15445
15614
|
const head = `NOPASSWD ${rule.who} (${rule.target}) `;
|
|
@@ -15457,9 +15626,90 @@ function kernelLine(m, line, file) {
|
|
|
15457
15626
|
)
|
|
15458
15627
|
};
|
|
15459
15628
|
}
|
|
15629
|
+
var DEFAULTS = /^Defaults(?=[:@>!\s]|$)/;
|
|
15630
|
+
var DEFAULTS_ONLY = `Defaults aren't supported, except timestamp_timeout and timestamp_type for everyone, a user or a %group: this line changes nothing`;
|
|
15631
|
+
var PARAM = /^(!*)([A-Za-z_]+)(?:\s*([-+]?=)\s*(.*))?$/s;
|
|
15632
|
+
var MINUTES = /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$/;
|
|
15633
|
+
var TYPES = ["tty", "ppid", "global"];
|
|
15634
|
+
function defaultsValue(raw2) {
|
|
15635
|
+
const quoted = /^"(.*)"$/s.exec(raw2);
|
|
15636
|
+
return quoted ? quoted[1] : raw2;
|
|
15637
|
+
}
|
|
15638
|
+
function timeoutParam({ text, negated, op, value: value2 }) {
|
|
15639
|
+
if (negated && op === void 0) return { timestampTimeout: 0 };
|
|
15640
|
+
if (negated || op !== "=" || !MINUTES.test(value2) || !Number.isFinite(Number(value2))) {
|
|
15641
|
+
return `timestamp_timeout takes minutes, such as =5 or =2.5 (0 always asks, below 0 never expires), not ${shown(text)}`;
|
|
15642
|
+
}
|
|
15643
|
+
return { timestampTimeout: Number(value2) };
|
|
15644
|
+
}
|
|
15645
|
+
function typeParam({ text, negated, op, value: value2 }) {
|
|
15646
|
+
if (!negated && op === "=" && TYPES.includes(value2)) {
|
|
15647
|
+
return { timestampType: value2 };
|
|
15648
|
+
}
|
|
15649
|
+
if (value2 === "kernel") return "timestamp_type=kernel isn't supported: use tty, ppid or global";
|
|
15650
|
+
return `timestamp_type takes =tty, =ppid or =global, not ${shown(text)}`;
|
|
15651
|
+
}
|
|
15652
|
+
function defaultsParam(param) {
|
|
15653
|
+
const m = PARAM.exec(param);
|
|
15654
|
+
if (!m) return DEFAULTS_ONLY;
|
|
15655
|
+
const [, bangs = "", name2, op, raw2 = ""] = m;
|
|
15656
|
+
const read = { text: param, negated: bangs !== "", op, value: defaultsValue(raw2.trim()) };
|
|
15657
|
+
const settings = name2 === "timestamp_timeout" ? timeoutParam(read) : name2 === "timestamp_type" ? typeParam(read) : DEFAULTS_ONLY;
|
|
15658
|
+
if (typeof settings === "string") return settings;
|
|
15659
|
+
const value2 = settings.timestampTimeout ?? settings.timestampType;
|
|
15660
|
+
return { settings, text: `${name2}=${value2}` };
|
|
15661
|
+
}
|
|
15662
|
+
function defaultsWho(binding) {
|
|
15663
|
+
if (binding === "") return null;
|
|
15664
|
+
if (!binding.startsWith(":")) return DEFAULTS_ONLY;
|
|
15665
|
+
const who = binding.slice(1).split(",");
|
|
15666
|
+
for (const name2 of who) {
|
|
15667
|
+
if (!validWho(name2))
|
|
15668
|
+
return `expected users, %groups or ALL after Defaults:, not ${shown(name2)}`;
|
|
15669
|
+
}
|
|
15670
|
+
return who;
|
|
15671
|
+
}
|
|
15672
|
+
function defaultsLine(text) {
|
|
15673
|
+
const m = /^Defaults(\S*)(?:\s+(.*))?$/s.exec(text);
|
|
15674
|
+
const who = defaultsWho(m?.[1] ?? "");
|
|
15675
|
+
if (typeof who === "string") return { error: who };
|
|
15676
|
+
const list = (m?.[2] ?? "").trim();
|
|
15677
|
+
if (list === "")
|
|
15678
|
+
return {
|
|
15679
|
+
error: "expected timestamp_timeout=<minutes> or timestamp_type=<type> after Defaults"
|
|
15680
|
+
};
|
|
15681
|
+
const settings = {};
|
|
15682
|
+
const params = [];
|
|
15683
|
+
for (const param of list.split(",")) {
|
|
15684
|
+
const read = defaultsParam(param.trim());
|
|
15685
|
+
if (typeof read === "string") return { error: read };
|
|
15686
|
+
Object.assign(settings, read.settings);
|
|
15687
|
+
params.push(read.text);
|
|
15688
|
+
}
|
|
15689
|
+
return { defaults: { who, settings, params } };
|
|
15690
|
+
}
|
|
15691
|
+
function resolveDefaults(entries, applies) {
|
|
15692
|
+
const everyone = entries.filter((e) => e.who === null);
|
|
15693
|
+
const mine = entries.filter((e) => e.who?.some(applies));
|
|
15694
|
+
const matching = [...everyone, ...mine];
|
|
15695
|
+
const settings = Object.assign({}, ...matching.map((e) => e.settings));
|
|
15696
|
+
return {
|
|
15697
|
+
timestampTimeout: settings.timestampTimeout ?? TIMESTAMP_TIMEOUT,
|
|
15698
|
+
timestampType: settings.timestampType ?? TIMESTAMP_TYPE,
|
|
15699
|
+
matching
|
|
15700
|
+
};
|
|
15701
|
+
}
|
|
15702
|
+
function defaultsText(entries) {
|
|
15703
|
+
const all = resolveDefaults(entries, () => false);
|
|
15704
|
+
return [
|
|
15705
|
+
`Defaults timestamp_timeout=${all.timestampTimeout}, timestamp_type=${all.timestampType}`,
|
|
15706
|
+
...entries.filter((e) => e.who !== null).map((e) => `Defaults:${e.who.join(",")} ${e.params.join(", ")}`)
|
|
15707
|
+
];
|
|
15708
|
+
}
|
|
15460
15709
|
function parseLine(line, file) {
|
|
15461
15710
|
const text = line.trim();
|
|
15462
15711
|
if (text === "") return null;
|
|
15712
|
+
if (DEFAULTS.test(text)) return defaultsLine(text);
|
|
15463
15713
|
const directive = directiveError(text);
|
|
15464
15714
|
if (directive !== void 0) return directive === null ? null : { error: directive };
|
|
15465
15715
|
const kernel = KERNEL_FORM.exec(text);
|
|
@@ -15471,14 +15721,16 @@ function parseLine(line, file) {
|
|
|
15471
15721
|
}
|
|
15472
15722
|
function parsePolicy(text, file) {
|
|
15473
15723
|
const rules = [];
|
|
15724
|
+
const defaults2 = [];
|
|
15474
15725
|
const errors = [];
|
|
15475
15726
|
for (const [n, line] of text.split("\n").entries()) {
|
|
15476
15727
|
const parsed2 = parseLine(line, file);
|
|
15477
15728
|
if (parsed2 === null) continue;
|
|
15478
15729
|
if ("error" in parsed2) errors.push({ line: n + 1, message: parsed2.error });
|
|
15730
|
+
else if ("defaults" in parsed2) defaults2.push(parsed2.defaults);
|
|
15479
15731
|
else rules.push(...parsed2.rules);
|
|
15480
15732
|
}
|
|
15481
|
-
return { rules, errors };
|
|
15733
|
+
return { rules, defaults: defaults2, errors };
|
|
15482
15734
|
}
|
|
15483
15735
|
|
|
15484
15736
|
// src/users.ts
|
|
@@ -15486,7 +15738,7 @@ var PASSWD = "/etc/passwd";
|
|
|
15486
15738
|
var GROUP = "/etc/group";
|
|
15487
15739
|
var DEFAULT_PASSWD = "root:x:0:0:root:/root:/bin/bash\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n";
|
|
15488
15740
|
var DEFAULT_GROUP = "root:x:0:\ndialout:x:20:\nsudo:x:27:\nusers:x:100:\nnogroup:x:65534:\n";
|
|
15489
|
-
var
|
|
15741
|
+
var NAME4 = /^[a-z_][a-z0-9_.-]*$/;
|
|
15490
15742
|
var MAX_NAME = 255;
|
|
15491
15743
|
var FIRST_UID = 1e3;
|
|
15492
15744
|
var LAST_UID = 6e4;
|
|
@@ -15734,7 +15986,7 @@ var UserDb = class {
|
|
|
15734
15986
|
add(options) {
|
|
15735
15987
|
return this.serial(async () => {
|
|
15736
15988
|
const { name: name2 } = options;
|
|
15737
|
-
if (typeof name2 !== "string" || name2.length > MAX_NAME || !
|
|
15989
|
+
if (typeof name2 !== "string" || name2.length > MAX_NAME || !NAME4.test(name2)) {
|
|
15738
15990
|
throw fsError("EINVAL", `not a user name: ${String(name2)}`);
|
|
15739
15991
|
}
|
|
15740
15992
|
const remember = () => {
|
|
@@ -15788,7 +16040,7 @@ var UserDb = class {
|
|
|
15788
16040
|
}
|
|
15789
16041
|
addGroup(name2, gid) {
|
|
15790
16042
|
return this.serial(async () => {
|
|
15791
|
-
if (typeof name2 !== "string" || name2.length > MAX_NAME || !
|
|
16043
|
+
if (typeof name2 !== "string" || name2.length > MAX_NAME || !NAME4.test(name2)) {
|
|
15792
16044
|
throw fsError("EINVAL", `not a group name: ${String(name2)}`);
|
|
15793
16045
|
}
|
|
15794
16046
|
if (typeof gid !== "number" || !Number.isInteger(gid) || gid < 0 || gid > MAX_GID) {
|
|
@@ -15863,7 +16115,14 @@ var SECURE_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
|
15863
16115
|
var DEFAULT_SUDOERS = "%sudo Cmnd *\n";
|
|
15864
16116
|
var SUDO_TIMEOUT_MS = 3e5;
|
|
15865
16117
|
var TERM = /^[A-Za-z0-9._+-]+$/;
|
|
15866
|
-
var USAGE =
|
|
16118
|
+
var USAGE = [
|
|
16119
|
+
"usage: sudo -K | -k",
|
|
16120
|
+
" sudo -v [-kn] [-u user]",
|
|
16121
|
+
" sudo -l [-kn]",
|
|
16122
|
+
" sudo [-kn] [-u user] [--] command [args...]"
|
|
16123
|
+
].join("\n");
|
|
16124
|
+
var DONE = { done: true };
|
|
16125
|
+
var NOT_STAMPED = "sudo: approved, but the approver didn't say a person decided, so no timestamp was recorded";
|
|
15867
16126
|
var DENIED2 = "sudo: approval denied";
|
|
15868
16127
|
var DENIAL_MAX = 500;
|
|
15869
16128
|
var ABORTED = /* @__PURE__ */ Symbol("aborted");
|
|
@@ -15937,6 +16196,17 @@ async function answerOf(approve, req, signal) {
|
|
|
15937
16196
|
return cut === void 0 ? "deny" : await cut;
|
|
15938
16197
|
}
|
|
15939
16198
|
}
|
|
16199
|
+
function approvalOf(answer2) {
|
|
16200
|
+
if (answer2 === "once" || answer2 === "always") return { answer: answer2, person: false };
|
|
16201
|
+
if (typeof answer2 !== "object" || answer2 === null) return void 0;
|
|
16202
|
+
try {
|
|
16203
|
+
const { answer: kind, timestamp } = answer2;
|
|
16204
|
+
if (kind !== "once" && kind !== "always" || timestamp !== true) return void 0;
|
|
16205
|
+
return { answer: kind, person: true };
|
|
16206
|
+
} catch {
|
|
16207
|
+
return void 0;
|
|
16208
|
+
}
|
|
16209
|
+
}
|
|
15940
16210
|
function recordedCommand(argv) {
|
|
15941
16211
|
const [path = "", ...args] = argv;
|
|
15942
16212
|
return commandText({ path, args: args.map((a) => [a]) });
|
|
@@ -15946,6 +16216,9 @@ function flags(parsed2, arg, next3) {
|
|
|
15946
16216
|
const flag = arg[j];
|
|
15947
16217
|
if (flag === "n") parsed2.nonInteractive = true;
|
|
15948
16218
|
else if (flag === "l") parsed2.list = true;
|
|
16219
|
+
else if (flag === "v") parsed2.validate = true;
|
|
16220
|
+
else if (flag === "k") parsed2.reset = true;
|
|
16221
|
+
else if (flag === "K") parsed2.removeAll = true;
|
|
15949
16222
|
else if (flag !== "u") return `sudo: invalid option -- '${flag}'
|
|
15950
16223
|
${USAGE}`;
|
|
15951
16224
|
else {
|
|
@@ -15959,7 +16232,15 @@ ${USAGE}`;
|
|
|
15959
16232
|
return 0;
|
|
15960
16233
|
}
|
|
15961
16234
|
function parseArgs(args) {
|
|
15962
|
-
const parsed2 = {
|
|
16235
|
+
const parsed2 = {
|
|
16236
|
+
list: false,
|
|
16237
|
+
nonInteractive: false,
|
|
16238
|
+
validate: false,
|
|
16239
|
+
reset: false,
|
|
16240
|
+
removeAll: false,
|
|
16241
|
+
user: "root",
|
|
16242
|
+
command: []
|
|
16243
|
+
};
|
|
15963
16244
|
let i = 0;
|
|
15964
16245
|
for (; i < args.length; i++) {
|
|
15965
16246
|
const arg = args[i];
|
|
@@ -15973,8 +16254,17 @@ function parseArgs(args) {
|
|
|
15973
16254
|
i += used;
|
|
15974
16255
|
}
|
|
15975
16256
|
parsed2.command = args.slice(i);
|
|
15976
|
-
|
|
15977
|
-
|
|
16257
|
+
return usable(parsed2) ? parsed2 : USAGE;
|
|
16258
|
+
}
|
|
16259
|
+
function usable(parsed2) {
|
|
16260
|
+
const command = parsed2.command.length > 0;
|
|
16261
|
+
const modes = [parsed2.list, parsed2.validate, parsed2.removeAll].filter(Boolean).length;
|
|
16262
|
+
if (modes > 1 || parsed2.removeAll && parsed2.reset) return false;
|
|
16263
|
+
if (modes === 1) return !command;
|
|
16264
|
+
return command || parsed2.reset;
|
|
16265
|
+
}
|
|
16266
|
+
function policyOf(settings) {
|
|
16267
|
+
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
15978
16268
|
}
|
|
15979
16269
|
function described(rule) {
|
|
15980
16270
|
const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
|
|
@@ -15986,9 +16276,11 @@ var Sudo = class {
|
|
|
15986
16276
|
o;
|
|
15987
16277
|
warned = /* @__PURE__ */ new Set();
|
|
15988
16278
|
pending = /* @__PURE__ */ new Set();
|
|
16279
|
+
timestamps;
|
|
15989
16280
|
next = 0;
|
|
15990
16281
|
constructor(options) {
|
|
15991
16282
|
this.o = options;
|
|
16283
|
+
this.timestamps = new Timestamps(options.now ?? (() => performance.now()));
|
|
15992
16284
|
}
|
|
15993
16285
|
warn(message3) {
|
|
15994
16286
|
if (this.warned.has(message3)) return;
|
|
@@ -16075,8 +16367,8 @@ var Sudo = class {
|
|
|
16075
16367
|
const code = err.code;
|
|
16076
16368
|
return skipped(`it can't be read (${typeof code === "string" ? code : "error"})`);
|
|
16077
16369
|
}
|
|
16078
|
-
const { rules, errors } = parsePolicy(text, path);
|
|
16079
|
-
return { path, rules, problems: errors, ok: true };
|
|
16370
|
+
const { rules, defaults: defaults2, errors } = parsePolicy(text, path);
|
|
16371
|
+
return { path, rules, defaults: defaults2, problems: errors, ok: true };
|
|
16080
16372
|
}
|
|
16081
16373
|
async policy() {
|
|
16082
16374
|
const files = [await this.policyFile(SUDOERS)];
|
|
@@ -16105,7 +16397,10 @@ var Sudo = class {
|
|
|
16105
16397
|
}
|
|
16106
16398
|
}
|
|
16107
16399
|
if (!detailed && skipped > 0) await caller.report?.(skippedNotice(skipped));
|
|
16108
|
-
return
|
|
16400
|
+
return {
|
|
16401
|
+
rules: files.flatMap((f) => f.rules),
|
|
16402
|
+
defaults: files.flatMap((f) => f.defaults ?? [])
|
|
16403
|
+
};
|
|
16109
16404
|
}
|
|
16110
16405
|
async check(cred, call) {
|
|
16111
16406
|
const lines2 = [];
|
|
@@ -16118,17 +16413,26 @@ var Sudo = class {
|
|
|
16118
16413
|
if (read && problems.length === 0) lines2.push([1, `${path}: parsed OK`]);
|
|
16119
16414
|
if (problems.length > 0) ok4 = false;
|
|
16120
16415
|
};
|
|
16416
|
+
const values = (defaults2) => {
|
|
16417
|
+
for (const line of defaultsText(defaults2)) lines2.push([1, line]);
|
|
16418
|
+
};
|
|
16121
16419
|
if (call.files !== void 0) {
|
|
16420
|
+
const defaults2 = [];
|
|
16122
16421
|
for (const { name: name2, text } of call.files) {
|
|
16123
|
-
|
|
16422
|
+
const parsed2 = parsePolicy(text);
|
|
16423
|
+
defaults2.push(...parsed2.defaults);
|
|
16424
|
+
report({ path: name2, rules: [], problems: parsed2.errors, ok: true });
|
|
16124
16425
|
}
|
|
16426
|
+
if (defaults2.length > 0) values(defaults2);
|
|
16125
16427
|
return { lines: lines2, ok: ok4 };
|
|
16126
16428
|
}
|
|
16127
16429
|
if (this.o.mode === "enforce" && cred.euid !== 0) {
|
|
16128
16430
|
const hint = "check the policy as root (sudo visudo -c), or a file with visudo -c -f <file>";
|
|
16129
16431
|
return { lines: [[2, `visudo: ${SUDOERS}: Permission denied: ${hint}`]], ok: false };
|
|
16130
16432
|
}
|
|
16131
|
-
|
|
16433
|
+
const files = await this.policy();
|
|
16434
|
+
for (const file of files) report(file);
|
|
16435
|
+
values(files.flatMap((f) => f.defaults ?? []));
|
|
16132
16436
|
return { lines: lines2, ok: ok4 };
|
|
16133
16437
|
}
|
|
16134
16438
|
async groupIds() {
|
|
@@ -16137,9 +16441,9 @@ var Sudo = class {
|
|
|
16137
16441
|
async applies(caller, cred) {
|
|
16138
16442
|
const gids = await this.groupIds();
|
|
16139
16443
|
const mine = /* @__PURE__ */ new Set([cred.rgid, cred.egid, ...cred.groups]);
|
|
16140
|
-
return (
|
|
16141
|
-
if (
|
|
16142
|
-
const gid =
|
|
16444
|
+
return (who) => {
|
|
16445
|
+
if (who === "ALL" || who === caller.name) return true;
|
|
16446
|
+
const gid = who.startsWith("%") ? gids.get(who.slice(1)) : void 0;
|
|
16143
16447
|
return gid !== void 0 && mine.has(gid);
|
|
16144
16448
|
};
|
|
16145
16449
|
}
|
|
@@ -16184,18 +16488,40 @@ var Sudo = class {
|
|
|
16184
16488
|
const mode = this.o.mode;
|
|
16185
16489
|
if (parsed2.list) return { list: `sudo: users are '${mode}': commands run as the caller
|
|
16186
16490
|
` };
|
|
16187
|
-
return { run: parsed2.command };
|
|
16491
|
+
return parsed2.command.length > 0 ? { run: parsed2.command } : DONE;
|
|
16188
16492
|
}
|
|
16189
16493
|
const { cred } = caller;
|
|
16190
16494
|
const policy = await this.policyRules(caller);
|
|
16191
16495
|
const self = await this.o.users.lookup(cred.ruid);
|
|
16192
16496
|
if (!self) return failed2("EPERM", `sudo: uid ${cred.ruid} is not in the passwd database`);
|
|
16497
|
+
const owner = {
|
|
16498
|
+
uid: cred.ruid,
|
|
16499
|
+
terminal: caller.terminal,
|
|
16500
|
+
parent: caller.parent
|
|
16501
|
+
};
|
|
16502
|
+
const applies = await this.applies(self, cred);
|
|
16503
|
+
const settings = resolveDefaults(policy.defaults, applies);
|
|
16504
|
+
if (parsed2.removeAll) {
|
|
16505
|
+
this.timestamps.remove(owner.uid);
|
|
16506
|
+
return DONE;
|
|
16507
|
+
}
|
|
16508
|
+
if (parsed2.reset && !parsed2.validate && !parsed2.list && parsed2.command.length === 0) {
|
|
16509
|
+
this.timestamps.invalidate(owner, settings.timestampType);
|
|
16510
|
+
return DONE;
|
|
16511
|
+
}
|
|
16193
16512
|
const target = await this.lookup(parsed2.user);
|
|
16194
16513
|
if (!target) return failed2("ENOENT", `sudo: unknown user ${parsed2.user}`);
|
|
16195
16514
|
const root = cred.euid === 0;
|
|
16196
|
-
const rules = root ? [] : policy;
|
|
16197
|
-
const
|
|
16198
|
-
if (parsed2.list) return this.listed(self, root,
|
|
16515
|
+
const rules = root ? [] : policy.rules;
|
|
16516
|
+
const mine = rules.filter((rule) => applies(rule.who));
|
|
16517
|
+
if (parsed2.list) return this.listed(self, root, mine, settings.matching);
|
|
16518
|
+
const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
|
|
16519
|
+
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
16520
|
+
return this.command(session, root, mine, call);
|
|
16521
|
+
}
|
|
16522
|
+
async command(s, root, mine, call) {
|
|
16523
|
+
const { caller, self, target, parsed: parsed2 } = s;
|
|
16524
|
+
const { cred } = caller;
|
|
16199
16525
|
const [word = "", ...args] = parsed2.command;
|
|
16200
16526
|
const cwd = await this.canonical(call.cwd);
|
|
16201
16527
|
if (cwd === void 0 || !await this.reachable(cred, cwd)) {
|
|
@@ -16209,8 +16535,8 @@ var Sudo = class {
|
|
|
16209
16535
|
const untrusted2 = await this.untrusted(path, cwd, word);
|
|
16210
16536
|
if (untrusted2) return untrusted2;
|
|
16211
16537
|
const argv = [path, ...args];
|
|
16212
|
-
const matching =
|
|
16213
|
-
(rule) =>
|
|
16538
|
+
const matching = mine.filter(
|
|
16539
|
+
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv)
|
|
16214
16540
|
);
|
|
16215
16541
|
const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
|
|
16216
16542
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
@@ -16221,23 +16547,67 @@ var Sudo = class {
|
|
|
16221
16547
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
16222
16548
|
);
|
|
16223
16549
|
}
|
|
16550
|
+
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
16551
|
+
this.timestamps.stamp(s.owner, s.policy);
|
|
16552
|
+
this.logged(s, cwd, argv);
|
|
16553
|
+
return grant();
|
|
16554
|
+
}
|
|
16224
16555
|
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;
|
|
16556
|
+
return this.pendingFor(cred.ruid, async () => {
|
|
16557
|
+
const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd });
|
|
16558
|
+
if ("error" in answer2) return answer2;
|
|
16230
16559
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
16231
16560
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
16232
16561
|
}
|
|
16233
16562
|
const changed = await this.untrusted(path, cwd, word);
|
|
16234
16563
|
if (changed) return changed;
|
|
16235
|
-
|
|
16236
|
-
|
|
16564
|
+
const result = answer2.answer === "once" ? grant() : await this.always(self, target, cwd, argv, recordable(cwd, parsed2.command), grant);
|
|
16565
|
+
if (!("error" in result)) this.stamped(s, answer2);
|
|
16566
|
+
return result;
|
|
16567
|
+
});
|
|
16568
|
+
}
|
|
16569
|
+
async validate(s, root, mine, dir) {
|
|
16570
|
+
const { self, parsed: parsed2 } = s;
|
|
16571
|
+
if (root) return DONE;
|
|
16572
|
+
if (mine.length === 0) {
|
|
16573
|
+
return failed2("EPERM", `Sorry, user ${self.name} may not run sudo on ${this.o.hostname}.`);
|
|
16574
|
+
}
|
|
16575
|
+
if (mine.every((rule) => rule.nopasswd)) return DONE;
|
|
16576
|
+
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
16577
|
+
this.timestamps.stamp(s.owner, s.policy);
|
|
16578
|
+
return DONE;
|
|
16579
|
+
}
|
|
16580
|
+
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
16581
|
+
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
16582
|
+
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
16583
|
+
const answer2 = await this.ask(s, { argv: [], path: "", cwd, validate: true });
|
|
16584
|
+
if ("error" in answer2) return answer2;
|
|
16585
|
+
if (!answer2.person) await s.caller.report?.(NOT_STAMPED);
|
|
16586
|
+
this.stamped(s, answer2);
|
|
16587
|
+
return DONE;
|
|
16588
|
+
});
|
|
16589
|
+
}
|
|
16590
|
+
async pendingFor(uid, work) {
|
|
16591
|
+
if (this.pending.has(uid)) return failed2("EBUSY", "sudo: another request is pending");
|
|
16592
|
+
this.pending.add(uid);
|
|
16593
|
+
try {
|
|
16594
|
+
return await work();
|
|
16237
16595
|
} finally {
|
|
16238
|
-
this.pending.delete(
|
|
16596
|
+
this.pending.delete(uid);
|
|
16239
16597
|
}
|
|
16240
16598
|
}
|
|
16599
|
+
stamped(s, answer2) {
|
|
16600
|
+
if (answer2.person && !s.parsed.reset) this.timestamps.stamp(s.owner, s.policy);
|
|
16601
|
+
}
|
|
16602
|
+
logged(s, cwd, argv) {
|
|
16603
|
+
const { caller, self, target, owner, policy } = s;
|
|
16604
|
+
const type = policy.type === "tty" && !owner.terminal ? "ppid" : policy.type;
|
|
16605
|
+
const tty = owner.terminal?.tty ?? caller.tty ?? "unknown";
|
|
16606
|
+
const command = argv.map(quoteWord).join(" ");
|
|
16607
|
+
(this.o.warn ?? console.warn)(
|
|
16608
|
+
`sudo: ${self.name} : TTY=${tty} ; PWD=${quoteWord(cwd)} ; USER=${target.name} ; TIMESTAMP=${type} ; COMMAND=${command}`
|
|
16609
|
+
);
|
|
16610
|
+
}
|
|
16241
16611
|
async always(self, target, cwd, argv, shown2, grant) {
|
|
16242
16612
|
if (!shown2 || !recordable(cwd, argv)) return { ...grant(), notice: RAN_ONCE };
|
|
16243
16613
|
if (!await this.remember(self, target, cwd, argv)) {
|
|
@@ -16245,7 +16615,7 @@ var Sudo = class {
|
|
|
16245
16615
|
}
|
|
16246
16616
|
return grant();
|
|
16247
16617
|
}
|
|
16248
|
-
listed(self, root, rules) {
|
|
16618
|
+
listed(self, root, rules, defaults2) {
|
|
16249
16619
|
const host = this.o.hostname;
|
|
16250
16620
|
if (root) {
|
|
16251
16621
|
return {
|
|
@@ -16258,11 +16628,20 @@ var Sudo = class {
|
|
|
16258
16628
|
return failed2("EPERM", `User ${self.name} is not allowed to run sudo on ${host}.`);
|
|
16259
16629
|
}
|
|
16260
16630
|
const lines2 = rules.map(described).join("\n");
|
|
16261
|
-
|
|
16631
|
+
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
16632
|
+
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
16633
|
+
${params}
|
|
16634
|
+
|
|
16635
|
+
`;
|
|
16636
|
+
return {
|
|
16637
|
+
list: `${matching}User ${self.name} may run the following commands on ${host}:
|
|
16262
16638
|
${lines2}
|
|
16263
|
-
`
|
|
16639
|
+
`
|
|
16640
|
+
};
|
|
16264
16641
|
}
|
|
16265
|
-
async ask(
|
|
16642
|
+
async ask(s, asked) {
|
|
16643
|
+
const { caller, self, target, signal: requester } = s;
|
|
16644
|
+
const { argv, path, cwd } = asked;
|
|
16266
16645
|
const { approve } = this.o;
|
|
16267
16646
|
if (!approve || requester.aborted) return failed2("EACCES", DENIED2);
|
|
16268
16647
|
const createdAt = Date.now();
|
|
@@ -16281,9 +16660,11 @@ ${lines2}
|
|
|
16281
16660
|
...caller.tty ? { tty: caller.tty } : {},
|
|
16282
16661
|
createdAt,
|
|
16283
16662
|
expiresAt: createdAt + timeout,
|
|
16284
|
-
always: recordable(cwd, [path, ...argv]),
|
|
16663
|
+
always: asked.validate ? false : recordable(cwd, [path, ...argv]),
|
|
16285
16664
|
stdin: caller.stdin,
|
|
16286
|
-
rootOnly: await this.rootOnly(cwd, argv.slice(1))
|
|
16665
|
+
rootOnly: asked.validate ? {} : await this.rootOnly(cwd, argv.slice(1)),
|
|
16666
|
+
...asked.validate ? { validate: true } : {},
|
|
16667
|
+
timestamp: true
|
|
16287
16668
|
};
|
|
16288
16669
|
const control = new AbortController();
|
|
16289
16670
|
const timer = setTimeout(() => control.abort("expired"), timeout);
|
|
@@ -16293,9 +16674,8 @@ ${lines2}
|
|
|
16293
16674
|
try {
|
|
16294
16675
|
const answer2 = await answerOf(approve, req, control.signal);
|
|
16295
16676
|
if (answer2 === ABORTED) return ended();
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
}
|
|
16677
|
+
const approved = approvalOf(answer2);
|
|
16678
|
+
if (approved) return control.signal.aborted ? ended() : approved;
|
|
16299
16679
|
return failed2("EACCES", denialText(answer2));
|
|
16300
16680
|
} finally {
|
|
16301
16681
|
clearTimeout(timer);
|
|
@@ -16305,7 +16685,7 @@ ${lines2}
|
|
|
16305
16685
|
async remember(self, target, cwd, argv) {
|
|
16306
16686
|
const { fs } = this.o;
|
|
16307
16687
|
const path = `${SUDOERS_D}/${self.name}`;
|
|
16308
|
-
if (!
|
|
16688
|
+
if (!NAME3.test(self.name)) return false;
|
|
16309
16689
|
try {
|
|
16310
16690
|
await this.provision();
|
|
16311
16691
|
if (!await this.trusted(SUDOERS_D)) return false;
|
|
@@ -17674,8 +18054,8 @@ function sameCert(a, b) {
|
|
|
17674
18054
|
if (!a || !b) return a === b;
|
|
17675
18055
|
return a.cert.length === b.cert.length && a.cert.every((byte, i) => byte === b.cert[i]);
|
|
17676
18056
|
}
|
|
17677
|
-
function winner(current, expected,
|
|
17678
|
-
return sameCert(current, expected) ?
|
|
18057
|
+
function winner(current, expected, record3) {
|
|
18058
|
+
return sameCert(current, expected) ? record3 : current;
|
|
17679
18059
|
}
|
|
17680
18060
|
var DAY = 24 * 60 * 60 * 1e3;
|
|
17681
18061
|
var CA_LIFETIME = 3650 * DAY;
|
|
@@ -17692,14 +18072,14 @@ function validLeafName(host) {
|
|
|
17692
18072
|
}
|
|
17693
18073
|
var RealmCa = class _RealmCa {
|
|
17694
18074
|
record;
|
|
17695
|
-
constructor(
|
|
17696
|
-
this.record =
|
|
18075
|
+
constructor(record3) {
|
|
18076
|
+
this.record = record3;
|
|
17697
18077
|
}
|
|
17698
18078
|
static async open(owner, store, now = Date.now()) {
|
|
17699
18079
|
const stored = await store.get(owner).catch(() => void 0);
|
|
17700
18080
|
if (stored && stored.notAfter - now > CA_RENEW_BEFORE) return new _RealmCa(stored);
|
|
17701
|
-
const
|
|
17702
|
-
return new _RealmCa(await store.swap(owner, stored,
|
|
18081
|
+
const record3 = await _RealmCa.create(owner, now);
|
|
18082
|
+
return new _RealmCa(await store.swap(owner, stored, record3));
|
|
17703
18083
|
}
|
|
17704
18084
|
static async create(owner, now) {
|
|
17705
18085
|
const pair = await crypto.subtle.generateKey(ECDSA, false, ["sign", "verify"]);
|
|
@@ -17925,8 +18305,8 @@ function memoryCaStore() {
|
|
|
17925
18305
|
const records = /* @__PURE__ */ new Map();
|
|
17926
18306
|
return {
|
|
17927
18307
|
get: async (owner) => records.get(owner),
|
|
17928
|
-
swap: async (owner, expected,
|
|
17929
|
-
const kept = winner(records.get(owner), expected,
|
|
18308
|
+
swap: async (owner, expected, record3) => {
|
|
18309
|
+
const kept = winner(records.get(owner), expected, record3);
|
|
17930
18310
|
records.set(owner, kept);
|
|
17931
18311
|
return kept;
|
|
17932
18312
|
}
|
|
@@ -19852,7 +20232,7 @@ function replaced(text, values) {
|
|
|
19852
20232
|
for (const [ph, value2] of Object.entries(values)) out = out.split(ph).join(binary(value2));
|
|
19853
20233
|
return out;
|
|
19854
20234
|
}
|
|
19855
|
-
function
|
|
20235
|
+
function usable2(values) {
|
|
19856
20236
|
const out = {};
|
|
19857
20237
|
for (const [ph, value2] of Object.entries(values ?? {}))
|
|
19858
20238
|
if (encoder8.encode(value2).length >= MIN_VALUE) out[ph] = value2;
|
|
@@ -19867,7 +20247,7 @@ async function substituteHead(head, ask) {
|
|
|
19867
20247
|
if (!request || !found || found.length !== 1) return void 0;
|
|
19868
20248
|
const at = found[0];
|
|
19869
20249
|
if (at.placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
19870
|
-
const values =
|
|
20250
|
+
const values = usable2(
|
|
19871
20251
|
await ask(`header:${at.name}`, at.placeholders, {
|
|
19872
20252
|
method: request[1],
|
|
19873
20253
|
path: request[2]
|
|
@@ -20177,7 +20557,7 @@ async function cleartext(message3, asks) {
|
|
|
20177
20557
|
const placeholders = placeholdersIn(password);
|
|
20178
20558
|
if (placeholders.length === 0 || placeholders.length > SESSION_PLACEHOLDERS)
|
|
20179
20559
|
return { bytes: message3, done: true };
|
|
20180
|
-
const values =
|
|
20560
|
+
const values = usable2(await asks.cleartext(placeholders));
|
|
20181
20561
|
if (Object.keys(values).length === 0) return { bytes: message3, done: true };
|
|
20182
20562
|
return { bytes: frame3(112, `${replaced(password, values)}\0`), values, done: true };
|
|
20183
20563
|
}
|
|
@@ -20242,7 +20622,7 @@ var PgClient = class {
|
|
|
20242
20622
|
const x = got.exchange;
|
|
20243
20623
|
const binding = this.header === END_POINT ? this.server.binding : void 0;
|
|
20244
20624
|
const serverFinal = `c=${channelOf(this.header, binding)},r=${got.nonce}`;
|
|
20245
|
-
const values =
|
|
20625
|
+
const values = usable2(
|
|
20246
20626
|
await this.asks.scram({
|
|
20247
20627
|
clientFirstBare: x.clientFirstBare,
|
|
20248
20628
|
serverFirst: x.serverFirst,
|
|
@@ -20291,7 +20671,7 @@ function decoded(b642) {
|
|
|
20291
20671
|
async function swapped(text, position, ask) {
|
|
20292
20672
|
const placeholders = placeholdersIn(text);
|
|
20293
20673
|
if (placeholders.length === 0 || placeholders.length > SESSION_PLACEHOLDERS) return void 0;
|
|
20294
|
-
const values =
|
|
20674
|
+
const values = usable2(await ask(position, placeholders));
|
|
20295
20675
|
if (Object.keys(values).length === 0) return void 0;
|
|
20296
20676
|
return { text: replaced(text, values), values };
|
|
20297
20677
|
}
|
|
@@ -26495,7 +26875,7 @@ function header2(id2, flags2, counts) {
|
|
|
26495
26875
|
function opt() {
|
|
26496
26876
|
return [0, 0, TYPE_OPT, UDP_EDNS >> 8, UDP_EDNS & 255, 0, 0, 0, 0, 0, 0];
|
|
26497
26877
|
}
|
|
26498
|
-
function
|
|
26878
|
+
function record2(type, rdata) {
|
|
26499
26879
|
const ttl = DNS_STUB_TTL;
|
|
26500
26880
|
return [
|
|
26501
26881
|
192,
|
|
@@ -26603,14 +26983,14 @@ var DnsStub = class {
|
|
|
26603
26983
|
const address = reverseAddress(q.name);
|
|
26604
26984
|
const name2 = address !== void 0 ? await this.reverse(address) : void 0;
|
|
26605
26985
|
if (name2 !== void 0)
|
|
26606
|
-
return answerReply(query, q, NOERROR, [
|
|
26986
|
+
return answerReply(query, q, NOERROR, [record2(TYPE_PTR, encodeName(name2))], true);
|
|
26607
26987
|
return this.forward(query, q);
|
|
26608
26988
|
}
|
|
26609
26989
|
if (q.type === TYPE_A || q.type === TYPE_AAAA) {
|
|
26610
26990
|
const addresses = q.name === "" ? [] : await this.lookup(q.name);
|
|
26611
26991
|
const answers2 = addresses.flatMap((a) => {
|
|
26612
26992
|
const data = addressData(a, q.type);
|
|
26613
|
-
return data ? [
|
|
26993
|
+
return data ? [record2(q.type, data)] : [];
|
|
26614
26994
|
});
|
|
26615
26995
|
if (answers2.length > 0 || local(q.name) || await this.known(q.name)) {
|
|
26616
26996
|
const rcode = addresses.length > 0 ? NOERROR : NXDOMAIN;
|
|
@@ -28938,8 +29318,8 @@ var OpfsFs = class {
|
|
|
28938
29318
|
const [entry] = await this.meta.get([target]);
|
|
28939
29319
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
|
|
28940
29320
|
const kept = fresh3 ? cached : this.files.get(target);
|
|
28941
|
-
const
|
|
28942
|
-
return [
|
|
29321
|
+
const usable3 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
|
|
29322
|
+
return [usable3 ? kept : void 0, target, true];
|
|
28943
29323
|
}
|
|
28944
29324
|
async writeAt(handle, target, path, offset, bytes) {
|
|
28945
29325
|
await this.counting(target);
|
|
@@ -29741,8 +30121,8 @@ var OpfsFs = class {
|
|
|
29741
30121
|
renamingDirs = /* @__PURE__ */ new Set();
|
|
29742
30122
|
intents = 0;
|
|
29743
30123
|
async intend(rename, at = `${RENAMES2}/${crypto.randomUUID()}`) {
|
|
29744
|
-
const
|
|
29745
|
-
await this.meta.update(at, () => ({ path: at, rename:
|
|
30124
|
+
const record3 = { ...rename, at: Date.now() * 1e3 + this.intents++ % 1e3 };
|
|
30125
|
+
await this.meta.update(at, () => ({ path: at, rename: record3 }));
|
|
29746
30126
|
return at;
|
|
29747
30127
|
}
|
|
29748
30128
|
async rollBack(from, to, intents) {
|
|
@@ -31935,6 +32315,7 @@ var SUDO_SOURCE = [
|
|
|
31935
32315
|
" await say(1, answer.list);",
|
|
31936
32316
|
" return 0;",
|
|
31937
32317
|
" }",
|
|
32318
|
+
" if (answer.done === true) return 0;",
|
|
31938
32319
|
" if (answer.run !== undefined) {",
|
|
31939
32320
|
" try {",
|
|
31940
32321
|
" pid = (await ctx.spawn({ argv: answer.run, pgid: 0 })).pid;",
|
|
@@ -32570,6 +32951,7 @@ var Launcher = class {
|
|
|
32570
32951
|
);
|
|
32571
32952
|
}
|
|
32572
32953
|
this.users = new UserDb(this.fs, this.owners);
|
|
32954
|
+
const { sudoClock } = options;
|
|
32573
32955
|
this.sudoer = new Sudo({
|
|
32574
32956
|
fs: this.fs,
|
|
32575
32957
|
users: this.users,
|
|
@@ -32579,7 +32961,8 @@ var Launcher = class {
|
|
|
32579
32961
|
pnpmHome: this.pnpmHome,
|
|
32580
32962
|
backing: (path, cwd) => this.backing(path, cwd),
|
|
32581
32963
|
approve: options.approve,
|
|
32582
|
-
timeoutMs: options.sudoTimeoutMs
|
|
32964
|
+
timeoutMs: options.sudoTimeoutMs,
|
|
32965
|
+
now: sudoClock ? () => sudoClock.now() : void 0
|
|
32583
32966
|
});
|
|
32584
32967
|
this.cron = this.cronService(options.cron);
|
|
32585
32968
|
void this.unlinked.sweep("/");
|
|
@@ -33150,8 +33533,8 @@ var Launcher = class {
|
|
|
33150
33533
|
const caller = this.jobs.shown(ppid);
|
|
33151
33534
|
const named = typeof req.argv0 === "string" && !planned.script && !planned.target.fixed;
|
|
33152
33535
|
const argv = named ? [req.argv0, ...req.argv.slice(1)] : req.argv;
|
|
33153
|
-
const
|
|
33154
|
-
if (!
|
|
33536
|
+
const exact2 = booked?.pid === caller && req.file === booked.path && await physicalPathAsync(this.base, req.cwd) === booked.cwd && await argvDigest(argv) === booked.argv;
|
|
33537
|
+
if (!exact2) throw new SpawnError("EACCES");
|
|
33155
33538
|
const { requested: path, requestedCwd: cwd, path: resolved2 } = booked;
|
|
33156
33539
|
const pid = this.nextPid++;
|
|
33157
33540
|
const ids = { pid: req.exec ? caller : pid, ppid: caller };
|
|
@@ -33985,11 +34368,26 @@ var Launcher = class {
|
|
|
33985
34368
|
pid: this.jobs.shown(pid),
|
|
33986
34369
|
tags: this.tagsOf(pid),
|
|
33987
34370
|
tty: this.described.get(pid)?.tty,
|
|
34371
|
+
...this.timestampOwner(pid),
|
|
33988
34372
|
stdin,
|
|
33989
34373
|
report
|
|
33990
34374
|
};
|
|
33991
34375
|
return this.sudoer.request(caller, call, signal);
|
|
33992
34376
|
}
|
|
34377
|
+
timestampOwner(pid) {
|
|
34378
|
+
const sid = this.jobs.sidOf(pid);
|
|
34379
|
+
const tty = this.jobs.controllingTerminal(pid)?.name;
|
|
34380
|
+
const leader = sid === void 0 ? void 0 : this.startedOf(sid);
|
|
34381
|
+
const host = this.jobs.shownParent(pid) ?? this.jobs.shown(pid);
|
|
34382
|
+
const started = this.startedOf(host);
|
|
34383
|
+
return {
|
|
34384
|
+
...tty && sid !== void 0 && leader !== void 0 ? { terminal: { tty, sid, started: leader } } : {},
|
|
34385
|
+
...started === void 0 ? {} : { parent: { pid: host, started } }
|
|
34386
|
+
};
|
|
34387
|
+
}
|
|
34388
|
+
startedOf(shown2) {
|
|
34389
|
+
return this.running(shown2) ? this.described.get(shown2)?.started : void 0;
|
|
34390
|
+
}
|
|
33993
34391
|
visudo(pid, call) {
|
|
33994
34392
|
return this.sudoer.check(this.credOf(pid), call);
|
|
33995
34393
|
}
|
|
@@ -34499,9 +34897,9 @@ var Launcher = class {
|
|
|
34499
34897
|
}
|
|
34500
34898
|
pnpmReady() {
|
|
34501
34899
|
if (this.pnpmMade !== void 0) return this.pnpmMade;
|
|
34502
|
-
const
|
|
34900
|
+
const epoch2 = this.pnpmEpoch;
|
|
34503
34901
|
const made2 = this.base.mkdir(this.pnpmHome, { recursive: true }).then(() => this.owners ? this.stampLayout() : void 0).catch(() => {
|
|
34504
|
-
if (this.pnpmEpoch ===
|
|
34902
|
+
if (this.pnpmEpoch === epoch2) this.pnpmMade = void 0;
|
|
34505
34903
|
});
|
|
34506
34904
|
this.pnpmMade = made2;
|
|
34507
34905
|
return made2;
|
|
@@ -37676,6 +38074,7 @@ async function createNodeKernel(options = {}) {
|
|
|
37676
38074
|
...options.clipboard ? { clipboard: options.clipboard } : {},
|
|
37677
38075
|
...options.requests ? { embedder: checkedRequests(options.requests) } : {},
|
|
37678
38076
|
...options.approve ? { approve: options.approve } : {},
|
|
38077
|
+
...options.sudo?.clock ? { sudoClock: options.sudo.clock } : {},
|
|
37679
38078
|
...options.wasmMemories ? { wasmMemories: options.wasmMemories } : {},
|
|
37680
38079
|
cron: { lock: cronLock(root), ...options.cron?.clock ? { clock: options.cron.clock } : {} }
|
|
37681
38080
|
});
|