drscrewdriver/dsh-perm-gate ↗★ 1
dsh-perm-gate
提供确定性优先、默认拒绝的DSH权限控制网关 适合需要对AI执行的命令和敏感操作进行严格安全审计与授权拦截的场景。
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:drscrewdriver/dsh-perm-gate说明文档
阅读完整 README ↗Configuration
Add the plugin to cordis.yml:
- id: dsh-perm-gate
name: dsh-perm-gate
config:
rulesFile: ./permissions.yaml # optional; defaults to $DSH_HOME/perm-gate/rules.yml
dshHome: $DSH_HOME # root pinned for protected-target checks
defaultAction: ask # allow | ask | deny
gatePresets: [permissive, permissive-full] # tiers where the gate is active (default)
sessionSweep: true # hourly cleanup of archived/dead sessions' gate data
Session sweep
On startup and every hour the gate reads DSH's workspace store
($DSH_HOME/storages/workspace.json, read-only) and classifies every session it
holds authorization-chain data for. Sessions DSH has archived (global.archivedSessionIds)
or no longer tracks at all have their decision events dropped from
$DSH_HOME/perm-gate/events.jsonl and their pre-change snapshot files deleted
from $DSH_HOME/perm-gate/snapshots/ — history the review page can no longer
reach, for data the harness itself considers gone. Live sessions are untouched,
unattributable rows (empty session id) are never deleted, and any failure is
fail-open: the round is skipped and retried an hour later. Set sessionSweep: false
to disable; workspaceStoreFile overrides the store path. Restoring an archived
session does not restore its swept history.
Rules file
permissions:
defaultAction: ask
deny:
- command: [rm#recursive]
reason: no recursive rm
- command: [ssh]
reason: no direct ssh
- paths: [.dsh/**]
reason: protect harness metadata
allow:
- command: [pnpm, node]
reason: dev tools
- command: [curl, wget]
args: ["https://*.example.com/*"]
reason: allowed endpoint
ask:
- command: [bash, sh]
reason: ask shells
A command entry word#flag matches the command word (word) with the modifier recursive or
force — so rm#recursive matches rm -rf, env rm -rf, and sh -c "rm -rf /".
Network policy (opt-in)
A local HTTP/CONNECT proxy that adjudicates the outbound traffic of shell subprocesses against the same rules file, plus an approval path for targets no rule covers. Off by default — enabling it binds a loopback port and rewrites the proxy environment for child processes, so it is never turned on implicitly.
- id: dsh-perm-gate
config:
networkEnabled: false # master switch (default false)
networkMode: whitelist # deny-all | whitelist | allow-all
networkUnlisted: ask # ask | deny — unlisted target handling
networkUnattributed: allow # allow | deny — traffic with no shell attribution
networkInjectEnv: true # rewrite HTTP(S)_PROXY/ALL_PROXY for children
networkAskTimeoutMs: 120000 # approval wait before failing closed
networkGrantTtlMs: 1800000 # how long one approval covers its target
Tiered behaviour. Nothing reaches the network without an allow rule.
An unlisted target is escalated to the interactive approval seam, raised on
behalf of the shell command that opened the connection; approving widens reach
for that target for the session. A deny rule is never escalated —
approval can widen what an unlisted target may reach, but it can never override
a rule that says no.
The boundary — read this before relying on it. The proxy is a cooperative policy layer, not an enforcement boundary. It only sees traffic from clients that read the proxy environment:
| Client | Covered? |
|---|---|
curl, wget, git, Go net/http, Python requests | yes |
Node.js http/https/fetch | no — connects directly |
Java (without -D proxy flags), .NET HttpClient | no |
| Raw sockets, custom TCP | no |
| DNS, QUIC/HTTP3, non-HTTP protocols | no |
| Connections to a literal IP | no |
So a shell command like node -e "require('http').get('http://host/')" is not
intercepted. Treat this as a guardrail against accidents and a place to state
intent, not as a hermetic sandbox.
DSH's own network traffic — the built-in network tools and the LLM
transport — is deliberately left alone. Those connections carry no shell
attribution, and networkUnattributed: allow (the default) passes them
through unreviewed: reviewing them would let the host block itself, which is
a worse failure than a missed block. Set networkUnattributed: deny only if
you know your host's clients ignore the proxy environment.
Query the live state at GET /api/dsh-perm-gate/network (mode, bind, port,
proxy liveness, env-injection state, block counters, recent blocks).