From bfca5feed72604b4859f4fa30746e145df09de6b Mon Sep 17 00:00:00 2001 From: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com> Date: Mon, 4 May 2026 02:30:39 +1000 Subject: [PATCH] fix: reject conflicting managed proxy activation --- src/infra/net/proxy/proxy-lifecycle.test.ts | 27 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/infra/net/proxy/proxy-lifecycle.test.ts b/src/infra/net/proxy/proxy-lifecycle.test.ts index af87b79d492..382e4e86153 100644 --- a/src/infra/net/proxy/proxy-lifecycle.test.ts +++ b/src/infra/net/proxy/proxy-lifecycle.test.ts @@ -291,7 +291,7 @@ describe("startProxy", () => { expect((global as Record)["GLOBAL_AGENT"]).toBeUndefined(); }); - it("keeps process-wide proxy hooks active until the last overlapping handle stops", async () => { + it("keeps process-wide proxy hooks active until the last same-URL overlapping handle stops", async () => { const patchedHttpRequest = vi.fn() as unknown as typeof http.request; const patchedHttpGet = vi.fn() as unknown as typeof http.get; const patchedHttpsRequest = vi.fn() as unknown as typeof https.request; @@ -313,18 +313,18 @@ describe("startProxy", () => { }); const secondHandle = await startProxy({ enabled: true, - proxyUrl: "http://127.0.0.1:3129", + proxyUrl: "http://127.0.0.1:3128", }); expect(http.request).toBe(patchedHttpRequest); expect(https.request).toBe(patchedHttpsRequest); - expect(process.env["HTTP_PROXY"]).toBe("http://127.0.0.1:3129"); + expect(process.env["HTTP_PROXY"]).toBe("http://127.0.0.1:3128"); await stopProxy(firstHandle); expect(http.request).toBe(patchedHttpRequest); expect(https.request).toBe(patchedHttpsRequest); - expect(process.env["HTTP_PROXY"]).toBe("http://127.0.0.1:3129"); + expect(process.env["HTTP_PROXY"]).toBe("http://127.0.0.1:3128"); expect(process.env["OPENCLAW_PROXY_ACTIVE"]).toBe("1"); await stopProxy(secondHandle); @@ -337,6 +337,25 @@ describe("startProxy", () => { expect(process.env["OPENCLAW_PROXY_ACTIVE"]).toBeUndefined(); }); + it("rejects overlapping handles with different managed proxy URLs", async () => { + const firstHandle = await startProxy({ + enabled: true, + proxyUrl: "http://127.0.0.1:3128", + }); + + await expect( + startProxy({ + enabled: true, + proxyUrl: "http://127.0.0.1:3129", + }), + ).rejects.toThrow("cannot activate a different managed proxy"); + + expect(process.env["HTTP_PROXY"]).toBe("http://127.0.0.1:3128"); + expect(process.env["OPENCLAW_PROXY_ACTIVE"]).toBe("1"); + + await stopProxy(firstHandle); + }); + it("restores env and throws when undici activation fails", async () => { mockForceResetGlobalDispatcher.mockImplementationOnce(() => { throw new Error("dispatcher failed");