fix(relay): share one private-host guard across the three relay workers (#10941)

Reconciliado com #10935 (já mergeada) — mesclado o guard inline recém-mergeado com a extração para `privateHostname.ts` deste PR, mantendo a intenção original: os 3 workers de relay agora usam a MESMA função compartilhada. Validado: lint limpo, 49/49 testes focados passando (incluindo verificação de que nenhum worker mantém cópia inline). Hardening de segurança real e bem documentado (4 gaps de bypass: `::`, `localhost.`, `::127.0.0.1`, `feb0::1`). CI vermelho é o base-red já rastreado em #9985. Obrigado!
This commit is contained in:
Nguyen Thanh Dat
2026-08-21 14:17:25 +07:00
committed by GitHub
parent 6eaa737333
commit 4fa204de68
8 changed files with 241 additions and 95 deletions

View File

@@ -30,6 +30,7 @@
*/
import { randomUUID } from "crypto";
import { resolveRelayTarget } from "@/app/api/settings/proxy/deno-deploy/route";
import { isPrivateRelayHostname } from "@/lib/proxyRelay/privateHostname";
/**
* Build the multipart/form-data request body for Cloudflare's Worker
@@ -84,33 +85,7 @@ export function buildCloudflareWorkerScript(relayAuth: string): string {
return `// OmniRoute Cloudflare Worker proxy relay — generated at deploy time.
const resolveRelayTarget = ${resolveRelayTarget.toString()};
function isPrivateHostname(h) {
if (!h) return true;
const host = h.trim().toLowerCase().replace(/^\\[|\\]$/g, "");
if (
host === "localhost" ||
host === "0.0.0.0" || host === "127.0.0.1" || host === "::1" ||
host.endsWith(".localhost") ||
host.endsWith(".local") ||
host.endsWith(".internal") ||
host.startsWith("::ffff:")
) return true;
const v4 = host.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);
if (v4) {
const a = +v4[1], b = +v4[2];
if (a === 0 || a === 10 || a === 127) return true;
if (a === 169 && b === 254) return true; // link-local IPv4
if (a === 192 && b === 168) return true;
if (a === 172 && b >= 16 && b <= 31) return true;
if (a === 100 && b >= 64 && b <= 127) return true;
return false;
}
if (host.includes(":")) {
// IPv6 loopback/ULA/link-local (fe80::/10)
return host === "::1" || host.startsWith("fc") || host.startsWith("fd") || host.startsWith("fe80:");
}
return false;
}
const isPrivateHostname = ${isPrivateRelayHostname.toString()};
async function handleRelay(request) {
const auth = request.headers.get("x-relay-auth");