mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 20:32:20 +03:00
* fix(providers): honor configured proxy on Grok Build egress The grok-cli executor reaches Grok Build over raw `https.request()` (forced IPv4, to dodge Cloudflare blocking on the direct path) rather than the process-wide patched `fetch()` that every other executor uses. `https.request()` never consults the proxy AsyncLocalStorage context, so the proxy the caller already pinned upstream in chatHelpers.ts (`runWithProxyContext`) was silently ignored on BOTH grok-cli paths: chat inference (`nativePost`) and OAuth token refresh (`nativeHttpsPost`, POST https://auth.x.ai/oauth2/token). User-visible effect: an operator who assigns a proxy to a Grok Build connection (or provider/global scope) still egresses on the host's real IP — an IP leak that defeats account-isolation/anonymity setups, and breaks Grok Build entirely for operators who must egress through a proxy. Fix is delta-only: `resolveGrokRequestDispatch()` reads the already-resolved proxy via the shared `resolveProxyForRequest()` and returns either an HttpsProxyAgent bound to it, or — when no proxy is configured — the existing forced-IPv4 direct options, unchanged. Only HTTP/HTTPS CONNECT proxies are supported on this path; an explicitly configured proxy of another kind (SOCKS5) fails closed rather than silently leaking direct, matching the fail-closed convention for OAuth/account proxies (#3051). The proxy URL is never logged, so proxy credentials cannot leak into logs. Regression test: tests/unit/grok-cli-proxy-selection.test.ts (RED before the fix — `resolveGrokRequestDispatch` did not exist and both request builders hardcoded `family: 4` with no agent; GREEN after). Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> Inspired-by: https://github.com/decolua/9router/pull/2343 * chore(changelog): fragment for #7244 --------- Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com>
57 lines
2.5 KiB
TypeScript
57 lines
2.5 KiB
TypeScript
// Regression test for: Grok Build (grok-cli) inference/refresh requests bypassed the
|
|
// operator's configured proxy entirely. The executor talks to Grok's upstream via raw
|
|
// Node `https.request()` (forced IPv4, to dodge Cloudflare blocking on the direct path)
|
|
// instead of the process-wide patched `fetch()` that every other executor uses — so a
|
|
// proxy pinned to the connection/provider/global scope was silently ignored, leaking the
|
|
// real egress IP and defeating account-isolation/anonymity setups. Mirrors the class of
|
|
// bug fixed upstream in decolua/9router#2343 ("fix(oauth): honor proxy selection during
|
|
// OAuth login"), adapted to OmniRoute's actual grok-cli architecture (import-token flow,
|
|
// no device-code polling) where the leak lives in `resolveGrokRequestDispatch()`.
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { resolveGrokRequestDispatch } from "../../open-sse/executors/grok-cli.ts";
|
|
|
|
const TARGET_URL = "https://grok.x.ai/rest/app-chat/conversations/new";
|
|
|
|
test("resolveGrokRequestDispatch: no proxy configured -> direct IPv4 dispatch (unchanged behavior)", () => {
|
|
const dispatch = resolveGrokRequestDispatch(TARGET_URL, () => ({
|
|
source: "direct",
|
|
proxyUrl: null,
|
|
}));
|
|
|
|
assert.equal(dispatch.family, 4);
|
|
assert.equal(dispatch.agent, undefined);
|
|
});
|
|
|
|
test("resolveGrokRequestDispatch: HTTP proxy configured -> request is dispatched through a proxy agent, not direct", () => {
|
|
const dispatch = resolveGrokRequestDispatch(TARGET_URL, () => ({
|
|
source: "context",
|
|
proxyUrl: "http://proxy.internal:8080",
|
|
}));
|
|
|
|
// The fix: an agent bound to the configured proxy must be present, and the
|
|
// direct-IPv4 workaround must NOT be applied (it would race the proxy tunnel).
|
|
assert.ok(dispatch.agent, "expected a proxy agent to be constructed");
|
|
assert.notEqual(dispatch.family, 4);
|
|
});
|
|
|
|
test("resolveGrokRequestDispatch: HTTPS proxy configured -> request is dispatched through a proxy agent", () => {
|
|
const dispatch = resolveGrokRequestDispatch(TARGET_URL, () => ({
|
|
source: "context",
|
|
proxyUrl: "https://user:pass@proxy.internal:8443",
|
|
}));
|
|
|
|
assert.ok(dispatch.agent, "expected a proxy agent to be constructed");
|
|
});
|
|
|
|
test("resolveGrokRequestDispatch: unsupported proxy protocol (socks5) fails closed instead of leaking direct", () => {
|
|
assert.throws(
|
|
() =>
|
|
resolveGrokRequestDispatch(TARGET_URL, () => ({
|
|
source: "context",
|
|
proxyUrl: "socks5://proxy.internal:1080",
|
|
})),
|
|
/proxy/i
|
|
);
|
|
});
|