- Shipped
- September 23, 2026 at 7:50 AM UTC
- Author
- Kamo
- Commit
- 10784ec
POST /api/email/member-imap/test and /{id}/imap/test connected to whatever host and port the caller supplied and returned the raw exception message. Any authenticated member could point either at cluster-internal infrastructure — Redis, another pod, the Kubernetes API — and read the exact wording that came back (refused, timed out, reset) to fingerprint what was listening there, one host at a time: a port scanner built entirely out of a "test my mail settings" button. ImapConnectionTester now resolves both hosts and refuses before either socket opens: loopback, the RFC 1918 and carrier-grade-NAT (100.64.0.0/10) IPv4 ranges, link-local, multicast, unspecified, the IPv6 fc00::/7 Unique Local range (Java's isSiteLocalAddress() only recognises the older, deprecated fec0::/10, so ULA needed its own check), dotless single-label names, and Kubernetes' **************** suffixes are all rejected with the identical, generic "Could not connect to that host" — every refusal reads the same, so a caller can no longer tell an internal loopback from an internal /8 from a name DNS never answers. The same guard runs before a host is SAVED, not only before it is tested — MemberImapController's PUT and PersonalMailboxService#saveImap — because a member who skips the test button still has these settings dialed later by the background sync, on a timer, with no test result anyone reads. **************** **************** and two tests added to PersonalMailboxServiceTest fail without the change — a private-range host currently connects (or saves) instead of being refused, and the reported error currently varies by what was actually listening on the other end.
