@ai-ecoverse/slicc-kernel 1.178.1 → 1.178.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +255 -113
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.cjs +1 -1
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +272 -130
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/kernel/children.d.ts +5 -0
- package/dist/types/launcher.d.ts +3 -0
- package/dist/types/node.d.ts +1 -1
- package/dist/types/sudo-timestamp.d.ts +4 -2
- package/dist/types/sudo.d.ts +12 -1
- package/dist/types/sudoers.d.ts +3 -1
- package/package.json +3 -3
package/dist/kernel-worker.js
CHANGED
|
@@ -1339,8 +1339,8 @@ var RemoteWatch = class {
|
|
|
1339
1339
|
const { held = [], pending = [] } = await this.locks.query();
|
|
1340
1340
|
if (![...held, ...pending].some((lock) => lock.name === name2)) {
|
|
1341
1341
|
this.mourned.delete(peer);
|
|
1342
|
-
const
|
|
1343
|
-
if (tries > 1 &&
|
|
1342
|
+
const live2 = this.peers.has(peer) || this.flushes.has(peer);
|
|
1343
|
+
if (tries > 1 && live2) {
|
|
1344
1344
|
setTimeout(() => void this.mourn(peer, tries - 1), MOURN_RETRY_MS);
|
|
1345
1345
|
} else this.flushedAt(peer);
|
|
1346
1346
|
return;
|
|
@@ -1830,11 +1830,11 @@ async function positioned(handle, op) {
|
|
|
1830
1830
|
}
|
|
1831
1831
|
return;
|
|
1832
1832
|
}
|
|
1833
|
-
const
|
|
1833
|
+
const writable3 = await handle.createWritable({ keepExistingData: true });
|
|
1834
1834
|
try {
|
|
1835
1835
|
await op({
|
|
1836
1836
|
write: async (at, bytes) => {
|
|
1837
|
-
await
|
|
1837
|
+
await writable3.write({
|
|
1838
1838
|
type: "write",
|
|
1839
1839
|
position: at,
|
|
1840
1840
|
data: bytes
|
|
@@ -1842,15 +1842,15 @@ async function positioned(handle, op) {
|
|
|
1842
1842
|
extent.wrote(at + bytes.length);
|
|
1843
1843
|
},
|
|
1844
1844
|
truncate: async (n) => {
|
|
1845
|
-
await
|
|
1845
|
+
await writable3.truncate(n);
|
|
1846
1846
|
extent.cut(n);
|
|
1847
1847
|
}
|
|
1848
1848
|
});
|
|
1849
1849
|
} catch (err) {
|
|
1850
|
-
await
|
|
1850
|
+
await writable3.abort();
|
|
1851
1851
|
throw err;
|
|
1852
1852
|
}
|
|
1853
|
-
await
|
|
1853
|
+
await writable3.close();
|
|
1854
1854
|
if (extent.big()) extent.check((await handle.getFile()).size);
|
|
1855
1855
|
}
|
|
1856
1856
|
function syncAccessHandles(scope = globalThis) {
|
|
@@ -2936,21 +2936,21 @@ var OpfsFs = class {
|
|
|
2936
2936
|
}
|
|
2937
2937
|
async writeSession(path, truncate) {
|
|
2938
2938
|
const [handle, , target] = await this.fileFor(path);
|
|
2939
|
-
const
|
|
2939
|
+
const writable3 = await this.retrying(
|
|
2940
2940
|
path,
|
|
2941
2941
|
() => this.io(handle.createWritable({ keepExistingData: !truncate })),
|
|
2942
2942
|
true
|
|
2943
2943
|
);
|
|
2944
2944
|
return {
|
|
2945
|
-
write: (at, bytes) =>
|
|
2945
|
+
write: (at, bytes) => writable3.write({ type: "write", position: at, data: bytes }).catch((err) => {
|
|
2946
2946
|
throw translate(err, path, "write", false);
|
|
2947
2947
|
}),
|
|
2948
|
-
truncate: (size) =>
|
|
2948
|
+
truncate: (size) => writable3.truncate(size).catch((err) => {
|
|
2949
2949
|
throw translate(err, path, "write", false);
|
|
2950
2950
|
}),
|
|
2951
2951
|
close: async () => {
|
|
2952
2952
|
this.wrote(handle);
|
|
2953
|
-
await
|
|
2953
|
+
await writable3.close().catch((err) => {
|
|
2954
2954
|
this.wrote(handle);
|
|
2955
2955
|
throw translate(err, path, "commit", false);
|
|
2956
2956
|
});
|
|
@@ -3002,19 +3002,19 @@ var OpfsFs = class {
|
|
|
3002
3002
|
const [file, existed, target] = await this.fileFor(path, owner?.existing !== true);
|
|
3003
3003
|
if (!existed) await this.stampNew(target, owner, false);
|
|
3004
3004
|
if (!existed && content.length === 0) return;
|
|
3005
|
-
let
|
|
3005
|
+
let writable3;
|
|
3006
3006
|
this.wrote(file);
|
|
3007
3007
|
try {
|
|
3008
|
-
|
|
3009
|
-
await
|
|
3010
|
-
await
|
|
3008
|
+
writable3 = await this.io(file.createWritable());
|
|
3009
|
+
await writable3.write(content);
|
|
3010
|
+
await writable3.close();
|
|
3011
3011
|
if (typeof content !== "string" && content.length >= CHECKED_BYTES) {
|
|
3012
3012
|
const extent = new Extent();
|
|
3013
3013
|
extent.cut(content.length);
|
|
3014
3014
|
extent.check((await file.getFile()).size);
|
|
3015
3015
|
}
|
|
3016
3016
|
} catch (err) {
|
|
3017
|
-
await
|
|
3017
|
+
await writable3?.abort().catch(() => void 0);
|
|
3018
3018
|
throw translate(err, path, "commit", false);
|
|
3019
3019
|
} finally {
|
|
3020
3020
|
this.wrote(file);
|
|
@@ -3739,7 +3739,7 @@ var OpfsFs = class {
|
|
|
3739
3739
|
created4 = true;
|
|
3740
3740
|
}
|
|
3741
3741
|
let target = destination;
|
|
3742
|
-
let
|
|
3742
|
+
let writable3;
|
|
3743
3743
|
let handle;
|
|
3744
3744
|
try {
|
|
3745
3745
|
const kept = created4 ? this.files.get(destination) : void 0;
|
|
@@ -3758,11 +3758,11 @@ var OpfsFs = class {
|
|
|
3758
3758
|
if (entry?.fifo || entry?.socket) throw fsError("ENXIO", to);
|
|
3759
3759
|
}
|
|
3760
3760
|
this.wrote(handle);
|
|
3761
|
-
|
|
3762
|
-
await
|
|
3763
|
-
await
|
|
3761
|
+
writable3 = await handle.createWritable();
|
|
3762
|
+
await writable3.write(file);
|
|
3763
|
+
await writable3.close();
|
|
3764
3764
|
} catch (err) {
|
|
3765
|
-
await
|
|
3765
|
+
await writable3?.abort().catch(() => void 0);
|
|
3766
3766
|
if (created4) await this.rm(target, { file: true }).catch(() => void 0);
|
|
3767
3767
|
throw translate(err, to, "commit", false);
|
|
3768
3768
|
} finally {
|
|
@@ -3851,10 +3851,10 @@ var OpfsFs = class {
|
|
|
3851
3851
|
}
|
|
3852
3852
|
if (handle.kind === "file") {
|
|
3853
3853
|
const made2 = await this.io(dir.getFileHandle(name2, { create: true }));
|
|
3854
|
-
const
|
|
3854
|
+
const writable3 = await this.io(made2.createWritable());
|
|
3855
3855
|
const file = await this.io(handle.getFile());
|
|
3856
|
-
await
|
|
3857
|
-
await
|
|
3856
|
+
await writable3.write(file);
|
|
3857
|
+
await writable3.close();
|
|
3858
3858
|
copies?.set(rest2, { before: copies.get(rest2)?.before ?? file.lastModified, made: made2 });
|
|
3859
3859
|
return;
|
|
3860
3860
|
}
|
|
@@ -8777,7 +8777,7 @@ function satisfies(version, range2) {
|
|
|
8777
8777
|
}
|
|
8778
8778
|
|
|
8779
8779
|
// src/version.ts
|
|
8780
|
-
var KERNEL_VERSION = true ? "1.178.
|
|
8780
|
+
var KERNEL_VERSION = true ? "1.178.3" : "0.0.0-dev";
|
|
8781
8781
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8782
8782
|
function procVersion() {
|
|
8783
8783
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10059,9 +10059,9 @@ var UnlinkedKeeper = class {
|
|
|
10059
10059
|
if (kept.whole) return kept.undoing(async () => void 0);
|
|
10060
10060
|
await kept.undoing(
|
|
10061
10061
|
() => replace(target, async (gated, sealed) => {
|
|
10062
|
-
const
|
|
10063
|
-
if (
|
|
10064
|
-
else await keeper.settle(
|
|
10062
|
+
const pinned2 = await keeper.pin(target, gated, sealed);
|
|
10063
|
+
if (pinned2.moved.has(target)) pinned2.commit();
|
|
10064
|
+
else await keeper.settle(pinned2, () => fs.rm(path, options));
|
|
10065
10065
|
})
|
|
10066
10066
|
);
|
|
10067
10067
|
},
|
|
@@ -10074,8 +10074,8 @@ var UnlinkedKeeper = class {
|
|
|
10074
10074
|
() => keeper.marks.around(
|
|
10075
10075
|
[source],
|
|
10076
10076
|
() => replace(target, async (gated, sealed) => {
|
|
10077
|
-
const
|
|
10078
|
-
await keeper.settle(
|
|
10077
|
+
const pinned2 = await keeper.pin(target, gated, sealed);
|
|
10078
|
+
await keeper.settle(pinned2, () => fs.rename(from, to));
|
|
10079
10079
|
})
|
|
10080
10080
|
)
|
|
10081
10081
|
);
|
|
@@ -10110,14 +10110,14 @@ var UnlinkedKeeper = class {
|
|
|
10110
10110
|
}
|
|
10111
10111
|
return false;
|
|
10112
10112
|
}
|
|
10113
|
-
async settle(
|
|
10113
|
+
async settle(pinned2, op) {
|
|
10114
10114
|
try {
|
|
10115
10115
|
await op();
|
|
10116
10116
|
} catch (err) {
|
|
10117
|
-
await
|
|
10117
|
+
await pinned2.rollback();
|
|
10118
10118
|
throw err;
|
|
10119
10119
|
}
|
|
10120
|
-
|
|
10120
|
+
pinned2.commit();
|
|
10121
10121
|
}
|
|
10122
10122
|
heldWithin(target) {
|
|
10123
10123
|
const held = /* @__PURE__ */ new Map();
|
|
@@ -12641,6 +12641,13 @@ var SpawnError = class extends Error {
|
|
|
12641
12641
|
this.code = code;
|
|
12642
12642
|
}
|
|
12643
12643
|
};
|
|
12644
|
+
var ChangedError = class extends SpawnError {
|
|
12645
|
+
notice;
|
|
12646
|
+
constructor(notice) {
|
|
12647
|
+
super("EPERM");
|
|
12648
|
+
this.notice = notice;
|
|
12649
|
+
}
|
|
12650
|
+
};
|
|
12644
12651
|
function interrupted(signal) {
|
|
12645
12652
|
const { promise, reject } = Promise.withResolvers();
|
|
12646
12653
|
const fail2 = () => reject(new KernelError("EINTR"));
|
|
@@ -17082,11 +17089,11 @@ function systemFile(name2, listing, now, memory) {
|
|
|
17082
17089
|
listing.processes.reduce((sum2, p) => sum2 + p.memory, 0)
|
|
17083
17090
|
);
|
|
17084
17091
|
}
|
|
17085
|
-
const
|
|
17092
|
+
const live2 = listing.processes.filter((p) => p.state !== "Z");
|
|
17086
17093
|
if (name2 === "loadavg") {
|
|
17087
17094
|
const last = Math.max(0, ...listing.processes.map((p) => p.pid));
|
|
17088
17095
|
const [one, five, fifteen] = (listing.load ?? [0, 0, 0]).map((n) => n.toFixed(2));
|
|
17089
|
-
return `${one} ${five} ${fifteen} 1/${
|
|
17096
|
+
return `${one} ${five} ${fifteen} 1/${live2.length} ${last}
|
|
17090
17097
|
`;
|
|
17091
17098
|
}
|
|
17092
17099
|
const sum = (key) => listing.cpu?.[key] ?? listing.processes.reduce((n, p) => n + (p[key] ?? 0), 0);
|
|
@@ -19575,30 +19582,49 @@ function timestampKey(owner, type) {
|
|
|
19575
19582
|
}
|
|
19576
19583
|
return parent ? JSON.stringify(["ppid", parent.pid, parent.started]) : void 0;
|
|
19577
19584
|
}
|
|
19585
|
+
function boundKey(bound3) {
|
|
19586
|
+
return JSON.stringify(
|
|
19587
|
+
Object.keys(bound3).sort().map((path) => [path, bound3[path]])
|
|
19588
|
+
);
|
|
19589
|
+
}
|
|
19590
|
+
function writable2(bound3) {
|
|
19591
|
+
return bound3 !== void 0 && Object.keys(bound3).length > 0;
|
|
19592
|
+
}
|
|
19593
|
+
function live(stamp, policy, now) {
|
|
19594
|
+
return stamp.at <= now && (policy.minutes < 0 || now - stamp.at < policy.minutes * MINUTE_MS2);
|
|
19595
|
+
}
|
|
19578
19596
|
var Timestamps = class {
|
|
19579
19597
|
users = /* @__PURE__ */ new Map();
|
|
19580
19598
|
now;
|
|
19581
19599
|
constructor(now) {
|
|
19582
19600
|
this.now = now;
|
|
19583
19601
|
}
|
|
19584
|
-
current(owner, policy) {
|
|
19602
|
+
current(owner, policy, bound3) {
|
|
19585
19603
|
if (policy.minutes === 0) return false;
|
|
19586
19604
|
const key = timestampKey(owner, policy.type);
|
|
19587
|
-
const
|
|
19588
|
-
if (
|
|
19605
|
+
const stamp = key === void 0 ? void 0 : this.users.get(owner.uid)?.get(key);
|
|
19606
|
+
if (stamp === void 0) return false;
|
|
19589
19607
|
const now = this.now();
|
|
19590
|
-
|
|
19608
|
+
if (!live(stamp, policy, now)) {
|
|
19609
|
+
this.drop(owner.uid, key);
|
|
19610
|
+
return false;
|
|
19611
|
+
}
|
|
19612
|
+
if (!writable2(bound3)) return true;
|
|
19613
|
+
return stamp.covered.has(boundKey(bound3));
|
|
19591
19614
|
}
|
|
19592
|
-
stamp(owner, policy) {
|
|
19615
|
+
stamp(owner, policy, bound3) {
|
|
19593
19616
|
const key = policy.minutes === 0 ? void 0 : timestampKey(owner, policy.type);
|
|
19594
19617
|
if (key === void 0) return false;
|
|
19595
19618
|
const now = this.now();
|
|
19596
19619
|
const records = this.users.get(owner.uid) ?? /* @__PURE__ */ new Map();
|
|
19597
19620
|
this.users.set(owner.uid, records);
|
|
19621
|
+
const prior = records.get(key);
|
|
19622
|
+
const covered = prior !== void 0 && live(prior, policy, now) ? prior.covered : /* @__PURE__ */ new Set();
|
|
19623
|
+
if (writable2(bound3)) covered.add(boundKey(bound3));
|
|
19598
19624
|
records.delete(key);
|
|
19599
|
-
records.set(key, now);
|
|
19600
|
-
for (const [old,
|
|
19601
|
-
const stale = policy.minutes > 0 && (
|
|
19625
|
+
records.set(key, { at: now, covered });
|
|
19626
|
+
for (const [old, stamp] of records) {
|
|
19627
|
+
const stale = policy.minutes > 0 && !live(stamp, policy, now);
|
|
19602
19628
|
if (stale || records.size > TIMESTAMPS_MAX) records.delete(old);
|
|
19603
19629
|
}
|
|
19604
19630
|
return true;
|
|
@@ -19612,6 +19638,11 @@ var Timestamps = class {
|
|
|
19612
19638
|
remove(uid) {
|
|
19613
19639
|
this.users.delete(uid);
|
|
19614
19640
|
}
|
|
19641
|
+
drop(uid, key) {
|
|
19642
|
+
const records = this.users.get(uid);
|
|
19643
|
+
records?.delete(key);
|
|
19644
|
+
if (records?.size === 0) this.users.delete(uid);
|
|
19645
|
+
}
|
|
19615
19646
|
};
|
|
19616
19647
|
|
|
19617
19648
|
// src/sudoers.ts
|
|
@@ -19620,8 +19651,10 @@ var SUDOERS_D = "/etc/sudoers.d";
|
|
|
19620
19651
|
var NAME4 = /^[a-z_][a-z0-9_.-]*$/;
|
|
19621
19652
|
var WORD = String.raw`(?:'[^']*'|"(?:[^"\\]|\\[\s\S])*"|\\[\s\S]|[^\s'"\\])+`;
|
|
19622
19653
|
var KERNEL_FORM = new RegExp(
|
|
19623
|
-
String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()]*)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(\S+)\s+)?Cmnd(?:\s+(.*))?$`
|
|
19654
|
+
String.raw`^(NOPASSWD\s+)?(\S+)\s+(?:\(([^()]*)\)\s+)?(?:Cwd\s+(${WORD})\s+)?(?:Stdin\s+(\S+)\s+)?((?:Sha256\s+${WORD}\s+)*)Cmnd(?:\s+(.*))?$`
|
|
19624
19655
|
);
|
|
19656
|
+
var SHA256_CLAUSE = new RegExp(String.raw`Sha256\s+(${WORD})`, "g");
|
|
19657
|
+
var HEX = /^[0-9a-f]{64}$/;
|
|
19625
19658
|
var SUDOERS_FORM = /^(\S+)\s+([^\s=]+)\s*=\s*(.*)$/;
|
|
19626
19659
|
var RUNAS = /^\(([^()]*)\)\s*/;
|
|
19627
19660
|
var TAG = /^([A-Z_]+)\s*:\s*/;
|
|
@@ -19630,7 +19663,7 @@ var PLAIN2 = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
|
19630
19663
|
var CONTROL2 = /[\p{Cc}\u2028\u2029]/u;
|
|
19631
19664
|
var CONTROLS = /[\p{Cc}\u2028\u2029]/gu;
|
|
19632
19665
|
var SHOWN_MAX = 60;
|
|
19633
|
-
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] Cmnd <command\u2026|*>";
|
|
19666
|
+
var KERNEL_SYNTAX = "[NOPASSWD] <user|%group|ALL> [(<target>)] [Cwd <dir>] [Stdin none] [Sha256 <file>=<sha256>\u2026] Cmnd <command\u2026|*>";
|
|
19634
19667
|
var SUDOERS_SYNTAX = "<user|%group|ALL> ALL=[(<target>[,<target>\u2026])] [NOPASSWD:] <command\u2026|ALL>";
|
|
19635
19668
|
var EXPECTED = `expected '${KERNEL_SYNTAX}' or '${SUDOERS_SYNTAX}'`;
|
|
19636
19669
|
var TIMESTAMP_TIMEOUT = 5;
|
|
@@ -19797,6 +19830,23 @@ function commandOf2(text, after) {
|
|
|
19797
19830
|
if (only && /\s""$/.test(text)) return { path, args: [] };
|
|
19798
19831
|
return args.length === 0 ? { path } : { path, args };
|
|
19799
19832
|
}
|
|
19833
|
+
function sha256Of(clauses) {
|
|
19834
|
+
const files = {};
|
|
19835
|
+
for (const [, word = ""] of clauses.matchAll(SHA256_CLAUSE)) {
|
|
19836
|
+
const [text = ""] = splitWords(word) ?? [];
|
|
19837
|
+
const at = text.lastIndexOf("=");
|
|
19838
|
+
const path = text.slice(0, at);
|
|
19839
|
+
const hex4 = text.slice(at + 1).toLowerCase();
|
|
19840
|
+
if (!path.startsWith("/") || !HEX.test(hex4)) {
|
|
19841
|
+
return `Sha256 expects <absolute file>=<sha256 in hex>, not ${shown(word)}`;
|
|
19842
|
+
}
|
|
19843
|
+
files[normalizePath(path)] = hex4;
|
|
19844
|
+
}
|
|
19845
|
+
return files;
|
|
19846
|
+
}
|
|
19847
|
+
function sha256Text(files) {
|
|
19848
|
+
return Object.entries(files).map(([path, hex4]) => `Sha256 ${quoteWord(`${path}=${hex4}`)} `).join("");
|
|
19849
|
+
}
|
|
19800
19850
|
function targetError(list) {
|
|
19801
19851
|
if (list.includes(",")) {
|
|
19802
19852
|
return `(${list}) names one user in this form: write one line per user, or use '${SUDOERS_SYNTAX}'`;
|
|
@@ -19804,19 +19854,22 @@ function targetError(list) {
|
|
|
19804
19854
|
return `expected one user name or ALL in (\u2026), not ${shown(`(${list})`)}`;
|
|
19805
19855
|
}
|
|
19806
19856
|
function kernelRules(m) {
|
|
19807
|
-
const [, nopasswd, who = "", target = "root", dir, stdin, rest2 = ""] = m;
|
|
19857
|
+
const [, nopasswd, who = "", target = "root", dir, stdin, sums = "", rest2 = ""] = m;
|
|
19808
19858
|
const bad = whoError(who);
|
|
19809
19859
|
if (bad !== void 0) return bad;
|
|
19810
19860
|
if (target !== "ALL" && !NAME4.test(target)) return targetError(target);
|
|
19811
19861
|
const cwd = dir === void 0 ? void 0 : cwdOf(dir);
|
|
19812
19862
|
if (cwd === "") return `Cwd expects one absolute directory, not ${shown(dir)}`;
|
|
19813
19863
|
if (stdin !== void 0 && stdin !== "none") return `Stdin takes none, not ${shown(stdin)}`;
|
|
19864
|
+
const sha2564 = sha256Of(sums);
|
|
19865
|
+
if (typeof sha2564 === "string") return sha2564;
|
|
19814
19866
|
const rule = {
|
|
19815
19867
|
nopasswd: !!nopasswd,
|
|
19816
19868
|
who,
|
|
19817
19869
|
target,
|
|
19818
19870
|
...cwd !== void 0 ? { cwd } : {},
|
|
19819
|
-
...stdin === "none" ? { stdin: "none" } : {}
|
|
19871
|
+
...stdin === "none" ? { stdin: "none" } : {},
|
|
19872
|
+
...sums === "" ? {} : { sha256: sha2564 }
|
|
19820
19873
|
};
|
|
19821
19874
|
const text = rest2.trim();
|
|
19822
19875
|
if (text === "*") return [{ ...rule, cmnd: "*" }];
|
|
@@ -20069,12 +20122,26 @@ var RAN_ONCE = "sudo: ran once; this command line can't be recorded as always";
|
|
|
20069
20122
|
var ROOT_ONLY_MAX = 64;
|
|
20070
20123
|
var OPERANDS_MAX = 256;
|
|
20071
20124
|
var PATH_MAX = 4096;
|
|
20125
|
+
function userWritable(bound3) {
|
|
20126
|
+
const files = Object.entries(bound3).map(([path, hex4]) => ({ path, sha256: hex4 }));
|
|
20127
|
+
return files.length > 0 ? { userWritable: files } : {};
|
|
20128
|
+
}
|
|
20072
20129
|
function stdinMatches(rule, stdin) {
|
|
20073
20130
|
return rule.stdin === void 0 || stdin === "tty" || stdin === "none";
|
|
20074
20131
|
}
|
|
20075
20132
|
function recordable(cwd, argv) {
|
|
20076
20133
|
return !hasControl(cwd) && !argv.some(hasControl);
|
|
20077
20134
|
}
|
|
20135
|
+
function changedText(file) {
|
|
20136
|
+
return `sudo: ${file} changed since it was approved`;
|
|
20137
|
+
}
|
|
20138
|
+
function changedFile(approved, now) {
|
|
20139
|
+
const files = /* @__PURE__ */ new Set([...Object.keys(approved), ...Object.keys(now)]);
|
|
20140
|
+
return [...files].find((file) => approved[file] !== now[file]);
|
|
20141
|
+
}
|
|
20142
|
+
function pinned(rule, bound3) {
|
|
20143
|
+
return rule.sha256 === void 0 || changedFile(rule.sha256, bound3) === void 0;
|
|
20144
|
+
}
|
|
20078
20145
|
var VISUDO_FILES_MAX = 64;
|
|
20079
20146
|
var VISUDO_TEXT_MAX = 1024 * 1024;
|
|
20080
20147
|
function visudoCall(req) {
|
|
@@ -20205,11 +20272,13 @@ function usable(parsed2) {
|
|
|
20205
20272
|
function policyOf(settings) {
|
|
20206
20273
|
return { type: settings.timestampType, minutes: settings.timestampTimeout };
|
|
20207
20274
|
}
|
|
20208
|
-
function described(rule) {
|
|
20275
|
+
function described(rule, writable3) {
|
|
20209
20276
|
const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
|
|
20210
20277
|
const cwd = rule.cwd === void 0 ? "" : `CWD=${quoteWord(rule.cwd)} `;
|
|
20211
20278
|
const stdin = rule.stdin === void 0 ? "" : "STDIN=none ";
|
|
20212
|
-
|
|
20279
|
+
const sums = Object.entries(rule.sha256 ?? {}).map(([path, hex4]) => `SHA256=${quoteWord(path)}:${hex4} `).join("");
|
|
20280
|
+
const flag = writable3 ? " # user-writable command" : "";
|
|
20281
|
+
return ` (${rule.target}) ${cwd}${stdin}${sums}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}${flag}`;
|
|
20213
20282
|
}
|
|
20214
20283
|
var Sudo = class {
|
|
20215
20284
|
o;
|
|
@@ -20400,15 +20469,31 @@ var Sudo = class {
|
|
|
20400
20469
|
if (walked.denied !== void 0 || walked.missing) return false;
|
|
20401
20470
|
return (await this.o.fs.stat(cwd).catch(() => void 0))?.isDirectory === true;
|
|
20402
20471
|
}
|
|
20403
|
-
async
|
|
20404
|
-
const
|
|
20405
|
-
|
|
20406
|
-
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
|
|
20472
|
+
async real(file) {
|
|
20473
|
+
const { fs } = this.o;
|
|
20474
|
+
return await fs.realpath?.(file, true).catch(() => file) ?? file;
|
|
20475
|
+
}
|
|
20476
|
+
async digests(files) {
|
|
20477
|
+
const { fs } = this.o;
|
|
20478
|
+
const found = {};
|
|
20479
|
+
for (const file of new Set(files)) {
|
|
20480
|
+
if (await this.trusted(file)) continue;
|
|
20481
|
+
const real = await this.real(file);
|
|
20482
|
+
found[real] = await fs.readFileBuffer(real).then(sha256, () => "");
|
|
20410
20483
|
}
|
|
20411
|
-
return
|
|
20484
|
+
return found;
|
|
20485
|
+
}
|
|
20486
|
+
async bound(path, cwd) {
|
|
20487
|
+
const files = this.o.backing ? await this.o.backing(path, cwd) : [];
|
|
20488
|
+
const bound3 = files && await this.digests([path, ...files]);
|
|
20489
|
+
return bound3 && !Object.values(bound3).includes("") ? bound3 : void 0;
|
|
20490
|
+
}
|
|
20491
|
+
async writable(rule) {
|
|
20492
|
+
if (rule.cmnd === "*") return false;
|
|
20493
|
+
const { path } = rule.cmnd;
|
|
20494
|
+
const bound3 = await this.bound(path, "/");
|
|
20495
|
+
if (bound3) return Object.keys(bound3).length > 0;
|
|
20496
|
+
return !await this.trusted(await this.existing(path));
|
|
20412
20497
|
}
|
|
20413
20498
|
async locate(word, cwd) {
|
|
20414
20499
|
const { fs } = this.o;
|
|
@@ -20453,7 +20538,7 @@ var Sudo = class {
|
|
|
20453
20538
|
const root = cred.euid === 0;
|
|
20454
20539
|
const rules = root ? [] : policy.rules;
|
|
20455
20540
|
const mine = rules.filter((rule) => applies(rule.who));
|
|
20456
|
-
if (parsed2.list) return this.listed(self, root, mine, settings.matching);
|
|
20541
|
+
if (parsed2.list) return await this.listed(self, root, mine, settings.matching);
|
|
20457
20542
|
const session = { caller, self, target, owner, policy: policyOf(settings), parsed: parsed2, signal };
|
|
20458
20543
|
if (parsed2.validate) return this.validate(session, root, mine, call.cwd);
|
|
20459
20544
|
return this.command(session, root, mine, call);
|
|
@@ -20470,14 +20555,13 @@ var Sudo = class {
|
|
|
20470
20555
|
);
|
|
20471
20556
|
}
|
|
20472
20557
|
const path = await this.locate(word, cwd);
|
|
20473
|
-
|
|
20474
|
-
|
|
20475
|
-
if (untrusted2) return untrusted2;
|
|
20558
|
+
const bound3 = path && await this.bound(path, cwd);
|
|
20559
|
+
if (!path || !bound3) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20476
20560
|
const argv = [path, ...args];
|
|
20477
20561
|
const matching = mine.filter(
|
|
20478
|
-
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv)
|
|
20562
|
+
(rule) => (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && stdinMatches(rule, caller.stdin) && commandMatches(rule.cmnd, argv) && pinned(rule, bound3)
|
|
20479
20563
|
);
|
|
20480
|
-
const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
|
|
20564
|
+
const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term, bound3);
|
|
20481
20565
|
if (root || matching.some((rule) => rule.nopasswd)) return grant();
|
|
20482
20566
|
const quoted = argv.map(quoteWord).join(" ");
|
|
20483
20567
|
if (matching.length === 0) {
|
|
@@ -20486,22 +20570,23 @@ var Sudo = class {
|
|
|
20486
20570
|
`sudo: ${self.name} is not allowed to run ${quoted} as ${target.name}`
|
|
20487
20571
|
);
|
|
20488
20572
|
}
|
|
20489
|
-
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy)) {
|
|
20490
|
-
this.timestamps.stamp(s.owner, s.policy);
|
|
20573
|
+
if (!parsed2.reset && this.timestamps.current(s.owner, s.policy, bound3)) {
|
|
20574
|
+
this.timestamps.stamp(s.owner, s.policy, bound3);
|
|
20491
20575
|
this.logged(s, cwd, argv);
|
|
20492
20576
|
return grant();
|
|
20493
20577
|
}
|
|
20494
20578
|
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20495
20579
|
return this.pendingFor(cred.ruid, async () => {
|
|
20496
|
-
const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd });
|
|
20580
|
+
const answer2 = await this.ask(s, { argv: parsed2.command, path, cwd, bound: bound3 });
|
|
20497
20581
|
if ("error" in answer2) return answer2;
|
|
20498
20582
|
if (await this.canonical(call.cwd) !== cwd || !await this.reachable(cred, cwd)) {
|
|
20499
20583
|
return failed2("EACCES", `sudo: ${call.cwd} changed while the request waited`);
|
|
20500
20584
|
}
|
|
20501
|
-
const
|
|
20502
|
-
if (
|
|
20503
|
-
const
|
|
20504
|
-
|
|
20585
|
+
const now = await this.bound(path, cwd);
|
|
20586
|
+
if (!now) return failed2("ENOENT", `sudo: ${word}: command not found`);
|
|
20587
|
+
const shown2 = recordable(cwd, [...parsed2.command, ...Object.keys(bound3)]);
|
|
20588
|
+
const result = answer2.answer === "once" ? grant() : await this.always(self, target, { cwd, argv, bound: bound3 }, now, shown2, grant);
|
|
20589
|
+
if (!("error" in result)) this.stamped(s, answer2, bound3);
|
|
20505
20590
|
return result;
|
|
20506
20591
|
});
|
|
20507
20592
|
}
|
|
@@ -20519,7 +20604,7 @@ var Sudo = class {
|
|
|
20519
20604
|
if (parsed2.nonInteractive) return failed2("EPERM", "sudo: a password is required");
|
|
20520
20605
|
const cwd = await this.canonical(dir) ?? normalizePath(dir);
|
|
20521
20606
|
return this.pendingFor(s.caller.cred.ruid, async () => {
|
|
20522
|
-
const answer2 = await this.ask(s, { argv: [], path: "", cwd, validate: true });
|
|
20607
|
+
const answer2 = await this.ask(s, { argv: [], path: "", cwd, bound: {}, validate: true });
|
|
20523
20608
|
if ("error" in answer2) return answer2;
|
|
20524
20609
|
if (!answer2.person) await s.caller.report?.(NOT_STAMPED);
|
|
20525
20610
|
this.stamped(s, answer2);
|
|
@@ -20535,8 +20620,8 @@ var Sudo = class {
|
|
|
20535
20620
|
this.pending.delete(uid);
|
|
20536
20621
|
}
|
|
20537
20622
|
}
|
|
20538
|
-
stamped(s, answer2) {
|
|
20539
|
-
if (answer2.person && !s.parsed.reset) this.timestamps.stamp(s.owner, s.policy);
|
|
20623
|
+
stamped(s, answer2, bound3 = {}) {
|
|
20624
|
+
if (answer2.person && !s.parsed.reset) this.timestamps.stamp(s.owner, s.policy, bound3);
|
|
20540
20625
|
}
|
|
20541
20626
|
logged(s, cwd, argv) {
|
|
20542
20627
|
const { caller, self, target, owner, policy } = s;
|
|
@@ -20547,14 +20632,17 @@ var Sudo = class {
|
|
|
20547
20632
|
`sudo: ${self.name} : TTY=${tty} ; PWD=${quoteWord(cwd)} ; USER=${target.name} ; TIMESTAMP=${type} ; COMMAND=${command}`
|
|
20548
20633
|
);
|
|
20549
20634
|
}
|
|
20550
|
-
async always(self, target,
|
|
20635
|
+
async always(self, target, line, now, shown2, grant) {
|
|
20636
|
+
const { cwd, argv, bound: bound3 } = line;
|
|
20551
20637
|
if (!shown2 || !recordable(cwd, argv)) return { ...grant(), notice: RAN_ONCE };
|
|
20552
|
-
|
|
20638
|
+
const changed = changedFile(bound3, now);
|
|
20639
|
+
if (changed !== void 0) return failed2("EPERM", changedText(changed));
|
|
20640
|
+
if (!await this.remember(self, target, line)) {
|
|
20553
20641
|
return failed2("EIO", "sudo: the rule could not be recorded");
|
|
20554
20642
|
}
|
|
20555
20643
|
return grant();
|
|
20556
20644
|
}
|
|
20557
|
-
listed(self, root, rules, defaults2) {
|
|
20645
|
+
async listed(self, root, rules, defaults2) {
|
|
20558
20646
|
const host = this.o.hostname;
|
|
20559
20647
|
if (root) {
|
|
20560
20648
|
return {
|
|
@@ -20566,7 +20654,9 @@ var Sudo = class {
|
|
|
20566
20654
|
if (rules.length === 0) {
|
|
20567
20655
|
return failed2("EPERM", `User ${self.name} is not allowed to run sudo on ${host}.`);
|
|
20568
20656
|
}
|
|
20569
|
-
const
|
|
20657
|
+
const shown2 = [];
|
|
20658
|
+
for (const rule of rules) shown2.push(described(rule, await this.writable(rule)));
|
|
20659
|
+
const lines2 = shown2.join("\n");
|
|
20570
20660
|
const params = defaults2.flatMap((d) => d.params).join(", ");
|
|
20571
20661
|
const matching = params === "" ? "" : `Matching Defaults entries for ${self.name} on ${host}:
|
|
20572
20662
|
${params}
|
|
@@ -20599,9 +20689,10 @@ ${lines2}
|
|
|
20599
20689
|
...caller.tty ? { tty: caller.tty } : {},
|
|
20600
20690
|
createdAt,
|
|
20601
20691
|
expiresAt: createdAt + timeout,
|
|
20602
|
-
always: asked.validate ? false : recordable(cwd, [path, ...argv]),
|
|
20692
|
+
always: asked.validate ? false : recordable(cwd, [path, ...argv, ...Object.keys(asked.bound)]),
|
|
20603
20693
|
stdin: caller.stdin,
|
|
20604
20694
|
rootOnly: asked.validate ? {} : await this.rootOnly(cwd, argv.slice(1)),
|
|
20695
|
+
...userWritable(asked.bound),
|
|
20605
20696
|
...asked.validate ? { validate: true } : {},
|
|
20606
20697
|
timestamp: true
|
|
20607
20698
|
};
|
|
@@ -20621,7 +20712,7 @@ ${lines2}
|
|
|
20621
20712
|
requester.removeEventListener("abort", withdraw);
|
|
20622
20713
|
}
|
|
20623
20714
|
}
|
|
20624
|
-
async remember(self, target, cwd, argv) {
|
|
20715
|
+
async remember(self, target, { cwd, argv, bound: bound3 }) {
|
|
20625
20716
|
const { fs } = this.o;
|
|
20626
20717
|
const path = `${SUDOERS_D}/${self.name}`;
|
|
20627
20718
|
if (!NAME4.test(self.name)) return false;
|
|
@@ -20632,7 +20723,7 @@ ${lines2}
|
|
|
20632
20723
|
if (st && !await this.trusted(path)) return false;
|
|
20633
20724
|
const old = st ? await fs.readFile(path) : "";
|
|
20634
20725
|
const words2 = recordedCommand(argv);
|
|
20635
|
-
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none Cmnd ${words2}
|
|
20726
|
+
const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Stdin none ${sha256Text(bound3)}Cmnd ${words2}
|
|
20636
20727
|
`;
|
|
20637
20728
|
const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
|
|
20638
20729
|
${line}`;
|
|
@@ -20643,7 +20734,7 @@ ${line}`;
|
|
|
20643
20734
|
return false;
|
|
20644
20735
|
}
|
|
20645
20736
|
}
|
|
20646
|
-
grant(self, target, cred, argv, file, cwd, term) {
|
|
20737
|
+
grant(self, target, cred, argv, file, cwd, term, files) {
|
|
20647
20738
|
const env = {
|
|
20648
20739
|
...this.o.env(),
|
|
20649
20740
|
...userEnv(target, this.o.pnpmHome),
|
|
@@ -20656,7 +20747,9 @@ ${line}`;
|
|
|
20656
20747
|
SUDO_GID: String(cred.rgid),
|
|
20657
20748
|
SUDO_COMMAND: [file, ...argv.slice(1)].join(" ")
|
|
20658
20749
|
};
|
|
20659
|
-
return {
|
|
20750
|
+
return {
|
|
20751
|
+
grant: { file, argv: [...argv], env, cwd, cred: credOf(target), caller: cred, files }
|
|
20752
|
+
};
|
|
20660
20753
|
}
|
|
20661
20754
|
};
|
|
20662
20755
|
|
|
@@ -26885,18 +26978,18 @@ var VfsNodes = class {
|
|
|
26885
26978
|
if (id2 !== void 0) this.know(node, id2);
|
|
26886
26979
|
return node;
|
|
26887
26980
|
}
|
|
26888
|
-
open(path, pin,
|
|
26981
|
+
open(path, pin, writable3 = false, id2, ino) {
|
|
26889
26982
|
const found = this.named(path, id2);
|
|
26890
26983
|
const same3 = !found || ino === void 0 || found.ino === void 0 || found.ino === ino;
|
|
26891
26984
|
if (found && !same3) this.orphan(found);
|
|
26892
26985
|
const node = (same3 ? found : void 0) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
|
|
26893
26986
|
node.ino ??= ino;
|
|
26894
26987
|
node.opens++;
|
|
26895
|
-
if (
|
|
26988
|
+
if (writable3) node.writers++;
|
|
26896
26989
|
if (node.opens === 1) this.opened?.(node);
|
|
26897
26990
|
return node;
|
|
26898
26991
|
}
|
|
26899
|
-
adopt(path, contents2, dirty,
|
|
26992
|
+
adopt(path, contents2, dirty, writable3 = false, id2) {
|
|
26900
26993
|
let node = this.named(path, id2);
|
|
26901
26994
|
if (!node) {
|
|
26902
26995
|
node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
|
|
@@ -26904,7 +26997,7 @@ var VfsNodes = class {
|
|
|
26904
26997
|
node.replace(contents2);
|
|
26905
26998
|
}
|
|
26906
26999
|
node.opens++;
|
|
26907
|
-
if (
|
|
27000
|
+
if (writable3) node.writers++;
|
|
26908
27001
|
if (node.opens === 1) this.opened?.(node);
|
|
26909
27002
|
return node;
|
|
26910
27003
|
}
|
|
@@ -26917,11 +27010,11 @@ var VfsNodes = class {
|
|
|
26917
27010
|
if (node.writers > 0 && within9(path, prefix)) return true;
|
|
26918
27011
|
return false;
|
|
26919
27012
|
}
|
|
26920
|
-
closed(node,
|
|
27013
|
+
closed(node, writable3 = false) {
|
|
26921
27014
|
node.opens--;
|
|
26922
|
-
if (
|
|
27015
|
+
if (writable3) node.writers--;
|
|
26923
27016
|
if (node.opens > 0) {
|
|
26924
|
-
if (
|
|
27017
|
+
if (writable3 && node.writers === 0) this.onClosed?.(node.path);
|
|
26925
27018
|
return;
|
|
26926
27019
|
}
|
|
26927
27020
|
this.forget(node);
|
|
@@ -27240,8 +27333,8 @@ function keepingOpen(fs, nodes, changed = () => {
|
|
|
27240
27333
|
function vfsFile(fs, opts, nodes) {
|
|
27241
27334
|
const access2 = opts.flags & O_ACCMODE;
|
|
27242
27335
|
const readable2 = access2 !== O_WRONLY;
|
|
27243
|
-
const
|
|
27244
|
-
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true,
|
|
27336
|
+
const writable3 = access2 === O_WRONLY || access2 === O_RDWR;
|
|
27337
|
+
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable3, opts.id) : nodes.open(opts.path, opts.pin, writable3, opts.id, opts.ino);
|
|
27245
27338
|
let openError;
|
|
27246
27339
|
const atOpen = (op) => {
|
|
27247
27340
|
void node.serial(op).catch((err) => {
|
|
@@ -27268,7 +27361,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
27268
27361
|
offset += out.length;
|
|
27269
27362
|
return out;
|
|
27270
27363
|
}) : void 0,
|
|
27271
|
-
write:
|
|
27364
|
+
write: writable3 ? (bytes) => serial(async () => {
|
|
27272
27365
|
if (opts.flags & O_APPEND) offset = await node.append(bytes);
|
|
27273
27366
|
else offset += await node.pwrite(bytes, offset);
|
|
27274
27367
|
return bytes.length;
|
|
@@ -27287,11 +27380,11 @@ function vfsFile(fs, opts, nodes) {
|
|
|
27287
27380
|
return node.pread(max, at);
|
|
27288
27381
|
}),
|
|
27289
27382
|
pwrite: (bytes, at) => serial(() => {
|
|
27290
|
-
if (!
|
|
27383
|
+
if (!writable3) throw new KernelError("EBADF");
|
|
27291
27384
|
return node.pwrite(bytes, at);
|
|
27292
27385
|
}),
|
|
27293
27386
|
resize: (size) => serial(() => {
|
|
27294
|
-
if (!
|
|
27387
|
+
if (!writable3) throw new KernelError("EBADF");
|
|
27295
27388
|
return node.truncate(size);
|
|
27296
27389
|
}),
|
|
27297
27390
|
stat: () => serial(async () => ({
|
|
@@ -27307,7 +27400,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
27307
27400
|
takeOpenError();
|
|
27308
27401
|
await node.flush();
|
|
27309
27402
|
} finally {
|
|
27310
|
-
nodes?.closed(node,
|
|
27403
|
+
nodes?.closed(node, writable3);
|
|
27311
27404
|
}
|
|
27312
27405
|
})
|
|
27313
27406
|
});
|
|
@@ -28983,14 +29076,20 @@ var WasmProcess = class {
|
|
|
28983
29076
|
if (!("grant" in answer2)) return answer2;
|
|
28984
29077
|
if (this.dying) throw new KernelError("EINTR");
|
|
28985
29078
|
if (answer2.notice !== void 0) await this.complainLine(answer2.notice);
|
|
28986
|
-
const { file, argv, env, cwd, cred, caller } = answer2.grant;
|
|
29079
|
+
const { file, argv, env, cwd, cred, caller, files } = answer2.grant;
|
|
28987
29080
|
const stdio = [{ fd: 0 }, { fd: 1 }, { fd: 2 }];
|
|
28988
|
-
|
|
28989
|
-
|
|
28990
|
-
|
|
28991
|
-
|
|
28992
|
-
|
|
28993
|
-
|
|
29081
|
+
try {
|
|
29082
|
+
return {
|
|
29083
|
+
pid: await this.children.spawn({ file, argv, env, cwd, pgid: 0 }, stdio, [], {
|
|
29084
|
+
cred,
|
|
29085
|
+
caller,
|
|
29086
|
+
files
|
|
29087
|
+
})
|
|
29088
|
+
};
|
|
29089
|
+
} catch (err) {
|
|
29090
|
+
if (err instanceof ChangedError) return { error: err.notice, errno: err.code };
|
|
29091
|
+
throw err;
|
|
29092
|
+
}
|
|
28994
29093
|
}
|
|
28995
29094
|
stdinKind() {
|
|
28996
29095
|
if (!this.fds.has(0)) return "none";
|
|
@@ -29030,8 +29129,8 @@ var WasmProcess = class {
|
|
|
29030
29129
|
};
|
|
29031
29130
|
withDl(state) {
|
|
29032
29131
|
if (this.dlLog.length === 0) return state;
|
|
29033
|
-
const { next: next3, handle, free, live } = this.closures;
|
|
29034
|
-
const dl = { log: [...this.dlLog], next: next3, handle, free: [...free], live: [...
|
|
29132
|
+
const { next: next3, handle, free, live: live2 } = this.closures;
|
|
29133
|
+
const dl = { log: [...this.dlLog], next: next3, handle, free: [...free], live: [...live2] };
|
|
29035
29134
|
return { ...state, dl };
|
|
29036
29135
|
}
|
|
29037
29136
|
seedDl(dl) {
|
|
@@ -32255,7 +32354,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
32255
32354
|
if (remote.client.token === remote.token && !remote.client.gone) return remote;
|
|
32256
32355
|
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
32257
32356
|
};
|
|
32258
|
-
const
|
|
32357
|
+
const live2 = (fh) => {
|
|
32259
32358
|
const remote = handles.get(fh);
|
|
32260
32359
|
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
32261
32360
|
return fresh3(remote);
|
|
@@ -32274,7 +32373,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
32274
32373
|
}, againMs);
|
|
32275
32374
|
timer.unref?.();
|
|
32276
32375
|
});
|
|
32277
|
-
async function
|
|
32376
|
+
async function writable3(client, path, flags2) {
|
|
32278
32377
|
const { ifMatch: _, ...asked } = flags2;
|
|
32279
32378
|
const opening2 = () => client.call("open", { path: rel(path), ...asked });
|
|
32280
32379
|
try {
|
|
@@ -32305,7 +32404,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
32305
32404
|
async open(path, flags2) {
|
|
32306
32405
|
const client = current();
|
|
32307
32406
|
const token = client.token;
|
|
32308
|
-
const { opened: opened2, restore } = await
|
|
32407
|
+
const { opened: opened2, restore } = await writable3(client, path, flags2);
|
|
32309
32408
|
if (flags2.ifMatch !== void 0 && opened2.attr?.etag !== flags2.ifMatch) {
|
|
32310
32409
|
await client.call("release", { fh: opened2.fh }, true);
|
|
32311
32410
|
throw fsError2("ESTALE", `${path} changed on the host while it was being read`);
|
|
@@ -32320,7 +32419,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
32320
32419
|
return nextFh;
|
|
32321
32420
|
},
|
|
32322
32421
|
async read(fh, offset, size) {
|
|
32323
|
-
const remote =
|
|
32422
|
+
const remote = live2(fh);
|
|
32324
32423
|
return remote.client.read(
|
|
32325
32424
|
remote.fh,
|
|
32326
32425
|
offset,
|
|
@@ -32329,7 +32428,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
|
|
|
32329
32428
|
);
|
|
32330
32429
|
},
|
|
32331
32430
|
async write(fh, offset, bytes) {
|
|
32332
|
-
const remote =
|
|
32431
|
+
const remote = live2(fh);
|
|
32333
32432
|
const most = remote.client.maxIo;
|
|
32334
32433
|
for (let at = 0; at < bytes.length; at += most) {
|
|
32335
32434
|
await remote.client.write(remote.fh, offset + at, bytes.subarray(at, at + most));
|
|
@@ -33569,6 +33668,10 @@ function describe(req, tty) {
|
|
|
33569
33668
|
};
|
|
33570
33669
|
return { argv: [req.argv0, ...req.args], tty, started: Date.now(), ...exe };
|
|
33571
33670
|
}
|
|
33671
|
+
function refuseChanged(approved, now) {
|
|
33672
|
+
const changed = changedFile(approved, now);
|
|
33673
|
+
if (changed !== void 0) throw new ChangedError(changedText(changed));
|
|
33674
|
+
}
|
|
33572
33675
|
function linksOf2(req) {
|
|
33573
33676
|
return req.links === void 0 ? {} : { links: req.links };
|
|
33574
33677
|
}
|
|
@@ -34702,6 +34805,27 @@ var Launcher = class {
|
|
|
34702
34805
|
}
|
|
34703
34806
|
return true;
|
|
34704
34807
|
}
|
|
34808
|
+
async unchanged(planned, file, cwd, grant) {
|
|
34809
|
+
const approved = grant?.files;
|
|
34810
|
+
if (!approved) return {};
|
|
34811
|
+
const files = await this.backingOf(planned, file, cwd, "");
|
|
34812
|
+
refuseChanged(approved, await this.sudoer.digests([file, ...files]));
|
|
34813
|
+
return { pinned: approved };
|
|
34814
|
+
}
|
|
34815
|
+
async pinOf(path, caller) {
|
|
34816
|
+
const { pinned: pinned2 } = caller;
|
|
34817
|
+
if (!pinned2) return {};
|
|
34818
|
+
const real = await this.sudoer.real(path);
|
|
34819
|
+
const hex4 = pinned2[real];
|
|
34820
|
+
if (hex4 !== void 0) return { [real]: hex4 };
|
|
34821
|
+
if (await this.sudoer.trusted(real)) return {};
|
|
34822
|
+
throw new ChangedError(changedText(real));
|
|
34823
|
+
}
|
|
34824
|
+
async pinnedBytes(pin, bytes) {
|
|
34825
|
+
const [real = ""] = Object.keys(pin);
|
|
34826
|
+
refuseChanged(pin, { [real]: await sha256(bytes) });
|
|
34827
|
+
return bytes;
|
|
34828
|
+
}
|
|
34705
34829
|
async grantedCwd(caller, cwd) {
|
|
34706
34830
|
const real = await physicalPathAsync(this.base, cwd).catch(() => void 0);
|
|
34707
34831
|
return real === cwd && await this.sudoer.reachable(caller, cwd);
|
|
@@ -34790,13 +34914,19 @@ var Launcher = class {
|
|
|
34790
34914
|
return found;
|
|
34791
34915
|
}
|
|
34792
34916
|
async module(path, caller, asked) {
|
|
34793
|
-
const
|
|
34917
|
+
const pin = await this.pinOf(path, caller);
|
|
34918
|
+
const sum = Object.values(pin)[0];
|
|
34919
|
+
const key = sum === void 0 ? await this.cacheKey(path, asked) : `${path}:sha256:${sum}`;
|
|
34794
34920
|
return this.cached(this.compiled, key, async () => {
|
|
34795
|
-
const { bytes, key:
|
|
34921
|
+
const { bytes: read, key: readKey } = await this.keyed(path, caller, asked);
|
|
34922
|
+
const bytes = sum === void 0 ? read : await this.pinnedBytes(pin, read);
|
|
34796
34923
|
const memory = importedMemory(bytes);
|
|
34797
34924
|
const foreign = foreignImports(bytes);
|
|
34798
34925
|
const compiled = await WebAssembly.compile(bytes);
|
|
34799
|
-
return {
|
|
34926
|
+
return {
|
|
34927
|
+
value: { module: compiled, ...memory ? { memory } : {}, foreign },
|
|
34928
|
+
key: sum === void 0 ? readKey : key
|
|
34929
|
+
};
|
|
34800
34930
|
});
|
|
34801
34931
|
}
|
|
34802
34932
|
async checkedSources(planned, caller) {
|
|
@@ -34821,6 +34951,11 @@ var Launcher = class {
|
|
|
34821
34951
|
}
|
|
34822
34952
|
}
|
|
34823
34953
|
async text(path, caller, asked) {
|
|
34954
|
+
const pin = await this.pinOf(path, caller);
|
|
34955
|
+
if (Object.keys(pin).length > 0) {
|
|
34956
|
+
const { bytes } = await this.keyed(path, caller, asked);
|
|
34957
|
+
return new TextDecoder().decode(await this.pinnedBytes(pin, bytes));
|
|
34958
|
+
}
|
|
34824
34959
|
if (!this.integrity) return this.base.readFile(path);
|
|
34825
34960
|
const key = await this.cacheKey(path, asked);
|
|
34826
34961
|
return this.cached(this.texts, key, async () => {
|
|
@@ -35009,7 +35144,12 @@ var Launcher = class {
|
|
|
35009
35144
|
const own = env.HOSTNAME;
|
|
35010
35145
|
if (asked.target.abi === "wasi") env.HOSTNAME = this.hostname;
|
|
35011
35146
|
const planned = chosen2(asked, env);
|
|
35012
|
-
const caller = {
|
|
35147
|
+
const caller = {
|
|
35148
|
+
pid: req.ppid ?? 0,
|
|
35149
|
+
uid: req.cred.euid,
|
|
35150
|
+
tag: req.tags?.[0],
|
|
35151
|
+
pinned: req.pinned
|
|
35152
|
+
};
|
|
35013
35153
|
if (this.integrity) await this.checkedSources(planned, caller);
|
|
35014
35154
|
const [program, exeId] = await this.loaded(planned.target, env, caller);
|
|
35015
35155
|
if (readsEtcHostname(program)) setOrDrop(env, "HOSTNAME", own);
|
|
@@ -35524,6 +35664,7 @@ var Launcher = class {
|
|
|
35524
35664
|
const scope = grant ? "" : await this.scopeOf(cred.euid);
|
|
35525
35665
|
const file = booked === void 0 ? req.file : await this.ticketFile(req, fds);
|
|
35526
35666
|
const planned = await this.checkedPlan({ ...req, file }, { ppid, cred, scope }, fds);
|
|
35667
|
+
const pin = await this.unchanged(planned, file, req.cwd, grant);
|
|
35527
35668
|
const { recheck, ...redeemed } = await this.redeemed(
|
|
35528
35669
|
{ ...req, file },
|
|
35529
35670
|
{ ppid, cred, scope, booked },
|
|
@@ -35535,6 +35676,7 @@ var Launcher = class {
|
|
|
35535
35676
|
const replan = () => this.planOrSearch({ ...req, file }, { ppid, cred, scope });
|
|
35536
35677
|
const again = { ...planned, replan, recheck };
|
|
35537
35678
|
const handle = await this.launch(again, {
|
|
35679
|
+
...pin,
|
|
35538
35680
|
...deciding ? { decided: deciding.promise } : {},
|
|
35539
35681
|
cwd: req.cwd,
|
|
35540
35682
|
cwdFrom: inheritedCwd(req.chdir, this.cwdSlots.get(ppid)),
|