mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-13 10:43:43 +03:00
When OmniRoute itself runs behind an external reverse proxy (nginx, Caddy, Cloudflare Tunnel), `req.socket.remoteAddress` on the inbound connection is the proxy hop -- usually 127.0.0.1 -- not the real end-user. The custom Node server stamped that loopback IP as the trusted PEER_IP_HEADER, so `classifyHostLocality()` returned "loopback" for every remote caller arriving via the proxy. The LOCAL_ONLY route guard (which gates spawn-capable routes: /api/mcp/, /api/cli-tools/runtime/, /api/services/, /api/plugins/, /api/system/version, /api/tools/agent-bridge/, ...) was therefore effectively bypassed: a leaked JWT over the public tunnel could trigger arbitrary child-process spawning (Hard Rules #15 + #17). Fix mirrors decolua/9router commit da667836: 1. scripts/dev/peer-stamp.mjs stamps a companion VIA_PROXY_HEADER as `<token>|1` whenever the inbound request carries forwarding headers (`x-forwarded-for` / `x-real-ip`), token-protected with the same per-process secret as PEER_IP_HEADER so a remote caller cannot forge either its presence or its absence. 2. src/server/authz/peerStamp.ts exposes a new `classifyStampedPeerLocality(peer, viaProxy, token)` that combines the two stamps: when the via-proxy marker is present, a loopback / private-LAN socket is downgraded to "remote" so the LOCAL_ONLY tier is not bypassed. 3. src/server/authz/pipeline.ts switches the AUTHZ_HEADER_PEER_LOCALITY stamp to the new helper and strips VIA_PROXY_HEADER from the forwarded headers alongside PEER_IP_HEADER. 4. src/server/authz/policies/management.ts gates `isLoopbackRequest()` and `isPrivateLanRequest()` on the same via-proxy check so the management policy carve-outs (CLI-token gate, LAN-allowed LOCAL_ONLY surface) also refuse to honour a proxy-hop socket. TDD: tests/unit/route-guard-loopback-via-proxy.test.ts proves a request with `socket.remoteAddress=127.0.0.1` + a via-proxy stamp is classified "remote". RED first, then GREEN. The local-CLI happy path (loopback socket, no proxy stamp) is unchanged. Untokened via-proxy hints from a remote attacker are ignored. Existing tests (route-guard-private-lan, authz/routeGuard, authz/pipeline, authz/management-policy) all stay green. Inspired-by: decolua/9router@da667836 Co-authored-by: decolua <decoluadt@example.com>