dsh-ast-guard
Verified@cfitsec/dsh-ast-guard · v2.0.1 · MIT
PowerShell syntax-tree guard for DSH: a resident parser plus its own rule file. Judges pwsh commands by syntax tree (not by text), and adds a deliberation tier for costly-but-legitimate actions.
Install
dsh plugin add @cfitsec/dsh-ast-guard Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-ast-guard
DSH(DeepSeek Harness)的命令守卫插件:在工具执行前,把
pwsh/powershell命令交给 PowerShell 自带的语法树解析器判定,按效应拦截(不是按命令名);规则写在 YAML 里, 代码里不写死任何策略。
前置
- 已经装好 DSH —— 这是 DSH 插件,不是独立程序;
- Windows(解析器用
powershell.exe,5.1 即可); - Node ≥ 22.19。
拿到它
从 npm 装(推荐):
dsh plugin --profile web add @cfitsec/dsh-ast-guard
想改源码再装:git clone https://github.com/CFITCorporation/dsh-ast-guard,然后把那个目录交给 dsh plugin add。
装进 DSH
装完重启 DSH(插件与配置在启动时加载,不热更新)。
插件会自己把配置挂上(包里有 cordis.patch.yml),规则用包内的 rules/default.yaml ——
不需要你搬任何文件。
没有 dsh plugin add 这个命令时,手动挂(两步)
在
$env:DSH_HOME\profiles\<你的 profile>\package.json的dependencies里加一行(DSH_HOME是 DSH 的主目录,默认在~/.dsh):"@cfitsec/dsh-ast-guard": "link:<你 clone 下来的绝对路径>"在同一个文件里,把它加进
dsh.profile.bundles数组。然后重启 DSH。
验证它真的在拦
重启后,随便开一个 pwsh 会话,跑一条会被拦的命令:
Set-Content -Path "$env:TEMP\ast-guard-探针.txt" -Value x
应该看到:
[ast-guard] 拒绝:content-write
…(dsh-ast-guard v2.0.0;规则来自 …\rules\default.yaml;…)
看到了 ⇒ 装好了。 没看到的话:
- 看 DSH 日志里有没有
ast-guard:开头的行(插件加载失败会写在那里); - 确认重启过宿主;
- 在插件目录里跑一次
npm install(它只有一个依赖yaml,装好再重启)。
它管什么
- 写文件内容:
Set-Content/Add-Content/Clear-Content/Out-File/Tee-Object/Export-*、.NET的WriteAll*、重定向>>>($null豁免)、以及iex "Set-Content …"这种字符串里的写入; - 删除 / 移动 / 改名:受保护位置(
.git、node_modules、.ssh…)、递归、通配、-Force触发三思; - 内联代码:
node -e、python -c、powershell -Command、-EncodedCommand…(逐命令看参数); - 凭据文件:
edit/write的目标路径(.npmrc/.credentials/id_rsa…),只看路径,不碰正文; - 注册表增删改、git 范围收缩(
git checkout -- .这类)⇒ 三思。
三思不是拒绝:插一道 10 秒冷却窗口,窗口内回答"是"和"否"都无效;冷却结束时先跑规则里声明的
只读探针,把"这条命令会丢什么"现算出来放进窗口,再用 ast_confirm 带编号回答。
两档:拒绝和三思
命中规则后只有两种结局,区别在于这事有没有正当用途:
| 档 | 什么时候用 | 表现 |
|---|---|---|
拒绝 deny |
这件事不该这么做(有更好的做法) | 直接不执行,文案告诉你该怎么改 |
三思 deliberate |
事情正当但代价高(删、移、改范围) | 停一下,给你一个冷静窗口,再由你决定 |
三思窗口长这样:
[ast-guard] 三思:destructive-delete
- 命令:Remove-Item .\src -Recurse
触发原因:带标志 -Recurse
…插一段只读探针现算出来的"这条命令会丢什么"…
冷却期 10 秒:这段时间内你的任何回答都无效 —— 回答"是"也一样。
待确认编号:ed885e
四条规则,缺一不可:
- 冷却期内回答无效 —— "是"和"否"都不算数,逼你先看一眼再决定;
- 必须带编号:
ast_confirm({ id: "ed885e", decision: "yes" })。不带编号的"是"一律无效, 免得上一条命令的确认落到下一条上; - 一次性、绑命令:放行只对这一条命令(按哈希绑定)生效,用掉或过期即作废;
- 2 分钟不处理就作废 —— 防止"忘了之后顺手确认"。
冷却秒数默认 10(规则里 cooldownSeconds 可调,但不能小于 10);窗口里那个"会丢什么"
来自规则里声明的只读探针(evidence),探针带写动词会在编译期就被拒掉。
默认规则里有四组进三思:递归/通配/-Force 的删除、覆盖式移动改名、注册表增删改、
git checkout -- . 这类范围收缩(后者带三个 git 只读探针)。
改规则
默认规则是一套能直接用的(写内容族 + 条目操作族 + 内联 + 凭据 + 受保护目录 + 五条三思规则)。 想改成自己的:
复制一份:
Copy-Item rules\default.yaml my-rules.yaml,按需要改;在 profile 的插件配置里指过去(
$env:DSH_HOME\profiles\<profile>\cordis.patch.yml):- id: ast-guard config: rulesFile: <绝对路径,或相对会话 cwd 的路径>重启(配置不热更新;规则文件本身改了会热加载)。
完整规则词表:commands · memberNames · redirects ·
when{ flags, wildcard, paths, subcommands, argsExact, argsContain, all } ·
tools · paths · mentions · evidence · 三思的 cooldownSeconds / when / evidence。
判不了就拒(fail-closed)
规则文件读坏、解析器起不来 ⇒ 拒绝并说明怎么修,不是静默放行。
理由很简单:一个"看起来在拦、其实没判"的守卫比没有守卫更危险 —— 它骗的是使用者。
(对应配置:onRuleError: closed / failPolicy: closed,都能改成 open,但要想清楚代价。)
边界(别误当成安全边界)
- 只接
pwsh/powershell;bash 不在范围内 —— 规则层很简单,欢迎按目标 shell 自行移植。 - 护的是"忘了那一次",不防"故意绕":对故意的,靠回退点与审计,不靠墙。
- 看不见字符串里的东西(
Start-Process … -ArgumentList、[scriptblock]::Create("…")、cmd /c "…")。 - 覆盖面是正面清单,列不完;只读列举受保护目录也会被拦("提及即停"的已知代价)。
跑自带的测试(可选)
✅ 测试随包发布(在
test/里,纯 dry-run:只解析、不执行、不联网、只写临时目录)。 所以从 npm 装好之后,也能直接拿它验证你改的规则。
在包目录里(npm publish 装的话是 node_modules/@cfitsec/dsh-ast-guard):
npm install # 只有一个依赖 yaml(以插件方式装好时通常已就位)
npm test # = smoke + hook 两套
node test/import-check.mjs # 入口能否装载(不启动)
node test/smoke.mjs # 30 条裁决用例:只解析、不执行;不联网、不写任何文件
node test/hook.mjs # 12 条插件级用例:走真的 apply()(假 ctx)
改规则之前先读这一份
docs/维护规则.md —— 给要动规则的人或智能体:
维度速查、加规则的流程、我们踩过的坑(别名 / args[0] / YAML 单引号 / cmdlet 没有子命令)、
以及永远不要做的三件事(留反向后门、用内容白名单、静默放行)。
与生态里同类项目的关系
npm 上已经有两个相邻的项目。先认账,再说差异:
| 项目 | 它做什么 | 我们的差异 |
|---|---|---|
dsh-ast-guard |
tree-sitter 解析 bash + powershell CST;allow/ask/block;DNA 模式;沙箱升级联动 |
我们用 PowerShell 自带解析器(不引 WASM grammar、零原生依赖);我们是 三思冷却窗口(不是弹窗四选);我们有 只读探针把"这条命令会丢什么"现算出来;规则是朴素 YAML + 自有维度集 |
dsh-pwsh-guard |
禁 shell 文本 I/O(Select-String / Get-Content / node -e …),指向 read / grep / glob |
目标不同:它管读的方式,我们管写内容 / 删除 / 范围收缩。可以并存 |