@deepseek-ai/dsh-sandbox-windows-acl 0.1.1-rc.2 → 0.1.2-alpha.2
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.i18n.yaml +2 -2
- package/README.md +125 -39
- package/README.zh.md +127 -43
- package/lib/index.js +2 -2
- package/lib/runner.js +1 -1
- package/lib/types/ffi.d.ts +54 -193
- package/lib/types/index.d.ts +0 -2
- package/lib/types/spawn.d.ts +24 -85
- package/lib/types/token.d.ts +3 -2
- package/lib/types/win32-abi.d.ts +50 -130
- package/lib/{types-CNjZgO4h.js → types-DuU3lSVe.js} +119 -606
- package/package.json +9 -8
- package/lib/types/errors.d.ts +0 -15
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/sandbox/sandbox-windows-acl/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: d742789e3f90711ff0cb322a71e73c5caf291a39
|
|
6
|
+
README.zh.md: a5c3a3e92788f96b969d42b127cd7010a0d52d2f
|
package/README.md
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "The Windows write-restriction sandbox backend for users and maintainers choosing, configuring, or debugging restricted-token process confinement on Windows."
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-sandbox-windows-acl
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`dsh-sandbox-windows-acl` confines Windows processes by write restriction: a child runs under a restricted token whose write access is limited to the workspace and a private temp directory, so `workspace-write` allows those writes and `read-only` allows none. It ships as the win32 rung of `dsh-sandbox-local`: mounting the local provider on Windows gives every confined bash or pwsh call this backend automatically. It can also be embedded directly through the `AclSandbox` API to spawn confined children with captured stdio. Every Win32 call is checked and failures throw, so a child is never spawned unrestricted. Enforcement is partial by design — the restricted token must retain Everyone for process initialization, and NTFS hard links can alias one file object across paths — so the backend reports `partial` and callers that need the absolute boundary can surface it.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
6
24
|
|
|
7
|
-
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
8
27
|
|
|
9
|
-
|
|
28
|
+
On Windows, mounting the local sandbox provider makes this backend the runner behind `ctx.sandbox` — no extra configuration. Embed the `AclSandbox` API directly when you spawn confined children outside the harness.
|
|
10
29
|
|
|
11
|
-
|
|
30
|
+
### When to choose it
|
|
31
|
+
|
|
32
|
+
Choose it for Windows compositions that confine subprocess file effects under `read-only` or `workspace-write`. Choose a different mechanism when the child must also be read-confined or network-restricted: `WRITE_RESTRICTED` intersects write accesses only, so pair this backend with a read-side policy or an AppContainer capability token for stronger confinement.
|
|
33
|
+
|
|
34
|
+
### Direct API
|
|
35
|
+
|
|
36
|
+
`AclSandbox` spawns a confined child with captured stdio (or inherited stdio for runner-style use). It requires an explicit private temp directory, or `tempDir: null` to disable temp writes — the ambient temp root is never an implicit grant.
|
|
12
37
|
|
|
13
38
|
```ts
|
|
14
39
|
import { mkdtempSync, rmSync } from 'node:fs'
|
|
@@ -38,53 +63,95 @@ sandbox.dispose() // revokes the revocable (temp) grant, keeps the standing work
|
|
|
38
63
|
rmSync(tempDir, { recursive: true, force: true })
|
|
39
64
|
```
|
|
40
65
|
|
|
41
|
-
|
|
66
|
+
The workspace ACEs are granted standing — `dispose()` leaves them, because they are the cross-instance reuse cache — while the distinct temp SID is granted revocably. The server-side counterpart is the `AclWriteGrant` class: `add(path, standing)` per directory, and `dispose()` revokes the revocable paths and frees the SID.
|
|
67
|
+
|
|
68
|
+
### What confinement gives you
|
|
69
|
+
|
|
70
|
+
Under `workspace-write`, the child may write into the workspace and its private temp directory; other ACL-addressable writes are denied except the documented Everyone and hard-link boundaries. Under `read-only`, no explicit write grants exist, so writes are denied with the same documented ambient boundaries.
|
|
71
|
+
|
|
72
|
+
Temp isolation is per live session/workspace pair: sessions sharing a workspace share its write authority but cannot write one another's temp directories. A fresh provider always chooses a new temp path and SID, so crash residue cannot block or authorize a resumed session.
|
|
73
|
+
|
|
74
|
+
### Failures and recovery
|
|
75
|
+
|
|
76
|
+
`init()` throws on any Win32 failure — the child is never spawned unrestricted. A runner that fails before executing the command prints `windows-acl-run: <detail>` to stderr and exits 127, which the seam's runner-failure rules classify as a broken sandbox rather than a denial. Cleanup is best-effort by design: `dispose()` attempts every temp revocation and aggregates failures into an `AggregateError`.
|
|
77
|
+
|
|
78
|
+
-----
|
|
79
|
+
|
|
80
|
+
<a id="understand-the-implementation"></a>
|
|
81
|
+
## Understand the implementation
|
|
82
|
+
|
|
83
|
+
<details>
|
|
84
|
+
<summary>Implementation internals — click to expand</summary>
|
|
85
|
+
|
|
86
|
+
This section explains the restricted-token mechanism, the token lists, the runner contract, and the verified boundaries; the observable behavior is fully covered in [Use this package](#use-this-package).
|
|
87
|
+
|
|
88
|
+
### Mechanism
|
|
42
89
|
|
|
43
|
-
|
|
90
|
+
The caller's token is duplicated into a `WRITE_RESTRICTED` token whose restricting SIDs carry separate workspace and private-temp capabilities. Windows performs the access check twice — once against the normal SIDs, once against the restricting SIDs — and grants write-class access only where both checks pass. The workspace SID is derived deterministically from the canonical workspace path (`workspaceWriteSid`), so the workspace-root ACE materializes once per workspace per machine and every later session, call, or restart hits the exact-ACE skip. Each live session/workspace pair instead receives a random private temp directory and a SID derived from that path (`tempWriteSid`), so sessions share the intended workspace authority without inheriting one another's temp authority. Every policy-specific Win32 call and every process primitive from [`dsh-win32-process`](../../subprocess/win32-process/README.md) is checked; failures throw `Win32Error` carrying the API name, exact code, system text, and failing context — fail-closed by construction.
|
|
44
91
|
|
|
45
|
-
|
|
92
|
+
### Modes and token lists
|
|
93
|
+
|
|
94
|
+
`workspace-write` (logon SID, Everyone, workspace SID, temp SID) grants the workspace and the session's private temp subdirectory separate Write grants; `read-only` (logon SID, Everyone — no write SID) grants none. The keep-alive group (logon SID + Everyone) is present in both modes: without it, early DLL initialization dies with `0xC0000142` and CNG crashes pwsh with `0xE0434352`. The write SID stays out of the read-only list on purpose: the standing workspace grant from an earlier workspace-write period remains inert because the write-restricted pass-2 check grants only what the restricting list carries, while the standing ACE keeps a re-upgrade free of re-propagation.
|
|
95
|
+
|
|
96
|
+
NUL writes are ambient, not granted: the device DACL grants Everyone read+write+execute (`0x1201BF`), so openers whose mask fits it (cmd `> NUL`, node `\\.\NUL`) can write it in both modes. `Set-Content NUL` fails in both modes (a PowerShell/.NET-layer effect, not the device DACL), while PowerShell's `> $null` redirection keeps working.
|
|
97
|
+
|
|
98
|
+
Authenticated Users is absent from both lists — the WMI namespace security check fails (`0x80041003`), so CIM cmdlets and `Get-ComputerInfo` are unavailable in every confined mode, and the C:\-root tree-creation escape is closed. INTERACTIVE/LOCAL are absent too: the host's Public tree grants write to INTERACTIVE, so Public writes are denied.
|
|
99
|
+
|
|
100
|
+
### The confinement runner
|
|
101
|
+
|
|
102
|
+
The seam-facing shape is the runner entry (`./runner`): an argv-prefix wrapper `dsh-sandbox-local` spawns in place of the caller's command, with the same architecture as bwrap/landlock-run/sandbox-exec. The runner creates the restricted token, spawns the wrapped argv under it with the caller's stdio passed straight through, wraps the child in a `KILL_ON_JOB_CLOSE` job, mirrors the child's exit code, and revokes its self-managed temp grant on exit. Every runner-side failure prints `windows-acl-run: <detail>` to stderr and exits 127 — the seam's runner-failure rules match that signature.
|
|
46
103
|
|
|
47
104
|
```sh
|
|
48
105
|
node runner.js --workspace <dir> --temp <dir> --mode <read-only|workspace-write> [--write-sid <S-1-4-…> --temp-write-sid <S-1-4-…>] -- <argv...>
|
|
49
106
|
```
|
|
50
107
|
|
|
51
|
-
The
|
|
108
|
+
The seam materializes the deterministic workspace SID's ACE standing (once per workspace per server lifetime — the reuse cache), then creates a random private temp directory and a distinct revocable SID for each live session/workspace pair, passing both as the required `--write-sid`/`--temp-write-sid` pair; the runner verifies each against its owning path and neither grants nor revokes (`manageDacls: false`). A fork receives a different temp capability, and a fresh provider gives even the same resumed session a new path and SID, so crash residue is inert litter. Without the pair, `--temp` names a root: an agentless workspace-write runner creates a random private child, self-manages its temp SID, rewrites TMP/TEMP, and removes the child on exit. Re-granting the standing workspace ACE after a restart is idempotent: `grantWrite` reads the current DACL and skips the re-propagation when the exact ACE already stands. A workspace equal to or containing the temp root is rejected before any grant.
|
|
52
109
|
|
|
53
|
-
|
|
110
|
+
### Verified boundaries
|
|
54
111
|
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
112
|
+
- **Everyone grants remain ambient write authority** — Everyone must stay in both restricting lists (removing it breaks early DLL initialization and CNG); an external NTFS object whose DACL grants Everyone a requested write right clears both checks and stays writable under both modes.
|
|
113
|
+
- **Hard links are file-object aliases, not path aliases** — an inheritable workspace ACE propagated onto an existing hard link changes the one underlying file security descriptor, so the same object is writable through an external alias; rejecting multiply-linked files is not viable for ordinary pnpm installations.
|
|
114
|
+
- **Writes are restricted; reads, network, and process visibility are not** — `WRITE_RESTRICTED` intersects write accesses only, so a confined child can read any caller-readable file and open sockets; `read-only` therefore needs a read-side policy to be expressed.
|
|
115
|
+
- **Console isolation is unavailable** — children created with `CREATE_NO_WINDOW` / `CREATE_NEW_CONSOLE` die during DLL initialization with `STATUS_DLL_INIT_FAILED` (`0xC0000142`); children share the host console, and pipe-based stdio redirection is unaffected.
|
|
116
|
+
- **ACL grants are standing directory mutations** — workspace ACEs stand by design (the reuse cache, never revoked); temp ACEs are revoked by `dispose()`; manual `icacls` cleanup cannot revoke them on this platform (`ERROR_NONE_MAPPED`, 1332), so revoke through this module.
|
|
117
|
+
- **Granted directories must be caller-owned** — the owner's implicit `WRITE_DAC` is what lets the sandbox edit the DACL without elevation.
|
|
118
|
+
- **The ambient temp root is never granted implicitly** — direct callers must supply an existing private `tempDir` plus its distinct `tempWriteSid`, or disable temp writes with `tempDir: null`; the actual temp directory must be disjoint from every writable root.
|
|
119
|
+
- **The confined child's temp capability is private per live session/workspace pair** — the runner rewrites TMP/TEMP to that private directory before the spawn; two tokens sharing the same workspace SID cannot write one another's temp directories.
|
|
120
|
+
- **`whoami` and token-inspection cmdlets fail under the restricted token** — `GetTokenInformation` on the duplicate is partially unavailable to the child, which is diagnostic noise rather than an operational failure.
|
|
58
121
|
|
|
59
|
-
|
|
122
|
+
### Header verification and source map
|
|
60
123
|
|
|
61
|
-
The
|
|
124
|
+
The sandbox-owned SID, ACL, token, file, and lock declarations are checked against Windows headers by [`verify/abi-probe.cpp`](verify/abi-probe.cpp). The shared process, stdio, and Job ABI is owned and verified by [`@deepseek-ai/dsh-win32-process`](../../subprocess/win32-process/README.md#header-verification).
|
|
62
125
|
|
|
63
|
-
|
|
126
|
+
| File | Role |
|
|
127
|
+
|---|---|
|
|
128
|
+
| [`src/index.ts`](src/index.ts) | `AclSandbox`: restricted-token policy, DACL grants, fail-closed spawn and dispose |
|
|
129
|
+
| [`src/runner.ts`](src/runner.ts) | The runner entry over shared Win32 process primitives |
|
|
130
|
+
| [`src/grant.ts`](src/grant.ts) | `AclWriteGrant`: server-side grant materialization and revocation |
|
|
131
|
+
| [`src/token.ts`](src/token.ts) + [`src/acl.ts`](src/acl.ts) | Win32 token and DACL primitives behind the sandbox |
|
|
64
132
|
|
|
65
|
-
|
|
133
|
+
</details>
|
|
66
134
|
|
|
67
|
-
|
|
68
|
-
g++ -std=c++20 -municode -O2 -o abi-probe.exe verify/abi-probe.cpp -ladvapi32 && ./abi-probe.exe
|
|
69
|
-
```
|
|
135
|
+
-----
|
|
70
136
|
|
|
71
|
-
|
|
137
|
+
<a id="further-exploration"></a>
|
|
138
|
+
## Further Exploration
|
|
72
139
|
|
|
73
|
-
|
|
140
|
+
Start with the subsystem reference for the shared vocabulary, then the provider that mounts this rung, its consumers, and the design decision.
|
|
74
141
|
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
- **The ambient temp root is never granted implicitly.** A direct `AclSandbox` workspace-write caller must supply an existing private `tempDir` plus its distinct `tempWriteSid`, or explicitly disable temp writes with `tempDir: null`. The actual temp directory must be disjoint from every writable root. The seam creates a random private directory; agentless runner calls treat `--temp` as the parent root and create their own random child, but reject a workspace equal to or containing that parent before any ACL mutation.
|
|
82
|
-
- **The confined child's temp capability is private per live session/workspace pair.** The runner rewrites TMP/TEMP via `SetEnvironmentVariableW` to that private directory before the spawn and the child inherits the rewritten block (bwrap `--tmpfs /tmp` semantics). The temp ACE and directory are removed on provider disposal, or after each agentless invocation. A crash can leave inert `%TEMP%` litter, but a resumed provider chooses a new random path and SID instead of colliding with or reauthorizing the residue. The native runner suite proves that two tokens sharing the same workspace SID cannot write one another's temp directories.
|
|
83
|
-
- **`whoami` and token-inspection cmdlets fail under the restricted token.** `GetTokenInformation` on the duplicate is partially unavailable to the child, so `whoami /all` reports errors — diagnostic noise of the restriction scheme, not an operational failure; the denial surfaces that matter (file writes) are unaffected.
|
|
142
|
+
- [Process sandbox subsystem](../../../docs/subsystems/sandbox.md) — modes, per-call policy, and enforcement semantics.
|
|
143
|
+
- [Local sandbox backends](../sandbox-local/README.md) — the provider that mounts this backend as the win32 rung.
|
|
144
|
+
- [Sandbox seam package](../sandbox/README.md) — the service contract this backend implements.
|
|
145
|
+
- [Win32 process library](../../subprocess/win32-process/README.md) — shared restricted-process, stdio, Job, wait, and handle-cleanup primitives.
|
|
146
|
+
- [Bash sandbox executor](../../shell/bash-sandbox/README.md) and [pwsh sandbox executor](../../shell/pwsh-sandbox/README.md) — the confined executors that consume it.
|
|
147
|
+
- [Windows ACL restricted-token sandbox decision](../../../.agents/notes/implemented/feature/2026-08-08-windows-acl-restricted-token-sandbox.md) — why raw ACL restricted tokens over mxc and AppContainer.
|
|
84
148
|
|
|
149
|
+
-----
|
|
150
|
+
|
|
151
|
+
<a id="model-experience"></a>
|
|
85
152
|
## Model Experience
|
|
86
153
|
|
|
87
|
-
Indirectly, through [`dsh-bash-sandbox`](../../shell/bash-sandbox/README.md), [`dsh-pwsh-sandbox`](../../shell/pwsh-sandbox/README.md), and their tools, which render this backend's partial-enforcement and denial facts (the confined stderr the tool layer classifies through `denialSignatures`) while the [`dsh-sandbox`](../sandbox/README.md) seam owns the `SANDBOX_UNAVAILABLE` text and runner selection.
|
|
154
|
+
Indirectly, through [`dsh-bash-sandbox`](../../shell/bash-sandbox/README.md), [`dsh-pwsh-sandbox`](../../shell/pwsh-sandbox/README.md), and their tools, which render this backend's partial-enforcement and denial facts (the confined stderr the tool layer classifies through `denialSignatures`) while the [`dsh-sandbox`](../sandbox/README.md) seam owns the `SANDBOX_UNAVAILABLE` text and `sandbox-local` owns runner selection.
|
|
88
155
|
|
|
89
156
|
#### KV Cache effect
|
|
90
157
|
|
|
@@ -92,12 +159,31 @@ None directly; the denial surface belongs to the tool layer.
|
|
|
92
159
|
|
|
93
160
|
## Known Limitations and Deferred Work
|
|
94
161
|
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
- **
|
|
162
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
These limits define when the backend is a poor fit or needs special operational care. They are current package constraints, not a general Windows comparison or a task backlog.
|
|
166
|
+
|
|
167
|
+
- **One write allowlist per workspace** — the write SID is the unit of the allowlist and IS the workspace identity; reusing one sandbox instance across two workspaces widens both grants to both roots. Create one instance per workspace root — the seam does exactly this, keyed by the workspace path.
|
|
168
|
+
- **Cleanup is best-effort by design** — `dispose()` attempts every temp revocation and aggregates failures into an `AggregateError`; a cleanup failure can leave the random directory and its temp-SID-only ACE behind. Once the process exits no future token carries that SID, so the residue is inert until OS temp hygiene or manual removal reclaims it.
|
|
169
|
+
- **Standing workspace ACEs are invisible residue** — renaming a workspace derives a new SID; the old ACEs on the old path stay (inert, write-SID-only), and a future cleanup command may reap them.
|
|
170
|
+
- **NULL-DACL directories are not identity-preserving under grant+revoke** — a directory with a NULL DACL means "everyone full control"; `grantWrite` builds the new ACL from that null, and the revoke round-trip leaves an empty (deny-all) DACL rather than the original NULL DACL. Real workspace and temp directories carry real DACLs, so this stays a documented edge.
|
|
171
|
+
- **Piped stdio capture is impossible for confined grandchildren** — libuv's pipe stdio uses named pipes, whose client-end open requests write access no restricting SID is granted (the Win32 layer's default SD template, not the token default DACL), so `spawn(..., { stdio: 'pipe' })` inside a confined process fails with EPERM; inherited and ignored stdio spawns work, and anonymous pipes (PowerShell pipelines) work because the restricted token's default DACL carries a full-access restricting-SID ACE.
|
|
172
|
+
- **Grant materialization is an eager full-tree propagation** — `SetNamedSecurityInfoW` on a directory with inheritable ACEs walks every descendant immediately (tens of seconds on large workspace trees); the per-workspace identity pays it once per workspace per machine, and the exact-ACE skip makes every later provision cheap.
|
|
101
173
|
- **Read-side confinement and network policy are out of scope** — `WRITE_RESTRICTED` intersects write accesses only; pair this backend with a read-side policy for stronger confinement.
|
|
102
|
-
- **Wide-directory and FAT-volume warnings are deferred; FAT-class targets stay writable
|
|
103
|
-
- **PowerShell language mode differs by confined mode
|
|
174
|
+
- **Wide-directory and FAT-volume warnings are deferred; FAT-class targets stay writable** — the UI-side warnings are not implemented, a FAT volume as a grant root fails loudly, and a FAT-class target outside the granted roots has no security descriptors so it stays writable under both confined modes; FAT is treated as legacy residue.
|
|
175
|
+
- **PowerShell language mode differs by confined mode** — under `read-only`, PowerShell cannot create its AppLocker probe files in temp and conservatively starts in ConstrainedLanguage (`Add-Type`, non-core .NET static calls, COM, and reflection fail); the shipped `workspace-write` path lets the probe complete, so pwsh stays in FullLanguage unless host-wide WDAC/AppLocker policy says otherwise, while a direct `AclSandbox` with `tempDir: null` has no such guarantee. This split is PowerShell startup behavior, not part of the ACL write boundary.
|
|
176
|
+
|
|
177
|
+
<a id="dev-note"></a>
|
|
178
|
+
### Dev Note
|
|
179
|
+
|
|
180
|
+
<details>
|
|
181
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
182
|
+
|
|
183
|
+
This Dev Note is working context for maintainers: undecided directions and open questions. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes.
|
|
184
|
+
|
|
185
|
+
#### Future: warnings and cleanup surfaces
|
|
186
|
+
|
|
187
|
+
The warn-only posture for unusually wide directories and FAT-class volumes is documented in the limitations above but not implemented, and a cleanup command that reaps standing workspace ACEs from renamed workspaces is undecided. Both are open directions, not shipped behavior.
|
|
188
|
+
|
|
189
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "面向 Windows 上选择、配置或排查受限令牌进程隔离的用户与维护者的 Windows 写入限制沙箱后端。"
|
|
3
|
+
kind: "package-library"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-sandbox-windows-acl
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-sandbox-windows-acl` 通过写入限制隔离 Windows 进程:子进程在受限令牌下运行,其写访问仅限于工作区与私有临时目录,因此 `workspace-write` 允许这些写入,`read-only` 则不允许任何写入。它作为 `dsh-sandbox-local` 的 win32 档交付:在 Windows 上挂载本地提供方,就能让每次受限 bash 或 pwsh 调用自动使用此后端。也可以通过 `AclSandbox` API 直接嵌入,以捕获 stdio 的方式 spawn 受限子进程。每个 Win32 调用都有检查,失败即抛出异常,因此子进程绝不会不受限制地 spawn。强制执行按设计为部分实现——受限令牌必须为进程初始化保留 Everyone,且 NTFS 硬链接可以把同一文件对象别名为多个路径——因此后端报告 `partial`,需要绝对边界的调用方可以向上暴露它。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
6
24
|
|
|
7
|
-
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
8
27
|
|
|
9
|
-
|
|
28
|
+
在 Windows 上,挂载本地沙箱提供方后,此后端就是 `ctx.sandbox` 背后的 runner——无需额外配置。要在 harness 之外 spawn 受限子进程时,直接嵌入 `AclSandbox` API。
|
|
10
29
|
|
|
11
|
-
|
|
30
|
+
### 何时选择
|
|
31
|
+
|
|
32
|
+
为在 `read-only` 或 `workspace-write` 下隔离子进程文件操作的 Windows 组合选择它。当子进程还需要读侧隔离或网络限制时请另选机制:`WRITE_RESTRICTED` 只交叉检查写访问,因此请把此后端与读侧策略或 AppContainer 能力令牌配对以获得更强隔离。
|
|
33
|
+
|
|
34
|
+
### 直接 API
|
|
35
|
+
|
|
36
|
+
`AclSandbox` 以捕获 stdio 的方式 spawn 受限子进程(runner 风格使用可用继承 stdio)。它要求显式提供私有临时目录,或用 `tempDir: null` 禁用临时写入——环境临时根目录绝不会被隐式授权。
|
|
12
37
|
|
|
13
38
|
```ts
|
|
14
39
|
import { mkdtempSync, rmSync } from 'node:fs'
|
|
@@ -38,68 +63,127 @@ sandbox.dispose() // revokes the revocable (temp) grant, keeps the standing work
|
|
|
38
63
|
rmSync(tempDir, { recursive: true, force: true })
|
|
39
64
|
```
|
|
40
65
|
|
|
41
|
-
|
|
66
|
+
工作区 ACE 以常驻方式授予——`dispose()` 保留它们,因为它们是跨实例的复用缓存——而不同的临时 SID 以可回收方式授予。服务端对应物是 `AclWriteGrant` 类:每个目录一次 `add(path, standing)`,`dispose()` 撤销可回收路径并释放 SID。
|
|
67
|
+
|
|
68
|
+
### 隔离给你带来什么
|
|
69
|
+
|
|
70
|
+
在 `workspace-write` 下,子进程可以写入工作区及其私有临时目录;受 ACL 管辖的其他写入都会被拒绝,已记录的 Everyone 与硬链接边界除外。在 `read-only` 下不存在显式写入授权,因此写入会被拒绝,同样带有已记录的边界。
|
|
71
|
+
|
|
72
|
+
临时隔离按每个活跃的会话/工作区对进行:共享工作区的会话共享其写权限,但无法写入彼此的临时目录。新的提供方总会选择新的临时路径和 SID,因此崩溃残留既无法阻止恢复的会话,也无法向其授权。
|
|
73
|
+
|
|
74
|
+
### 失败与恢复
|
|
75
|
+
|
|
76
|
+
`init()` 在任何 Win32 失败时抛出——子进程绝不会不受限制地 spawn。执行命令前失败的 runner 会向 stderr 打印 `windows-acl-run: <detail>` 并以 127 退出,seam 的 runner 失败规则将其归类为损坏的沙箱,而非拒绝。清理按设计尽力而为:`dispose()` 会尝试全部临时撤销并把失败聚合为 `AggregateError`。
|
|
77
|
+
|
|
78
|
+
-----
|
|
79
|
+
|
|
80
|
+
<a id="understand-the-implementation"></a>
|
|
81
|
+
## 理解实现
|
|
82
|
+
|
|
83
|
+
<details>
|
|
84
|
+
<summary>实现细节——点击展开</summary>
|
|
85
|
+
|
|
86
|
+
本节解释受限令牌机制、令牌列表、runner 约定与已验证边界;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
87
|
+
|
|
88
|
+
### 机制
|
|
42
89
|
|
|
43
|
-
|
|
90
|
+
调用者令牌被复制为 `WRITE_RESTRICTED` 受限令牌,其 restricting SIDs 携带彼此独立的工作区与私有临时目录能力。Windows 执行两次访问检查——先对正常 SID,再对 restricting SID——并且只在两次检查都通过时才授予写类访问。工作区 SID 由规范工作区路径确定性派生(`workspaceWriteSid`),因此工作区根目录 ACE 每台机器每个工作区只物化一次,之后每次会话、调用或重启都命中精确 ACE 跳过。每个活跃的会话/工作区对则获得一个随机私有临时目录,以及一个从该路径派生的 SID(`tempWriteSid`),因此各会话共享预期的工作区权限,却不会继承彼此的临时目录权限。每个策略专用 Win32 调用和 [`dsh-win32-process`](../../subprocess/win32-process/README.zh.md) 提供的进程原语都有检查;失败抛出携带 API 名、精确错误码、系统文本与失败上下文的 `Win32Error`——从构造上 fail-closed。
|
|
44
91
|
|
|
45
|
-
|
|
92
|
+
### 模式与令牌列表
|
|
46
93
|
|
|
47
|
-
|
|
94
|
+
`workspace-write`(登录 SID、Everyone、工作区 SID、临时 SID)为工作区与会话的私有临时子目录分别授予 Write;`read-only`(登录 SID、Everyone——不含写入 SID)不授予任何内容。保活组(登录 SID + Everyone)在两种模式下都存在:没有它,早期 DLL 初始化会以 `0xC0000142` 死亡、CNG 会让 pwsh 以 `0xE0434352` 崩溃。写入 SID 有意留在 read-only 列表之外:先前 workspace-write 时期留下的常驻授权 ACE 仍然失效,因为 write-restricted 的 pass-2 检查只授予 restricting 列表所携带的内容,而常驻 ACE 让重新升级免于重新传播。
|
|
95
|
+
|
|
96
|
+
NUL 写入是环境性的、不是被授权的:设备 DACL 授予 Everyone 读+写+执行(`0x1201BF`),因此访问掩码落在其内的打开者(cmd 的 `> NUL`、node 的 `\\.\NUL`)在两种模式下都能写。`Set-Content NUL` 在两种模式下都失败(PowerShell/.NET 层效应,非设备 DACL 所致),而 PowerShell 的 `> $null` 重定向不受影响。
|
|
97
|
+
|
|
98
|
+
Authenticated Users 在两种列表中都不存在——WMI 命名空间安全检查失败(`0x80041003`),因此 CIM cmdlet 与 `Get-ComputerInfo` 在所有受限模式下都不可用,且 C:\-root 树创建逃逸被关闭。INTERACTIVE/LOCAL 同样不存在:宿主的 Public 树向 INTERACTIVE 授予写权限,因此 Public 写入被拒绝。
|
|
99
|
+
|
|
100
|
+
### 隔离 runner
|
|
101
|
+
|
|
102
|
+
面向 seam 的形态是 runner 入口(`./runner`):`dsh-sandbox-local` 在调用者命令的位置 spawn 的 argv 前缀包装——与 bwrap/landlock-run/sandbox-exec 同一架构。runner 创建受限令牌,在它之下 spawn 包装后的 argv,调用者的 stdio 直接透传,把子进程包进 `KILL_ON_JOB_CLOSE` job,镜像子进程的退出码,并在退出时撤销其自行管理的临时授权。每个 runner 侧失败都会向 stderr 打印 `windows-acl-run: <detail>` 并以 127 退出——seam 的 runner 失败规则匹配该签名。
|
|
48
103
|
|
|
49
104
|
```sh
|
|
50
105
|
node runner.js --workspace <dir> --temp <dir> --mode <read-only|workspace-write> [--write-sid <S-1-4-…> --temp-write-sid <S-1-4-…>] -- <argv...>
|
|
51
106
|
```
|
|
52
107
|
|
|
53
|
-
|
|
108
|
+
seam 先把确定性工作区 SID 的 ACE 常驻物化(每个工作区每服务器生命周期一次——复用缓存),再为每个活跃的会话/工作区对创建随机私有临时目录和不同的可回收 SID,把两种身份作为必须成对出现的 `--write-sid`/`--temp-write-sid` 传入;runner 对照各自所属路径验证二者,既不授权也不撤销(`manageDacls: false`)。fork 获得不同的临时能力;即使恢复的是同一会话,新的提供方也会给出新的路径和 SID,因此崩溃残留只是失效垃圾。如果不带这一对标志,`--temp` 指定的是根目录:无 agent(智能体)/独立的 workspace-write runner 会创建随机私有子目录,自行管理其临时 SID,重写 TMP/TEMP,并在退出时移除该子目录。重启后重新授权常驻工作区 ACE 是幂等的:`grantWrite` 读取当前 DACL,当完全相同的 ACE 已存在时跳过重新传播。工作区若等于或包含临时根目录,会在任何授权前被拒绝。
|
|
54
109
|
|
|
55
|
-
|
|
110
|
+
### 已验证边界
|
|
56
111
|
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
112
|
+
- **Everyone 授权仍是环境中的写权限来源。** Everyone 必须保留在两种 restricting 列表中(移除它会破坏早期 DLL 初始化与 CNG);外部 NTFS 对象若其 DACL 向 Everyone 授予所请求的写权限,就会同时通过两次检查,并在两种模式下保持可写。
|
|
113
|
+
- **硬链接是文件对象别名,而非路径别名。** 传播到已有硬链接上的可继承工作区 ACE 会修改底层同一文件的安全描述符,因此同一对象也可通过外部别名写入;拒绝工作区中的所有多链接文件不具可行性,因为普通 pnpm 安装会使用硬链接。
|
|
114
|
+
- **写入受限;读取、网络与进程可见性不受限。** `WRITE_RESTRICTED` 只交叉检查写访问,因此受限子进程可以读取调用者可读的任何文件并打开套接字;`read-only` 因而需要读侧策略才能表达。
|
|
115
|
+
- **控制台隔离不可用。** 以 `CREATE_NO_WINDOW` / `CREATE_NEW_CONSOLE` 创建的子进程在 DLL 初始化期间以 `STATUS_DLL_INIT_FAILED`(`0xC0000142`)死亡;子进程共享宿主控制台,基于管道的 stdio 重定向不受影响。
|
|
116
|
+
- **ACL 授权是对真实目录的驻留改动。** 工作区 ACE 按设计常驻(复用缓存,绝不撤销);临时 ACE 由 `dispose()` 撤销;手工 `icacls` 清理无法在本平台回收它们(`ERROR_NONE_MAPPED` 1332),请通过本模块回收。
|
|
117
|
+
- **被授权目录必须由调用者拥有。** 所有者的隐式 `WRITE_DAC` 是沙箱无需提权即可编辑 DACL 的原因。
|
|
118
|
+
- **环境临时根目录绝不会被隐式授权。** 直接调用方必须提供已存在的私有 `tempDir` 及其不同的 `tempWriteSid`,或用 `tempDir: null` 禁用临时写入;实际临时目录不得与任何可写根目录重叠。
|
|
119
|
+
- **受限子进程的临时能力按每个活跃的会话/工作区对私有。** runner 在 spawn 之前把 TMP/TEMP 改写为该私有目录;共享同一工作区 SID 的两个令牌无法写入彼此的临时目录。
|
|
120
|
+
- **受限令牌下 `whoami` 与令牌检查 cmdlet 会失败。** 子进程对复制令牌的 `GetTokenInformation` 部分不可用,这是诊断噪音而非运行故障。
|
|
60
121
|
|
|
61
|
-
|
|
122
|
+
### 头部验证与源码地图
|
|
62
123
|
|
|
63
|
-
|
|
124
|
+
沙箱拥有的 SID、ACL、令牌、文件与锁声明由 [`verify/abi-probe.cpp`](verify/abi-probe.cpp) 对照 Windows 头文件检查。共享进程、stdio 与 Job ABI 由 [`@deepseek-ai/dsh-win32-process`](../../subprocess/win32-process/README.zh.md#header-verification) 归属并验证。
|
|
64
125
|
|
|
65
|
-
|
|
126
|
+
| 文件 | 职责 |
|
|
127
|
+
|---|---|
|
|
128
|
+
| [`src/index.ts`](src/index.ts) | `AclSandbox`:受限令牌策略、DACL 授权、故障关闭的 spawn 与 dispose |
|
|
129
|
+
| [`src/runner.ts`](src/runner.ts) | 基于共享 Win32 进程原语的 runner 入口 |
|
|
130
|
+
| [`src/grant.ts`](src/grant.ts) | `AclWriteGrant`:服务端授权物化与撤销 |
|
|
131
|
+
| [`src/token.ts`](src/token.ts) + [`src/acl.ts`](src/acl.ts) | 沙箱背后的 Win32 令牌与 DACL 原语 |
|
|
66
132
|
|
|
67
|
-
|
|
133
|
+
</details>
|
|
68
134
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
135
|
+
-----
|
|
136
|
+
|
|
137
|
+
<a id="further-exploration"></a>
|
|
138
|
+
## 进一步探索
|
|
72
139
|
|
|
73
|
-
|
|
140
|
+
先从子系统参考文档了解共享词汇,再看挂载此档的提供方、其消费方与设计决策。
|
|
74
141
|
|
|
75
|
-
|
|
142
|
+
- [进程沙箱子系统](../../../docs/subsystems/sandbox.zh.md)——模式、逐调用策略与强制执行语义。
|
|
143
|
+
- [本地沙箱后端](../sandbox-local/README.zh.md)——把此后端挂载为 win32 档的提供方。
|
|
144
|
+
- [沙箱 seam 包](../sandbox/README.zh.md)——此后端实现的服务约定。
|
|
145
|
+
- [Win32 进程库](../../subprocess/win32-process/README.zh.md)——共享的受限进程、stdio、Job、等待与句柄清理原语。
|
|
146
|
+
- [Bash 沙箱执行器](../../shell/bash-sandbox/README.zh.md)与[pwsh 沙箱执行器](../../shell/pwsh-sandbox/README.zh.md)——消费它的受限执行器。
|
|
147
|
+
- [Windows ACL 受限令牌沙箱决策](../../../.agents/notes/implemented/feature/2026-08-08-windows-acl-restricted-token-sandbox.zh.md)——为何选择原始 ACL 受限令牌而非 mxc 与 AppContainer。
|
|
76
148
|
|
|
77
|
-
|
|
78
|
-
- **硬链接是文件对象别名,而非路径别名。** 传播到已有 NTFS 硬链接上的可继承工作区 ACE 会修改底层同一文件的安全描述符,因此同一对象也可通过外部别名写入。拒绝工作区中的所有多链接文件不具可行性,因为普通 pnpm 安装会使用硬链接指向其内容寻址存储;原生 runner 套件钉住该缺口,提供方的部分强制执行报告则点明其后果。
|
|
79
|
-
- **写入受限;读取、网络与进程可见性不受限。** `WRITE_RESTRICTED` 只交叉检查写访问,因此受限子进程可以读取调用者可读的任何文件并打开套接字。`read-only` 模式因而不能仅靠该机制表达;将其与读侧策略或 AppContainer/`S-1-15-2` capability 令牌配对以获得更强隔离。
|
|
80
|
-
- **控制台隔离不可用。** 在受限令牌下,以 `CREATE_NO_WINDOW` / `CREATE_NEW_CONSOLE` 创建的子进程在 DLL 初始化期间以 `STATUS_DLL_INIT_FAILED`(`0xC0000142`)死亡。POC 尝试把控制台登录 SID(`S-1-2-1`)加入 restricting 列表来修复;在 Windows 11 26200 上 `CreateWellKnownSid(WinLocalLogonSid)` 以 `ERROR_INVALID_PARAMETER`(87)失败,正确的 `WinConsoleLogonSid` 能产出合法 `S-1-2-1` 但子进程仍然死亡,POC 的最终修订同时移除了该 SID 与控制台隔离。子进程因此共享宿主控制台;stdio 重定向走管道,不受影响。
|
|
81
|
-
- **ACL 授权是对真实目录的驻留改动。** 进程中途死亡会留下授权;工作区 ACE **按设计**常驻(绝不撤销——复用缓存),临时 ACE 由 `dispose()` 撤销(后续步骤失败时 `init()` 也会撤销已应用的临时授权)。POC 注释里的手工清理命令(`icacls <dir> /remove '*S-1-4-…'`)在本平台实测失败(`ERROR_NONE_MAPPED` 1332)——请通过本模块回收。工作区 ACE 在异常关闭后无需自愈:派生 SID 在下一次供给时重新命中常驻 ACE(跳过应用);写入 SID ACE 不会因每次重启而累积第二个身份,因为身份**就是**工作区。
|
|
82
|
-
- **被授权目录必须由调用者拥有。** 所有者的隐式 `WRITE_DAC` 是沙盒无需提权即可编辑 DACL 的原因。
|
|
83
|
-
- **环境临时根目录绝不会被隐式授权。** 直接使用 `AclSandbox` 的 workspace-write 调用方必须提供一个已存在的私有 `tempDir` 及其不同的 `tempWriteSid`,或通过 `tempDir: null` 显式禁用临时写入。实际临时目录不得与任何可写根目录重叠。seam 会创建随机私有目录;无 agent runner 调用把 `--temp` 视为父根目录并自行创建随机子目录,但如果工作区等于或包含该父根目录,就会在任何 ACL 改动前拒绝调用。
|
|
84
|
-
- **受限子进程的临时能力按每个活跃的会话/工作区对私有。** runner 在 spawn 之前用 `SetEnvironmentVariableW` 把 TMP/TEMP 改写为该私有目录,子进程继承改写后的环境块(bwrap `--tmpfs /tmp` 的语义)。临时 ACE 与目录会在提供方 dispose 时移除,或在每次无 agent 调用后移除。崩溃可能留下失效的 `%TEMP%` 垃圾,但恢复后的提供方会选择新的随机路径和 SID,而不会与残留发生冲突或重新向其授权。原生 runner 套件证明,共享同一工作区 SID 的两个令牌无法写入彼此的临时目录。
|
|
85
|
-
- **受限令牌下 `whoami` 与令牌检查 cmdlet 会失败。** 子进程对复制令牌的 `GetTokenInformation` 部分不可用,因此 `whoami /all` 报错——这是限制方案的诊断噪音,不是运行故障;真正重要的拒绝面(文件写入)不受影响。
|
|
149
|
+
-----
|
|
86
150
|
|
|
151
|
+
<a id="model-experience"></a>
|
|
87
152
|
## 模型体验
|
|
88
153
|
|
|
89
|
-
间接地通过 [`dsh-bash-sandbox`](../../shell/bash-sandbox/README.zh.md)、[`dsh-pwsh-sandbox`](../../shell/pwsh-sandbox/README.zh.md)
|
|
154
|
+
间接地通过 [`dsh-bash-sandbox`](../../shell/bash-sandbox/README.zh.md)、[`dsh-pwsh-sandbox`](../../shell/pwsh-sandbox/README.zh.md) 及其工具呈现;它们渲染此后端的部分强制执行与拒绝事实(工具层通过 `denialSignatures` 分类的受限 stderr),而 [`dsh-sandbox`](../sandbox/README.zh.md) seam 拥有 `SANDBOX_UNAVAILABLE` 文本、`sandbox-local` 拥有 runner 选择。
|
|
90
155
|
|
|
91
156
|
#### KV Cache 影响
|
|
92
157
|
|
|
93
158
|
无直接影响;拒绝面属于工具层。
|
|
94
159
|
|
|
95
|
-
##
|
|
96
|
-
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
- **
|
|
160
|
+
## 已知限制与延期工作
|
|
161
|
+
|
|
162
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
这些限制说明后端何时不合适,或何时需要特别运维。它们是当前包约束,不是通用 Windows 对比或任务积压。
|
|
166
|
+
|
|
167
|
+
- **每个工作区一个写入白名单**——写入 SID 是白名单的基本单位,且就是工作区身份;同一沙盒实例跨两个工作区复用时,两个根目录会互相扩大授权面。请按工作区根目录各建一个实例——seam 正是这样做的,以工作区路径为键。
|
|
168
|
+
- **清理按设计尽力而为**——`dispose()` 会尝试全部临时撤销并把失败聚合为 `AggregateError`;清理失败可能留下随机目录及其仅含临时 SID 的 ACE。进程退出后,不会再有令牌携带该 SID,因此残留保持失效,直到 OS 临时目录卫生或手动移除目录将其回收。
|
|
169
|
+
- **常驻工作区 ACE 是不可见残留。** 工作区改名会派生新的 SID;旧路径上的旧 ACE 留在原地(失效、仅含写入 SID),未来的清理命令可以回收它们。
|
|
170
|
+
- **NULL-DACL 目录在 grant+revoke 往返下不保持身份。** 带 NULL DACL 的目录意味着「所有人完全控制」;`grantWrite` 从该 null 构建新 ACL,撤销往返后留下的是 EMPTY(全部拒绝)DACL 而非原始 NULL DACL。真实工作区与临时目录都带真实 DACL,因此这仍是记录在案的边界情形。
|
|
171
|
+
- **受限孙进程的管道 stdio 捕获不可用。** libuv 的管道 stdio 用的是 NAMED pipe,其 client 端打开所请求的写访问没有任何 restricting SID 被授予(是 Win32 层的默认 SD 模板,而非令牌默认 DACL),因此受限进程内 `spawn(..., { stdio: 'pipe' })` 以 EPERM 失败;继承与忽略 stdio 的 spawn 可用,匿名管道(PowerShell 的管道)因受限令牌默认 DACL 携带 restricting SID 全权 ACE 而可用。
|
|
172
|
+
- **授权物化是急切的全树传播。** 在带可继承 ACE 的目录上调用 `SetNamedSecurityInfoW` 会立即遍历每个后代(大型工作区树上以数十秒计);按工作区身份每台机器每个工作区只付一次,精确 ACE 跳过让后续每次供给都很便宜。
|
|
173
|
+
- **读侧隔离与网络策略不在范围内**——`WRITE_RESTRICTED` 只交叉检查写访问;将此后端与读侧策略配对以获得更强隔离。
|
|
174
|
+
- **宽目录与 FAT 卷警告已推迟;FAT 类目标保持可写。** UI 侧警告尚未实现,FAT 卷作为授权根会大声失败,而授权根之外的 FAT 类目标没有安全描述符,因此在两种受限模式下都可写;FAT 被视为遗留残留。
|
|
175
|
+
- **PowerShell 语言模式因受限模式而异。** 在 `read-only` 下,PowerShell 无法在临时目录中创建 AppLocker 探针文件,因此会保守地以 ConstrainedLanguage 启动(`Add-Type`、非核心 .NET 静态调用、COM 与反射失败);交付的 `workspace-write` 路径可让探针完成,因此除非主机范围的 WDAC/AppLocker 策略另有规定,否则 pwsh 保持 FullLanguage,而直接使用 `AclSandbox` 并配置 `tempDir: null` 时则没有这一保证。这一区别属于 PowerShell 启动行为,不是 ACL 写入边界的一部分。
|
|
176
|
+
|
|
177
|
+
<a id="dev-note"></a>
|
|
178
|
+
### 开发备注
|
|
179
|
+
|
|
180
|
+
<details>
|
|
181
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
182
|
+
|
|
183
|
+
本开发备注是维护者的工作上下文:未决方向与开放问题。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码和相关 Agent Note 为准。
|
|
184
|
+
|
|
185
|
+
#### 未来:警告与清理表面
|
|
186
|
+
|
|
187
|
+
对异常宽的目录与 FAT 类卷的仅警告立场已记录在上方限制中但尚未实现,回收改名工作区常驻 ACE 的清理命令也尚未决定。两者都是开放方向,不是已交付行为。
|
|
188
|
+
|
|
189
|
+
</details>
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { AclSandbox, AclWriteGrant,
|
|
1
|
+
import { a as assertTempRootOutsideWorkspace, i as AclWriteGrant, n as tempWriteSid, r as workspaceWriteSid, t as AclSandbox } from "./types-DuU3lSVe.js";
|
|
2
|
+
export { AclSandbox, AclWriteGrant, assertTempRootOutsideWorkspace, tempWriteSid, workspaceWriteSid };
|
package/lib/runner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as tempWriteSid, o as
|
|
1
|
+
import { a as assertTempRootOutsideWorkspace, n as tempWriteSid, o as win32, r as workspaceWriteSid, t as AclSandbox } from "./types-DuU3lSVe.js";
|
|
2
2
|
import { existsSync, mkdtempSync, rmSync, statSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
//#region lib/types/runner.js
|