@sysid/sandbox-runtime-improved 0.0.52-sysid.1 → 0.0.55-sysid.1
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 +5 -2
- package/dist/cli.js +91 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/sandbox/http-proxy.d.ts +7 -0
- package/dist/sandbox/http-proxy.d.ts.map +1 -1
- package/dist/sandbox/http-proxy.js +20 -0
- package/dist/sandbox/http-proxy.js.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.d.ts +2 -0
- package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.js +107 -46
- package/dist/sandbox/linux-sandbox-utils.js.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.d.ts +3 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.js +32 -3
- package/dist/sandbox/macos-sandbox-utils.js.map +1 -1
- package/dist/sandbox/mitm-leaf.d.ts.map +1 -1
- package/dist/sandbox/mitm-leaf.js +18 -5
- package/dist/sandbox/mitm-leaf.js.map +1 -1
- package/dist/sandbox/sandbox-config.d.ts +63 -2
- package/dist/sandbox/sandbox-config.d.ts.map +1 -1
- package/dist/sandbox/sandbox-config.js +52 -2
- package/dist/sandbox/sandbox-config.js.map +1 -1
- package/dist/sandbox/sandbox-manager.d.ts +5 -0
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox/sandbox-manager.js +208 -44
- package/dist/sandbox/sandbox-manager.js.map +1 -1
- package/dist/sandbox/sandbox-schemas.d.ts +9 -3
- package/dist/sandbox/sandbox-schemas.d.ts.map +1 -1
- package/dist/sandbox/sandbox-utils.d.ts +1 -1
- package/dist/sandbox/sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/sandbox-utils.js +46 -18
- package/dist/sandbox/sandbox-utils.js.map +1 -1
- package/dist/sandbox/socks-proxy.d.ts +6 -0
- package/dist/sandbox/socks-proxy.d.ts.map +1 -1
- package/dist/sandbox/socks-proxy.js +11 -0
- package/dist/sandbox/socks-proxy.js.map +1 -1
- package/dist/sandbox/tls-terminate-proxy.js +18 -4
- package/dist/sandbox/tls-terminate-proxy.js.map +1 -1
- package/dist/sandbox/windows-sandbox-utils.d.ts +224 -0
- package/dist/sandbox/windows-sandbox-utils.d.ts.map +1 -0
- package/dist/sandbox/windows-sandbox-utils.js +433 -0
- package/dist/sandbox/windows-sandbox-utils.js.map +1 -0
- package/dist/vendor/srt-win/Cargo.lock +361 -0
- package/dist/vendor/srt-win/Cargo.toml +46 -0
- package/dist/vendor/srt-win/ci/cleanup.ps1 +49 -0
- package/dist/vendor/srt-win/ci/smoke-exec.ps1 +446 -0
- package/dist/vendor/srt-win/ci/smoke.ps1 +307 -0
- package/dist/vendor/srt-win/src/job.rs +102 -0
- package/dist/vendor/srt-win/src/launch.rs +732 -0
- package/dist/vendor/srt-win/src/lib.rs +20 -0
- package/dist/vendor/srt-win/src/main.rs +669 -0
- package/dist/vendor/srt-win/src/self_protect.rs +169 -0
- package/dist/vendor/srt-win/src/sid.rs +296 -0
- package/dist/vendor/srt-win/src/token.rs +341 -0
- package/dist/vendor/srt-win/src/util.rs +42 -0
- package/dist/vendor/srt-win/src/wfp.rs +992 -0
- package/dist/vendor/srt-win/src/winsta.rs +209 -0
- package/dist/vendor/srt-win/tests/sd_access_check_matrix.rs +259 -0
- package/package.json +2 -2
- package/vendor/srt-win/Cargo.lock +361 -0
- package/vendor/srt-win/Cargo.toml +46 -0
- package/vendor/srt-win/ci/cleanup.ps1 +49 -0
- package/vendor/srt-win/ci/smoke-exec.ps1 +446 -0
- package/vendor/srt-win/ci/smoke.ps1 +307 -0
- package/vendor/srt-win/src/job.rs +102 -0
- package/vendor/srt-win/src/launch.rs +732 -0
- package/vendor/srt-win/src/lib.rs +20 -0
- package/vendor/srt-win/src/main.rs +669 -0
- package/vendor/srt-win/src/self_protect.rs +169 -0
- package/vendor/srt-win/src/sid.rs +296 -0
- package/vendor/srt-win/src/token.rs +341 -0
- package/vendor/srt-win/src/util.rs +42 -0
- package/vendor/srt-win/src/wfp.rs +992 -0
- package/vendor/srt-win/src/winsta.rs +209 -0
- package/vendor/srt-win/tests/sd_access_check_matrix.rs +259 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import type { SandboxDependencyCheck } from './linux-sandbox-utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Windows sandbox backend.
|
|
4
|
+
*
|
|
5
|
+
* Network isolation is enforced by `srt-win.exe` — a Rust helper that
|
|
6
|
+
* manages a local discriminator group, a machine-wide WFP filter set
|
|
7
|
+
* keyed on that group's SID, and an `exec` subcommand that spawns the
|
|
8
|
+
* target under a restricted token (group flipped deny-only) inside a
|
|
9
|
+
* hardened job. The sandboxed child reaches the host only via the JS
|
|
10
|
+
* http/socks proxies, which `srt-win exec` points at via env vars.
|
|
11
|
+
*
|
|
12
|
+
* This module is a thin wrapper around the `srt-win` CLI; all status
|
|
13
|
+
* comes from live enumeration (group via `LookupAccountNameW` +
|
|
14
|
+
* token-membership check; WFP via providerData-tag enumeration under
|
|
15
|
+
* the configured sublayer). There is no marker file.
|
|
16
|
+
*
|
|
17
|
+
* Filesystem restrictions are NOT enforced on Windows yet.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_WINDOWS_GROUP_NAME = "sandbox-runtime-net";
|
|
20
|
+
export declare const DEFAULT_WINDOWS_PROXY_PORT_RANGE: readonly [number, number];
|
|
21
|
+
/** Identifies the discriminator group either by name or by SID. */
|
|
22
|
+
export interface WindowsGroupRef {
|
|
23
|
+
/** Local or domain group name. Default: `sandbox-runtime-net`. */
|
|
24
|
+
groupName?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Group SID in `S-1-…` form. Takes precedence over `groupName` —
|
|
27
|
+
* use for domain groups or where name resolution is unreliable.
|
|
28
|
+
*/
|
|
29
|
+
groupSid?: string;
|
|
30
|
+
}
|
|
31
|
+
export type WindowsGroupStatus = 'absent' | 'created-not-on-token' | 'ready';
|
|
32
|
+
export interface WindowsGroupStatusResult {
|
|
33
|
+
state: WindowsGroupStatus;
|
|
34
|
+
sid?: string;
|
|
35
|
+
warning?: string;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
export type WindowsWfpStatus = 'absent' | 'installed';
|
|
39
|
+
export interface WindowsWfpStatusResult {
|
|
40
|
+
state: WindowsWfpStatus;
|
|
41
|
+
filters: number;
|
|
42
|
+
/** `[low, high]` from the `permit-loopback` filter's tag, when present. */
|
|
43
|
+
portRange?: [number, number];
|
|
44
|
+
}
|
|
45
|
+
export interface WindowsSandboxParams {
|
|
46
|
+
command: string;
|
|
47
|
+
group: WindowsGroupRef;
|
|
48
|
+
/** JS HTTP proxy port — fed to `generateProxyEnvVars` for the returned env. */
|
|
49
|
+
httpProxyPort?: number;
|
|
50
|
+
/** JS SOCKS proxy port — fed to `generateProxyEnvVars` for the returned env. */
|
|
51
|
+
socksProxyPort?: number;
|
|
52
|
+
/** Per-session proxy auth token; embedded in proxy env URLs. */
|
|
53
|
+
proxyAuthToken?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Inner shell. `cmd` (default), `powershell`, or `pwsh`. The child's
|
|
56
|
+
* post-`/c` content is **passthrough** — `&` chains, `"…"` quotes
|
|
57
|
+
* exactly as written. The security boundary is at the OUTER spawn
|
|
58
|
+
* (this argv is spawned with `shell:false`); the inner cmd.exe runs
|
|
59
|
+
* INSIDE the sandbox so its metachars are the user's tool.
|
|
60
|
+
*/
|
|
61
|
+
binShell?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Locate `srt-win.exe`. Resolution order:
|
|
65
|
+
* 1. `SRT_WIN_PATH` env var (CI sets this to the freshly-built binary).
|
|
66
|
+
* 2. `<repo>/vendor/srt-win/target/release/srt-win.exe` (local cargo build).
|
|
67
|
+
* 3. `<repo>/dist/vendor/srt-win/target/release/srt-win.exe`
|
|
68
|
+
* (post-`npm run build` shape, when running from compiled output).
|
|
69
|
+
*
|
|
70
|
+
* Resolution via the optional `@anthropic-ai/sandbox-runtime-win32-*`
|
|
71
|
+
* platform packages is added separately.
|
|
72
|
+
*
|
|
73
|
+
* @throws if none exist.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getSrtWinPath(): string;
|
|
76
|
+
/**
|
|
77
|
+
* Query the discriminator group's state in SAM and in the current
|
|
78
|
+
* process's `TokenGroups`. `ready` means the group exists AND is
|
|
79
|
+
* enabled in the caller's token (i.e. the logout/login dance has
|
|
80
|
+
* happened). `created-not-on-token` means the install step ran but
|
|
81
|
+
* a fresh logon is needed before {@link initialize} can succeed.
|
|
82
|
+
*/
|
|
83
|
+
export declare function getWindowsGroupStatus(ref: WindowsGroupRef): WindowsGroupStatusResult;
|
|
84
|
+
/**
|
|
85
|
+
* Query the WFP filter set under the given sublayer. `installed` means
|
|
86
|
+
* srt-win-tagged `permit-group` AND `block` filters are both present
|
|
87
|
+
* under that sublayer. Detection is **tag-based** (providerData JSON);
|
|
88
|
+
* filters installed by other tooling without the tag are not counted.
|
|
89
|
+
*/
|
|
90
|
+
export declare function getWindowsWfpStatus(opts?: {
|
|
91
|
+
sublayerGuid?: string;
|
|
92
|
+
}): WindowsWfpStatusResult;
|
|
93
|
+
export interface WindowsInstallOptions extends WindowsGroupRef {
|
|
94
|
+
/** Add this user (instead of the current user) to the group. */
|
|
95
|
+
userSid?: string;
|
|
96
|
+
/** WFP sublayer GUID. Omit for srt-win's compile-time default. */
|
|
97
|
+
sublayerGuid?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Loopback PERMIT port range. Must match what
|
|
100
|
+
* `SandboxRuntimeConfig.windows.proxyPortRange` will be set to.
|
|
101
|
+
* Default {@link DEFAULT_WINDOWS_PROXY_PORT_RANGE}.
|
|
102
|
+
*/
|
|
103
|
+
proxyPortRange?: readonly [number, number];
|
|
104
|
+
/**
|
|
105
|
+
* Replace an existing install whose configuration differs
|
|
106
|
+
* (different group SID or port range under the same sublayer).
|
|
107
|
+
* Without this, install refuses with "already installed with
|
|
108
|
+
* different config" rather than silently overwriting.
|
|
109
|
+
*/
|
|
110
|
+
force?: boolean;
|
|
111
|
+
}
|
|
112
|
+
export interface WindowsInstallResult {
|
|
113
|
+
/** Post-install group state. */
|
|
114
|
+
group: WindowsGroupStatusResult;
|
|
115
|
+
/** Post-install WFP state. */
|
|
116
|
+
wfp: WindowsWfpStatusResult;
|
|
117
|
+
/**
|
|
118
|
+
* `true` if the user dismissed the UAC prompt. Not an error —
|
|
119
|
+
* the install simply didn't happen. Re-run when the user is
|
|
120
|
+
* ready to grant elevation.
|
|
121
|
+
*/
|
|
122
|
+
cancelled?: true;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* One-shot install: creates the discriminator group, adds the
|
|
126
|
+
* current user (or `userSid`), and installs the machine-wide WFP
|
|
127
|
+
* filter set — all in a single self-elevating process (one UAC
|
|
128
|
+
* prompt). Idempotent.
|
|
129
|
+
*
|
|
130
|
+
* Network for the calling user is **not disrupted** before the
|
|
131
|
+
* required logout: while the group is absent from the token, WFP
|
|
132
|
+
* filter-0 (PERMIT non-members) matches and traffic flows normally.
|
|
133
|
+
* After log-out/log-in, the group is enabled in the token and
|
|
134
|
+
* filter-1 (PERMIT group-enabled) takes over for the broker; only
|
|
135
|
+
* `srt-win exec` children (group flipped deny-only) fall through to
|
|
136
|
+
* the loopback/BLOCK filters.
|
|
137
|
+
*
|
|
138
|
+
* Returns the post-call group + WFP state. If the user cancels the
|
|
139
|
+
* UAC prompt this returns `{cancelled: true, …}` rather than
|
|
140
|
+
* throwing — cancellation is a user choice, not an error.
|
|
141
|
+
*
|
|
142
|
+
* @throws on group/WFP creation failure, or if filters already
|
|
143
|
+
* exist under `sublayerGuid` with different configuration and
|
|
144
|
+
* `force` is not set.
|
|
145
|
+
*/
|
|
146
|
+
export declare function installWindowsSandbox(opts?: WindowsInstallOptions): WindowsInstallResult;
|
|
147
|
+
/**
|
|
148
|
+
* Remove the WFP filter set under `sublayerGuid` (one UAC prompt).
|
|
149
|
+
* Idempotent.
|
|
150
|
+
*
|
|
151
|
+
* **Does NOT delete the discriminator group** — group membership is
|
|
152
|
+
* persistent user state and removing it would force every user to
|
|
153
|
+
* re-do the logout dance on the next install. Call
|
|
154
|
+
* {@link deleteWindowsGroup} explicitly if you want full teardown.
|
|
155
|
+
*
|
|
156
|
+
* @returns `{cancelled: true}` if the user dismissed UAC.
|
|
157
|
+
*/
|
|
158
|
+
export declare function uninstallWindowsSandbox(opts?: {
|
|
159
|
+
sublayerGuid?: string;
|
|
160
|
+
}): {
|
|
161
|
+
cancelled?: true;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Delete the discriminator group. Separate from
|
|
165
|
+
* {@link uninstallWindowsSandbox} so that uninstall→reinstall
|
|
166
|
+
* doesn't force a fresh logout for every member. **Requires
|
|
167
|
+
* elevation.** Idempotent (no-op if the group doesn't exist).
|
|
168
|
+
*/
|
|
169
|
+
export declare function deleteWindowsGroup(ref: WindowsGroupRef): void;
|
|
170
|
+
/**
|
|
171
|
+
* Granular primitive: create the discriminator group and add the
|
|
172
|
+
* current user (or `userSid`). Most callers should use
|
|
173
|
+
* {@link installWindowsSandbox} instead; this exists for
|
|
174
|
+
* enterprise/CI flows that manage group and WFP separately.
|
|
175
|
+
* **Requires elevation.** Idempotent.
|
|
176
|
+
*/
|
|
177
|
+
export declare function createWindowsGroup(ref: WindowsGroupRef & {
|
|
178
|
+
userSid?: string;
|
|
179
|
+
}): void;
|
|
180
|
+
/**
|
|
181
|
+
* Granular primitive: install the machine-wide WFP filter set
|
|
182
|
+
* under `sublayerGuid` keyed on the group SID. Most callers should
|
|
183
|
+
* use {@link installWindowsSandbox} instead; this exists for
|
|
184
|
+
* enterprise/CI flows that manage group and WFP separately.
|
|
185
|
+
* **Requires elevation.** Idempotent — re-running replaces any
|
|
186
|
+
* existing srt-win-tagged filters under that sublayer.
|
|
187
|
+
*/
|
|
188
|
+
export declare function createWindowsWfp(ref: WindowsGroupRef & {
|
|
189
|
+
sublayerGuid?: string;
|
|
190
|
+
proxyPortRange?: readonly [number, number];
|
|
191
|
+
}): void;
|
|
192
|
+
/**
|
|
193
|
+
* Build the spawn descriptor for running `command` inside the Windows
|
|
194
|
+
* sandbox: an `argv` array plus the `env` to spawn it with.
|
|
195
|
+
*
|
|
196
|
+
* Caller MUST spawn the result with `{shell: false}` — that is the
|
|
197
|
+
* security boundary that keeps untrusted bytes off the host's shell
|
|
198
|
+
* (the inner `cmd.exe /c` runs INSIDE the sandbox; see
|
|
199
|
+
* `vendor/srt-win/src/launch.rs` `build_cmdline` for the passthrough
|
|
200
|
+
* rationale) — AND with the returned `env`.
|
|
201
|
+
*
|
|
202
|
+
* Proxy configuration is single-sourced by {@link generateProxyEnvVars}
|
|
203
|
+
* (the same canonical builder used on macOS/Linux). `srt-win exec`
|
|
204
|
+
* takes no `--http-proxy` / `--socks-proxy` flags and synthesizes no
|
|
205
|
+
* proxy env; it forwards its own environment to the sandboxed child
|
|
206
|
+
* verbatim. So the full proxy set is merged over the broker's
|
|
207
|
+
* environment here and the child inherits it through the spawn.
|
|
208
|
+
*/
|
|
209
|
+
export declare function wrapCommandWithSandboxWindows(p: WindowsSandboxParams): {
|
|
210
|
+
argv: string[];
|
|
211
|
+
env: NodeJS.ProcessEnv;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Install instructions, surfaced verbatim in error messages.
|
|
215
|
+
* Tailored to the observed group state: if the install already
|
|
216
|
+
* ran (`created-not-on-token`), only the logout is missing.
|
|
217
|
+
*/
|
|
218
|
+
export declare function windowsInstallInstructions(ref: WindowsGroupRef, sublayerGuid: string | undefined, groupState: WindowsGroupStatus): string;
|
|
219
|
+
/**
|
|
220
|
+
* Check the Windows backend is ready to sandbox. Errors block
|
|
221
|
+
* `initialize()`; warnings are informational.
|
|
222
|
+
*/
|
|
223
|
+
export declare function checkWindowsDependencies(ref: WindowsGroupRef, sublayerGuid?: string): SandboxDependencyCheck;
|
|
224
|
+
//# sourceMappingURL=windows-sandbox-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows-sandbox-utils.d.ts","sourceRoot":"","sources":["../../src/sandbox/windows-sandbox-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { logForDebugging } from '../utils/debug.js';
|
|
6
|
+
import { generateProxyEnvVars } from './sandbox-utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Windows sandbox backend.
|
|
9
|
+
*
|
|
10
|
+
* Network isolation is enforced by `srt-win.exe` — a Rust helper that
|
|
11
|
+
* manages a local discriminator group, a machine-wide WFP filter set
|
|
12
|
+
* keyed on that group's SID, and an `exec` subcommand that spawns the
|
|
13
|
+
* target under a restricted token (group flipped deny-only) inside a
|
|
14
|
+
* hardened job. The sandboxed child reaches the host only via the JS
|
|
15
|
+
* http/socks proxies, which `srt-win exec` points at via env vars.
|
|
16
|
+
*
|
|
17
|
+
* This module is a thin wrapper around the `srt-win` CLI; all status
|
|
18
|
+
* comes from live enumeration (group via `LookupAccountNameW` +
|
|
19
|
+
* token-membership check; WFP via providerData-tag enumeration under
|
|
20
|
+
* the configured sublayer). There is no marker file.
|
|
21
|
+
*
|
|
22
|
+
* Filesystem restrictions are NOT enforced on Windows yet.
|
|
23
|
+
*/
|
|
24
|
+
// ────────────────────────────────────────────────────────────────────
|
|
25
|
+
// Types
|
|
26
|
+
// ────────────────────────────────────────────────────────────────────
|
|
27
|
+
export const DEFAULT_WINDOWS_GROUP_NAME = 'sandbox-runtime-net';
|
|
28
|
+
export const DEFAULT_WINDOWS_PROXY_PORT_RANGE = [
|
|
29
|
+
60080, 60089,
|
|
30
|
+
];
|
|
31
|
+
// ────────────────────────────────────────────────────────────────────
|
|
32
|
+
// Binary resolution
|
|
33
|
+
// ────────────────────────────────────────────────────────────────────
|
|
34
|
+
function repoRoot() {
|
|
35
|
+
// src/sandbox/windows-sandbox-utils.ts → repo root (compiled: dist/sandbox/…)
|
|
36
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
return path.resolve(here, '..', '..');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Locate `srt-win.exe`. Resolution order:
|
|
41
|
+
* 1. `SRT_WIN_PATH` env var (CI sets this to the freshly-built binary).
|
|
42
|
+
* 2. `<repo>/vendor/srt-win/target/release/srt-win.exe` (local cargo build).
|
|
43
|
+
* 3. `<repo>/dist/vendor/srt-win/target/release/srt-win.exe`
|
|
44
|
+
* (post-`npm run build` shape, when running from compiled output).
|
|
45
|
+
*
|
|
46
|
+
* Resolution via the optional `@anthropic-ai/sandbox-runtime-win32-*`
|
|
47
|
+
* platform packages is added separately.
|
|
48
|
+
*
|
|
49
|
+
* @throws if none exist.
|
|
50
|
+
*/
|
|
51
|
+
export function getSrtWinPath() {
|
|
52
|
+
const envPath = process.env.SRT_WIN_PATH;
|
|
53
|
+
if (envPath && fs.existsSync(envPath)) {
|
|
54
|
+
return envPath;
|
|
55
|
+
}
|
|
56
|
+
const root = repoRoot();
|
|
57
|
+
const candidates = [
|
|
58
|
+
path.join(root, 'vendor', 'srt-win', 'target', 'release', 'srt-win.exe'),
|
|
59
|
+
path.join(root, 'dist', 'vendor', 'srt-win', 'target', 'release', 'srt-win.exe'),
|
|
60
|
+
];
|
|
61
|
+
for (const c of candidates) {
|
|
62
|
+
if (fs.existsSync(c))
|
|
63
|
+
return c;
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`srt-win.exe not found. Set SRT_WIN_PATH or build with ` +
|
|
66
|
+
`\`cargo build --release --manifest-path vendor/srt-win/Cargo.toml\`. ` +
|
|
67
|
+
`Looked in: ${[envPath, ...candidates].filter(Boolean).join(', ')}`);
|
|
68
|
+
}
|
|
69
|
+
// ────────────────────────────────────────────────────────────────────
|
|
70
|
+
// Internal: spawn helpers
|
|
71
|
+
// ────────────────────────────────────────────────────────────────────
|
|
72
|
+
function groupRefArgs(ref) {
|
|
73
|
+
if (ref.groupSid)
|
|
74
|
+
return ['--group-sid', ref.groupSid];
|
|
75
|
+
return ['--name', ref.groupName ?? DEFAULT_WINDOWS_GROUP_NAME];
|
|
76
|
+
}
|
|
77
|
+
function runSrtWin(args) {
|
|
78
|
+
const exe = getSrtWinPath();
|
|
79
|
+
const r = spawnSync(exe, args, { encoding: 'utf8', timeout: 15000 });
|
|
80
|
+
if (r.error) {
|
|
81
|
+
throw new Error(`srt-win ${args[0]}: spawn failed: ${r.error.message}`);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
status: r.status,
|
|
85
|
+
stdout: (r.stdout ?? '').trim(),
|
|
86
|
+
stderr: (r.stderr ?? '').trim(),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function runSrtWinJson(args) {
|
|
90
|
+
const r = runSrtWin(args);
|
|
91
|
+
if (r.status !== 0) {
|
|
92
|
+
throw new Error(`srt-win ${args.join(' ')} exited ${r.status}: ${r.stderr || r.stdout}`);
|
|
93
|
+
}
|
|
94
|
+
// Status subcommands print exactly one line of JSON to stdout. stderr
|
|
95
|
+
// may carry `srt-win:` diagnostics — ignore it for parsing.
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(r.stdout);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
throw new Error(`srt-win ${args.join(' ')}: unparseable JSON output ` +
|
|
101
|
+
`${JSON.stringify(r.stdout)}: ${e.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ────────────────────────────────────────────────────────────────────
|
|
105
|
+
// Status / install API
|
|
106
|
+
// ────────────────────────────────────────────────────────────────────
|
|
107
|
+
/**
|
|
108
|
+
* Query the discriminator group's state in SAM and in the current
|
|
109
|
+
* process's `TokenGroups`. `ready` means the group exists AND is
|
|
110
|
+
* enabled in the caller's token (i.e. the logout/login dance has
|
|
111
|
+
* happened). `created-not-on-token` means the install step ran but
|
|
112
|
+
* a fresh logon is needed before {@link initialize} can succeed.
|
|
113
|
+
*/
|
|
114
|
+
export function getWindowsGroupStatus(ref) {
|
|
115
|
+
return runSrtWinJson(['group', 'status', ...groupRefArgs(ref)]);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Query the WFP filter set under the given sublayer. `installed` means
|
|
119
|
+
* srt-win-tagged `permit-group` AND `block` filters are both present
|
|
120
|
+
* under that sublayer. Detection is **tag-based** (providerData JSON);
|
|
121
|
+
* filters installed by other tooling without the tag are not counted.
|
|
122
|
+
*/
|
|
123
|
+
export function getWindowsWfpStatus(opts = {}) {
|
|
124
|
+
const args = ['wfp', 'status'];
|
|
125
|
+
if (opts.sublayerGuid)
|
|
126
|
+
args.push('--sublayer-guid', opts.sublayerGuid);
|
|
127
|
+
const raw = runSrtWinJson(args);
|
|
128
|
+
return {
|
|
129
|
+
state: raw.state,
|
|
130
|
+
filters: raw.filters,
|
|
131
|
+
...(raw.port_range && { portRange: raw.port_range }),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* One-shot install: creates the discriminator group, adds the
|
|
136
|
+
* current user (or `userSid`), and installs the machine-wide WFP
|
|
137
|
+
* filter set — all in a single self-elevating process (one UAC
|
|
138
|
+
* prompt). Idempotent.
|
|
139
|
+
*
|
|
140
|
+
* Network for the calling user is **not disrupted** before the
|
|
141
|
+
* required logout: while the group is absent from the token, WFP
|
|
142
|
+
* filter-0 (PERMIT non-members) matches and traffic flows normally.
|
|
143
|
+
* After log-out/log-in, the group is enabled in the token and
|
|
144
|
+
* filter-1 (PERMIT group-enabled) takes over for the broker; only
|
|
145
|
+
* `srt-win exec` children (group flipped deny-only) fall through to
|
|
146
|
+
* the loopback/BLOCK filters.
|
|
147
|
+
*
|
|
148
|
+
* Returns the post-call group + WFP state. If the user cancels the
|
|
149
|
+
* UAC prompt this returns `{cancelled: true, …}` rather than
|
|
150
|
+
* throwing — cancellation is a user choice, not an error.
|
|
151
|
+
*
|
|
152
|
+
* @throws on group/WFP creation failure, or if filters already
|
|
153
|
+
* exist under `sublayerGuid` with different configuration and
|
|
154
|
+
* `force` is not set.
|
|
155
|
+
*/
|
|
156
|
+
export function installWindowsSandbox(opts = {}) {
|
|
157
|
+
const args = ['install', ...groupRefArgs(opts)];
|
|
158
|
+
if (opts.userSid)
|
|
159
|
+
args.push('--user-sid', opts.userSid);
|
|
160
|
+
if (opts.sublayerGuid)
|
|
161
|
+
args.push('--sublayer-guid', opts.sublayerGuid);
|
|
162
|
+
if (opts.proxyPortRange) {
|
|
163
|
+
args.push('--proxy-port-range', `${opts.proxyPortRange[0]}-${opts.proxyPortRange[1]}`);
|
|
164
|
+
}
|
|
165
|
+
if (opts.force)
|
|
166
|
+
args.push('--force');
|
|
167
|
+
const r = runSrtWin(args);
|
|
168
|
+
logForDebugging(`[Sandbox Windows] install exit=${r.status}: ${r.stderr || r.stdout}`);
|
|
169
|
+
// srt-win install exit-code contract:
|
|
170
|
+
// 0 ok
|
|
171
|
+
// 10 user cancelled UAC elevation
|
|
172
|
+
// 11 group create failed
|
|
173
|
+
// 12 WFP install failed
|
|
174
|
+
// 13 already installed with different config (use --force)
|
|
175
|
+
// 1 other error (stderr has detail)
|
|
176
|
+
const out = r.stderr || r.stdout;
|
|
177
|
+
switch (r.status) {
|
|
178
|
+
case 0:
|
|
179
|
+
break;
|
|
180
|
+
case 10:
|
|
181
|
+
return {
|
|
182
|
+
group: getWindowsGroupStatus(opts),
|
|
183
|
+
wfp: getWindowsWfpStatus({ sublayerGuid: opts.sublayerGuid }),
|
|
184
|
+
cancelled: true,
|
|
185
|
+
};
|
|
186
|
+
case 11:
|
|
187
|
+
throw new Error(`srt-win install: group create failed: ${out}`);
|
|
188
|
+
case 12:
|
|
189
|
+
throw new Error(`srt-win install: WFP filter install failed: ${out}`);
|
|
190
|
+
case 13:
|
|
191
|
+
throw new Error(`srt-win install: filters already exist under this sublayer with ` +
|
|
192
|
+
`different configuration (group SID or port range). ` +
|
|
193
|
+
`Pass {force: true} to replace, or pick a different sublayerGuid. ` +
|
|
194
|
+
`Output: ${out}`);
|
|
195
|
+
default:
|
|
196
|
+
throw new Error(`srt-win install failed (exit ${r.status}): ${out}`);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
group: getWindowsGroupStatus(opts),
|
|
200
|
+
wfp: getWindowsWfpStatus({ sublayerGuid: opts.sublayerGuid }),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Remove the WFP filter set under `sublayerGuid` (one UAC prompt).
|
|
205
|
+
* Idempotent.
|
|
206
|
+
*
|
|
207
|
+
* **Does NOT delete the discriminator group** — group membership is
|
|
208
|
+
* persistent user state and removing it would force every user to
|
|
209
|
+
* re-do the logout dance on the next install. Call
|
|
210
|
+
* {@link deleteWindowsGroup} explicitly if you want full teardown.
|
|
211
|
+
*
|
|
212
|
+
* @returns `{cancelled: true}` if the user dismissed UAC.
|
|
213
|
+
*/
|
|
214
|
+
export function uninstallWindowsSandbox(opts = {}) {
|
|
215
|
+
const args = ['uninstall'];
|
|
216
|
+
if (opts.sublayerGuid)
|
|
217
|
+
args.push('--sublayer-guid', opts.sublayerGuid);
|
|
218
|
+
const r = runSrtWin(args);
|
|
219
|
+
logForDebugging(`[Sandbox Windows] uninstall exit=${r.status}: ${r.stderr || r.stdout}`);
|
|
220
|
+
if (r.status === 10)
|
|
221
|
+
return { cancelled: true };
|
|
222
|
+
if (r.status !== 0) {
|
|
223
|
+
throw new Error(`srt-win uninstall failed (exit ${r.status}): ${r.stderr || r.stdout}`);
|
|
224
|
+
}
|
|
225
|
+
return {};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Delete the discriminator group. Separate from
|
|
229
|
+
* {@link uninstallWindowsSandbox} so that uninstall→reinstall
|
|
230
|
+
* doesn't force a fresh logout for every member. **Requires
|
|
231
|
+
* elevation.** Idempotent (no-op if the group doesn't exist).
|
|
232
|
+
*/
|
|
233
|
+
export function deleteWindowsGroup(ref) {
|
|
234
|
+
const r = runSrtWin(['group', 'delete', ...groupRefArgs(ref)]);
|
|
235
|
+
if (r.status !== 0) {
|
|
236
|
+
throw new Error(`srt-win group delete failed (exit ${r.status}). ` +
|
|
237
|
+
`Requires elevation. Output: ${r.stderr || r.stdout}`);
|
|
238
|
+
}
|
|
239
|
+
logForDebugging(`[Sandbox Windows] group delete: ${r.stderr || r.stdout}`);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Granular primitive: create the discriminator group and add the
|
|
243
|
+
* current user (or `userSid`). Most callers should use
|
|
244
|
+
* {@link installWindowsSandbox} instead; this exists for
|
|
245
|
+
* enterprise/CI flows that manage group and WFP separately.
|
|
246
|
+
* **Requires elevation.** Idempotent.
|
|
247
|
+
*/
|
|
248
|
+
export function createWindowsGroup(ref) {
|
|
249
|
+
const args = ['group', 'create', ...groupRefArgs(ref)];
|
|
250
|
+
if (ref.userSid)
|
|
251
|
+
args.push('--user-sid', ref.userSid);
|
|
252
|
+
const r = runSrtWin(args);
|
|
253
|
+
if (r.status !== 0) {
|
|
254
|
+
throw new Error(`srt-win group create failed (exit ${r.status}). ` +
|
|
255
|
+
`This requires elevation — run as administrator. ` +
|
|
256
|
+
`Output: ${r.stderr || r.stdout}`);
|
|
257
|
+
}
|
|
258
|
+
logForDebugging(`[Sandbox Windows] group create: ${r.stderr || r.stdout}`);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Granular primitive: install the machine-wide WFP filter set
|
|
262
|
+
* under `sublayerGuid` keyed on the group SID. Most callers should
|
|
263
|
+
* use {@link installWindowsSandbox} instead; this exists for
|
|
264
|
+
* enterprise/CI flows that manage group and WFP separately.
|
|
265
|
+
* **Requires elevation.** Idempotent — re-running replaces any
|
|
266
|
+
* existing srt-win-tagged filters under that sublayer.
|
|
267
|
+
*/
|
|
268
|
+
export function createWindowsWfp(ref) {
|
|
269
|
+
const args = ['wfp', 'install', ...groupRefArgs(ref)];
|
|
270
|
+
if (ref.sublayerGuid)
|
|
271
|
+
args.push('--sublayer-guid', ref.sublayerGuid);
|
|
272
|
+
if (ref.proxyPortRange) {
|
|
273
|
+
args.push('--proxy-port-range', `${ref.proxyPortRange[0]}-${ref.proxyPortRange[1]}`);
|
|
274
|
+
}
|
|
275
|
+
const r = runSrtWin(args);
|
|
276
|
+
if (r.status !== 0) {
|
|
277
|
+
throw new Error(`srt-win wfp install failed (exit ${r.status}). ` +
|
|
278
|
+
`This requires elevation — run as administrator. ` +
|
|
279
|
+
`Output: ${r.stderr || r.stdout}`);
|
|
280
|
+
}
|
|
281
|
+
logForDebugging(`[Sandbox Windows] wfp install: ${r.stderr || r.stdout}`);
|
|
282
|
+
}
|
|
283
|
+
// ────────────────────────────────────────────────────────────────────
|
|
284
|
+
// Wrap
|
|
285
|
+
// ────────────────────────────────────────────────────────────────────
|
|
286
|
+
/**
|
|
287
|
+
* Build the spawn descriptor for running `command` inside the Windows
|
|
288
|
+
* sandbox: an `argv` array plus the `env` to spawn it with.
|
|
289
|
+
*
|
|
290
|
+
* Caller MUST spawn the result with `{shell: false}` — that is the
|
|
291
|
+
* security boundary that keeps untrusted bytes off the host's shell
|
|
292
|
+
* (the inner `cmd.exe /c` runs INSIDE the sandbox; see
|
|
293
|
+
* `vendor/srt-win/src/launch.rs` `build_cmdline` for the passthrough
|
|
294
|
+
* rationale) — AND with the returned `env`.
|
|
295
|
+
*
|
|
296
|
+
* Proxy configuration is single-sourced by {@link generateProxyEnvVars}
|
|
297
|
+
* (the same canonical builder used on macOS/Linux). `srt-win exec`
|
|
298
|
+
* takes no `--http-proxy` / `--socks-proxy` flags and synthesizes no
|
|
299
|
+
* proxy env; it forwards its own environment to the sandboxed child
|
|
300
|
+
* verbatim. So the full proxy set is merged over the broker's
|
|
301
|
+
* environment here and the child inherits it through the spawn.
|
|
302
|
+
*/
|
|
303
|
+
export function wrapCommandWithSandboxWindows(p) {
|
|
304
|
+
const exe = getSrtWinPath();
|
|
305
|
+
const argv = [exe, 'exec', ...groupRefArgs(p.group)];
|
|
306
|
+
argv.push('--');
|
|
307
|
+
const systemRoot = process.env.SystemRoot ?? 'C:\\Windows';
|
|
308
|
+
const shell = (p.binShell ?? 'cmd').toLowerCase();
|
|
309
|
+
if (shell === 'pwsh' || shell.includes('powershell')) {
|
|
310
|
+
const psExe = shell === 'pwsh'
|
|
311
|
+
? 'pwsh.exe'
|
|
312
|
+
: path.join(systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
313
|
+
argv.push(psExe, '-NoProfile', '-Command', p.command);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// cmd /d (no AutoRun) /s (strip first+last quote of post-/c by
|
|
317
|
+
// position) /c (run-then-exit). The `command` string lands as a
|
|
318
|
+
// single argv element; srt-win's build_cmdline wraps it in one
|
|
319
|
+
// outer "…" pair for /s to consume. See launch.rs.
|
|
320
|
+
argv.push(path.join(systemRoot, 'System32', 'cmd.exe'), '/d', '/s', '/c', p.command);
|
|
321
|
+
}
|
|
322
|
+
// Generated proxy vars override any inherited ones so the child
|
|
323
|
+
// always routes through this sandbox's proxies.
|
|
324
|
+
const generated = envListToObject(generateProxyEnvVars(p.httpProxyPort, p.socksProxyPort, undefined, p.proxyAuthToken));
|
|
325
|
+
// TMPDIR is a POSIX path meant for the macOS/Linux FS sandbox — it
|
|
326
|
+
// serves no purpose on Windows and breaks msys2 tools (mktemp etc.).
|
|
327
|
+
delete generated.TMPDIR;
|
|
328
|
+
const env = { ...process.env, ...generated };
|
|
329
|
+
return { argv, env };
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Parse a list of `KEY=VALUE` strings (as produced by
|
|
333
|
+
* {@link generateProxyEnvVars}) into an object. Splits on the FIRST
|
|
334
|
+
* `=` only, so values containing `=` survive intact.
|
|
335
|
+
*/
|
|
336
|
+
function envListToObject(list) {
|
|
337
|
+
const out = {};
|
|
338
|
+
for (const entry of list) {
|
|
339
|
+
const eq = entry.indexOf('=');
|
|
340
|
+
if (eq === -1)
|
|
341
|
+
continue;
|
|
342
|
+
out[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
343
|
+
}
|
|
344
|
+
return out;
|
|
345
|
+
}
|
|
346
|
+
// ────────────────────────────────────────────────────────────────────
|
|
347
|
+
// Dependency / readiness check
|
|
348
|
+
// ────────────────────────────────────────────────────────────────────
|
|
349
|
+
/**
|
|
350
|
+
* Install instructions, surfaced verbatim in error messages.
|
|
351
|
+
* Tailored to the observed group state: if the install already
|
|
352
|
+
* ran (`created-not-on-token`), only the logout is missing.
|
|
353
|
+
*/
|
|
354
|
+
export function windowsInstallInstructions(ref, sublayerGuid, groupState) {
|
|
355
|
+
if (groupState === 'created-not-on-token') {
|
|
356
|
+
return (`The discriminator group exists but is not yet in this session's ` +
|
|
357
|
+
`token. LOG OUT and back in to pick up the new group membership ` +
|
|
358
|
+
`(it enters TokenGroups at logon). Network is not disrupted ` +
|
|
359
|
+
`meanwhile — WFP filter-0 PERMITs traffic while the group is absent ` +
|
|
360
|
+
`from your token.`);
|
|
361
|
+
}
|
|
362
|
+
const g = ref.groupSid
|
|
363
|
+
? `--group-sid ${ref.groupSid}`
|
|
364
|
+
: `--name ${ref.groupName ?? DEFAULT_WINDOWS_GROUP_NAME}`;
|
|
365
|
+
const sl = sublayerGuid ? ` --sublayer-guid ${sublayerGuid}` : '';
|
|
366
|
+
return (`Windows sandbox needs a one-time install (one UAC prompt):\n` +
|
|
367
|
+
` npx sandbox-runtime windows-install\n` +
|
|
368
|
+
` — or call installWindowsSandbox(), or run ` +
|
|
369
|
+
`\`srt-win.exe install ${g}${sl}\` directly —\n` +
|
|
370
|
+
`then LOG OUT and back in (the group SID enters TokenGroups at logon).\n` +
|
|
371
|
+
`Network is not disrupted before the logout: while the group is absent ` +
|
|
372
|
+
`from your token, WFP filter-0 PERMITs all traffic.`);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Check the Windows backend is ready to sandbox. Errors block
|
|
376
|
+
* `initialize()`; warnings are informational.
|
|
377
|
+
*/
|
|
378
|
+
export function checkWindowsDependencies(ref, sublayerGuid) {
|
|
379
|
+
const errors = [];
|
|
380
|
+
const warnings = [];
|
|
381
|
+
// 1. Binary present.
|
|
382
|
+
let exe;
|
|
383
|
+
try {
|
|
384
|
+
exe = getSrtWinPath();
|
|
385
|
+
}
|
|
386
|
+
catch (e) {
|
|
387
|
+
return { errors: [e.message], warnings };
|
|
388
|
+
}
|
|
389
|
+
logForDebugging(`[Sandbox Windows] using srt-win at ${exe}`);
|
|
390
|
+
// 2. Group ready (exists AND enabled in the caller's token).
|
|
391
|
+
let gs;
|
|
392
|
+
try {
|
|
393
|
+
gs = getWindowsGroupStatus(ref);
|
|
394
|
+
}
|
|
395
|
+
catch (e) {
|
|
396
|
+
errors.push(`srt-win group status failed: ${e.message}`);
|
|
397
|
+
return { errors, warnings };
|
|
398
|
+
}
|
|
399
|
+
if (gs.state !== 'ready') {
|
|
400
|
+
errors.push(`Discriminator group is ${gs.state}` +
|
|
401
|
+
(gs.sid ? ` (sid=${gs.sid})` : '') +
|
|
402
|
+
`. ` +
|
|
403
|
+
windowsInstallInstructions(ref, sublayerGuid, gs.state));
|
|
404
|
+
}
|
|
405
|
+
if (gs.warning)
|
|
406
|
+
warnings.push(gs.warning);
|
|
407
|
+
// 3. WFP filters installed under the sublayer.
|
|
408
|
+
let ws;
|
|
409
|
+
try {
|
|
410
|
+
ws = getWindowsWfpStatus({ sublayerGuid });
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
errors.push(`srt-win wfp status failed: ${e.message}`);
|
|
414
|
+
return { errors, warnings };
|
|
415
|
+
}
|
|
416
|
+
if (ws.state !== 'installed') {
|
|
417
|
+
// If the group is also not-ready, the group-state error above
|
|
418
|
+
// already gave the right instruction; don't repeat. Only
|
|
419
|
+
// surface a separate WFP error when group IS ready (i.e.
|
|
420
|
+
// someone uninstalled filters but kept the group).
|
|
421
|
+
if (gs.state === 'ready') {
|
|
422
|
+
errors.push(`WFP filters not installed under sublayer ` +
|
|
423
|
+
`${sublayerGuid ?? '(default)'}. ` +
|
|
424
|
+
windowsInstallInstructions(ref, sublayerGuid, 'absent'));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else if (ws.portRange) {
|
|
428
|
+
logForDebugging(`[Sandbox Windows] WFP installed: ${ws.filters} filters, ` +
|
|
429
|
+
`proxy port range ${ws.portRange[0]}-${ws.portRange[1]}`);
|
|
430
|
+
}
|
|
431
|
+
return { errors, warnings };
|
|
432
|
+
}
|
|
433
|
+
//# sourceMappingURL=windows-sandbox-utils.js.map
|