信封路径(非 2xx + 信封体、2xx + code!=0)此前把上游 message 经 RedactText 拼进 error,而上游会在 message 里回显被拒的请求正文 —— 她的原话因此进了错误 与调用方日志(M49 探针实测:code 1: invalid content: 她刚才说的那句话 ...)。 - envelope message 改为**默认不带**(新 opt-in WithEnvelopeMessage(),与 trace.UpstreamError 的 message 同一口径);code 与上游 status 保留, errors.Is 的 ErrAPI / trace.ErrUpstream 链不变。 - opt-in 下统一走单一清洗 upstreamText:压平空白 + trace.RedactTextLimited, 上限 40 rune(比 trace 的 128 短:错误里那句话是提示,不是摘要)。 - plugins/channels 同族出口一并收口(扫过全部,命中 4 处): telegram description、qq_obv11 msg(错误 + logEchoFailure 日志)、 weixin_ilink CDN x-error-message;qq 的 status 字段改走 trace.ErrorToken 令牌守卫。被剥掉的一律由 status / error_code / retcode 承担分类。 每条新增/改写用例都做过变异验证(把字段拼回去即变红)。
49 lines
2.9 KiB
Go
49 lines
2.9 KiB
Go
package httpclient
|
||
|
||
// 本文件是**渠道侧上游自由文本的唯一清洗入口**(M50)。它管的是同一件事在四个出口上
|
||
// 的形态:KOOK 一类 `{code,message}` 信封的 `message`、Telegram Bot API 的
|
||
// `description`、OneBot v11 信封的 `msg` / `wording`、iLink CDN 的 `x-error-message`
|
||
// 头。四者都是上游给的**人话**,都可能把被拒的请求内容回显出来 —— 而请求内容里就有
|
||
// 她的原话。
|
||
//
|
||
// 🔴 契约(改本文件前先读):
|
||
// - 上层口径是 **默认不带**:这些字段默认不进 error、不进日志(见 client.go 的
|
||
// WithEnvelopeMessage 与各渠道调用点的注释)。本文件的函数**只在调用方已经显式
|
||
// 选择保留**时才被调用 —— 它是「保留时的上限」,不是「可以带」的许可。
|
||
// - 清洗只有一份:`trace.RedactTextLimited`(凭据形状打码 + rune 截断)。本包**不**
|
||
// 另写一套正则或截断,另写一套必然漂移,而漂移的方向总是「其中一条出口开始原样
|
||
// 吐凭据」。
|
||
// - 压平空白也是必需的一步:一条诊断只许占一行,否则上游可以用换行伪造日志行
|
||
// (log injection)。
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
|
||
)
|
||
|
||
// maxUpstreamTextRunes 是上游自由文本允许保留的 rune 上限:**40**。
|
||
//
|
||
// 为什么比 `trace.RedactText` 的 80 更短:那一档服务的是**摘要素材**(出图、工具结果
|
||
// 摘要),读者只看这一条;本文件的产物进的是**错误链与日志**,同一条信息还会被调用方
|
||
// 再落一次,长度预算没有理由按摘要给。而 40 rune 已足够装下渠道侧真正有用的那类说明
|
||
// (`invalid token`、`参数错误`、`缺少 code`、`FLOOD_WAIT_30`),又短到装不下一句她
|
||
// 说的话 —— 上游回显 `invalid content: <正文>` 时,前半句就吃掉大半预算。
|
||
const maxUpstreamTextRunes = 40
|
||
|
||
// upstreamText 把一段上游自由文本收敛成可以出镜的短诊断:压平空白,再交给
|
||
// `trace.RedactTextLimited` 打码凭据形状并截到 maxUpstreamTextRunes(超出的带省略号)。
|
||
//
|
||
// 空串进 ⇒ 空串出;调用方据此判断「这一段有没有东西可显示」。
|
||
//
|
||
// 🔴 它**不**决定该不该带(默认不带)。把它的结果无条件拼进 error 仍然是把她的原话
|
||
// 送上日志 —— 调用点必须自己持有那个显式的开关(本包只有 WithEnvelopeMessage 一处)。
|
||
// 渠道插件当前**不带**任何自由文本,因此没有第二个调用点;将来若真要开,也要回到
|
||
// 这一条(`trace` 的唯一清洗 + 40 rune),不要在插件里另写一套截断。
|
||
func upstreamText(s string) string {
|
||
if s == "" {
|
||
return ""
|
||
}
|
||
return trace.RedactTextLimited(strings.Join(strings.Fields(s), " "), maxUpstreamTextRunes)
|
||
}
|