From 5093aec69ebe9fd186150aa719583094a95713c2 Mon Sep 17 00:00:00 2001 From: Kevin Howell Date: Wed, 3 Mar 2021 21:20:12 -0500 Subject: [PATCH 1/4] Remove the host header before forwarding With some hosts behind shared TLS (using SNI), keeping the Host header was causing the target host to be misidentified. I also observed issues related to TLS verification (as seen in #156). Fixes #156 --- index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.ts b/index.ts index 27d1b1c1..2da510ad 100644 --- a/index.ts +++ b/index.ts @@ -43,6 +43,9 @@ class Client { delete data.query + // Remove the host header, leaving it causes issues with SNI and TLS verification + delete data.host + const req = superagent.post(url.format(target)).send(data.body) delete data.body From f8114b18366005e49e5ef324287f10d2dc69b9a5 Mon Sep 17 00:00:00 2001 From: Aras Abbasi Date: Thu, 7 Dec 2023 13:05:11 +0100 Subject: [PATCH 2/4] Update index.ts --- index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.ts b/index.ts index 22b3c4fa..fb73f4d8 100644 --- a/index.ts +++ b/index.ts @@ -57,7 +57,7 @@ class Client { delete data.query; // Remove the host header, leaving it causes issues with SNI and TLS verification - delete data.host + delete data.host; const body = JSON.stringify(data.body); delete data.body; From eb26c71ca9c8f753069890090897a82e7774daab Mon Sep 17 00:00:00 2001 From: uzlopak Date: Thu, 4 Jan 2024 03:20:43 +0100 Subject: [PATCH 3/4] remove host-header, add integration test --- index.ts | 3 -- package-lock.json | 22 +++++++++++ package.json | 1 + test/host-header.test.ts | 80 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 test/host-header.test.ts diff --git a/index.ts b/index.ts index fb73f4d8..5016592f 100644 --- a/index.ts +++ b/index.ts @@ -56,9 +56,6 @@ class Client { delete data.query; - // Remove the host header, leaving it causes issues with SNI and TLS verification - delete data.host; - const body = JSON.stringify(data.body); delete data.body; diff --git a/package-lock.json b/package-lock.json index f5af3580..d58c3a20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "fastify": "^4.24.3", "prettier": "^3.1.0", "typescript": "^5.0.0", + "undici": "^6.0.1", "vitest": "^1.0.0" } }, @@ -453,6 +454,15 @@ "fast-uri": "^2.0.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@fastify/deepmerge": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.3.0.tgz", @@ -2350,6 +2360,18 @@ "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", "dev": true }, + "node_modules/undici": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.0.1.tgz", + "integrity": "sha512-eZFYQLeS9BiXpsU0cuFhCwfeda2MnC48EVmmOz/eCjsTgmyTdaHdVsPSC/kwC2GtW2e0uH0HIPbadf3/bRWSxw==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=18.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/package.json b/package.json index 58cc32e6..02750f92 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "fastify": "^4.24.3", "prettier": "^3.1.0", "typescript": "^5.0.0", + "undici": "^6.0.1", "vitest": "^1.0.0" } } diff --git a/test/host-header.test.ts b/test/host-header.test.ts new file mode 100644 index 00000000..cee019e5 --- /dev/null +++ b/test/host-header.test.ts @@ -0,0 +1,80 @@ +import { request } from "undici"; +import Client from "../index"; +import { describe, test, expect } from "vitest"; +import { fastify as Fastify } from "fastify"; + +describe("host-header", () => { + test("host is not passed by payload", async () => { + expect.assertions(1); + + let finishedPromise = { + promise: undefined, + reject: undefined, + resolve: undefined, + } as { + promise?: Promise; + resolve?: (value?: any) => any; + reject?: (reason?: any) => any; + }; + + finishedPromise.promise = new Promise((resolve, reject) => { + finishedPromise.resolve = resolve; + finishedPromise.reject = reject; + }); + + const fastify = Fastify(); + + fastify.route({ + url: "/", + method: "POST", + handler: async (request, reply) => { + expect(request.headers.host).not.toBe( + "smee.io" + ); + finishedPromise.resolve!(); + return reply + .send(request.body) + .header("content-type", "application/json"); + }, + }); + + const target = await fastify.listen(); + + const source = await Client.createChannel(); + const client = new Client({ + source, + target, + }); + + let readyPromise = { + promise: undefined, + reject: undefined, + resolve: undefined, + } as { + promise?: Promise; + resolve?: (value?: any) => any; + reject?: (reason?: any) => any; + }; + + readyPromise.promise = new Promise((resolve, reject) => { + readyPromise.resolve = resolve; + readyPromise.reject = reject; + }); + + client.onopen = readyPromise.resolve!; + client.onerror = readyPromise.reject!; + client.start(); + + await readyPromise.promise; + + await request(source, { + method: "POST", + headers: { + host: "smee.io", + "content-type": "application/json", + }, + }) + + await finishedPromise.promise; + }, { timeout: 10000 }); +}); From f1f99f5b509f330cdaed66fa119c236544407fc6 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Thu, 4 Jan 2024 03:22:26 +0100 Subject: [PATCH 4/4] fix linting issue --- test/host-header.test.ts | 124 ++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/test/host-header.test.ts b/test/host-header.test.ts index cee019e5..a901795c 100644 --- a/test/host-header.test.ts +++ b/test/host-header.test.ts @@ -4,77 +4,79 @@ import { describe, test, expect } from "vitest"; import { fastify as Fastify } from "fastify"; describe("host-header", () => { - test("host is not passed by payload", async () => { - expect.assertions(1); + test( + "host is not passed by payload", + async () => { + expect.assertions(1); - let finishedPromise = { - promise: undefined, - reject: undefined, - resolve: undefined, - } as { - promise?: Promise; - resolve?: (value?: any) => any; - reject?: (reason?: any) => any; - }; + let finishedPromise = { + promise: undefined, + reject: undefined, + resolve: undefined, + } as { + promise?: Promise; + resolve?: (value?: any) => any; + reject?: (reason?: any) => any; + }; - finishedPromise.promise = new Promise((resolve, reject) => { - finishedPromise.resolve = resolve; - finishedPromise.reject = reject; - }); + finishedPromise.promise = new Promise((resolve, reject) => { + finishedPromise.resolve = resolve; + finishedPromise.reject = reject; + }); - const fastify = Fastify(); + const fastify = Fastify(); - fastify.route({ - url: "/", - method: "POST", - handler: async (request, reply) => { - expect(request.headers.host).not.toBe( - "smee.io" - ); - finishedPromise.resolve!(); - return reply - .send(request.body) - .header("content-type", "application/json"); - }, - }); + fastify.route({ + url: "/", + method: "POST", + handler: async (request, reply) => { + expect(request.headers.host).not.toBe("smee.io"); + finishedPromise.resolve!(); + return reply + .send(request.body) + .header("content-type", "application/json"); + }, + }); - const target = await fastify.listen(); + const target = await fastify.listen(); - const source = await Client.createChannel(); - const client = new Client({ - source, - target, - }); + const source = await Client.createChannel(); + const client = new Client({ + source, + target, + }); - let readyPromise = { - promise: undefined, - reject: undefined, - resolve: undefined, - } as { - promise?: Promise; - resolve?: (value?: any) => any; - reject?: (reason?: any) => any; - }; + let readyPromise = { + promise: undefined, + reject: undefined, + resolve: undefined, + } as { + promise?: Promise; + resolve?: (value?: any) => any; + reject?: (reason?: any) => any; + }; - readyPromise.promise = new Promise((resolve, reject) => { - readyPromise.resolve = resolve; - readyPromise.reject = reject; - }); + readyPromise.promise = new Promise((resolve, reject) => { + readyPromise.resolve = resolve; + readyPromise.reject = reject; + }); - client.onopen = readyPromise.resolve!; - client.onerror = readyPromise.reject!; - client.start(); + client.onopen = readyPromise.resolve!; + client.onerror = readyPromise.reject!; + client.start(); - await readyPromise.promise; + await readyPromise.promise; - await request(source, { - method: "POST", - headers: { - host: "smee.io", - "content-type": "application/json", - }, - }) + await request(source, { + method: "POST", + headers: { + host: "smee.io", + "content-type": "application/json", + }, + }); - await finishedPromise.promise; - }, { timeout: 10000 }); + await finishedPromise.promise; + }, + { timeout: 10000 }, + ); });