diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs index 2e28b0cb6ac3d..c7743f0144baa 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs @@ -27,6 +27,11 @@ public override bool IsValid(object? value) return false; } + if (valueAsString.AsSpan().ContainsAny('\r', '\n')) + { + return false; + } + // only return true if there is only 1 '@' character // and it is neither the first nor the last character int index = valueAsString.IndexOf('@'); diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs index a0c67ed7d2b92..da80016608e40 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs @@ -29,6 +29,7 @@ protected override IEnumerable InvalidValues() yield return new TestCase(new EmailAddressAttribute(), 0); yield return new TestCase(new EmailAddressAttribute(), ""); yield return new TestCase(new EmailAddressAttribute(), " \r \t \n" ); + yield return new TestCase(new EmailAddressAttribute(), "someName@[\r\n\tsomeDomain]"); yield return new TestCase(new EmailAddressAttribute(), "@someDomain.com"); yield return new TestCase(new EmailAddressAttribute(), "@someDomain@abc.com"); yield return new TestCase(new EmailAddressAttribute(), "someName"); diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs index 47ac65dbf9b5d..a4cc338453b8e 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs @@ -216,6 +216,12 @@ private string GetHost(bool allowUnicode) throw new SmtpException(SR.Format(SR.SmtpInvalidHostName, Address), argEx); } } + + if (domain.AsSpan().ContainsAny('\r', '\n')) + { + throw new SmtpException(SR.Format(SR.SmtpInvalidHostName, Address)); + } + return domain; } diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs index 306368df20218..c8c5734b04cf4 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs @@ -9,11 +9,15 @@ // (C) 2006 John Luke // +using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; using Systen.Net.Mail.Tests; using System.Net.Test.Common; using Xunit; @@ -573,5 +577,41 @@ public void TestGssapiAuthentication() Assert.Equal("GSSAPI", server.AuthMethodUsed, StringComparer.OrdinalIgnoreCase); } + + [Theory] + [MemberData(nameof(SendMail_MultiLineDomainLiterals_Data))] + public async Task SendMail_MultiLineDomainLiterals_Disabled_Throws(string from, string to, bool asyncSend) + { + using var server = new LoopbackSmtpServer(); + + using SmtpClient client = server.CreateClient(); + client.Credentials = new NetworkCredential("Foo", "Bar"); + + using var msg = new MailMessage(@from, @to, "subject", "body"); + + await Assert.ThrowsAsync(async () => + { + if (asyncSend) + { + await client.SendMailAsync(msg).WaitAsync(TimeSpan.FromSeconds(30)); + } + else + { + client.Send(msg); + } + }); + } + + public static IEnumerable SendMail_MultiLineDomainLiterals_Data() + { + foreach (bool async in new[] { true, false }) + { + foreach (string address in new[] { "foo@[\r\n bar]", "foo@[bar\r\n ]", "foo@[bar\r\n baz]" }) + { + yield return new object[] { address, "foo@example.com", async }; + yield return new object[] { "foo@example.com", address, async }; + } + } + } } }