games-development-ai/.agents/rules/security-and-reliability.md
zizi d97e194383 docs(治理): SoT注册表+docs-gate六检门,清缩历史档126→69
- 注册表:docs/architecture/README.md §2,37 份 canonical(frontmatter topic+canonical:true)与表双向机器对账
- 门:.agents/tools/docs-gate.py 六检(品牌根/canonical唯一/死链/入口卫生/留痕隔离/设计档申报),挂 .githooks/pre-commit(本仓已激活)+ .gitea/workflows/docs-gate.yml(待runner)+ wave-close 第8步;旧 check-deadlinks.sh 退役并入 G3
- 清缩:删 54 项历史档(plans 16/agent-specs 设计与spike 20/brainstorms 5/memorys 3/goals+作战清单完成史归档/王蓝莓design/SAA现状html/add-game-template);channel-spike 564K 代码资产迁仓级 spikes/;六件删前蒸馏已迁(代码评审16条open项→进度总账§5、prefix-cache字段表→cheap-model skill、意图基线29条→需求清单附录、九门降级rationale→验收门、A11 TODO→tech-decisions、3layer边界→littlejs-game-dev 指针)
- 修口径约90处:六处SAA『现行主线』旧标、Nacos/RocketMQ『未部署』旧述(07-01反转)、gameDefinition残留、全部死链改 git show 定位;AGENTS.md 249→136行(决策史归tech-decisions);_index 改纯在飞板;对外演示版 md→html
- 依据:docs/agent-specs/2026-07-02-文档治理-{全量普查与裁决-report,SoT注册表与治理门-设计}.md(四路普查191份md+Codex/Opus双评审必修项已折入);恢复基线 8ea97234(单档 git checkout 8ea97234 -- <路径>)
2026-07-02 14:24:12 +08:00

200 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 安全、合规、可靠性与一致性硬底线
> 本文是绘境AI 不可逾越的**红线**。涉及安全、合规、幂等、一致性、可靠性、可观测性与 SDK 降级。任何设计/实现违反即驳回。
> 蒸馏来源:`docs/architecture/架构/README.md`(§3.4 SDK 降级 / §7.1-7.5 非功能 / §8 风险)、`docs/architecture/架构/README.md`(§4.2 / §10.5 内容安全)、`docs/architecture/架构/13模块.md`(compliance 模块 T-CMP-* 技术功能,含锁风门 Gate)。
> 配套:编码/契约规范见 [`engineering-conventions.md`](engineering-conventions.md);元流程见 [`../workflows/ai-development-protocol.md`](../workflows/ai-development-protocol.md);事实蓝图见 [`../knowledge/product-and-architecture.md`](../knowledge/product-and-architecture.md);运行时/沙箱手册见 [`../skills/runtime-and-multichannel.md`](../skills/runtime-and-multichannel.md)。
---
## 1. 安全基线
| 层面 | 措施 | 落地阶段 |
|---|---|---|
| 接入防护 | WAF(阿里云/Cloudflare)+ DDoS 高防 | 上线前 |
| 传输 | 全站 HTTPS + HSTS + TLS 1.3 | Phase 1 |
| 鉴权 | OAuth2 + JWT + **Refresh Token 轮换(access 7天 / refresh 30天)** | Phase 1 |
| 权限 | RBAC + **DataPermission(创作者只看自己的项目/资产)** | Phase 1 |
| 游戏沙箱 | `iframe sandbox` + CSP + postMessage 来源校验 + schema 校验 | Phase 2 |
| 内容安全 | 四类检测:文本 / 图片 / AI 输出 / 音频 | Phase 2 |
| 注入防护 | Prompt 注入检测 + SQL 参数化 + XSS 过滤 + **SSRF 白名单**(内网 IP 阻断) | Phase 1 |
| 密钥管理 | Nacos 加密 / K8s Secret,**密钥 90 天轮换**;禁止硬编码进代码 | Phase 1 |
| 审计 | 全量操作日志 + **保留 180 天** + 关键操作实时告警 | Phase 1 |
| 渗透测试 | 上线前一次 + **每季度一次**,无 Critical/High 才放行 | 上线前 / 季度 |
> **权限必须在可信边界(网关 + 服务端)强制,前端不是边界。** 匿名玩家只能浏览试玩,不能发布/收藏/进后台。
> **模型接入统一出口(2026-06-22 纠正口径)**:所有 LLM 调用统一经 **new-api 网关**——client 的 baseUrl 指 new-api 端点、用 `NEWAPI_KEY` 驱动(统一计费 / 密钥 / 审计;密钥只走 env / `.env`,绝不入库)。**协议与 SDK 不锁**:OpenAI 兼容(`/v1/chat/completions`)、Anthropic 原生(`/v1/messages`,thinking 分离)、厂商 SDK 均可,只要出口落 new-api、key 用 newapi-key。**作废旧表述**「唯一 OpenAI 兼容出口 / 绝不直连厂商 SDK」(new-api 只是统一端点,不锁协议)。端点/key 见 [`docs/内网凭据与端点.md`](../../docs/内网凭据与端点.md)。
> **`@PermitAll` 公开读端点纪律(P-OPN-08 实证)**:对外匿名可读端点须守三条——① **只暴露公开字段**:RespVO 显式裁剪,绝不含 PII/内部态(status / audit 列 / tenant_id / cost 等);如 creatorName 取昵称(同 feed authorName 源),非登录名/手机。② **DataPermission**:本仓 DataPermission 规则仅注册于 AdminUserDO/DeptDO(系统 DeptDataPermissionRule),业务表(game_project 等)的「创作者只见己」是 Service/Mapper 手动 `eq(creatorUserId)`——故跨创作者公开读**天然不被数据权限拦截器过滤、无需 `@DataPermission(enable=false)`**(无规则可禁);但务必按「目标 id(路径参)」查、勿误用登录人。③ **租户 scoping(坑)**:TenantBaseDO 表的 @PermitAll 读仍被 `tenant-id` 请求头 scoping——prod(tenant.enable=true)下**无租户头的匿名请求直接 BAD_REQUEST**(故此处「公开」=对带租户头的客户端公开,首方前端恒注 `tenant-id=1`);多租户时不可信头可枚举他租「已公开」数据。**真·租户无关的公开读**须 pin 租户(由资源 id 反解)或入 `tenant.ignore-urls`,并加双租户 staging 测试锁行为。
### 1.1 游戏沙箱具体配置(runtime / compliance 共守)
- `iframe sandbox="allow-scripts allow-same-origin"`。**红线(2026-06-10 审计补,HJ-AUDIT-001)**:`allow-same-origin` 仅当游戏包托管于**独立源**(usercontent 子域,与宿主不同源)时方可使用——同源下 iframe 可触宿主 DOM/存储、甚至自移除 sandbox;独立源就绪前**禁用 `allow-same-origin`**。
- CSP:`script-src 'self'; connect-src 'none'`(**游戏内禁止任何网络请求**);CSP 经游戏包托管侧 **HTTP 响应头**下发,不以 meta 标签为唯一依赖。
- **`'unsafe-eval'` 为受控代码执行面硬需(2026-06-20 实证,bug#2「部分生成游戏静止不可玩」根因)**:生成游戏的逻辑(gamedef behaviors / 或 agent 写的工厂代码)经 `new Function` 编译执行;**studio iframe srcdoc CSP(`game-studio/src/host/inject.ts`)的 `script-src` 必须含 `'unsafe-eval'`**——缺它则 `new Function` 抛 `EvalError`、被 runtime 静默吞进 `errors[]` → 世界永不演进(仅在 iframe CSP 下复现;顶层页/Node 无 CSP 限制故 false-negative 测不出)。**别当多余权限删掉**。eval 风险由三重边界承接:① 出厂前静态扫描拦危险面(禁 process/require/动态 import/fetch/eval/Function/DOM/网络/死循环);② `connect-src 'none'` 杜绝任何出站;③ 浏览器 iframe sandbox。下条「禁 innerHTML/eval」针对的是 **LLM 文案字段**,不否定受控 `new Function` 执行面。
- postMessage:校验来源 origin 在白名单 + 消息 schema 校验,防伪造。
- **LLM 产物消毒**:GameConfig 文案字段(title/label/theme 等)入库前白名单字符集+长度校验,渲染侧一律转义后绘制,**禁止 innerHTML/eval**(~~模板驱动后注入面=配置字符串~~ → **2026-06-12 主线改 agent 写码后,注入面扩大为「生成代码本体」**:文案消毒规则不变,代码面的安全=下一条沙箱边界+L2 信任边界七层〔HJ-GEN-001 §3.1〕,生成代码只许调插件公开 API、过验证门才出厂)。
- 资源总大小 ≤ 10MB,首屏 ≤ 2MB;~~创作者通过配置(非代码)驱动游戏,平台对运行时代码有完全控制权~~ → **生成主线=agent 写码(HJ-GEN-001),「平台完全控制」的实现改为:代码生成于受控 harness、只依赖平台插件库公开面、CSP 沙箱 iframe 运行、验证门(含合规 schema+IP gate+取证导出)全过才发布**——控制点从「禁代码」迁移到「管代码的生成与出厂」。
### 1.2 内容安全分层(自部署快检 + 商用兜底)
- 第一道:`safe-content-ai`(自部署)快检——免费、低延迟。
- 第二道:高风险样本送**阿里云内容安全**二次确认;文本/音频审核走阿里云 API;AI 输出的注入检测 + 输出 schema 校验落在生成侧门禁 + Prompt Registry 门(见 [`../skills/prompt-governance.md`](../skills/prompt-governance.md)),**不依赖 Dify(Dify 降级远期、从未部署)**。
- 阈值(Nacos 配置):**block = 0.7(直接拦截)/ review = 0.4(送人工)/ pass < 0.4(自动通过)**。
### 1.3 内网服务间回调安全(生成 worker 回调实证)
生成走「执行器派发 job → 外置 worker 产物 → 回调写库入 feed」时,回调是免登录的内网 HTTP 路(仿 SmsCallback / pay-notify)。三条红线缺一即被伪造或卡死:
- **唯一写入路径,禁旁路**:任何生成来源(进程内执行器 / 外置 worker / 未来真第三方)回写版本与产物,一律复用同一个写库服务方法(三表同事务:task / version / runtime_package,putManifest 未命中即显式失败)。新增来源不得另开写库旁路。
- **免鉴权回调必自注入系统身份**:`@PermitAll` 回调路在 web 线程无 LoginUser,审计字段 creator/updater 取不到值会撞 NOT NULL、任务卡死。回调 controller 内必须自注入系统身份(`LoginUser(id=0, ADMIN)` 入 SecurityContext),`finally` 必 `clearContext()`。
- **服务间回调必 HMAC 验签**:仅靠「内网不可外达」兜底,等于任意可达请求都能伪造回调驱动落包。回调体按**原始字节** HMAC-SHA256 验签(`MessageDigest.isEqual` 常数时间比对)、**先验签错签直接 401、再反序列化**;两侧密钥与签名字节须逐字节一致。
---
## 2. 创作链路 7 道门禁
在创作全链路设置 7 道门禁,确保产出质量与平台安全,缺一不可:
| 门禁 | 检测内容 | 阻断条件 | 责任模块 |
|---|---|---|---|
| ① Prompt 安全 | 违禁词 / 敏感意图 / 注入攻击 | 命中 → 拒绝 + 提示修改 | compliance |
| ② AI 产出合规 | 图片涉黄涉暴 / 文本违规 / 音频侵权 | 不通过 → 不入库 | compliance |
| ③ 资产入库 | 版权声明完整 / 与 IP 库比对 / 文件安全 | 疑似侵权 → 冻结 + 人工复核 | ip |
| ④ 组装完整性 | GameConfig JSON Schema / 必填字段 / 资源引用有效 | 缺失 → 编译失败 + 提示 | runtime |
| ⑤ 性能门禁 | **包 ≤10MB / 首屏 ≤2MB / 无外部网络请求** | 超限 → 阻断 + 优化建议 | runtime |
| ⑥ 可玩性自测 | 加载成功 / 启动成功 / 30s 无崩溃 / 结束事件触发 | 失败 → 阻断发布 | runtime |
| ⑦ 发布终审 | 标题/简介/封面/标签/适龄/全链路合规汇总 | 人工或自动决策 | bpm + compliance |
---
## 3. 幂等性(写操作必须幂等)
| 场景 | 问题 | 方案 |
|---|---|---|
| 用户重复点击"生成" | 重复创建生成任务 | 前端防抖 + 后端 **`idempotency_key`(Redis 5min 去重)** |
| MQ 重复消费 | 同一消息处理多次 | 每条消息携带 `message_id`,消费前查 **Redis SET 已处理集合(5min TTL)** |
| 支付回调重复 | 重复入账 | **订单状态机 + 乐观锁(version 字段)**,已完成订单不可重入 |
| 发布重复提交 | 重复创建审核流程 | `project_version` **唯一约束 + 状态前置校验** |
---
## 4. 分布式一致性
**原则:尽量避免分布式事务,用最终一致性 + 补偿替代。**
| 场景 | 涉及模块 | 方案 |
|---|---|---|
| 生成成功 → 写版本 + 扣积分 | aigc → project + pay | 本地事务写版本 + MQ 通知扣积分;扣积分失败 → 补偿(标记版本待支付) |
| 审核通过 → 上架 + 刷新 feed | bpm → project + feed | 审核通过本地事务写状态 + MQ 广播 feed 刷新缓存 |
| 支付成功 → 发货 + 通知 | pay → project/ad + community | 支付本地事务 + MQ 事件扇出(各模块独立消费) |
> **兜底机制(必做)**:定时任务扫描"中间态"超 **5 分钟**的记录 → 自动重试或告警。任何跨模块写都要能被对账扫描发现。
> **外部 IO 一律不入 DB 事务(红线·泛化)**:任何外部 IO——Redis(含 ZSET / 缓存写)、WebSocket 推送/广播、文件/对象存储(infra `FileApi`)、Feign/HTTP、MQ 发送——**禁止在 `@Transactional` 方法内执行**;移到事务提交后(`TransactionSynchronization.afterCommit`)或事务外。理由:① 外部调用占用 DB 连接/锁、放大持锁时长;② DB 回滚回滚不了外部副作用(ZSET 多计 / 文件孤儿 / 已广播的幻象事件)。fire-and-forget 广播尤须落库提交后再发,否则回滚后客户端已收到不存在的事件。下面 §4.1「打款外部调用不入事务」是本红线在**资金场景的特例**;plan002 评审在 U3(Redis ZSET `incrementScore` + 弹幕 WS 广播)、U6(infra `FileApi.createFile`)各独立逮一次,证其为通用红线。范本:`afterCommit` 注册见 community `RankServiceImpl`(ZSET 提交后累加)/ `CommentServiceImpl`(举报转 compliance 接缝提交后发);单 insert 无需事务时直接去掉 `@Transactional`、提交后再做 IO(studio `StudioMaterialServiceImpl`,并就近记录文件孤儿无法回滚的补偿口径)。
### 4.1 资金打款/发放红线(M4 变现真实化实证,HJ-M4-REAL-001)
钱财动作(提现打款 / 奖励发放 / 分账入账)的不可逾越规则——任一违反即驳回:
| # | 红线 | 实现要点(trade WithdrawService / RewardPayoutService 范本) |
|---|---|---|
| 1 | **发起 ≠ 终态解耦** | 打款"发起转账"与"置已打款"必须分属两个事务:审核事务内只 CAS `0→1(打款中)`,**提交后**才发起转账(外部调用不入事务);资金终态(`1→2`/`1→4`)一律由**回调驱动**(mock 渠道同步直通=发起后本进程直接驱动,**但仍走 status CAS,不抄近路直接置 2**)。严禁审核事务内同步置已打款。 |
| 2 | **降级 fail-fast,禁静默 mock 发真钱** | 渠道工厂区别于一般 SPI「未命中降级 mock」:**打款降级 mock = 假打款吞真钱**。`PayoutClientFactory.current()` 对配置的真实渠道(wxpay/alipay)缺实现时**抛错挂起提现单(留 status=1)**,绝不兜底 mock。对比:广告计费降级 mock 可接受(只少算收入)。 |
| 3 | **每个资金动作有幂等键 + CAS** | 入账=`uk_source(source,source_ref)`;回调驱动终态=`WHERE status=1` 乐观 CAS(重复回调命中 0 行即跳过,返回 false 不二次发钱);奖励发放回写=`reward.status CAS(0→1)`。**先动账后回写**(先 recordIncome 再 markGranted;回写失败下轮被 uk 拦重复入账只补回写)。 |
| 4 | **回调丢失靠对账补偿 job 兜底** | 异步打款引入「卡 status=1」窗口 → 定时 job 扫超时单(如 >15min)主动查渠道转账状态补驱动终态(`SUCCESS→1→2 / FAILED→1→4 / PENDING→不动`),幂等靠 handlePayoutNotify 的 CAS 与回调赛跑不重复发钱。 |
| 5 | **切换点=配置/注入级,业务码零分支** | 打款渠道走 Nacos `trade.payout-channel(mock/wxpay/alipay)` 单开关,业务码只认渠道码不认具体钱包(PayoutClient SPI);广告联盟走 `game_ad_slot.provider`(SPI)。切真=改配置/注入实现,不改业务逻辑、不重新发版改状态机。 |
> **@Transactional 自调用陷阱**:编排方法(如 auditWithdraw,非事务)内调用本类的 @Transactional 方法(approveToPaying/handlePayoutNotify)会**绕过 Spring 事务代理致事务不生效**。解法:注入自身代理 `@Lazy @Resource XxxService self`,经 `self.xxx()` 调用(本项目无 AopContext 配置,用 self 注入)。
>
> **跨模块发放衔接范式**(community reward → trade 入账):发放编排放在**资金权威侧**(trade,已依赖对方 -api),消费对方 `-api` 的「拉待发放 + 回写已发放」两个 Feign 方法(仿 SettlementService 消费 ad 的 getUnsettled/markSettled),现金奖励 `recordIncome(source=TIP, shareRate=1.0 全额)`。被发放侧(community)只暴露 `-api` 查询/CAS 回写,不反向依赖 trade。
---
## 5. SLO 与可靠性策略
### 5.1 SLO 与 Error Budget
| 服务 | SLO | Error Budget(月) |
|---|---|---|
| 游戏流 API | **99.5%** 可用 | 3.6 小时 |
| AI 生成 | **99%**(允许更高失败率) | 7.2 小时 |
| 支付 | **99.9%** | 43 分钟 |
### 5.2 可靠性策略
| 策略 | 实现 | 落地阶段 |
|---|---|---|
| 熔断降级 | **Sentinel**(Huijing 集成),per-API 规则 | Phase 1 |
| 生成降级 | LLM 不可用 → **确定性 Fallback 生成器**(模板填充) | Phase 2 |
| 游戏加载降级 | 加载超时 **5s → 自动跳过** + 错误记录 + 降权 | Phase 3 |
| 数据库高可用 | MySQL **主从(生产)**;MVP 单节点 + 每日全量备份 + binlog | 上线前 |
| 消息可靠 | RocketMQ **同步刷盘 + 死信队列** + 延迟消息 | Phase 2 |
| 回滚 | 每次部署**保留前 3 个版本镜像,5 分钟内可回退** | CI/CD 内置 |
> 凡涉及外部服务、异步任务、支付、通知、文件、模型调用、远程 API,都必须处理**超时、失败、重试、幂等、补偿**五件事。
---
## 6. 可观测性与日志规范
| 规则 | 硬要求 |
|---|---|
| 格式 | JSON 结构化:`timestamp / level / trace_id / span_id / module / message / context` |
| 级别 | ERROR(需人处理)/ WARN(需关注)/ INFO(关键链路)/ DEBUG(仅 dev) |
| 脱敏 | Token / 密码 / 手机号 / 身份证 **脱敏后输出**(如 `138****1234`),禁止明文落日志 |
| trace_id | **网关入口注入,全链路透传**(含 new-api 网关、RocketMQ 异步 gen 队列、AgentScope 生成 Service 调用),调试模式贯穿生成→编译→加载→运行 |
| 保留 | ERROR/WARN **90 天**;INFO **30 天**;DEBUG 仅 dev |
**告警升级链**:
| 级别 | 条件 | 通知方式 | 响应时间 |
|---|---|---|---|
| P0 Critical | 服务不可用 / 数据丢失 / 安全事件 | 电话 + 短信 + 群 | 5 分钟 |
| P1 High | 5xx > 2% / 生成成功率 < 70% / 支付异常 | 短信 + 群 | 15 分钟 |
| P2 Medium | P95 > 800ms / MQ 积压 > 500 / 错误率上升 | 群通知 | 1 小时 |
| P3 Low | 非核心模块降级 / 日志异常增长 | 群通知 | 工作时间 |
---
## 7. SDK 降级铁律(WanxiangGameSDK)
SDK 是平台能力注入 iframe 沙箱游戏的唯一通道,其稳定性直接决定游戏体验,**铁律不可破**:
| 分类 | 模块 | 失败时游戏行为 | 用户感知 |
|---|---|---|---|
| **核心(异步不阻塞)** | Lifecycle / Telemetry / ErrorTrack | 上报失败 → **静默丢弃**,游戏不受影响 | 零感知 |
| **非核心(失败即跳过)** | Ad | 加载失败/超时 → **跳过广告,给玩家免费奖励** | "广告不可用,已赠送奖励" |
| **非核心(失败即跳过)** | Pay | 网络异常 → 提示稍后重试,**不阻断游戏** | toast 提示 |
| **非核心(失败即跳过)** | Social | 排行/好友失败 → 展示本地缓存或空态 | 功能降级,可继续玩 |
| **非核心(失败即跳过)** | Storage | 云存档失败 → **回退 localStorage** | 换设备可能丢进度 |
**编码铁律**:
- Plugin 层每个调用都包裹 `try-catch`,**异常绝不向游戏抛**。
- 每个 Plugin 调用带 **5s 超时**(`Promise.race([fn(), timeout(5000)])`),失败返回 fallback 让游戏继续。
- **游戏主循环(requestAnimationFrame)永远不被 SDK 阻塞**;Core 上报失败静默丢弃(稳定性 > 数据完整性)。
- 广告/支付/社交 SDK 在**宿主侧 iframe 外部**运行(游戏 iframe 无网络权限),游戏只通过抽象 API 触发。
**合规定位**:
- **同意即可用,拒绝即不可用**(与抖音/微信小游戏同模式,法律基础《个保法》第13条第(二)款)。
- **识别未成年人后**:禁止广告展示 + 禁止付费 + 限制时长 + 最小数据采集(实名认证后触发)。
- 隐私政策注册前强制展示,列出 SDK + 第三方 SDK(穿山甲/优量汇/微信支付等)采集的数据类型/用途/保留期;提供数据删除权入口。
---
## 8. 关键技术风险与应对
| # | 风险 | 概率/影响 | 应对 | 降级方案 |
|---|---|---|---|---|
| 1 | LLM 调用不稳定(超时/限流/幻觉) | 高/高 | new-api 网关多模型热切换 + harness 门兜底重试 | 确定性 Fallback 生成器 |
| 2 | 生成游戏质量不可控 | 高/高 | JSON Schema 强校验 + 可玩性自动测试 + 模板约束 | 质量不达标不入库 |
| 3 | 游戏沙箱逃逸 | 低/极高 | CSP + sandbox + 无网络 + postMessage 校验 | 检测异常立即销毁 iframe |
| 4 | ~~OpenGame 社区停更~~(OpenGame 降级远期、MVP 从未部署,本行 moot) | — | — | 现行生成主线 = agent 写码于插件库经 new-api,无 OpenGame 依赖 |
| 5 | ~~Dify 版本升级不兼容~~(Dify 降级远期、MVP 从未部署,本行 moot) | — | — | 现行 agentic 编排 = AgentScope,无 Dify 依赖 |
| 6 | 广告联盟审核不通过 | 中/高 | 提前申请资质 + 内容合规前置 | 延迟广告上线,先做订阅/B端 |
| 7 | MQ 重复消费致数据不一致 | 中/高 | 消息幂等消费(§3) | 定时任务修复 + 告警 |
| 8 | 分布式事务部分失败 | 中/高 | 最终一致性 + 补偿(§4) | 中间态扫描 + 人工介入 |
| 9 | 第三方 SDK 数据泄露 | 低/极高 | 广告/支付 SDK 宿主侧隔离 + 最小权限 | 紧急下线第三方 SDK |