docs(游戏共创): M2b 证据表与里程碑改为冻结点的实测数字(含补强证据与已知留白)
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- 技术方案 §5.2 三处过期数字按**自己去代码里数一遍**的结果改:校验器单测 16 → **19**(`project_bundle::tests`)、模块总测 69 → **73**、e2e 由「46 项 46 PASS」改为「**51 个 `check(` 调用点**」(复核另按 72 条断言计,差值来自同一 `check` 内的多条子断言);「`package.rs` 零改动」改为「已同批补强(读取层按声明封顶 + 压缩比分母改为条目压缩字节,+70/−4)」;客户端行补「打包器 **24 条单测**、上传前按 `max_archive_bytes` 预检、定向 `cargo test -- project_bundle` **24 passed**」 - 技术方案 §5.2 新增四行:**校验器对抗性补强(P0-a/b/c)**(凭据目录/前缀/后缀/全名 + 小文本内容嗅探、嵌套包扩展名并集 + magic 嗅探、读取层封顶、新错误变体 `CredentialDirectoryNotAllowed` / `SecretContentDetected`)、**执行级交叉证据(P1-b)**(dev-dependency 交叉测试)、**一致性门禁路径形状维度(P1-a)**(含「故意破坏会红」实测)、**已知留白** - 里程碑:`前置依赖 ②` 里「客户端预检属待办」改为**已完成**并给出代码位置(`write_project_bundle_zip` 内 `bytes.len() > limits.max_archive_bytes` 失败关闭);证据行与验收项重数、并补齐补强后的新增规则;新增「已知留白」条目(`.bz2`/`.xz`/`.zst`、`*.db`/`*.sql`、`.gcloud`/`.azure`/`.password-store`、CP437 条目名与 `external_attributes == 0`、峰值内存未量测、内容嗅探误报率无实测) - 本块在 rebase 之后完成,基线为新的 merge-base `21c79f54a` - 门禁:`check:doc-index` 248 份 OK;`check:encoding` 5305 files OK;`git diff --check` 0
This commit is contained in:
@@ -36,7 +36,7 @@
|
||||
- 「游戏共创授权与血缘」里程碑已通过验收。
|
||||
- 「成品包改造闭环」里程碑已通过验收(内容下发入口、客户端建项基座、发布携带来源、授权面板均在那一里程碑落地)。
|
||||
- **前置依赖 ①「工程包下载/授权」接口——已落地(2026-10-05)**:`GET /games/{gameId}/fork-source` 在有工程包时返回 `source: "project"`,本体的独立路径 `GET /games/{gameId}/fork-source/project` 与成品包共用同一套鉴权与校验函数(不重复实现),**不下发对象键**、只服务当前公开版本、`no-store`。读取复用现役 OSS 读路径,**缓存键含资产维度**(对象键 `…/{version_id}.project.zip` 与 `…/{version_id}.zip` 不同),两份资产不串味。
|
||||
- **前置依赖 ②包内禁项与规模上限——已定稿(2026-10-05)**:服务端 `module-game-distribution/src/project_bundle.rs` 的 `validate_project_bundle_zip` 独立复核,拒绝清单与上限见技术方案 §3.5.2。**512 MiB(客户端打包上限)与 200 MiB(服务端/反代放行量)的不对齐按下述口径收敛**:服务端上限与发行包逐项相等(200 MiB / 展开 500 MiB / 单文件 64 MiB / 条目 10 000 / 压缩比 100),因此**客户端在上传前必须按服务端上限预检**(客户端打包器的 512 MiB 只是本机护栏),超限包应在本地就被拦下而不是上传到一半被拒——这一条属客户端待办。
|
||||
- **前置依赖 ②包内禁项与规模上限——已定稿并已落地(2026-10-05)**:服务端 `module-game-distribution/src/project_bundle.rs` 的 `validate_project_bundle_zip` 独立复核,拒绝清单与上限见技术方案 §3.5.2。**512 MiB(客户端打包上限)与 200 MiB(服务端/反代放行量)的不对齐按下述口径收敛**:服务端上限与发行包逐项相等(200 MiB / 展开 500 MiB / 单文件 64 MiB / 条目 10 000 / 压缩比 100)。**客户端预检已完成**(原先属待办):打包器在产出后即按 `limits.max_archive_bytes` 失败关闭(`apps/ai-game-creator-shell/src-tauri/src/project_bundle.rs` 的 `write_project_bundle_zip` 内 `bytes.len() > limits.max_archive_bytes` 分支),超限包在本地就被拦下,不会「上传到一半被拒」;单文件 / 累计展开 / 条目数更是在压缩前就检查。
|
||||
- 客户端已有的「从包安装并建项」基座与包内容门禁可直接复用;本里程碑不新建第二套解压或建项路径。
|
||||
- 决策:产品已同意引入工程源包,且**不设**独立的「源码可见性」授权位(口径见文首)。
|
||||
|
||||
@@ -44,14 +44,15 @@
|
||||
|
||||
- 服务端:块 A `c6bd2c081`(三列 + 契约 + 绑定)、块 B `9403a47c0`(工程包校验器)、块 C+D `d061b2e37`(上行 5 条路由 + 下行优先 + 缓存资产维度);文档块 E `3aa145d91`。
|
||||
- 客户端:`4d070dd1a`(规则对齐 + `check-project-bundle-policy-parity` 机器门禁)、`5dd9d1c5d`(发布时上传,失败不阻断)、`72f08c264`(取件按 `source` 分支建项 + 来源记录 v2 兼容 v1)、`da24dd5be`(取件提示按形态如实措辞);`ff763abe5`(端到端验收脚本)。
|
||||
- 证据:`npm run check:game-distribution-project-bundle-e2e` **46/46**;`npm run check-project-bundle-policy-parity` OK;`cargo test -p module-game-distribution` 69 passed(含校验器 16);`cargo test -p api-server game_distribution` 60 passed;AGC `vitest` 1961 passed、定向 `cargo test` 17 + 16 passed。
|
||||
- 证据(按冻结点实测重数):`npm run check:game-distribution-project-bundle-e2e` **51 个 `check(` 调用点**;`npm run check-project-bundle-policy-parity` OK(服务端 51 条规则全被客户端覆盖 + 路径形状维度);`cargo test -p module-game-distribution` **73 passed**(其中 `project_bundle::tests` **19 条**);`cargo test -p api-server game_distribution` 60 passed;AGC `vitest` 1961 passed、客户端打包器 `cargo test -- project_bundle` **24 passed**(含 dev-dependency 执行级交叉测试)。
|
||||
- **未做(含原因)**:① 已公开版本的补传(backfill)——按产品口径不做,阶段门与发行包确认一致;② 真实 >200 MiB / 10 001 条目 / 极端半包续传边界——未测(成本高,靠常量单测钉住);③ AGC 真机取件→建项链路——未跑(需要真机客户端窗口);④ 结构化埋点事件——未接(待产品定事件口径)。
|
||||
- **已知留白(对抗性复核后如实登记,别当成已全覆盖)**:归档格式未纳入 `.bz2` / `.xz` / `.zst`;数据库转储 `*.db` / `*.sql` 未纳入;凭据目录未纳入 `.gcloud` / `.azure` / `.password-store`;zip 条目名的 CP437 解码与 `external_attributes == 0` 的条目未覆盖;「爆炸包峰值内存」只有机制级封顶 + 常量单测,未做真实量测;内容嗅探的误报率无实测数据(只做了「不误报」的反例:`.sshrc` / `Dockerfile` / 非私钥证书块 / `AKIA` 长度不足 / 二进制扩展名不扫)。
|
||||
|
||||
## 验收标准
|
||||
|
||||
> 标注:✅ = 已落地并有证据(单测 / 门禁 / 端到端脚本,本地);⏳ = 仍待补(真机或极端边界);⏭ = 明确不做。
|
||||
|
||||
- [x] ✅ 工程包校验器拒绝清单齐备:绝对路径、`..`、盘符、反斜杠、通配符、符号链接、加密条目、嵌套 `.zip`、完全重复/大小写折叠重复路径、任意层级 `node_modules` / `.git` / `.svn` / `.agent`、根级 `dist` / `build` / `library` / `temp` / `local` / `.idea` / `.vscode`、凭据与隐私文件(`.env*`、`*.pem`、`*.key`、`*.p12`、`*.pfx`、`.npmrc`、`.netrc`、`.git-credentials`、`id_rsa*`、`id_ed25519*`、`*.map`)、单文件/累计/压缩比超限;合法源码包(**不含** `index.html`)通过。证据:`module-game-distribution` 16 条校验器单测 + `check-project-bundle-policy-parity`(客户端打包器与它逐项对齐)。
|
||||
- [x] ✅ 工程包校验器拒绝清单齐备:绝对路径、`..`、盘符、反斜杠、通配符、符号链接、加密条目、**嵌套归档扩展名并集(`.zip`/`.tar`/`.gz`/`.tgz`/`.7z`/`.rar`/`.jar`/`.whl`/`.nupkg`)与 magic bytes 嗅探(改名也拦)**、完全重复/大小写折叠重复路径、任意层级 `node_modules` / `.git` / `.svn` / `.agent` / **`.aws` / `.ssh` / `.kube` / `.docker` / `.gnupg` / `.terraform` / `.secrets`**、根级 `dist` / `build` / `library` / `temp` / `local` / `.idea` / `.vscode`、凭据与隐私文件(`.env*`、`*.pem`、`*.key`、`*.p12`、`*.pfx`、`.npmrc`、`.netrc`、`.git-credentials`、`id_rsa*`、`id_ed25519*`、**`credentials*` / `id_ecdsa*` / `id_dsa*` / `terraform.tfstate*` / `service-account*` / `*.jks` / `*.keystore` / `*.ppk` / `*.p8` / `*.kdbx` / `*.der` / `.htpasswd` / `.pgpass` / `*.map`**)、**小体积文本条目的内容嗅探(PEM 私钥块 / `AKIA…` / `ghp_` / `github_pat_` / `xox*`)**、单文件/累计/压缩比超限;合法源码包(**不含** `index.html`)通过。证据:`module-game-distribution` **19 条校验器单测**(+ `package.rs` 6 条)与 `check-project-bundle-policy-parity`(客户端打包器与它逐项对齐)。
|
||||
- [x] ✅ 同一版本重复确认同一工程包不产生第二份对象(同内容幂等重放);换内容重传按冲突拒绝(409 `同一版本已存在不同的工程源包`)。证据:模块事务 + `check-game-distribution-project-bundle-e2e` A4。
|
||||
- [x] ✅ 阶段门:只有 `awaiting_upload` / `upload_failed` 可写;已上传、验证中、待审核、已拒绝、已公开、已撤回、已取消一律拒绝。**顺序按实现**:「已存在工程包」先判(409 `PROJECT_BUNDLE_ALREADY_EXISTS`),故「已公开且已有工程包」返回 `ALREADY_EXISTS`;「已公开但无工程包」才返回 `UPLOAD_NOT_ALLOWED`。证据:e2e A5 / A5b + api-server 单测。
|
||||
- [x] ✅ 非作者上传 → **404**(按「版本不存在」处理,与发行包上行族同口径,不泄露版本存在性),未带 Bearer → 401。证据:e2e A6 + 路由级 401 单测。
|
||||
|
||||
@@ -532,12 +532,16 @@ A 路线里有一个必须提前知道的互斥点:`create_npm_scaffold` 的
|
||||
| 项 | 证据 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| 数据模型与契约 | 版本行三列(`project_bundle_object_key` / `_bytes` / `_sha256`)落地;`GameDistributionPrivateVersion` 两侧同步(对象键不下发);生成绑定只保留 `module_bindings/game_distribution*` 与 `module_bindings.rs`,其余漂移还原 | ✅ 已验证(DTO parity 42 组、`check:spacetime-schema` 92 tables、`cargo check --all-targets` 0) |
|
||||
| 工程包校验器 | `module-game-distribution` 的 `validate_project_bundle_zip`:拒绝清单见 §3.5.2,上限与发行包**逐项相等**(测试用 `assert_eq!` 钉住);`package.rs` 零改动 | ✅ 已验证(16 条单测;`cargo test -p module-game-distribution` 69 passed) |
|
||||
| 工程包校验器 | `module-game-distribution` 的 `validate_project_bundle_zip`:拒绝清单见 §3.5.2,上限与发行包**逐项相等**(测试用 `assert_eq!` 钉住);`package.rs` 已同批补强(读取层按声明大小封顶 + 压缩比分母改为条目压缩字节,+70/−4) | ✅ 已验证(**19 条校验器单测**;`cargo test -p module-game-distribution` **73 passed**) |
|
||||
| 上行路由族 | 5 条路由(整包 / upload-state / chunk / complete / reset);对象键服务端派生且与发行包不同;唯一共享阶段门;`complete` 独立复核 zip + 422 映射 | ✅ 已验证(`cargo test -p api-server game_distribution` **60 passed**,含 6 条路由与阶段门/资产选择用例) |
|
||||
| 下行优先与缓存 | `source` 取 `Project`(优先)/`Package`;半写行回落 `package`;`/fork-source/project` 无工程包时 409 而非静默回落;缓存键 = 对象键(资产维度) | ✅ 已验证(同一批 api-server 用例:对象键互不相等 + `ReleasePackageCache` 双资产不串味) |
|
||||
| 端到端(真实本地栈 + 真实客户端链路) | `npm run check:game-distribution-project-bundle-e2e` → **46 项 46 PASS**(含 A5 已公开且已有工程包 → `ALREADY_EXISTS`、A5b 已公开但无工程包 → `UPLOAD_NOT_ALLOWED`、A6 非作者 → **404** 且响应不含对象键) | ✅ 已验证 |
|
||||
| 端到端(真实本地栈 + 真实客户端链路) | `npm run check:game-distribution-project-bundle-e2e` → **51 个 `check(` 调用点**(复核另按 72 条断言计,差值来自同一 `check` 内的多条子断言;含 A4 / A5 / A5b / A6 与对抗用例:路径形状、凭据目录、嵌套包改名、符号链接、声明说谎) | ✅ 已验证 |
|
||||
| 两端规则一致(机器门禁) | `npm run check-project-bundle-policy-parity` → OK(客户端打包器与服务端校验器的排除清单/上限逐项对齐,防止两端口径漂移) | ✅ 已验证 |
|
||||
| 客户端 | AGC 打包器(确定性打包)、发布时上传(失败不阻断)、按 `source` 分支的取件建项、来源记录 v2(兼容 v1);`npx vitest run apps/ai-game-creator-shell/tests` **1961 passed**;AGC 定向 `cargo test` 17 + 16 passed | ✅ 已验证 |
|
||||
| 客户端 | AGC 打包器(确定性打包,**24 条单测**,含路径形状检查)、发布时上传(失败不阻断,且**上传前按 `max_archive_bytes` 预检**)、按 `source` 分支的取件建项、来源记录 v2(兼容 v1);`npx vitest run apps/ai-game-creator-shell/tests` 1961 passed(补强前一次的运行;本次未改前端 TS);AGC 定向 `cargo test -- project_bundle` **24 passed** | ✅ 已验证 |
|
||||
| 校验器对抗性补强(P0-a/b/c) | 凭据识别补强(目录 `.aws` / `.ssh` / `.kube` / `.docker` / `.gnupg` / `.terraform` / `.secrets`;前缀 `credentials` / `id_ecdsa` / `id_dsa` / `terraform.tfstate` / `service-account`;后缀 `.jks` / `.keystore` / `.ppk` / `.p8` / `.kdbx` / `.der`;全名 `.htpasswd` / `.pgpass`,外加小文本内容嗅探 PEM / `AKIA…` / `ghp_` / `xox*`);嵌套包扩展名并集 + magic bytes 嗅探(改名也拦);读取层按声明大小封顶 + 压缩比分母改为条目压缩字节;新增错误变体 `CredentialDirectoryNotAllowed` / `SecretContentDetected` | ✅ 已验证(19 条校验器单测;**发行包回归** `cargo test -p api-server game_distribution` 60 passed,含新增「声明说谎 → `ReadFailed`」) |
|
||||
| 执行级交叉证据(P1-b) | 客户端 crate 用 dev-dependency 依赖 `module-game-distribution`:新测试用**真实打包器**产出字节喂服务端 `validate_project_bundle_zip` 断言接受(并逐项比对摘要 / 字节数 / 条目顺序),再手造含 `.env` / `node_modules` 的包断言服务端独立拒绝 | ✅ 已验证(AGC `cargo test -- project_bundle` 24 passed) |
|
||||
| 一致性门禁(P1-a) | `check-project-bundle-policy-parity` 新增**路径形状**维度:服务端 `normalize_archive_path` 的哨兵段 / 结尾字符 / 禁止字符必须被客户端常量逐 token 覆盖,并单独钉「两侧都必须显式拒 `/` 开头与 `\`」;空集合一律报错 | ✅ 已验证(服务端 51 条规则全被客户端覆盖;「故意改一侧会红」已实测) |
|
||||
| 已知留白 | 未纳入的归档格式(`.bz2` / `.xz` / `.zst`)与数据库转储(`*.db` / `*.sql`)、未纳入的凭据目录(`.gcloud` / `.azure` / `.password-store`)、CP437 条目名与 `external_attributes == 0` 的条目、以及「爆炸包峰值内存」未做真实量测 | ⏳ 已知限制(详见 §3.5.2 与里程碑「已知留白」) |
|
||||
| 已公开版本的补传(backfill) | **未做**(按产品口径不做:阶段门与发行包确认一致,公开后不接受内容写入),文档三处已如实写明 | ⏭ 明确不做 |
|
||||
| 真实边界(>200 MiB / 10 001 条目 / 半包续传的极端组合)与结构化埋点事件 | 未跑/未接:边界靠常量与单测钉住,结构化埋点(`game_fork_source_downloaded` 等)待产品定口径 | ⏳ 待补 |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user