Files
ycode/docs/沙盒只读文件桥接入-2026-10-18.md
T

8.9 KiB
Raw Blame History

沙盒只读文件桥接入(2026-10-18)

后续增量:宿主产物提交/补偿/恢复已在组件 0.5.0 接入原生写回事务。本文件保留读取桥交付时的范围与证据;读取桥本身仍只读,宿主原生事务不等于完整文件写入调用桥已经完成。

当前交付

在首批接入上,P1/P3 已新增真实的工作区只读文件桥:模型可显式选择,Node 同步/异步客户端通过已绑定 Job 的 stdio 调用原生文件读取,操作结果在回复前落盘,断线后可以查询。完整目标继续按计划及验收矩阵推进。

本批另修复了 AppContainer 私有 TEMP 目录,并补充显式 Node 同步子进程适配器。该适配器不依赖读取桥,也不增加宿主文件/网络权限。

这是一套 YCode 自有实现。本批没有新增 WorkBuddy 取证,也没有原版等价结论。filesystem-broker 整体能力仍不可用;独立的 workspace-read-bridge 只声明下述读取子集。

调用与执行点

{
  "command": "node inspect.cjs",
  "runtimes": ["node"],
  "bridge": "workspace-read"
}
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。

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 规定共享模式及打开 reparse 对象的行为;GetFinalPathNameByHandleW 提供句柄规范路径和 volume GUID 形式;FILE_ID_BOTH_DIR_INFO 定义句柄目录枚举记录。这些文档用于解释具体执行点;当前支持程度由自有实际攻击/故障测试记录限定,不由 API 名称推定完整文件代理安全。