fix(weixin): 扫码上游字段过令牌守卫,errortoken 契约改写

复审 p2 两条:

① 第五个出口:get_qrcode_status 的 status 与 scaned_but_redirect 的
redirect_host 此前原样进 INFO;同一 status 经 setPhase 还从复用二维码那条
INFO(phase)二次出镜。三处一律过 trace.ErrorToken;**控制流仍用原值**
(状态机读 status.Status、路由读 status.RedirectHost、confirmingPhase 读
phase),用例同时钉住「日志变空串」与「路由/判定不变」两个方向。

② 顺带补扫出一处 URL 类:get_bot_qrcode 的 qrcode_img_content(上游给的
图片地址,可能是带签名的 URL 或 data: 内联图)原样进 INFO ⇒ 改走
trace.MaskQuery(凭据类 query 打码、保留路径、解析不出 host 回空串)。

③ errortoken.go 的契约改写:原「本包之外不应出现 ErrorToken 调用」已被
M50 的正当调用点证伪。新口径按**字段类别**判定:协议枚举字段(status/
code/type/request_id)可在渲染点调用;自由文本字段(message/description/
msg/wording)不得靠它出镜 —— 那类默认不带,要带必须 opt-in + RedactText。
dashscope 的 request_id 例外一并写入。

每条用例都做了变异验证(去掉守卫 / 把守卫挪进控制流 / 改回原串,均打红)。
This commit is contained in:
AskaEth committed 2026-10-26 19:41:28 +08:00
1 parent 996201c4ed
commit 1b2576ecaf
4 files changed
+269 -11

No files matched your search

+7 -2
View File
@@ -11,6 +11,8 @@ import (
"fmt"
"sync"
"time"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
)
// Adapter.LoginStatus 上报的登录状态取值。
@@ -119,7 +121,10 @@ func (a *Adapter) StartLogin(ctx context.Context) (LoginQR, error) {
// 无条件复用当前二维码加以保护。只有仍未被扫描、且已过那个极小复用窗口的
// 二维码才会被重新生成,使显式的「刷新二维码」能拿到全新码。
if sess.confirmingPhase() {
a.log.Info("weixin_ilink: login in progress, reusing qrcode", "phase", sess.currentPhase())
// phase 是**上游字段**(`get_qrcode_status` 的 status 原值经 setPhase 存下),
// 因此进日志前过令牌守卫 —— 与 login.go 里那一行同一道(M50 复审:同一字段
// 的第二个出口)。`confirmingPhase()` 的判定用的是原值,不受渲染口径影响。
a.log.Info("weixin_ilink: login in progress, reusing qrcode", "phase", trace.ErrorToken(sess.currentPhase()))
return LoginQR{Qrcode: sess.qrcode, Image: sess.image}, nil
}
// 码仍然新鲜或已被扫描时都返回同一个码——一律复用、绝不替换,使进行中的
@@ -127,7 +132,7 @@ func (a *Adapter) StartLogin(ctx context.Context) (LoginQR, error) {
// 用户扫码期间取消会话。只有真正过期(超过服务端 60s TTL)且从未被扫描的码
// 才会重新生成。
if time.Since(sess.startedAt) < 60*time.Second {
a.log.Info("weixin_ilink: login in progress, reusing qrcode", "phase", sess.currentPhase())
a.log.Info("weixin_ilink: login in progress, reusing qrcode", "phase", trace.ErrorToken(sess.currentPhase()))
return LoginQR{Qrcode: sess.qrcode, Image: sess.image}, nil
}
// 码已过期(>60s)且从未被扫描:生成新码,并取消旧的那次(已过期、未扫描的)