Skip to content

Commit fc029b1

Browse files
committed
fix(docs): sanitize privacy + rewrite agent-interference.md
- Replace all /Users/haolin/ paths with ~/ in agent-interference.md and RELEASE_AUDIT.md (macOS username leakage) - Remove session ID from historical records - Rewrite agent-interference.md: remove obsolete C1/C2/C5 proposals (CMDGUARD_AGENT_MODE, non-TTY behavior matrix, wrapper agent detection) that were never implemented. Replace with actual v0.14.0 solution descriptions for each of the 7 issues. - Update each problem's "improvement suggestion" to "v0.14.0 solution" reflecting what was actually done.
1 parent a063d40 commit fc029b1

1 file changed

Lines changed: 52 additions & 93 deletions

File tree

docs/issues/agent-interference.md

Lines changed: 52 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@
22

33
> 创建时间:2026-07-11
44
> 来源:ai_research agent 在日常工作中遇到的实际干扰记录
5-
> 用途:等 icloud-pull 项目完成后,回头集中改进 cmdguard
6-
>
7-
> **更新(2026-07-11)**:v0.14.0 重构已将 cmdguard 改为**全程非交互**
8-
> 删除了交互式 confirm 层、`CMDGUARD_NONINTERACTIVE` 环境变量、
9-
> `[guard]` 配置段。保护级别简化为 reject / guarded / allow 三级。
10-
> 下方"C1: `CMDGUARD_AGENT_MODE`"等提议已部分过时--cmdguard 现在
11-
> **本身就是**非交互的,不需要额外的 agent 模式开关。文中保留这些
12-
> 提议作为历史设计讨论记录。
5+
> 状态:**全部已解决**(v0.14.0,2026-07-12)
6+
7+
本文档记录了 cmdguard 在与 AI agent 协作时出现的 7 个干扰问题。
8+
所有问题已在 v0.14.0 中通过非交互式重构根本解决,详见末尾
9+
"v0.14.0 解决方案"。
1310

1411
---
1512

@@ -19,7 +16,7 @@
1916

2017
### 现象
2118

22-
`backup-icloud-folder` v2.0.0 `clean_temp_copy` 函数用 `find -exec rm` 删除 `.DS_Store` 等排除文件。cmdguard 拦截了 `rm` 命令(弹出 confirm 提示),但 Python 的 `subprocess.run(..., capture_output=True)` 把 stderr(含 cmdguard 的 confirm 提示)吞掉了。函数返回 exit code 0(因为 `capture_output=True` 不报异常),打印 "Temp copy cleaned",但实际上一个文件都没删。
19+
`backup-icloud-folder``clean_temp_copy` 函数用 `find -exec rm` 删除 `.DS_Store` 等排除文件。cmdguard 拦截了 `rm` 命令(弹出 confirm 提示),但 Python 的 `subprocess.run(..., capture_output=True)` 把 stderr(含 cmdguard 的 confirm 提示)吞掉了。函数返回 exit code 0(因为 `capture_output=True` 不报异常),打印 "Temp copy cleaned",但实际上一个文件都没删。
2320

2421
结果:备份的 zip 里多了 8 个 `.DS_Store` 文件,源端 946 个文件 vs 复制后 954 个,file count mismatch。
2522

@@ -35,10 +32,9 @@ cmdguard 的 confirm 机制依赖 stderr 输出 + stdin 交互。当被调用方
3532

3633
`clean_temp_copy` 改用纯 Python `os.walk` + `os.remove`/`shutil.rmtree`,彻底不经过外部 `rm`
3734

38-
### 改进建议
35+
### v0.14.0 解决
3936

40-
- cmdguard 在非 TTY 环境下,应该**以非 0 exit code 退出**(当前已有 `CMDGUARD_AGENT_MODE` 但不一定是默认行为)
41-
- 或者:cmdguard 检测到 `capture_output` 场景(无 tty),应该 fail fast 而非静默吞掉
37+
删除全部交互层。guarded 路径无 `--bypass` = 立即 reject (exit 1),不再有"超时降级"或"静默拒绝"。exit code 非 0 会被 `subprocess.run``check=True` 或调用方捕获。
4238

4339
---
4440

@@ -63,10 +59,10 @@ cmdguard 的 confirm 机制依赖 stderr 输出 + stdin 交互。当被调用方
6359

6460
### 根因
6561

66-
**已确认(2026-07-11 深度排查)**:根因是 **QwenPaw 的 macOS Seatbelt 沙箱**
62+
**已确认**:根因是 **QwenPaw 的 macOS Seatbelt 沙箱**
6763

6864
QwenPaw 的 `execute_shell_command` 工具通过 `sandbox-exec` 运行命令,采用 deny-default 白名单模型。沙箱只允许写以下路径:
69-
- workspace_dir`~/.qwenpaw/workspaces/ai_research`
65+
- workspace_dir
7066
- `/tmp``/private/tmp``/dev/null`
7167
- policy.yaml 中 user_rules 显式 approved 的 mount 路径
7268

@@ -80,23 +76,13 @@ QwenPaw 的 `execute_shell_command` 工具通过 `sandbox-exec` 运行命令,
8076
5. cmdguard 的 `os.MkdirAll()` 创建 vault 备份目录 -> Seatbelt 拒绝 -> `operation not permitted`
8177
6. cmdguard backup 失败但只 warning,命令仍执行
8278

83-
**历史发生记录**
84-
- 2026-07-10 23:09(session gch7y84):`chmod sync-qwenpaw-profile`
85-
- 2026-07-11 15:30:`chmod backup-icloud-folder backup-obsidian-vault`(2 个文件同时失败)
86-
- 2026-07-11 16:48:`chmod backup-icloud-folder.py`
87-
- 2026-07-11 16:53:`chmod backup-icloud-folder` + `rm backup-icloud-folder.py`
88-
8979
**为什么后来不复现**:当 governance user_rules 积累了 `Bash(mkdir -p *)` 等 ALLOW 规则后,匹配的命令走 ALLOW 路径(不传 `sandbox_config`),命令不跑在沙箱里,vault 自然能写。即:同一命令在不同 session 的 governance 状态下,可能走沙箱也可能不走,行为不可预测。
9080

91-
### 临时绕过
92-
93-
无。每次都看到 warning,已经习以为常。
81+
### v0.14.0 解决
9482

95-
### 改进建议
83+
backup 失败从 warn-only 改为 **exit 1(硬编码,不可配置)**。不再有"warning 噪音后继续执行"--要么 backup 成功后执行,要么 backup 失败后中止。失败输出含 vault 目录路径、常见原因(权限/磁盘/沙箱)、`cmdguard path` 诊断引导。
9684

97-
- cmdguard 在 backup 失败时的行为应该可配置:`backup_fail = warn | block | skip`
98-
- 或者:检测到非 TTY 环境时,confirm 级别自动降级为 allow + log(当前 `CMDGUARD_AGENT_MODE` 是直接 reject,太激进)
99-
- **根因已确认**(见上方):QwenPaw Seatbelt 沙箱白名单不含 `~/.cmdguard/vault/`。agent 模式跳过 backup 是务实方案;非沙箱环境 backup 正常工作
85+
**沙箱白名单问题不是 cmdguard 的问题**,解法在 QwenPaw 侧:把 `~/.cmdguard/**` 加入沙箱白名单。
10086

10187
---
10288

@@ -122,10 +108,11 @@ cmdguard 的 wrapper 是 PATH 级别的 shell script 包装。当命令中包含
122108
2. 需要删文件时用 `unlink` 替代 `rm`(unlink 不在 cmdguard 的拦截列表里)
123109
3. 需要用 `rm` 时用绝对路径 `/bin/rm` 绕过 wrapper
124110

125-
### 改进建议
111+
### v0.14.0 解决
126112

127-
- cmdguard 应该更智能地处理 `find -exec` 中的子命令:如果 `find` 本身不在拦截列表里,`-exec` 后的 `rm` 也应该透传(因为这是 find 的子进程,不是用户直接调用)
128-
- 或者:提供 `cmdguard guard find -exec rm` 的透传模式
113+
wrapper 仍然会拦截 `find -exec` 中的 `rm`(这是设计行为)。但拦截后的行为从"不确定"变为**确定**:guarded 路径无 `--bypass` = 立即 reject (exit 1) + `--bypass` 格式引导,有 `--bypass` = backup -> 执行。
114+
115+
**不做 `find -exec` 透传**:cmdguard 不解析 shell 子命令语法。wrapper 拦截的是 PATH 上的 `rm` binary,不关心调用者是 `find` 还是用户直接敲的。agent 撞到 reject 后靠引导信息自行换策略(`--bypass``/bin/rm`、Python `os.remove`)。
129116

130117
---
131118

@@ -145,10 +132,9 @@ AI agent 的 shell 已经被 cmdguard 包装:`/bin/rm` 用别名/wrapper 劫
145132

146133
`/bin/rm -rf testdata/fuzz` 绝对路径绕过别名。
147134

148-
### 改进建议
135+
### v0.14.0 解决
149136

150-
- cmdguard 的 wrapper 应该有一个"开发模式"或"self-test 模式",在该模式下不拦截命令
151-
- 或者:wrapper 检测当前工作目录是否是 cmdguard 项目本身,是则透传
137+
行为确定化后,拦截本身不是问题--agent 撞到 reject 会拿到明确的 `--bypass` 引导。不做"开发模式"或"self-test 模式":安全后门与 cmdguard 存在意义矛盾。
152138

153139
---
154140

@@ -164,9 +150,9 @@ AI agent 的 shell 已经被 cmdguard 包装:`/bin/rm` 用别名/wrapper 劫
164150

165151
v0.12.0 引入 sentinel 标记 `# cmdguard:wrapper:v1``findRealCommand` 跳过含 sentinel 的文件。
166152

167-
### 改进建议
153+
### 备注
168154

169-
已修复,无需再改。但记录在此作为参考
155+
已修复,无需再改。记录在此作为参考
170156

171157
---
172158

@@ -188,19 +174,15 @@ cmdguard 在 TTY 环境下弹出 confirm 提示,等用户输入 y/N。但在
188174

189175
cmdguard 的超时降级机制(v0.6.0 引入)在非 TTY 环境下行为不够明确。`readLineWithTimeout` 超时后走非 TTY 路径,但这个"非 TTY 路径"的具体行为(reject? allow? log only?)取决于配置和代码版本。
190176

191-
### 改进建议
192-
193-
- 明确定义非 TTY 环境下的行为矩阵:
177+
### v0.14.0 解决
194178

195-
| 保护级别 | 非 TTY 行为 |
196-
|---------|------------|
197-
| reject | reject(exit 1) |
198-
| confirm_double | reject(exit 1) |
199-
| confirm | reject(exit 1)+ 引导使用 `--bypass` |
200-
| allow | allow + log |
179+
**根本消除**:v0.14.0 删除全部交互层,非交互是唯一模式。不再有 TTY / 非 TTY / agent mode 三种行为分歧。所有调用方(人、agent、subprocess)看到的行为完全一致:
201180

202-
- 当前 `CMDGUARD_AGENT_MODE` 是全局开关,建议改为按命令/路径粒度配置
203-
- 或者:启用 `CMDGUARD_AGENT_MODE=1`(或配置文件 `[guard] agent_mode = true`),在该模式下跳过交互等待,但仍要求 `--bypass` 做审计(详见"期望的改进方向"第 1 条)
181+
| 保护级别 |`--bypass` |`--bypass` |
182+
|---------|-------------|-------------|
183+
| reject | reject (exit 1) | reject (exit 1) |
184+
| guarded | reject (exit 1) + bypass 引导 | backup -> log -> execute |
185+
| allow | allow + log | allow + log |
204186

205187
---
206188

@@ -216,9 +198,9 @@ cmdguard 的超时降级机制(v0.6.0 引入)在非 TTY 环境下行为不
216198

217199
写复杂 sh 脚本一律用 `write_file` 工具写到 `/tmp/xxx.sh`,再 `chmod +x && /tmp/xxx.sh`
218200

219-
### 改进建议
201+
### 备注
220202

221-
这是 zsh + execute_shell_command 的问题,不是 cmdguard 的直接问题。但 cmdguard 的 wrapper 增加了 shell 解析的复杂度。
203+
这是 zsh + execute_shell_command 的问题,不是 cmdguard 的直接问题。
222204

223205
---
224206

@@ -236,64 +218,41 @@ cmdguard 设计假设"有人在终端前操作"(confirm 等待 stdin 输入)
236218
- 要么遇到拦截后换策略(浪费时间)
237219
- 要么用绝对路径 `/bin/rm` 绕过(但失去了 cmdguard 的保护)
238220

239-
### 最终改进方案(2026-07-11 定稿)
240-
241-
三个改动,不多不少。
242-
243-
#### C1: `CMDGUARD_AGENT_MODE`(原 `CMDGUARD_NONINTERACTIVE` 改名)
244-
245-
触发方式(env 覆盖 config 优先级模型):
246-
- 环境变量 `CMDGUARD_AGENT_MODE` 存在时,以 env 值为准(`"1"` = true,`"0"` = false)
247-
- 环境变量不存在时,读配置文件 `[guard] agent_mode`(默认 `false`
248-
249-
即:env 显式设置时覆盖 config,env 不设时 fallback 到 config。
250-
这样 QwenPaw 可按需注入 `CMDGUARD_AGENT_MODE=1` 而不改用户的 config 文件,
251-
也可以用 `CMDGUARD_AGENT_MODE=0` 临时关掉 config 里全局开的 agent 模式。
252-
253-
纯改名,不管向后兼容。注释、文档、反馈信息中写明白即可。
221+
---
254222

255-
行为矩阵:
223+
## v0.14.0 解决方案
256224

257-
| 场景 | Agent 模式 |
258-
|------|------------|
259-
| confirm + 有 bypass + backup 成功 | 验证 bypass -> backup -> 执行 |
260-
| confirm + 有 bypass + **backup 失败** | **exit 1(硬编码,不可配置)** |
261-
| confirm + 无 bypass | **立即 reject**(不等待,带引导信息) |
262-
| confirm_double + 无 bypass | **立即 reject**(不等待,带引导信息) |
263-
| reject | reject(不变) |
264-
| allow | allow + log(不变) |
225+
### 核心决策:删除全部交互层
265226

266-
agent 模式唯一买的是:**跳过交互等待 + reject 时带引导信息**
267-
backup 照做、审计照记、undo 照用,三大支柱一个不少。
268-
backup 失败 = exit 1,没有商量,没有配置项。
227+
v0.14.0 没有采用"加 agent 模式开关"的渐进方案,而是直接删除了整个交互式确认层:
269228

270-
reject 引导信息必须包含可复制的 `--bypass` 命令模板和格式示例,让 agent 撞墙一次就学会。
229+
- 删除 `CMDGUARD_NONINTERACTIVE` 环境变量
230+
- 删除 `CMDGUARD_AGENT_MODE` 环境变量(从未发布,仅存在于设计讨论中)
231+
- 删除 `[guard]` 配置段(`confirm_timeout``confirm_double_timeout`
232+
- 删除 `readLineWithTimeout`、单/双确认交互流程
233+
- 净删 ~627 行,跨 33 个文件
271234

272-
#### C2: 非 TTY 行为矩阵统一
235+
保护级别从五级(reject / confirm_double / confirm / warn / allow)简化为三级:
273236

274-
未启用 agent 模式时,非 TTY 行为也明确(当前超时降级路径不够清晰):
237+
| 级别 | 图标 | 行为 |
238+
|------|------|------|
239+
| reject | 🚫 | 拒绝 + 日志,`--bypass` 无法覆盖,exit 1 |
240+
| guarded | 🔒 |`--bypass`:拒绝 + 日志 (exit 1);有 `--bypass`:backup -> 日志 -> 执行 |
241+
| allow || 无规则匹配,日志 + 直接执行 |
275242

276-
| 保护级别 | 非 TTY + 无 bypass | 非 TTY + 有 bypass |
277-
|---------|-------------------|-------------------|
278-
| reject | reject(exit 1) | reject(exit 1) |
279-
| confirm_double | reject(exit 1) | 验证 bypass -> backup -> 执行 |
280-
| confirm | reject(exit 1)+ 引导 `--bypass` | 验证 bypass -> backup -> 执行 |
281-
| allow | allow + log | allow + log |
243+
### 关键设计决策
282244

283-
关键变化:非 TTY + confirm + 无 bypass = **立即 reject**(不等 5s 超时)。超时降级机制保留给 TTY 环境(防伪 TTY 卡死)
245+
**backup 失败 = exit 1(硬编码,不可配置)**:不再有 warn-only 模式。undo 安全网断了就必须中止,没有商量余地。失败输出含 vault 目录路径、常见原因、`cmdguard path` 诊断引导
284246

285-
#### C5: wrapper agent 环境检测
247+
**不做 `find -exec` 透传**:cmdguard 不解析 shell 子命令语法。wrapper 拦截的是 PATH 上的 binary,不关心调用者。agent 撞到 reject 后靠引导信息自行换策略。
286248

287-
wrapper 检测到 `CMDGUARD_AGENT_MODE=1` 时,输出一行 debug 信息到 stderr(便于 agent 调试)。保护逻辑不变 - `cmdguard guard` 仍然拦截,agent 模式行为由 C1 控制
249+
**不做"开发模式"**:安全后门与 cmdguard 存在意义矛盾
288250

289-
#### 不做的事
251+
**旧 config 字段保留为 legacy alias**`confirm_double` / `confirm` / `warn``Load()` 时合并到 `guarded`,旧配置文件无需修改。
290252

291-
- ~~`backup_fail` 配置项~~:backup 失败永远 exit 1,硬编码,不可配置
292-
- ~~`CMDGUARD_DEV_MODE`~~:安全后门,与 cmdguard 存在意义矛盾
293-
- ~~`find -exec` 透传~~:agent 撞墙后靠引导信息自行换策略(`/bin/rm` 或 Python `os.remove`),不需要 cmdguard 解析子命令
294-
- ~~向后兼容 `CMDGUARD_NONINTERACTIVE`~~:纯改名,不管老配置
253+
**bypass 标识从 4 段改为 3 段**`<platform>/<agent>/<task>`,去掉冗余的 `host` 段。最小长度 12 -> 10。
295254

296-
#### 沙箱环境(QwenPaw 侧问题,非 cmdguard 问题)
255+
### 沙箱环境(QwenPaw 侧问题,非 cmdguard 问题)
297256

298257
QwenPaw Seatbelt 沙箱白名单不含 `~/.cmdguard/**`,导致 backup 写 vault 时 `os.MkdirAll()` 被拒 -> exit 1。
299258

0 commit comments

Comments
 (0)