@jigging/jig 0.1.0-alpha.24 → 0.1.0-alpha.26
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 +6 -2
- package/THIRD_PARTY_NOTICES +13 -6
- package/bin/jig +59 -10
- package/dist/index.js +3989 -29
- package/libexec/agent/claude-agent-acp.js +34 -34
- package/libexec/agent/claude-agent-launcher.js +2 -0
- package/libexec/agent/codex-acp.js +22 -0
- package/libexec/agent/codex-agent-launcher.js +35 -14
- package/libexec/agent/pi-acp.js +2 -2
- package/libexec/agent/pi-agent-launcher.js +5 -3
- package/libexec/evaluator/project-evaluator-sdk.bundle.js +3989 -29
- package/libexec/evaluator/project-evaluator-worker.js +2 -1
- package/libexec/http-request-worker.js +3 -1
- package/libexec/installed-cli.js +22693 -13077
- package/libexec/macos-descriptor-bridge.dylib +0 -0
- package/libexec/macos-exec +0 -0
- package/libexec/macos-native-supervisor.js +2216 -0
- package/libexec/markdown-runtime.js +4395 -44
- package/libexec/preparation/bun-native-preparation-worker.js +4008 -27
- package/package.json +11 -7
|
@@ -0,0 +1,2216 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/internal/macos-native-supervisor.ts
|
|
3
|
+
import { connect } from "net";
|
|
4
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
5
|
+
import { Transform } from "stream";
|
|
6
|
+
|
|
7
|
+
// src/internal/macos-control-channel.ts
|
|
8
|
+
function privateMacosControlChannel(socket) {
|
|
9
|
+
const messages = [];
|
|
10
|
+
let buffer = Buffer.alloc(0);
|
|
11
|
+
let ended = false;
|
|
12
|
+
let failure;
|
|
13
|
+
let pending;
|
|
14
|
+
const fail = (reason) => {
|
|
15
|
+
failure ??= new Error(`macOS control channel ${reason}`);
|
|
16
|
+
ended = true;
|
|
17
|
+
messages.length = 0;
|
|
18
|
+
pending?.reject(failure);
|
|
19
|
+
pending = undefined;
|
|
20
|
+
socket.destroy();
|
|
21
|
+
};
|
|
22
|
+
const end = () => {
|
|
23
|
+
if (buffer.length !== 0)
|
|
24
|
+
return fail("truncated");
|
|
25
|
+
ended = true;
|
|
26
|
+
failure ??= new Error("macOS control channel ended");
|
|
27
|
+
pending?.reject(failure);
|
|
28
|
+
pending = undefined;
|
|
29
|
+
};
|
|
30
|
+
socket.on("data", (data) => {
|
|
31
|
+
try {
|
|
32
|
+
if (ended)
|
|
33
|
+
return fail("socket failed");
|
|
34
|
+
if (buffer.length + data.length > 65536)
|
|
35
|
+
return fail("capacity exceeded");
|
|
36
|
+
buffer = Buffer.concat([buffer, data]);
|
|
37
|
+
for (;; ) {
|
|
38
|
+
const newline = buffer.indexOf(10);
|
|
39
|
+
if (newline < 0)
|
|
40
|
+
break;
|
|
41
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, newline));
|
|
42
|
+
buffer = buffer.subarray(newline + 1);
|
|
43
|
+
const message = JSON.parse(text);
|
|
44
|
+
if (messages.length >= 8)
|
|
45
|
+
return fail("capacity exceeded");
|
|
46
|
+
if (pending !== undefined) {
|
|
47
|
+
const waiter = pending;
|
|
48
|
+
pending = undefined;
|
|
49
|
+
waiter.resolve(message);
|
|
50
|
+
} else
|
|
51
|
+
messages.push(message);
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
fail("malformed");
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
socket.on("error", () => fail("socket failed"));
|
|
58
|
+
socket.on("end", end);
|
|
59
|
+
socket.on("close", end);
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
receive() {
|
|
62
|
+
if (messages.length)
|
|
63
|
+
return Promise.resolve(messages.shift());
|
|
64
|
+
if (failure !== undefined)
|
|
65
|
+
return Promise.reject(failure);
|
|
66
|
+
if (pending !== undefined)
|
|
67
|
+
return Promise.reject(new Error("concurrent macOS control readers"));
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
pending = { resolve, reject };
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
send(value) {
|
|
73
|
+
const bytes = Buffer.from(`${JSON.stringify(value)}
|
|
74
|
+
`);
|
|
75
|
+
if (ended || bytes.length > 65536 || socket.writableLength + bytes.length > 65536)
|
|
76
|
+
throw new Error("macOS control delivery unavailable");
|
|
77
|
+
socket.write(bytes);
|
|
78
|
+
},
|
|
79
|
+
close: () => socket.destroy()
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function privateMacosBefore(promise, milliseconds) {
|
|
83
|
+
let timer;
|
|
84
|
+
try {
|
|
85
|
+
return await Promise.race([
|
|
86
|
+
promise,
|
|
87
|
+
new Promise((_, reject) => {
|
|
88
|
+
timer = setTimeout(() => reject(new Error("macOS control deadline expired")), Math.max(1, milliseconds));
|
|
89
|
+
})
|
|
90
|
+
]);
|
|
91
|
+
} finally {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/internal/macos-descriptor-handoff.ts
|
|
97
|
+
import { closeSync as closeSync3, constants as constants3, fstatSync as fstatSync2, lstatSync as lstatSync3, realpathSync as realpathSync3 } from "fs";
|
|
98
|
+
import { open as open2 } from "fs/promises";
|
|
99
|
+
import { createRequire as createRequire3 } from "module";
|
|
100
|
+
import { join as join3 } from "path";
|
|
101
|
+
import { getSystemErrorName as getSystemErrorName2 } from "util";
|
|
102
|
+
|
|
103
|
+
// src/internal/macos-descriptor-files.ts
|
|
104
|
+
import { closeSync as closeSync2, constants as constants2, readFileSync, realpathSync as realpathSync2, lstatSync as lstatSync2 } from "fs";
|
|
105
|
+
import { createHash } from "crypto";
|
|
106
|
+
import { fileURLToPath } from "url";
|
|
107
|
+
import { mkdtemp, open, rmdir } from "fs/promises";
|
|
108
|
+
import { createRequire as createRequire2 } from "module";
|
|
109
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
110
|
+
import { getSystemErrorName } from "util";
|
|
111
|
+
|
|
112
|
+
// src/internal/macos-process-controls.ts
|
|
113
|
+
import { createRequire } from "module";
|
|
114
|
+
import { release } from "os";
|
|
115
|
+
|
|
116
|
+
// src/internal/macos-owner-state.ts
|
|
117
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
118
|
+
import {
|
|
119
|
+
closeSync,
|
|
120
|
+
constants,
|
|
121
|
+
fstatSync,
|
|
122
|
+
fsyncSync,
|
|
123
|
+
lstatSync,
|
|
124
|
+
openSync,
|
|
125
|
+
readSync,
|
|
126
|
+
realpathSync,
|
|
127
|
+
rmdirSync,
|
|
128
|
+
unlinkSync,
|
|
129
|
+
writeFileSync
|
|
130
|
+
} from "fs";
|
|
131
|
+
import { dirname, join, posix } from "path";
|
|
132
|
+
var O_NOFOLLOW_ANY = 536870912;
|
|
133
|
+
var O_CLOEXEC = 16777216;
|
|
134
|
+
var handles = new WeakMap;
|
|
135
|
+
var tokenPattern = /^[0-9a-f]{64}$/;
|
|
136
|
+
function privateMacosStorageRecoveryToken(token) {
|
|
137
|
+
if (!tokenPattern.test(token))
|
|
138
|
+
throw new TypeError("invalid macOS ownership token");
|
|
139
|
+
return createHmac("sha256", Buffer.from(token, "hex")).update("jig-macos-storage-recovery").digest("hex");
|
|
140
|
+
}
|
|
141
|
+
function privateDirectory(path) {
|
|
142
|
+
if (process.platform !== "darwin" || !path.startsWith("/") || realpathSync(path) !== path)
|
|
143
|
+
throw new Error("macOS ownership requires a canonical protected directory");
|
|
144
|
+
const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | O_NOFOLLOW_ANY | O_CLOEXEC);
|
|
145
|
+
const stat = fstatSync(fd);
|
|
146
|
+
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0) {
|
|
147
|
+
closeSync(fd);
|
|
148
|
+
throw new Error("macOS ownership directory is not private");
|
|
149
|
+
}
|
|
150
|
+
return fd;
|
|
151
|
+
}
|
|
152
|
+
function requirePrivateMacosOwnerDirectory(path) {
|
|
153
|
+
closeSync(privateDirectory(path));
|
|
154
|
+
}
|
|
155
|
+
function signature(token, identity, domain = "jig-macos-owner") {
|
|
156
|
+
if (!tokenPattern.test(token))
|
|
157
|
+
throw new TypeError("invalid macOS ownership token");
|
|
158
|
+
return createHmac("sha256", Buffer.from(token, "hex")).update(`${domain}\x00`).update(JSON.stringify(identity)).digest();
|
|
159
|
+
}
|
|
160
|
+
function recordPrivateMacosOwner(directory, token, control) {
|
|
161
|
+
const owner = requirePrivateMacosCoalition(control);
|
|
162
|
+
if (!owner.empty())
|
|
163
|
+
throw new Error("macOS ownership must be durable before creating descendants");
|
|
164
|
+
const identity = owner.identity;
|
|
165
|
+
const mac = signature(token, identity).toString("hex");
|
|
166
|
+
const parent = privateDirectory(directory);
|
|
167
|
+
let fd;
|
|
168
|
+
try {
|
|
169
|
+
fd = openSync(join(directory, "owner.json"), constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | O_NOFOLLOW_ANY | O_CLOEXEC, 384);
|
|
170
|
+
writeFileSync(fd, `${JSON.stringify({ identity, mac })}
|
|
171
|
+
`);
|
|
172
|
+
fsyncSync(fd);
|
|
173
|
+
fsyncSync(parent);
|
|
174
|
+
} finally {
|
|
175
|
+
if (fd !== undefined)
|
|
176
|
+
closeSync(fd);
|
|
177
|
+
closeSync(parent);
|
|
178
|
+
}
|
|
179
|
+
return readPrivateMacosOwner(directory, token);
|
|
180
|
+
}
|
|
181
|
+
function readRecord(directory, name) {
|
|
182
|
+
const parent = privateDirectory(directory);
|
|
183
|
+
let fd;
|
|
184
|
+
try {
|
|
185
|
+
fd = openSync(join(directory, name), constants.O_RDONLY | O_NOFOLLOW_ANY | O_CLOEXEC);
|
|
186
|
+
const before = fstatSync(fd, { bigint: true });
|
|
187
|
+
if (!before.isFile() || before.uid !== BigInt(process.getuid?.() ?? -1) || (before.mode & 0o177n) !== 0n || before.nlink !== 1n || before.size < 1n || before.size > 1024n)
|
|
188
|
+
throw new Error("macOS ownership journal is unsafe");
|
|
189
|
+
const buffer = Buffer.alloc(1025);
|
|
190
|
+
let count = 0;
|
|
191
|
+
while (count < buffer.length) {
|
|
192
|
+
const read = readSync(fd, buffer, count, buffer.length - count, count);
|
|
193
|
+
if (read === 0)
|
|
194
|
+
break;
|
|
195
|
+
count += read;
|
|
196
|
+
}
|
|
197
|
+
if (BigInt(count) !== before.size)
|
|
198
|
+
throw new Error("macOS ownership journal size changed");
|
|
199
|
+
const bytes = buffer.subarray(0, count);
|
|
200
|
+
const after = fstatSync(fd, { bigint: true });
|
|
201
|
+
if (before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || after.nlink !== 1n)
|
|
202
|
+
throw new Error("macOS ownership journal changed");
|
|
203
|
+
const record = JSON.parse(bytes.toString("utf8"));
|
|
204
|
+
if (record === null || typeof record !== "object" || Object.keys(record).sort().join() !== "identity,mac")
|
|
205
|
+
throw new Error("invalid macOS ownership journal");
|
|
206
|
+
return record;
|
|
207
|
+
} finally {
|
|
208
|
+
if (fd !== undefined)
|
|
209
|
+
closeSync(fd);
|
|
210
|
+
closeSync(parent);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function readPrivateMacosOwner(directory, token) {
|
|
214
|
+
if (!tokenPattern.test(token))
|
|
215
|
+
throw new TypeError("invalid macOS ownership token");
|
|
216
|
+
const record = readRecord(directory, "owner.json");
|
|
217
|
+
const raw = record.identity;
|
|
218
|
+
if (raw === null || typeof raw !== "object" || Object.keys(raw).sort().join() !== "bootId,coalition,guardianPid,guardianVersion" || typeof raw.bootId !== "string" || !/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/.test(raw.bootId) || typeof raw.coalition !== "string" || !/^[1-9][0-9]{0,19}$/.test(raw.coalition) || BigInt(raw.coalition) > 0xffffffffffffffffn || !Number.isSafeInteger(raw.guardianPid) || raw.guardianPid <= 1 || raw.guardianPid > 2147483647 || !Number.isSafeInteger(raw.guardianVersion) || raw.guardianVersion < 0 || raw.guardianVersion > 4294967295 || typeof record.mac !== "string" || !tokenPattern.test(record.mac))
|
|
219
|
+
throw new Error("invalid macOS ownership identity");
|
|
220
|
+
const identity = Object.freeze({
|
|
221
|
+
bootId: raw.bootId,
|
|
222
|
+
coalition: raw.coalition,
|
|
223
|
+
guardianPid: raw.guardianPid,
|
|
224
|
+
guardianVersion: raw.guardianVersion
|
|
225
|
+
});
|
|
226
|
+
if (!timingSafeEqual(signature(token, identity), Buffer.from(record.mac, "hex")))
|
|
227
|
+
throw new Error("macOS ownership authentication failed");
|
|
228
|
+
const handle = Object.freeze({ identity });
|
|
229
|
+
handles.set(handle, identity);
|
|
230
|
+
return handle;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/internal/macos-process-controls.ts
|
|
234
|
+
var PROC_PIDUNIQIDENTIFIERINFO = 17;
|
|
235
|
+
var PROC_PIDCOALITIONINFO = 20;
|
|
236
|
+
var RESOURCE_USAGE_BYTES = 37 * 8;
|
|
237
|
+
var MAX_PROCESSES = 65536;
|
|
238
|
+
var signals = Object.freeze({ stop: 17, continue: 19, kill: 9 });
|
|
239
|
+
var native;
|
|
240
|
+
var authenticCoalitions = new WeakSet;
|
|
241
|
+
function calls() {
|
|
242
|
+
if (native !== undefined)
|
|
243
|
+
return native;
|
|
244
|
+
if (process.platform !== "darwin" || !["x64", "arm64"].includes(process.arch))
|
|
245
|
+
throw new Error("macOS process controls are not qualified on this kernel");
|
|
246
|
+
const ffi = createRequire(import.meta.url)("bun:ffi");
|
|
247
|
+
const library = ffi.dlopen("/usr/lib/libSystem.B.dylib", {
|
|
248
|
+
coalition_info_resource_usage: { args: ["u64", "ptr", "u64"], returns: "i32" },
|
|
249
|
+
mach_timebase_info: { args: ["ptr"], returns: "i32" },
|
|
250
|
+
proc_pidinfo: { args: ["i32", "i32", "u64", "ptr", "i32"], returns: "i32" },
|
|
251
|
+
proc_listallpids: { args: ["ptr", "i32"], returns: "i32" },
|
|
252
|
+
proc_pid_rusage: { args: ["i32", "i32", "ptr"], returns: "i32" },
|
|
253
|
+
proc_signal_with_audittoken: { args: ["ptr", "i32"], returns: "i32" },
|
|
254
|
+
sysctlbyname: { args: ["ptr", "ptr", "ptr", "ptr", "u64"], returns: "i32" },
|
|
255
|
+
getsockopt: { args: ["i32", "i32", "i32", "ptr", "ptr"], returns: "i32" },
|
|
256
|
+
__error: { args: [], returns: "ptr" }
|
|
257
|
+
});
|
|
258
|
+
const timebase = Buffer.alloc(8);
|
|
259
|
+
if (library.symbols.mach_timebase_info(ffi.ptr(timebase)) !== 0 || timebase.readUInt32LE(0) === 0 || timebase.readUInt32LE(4) === 0)
|
|
260
|
+
throw new Error("macOS CPU timebase is unavailable");
|
|
261
|
+
const opened = {
|
|
262
|
+
ptr: ffi.ptr,
|
|
263
|
+
read: ffi.read,
|
|
264
|
+
symbols: library.symbols,
|
|
265
|
+
timeNumerator: BigInt(timebase.readUInt32LE(0)),
|
|
266
|
+
timeDenominator: BigInt(timebase.readUInt32LE(4))
|
|
267
|
+
};
|
|
268
|
+
if (!platformFor(kernelString(opened, "kern.osversion")))
|
|
269
|
+
throw new Error("macOS process controls are not qualified on this kernel build");
|
|
270
|
+
native = opened;
|
|
271
|
+
return native;
|
|
272
|
+
}
|
|
273
|
+
function privateMacosPeerIdentity(socket) {
|
|
274
|
+
const fd = socket._handle?.fd;
|
|
275
|
+
if (socket.destroyed || fd === undefined || !Number.isSafeInteger(fd) || fd < 0)
|
|
276
|
+
throw new Error("macOS control socket descriptor is unavailable");
|
|
277
|
+
return privateMacosDescriptorPeerIdentity(fd);
|
|
278
|
+
}
|
|
279
|
+
function privateMacosDescriptorPeerIdentity(fd) {
|
|
280
|
+
if (!Number.isSafeInteger(fd) || fd < 0)
|
|
281
|
+
throw new Error("macOS control socket descriptor is unavailable");
|
|
282
|
+
const { ptr, symbols } = calls();
|
|
283
|
+
const token = Buffer.alloc(32);
|
|
284
|
+
const length = Buffer.alloc(4);
|
|
285
|
+
length.writeUInt32LE(token.length);
|
|
286
|
+
if (symbols.getsockopt(fd, 0, 6, ptr(token), ptr(length)) !== 0 || length.readUInt32LE() !== 32)
|
|
287
|
+
throw new Error("macOS control peer identity is unavailable");
|
|
288
|
+
return Object.freeze({
|
|
289
|
+
uid: token.readUInt32LE(4),
|
|
290
|
+
realUid: token.readUInt32LE(12),
|
|
291
|
+
pid: token.readUInt32LE(20),
|
|
292
|
+
version: token.readUInt32LE(28)
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function kernelString(native2, key) {
|
|
296
|
+
const name = Buffer.from(`${key}\x00`);
|
|
297
|
+
const bytes = Buffer.alloc(64);
|
|
298
|
+
const size = Buffer.alloc(8);
|
|
299
|
+
size.writeBigUInt64LE(BigInt(bytes.length));
|
|
300
|
+
if (native2.symbols.sysctlbyname(native2.ptr(name), native2.ptr(bytes), native2.ptr(size), 0, 0) !== 0)
|
|
301
|
+
throw new Error("macOS kernel identity is unavailable");
|
|
302
|
+
const length = size.readBigUInt64LE();
|
|
303
|
+
const end = bytes.indexOf(0);
|
|
304
|
+
if (length < 1n || length > 64n || end !== Number(length) - 1)
|
|
305
|
+
throw new Error("macOS kernel identity is invalid");
|
|
306
|
+
return bytes.subarray(0, end).toString("ascii");
|
|
307
|
+
}
|
|
308
|
+
function processIdentity(pid) {
|
|
309
|
+
const { ptr, symbols } = calls();
|
|
310
|
+
const unique = Buffer.alloc(56);
|
|
311
|
+
const coalitions = Buffer.alloc(40);
|
|
312
|
+
if (symbols.proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 0, ptr(unique), 56) !== 56)
|
|
313
|
+
return;
|
|
314
|
+
if (symbols.proc_pidinfo(pid, PROC_PIDCOALITIONINFO, 0, ptr(coalitions), 40) !== 40)
|
|
315
|
+
return;
|
|
316
|
+
return { version: unique.readUInt32LE(32), coalition: coalitions.readBigUInt64LE(0) };
|
|
317
|
+
}
|
|
318
|
+
function bootIdentity() {
|
|
319
|
+
const result = kernelString(calls(), "kern.bootsessionuuid");
|
|
320
|
+
if (!/^[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/.test(result))
|
|
321
|
+
throw new Error("macOS boot identity is invalid");
|
|
322
|
+
return result.toLowerCase();
|
|
323
|
+
}
|
|
324
|
+
function privateMacosCurrentProcessIdentity() {
|
|
325
|
+
const identity = processIdentity(process.pid);
|
|
326
|
+
const uid = process.getuid?.();
|
|
327
|
+
if (identity === undefined || uid === undefined || uid === 0)
|
|
328
|
+
throw new Error("macOS coordinator identity is unavailable");
|
|
329
|
+
return Object.freeze({ uid, pid: process.pid, version: identity.version });
|
|
330
|
+
}
|
|
331
|
+
function usage(coalition, absentIsEmpty = false) {
|
|
332
|
+
const { ptr, read, symbols } = calls();
|
|
333
|
+
const bytes = Buffer.alloc(RESOURCE_USAGE_BYTES);
|
|
334
|
+
if (symbols.coalition_info_resource_usage(coalition, ptr(bytes), bytes.length) !== 0) {
|
|
335
|
+
if (read.i32(symbols.__error()) === 3 && absentIsEmpty)
|
|
336
|
+
return { active: 0n, cpuNanoseconds: 0n };
|
|
337
|
+
throw new Error("macOS coalition accounting is unavailable");
|
|
338
|
+
}
|
|
339
|
+
const started = bytes.readBigUInt64LE(0);
|
|
340
|
+
const exited = bytes.readBigUInt64LE(8);
|
|
341
|
+
if (started < exited)
|
|
342
|
+
throw new Error("macOS coalition accounting is invalid");
|
|
343
|
+
const timebase = calls();
|
|
344
|
+
return {
|
|
345
|
+
active: started - exited,
|
|
346
|
+
cpuNanoseconds: bytes.readBigUInt64LE(24) * timebase.timeNumerator / timebase.timeDenominator
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function coalitionMembers(coalition, exclude) {
|
|
350
|
+
const { ptr, symbols } = calls();
|
|
351
|
+
const bytes = Buffer.alloc(MAX_PROCESSES * 4);
|
|
352
|
+
const count = symbols.proc_listallpids(ptr(bytes), bytes.length);
|
|
353
|
+
if (count < 1 || count >= MAX_PROCESSES)
|
|
354
|
+
throw new Error("macOS process enumeration is incomplete");
|
|
355
|
+
const result = [];
|
|
356
|
+
for (let index = 0;index < count; index++) {
|
|
357
|
+
const candidate = bytes.readInt32LE(index * 4);
|
|
358
|
+
if (candidate <= 1 || candidate === exclude)
|
|
359
|
+
continue;
|
|
360
|
+
const observed = processIdentity(candidate);
|
|
361
|
+
if (observed?.coalition === coalition)
|
|
362
|
+
result.push({ pid: candidate, version: observed.version });
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
function signalMembers(members, signal) {
|
|
367
|
+
if (!Object.hasOwn(signals, signal))
|
|
368
|
+
throw new TypeError("invalid macOS ownership signal");
|
|
369
|
+
const { ptr, read, symbols } = calls();
|
|
370
|
+
let count = 0;
|
|
371
|
+
for (const member of members) {
|
|
372
|
+
const token = Buffer.alloc(32);
|
|
373
|
+
token.writeUInt32LE(member.pid, 20);
|
|
374
|
+
token.writeUInt32LE(member.version, 28);
|
|
375
|
+
const result = symbols.proc_signal_with_audittoken(ptr(token), signals[signal]);
|
|
376
|
+
if (result !== 0 && read.i32(symbols.__error()) !== 3)
|
|
377
|
+
throw new Error("macOS owned process signaling failed");
|
|
378
|
+
if (result === 0)
|
|
379
|
+
count++;
|
|
380
|
+
}
|
|
381
|
+
return count;
|
|
382
|
+
}
|
|
383
|
+
function requirePrivateMacosCoalition(value) {
|
|
384
|
+
if (value === null || typeof value !== "object" || !authenticCoalitions.has(value))
|
|
385
|
+
throw new TypeError("macOS coalition control is not authentic");
|
|
386
|
+
return value;
|
|
387
|
+
}
|
|
388
|
+
function acquirePrivateMacosCoalition() {
|
|
389
|
+
const pid = process.pid;
|
|
390
|
+
const owner = processIdentity(pid);
|
|
391
|
+
if (owner === undefined || owner.coalition === 0n || usage(owner.coalition).active !== 1n)
|
|
392
|
+
throw new Error("macOS guardian requires an exclusive resource coalition");
|
|
393
|
+
const identity = Object.freeze({
|
|
394
|
+
bootId: bootIdentity(),
|
|
395
|
+
coalition: owner.coalition.toString(),
|
|
396
|
+
guardianPid: pid,
|
|
397
|
+
guardianVersion: owner.version
|
|
398
|
+
});
|
|
399
|
+
const validateOwner = () => {
|
|
400
|
+
const current = processIdentity(process.pid);
|
|
401
|
+
if (process.pid !== pid || current?.coalition !== owner.coalition || current.version !== owner.version)
|
|
402
|
+
throw new Error("macOS guardian identity changed");
|
|
403
|
+
};
|
|
404
|
+
const members = () => {
|
|
405
|
+
validateOwner();
|
|
406
|
+
return coalitionMembers(owner.coalition, pid);
|
|
407
|
+
};
|
|
408
|
+
const control = Object.freeze({
|
|
409
|
+
identity,
|
|
410
|
+
contains(pid2, version) {
|
|
411
|
+
validateOwner();
|
|
412
|
+
if (!Number.isSafeInteger(pid2) || pid2 <= 1 || !Number.isSafeInteger(version) || version < 0)
|
|
413
|
+
return false;
|
|
414
|
+
const current = processIdentity(pid2);
|
|
415
|
+
return current?.version === version && current.coalition === owner.coalition;
|
|
416
|
+
},
|
|
417
|
+
sample() {
|
|
418
|
+
const start = performance.now();
|
|
419
|
+
validateOwner();
|
|
420
|
+
const before = usage(owner.coalition);
|
|
421
|
+
const { ptr, symbols } = calls();
|
|
422
|
+
let footprintBytes = 0n;
|
|
423
|
+
let observedMembers = 0;
|
|
424
|
+
let complete = true;
|
|
425
|
+
for (const member of members()) {
|
|
426
|
+
const bytes = Buffer.alloc(96);
|
|
427
|
+
const read = symbols.proc_pid_rusage(member.pid, 0, ptr(bytes));
|
|
428
|
+
const after2 = processIdentity(member.pid);
|
|
429
|
+
if (read !== 0 || after2?.version !== member.version || after2.coalition !== owner.coalition) {
|
|
430
|
+
complete = false;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
footprintBytes += bytes.readBigUInt64LE(72);
|
|
434
|
+
observedMembers++;
|
|
435
|
+
}
|
|
436
|
+
const after = usage(owner.coalition);
|
|
437
|
+
if (after.cpuNanoseconds < before.cpuNanoseconds)
|
|
438
|
+
throw new Error("macOS CPU accounting moved backwards");
|
|
439
|
+
return Object.freeze({
|
|
440
|
+
...after,
|
|
441
|
+
footprintBytes,
|
|
442
|
+
observedMembers,
|
|
443
|
+
complete: complete && before.active === after.active && after.active === BigInt(observedMembers + 1),
|
|
444
|
+
sampleMilliseconds: performance.now() - start
|
|
445
|
+
});
|
|
446
|
+
},
|
|
447
|
+
signalMembers(signal) {
|
|
448
|
+
return signalMembers(members(), signal);
|
|
449
|
+
},
|
|
450
|
+
empty() {
|
|
451
|
+
validateOwner();
|
|
452
|
+
return usage(owner.coalition).active === 1n;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
authenticCoalitions.add(control);
|
|
456
|
+
return control;
|
|
457
|
+
}
|
|
458
|
+
function platformFor(build) {
|
|
459
|
+
if (process.arch === "x64" && release() === "23.4.0" && build === "23E224")
|
|
460
|
+
return "darwin-x64-23.4.0-23E224";
|
|
461
|
+
if (release() === "24.6.0" && build === "24G830") {
|
|
462
|
+
if (process.arch === "x64")
|
|
463
|
+
return "darwin-x64-24.6.0-24G830";
|
|
464
|
+
if (process.arch === "arm64")
|
|
465
|
+
return "darwin-arm64-24.6.0-24G830";
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/internal/macos-descriptor-files.ts
|
|
470
|
+
var native2;
|
|
471
|
+
var handleClass;
|
|
472
|
+
var CLOEXEC = 16777216;
|
|
473
|
+
var NOFOLLOW_ANY = 536870912;
|
|
474
|
+
function calls2() {
|
|
475
|
+
if (native2 !== undefined)
|
|
476
|
+
return native2;
|
|
477
|
+
privateMacosCurrentProcessIdentity();
|
|
478
|
+
if (process.versions.bun !== "1.4.2")
|
|
479
|
+
throw new Error("macOS descriptor adoption requires the qualified Bun runtime");
|
|
480
|
+
const ffi = createRequire2(import.meta.url)("bun:ffi");
|
|
481
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
482
|
+
const libexec = modulePath.lastIndexOf("/libexec/");
|
|
483
|
+
const bridge = libexec >= 0 ? join2(modulePath.slice(0, libexec), "libexec/macos-descriptor-bridge.dylib") : join2(dirname2(modulePath), modulePath.includes("/internal/") ? "../../support/macos-descriptor-bridge.dylib" : "../support/macos-descriptor-bridge.dylib");
|
|
484
|
+
const canonical = realpathSync2(bridge);
|
|
485
|
+
const metadata = lstatSync2(bridge);
|
|
486
|
+
if (canonical !== bridge || !metadata.isFile() || metadata.size > 1024 * 1024 || (metadata.mode & 18) !== 0 || createHash("sha256").update(readFileSync(bridge)).digest("hex") !== "c8418f5bfc1bd2b346eb962c9379605df12f79b6d7395eefb2fde4ff7ded9b4e")
|
|
487
|
+
throw new Error("native descriptor bridge identity is invalid");
|
|
488
|
+
const declarations = {
|
|
489
|
+
openat: { args: ["i32", "ptr", "i32", "u32"], returns: "i32" },
|
|
490
|
+
fcntl: { args: ["i32", "i32", "u64"], returns: "i32" },
|
|
491
|
+
fstatat: { args: ["i32", "ptr", "ptr", "i32"], returns: "i32" },
|
|
492
|
+
fstatfs: { args: ["i32", "ptr"], returns: "i32" },
|
|
493
|
+
fdopendir: { args: ["i32", "ptr"], returns: "ptr" },
|
|
494
|
+
readdir: { args: ["ptr", "ptr"], returns: "ptr" },
|
|
495
|
+
closedir: { args: ["ptr"], returns: "i32" },
|
|
496
|
+
mkdirat: { args: ["i32", "ptr", "u32"], returns: "i32" },
|
|
497
|
+
unlinkat: { args: ["i32", "ptr", "i32"], returns: "i32" },
|
|
498
|
+
renameatx_np: { args: ["i32", "ptr", "i32", "ptr", "u32"], returns: "i32" },
|
|
499
|
+
readlinkat: { args: ["i32", "ptr", "ptr", "u64"], returns: "i64" },
|
|
500
|
+
symlinkat: { args: ["ptr", "i32", "ptr"], returns: "i32" },
|
|
501
|
+
linkat: { args: ["i32", "ptr", "i32", "ptr", "i32"], returns: "i32" }
|
|
502
|
+
};
|
|
503
|
+
const fixed = ffi.dlopen(bridge, Object.fromEntries(Object.entries(declarations).map(([name, declaration]) => [`jig_${name}`, declaration])));
|
|
504
|
+
const symbols = Object.fromEntries(Object.keys(declarations).map((name) => [name, fixed.symbols[`jig_${name}`]]));
|
|
505
|
+
native2 = { ptr: ffi.ptr, toArrayBuffer: ffi.toArrayBuffer, symbols };
|
|
506
|
+
return native2;
|
|
507
|
+
}
|
|
508
|
+
function checked(result) {
|
|
509
|
+
if (result >= 0)
|
|
510
|
+
return result;
|
|
511
|
+
const code = getSystemErrorName(result);
|
|
512
|
+
throw Object.assign(new Error(`macOS descriptor operation failed (${code})`), { code });
|
|
513
|
+
}
|
|
514
|
+
function nameBytes(value) {
|
|
515
|
+
const bytes = Buffer.from(value);
|
|
516
|
+
if (!bytes.length || bytes.length > 1023 || bytes.includes(0) || bytes.includes(47) || bytes.equals(Buffer.from(".")) || bytes.equals(Buffer.from("..")))
|
|
517
|
+
throw new TypeError("descriptor operation requires one leaf name");
|
|
518
|
+
return Buffer.concat([bytes, Buffer.from([0])]);
|
|
519
|
+
}
|
|
520
|
+
async function handleConstructor() {
|
|
521
|
+
calls2();
|
|
522
|
+
handleClass ??= (async () => {
|
|
523
|
+
const handle = await open("/dev/null", constants2.O_RDONLY | CLOEXEC);
|
|
524
|
+
try {
|
|
525
|
+
return handle.constructor;
|
|
526
|
+
} finally {
|
|
527
|
+
await handle.close();
|
|
528
|
+
}
|
|
529
|
+
})();
|
|
530
|
+
return handleClass;
|
|
531
|
+
}
|
|
532
|
+
async function privateMacosOpenAt(parent, name, flags, mode = 384) {
|
|
533
|
+
const Constructor = await handleConstructor();
|
|
534
|
+
const fd = privateMacosOpenFdAt(parent, name, flags, mode);
|
|
535
|
+
try {
|
|
536
|
+
return new Constructor(fd);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
closeSync2(fd);
|
|
539
|
+
throw error;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function privateMacosOpenFdAt(parent, name, flags, mode = 384) {
|
|
543
|
+
const { ptr, symbols } = calls2();
|
|
544
|
+
const path = nameBytes(name);
|
|
545
|
+
return checked(symbols.openat(parent, ptr(path), flags & ~constants2.O_NOFOLLOW | CLOEXEC | NOFOLLOW_ANY, mode));
|
|
546
|
+
}
|
|
547
|
+
function privateMacosFcntl(fd, command, value = 0) {
|
|
548
|
+
return checked(calls2().symbols.fcntl(fd, command, value));
|
|
549
|
+
}
|
|
550
|
+
function privateMacosFilesystem(fd) {
|
|
551
|
+
const { ptr, symbols } = calls2();
|
|
552
|
+
const bytes = Buffer.alloc(2168);
|
|
553
|
+
checked(symbols.fstatfs(fd, ptr(bytes)));
|
|
554
|
+
const string = (start, size) => {
|
|
555
|
+
const value = bytes.subarray(start, start + size);
|
|
556
|
+
const end = value.indexOf(0);
|
|
557
|
+
if (end < 0)
|
|
558
|
+
throw new Error("macOS filesystem identity is invalid");
|
|
559
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(value.subarray(0, end));
|
|
560
|
+
};
|
|
561
|
+
return Object.freeze({
|
|
562
|
+
capacityBytes: BigInt(bytes.readUInt32LE(0)) * bytes.readBigUInt64LE(8),
|
|
563
|
+
owner: bytes.readUInt32LE(56),
|
|
564
|
+
flags: bytes.readUInt32LE(64),
|
|
565
|
+
subtype: bytes.readUInt32LE(68),
|
|
566
|
+
type: string(72, 16),
|
|
567
|
+
mountpoint: string(88, 1024),
|
|
568
|
+
device: string(1112, 1024)
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
function privateMacosStatAt(parent, name) {
|
|
572
|
+
const { ptr, symbols } = calls2();
|
|
573
|
+
const path = nameBytes(name), bytes = Buffer.alloc(144);
|
|
574
|
+
checked(symbols.fstatat(parent, ptr(path), ptr(bytes), 32));
|
|
575
|
+
const mode = BigInt(bytes.readUInt16LE(4));
|
|
576
|
+
const atimeNs = bytes.readBigInt64LE(32) * 1000000000n + bytes.readBigInt64LE(40);
|
|
577
|
+
const mtimeNs = bytes.readBigInt64LE(48) * 1000000000n + bytes.readBigInt64LE(56);
|
|
578
|
+
const ctimeNs = bytes.readBigInt64LE(64) * 1000000000n + bytes.readBigInt64LE(72);
|
|
579
|
+
const birthtimeNs = bytes.readBigInt64LE(80) * 1000000000n + bytes.readBigInt64LE(88);
|
|
580
|
+
const kind = (type) => (mode & 0xf000n) === type;
|
|
581
|
+
return {
|
|
582
|
+
dev: BigInt(bytes.readInt32LE(0)),
|
|
583
|
+
mode,
|
|
584
|
+
nlink: BigInt(bytes.readUInt16LE(6)),
|
|
585
|
+
ino: bytes.readBigUInt64LE(8),
|
|
586
|
+
uid: BigInt(bytes.readUInt32LE(16)),
|
|
587
|
+
gid: BigInt(bytes.readUInt32LE(20)),
|
|
588
|
+
rdev: BigInt(bytes.readInt32LE(24)),
|
|
589
|
+
size: bytes.readBigInt64LE(96),
|
|
590
|
+
blocks: bytes.readBigInt64LE(104),
|
|
591
|
+
blksize: BigInt(bytes.readInt32LE(112)),
|
|
592
|
+
atimeNs,
|
|
593
|
+
mtimeNs,
|
|
594
|
+
ctimeNs,
|
|
595
|
+
birthtimeNs,
|
|
596
|
+
atimeMs: atimeNs / 1000000n,
|
|
597
|
+
mtimeMs: mtimeNs / 1000000n,
|
|
598
|
+
ctimeMs: ctimeNs / 1000000n,
|
|
599
|
+
birthtimeMs: birthtimeNs / 1000000n,
|
|
600
|
+
atime: new Date(Number(atimeNs / 1000000n)),
|
|
601
|
+
mtime: new Date(Number(mtimeNs / 1000000n)),
|
|
602
|
+
ctime: new Date(Number(ctimeNs / 1000000n)),
|
|
603
|
+
birthtime: new Date(Number(birthtimeNs / 1000000n)),
|
|
604
|
+
isFile: () => kind(0x8000n),
|
|
605
|
+
isDirectory: () => kind(0x4000n),
|
|
606
|
+
isSymbolicLink: () => kind(0xa000n),
|
|
607
|
+
isBlockDevice: () => kind(0x6000n),
|
|
608
|
+
isCharacterDevice: () => kind(0x2000n),
|
|
609
|
+
isFIFO: () => kind(0x1000n),
|
|
610
|
+
isSocket: () => kind(0xc000n)
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
function privateMacosDirectory(fd) {
|
|
614
|
+
const { ptr, symbols, toArrayBuffer } = calls2();
|
|
615
|
+
const dot = Buffer.from(".\x00");
|
|
616
|
+
const duplicate = checked(symbols.openat(fd, ptr(dot), constants2.O_RDONLY | constants2.O_DIRECTORY | CLOEXEC | NOFOLLOW_ANY, 0));
|
|
617
|
+
const error = Buffer.alloc(4);
|
|
618
|
+
const stream = symbols.fdopendir(duplicate, ptr(error));
|
|
619
|
+
if (!stream) {
|
|
620
|
+
const code = error.readInt32LE();
|
|
621
|
+
closeSync2(duplicate);
|
|
622
|
+
throw Object.assign(new Error("macOS directory stream unavailable"), {
|
|
623
|
+
code: getSystemErrorName(-code)
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
let closed = false;
|
|
627
|
+
const read = () => {
|
|
628
|
+
if (closed)
|
|
629
|
+
throw new Error("macOS directory stream is closed");
|
|
630
|
+
for (;; ) {
|
|
631
|
+
const entry = symbols.readdir(stream, ptr(error));
|
|
632
|
+
if (!entry) {
|
|
633
|
+
if (error.readInt32LE() !== 0)
|
|
634
|
+
checked(-error.readInt32LE());
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
const header = Buffer.from(toArrayBuffer(entry, 0, 21));
|
|
638
|
+
const length = header.readUInt16LE(18), recordLength = header.readUInt16LE(16), type = header[20];
|
|
639
|
+
if (length > 1023 || recordLength < 22 + length || recordLength > 1048)
|
|
640
|
+
throw new Error("invalid macOS directory entry");
|
|
641
|
+
const bytes = Buffer.from(new Uint8Array(toArrayBuffer(entry, 21, length + 1)));
|
|
642
|
+
if (bytes[length] !== 0 || bytes.subarray(0, length).includes(0))
|
|
643
|
+
throw new Error("invalid macOS directory name");
|
|
644
|
+
const name = bytes.subarray(0, length);
|
|
645
|
+
if (name.equals(Buffer.from(".")) || name.equals(Buffer.from("..")))
|
|
646
|
+
continue;
|
|
647
|
+
return {
|
|
648
|
+
name,
|
|
649
|
+
parentPath: "",
|
|
650
|
+
isFile: () => type === 8,
|
|
651
|
+
isDirectory: () => type === 4,
|
|
652
|
+
isSymbolicLink: () => type === 10,
|
|
653
|
+
isBlockDevice: () => type === 6,
|
|
654
|
+
isCharacterDevice: () => type === 2,
|
|
655
|
+
isFIFO: () => type === 1,
|
|
656
|
+
isSocket: () => type === 12
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
const close = () => {
|
|
661
|
+
if (closed)
|
|
662
|
+
return;
|
|
663
|
+
closed = true;
|
|
664
|
+
checked(symbols.closedir(stream));
|
|
665
|
+
};
|
|
666
|
+
return {
|
|
667
|
+
readSync: read,
|
|
668
|
+
closeSync: close,
|
|
669
|
+
async read() {
|
|
670
|
+
return read();
|
|
671
|
+
},
|
|
672
|
+
async close() {
|
|
673
|
+
close();
|
|
674
|
+
},
|
|
675
|
+
async* [Symbol.asyncIterator]() {
|
|
676
|
+
try {
|
|
677
|
+
for (;; ) {
|
|
678
|
+
const entry = read();
|
|
679
|
+
if (entry === null)
|
|
680
|
+
break;
|
|
681
|
+
yield entry;
|
|
682
|
+
}
|
|
683
|
+
} finally {
|
|
684
|
+
close();
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
function privateMacosMkdirAt(parent, name, mode) {
|
|
690
|
+
const { ptr, symbols } = calls2(), path = nameBytes(name);
|
|
691
|
+
checked(symbols.mkdirat(parent, ptr(path), mode));
|
|
692
|
+
}
|
|
693
|
+
function privateMacosUnlinkAt(parent, name, directory = false) {
|
|
694
|
+
const { ptr, symbols } = calls2(), path = nameBytes(name);
|
|
695
|
+
checked(symbols.unlinkat(parent, ptr(path), directory ? 128 : 0));
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// src/internal/macos-descriptor-handoff.ts
|
|
699
|
+
var MAX_DESCRIPTORS = 64;
|
|
700
|
+
var CONTROL_BYTES = 4096;
|
|
701
|
+
var MAX_WAIT_MS = 1e4;
|
|
702
|
+
var MAGIC = Buffer.from("JFD1");
|
|
703
|
+
var native3;
|
|
704
|
+
function calls3() {
|
|
705
|
+
if (native3 !== undefined)
|
|
706
|
+
return native3;
|
|
707
|
+
privateMacosCurrentProcessIdentity();
|
|
708
|
+
const ffi = createRequire3(import.meta.url)("bun:ffi");
|
|
709
|
+
const { symbols } = ffi.dlopen("/usr/lib/libSystem.B.dylib", {
|
|
710
|
+
socket: { args: ["i32", "i32", "i32"], returns: "i32" },
|
|
711
|
+
bind: { args: ["i32", "ptr", "u32"], returns: "i32" },
|
|
712
|
+
listen: { args: ["i32", "i32"], returns: "i32" },
|
|
713
|
+
accept: { args: ["i32", "ptr", "ptr"], returns: "i32" },
|
|
714
|
+
connect: { args: ["i32", "ptr", "u32"], returns: "i32" },
|
|
715
|
+
poll: { args: ["ptr", "u32", "i32"], returns: "i32" },
|
|
716
|
+
getsockopt: { args: ["i32", "i32", "i32", "ptr", "ptr"], returns: "i32" },
|
|
717
|
+
sendmsg: { args: ["i32", "ptr", "i32"], returns: "i64" },
|
|
718
|
+
recvmsg: { args: ["i32", "ptr", "i32"], returns: "i64" },
|
|
719
|
+
send: { args: ["i32", "ptr", "u64", "i32"], returns: "i64" },
|
|
720
|
+
recv: { args: ["i32", "ptr", "u64", "i32"], returns: "i64" },
|
|
721
|
+
shutdown: { args: ["i32", "i32"], returns: "i32" },
|
|
722
|
+
__error: { args: [], returns: "ptr" }
|
|
723
|
+
});
|
|
724
|
+
native3 = { symbols, ptr: ffi.ptr, read: ffi.read };
|
|
725
|
+
return native3;
|
|
726
|
+
}
|
|
727
|
+
function errno() {
|
|
728
|
+
const { symbols, read } = calls3();
|
|
729
|
+
return read.i32(symbols.__error());
|
|
730
|
+
}
|
|
731
|
+
function checked2(value) {
|
|
732
|
+
if (value >= 0)
|
|
733
|
+
return value;
|
|
734
|
+
throw Object.assign(new Error("macOS descriptor handoff operation failed"), {
|
|
735
|
+
code: getSystemErrorName2(-errno())
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function socket() {
|
|
739
|
+
const fd = checked2(calls3().symbols.socket(1, 1, 0));
|
|
740
|
+
try {
|
|
741
|
+
configure(fd);
|
|
742
|
+
return fd;
|
|
743
|
+
} catch (error) {
|
|
744
|
+
closeSync3(fd);
|
|
745
|
+
throw error;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function configure(fd) {
|
|
749
|
+
privateMacosFcntl(fd, 2, 1);
|
|
750
|
+
privateMacosFcntl(fd, 4, privateMacosFcntl(fd, 3) | 4);
|
|
751
|
+
if ((privateMacosFcntl(fd, 1) & 1) !== 1 || (privateMacosFcntl(fd, 3) & 4) !== 4)
|
|
752
|
+
throw new Error("macOS descriptor socket flags are unavailable");
|
|
753
|
+
}
|
|
754
|
+
function address(path) {
|
|
755
|
+
const encoded = Buffer.from(path);
|
|
756
|
+
if (!path.startsWith("/") || path.includes("\x00") || encoded.length > 103 || encoded.toString("utf8") !== path)
|
|
757
|
+
throw new Error("macOS descriptor handoff path is invalid");
|
|
758
|
+
const bytes = Buffer.alloc(106);
|
|
759
|
+
bytes[0] = encoded.length + 3;
|
|
760
|
+
bytes[1] = 1;
|
|
761
|
+
encoded.copy(bytes, 2);
|
|
762
|
+
return bytes;
|
|
763
|
+
}
|
|
764
|
+
function authenticate(fd, expected) {
|
|
765
|
+
if (!Number.isSafeInteger(expected.pid) || expected.pid < 1 || !Number.isSafeInteger(expected.version) || expected.version < 1)
|
|
766
|
+
throw new Error("macOS descriptor handoff peer is invalid");
|
|
767
|
+
const actual = privateMacosDescriptorPeerIdentity(fd);
|
|
768
|
+
if (actual.uid !== process.getuid?.() || actual.realUid !== actual.uid || actual.pid !== expected.pid || actual.version !== expected.version)
|
|
769
|
+
throw new Error("macOS descriptor handoff peer does not match");
|
|
770
|
+
}
|
|
771
|
+
function budget(timeoutMs, signal) {
|
|
772
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_WAIT_MS)
|
|
773
|
+
throw new Error("macOS descriptor handoff deadline is invalid");
|
|
774
|
+
const deadline = performance.now() + timeoutMs;
|
|
775
|
+
return async () => {
|
|
776
|
+
signal?.throwIfAborted();
|
|
777
|
+
if (performance.now() >= deadline)
|
|
778
|
+
throw new Error("macOS descriptor handoff timed out");
|
|
779
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
780
|
+
signal?.throwIfAborted();
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
function retry(value) {
|
|
784
|
+
return value < 0 && [4, 35].includes(errno());
|
|
785
|
+
}
|
|
786
|
+
function message(body, control) {
|
|
787
|
+
const { ptr } = calls3();
|
|
788
|
+
const vector = Buffer.alloc(16), header = Buffer.alloc(48);
|
|
789
|
+
vector.writeBigUInt64LE(BigInt(ptr(body)), 0);
|
|
790
|
+
vector.writeBigUInt64LE(BigInt(body.length), 8);
|
|
791
|
+
header.writeBigUInt64LE(BigInt(ptr(vector)), 16);
|
|
792
|
+
header.writeInt32LE(1, 24);
|
|
793
|
+
header.writeBigUInt64LE(BigInt(ptr(control)), 32);
|
|
794
|
+
header.writeUInt32LE(control.length, 40);
|
|
795
|
+
return { header, vector, body, control };
|
|
796
|
+
}
|
|
797
|
+
var receivedBundles = new WeakMap;
|
|
798
|
+
function requirePrivateMacosReceivedDescriptors(bundle) {
|
|
799
|
+
const record = bundle !== null && typeof bundle === "object" ? receivedBundles.get(bundle) : undefined;
|
|
800
|
+
if (record === undefined || record.closed)
|
|
801
|
+
throw new Error("macOS descriptor bundle is not active");
|
|
802
|
+
return record.descriptors;
|
|
803
|
+
}
|
|
804
|
+
async function createPrivateMacosDescriptorReceiver(directory, leaf) {
|
|
805
|
+
privateMacosCurrentProcessIdentity();
|
|
806
|
+
if (!["inputs", "output", "roots"].includes(leaf))
|
|
807
|
+
throw new Error("invalid descriptor handoff role");
|
|
808
|
+
const parent = await open2(directory, constants3.O_RDONLY | constants3.O_DIRECTORY | 536870912 | 16777216);
|
|
809
|
+
let listener;
|
|
810
|
+
let identity;
|
|
811
|
+
let closed = false, receiving = false, consumed = false;
|
|
812
|
+
const path = join3(directory, `fd-${leaf}`);
|
|
813
|
+
const name = `fd-${leaf}`;
|
|
814
|
+
const sameParent = () => {
|
|
815
|
+
const held = fstatSync2(parent.fd, { bigint: true }), visible = lstatSync3(directory, { bigint: true });
|
|
816
|
+
if (!held.isDirectory() || held.dev !== visible.dev || held.ino !== visible.ino || held.uid !== BigInt(process.getuid()) || (held.mode & 0o077n) !== 0n || realpathSync3(directory) !== directory)
|
|
817
|
+
throw new Error("macOS descriptor handoff owner changed");
|
|
818
|
+
};
|
|
819
|
+
const close = async () => {
|
|
820
|
+
if (closed)
|
|
821
|
+
return;
|
|
822
|
+
closed = true;
|
|
823
|
+
try {
|
|
824
|
+
if (listener !== undefined) {
|
|
825
|
+
closeSync3(listener);
|
|
826
|
+
listener = undefined;
|
|
827
|
+
}
|
|
828
|
+
if (identity !== undefined) {
|
|
829
|
+
const entry = privateMacosStatAt(parent.fd, name);
|
|
830
|
+
if (!entry.isSocket() || entry.dev !== identity.dev || entry.ino !== identity.ino)
|
|
831
|
+
throw new Error("macOS descriptor handoff socket changed");
|
|
832
|
+
privateMacosUnlinkAt(parent.fd, name);
|
|
833
|
+
}
|
|
834
|
+
} finally {
|
|
835
|
+
await parent.close();
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
try {
|
|
839
|
+
sameParent();
|
|
840
|
+
const sockaddr = address(path), { symbols, ptr } = calls3();
|
|
841
|
+
listener = socket();
|
|
842
|
+
checked2(symbols.bind(listener, ptr(sockaddr), sockaddr[0]));
|
|
843
|
+
sameParent();
|
|
844
|
+
const entry = privateMacosStatAt(parent.fd, name);
|
|
845
|
+
if (!entry.isSocket() || entry.uid !== BigInt(process.getuid()))
|
|
846
|
+
throw new Error("macOS descriptor handoff socket is invalid");
|
|
847
|
+
identity = { dev: entry.dev, ino: entry.ino };
|
|
848
|
+
checked2(symbols.listen(listener, 1));
|
|
849
|
+
return Object.freeze({
|
|
850
|
+
path,
|
|
851
|
+
close,
|
|
852
|
+
async receive(peer, timeoutMs, signal) {
|
|
853
|
+
if (closed || receiving || consumed)
|
|
854
|
+
throw new Error("macOS descriptor receiver is unavailable");
|
|
855
|
+
const delay = budget(timeoutMs, signal);
|
|
856
|
+
const wait = async () => {
|
|
857
|
+
if (closed)
|
|
858
|
+
throw new Error("macOS descriptor receiver is closed");
|
|
859
|
+
await delay();
|
|
860
|
+
if (closed)
|
|
861
|
+
throw new Error("macOS descriptor receiver is closed");
|
|
862
|
+
};
|
|
863
|
+
receiving = true;
|
|
864
|
+
let connection;
|
|
865
|
+
try {
|
|
866
|
+
for (;; ) {
|
|
867
|
+
signal?.throwIfAborted();
|
|
868
|
+
if (closed)
|
|
869
|
+
throw new Error("macOS descriptor receiver is closed");
|
|
870
|
+
const accepted = symbols.accept(listener, 0, 0);
|
|
871
|
+
if (accepted >= 0) {
|
|
872
|
+
connection = accepted;
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
if (!retry(accepted))
|
|
876
|
+
checked2(accepted);
|
|
877
|
+
await wait();
|
|
878
|
+
}
|
|
879
|
+
configure(connection);
|
|
880
|
+
authenticate(connection, peer);
|
|
881
|
+
consumed = true;
|
|
882
|
+
return await receive(connection, wait);
|
|
883
|
+
} finally {
|
|
884
|
+
if (connection !== undefined)
|
|
885
|
+
closeSync3(connection);
|
|
886
|
+
receiving = false;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
} catch (error) {
|
|
891
|
+
await close();
|
|
892
|
+
throw error;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
async function receive(fd, wait) {
|
|
896
|
+
const { ptr, symbols } = calls3();
|
|
897
|
+
const descriptors = [], body = Buffer.alloc(9);
|
|
898
|
+
let used = 0, delivered = false;
|
|
899
|
+
try {
|
|
900
|
+
for (;; ) {
|
|
901
|
+
const packet = message(body.subarray(used), Buffer.alloc(CONTROL_BYTES));
|
|
902
|
+
const count = Number(symbols.recvmsg(fd, ptr(packet.header), 128));
|
|
903
|
+
if (retry(count)) {
|
|
904
|
+
await wait();
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
checked2(count);
|
|
908
|
+
const controlBytes = packet.header.readUInt32LE(40);
|
|
909
|
+
let offset = 0;
|
|
910
|
+
while (offset + 12 <= controlBytes && controlBytes <= packet.control.length) {
|
|
911
|
+
const length = packet.control.readUInt32LE(offset);
|
|
912
|
+
if (length < 12 || length > controlBytes - offset || (length - 12) % 4 !== 0 || packet.control.readInt32LE(offset + 4) !== 65535 || packet.control.readInt32LE(offset + 8) !== 1)
|
|
913
|
+
throw new Error("invalid macOS descriptor control frame");
|
|
914
|
+
for (let index = offset + 12;index < offset + length; index += 4) {
|
|
915
|
+
const received = packet.control.readInt32LE(index);
|
|
916
|
+
if (received < 0)
|
|
917
|
+
throw new Error("invalid macOS received descriptor");
|
|
918
|
+
descriptors.push(received);
|
|
919
|
+
privateMacosFcntl(received, 2, 1);
|
|
920
|
+
}
|
|
921
|
+
offset += length;
|
|
922
|
+
}
|
|
923
|
+
if (offset !== controlBytes || (packet.header.readInt32LE(44) & ~128) !== 0 || descriptors.length > MAX_DESCRIPTORS)
|
|
924
|
+
throw new Error("truncated macOS descriptor frame");
|
|
925
|
+
used += count;
|
|
926
|
+
if (used > 8)
|
|
927
|
+
throw new Error("trailing macOS descriptor data");
|
|
928
|
+
if (count === 0)
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
931
|
+
if (used !== 8 || !body.subarray(0, 4).equals(MAGIC) || descriptors.length === 0 || descriptors.length !== body.readUInt32LE(4))
|
|
932
|
+
throw new Error("invalid macOS descriptor frame");
|
|
933
|
+
for (const descriptor of descriptors) {
|
|
934
|
+
const info = fstatSync2(descriptor);
|
|
935
|
+
if (!info.isFile() && !info.isDirectory() || (privateMacosFcntl(descriptor, 3) & 3) !== 0)
|
|
936
|
+
throw new Error("macOS handoff requires read-only files or directories");
|
|
937
|
+
}
|
|
938
|
+
const ack = Buffer.from("A");
|
|
939
|
+
if (checked2(Number(symbols.send(fd, ptr(ack), 1, 524288))) !== 1)
|
|
940
|
+
throw new Error("macOS descriptor acknowledgement failed");
|
|
941
|
+
const record = { descriptors: Object.freeze([...descriptors]), closed: false };
|
|
942
|
+
const result = Object.freeze({
|
|
943
|
+
descriptors: record.descriptors,
|
|
944
|
+
close() {
|
|
945
|
+
if (record.closed)
|
|
946
|
+
return;
|
|
947
|
+
record.closed = true;
|
|
948
|
+
const errors = [];
|
|
949
|
+
for (const descriptor of descriptors.splice(0)) {
|
|
950
|
+
try {
|
|
951
|
+
closeSync3(descriptor);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
errors.push(error);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if (errors.length)
|
|
957
|
+
throw new AggregateError(errors, "macOS transferred descriptor closure failed");
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
receivedBundles.set(result, record);
|
|
961
|
+
delivered = true;
|
|
962
|
+
return result;
|
|
963
|
+
} finally {
|
|
964
|
+
if (!delivered)
|
|
965
|
+
for (const descriptor of descriptors.splice(0))
|
|
966
|
+
closeSync3(descriptor);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
async function sendPrivateMacosDescriptors(path, peer, descriptors, timeoutMs, signal) {
|
|
970
|
+
if (!Array.isArray(descriptors) || descriptors.length < 1 || descriptors.length > MAX_DESCRIPTORS || descriptors.some((fd2) => !Number.isSafeInteger(fd2) || fd2 < 0))
|
|
971
|
+
throw new Error("invalid macOS descriptor bundle");
|
|
972
|
+
const selected = [...descriptors], wait = budget(timeoutMs, signal), sockaddr = address(path);
|
|
973
|
+
const { symbols, ptr } = calls3(), fd = socket();
|
|
974
|
+
try {
|
|
975
|
+
signal?.throwIfAborted();
|
|
976
|
+
const connected = symbols.connect(fd, ptr(sockaddr), sockaddr[0]);
|
|
977
|
+
if (connected < 0) {
|
|
978
|
+
if (![4, 35, 36].includes(errno()))
|
|
979
|
+
checked2(connected);
|
|
980
|
+
const poll = Buffer.alloc(8);
|
|
981
|
+
poll.writeInt32LE(fd);
|
|
982
|
+
poll.writeInt16LE(4, 4);
|
|
983
|
+
while (checked2(symbols.poll(ptr(poll), 1, 0)) === 0)
|
|
984
|
+
await wait();
|
|
985
|
+
const error = Buffer.alloc(4), size = Buffer.alloc(4);
|
|
986
|
+
size.writeUInt32LE(4);
|
|
987
|
+
checked2(symbols.getsockopt(fd, 65535, 4103, ptr(error), ptr(size)));
|
|
988
|
+
if (error.readInt32LE() !== 0)
|
|
989
|
+
throw new Error("macOS descriptor connection failed");
|
|
990
|
+
}
|
|
991
|
+
authenticate(fd, peer);
|
|
992
|
+
const body = Buffer.alloc(8), control = Buffer.alloc(12 + selected.length * 4);
|
|
993
|
+
MAGIC.copy(body);
|
|
994
|
+
body.writeUInt32LE(selected.length, 4);
|
|
995
|
+
control.writeUInt32LE(control.length);
|
|
996
|
+
control.writeInt32LE(65535, 4);
|
|
997
|
+
control.writeInt32LE(1, 8);
|
|
998
|
+
selected.forEach((descriptor, index) => {
|
|
999
|
+
control.writeInt32LE(descriptor, 12 + index * 4);
|
|
1000
|
+
});
|
|
1001
|
+
const packet = message(body, control);
|
|
1002
|
+
if (checked2(Number(symbols.sendmsg(fd, ptr(packet.header), 524416))) !== body.length)
|
|
1003
|
+
throw new Error("macOS descriptor send was incomplete");
|
|
1004
|
+
checked2(symbols.shutdown(fd, 1));
|
|
1005
|
+
const ack = Buffer.alloc(2);
|
|
1006
|
+
for (;; ) {
|
|
1007
|
+
const count = Number(symbols.recv(fd, ptr(ack), ack.length, 128));
|
|
1008
|
+
if (retry(count)) {
|
|
1009
|
+
await wait();
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
if (checked2(count) !== 1 || ack[0] !== 65)
|
|
1013
|
+
throw new Error("macOS descriptor receipt is unconfirmed");
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
} finally {
|
|
1017
|
+
closeSync3(fd);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// src/internal/macos-guardian-storage.ts
|
|
1022
|
+
import { constants as constants5 } from "fs";
|
|
1023
|
+
import { lstat, open as open4 } from "fs/promises";
|
|
1024
|
+
import { join as join5, posix as posix3 } from "path";
|
|
1025
|
+
|
|
1026
|
+
// src/internal/macos-volume.ts
|
|
1027
|
+
import { execFile } from "child_process";
|
|
1028
|
+
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
1029
|
+
import { constants as constants4 } from "fs";
|
|
1030
|
+
import { open as open3, realpath } from "fs/promises";
|
|
1031
|
+
import { dirname as dirname3, join as join4, posix as posix2 } from "path";
|
|
1032
|
+
var OPEN_DIRECTORY = constants4.O_RDONLY | constants4.O_DIRECTORY | 536870912 | 16777216;
|
|
1033
|
+
var HEX = /^[0-9a-f]{64}$/;
|
|
1034
|
+
var INTEGER = /^(0|[1-9][0-9]{0,19})$/;
|
|
1035
|
+
var MIB = 1024 * 1024;
|
|
1036
|
+
var IMAGE = "volume.dmg";
|
|
1037
|
+
var INTENT = "volume.json";
|
|
1038
|
+
var IMAGE_RECORD = "volume-image.json";
|
|
1039
|
+
var RETIRE_RECORD = "storage.release.json";
|
|
1040
|
+
function identity(stat) {
|
|
1041
|
+
return { device: String(stat.dev), inode: String(stat.ino) };
|
|
1042
|
+
}
|
|
1043
|
+
function matches(stat, value) {
|
|
1044
|
+
return String(stat.dev) === value.device && String(stat.ino) === value.inode;
|
|
1045
|
+
}
|
|
1046
|
+
function isIdentity(value) {
|
|
1047
|
+
if (value === null || typeof value !== "object")
|
|
1048
|
+
return false;
|
|
1049
|
+
const raw = value;
|
|
1050
|
+
return typeof raw.device === "string" && INTEGER.test(raw.device) && typeof raw.inode === "string" && INTEGER.test(raw.inode) && raw.inode !== "0";
|
|
1051
|
+
}
|
|
1052
|
+
function capacity(bytes) {
|
|
1053
|
+
if (!Number.isSafeInteger(bytes) || bytes < 16 * MIB || bytes > 512 * MIB || bytes % MIB !== 0)
|
|
1054
|
+
throw new TypeError("macOS volume capacity is outside its finite profile");
|
|
1055
|
+
}
|
|
1056
|
+
function canonical(path) {
|
|
1057
|
+
if (typeof path !== "string" || !path.startsWith("/") || posix2.normalize(path) !== path || path === "/" || Buffer.byteLength(path) > 900 || [...path].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127) || Buffer.from(path).toString("utf8") !== path)
|
|
1058
|
+
throw new TypeError("macOS volume requires a canonical private path");
|
|
1059
|
+
}
|
|
1060
|
+
async function privateDirectory2(path) {
|
|
1061
|
+
privateMacosCurrentProcessIdentity();
|
|
1062
|
+
canonical(path);
|
|
1063
|
+
if (await realpath(path) !== path)
|
|
1064
|
+
throw new Error("macOS volume directory is aliased");
|
|
1065
|
+
const directory = await open3(path, OPEN_DIRECTORY);
|
|
1066
|
+
try {
|
|
1067
|
+
const stat = await directory.stat({ bigint: true });
|
|
1068
|
+
if (!stat.isDirectory() || stat.uid !== BigInt(process.getuid()) || (stat.mode & 0o777n) !== 0o700n)
|
|
1069
|
+
throw new Error("macOS volume directory is not private");
|
|
1070
|
+
return directory;
|
|
1071
|
+
} catch (error) {
|
|
1072
|
+
await directory.close();
|
|
1073
|
+
throw error;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
async function sameDirectory(path, expected) {
|
|
1077
|
+
const directory = await privateDirectory2(path);
|
|
1078
|
+
try {
|
|
1079
|
+
if (!matches(await directory.stat({ bigint: true }), expected))
|
|
1080
|
+
throw new Error("macOS volume directory identity changed");
|
|
1081
|
+
} finally {
|
|
1082
|
+
await directory.close();
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
function signature2(token, name, value) {
|
|
1086
|
+
if (!HEX.test(token))
|
|
1087
|
+
throw new TypeError("invalid macOS volume ownership token");
|
|
1088
|
+
return createHmac2("sha256", Buffer.from(token, "hex")).update(`jig-macos-volume\x00${name}\x00`).update(JSON.stringify(value)).digest();
|
|
1089
|
+
}
|
|
1090
|
+
async function writeRecord(parent, name, token, value, signatureName = name) {
|
|
1091
|
+
const mac = signature2(token, signatureName, value).toString("hex");
|
|
1092
|
+
const file = await privateMacosOpenAt(parent.fd, name, constants4.O_WRONLY | constants4.O_CREAT | constants4.O_EXCL);
|
|
1093
|
+
try {
|
|
1094
|
+
await file.writeFile(`${JSON.stringify({ value, mac })}
|
|
1095
|
+
`);
|
|
1096
|
+
await file.sync();
|
|
1097
|
+
await parent.sync();
|
|
1098
|
+
} finally {
|
|
1099
|
+
await file.close();
|
|
1100
|
+
}
|
|
1101
|
+
return mac;
|
|
1102
|
+
}
|
|
1103
|
+
async function readRecord2(parent, name, token) {
|
|
1104
|
+
const file = await privateMacosOpenAt(parent.fd, name, constants4.O_RDONLY | constants4.O_NONBLOCK);
|
|
1105
|
+
try {
|
|
1106
|
+
const before = await file.stat({ bigint: true });
|
|
1107
|
+
if (!before.isFile() || before.uid !== BigInt(process.getuid()) || (before.mode & 0o777n) !== 0o600n || before.nlink !== 1n || before.size < 1n || before.size > 4096n)
|
|
1108
|
+
throw new Error("macOS volume journal is unsafe");
|
|
1109
|
+
const bytes = Buffer.alloc(Number(before.size) + 1);
|
|
1110
|
+
let count = 0;
|
|
1111
|
+
while (count < bytes.length) {
|
|
1112
|
+
const read = await file.read(bytes, count, bytes.length - count, count);
|
|
1113
|
+
if (!read.bytesRead)
|
|
1114
|
+
break;
|
|
1115
|
+
count += read.bytesRead;
|
|
1116
|
+
}
|
|
1117
|
+
const after = await file.stat({ bigint: true });
|
|
1118
|
+
if (BigInt(count) !== before.size || before.size !== after.size || after.nlink !== 1n || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs)
|
|
1119
|
+
throw new Error("macOS volume journal changed");
|
|
1120
|
+
const parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, count)));
|
|
1121
|
+
if (parsed === null || typeof parsed !== "object" || Object.keys(parsed).sort().join() !== "mac,value" || typeof parsed.mac !== "string" || !HEX.test(parsed.mac) || !timingSafeEqual2(signature2(token, name, parsed.value), Buffer.from(parsed.mac, "hex")))
|
|
1122
|
+
throw new Error("macOS volume ownership authentication failed");
|
|
1123
|
+
return parsed;
|
|
1124
|
+
} finally {
|
|
1125
|
+
await file.close();
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
function systemTool(tool, args, input, signal) {
|
|
1129
|
+
signal?.throwIfAborted();
|
|
1130
|
+
return new Promise((resolve, reject) => {
|
|
1131
|
+
const child = execFile(tool, args, {
|
|
1132
|
+
cwd: "/",
|
|
1133
|
+
env: { PATH: "/usr/bin:/bin", LC_ALL: "C" },
|
|
1134
|
+
encoding: "buffer",
|
|
1135
|
+
timeout: 30000,
|
|
1136
|
+
killSignal: "SIGKILL",
|
|
1137
|
+
maxBuffer: MIB,
|
|
1138
|
+
signal
|
|
1139
|
+
}, (error, stdout) => {
|
|
1140
|
+
if (error)
|
|
1141
|
+
reject(new Error(`macOS volume ${tool === "/usr/bin/hdiutil" ? args[0] ?? "operation" : "plist conversion"} failed`, { cause: error }));
|
|
1142
|
+
else
|
|
1143
|
+
resolve(stdout);
|
|
1144
|
+
});
|
|
1145
|
+
child.stdin?.on("error", () => {
|
|
1146
|
+
return;
|
|
1147
|
+
});
|
|
1148
|
+
child.stdin?.end(input);
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
async function information(signal) {
|
|
1152
|
+
const plist = await systemTool("/usr/bin/hdiutil", ["info", "-plist"], undefined, signal);
|
|
1153
|
+
const json = await systemTool("/usr/bin/plutil", ["-convert", "json", "-o", "-", "-"], plist, signal);
|
|
1154
|
+
const parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(json));
|
|
1155
|
+
if (!Array.isArray(parsed?.images) || parsed.images.length > 1024 || parsed.images.some((value) => value === null || typeof value !== "object"))
|
|
1156
|
+
throw new Error("macOS image inventory is invalid");
|
|
1157
|
+
return parsed.images;
|
|
1158
|
+
}
|
|
1159
|
+
async function attachedDeviceFromResult(plist, allocation, signal) {
|
|
1160
|
+
const json = await systemTool("/usr/bin/plutil", ["-convert", "json", "-o", "-", "-"], plist, signal);
|
|
1161
|
+
const parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(json));
|
|
1162
|
+
const entities = parsed?.["system-entities"];
|
|
1163
|
+
if (!Array.isArray(entities) || entities.length !== 1) {
|
|
1164
|
+
throw new Error("macOS volume attachment result is invalid");
|
|
1165
|
+
}
|
|
1166
|
+
const entity = entities[0];
|
|
1167
|
+
if (entity === null || typeof entity !== "object" || typeof entity["dev-entry"] !== "string" || !/^\/dev\/disk[1-9][0-9]{0,5}$/.test(entity["dev-entry"]) || entity["mount-point"] !== allocation.mount.path || entity["volume-kind"] !== "hfs")
|
|
1168
|
+
throw new Error("macOS volume attachment does not match its allocation");
|
|
1169
|
+
return entity["dev-entry"];
|
|
1170
|
+
}
|
|
1171
|
+
async function attached(controlPath, allocation, signal) {
|
|
1172
|
+
const imagePath = join4(controlPath, IMAGE);
|
|
1173
|
+
const images = await information(signal);
|
|
1174
|
+
const candidates = images.filter((value) => value["image-path"] === imagePath);
|
|
1175
|
+
if (candidates.length === 0)
|
|
1176
|
+
return;
|
|
1177
|
+
const item = candidates[0];
|
|
1178
|
+
const entities = item["system-entities"];
|
|
1179
|
+
if (candidates.length !== 1 || item["owner-uid"] !== process.getuid() || item.blocksize !== 512 || item.blockcount !== allocation.bytes / 512 || item["image-encrypted"] !== false || !Array.isArray(entities) || entities.length !== 1)
|
|
1180
|
+
throw new Error("macOS image attachment does not match its allocation");
|
|
1181
|
+
const entity = entities[0];
|
|
1182
|
+
if (entity === null || typeof entity !== "object" || typeof entity["dev-entry"] !== "string" || !/^\/dev\/disk[1-9][0-9]{0,5}$/.test(entity["dev-entry"]) || entity["mount-point"] !== undefined && entity["mount-point"] !== allocation.mount.path)
|
|
1183
|
+
throw new Error("macOS volume device does not match its allocation");
|
|
1184
|
+
return entity["dev-entry"];
|
|
1185
|
+
}
|
|
1186
|
+
async function imageFile(parent, bytes, expected) {
|
|
1187
|
+
const image = await privateMacosOpenAt(parent.fd, IMAGE, constants4.O_RDONLY | constants4.O_NONBLOCK);
|
|
1188
|
+
try {
|
|
1189
|
+
const stat = await image.stat({ bigint: true });
|
|
1190
|
+
if (!stat.isFile() || stat.uid !== BigInt(process.getuid()) || stat.nlink !== 1n || stat.size > BigInt(bytes) || expected !== undefined && (!matches(stat, expected) || stat.size !== BigInt(bytes) || (stat.mode & 0o777n) !== 0o600n))
|
|
1191
|
+
throw new Error("macOS volume backing identity changed");
|
|
1192
|
+
return image;
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
await image.close();
|
|
1195
|
+
throw error;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
function validateAllocation(value) {
|
|
1199
|
+
const raw = value;
|
|
1200
|
+
if (raw === null || typeof raw !== "object" || Object.keys(raw).sort().join() !== "bytes,control,mount" || !isIdentity(raw.control) || Object.keys(raw.control).sort().join() !== "device,inode" || !isIdentity(raw.mount) || Object.keys(raw.mount).sort().join() !== "device,inode,path")
|
|
1201
|
+
throw new Error("invalid macOS volume allocation");
|
|
1202
|
+
canonical(raw.mount.path);
|
|
1203
|
+
capacity(raw.bytes);
|
|
1204
|
+
return raw;
|
|
1205
|
+
}
|
|
1206
|
+
async function attachPrivateMacosVolume(controlPath, token, signal) {
|
|
1207
|
+
const parent = await privateDirectory2(controlPath);
|
|
1208
|
+
let mounted;
|
|
1209
|
+
try {
|
|
1210
|
+
signal?.throwIfAborted();
|
|
1211
|
+
const intent = await readRecord2(parent, INTENT, token);
|
|
1212
|
+
const allocation = validateAllocation(intent.value);
|
|
1213
|
+
const allocationMac = intent.mac;
|
|
1214
|
+
const bytes = allocation.bytes, mountPath = allocation.mount.path;
|
|
1215
|
+
if (!matches(await parent.stat({ bigint: true }), allocation.control))
|
|
1216
|
+
throw new Error("macOS volume control directory changed");
|
|
1217
|
+
for await (const entry of privateMacosDirectory(parent.fd))
|
|
1218
|
+
if (entry.name.toString("utf8") !== INTENT)
|
|
1219
|
+
throw new Error("macOS volume allocation has already been used");
|
|
1220
|
+
await sameDirectory(mountPath, allocation.mount);
|
|
1221
|
+
await sameDirectory(controlPath, allocation.control);
|
|
1222
|
+
await systemTool("/usr/bin/hdiutil", [
|
|
1223
|
+
"create",
|
|
1224
|
+
"-sectors",
|
|
1225
|
+
String(bytes / 512),
|
|
1226
|
+
"-fs",
|
|
1227
|
+
"Case-sensitive HFS+",
|
|
1228
|
+
"-volname",
|
|
1229
|
+
`Jig-${token.slice(0, 16)}`,
|
|
1230
|
+
"-type",
|
|
1231
|
+
"UDIF",
|
|
1232
|
+
"-layout",
|
|
1233
|
+
"NONE",
|
|
1234
|
+
"-nospotlight",
|
|
1235
|
+
join4(controlPath, IMAGE)
|
|
1236
|
+
], undefined, signal);
|
|
1237
|
+
const image = await imageFile(parent, bytes);
|
|
1238
|
+
let recordedImage;
|
|
1239
|
+
try {
|
|
1240
|
+
await image.chmod(384);
|
|
1241
|
+
await image.sync();
|
|
1242
|
+
const stat2 = await image.stat({ bigint: true });
|
|
1243
|
+
if (stat2.size !== BigInt(bytes))
|
|
1244
|
+
throw new Error("macOS volume capacity changed");
|
|
1245
|
+
recordedImage = { ...identity(stat2), allocationMac };
|
|
1246
|
+
await writeRecord(parent, IMAGE_RECORD, token, recordedImage);
|
|
1247
|
+
} finally {
|
|
1248
|
+
await image.close();
|
|
1249
|
+
}
|
|
1250
|
+
await sameDirectory(controlPath, allocation.control);
|
|
1251
|
+
await sameDirectory(mountPath, allocation.mount);
|
|
1252
|
+
const attachment = await systemTool("/usr/bin/hdiutil", [
|
|
1253
|
+
"attach",
|
|
1254
|
+
"-kernel",
|
|
1255
|
+
"-nobrowse",
|
|
1256
|
+
"-noautoopen",
|
|
1257
|
+
"-noautofsck",
|
|
1258
|
+
"-owners",
|
|
1259
|
+
"on",
|
|
1260
|
+
"-mountpoint",
|
|
1261
|
+
mountPath,
|
|
1262
|
+
"-mount",
|
|
1263
|
+
"required",
|
|
1264
|
+
"-plist",
|
|
1265
|
+
join4(controlPath, IMAGE)
|
|
1266
|
+
], undefined, signal);
|
|
1267
|
+
const device = await attachedDeviceFromResult(attachment, allocation, signal);
|
|
1268
|
+
const checkedImage = await imageFile(parent, bytes, recordedImage);
|
|
1269
|
+
await checkedImage.close();
|
|
1270
|
+
await sameDirectory(controlPath, allocation.control);
|
|
1271
|
+
mounted = await open3(mountPath, OPEN_DIRECTORY);
|
|
1272
|
+
const filesystem = privateMacosFilesystem(mounted.fd);
|
|
1273
|
+
const stat = await mounted.stat({ bigint: true });
|
|
1274
|
+
if (filesystem.device !== device || filesystem.mountpoint !== mountPath || filesystem.type !== "hfs" || filesystem.capacityBytes < 1n || filesystem.capacityBytes > BigInt(bytes) || filesystem.owner !== process.getuid() || (filesystem.flags & 2097153) !== 0 || (filesystem.flags & 24) !== 24 || stat.dev === BigInt(allocation.mount.device))
|
|
1275
|
+
throw new Error("macOS volume mount enforcement is unavailable");
|
|
1276
|
+
await mounted.chmod(448);
|
|
1277
|
+
await mounted.sync();
|
|
1278
|
+
const result = Object.freeze({
|
|
1279
|
+
path: mountPath,
|
|
1280
|
+
directory: mounted,
|
|
1281
|
+
capacityBytes: Number(filesystem.capacityBytes)
|
|
1282
|
+
});
|
|
1283
|
+
mounted = undefined;
|
|
1284
|
+
return result;
|
|
1285
|
+
} finally {
|
|
1286
|
+
await mounted?.close();
|
|
1287
|
+
await parent.close();
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
async function recoverPrivateMacosVolume(controlPath, token, signal) {
|
|
1291
|
+
signal?.throwIfAborted();
|
|
1292
|
+
const parent = await privateDirectory2(controlPath);
|
|
1293
|
+
try {
|
|
1294
|
+
let intent;
|
|
1295
|
+
try {
|
|
1296
|
+
intent = await readRecord2(parent, INTENT, token);
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
if (error.code !== "ENOENT")
|
|
1299
|
+
throw error;
|
|
1300
|
+
const owner = await privateDirectory2(dirname3(controlPath));
|
|
1301
|
+
try {
|
|
1302
|
+
const retired = validateAllocation((await readRecord2(owner, RETIRE_RECORD, token)).value);
|
|
1303
|
+
intent = { value: retired, mac: signature2(token, INTENT, retired).toString("hex") };
|
|
1304
|
+
} finally {
|
|
1305
|
+
await owner.close();
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
const allocation = validateAllocation(intent.value);
|
|
1309
|
+
if (!matches(await parent.stat({ bigint: true }), allocation.control))
|
|
1310
|
+
throw new Error("macOS volume control directory changed");
|
|
1311
|
+
let recordedImage;
|
|
1312
|
+
try {
|
|
1313
|
+
const record = await readRecord2(parent, IMAGE_RECORD, token);
|
|
1314
|
+
const value = record.value;
|
|
1315
|
+
if (!isIdentity(value) || Object.keys(value).sort().join() !== "allocationMac,device,inode" || value.allocationMac !== intent.mac)
|
|
1316
|
+
throw new Error("macOS volume backing journal is invalid");
|
|
1317
|
+
recordedImage = value;
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
if (error.code !== "ENOENT")
|
|
1320
|
+
throw error;
|
|
1321
|
+
}
|
|
1322
|
+
let image;
|
|
1323
|
+
try {
|
|
1324
|
+
try {
|
|
1325
|
+
image = await imageFile(parent, allocation.bytes, recordedImage);
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
if (error.code !== "ENOENT")
|
|
1328
|
+
throw error;
|
|
1329
|
+
}
|
|
1330
|
+
const device = await attached(controlPath, allocation, signal);
|
|
1331
|
+
if (device !== undefined) {
|
|
1332
|
+
if (recordedImage === undefined || image === undefined)
|
|
1333
|
+
throw new Error("macOS attached image has no authenticated backing");
|
|
1334
|
+
await sameDirectory(controlPath, allocation.control);
|
|
1335
|
+
if (!matches(privateMacosStatAt(parent.fd, IMAGE), recordedImage))
|
|
1336
|
+
throw new Error("macOS volume backing changed before detach");
|
|
1337
|
+
await systemTool("/usr/bin/hdiutil", ["detach", device], undefined, signal);
|
|
1338
|
+
await sameDirectory(allocation.mount.path, allocation.mount);
|
|
1339
|
+
}
|
|
1340
|
+
try {
|
|
1341
|
+
await sameDirectory(allocation.mount.path, allocation.mount);
|
|
1342
|
+
const mountParent = await open3(dirname3(allocation.mount.path), OPEN_DIRECTORY);
|
|
1343
|
+
try {
|
|
1344
|
+
const name = posix2.basename(allocation.mount.path);
|
|
1345
|
+
if (!matches(privateMacosStatAt(mountParent.fd, name), allocation.mount))
|
|
1346
|
+
throw new Error("macOS volume mount allocation changed");
|
|
1347
|
+
privateMacosUnlinkAt(mountParent.fd, name, true);
|
|
1348
|
+
await mountParent.sync();
|
|
1349
|
+
} finally {
|
|
1350
|
+
await mountParent.close();
|
|
1351
|
+
}
|
|
1352
|
+
} catch (error) {
|
|
1353
|
+
if (error.code !== "ENOENT")
|
|
1354
|
+
throw error;
|
|
1355
|
+
}
|
|
1356
|
+
if (image !== undefined) {
|
|
1357
|
+
const current = privateMacosStatAt(parent.fd, IMAGE);
|
|
1358
|
+
if (!matches(current, identity(await image.stat({ bigint: true }))))
|
|
1359
|
+
throw new Error("macOS volume backing changed before removal");
|
|
1360
|
+
privateMacosUnlinkAt(parent.fd, IMAGE);
|
|
1361
|
+
await parent.sync();
|
|
1362
|
+
}
|
|
1363
|
+
} finally {
|
|
1364
|
+
await image?.close();
|
|
1365
|
+
}
|
|
1366
|
+
} finally {
|
|
1367
|
+
await parent.close();
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// src/internal/macos-guardian-storage.ts
|
|
1372
|
+
var DIRECTORY = constants5.O_RDONLY | constants5.O_DIRECTORY | 536870912 | 16777216;
|
|
1373
|
+
var controlPath = (owner) => join5(owner, "storage");
|
|
1374
|
+
function requirePrivateMacosGuardianStorage(storage, files, cwd) {
|
|
1375
|
+
if (storage === null || typeof storage !== "object" || Object.keys(storage).sort().join() !== "bytes,collect,mountPath" || typeof storage.mountPath !== "string" || !storage.mountPath.startsWith("/") || storage.mountPath === "/" || posix3.normalize(storage.mountPath) !== storage.mountPath || !Number.isSafeInteger(storage.bytes) || storage.bytes < 16 * 1024 * 1024 || storage.bytes > 512 * 1024 * 1024 || storage.bytes % (1024 * 1024) !== 0 || ![null, "output", "work"].includes(storage.collect))
|
|
1376
|
+
throw new TypeError("invalid macOS guardian storage");
|
|
1377
|
+
const roots = ["work", "tmp", "output"].map((name) => join5(storage.mountPath, name));
|
|
1378
|
+
if (files.writableTrees.some((path) => !roots.includes(path)) || !roots.some((root) => cwd === root || cwd.startsWith(`${root}/`)))
|
|
1379
|
+
throw new TypeError("macOS writable projection is outside bounded storage");
|
|
1380
|
+
}
|
|
1381
|
+
async function preparePrivateMacosGuardianStorage(owner, token, storage, signal) {
|
|
1382
|
+
const volume = await attachPrivateMacosVolume(controlPath(owner), token, signal);
|
|
1383
|
+
try {
|
|
1384
|
+
signal.throwIfAborted();
|
|
1385
|
+
if (volume.path !== storage.mountPath || volume.capacityBytes > storage.bytes)
|
|
1386
|
+
throw new Error("macOS guardian storage allocation changed");
|
|
1387
|
+
for (const name of ["work", "tmp", "output", "inputs"])
|
|
1388
|
+
privateMacosMkdirAt(volume.directory.fd, name, 448);
|
|
1389
|
+
const collector = storage.collect === null ? undefined : await privateMacosOpenAt(volume.directory.fd, storage.collect, DIRECTORY);
|
|
1390
|
+
return Object.freeze({ directory: volume.directory, collector });
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
await volume.directory.close();
|
|
1393
|
+
throw error;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
async function recoverPrivateMacosGuardianStorage(owner, token, signal) {
|
|
1397
|
+
try {
|
|
1398
|
+
await lstat(controlPath(owner));
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
if (error.code === "ENOENT")
|
|
1401
|
+
return;
|
|
1402
|
+
throw error;
|
|
1403
|
+
}
|
|
1404
|
+
await recoverPrivateMacosVolume(controlPath(owner), token, signal);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// src/internal/macos-input-projection.ts
|
|
1408
|
+
import { constants as constants6, fstatSync as fstatSync3, readSync as readSync2 } from "fs";
|
|
1409
|
+
|
|
1410
|
+
// src/internal/file-input-policy.ts
|
|
1411
|
+
import { createHash as createHash2 } from "crypto";
|
|
1412
|
+
var PRIVATE_FILE_LIMITS = Object.freeze({
|
|
1413
|
+
bytes: 8 * 1024 * 1024,
|
|
1414
|
+
files: 64,
|
|
1415
|
+
entries: 256,
|
|
1416
|
+
pathBytes: 512,
|
|
1417
|
+
depth: 16,
|
|
1418
|
+
captureMs: 1e4,
|
|
1419
|
+
deliveryMs: 20000
|
|
1420
|
+
});
|
|
1421
|
+
var PRIVATE_CAPTURE_LIMITS = Object.freeze({
|
|
1422
|
+
input: PRIVATE_FILE_LIMITS.bytes,
|
|
1423
|
+
output: 16 * 1024 * 1024
|
|
1424
|
+
});
|
|
1425
|
+
var NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
1426
|
+
var protectedParts = new Set([".jig"]);
|
|
1427
|
+
var FILE_CONTROLS = /[\x00-\x1f\x7f]/;
|
|
1428
|
+
|
|
1429
|
+
class PrivateFileInputError extends Error {
|
|
1430
|
+
reason;
|
|
1431
|
+
limit;
|
|
1432
|
+
constructor(reason, limit) {
|
|
1433
|
+
super({
|
|
1434
|
+
filesystem: process.platform === "darwin" ? "use a local APFS or HFS+ filesystem" : "use a local ext4, XFS, Btrfs, or tmpfs filesystem",
|
|
1435
|
+
native: process.platform === "darwin" ? "Jig could not load its native Mac file controls; check the supported-host requirements" : "Jig could not load its Linux file controls; check the supported glibc loader and libc installation",
|
|
1436
|
+
mount: "the selected attachment mount could not be identified; select an available local directory",
|
|
1437
|
+
symlink: "the selected attachment contains a symbolic link; select a directory and files without links",
|
|
1438
|
+
missing: "the selected attachment directory or file does not exist; check --attach and --select",
|
|
1439
|
+
access: "the selected attachment cannot be read by the current operator; check its file and directory permissions",
|
|
1440
|
+
"cross-mount": "the selected attachment crosses a mount boundary; select each mounted directory separately",
|
|
1441
|
+
kernel: "the kernel does not provide the required file controls; check the supported-host requirements",
|
|
1442
|
+
path: "select a relative path without traversal or links, within 16 components and 512 UTF-8 bytes",
|
|
1443
|
+
protected: "Jig state and host control files cannot be selected as input",
|
|
1444
|
+
linked: "select only singly linked regular files",
|
|
1445
|
+
"input-link": "the selected --input file cannot be a symbolic link",
|
|
1446
|
+
regular: "select a regular input file, not a directory or special file",
|
|
1447
|
+
changed: "selected input changed during capture; capture a stable source tree",
|
|
1448
|
+
"input-changed": "the selected --input file changed during capture; retry with a stable file",
|
|
1449
|
+
bytes: `selected file exceeds the remaining ${limit ?? 0}-byte input budget; select less data`,
|
|
1450
|
+
files: "input exceeds 64 files; narrow the selection with --select",
|
|
1451
|
+
entries: "input exceeds 256 tree entries; narrow the selection with --select",
|
|
1452
|
+
deadline: "input capture exceeded its 10-second budget; select a smaller local tree"
|
|
1453
|
+
}[reason]);
|
|
1454
|
+
this.reason = reason;
|
|
1455
|
+
this.limit = limit;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
function privateFilePath(value) {
|
|
1459
|
+
if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value) > PRIVATE_FILE_LIMITS.pathBytes || Buffer.from(value).toString("utf8") !== value || value.includes("\\") || FILE_CONTROLS.test(value) || value.split("/").some((part) => !part || part === "." || part === ".." || protectedParts.has(process.platform === "darwin" ? part.toLowerCase() : part)) || value.split("/").length > PRIVATE_FILE_LIMITS.depth)
|
|
1460
|
+
throw new PrivateFileInputError("path");
|
|
1461
|
+
return value;
|
|
1462
|
+
}
|
|
1463
|
+
function privateAttachmentName(value) {
|
|
1464
|
+
if (typeof value !== "string" || value.length > 64 || !NAME.test(value))
|
|
1465
|
+
throw new TypeError("invalid attachment name");
|
|
1466
|
+
return value;
|
|
1467
|
+
}
|
|
1468
|
+
function sha256(bytes) {
|
|
1469
|
+
return `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
// src/internal/macos-input-projection.ts
|
|
1473
|
+
var DIRECTORY2 = constants6.O_RDONLY | constants6.O_DIRECTORY | 536870912 | 16777216;
|
|
1474
|
+
function normalizePrivateMacosInputDirectories(value) {
|
|
1475
|
+
if (!Array.isArray(value) || value.length > 8 || new Set(value).size !== value.length)
|
|
1476
|
+
throw new TypeError("invalid macOS input directories");
|
|
1477
|
+
return Object.freeze(value.map((name) => privateFilePath(privateAttachmentName(name))));
|
|
1478
|
+
}
|
|
1479
|
+
function normalizePrivateMacosInputs(value, inputDirectories) {
|
|
1480
|
+
if (!Array.isArray(value) || value.length > PRIVATE_FILE_LIMITS.files)
|
|
1481
|
+
throw new TypeError("invalid macOS input manifest");
|
|
1482
|
+
const roots = new Set(normalizePrivateMacosInputDirectories(inputDirectories));
|
|
1483
|
+
let bytes = 0;
|
|
1484
|
+
const names = new Set, directories = new Set;
|
|
1485
|
+
const files = value.map((file) => {
|
|
1486
|
+
const entry = file;
|
|
1487
|
+
if (entry === null || typeof entry !== "object" || Object.keys(entry).sort().join() !== "bytes,digest,path" || !Number.isSafeInteger(entry.bytes) || entry.bytes < 0 || typeof entry.digest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(entry.digest))
|
|
1488
|
+
throw new TypeError("invalid macOS input identity");
|
|
1489
|
+
if (typeof entry.path !== "string")
|
|
1490
|
+
throw new TypeError("invalid macOS input path");
|
|
1491
|
+
const separator = entry.path.indexOf("/");
|
|
1492
|
+
if (separator < 1 || !roots.has(entry.path.slice(0, separator)))
|
|
1493
|
+
throw new TypeError("macOS input is outside its declared root");
|
|
1494
|
+
privateFilePath(entry.path.slice(separator + 1));
|
|
1495
|
+
const path = entry.path;
|
|
1496
|
+
bytes += entry.bytes;
|
|
1497
|
+
if (names.has(path) || bytes > PRIVATE_FILE_LIMITS.bytes)
|
|
1498
|
+
throw new TypeError("macOS captured input exceeds its bounds");
|
|
1499
|
+
names.add(path);
|
|
1500
|
+
const parts = path.split("/");
|
|
1501
|
+
for (let count = 2;count < parts.length; count++)
|
|
1502
|
+
directories.add(parts.slice(0, count).join("/"));
|
|
1503
|
+
return Object.freeze({ path, bytes: entry.bytes, digest: entry.digest });
|
|
1504
|
+
});
|
|
1505
|
+
if (names.size + directories.size > PRIVATE_FILE_LIMITS.entries || [...names].some((name) => directories.has(name)))
|
|
1506
|
+
throw new TypeError("macOS input paths conflict or exceed their entry bound");
|
|
1507
|
+
return Object.freeze(files);
|
|
1508
|
+
}
|
|
1509
|
+
async function projectPrivateMacosInputs(volume, manifest, inputDirectories, bundle, signal) {
|
|
1510
|
+
const roots = normalizePrivateMacosInputDirectories(inputDirectories);
|
|
1511
|
+
const files = normalizePrivateMacosInputs(manifest, roots);
|
|
1512
|
+
const descriptors = bundle === undefined ? [] : requirePrivateMacosReceivedDescriptors(bundle);
|
|
1513
|
+
if (files.length !== descriptors.length)
|
|
1514
|
+
throw new Error("macOS input handoff count changed");
|
|
1515
|
+
const root = await privateMacosOpenAt(volume.fd, "inputs", DIRECTORY2);
|
|
1516
|
+
const directories = new Map([["", root]]);
|
|
1517
|
+
const failures = [];
|
|
1518
|
+
try {
|
|
1519
|
+
for (const name of roots) {
|
|
1520
|
+
signal.throwIfAborted();
|
|
1521
|
+
privateMacosMkdirAt(root.fd, name, 448);
|
|
1522
|
+
directories.set(name, await privateMacosOpenAt(root.fd, name, DIRECTORY2));
|
|
1523
|
+
}
|
|
1524
|
+
for (const [index, file] of files.entries()) {
|
|
1525
|
+
signal.throwIfAborted();
|
|
1526
|
+
const fd = descriptors[index];
|
|
1527
|
+
const before = fstatSync3(fd, { bigint: true });
|
|
1528
|
+
if (!before.isFile() || before.nlink !== 0n || before.size !== BigInt(file.bytes))
|
|
1529
|
+
throw new Error("macOS transferred input is not the declared anonymous file");
|
|
1530
|
+
const bytes = Buffer.alloc(file.bytes);
|
|
1531
|
+
try {
|
|
1532
|
+
for (let offset = 0;offset < bytes.length; ) {
|
|
1533
|
+
const count = readSync2(fd, bytes, offset, bytes.length - offset, offset);
|
|
1534
|
+
if (count === 0)
|
|
1535
|
+
throw new Error("macOS transferred input ended early");
|
|
1536
|
+
offset += count;
|
|
1537
|
+
}
|
|
1538
|
+
const after = fstatSync3(fd, { bigint: true });
|
|
1539
|
+
if (sha256(bytes) !== file.digest || before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || after.nlink !== 0n || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs)
|
|
1540
|
+
throw new Error("macOS transferred input identity changed");
|
|
1541
|
+
const parts = file.path.split("/");
|
|
1542
|
+
let parent = root;
|
|
1543
|
+
for (let count = 1;count < parts.length; count++) {
|
|
1544
|
+
const key = parts.slice(0, count).join("/");
|
|
1545
|
+
let directory = directories.get(key);
|
|
1546
|
+
if (directory === undefined) {
|
|
1547
|
+
privateMacosMkdirAt(parent.fd, parts[count - 1], 448);
|
|
1548
|
+
directory = await privateMacosOpenAt(parent.fd, parts[count - 1], DIRECTORY2);
|
|
1549
|
+
directories.set(key, directory);
|
|
1550
|
+
}
|
|
1551
|
+
parent = directory;
|
|
1552
|
+
}
|
|
1553
|
+
const output = await privateMacosOpenAt(parent.fd, parts.at(-1), constants6.O_WRONLY | constants6.O_CREAT | constants6.O_EXCL, 384);
|
|
1554
|
+
try {
|
|
1555
|
+
await output.writeFile(bytes);
|
|
1556
|
+
await output.chmod(292);
|
|
1557
|
+
} finally {
|
|
1558
|
+
await output.close();
|
|
1559
|
+
}
|
|
1560
|
+
} finally {
|
|
1561
|
+
bytes.fill(0);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
for (const directory of [...directories.values()].reverse())
|
|
1565
|
+
await directory.chmod(365);
|
|
1566
|
+
signal.throwIfAborted();
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
failures.push(error);
|
|
1569
|
+
}
|
|
1570
|
+
const results = await Promise.allSettled([...directories.values()].map((directory) => directory.close()));
|
|
1571
|
+
for (const result of results)
|
|
1572
|
+
if (result.status === "rejected")
|
|
1573
|
+
failures.push(result.reason);
|
|
1574
|
+
if (failures.length)
|
|
1575
|
+
throw new AggregateError(failures, "macOS input projection failed");
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// src/internal/macos-output-policy.ts
|
|
1579
|
+
var PRIVATE_MACOS_MAX_OUTPUT_BYTES = 128 * 1024 * 1024;
|
|
1580
|
+
|
|
1581
|
+
// src/internal/macos-scope-execution.ts
|
|
1582
|
+
import { spawn } from "child_process";
|
|
1583
|
+
|
|
1584
|
+
// src/internal/macos-sandbox-profile.ts
|
|
1585
|
+
import { posix as posix4 } from "path";
|
|
1586
|
+
var SYSTEM_TREES = ["/usr/lib", "/System/Library", "/usr/share/icu"];
|
|
1587
|
+
var RESOLVER_FILES = [
|
|
1588
|
+
"/etc",
|
|
1589
|
+
"/var",
|
|
1590
|
+
"/etc/resolv.conf",
|
|
1591
|
+
"/private/etc/resolv.conf",
|
|
1592
|
+
"/private/var/run/resolv.conf",
|
|
1593
|
+
"/etc/hosts",
|
|
1594
|
+
"/private/etc/hosts",
|
|
1595
|
+
"/var/run/mDNSResponder",
|
|
1596
|
+
"/private/var/run/mDNSResponder"
|
|
1597
|
+
];
|
|
1598
|
+
function path(value) {
|
|
1599
|
+
if (typeof value !== "string" || !posix4.isAbsolute(value) || posix4.normalize(value) !== value || Buffer.byteLength(value) > 4096 || Buffer.from(value).toString("utf8") !== value || [...value].some((c) => c.charCodeAt(0) < 32 || c.charCodeAt(0) === 127))
|
|
1600
|
+
throw new TypeError("invalid macOS sandbox path");
|
|
1601
|
+
return value;
|
|
1602
|
+
}
|
|
1603
|
+
function inside(value, root) {
|
|
1604
|
+
return root === "/" || value === root || value.startsWith(`${root}/`);
|
|
1605
|
+
}
|
|
1606
|
+
var intersects = (left, right) => inside(left, right) || inside(right, left);
|
|
1607
|
+
var literal = (value) => `(literal ${JSON.stringify(value)})`;
|
|
1608
|
+
var tree = (value) => `(subpath ${JSON.stringify(value)})`;
|
|
1609
|
+
function paths(values) {
|
|
1610
|
+
if (!Array.isArray(values) || values.length > 128)
|
|
1611
|
+
throw new TypeError("too many sandbox paths");
|
|
1612
|
+
return [...new Set(values.map(path))].sort();
|
|
1613
|
+
}
|
|
1614
|
+
function privateMacosSandboxProfile(input) {
|
|
1615
|
+
const readOnlyFiles = paths(input.readOnlyFiles);
|
|
1616
|
+
const readOnlyTrees = paths(input.readOnlyTrees);
|
|
1617
|
+
const writableTrees = paths(input.writableTrees);
|
|
1618
|
+
const protectedRoots = paths(input.protectedRoots);
|
|
1619
|
+
if (protectedRoots.length === 0 || !["isolated", "inherited"].includes(input.network))
|
|
1620
|
+
throw new TypeError("macOS sandbox requires explicit control roots and network policy");
|
|
1621
|
+
const grants = [...SYSTEM_TREES, ...readOnlyFiles, ...readOnlyTrees, ...writableTrees];
|
|
1622
|
+
if (input.network === "inherited")
|
|
1623
|
+
grants.push(...RESOLVER_FILES);
|
|
1624
|
+
if (grants.some((grant) => protectedRoots.some((root) => intersects(grant, root))))
|
|
1625
|
+
throw new TypeError("macOS payload grant overlaps host control");
|
|
1626
|
+
if (writableTrees.some((write) => [...SYSTEM_TREES, ...readOnlyFiles, ...readOnlyTrees].some((read) => intersects(write, read))))
|
|
1627
|
+
throw new TypeError("macOS writable grant overlaps immutable data");
|
|
1628
|
+
const ancestors = new Set(["/"]);
|
|
1629
|
+
for (const grant of grants) {
|
|
1630
|
+
let parent = posix4.dirname(grant);
|
|
1631
|
+
for (;; ) {
|
|
1632
|
+
ancestors.add(parent);
|
|
1633
|
+
if (parent === "/")
|
|
1634
|
+
break;
|
|
1635
|
+
parent = posix4.dirname(parent);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const fileParents = [...new Set(readOnlyFiles.map((file) => posix4.dirname(file)))].sort();
|
|
1639
|
+
const rules = [
|
|
1640
|
+
"(version 1)",
|
|
1641
|
+
"(deny default)",
|
|
1642
|
+
"(deny process-info* (require-not (target self)))",
|
|
1643
|
+
"(allow process-fork process-exec)",
|
|
1644
|
+
"(allow signal (target same-sandbox))",
|
|
1645
|
+
'(allow sysctl-read (sysctl-name-regex #"^hw[.]") (sysctl-name "kern.osrelease") (sysctl-name "kern.osversion") (sysctl-name "kern.ostype"))',
|
|
1646
|
+
'(allow file-read-data (literal "/"))',
|
|
1647
|
+
`(allow file-read-metadata ${[...ancestors].sort().map(literal).join(" ")})`,
|
|
1648
|
+
...fileParents.length === 0 ? [] : [`(allow file-read-data ${fileParents.map(literal).join(" ")})`],
|
|
1649
|
+
`(allow file-read* file-map-executable ${SYSTEM_TREES.map(tree).join(" ")} ${readOnlyFiles.map(literal).join(" ")} ${readOnlyTrees.map(tree).join(" ")})`,
|
|
1650
|
+
...writableTrees.length ? [`(allow file-read* file-write* file-map-executable ${writableTrees.map(tree).join(" ")})`] : [],
|
|
1651
|
+
'(allow file-read-data file-write-data (literal "/dev/null") (literal "/dev/zero"))',
|
|
1652
|
+
'(allow file-read-data (literal "/dev/random") (literal "/dev/urandom"))',
|
|
1653
|
+
'(deny file-read* file-test-existence (with errno ENOENT) (subpath "/etc/codex") (subpath "/private/etc/codex"))'
|
|
1654
|
+
];
|
|
1655
|
+
if (input.network === "inherited")
|
|
1656
|
+
rules.push('(allow network-outbound (remote ip "*:*"))', `(allow file-read* ${RESOLVER_FILES.map(literal).join(" ")})`, '(allow network-outbound (literal "/var/run/mDNSResponder") (literal "/private/var/run/mDNSResponder"))', '(allow mach-lookup (global-name "com.apple.SystemConfiguration.DNSConfiguration"))');
|
|
1657
|
+
const text = `${rules.join(`
|
|
1658
|
+
`)}
|
|
1659
|
+
`;
|
|
1660
|
+
if (Buffer.byteLength(text) > 32768)
|
|
1661
|
+
throw new TypeError("macOS sandbox profile exceeds its bound");
|
|
1662
|
+
return Object.freeze({ text, bootstrap: input.network === "inherited" ? "dns" : "closed" });
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
// src/internal/macos-scope-execution.ts
|
|
1666
|
+
function requirePrivateMacosScopeLimits(value) {
|
|
1667
|
+
const limits = Object.freeze({ ...value });
|
|
1668
|
+
if (Object.keys(limits).sort().join() !== "cleanupTimeoutMs,cpuPeriodMicros,cpuQuotaMicros,deadlineUnixMs,memoryBytes,pids" || Object.values(limits).some((value2) => !Number.isSafeInteger(value2) || value2 <= 0) || limits.memoryBytes > 512 * 1024 * 1024 || limits.pids > 128 || limits.cpuQuotaMicros > limits.cpuPeriodMicros || limits.cpuPeriodMicros !== 1e5 || limits.cleanupTimeoutMs > 5000 || limits.deadlineUnixMs - Date.now() > 86400000)
|
|
1669
|
+
throw new TypeError("invalid macOS scope limits");
|
|
1670
|
+
return limits;
|
|
1671
|
+
}
|
|
1672
|
+
async function preparePrivateMacosScope(input) {
|
|
1673
|
+
const owner = requirePrivateMacosCoalition(input.owner);
|
|
1674
|
+
if (!owner.empty())
|
|
1675
|
+
throw new Error("macOS scope is not initially empty");
|
|
1676
|
+
const limits = requirePrivateMacosScopeLimits(input.limits);
|
|
1677
|
+
const command = [...input.command];
|
|
1678
|
+
const environment = { ...input.environment };
|
|
1679
|
+
if (!input.launcher.startsWith("/") || !input.cwd.startsWith("/") || !command[0]?.startsWith("/") || command.length > 64 || command.some((value) => typeof value !== "string" || value.includes("\x00")) || Buffer.byteLength(command.join("\x00")) > 16384 || Object.entries(environment).some(([name, value]) => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || /^(?:_?DYLD_|LD_)/.test(name) || typeof value !== "string" || value.includes("\x00")) || Buffer.byteLength(JSON.stringify(environment)) > 16384)
|
|
1680
|
+
throw new TypeError("invalid macOS native launch");
|
|
1681
|
+
const profile = privateMacosSandboxProfile(input.files);
|
|
1682
|
+
if (![...input.files.readOnlyTrees, ...input.files.writableTrees].some((root) => input.cwd === root || input.cwd.startsWith(`${root}/`)))
|
|
1683
|
+
throw new TypeError("macOS working directory is outside its directory projections");
|
|
1684
|
+
const hardDeadline = performance.now() + Math.max(0, limits.deadlineUnixMs - Date.now());
|
|
1685
|
+
const initial = owner.sample();
|
|
1686
|
+
if (!initial.complete || initial.active !== 1n)
|
|
1687
|
+
throw new Error("macOS initial accounting is incomplete");
|
|
1688
|
+
let previousCpu = initial.cpuNanoseconds;
|
|
1689
|
+
let previousTime = performance.now();
|
|
1690
|
+
let credit = BigInt(limits.cpuQuotaMicros) * 1000n;
|
|
1691
|
+
const burst = credit;
|
|
1692
|
+
const debtLimit = 2500000000n * BigInt(limits.cpuQuotaMicros) / BigInt(limits.cpuPeriodMicros);
|
|
1693
|
+
let paused = false, admitted = false, rootExited = false;
|
|
1694
|
+
let reason;
|
|
1695
|
+
let incompleteSince;
|
|
1696
|
+
let peakFootprint = 0n, peakTasks = 0, samples = 0, pauses = 0, incompleteSamples = 0, maxSample = 0;
|
|
1697
|
+
let terminal;
|
|
1698
|
+
let readyFrame;
|
|
1699
|
+
let frameBytes = Buffer.alloc(0);
|
|
1700
|
+
const stop = (why) => {
|
|
1701
|
+
if (reason === undefined || reason === "payload_exit")
|
|
1702
|
+
reason = why;
|
|
1703
|
+
};
|
|
1704
|
+
const child = spawn(input.launcher, ["--launch", String(Buffer.byteLength(profile.text)), input.cwd, profile.bootstrap, ...command], { cwd: "/", env: environment, stdio: ["pipe", "pipe", "pipe", "pipe", "pipe", "pipe"] });
|
|
1705
|
+
child.on("error", () => stop("setup_failed"));
|
|
1706
|
+
const { stdin, stdout, stderr } = child;
|
|
1707
|
+
const configuration = child.stdio[3];
|
|
1708
|
+
const gate = child.stdio[4];
|
|
1709
|
+
const frames = child.stdio[5];
|
|
1710
|
+
if (stdin === null || stdout === null || stderr === null || !configuration || !gate || !frames) {
|
|
1711
|
+
const cleanupBy = performance.now() + limits.cleanupTimeoutMs;
|
|
1712
|
+
while (!owner.empty()) {
|
|
1713
|
+
owner.signalMembers("kill");
|
|
1714
|
+
if (performance.now() >= cleanupBy)
|
|
1715
|
+
throw new Error("macOS descendant fencing is unconfirmed");
|
|
1716
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1717
|
+
}
|
|
1718
|
+
throw new Error("macOS launch handoffs are unavailable");
|
|
1719
|
+
}
|
|
1720
|
+
let resolveReady = () => {};
|
|
1721
|
+
const ready = new Promise((resolve) => {
|
|
1722
|
+
resolveReady = resolve;
|
|
1723
|
+
});
|
|
1724
|
+
ready.catch(() => {
|
|
1725
|
+
return;
|
|
1726
|
+
});
|
|
1727
|
+
const receiveFrame = (bytes) => {
|
|
1728
|
+
if (frameBytes.length + bytes.length > 32)
|
|
1729
|
+
return stop("setup_failed");
|
|
1730
|
+
frameBytes = Buffer.concat([frameBytes, bytes]);
|
|
1731
|
+
if (frameBytes.length >= 16 && readyFrame === undefined) {
|
|
1732
|
+
const pid = frameBytes.readUInt32LE(4), version = frameBytes.readUInt32LE(8);
|
|
1733
|
+
if (frameBytes.readUInt32LE(0) !== 1246316365 || frameBytes.readUInt32LE(12) !== 0 || !owner.contains(pid, version))
|
|
1734
|
+
return stop("setup_failed");
|
|
1735
|
+
readyFrame = { pid, version };
|
|
1736
|
+
resolveReady(readyFrame);
|
|
1737
|
+
}
|
|
1738
|
+
if (frameBytes.length === 32) {
|
|
1739
|
+
const code = frameBytes.readUInt32LE(24), signal = frameBytes.readUInt32LE(28);
|
|
1740
|
+
if (frameBytes.readUInt32LE(16) !== 1246316376 || frameBytes.readUInt32LE(20) !== readyFrame?.pid || !(code <= 255 && signal === 0 || code === 4294967295 && signal > 0 && signal < 32))
|
|
1741
|
+
return stop("setup_failed");
|
|
1742
|
+
terminal = { exitCode: code === 4294967295 ? null : code, signal: signal || null };
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
frames.on("data", (bytes) => {
|
|
1746
|
+
try {
|
|
1747
|
+
receiveFrame(bytes);
|
|
1748
|
+
} catch {
|
|
1749
|
+
stop("setup_failed");
|
|
1750
|
+
}
|
|
1751
|
+
});
|
|
1752
|
+
stdin.on("error", (error) => {
|
|
1753
|
+
if (!admitted || error.code !== "EPIPE")
|
|
1754
|
+
stop("setup_failed");
|
|
1755
|
+
});
|
|
1756
|
+
for (const stream of [stdout, stderr, configuration, gate, frames])
|
|
1757
|
+
stream.on("error", () => stop("setup_failed"));
|
|
1758
|
+
const frameEnd = new Promise((resolve) => frames.once("end", resolve));
|
|
1759
|
+
const exit = new Promise((resolve) => {
|
|
1760
|
+
child.once("error", () => {
|
|
1761
|
+
stop("setup_failed");
|
|
1762
|
+
rootExited = true;
|
|
1763
|
+
resolve();
|
|
1764
|
+
});
|
|
1765
|
+
child.once("exit", (code) => {
|
|
1766
|
+
if (code !== 0 && reason === undefined)
|
|
1767
|
+
stop("setup_failed");
|
|
1768
|
+
rootExited = true;
|
|
1769
|
+
resolve();
|
|
1770
|
+
});
|
|
1771
|
+
});
|
|
1772
|
+
configuration.end(profile.text);
|
|
1773
|
+
const startupTimeout = setTimeout(() => stop("setup_failed"), 9000);
|
|
1774
|
+
const completion = (async () => {
|
|
1775
|
+
try {
|
|
1776
|
+
while (reason === undefined && !rootExited) {
|
|
1777
|
+
const now = performance.now();
|
|
1778
|
+
if (now >= hardDeadline) {
|
|
1779
|
+
stop("deadline");
|
|
1780
|
+
break;
|
|
1781
|
+
}
|
|
1782
|
+
const sample = owner.sample();
|
|
1783
|
+
samples++;
|
|
1784
|
+
maxSample = Math.max(maxSample, sample.sampleMilliseconds);
|
|
1785
|
+
if (sample.footprintBytes > peakFootprint)
|
|
1786
|
+
peakFootprint = sample.footprintBytes;
|
|
1787
|
+
peakTasks = Math.max(peakTasks, Number(sample.active - 1n));
|
|
1788
|
+
if (sample.footprintBytes > BigInt(limits.memoryBytes)) {
|
|
1789
|
+
stop("memory_limit");
|
|
1790
|
+
break;
|
|
1791
|
+
}
|
|
1792
|
+
if (sample.active - 1n > BigInt(limits.pids)) {
|
|
1793
|
+
stop("process_limit");
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
if (!sample.complete) {
|
|
1797
|
+
incompleteSamples++;
|
|
1798
|
+
incompleteSince ??= now;
|
|
1799
|
+
} else
|
|
1800
|
+
incompleteSince = undefined;
|
|
1801
|
+
if (incompleteSince !== undefined && now - incompleteSince > 500) {
|
|
1802
|
+
stop("accounting_failed");
|
|
1803
|
+
break;
|
|
1804
|
+
}
|
|
1805
|
+
if (sample.cpuNanoseconds < previousCpu) {
|
|
1806
|
+
stop("accounting_failed");
|
|
1807
|
+
break;
|
|
1808
|
+
}
|
|
1809
|
+
credit += BigInt(Math.max(0, Math.floor((now - previousTime) * 1e6))) * BigInt(limits.cpuQuotaMicros) / BigInt(limits.cpuPeriodMicros) - (sample.cpuNanoseconds - previousCpu);
|
|
1810
|
+
if (credit > burst)
|
|
1811
|
+
credit = burst;
|
|
1812
|
+
previousCpu = sample.cpuNanoseconds;
|
|
1813
|
+
previousTime = now;
|
|
1814
|
+
if (credit < -debtLimit) {
|
|
1815
|
+
stop("cpu_limit");
|
|
1816
|
+
break;
|
|
1817
|
+
}
|
|
1818
|
+
if (credit < 0) {
|
|
1819
|
+
owner.signalMembers("stop");
|
|
1820
|
+
if (!paused)
|
|
1821
|
+
pauses++;
|
|
1822
|
+
paused = true;
|
|
1823
|
+
} else if (paused) {
|
|
1824
|
+
owner.signalMembers("continue");
|
|
1825
|
+
paused = false;
|
|
1826
|
+
}
|
|
1827
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1828
|
+
}
|
|
1829
|
+
} catch {
|
|
1830
|
+
stop("accounting_failed");
|
|
1831
|
+
} finally {
|
|
1832
|
+
clearTimeout(startupTimeout);
|
|
1833
|
+
}
|
|
1834
|
+
reason ??= "payload_exit";
|
|
1835
|
+
const cleanupBy = performance.now() + limits.cleanupTimeoutMs;
|
|
1836
|
+
while (!owner.empty()) {
|
|
1837
|
+
owner.signalMembers("kill");
|
|
1838
|
+
if (performance.now() >= cleanupBy)
|
|
1839
|
+
throw new Error("macOS descendant fencing is unconfirmed");
|
|
1840
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1841
|
+
}
|
|
1842
|
+
await bounded(exit, Math.max(1, cleanupBy - performance.now()));
|
|
1843
|
+
await bounded(frameEnd, Math.max(1, cleanupBy - performance.now())).catch(() => {
|
|
1844
|
+
return;
|
|
1845
|
+
});
|
|
1846
|
+
if (reason === "payload_exit" && terminal === undefined)
|
|
1847
|
+
reason = "setup_failed";
|
|
1848
|
+
const final = owner.sample();
|
|
1849
|
+
if (!final.complete || final.active !== 1n || final.cpuNanoseconds < previousCpu)
|
|
1850
|
+
throw new Error("macOS final resource accounting is unconfirmed");
|
|
1851
|
+
return Object.freeze({
|
|
1852
|
+
reason,
|
|
1853
|
+
exitCode: terminal?.exitCode ?? null,
|
|
1854
|
+
signal: terminal?.signal ?? null,
|
|
1855
|
+
fenced: true,
|
|
1856
|
+
evidence: Object.freeze({
|
|
1857
|
+
cpuNanoseconds: (final.cpuNanoseconds - initial.cpuNanoseconds).toString(),
|
|
1858
|
+
peakFootprintBytes: peakFootprint.toString(),
|
|
1859
|
+
peakTasks,
|
|
1860
|
+
samples,
|
|
1861
|
+
pauses,
|
|
1862
|
+
incompleteSamples,
|
|
1863
|
+
maxSampleMilliseconds: maxSample
|
|
1864
|
+
})
|
|
1865
|
+
});
|
|
1866
|
+
})();
|
|
1867
|
+
completion.catch(() => {
|
|
1868
|
+
return;
|
|
1869
|
+
});
|
|
1870
|
+
try {
|
|
1871
|
+
const identity2 = await Promise.race([
|
|
1872
|
+
ready,
|
|
1873
|
+
completion.then((result) => {
|
|
1874
|
+
throw new Error(`macOS scope ended before readiness (${result.reason}, exit ${result.exitCode ?? "null"}, signal ${result.signal ?? "null"})`);
|
|
1875
|
+
})
|
|
1876
|
+
]);
|
|
1877
|
+
return Object.freeze({
|
|
1878
|
+
stdin,
|
|
1879
|
+
stdout,
|
|
1880
|
+
stderr,
|
|
1881
|
+
...identity2,
|
|
1882
|
+
completion,
|
|
1883
|
+
admit() {
|
|
1884
|
+
if (performance.now() >= hardDeadline)
|
|
1885
|
+
stop("deadline");
|
|
1886
|
+
if (admitted || reason !== undefined || !owner.contains(identity2.pid, identity2.version))
|
|
1887
|
+
throw new Error("macOS scope is no longer admissible");
|
|
1888
|
+
admitted = true;
|
|
1889
|
+
clearTimeout(startupTimeout);
|
|
1890
|
+
gate.end("A");
|
|
1891
|
+
},
|
|
1892
|
+
stop: (why) => stop(why)
|
|
1893
|
+
});
|
|
1894
|
+
} catch (error) {
|
|
1895
|
+
stop("setup_failed");
|
|
1896
|
+
await completion;
|
|
1897
|
+
throw error;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
async function bounded(promise, milliseconds) {
|
|
1901
|
+
let timer;
|
|
1902
|
+
try {
|
|
1903
|
+
return await Promise.race([
|
|
1904
|
+
promise,
|
|
1905
|
+
new Promise((_, reject) => {
|
|
1906
|
+
timer = setTimeout(() => reject(new Error("macOS native settlement expired")), milliseconds);
|
|
1907
|
+
})
|
|
1908
|
+
]);
|
|
1909
|
+
} finally {
|
|
1910
|
+
clearTimeout(timer);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
// src/internal/macos-native-supervisor.ts
|
|
1915
|
+
var POLICY = ["--no-env-file", "--no-install", "--config=/dev/null"];
|
|
1916
|
+
function object(value, keys) {
|
|
1917
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== keys.split(",").sort().join())
|
|
1918
|
+
throw new Error("invalid macOS guardian message");
|
|
1919
|
+
return value;
|
|
1920
|
+
}
|
|
1921
|
+
function startMessage(value) {
|
|
1922
|
+
if (value?.type === "recover-storage") {
|
|
1923
|
+
const record2 = object(value, "type,ownerDirectory,ownerToken,targetDirectory,targetToken,deadlineUnixMs");
|
|
1924
|
+
if (typeof record2.ownerDirectory !== "string" || typeof record2.ownerToken !== "string" || typeof record2.targetDirectory !== "string" || typeof record2.targetToken !== "string" || record2.ownerDirectory !== join6(record2.targetDirectory, "recovery") || record2.ownerToken !== privateMacosStorageRecoveryToken(record2.targetToken) || !Number.isSafeInteger(record2.deadlineUnixMs) || record2.deadlineUnixMs <= Date.now() || record2.deadlineUnixMs > Date.now() + 30000)
|
|
1925
|
+
throw new Error("invalid macOS storage recovery configuration");
|
|
1926
|
+
requirePrivateMacosOwnerDirectory(record2.targetDirectory);
|
|
1927
|
+
return record2;
|
|
1928
|
+
}
|
|
1929
|
+
const record = object(value, `type,ownerDirectory,ownerToken,launcher,cwd,command,environment,files,limits,maxOutputBytes${[
|
|
1930
|
+
"storage",
|
|
1931
|
+
"inputs",
|
|
1932
|
+
"inputDirectories"
|
|
1933
|
+
].filter((key) => value !== null && typeof value === "object" && Object.hasOwn(value, key)).map((key) => `,${key}`).join("")}`);
|
|
1934
|
+
if (record.type !== "start" || typeof record.ownerDirectory !== "string" || !record.ownerDirectory.startsWith("/") || typeof record.ownerToken !== "string" || !/^[a-f0-9]{64}$/.test(record.ownerToken) || typeof record.launcher !== "string" || typeof record.cwd !== "string" || !Array.isArray(record.command) || record.environment === null || typeof record.environment !== "object" || Array.isArray(record.environment) || !Number.isSafeInteger(record.maxOutputBytes) || record.maxOutputBytes < 1 || record.maxOutputBytes > PRIVATE_MACOS_MAX_OUTPUT_BYTES)
|
|
1935
|
+
throw new Error("invalid macOS guardian configuration");
|
|
1936
|
+
object(record.files, "readOnlyFiles,readOnlyTrees,writableTrees,protectedRoots,network");
|
|
1937
|
+
object(record.limits, "memoryBytes,pids,cpuQuotaMicros,cpuPeriodMicros,deadlineUnixMs,cleanupTimeoutMs");
|
|
1938
|
+
requirePrivateMacosScopeLimits(record.limits);
|
|
1939
|
+
if (record.storage !== undefined)
|
|
1940
|
+
requirePrivateMacosGuardianStorage(record.storage, record.files, record.cwd);
|
|
1941
|
+
if (record.inputs !== undefined || record.inputDirectories !== undefined) {
|
|
1942
|
+
const roots = normalizePrivateMacosInputDirectories(record.inputDirectories);
|
|
1943
|
+
normalizePrivateMacosInputs(record.inputs, roots);
|
|
1944
|
+
if (record.storage === undefined || !record.files.readOnlyTrees.includes(join6(record.storage.mountPath, "inputs")))
|
|
1945
|
+
throw new Error("macOS inputs require an immutable bounded projection");
|
|
1946
|
+
}
|
|
1947
|
+
return record;
|
|
1948
|
+
}
|
|
1949
|
+
async function connected(path2, pid, version) {
|
|
1950
|
+
const socket2 = connect(path2);
|
|
1951
|
+
socket2.on("error", () => {
|
|
1952
|
+
return;
|
|
1953
|
+
});
|
|
1954
|
+
try {
|
|
1955
|
+
await privateMacosBefore(new Promise((resolve, reject) => {
|
|
1956
|
+
socket2.once("connect", resolve);
|
|
1957
|
+
socket2.once("error", reject);
|
|
1958
|
+
}), 1e4);
|
|
1959
|
+
const peer = privateMacosPeerIdentity(socket2);
|
|
1960
|
+
if (peer.uid !== process.getuid?.() || peer.realUid !== peer.uid || peer.pid !== pid || peer.version !== version)
|
|
1961
|
+
throw new Error("macOS coordinator identity does not match");
|
|
1962
|
+
return socket2;
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
socket2.destroy();
|
|
1965
|
+
throw error;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
async function supervise(path2, coordinatorPid, coordinatorVersion) {
|
|
1969
|
+
const owner = acquirePrivateMacosCoalition();
|
|
1970
|
+
const control = await connected(path2, coordinatorPid, coordinatorVersion);
|
|
1971
|
+
const channel = privateMacosControlChannel(control);
|
|
1972
|
+
const streams = [];
|
|
1973
|
+
let execution;
|
|
1974
|
+
let collector;
|
|
1975
|
+
let storageRoot;
|
|
1976
|
+
let inputReceiver;
|
|
1977
|
+
let inputBundle;
|
|
1978
|
+
let configuration;
|
|
1979
|
+
const cancellation = new AbortController;
|
|
1980
|
+
let releaseCollection = () => {};
|
|
1981
|
+
const collection = new Promise((resolve) => {
|
|
1982
|
+
releaseCollection = resolve;
|
|
1983
|
+
});
|
|
1984
|
+
let stopped;
|
|
1985
|
+
let resolveAdmission = () => {};
|
|
1986
|
+
let rejectAdmission = () => {};
|
|
1987
|
+
const admission = new Promise((resolve, reject) => {
|
|
1988
|
+
resolveAdmission = resolve;
|
|
1989
|
+
rejectAdmission = reject;
|
|
1990
|
+
});
|
|
1991
|
+
admission.catch(() => {
|
|
1992
|
+
return;
|
|
1993
|
+
});
|
|
1994
|
+
let phase = "starting";
|
|
1995
|
+
const currentPhase = () => phase;
|
|
1996
|
+
const stop = (reason) => {
|
|
1997
|
+
stopped ??= reason;
|
|
1998
|
+
cancellation.abort();
|
|
1999
|
+
releaseCollection();
|
|
2000
|
+
rejectAdmission(new Error("macOS guardian admission stopped"));
|
|
2001
|
+
execution?.stop(reason);
|
|
2002
|
+
};
|
|
2003
|
+
control.on("close", () => {
|
|
2004
|
+
if (phase !== "terminal")
|
|
2005
|
+
stop("coordinator_lost");
|
|
2006
|
+
});
|
|
2007
|
+
let timer;
|
|
2008
|
+
try {
|
|
2009
|
+
configuration = startMessage(await privateMacosBefore(channel.receive(), 1e4));
|
|
2010
|
+
if (stopped !== undefined)
|
|
2011
|
+
throw new Error("macOS guardian coordinator lost during setup");
|
|
2012
|
+
recordPrivateMacosOwner(configuration.ownerDirectory, configuration.ownerToken, owner);
|
|
2013
|
+
if (configuration.type === "start" && (configuration.inputs?.length ?? 0) > 0)
|
|
2014
|
+
inputReceiver = await createPrivateMacosDescriptorReceiver(dirname4(path2), "inputs");
|
|
2015
|
+
for (const suffix of [".in", ".out", ".err"]) {
|
|
2016
|
+
const stream = await connected(`${path2}${suffix}`, coordinatorPid, coordinatorVersion);
|
|
2017
|
+
stream.on("error", () => stop("coordinator_lost"));
|
|
2018
|
+
streams.push(stream);
|
|
2019
|
+
}
|
|
2020
|
+
const [stdin, stdout, stderr] = streams;
|
|
2021
|
+
phase = "prepared";
|
|
2022
|
+
channel.send({ type: "prepared", identity: owner.identity });
|
|
2023
|
+
const operationDeadline = configuration.type === "start" ? configuration.limits.deadlineUnixMs : configuration.deadlineUnixMs;
|
|
2024
|
+
timer = setTimeout(() => stop("cancelled"), Math.max(0, Math.min(1e4, operationDeadline - Date.now())));
|
|
2025
|
+
const commands = (async () => {
|
|
2026
|
+
for (;; ) {
|
|
2027
|
+
const next = object(await channel.receive(), "type");
|
|
2028
|
+
if (next.type === "cancel")
|
|
2029
|
+
stop("cancelled");
|
|
2030
|
+
else if (next.type === "release" && currentPhase() === "collecting")
|
|
2031
|
+
releaseCollection();
|
|
2032
|
+
else if (next.type === "admit" && phase === "prepared" && stopped === undefined) {
|
|
2033
|
+
clearTimeout(timer);
|
|
2034
|
+
timer = setTimeout(() => stop("cancelled"), Math.max(0, operationDeadline - Date.now()));
|
|
2035
|
+
phase = "preparing";
|
|
2036
|
+
resolveAdmission();
|
|
2037
|
+
} else if (next.type === "continue" && currentPhase() === "ready" && stopped === undefined) {
|
|
2038
|
+
execution?.admit();
|
|
2039
|
+
phase = "running";
|
|
2040
|
+
} else
|
|
2041
|
+
throw new Error("invalid macOS guardian transition");
|
|
2042
|
+
}
|
|
2043
|
+
})();
|
|
2044
|
+
commands.catch(() => stop("coordinator_lost"));
|
|
2045
|
+
await admission;
|
|
2046
|
+
if (stopped !== undefined)
|
|
2047
|
+
throw new Error("macOS guardian stopped before preparation");
|
|
2048
|
+
if (configuration.type === "recover-storage") {
|
|
2049
|
+
await recoverPrivateMacosGuardianStorage(configuration.targetDirectory, configuration.targetToken, cancellation.signal);
|
|
2050
|
+
const fencedBy = performance.now() + 5000;
|
|
2051
|
+
while (!owner.empty()) {
|
|
2052
|
+
owner.signalMembers("kill");
|
|
2053
|
+
if (performance.now() >= fencedBy)
|
|
2054
|
+
throw new Error("macOS recovery tools are not fenced");
|
|
2055
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
2056
|
+
}
|
|
2057
|
+
cancellation.signal.throwIfAborted();
|
|
2058
|
+
phase = "terminal";
|
|
2059
|
+
stdin.destroy();
|
|
2060
|
+
stdout.end();
|
|
2061
|
+
stderr.end();
|
|
2062
|
+
channel.send({ type: "terminal", result: null, outputLost: false });
|
|
2063
|
+
await privateMacosBefore(new Promise((resolve) => control.end(resolve)), 1000);
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
if (inputReceiver !== undefined) {
|
|
2067
|
+
inputBundle = await inputReceiver.receive({ pid: coordinatorPid, version: coordinatorVersion }, 5000, cancellation.signal);
|
|
2068
|
+
await inputReceiver.close();
|
|
2069
|
+
}
|
|
2070
|
+
if (configuration.storage !== undefined) {
|
|
2071
|
+
const storage = await preparePrivateMacosGuardianStorage(configuration.ownerDirectory, configuration.ownerToken, configuration.storage, cancellation.signal);
|
|
2072
|
+
collector = storage.collector;
|
|
2073
|
+
storageRoot = storage.directory;
|
|
2074
|
+
}
|
|
2075
|
+
if ((configuration.inputDirectories?.length ?? 0) > 0) {
|
|
2076
|
+
await projectPrivateMacosInputs(storageRoot, configuration.inputs, configuration.inputDirectories, inputBundle, cancellation.signal);
|
|
2077
|
+
inputBundle?.close();
|
|
2078
|
+
inputBundle = undefined;
|
|
2079
|
+
}
|
|
2080
|
+
await storageRoot?.close();
|
|
2081
|
+
storageRoot = undefined;
|
|
2082
|
+
cancellation.signal.throwIfAborted();
|
|
2083
|
+
execution = await preparePrivateMacosScope({
|
|
2084
|
+
...configuration,
|
|
2085
|
+
owner,
|
|
2086
|
+
files: {
|
|
2087
|
+
...configuration.files,
|
|
2088
|
+
protectedRoots: [
|
|
2089
|
+
...configuration.files.protectedRoots,
|
|
2090
|
+
configuration.ownerDirectory,
|
|
2091
|
+
dirname4(path2)
|
|
2092
|
+
]
|
|
2093
|
+
}
|
|
2094
|
+
});
|
|
2095
|
+
clearTimeout(timer);
|
|
2096
|
+
if (stopped !== undefined)
|
|
2097
|
+
execution.stop(stopped);
|
|
2098
|
+
let outputBytes = 0;
|
|
2099
|
+
let outputLost = false;
|
|
2100
|
+
const maxOutputBytes = configuration.maxOutputBytes;
|
|
2101
|
+
for (const [source, destination] of [
|
|
2102
|
+
[execution.stdout, stdout],
|
|
2103
|
+
[execution.stderr, stderr]
|
|
2104
|
+
]) {
|
|
2105
|
+
const bounded2 = new Transform({
|
|
2106
|
+
transform(bytes, _encoding, callback) {
|
|
2107
|
+
if (outputBytes + bytes.length > maxOutputBytes) {
|
|
2108
|
+
callback(new Error("macOS payload output exceeded its limit"));
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
outputBytes += bytes.length;
|
|
2112
|
+
callback(null, bytes);
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2115
|
+
bounded2.on("error", () => {
|
|
2116
|
+
outputLost = true;
|
|
2117
|
+
stop("cancelled");
|
|
2118
|
+
source.unpipe(bounded2);
|
|
2119
|
+
source.pause();
|
|
2120
|
+
destination.end();
|
|
2121
|
+
});
|
|
2122
|
+
source.pipe(bounded2).pipe(destination);
|
|
2123
|
+
destination.on("close", () => {
|
|
2124
|
+
if (!destination.writableFinished && phase !== "terminal") {
|
|
2125
|
+
outputLost = true;
|
|
2126
|
+
stop("coordinator_lost");
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
stdin.pipe(execution.stdin);
|
|
2131
|
+
phase = "ready";
|
|
2132
|
+
channel.send({ type: "ready", pid: execution.pid, version: execution.version });
|
|
2133
|
+
const result = await execution.completion;
|
|
2134
|
+
stdin.destroy();
|
|
2135
|
+
for (const destination of [stdout, stderr]) {
|
|
2136
|
+
if (!destination.writableFinished) {
|
|
2137
|
+
try {
|
|
2138
|
+
await privateMacosBefore(new Promise((resolve, reject) => {
|
|
2139
|
+
destination.once("finish", resolve);
|
|
2140
|
+
destination.once("error", reject);
|
|
2141
|
+
destination.once("close", () => reject(new Error("macOS output closed before delivery")));
|
|
2142
|
+
}), 1000);
|
|
2143
|
+
} catch {
|
|
2144
|
+
outputLost = true;
|
|
2145
|
+
destination.destroy();
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
execution.stdout.destroy();
|
|
2150
|
+
execution.stderr.destroy();
|
|
2151
|
+
if (configuration.storage !== undefined) {
|
|
2152
|
+
phase = "collecting";
|
|
2153
|
+
const collect = collector !== undefined && stopped === undefined && !outputLost && result.reason === "payload_exit" && result.exitCode === 0;
|
|
2154
|
+
channel.send({ type: "fenced", result, outputLost, collect });
|
|
2155
|
+
if (collect) {
|
|
2156
|
+
await sendPrivateMacosDescriptors(join6(dirname4(path2), "fd-output"), { pid: coordinatorPid, version: coordinatorVersion }, [collector.fd], 5000, cancellation.signal);
|
|
2157
|
+
await privateMacosBefore(collection, 21000);
|
|
2158
|
+
}
|
|
2159
|
+
await collector?.close();
|
|
2160
|
+
collector = undefined;
|
|
2161
|
+
await recoverPrivateMacosGuardianStorage(configuration.ownerDirectory, configuration.ownerToken, AbortSignal.timeout(30000));
|
|
2162
|
+
if (stopped !== undefined)
|
|
2163
|
+
outputLost = true;
|
|
2164
|
+
}
|
|
2165
|
+
phase = "terminal";
|
|
2166
|
+
channel.send({ type: "terminal", result, outputLost });
|
|
2167
|
+
await privateMacosBefore(new Promise((resolve) => control.end(resolve)), 1000);
|
|
2168
|
+
} catch (error) {
|
|
2169
|
+
cancellation.abort();
|
|
2170
|
+
execution?.stop("coordinator_lost");
|
|
2171
|
+
let result;
|
|
2172
|
+
if (execution !== undefined)
|
|
2173
|
+
result = await execution.completion;
|
|
2174
|
+
const cleanupBy = performance.now() + (configuration?.type === "start" ? configuration.limits.cleanupTimeoutMs : 5000);
|
|
2175
|
+
while (!owner.empty()) {
|
|
2176
|
+
owner.signalMembers("kill");
|
|
2177
|
+
if (performance.now() >= cleanupBy)
|
|
2178
|
+
throw new Error("macOS guardian cleanup is unconfirmed");
|
|
2179
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
2180
|
+
}
|
|
2181
|
+
if (!owner.empty())
|
|
2182
|
+
throw new Error("macOS guardian cleanup is unconfirmed");
|
|
2183
|
+
await storageRoot?.close();
|
|
2184
|
+
storageRoot = undefined;
|
|
2185
|
+
await collector?.close();
|
|
2186
|
+
collector = undefined;
|
|
2187
|
+
if (configuration?.type === "start" && configuration.storage !== undefined)
|
|
2188
|
+
await recoverPrivateMacosGuardianStorage(configuration.ownerDirectory, configuration.ownerToken, AbortSignal.timeout(30000));
|
|
2189
|
+
phase = "terminal";
|
|
2190
|
+
try {
|
|
2191
|
+
channel.send({ type: "terminal", result: result ?? null, outputLost: true });
|
|
2192
|
+
await privateMacosBefore(new Promise((resolve) => control.end(resolve)), 1000);
|
|
2193
|
+
} catch {}
|
|
2194
|
+
if (stopped === undefined)
|
|
2195
|
+
throw error;
|
|
2196
|
+
} finally {
|
|
2197
|
+
clearTimeout(timer);
|
|
2198
|
+
inputBundle?.close();
|
|
2199
|
+
await inputReceiver?.close();
|
|
2200
|
+
await storageRoot?.close();
|
|
2201
|
+
await collector?.close();
|
|
2202
|
+
for (const socket2 of streams)
|
|
2203
|
+
socket2.destroy();
|
|
2204
|
+
channel.close();
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
if (import.meta.main) {
|
|
2208
|
+
(async () => {
|
|
2209
|
+
const [mode, path2, pid, version, extra] = process.argv.slice(2);
|
|
2210
|
+
if (mode !== "--guardian" || path2 === undefined || !path2.startsWith("/") || Buffer.byteLength(path2) > 98 || !/^[1-9][0-9]*$/.test(pid ?? "") || !/^[0-9]+$/.test(version ?? "") || extra !== undefined || process.cwd() !== "/" || Object.keys(process.env).length !== 0 || process.execArgv.join("\x00") !== POLICY.join("\x00"))
|
|
2211
|
+
throw new Error("invalid macOS guardian startup posture");
|
|
2212
|
+
await supervise(path2, Number(pid), Number(version));
|
|
2213
|
+
})().catch(() => {
|
|
2214
|
+
process.exitCode = 70;
|
|
2215
|
+
});
|
|
2216
|
+
}
|