
























1+import { mkdtemp, rm } from "node:fs/promises";
2+import { createServer as createHttpServer } from "node:http";
3+import { Socket, type AddressInfo } from "node:net";
4+import { tmpdir } from "node:os";
5+import { join } from "node:path";
6+import { afterEach, beforeEach, describe, expect, it } from "vitest";
7+import { assertDebugProxyDirectUpstreamAllowed, startDebugProxyServer } from "./proxy-server.js";
8+9+let testRoot: string | undefined;
10+11+async function cleanupTestDirs(): Promise<void> {
12+if (!testRoot) {
13+return;
14+}
15+const root = testRoot;
16+testRoot = undefined;
17+await rm(root, { recursive: true, force: true });
18+}
19+20+async function makeSettings() {
21+testRoot = await mkdtemp(join(tmpdir(), "openclaw-debug-proxy-managed-proxy-"));
22+return {
23+enabled: true,
24+required: false,
25+dbPath: ":memory:",
26+blobDir: join(testRoot, "blobs"),
27+certDir: join(testRoot, "certs"),
28+sessionId: "debug-proxy-managed-proxy-test",
29+sourceProcess: "test",
30+};
31+}
32+33+async function connectThroughProxy(proxyUrl: string): Promise<string> {
34+const target = new URL(proxyUrl);
35+const socket = new Socket();
36+let data = "";
37+socket.setEncoding("utf8");
38+socket.on("data", (chunk) => {
39+data += chunk;
40+});
41+await new Promise<void>((resolve, reject) => {
42+socket.once("error", reject);
43+socket.connect(Number(target.port), target.hostname, resolve);
44+});
45+socket.write("CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n");
46+await new Promise<void>((resolve) => socket.once("end", resolve));
47+socket.destroy();
48+return data;
49+}
50+51+async function requestThroughProxy(proxyUrl: string, targetUrl: string): Promise<string> {
52+const proxy = new URL(proxyUrl);
53+const target = new URL(targetUrl);
54+const socket = new Socket();
55+let data = "";
56+socket.setEncoding("utf8");
57+socket.on("data", (chunk) => {
58+data += chunk;
59+});
60+await new Promise<void>((resolve, reject) => {
61+socket.once("error", reject);
62+socket.connect(Number(proxy.port), proxy.hostname, resolve);
63+});
64+socket.write(`GET ${target.href} HTTP/1.1\r\nHost: ${target.host}\r\nConnection: close\r\n\r\n`);
65+await new Promise<void>((resolve) => socket.once("end", resolve));
66+socket.destroy();
67+return data;
68+}
69+70+async function startCanaryOrigin(): Promise<{
71+requestCount: () => number;
72+stop: () => Promise<void>;
73+url: string;
74+}> {
75+let requests = 0;
76+const server = createHttpServer((_req, res) => {
77+requests += 1;
78+res.end("ok");
79+});
80+await new Promise<void>((resolve, reject) => {
81+server.once("error", reject);
82+server.listen(0, "127.0.0.1", () => {
83+server.off("error", reject);
84+resolve();
85+});
86+});
87+const address = server.address() as AddressInfo;
88+return {
89+requestCount: () => requests,
90+stop: async () =>
91+await new Promise<void>((resolve, reject) => {
92+server.close((error) => {
93+if (error) {
94+reject(error);
95+return;
96+}
97+resolve();
98+});
99+}),
100+url: `http://127.0.0.1:${address.port}/metadata`,
101+};
102+}
103+104+describe("debug proxy managed-proxy direct upstream policy", () => {
105+const originalProxyActive = process.env["OPENCLAW_PROXY_ACTIVE"];
106+const originalAllowDirect =
107+process.env["OPENCLAW_DEBUG_PROXY_ALLOW_DIRECT_CONNECT_WITH_MANAGED_PROXY"];
108+109+beforeEach(async () => {
110+await cleanupTestDirs();
111+delete process.env["OPENCLAW_PROXY_ACTIVE"];
112+delete process.env["OPENCLAW_DEBUG_PROXY_ALLOW_DIRECT_CONNECT_WITH_MANAGED_PROXY"];
113+});
114+115+afterEach(async () => {
116+if (originalProxyActive === undefined) {
117+delete process.env["OPENCLAW_PROXY_ACTIVE"];
118+} else {
119+process.env["OPENCLAW_PROXY_ACTIVE"] = originalProxyActive;
120+}
121+if (originalAllowDirect === undefined) {
122+delete process.env["OPENCLAW_DEBUG_PROXY_ALLOW_DIRECT_CONNECT_WITH_MANAGED_PROXY"];
123+} else {
124+process.env["OPENCLAW_DEBUG_PROXY_ALLOW_DIRECT_CONNECT_WITH_MANAGED_PROXY"] =
125+originalAllowDirect;
126+}
127+await cleanupTestDirs();
128+});
129+130+it("allows direct upstreams when managed proxy mode is inactive", () => {
131+expect(() => assertDebugProxyDirectUpstreamAllowed()).not.toThrow();
132+});
133+134+it("rejects direct upstreams while managed proxy mode is active", () => {
135+process.env["OPENCLAW_PROXY_ACTIVE"] = "1";
136+137+expect(() => assertDebugProxyDirectUpstreamAllowed()).toThrow(
138+/Debug proxy direct upstream forwarding is disabled/,
139+);
140+});
141+142+it("uses shared truthy parsing for managed proxy mode", () => {
143+process.env["OPENCLAW_PROXY_ACTIVE"] = "true";
144+145+expect(() => assertDebugProxyDirectUpstreamAllowed()).toThrow(
146+/Debug proxy direct upstream forwarding is disabled/,
147+);
148+});
149+150+it("allows direct upstreams with explicit diagnostic override", () => {
151+process.env["OPENCLAW_PROXY_ACTIVE"] = "1";
152+process.env["OPENCLAW_DEBUG_PROXY_ALLOW_DIRECT_CONNECT_WITH_MANAGED_PROXY"] = "1";
153+154+expect(() => assertDebugProxyDirectUpstreamAllowed()).not.toThrow();
155+});
156+157+it("rejects CONNECT upstreams before opening direct sockets while managed proxy mode is active", async () => {
158+process.env["OPENCLAW_PROXY_ACTIVE"] = "1";
159+const server = await startDebugProxyServer({ settings: await makeSettings() });
160+try {
161+const response = await connectThroughProxy(server.proxyUrl);
162+163+expect(response).toContain("403 Forbidden");
164+expect(response).toContain("Connection: close");
165+expect(response).toContain("Debug proxy direct upstream forwarding is disabled");
166+} finally {
167+await server.stop();
168+}
169+});
170+171+it("rejects absolute-form HTTP proxy requests before opening direct upstreams while managed proxy mode is active", async () => {
172+process.env["OPENCLAW_PROXY_ACTIVE"] = "1";
173+const origin = await startCanaryOrigin();
174+const server = await startDebugProxyServer({ settings: await makeSettings() });
175+try {
176+const response = await requestThroughProxy(server.proxyUrl, origin.url);
177+178+expect(response).toContain("403 Forbidden");
179+expect(response).toContain("Connection: close");
180+expect(response).toContain("Debug proxy direct upstream forwarding is disabled");
181+expect(origin.requestCount()).toBe(0);
182+} finally {
183+await server.stop();
184+await origin.stop();
185+}
186+});
187+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。