polyrouter is a self-hostable LLM router that sits between your agents and your LLM providers, holding provider API keys and notification credentials. We take its security posture seriously.
Please do not open a public issue for security vulnerabilities.
Report privately via one of:
- GitHub Security Advisories — the "Report a vulnerability" button under the repository's Security tab (preferred; lets us collaborate on a fix privately).
- Email — send details to the maintainers (see the repository owner's profile). Encrypt with our public key if you have highly sensitive details.
Please include:
- a description of the issue and its impact,
- the affected version / commit and configuration (e.g.
MODE=selfhostedvs cloud), - reproduction steps or a proof of concept,
- any known mitigations.
We aim to acknowledge a report within 72 hours and to provide a remediation timeline after triage. We will credit reporters who wish to be named once a fix is released.
In scope: the control plane and proxy (packages/control-plane, packages/data-plane), the SPA
(packages/frontend), the shared security utilities (packages/shared), the container image, and the
install.sh bootstrap.
Particularly sensitive areas, by design:
- SSRF — every server-fetched, user-supplied URL/host (provider
base_url, Apprise/webhook targets,APPRISE_API_URL, SMTP host) is validated against private/loopback/link-local/metadata ranges, resolving the IP to defeat DNS rebinding. Loopback is permitted only for local providers underMODE=selfhosted. - Credential handling — agent API keys are HMAC-SHA256 (prefix-indexed); session passwords use a slow hash; provider and notification-channel credentials are encrypted at rest (AES-256-GCM). Secrets are never logged and never returned by the API.
- Tenant isolation — every owned resource is ownership-scoped centrally.
- Privacy — metadata only by default; prompt/response bodies are never persisted without explicit opt-in.
/api/health and /metrics are unauthenticated by design (orchestration + Prometheus). The app
publishes on loopback by default; exposing it beyond loopback is a deliberate step to take after
claiming the admin account, behind a reverse proxy with TLS and access control. See the README's
"Claim the instance, then expose it" section.