diff --git a/CHANGELOG.md b/CHANGELOG.md index 0777c1a5..5e6369c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If - Prometheus: event counters are exported with incorrect metric names. - Registry: Changing the type of an existing account from `user` to `group` panics. - Masked emails: Return `UnknownRecipient` only for disabled or expired masked emails. +- IDN: `sanitize_email` rejects valid Punycode domains. ## [0.16.11] - 2026-06-25 diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 76ea7cd0..d657e9e5 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -285,10 +285,16 @@ pub fn sanitize_email(email: &str) -> Option { for ch in chars { match ch { - '.' | '-' | '_' => { + '.' => { if !last_ch.is_alphanumeric() { return None; } + result.push('.'); + } + '-' | '_' => { + if last_ch == NIL_CHAR || last_ch == '.' { + return None; + } result.push(ch); } ' ' | '\x09'..='\x0d' => continue, @@ -477,6 +483,18 @@ mod tests { ); } + #[test] + fn a_label_email_domains_are_accepted_and_idempotent() { + assert_eq!( + sanitize_email("user@xn--fsqu00a.com").as_deref(), + Some("user@xn--fsqu00a.com") + ); + assert_eq!( + sanitize_email("User@例子.com").as_deref(), + sanitize_email("user@xn--fsqu00a.com").as_deref() + ); + } + #[test] fn to_ascii_domain_borrows_ascii_owns_idn() { assert!(matches!(