Files
Genarrative/local-docs/【修复记录】Issue226跨Origin重定向标记泄漏修复-2026-09-02.md
T
lhk229 65d7a57eb7
Project CI / Frontend tests (push) Successful in 4m0s
Project CI / Repository checks (push) Successful in 4m4s
Project CI / Backend tests (push) Successful in 8m40s
Project CI / Native shell tests (push) Successful in 17m56s
Project CI / Frontend tests (pull_request) Successful in 5m16s
Project CI / Repository checks (pull_request) Successful in 6m57s
Project CI / Backend tests (pull_request) Successful in 6m31s
Project CI / Native shell tests (pull_request) Successful in 16m57s
AGC对主站的请求添加header (#241)
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/241
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-02 14:39:01 +08:00

4.7 KiB
Raw Blame History

Issue #226:跨 origin 重定向标记泄漏复审修复

更新时间:2026-09-02 关联 Issue#226 添加客户端特殊标识;交接 Issue#225 添加客户端埋点统计

1. 修复结论

采纳评审意见:AGC 主站 Client 的默认重定向策略不能继续使用 reqwest 的“任意 origin 默认跟随”,否则 default_headers 中的 X-Genarrative-Client: agc 可能被复制到 OSS 或第三方 origin。

本次修复将工厂默认策略调整为:

  • 目标 URL 与原始请求 URL 的完整 originscheme + host + effective port)相同:委托 reqwest 默认 redirect policy。
  • origin 不同:stop,返回当前 3xx,不发起下一跳请求。
  • 无法取得原始 URL 或 origin 无法确认:按 fail-closed 处理,停止重定向。

这不是把 factory 改成全局 Policy::none()。同 origin 的 redirect 仍保留原有默认限制、方法和请求体处理;调用点显式设置的 Policy::none() 继续有效。

另针对评审发现的同名 Header 覆盖缺口,补充了主站请求终结器:default_headers 继续负责普通请求的默认注入;所有已审计的 AGC 主站请求在发送前统一调用 with_agc_main_site_marker,使用 RequestBuilder::headers 替换调用方可能传入的同名 Header,确保最终值固定为 agc

2. 修改范围

修改 AGC Rust HTTP Client factory、主站请求终结器、已审计主站直发调用点和定向测试:

  • apps/ai-game-creator-shell/src-tauri/src/http_client.rs
  • 已审计主站请求所在的 assets.rscommands.rscanvas_generation.rsdirect_runtime.rsdirect_tool_bridge.rsproject/asset_canvas/generation.rsproject/resource_editor.rs
  • 本地实施方案、阶段计划和本复审记录

不修改:

  • #225 的主站 tracking、数据库和后台代码。
  • OSS/签名下载、Provider、受控搜索、loopback、更新下载等第三方 Client。
  • 各业务调用点的 timeout、connect timeout、认证、幂等或请求体逻辑。

3. 重定向行为契约

场景 处理 标记是否到达下一跳
同 scheme、host、effective port 继续按 reqwest 默认策略 是,仍是主站 origin
scheme、host 或端口任一不同 返回当前 3xx,不 follow 否,不发起请求
调用点显式 Policy::none() 继续不 follow

使用 Url::origin() 比较,不比较 URL 字符串前缀;路径、查询参数和 fragment 不参与 origin 判断。

4. 同名 Header 覆盖契约

reqwestClientBuilder::default_headers 只会为请求补充缺失字段,请求级同名 Header 默认优先。因此不能把 default_headers 单独当作“不可伪造”的约束。

当前实现由两层组成:

  • factory 设置 default_headers,覆盖普通未显式设置 Header 的请求;
  • with_agc_main_site_marker(request) 在主站请求发送前用 RequestBuilder::headers 替换同名字段。external_editor_json_request 已内置该终结器,直接 .send() 的主站路径也显式经过该终结器。

这样既保留方案一的 Client factory 形态,又满足“调用方传入同名 Header 时最终仍为 X-Genarrative-Client: agc”的冻结契约。

5. 测试证据

新增或调整 http_client 定向测试,覆盖:

  • 完整 origin 比较:默认端口等价,scheme/host/非默认端口变化视为跨 origin。
  • 同 origin 302:下一跳实际收到 X-Genarrative-Client: agc,最终响应成功。
  • 跨 origin 302:当前响应为 302,外部 listener 没有收到连接。
  • 显式 Policy::none():仍然覆盖 factory 默认策略。
  • 请求级伪造同名 Header:终结器覆盖调用方值,服务端只收到 agc

验证命令及结果:

cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml http_client -- --nocapture
8 tests passed

6. 对 #225 的交接

本修复不改变 Header 契约:

X-Genarrative-Client: agc

主站只会收到客户端实际发出的主站请求;跨 origin 3xx 不会产生第二个第三方请求,因此不会出现客户端把该标记发送到 OSS/第三方 origin 的情况。#225 不需要回改 tracking、数据库或后台设计。

7. 后续限制

agc_main_site_client_builder() 仍返回原始 reqwest::ClientBuilder,理论上调用方可以再次覆盖 redirect policy,或新增请求时绕过终结器。当前已审计生产调用点均已经过终结器,只有显式 Policy::none() 的 redirect 覆盖,没有重新启用任意 origin follow 的调用。若未来需要类型级不可绕过,再单独评估 AgcMainSiteClient wrapper,不在本次复审修复中扩大范围。