mirror of
https://github.com/iamlukethedev/Claw3D.git
synced 2026-08-14 00:58:04 +00:00
fix(gateway+server): improve diagnostics for origin/auth rejection and silent upstream reject (#125)
* fix(gateway): don't surface protocol-mismatch hint when server rejects origin/auth The heuristic that flags INVALID_REQUEST errors mentioning minProtocol or maxProtocol as a possible protocol mismatch can fire on any schema validation error that happens to mention those field names, including rejections whose real cause is origin allowlist, missing device identity, or upstream policy. Prefer the structured details.code when the gateway provides one, and treat known non-protocol codes (CONTROL_UI_ORIGIN_NOT_ALLOWED, CONTROL_UI_DEVICE_IDENTITY_REQUIRED, UPSTREAM_NOT_ALLOWED) as non-mismatches so the UI does not send operators down the wrong diagnostic path. * fix(server): log explicit warning when gateway proxy rejects an upstream Previously isUpstreamAllowed returned false silently when UPSTREAM_ALLOWLIST was empty in production, when the upstream host was not in the allowlist, or when the URL could not be parsed. The caller closes the connection without forwarding anything to the browser, which sees an opaque 'WebSocket closed before the connection is established' and the server logs contain no hint of what happened. Emit a console.warn in each of the three rejection branches with an actionable message that names the missing env var or the rejected hostname, so operators can diagnose the rejection from the server logs without reading the proxy source. * fix(gateway): don't bias the local-timeout hint toward a protocol mismatch formatGatewayError unconditionally appended a hint suggesting the user upgrade OpenClaw or switch to the Hermes adapter whenever the browser reported a generic "timed out connecting to the gateway" Error. That Error is raised by a local Promise.race timeout, so it carries no information about why the upstream did not respond. Common real causes are network reachability, nginx idle timeouts, origin allowlist, missing UPSTREAM_ALLOWLIST in production, or credential mismatches — none of which are addressed by upgrading the gateway. Replace the biased suggestion with a neutral pointer to the likely diagnostic paths so the hint helps in the common cases without misleading away from origin/auth/policy issues. --------- Co-authored-by: Jose Antonio Martinez <257598434+jamartineztelecoengineer84-dotcom@users.noreply.github.com>
This commit is contained in:
co-authored by
Jose Antonio Martinez
parent
5f96276133
commit
51cd3a1e94
+25
-3
@@ -71,7 +71,15 @@ const createFrameRateLimiter = (
|
||||
const isUpstreamAllowed = (url) => {
|
||||
const allowlist = (process.env.UPSTREAM_ALLOWLIST || "").trim();
|
||||
if (!allowlist) {
|
||||
return process.env.NODE_ENV !== "production";
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
console.warn(
|
||||
"[gateway-proxy] refusing upstream connection: UPSTREAM_ALLOWLIST is " +
|
||||
"empty in production. Set UPSTREAM_ALLOWLIST=host1,host2 (comma-" +
|
||||
"separated hostnames) to allow specific upstream hosts."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
@@ -79,8 +87,22 @@ const isUpstreamAllowed = (url) => {
|
||||
.split(",")
|
||||
.map((h) => h.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
return allowed.includes(parsed.hostname.toLowerCase());
|
||||
} catch {
|
||||
const hostname = parsed.hostname.toLowerCase();
|
||||
if (!allowed.includes(hostname)) {
|
||||
console.warn(
|
||||
`[gateway-proxy] refusing upstream connection to "${hostname}": host ` +
|
||||
`not in UPSTREAM_ALLOWLIST (${allowlist}). Add the host to the ` +
|
||||
`allowlist if you trust it.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
const reason = err && typeof err === "object" && "message" in err ? err.message : "invalid URL";
|
||||
console.warn(
|
||||
`[gateway-proxy] refusing upstream connection: could not parse URL ` +
|
||||
`"${url}" (${reason}).`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -544,6 +544,23 @@ const requiresDeviceIdentityHint =
|
||||
|
||||
const isGatewayProtocolMismatchError = (error: GatewayResponseError) => {
|
||||
if (error.code.trim().toUpperCase() !== "INVALID_REQUEST") return false;
|
||||
// The gateway may provide a structured details.code alongside the
|
||||
// generic INVALID_REQUEST. Known non-protocol rejection codes must
|
||||
// not surface the "possible protocol mismatch" hint, since it
|
||||
// misleads operators whose real problem is origin allowlist, missing
|
||||
// device identity, or upstream policy.
|
||||
const details = error.details;
|
||||
if (details && typeof details === "object") {
|
||||
const code = (details as { code?: unknown }).code;
|
||||
if (typeof code === "string") {
|
||||
const NON_PROTOCOL_DETAIL_CODES = new Set([
|
||||
"CONTROL_UI_ORIGIN_NOT_ALLOWED",
|
||||
"CONTROL_UI_DEVICE_IDENTITY_REQUIRED",
|
||||
"UPSTREAM_NOT_ALLOWED",
|
||||
]);
|
||||
if (NON_PROTOCOL_DETAIL_CODES.has(code)) return false;
|
||||
}
|
||||
}
|
||||
const message = error.message.trim();
|
||||
if (!message) return false;
|
||||
return /minProtocol|maxProtocol/i.test(message);
|
||||
@@ -573,7 +590,12 @@ const formatGatewayError = (error: unknown) => {
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
if (/timed out connecting to the gateway/i.test(error.message)) {
|
||||
return `${error.message} If you are testing locally, an older OpenClaw build may be speaking an incompatible protocol. Try upgrading OpenClaw, using the Hermes adapter, or running \`npm run demo-gateway\`.`;
|
||||
// A local timeout carries no information about why the upstream did
|
||||
// not respond. Suggest the directions the operator can actually check,
|
||||
// without biasing toward a protocol mismatch — that is only one of
|
||||
// several possible root causes (network, origin allowlist, upstream
|
||||
// policy, credentials, nginx idle timeout, ...).
|
||||
return `${error.message} Verify that the gateway is reachable at the configured URL, that origin and credentials meet the gateway's requirements, and (if testing locally with a self-built gateway) consider \`npm run demo-gateway\` to isolate the problem.`;
|
||||
}
|
||||
return error.message;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user