已开启
[Bug-Report|缺陷反馈]: cpx 的 Brotli 请求体无法解码,明文捕获丢失请求内容 #52
tianjun创建于  16 天前
tianjun
16 天前 创建

问题背景

cpx 的 requestBodyForCapture() 用 zlib.unzipSync() 处理除 zstd 之外的压缩请求,但这个 API 不解码 Brotli。请求头为 Content-Encoding: br 时,解码失败被 catch 吞掉,函数返回原始压缩字节。server.ts::handleProxy() 随后将这些字节当作 UTF-8 JSON 解析,parsedBody 变成 null,捕获内容无法取得请求中的 messages/input 及 stream 字段。

原始请求字节仍会转发上游,因此代理请求可以继续执行;缺陷在本地明文捕获解析。此报告只讨论单个 br 编码,不涉及叠加压缩编码。

环境

  • master 基线:9ce8017。
  • Host Node.js 24.19.0、Vitest 3.2.7;使用合成请求,不包含真实会话或密钥。
  • packages/cpx/src/request-body-decoder.ts 及 src/server.ts:120-125。

复现步骤

在 packages/cpx 使用 tsx 调用真实解码器:

import zlib from 'node:zlib';
import { requestBodyForCapture } from './src/request-body-decoder.ts';
const json = Buffer.from(JSON.stringify({model: 'example-model', stream: true,
  messages: [{role: 'user', content: 'synthetic prompt'}]}));
const br = zlib.brotliCompressSync(json);
const decoded = requestBodyForCapture(br, 'openai', 'br');
console.log(decoded.equals(json));
JSON.parse(decoded.toString('utf-8'));

实际行为

相等判断为 false,随后 JSON 解析失败。新增回归在修复前 6 failed、4 passed:三种已识别协议和 null 协议均不能解码 Brotli,大小写/数组头用例也失败;gzip、deflate 和原有回退行为通过。

期望行为

用 Node 内置 Brotli 解码器还原用于捕获解析的副本,保持转发上游的原始 Buffer 字节不变。损坏压缩流继续使用原有原文回退行为。

验证与去重

修复后 cpx 全量 UT 为 20 个测试文件、190 个测试全部通过,其中新增解码回归 10 个;适用 pre-commit / OAT 通过。已检查 51 个历史 Issue 和 36 个开放 PR,没有发现该解码问题。#27 / #58 讨论捕获落盘的 gzip 与 7z 格式,属于不同层次。

likedislike
tianjun
16 天前 评论:

/assign @Ethan_Zou

likedislike
CANN-robotCANN-robot成员
16 天前 将 Ethan_Zou 设为负责人
Ttianjun
16 天前 关联了pull request:fix(cpx): decode Brotli request bodies for capture