🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

取证护栏 a5557/dsh-agent-guard

DeepSeek Harness
a555728 天前有提交已提供中文文档扫描:含敏感能力

读取一手证据,拦截越权写入并留回滚点

29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/4 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add a5557/dsh-agent-guard
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · security
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 28 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 130 处,展示前 99 条)
  • 读取本地文件
    lib/index.js:62
    return JSON.parse(readFileSync(join(PACKAGE_ROOT, 'rules', 'default.json'), 'utf8'))
  • 读取本地文件
    lib/index.js:342
    return JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8')).version ?? null
  • 发起网络请求
    lib/client.js:87
    fetch(BASE + '/api?action=state')
  • 发起网络请求
    lib/client.js:92
    fetch(BASE + '/api?action=recent')
  • 发起网络请求
    lib/client.js:165
    fetch(BASE + '/api?action=state')
  • 读取本地文件
    lib/inspect.js:115
    raw = readFileSync(file, 'utf8')
  • 读取本地文件
    lib/inspect.js:480
    const parsed = JSON.parse(readFileSync(file, 'utf8'))
  • 读取本地文件
    lib/inspect.js:623
    return statSync(file)
  • 写入本地文件
    lib/store.js:47
    const mkdir = io.mkdir ?? ((path) => mkdirSync(path, { recursive: true }))
  • 写入本地文件
    lib/store.js:48
    const write = io.writeFile ?? ((path, data) => writeFileSync(path, data))
  • 删除本地文件
    lib/store.js:49
    const remove = io.rm ?? ((path) => rmSync(path, { force: true }))
  • 读取本地文件
    lib/store.js:59
    const exists = io.exists ?? ((path) => existsSync(path))
  • 写入本地文件
    lib/store.js:139
    const mkdir = io.mkdir ?? ((path) => mkdirSync(path, { recursive: true }))
  • 写入本地文件
    lib/store.js:217
    const append = this.io.appendFile ?? ((path, data) => appendFileSync(path, data))
  • 写入本地文件
    lib/store.js:221
    const mkdir = this.io.mkdir ?? ((path) => mkdirSync(path, { recursive: true }))
  • 写入本地文件
    lib/store.js:278
    const append = this.io.appendFile ?? ((path, data) => appendFileSync(path, data))
  • 读取本地文件
    lib/store.js:326
    const exists = this.io.exists ?? ((path) => existsSync(path))
  • 读取本地文件
    lib/store.js:345
    const stat = this.io.stat ?? ((path) => statSync(path))
  • 读取本地文件
    lib/guard.js:94
    if (!existsSync(target)) return 'absent'
  • 读取本地文件
    lib/guard.js:95
    const stat = statSync(target)
  • 写入本地文件
    lib/backup.js:70
    const mkdir = input.io?.mkdir ?? ((path) => mkdirSync(path, { recursive: true }))
  • 写入本地文件
    lib/backup.js:71
    const copy = input.io?.copyFile ?? ((from, to) => copyFileSync(from, to))
  • 读取本地文件
    lib/backup.js:72
    const exists = input.io?.exists ?? ((path) => existsSync(path))
  • 读取本地文件
    lib/backup.js:73
    const stat = input.io?.stat ?? ((path) => statSync(path))
  • 写入本地文件
    lib/backup.js:112
    writeFileSync(join(dir, `${name}.meta.json`), JSON.stringify({
  • 写入本地文件
    lib/backup.js:151
    const mkdir = input.io?.mkdir ?? ((path) => mkdirSync(path, { recursive: true }))
  • 读取本地文件
    lib/backup.js:152
    const exists = input.io?.exists ?? ((path) => existsSync(path))
  • 写入本地文件
    lib/backup.js:192
    writeFileSync(layoutFile, content)
  • 写入本地文件
    lib/backup.js:208
    writeFileSync(metaFile, JSON.stringify(meta, null, 2))
  • 读取本地文件
    lib/backup.js:248
    const readdir = input.io?.readdir ?? ((path) => readdirSync(path))
  • 删除本地文件递归
    lib/backup.js:249
    const remove = input.io?.rm ?? ((path) => rmSync(path, { recursive: true, force: true }))
  • 读取本地文件
    lib/backup.js:343
    names = readdirSync(root)
  • 读取本地文件
    lib/backup.js:355
    for (const file of readdirSync(dir)) {
  • 读取本地文件
    lib/sessionlog.js:177
    spaces = readdirSync(sessionsRoot, { withFileTypes: true })
  • 读取本地文件
    lib/sessionlog.js:188
    entries = readdirSync(spaceDir, { withFileTypes: true })
  • 读取本地文件
    lib/sessionlog.js:203
    stat = statSync(file)
  • 读取本地文件
    lib/sessionlog.js:228
    names = readdirSync(dir)
  • 读取本地文件
    lib/paths.js:96
    stat = lstatSync(target)
  • 读取本地文件
    checks/check-audit-leak.mjs:36
    if (!existsSync(SOURCE)) {
  • 读取本地文件
    checks/check-audit-leak.mjs:40
    if (!existsSync(REPORT)) {
  • 读取本地文件
    checks/check-audit-leak.mjs:45
    const pool = candidates(readFileSync(SOURCE, 'utf8'))
  • 读取本地文件
    checks/check-audit-leak.mjs:46
    const reportLower = readFileSync(REPORT, 'utf8').toLowerCase()
  • 读取本地文件
    checks/check-ci.mjs:16
    if (!existsSync(FILE)) {
  • 读取本地文件
    checks/check-ci.mjs:21
    const text = readFileSync(FILE, 'utf8')
  • 读取本地文件
    checks/check-coverage.mjs:20
    for (const name of readdirSync(dir)) {
  • 读取本地文件
    checks/check-coverage.mjs:22
    parts.push(readFileSync(join(dir, name), 'utf8'))
  • 读取本地文件
    checks/check-coverage.mjs:39
    for (const name of readdirSync(LIB)) {
  • 读取本地文件
    checks/check-coverage.mjs:41
    const text = readFileSync(join(LIB, name), 'utf8')
  • 读取本地文件
    checks/check-coverage.mjs:138
    const source = readFileSync(join(ROOT, entry.where), 'utf8')
  • 读取本地文件
    checks/check-coverage.mjs:150
    const rulesText = readFileSync(join(LIB, 'rules.js'), 'utf8')
  • 读取本地文件
    checks/check-encoding.mjs:26
    for (const entry of readdirSync(dir, { withFileTypes: true })) {
  • 读取本地文件
    checks/check-encoding.mjs:46
    const text = readFileSync(file, 'utf8')
  • 读取本地文件
    checks/check-identity-space.mjs:49
    for (const file of readdirSync(LIB).filter((name) => name.endsWith('.js')).sort()) {
  • 读取本地文件
    checks/check-identity-space.mjs:50
    const text = readFileSync(join(LIB, file), 'utf8')
  • 写入本地文件
    checks/check-identity-space.mjs:100
    for (const dir of ['sessions', 'storages', 'profiles']) mkdirSync(join(physicalHome, dir), { recursive: true })
  • 写入本地文件
    checks/check-identity-space.mjs:102
    mkdirSync(physicalWorkspace, { recursive: true })
  • 写入本地文件
    checks/check-identity-space.mjs:104
    writeFileSync(join(physicalHome, 'storages', 'workspace.json'), '{}')
  • 写入本地文件
    checks/check-identity-space.mjs:162
    mkdirSync(join(registryHome, 'storages'), { recursive: true })
  • 写入本地文件
    checks/check-identity-space.mjs:163
    mkdirSync(join(registryHome, 'sessions'), { recursive: true })
  • 写入本地文件
    checks/check-identity-space.mjs:169
    writeFileSync(join(registryHome, 'storages', 'workspace.json'), JSON.stringify(table), 'utf8')
  • 删除本地文件递归
    checks/check-identity-space.mjs:179
    rmSync(linkRoot, { recursive: true, force: true })
  • 删除本地文件递归
    checks/check-identity-space.mjs:180
    rmSync(realRoot, { recursive: true, force: true })
  • 读取本地文件
    checks/check-isolation.mjs:41
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    checks/check-isolation.mjs:54
    out.set(rel, statSync(full).size)
  • 读取本地文件
    checks/check-isolation.mjs:72
    if (!existsSync(REAL_HOME)) {
  • 读取本地文件
    checks/check-isolation.mjs:78
    console.log('测试前 agent-guard 是否存在 :', existsSync(join(REAL_HOME, 'agent-guard')))
  • 执行外部命令来自配置/变量
    checks/check-isolation.mjs:90
    execFileSync(process.execPath, ['test/run.mjs'], { cwd: ROOT, stdio: 'ignore' })
  • 读取本地文件
    checks/check-isolation.mjs:104
    const guardDirCreated = existsSync(join(REAL_HOME, 'agent-guard'))
  • 读取本地文件
    checks/check-publish-ready.mjs:22
    const manifest = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'))
  • 执行外部命令来自配置/变量
    checks/check-publish-ready.mjs:33
    const result = spawnSync(process.execPath, args, { cwd: ROOT, stdio: ['ignore', fd, fd], timeout: 300_000 })
  • 读取本地文件
    checks/check-publish-ready.mjs:37
    out = readFileSync(report, 'utf8')
  • 读取本地文件
    checks/check-publish-ready.mjs:54
    if (!existsSync(file)) continue
  • 读取本地文件
    checks/check-publish-ready.mjs:55
    for (const line of readFileSync(file, 'utf8').split(/ ? /)) {
  • 发起网络请求
    checks/check-publish-ready.mjs:71
    const response = await fetch(`${registry.replace(/ $/, '')}/${encodeURIComponent(name)}`, { headers })
  • 读取本地文件
    checks/check-publish-ready.mjs:85
    const shipped = readdirSync(join(ROOT, 'lib')).filter((name) => name.endsWith('.js')).map((name) => join('lib', name))
  • 读取本地文件
    checks/check-publish-ready.mjs:108
    const changelog = readFileSync(join(ROOT, 'CHANGELOG.md'), 'utf8')
  • 读取本地文件
    checks/check-readonly.mjs:25
    for (const entry of readdirSync(dir, { withFileTypes: true })) {
  • 读取本地文件
    checks/check-readonly.mjs:39
    const text = readFileSync(file, 'utf8')
  • 读取本地文件
    checks/check-redaction.mjs:72
    for (const entry of readdirSync(dir, { withFileTypes: true })) {
  • 读取本地文件
    checks/check-redaction.mjs:90
    text = readFileSync(file, 'utf8')
  • 读取本地文件
    checks/check-release-checklist.mjs:24
    for (const entry of readdirSync(dir, { withFileTypes: true })) {
  • 读取本地文件
    checks/check-release-checklist.mjs:94
    const text = readFileSync(file, 'utf8')
  • 读取本地文件
    checks/check-release-checklist.mjs:110
    const text = readFileSync(file, 'utf8')
  • 读取本地文件
    checks/check-release-checklist.mjs:128
    const gitignore = readFileSync(join(ROOT, '.gitignore'), 'utf8')
  • 读取本地文件
    checks/check-release-checklist.mjs:139
    const fixtures = readFileSync(join(ROOT, 'test', 'fixtures.mjs'), 'utf8')
  • 读取本地文件
    checks/check-release-checklist.mjs:162
    const manifest = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'))
  • 写入本地文件
    checks/demo-blocked-write.mjs:33
    for (const dir of ['sessions', 'storages', 'profiles']) mkdirSync(join(home, dir), { recursive: true })
  • 写入本地文件
    checks/demo-blocked-write.mjs:34
    writeFileSync(join(home, 'storages', 'workspace.json'), '{"unit":{"name":"workspace","version":2}}')
  • 写入本地文件
    checks/demo-blocked-write.mjs:35
    mkdirSync(project, { recursive: true })
  • 读取本地文件
    checks/demo-blocked-write.mjs:100
    const rows = readFileSync(join(home, 'agent-guard', 'journal.jsonl'), 'utf8').trim().split('\n').map((line) => JSON.parse(line))
  • 删除本地文件递归
    checks/demo-blocked-write.mjs:112
    rmSync(root, { recursive: true, force: true })
  • 读取本地文件
    checks/final-check.mjs:14
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    checks/final-check.mjs:23
    out.push(`${full}:${statSync(full).size}`)
  • 读取本地文件
    checks/final-check.mjs:89
    if (existsSync(realHome)) {
  • 读取本地文件
    checks/final-check.mjs:105
    existsSync(join(realHome, 'agent-guard')) ? '⚠ 存在(需确认是否由本次引入)' : '✅ 不存在,本次未在真实 home 写任何东西')
  • 删除本地文件递归
    checks/path-link.mjs:36
    rmSync(linkPath, { recursive: true, force: true })
  • 删除本地文件递归
    checks/path-link.mjs:46
    rmSync(linkPath, { recursive: true, force: true })
  • 写入本地文件
    checks/preview-panel.mjs:26
    for (const dir of ['sessions', 'storages', 'profiles']) mkdirSync(join(home, dir), { recursive: true, mode: 0o700 })
  • 删除本地文件递归
    checks/preview-panel.mjs:76
    rmSync(home, { recursive: true, force: true })
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 证据优先排查
  • 拦截越权写入
  • 逐轮回滚点

DSH 护栏插件:证据优先排查、写入治理、逐轮回滚点。零运行时依赖,不联网,不上报遥测。

README

由 DeepSeek 最新模型翻译生成
dsh-agent-guard(中文) 一个让「读一手证据」成为最省事路径的 DSH 护栏插件。 零网络、零遥测、零运行时依赖。 dsh-agent-guard 来自一次真实事故:一个 agent 没有读一手证据,把正常设计误判成缺陷,然后改写了应用自己的私有存储,导致应用崩溃。这个插件就是那次事故本该撞上的护栏。 当前版本:只读取证 + 写入治理 + 留痕与回滚点。所有结论都有实证,见 VERIFY.md。 它为什么存在 一次完全正常的目录整理之后,一个 agent 会话看到「磁盘上的会话目录数」多于「注册表登记的工作区数」,据此断定「丢了 40 个对话」,然后开始"修复":先用目录联接,再用一个「重命名会话目录 + 重写注册表」的脚本。应用崩溃了。那些"丢失的对话"从未丢失——它们是 DSH 有意不登记的 origin: "subagent" 内部记录。
展开完整文档(README 剩余 1 节)
dsh-agent-guard(中文)

一个让「读一手证据」成为最省事路径的 DSH 护栏插件。
零网络、零遥测、零运行时依赖。

dsh-agent-guard 来自一次真实事故:一个 agent 没有读一手证据,把正常设计误判成缺陷,然后改写了应用自己的私有存储,导致应用崩溃。这个插件就是那次事故本该撞上的护栏。

当前版本:只读取证 + 写入治理 + 留痕与回滚点。所有结论都有实证,见 VERIFY.md。

它为什么存在

一次完全正常的目录整理之后,一个 agent 会话看到「磁盘上的会话目录数」多于「注册表登记的工作区数」,据此断定「丢了 40 个对话」,然后开始"修复":先用目录联接,再用一个「重命名会话目录 + 重写注册表」的脚本。应用崩溃了。那些"丢失的对话"从未丢失——它们是 DSH 有意不登记的 origin: "subagent" 内部记录。

四条根因与对应措施:

| 根因 | 会出什么事 | 本插件的对策 |
| R1 路径即身份 | 工作区身份、存储键、会话文件头都以绝对路径为准;移动文件夹 = 换身份 | 检测并报告;绝不通过制造「第二个名字」去"修复" |
| R2 用汇总数字代替一手证据 | 「目录数 ≠ 登记数」被读成数据丢失 | guard_inspect 读每个会话的首帧身份头,把内部记录标为 countsAsConversation: false |
| R3 影响预算失衡 | 显示类问题被"修复"成改写全部核心数据 | 影响预算:display 类目标一律不得写核心数据 |
| R4 把不确定性转成用户的一次双击 | 破坏性脚本被做成 .bat 交付 | 生成这类脚本是告警动作,绝不"一键" |

安装

本地目录(路径需为绝对路径):
dsh plugin --profile <profile> add <本目录绝对路径>

发布后按精确版本安装:
dsh plugin --profile <profile> add dsh-agent-guard@0.1.0

package.json 声明了 dsh.bundle.patch,所以安装即挂载为 profile 的一层。
注意:没有声明 dsh.bundle 的包会被安装但不会挂载——本插件声明了。

卸载:

dsh plugin --profile <profile> remove dsh-agent-guard

卸载只移除注册,不删任何数据,包括回滚点——那是用户自己的资产。
(DSH 没有插件级卸载回调,所以 $DSH_HOME/agent-guard/ 会保留;README 下方有手动清理说明。)

用法

作为工具(已安装)

guard_inspect(scope?, workspace?, maxSessions?, includeHeaders?, redact?)
guard_journal(action?, limit?, snapshot?)

作为独立 CLI(零 profile 改动)

v0 的设计目标之一就是:DSH 起不来时它照样能用——而那正是最需要证据的时候。

dsh-agent-guard inspect # 人类可读摘要 dsh-agent-guard inspect --json # 机器可读报告 dsh-agent-guard inspect --redact # 路径/标题/id 换占位符 dsh-agent-guard inspect --no-headers # 只做目录级清点(更快) dsh-agent-guard inspect --scope workspaces,processes

界面(原生席位)

插件带客户端半边,所以它在界面里有原生席位:

- 侧边栏图标(sidebar.panellist,官方目前没有占用者)→ 点开主面板,显示护栏状态、回滚点与最近受保护操作;
- 设置页(「环境护栏」)→ 同样的状态,外加如何停用的准确做法。

两处都严格只读:没有修复、没有迁移、没有回滚按钮。
客户端半边是手写的 window.__ModuleLoader__.load bundle,因此本包零构建步骤、 零 devDependencies。同时在 /agent-guard 提供一份等价的同源 HTTP 面板,作为宿主没有 client bundle 通道时的降级方案。

说明:官方 client bundle 由 tsdown 构建,本包按同一契约手写(用 React.createElement 代替 JSX),并由单测在受控沙箱里真实执行、断言三个席位的注册形态与"无写入口"。

截图

目前没有,而且只接受真实运行出来的截图。 本项目的规矩和它要求别人的一样:先有证据,再下结论。用示意图代替真实界面,正是这个插件存在的意义所要拒绝的那种"用汇总代替一手证据"。

| 建议截什么 | 来源 |
| 1-panel.png | 正常安装后,在不含私人内容的工作区会话里,打开侧边栏面板或设置页 |
| 2-blocked-write.png | DSH 运行中,agent 尝试写 $DSH_HOME/storages/workspace.json → 审批/拒绝提示,以及 guard_journal 里对应那条记录 |
| 3-inspect.png | dsh-agent-guard inspect --redact 的输出(已脱敏,可直接公开) |

欢迎按这个方式贡献截图;公开前请先脱敏(CLI 视图用 --redact 即可)。

输出长什么样

会话:磁盘上 74 个会话目录,本次解出 74 个身份头
· 用户对话 31 条 | origin:"subagent" 或 delegationDepth > 0 的内部记录 43 条(内部记录由应用有意不登记,不计入「缺失对话」)
· <workspace>:登记 9(磁盘上找到 9)| 磁盘 12(对话 9 / 内部记录 3)| 未登记 3

发现(4 条) [info] orphan-session-space 存在一个会话存储目录,但没有任何已登记工作区的路径编码到它……
[info] unregistered-subagent-records
3 条记录带 origin: "subagent" 或 delegationDepth > 0:这是应用有意不登记的内部记录,不是丢失的对话。

请注意每一条结论的形状:「未登记」永远和 origin 分解一起给出,读不出来的身份头记为 unreadable——绝不当作「不存在」。「读不出来」和「没有」是两件不同的事。

能力与红线

能做的

- 读工作区注册表、会话身份头、目录布局与宿主运行状态;
- 在受保护路径被写入前拦截:先备份再放行,备份失败则拒绝写入;
- 只追加留痕(带哈希链,可发现改写),每轮生成回滚点,按保留策略只清理自己的快照;
- 用 guard_journal 查看护栏自身状态、最近记录与人工回滚说明。

拦不住的(必须知道)

| 旁路 | 原因 |
| pwsh/bash 里的改名、删除 | DSH 没有 rename/delete 工具,这些动作只以不透明的命令行文本出现,只能启发式提取路径 |
| 后台任务(run_in_background) | 命令交给 job registry;前台调用超时后会被提升为 job,而拦截早已执行完 |
| 宿主终端(ctx.terminals) | 它是 PTY 服务,不注册任何工具 |
| 客户端/HTTP 宿主 API | 不经过工具流水线 |
| 用户手工双击脚本、其他进程写同一路径 | 插件在宿主进程内,管不到进程外 |

所以正确的说法是「能阻止本会话内 agent 的工具调用」,而不是「能阻止一切破坏」。

另有一条性能边界:pre-execute 没有超时,且运行在单一有序通道里——在其中做的备份会阻塞该回合且中途不可取消,因此备份预算被刻意压得很小(默认单文件上限 8 MiB,超限即拒绝写入)。

永久红线

- 绝不写 DSH 的 sessions/、storages/、profiles/;
- 凭据文件只检查存在与权限元数据,绝不读内容;
- 不提供任何「一键修复/迁移 DSH 数据」的功能;
- 不生成可双击的改数据脚本;
- 无网络、无遥测、无安装钩子。

关键设计取舍

- 规则引擎是纯函数、无 I/O,所以 T1–T9 可以直接单测,不需要起整个应用。
- 引擎故障时 fail-safe:降级为「先备份 + 告警」,绝不无备份放行。
- 自身数据目录:$DSH_HOME/agent-guard/(可配置)。若沙箱拒绝写入,会降级到临时目录 并告警;两者都不可写则进入「仅内存」模式并明确提示记录会丢失——降级可以,静默降级不行。
- 日志不记命令原文,只记 12 位摘要;命令、消息正文、凭据都不进日志。
- 回滚只给人工说明,不给自动回滚命令:自动改写应用私有存储正是事故的形态。

兼容性

| 组件 | 已验证版本 |
| DSH | 0.1.7-rc.2 |
| Node | ≥ 22(使用内建 node:zlib 的 zstd;实测 24.x) |
| 平台 | Windows 已验证;核心只依赖路径字符串,Windows 专有探测在其他平台降级为「不支持」 |
| 运行时依赖 | 无 |

常见问题

为什么不直接帮我修会话数据? 因为那就是事故本身。DSH 的存储是应用私有的:会话目录名是工作区路径的编码键,注册表由应用自己持有并重写。安全做法是物理的(把目录搬回去)或在应用内完成(让用户重新登记工作区),而不是由第三方改写私有存储。

它说 43 条记录未登记,我丢对话了吗? 没有。那些是内部 origin: "subagent" 记录,DSH 有意不登记、不显示。
报告永远在计数旁边给出这个分解,就是为了让这个数字不会被误读。

会联网吗? 不会。本包没有任何 HTTP 客户端,没有遥测,没有统计。想分享报告时用 --redact。

身份头读不出来会怎样? 会被记为 unreadable / incomplete,并且排除在对话/记录统计之外。
不确定永远不会被当成"不存在"。

怎么手动清理插件数据? 确认不再需要回滚点后,自行删除 $DSH_HOME/agent-guard/ 即可。插件不会替你删。

文档

- VERIFY.md — 每条 API 结论与一手证据,以及已知缺口
- IMPLEMENTATION-PLAN.md — 范围、模块划分、验收判据
- docs/incident-redacted.md — 事故复盘(脱敏,只讲机制)

完整设计文档与发布/验收报告刻意不放进本仓库:它们是绑定了作者本机环境的工作文档。
使用本插件所需的关键结论都在 VERIFY.md 里。

许可证

MIT。
预览分享卡片长什么样 ↓a5557/dsh-agent-guard
分享 · DeepSeek Harness Hub
a5557/dsh-agent-guard

读取一手证据,拦截越权写入并留回滚点

回到顶部