fix(security): restrict canvas IP-based auth to private networks (#14661)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 9e4e1aca4a
Co-authored-by: sumleo <29517764+sumleo@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
This commit is contained in:
Yi Liu
2026-02-14 00:13:31 +08:00
committed by GitHub
parent e665d77917
commit 14fc742000
5 changed files with 133 additions and 12 deletions

View File

@@ -51,7 +51,7 @@ import {
} from "./hooks.js";
import { sendGatewayAuthFailure } from "./http-common.js";
import { getBearerToken, getHeader } from "./http-utils.js";
import { resolveGatewayClientIp } from "./net.js";
import { isPrivateOrLoopbackAddress, resolveGatewayClientIp } from "./net.js";
import { handleOpenAiHttpRequest } from "./openai-http.js";
import { handleOpenResponsesHttpRequest } from "./openresponses-http.js";
import { handleToolsInvokeHttpRequest } from "./tools-invoke-http.js";
@@ -143,6 +143,13 @@ async function authorizeCanvasRequest(params: {
if (!clientIp) {
return lastAuthFailure ?? { ok: false, reason: "unauthorized" };
}
// IP-based fallback is only safe for machine-scoped addresses.
// Only allow IP-based fallback for private/loopback addresses to prevent
// cross-session access in shared-IP environments (corporate NAT, cloud).
if (!isPrivateOrLoopbackAddress(clientIp)) {
return lastAuthFailure ?? { ok: false, reason: "unauthorized" };
}
if (hasAuthorizedWsClientForIp(clients, clientIp)) {
return { ok: true };
}