Summary
There is no rate limiting anywhere in the system, and the list endpoint allows unbounded pagination and unindexed ILIKE scans — allowing authenticated tenants to drive DB load and the public redirector to be flooded into a Postgres pool exhaustion outage.
Evidence
- No rate-limit middleware is registered in either server (
crates/vym-fyi-server-crud/src/main.rs:41-52, crates/vym-fyi-server-redirect/src/main.rs:36-47).
crates/vym-fyi-server-redirect/src/app.rs:36-39 — redirect pool is capped at 5 connections; every GET /{slug} is a Postgres query with no server-side cache (repos.rs:175-190).
crates/vym-fyi-server-crud/src/handlers/links.rs:170-173 — page is an unbounded u32; offset = (page-1)*per_page can reach ~4.3e11 → deep Postgres scans.
crates/vym-fyi-server-crud/src/handlers/links.rs:206-208 — target_contains becomes an unindexed ILIKE '%…%' (no prefix requirement).
Impact
- Public redirect flood: no load shedding → pool starvation → global outage of the redirector.
- Write flood: a single tenant can issue unlimited
create_link calls (each ≥1 DB write, up to 5 collision retries) and exhaust the pool of 5.
- Slow queries: ILIKE/full-table list scans hold all pool connections.
Severity: MEDIUM.
Suggested fix
- Rate-limit
POST /api/links per authenticated API key.
- Rate-limit
GET /{slug} per client IP (enforced before the DB query).
- Cap
page (e.g. reject page > some bound) and require a prefix match (or an index) for target_contains.
- Add statement timeouts on all queries.
Summary
There is no rate limiting anywhere in the system, and the list endpoint allows unbounded pagination and unindexed
ILIKEscans — allowing authenticated tenants to drive DB load and the public redirector to be flooded into a Postgres pool exhaustion outage.Evidence
crates/vym-fyi-server-crud/src/main.rs:41-52,crates/vym-fyi-server-redirect/src/main.rs:36-47).crates/vym-fyi-server-redirect/src/app.rs:36-39— redirect pool is capped at 5 connections; everyGET /{slug}is a Postgres query with no server-side cache (repos.rs:175-190).crates/vym-fyi-server-crud/src/handlers/links.rs:170-173—pageis an unboundedu32;offset = (page-1)*per_pagecan reach ~4.3e11 → deep Postgres scans.crates/vym-fyi-server-crud/src/handlers/links.rs:206-208—target_containsbecomes an unindexedILIKE '%…%'(no prefix requirement).Impact
create_linkcalls (each ≥1 DB write, up to 5 collision retries) and exhaust the pool of 5.Severity: MEDIUM.
Suggested fix
POST /api/linksper authenticated API key.GET /{slug}per client IP (enforced before the DB query).page(e.g. rejectpage> some bound) and require a prefix match (or an index) fortarget_contains.