From 65c9ca62de57fc86dc66de120dcf252269542ffb Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 31 Oct 2025 09:39:16 +0100 Subject: [PATCH 1/7] Subscribe to more error events to prevent the process from crashing --- .../src/registryProxy/mitmRequestHandler.js | 23 +++++++++++++++++- .../src/registryProxy/plainHttpProxy.js | 9 +++++-- .../src/registryProxy/tunnelRequestHandler.js | 24 ++++++++++++------- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js index 63a8168..e95bb62 100644 --- a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js @@ -37,13 +37,19 @@ function createHttpsServer(hostname, isAllowed) { forwardRequest(req, hostname, res); } - return https.createServer( + const server = https.createServer( { key: cert.privateKey, cert: cert.certificate, }, handleRequest ); + + server.on("error", (err) => { + ui.writeError(`Safe-chain: HTTPS server error: ${err.message}`); + }); + + return server; } function getRequestPathAndQuery(url) { @@ -62,6 +68,11 @@ function forwardRequest(req, hostname, res) { res.end("Bad Gateway"); }); + req.on("error", (err) => { + ui.writeError(`Safe-chain: Error reading client request: ${err.message}`); + proxyReq.destroy(); + }); + req.on("data", (chunk) => { proxyReq.write(chunk); }); @@ -88,6 +99,16 @@ function createProxyRequest(hostname, req, res) { } const proxyReq = https.request(options, (proxyRes) => { + proxyRes.on("error", (err) => { + ui.writeError( + `Safe-chain: Error reading upstream response: ${err.message}` + ); + if (!res.headersSent) { + res.writeHead(502); + res.end("Bad Gateway"); + } + }); + res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); }); diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js index e337b44..ac3dc69 100644 --- a/packages/safe-chain/src/registryProxy/plainHttpProxy.js +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -43,8 +43,13 @@ export function handleHttpProxyRequest(req, res) { } ) .on("error", (err) => { - res.writeHead(502); - res.end(`Bad Gateway: ${err.message}`); + if (!res.headersSent) { + res.writeHead(502); + res.end(`Bad Gateway: ${err.message}`); + } else { + // Headers already sent, just destroy the response + res.destroy(); + } }); req.on("error", () => { diff --git a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js index c28a022..91b1c40 100644 --- a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js @@ -24,12 +24,6 @@ export function tunnelRequest(req, clientSocket, head) { function tunnelRequestToDestination(req, clientSocket, head) { const { port, hostname } = new URL(`http://${req.url}`); - clientSocket.on("error", () => { - // NO-OP - // This can happen if the client TCP socket sends RST instead of FIN. - // Not subscribing to 'close' event will cause node to throw and crash. - }); - const serverSocket = net.connect(port || 443, hostname, () => { clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); serverSocket.write(head); @@ -37,6 +31,14 @@ function tunnelRequestToDestination(req, clientSocket, head) { clientSocket.pipe(serverSocket); }); + clientSocket.on("error", () => { + // This can happen if the client TCP socket sends RST instead of FIN. + // Not subscribing to 'close' event will cause node to throw and crash. + if (serverSocket.writable) { + serverSocket.end(); + } + }); + serverSocket.on("error", (err) => { ui.writeError( `Safe-chain: error connecting to ${hostname}:${port} - ${err.message}` @@ -100,9 +102,13 @@ function tunnelRequestViaProxy(req, clientSocket, head, proxyUrl) { proxy.port || 8080 } - ${err.message}` ); - if (clientSocket.writable) { - clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); - } + } else { + ui.writeError( + `Safe-chain: proxy socket error after connection - ${err.message}` + ); + } + if (clientSocket.writable) { + clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); } }); From efb0044419d747df02f2c063dd530ee3006aff2f Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 31 Oct 2025 10:26:56 +0100 Subject: [PATCH 2/7] Add global exception handlers --- packages/safe-chain/src/main.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/safe-chain/src/main.js b/packages/safe-chain/src/main.js index e106e83..c3af410 100644 --- a/packages/safe-chain/src/main.js +++ b/packages/safe-chain/src/main.js @@ -11,6 +11,21 @@ export async function main(args) { const proxy = createSafeChainProxy(); await proxy.startServer(); + // Global error handlers to log unhandled errors + process.on("uncaughtException", (error) => { + ui.writeError(`Safe-chain: Uncaught exception: ${error.message}`); + ui.writeVerbose(`Stack trace: ${error.stack}`); + process.exit(1); + }); + + process.on("unhandledRejection", (reason) => { + ui.writeError(`Safe-chain: Unhandled promise rejection: ${reason}`); + if (reason instanceof Error) { + ui.writeVerbose(`Stack trace: ${reason.stack}`); + } + process.exit(1); + }); + try { // This parses all the --safe-chain arguments and removes them from the args array args = initializeCliArguments(args); From bae43d0dcda6622c4e53eb0b9887a0755ce8a19c Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 31 Oct 2025 11:38:16 +0100 Subject: [PATCH 3/7] MITM handler: Close the response on server error --- packages/safe-chain/src/registryProxy/mitmRequestHandler.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js index e95bb62..5967d1a 100644 --- a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js @@ -47,6 +47,12 @@ function createHttpsServer(hostname, isAllowed) { server.on("error", (err) => { ui.writeError(`Safe-chain: HTTPS server error: ${err.message}`); + if (!res.headersSent) { + res.writeHead(502); + res.end("Bad Gateway"); + } else if (res.writable) { + res.destroy(); + } }); return server; From df5c424a42721b0aa0337f55869211270c2f939f Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 31 Oct 2025 11:38:39 +0100 Subject: [PATCH 4/7] Add missing import (ui) in mitmRequestHandler.js --- packages/safe-chain/src/registryProxy/mitmRequestHandler.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js index 5967d1a..101fdd8 100644 --- a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js @@ -1,6 +1,7 @@ import https from "https"; import { generateCertForHost } from "./certUtils.js"; import { HttpsProxyAgent } from "https-proxy-agent"; +import { ui } from "../environment/userInteraction.js"; export function mitmConnect(req, clientSocket, isAllowed) { const { hostname } = new URL(`http://${req.url}`); From 4dc14397ad45596b1afcac015362ca8c0dcdf5f9 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 31 Oct 2025 11:40:01 +0100 Subject: [PATCH 5/7] Use correct event name in comment (error) --- packages/safe-chain/src/registryProxy/tunnelRequestHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js index 91b1c40..ceaf91d 100644 --- a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js @@ -33,7 +33,7 @@ function tunnelRequestToDestination(req, clientSocket, head) { clientSocket.on("error", () => { // This can happen if the client TCP socket sends RST instead of FIN. - // Not subscribing to 'close' event will cause node to throw and crash. + // Not subscribing to 'error' event will cause node to throw and crash. if (serverSocket.writable) { serverSocket.end(); } From 78fd93b72a3adc71c3b16a979039b6f637750d3e Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 31 Oct 2025 11:41:39 +0100 Subject: [PATCH 6/7] End clientsocket without 502 in case of proxySocket error --- .../safe-chain/src/registryProxy/tunnelRequestHandler.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js index ceaf91d..5c764f5 100644 --- a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js @@ -102,13 +102,16 @@ function tunnelRequestViaProxy(req, clientSocket, head, proxyUrl) { proxy.port || 8080 } - ${err.message}` ); + if (clientSocket.writable) { + clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); + } } else { ui.writeError( `Safe-chain: proxy socket error after connection - ${err.message}` ); - } - if (clientSocket.writable) { - clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); + if (clientSocket.writable) { + clientSocket.end(); + } } }); From 3721ca91131ee792a13972e6809048403a9b9d30 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 31 Oct 2025 13:56:35 +0100 Subject: [PATCH 7/7] Fix linter issues --- .oxlintrc.json | 3 ++- .../src/registryProxy/mitmRequestHandler.js | 19 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index b76f2ad..b9c483c 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -14,7 +14,8 @@ }, "rules": { "eslint/no-console": "error", - "eslint/no-empty": "error" + "eslint/no-empty": "error", + "eslint/no-undef": "error" }, "overrides": [ { diff --git a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js index 101fdd8..fe8998e 100644 --- a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js @@ -14,6 +14,15 @@ export function mitmConnect(req, clientSocket, isAllowed) { const server = createHttpsServer(hostname, isAllowed); + server.on("error", (err) => { + ui.writeError(`Safe-chain: HTTPS server error: ${err.message}`); + if (!clientSocket.headersSent) { + clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); + } else if (clientSocket.writable) { + clientSocket.end(); + } + }); + // Establish the connection clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); @@ -46,16 +55,6 @@ function createHttpsServer(hostname, isAllowed) { handleRequest ); - server.on("error", (err) => { - ui.writeError(`Safe-chain: HTTPS server error: ${err.message}`); - if (!res.headersSent) { - res.writeHead(502); - res.end("Bad Gateway"); - } else if (res.writable) { - res.destroy(); - } - }); - return server; }