From 1e3f67b079aa3f75a7f34a6e301dfade0bc8f9e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 20:54:56 +0000 Subject: [PATCH 01/10] Initial plan From 353763c2c519f9fd5505dd9a77da1cffa5e38709 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:01:38 +0000 Subject: [PATCH 02/10] fix: use strict-url-sanitise to prevent RCE vulnerability (CVE-2025-11953) Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- packages/cli-server-api/package.json | 1 + .../src/__tests__/openURLMiddleware.test.ts | 66 +++++++++++++++++++ .../cli-server-api/src/openURLMiddleware.ts | 21 +++--- yarn.lock | 5 ++ 4 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts diff --git a/packages/cli-server-api/package.json b/packages/cli-server-api/package.json index 4610149de..e4a015479 100644 --- a/packages/cli-server-api/package.json +++ b/packages/cli-server-api/package.json @@ -16,6 +16,7 @@ "open": "^6.2.0", "pretty-format": "^29.7.0", "serve-static": "^1.13.1", + "strict-url-sanitise": "0.0.1", "ws": "^6.2.3" }, "devDependencies": { diff --git a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts new file mode 100644 index 000000000..6d98407f8 --- /dev/null +++ b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts @@ -0,0 +1,66 @@ +import http from 'http'; +import open from 'open'; +import {openURLMiddleware} from '../openURLMiddleware'; + +jest.mock('open'); +jest.mock('strict-url-sanitise', () => ({ + sanitizeUrl: jest.fn((url: string) => { + // Simulate the behavior of strict-url-sanitise + const parsed = new URL(url); + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { + throw new Error(`Invalid url to pass to open(): ${url}`); + } + if (parsed.hostname !== encodeURIComponent(parsed.hostname)) { + throw new Error(`Invalid url to pass to open(): ${url}`); + } + return url; + }), +})); + +describe('openURLMiddleware', () => { + let req: http.IncomingMessage & {body?: Object}; + let res: jest.Mocked; + let next: jest.Mock; + + beforeEach(() => { + req = { + method: 'POST', + body: {}, + } as any; + + res = { + writeHead: jest.fn(), + end: jest.fn(), + } as any; + + next = jest.fn(); + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should return 400 for non-string URL', async () => { + req.body = {url: 123}; + + await openURLMiddleware(req, res, next); + + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('URL must be a string'); + }); + + it('should reject malicious URL with invalid hostname', async () => { + const maliciousUrl = 'https://www.$(calc.exe).com/foo'; + req.body = {url: maliciousUrl}; + + await openURLMiddleware(req, res, next); + + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith( + expect.stringContaining('Invalid url to pass to open()'), + ); + }); +}); diff --git a/packages/cli-server-api/src/openURLMiddleware.ts b/packages/cli-server-api/src/openURLMiddleware.ts index 588600790..d424739dc 100644 --- a/packages/cli-server-api/src/openURLMiddleware.ts +++ b/packages/cli-server-api/src/openURLMiddleware.ts @@ -14,7 +14,7 @@ import open from 'open'; /** * Open a URL in the system browser. */ -async function openURLMiddleware( +export async function openURLMiddleware( req: IncomingMessage & { // Populated by body-parser body?: Object; @@ -31,20 +31,23 @@ async function openURLMiddleware( const {url} = req.body as {url: string}; + if (typeof url !== 'string') { + res.writeHead(400); + res.end('URL must be a string'); + return; + } + + let sanitizedUrl: string; try { - const parsedUrl = new URL(url); - if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { - res.writeHead(400); - res.end('Invalid URL protocol'); - return; - } + const {sanitizeUrl} = await import('strict-url-sanitise'); + sanitizedUrl = sanitizeUrl(url); } catch (error) { res.writeHead(400); - res.end('Invalid URL format'); + res.end(error instanceof Error ? error.message : 'Invalid URL'); return; } - await open(url); + await open(sanitizedUrl); res.writeHead(200); res.end(); diff --git a/yarn.lock b/yarn.lock index 09cccd258..222f1725c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9703,6 +9703,11 @@ stream-buffers@2.2.x: resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== +strict-url-sanitise@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/strict-url-sanitise/-/strict-url-sanitise-0.0.1.tgz#10cfac63c9dfdd856d98ab9f76433dad5ce99e0c" + integrity sha512-nuFtF539K8jZg3FjaWH/L8eocCR6gegz5RDOsaWxfdbF5Jqr2VXWxZayjTwUzsWJDC91k2EbnJXp6FuWW+Z4hg== + string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" From 5f5105bf7fcd1fe4579ecd2a444d6c8d178a4146 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:04:54 +0000 Subject: [PATCH 03/10] refactor: cache sanitizeUrl to improve performance Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- packages/cli-server-api/src/openURLMiddleware.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/cli-server-api/src/openURLMiddleware.ts b/packages/cli-server-api/src/openURLMiddleware.ts index d424739dc..8f47a4293 100644 --- a/packages/cli-server-api/src/openURLMiddleware.ts +++ b/packages/cli-server-api/src/openURLMiddleware.ts @@ -11,6 +11,17 @@ import {json} from 'body-parser'; import connect from 'connect'; import open from 'open'; +// Cache the imported sanitizeUrl function to avoid repeated dynamic imports +let sanitizeUrlFn: ((url: string) => string) | null = null; + +async function getSanitizeUrl(): Promise<(url: string) => string> { + if (sanitizeUrlFn === null) { + const module = await import('strict-url-sanitise'); + sanitizeUrlFn = module.sanitizeUrl; + } + return sanitizeUrlFn; +} + /** * Open a URL in the system browser. */ @@ -39,7 +50,7 @@ export async function openURLMiddleware( let sanitizedUrl: string; try { - const {sanitizeUrl} = await import('strict-url-sanitise'); + const sanitizeUrl = await getSanitizeUrl(); sanitizedUrl = sanitizeUrl(url); } catch (error) { res.writeHead(400); From f8a1ff05f52c06cef19a5c52a081810bb088be3e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:11:11 +0000 Subject: [PATCH 04/10] simplify: use "Invalid URL" error message, remove strict-url-sanitise mock Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- .../src/__tests__/openURLMiddleware.test.ts | 17 +---------------- .../cli-server-api/src/openURLMiddleware.ts | 4 ++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts index 6d98407f8..b89f6c4c6 100644 --- a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts +++ b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts @@ -3,19 +3,6 @@ import open from 'open'; import {openURLMiddleware} from '../openURLMiddleware'; jest.mock('open'); -jest.mock('strict-url-sanitise', () => ({ - sanitizeUrl: jest.fn((url: string) => { - // Simulate the behavior of strict-url-sanitise - const parsed = new URL(url); - if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { - throw new Error(`Invalid url to pass to open(): ${url}`); - } - if (parsed.hostname !== encodeURIComponent(parsed.hostname)) { - throw new Error(`Invalid url to pass to open(): ${url}`); - } - return url; - }), -})); describe('openURLMiddleware', () => { let req: http.IncomingMessage & {body?: Object}; @@ -59,8 +46,6 @@ describe('openURLMiddleware', () => { expect(open).not.toHaveBeenCalled(); expect(res.writeHead).toHaveBeenCalledWith(400); - expect(res.end).toHaveBeenCalledWith( - expect.stringContaining('Invalid url to pass to open()'), - ); + expect(res.end).toHaveBeenCalledWith('Invalid URL'); }); }); diff --git a/packages/cli-server-api/src/openURLMiddleware.ts b/packages/cli-server-api/src/openURLMiddleware.ts index 8f47a4293..299318aed 100644 --- a/packages/cli-server-api/src/openURLMiddleware.ts +++ b/packages/cli-server-api/src/openURLMiddleware.ts @@ -52,9 +52,9 @@ export async function openURLMiddleware( try { const sanitizeUrl = await getSanitizeUrl(); sanitizedUrl = sanitizeUrl(url); - } catch (error) { + } catch { res.writeHead(400); - res.end(error instanceof Error ? error.message : 'Invalid URL'); + res.end('Invalid URL'); return; } From 08bba3f3b138a6d4373aa5c98fc0cac51b3e811b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:11:34 +0000 Subject: [PATCH 05/10] style: replace it() with test() in openURLMiddleware tests Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- .../cli-server-api/src/__tests__/openURLMiddleware.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts index b89f6c4c6..139950ddc 100644 --- a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts +++ b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts @@ -28,7 +28,7 @@ describe('openURLMiddleware', () => { jest.restoreAllMocks(); }); - it('should return 400 for non-string URL', async () => { + test('should return 400 for non-string URL', async () => { req.body = {url: 123}; await openURLMiddleware(req, res, next); @@ -38,7 +38,7 @@ describe('openURLMiddleware', () => { expect(res.end).toHaveBeenCalledWith('URL must be a string'); }); - it('should reject malicious URL with invalid hostname', async () => { + test('should reject malicious URL with invalid hostname', async () => { const maliciousUrl = 'https://www.$(calc.exe).com/foo'; req.body = {url: maliciousUrl}; From a340aff45767e796e1bf1c62579e962fba6d1beb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:19:44 +0000 Subject: [PATCH 06/10] refactor: use ordinary import for strict-url-sanitise Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- packages/cli-server-api/src/openURLMiddleware.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/cli-server-api/src/openURLMiddleware.ts b/packages/cli-server-api/src/openURLMiddleware.ts index 299318aed..480d9e322 100644 --- a/packages/cli-server-api/src/openURLMiddleware.ts +++ b/packages/cli-server-api/src/openURLMiddleware.ts @@ -10,17 +10,7 @@ import type {IncomingMessage, ServerResponse} from 'http'; import {json} from 'body-parser'; import connect from 'connect'; import open from 'open'; - -// Cache the imported sanitizeUrl function to avoid repeated dynamic imports -let sanitizeUrlFn: ((url: string) => string) | null = null; - -async function getSanitizeUrl(): Promise<(url: string) => string> { - if (sanitizeUrlFn === null) { - const module = await import('strict-url-sanitise'); - sanitizeUrlFn = module.sanitizeUrl; - } - return sanitizeUrlFn; -} +import {sanitizeUrl} from 'strict-url-sanitise'; /** * Open a URL in the system browser. @@ -50,7 +40,6 @@ export async function openURLMiddleware( let sanitizedUrl: string; try { - const sanitizeUrl = await getSanitizeUrl(); sanitizedUrl = sanitizeUrl(url); } catch { res.writeHead(400); From 9cdcd7ac024a93ea97e4891088df3340c06ad75f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:30:48 +0000 Subject: [PATCH 07/10] refactor: remove named export of openURLMiddleware, test default export Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- .../src/__tests__/openURLMiddleware.test.ts | 56 ++++++++++++------- .../cli-server-api/src/openURLMiddleware.ts | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts index 139950ddc..65d0c41dc 100644 --- a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts +++ b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts @@ -1,23 +1,35 @@ import http from 'http'; +import {Readable} from 'stream'; import open from 'open'; -import {openURLMiddleware} from '../openURLMiddleware'; +import openURLMiddleware from '../openURLMiddleware'; jest.mock('open'); +function createMockRequest(method: string, body: object): http.IncomingMessage { + const bodyStr = JSON.stringify(body); + const readable = new Readable(); + readable.push(bodyStr); + readable.push(null); + + return Object.assign(readable, { + method, + url: '/', + headers: { + 'content-type': 'application/json', + 'content-length': String(Buffer.byteLength(bodyStr)), + }, + }) as unknown as http.IncomingMessage; +} + describe('openURLMiddleware', () => { - let req: http.IncomingMessage & {body?: Object}; let res: jest.Mocked; let next: jest.Mock; beforeEach(() => { - req = { - method: 'POST', - body: {}, - } as any; - res = { writeHead: jest.fn(), end: jest.fn(), + setHeader: jest.fn(), } as any; next = jest.fn(); @@ -28,24 +40,30 @@ describe('openURLMiddleware', () => { jest.restoreAllMocks(); }); - test('should return 400 for non-string URL', async () => { - req.body = {url: 123}; + test('should return 400 for non-string URL', (done) => { + const req = createMockRequest('POST', {url: 123}); - await openURLMiddleware(req, res, next); + res.end = jest.fn(() => { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('URL must be a string'); + done(); + }) as any; - expect(open).not.toHaveBeenCalled(); - expect(res.writeHead).toHaveBeenCalledWith(400); - expect(res.end).toHaveBeenCalledWith('URL must be a string'); + openURLMiddleware(req, res, next); }); - test('should reject malicious URL with invalid hostname', async () => { + test('should reject malicious URL with invalid hostname', (done) => { const maliciousUrl = 'https://www.$(calc.exe).com/foo'; - req.body = {url: maliciousUrl}; + const req = createMockRequest('POST', {url: maliciousUrl}); - await openURLMiddleware(req, res, next); + res.end = jest.fn(() => { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('Invalid URL'); + done(); + }) as any; - expect(open).not.toHaveBeenCalled(); - expect(res.writeHead).toHaveBeenCalledWith(400); - expect(res.end).toHaveBeenCalledWith('Invalid URL'); + openURLMiddleware(req, res, next); }); }); diff --git a/packages/cli-server-api/src/openURLMiddleware.ts b/packages/cli-server-api/src/openURLMiddleware.ts index 480d9e322..14d5eae1a 100644 --- a/packages/cli-server-api/src/openURLMiddleware.ts +++ b/packages/cli-server-api/src/openURLMiddleware.ts @@ -15,7 +15,7 @@ import {sanitizeUrl} from 'strict-url-sanitise'; /** * Open a URL in the system browser. */ -export async function openURLMiddleware( +async function openURLMiddleware( req: IncomingMessage & { // Populated by body-parser body?: Object; From 32fdc763ec8f0d7ce0d2097966cb06b81481eb4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:58:12 +0000 Subject: [PATCH 08/10] test: add CVE-2025-11953 test cases for Windows pipe and command exfiltration Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- .../src/__tests__/openURLMiddleware.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts index 65d0c41dc..bed04e86b 100644 --- a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts +++ b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts @@ -66,4 +66,35 @@ describe('openURLMiddleware', () => { openURLMiddleware(req, res, next); }); + + // CVE-2025-11953 + test('should reject URL with Windows pipe separator', (done) => { + const maliciousUrl = 'https://evil.com?|calc.exe'; + const req = createMockRequest('POST', {url: maliciousUrl}); + + res.end = jest.fn(() => { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('Invalid URL'); + done(); + }) as any; + + openURLMiddleware(req, res, next); + }); + + // CVE-2025-11953 + test('should reject URL with Windows command exfiltration', (done) => { + // Encodes to reveal %BETA% env var + const maliciousUrl = 'https://example.com/?a=%¾TA%'; + const req = createMockRequest('POST', {url: maliciousUrl}); + + res.end = jest.fn(() => { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('Invalid URL'); + done(); + }) as any; + + openURLMiddleware(req, res, next); + }); }); From d4f2711a172e91a68e0915a91145260530994855 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:00:40 +0000 Subject: [PATCH 09/10] test: add CVE-2025-11953 comment above second test case Co-authored-by: huntie <2547783+huntie@users.noreply.github.com> --- packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts index bed04e86b..112d50798 100644 --- a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts +++ b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts @@ -53,6 +53,7 @@ describe('openURLMiddleware', () => { openURLMiddleware(req, res, next); }); + // CVE-2025-11953 test('should reject malicious URL with invalid hostname', (done) => { const maliciousUrl = 'https://www.$(calc.exe).com/foo'; const req = createMockRequest('POST', {url: maliciousUrl}); From 41cef8e3272f11633d1ca145839dbff70a49fa6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Wed, 28 Jan 2026 11:02:48 +0100 Subject: [PATCH 10/10] fixup tests to not hang when fail --- .../src/__tests__/openURLMiddleware.test.ts | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts index 112d50798..0f26e8274 100644 --- a/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts +++ b/packages/cli-server-api/src/__tests__/openURLMiddleware.test.ts @@ -44,10 +44,14 @@ describe('openURLMiddleware', () => { const req = createMockRequest('POST', {url: 123}); res.end = jest.fn(() => { - expect(open).not.toHaveBeenCalled(); - expect(res.writeHead).toHaveBeenCalledWith(400); - expect(res.end).toHaveBeenCalledWith('URL must be a string'); - done(); + try { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('URL must be a string'); + done(); + } catch (error) { + done(error); + } }) as any; openURLMiddleware(req, res, next); @@ -59,10 +63,14 @@ describe('openURLMiddleware', () => { const req = createMockRequest('POST', {url: maliciousUrl}); res.end = jest.fn(() => { - expect(open).not.toHaveBeenCalled(); - expect(res.writeHead).toHaveBeenCalledWith(400); - expect(res.end).toHaveBeenCalledWith('Invalid URL'); - done(); + try { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('Invalid URL'); + done(); + } catch (error) { + done(error); + } }) as any; openURLMiddleware(req, res, next); @@ -74,10 +82,14 @@ describe('openURLMiddleware', () => { const req = createMockRequest('POST', {url: maliciousUrl}); res.end = jest.fn(() => { - expect(open).not.toHaveBeenCalled(); - expect(res.writeHead).toHaveBeenCalledWith(400); - expect(res.end).toHaveBeenCalledWith('Invalid URL'); - done(); + try { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('Invalid URL'); + done(); + } catch (error) { + done(error); + } }) as any; openURLMiddleware(req, res, next); @@ -90,10 +102,14 @@ describe('openURLMiddleware', () => { const req = createMockRequest('POST', {url: maliciousUrl}); res.end = jest.fn(() => { - expect(open).not.toHaveBeenCalled(); - expect(res.writeHead).toHaveBeenCalledWith(400); - expect(res.end).toHaveBeenCalledWith('Invalid URL'); - done(); + try { + expect(open).not.toHaveBeenCalled(); + expect(res.writeHead).toHaveBeenCalledWith(400); + expect(res.end).toHaveBeenCalledWith('Invalid URL'); + done(); + } catch (error) { + done(error); + } }) as any; openURLMiddleware(req, res, next);