写入对象存储时下发对象级 ACL,修掉发行包可匿名直取
Project CI / AI game creator shell Rust crates (push) Successful in 1m30s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m52s
Project CI / Backend tests (push) Successful in 4m55s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m4s
Project CI / Native shell tests (push) Successful in 6m20s
Project CI / Frontend tests (push) Successful in 2m11s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m6s
Project CI / AI game creator shell web tests (push) Successful in 1m40s
Project CI / Repository checks (push) Successful in 2m12s
Project CI / AI game creator shell Rust crates (push) Successful in 1m30s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m52s
Project CI / Backend tests (push) Successful in 4m55s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m4s
Project CI / Native shell tests (push) Successful in 6m20s
Project CI / Frontend tests (push) Successful in 2m11s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m6s
Project CI / AI game creator shell web tests (push) Successful in 1m40s
Project CI / Repository checks (push) Successful in 2m12s
- platform-oss 的内部 PUT 与分片追加显式下发 x-oss-object-acl(Private 为 private、Public 为 public-read),OssAppendInternalObjectRequest 增加 access 字段 - 直传 policy 增加 x-oss-object-acl 条件,表单字段与 shared-contracts 的 DirectUploadTicketFormFields 同步新增并透传到 ticket 响应 - 媒体链路 E2E 增加匿名直取直传封面与发行包两条探测:默认 WARN,E2E_REQUIRE_PRIVATE_BUCKET=1 时按失败处理 - 里程碑第 7 条、pitfalls 与 decision-log 记录根因、修复与复验(严格模式 65 项 PASS,两处匿名直取都 403)
This commit is contained in:
@@ -188,4 +188,4 @@
|
||||
- 已勾选(第 4 条:游客目录、详情和启动接口只返回已公开投影;未公开和已下架状态均不可见、不返回私有快照地址)——本轮补齐
|
||||
- 待审期间公开目录不含该游戏、封面没有匿名读授权(第 1 条取证里已跑);**本轮**补下架半边:作者 `unpublish` 成功后公开目录不含该游戏、`GET /api/game-distribution/games/{id}` 返回 404、发行网关 `GET /api/game-distribution/releases/{id}/index.html` 返回 404,且目录与详情的响应文本里没有 `aliyuncs.com` / `oss-cn` / `Signature=` 这类对象存储地址。- 仍未勾选(阶段 B 其余条目)
|
||||
- 第 5/6 条(每游戏独立 HTTPS origin、iframe sandbox、CSP/CORS/MIME/禁止 Worker、外站请求阻断):需要真实域名与真实浏览器,本轮未做;api-server 侧只有同源网关单测(`release_gateway_is_mounted_and_never_serves_cookie_bearing_requests`、`release_response_sets_nosniff_and_scopes_csp_to_html`、`release_response_allows_opaque_sandbox_asset_loads`)。
|
||||
- 第 7 条(下架会关闭新启动与发行读取、不能绕过网关直取公开 OSS、撤销传播在最大缓存窗口内):前半句本轮已取证——作者 `unpublish` 与管理员 `suspend` 之后公开目录不含该游戏、详情 404、发行网关 404,`restore` 后重新可玩。**仍缺**:①「不能绕过网关直取公开 OSS」在当前环境**不成立**:匿名 `GET https://agc-dev.oss-rg-china-mainland.aliyuncs.com/agc/project-snapshots/v1/game-distribution/<gameId>/<versionId>.zip` 返回 **200**(同 bucket 不存在的 key 是 404),根因是 `platform-oss` 的 `put_internal_object_bytes` 收了 `OssObjectAccess` 却从不发送 `x-oss-object-acl`,对象继承 bucket 默认 ACL;脚本默认把这条打成 `WARN`,`E2E_REQUIRE_PRIVATE_BUCKET=1` 时转成硬失败(本轮两种模式都实测)。②「撤销传播符合最大缓存窗口」需要真实 CDN/TTL,本轮未做。
|
||||
- 第 7 条(下架会关闭新启动与发行读取、不能绕过网关直取公开 OSS、撤销传播在最大缓存窗口内):前半句本轮已取证——作者 `unpublish` 与管理员 `suspend` 之后公开目录不含该游戏、详情 404、发行网关 404,`restore` 后重新可玩。①「不能绕过网关直取公开 OSS」**本轮已修并复验**:先用同一条探测证明匿名直取发行包返回 200(同 bucket 不存在的 key 是 404),根因是 `platform-oss` 收了 `OssObjectAccess` 却从不发送 `x-oss-object-acl`、对象继承 bucket 默认 ACL;现在内部 PUT、分片追加(`OssAppendInternalObjectRequest` 新增 `access`)与直传 policy/表单(`DirectUploadTicketFormFields` 新增 `x-oss-object-acl`)都显式下发对象级 ACL,`E2E_REQUIRE_PRIVATE_BUCKET=1` 下匿名直取直传封面与发行包都返回 **403**(65 项 PASS)。②「撤销传播符合最大缓存窗口」需要真实 CDN/TTL,本轮未做。
|
||||
|
||||
@@ -9696,5 +9696,5 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 决策(确定失败与未知结果的分界):结构合法但摘要/体积/文件数/入口与声明不符 → `409 PACKAGE_MISMATCH`,版本落 `upload_failed` 且 `recoveryAction=reupload`;中断、进程退出与并发落败都停在原版本 `awaiting_upload`,靠 `upload-state` 的权威偏移继续,不落成失败态。
|
||||
- 决策(错误 envelope 与成功 envelope 同一份 meta):`AppError::into_response` 之前固定按「无请求上下文」构造错误响应,错误 envelope 里没有 `meta.requestId` / `meta.operation`,而成功 envelope 有,客户端在报错时拿不到可用于排查的 requestId。现在 `attach_request_context` 用 `tokio::task_local!` 的 `CURRENT_REQUEST_CONTEXT` 把上下文作用域套住整个 handler,错误转换读同一份上下文;脱离请求任务(单测、后台任务)时退回无上下文形状。取证:`check:game-distribution-owner-isolation` 新增成功/失败 envelope 两条断言,修复前 `requestId=` 为空失败、修复后 23 项 PASS。
|
||||
- 决策(envelope 一致性按可消费取证):TS 侧没有 envelope 的类型镜像,只有 `packages/shared/src/http.ts` 与 `src/services/apiClient.ts` 的运行时守卫,因此这一条按「客户端能一致消费真实 envelope」取证(字段名 `ok` / `data` / `error.code` / `meta.apiVersion` / `meta.requestId`),不是类型级镜像;字段门禁比对的是名字而不是值类型。- 影响范围:`server-rs/crates/api-server/src/modules/game_distribution.rs`、`scripts/check-game-distribution-owner-isolation.mjs`、`scripts/check-game-distribution-upload-safety.mjs`、`scripts/check-game-distribution-upload-resume.mjs`、`package.json`、游戏分发里程碑取证。
|
||||
- 验证:本地真实栈三个脚本全部 PASS(越权隔离 23 项、上传安全 24 项、分片续传 prepare 8 项 + resume 9 项、媒体链路 44 项);分片续传中途杀掉 api-server 进程(PID `53844` → 重启 `7728`)后仍从 `8,388,608` 偏移续传成功;`cargo test -p api-server -- package_` 6 passed 与 `game_distribution` 27 passed、`npm run lint`、`check:encoding`、`check:doc-index`、`git diff --check`。
|
||||
- 决策(写入必须显式下发对象级 ACL):`platform-oss` 的 `OssObjectAccess` 之前只用于日志,对象继承 bucket 默认 ACL,公共读 bucket 上「private」对象可被匿名直取。现在内部 PUT、分片追加与直传 policy / 表单三处都下发 `x-oss-object-acl`(`Private` → `private`,`Public` → `public-read`);`OssAppendInternalObjectRequest` 新增 `access`,`DirectUploadTicketFormFields` 新增 `x-oss-object-acl`。验证:`cargo test -p platform-oss` 76 passed;`E2E_REQUIRE_PRIVATE_BUCKET=1` 的媒体链路 E2E 里直传封面与发行包对象匿名直取都 403(65 项 PASS)。- 验证:本地真实栈三个脚本全部 PASS(越权隔离 23 项、上传安全 24 项、分片续传 prepare 8 项 + resume 9 项、媒体链路 44 项);分片续传中途杀掉 api-server 进程(PID `53844` → 重启 `7728`)后仍从 `8,388,608` 偏移续传成功;`cargo test -p api-server -- package_` 6 passed 与 `game_distribution` 27 passed、`npm run lint`、`check:encoding`、`check:doc-index`、`git diff --check`。
|
||||
- 边界:证据来自本机 dev 栈与 dev bucket;生产域名、CDN 缓存窗口与真实客户端安装版的自动上传仍未验证。
|
||||
@@ -6059,4 +6059,4 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **根因**:`server-rs/crates/platform-oss/src/lib.rs` 的 `put_internal_object_bytes` 收下 `OssObjectAccess` 却只把它交给 `oss_access_label` 写日志,PUT 请求从不带 `x-oss-object-acl`;对象 ACL 因此继承 bucket 默认值(dev 的 `agc-dev` 是允许匿名读的)。
|
||||
- **判据**:`npm run check:game-distribution-media-e2e` 默认把这条打印成 `WARN`(本地 dev bucket 允许匿名读是环境现状),设 `E2E_REQUIRE_PRIVATE_BUCKET=1` 时按失败处理;本轮两种模式都实测过(默认 63 PASS + WARN,强制模式该条 FAIL)。
|
||||
- **处理**:上线前必须 ① 确认生产 bucket 不是 public-read;② 给 `platform-oss` 的内部 PUT、分片追加与直传策略补对象级 ACL(或干脆在 bucket 策略层对 `agc/project-snapshots/**` 收紧)。
|
||||
- **影响面**:所有经 `platform-oss` 写入「private」对象的链路(游戏发行包、AGC 项目快照、后台导出)在公开 bucket 上都继承公开读。
|
||||
- **已修(2026-09-28)**:`platform-oss` 现在在内部 PUT(`build_put_object_headers` 收 `access`)、分片追加(`OssAppendInternalObjectRequest` 新增 `access`)与直传 policy / 表单(`DirectUploadTicketFormFields` 新增 `x-oss-object-acl`,policy 里同值做条件约束)三处都显式下发对象级 ACL(`Private` → `private`,`Public` → `public-read`)。复验:`cargo test -p platform-oss` 76 passed;`E2E_REQUIRE_PRIVATE_BUCKET=1 npm run check:game-distribution-media-e2e` 下匿名直取直传封面与发行包对象都返回 **403**(65 项 PASS)。存量对象仍是旧的 bucket 默认 ACL,需要按需回填或换键。- **影响面**:所有经 `platform-oss` 写入「private」对象的链路(游戏发行包、AGC 项目快照、后台导出)在公开 bucket 上都继承公开读。
|
||||
Reference in New Issue
Block a user