- Shipped
- September 23, 2026 at 8:37 AM UTC
- Author
- Kamo
- Commit
- 133a70d
CareersPublicController's POST /{orgRef}/alerts is the one anonymous, unauthenticated endpoint in this service that sends an email on every accepted call, with nothing gating how often: a caller could mail a confirmation link to any address, repeatedly, at any rate — harassment of a real inbox, or unbounded spend/reputation risk on the platform's own sending domain. Add CareersAlertThrottle (Redis-backed; every rule is a time window, so no schema change): at most one confirmation per hour per address, no re-send while a 24h unconfirmed-pending window is running, and a per-source-IP attempt cap. Also cap how many unconfirmed rows one org can hold at once, so a flood of brand-new addresses can't grow the table without bound. Every check answers by staying silent (subscribe() still returns 202) rather than surfacing a distinguishable error — matching the endpoint's existing rule that a public caller must never be able to tell "throttled" apart from "sent", which would itself be an oracle for "does this address exist". The per-IP key is resolved via LegalClientIp.resolve (the right-most PUBLIC X-Forwarded-For hop), not the **************** style left-most-hop reading used for audit trails elsewhere in this service: the left-most hop is the one a CLIENT writes, so keying a throttle on it would let an attacker defeat the per-IP cap simply by sending a different value on every request. LegalClientIp's own class Javadoc documents exactly this failure mode for the evidentiary case; it applies just as directly here.
