fix Dechunk HTTP Response leaks terminating chunk and trailers into output

The loop did not break on chunkSize === 0, causing the terminating
chunk's trailing metadata (CRLF or trailer headers) to be appended
to the decoded output. Now break on the zero-length terminating chunk
and return only the decoded body.

closes #2247
This commit is contained in:
Willi Ballenthin 2026-03-24 09:38:22 +01:00
parent b0fa1f8d1b
commit 4dd8767597
3 changed files with 71 additions and 1 deletions

View File

@ -45,12 +45,15 @@ class DechunkHTTPResponse extends Operation {
const lineEndingsLength = lineEndings.length; const lineEndingsLength = lineEndings.length;
let chunkSize = parseInt(input.slice(0, chunkSizeEnd), 16); let chunkSize = parseInt(input.slice(0, chunkSizeEnd), 16);
while (!isNaN(chunkSize)) { while (!isNaN(chunkSize)) {
if (chunkSize === 0) {
break;
}
chunks.push(input.slice(chunkSizeEnd, chunkSize + chunkSizeEnd)); chunks.push(input.slice(chunkSizeEnd, chunkSize + chunkSizeEnd));
input = input.slice(chunkSizeEnd + chunkSize + lineEndingsLength); input = input.slice(chunkSizeEnd + chunkSize + lineEndingsLength);
chunkSizeEnd = input.indexOf(lineEndings) + lineEndingsLength; chunkSizeEnd = input.indexOf(lineEndings) + lineEndingsLength;
chunkSize = parseInt(input.slice(0, chunkSizeEnd), 16); chunkSize = parseInt(input.slice(0, chunkSizeEnd), 16);
} }
return chunks.join("") + input; return chunks.join("");
} }
} }

View File

@ -61,6 +61,7 @@ import "./tests/CRCChecksum.mjs";
import "./tests/Crypt.mjs"; import "./tests/Crypt.mjs";
import "./tests/CSV.mjs"; import "./tests/CSV.mjs";
import "./tests/DateTime.mjs"; import "./tests/DateTime.mjs";
import "./tests/DechunkHTTPResponse.mjs";
import "./tests/DefangIP.mjs"; import "./tests/DefangIP.mjs";
import "./tests/DisassembleARM.mjs"; import "./tests/DisassembleARM.mjs";
import "./tests/DropNthBytes.mjs"; import "./tests/DropNthBytes.mjs";

View File

@ -0,0 +1,66 @@
/**
* DechunkHTTPResponse operation tests.
*
* @author Willi Ballenthin
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "Dechunk HTTP response: CRLF line endings",
input: "7\r\nMozilla\r\n9\r\nDeveloper\r\n7\r\nNetwork\r\n0\r\n\r\n",
expectedOutput: "MozillaDeveloperNetwork",
recipeConfig: [
{
op: "Dechunk HTTP response",
args: [],
},
],
},
{
name: "Dechunk HTTP response: LF line endings",
input: "7\nMozilla\n9\nDeveloper\n7\nNetwork\n0\n\n",
expectedOutput: "MozillaDeveloperNetwork",
recipeConfig: [
{
op: "Dechunk HTTP response",
args: [],
},
],
},
{
name: "Dechunk HTTP response: single chunk",
input: "5\r\nHello\r\n0\r\n\r\n",
expectedOutput: "Hello",
recipeConfig: [
{
op: "Dechunk HTTP response",
args: [],
},
],
},
{
name: "Dechunk HTTP response: trailing headers discarded",
input: "7\nMozilla\n9\nDeveloper\n7\nNetwork\n0\nExpires: Wed, 21 Oct 2015 07:28:00 GMT\n",
expectedOutput: "MozillaDeveloperNetwork",
recipeConfig: [
{
op: "Dechunk HTTP response",
args: [],
},
],
},
{
name: "Dechunk HTTP response: hex chunk sizes",
input: "a\r\n0123456789\r\n0\r\n\r\n",
expectedOutput: "0123456789",
recipeConfig: [
{
op: "Dechunk HTTP response",
args: [],
},
],
},
]);