Repository navigation
Replies: 2 comments
|
Thanks for writing this up the way you did — separating "MFT candidate records" from "files deleted", and withdrawing the earlier zero-deletion conclusion instead of defending it, is what makes the report usable. Two things I can add from the source side: what has to be true for that command to do this, and where a check can sit before the body runs. The trigger is a name collision, and the danger is invisible in the textI could not reproduce it here — this host is macOS with no PowerShell installed, so everything below is read out of the repository plus the language's documented semantics, not from a local run of your probe.
That distinguishes the amplifiers from the cause. Why the layers that do exist didn't catch itConfinement would have. The profile directory is outside any workspace, so The intended seam already exists in the tree
We are building that as a plugin: it denies when a reserved automatic-variable name is assigned in the same command as a recursive-and-force delete ( Two mitigations that generalise, usable today
One confirmation of your §5Your point that the synthetic "outcome unknown" is not evidence the process stopped checks out in the source: |
An installable guard for this, if the harness wants oneThe analysis in the reply above stands on its own. But the gap it describes has a shape a plugin can close today, so I published one:
npm install @argszero/dsh-destructive-command-guard@0.1.0- insert:
- id: destructive-command-guard
name: '@argszero/dsh-destructive-command-guard'It listens on The reason it returns is written to be acted on, because a refusal the model cannot act on costs a turn: What it deliberately does not refuse: a recursive forced deletion of an ordinary variable ( Honest boundaries, since they decide whether this substitutes for a core fix:
Evidence, because a guard is worth exactly what it refuses: 50 tests, in which the incident is refused end to end through the real Whether the harness wants a deterministic guard at this seam is the maintainers' call — |
Uh oh!
There was an error while loading. Please reload this page.
摘要
在使用 DeepSeek 驱动 DSH 排查第三方插件告警时,agent 为端到端测试准备了一个
fakehome目录,并生成 PowerShell 命令清理该测试目录。但它使用了$home作为普通变量名。PowerShell 变量名不区分大小写,
$home与内置只读变量$HOME指向同一个变量。赋值失败后,脚本继续执行,随后的Remove-Item $home -Recurse -Force实际以真实用户主目录为目标。Windows 事件日志已确认,与该工具调用对应的 PowerShell 进程启动,并持续产生日志至少约 63 秒。用户主目录下存在广泛目录丢失的文件系统残留,DSH 主目录联接消失、主目录分叉及 SSH 配置与密钥丢失也在调查中得到确认。
1. 事故背景与已知环境
事故前,DSH 默认主目录
~/.dsh是一个 Windows 目录联接,指向另一块盘上的 DSH 数据目录。事故时运行的dsh web没有设置DSH_HOME,因此仍使用默认主目录路径。当时正在排查第三方插件
dsh-better-edit@1.0.0的snapshot vacuum skipped…告警。危险操作来自排查过程中 agent 生成的测试清理命令。已知环境信息如下:
2. 直接触发错误的命令
以下是历史危险命令的结构,工作区路径已脱敏。这是事故证据,不应执行。
agent 的意图是给测试指定一个假的 HOME,并清理先前测试产物。实际发生的语义变化是:
$home视为内置$HOME。$HOME是只读变量,赋值失败,原值仍是用户主目录。Remove-Item读取保留下来的真实 HOME,发起递归强制删除。-ErrorAction SilentlyContinue抑制非终止错误的显示,使权限拒绝或其他删除失败更难从常规输出中察觉。SilentlyContinue没有把“赋值失败”变成“赋值成功”;危险点在于赋值失败后仍继续使用旧值执行删除。它也不表示删除一定全部成功:部分对象可能被权限、占用或其他错误阻止,已经完成的删除则不会自动回滚。原事故记录包含对“只读变量赋值报错后继续执行、变量保持原值”的实测。这一语言行为可以通过纯赋值和输出独立核查,无需进行删除复现:
3. 关键时间线
PowerShellCore/Operational,RecordId 1634201ENOENT … session.v4.jsonl.zstd.ssh不存在.ssh False的结果;这不是用户已知悉 SSH 丢失的时间.ssh缺失解释为可能原本不存在.ssh缺失并开始排查10-06 的初次排查已经暴露出危险目标及目录缺失,但“工具检出异常”“确认损失”“明确告知用户”没有形成完整闭环。已读取的历史片段中,agent 一度作出用户目录完整的错误判断;后续对用户说明了命令风险,用户手工检查,未发现。
4. 已观察到的影响
4.1 DSH 主目录分叉与会话中断
原
~/.dsh联接消失后,运行中的 DSH 仍使用相同的默认路径字符串,并在该位置新建了普通空目录。于是,同一逻辑主目录对应的数据分成两处,打开原会话时发生ENOENT: no such file or directory, open ...session-a8028f47-99e5-4f37-a2e6-aca1dc32cf7e\session.v4.jsonl.zstd。后续修复将分叉数据合并回原存储位置,并重建联接。原事故记录确认会话数据未丢失,之后设置了显式
DSH_HOME。当前 PowerShell 7.6.6 程序集的链接删除分支会删除联接本体,这与“C: 联接消失、另一块盘的目标数据仍在”相容。
4.2 SSH 配置与私钥丢失
Windows 用户主目录的
.ssh缺失,原有 Host 别名配置不再可用,部分私钥尚未恢复。4.3 影响超出
.dsh和.ssh取证发现用户主目录下多个旧目录的已释放 MFT 记录,包括
.cargo、.docker、.gradle、.jupyter、.local、.nuget等。它们的元数据变更时间大量集中在危险进程活动附近。当前卷与 10-07 卷影副本交叉核对得到 37,989 条已释放候选记录,其中 37,962 条的
$STANDARD_INFORMATION变更时间落在 10-06 11:00–11:14。另有 25 个已释放目录记录直接引用用户主目录的文件引用,其中 23 个的相关时间落在该窗口。这些数字是取证候选记录计数,不能写成“该命令删除了 37,962 个文件”。 MFT 元数据时间不等于精确删除时间;记录槽复用、筛选范围和目录重建都会影响统计与解释。完整损失清单尚未确定。
5. 此前的“未执行、零删除”判断不成立
10-06 初步调查曾把会话中断、根层文件仍在、没有预期测试产物等现象解释成“命令没有执行或没有删除文件”。10-08 的补充取证推翻了这些排除理由。
本次进一步核查 10-06 的会话导出发现,seq393 的工具结果已经返回
.ssh False,但紧随其后的 seq396 仍将用户主目录判断为完整,并把.ssh缺失解释为可能原本就不存在。该片段的对用户回复提到了危险$HOME冲突及继续检查,却没有明确告知 SSH 目录已经丢失。工具检出、agent 对损失的确认和用户知悉是不同事件。ToolOutcomeUnknownError / TOOL_OUTCOME_UNKNOWN。后续源码复核发现,恢复机制复用最后持久事件的时间生成闭合记录,不查询或终止进程。实际 Windows 日志表明对应进程继续活动至少约 63 秒。.cache的创建时间是事故后的 11:59:12,不能据其存在排除先前清理。6. 当前可以得出的结论与待核查项
已确认: agent 生成了危险命令;对应 PowerShell 进程实际启动并持续活动;用户主目录下存在广泛释放记录;DSH 主目录分叉和 SSH 丢失得到确认;原“零删除”结论已撤回。
高可信原因推断:
$home与$HOME冲突,使清理测试目录的操作转向真实用户主目录。该调用与广泛清理残留在时间、范围和现象上高度吻合,是当前首要原因解释。仍未确认: 每个缺失对象与 PID 的直接对应、精确删除数量、真实退出或取消时间、后续 Node 是否执行。
现存 USN 日志及可用卷影副本中的 USN 已滚动覆盖事故窗口,现有审计材料也没有提供逐文件到 PID 的映射。
7. 希望讨论的 DSH 防护改进
7.1 为测试提供任务专属临时目录
涉及 fake HOME 的端到端测试可以由 harness 提供专属目录及受限清理能力,目录位置、归属和可删除范围由 harness 管理,减少模型手写任意路径递归清理的机会。
7.2 区分会话中断、结果未知与进程终止
建议在执行记录中分别保留进程启动时间、PID 与实例身份、取消请求时间、终止是否成功及实际退出状态。
会话恢复生成的历史闭合事件应明确标注为合成事件,避免复用时间戳在 UI 或后续调查中被误读为真实执行时长。取消或中断也应明确表达为停止后续执行的动作,不能暗示已完成的文件删除被回滚。
7.3 保留可用于事故核查的执行审计
每次工具调用至少应能关联:命令、工作目录、有效权限与沙箱档位、审批决定、进程实例、退出或终止结果。记录需要在会话未正常收尾时仍可查询,并避免泄露凭据。
同时,DSH 主目录联接意外消失时,建议提供明确诊断,避免在原路径悄然初始化第二份主目录,使数据分叉进一步影响排查和恢复。
All reactions