mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-15 08:16:50 +00:00
fix: guard self-signed routing request setup
This commit is contained in:
1 parent
d49bbdaec3
commit
5807328cb5
2 files changed
+87
-9
No files matched your search
@@ -1,4 +1,5 @@
|
|||||||
import { describe, expect, it } from 'bun:test';
|
import * as https from 'https';
|
||||||
|
import { describe, expect, it, mock, spyOn } from 'bun:test';
|
||||||
import type { ProxyTarget } from '../../proxy/proxy-target-resolver';
|
import type { ProxyTarget } from '../../proxy/proxy-target-resolver';
|
||||||
|
|
||||||
async function loadRoutingHttpModule() {
|
async function loadRoutingHttpModule() {
|
||||||
@@ -22,6 +23,73 @@ describe('routing-strategy-http', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('rejects malformed self-signed HTTPS URLs before arming the request timeout', async () => {
|
||||||
|
const { fetchCliproxyRoutingResponse } = await loadRoutingHttpModule();
|
||||||
|
const originalSetTimeout = globalThis.setTimeout;
|
||||||
|
globalThis.setTimeout = mock(((handler: TimerHandler, timeout?: number) => {
|
||||||
|
void handler;
|
||||||
|
void timeout;
|
||||||
|
return 1 as unknown as ReturnType<typeof setTimeout>;
|
||||||
|
}) as typeof setTimeout);
|
||||||
|
|
||||||
|
const target: ProxyTarget = {
|
||||||
|
host: 'bad host',
|
||||||
|
port: 443,
|
||||||
|
protocol: 'https',
|
||||||
|
allowSelfSigned: true,
|
||||||
|
isRemote: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await expect(fetchCliproxyRoutingResponse(target, 'GET')).rejects.toThrow();
|
||||||
|
expect(globalThis.setTimeout).not.toHaveBeenCalled();
|
||||||
|
} finally {
|
||||||
|
globalThis.setTimeout = originalSetTimeout;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clears the request timeout when https.request throws synchronously', async () => {
|
||||||
|
const { fetchCliproxyRoutingResponse } = await loadRoutingHttpModule();
|
||||||
|
const originalSetTimeout = globalThis.setTimeout;
|
||||||
|
const originalClearTimeout = globalThis.clearTimeout;
|
||||||
|
const activeTimers = new Set<number>();
|
||||||
|
|
||||||
|
globalThis.setTimeout = mock(((handler: TimerHandler, timeout?: number) => {
|
||||||
|
void handler;
|
||||||
|
void timeout;
|
||||||
|
const timerId = activeTimers.size + 1;
|
||||||
|
activeTimers.add(timerId);
|
||||||
|
return timerId as unknown as ReturnType<typeof setTimeout>;
|
||||||
|
}) as typeof setTimeout);
|
||||||
|
globalThis.clearTimeout = mock(((timerId?: ReturnType<typeof setTimeout>) => {
|
||||||
|
activeTimers.delete(timerId as unknown as number);
|
||||||
|
}) as typeof clearTimeout);
|
||||||
|
const requestSpy = spyOn(https, 'request').mockImplementation(() => {
|
||||||
|
throw new Error('sync request failure');
|
||||||
|
});
|
||||||
|
|
||||||
|
const target: ProxyTarget = {
|
||||||
|
host: 'proxy.example.com',
|
||||||
|
port: 443,
|
||||||
|
protocol: 'https',
|
||||||
|
allowSelfSigned: true,
|
||||||
|
isRemote: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await expect(fetchCliproxyRoutingResponse(target, 'GET')).rejects.toThrow(
|
||||||
|
'sync request failure'
|
||||||
|
);
|
||||||
|
expect(globalThis.setTimeout).toHaveBeenCalledTimes(1);
|
||||||
|
expect(globalThis.clearTimeout).toHaveBeenCalledTimes(1);
|
||||||
|
expect(activeTimers.size).toBe(0);
|
||||||
|
} finally {
|
||||||
|
requestSpy.mockRestore();
|
||||||
|
globalThis.setTimeout = originalSetTimeout;
|
||||||
|
globalThis.clearTimeout = originalClearTimeout;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('builds the remote management URL for routing strategy writes', async () => {
|
it('builds the remote management URL for routing strategy writes', async () => {
|
||||||
const { getCliproxyRoutingManagementUrl } = await loadRoutingHttpModule();
|
const { getCliproxyRoutingManagementUrl } = await loadRoutingHttpModule();
|
||||||
const target: ProxyTarget = {
|
const target: ProxyTarget = {
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ export async function fetchCliproxyRoutingResponse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestUrl = new URL(url);
|
||||||
|
|
||||||
return new Promise<Response>((resolve, reject) => {
|
return new Promise<Response>((resolve, reject) => {
|
||||||
const agent = new https.Agent({ rejectUnauthorized: false });
|
const agent = new https.Agent({ rejectUnauthorized: false });
|
||||||
let settled = false;
|
let settled = false;
|
||||||
@@ -51,14 +53,16 @@ export async function fetchCliproxyRoutingResponse(
|
|||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let req: ReturnType<typeof https.request> | undefined;
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
const error = new Error('Request timeout');
|
const error = new Error('Request timeout');
|
||||||
req.destroy(error);
|
req?.destroy(error);
|
||||||
settle(() => reject(error));
|
settle(() => reject(error));
|
||||||
}, ROUTING_TIMEOUT_MS);
|
}, ROUTING_TIMEOUT_MS);
|
||||||
|
|
||||||
const req = https.request(
|
try {
|
||||||
url,
|
req = https.request(
|
||||||
|
requestUrl,
|
||||||
{
|
{
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
@@ -87,21 +91,27 @@ export async function fetchCliproxyRoutingResponse(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
settle(() => reject(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
req.on('error', (error) => {
|
const request = req;
|
||||||
|
|
||||||
|
request.on('error', (error) => {
|
||||||
settle(() => reject(error));
|
settle(() => reject(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('timeout', () => {
|
request.on('timeout', () => {
|
||||||
const error = new Error('Request timeout');
|
const error = new Error('Request timeout');
|
||||||
req.destroy(error);
|
request.destroy(error);
|
||||||
settle(() => reject(error));
|
settle(() => reject(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (body) {
|
if (body) {
|
||||||
req.write(JSON.stringify(body));
|
request.write(JSON.stringify(body));
|
||||||
}
|
}
|
||||||
req.end();
|
request.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user