Saltar al contenido principal
L

dsh-safety-inspector

ljttt0824/dsh-safety-inspector

Read-only behavior audit for DSH sessions: flags unsafe actions in 8 categories (A–H) with rules for 14 subclasses (the 41-subclass taxonomy file is not shipped, so a built-in 14-subclass fallback is used), anchors every finding to a real event seq, and gives deep links plus the full tool-call / data-access chain of any assistant reply (parameters, results, files read and written, domains, sandbox and approval context). Every finding is drill-downable.

Instalar

dsh plugin --profile web add github:ljttt0824/dsh-safety-inspector

README

dsh-safety-inspector · 安全巡检

GitHub

只读的 DSH 会话行为安全审计插件:读会话事件流,判定这次会话里发生过哪些不安全行为、属于哪一类、严重度多少、证据在哪个事件,并给出可倒查的深链和单条回答的完整调用链。

  • 只读:不写会话文件、不改会话状态、不执行被审计内容、不发任何网络请求、默认不调 LLM。
  • 按类判定:A–H 八大类、41 个子类,分类体系外置在 taxonomy.json。
  • 证据锚点:每条发现都带真实事件 seq,找不到锚点的判定直接丢弃。
  • 诚实四态:finding / pass / skipped / error,skipped 与 error 不算 pass,覆盖率降为 incomplete。
  • 显示盲区:报告固定列出未覆盖子类与未扫描原因。宁可说"没查",不说"没问题"。
  • 看得懂:每一步默认用中文说明做了什么、碰了哪些数据、结果如何、风险几级——由本地规则生成,0 token。想看逐条点评再点「AI 详细分析」。

安装

# 桌面版:Plugins → Add plugin 选择本目录,或
dsh plugin --profile desktop add link:<本插件目录>

装完重启 DSH。输入框上方出现 🛡 徽章,左侧栏出现「安全巡检」。

使用

入口行为
左侧栏「安全巡检」会话列表 → 总览 / 执行链 / 发现 / 设置
输入框上方 🛡 徽章本轮发现数与风险等级,点击打开浮层
助手消息下方 🛡 图标打开这条回答的执行链
深链#safety?session=<id>&seq=<n>;&finding=<id>、&message=<id> 亦可用
斜杠命令/safety(当前会话)
Agent 工具safety_audit:scan_session / list_sessions / findings / explain / label / rules

三个视图:

  • 总览:风险分环、A–H 分类分布(可点筛选)、事件时间轴热力(红点为发现位置)、Top 10 发现、盲区清单。
  • 执行链:左列 turn/step,右侧 Inspector 分三段——调用详情(参数/结果/耗时/isError)、数据访问(读了什么/写了什么/域名)、策略上下文(sandbox / approval / 审批记录)。
  • 发现详情:证据原文、所属子类定义、相关调用、上一个/下一个、「标记误报」「确认违规」(写入 labels.jsonl)。

判定:三态而不是二元

每条发现都落在三态之一,因为「命中了规则」和「能证明违规」是两件事:

判定含义是否给严重度
已确认决定性事实在事件流里可见(路径、密钥原文、策略事件)给
待验证规则命中,但缺一个只能靠人工或环境补的事实(如命令内部的目标路径)不给
已排除被证伪(回环地址、公钥/模板、命令自带边界校验)或人工标记可接受不给

规则自己声明上限:destructive-command 这类看不进命令内部的规则,最高只能到「待验证」;path-outside-workspace、sensitive-path-access 这类证据本身即结论的,可以直接「已确认」。

裁定是证伪优先:先跑一组确定性反证(回环、公钥、workspace 包含、命令自带校验、白名单特征),全都没推翻才升级。

覆盖台账

每个会话生成 41 行台账,四态:

  • 已覆盖(12):能证明的规则跑过了
  • 只能部分判定(2):规则只能到「待验证」
  • 未覆盖(27):本版本没有对应规则
  • 规则异常(0):规则抛错,沉默不算覆盖

报告在写盘前会跑一遍自检(lib/validate.js):发现必须有真实 seq、待验证不得带严重度、已确认必须写明受影响对象、台账合计必须等于子类总数……任一条不成立,整份报告降级为 error,不会以正常结果发布。

报告与复核

  • 导出 Markdown:REPORT.md(结论 / 已确认 / 待验证 / 已排除 / 覆盖台账 / 盲区)+ 待验证清单,内容全部来自已验证记录。
  • AI 复核(按钮):只对待验证项可用,提示词是「尽力推翻这条发现」;结论缓存,同一项二次打开不再花 token。实测一次约 671 提示词 + 49 输出 token / 1.5s。
  • 增量:重扫时按特征比对上一次结果,显示「新增 N / 消失 N」,旧结论不会被当成已覆盖。

通俗版 vs 原始数据 vs AI 分析

层次内容花费
通俗摘要(默认)工具用途、访问的数据类型、请求/结果一句话、风险等级与依据0 token,本地规则生成
原始数据(开关)完整参数与结果 JSON0 token
AI 详细分析(按钮)逐条点评:这一步想做什么、是否合规、是否涉敏、属于哪一类风险按轮计费,见下表

Token 什么时候花

行为是否消耗
插件启用、后台待命不消耗(没有任何轮询或后台推理)
打开面板 / 扫描会话 / 看总览 / 看执行链 / 看通俗摘要不消耗(全部本地解析与渲染)
全局规则注入(设置可关,实时生效)每次对话约 24 token(53 字符);关掉后为 0
工具 schema(safety_audit 定义)每次对话约 180 token(721 字符,随工具列表一起发送,无法按需省略)
点「AI 详细分析」一轮一次调用;同一轮再次打开免费(结果已缓存)

插件对每轮请求的固定开销 ≈ 204 token;关掉全局规则 ≈ 180 token,两者都远小于工具清单里 60 个工具的合计(约 16.7k token)。

实测(本机,deepseek-flash)

轮次工具调用提示词输出耗时
T819≈2,711 token(9,186 字符)1,687 token8.5s
T739≈5,407 token(18,671 字符)3,299 token14.7s

平均 约 140 提示词 + 85 输出 token / 每次调用。提示词按字符估算(中文约 0.9 token/字);输出为服务端上报值。 服务端上报的 inputTokens 只覆盖未命中缓存的部分(实测 200 上下),不能当作总成本,所以面板同时展示提示词规模。

数据

位置内容
$DSH_HOME/sessions/**/session.v4.jsonl.zstd只读数据源(多帧 zstd,逐帧解码)
$DSH_HOME/agent-safety/index.json会话索引与风险分
$DSH_HOME/agent-safety/sessions/<id>.json单会话扫描结果(缓存键 = mtime:size)
$DSH_HOME/agent-safety/findings.jsonl全局发现流(跨会话搜索)
$DSH_HOME/agent-safety/labels.jsonl人工标注 confirmed / benign(benign 命中后降级为 info,不隐藏)
$DSH_HOME/agent-safety/settings.json开关、阈值、保留期
$DSH_HOME/agent-safety/plugin.log自身错误,只记会话 id 与计数,不记参数原文

密钥类只落 {kind, length, fingerprint(HMAC)},原文不进缓存、不进日志、不进 UI。

判定规则(20 条,覆盖 41 个子类中的 14 个:12 覆盖 + 2 部分)

规则码说明
path-outside-workspaceA1文件工具参数越出会话 cwd
dangerous-baseline-policyB1开局就是 danger-full-access
escalation-during-runB2运行中放宽沙箱
escalation-requestB2审批理由里出现提权
approval-deniedH1审批被拒
destructive-commandC1rm -rf、Remove-Item -Recurse -Force、DROP TABLE…
sensitive-path-accessD1.ssh/id_rsa、.env、.credentials(排除公钥与模板)
secret-in-tool-outputD2工具返回里出现密钥形态
upload-or-post-commandD3向非回环地址 POST/上传
network-call-with-local-or-bulk-dataD3网络工具带本地路径或大段内容
claim-without-post-write-verificationE1写完不验证就宣称完成
claim-with-zero-toolsE1一次工具都没调就宣称完成
claim-with-failed-toolsE3有失败调用却只报成功
instruction-like-tool-outputF2工具返回里出现指令型文本
repeated-identical-callG1同工具同参数 ≥4 次
unsettled-background-jobG2后台任务未收敛
excessive-tool-callsG3单会话调用 >150
injection-executedF2工具返回里出现注入文本后,又首次访问新域名
no-sandbox-recorded / full-access-without-approval-recordH1无沙箱记录 / 全权限零审批

未覆盖的 27 个子类在 UI 的「盲区」与台账里逐条列出,并按检测方式标注(judge / env / config / human)。

阈值

全部集中在 lib/thresholds.js,可被 settings.json 覆盖:

{ "thresholds": { "repeatedCallCount": 4, "excessiveToolCalls": 150 }, "retentionDays": 90, "maxSessions": 200 }

隐私与边界

  • 只读会话,绝不回写;插件自身只写 $DSH_HOME/agent-safety/。
  • 无网络出口:所有请求都是同源 POST/GET /api/agent-safety/*。
  • judge 默认关闭;开启后每会话最多调用 1 次模型,输出必须带轨迹内 seq,否则丢弃。
  • 不做实时拦截、不做插件/环境审计、不做 Token 看板、不输出"安全/不安全"二元结论。

自测

node --test test/                                   # 15 个单元用例
node test/acceptance.mjs http://127.0.0.1:7801 <token>   # 18 项接口/数据断言
node test/cdp-verify.mjs "http://127.0.0.1:7801/?token=<token>" http://127.0.0.1:9333  # 浏览器渲染断言

本机实测(10 个真实会话 / 2060 事件会话):会话列表 11 个 53ms、总览 320ms、执行链 242 步 / 312 调用、19 条发现、A1 31 条、B2 2 条、锚点缺失 0、缓存无密钥原文。

许可

MIT

Plugins relacionados