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:
1 parent
996201c4ed
commit
1b2576ecaf
4 files changed
+269
-11
No files matched your search
@@ -10,6 +10,8 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
|
||||
)
|
||||
|
||||
// loginPollInterval 是 Connect 轮询二维码确认状态的默认间隔。它是文档记录的约 1s
|
||||
@@ -86,10 +88,17 @@ func (a *Adapter) runLogin(sess *loginSession, sessCtx context.Context) {
|
||||
}
|
||||
// 跟踪线上的原始阶段,便于 StartLogin 保护「已扫描/确认中」的绑定不被重新取码。
|
||||
// 仅在状态变化时打日志以避免噪声,且绝不记录 qrcode 标识符或 token。
|
||||
//
|
||||
// 🔴 status 是**上游字段**:进日志前过令牌守卫(trace.ErrorToken)。真实取值
|
||||
// (wait / scaned / confirmed / scaned_but_redirect / …)全是短令牌 ⇒ 原样通过;
|
||||
// 上游若在这一栏塞散文,这一行就是空串 —— 与 qq_obv11 的 status、
|
||||
// trace.UpstreamError 的 code/type 同一道守卫(M50 复审的第五个出口)。
|
||||
// **控制流仍用原值**:setPhase 与下面的 switch 都读 status.Status,渲染口径
|
||||
// 不参与判定。
|
||||
sess.setPhase(status.Status)
|
||||
if status.Status != prevStatus {
|
||||
prevStatus = status.Status
|
||||
a.log.Info("weixin_ilink: get_qrcode_status", "status", status.Status)
|
||||
a.log.Info("weixin_ilink: get_qrcode_status", "status", trace.ErrorToken(status.Status))
|
||||
}
|
||||
|
||||
switch status.Status {
|
||||
@@ -115,8 +124,13 @@ func (a *Adapter) runLogin(sess *loginSession, sessCtx context.Context) {
|
||||
a.setLoginState(LoginStatusWaiting, loginScannedDetail)
|
||||
case "scaned_but_redirect":
|
||||
if status.RedirectHost != "" {
|
||||
// 🔴 路由用**原值**(下一行),日志只留守卫后的形态:redirect_host 是上游
|
||||
// 字段,与上面的 status 同一道令牌守卫(trace.ErrorToken)。真实主机名通过;
|
||||
// 带端口(`h:8443`)或散文的整体丢弃 —— 宁可日志少一个字段,也不让这一栏
|
||||
// 成为第二个正文通道。
|
||||
a.setPollBaseURL(status.RedirectHost)
|
||||
a.log.Info("weixin_ilink: idc redirect, switching polling host", "host", status.RedirectHost)
|
||||
a.log.Info("weixin_ilink: idc redirect, switching polling host",
|
||||
"host", trace.ErrorToken(status.RedirectHost))
|
||||
} else {
|
||||
a.log.Warn("weixin_ilink: scaned_but_redirect without redirect_host, continuing on current host")
|
||||
}
|
||||
@@ -240,7 +254,12 @@ func (a *Adapter) fetchQRCode(ctx context.Context) (qrcodeResp, error) {
|
||||
}
|
||||
// 只记录可渲染的二维码 URL,绝不记录原始 qrcode 标识符(它是回传给
|
||||
// get_qrcode_status 的密钥)。时间戳由 logger 附加。
|
||||
a.log.Info("weixin_ilink: get_bot_qrcode ok", "url", out.QrcodeImgContent)
|
||||
//
|
||||
// 🔴 URL 一律走 trace.MaskQuery(M50 复审补扫):它是**上游给的**串,可能是带签名
|
||||
// 的图片地址,也可能整段是 `data:` 内联图 —— 原样落日志等于把二维码(扫码即绑定)
|
||||
// 抄进一个会被 tail、采集、粘进工单的面。MaskQuery 打码凭据类 query 值并保留路径,
|
||||
// 解析不出 host(data: URI、畸形串)时回空串而**不**回原串。
|
||||
a.log.Info("weixin_ilink: get_bot_qrcode ok", "url", trace.MaskQuery(out.QrcodeImgContent))
|
||||
return out, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user