Feature/84 ipv6 rate limit key #86

Merged
bermudalamb merged 2 commits from feature/84-ipv6-rate-limit-key into main 2026-08-20 18:44:49 -05:00
Owner
No description provided.
bermudalamb added 2 commits 2026-08-20 18:43:46 -05:00
The QA stack has been logging ERR_ERL_KEY_GEN_IPV6 at every boot, and express-rate-limit was right to complain.

keyByCallerAndEmail built its key from req.ip raw. For an IPv4 caller that is one address and the limiter worked as intended. For an IPv6 caller it is the full 128 bits — and a residential IPv6 customer is delegated an entire prefix and can source every request from a different address inside it at no cost. Keyed that way, each request counted as a new caller and the allowance of five per fifteen minutes never bound at all.

That matters more here than it would elsewhere, because of what this limiter is for. Its own comment says it: without one, anyone can make the server send unlimited mail to any address they choose. For IPv6 clients there effectively was no limiter, while the code read as though there were.

The caller half of the key now goes through express-rate-limit's ipKeyGenerator, which groups IPv6 by prefix and returns IPv4 unchanged. The helper's default is /56 rather than /64, and that default is kept deliberately: /56 covers a whole delegated site, so an attacker cannot escape their bucket by moving within their own allocation. It does mean several households behind one delegation share an allowance — acceptable only because the key also carries the email address, so they collide just when targeting the same account. The reasoning sits next to the code, because a future reader tightening it to /64 would silently reopen the hole.

keyByCallerAndEmail is now exported so it can be tested directly. The limiter's allowance is still not asserted anywhere, and should not be: its store is process-wide, so a test that exhausts it leaks into every later test from the same address and fails something unrelated later. The key function is pure, and it is where the bug was.

Verified by firing the guard rather than reasoning about it: building main and loading the module reproduces the ValidationError, and the same load with this change is silent. Seven new unit tests cover an IPv4 caller unchanged, two addresses in one delegation collapsing to a single key, separate delegations staying apart, an IPv4-mapped address keying the same as the plain IPv4 one, email normalisation, a non-string email, and a request with no address at all.

86 unit tests pass, 144 integration, lint 0 errors and 8 warnings — unchanged.

Closes #84
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge branch 'main' into feature/84-ipv6-rate-limit-key
SonarQube Analysis / sonarqube (pull_request) Failing after 10m38s
Tests / lint (pull_request) Successful in 1m41s
Tests / backend-unit (pull_request) Successful in 50s
Tests / frontend-e2e (pull_request) Failing after 7m43s
44465fdd88
bermudalamb merged commit d4d602da3e into main 2026-08-20 18:44:49 -05:00
bermudalamb deleted branch feature/84-ipv6-rate-limit-key 2026-08-20 18:44:49 -05:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bermudalamb/redefined-designs#86