@dsh-electron/dsh-plugin-multi-root-workspace 0.1.5 → 0.1.7
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 +56 -1
- package/CHANGELOG.md +56 -1
- package/README.en.md +157 -92
- package/README.md +158 -93
- package/cordis.patch.yml +61 -0
- package/lib/client.js +2819 -115
- package/lib/client.js.map +1 -1
- package/lib/command-CESuwlVt.js +1306 -0
- package/lib/command.js +2 -2
- package/lib/{compat-82Y27Iom.js → compat-DF_yL1bB.js} +180 -4
- package/lib/compat.js +1 -1
- package/lib/contract-DZ4zczDT.js +238 -0
- package/lib/{fs-D_Sw3o5z.js → fs-9JIzGaf3.js} +55 -19
- package/lib/fs.js +2 -1
- package/lib/index.js +10 -8
- package/lib/{instructions-Cn1Jj5Xi.js → instructions-D19jRxN0.js} +109 -28
- package/lib/instructions.js +1 -1
- package/lib/lsp.js +43 -0
- package/lib/method-wrapper-CFY3p3j2.js +26 -0
- package/lib/process-fence.js +280 -0
- package/lib/providers.js +87 -0
- package/lib/{registry-DySMbjgW.js → registry-DCxC5Lay.js} +256 -35
- package/lib/registry.js +2 -2
- package/lib/{roots-DasWEBJk.js → roots-B-0l8mNG.js} +117 -20
- package/lib/sandbox-Kup4_ZIb.js +747 -0
- package/lib/sandbox.js +2 -1
- package/lib/scope.js +240 -25
- package/lib/session-directory-BLOstut1.js +9 -0
- package/lib/tool-context-CpgIFcCB.js +6 -0
- package/lib/types/client/RootSwitcher.d.ts +63 -0
- package/lib/types/client/WorkspaceFoldersAction.d.ts +41 -0
- package/lib/types/client/files-tree/FilesBody.module.css.d.ts +24 -0
- package/lib/types/client/files-tree/FilesTree.d.ts +47 -0
- package/lib/types/client/files-tree/adapter.d.ts +94 -0
- package/lib/types/client/files-tree/directory-node.d.ts +62 -0
- package/lib/types/client/files-tree/face.d.ts +107 -0
- package/lib/types/client/files-tree/locales.d.ts +10 -0
- package/lib/types/client/files-tree/primitives.d.ts +33 -0
- package/lib/types/client/files-tree/store.d.ts +81 -0
- package/lib/types/client/index.d.ts +13 -2
- package/lib/types/client/locales.d.ts +60 -0
- package/lib/types/client/open-in-app/OpenInAppAction.d.ts +27 -0
- package/lib/types/client/open-in-app/OpenTargetButton.d.ts +52 -0
- package/lib/types/client/open-in-app/OpenTargetButton.module.css.d.ts +29 -0
- package/lib/types/client/open-in-app/adapter.d.ts +46 -0
- package/lib/types/client/open-in-app/applications.d.ts +4 -0
- package/lib/types/client/open-in-app/controller.d.ts +53 -0
- package/lib/types/client/open-in-app/locales.d.ts +10 -0
- package/lib/types/client/open-in-app/open-failure-toast.d.ts +11 -0
- package/lib/types/client/open-in-app/open-path.d.ts +13 -0
- package/lib/types/client/panel-client.d.ts +2 -1
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/command.d.ts +16 -3
- package/lib/types/compat/client-icons.d.ts +18 -0
- package/lib/types/compat/client-session.d.ts +2 -0
- package/lib/types/compat/dsh-version.d.ts +5 -3
- package/lib/types/compat/instruction-overlap.d.ts +7 -0
- package/lib/types/compat/open-in-app-routes.d.ts +38 -0
- package/lib/types/compat/process-entrypoints.d.ts +31 -0
- package/lib/types/compat/process-result.d.ts +14 -0
- package/lib/types/compat/pty-marker.d.ts +83 -0
- package/lib/types/compat/sandbox-confine.d.ts +2 -0
- package/lib/types/compat/session-directory.d.ts +4 -0
- package/lib/types/compat/storage-zod.d.ts +65 -0
- package/lib/types/compat/tool-context.d.ts +9 -0
- package/lib/types/compat/windows-deny-preflight.d.ts +21 -0
- package/lib/types/compat/windows-native.d.ts +68 -0
- package/lib/types/compat/windows-path-guard.d.ts +56 -0
- package/lib/types/compat/workspace-files.d.ts +7 -0
- package/lib/types/contract.d.ts +35 -4
- package/lib/types/dialects.d.ts +99 -3
- package/lib/types/fs.d.ts +32 -10
- package/lib/types/index.d.ts +5 -0
- package/lib/types/lsp.d.ts +4 -0
- package/lib/types/method-wrapper.d.ts +2 -0
- package/lib/types/process-fence.d.ts +54 -0
- package/lib/types/profile-isolation.d.ts +178 -0
- package/lib/types/providers.d.ts +70 -0
- package/lib/types/registry.d.ts +132 -31
- package/lib/types/roots.d.ts +100 -13
- package/lib/types/sandbox.d.ts +59 -12
- package/lib/types/scope.d.ts +203 -14
- package/lib/types/windows-broker.d.ts +17 -0
- package/lib/types/windows-deny.d.ts +1 -0
- package/lib/types/windows-profile.d.ts +10 -0
- package/lib/types/windows-runner-contract.d.ts +97 -0
- package/lib/types/windows-runner.d.ts +1 -0
- package/lib/types/workspace-files.d.ts +4 -0
- package/lib/types/workspace-routing.d.ts +11 -0
- package/lib/windows-runner-contract-CEfmZzu0.js +141 -0
- package/lib/windows-runner.js +1311 -0
- package/lib/workspace-files.js +203 -0
- package/lib/workspace-routing-YPxaQ4V3.js +26 -0
- package/package.json +116 -51
- package/scripts/windows-broker/install.ps1 +89 -0
- package/scripts/windows-broker/rights.cs +58 -0
- package/scripts/windows-broker/service.cs +323 -0
- package/scripts/windows-broker/verify-install.ps1 +80 -0
- package/lib/command-DyOWujPo.js +0 -690
- package/lib/sandbox-D8Fw95FM.js +0 -290
- package/lib/{registry-lease-win32-dy8TQMD9.js → registry-lease-win32-DTO6QpT9.js} +2 -2
package/lib/sandbox-D8Fw95FM.js
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { SandboxUnavailableError } from "@deepseek-ai/dsh-sandbox";
|
|
2
|
-
import { LocalSandboxProvider } from "@deepseek-ai/dsh-sandbox-local";
|
|
3
|
-
//#region src/compat/sandbox-confine.ts
|
|
4
|
-
/** Whether a value is a thenable, i.e. whether the installed `confine` is async. */
|
|
5
|
-
function isThenable(value) {
|
|
6
|
-
return typeof value?.then === "function";
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Call the upstream `confine` and widen its result, preserving sync/async shape.
|
|
10
|
-
*
|
|
11
|
-
* @param base - the upstream `confine`, already bound to its provider.
|
|
12
|
-
* @param widen - the pure transformation to apply to the upstream result. It
|
|
13
|
-
* runs exactly once per call, on whichever release; it may throw, and the
|
|
14
|
-
* throw surfaces synchronously or as a rejection to match the base shape.
|
|
15
|
-
* @param argv - the caller's argv, forwarded verbatim.
|
|
16
|
-
* @param policy - the per-call policy, forwarded verbatim.
|
|
17
|
-
* @param signal - forwarded when the installed release accepts it.
|
|
18
|
-
* @returns the widened result, in the shape the installed release uses.
|
|
19
|
-
*/
|
|
20
|
-
function widenConfined(base, widen, argv, policy, signal) {
|
|
21
|
-
const confined = base(argv, policy, signal);
|
|
22
|
-
return isThenable(confined) ? confined.then(widen) : widen(confined);
|
|
23
|
-
}
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/dialects.ts
|
|
26
|
-
/**
|
|
27
|
-
* Raised when a confined argv cannot be recognized or widened. The provider
|
|
28
|
-
* converts this into the fail-closed `SandboxUnavailableError`: an unrecognized
|
|
29
|
-
* dialect must never degrade into "the primary root was granted after all".
|
|
30
|
-
*/
|
|
31
|
-
var DialectUnrecognizedError = class extends Error {
|
|
32
|
-
constructor(message) {
|
|
33
|
-
super(message);
|
|
34
|
-
this.name = "DialectUnrecognizedError";
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
/** The one SBPL form that carries the writable subpath grants. */
|
|
38
|
-
const SBPL_SUBPATH_FORM = /\(allow file-write\*\s*(?:\(subpath\s+"(?:[^"\\]|\\.)*"\)\s*)+\)/;
|
|
39
|
-
/** One SBPL `(subpath "…")` token inside that form. */
|
|
40
|
-
const SBPL_SUBPATH_TOKEN = /\(subpath\s+("(?:[^"\\]|\\.)*")\)/g;
|
|
41
|
-
/**
|
|
42
|
-
* Split a `confine()` result at the separator by arithmetic, then VERIFY the
|
|
43
|
-
* split: `confine` builds `[...profileArgs, '--', ...argv]`, so the separator
|
|
44
|
-
* sits exactly one element before the caller's argv. A mismatch means the
|
|
45
|
-
* provider no longer wraps argv the way this module assumes.
|
|
46
|
-
* @param confinedArgv - the argv `confine` returned.
|
|
47
|
-
* @param commandArgv - the argv the caller passed to `confine`.
|
|
48
|
-
* @returns the recognized dialect and the split parts.
|
|
49
|
-
*/
|
|
50
|
-
function splitConfined(confinedArgv, commandArgv) {
|
|
51
|
-
const separator = confinedArgv.length - commandArgv.length - 1;
|
|
52
|
-
if (separator < 1 || confinedArgv[separator] !== "--") throw new DialectUnrecognizedError(`confined argv is not "[...profile, --, ...argv]" (separator expected at ${separator}, got ${JSON.stringify(confinedArgv[separator])})`);
|
|
53
|
-
const commandArgs = confinedArgv.slice(separator + 1);
|
|
54
|
-
if (commandArgs.length !== commandArgv.length || commandArgs.some((argument, index) => argument !== commandArgv[index])) throw new DialectUnrecognizedError("confined argv does not end with the caller's argv verbatim");
|
|
55
|
-
const profileArgs = confinedArgv.slice(0, separator);
|
|
56
|
-
return {
|
|
57
|
-
dialect: detectDialect(profileArgs),
|
|
58
|
-
profileArgs,
|
|
59
|
-
separator,
|
|
60
|
-
commandArgs
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Recognize the runner dialect from the profile arguments alone. Exactly one
|
|
65
|
-
* marker must match; zero or several are both unrecognized.
|
|
66
|
-
* @param profileArgs - everything before the separator.
|
|
67
|
-
* @returns the dialect.
|
|
68
|
-
*/
|
|
69
|
-
function detectDialect(profileArgs) {
|
|
70
|
-
const matches = [];
|
|
71
|
-
if (profileArgs.length >= 2 && profileArgs[profileArgs.length - 2] === "-p") matches.push("seatbelt");
|
|
72
|
-
if (profileArgs.includes("--mode")) matches.push("windows-acl");
|
|
73
|
-
if (profileArgs.includes("--rw")) matches.push("landlock");
|
|
74
|
-
if (profileArgs.includes("--ro-bind")) matches.push("bwrap");
|
|
75
|
-
const [first, ...rest] = matches;
|
|
76
|
-
if (first === void 0) throw new DialectUnrecognizedError(`unrecognized sandbox profile arguments: ${JSON.stringify(profileArgs)}`);
|
|
77
|
-
if (rest.length > 0) throw new DialectUnrecognizedError(`ambiguous sandbox profile arguments (${matches.join(", ")}): ${JSON.stringify(profileArgs)}`);
|
|
78
|
-
return first;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Widen one dialect's profile arguments with the additional roots.
|
|
82
|
-
*
|
|
83
|
-
* Only `workspace-write` grants roots, mirroring the upstream mode meaning and
|
|
84
|
-
* the filesystem fence: `read-only` grants nothing, and `danger-full-access`
|
|
85
|
-
* never confines at all (bash and the PTY backend skip `confine` in that mode).
|
|
86
|
-
*
|
|
87
|
-
* A root the dialect ALREADY grants is not granted twice — that keeps this
|
|
88
|
-
* function aligned with the fence's own deduplication and, on bwrap, avoids
|
|
89
|
-
* binding the real `/tmp` over the ephemeral `--tmpfs /tmp` mount.
|
|
90
|
-
* @param dialect - the recognized dialect.
|
|
91
|
-
* @param profileArgs - the profile arguments to widen.
|
|
92
|
-
* @param policy - the per-call policy (its root is the policy root, never a new grant).
|
|
93
|
-
* @param additionalRoots - canonical additional roots, in scope order.
|
|
94
|
-
* @returns the widened profile arguments.
|
|
95
|
-
*/
|
|
96
|
-
function widenProfileArgs(dialect, profileArgs, policy, additionalRoots) {
|
|
97
|
-
if (policy.mode !== "workspace-write") throw new DialectUnrecognizedError(`dialect "${dialect}" grants write roots only under workspace-write, got mode "${policy.mode}"`);
|
|
98
|
-
if (additionalRoots.length === 0) return [...profileArgs];
|
|
99
|
-
switch (dialect) {
|
|
100
|
-
case "seatbelt": return widenSeatbelt(profileArgs, additionalRoots);
|
|
101
|
-
case "bwrap": return widenBwrap(profileArgs, policy.workspaceRoot, additionalRoots);
|
|
102
|
-
case "landlock": return widenLandlock(profileArgs, policy.workspaceRoot, additionalRoots);
|
|
103
|
-
case "windows-acl": return [...profileArgs];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/** Quote one path exactly the way the upstream Seatbelt profile builder does. */
|
|
107
|
-
function sbplString(path) {
|
|
108
|
-
return `"${path.replaceAll("\\", String.raw`\\`).replaceAll("\"", String.raw`\"`)}"`;
|
|
109
|
-
}
|
|
110
|
-
/** Unquote one SBPL string literal (including its surrounding quotes). */
|
|
111
|
-
function sbplValue(literal) {
|
|
112
|
-
return literal.slice(1, -1).replaceAll(String.raw`\"`, "\"").replaceAll(String.raw`\\`, "\\");
|
|
113
|
-
}
|
|
114
|
-
/** Append the additional roots to the SBPL form that carries the subpath grants. */
|
|
115
|
-
function widenSeatbelt(profileArgs, additionalRoots) {
|
|
116
|
-
const profile = profileArgs[profileArgs.length - 1];
|
|
117
|
-
if (profile === void 0 || profileArgs[profileArgs.length - 2] !== "-p") throw new DialectUnrecognizedError(`seatbelt profile is not "[<sandbox-exec>, …, -p, <SBPL>]": ${JSON.stringify(profileArgs)}`);
|
|
118
|
-
const form = SBPL_SUBPATH_FORM.exec(profile);
|
|
119
|
-
if (form === null) throw new DialectUnrecognizedError(`seatbelt profile has no "(allow file-write* (subpath …))" form to extend: ${JSON.stringify(profile)}`);
|
|
120
|
-
const matched = form[0];
|
|
121
|
-
const granted = new Set([...matched.matchAll(SBPL_SUBPATH_TOKEN)].map((token) => sbplValue(token[1])));
|
|
122
|
-
const added = additionalRoots.filter((root) => !granted.has(root)).map((root) => `(subpath ${sbplString(root)})`);
|
|
123
|
-
if (added.length === 0) return [...profileArgs];
|
|
124
|
-
const widened = profile.slice(0, form.index) + matched.slice(0, -1) + ` ${added.join(" ")})` + profile.slice(form.index + matched.length);
|
|
125
|
-
return [...profileArgs.slice(0, -1), widened];
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* The observed bwrap bind flag: the flag of the `[flag, <root>, <root>]` triple
|
|
129
|
-
* upstream built for the policy root.
|
|
130
|
-
*/
|
|
131
|
-
function bwrapBindFlag(profileArgs, workspaceRoot) {
|
|
132
|
-
for (let index = 2; index < profileArgs.length; index += 1) {
|
|
133
|
-
if (profileArgs[index] !== workspaceRoot || profileArgs[index - 1] !== workspaceRoot) continue;
|
|
134
|
-
const flag = profileArgs[index - 2];
|
|
135
|
-
if (flag === void 0 || !flag.startsWith("--") || flag.startsWith("--ro")) continue;
|
|
136
|
-
return flag;
|
|
137
|
-
}
|
|
138
|
-
throw new DialectUnrecognizedError(`bwrap profile has no writable bind of the policy root ${JSON.stringify(workspaceRoot)}: ${JSON.stringify(profileArgs)}`);
|
|
139
|
-
}
|
|
140
|
-
/** Append the additional roots as binds, after upstream's own root bind. */
|
|
141
|
-
function widenBwrap(profileArgs, workspaceRoot, additionalRoots) {
|
|
142
|
-
const flag = bwrapBindFlag(profileArgs, workspaceRoot);
|
|
143
|
-
const granted = /* @__PURE__ */ new Set();
|
|
144
|
-
for (let index = 0; index + 2 < profileArgs.length; index += 1) if (profileArgs[index] === flag && profileArgs[index + 1] === profileArgs[index + 2]) granted.add(profileArgs[index + 2]);
|
|
145
|
-
for (let index = 0; index + 1 < profileArgs.length; index += 1) if (profileArgs[index] === "--tmpfs") granted.add(profileArgs[index + 1]);
|
|
146
|
-
const added = additionalRoots.filter((root) => !granted.has(root)).flatMap((root) => [
|
|
147
|
-
flag,
|
|
148
|
-
root,
|
|
149
|
-
root
|
|
150
|
-
]);
|
|
151
|
-
return [...profileArgs, ...added];
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* The observed Landlock read-write flag: the flag preceding the policy root in
|
|
155
|
-
* the launcher's grant arguments.
|
|
156
|
-
*/
|
|
157
|
-
function landlockReadWriteFlag(profileArgs, workspaceRoot) {
|
|
158
|
-
for (let index = 1; index < profileArgs.length; index += 1) {
|
|
159
|
-
if (profileArgs[index] !== workspaceRoot) continue;
|
|
160
|
-
const flag = profileArgs[index - 1];
|
|
161
|
-
if (flag === void 0 || !flag.startsWith("--") || flag.startsWith("--ro")) continue;
|
|
162
|
-
return flag;
|
|
163
|
-
}
|
|
164
|
-
throw new DialectUnrecognizedError(`landlock profile has no read-write grant of the policy root ${JSON.stringify(workspaceRoot)}: ${JSON.stringify(profileArgs)}`);
|
|
165
|
-
}
|
|
166
|
-
/** Append the additional roots as read-write grants, after upstream's own root grant. */
|
|
167
|
-
function widenLandlock(profileArgs, workspaceRoot, additionalRoots) {
|
|
168
|
-
const flag = landlockReadWriteFlag(profileArgs, workspaceRoot);
|
|
169
|
-
const granted = /* @__PURE__ */ new Set();
|
|
170
|
-
for (let index = 0; index + 1 < profileArgs.length; index += 1) if (profileArgs[index] === flag) granted.add(profileArgs[index + 1]);
|
|
171
|
-
const added = additionalRoots.filter((root) => !granted.has(root)).flatMap((root) => [flag, root]);
|
|
172
|
-
return [...profileArgs, ...added];
|
|
173
|
-
}
|
|
174
|
-
//#endregion
|
|
175
|
-
//#region src/sandbox.ts
|
|
176
|
-
/**
|
|
177
|
-
* `MultiRootSandboxProvider`: the kernel-sandbox dialect provider mounted as
|
|
178
|
-
* `ctx.sandbox`.
|
|
179
|
-
*
|
|
180
|
-
* Every confined execution in this composition funnels through `confine()` —
|
|
181
|
-
* the bash executor wraps `['bash','-c',command]`, and the terminal backend
|
|
182
|
-
* wraps its PTY spawn argv — so widening the grant here is what gives bash, PTY,
|
|
183
|
-
* and the fs fence one and the same root set (requirement §13). The bash
|
|
184
|
-
* executor itself is therefore left upstream: it never computes roots.
|
|
185
|
-
*
|
|
186
|
-
* The widening itself lives in `./dialects.ts`: the upstream profile builders
|
|
187
|
-
* are unreachable in the published package, so the additional roots are grafted
|
|
188
|
-
* onto the profile `super.confine` actually produced (recognize the dialect
|
|
189
|
-
* structurally, clone its grant spelling, fail loudly when the shape is not
|
|
190
|
-
* known). This class owns only the policy that decides WHEN to graft:
|
|
191
|
-
*
|
|
192
|
-
* 1. with no additional roots, or under any mode other than `workspace-write`,
|
|
193
|
-
* the upstream result is returned ELEMENT FOR ELEMENT — same argv, same
|
|
194
|
-
* `enforcement`, same `denialSignatures`, same `runnerFailureRules`;
|
|
195
|
-
* 2. `enforcement`, `denialSignatures`, and `runnerFailureRules` are NEVER
|
|
196
|
-
* recomputed here, even when the argv is widened — the bash executor derives
|
|
197
|
-
* its denial and enforcement reporting from them;
|
|
198
|
-
* 3. only `argv` may differ, and a dialect whose shape cannot be extended fails
|
|
199
|
-
* loudly (`SandboxUnavailableError`) instead of silently confining to the
|
|
200
|
-
* primary root alone.
|
|
201
|
-
*
|
|
202
|
-
* @module @dsh-electron/dsh-plugin-multi-root-workspace/sandbox
|
|
203
|
-
*/
|
|
204
|
-
/**
|
|
205
|
-
* Local process-sandbox provider over the multi-root scope. Registers as
|
|
206
|
-
* `ctx.sandbox`; the runner chain, probing, denial dialects, and Windows ACL
|
|
207
|
-
* grant mechanics stay the upstream implementation's.
|
|
208
|
-
*/
|
|
209
|
-
var MultiRootSandboxProvider = class extends LocalSandboxProvider {
|
|
210
|
-
static inject = [
|
|
211
|
-
"multiRootCompat",
|
|
212
|
-
"sandboxPolicy",
|
|
213
|
-
"multiRootScope"
|
|
214
|
-
];
|
|
215
|
-
/** Whether the Windows ACL limitation has already been reported (once per provider). */
|
|
216
|
-
warnedAboutWindowsAcl = false;
|
|
217
|
-
/**
|
|
218
|
-
* Wrap `argv` so it executes confined under `policy` on this host, granting
|
|
219
|
-
* the scope's additional roots inside the SAME dialect profile.
|
|
220
|
-
*
|
|
221
|
-
* Under `read-only` nothing is added (the mode denies every write, additional
|
|
222
|
-
* roots included), and `danger-full-access` never reaches here at all: the
|
|
223
|
-
* bash executor and the PTY backend both hand their argv straight to the local
|
|
224
|
-
* runtime in that mode. Widening therefore only ever ADDS the scope's
|
|
225
|
-
* additional roots to an upstream `workspace-write` profile.
|
|
226
|
-
*
|
|
227
|
-
* The return shape is the INSTALLED upstream provider's: synchronous on a
|
|
228
|
-
* release with a synchronous `confine`, a promise on a release with an
|
|
229
|
-
* asynchronous one. `widenConfined` preserves whichever it is — see
|
|
230
|
-
* `./compat/sandbox-confine.ts`.
|
|
231
|
-
* @param argv - the exact argv the caller is about to spawn.
|
|
232
|
-
* @param policy - the file-effect policy this execution runs under.
|
|
233
|
-
* @param signal - cancellation, forwarded to releases that accept it.
|
|
234
|
-
* @returns the argv to spawn instead, plus the selected backend's facts.
|
|
235
|
-
*/
|
|
236
|
-
confine(argv, policy, signal) {
|
|
237
|
-
const base = super.confine;
|
|
238
|
-
return widenConfined(base.bind(this), (confined) => this.widenForScope(confined, argv, policy), argv, policy, signal);
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Graft the scope's additional roots onto one upstream `confine` result.
|
|
242
|
-
*
|
|
243
|
-
* Pure with respect to the upstream call: it only ever reads the scope and
|
|
244
|
-
* rewrites `argv`. `enforcement`, `denialSignatures` and `runnerFailureRules`
|
|
245
|
-
* are passed through untouched, because the bash executor derives its denial
|
|
246
|
-
* and enforcement reporting from them.
|
|
247
|
-
* @param confined - the upstream result for this call.
|
|
248
|
-
* @param argv - the caller's argv, needed to re-split the wrapped form.
|
|
249
|
-
* @param policy - the per-call policy.
|
|
250
|
-
* @returns the upstream result element for element, or one with a widened argv.
|
|
251
|
-
*/
|
|
252
|
-
widenForScope(confined, argv, policy) {
|
|
253
|
-
if (policy.mode !== "workspace-write") return confined;
|
|
254
|
-
const scope = this.ctx.multiRootScope.resolve(policy);
|
|
255
|
-
if (scope.additionalRoots.length === 0) return confined;
|
|
256
|
-
try {
|
|
257
|
-
const shape = splitConfined(confined.argv, argv);
|
|
258
|
-
if (shape.dialect === "windows-acl") {
|
|
259
|
-
this.warnAboutWindowsAcl(scope.additionalRoots.length);
|
|
260
|
-
return confined;
|
|
261
|
-
}
|
|
262
|
-
const profileArgs = widenProfileArgs(shape.dialect, shape.profileArgs, policy, scope.additionalRoots);
|
|
263
|
-
return {
|
|
264
|
-
...confined,
|
|
265
|
-
argv: [
|
|
266
|
-
...profileArgs,
|
|
267
|
-
"--",
|
|
268
|
-
...argv
|
|
269
|
-
]
|
|
270
|
-
};
|
|
271
|
-
} catch (error) {
|
|
272
|
-
if (!(error instanceof DialectUnrecognizedError)) throw error;
|
|
273
|
-
throw new SandboxUnavailableError(policy.mode, `multi-root workspace: cannot grant the additional workspace roots [${scope.additionalRoots.join(", ")}] under the primary root ${scope.primaryRoot} — ${error.message}`);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Report the documented first-release limitation exactly once: the Windows ACL
|
|
278
|
-
* rung grants through per-workspace write SIDs rather than argv paths, so
|
|
279
|
-
* additional roots stay unenforced for confined commands there while the
|
|
280
|
-
* in-process filesystem fence still allows them.
|
|
281
|
-
* @param rootCount - how many additional roots the scope carries.
|
|
282
|
-
*/
|
|
283
|
-
warnAboutWindowsAcl(rootCount) {
|
|
284
|
-
if (this.warnedAboutWindowsAcl) return;
|
|
285
|
-
this.warnedAboutWindowsAcl = true;
|
|
286
|
-
this.ctx.logger.warn(`multi-root workspace: this scope carries ${rootCount} additional workspace root(s), but the Windows ACL runner grants write access per workspace SID and cannot express them; confined bash/PTY runs will not be able to write them, while ctx.fs still can. Kernel-level multi-root on Windows is out of scope for the first release.`);
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
//#endregion
|
|
290
|
-
export { widenConfined as n, MultiRootSandboxProvider as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
3
1
|
import { realpathSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
4
|
//#region src/registry-lease-win32.ts
|
|
5
5
|
/**
|
|
6
6
|
* Windows named-kernel-semaphore adapter for the registry authority lease.
|