Files
ycode/docs/沙盒Node子进程与临时目录修复-2026-10-18.md
T

4.3 KiB

沙盒 Node 子进程与临时目录修复(2026-10-18)

已确认的问题与修复

继续实现只读文件桥时,真实测试发现三个独立问题:

  1. Windows 把 AppContainer 内的 TEMP 派生为私有 LOCALAPPDATA/Packages/<profile>/AC/Temp;最初只创建了 stage/tmp,派生目录不存在,Node 临时文件创建返回 ENOENT。runner 现已在启动和授予 ACL 前创建对应私有目录,仍随本次 stage 清理。
  2. 当前受测 Node 的默认同步管道捕获会在实际 AppContainer 中挂起,甚至其自身 timeout 尚未有效结束该启动;最终由外层 Job 时限终止。继承 stdio 和传入文件描述符的同步捕获均可用。提供显式预加载的自有适配器,用私有临时文件描述符交给原版 Node spawn 实现,保持原 token/Job/网络约束。
  3. Python 3.13 起的 Windows mkdir(0o700) 创建显式 user/admin DACL,没有 AppContainer SID;真实 Python 3.14.7 的 TemporaryDirectory 因此创建后立即不可访问。新增只读 python-sitecustomize.py,仅对当前私有 stage 内的 0o700 建目录改为继承原生私有父 DACL;stage 外仍用原行为,访问继续由系统 ACL 拒绝。

未将该问题归因于已证明的 Windows/Node 内部机制:当前证据只证明上述允许/失败路径。未修改用户安装的 Node、libuv 或全局设置,也未回退宿主执行。

使用与维护

{
  "command": "node --require \"%YCODE_SANDBOX_PROCESS%\" build.cjs",
  "runtimes": ["node"]
}

源文件是 native/sandbox/node-process.cjs。随 sandbox:build 复制到资源目录,manifest 记录源码和成品摘要;每次选择 Node 时校验、复制到只读 tools 目录。适配器通过显式 --require 或 require 启用,不更改宿主 Node 设置,不自动覆盖所有项目的运行方式。

启用后覆盖该 Node 进程公开的 spawnSync、execFileSync、execSync,并同步 ESM 内建命名导出。子进程依然由 Node 原生启动,AppContainer/Job 继承和系统 ACL 继续生效。默认捕获及 stdio: 'pipe' 使用私有输入/输出文件,其他显式 stdio 配置交给原实现。私有文件句柄在操作后关闭,文件和空目录尽力移除;最终 stage 清理负责收尾。

输入与 maxBuffer 各限制为最多 16 MiB;默认 maxBuffer 为 1 MiB。适配器按上限读取捕获内容,超限返回 ENOBUFS。这里是结果缓冲限制,不是子进程运行中的磁盘硬限额;超限检测发生于子进程返回后,不能由 ENOBUFS 推断进程在产生超量输出时已被终止。timeout 继续交给 Node 原生实现,整个进程树另由外层 Job 管理。

当前验证

npm run test:sandbox:process 最终 20/20 通过,报告 .tmp/sandbox-process-9EyXTm/report.json。测试使用隔离状态与运行时,真实执行 Node 同步二进制/stdin/错误/cwd/env/超时、继承 stdio、ESM 导出、缓冲上限、临时文件清理、原生 ACL、宿主文件拒绝,Python tempfile/subprocess/0o700 和宿主路径拒绝,以及三个资源被篡改时安装状态/命令共同拒绝。

已保留的原始诊断:.tmp/sandbox-spawn-compat.log(默认捕获超时)、.tmp/sandbox-spawn-compat-default-fixed-temp.log(创建 TEMP 后默认捕获仍超时)、.tmp/sandbox-spawn-compat-files-fixed-temp.log(文件描述符捕获成功)、.tmp/sandbox-spawn-compat-adapter.log(显式适配后 Node/宿主专用读取器的捕获与拒绝均成立)。

该适配器不是完整 child_process 复刻。异步 spawn/fork/IPC、流式事件/背压、额外 fd、所有 stdio/平台版本组合及输出磁盘硬限额继续待验;不启用适配器的默认 Node 管道仍有上述已知问题。P2/P5 不因此标记完成,也没有原版等价结论。

Python 适配器通过受控 PYTHONPATH 加载,具有清单摘要和只读 tools ACL。它不提供宿主目录授权,不修改宿主 Python;-I/-S 等绕过 site/PYTHONPATH 的方式也会绕过兼容修复。这里保留父 DACL 的私有范围,不宣称 POSIX mode/元数据完全兼容。原生行为依据是 Python os.mkdir 文档 对 Windows 0o700 及 3.13 变更的说明;该行为与 AppContainer 的组合失败及修复效果由隔离实测记录。