Repository navigation
[codex] Harden provider node URL validation - #5760
diegosouzapw merged 1 commit into
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
diegosouzapw
left a comment
There was a problem hiding this comment.
Thanks for the security-hardening work, @nguyenxvotanminh3 — the direction is right, but there are a few blocking issues (verified against the code). Requesting changes:
🔴 1. Wrong guard function — breaks local providers by default. urlGuard.ts calls getProviderOutboundGuard(), which is arePrivateProviderUrlsAllowed() ? "none" : "public-only" — so with the default (private URLs OFF) it returns public-only and blocks http://127.0.0.1:11434 (Ollama / LM Studio / llama.cpp) with a 400. That contradicts OmniRoute's local-first stance. Use getProviderValidationGuard() instead: its default is block-metadata (allows LAN/loopback, blocks only cloud-metadata like 169.254.169.254). The new test provider nodes route blocks private ... base URLs by default asserts the broken 400, so please flip it to assert 200 for 127.0.0.1:11434 by default and 400 only for a cloud-metadata URL (or when OMNIROUTE_ALLOW_LOCAL_PROVIDER_URLS=false).
🔴 2. CORS loopback check has a bypass. isLoopbackBrowserOrigin() uses host.startsWith("127."), which matches attacker-registrable public hostnames — http://127.evil.com, http://127.0.0.1.nip.io — and echoes them into Access-Control-Allow-Origin. Please use an exact match (host === "127.0.0.1" || host === "::1" || host === "localhost" || host.endsWith(".localhost")) or validate the full 127.0.0.0/8 range numerically, not by string prefix.
🟠 3. Hard Rule #12 (error sanitization). For an OutboundUrlGuardError with code OUTBOUND_URL_INVALID, error.message echoes the raw user-supplied URL back in error.details[].message. Please substitute a fixed string (e.g. "Invalid provider base URL format") for that code path (the PROVIDER_URL_BLOCKED / CLOUD_METADATA_BLOCKED messages are already fixed strings and are fine).
🟠 4. CORS tightening is a breaking change + overlaps #5759. Narrowing /v1/* relaxForTokenAuth from echo-any-origin to loopback-only will CORS-block public-hosted browser API clients (these routes are token-authed via Authorization/x-api-key, which browsers never auto-attach — no CSRF risk, which is why the echo was intentional). If we want this, it needs a CHANGELOG entry + a docs note that operators must set CORS_ALLOWED_ORIGINS. It also overlaps the in-flight #5759 (CORS work for #5602) — let's reconcile the two before merging so we don't double-touch applyCorsHeaders.
Happy to help once these are addressed. The SSRF guard (with the correct function) and the exact-loopback CORS check are both good additions.
|
Thanks for the hardening pass. Three blocking issues before this can land, plus a base + coordination note:
Leaving the PR open for the revision. |
b50f8fb to
8c16e31
Compare
|
Thanks Diego, I updated the PR based on your review. Changes made:
Verification performed locally:
I also ran a live local-first verification from this branch on an isolated data dir/port: created an OpenAI-compatible provider node pointing at a dummy local provider on |
|
Maintainer note — validated release-green ✅ I merged this PR's latest revision onto the current
The switch from This is ready to merge; it's only being held right now by the active release freeze (#5826 — v3.8.43 reconciliation). It'll be merged once the freeze lifts. |
Stale review — all round-6 findings were fully addressed in the force-pushed head (local-first guard via getProviderValidationGuard, CORS scope removed, error message sanitized with regression test). Verified in re-review 2026-07-01.
19703cd
into
cheaper-inference:release/v3.8.43
|
Thanks @nguyenxvotanminh3! You addressed every point from the previous review — validation guard local-first by default, metadata endpoints blocked, sanitized errors with regression tests. Textbook response to feedback. Merged into release/v3.8.43. 🙌 |
Integrated into release/v3.8.43
Summary
OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS=trueopt-in still works.Validation
node --import tsx/esm --test tests/unit/custom-headers-provider-nodes.test.ts tests/unit/cc-compatible-provider.test.ts tests/unit/provider-nodes-route.test.ts tests/unit/authz/pipeline.test.ts tests/unit/cors/origins.test.tsnpm run typecheck:corenpx eslint src/app/api/provider-nodes/urlGuard.ts src/app/api/provider-nodes/route.ts src/app/api/provider-nodes/[id]/route.ts src/server/cors/origins.ts tests/unit/provider-nodes-route.test.ts tests/unit/cors/origins.test.ts tests/unit/authz/pipeline.test.tsgit diff --check