dsh-risk-gate:給破甲補一道人肉閘門

前言

用 DSH 跑智能體時,很多人會裝 dsh-purge 這類補丁,把審批改爲自動放行、沙箱默認全開。代價是 git push、遞歸刪除、改註冊表這類高危動作不再有人攔。dsh-risk-gate 不撤銷破甲,而是在這些動作上加一道機械閘:命中規則直接 deny,必須由用戶本人回覆一次性編號才放行。它由 oobitoooo 維護,分類爲 admin-security,GitHub 上有 1 個星標,屬於社區小工具,不是官方組件。

它解決什麼問題

dsh-purge 的 patch #9 把 @deepseek-ai/dsh-user-approval 替換成「每次請求直接 allowed-once」:

// 宿主被打補丁後的實際行爲
if (this.effectivePolicy(session) === "never") return "allowed-once";
return "allowed-once"; // 不再彈窗、不再 fail-closed

這意味着任何依賴 kind: 'ask' 的方案都等於自動同意。把 approval 配置改回 ask 也沒用,代碼層已被改寫;讓 purge 少打補丁也不行,它不支持按補丁禁用,升級還會重打。

本插件不走 approval,而是自己掛門:

  1. 掛 tools/pre-execute(可重排的 allow/deny 門禁),命中就 deny;
  2. 再掛 ctx.tools.guard() 做單調兜底,後續任何監聽器想把 deny 翻成 allow,都會被再擋一次;
  3. 放行憑據是用戶消息裏出現的一次性編號,從 agent/pre-step 讀取,模型自己編的編號無效;
  4. 編號一次性、同一條命令重試複用同一個編號、10 分鐘過期。

它不做什麼

  • 不修改宿主文件、不注入提示詞人格、不碰 app.asar;
  • 不撤銷破甲的其它行爲,連續執行、工具上限、子代理深度等照舊;
  • 不聯網,除了你自己的 policy.json,沒有任何外發;
  • 不是沙箱、不是權限系統。它攔的是工具調用參數,不是意圖。

安裝

需要 DSH Desktop 或 dsh web,Node ≥ 18。

方式一:GitHub 直裝(推薦)

dsh plugin --profile desktop add "https://github.com/oobitoooo/dsh-risk-gate/releases/latest/download/dsh-risk-gate-1.1.0.tgz"
# web profile 把 desktop 換成 web

想釘死版本,把 latest/download/ 換成下載具體版本號。

方式二:本地 clone / link(開發用)

powershell -ExecutionPolicy Bypass -File scripts\install.ps1            # 默認 desktop profile
powershell -ExecutionPolicy Bypass -File scripts\install.ps1 -Profile web

方式三:手動(不信任腳本時)

  1. 把倉庫放到 $DSH_HOME\plugins\dsh-risk-gate;
  2. profile 的 package.json 增加依賴 "dsh-risk-gate": "file:../../plugins/dsh-risk-gate",並把 "dsh-risk-gate" 追加到 dsh.profile.bundles;
  3. 在 profile 目錄 pnpm install,或手工建 node_modules\dsh-risk-gate 的 junction 指回源碼;
  4. 重啓客戶端。

裝完需要完全退出 DSH(含托盤圖標)再打開。改 package.json 或 profile 的 cordis.patch.yml 會觸發熱重載。README 安裝命令裏寫的是 1.1.0 的 tgz 路徑,而倉庫 package.json 當前版本是 1.3.1,使用時以實際 Release 文件名爲準。

典型用法:確認流程

一次命中規則的調用會被攔截,宿主返回類似下面的內容:

Error: [risk-gate] 已攔截:遞歸刪除 / 格式化文件(R1)
目標:Remove-Item -Recurse -Force "…\dist"

這是高危操作,需要用戶本人確認。請把下面這段原樣發給用戶,然後停下等回覆:
「…確認請回復:確認 8CFC6197」

用戶回覆 確認 8CFC6197 之後,原樣重試同一條調用纔會放行一次;再執行同一條命令又會被攔。編號只認用戶消息,模型自己寫「確認 xxx」不算,子代理和別的會話裏確認也不通用。

批量任務:授權窗口

發版、遷移、批量改名這類連串動作裏,逐條確認是反向升級。插件提供授權窗口:用戶發「授權 30 分鐘」後,30 分鐘內高危操作直接放行,逐條寫進 audit.jsonl(decision=allow-by-grant)。隨時發「取消授權」立即關閉。上限 120 分鐘,超出按 120 分鐘算。窗口內不改變規則判斷,只是跳過「要編號」這一步。

默認攔什麼

規則 內容
R1 遞歸刪除/格式化:Remove-Item -Recurse、rm -rf、rmtree、diskpart、Format-Volume
R2 git push、reset --hard、clean -f、改遠端、刪 release/repo
R3 註冊表、服務、計劃任務、防火牆、執行策略、Defender 排除、hosts、takeown/icacls
R4 裝/卸全局包與插件:npm/pnpm -g、winget、choco、pip install、dsh plugin add
R5 對外連接/外發:ssh/scp/nc/chisel/frp/ngrok、帶 body 的 POST
R6 下載即執行/編碼混淆:\| iex、DownloadString、-EncodedCommand、FromBase64String
R7 強殺進程/提權:Stop-Process -Force、taskkill /f、runas、psexec、sudo
R8 run_code 裏起子進程:child_process、subprocess、os.system
R9 刪除或寫關鍵路徑:C:\Windows、Program Files、ProgramData、resources\app、.ssh、.credentials

R1 有例外:目標落在允許根內且條目數不超過 maxEntries(默認 200)時直接放行;帶 ; | && 拼接的不享受例外。

配置

運行時讀 $DSH_HOME/risk-gate/policy.json(Windows 爲 %USERPROFILE%\.dsh\risk-gate\policy.json),改完熱讀,不用重啓。首次啓動會自動落盤默認值。

{
  "ttlMs": 600000,
  "rules": [
    { "id": "R1", "label": "遞歸刪除 / 格式化文件", "flags": "i",
      "pattern": "remove-item[^\\n]*-recurse|\\brm\\s+-rf\\b|shutil\\.rmtree" }
  ],
  "protectedRoots": ["c:\\\\windows", "\\\\resources\\\\app", "\\\\.ssh"],
  "recursiveDelete": {
    "allowRoots": ["C:\\\\work\\\\my-project"],
    "maxEntries": 200,
    "useWorkdirAsRoot": false
  },
  "allow": ["^\\s*git\\s+(status|diff|log|show|branch)\\b"]
}
字段 作用
rules[] 命中任意一條即攔截(正則,flags 默認 i)
protectedRoots[] write/edit 命中這些路徑片段視爲高危;刪除類命令也用它做 R9
recursiveDelete.allowRoots R1 的例外根
recursiveDelete.maxEntries 條目上限,超了重新要確認
recursiveDelete.useWorkdirAsRoot 把 workdir 也當作允許根(默認關)
allow[] 讀類命令白名單,只在命令沒有鏈式拼接時生效
ttlMs 編號有效期,默認 10 分鐘

其它產物:risk-gate/audit.jsonl(每次 block / allow-after-confirm / warn,2 MB 輪轉)、risk-gate/boot.log、risk-gate/pending.json、risk-gate/last-user.json。

審計與風險分級

審計用的 HMAC 鏈式結構來自社區項目 dsh-guardwall:每行帶 prev 與 h(HMAC-SHA256,密鑰 risk-gate/chain.key),每 20 行落一次 chain.checkpoint.json,跑 risk_gate action=verify 能發現改行、換密鑰、尾部截斷。落盤前會脫敏 ghp_、github_pat_、sk-、AKIA、Bearer …、password=…,命令裏帶令牌也不會原樣進日誌。規則可寫 risk: 1–10,配 thresholds.block(默認 7)和 thresholds.warn(默認 4)。

驗證與開發

node --check lib/index.js      # 語法
node test/smoke.mjs            # 純邏輯迴歸(假 ctx,不啓動 DSH),33 條
node scripts/livecheck.mjs     # 用當前 policy.json 跑 20 條樣例
node scripts/pack.mjs          # 生成 dsh-risk-gate-<version>.tgz

Windows 上另有 scripts\verify.ps1(四層接線體檢)、scripts\status.ps1、scripts\uninstall.ps1。新會話裏可以直接讓模型「跑一下 risk_gate status」,插件註冊了 risk_gate 工具(status / rules / pending / selftest / audit)。

適用場景與注意

適合已經在用 dsh-purge/purge-community、擔心模型自作主張執行高危命令的 DSH 用戶,以及想給團隊智能體加一道人工確認環節的開發者。插件以當前 DSH 進程的權限運行,安裝前建議先讀一遍源碼和 MIT 許可證,確認規則和審計行爲符合你的預期。它是一道按規則 + 編號的護欄,不是沙箱,也不隔離文件系統和網絡。

邊界要清楚:攔的是調用參數,不是意圖;python -c "print(1)" 放行,用 python 調 os.system 會被 R8 攔。規則是正則,繞法無窮。它也不是鎖,誰有本機寫權限就能改 policy.json 把規則刪空。設計上誤報優先於漏報,寧可多問一次。

小結

dsh-risk-gate 的價值在於:不與破甲對抗,只在高危動作上加一道可追溯的人肉閘門,編號只認用戶消息,審計日誌可驗證且脫敏。如果你已經在用 dsh-purge,值得一試。

目錄頁:https://www.skillhub.cn/plugins/oobitoooo/dsh-risk-gate
GitHub:https://github.com/oobitoooo/dsh-risk-gate

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜