@dsh-electron/dsh-plugin-multi-root-workspace 0.1.10 → 0.1.12
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/CHANGELOG.en.md +25 -1
- package/CHANGELOG.md +25 -1
- package/README.en.md +11 -9
- package/README.md +11 -9
- package/cordis.patch.yml +3 -2
- package/lib/auto-review.js +1 -1
- package/lib/client.js +252 -129
- package/lib/client.js.map +1 -1
- package/lib/{command-BGdz7VBH.js → command-CmLekG1M.js} +3 -2
- package/lib/command.js +1 -1
- package/lib/{compat-D-MljsPV.js → compat-Bg2HFVMX.js} +6 -1
- package/lib/compat.js +1 -1
- package/lib/{sandbox-CXk5h-ct.js → dialects-Dr4rDR-H.js} +6 -264
- package/lib/{fs-BTXySwSG.js → fs-CfzJq2wd.js} +23 -8
- package/lib/fs.js +1 -1
- package/lib/index.js +7 -6
- package/lib/{instructions-MePoRigy.js → instructions-KsUwUFE0.js} +53 -37
- package/lib/instructions.js +1 -1
- package/lib/lsp.js +19 -16
- package/lib/native-win32-2waCRkPI.js +312 -0
- package/lib/process-fence.js +62 -12
- package/lib/protocol-4zdwV7u4.js +641 -0
- package/lib/{registry-KSsmU393.js → registry-YTUOjmVu.js} +368 -25
- package/lib/registry-operation-C2ZV3Jhw.js +116 -0
- package/lib/registry.js +3 -2
- package/lib/{review-context-AH-SXHmj.js → review-context-NDpKeMCc.js} +1 -1
- package/lib/review-context.js +1 -1
- package/lib/sandbox-C-vCJCp3.js +278 -0
- package/lib/sandbox.js +1 -1
- package/lib/scope.js +70 -10
- package/lib/skills.js +48 -29
- package/lib/types/authority/native-win32.d.ts +7 -0
- package/lib/types/authority/native.d.ts +23 -0
- package/lib/types/authority/protocol.d.ts +70 -0
- package/lib/types/client/open-in-app/OpenInAppAction.d.ts +5 -3
- package/lib/types/client/open-in-app/OpenTargetButton.d.ts +16 -10
- package/lib/types/client/open-in-app/adapter.d.ts +5 -1
- package/lib/types/client/open-in-app/controller.d.ts +5 -2
- package/lib/types/client/open-in-app/device.d.ts +11 -0
- package/lib/types/client/open-in-app/locales.d.ts +1 -1
- package/lib/types/client/open-in-app/open-path.d.ts +6 -5
- package/lib/types/client/open-in-app/open-toast.d.ts +39 -0
- package/lib/types/client/open-in-app/outcome.d.ts +23 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/compat/authority-storage.d.ts +6 -0
- package/lib/types/compat/dsh-version.d.ts +4 -4
- package/lib/types/compat/open-in-app-device.d.ts +23 -0
- package/lib/types/compat/process-entrypoints.d.ts +2 -0
- package/lib/types/compat/process-profile.d.ts +3 -0
- package/lib/types/compat/sandbox-confine.d.ts +2 -0
- package/lib/types/fs.d.ts +4 -1
- package/lib/types/instructions.d.ts +0 -47
- package/lib/types/registry-operation.d.ts +31 -0
- package/lib/types/registry.d.ts +47 -6
- package/lib/types/scope.d.ts +12 -0
- package/lib/types/skills/catalog.d.ts +3 -0
- package/lib/types/skills/pool.d.ts +1 -1
- package/lib/workspace-files.js +63 -7
- package/package.json +87 -80
- package/lib/types/client/open-in-app/open-failure-toast.d.ts +0 -11
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
//#region src/authority/native-win32.ts
|
|
3
|
+
/** Windows local pipes: tokens, never a caller-supplied PID or a bearer file. */
|
|
4
|
+
var RestrictedRegistryPeerError = class extends Error {};
|
|
5
|
+
let apiPromise;
|
|
6
|
+
async function load() {
|
|
7
|
+
const koffi = (await import("koffi")).default;
|
|
8
|
+
const kernel = koffi.load("kernel32.dll"), advapi = koffi.load("advapi32.dll");
|
|
9
|
+
const k = (name, ret, args) => kernel.func("__stdcall", name, ret, args);
|
|
10
|
+
const a = (name, ret, args) => advapi.func("__stdcall", name, ret, args);
|
|
11
|
+
const close = k("CloseHandle", "int", ["intptr"]);
|
|
12
|
+
const createPipe = k("CreateNamedPipeW", "intptr", [
|
|
13
|
+
"str16",
|
|
14
|
+
"uint",
|
|
15
|
+
"uint",
|
|
16
|
+
"uint",
|
|
17
|
+
"uint",
|
|
18
|
+
"uint",
|
|
19
|
+
"uint",
|
|
20
|
+
"void *"
|
|
21
|
+
]);
|
|
22
|
+
const connect = k("ConnectNamedPipe", "int", ["intptr", "void *"]);
|
|
23
|
+
const open = k("CreateFileW", "intptr", [
|
|
24
|
+
"str16",
|
|
25
|
+
"uint",
|
|
26
|
+
"uint",
|
|
27
|
+
"void *",
|
|
28
|
+
"uint",
|
|
29
|
+
"uint",
|
|
30
|
+
"intptr"
|
|
31
|
+
]);
|
|
32
|
+
const state = k("SetNamedPipeHandleState", "int", [
|
|
33
|
+
"intptr",
|
|
34
|
+
"void *",
|
|
35
|
+
"void *",
|
|
36
|
+
"void *"
|
|
37
|
+
]);
|
|
38
|
+
const peek = k("PeekNamedPipe", "int", [
|
|
39
|
+
"intptr",
|
|
40
|
+
"void *",
|
|
41
|
+
"uint",
|
|
42
|
+
"void *",
|
|
43
|
+
"void *",
|
|
44
|
+
"void *"
|
|
45
|
+
]);
|
|
46
|
+
const read = k("ReadFile", "int", [
|
|
47
|
+
"intptr",
|
|
48
|
+
"void *",
|
|
49
|
+
"uint",
|
|
50
|
+
"void *",
|
|
51
|
+
"void *"
|
|
52
|
+
]);
|
|
53
|
+
const write = k("WriteFile", "int", [
|
|
54
|
+
"intptr",
|
|
55
|
+
"void *",
|
|
56
|
+
"uint",
|
|
57
|
+
"void *",
|
|
58
|
+
"void *"
|
|
59
|
+
]);
|
|
60
|
+
const error = k("GetLastError", "uint", []);
|
|
61
|
+
const serverPid = k("GetNamedPipeServerProcessId", "int", ["intptr", "void *"]);
|
|
62
|
+
const openProcess = k("OpenProcess", "intptr", [
|
|
63
|
+
"uint",
|
|
64
|
+
"int",
|
|
65
|
+
"uint"
|
|
66
|
+
]);
|
|
67
|
+
const openToken = a("OpenProcessToken", "int", [
|
|
68
|
+
"intptr",
|
|
69
|
+
"uint",
|
|
70
|
+
"void *"
|
|
71
|
+
]);
|
|
72
|
+
const openThreadToken = a("OpenThreadToken", "int", [
|
|
73
|
+
"intptr",
|
|
74
|
+
"uint",
|
|
75
|
+
"int",
|
|
76
|
+
"void *"
|
|
77
|
+
]);
|
|
78
|
+
const impersonate = a("ImpersonateNamedPipeClient", "int", ["intptr"]);
|
|
79
|
+
const revert = a("RevertToSelf", "int", []);
|
|
80
|
+
const restricted = a("IsTokenRestricted", "int", ["intptr"]);
|
|
81
|
+
const info = a("GetTokenInformation", "int", [
|
|
82
|
+
"intptr",
|
|
83
|
+
"int",
|
|
84
|
+
"void *",
|
|
85
|
+
"uint",
|
|
86
|
+
"void *"
|
|
87
|
+
]);
|
|
88
|
+
const sidString = a("ConvertSidToStringSidW", "int", ["void *", "void *"]);
|
|
89
|
+
const free = k("LocalFree", "intptr", ["intptr"]);
|
|
90
|
+
function tokenInfo(token, kind) {
|
|
91
|
+
const data = Buffer.alloc(65536), length = Buffer.alloc(4);
|
|
92
|
+
if (info(token, kind, data, data.length, length) === 0) throw new Error("cannot attest registry pipe token");
|
|
93
|
+
return data;
|
|
94
|
+
}
|
|
95
|
+
function sid(pointer) {
|
|
96
|
+
const output = Buffer.alloc(8);
|
|
97
|
+
if (sidString(pointer, output) === 0) throw new Error("cannot read registry peer SID");
|
|
98
|
+
const address = output.readBigUInt64LE();
|
|
99
|
+
try {
|
|
100
|
+
return String(koffi.decode(address, "char16_t", -1));
|
|
101
|
+
} finally {
|
|
102
|
+
free(Number(address));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function user(token) {
|
|
106
|
+
return sid(koffi.decode(tokenInfo(token, 1), "void *"));
|
|
107
|
+
}
|
|
108
|
+
const self = Buffer.alloc(8);
|
|
109
|
+
if (openToken(-1, 8, self) === 0) throw new Error("cannot identify registry Host user");
|
|
110
|
+
let ownUser;
|
|
111
|
+
try {
|
|
112
|
+
ownUser = user(Number(self.readBigUInt64LE()));
|
|
113
|
+
} finally {
|
|
114
|
+
close(Number(self.readBigUInt64LE()));
|
|
115
|
+
}
|
|
116
|
+
function attest(token) {
|
|
117
|
+
if (user(token) !== ownUser || restricted(token) !== 0 || tokenInfo(token, 29).readUInt32LE() !== 0) throw new RestrictedRegistryPeerError("registry pipe refuses restricted or foreign-user token");
|
|
118
|
+
const groups = tokenInfo(token, 2), count = groups.readUInt32LE();
|
|
119
|
+
if (count > 2048) throw new Error("registry token has too many groups");
|
|
120
|
+
for (let i = 0; i < count; i++) {
|
|
121
|
+
const group = sid(koffi.decode(groups.subarray(8 + i * 16), "void *"));
|
|
122
|
+
if (/^S-1-4-(?:\d+-){4}3$/.test(group)) throw new RestrictedRegistryPeerError("registry pipe refuses an ambient deny token");
|
|
123
|
+
}
|
|
124
|
+
const integrity = sid(koffi.decode(tokenInfo(token, 25), "void *"));
|
|
125
|
+
if (!/^S-1-16-\d+$/.test(integrity) || Number(integrity.split("-").at(-1)) < 8192) throw new RestrictedRegistryPeerError("registry pipe refuses low-integrity peer");
|
|
126
|
+
}
|
|
127
|
+
function clientToken(handle) {
|
|
128
|
+
if (impersonate(handle) === 0) throw new Error("cannot attest registry client");
|
|
129
|
+
const token = Buffer.alloc(8);
|
|
130
|
+
let opened = false, reverted = false;
|
|
131
|
+
try {
|
|
132
|
+
opened = openThreadToken(-2, 8, 1, token) !== 0;
|
|
133
|
+
} finally {
|
|
134
|
+
reverted = revert() !== 0;
|
|
135
|
+
}
|
|
136
|
+
if (!reverted) process.abort();
|
|
137
|
+
if (!opened) throw new Error("cannot read registry client token");
|
|
138
|
+
const value = Number(token.readBigUInt64LE());
|
|
139
|
+
try {
|
|
140
|
+
attest(value);
|
|
141
|
+
} finally {
|
|
142
|
+
close(value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function attestServer(handle) {
|
|
146
|
+
const pid = Buffer.alloc(4);
|
|
147
|
+
if (serverPid(handle, pid) === 0) throw new Error("cannot identify registry server");
|
|
148
|
+
const process = openProcess(4096, 0, pid.readUInt32LE());
|
|
149
|
+
if (!process) throw new Error("registry server has exited");
|
|
150
|
+
const token = Buffer.alloc(8);
|
|
151
|
+
try {
|
|
152
|
+
if (openToken(process, 8, token) === 0) throw new Error("cannot attest registry server");
|
|
153
|
+
const value = Number(token.readBigUInt64LE());
|
|
154
|
+
try {
|
|
155
|
+
attest(value);
|
|
156
|
+
} finally {
|
|
157
|
+
close(value);
|
|
158
|
+
}
|
|
159
|
+
} finally {
|
|
160
|
+
close(process);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
class Pipe extends EventEmitter {
|
|
164
|
+
handle;
|
|
165
|
+
closed = false;
|
|
166
|
+
attested;
|
|
167
|
+
timer;
|
|
168
|
+
constructor(handle, server) {
|
|
169
|
+
super();
|
|
170
|
+
this.handle = handle;
|
|
171
|
+
this.attested = !server;
|
|
172
|
+
this.on("error", () => {});
|
|
173
|
+
this.timer = setInterval(() => this.receive(), 20);
|
|
174
|
+
this.timer.unref();
|
|
175
|
+
}
|
|
176
|
+
receive() {
|
|
177
|
+
if (this.closed) return;
|
|
178
|
+
const available = Buffer.alloc(4);
|
|
179
|
+
if (peek(this.handle, null, 0, null, available, null) === 0) {
|
|
180
|
+
this.destroy();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (available.readUInt32LE() === 0) return;
|
|
184
|
+
const data = Buffer.alloc(Math.min(available.readUInt32LE(), 65536)), got = Buffer.alloc(4);
|
|
185
|
+
if (read(this.handle, data, data.length, got, null) === 0) {
|
|
186
|
+
this.destroy();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
if (!this.attested) {
|
|
191
|
+
clientToken(this.handle);
|
|
192
|
+
this.attested = true;
|
|
193
|
+
}
|
|
194
|
+
this.emit("data", data.subarray(0, got.readUInt32LE()));
|
|
195
|
+
} catch {
|
|
196
|
+
this.destroy();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
write(text) {
|
|
200
|
+
if (this.closed) return false;
|
|
201
|
+
const data = Buffer.from(text), wrote = Buffer.alloc(4);
|
|
202
|
+
if (data.length > 1048576 || write(this.handle, data, data.length, wrote, null) === 0 || wrote.readUInt32LE() !== data.length) {
|
|
203
|
+
this.destroy();
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
destroy() {
|
|
209
|
+
if (this.closed) return;
|
|
210
|
+
this.closed = true;
|
|
211
|
+
clearInterval(this.timer);
|
|
212
|
+
close(this.handle);
|
|
213
|
+
this.emit("close");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function create(endpoint, first) {
|
|
217
|
+
const value = createPipe(endpoint, 3 | (first ? 524288 : 0), 9, 255, 1048576, 1048576, 0, null);
|
|
218
|
+
if (value === -1 || value === 0) throw new Error("cannot create registry pipe");
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
const attestSelf = () => {
|
|
222
|
+
const token = Buffer.alloc(8);
|
|
223
|
+
if (openToken(-1, 8, token) === 0) throw new Error("cannot attest registry Host");
|
|
224
|
+
const value = Number(token.readBigUInt64LE());
|
|
225
|
+
try {
|
|
226
|
+
attest(value);
|
|
227
|
+
} finally {
|
|
228
|
+
close(value);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
return {
|
|
232
|
+
close,
|
|
233
|
+
connect,
|
|
234
|
+
open,
|
|
235
|
+
state,
|
|
236
|
+
error,
|
|
237
|
+
attestServer,
|
|
238
|
+
attestSelf,
|
|
239
|
+
Pipe,
|
|
240
|
+
create
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function api() {
|
|
244
|
+
return apiPromise ??= load();
|
|
245
|
+
}
|
|
246
|
+
async function connectWindows(endpoint) {
|
|
247
|
+
const native = await api();
|
|
248
|
+
const handle = native.open(endpoint, 3221225472, 0, null, 3, 0, 0);
|
|
249
|
+
if (handle === -1 || handle === 0) throw new Error("registry authority pipe is not ready");
|
|
250
|
+
try {
|
|
251
|
+
const mode = Buffer.alloc(4);
|
|
252
|
+
mode.writeUInt32LE(1);
|
|
253
|
+
if (native.state(handle, mode, null, null) === 0) throw new Error("cannot set nonblocking registry pipe");
|
|
254
|
+
native.attestServer(handle);
|
|
255
|
+
return new native.Pipe(handle, false);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
native.close(handle);
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function listenWindows(endpoint, accepted) {
|
|
262
|
+
const native = await api();
|
|
263
|
+
let handle = native.create(endpoint, true), closed = false;
|
|
264
|
+
const timer = setInterval(() => {
|
|
265
|
+
if (closed) return;
|
|
266
|
+
const result = native.connect(handle, null), error = result ? 0 : native.error();
|
|
267
|
+
if (!result && error !== 535) {
|
|
268
|
+
if (error === 232) {
|
|
269
|
+
native.close(handle);
|
|
270
|
+
try {
|
|
271
|
+
handle = native.create(endpoint, false);
|
|
272
|
+
} catch {
|
|
273
|
+
closed = true;
|
|
274
|
+
clearInterval(timer);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const ready = handle;
|
|
280
|
+
try {
|
|
281
|
+
handle = native.create(endpoint, false);
|
|
282
|
+
} catch {
|
|
283
|
+
native.close(ready);
|
|
284
|
+
closed = true;
|
|
285
|
+
clearInterval(timer);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
accepted(new native.Pipe(ready, true));
|
|
289
|
+
}, 20);
|
|
290
|
+
timer.unref();
|
|
291
|
+
return { close() {
|
|
292
|
+
if (closed) return;
|
|
293
|
+
closed = true;
|
|
294
|
+
clearInterval(timer);
|
|
295
|
+
native.close(handle);
|
|
296
|
+
} };
|
|
297
|
+
}
|
|
298
|
+
async function windowsHostAvailability() {
|
|
299
|
+
const native = await api();
|
|
300
|
+
try {
|
|
301
|
+
native.attestSelf();
|
|
302
|
+
return { available: true };
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if (error instanceof RestrictedRegistryPeerError) return {
|
|
305
|
+
available: false,
|
|
306
|
+
reason: error.message
|
|
307
|
+
};
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
//#endregion
|
|
312
|
+
export { connectWindows, listenWindows, windowsHostAvailability };
|
package/lib/process-fence.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { t as wrapMethod } from "./method-wrapper-CFY3p3j2.js";
|
|
2
|
+
import { s as withRegistryAdmission } from "./registry-operation-C2ZV3Jhw.js";
|
|
3
|
+
import { t as aroundTool } from "./tool-context-CpgIFcCB.js";
|
|
4
|
+
import { n as detectDialect, s as confineInstalled } from "./dialects-Dr4rDR-H.js";
|
|
2
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
+
//#region src/compat/process-profile.ts
|
|
7
|
+
/** Structural runner recognition shared by the synchronous process seams. */
|
|
8
|
+
function hasConfinedProfile(argv) {
|
|
9
|
+
return argv.includes("--ro-bind") || argv.includes("--ro") && argv.includes("--rw") || argv.includes("--workspace") && argv.includes("--mode") || argv.includes("-p") && argv.some((arg) => arg.includes("(version 1)"));
|
|
10
|
+
}
|
|
11
|
+
/** Agent PTYs are re-confined after their own fresh admission, never replayed. */
|
|
12
|
+
function originalProcessArgv(argv) {
|
|
13
|
+
if (!hasConfinedProfile(argv)) return argv;
|
|
14
|
+
const separator = argv.indexOf("--");
|
|
15
|
+
if (separator < 1 || separator === argv.length - 1) throw new Error("unrecognized confined terminal argv");
|
|
16
|
+
detectDialect(argv.slice(0, separator));
|
|
17
|
+
return argv.slice(separator + 1);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
3
20
|
//#region src/compat/process-result.ts
|
|
4
21
|
const object$1 = (value) => typeof value === "object" && value !== null;
|
|
5
22
|
const thenable = (value) => typeof value?.then === "function";
|
|
@@ -118,6 +135,10 @@ function shellEntrypoints(shell) {
|
|
|
118
135
|
function ptcEntrypoints(runtime) {
|
|
119
136
|
return typeof runtime.run === "function" ? ["run"] : [];
|
|
120
137
|
}
|
|
138
|
+
/** Async public entries can acquire admission before calling their implementation. */
|
|
139
|
+
function isAsyncProcessEntry(entry) {
|
|
140
|
+
return typeof entry === "function" && entry.constructor.name === "AsyncFunction";
|
|
141
|
+
}
|
|
121
142
|
//#endregion
|
|
122
143
|
//#region src/process-fence.ts
|
|
123
144
|
/**
|
|
@@ -185,6 +206,10 @@ function isThenable(value) {
|
|
|
185
206
|
}
|
|
186
207
|
const object = (value) => typeof value === "object" && value !== null;
|
|
187
208
|
function apply(ctx) {
|
|
209
|
+
aroundTool(ctx, async (exec, next) => {
|
|
210
|
+
if (!ctx.get("multiRootRegistry")?.shared || exec.agent === void 0) return next();
|
|
211
|
+
return withRegistryAdmission(ctx, ctx.sandboxPolicy.resolve({ session: exec.agent.session }), "process", next);
|
|
212
|
+
});
|
|
188
213
|
const policyOf = (spec) => spec?.sandboxPolicy ?? ctx.sandboxPolicy.resolve();
|
|
189
214
|
/**
|
|
190
215
|
* The confined facts for one Full-access call, or `undefined` when the call
|
|
@@ -217,11 +242,14 @@ function apply(ctx) {
|
|
|
217
242
|
ctx.inject(["subprocess"], (scope) => {
|
|
218
243
|
scope.effect(() => wrapMethod(scope.subprocess, "spawn", (original) => function(spec) {
|
|
219
244
|
const store = processDenyStore.getStore();
|
|
220
|
-
if (
|
|
221
|
-
const
|
|
245
|
+
if (scope.multiRootScope.admissionRequired && scope.multiRootScope.hasAdmission() && !scope.multiRootScope.admissionPathsValid()) throw new Error("multi-root workspace: an admitted root was redirected before process launch");
|
|
246
|
+
const argv = store === void 0 ? spec.argv : [...store.argvPrefix, ...spec.argv];
|
|
247
|
+
if (scope.multiRootScope.admissionRequired && (store !== void 0 || hasConfinedProfile(argv)) && !scope.multiRootScope.permitsProfile(argv)) throw new Error("multi-root workspace: a confined spawn requires a profile from this live admission");
|
|
248
|
+
const handle = original.call(this, store === void 0 ? spec : {
|
|
222
249
|
...spec,
|
|
223
|
-
argv
|
|
250
|
+
argv
|
|
224
251
|
});
|
|
252
|
+
if (store === void 0) return handle;
|
|
225
253
|
const stderr = object(handle) && object(handle.collected) && object(handle.collected.stderr) && typeof handle.collected.stderr.readFrom === "function" ? handle.collected.stderr : void 0;
|
|
226
254
|
if (stderr !== void 0) store.stderr = () => {
|
|
227
255
|
try {
|
|
@@ -242,12 +270,27 @@ function apply(ctx) {
|
|
|
242
270
|
throw new Error(`multi-root workspace: agent terminal spawn carries a session marker that does not resolve to a live session (${JSON.stringify(sessionId ?? ptySessionId)}), and explicit read-only roots are or may be registered — refusing to spawn an unconfined terminal`);
|
|
243
271
|
}
|
|
244
272
|
const policy = ctx.sandboxPolicy.resolve({ session });
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
273
|
+
return withRegistryAdmission(ctx, policy, "pty", async () => {
|
|
274
|
+
if (ctx.get("multiRootRegistry")?.shared) {
|
|
275
|
+
const command = originalProcessArgv(spec.argv);
|
|
276
|
+
const prefix = prepare(policy, spec.signal);
|
|
277
|
+
const argv = policy.mode === "danger-full-access" ? prefix === void 0 ? command : [...(await prefix).argv, ...command] : (await confineInstalled(ctx.sandbox, command, {
|
|
278
|
+
...policy,
|
|
279
|
+
mode: policy.mode
|
|
280
|
+
}, spec.signal)).argv;
|
|
281
|
+
if (!ctx.multiRootScope.admissionPathsValid()) throw new Error("an admitted terminal root was redirected");
|
|
282
|
+
return await original.call(this, {
|
|
283
|
+
...spec,
|
|
284
|
+
argv
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
const prepared = prepare(policy, spec.signal);
|
|
288
|
+
if (prepared === void 0) return await original.call(this, spec);
|
|
289
|
+
const argvPrefix = (await prepared).argv;
|
|
290
|
+
return await original.call(this, {
|
|
291
|
+
...spec,
|
|
292
|
+
argv: [...argvPrefix, ...spec.argv]
|
|
293
|
+
});
|
|
251
294
|
});
|
|
252
295
|
}));
|
|
253
296
|
});
|
|
@@ -260,9 +303,16 @@ function apply(ctx) {
|
|
|
260
303
|
const deniedEntry = (scope, target, key) => {
|
|
261
304
|
scope.effect(() => wrapMethod(target, key, (original) => function(spec, ...rest) {
|
|
262
305
|
const policy = policyOf(spec);
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
306
|
+
const run = () => {
|
|
307
|
+
const prepared = prepare(policy, spec?.signal);
|
|
308
|
+
if (prepared === void 0) return original.call(this, spec, ...rest);
|
|
309
|
+
return runDenied(prepared, policy, () => original.call(this, spec, ...rest));
|
|
310
|
+
};
|
|
311
|
+
if (ctx.get("multiRootRegistry")?.shared) {
|
|
312
|
+
if (isAsyncProcessEntry(original)) return withRegistryAdmission(ctx, policy, "process", run);
|
|
313
|
+
if (!ctx.multiRootScope.hasAdmission()) return ctx.get("multiRootRegistry").withPrimaryOperation(policy, run);
|
|
314
|
+
}
|
|
315
|
+
return run();
|
|
266
316
|
}));
|
|
267
317
|
};
|
|
268
318
|
ctx.inject(["shell"], (scope) => {
|