纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口 完成项目 TypeScript/Prettier 与 Rust 全量格式化 修复 Pingora expected executable 门禁的空白敏感误报 同步开发运维文档与 AGC skill pack 格式化忽略规则
7.4 KiB
AI Web 工程 Runner 与预览隔离威胁模型
更新时间:2026-06-13
范围
本文约束 /editor/agent 浏览器内 AI Web 工程编辑器的 MVP:固定 React / Vite / TypeScript 静态模板、虚拟文件系统、独立 runner 构建、独立 origin iframe 预览。
不覆盖 HMR、终端 shell、后端服务、任意依赖安装、任意端口代理和正式作品发布。这些能力进入后续阶段前必须单独补威胁模型。
信任边界
用户浏览器主站 origin
| /editor/agent 编辑器壳
api-server 控制面
| 最小任务能力
web-project-runner 执行面
| immutable artifact
preview gateway / 独立 preview origin
关键原则:
- 主站不执行 AI 工程代码。
- api-server 不执行 AI 工程代码。
- runner 无平台密钥、无宿主源码挂载、无 Docker socket。
- 预览域和主站不同 origin。
- 预览页拿不到平台 cookie、access token、SpacetimeDB、OSS 写权限或 LLM provider 密钥。
资产与威胁
| 资产 | 主要威胁 | MVP 缓解 |
|---|---|---|
| 主站 access token / cookie | 预览代码同源读取、XSS 窃取 | 独立 preview origin;iframe 不带主站 cookie;预览页不能访问主站 storage |
| 用户 Web 工程源码 | 跨租户读取、snapshot 枚举 | project / owner 校验;snapshotId 不可枚举;preview token 绑定 owner / project / snapshot |
| preview artifact | 路径穿越、MIME 错误、旧 token 访问 | preview gateway 校验 token;禁止 ..;按白名单 MIME 服务;短期 token 可撤销 |
| runner 临时工作区 | 逃逸到宿主源码、读取密钥 | 独立临时目录或容器;非 root;无宿主源码挂载;任务结束销毁 |
| 依赖缓存 | 缓存污染、恶意 postinstall | MVP 固定依赖;禁用 scripts;缓存 key 包含模板、Node 版本和 lock digest |
| api-server / SpacetimeDB | runner 横向访问内部服务 | runner 默认无内网访问;阻断 api-server 管理端口、SpacetimeDB 和生产数据库 |
| OSS / artifact store | 越权读写、签名 URL 泄露 | runner 只拿短期只读资产签名或受控写 artifact 能力;日志脱敏 |
| 构建日志 | 泄露环境变量、宿主路径、签名 URL | 日志限长、脱敏、错误摘要化;不回显平台密钥 |
| 用户浏览器 | 弹窗逃逸、下载、剪贴板、摄像头、Service Worker 常驻 | iframe sandbox;CSP;禁用 Service Worker;不授权敏感能力 |
Runner 限制
runner 必须 fail-closed。每个 job 至少限制:
- CPU。
- 内存。
- 磁盘。
- 进程数。
- 打开文件数。
- 任务时长。
- 日志大小。
- artifact 大小。
- 单文件大小。
执行环境要求:
- 非 root 用户。
- 只读基础镜像。
- 无 Docker socket。
- 无宿主源码目录挂载。
- 无平台
.env。 - 无平台 token。
- 临时工作区任务结束销毁。
构建超时或资源超限时直接 kill,job 进入 failed 或 expired,保留可展示错误摘要,不推进 active preview。
网络限制
MVP 构建期默认无外网。若模板构建确实需要网络,只允许:
- 平台受控 npm registry mirror。
- 平台资产只读签名域。
必须阻断:
- RFC1918 内网网段。
- 云 metadata 地址。
- api-server 管理端口。
- SpacetimeDB。
- 生产数据库。
- Docker daemon。
- 任意用户配置 registry。
需要覆盖 DNS rebinding 和 HTTP redirect 到内网的情况;不能只按初始 URL 字符串判断。
预览 token
preview token 必须满足:
- 绑定
ownerUserId。 - 绑定
projectId。 - 绑定
snapshotId。 - 绑定
artifactId。 - 短期有效。
- 可撤销。
- 不可枚举。
- 不能跨租户复用。
访问无权限 artifact 应返回 403 或不可区分的 404,不得泄露其它项目是否存在。
Preview Gateway
gateway 只读服务 immutable artifact。
必须校验:
- token 有效且未撤销。
- artifact 属于 token 绑定 snapshot。
- 请求路径规范化后仍在 artifact 根目录。
index.htmlfallback 只在 SPA 路由范围内生效。- MIME 类型来自白名单映射,不信任上传文件名。
必须测试:
- path traversal。
- 错误 MIME。
- HTML 注入。
- 缓存串租户。
- 旧 token 访问。
- artifact 删除后访问。
- token 过期后访问。
浏览器隔离
iframe sandbox MVP 建议最小化:
sandbox="allow-scripts"
如果后续需要表单或同源能力,必须单独评审。MVP 不允许:
allow-same-originallow-top-navigationallow-downloadsallow-popupsallow-modals- 摄像头。
- 麦克风。
- 剪贴板。
- 地理位置。
CSP 默认收紧:
default-src 'none';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https://assets.genarrative.world;
font-src 'self' data:;
connect-src 'none';
frame-ancestors https://www.genarrative.world;
worker-src 'none';
MVP 禁用 Service Worker 和持久缓存,避免恶意预览代码在用户浏览器里长期驻留。
Patch 校验
AI 只能提交结构化 patch:
- create file
- update file
- delete file
- rename file
- package manifest request
api-server 必须拒绝:
- 绝对路径。
..。- 符号链接。
.env、.npmrc、.git、.ssh等隐藏敏感文件。- 超深目录。
- 超大文件。
- 超大总 snapshot。
- 二进制膨胀。
- 大 Data URL。
- 可执行权限。
前端编辑器可以显示校验错误,但正式准入以 api-server 为准。
依赖供应链
MVP 固定模板依赖,不开放任意 npm。即便用户或 AI 修改 package.json,也必须拒绝或忽略:
preinstall。install。postinstall。prepare。git:依赖。file:依赖。http:/https:tarball 依赖。- 私有 registry。
- lockfile 篡改。
- native build。
- 二进制下载型包。
后续开放白名单依赖时,必须加入依赖 resolver、registry mirror、封禁列表、许可证 / 安全扫描和缓存污染防护。
审计与熔断
必须记录:
ownerUserIdprojectIdsnapshotIdjobIdartifactIdtemplateKey- 依赖 lock digest
- 构建耗时
- 资源用量
- 失败原因摘要
- runner id
- preview token 签发和撤销事件
必须提供运维开关:
- kill job。
- 禁用项目。
- 禁用 owner。
- 禁用模板。
- 禁用依赖。
- 清理 artifact。
- 暂停 runner。
- 全局关闭
/editor/agentpreview build。
发布门禁
临时 preview artifact 不能直接升为线上作品。作品化发布必须:
- 重新构建。
- 重新校验 snapshot。
- 重新签发发布 artifact。
- 使用 immutable artifact。
- 走平台作品身份、公开详情、统计和权限链路。