Skip to content
dsh.fish
Bundle

dsh-grantwrite-patch

Windows sandbox ACL-repair plugin for DeepSeek Harness (dsh) — fix the grantWrite failure (SetNamedSecurityInfoW Win32 5) and retry the command invisibly

Source
0-v-0
License
MIT
Updated
Updated 7 days ago

Readme

# dsh-grantwrite-patch

English | [中文](README.zh.md)

Windows sandbox ACL-repair plugin for DeepSeek Harness (dsh) — fix the grantWrite failure (SetNamedSecurityInfoW Win32 5) and retry the command invisibly.

## What it solves

On Windows, the `workspace-write` sandbox writes a write grant into the workspace root DACL before every command (`AclWriteGrant.add` → `grantWrite`). When any directory on the chain lacks an effective `WRITE_DAC`/`WRITE_OWNER`, `SetNamedSecurityInfoW` fails with Win32 5 (ERROR_ACCESS_DENIED), and **every** confined command on that workspace fails before spawn:

```
Error: SetNamedSecurityInfoW failed (Win32 5): grantWrite
```

This plugin intercepts that error, repairs permissions automatically, and retries the command so you keep working without noticing.

## How it works

The plugin picks a strategy from the workspace size:

- **Normal workspaces**: intercept the error in memory and retry with zero side effects — no file permissions are touched.
- **Large workspaces**: when the object count exceeds the threshold (20,000 by default), proactively apply a full grant to the workspace once, so the write grant is not re-paid on every DSH restart and large trees stop costing you waiting time.
- **Fallback**: when the above cannot apply, run the diagnose script once to repair permissions and retry; when the script is unavailable, fall back to an `icacls` grant.

The three paths never conflict; they stack on demand.

## Requirements

| Item | Requirement |
|---|---|
| OS | Windows (NTFS). The plugin disables itself on other platforms |
| DSH version | `>= 0.1.7-rc.1`. The full diagnose-script repair needs `>= 0.2.0-rc.2` |
| Sandbox policy | Only `workspace-write` needs this (`read-only` / `danger-full-access` do not) |

## Install

### From npm

```sh
dsh plugin --profile web add dsh-grantwrite-patch
```

### From source (GitHub)

```sh
git clone https://github.com/0-v-0/dsh-grantwrite-patch
cd dsh-grantwrite-patch
pnpm install && pnpm build
dsh plugin --profile web add .
```

Notes:

- `dsh plugin` behaves like adding a dependency to your profile. A bundle plugin is loaded once its full package name appears in the profile's `dsh.profile.bundles` list (added automatically); the bundle patch (`cordis.patch.yml`) applies on the next boot.
- The source install must run `pnpm build` first: `lib/` is not committed, and the FFI preload is resolved from the installed package (`lib/preload.cjs`).
- To update, run the same command again (`add dsh-grantwrite-patch@latest`).
- Alternatively, merge the `- insert:` block of `cordis.patch.yml` into the host's `cordis.yml` by hand.

## Configuration

```yaml
- insert:
    id: grantwrite-patch
    name: dsh-grantwrite-patch
    config:
      enable: true
      mode: auto            # auto | patch | repair
      objectThreshold: 20000    # object count at/above which a workspace counts as large -> persistent repair
      probeTimeoutMs: 10000     # size-probe timeout; a timed-out probe counts as large
      probeCooldownMs: 3600000  # per-workspace probe/decision cache duration
      repairTimeoutMs: 120000
      maxRetries: 1
      cooldownMs: 60000
      appendDiagnostics: true
      grantFallback: true      # fall back to a bare icacls grant when the diagnose script is unavailable (0.1.7) or fails; drive roots refused
      # scriptPath: <absolute path>   # default: resolved from @deepseek-ai/dsh-sandbox-windows-acl package assets (DSH >= 0.2.x)
      # outDir: <absolute path>       # default: $DSH_HOME/grantwrite-patch (~/.dsh/grantwrite-patch without DSH_HOME)
```

## Security model

- **patch path**: touches no system ACL and leaves no persistent side effect; the `WRITE_RESTRICTED` restricted token, capability-SID Allow ACEs, and the world-SID `FILE_DELETE_CHILD` Deny are preserved. The only thing lost is the Low-integrity label layer of defence in depth (children run at Medium), and only on hosts that could not apply the label in the first place.
- **repair path**: the repair scope is bounded by the script's `-AllowRoot`: only the target directory or objects strictly inside it are modified; reparse points and managed-application trees are refused; deny ACEs are never deleted; every change is backed up first and re-read for verification, and a failed batch is rolled back.
- **grant fallback** (`grantFallback`): when the script is unavailable or fails, run a bare `icacls <root> /grant "<user>:(OI)(CI)F"`. **Drive roots are hard-refused** (`isDriveRoot`: `D:\` and other drive roots are refused; only real workspaces are granted); inheritance policy is untouched and deny ACEs are not removed; roll back with `icacls <root> /remove:g "<user>"`. The script is preferred (safer: `-AllowRoot` bound + backup + verify); the fallback only covers the gap.
- The plugin process is the host process (a non-restricted token); modifying permissions is exactly what the sandbox's restricted token cannot do. The plugin never elevates its token.
- Only `SetNamedSecurityInfoW` failures in a `grantWrite` context are matched; other Win32 failures, command exit codes, EPERM, and so on all pass through untouched.
- The retry happens once (`maxRetries` is tunable from 0 to 3); a failed repair is not retried, which prevents loops.

## Credits & License

The FFI patch core is adapted from [masknull/dsh-acl-sandbox-patch](https://github.com/masknull/dsh-acl-sandbox-patch).

[MIT](LICENSE)

Install

dsh plugin --profile web add github:0-v-0/dsh-grantwrite-patch

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source