fix: redact sensitive query values from timeout diagnostics (#2319 round 23)

The timeout message interpolated the raw request URL, and
createFetchTracker records that message verbatim into the Network log
and the persisted session. An OAuth endpoint's query string can carry a
`code`, an `access_token` or a `client_secret` — exactly the fields
fetchTracking already redacts everywhere else it records a URL — so a
stalled signed endpoint would have written its secret to both.

Redacted with the existing `redactUrlQuery`, in the
OAuthRequestTimeoutError constructor rather than at each display site,
so both the message and the public `url` field are covered by
construction and no future consumer can reintroduce it. The route's own
proxied-timeout message and the URL in its 504 marker get the same
treatment, since both cross back to the browser and are recorded there.

The path and every non-sensitive parameter survive, so the endpoint
stays identifiable — which was the point of naming it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFnM6RqB4nXqNysgUYmjmq
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-09-11 07:57:38 -04:00
commit 1e501fa5cb

View file

@ -29,6 +29,7 @@ import { watch as chokidarWatch, type FSWatcher } from "chokidar";
import { createTransportNode } from "../../node/transport.js";
import { createProxyFetch } from "../../node/proxyFetch.js";
import { OAUTH_TIMEOUT_WIRE_CODE } from "../../../auth/requestTimeout.js";
import { redactUrlQuery } from "../../fetchTracking.js";
import type {
RemoteConnectRequest,
RemoteSendRequest,
@ -1266,6 +1267,11 @@ export function createRemoteApp(
Number.isFinite(requestedTimeoutMs)
? Math.min(2_147_483_647, Math.max(0, Math.round(requestedTimeoutMs)))
: undefined;
// Redacted for the same reason `OAuthRequestTimeoutError` redacts: this
// message and this URL are handed back to the browser, recorded in the
// Network log and persisted, and an OAuth endpoint's query string can carry
// a `code`, an `access_token` or a `client_secret` (Copilot).
const safeUrl = redactUrlQuery(url);
const timer =
deadlineMs === undefined
? undefined
@ -1273,7 +1279,7 @@ export function createRemoteApp(
deadlineFired = true;
controller.abort(
new Error(
`proxied request to ${url} timed out after ${deadlineMs}ms`,
`proxied request to ${safeUrl} timed out after ${deadlineMs}ms`,
),
);
}, deadlineMs);
@ -1338,7 +1344,7 @@ export function createRemoteApp(
{
error: msg,
code: OAUTH_TIMEOUT_WIRE_CODE,
url,
url: safeUrl,
timeoutMs: deadlineMs,
},
504,