# 沙盒只读文件桥接入(2026-10-18) > 后续增量:宿主产物提交/补偿/恢复已在组件 0.5.0 接入[原生写回事务](沙盒原生写回事务-2026-10-18.md)。本文件保留读取桥交付时的范围与证据;读取桥本身仍只读,宿主原生事务不等于完整文件写入调用桥已经完成。 ## 当前交付 在[首批接入](沙盒完整接入首批实施-2026-10-18.md)上,P1/P3 已新增真实的工作区只读文件桥:模型可显式选择,Node 同步/异步客户端通过已绑定 Job 的 stdio 调用原生文件读取,操作结果在回复前落盘,断线后可以查询。完整目标继续按[计划](沙盒完整接入计划-2026-10-18.md)及[验收矩阵](沙盒接入验收矩阵-2026-10-18.md)推进。 本批另修复了 AppContainer 私有 TEMP 目录,并补充[显式 Node 同步子进程适配器](沙盒Node子进程与临时目录修复-2026-10-18.md)。该适配器不依赖读取桥,也不增加宿主文件/网络权限。 这是一套 YCode 自有实现。本批没有新增 WorkBuddy 取证,也没有原版等价结论。`filesystem-broker` 整体能力仍不可用;独立的 `workspace-read-bridge` 只声明下述读取子集。 ## 调用与执行点 ```json { "command": "node inspect.cjs", "runtimes": ["node"], "bridge": "workspace-read" } ``` ```js const workspace = require(process.env.YCODE_SANDBOX_BRIDGE).connectSync() try { const bytes = workspace.readFileSync('src/example.ts') const stat = workspace.statSync('src/example.ts') const entries = workspace.readdirSync('src') console.log(bytes.length, stat.kind, entries.length) } finally { workspace.close() } ``` 异步使用 `connect()` 的 `readFile`、`stat`、`readdir`、`query` 和 `close`。一个 Job 同时只应有一个 stdio 客户端;客户端独占该进程 stdin,不能同时作为交互输入。它不覆盖 Node 标准 `fs`,也不会将普通绝对路径自动映射到宿主。`stat`/`readdir` 返回桥的有界 JSON 元数据,不声称兼容完整 Node Stats/Dirent。 | 源码 | 职责 | | --- | --- | | `native/sandbox/filesystem-read.h` | 宿主专用原生读取器;持有祖先/目标句柄,校验卷、工作区身份和最终路径;读取、元数据、句柄目录枚举 | | `native/sandbox/runner.cpp` | 拒绝容器内部调用宿主读取器;继续保持 AppContainer/Job 与默认禁网;转发有界 stdio 回复 | | `native/sandbox/node-bridge.cjs` | 自有同步/异步客户端、分块摘要检查、异步队列和退出清理 | | `src/main/sandbox/filesystem-reader.ts` | 策略及拒绝路径、实际原生调用、取消/时限、报告解析与最终名称复核 | | `src/main/sandbox/operation-broker.ts` | 严格帧、按 Job 绑定的归属、请求/快照上限、状态日志、结果查询与协议失败清理 | | `src/main/sandbox/maintenance.ts` | 重启/断线后的持久结果查询;未完成记录返回 unknown | 客户端随原生组件构建、打包;manifest 记录 runner/header/client 源码摘要及客户端摘要。选择文件桥时,客户端摘要不符会拒绝启动,不查找其他宿主模块。 ## 实际边界 1. 当前仅支持 Windows x64、本地固定 NTFS 卷。读取器从卷根开始逐级打开目录,保留整个祖先链,并拒绝 reparse point、设备和多重硬链接。句柄只共享读取,已有可写句柄导致 EBUSY;读取期间禁止对应对象的改写、删除或重命名,不放宽共享模式重试。 2. 每个 Job 固定工作区卷/文件 ID。每次调用重新核对根身份;同路径替换成另一个目录不会取得原授权。目标最终规范名称来自句柄,与根的 volume GUID 路径比较,并再次经过敏感名称/拒绝前缀规则。宿主状态目录和副本排除目录不向桥开放。 3. 相对路径严格拒绝 `..`、空组件、绝对路径、ADS/UNC/设备、尾部空格/点及敏感文件。目录列表会过滤敏感项和链接。普通 `fs` 及原生后代仍受原有 AppContainer 权限约束。 4. 调用归属来自主进程保存的 session/run/tool-call、策略版本/内容指纹及本次 sandboxRunId。子进程不能提交替代归属。该权限范围属于整个 Job;它没有独立的逐后代 PID 认证,不能据此声称更细粒度的进程规则已完成。 5. 单次读取先在原生句柄下取得完整有界内容,再以 16 KiB 块回复。后续块来自同一只读内存快照,宿主外部编辑不会拼接出混合版本。每块核对身份/摘要,客户端完成后再核对完整 SHA-256。 6. 桥不实现宿主写入/删除/联网。普通命令产物仍由副本差异、审批和原有事务写回;原有事务的宿主目标约束没有因本批读取器而自动升级。 | 限制 | 当前值 | | --- | --- | | 请求帧/回复值 | 8 KiB / 24 KiB | | 每 Job 并发/总请求 | 8 / 1024 | | 异步客户端总排队 | 32,向宿主最多发送 8 项 | | 单文件读取 | min(策略 fileBytes, 8 MiB) | | 读取快照 | 最多 8 个,总容量 min(策略 bytes, 16 MiB),空闲 60 秒失效 | | 目录枚举 | 最多 128 个原始条目;序列化超限则拒绝,不截断后冒充完整结果 | | 原生读取器/代理截止 | 4 秒 / 5 秒;原生另监控父进程退出 | ## 操作生命周期和查询 请求包含 version、UUID requestId、operation、args。未知/伪造归属字段、重放、超限或截断控制帧导致整个通道失败并取消 Job;无权操作和路径错误返回有界错误。普通命令输出可继续流式传递,不使用控制帧上限截断普通长行。 每个操作保存 pending/running 和最终 succeeded/denied/failed/cancelled 状态。最终记录在回复之前写入并刷新;只记录身份和元数据,不保存文件内容或活跃快照 token。回复丢失不会把已经成功的读取改成未执行;在运行中中断而没有最终状态的记录查询为 unknown。 ```powershell npm run sandbox:maintain -- broker-operation RUN_UUID REQUEST_UUID --state-root 'C:\absolute\userData\sandbox' ``` 真实 preload 提供 `window.ycode.app.sandboxBrokerOperation(runId, requestId)`;仅允许自有顶层应用页面,校验 UUID、记录类型以及 session/run/tool-call/策略归属。不能由另一个 Job 的 requestId 替换目标。查询接口不接受任意路径或 stateRoot。 ## 验证与未完成项 测试使用隔离工作区、状态目录和 Electron userData,不改用户生产策略/运行时,不重启用户当前窗口。 | 验证 | 已确认结果 | 报告 | | --- | --- | --- | | 实际读取桥 | 21/21;同步/异步、二进制分块、根替换/junction 竞争、后代绕过拒绝、缓存一致性、归属/重放和策略撤销 | `.tmp/sandbox-read-bridge-fcJNOa/report.json` | | 协议故障 | 15/15;长普通输出/分帧、快照/并发上限、伪造/重放/超限/截断、取消、丢失回复和 unknown 结果 | `.tmp/sandbox-read-protocol-faEL5f/report.json` | | 运行时兼容 | 20/20;Node 同步进程和 Python TEMP/0o700、真实子进程、原生拒绝及三个资源被篡改时 fail closed | `.tmp/sandbox-process-9EyXTm/report.json` | | 既有基础 | 原生 52/52、策略 38/38、旧 stdio 10/10 | `.tmp/sandbox-regression-GJmnRy/report.json`、`.tmp/sandbox-policy-0ZW6xX/report.json`、`.tmp/sandbox-stdio-AsODUy/report.json` | | 工具链/依赖 | 24/24;Node/npm、Python/pip 和工作区绑定的只读依赖层仍可用 | `.tmp/sandbox-toolchain-IvxSbF/report.json` | | 生产整应用 | 26/26;真实 main/preload/renderer、OMP RPC、localhost 模型;显式同步进程预加载、审批写回、读取桥及其持久查询 | `.tmp/sandbox-app-1LP378/report.json` | 最终 8 组共 206 项通过,完整生产构建/类型检查通过。原生资源更新为 0.2.0,协议仍为 1;三个适配资源均纳入安装状态校验,缺失或摘要不符时不显示 installed。源码/组件身份与报告摘要另存 `.tmp/sandbox-read-integration-validation.json`。文件格式检查已按本仓库 Windows CRLF 配置验证;并行模块未做全库换行重写。 尚未完成:宿主写入的原生事务代理、描述符/追加等完整文件语义、Python/Shell 桥、逐后代认证、标准用户与其他 Windows/文件系统矩阵、安全中心页面及原版 1:1。目录/文件元数据写回、磁盘硬限额、长期工作区、Git、受控出站、MCP/ConPTY 等继续按总计划推进。 ## 原生 API 依据 [CreateFileW](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew) 规定共享模式及打开 reparse 对象的行为;[GetFinalPathNameByHandleW](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew) 提供句柄规范路径和 volume GUID 形式;[FILE_ID_BOTH_DIR_INFO](https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info) 定义句柄目录枚举记录。这些文档用于解释具体执行点;当前支持程度由自有实际攻击/故障测试记录限定,不由 API 名称推定完整文件代理安全。