tancheng33/dsh-egress-guard
Runtime security gate for DeepSeek Harness: egress host allowlist, secret redaction in tool results, and an append-only audit log
已收录
1
Security
Bundle 已验证
预览
功能介绍
工具管线上的运行时安全网关:拦截出站白名单之外的主机调用,在 canonical value(而非仅渲染内容)层面脱敏结果中的凭据,每个决策写入 JSONL 审计日志;默认仅监控不拦截。
适合
- 希望先观察、再对工具调用强制执行出站主机策略的 DSH 运维人员。
- 需要在模型、日志或 Code Mode 程序读取前,从工具结果 canonical value 中脱敏凭证的团队。
- 需要用本地 JSONL 记录工具管线放行、拒绝、审批与脱敏决策的部署。
不适合
- 期望安装后立即产生拦截效果的用户;随附配置默认仅监控,需另行启用强制模式。
- 出站目标藏在不暴露可识别主机的不透明或编码参数中的工具调用。
- 需要扫描二进制结果,或需要防篡改集中审计日志的工作流;二进制块不扫描,本地日志也未签名。
README
dsh-egress-guard
| English | 中文 |
A runtime security gate for DeepSeek Harness tool calls.
Existing security plugins in the ecosystem scan configuration files before an agent runs. This one sits in the tool-execution pipeline and acts on the calls themselves:
| Rule | Extension point | What it does |
|---|---|---|
| Egress allowlist | tools/pre-execute |
Denies (or asks about) a call that names a network destination outside your allowlist — curl to a paste site, git push to an unknown remote, a fetch to an exfiltration endpoint. |
| Secret redaction | tools/post-execute |
Rewrites credentials out of a tool result before the model, the durable session log, or a Code Mode program can read them. |
| Audit log | both waterfalls | Appends every decision — including the ones monitor mode only would have made — to a JSONL file. |
No fork, no patched loop: three listeners on documented extension points, disposed cleanly on unload.
Install
dsh plugin --profile <name> add dsh-egress-guard
The bundle ships mode: monitor, so installing it cannot break a working setup: every rule is evaluated and audited, nothing is blocked or rewritten. Read the audit log for a day, then turn on enforcement in your profile’s cordis.patch.yml:
- id: egress-guard
config:
mode: enforce
egress:
enabled: true
allowHosts: ['*.github.com', '*.npmjs.org', 'api.deepseek.com']
denyHosts: []
allowLoopback: true
onViolation: deny
redact:
enabled: true
builtins: true
extraPatterns: []
placeholder: '[redacted:{name}]'
audit:
enabled: true
path: ''
logAllowed: false
A patch replaces a row’s whole config, so restate every key you want to keep.
Configuration
| Key | Default | Meaning |
|---|---|---|
mode |
monitor |
off registers nothing. monitor evaluates and audits without acting. enforce denies and redacts. |
egress.allowHosts |
[] in schema, a starter list in the bundle |
Permitted hosts. *.example.com covers the apex and every subdomain. An empty list means denylist-only. |
egress.denyHosts |
[] |
Always denied. Beats allowHosts and allowLoopback. |
egress.allowLoopback |
true |
Exempts localhost, 127.0.0.0/8, ::1, *.localhost. |
egress.onViolation |
deny |
ask routes to ctx.approval instead — and degrades to deny when no approval service is mounted. |
redact.builtins |
true |
Private keys, vendor API keys, JWTs, bearer headers, KEY=value assignments. |
redact.extraPatterns |
[] |
Extra regex sources, compiled with the global flag. |
redact.placeholder |
[redacted:{name}] |
{name} is the pattern that matched. |
audit.path |
$DSH_HOME/egress-guard.jsonl |
JSONL, one decision per line. |
audit.logAllowed |
false |
Also record calls that named a host and passed — this is how you build an allowlist out of real traffic. |
Building an allowlist from real traffic
# 1. Install (monitor mode) and work normally for a while, with logAllowed: true.
# 2. See which hosts your agent actually reaches:
jq -r '.hosts[]?' ~/.dsh/egress-guard.jsonl | sort | uniq -c | sort -rn
# 3. Put the legitimate ones in allowHosts, then flip mode to enforce.
Design notes
Redaction happens at the canonical value, not the rendered content. The registry’s contract is explicit that content replacement is not a confidentiality boundary — a Code Mode program receives the canonical value directly. So a successful result is redacted by replacing its value, and the content is re-rendered from the redacted value. Failed results carry no value (the registry rejects a value replacement on them), so their message is redacted as content.
The guard runs last in the post-execute waterfall. It delegates with next() first, then redacts whatever projection the composed decision actually carries, so a listener deeper in the waterfall cannot reinstate the original text. When another plugin replaced the content but the underlying value holds a secret, the guard replaces the value — losing that plugin’s presentation, but not leaking to programmatic consumers. That precedence is deliberate.
Denials tell the model not to route around them. A bare “denied” invites a retry with a different tool; the reason string names the hosts and says to ask the user instead.
Limitations — read this before trusting it
This is a guard rail, not a containment boundary. It raises the cost of an accident or a careless prompt injection; it does not stop a determined adversary running code on your machine.
-
Detection is textual. Destinations are found by scanning argument strings for URLs and
user@hostremotes. A command that assembles its destination at runtime (curl "$ENDPOINT", base64, string concatenation, an IP in decimal form) is invisible to the gate. Real containment is the sandbox seam’s job (dsh-bash-sandbox, network namespaces, a proxy), not a string matcher’s. - A tool that opens its own socket bypasses the gate entirely unless the destination appears in its arguments.
-
Redaction is pattern-based, so it misses credential shapes it does not know, and it can rewrite text that merely looks like a secret. Add
extraPatternsfor your own formats; check the audit log for false positives before enforcing. - Binary content is not scanned — image blocks and other non-text blocks pass through untouched.
- The audit log is local and unsigned. Anything that can write to your filesystem can edit it.
Compatibility
Built against the @deepseek-ai/dsh-tools 0.1.0-rc pipeline contract. The test suite runs against 0.1.0-rc.6 from npm, and the bundle was installed into a live dsh 0.1.0-rc.5 profile four ways — from npm, from a local path, from a packed tarball, and from git — reaching fiberPhase: active in all four.
Note that npm’s latest tag for the @deepseek-ai/* packages still points at an old 0.0.1-rc.1 line; the current releases are on the next tag. If you install harness packages by hand, ask for the version explicitly.
The harness is in developer preview and states that compatibility-breaking changes will happen. If a pipeline contract shifts, this plugin’s tests are designed to fail loudly — they execute real calls through a real registry rather than mocking the waterfalls.
Development
npm install
npm test # 61 tests: pure unit tests + end-to-end through a real ToolRuntime
npm run typecheck
npm run build
To try it against a live harness without publishing:
dsh plugin --profile <name> add /path/to/dsh-egress-guard
dsh --profile <name> --dump-config # shows the "# == dsh-egress-guard" layer
License
常见问题常见问题
在启用了 DSH 的终端中执行已验证命令 dsh plugin --profile default add github:tancheng33/dsh-egress-guard。命令会解析公开 package 元数据,并保持插件与本页展示的目录身份一致。
兼容性以页面上展示的 bundle 与 profile 状态为准。如果某个 profile 尚未检测到,请先保持禁用,并在生产启用前阅读仓库文档。
GitHub 链接和 activity 元数据是 release 与维护状态的来源。新版本发布后重新查看本页,确认目录已经观察到最新版本。