tracking: 安全面闭环——已落地机制的绕过(4 项)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 8/100
Direzione di ricerca
This is a tracking issue for four sandbox and auth bypasses; no code is to be written here. Start from the sub-issues it links (#150, #152, #155, #167) and their cited files, such as src/tools/transport_layer/transport.rs and src/tools/dispatch.rs. Each sub-issue needs its own design and PR, and the cross-cluster file overlaps need coordination first.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
优先级 P0 · 依赖:无 · 类型:tracking(簇设计入口,不产出代码;子单全部落地后关闭)
记号:正文中的「不变量 #N」指.dev/AGENTS.md编号,不是 issue 编号;#150这类才是 issue。本簇子单头部写depends-on: #N表示落地串行(同文件/同契约),不会与语义阻塞混淆。
基线 HEAD = 14bac15a(子单同基线)。本单来自一次全仓审计:先按根因归簇,再拆成可独立验收的子单。
共同根因
这四条的共同点是:机制已经落地,但能被绕过,或者声明与执行面不一致——比「功能缺失」更危险,因为运维会以为它生效了。
- 凭据面(#150):#89 的清洗只作用于子进程的 env 块(
src/tools/transport_layer/transport.rs:803-810的scrub_child_env),而exec_shell用Command::new("/bin/sh")由服务进程直接 spawn(:897-925),shell 的$PPID就是 recursive 本身;服务进程自己的环境没有任何清理(非测试代码里remove_var/prctl/PR_SET_DUMPABLE零命中),于是cat /proc/$PPID/environ就能拿走 #89 明确要拦下的全部 key。注意:remove_var修不了这个(glibc unsetenv 不改 execve 时的原始副本),必须靠PR_SET_DUMPABLE=0或干净环境 re-exec。 - 沙箱面(#155):
resolve_within_any只对已存在路径做 canonicalize 复查(src/tools/dispatch.rs:345-374),不存在的路径走词法starts_with并原样返回;WriteFile(src/tools/execution/fs.rs:567、648-702)与EditTool(src/tools/execution/edit.rs:462)不做祖先 canonicalize → 工作区里一个docs -> /外部的链接即可写出沙箱。我已实测:同一条链接,写已存在文件被拒、写新文件被放行。 - 归属面(#152):#85 给
/sessions/*建了归属校验(src/http/auth.rs:80-88may_access_session),但agui_run(src/http/handlers.rs:2581-2584)与agui_cancel(:2902-2905)不取身份,线程键只是blake3(threadId)(src/agui_session.rs:60-63);#147 落地后load_thread_seed会把 A 的完整历史播种进 B 的 run(src/http/agui.rs:730),取消也按原始 threadId 索引(handlers.rs:2906-2910)。 - 隔离声明面(#167):
DockerToolSetProvider在 Docker 不可用时只 warn 并保留宿主 Bash(src/tools/transport_layer/docker_provider.rs:79-83),sandbox_mode()仍返回Container(:88-90);遗留docker_sandbox.rs的容器用默认网络、root、无 cap_drop(:42-83),超时不杀 exec(:123-133),Drop无条件tokio::spawn(:141-155)。
子单与依赖
| 子单 | 依赖 | 范围 |
|---|---|---|
| #150 | 无 | 启动后 PR_SET_DUMPABLE=0(或干净环境 re-exec)+不再把凭据留在进程环境;补 cat /proc/$PPID/environ 回归 |
| #167 | depends-on: #150 | Docker 不可用即 fail-closed(或显著降级标志);遗留实现复用 secure_host_config、超时真杀、Drop 用 try_current |
| #152 | 无 | agui_run/agui_cancel 取 AuthIdentity;线程元数据记 owner/tenant;active_runs 用命名空间化键 |
| #155 | 无 | 不存在路径取最长已存在祖先 canonicalize 后复查;返回 canonical 路径;补 O_NOFOLLOW/openat 方向 |
落地思路
- #150 的修法要选对:
remove_var只是减少泄漏面,/proc/pid/environ仍是 execve 时的副本;主修复是 Linux 启动即prctl(PR_SET_DUMPABLE, 0)(同 uid 子进程读不到),或启动时用干净环境 re-exec。验收必须在子进程内读/proc/$PPID/environ,不能只断言Command::env。 - #155 返回 canonical 路径(而非原
abs_joined),缩短 check→write 的 TOCTOU 窗口;单测要覆盖「不存在的新文件」与「已存在的文件」两种,因为现有tests/invariants/sandbox.rs:71只覆盖「链接本身就是目标」。 - #152 与 #85 的语义保持一致:
admin仍可访问,其余按(subject, tenant)严格相等;agui_active_runs的键必须与线程键同源,否则改名后取消会失效。 - #167 首选 fail-closed:宁可不提供 Bash,也不要「报告 Container 却跑在宿主」。
- 与 C 簇在
transport.rs、run_background.rs重叠(C 的 #165/#177 也在 kill/环境路径上),与 B 簇在handlers.rs重叠 —— 按全局顺序错开。
簇级验收
- 子进程经
RunShell执行cat /proc/$PPID/environ,输出不含任一敏感变量名/值; root/docs -> /tmp/outside时,Write docs/new.txt(不存在)与docs/exists.txt(存在)都被拒,沙箱外无文件产生;- 两个不同 subject 的凭据下,B 访问 A 的 threadId(
/agui与/agui/{id}/cancel)得 403,A 的transcript.jsonl未被追加; - Docker 不可用时构建结果不是「宿主 Bash + Container 报告」。
边界
不含默认档多租户数据面互通(#86)、不含 HTTP 侧人审闭环(#88)、不含权限判定绕过(B 簇)——本簇只收「已有机制被绕过」与「声明与执行面不一致」。
实施顺序、PR 拆分与验收矩阵(补充 · 2026-10-10)
| 波 | 子单 | 理由 |
|---|---|---|
| W1 | #150 | 凭据面影响面最大(默认档即中招),且它要动 transport.rs 的启动/执行路径 |
| W2 | #167 | 同目录(transport_layer/),且 fail-closed 的判断要靠 #150 之后的环境模型稳定下来 |
| 并行 | #152、#155 | 分别只动 http/handlers.rs+agui* 与 tools/dispatch.rs,互不冲突,可与上面并行 |
跨簇文件重叠(必须错开,不能同时派工)
| 文件 | 本簇子单 | 另一簇子单 |
|---|---|---|
src/tools/transport_layer/transport.rs |
#150(启动期加固 + exec_shell 环境) | C 簇 #165(process_group 在同一个 exec_shell 里) |
src/tasks/run_background.rs |
#150(宿主档同父进程的证据点) | C 簇 #164/#177 |
src/http/handlers.rs |
#152(agui_run/agui_cancel) |
B 簇 #151/#154/#182(同一文件的权限路径) |
PR 拆分:一子单一 PR。#150 的 PR 必须先于 C 簇 #165(两者都在 exec_shell 里改 spawn 前的设置,先合 #150 可避免 #165 之后 rebase 冲突)。
簇级验收 → 测试映射
| 验收 | 落点 |
|---|---|
① cat /proc/$PPID/environ 不含敏感变量 |
tests/issue89_parent_environ.rs(#[cfg(target_os = "linux")],测试进程内先调 harden_process()) |
| ② 链接目录写新文件/已存在文件都被拒 | tests/invariants/sandbox.rs 新增两例(现有 :71 只覆盖"链接即目标") |
| ③ B 访问 A 的 threadId 得 403 且 A 的 jsonl 未变 | src/http/handlers.rs 的 #[cfg(test)] 或 tests/ 新增 HTTP 级用例(两个不同 AuthIdentity) |
| ④ Docker 不可用时不出现"宿主 Bash + Container 报告" | src/tools/transport_layer/docker_provider.rs 单测(注入失败的 docker 路径) |
共同陷阱
- 三处"未归属/未配置"的语义必须与既有实现一致:#85 的规则是"没有归属信息的会话只有 admin 可达"(
cold_load.rs:232-236),不要发明第二套规则。 - 单用户部署(未启用 auth)不能被打坏:
Extension<AuthIdentity>在 auth 中间件关闭时不存在,#152 必须把"取不到身份"当作"无归属模型、沿用旧行为",而不是 403。 - 本簇全部是"机制已存在但被绕过",因此每个 PR 都要包含原始机制仍然生效的对照断言(避免修 A 绕过时把 B 路径关掉)。
- Lingua principale
- Rust
- Stelle
- 4
- Fork
- 0
- Merge medio
- 5h 32m
- PR unite (30g)
- 7
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Nessun modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di jeffkit/recursive
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
jeffkit/recursive#159 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 15/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 15/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 12/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 12/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di jeffkit/recursive
Issue simili
-
[Bug]: Web chat input doesn't regain focus after a reply finishesForse già presa @GaijinSystems l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
zeroclaw-labs/zeroclaw#11658 ·
I maintainer di solito rispondono entro 2 giorni
-
good first issue help wanted
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
NuSkooler/enigma-bbs#907 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno