fix(billing): reject out-of-range image n before it multiplies the bill - #1211
Open
think-back wants to merge 1 commit into
Open
think-back wants to merge 1 commit into
think-back wants to merge 1 commit into
Conversation
A client-supplied `n` flows unchecked into the quota multiplier chain (ImageHelper -> PriceData.OtherRatios -> calculateTextQuotaSummary), so its value scales the request price directly. On 2026-09-13 a gpt-image-2 request arrived with n=4294967295 (uint32 max) and was billed 18,897,856,098,000 quota -- $37,795,712 for a single request whose upstream returned one image in 16s. The arithmetic checks out exactly: 0.011 * 500000 * 0.8 * 4294967295. n=0 is the same gap from the other side: AddOtherRatio drops any ratio <= 0, so the multiplier never lands and the request bills as if n=1 while the zero is still forwarded upstream. Validate n in [1, 10] (OpenAI's own ceiling, and at or above every upstream this gateway fronts) and reject anything else with a 400 before the request is relayed. Image traffic has a single entry point (controller/relay.go -> ImageHelper), so this covers every image path. This closes the one field we have evidence of. The broader gap -- request fields reaching the price multiplier without a value-domain check -- is worth a separate pass; the video path already guards zero/NaN/Inf ratios while the image path did not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
2026-09-13 15:32 UTC,一条
gpt-image-2请求被计费 $37,795,712(quota18,897,856,098,000)。算式分毫不差:
0.011 × 500000 × 0.8 × 4294967295 = 18,897,856,098,000客户端传的
n未经任何值域校验就进入计费乘法链(ImageHelper→PriceData.OtherRatios→calculateTextQuotaSummary),其数值直接放大账单。n的来源4294967295= 2³²−1(uint32 上限)。实测标准库encoding/json对*uint的行为,排除了所有「服务端算错」的可能:-1/-1.0/"4294967295"/4294967295.0/1e104294967295Go 不做负数回绕、不接受字符串或浮点,唯一可能是请求体里原样写着该整数。推测为客户端把
-1塞进uint32变量后回绕所致。注:上游只生成了一张图(16s / 515 completion tokens),只收了一张图的成本。这 3779 万是纯内部记账放大,用户未多获取任何资源 —— 是自伤,不是套利。
双向缺口
n无上限,直接作为乘数放大账单。PriceData.AddOtherRatio丢弃任何ratio <= 0,n=0的倍率永不落地,请求按n=1计费;而该零值仍会原样透传给上游,各家 provider 解释不一。relay/image_billing.go已专门把TotalTokens=0抬为 1 来堵「零值导致不收费」,说明这类陷阱此前已被意识到,但漏掉了n这个维度。视频路径已有 zero/NaN/Inf 倍率防护测试,图片路径没有。修改
校验
n ∈ [1, 10](OpenAI images API 官方上限,也不低于本网关对接的任一上游),越界在转发前返回 400。校验点放在
ImageHelper入口,紧邻同风格的temp_url400 检查。图片流量单一入口(controller/relay.go:40→ImageHelper),覆盖全部图片路径。验证
undefined: validateImageN),再实现转绿n=0、上限+1、边界值(nil / 1 / 10)go test ./relay/:baseline 与改动后均ok,零新增失败gofmt干净范围说明
本 PR 只关闭有实据的这一个字段。更广的缺口 —— 请求字段未经值域校验即参与价格乘法 —— 建议单独一轮排查。
另有两项不在本 PR 内、需业务决策:
n更根本。DecreaseUserQuota→mutateUserWalletQuota传requireAtLeast=0,而下限检查为if RequireAtLeast > 0(model/quota_lifecycle.go:109),条件恒不成立,故无任何下限保护。是有意设计还是疏漏,需确认。取证补充
30 天日志保留期内逐段扫描,
4294967295仅出现这一次,非反复试探。(排查提示:
gcloud logging read --freshness在该数据量下会返回假空,需改用精确timestamp时间窗分段查询。)