fix: macOS audit — process cleanup, credential backups and live script wiring - #17
Merged
Merged
Conversation
Lion-1209
reviewed
Sep 14, 2026
Lion-1209
left a comment
Owner
There was a problem hiding this comment.
感谢这份高质量审计。审查结论:两个代码修复(POSIX 进程组树杀、凭据备份权限)与 Live 脚本契约测试全部通过,远端 CI 7/7 绿,commit 拆分与 CHANGELOG 同步符合仓库纪律。
合并前一个调整:审计产物不入库——请从 PR 中移除 docs/audit/ 目录(114 个证据文件)和根目录的 PROJECT_REVIEW_2026-09-10.md。原因:sdist 白名单含 /docs,证据会随发版上 PyPI,与白名单"不发布内部审计材料"的意图相悖;报告属于时点快照,其结论已由 CHANGELOG [Unreleased] 承载,全文留在本 PR 描述中即可(GitHub 永久保存,本 PR 即为存档处)。请把报告全文补进 PR 描述,证据可留 gist 链接(可选)。
另外请把报告中的待解决发现各开一个 issue 跟踪:
- web_fetch 完整缓存后才限长
- TUI gate 异常装配默认放行
- OpenAI 流式 usage 被适配器重复累加(影响
/cost真实性,建议优先)
其余一条 nit 不阻塞:sandbox_runner.py 的 except Exception 可拆成 TimeoutExpired 与其他异常两支,纯可读性,可不改。
This was referenced Sep 15, 2026
Per maintainer review (PR Lion-1209#17 comment): the sdist allowlist ships /docs, so the 114 evidence files would publish to PyPI; the report is a point-in-time snapshot whose conclusions live in CHANGELOG [Unreleased]. Full report text moves to the PR description; open findings are tracked as issues Lion-1209#18-Lion-1209#21.
Owner
|
评审要求的调整已由维护者代为执行,无需重复操作:
代码部分审查已通过(CI 7/7 绿)。移除产物为 docs-only 变更,远端 CI 会自动重跑一遍,绿了即可合并。感谢这份审计——过程纪律和"先红后绿"的回归验证都做得很好。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
macOS 真机审计与真实阶跃补测。报告
PROJECT_REVIEW_2026-09-10.md(§7 为最新 Live 结果),证据和复现脚本在docs/audit/2026-09-10/。已修复(独立提交,各附真实工具回归):
本机验证:
真实 step-3.7-flash(Python 3.12,普通阶跃 API Key,不是 Coding Plan):
仍然未测/待解决:
macOS sandbox-exec 新后端方案见 #16,待维护者决策。本 PR 不改版本、不发布、不实现新 OS 沙箱、不自行 merge。远端 CI 上一轮为 action_required 且无 job,本机绿不能代替远端三 OS 验证。
维护者操作记录(2026-09-14)
按评审决定,审计产物不入库(sdist 白名单含
/docs会随发版发布证据;报告结论已由 CHANGELOG[Unreleased]承载):docs/audit/(114 个证据文件)与根目录PROJECT_REVIEW_2026-09-10.md已由维护者从本分支移除(commit6a4a401)。审计报告全文(存档)
coderio macOS 真机安全与可靠性审计
Lion-1209/coderio main@b87aa06511dc3d51d7dafffc66ec3f7c55a4e534;分支audit/macos-202609。git log/git show再分析。现有工作目录有个人未跟踪文件,因此使用独立克隆;GitHub 认证恢复后复用了已存在的KiritoStar/coderiofork。1. 执行摘要
[判断] 双版本基线全绿不能证明 macOS 运行时边界可靠。 本次新增真实实验复现三项 P1:沙箱降级超时遗留子进程、损坏凭据备份权限变宽、web_fetch 大小限制晚于完整响应读取。前两项已在本 PR 分别修复,均有先红后绿的真实工具回归测试。未确认 P0;这不是“证明没有 P0”。
默认
off的普通 POSIX 进程组超时杀有效;job/write的同一实验在修复前留下 PPID=1 的存活子进程。macOSwrite不提供文件隔离,明确提示降级,auto_allow_if_sandboxed在 CONFIRM 下不生效。人工 TUI、真实 provider、Windows/Linux 的平台行为不冒充实测。2. 实测结果与证据
完整命令输出在 证据目录。用户名、工作区绝对前缀和本机主机名做占位替换;PID、返回码、测试数保留。日志不含真实 key。实验脚本使用临时文件及短生命周期进程,可由仓库 venv 执行。
2.1 修改前后五项门禁
两套环境分别使用
UV_PROJECT_ENVIRONMENT=.venv311/.venv312和--python 3.11/3.12执行uv sync --frozen --extra dev,避免互相覆盖;运行命令额外加uv run --frozen保留原锁。uv sync --frozen --extra devuv run ruff check src testsuv run ruff format --check src testsuv run python -m pytest tests/ -q -rsuv run python -m mypy src/coderiouv build --wheelbaseline{311,312}-status.txt与after{311,312}-status.txt记录每条退出码,各检查有独立日志。uv lock --check当前成功(121 packages),不表示 CI 已检查新鲜度。独立uv export --frozen --no-emit-project --extra dev+uvx pip-audit --no-deps -r返回 No known vulnerabilities found(pip-audit.log);只表示本次查询的冻结集合。全量测试保留上游现有 mock;本次新增测试不 mock 工具、文件系统或进程。覆盖率补充运行第一次直接使用
.venv312/bin/python,导致三个 hooks 测试报/bin/sh: python: command not found(coverage.log,3 failed / 1307 passed / 22 skipped)。现场shutil.which("python")为 None,查阅 hook 测试命令后确认其依赖 PATH。规范的uv run会加入 venv PATH;这是审计命令偏差,已另行按规范复跑,不将失败日志隐藏或归因于 provider。规范复跑结果:1310 passed / 22 skipped,覆盖率 82.60%,75% 门通过(coverage-uv.log)。
2.2 skip 逐项分析
22 项完整清单及 pytest 原因。两版本清单相同:
CODERIO_PERF_TESTS=1和 key。本次缺少相关环境 key,未执行。test_python_fallback_used_when_no_rg因本机有 rg 跳过。这个分支也适用于 macOS;已将 PATH 限为 venv +/usr/bin:/bin,补测 1 passed(grep-no-rg.log)。2.3 macOS 清单
--help、config、mcp list、skills list全部 exit 0。mcp list 仅证明管理 CLI,无服务器连接;skills list 列出 13 个 bundled skills。安装依赖按 wheel 的范围重新解析,区别于冻结测试环境tests/cli/test_tui_startup.py -v:14 passed(tui-startup.log)none、auto_allow=False、有 macOS 无 OS 沙箱警告;生产 execute 返回[sandbox unavailable: ... ran WITHOUT ...]。见 mac_checks.py / mac-checks.log~/.coderio一次性子目录真实写入并用 ls 检查,结束清理(home-credentials.log),未改用户已有 key。损坏备份修复前 0644 → 修复后 0600.local均被拒绝;本机.local解析到了 ::1。不能外推为所有 mDNS/NAT64/DNS 重绑定情况均安全[[hooks]]→ HookRunner,真实 shell 读取 stdin JSON;字段含 session_id/cwd/permission_mode/hook_event_name/tool_name/tool_input,stderr=audit-denied、exit 2 → blocked=True。mac-checks.log/MixedCase.py创建、/mixedcase.py编辑同一文件;首次 undo 恢复 original,第二次删除新建文件。实际大小写不敏感检测为 True。不覆盖多实例并发、跨卷 rename、Unicode 归一化和符号链接竞态verify_harness_live.py、verify_deepagent_live.py未测;ANTHROPIC_API_KEY/Z_API_KEY/STEP_KEY/STEPFUN_API_KEY 环境均缺失,未修改 provider/协议配置3. 新发现(按优先级)
P1-N1:job / degraded write 超时只杀 shell,子进程继续运行【实测,已修复】
基线
src/coderio/tools/sandbox_runner.py:109-132使用start_new_session=True,却交给subprocess.run(timeout=...)处理超时,异常分支只返回 124。agent/deep_loop.py:234-272将 job/write 实际路由到这里;默认 off 的 Popen/kill_process_tree 不是同一路径。复现:
uv run python docs/audit/2026-09-10/probe.py。基线输出中 job 子进程 PID 66527 / PPID 1,write PID 66532 / PPID 1,状态 S;off 无记录。脚本只杀本次 PID,不碰其他进程。历史:
git log -- tools/sandbox_runner.py及git show bcf1c8b表明 09-04 修复增加了可见降级标记,但未改变该 fallback 的 subprocess.run;这是未覆盖的 POSIX 路径,不归因于供应商或依赖变化。修复:Popen 持有进程对象,异常时调用共享 kill_process_tree,再 kill/reap 直接子进程;超时仍返回 124,并保留 write 降级提示。回归
tests/tools/test_posix_process_tree.py直接执行真实 backend,修复前 job/write 红、off 绿,修复后全绿。P1-N2:损坏凭据备份泄漏原权限保护【实测,已修复】
基线
src/coderio/cli/credentials.py:78-81对.corrupt使用write_bytes,绕开_restrict_permissions。022 umask 下原文件 0600,备份 0644;备份可以包含尚可恢复的 key。[判断] 如果父目录可被其他本机账户遍历,文件会暴露 key;本次只测 mode bits,没有声称实际读取了其他账户的秘密。历史:
git show 86a7957,备份路径正是上一轮原子写修复新增。新的可恢复性功能没有延续保密性契约。修复:
xb独占创建,写入字节前调用同一权限限制函数,保留首份备份。真实文件测试tests/cli/test_credentials_backup_permissions.py在 022 umask 下验证备份内容、0600、二次损坏不覆盖;修复前失败、修复后通过。Windows ACL 调用的真机结果交给远端 Windows,不冒充本机验证。P1-N3:web_fetch 1 MB 上限在完整下载后才生效【实测 + 代码审阅,待修复】
src/coderio/tools/web_fetch.py:178为client.get();:202-211才 iter_bytes 并截断。冻结 httpx 的_client.py:879附近send(stream=False)默认路径先response.read()。因此文档的“stream up to 1 MB”并未实现:返回给模型的字符串有界,但传输/缓存不受此上限保护。复现
uv run python docs/audit/2026-09-10/fetch_buffer.py:真实本地 HTTP 服务被显式配置为代理,不向公共网络发请求。服务先发送 1,100,000 字节然后暂停,客户端 timeout=1。实际得到Error fetching ...: timed out,耗时 1.04s;已经收到超过上限的数据却仍等待剩余响应。此实验没有 mock HTTP 或 SSRF 函数,也不用于证明 proxy SSRF 绕过。[判断] 大/慢响应可耗尽内存或长期占据 turn;本次没有做 OOM 压力破坏。建议另修为
client.stream('GET', ...),在 context manager 内逐跳校验、检查类型并有界读取,覆盖大 body、chunked、压缩、重定向及异常资源释放的真实服务测试。本 PR 保留可执行复现,不把未完成的修复标为已解决。P2-N4:TUI gate 缺失确认接口时默认放行【代码审阅,待修复】
src/coderio/cli/repl.py:46-59:TuiPermissionGate._ask在tui没有request_confirmation时返回 True,与tools/permission.py的 bare gate fail-closed 修复不一致。[判断] 正常 CoderioTUI 实现该方法,未证实正常用户路径可触发;属于装配异常的防御缺口,不夸大为已利用的权限绕过。建议缺失接口返回 False,并测试真实 gate 对不具备确认能力对象的处理。
P2-N5:架构“当前状态”仍残留已删除/已完成机制【代码审阅,待同步】
docs/coderio-architecture.md:253仍列on_truncated;CHANGELOG 0.5.0 明确记录已删除,当前 StreamHandler 无此方法。agent/harness_middleware.py:155-169先取.exit_code,再取ToolMessage.artifact['exit_code'],然后才兼容文本。建议将文档对齐当前实现,并区分“结构化已接入、文本回退仍存在”。不把整个旧报告“文档大面积失真”的评价直接复用到当前版本。
4. 复核已知问题与历史修复
.github/workflows/ci.yml:81附近先 uv pip install pip-audit 再 uv run pip-audit,工具链与项目集合混合。建议 export frozen requirements + uvx pip-audit --no-deps -r;注意 CHANGELOG 记载 httpx2 经 langchain-openai → openai 进入项目依赖,不能把 09-09 那次具体 CVE 误称为仅审计器误报历史修复抽查,不将“测试通过”泛化为完整对抗证明:
${HOME}recursive rm、diskutil eraseDisk、Stop-Computer 均返回拒绝原因,echo safe 放行;只调用 policy,不执行破坏命令(mac-checks.log)。5. macOS 专项结论与 sandbox-exec 评估
[实测] 本机存在
/usr/bin/sandbox-exec,最小 profile 能允许临时目录普通写、拒绝指定子目录写。 见 seatbelt_probe.py / seatbelt.log。这只证明一个规则的运行能力。[实测] 本机
man sandbox-exec的 NAME/DESCRIPTION 明确写 DEPRECATED,并建议 App Sandbox。 因而“工具能运行”不等于适合承诺长期支持。[判断] 建议维护者先作正式产品决策:默认继续明确“macOS 无 OS 级沙箱”,若要实现,作为 opt-in 独立后端评估。 不在此次 PR 加入后端,不因配置为 write 就免确认。实施需覆盖:workspace realpath/APFS 大小写/符号链接、用户凭据与 trust-store deny、临时目录及构建工具可写例外、Python/Node 子进程继承、网络策略、错误后 fail-closed、超时与 Esc 生命周期、多个 macOS 版本。App Sandbox 也不能在未经验证时当作任意 CLI agent 的直接替代品。
方案已提交 issue #16,包含本次最小复现与验收矩阵;等待维护者决定接口与维护承诺。现阶段真正需要对抗性隔离的任务仍应使用独立 VM,和仓库自己的安全边界声明一致。
6. 未测项与局限
7. 阶跃真实 Key 补测(同日追加)
本轮基线
a207c18;Live 脚本修复提交6ffcbee。凭据通过隐藏 stdin 输入,仅存运行进程内;模型构造后从环境删除,避免真实 shell 子进程继承。未写用户配置/凭据文件,所有日志经过 Key 精确脱敏。证据在 live-rerun。7.1 模型与协议先验验证【实测】
用户的“3.7flash”对应规范 ID
step-3.7-flash,已由服务响应确认。先发最小请求,两条官方接口均 HTTP 200、返回 OK:https://api.stepfun.com/v1/chat/completions,约 1.04s。https://api.stepfun.com/v1/messages,约 1.02s。SDK base_url 为https://api.stepfun.com,由 SDK 拼/v1/messages。依据:阶跃官方模型示例、官方 Messages 文档。本次是普通 API Key 通道,不是
/step_planCoding Plan 额度验证;没有把两者混用。保存的 preflight JSON 仅保留状态、耗时、模型、usage 和可见文本,省略推理块及完整响应。7.2 本轮新发现与修复
P1-N6:两份 Live 脚本已脱离引擎接口【实测,已修复】
修复前,两脚本均在首次 run_deep_agent 调用处报
TypeError: ... unexpected keyword argument 'workdir',模型还未进入该引擎调用。定位scripts/verify_harness_live.py:77/99/112、scripts/verify_deepagent_live.py:54/67。git log -S 'spec: TurnSpec'定位e39b08d(09-01):引擎改为(user_input, spec, session, stream),常规测试/CLI 已迁移,Live 脚本遗漏。本 PR 将五处调用迁移为 TurnSpec;未改协议默认值、模型默认值或脚本递归上限。新增
tests/agent/test_live_script_contract.py,仅替换模型、执行真实 graph/写盘/shell,四项修复前失败、修复后通过。这也修正上一轮“Live 脚本只是缺 key 未测”留下的证据空洞。P2-N7:阶跃 OpenAI 流式累计 usage 被适配器重复相加【实测,待修复】
stepfun_api实际模型工厂的性能测试:QA 通过;单文件分析在tests/agent/test_perf_baseline.py:154失败,聚合 input_tokens=574422 > 50000。未放宽断言。进一步一次最小真实请求(
usage_probe.py/.json)通过透明 httpx stream 包装器只记录 usage,不修改响应字节:13 条 SSE usage 均带 prompt_tokens=16,completion_tokens 从 0 累计至 28(末条重复 28);最终供应商 usage 为 16/28/44,而 ChatOpenAI 聚合得到 208/180/388。冻结langchain_openai/chat_models/base.py的_convert_chunk_to_generation_chunk为各块生成 usage_metadata,合并后 coderioagent/deep_loop.py:1215-1217将聚合结果交给 stream.add_usage。[判断] 已证明当前流式统计不兼容,影响 UI/性能门的可信度;不能把 574422 当作实际计费 token,也不能用这次最小对照还原另一个请求的真实用量。未查询账单。建议在明确 provider 协议后规范化累计值→增量,覆盖 stream/non-stream、重复末块、工具轮次和重试;不要全局将所有 provider usage 改成取最后一项,也不要直接提高性能阈值。此项未在 PR 偷改模型适配器。
7.3 真实 Live 结果(Python 3.12)
实际观察:
python hello.py输出 hello-harness、exit 0 → GroundingGate 要求读取 → read_file → 正常结束;用时 62.27s(含限速)。python /marker.py得 exit 2,之后python marker.py成功;GroundingGate 要求 read_file 时图步骤耗尽。工具错误确实反馈给模型,模型也能修正。审计驱动器自身的失败也保留:perf.log 为日志包装器缺少 isatty,尚未发模型请求;perf-after.log 为驱动器占位 Z_API_KEY 优先于 OPENAI_API_KEY,导致两次 401。修正后先断言工厂持有本次输入 Key,再得到 perf-final.log;这两项不是项目新缺陷,也不是用户 Key 无效。
7.4 双版本与真机复跑
覆盖率重测 82.63%(1314 passed / 22 skipped,75% 门通过)。再次新建 wheel venv,四项 CLI 均 exit 0;再次独立冻结依赖 pip-audit:No known vulnerabilities found。完整套件仍默认禁用 Live 性能测试,22 skipped 不改写为 20;两项 Live 另行执行的 1 passed / 1 failed 单独报告。
本轮自动真机重测已经完成,但仍不满足“所有真实测试通过”:原 deepagent 低预算脚本和 OpenAI 统计性能门存在上述失败。人工 TUI 六项仍未收到人类确认;真实模型仅在 Python 3.12 执行,3.11 是冻结全量及系统行为重测;智谱、Coding Plan、真实 MCP、Windows/Linux 平台边界仍未补测。