DoS guardrails for the unauthenticated local APIs (synapse/main.py, synapse/nexus_config.py):
Request body cap — middleware rejects bodies over NEXUS_MAX_REQUEST_MB (default 32 MB) with 413.
Upload caps — /documents/upload enforces a decoded-size limit (NEXUS_MAX_UPLOAD_MB, default 20 MB) so a small base64 payload can't balloon in RAM, and PDF parsing stops at NEXUS_MAX_PDF_PAGES (default 500).
Concurrency limits — at most NEXUS_MAX_CONCURRENT_CHATS (4) simultaneous inference streams and NEXUS_MAX_CONCURRENT_UPLOADS (2) document ingests; excess requests get 429 instead of exhausting CPU/RAM.
Model-pull allowlist — opt-in NEXUS_MODEL_ALLOWLIST bounds which models /models/pull may download (a bare repo name matches all its tags). Empty default preserves current behaviour.
All limits are env-overridable with generous single-user defaults.
Testing
Oversized Content-Length gets 413 from the middleware; oversized decoded upload gets 413 from the endpoint.
PDF page cap verified against a generated many-page PDF.
Concurrency limiter unit-tested (acquire/release/slot, 429 when saturated).
This is PR 3 of 3, stacked on "SSRF guard + approval tokens" (which stacks on the network-boundary PR). Until those merge, this diff also shows their commits — review only the latest commit here, or merge in order.
## Summary
DoS guardrails for the unauthenticated local APIs (`synapse/main.py`, `synapse/nexus_config.py`):
- **Request body cap** — middleware rejects bodies over `NEXUS_MAX_REQUEST_MB` (default 32 MB) with 413.
- **Upload caps** — `/documents/upload` enforces a decoded-size limit (`NEXUS_MAX_UPLOAD_MB`, default 20 MB) so a small base64 payload can't balloon in RAM, and PDF parsing stops at `NEXUS_MAX_PDF_PAGES` (default 500).
- **Concurrency limits** — at most `NEXUS_MAX_CONCURRENT_CHATS` (4) simultaneous inference streams and `NEXUS_MAX_CONCURRENT_UPLOADS` (2) document ingests; excess requests get 429 instead of exhausting CPU/RAM.
- **Model-pull allowlist** — opt-in `NEXUS_MODEL_ALLOWLIST` bounds which models `/models/pull` may download (a bare repo name matches all its tags). Empty default preserves current behaviour.
All limits are env-overridable with generous single-user defaults.
## Testing
- Oversized `Content-Length` gets 413 from the middleware; oversized decoded upload gets 413 from the endpoint.
- PDF page cap verified against a generated many-page PDF.
- Concurrency limiter unit-tested (acquire/release/slot, 429 when saturated).
- Allowlist matching unit-tested (exact, repo-prefix, case-insensitivity, empty-list passthrough).
- `pytest` smoke tests pass.
## Stack note
This is **PR 3 of 3**, stacked on "SSRF guard + approval tokens" (which stacks on the network-boundary PR). Until those merge, this diff also shows their commits — review only the latest commit here, or merge in order.
The Synapse backend and memory service bound 0.0.0.0 with wildcard CORS and no
auth, exposing the full unauthenticated admin/data API to the LAN. Default the
uvicorn bind to 127.0.0.1 (NEXUS_BIND_HOST override), scope CORS to known local
origins instead of "*", and add TrustedHostMiddleware to reject foreign Host
headers (which defeats DNS-rebinding, something same-origin CORS cannot stop).
NEXUS_ALLOWED_HOSTS / NEXUS_ALLOWED_ORIGINS allow opt-in LAN exposure, intended
to be paired with real authentication.
Co-authored-by: Cursor <cursoragent@cursor.com>
Two tool/agent-layer hardening changes:
* fetch_url now resolves the target host and refuses to connect if any
resolved address is loopback, private (RFC1918/ULA), link-local (incl. the
169.254.169.254 cloud-metadata endpoint), multicast, reserved, or
unspecified. IPv4-mapped IPv6 is unwrapped first, and the guard re-runs on
every redirect hop so a public URL cannot 302 its way to an internal target.
* /chat/approve now requires a single-use token minted when the stream pauses
for approval and delivered only in that stream's tool_request event, compared
in constant time. Previously the pending approval was keyed solely on a
client-supplied conversation_id, so anyone who could enumerate a
conversation_id could approve another client's pending action.
The frontend threads the token from the tool_request event into the approve
call.
Co-authored-by: Cursor <cursoragent@cursor.com>
DoS/quota guardrails for the unauthenticated local APIs:
* Body-size middleware rejects oversized requests (Content-Length) before they
are buffered/base64-decoded (NEXUS_MAX_REQUEST_MB, default 32).
* Document upload enforces a decoded-byte cap (NEXUS_MAX_UPLOAD_MB, default 20)
and a PDF page-count cap (NEXUS_MAX_PDF_PAGES, default 500) as backstops for
chunked bodies and pathological files.
* A counter-based in-flight limiter bounds concurrent chats and document
ingests (NEXUS_MAX_CONCURRENT_CHATS/UPLOADS), returning 429 when saturated;
the chat slot is held for the whole SSE stream and released on completion or
client disconnect.
* /models/pull gains an opt-in allowlist (NEXUS_MODEL_ALLOWLIST); empty by
default so behaviour is unchanged, otherwise a bare repo name permits all its
tags.
Co-authored-by: Cursor <cursoragent@cursor.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
DoS guardrails for the unauthenticated local APIs (
synapse/main.py,synapse/nexus_config.py):NEXUS_MAX_REQUEST_MB(default 32 MB) with 413./documents/uploadenforces a decoded-size limit (NEXUS_MAX_UPLOAD_MB, default 20 MB) so a small base64 payload can't balloon in RAM, and PDF parsing stops atNEXUS_MAX_PDF_PAGES(default 500).NEXUS_MAX_CONCURRENT_CHATS(4) simultaneous inference streams andNEXUS_MAX_CONCURRENT_UPLOADS(2) document ingests; excess requests get 429 instead of exhausting CPU/RAM.NEXUS_MODEL_ALLOWLISTbounds which models/models/pullmay download (a bare repo name matches all its tags). Empty default preserves current behaviour.All limits are env-overridable with generous single-user defaults.
Testing
Content-Lengthgets 413 from the middleware; oversized decoded upload gets 413 from the endpoint.pytestsmoke tests pass.Stack note
This is PR 3 of 3, stacked on "SSRF guard + approval tokens" (which stacks on the network-boundary PR). Until those merge, this diff also shows their commits — review only the latest commit here, or merge in order.