diff --git a/src/js/ahead-of-time/node/_http_common.ts b/src/js/ahead-of-time/node/_http_common.ts index 40b6f1a43faf..74bc108dc84c 100644 --- a/src/js/ahead-of-time/node/_http_common.ts +++ b/src/js/ahead-of-time/node/_http_common.ts @@ -1,5 +1,5 @@ // What the compiler reads in place of src/js/node/_http_common.ts, which `bun run` evaluates. The two do the same. -// Written against that file with the SHA-256 e9bcb64b690623cf8e8b8ce199a3326e23a7430bdeed5fd855a1902f2798e2d8. +// Written against that file with the SHA-256 0d1fd706eaf5fdc6a02e5bf7ea7be7c963c340fb52ec8474fce8f9a9e74c2265. // When that file changes, bring the change here and write the new hash: test/internal/second-texts-of-modules.test.ts. // This is a port of Node.js's lib/_http_common.js @@ -183,7 +183,7 @@ function parserOnHeadersComplete( // If parser.maxHeaderPairs <= 0 assume that there's no limit. const maxHeaderPairs = parser.maxHeaderPairs; - if (maxHeaderPairs > 0) n = Math.min(n, maxHeaderPairs); + if (maxHeaderPairs > 0) n = $min(n, maxHeaderPairs); incoming._addHeaderLines(headers, n); diff --git a/src/js/node/_http_common.ts b/src/js/node/_http_common.ts index 61d8baec4dc2..2053ada6b98d 100644 --- a/src/js/node/_http_common.ts +++ b/src/js/node/_http_common.ts @@ -196,7 +196,7 @@ function parserOnHeadersComplete( // If parser.maxHeaderPairs <= 0 assume that there's no limit. const maxHeaderPairs = parser.maxHeaderPairs; - if (maxHeaderPairs > 0) n = Math.min(n, maxHeaderPairs); + if (maxHeaderPairs > 0) n = $min(n, maxHeaderPairs); incoming._addHeaderLines(headers, n); diff --git a/test/js/node/http/node-http-primoridals.test.ts b/test/js/node/http/node-http-primoridals.test.ts index abbd52bafe72..4dabb9ffee8d 100644 --- a/test/js/node/http/node-http-primoridals.test.ts +++ b/test/js/node/http/node-http-primoridals.test.ts @@ -1,4 +1,5 @@ import { afterEach, expect, test } from "bun:test"; +import { bunEnv, bunExe } from "harness"; import { once } from "node:events"; import type { AddressInfo } from "node:net"; import { text } from "node:stream/consumers"; @@ -196,3 +197,44 @@ test("does not read URL from globalThis", async () => { globalThis.URL = URL; } }); + +// The response header count is bounded with the engine's own min(), so a +// user-patched Math.min cannot make IncomingMessage read past the flat +// [name, value, ...] header array. +test("a patched Math.min does not reach IncomingMessage header parsing", async () => { + await using proc = Bun.spawn({ + cmd: [ + bunExe(), + "-e", + ` + const http = require("http"); + const server = http.createServer((req, res) => { + res.setHeader("x-server", "1"); + res.end("ok"); + }); + server.listen(0, "127.0.0.1", () => { + const origMin = Math.min; + Math.min = (...args) => origMin(...args) + 1; + process.on("uncaughtException", err => { + console.log("UNCAUGHT:", err.message); + process.exit(1); + }); + http.get({ host: "127.0.0.1", port: server.address().port, headers: { "x-h": "v" } }, res => { + res.resume(); + res.on("end", () => { + Math.min = origMin; + console.log("client: got", res.statusCode, res.headers["x-server"]); + server.close(); + }); + }); + }); + `, + ], + env: bunEnv, + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stdout).toBe("client: got 200 1\n"); + expect(stderr).toBe(""); + expect(exitCode).toBe(0); +});