diff --git a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js index 8268559..9d3388d 100644 --- a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js @@ -67,21 +67,29 @@ function createHttpsServer(hostname, port, interceptor) { return; } - const pathAndQuery = getRequestPathAndQuery(req.url); - const targetUrl = `https://${hostname}${pathAndQuery}`; + try { + const pathAndQuery = getRequestPathAndQuery(req.url); + const targetUrl = `https://${hostname}${pathAndQuery}`; - const requestInterceptor = await interceptor.handleRequest(targetUrl); - const blockResponse = requestInterceptor.blockResponse; + const requestInterceptor = await interceptor.handleRequest(targetUrl); + const blockResponse = requestInterceptor.blockResponse; - if (blockResponse) { - ui.writeVerbose(`Safe-chain: Blocking request to ${targetUrl}`); - res.writeHead(blockResponse.statusCode, blockResponse.message); - res.end(blockResponse.message); - return; + if (blockResponse) { + ui.writeVerbose(`Safe-chain: Blocking request to ${targetUrl}`); + res.writeHead(blockResponse.statusCode, blockResponse.message); + res.end(blockResponse.message); + return; + } + + // Collect request body + forwardRequest(req, hostname, port, res, requestInterceptor); + } catch (/** @type {any} */ error) { + ui.writeError( + `Safe-chain: Error handling request for ${req.url}: ${error.message}` + ); + res.writeHead(502, "Bad Gateway"); + res.end("Bad Gateway: Error handling request"); } - - // Collect request body - forwardRequest(req, hostname, port, res, requestInterceptor); } const server = https.createServer( diff --git a/test/e2e/dns-failure-resilience.e2e.spec.js b/test/e2e/dns-failure-resilience.e2e.spec.js new file mode 100644 index 0000000..9419979 --- /dev/null +++ b/test/e2e/dns-failure-resilience.e2e.spec.js @@ -0,0 +1,52 @@ +import { describe, it, before, beforeEach, afterEach } from "node:test"; +import { DockerTestContainer } from "./DockerTestContainer.js"; +import assert from "node:assert"; + +describe("E2E: DNS failure resilience", () => { + let container; + + before(async () => { + DockerTestContainer.buildImage(); + }); + + beforeEach(async () => { + container = new DockerTestContainer(); + await container.start(); + + const installationShell = await container.openShell("zsh"); + await installationShell.runCommand("safe-chain setup"); + }); + + afterEach(async () => { + if (container) { + await container.stop(); + container = null; + } + }); + + it("should not crash when the malware database is unreachable", async () => { + const shell = await container.openShell("zsh"); + + // Make the malware database domain unreachable + // This forces fetchMalwareDatabase to fail + await shell.runCommand( + 'echo "127.0.0.1 malware-list.aikido.dev" >> /etc/hosts' + ); + + const result = await shell.runCommand( + "npm install lodash --safe-chain-logging=verbose" + ); + + assert.ok( + result.output.includes("Safe-chain: Error handling request"), + `Output did not include expected error handling message. Output was:\n${result.output}` + ); + + // Ensure it did NOT crash with Unhandled Promise Rejection + assert.strictEqual( + result.output.includes("Unhandled promise rejection"), + false, + `Output indicates process crash (Unhandled promise rejection). Output was:\n${result.output}` + ); + }); +});