fix(readRawBody): fix case-sensitive `Transfer-Encoding` check causin… · h3js/h3@618ccf4

Original file line numberDiff line numberDiff line change

@@ -94,6 +94,42 @@ describe("body", () => {

9494

expect(await result.body.text()).toBe("200");

9595

});

9696
97+

it("handles case-insensitive Transfer-Encoding: chunked header (CVE)", async () => {

98+

// Test that Transfer-Encoding header check is case-insensitive per RFC 7230

99+

// This prevents HTTP Request Smuggling via TE.TE desynchronization attacks

100+
101+

const testCases = [

102+

{ encoding: "ChunKed", expected: '{"test":"data"}' },

103+

{ encoding: "CHUNKED", expected: '{"test":"data"}' },

104+

{ encoding: "Chunked", expected: '{"test":"data"}' },

105+

];

106+
107+

for (const testCase of testCases) {

108+

// Simulate a raw HTTP request with mixed-case Transfer-Encoding

109+

const mockReq = {

110+

method: "POST",

111+

headers: { "transfer-encoding": testCase.encoding },

112+

on(event: string, handler: (chunk?: Buffer) => void) {

113+

if (event === "data") {

114+

// Simulate chunked data arrival

115+

handler(Buffer.from(testCase.expected));

116+

} else if (event === "end") {

117+

handler();

118+

}

119+

return this;

120+

},

121+

};

122+
123+

const mockEvent = { method: "POST", node: { req: mockReq } };

124+
125+

const result = await readRawBody(mockEvent as any);

126+
127+

// Should properly read the body regardless of Transfer-Encoding case

128+

// If the check is case-sensitive, this will fail and return undefined

129+

expect(result).toEqual(testCase.expected);

130+

}

131+

});

132+
97133

it("returns an empty string if body is empty", async () => {

98134

let _body: string | undefined = "initial";

99135

app.use(