补充AIWeb工程预览方案文档
新增 /editor/agent 静态沙箱预览技术方案 新增 runner 与预览隔离威胁模型 新增 AIWeb 工程静态预览 MVP 验收清单 更新文档索引和团队决策记录
This commit is contained in:
@@ -16,6 +16,14 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-06-13 `/editor/agent` AI Web 工程编辑器采用静态沙箱预览 MVP
|
||||
|
||||
- 背景:`/editor/agent` 需要承载浏览器内类似 IDE 的 AI Web 工程编辑和实时预览能力,但 AI 生成工程的构建和运行不能进入 Genarrative 主站 JS 上下文、当前仓库源码目录或 api-server 进程。
|
||||
- 决策:第一版采用“平台编辑器壳 `/editor/agent` + api-server 控制面 + 独立 `web-project-runner` worker + 独立 preview origin”的四层结构。MVP 只支持固定 React / Vite / TypeScript 静态模板、虚拟文件系统、结构化 AI patch、平台固定构建命令、独立 runner 静态构建和独立域 iframe 预览;明确不做 HMR、终端 shell、后端服务、任意端口代理、任意 npm 安装、AI 自定义 shell script 或主站同源预览。
|
||||
- 影响范围:`/editor/agent` 前端入口、api-server Web project 控制面、Web project runtime job、runner 部署、preview gateway、artifact store、安全验收和后续作品化发布链路。
|
||||
- 验证方式:Phase 0 必须先完成技术方案、威胁模型和验收清单;Phase 1 只能在路径校验、runner 资源限制、网络隔离、preview token、iframe/CSP、失败保留上一版预览和刷新恢复验收口径明确后进入编码。
|
||||
- 关联文档:`docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md`、`docs/technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md`、`docs/technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md`。
|
||||
|
||||
## 2026-06-12 外部生成 worker 扩展到跳一跳、拼消消和敲木鱼
|
||||
|
||||
- 背景:外部图片生成已从 HTTP 长请求迁到 `external_generation_job` 队列;跳一跳、拼消消和敲木鱼继续扩展时需要统一 job 粒度、前端等待展示和本地 / 生产验证口径。
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
微信小程序虚拟支付接入、`wechat_mp_virtual` 渠道、`wx.requestVirtualPayment` 承接页和后端签名配置见 [【技术方案】微信虚拟支付接入-2026-05-26.md](./%E3%80%90%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88%E3%80%91%E5%BE%AE%E4%BF%A1%E8%99%9A%E6%8B%9F%E6%94%AF%E4%BB%98%E6%8E%A5%E5%85%A5-2026-05-26.md)。
|
||||
|
||||
`/editor/agent` 浏览器内 AI Web 工程编辑器的静态 SPA 沙箱预览 MVP,采用“平台编辑器壳 + api-server 控制面 + 独立 runner worker + 独立预览域”四层结构;技术方案、威胁模型和验收清单见 [【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md](./technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md)、[【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md](./technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md) 和 [【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md](./technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md)。
|
||||
|
||||
本地通过 SSH alias 管理多台服务器、查看硬件 / systemd / HTTP 健康状态并执行受控服务启停的 egui 桌面工具见 [【开发运维】本地SSH服务器管理面板技术方案-2026-06-11.md](./technical/【开发运维】本地SSH服务器管理面板技术方案-2026-06-11.md)。
|
||||
|
||||
生产部署切换到 systemd + Nginx + SpacetimeDB 自托管的总方案见 [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md),该文档也是当前生产 Jenkinsfile 的唯一入口。SpacetimeDB 表结构变更、自动迁移边界和保留旧数据的分阶段迁移流程见 [SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md);private 表迁移 JSON 导入导出、HTTP 413 分片导入和旧数据库迁移流水线经验见 [SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md](./technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md) 与 [JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md](./technical/JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md);后台管理独立前端工程技术方案见 [ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md](./technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md)。
|
||||
|
||||
241
docs/technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md
Normal file
241
docs/technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md
Normal file
@@ -0,0 +1,241 @@
|
||||
# AI Web 工程 Runner 与预览隔离威胁模型
|
||||
|
||||
更新时间:`2026-06-13`
|
||||
|
||||
## 范围
|
||||
|
||||
本文约束 `/editor/agent` 浏览器内 AI Web 工程编辑器的 MVP:固定 React / Vite / TypeScript 静态模板、虚拟文件系统、独立 runner 构建、独立 origin iframe 预览。
|
||||
|
||||
不覆盖 HMR、终端 shell、后端服务、任意依赖安装、任意端口代理和正式作品发布。这些能力进入后续阶段前必须单独补威胁模型。
|
||||
|
||||
## 信任边界
|
||||
|
||||
```text
|
||||
用户浏览器主站 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.html` fallback 只在 SPA 路由范围内生效。
|
||||
- MIME 类型来自白名单映射,不信任上传文件名。
|
||||
|
||||
必须测试:
|
||||
|
||||
- path traversal。
|
||||
- 错误 MIME。
|
||||
- HTML 注入。
|
||||
- 缓存串租户。
|
||||
- 旧 token 访问。
|
||||
- artifact 删除后访问。
|
||||
- token 过期后访问。
|
||||
|
||||
## 浏览器隔离
|
||||
|
||||
iframe sandbox MVP 建议最小化:
|
||||
|
||||
```text
|
||||
sandbox="allow-scripts"
|
||||
```
|
||||
|
||||
如果后续需要表单或同源能力,必须单独评审。MVP 不允许:
|
||||
|
||||
- `allow-same-origin`
|
||||
- `allow-top-navigation`
|
||||
- `allow-downloads`
|
||||
- `allow-popups`
|
||||
- `allow-modals`
|
||||
- 摄像头。
|
||||
- 麦克风。
|
||||
- 剪贴板。
|
||||
- 地理位置。
|
||||
|
||||
CSP 默认收紧:
|
||||
|
||||
```text
|
||||
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、封禁列表、许可证 / 安全扫描和缓存污染防护。
|
||||
|
||||
## 审计与熔断
|
||||
|
||||
必须记录:
|
||||
|
||||
- `ownerUserId`
|
||||
- `projectId`
|
||||
- `snapshotId`
|
||||
- `jobId`
|
||||
- `artifactId`
|
||||
- `templateKey`
|
||||
- 依赖 lock digest
|
||||
- 构建耗时
|
||||
- 资源用量
|
||||
- 失败原因摘要
|
||||
- runner id
|
||||
- preview token 签发和撤销事件
|
||||
|
||||
必须提供运维开关:
|
||||
|
||||
- kill job。
|
||||
- 禁用项目。
|
||||
- 禁用 owner。
|
||||
- 禁用模板。
|
||||
- 禁用依赖。
|
||||
- 清理 artifact。
|
||||
- 暂停 runner。
|
||||
- 全局关闭 `/editor/agent` preview build。
|
||||
|
||||
## 发布门禁
|
||||
|
||||
临时 preview artifact 不能直接升为线上作品。作品化发布必须:
|
||||
|
||||
- 重新构建。
|
||||
- 重新校验 snapshot。
|
||||
- 重新签发发布 artifact。
|
||||
- 使用 immutable artifact。
|
||||
- 走平台作品身份、公开详情、统计和权限链路。
|
||||
266
docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md
Normal file
266
docs/technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# 浏览器内 AI Web 工程沙箱预览方案
|
||||
|
||||
更新时间:`2026-06-13`
|
||||
|
||||
## 背景
|
||||
|
||||
`/editor/agent` 需要承载一个类似 IDE 的 AI Web 工程编辑器:用户在浏览器里看到文件树、代码编辑、AI 指令输入、构建日志和实时预览,AI 生成的是一个完整 Web 工程。这个工程的构建、依赖安装和运行必须与 Genarrative 主站、当前仓库源码目录、平台密钥和正式业务数据隔离。
|
||||
|
||||
第一版不追求“浏览器里完整云 IDE + 任意 npm + HMR + 后端服务”。目标是先跑通一个安全、可恢复、可验收的静态 SPA 预览闭环。
|
||||
|
||||
## 结论
|
||||
|
||||
MVP 采用四层结构:
|
||||
|
||||
```text
|
||||
平台编辑器壳 /editor/agent
|
||||
-> api-server 控制面
|
||||
-> 独立 web-project-runner worker
|
||||
-> 独立 preview origin / preview gateway
|
||||
```
|
||||
|
||||
第一版只支持固定 React / Vite / TypeScript 静态模板、虚拟文件系统、结构化 AI patch、平台固定构建命令、独立 runner 静态构建和独立域 iframe 预览。
|
||||
|
||||
MVP 明确不做:
|
||||
|
||||
- 终端 shell。
|
||||
- 任意后端服务。
|
||||
- 任意端口代理。
|
||||
- HMR / 长驻 dev server。
|
||||
- 任意 npm 依赖安装。
|
||||
- AI 自定义 build shell script。
|
||||
- 与主站同源的预览 iframe。
|
||||
- 将 AI 生成工程写入当前 Genarrative 仓库源码目录。
|
||||
|
||||
## 分层职责
|
||||
|
||||
### 平台编辑器壳
|
||||
|
||||
入口路由固定为 `/editor/agent`。这一层只负责前端体验:
|
||||
|
||||
- 文件树、代码编辑器、AI 指令输入。
|
||||
- 当前项目版本、diff、构建日志和预览 iframe。
|
||||
- 保存用户编辑和 AI patch。
|
||||
- 订阅构建 job SSE 状态。
|
||||
- 在构建成功后切换 iframe 的 `previewUrl`。
|
||||
- 构建失败时保留上一版可用预览。
|
||||
|
||||
平台编辑器壳不得直接执行用户工程代码,不持有 runner 临时工作区路径,不把平台 access token、用户 cookie、SpacetimeDB 连接信息或 OSS 写权限暴露给预览页。
|
||||
|
||||
如果后续接入现有创作链路,入口仍应走 `play_flow` 主干和 `shared-contracts` DTO,不在前端壳或 `app.rs` 中新建平行业务流程。
|
||||
|
||||
### api-server 控制面
|
||||
|
||||
控制面只管理权限、快照、任务和状态,不执行 AI 工程代码:
|
||||
|
||||
- 鉴权和项目归属校验。
|
||||
- 校验 AI patch 和用户编辑。
|
||||
- 保存虚拟文件系统 snapshot。
|
||||
- 创建 preview build job。
|
||||
- 给 runner 发放最小任务能力。
|
||||
- 输出构建日志和状态 SSE。
|
||||
- 签发短期 preview URL。
|
||||
- 将业务状态通过受控 procedure 或 BFF 更新到正式数据层。
|
||||
|
||||
api-server 不把 SpacetimeDB、OSS 写权限、LLM provider、支付、用户 cookie 或平台内部 token 传给 runner。runner 需要读取资产时,只拿只读短期签名 URL 或受控 fixture。
|
||||
|
||||
### web-project-runner worker
|
||||
|
||||
新增独立进程角色,例如 `web-project-runner`。它可以复用外部生成 worker 的“任务表 + lease + controller + worker”模式,但不应把 Web 工程执行塞进 `external_generation_job`,避免语义污染。更合适的落点是新增 `web_project_runtime_job` 或抽象出通用 job 模块。
|
||||
|
||||
runner 只做执行面:
|
||||
|
||||
- 拉取指定 snapshot。
|
||||
- 展开到独立临时工作区或容器。
|
||||
- 使用平台白名单模板依赖。
|
||||
- 执行平台固定构建命令。
|
||||
- 采集构建日志、资源用量和结果。
|
||||
- 上传静态 artifact。
|
||||
|
||||
runner 不能反向写平台业务表。业务状态回写必须经过 api-server / SpacetimeDB 的受控入口。
|
||||
|
||||
### preview gateway 与独立预览域
|
||||
|
||||
预览页使用独立 origin,例如:
|
||||
|
||||
```text
|
||||
https://preview-<token>.sandbox.genarrative.world
|
||||
```
|
||||
|
||||
或者统一域名加不可枚举 token path:
|
||||
|
||||
```text
|
||||
https://sandbox.genarrative.world/p/<previewToken>/
|
||||
```
|
||||
|
||||
预览域不得与主站同源,不带平台 cookie,不共享主站 `localStorage` / `sessionStorage`。主站只通过 sandbox iframe 嵌入预览。
|
||||
|
||||
MVP 只服务静态构建产物。HMR、WebSocket 代理和长驻 dev server 后置到单独阶段评审。
|
||||
|
||||
## MVP 流程
|
||||
|
||||
```text
|
||||
用户在 /editor/agent 输入需求或编辑文件
|
||||
-> AI 返回结构化 patch
|
||||
-> api-server 校验 patch
|
||||
-> 生成 workspace snapshot
|
||||
-> 前端 debounce 后创建 preview build job
|
||||
-> runner claim job 并构建静态 dist
|
||||
-> artifact 上传 artifact store
|
||||
-> preview gateway 只读服务 artifact
|
||||
-> 前端 SSE 收到 succeeded 后 iframe reload
|
||||
```
|
||||
|
||||
失败链路:
|
||||
|
||||
```text
|
||||
构建失败 / 超时 / runner 崩溃
|
||||
-> job failed / expired
|
||||
-> 前端展示错误日志摘要
|
||||
-> active preview 保持上一版 succeeded artifact
|
||||
```
|
||||
|
||||
## 接口草案
|
||||
|
||||
接口归属分成项目控制面和运行时控制面。
|
||||
|
||||
```text
|
||||
POST /api/creation/web-project/projects
|
||||
GET /api/creation/web-project/projects/{projectId}/snapshot
|
||||
PATCH /api/creation/web-project/projects/{projectId}/files
|
||||
POST /api/runtime/web-project/projects/{projectId}/preview-builds
|
||||
GET /api/runtime/web-project/preview-builds/{jobId}
|
||||
GET /api/runtime/web-project/preview-builds/{jobId}/events
|
||||
```
|
||||
|
||||
`/events` 复用现有 `src/services/sseStream.ts` 的 SSE 传输层口径。事件类型建议包括:
|
||||
|
||||
- `queued`
|
||||
- `running`
|
||||
- `log`
|
||||
- `succeeded`
|
||||
- `failed`
|
||||
- `cancelled`
|
||||
- `expired`
|
||||
- `stale`
|
||||
|
||||
前端刷新恢复时,先读取项目 active snapshot 和 active preview,再按未终态 job 继续订阅 SSE。
|
||||
|
||||
## 虚拟文件系统
|
||||
|
||||
平台内的“文件系统”是虚拟工作区,不是真实目录长期挂载。
|
||||
|
||||
snapshot manifest 建议包含:
|
||||
|
||||
- `projectId`
|
||||
- `snapshotId`
|
||||
- `parentSnapshotId`
|
||||
- `ownerUserId`
|
||||
- `templateKey`
|
||||
- `files[]`
|
||||
- `createdAt`
|
||||
- `createdBy`
|
||||
- `patchSummary`
|
||||
|
||||
文件项建议包含:
|
||||
|
||||
- `path`
|
||||
- `contentDigest`
|
||||
- `sizeBytes`
|
||||
- `mediaType`
|
||||
- `encoding`
|
||||
- `executable=false`
|
||||
|
||||
路径必须 fail-closed:
|
||||
|
||||
- 只允许相对路径。
|
||||
- 拒绝绝对路径。
|
||||
- 拒绝 `..`。
|
||||
- 拒绝符号链接。
|
||||
- 拒绝超深目录。
|
||||
- 拒绝超大文件。
|
||||
- 拒绝隐藏敏感文件名,例如 `.env`、`.npmrc`、`.ssh`、`.git`。
|
||||
- MVP 只支持文本源码和少量静态资源。
|
||||
|
||||
文件内容小文本可以按 digest 存对象;大一点的 snapshot 可打包成 tar / zip artifact 放受控 artifact store。图片、音频等大资产继续走现有资产链路,不让 AI 工程随意内嵌大 Data URL。
|
||||
|
||||
## 依赖和构建
|
||||
|
||||
MVP 只支持平台预置模板依赖:
|
||||
|
||||
- React
|
||||
- Vite
|
||||
- TypeScript
|
||||
- 平台已审核的 UI / 工具依赖子集
|
||||
|
||||
`package.json` 在 MVP 中不是事实源。AI 可以提出 manifest patch,但 api-server 会重写或忽略危险字段。构建命令由平台固定,例如:
|
||||
|
||||
```text
|
||||
npm ci --ignore-scripts
|
||||
npm exec vite build
|
||||
```
|
||||
|
||||
第一版应禁止:
|
||||
|
||||
- `preinstall` / `install` / `postinstall`
|
||||
- `git:` / `file:` / `http:` 依赖。
|
||||
- 私有 registry。
|
||||
- native build。
|
||||
- 二进制下载型包。
|
||||
- AI 自定义 shell script。
|
||||
|
||||
后续若要开放依赖,必须进入“受控依赖白名单”和“隔离依赖解析服务”阶段,不混入 MVP。
|
||||
|
||||
## 预览状态机
|
||||
|
||||
```text
|
||||
draft snapshot
|
||||
-> build queued
|
||||
-> build running
|
||||
-> build succeeded
|
||||
-> build failed
|
||||
-> build cancelled
|
||||
-> build expired
|
||||
-> build stale
|
||||
```
|
||||
|
||||
状态规则:
|
||||
|
||||
- 新 snapshot 触发新 build 时,旧 running build 应取消或标记 stale。
|
||||
- 只有当前 active snapshot 的 `succeeded` job 可以成为 active preview。
|
||||
- failed / cancelled / expired / stale 不能覆盖 active preview。
|
||||
- 回滚是切回历史 snapshot 并复用对应 immutable artifact,或重新构建该 snapshot。
|
||||
- 不允许复用 runner 临时目录作为回滚来源。
|
||||
|
||||
## 后续阶段
|
||||
|
||||
### Phase 0:文档和威胁模型
|
||||
|
||||
补齐技术方案、威胁模型和验收清单,明确 MVP 不可做能力、安全门禁、状态机和 QA 口径。
|
||||
|
||||
### Phase 1:静态模板预览纵切
|
||||
|
||||
完成 `/editor/agent` 编辑器壳、固定模板、虚拟文件系统、AI patch 保存、runner 静态构建、artifact 服务和 iframe reload。
|
||||
|
||||
### Phase 2:持久任务和恢复
|
||||
|
||||
引入 `web_project_runtime_job`,按 lease / controller / worker 模式实现任务队列、取消、stale、刷新恢复和日志 SSE。
|
||||
|
||||
### Phase 3:受控依赖安装
|
||||
|
||||
支持白名单依赖、lockfile 固化、依赖层缓存、安装失败可读错误、依赖封禁和审计。
|
||||
|
||||
### Phase 4:实时体验增强
|
||||
|
||||
在安全评审后再做长驻 dev server、HMR、WebSocket 代理、端口租约和空闲回收。
|
||||
|
||||
### Phase 5:作品化发布
|
||||
|
||||
将通过安全门禁的 Web project 接入平台作品类型。发布必须重新构建 immutable artifact,不直接提升临时预览 artifact。
|
||||
|
||||
## 与现有系统的关系
|
||||
|
||||
- 前端 SSE 读取复用 `src/services/sseStream.ts`。
|
||||
- 后端 job 模式参考外部生成 worker 的 lease / controller 经验,但使用新的 Web 工程 runtime 语义。
|
||||
- `/editor/agent` 是第一版用户入口,不新增重复 IDE 页面。
|
||||
- 业务真相仍通过 server-rs + SpacetimeDB 受控写入,前端和 runner 都不能绕过控制面。
|
||||
205
docs/technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md
Normal file
205
docs/technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# AI Web 工程静态预览 MVP 验收清单
|
||||
|
||||
更新时间:`2026-06-13`
|
||||
|
||||
## 范围
|
||||
|
||||
本清单用于验收 `/editor/agent` 浏览器内 AI Web 工程编辑器 MVP。
|
||||
|
||||
MVP 必须只支持:
|
||||
|
||||
- 固定 React / Vite / TypeScript 静态模板。
|
||||
- 虚拟文件系统。
|
||||
- 结构化 AI patch。
|
||||
- 独立 runner 静态构建。
|
||||
- 独立 preview origin iframe 预览。
|
||||
- 失败保留上一版可用预览。
|
||||
|
||||
MVP 不支持:
|
||||
|
||||
- 终端 shell。
|
||||
- 后端服务。
|
||||
- HMR。
|
||||
- 任意端口代理。
|
||||
- 任意 npm 安装。
|
||||
- AI 自定义 package scripts。
|
||||
- Service Worker。
|
||||
- 主站同源预览。
|
||||
|
||||
## Happy Path
|
||||
|
||||
- [ ] 用户打开 `/editor/agent` 能看到文件树、编辑器、AI 输入区、构建日志区和预览 iframe。
|
||||
- [ ] 创建新 Web project 后得到固定模板文件树。
|
||||
- [ ] AI patch 新增或修改一个 React 组件后,api-server 生成新 snapshot。
|
||||
- [ ] 前端 debounce 后创建 preview build job。
|
||||
- [ ] runner 构建成功并产出 immutable artifact。
|
||||
- [ ] SSE 返回 `queued -> running -> succeeded`。
|
||||
- [ ] iframe 切换到新 preview URL。
|
||||
- [ ] 刷新 `/editor/agent` 后能恢复当前 project、active snapshot、active preview 和未完成 job 状态。
|
||||
|
||||
## Patch 与路径校验
|
||||
|
||||
- [ ] 相对路径普通源码修改通过。
|
||||
- [ ] 绝对路径被拒绝。
|
||||
- [ ] `..` 路径被拒绝。
|
||||
- [ ] 符号链接被拒绝。
|
||||
- [ ] `.env` 被拒绝。
|
||||
- [ ] `.npmrc` 被拒绝。
|
||||
- [ ] `.git/` 被拒绝。
|
||||
- [ ] `.ssh/` 被拒绝。
|
||||
- [ ] 超深目录被拒绝。
|
||||
- [ ] 超大单文件被拒绝。
|
||||
- [ ] 超大 snapshot 被拒绝。
|
||||
- [ ] 大 Data URL 被拒绝或转资产流程。
|
||||
- [ ] 二进制膨胀被拒绝。
|
||||
- [ ] rename 后目标路径仍需重新校验。
|
||||
|
||||
## 构建与状态机
|
||||
|
||||
- [ ] job 状态覆盖 `queued/running/succeeded/failed/cancelled/expired/stale`。
|
||||
- [ ] 新 snapshot 创建后,旧 running job 被取消或标记 stale。
|
||||
- [ ] 只有当前 active snapshot 的 `succeeded` job 能推进 active preview。
|
||||
- [ ] failed job 不覆盖上一版 active preview。
|
||||
- [ ] cancelled job 不覆盖上一版 active preview。
|
||||
- [ ] expired job 不覆盖上一版 active preview。
|
||||
- [ ] stale job 不覆盖上一版 active preview。
|
||||
- [ ] 构建失败时展示可读错误摘要和日志片段。
|
||||
- [ ] 构建超时后 runner 被 kill,job 进入 failed 或 expired。
|
||||
- [ ] runner 崩溃后 job 能恢复为可重领或 expired。
|
||||
- [ ] 页面刷新后能通过 projectId / jobId 恢复日志和状态。
|
||||
- [ ] snapshot 回滚会复用对应 immutable artifact 或重新构建,不复用临时目录。
|
||||
|
||||
## Runner 隔离
|
||||
|
||||
- [ ] runner 进程无平台密钥环境变量。
|
||||
- [ ] runner 无宿主源码目录挂载。
|
||||
- [ ] runner 无 Docker socket。
|
||||
- [ ] runner 使用非 root 用户。
|
||||
- [ ] runner 工作区为任务级临时目录。
|
||||
- [ ] 任务结束后临时目录被销毁。
|
||||
- [ ] CPU 限制生效。
|
||||
- [ ] 内存限制生效。
|
||||
- [ ] 磁盘限制生效。
|
||||
- [ ] 进程数限制生效。
|
||||
- [ ] 打开文件数限制生效。
|
||||
- [ ] 日志大小限制生效。
|
||||
- [ ] artifact 大小限制生效。
|
||||
- [ ] 任务超时限制生效。
|
||||
|
||||
## 网络隔离
|
||||
|
||||
- [ ] 构建期默认不能访问公网。
|
||||
- [ ] 若允许 registry mirror,只能访问白名单域名。
|
||||
- [ ] 不能访问 RFC1918 内网地址。
|
||||
- [ ] 不能访问云 metadata 地址。
|
||||
- [ ] 不能访问 api-server 管理端口。
|
||||
- [ ] 不能访问 SpacetimeDB。
|
||||
- [ ] 不能访问生产数据库。
|
||||
- [ ] HTTP redirect 到内网时被阻断。
|
||||
- [ ] DNS rebinding 到内网时被阻断。
|
||||
|
||||
## 依赖供应链
|
||||
|
||||
- [ ] AI 修改 `package.json` 新增普通依赖时,MVP 拒绝或忽略。
|
||||
- [ ] `preinstall` 被拒绝或忽略。
|
||||
- [ ] `install` 被拒绝或忽略。
|
||||
- [ ] `postinstall` 被拒绝或忽略。
|
||||
- [ ] `prepare` 被拒绝或忽略。
|
||||
- [ ] `git:` 依赖被拒绝。
|
||||
- [ ] `file:` 依赖被拒绝。
|
||||
- [ ] `http:` / `https:` tarball 依赖被拒绝。
|
||||
- [ ] 私有 registry 被拒绝。
|
||||
- [ ] lockfile 篡改被拒绝或重写。
|
||||
- [ ] 构建命令由平台固定,不执行 AI 写入的 shell script。
|
||||
|
||||
## Preview Token 与 Gateway
|
||||
|
||||
- [ ] preview token 绑定 owner。
|
||||
- [ ] preview token 绑定 project。
|
||||
- [ ] preview token 绑定 snapshot。
|
||||
- [ ] preview token 绑定 artifact。
|
||||
- [ ] preview token 短期有效。
|
||||
- [ ] preview token 可撤销。
|
||||
- [ ] preview token 不可枚举。
|
||||
- [ ] 跨租户访问返回 403 或 404。
|
||||
- [ ] path traversal 被拒绝。
|
||||
- [ ] 错误 MIME 不会按可执行脚本服务。
|
||||
- [ ] SPA fallback 只在 artifact 根内生效。
|
||||
- [ ] artifact 删除后旧 URL 不能继续读取。
|
||||
- [ ] token 过期后旧 URL 不能继续读取。
|
||||
- [ ] preview cache 不串租户。
|
||||
|
||||
## 浏览器隔离
|
||||
|
||||
- [ ] 预览 origin 与主站 origin 不同。
|
||||
- [ ] 预览 iframe 不带主站 cookie。
|
||||
- [ ] 预览代码不能读取主站 `localStorage`。
|
||||
- [ ] 预览代码不能读取主站 `sessionStorage`。
|
||||
- [ ] 预览代码不能调用主站认证 API。
|
||||
- [ ] iframe sandbox 不允许 top navigation。
|
||||
- [ ] iframe sandbox 不允许 downloads。
|
||||
- [ ] iframe sandbox 不允许 popups。
|
||||
- [ ] iframe sandbox 不允许 clipboard。
|
||||
- [ ] iframe sandbox 不允许 camera / mic。
|
||||
- [ ] CSP 禁止未白名单 `connect-src`。
|
||||
- [ ] Service Worker 被禁用。
|
||||
|
||||
## 日志与错误
|
||||
|
||||
- [ ] 构建日志按 jobId 分段展示。
|
||||
- [ ] 日志限长。
|
||||
- [ ] 错误摘要可读。
|
||||
- [ ] 日志不包含平台 token。
|
||||
- [ ] 日志不包含 OSS 写签名。
|
||||
- [ ] 日志不包含完整宿主路径。
|
||||
- [ ] 日志不包含环境变量 dump。
|
||||
- [ ] SSE 断开后前端可重新拉取 job 状态。
|
||||
|
||||
## 取消、回滚与并发
|
||||
|
||||
- [ ] 用户连续编辑触发多个 snapshot 时,只保留最新 snapshot 的 active build 候选。
|
||||
- [ ] 用户手动取消当前 build 后,runner 停止或 job 被标记 cancelled。
|
||||
- [ ] 两个标签页同时编辑同一项目时,有明确版本冲突或 last-write 策略提示。
|
||||
- [ ] 回滚到历史 snapshot 后,active preview 对应历史 snapshot。
|
||||
- [ ] 历史 artifact 缺失时可重新构建。
|
||||
- [ ] 构建队列积压时 `/editor/agent` 显示确定状态,不假装实时完成。
|
||||
|
||||
## Artifact GC
|
||||
|
||||
- [ ] 未引用的 failed artifact 会清理。
|
||||
- [ ] 过期 preview artifact 会清理。
|
||||
- [ ] active preview artifact 不会被误删。
|
||||
- [ ] 历史 snapshot 的可回滚 artifact 按保留策略保留或可重建。
|
||||
- [ ] GC 后 preview gateway 对已删除 artifact 返回确定错误。
|
||||
|
||||
## 最小自动化验证建议
|
||||
|
||||
后端 / runner:
|
||||
|
||||
```bash
|
||||
cargo test -p api-server web_project --manifest-path server-rs/Cargo.toml
|
||||
cargo test -p spacetime-module web_project --manifest-path server-rs/Cargo.toml
|
||||
```
|
||||
|
||||
前端:
|
||||
|
||||
```bash
|
||||
npm run test -- src/services/sseStream.test.ts
|
||||
npm run test -- src/components/editor/agent
|
||||
npm run typecheck
|
||||
npm run check:encoding
|
||||
git diff --check
|
||||
```
|
||||
|
||||
浏览器 smoke:
|
||||
|
||||
```text
|
||||
打开 /editor/agent
|
||||
创建模板项目
|
||||
提交一次 AI patch
|
||||
等待静态构建成功
|
||||
确认 iframe 展示新预览
|
||||
提交一次故意破坏构建的 patch
|
||||
确认错误出现且上一版预览仍保留
|
||||
刷新页面确认项目、日志和 active preview 可恢复
|
||||
```
|
||||
Reference in New Issue
Block a user