Skip to content

Commit

Permalink
MailAddress accepts invalid email address with consecutive dots (#109690
Browse files Browse the repository at this point in the history
)

* MailAddress accepts invalid email address with consecutive dots

* update DotAtomReader class comment
  • Loading branch information
pedrobsaila authored Nov 21, 2024
1 parent d2fdf24 commit 0d55c5b
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 36 deletions.
19 changes: 16 additions & 3 deletions src/libraries/System.Net.Mail/src/System/Net/Mail/DotAtomReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace System.Net.Mail
// RFC 2822 Section 3.2.4 - Atom, Dot-Atom
//
// A Dot-Atom is a string of ASCII characters separated by dots. Dots would normally not be allowed at the start
// or end, but we do allow dots at the end for compatibility with other mail clients. We also allow
// multiple consecutive dots, which would normally be invalid.
// or end, but we do allow dots at the end for compatibility with other mail clients. We don't allow
// multiple consecutive dots as specified in RFC 2822 section 3.4.1.
//
internal static class DotAtomReader
{
Expand Down Expand Up @@ -45,7 +45,8 @@ internal static bool TryReadReverse(string data, int index, out int outIndex, bo
for (; 0 <= index; index--)
{
if (Ascii.IsValid(data[index]) // Any ASCII allowed
&& (data[index] != MailBnfHelper.Dot && !MailBnfHelper.Atext[data[index]])) // Invalid char
&& ((data[index] != MailBnfHelper.Dot && !MailBnfHelper.Atext[data[index]])
|| (data[index] == MailBnfHelper.Dot && index > 0 && data[index - 1] == MailBnfHelper.Dot))) // Invalid char
{
break;
}
Expand All @@ -64,6 +65,18 @@ internal static bool TryReadReverse(string data, int index, out int outIndex, bo
return false;
}
}
else if (index > 0 && data[index] == MailBnfHelper.Dot && data[index - 1] == MailBnfHelper.Dot)
{
if (throwExceptionIfFail)
{
throw new FormatException(SR.Format(SR.MailHeaderFieldInvalidCharacter, MailBnfHelper.ConsecutiveDots));
}
else
{
outIndex = default;
return false;
}
}
// Check for leading dot
else if (data[index + 1] == MailBnfHelper.Dot)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ internal static class MailBnfHelper
internal const char EndSquareBracket = ']';
internal const char Comma = ',';
internal const char Dot = '.';
internal const string ConsecutiveDots = "..";

// NOTE: See RFC 2822 for more detail. By default, every value in the array is false and only
// those values which are allowed in that particular set are then set to true. The numbers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public static IEnumerable<object[]> GetInvalid_Address()
yield return new object[] { "forbar" };
yield return new object[] { "" };
yield return new object[] { null };
yield return new object[] { "fo..o@example.com" };
yield return new object[] { "foo@exa..mple.com" };
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ public class MailAddressParserTest
private const string UnicodeQuotedString = "I have \u3069 unicode";
private const string ValidDotAtom = " a.something#-text";
private const string ValidDotAtomResult = "a.something#-text";
private const string ValidDotAtomDoubleDots = " a.d....d";
private const string ValidDotAtomDoubleDotsResult = "a.d....d";
private const string ValidDotAtomEndsInDot = "a.something.";
private const string InvalidDotAtom = "a.something\"test";
private const string InvalidDotAtomStartsWithDot = ".test";
Expand Down Expand Up @@ -266,15 +264,6 @@ public void TryReadDotAtom_WithValidDotAtom_ShouldReadCorrectly()
Assert.Equal(0, index);
}

[Fact]
public void TryReadDotAtom_WithValidDotAtomAndDoubleDots_ShouldReadCorrectly()
{
int index = ValidDotAtomDoubleDots.Length - 1;
Assert.True(DotAtomReader.TryReadReverse(ValidDotAtomDoubleDots, index, out index, throwExceptionIfFail: true));

Assert.Equal(0, index);
}

[Fact]
public void TryReadDotAtom_EndsInDot_ShouldReadCorrectly()
{
Expand Down Expand Up @@ -381,16 +370,6 @@ public void TryParseAddress_WithEscapedCharacters_AndQuotedLocalPart_ShouldReadC
Assert.Equal("[ ncl\\@bld-001 \t ]", result.Host);
}

[Fact]
public void TryParseAddress_WithNoDisplayNameAndDotAtom_ShouldReadCorrectly()
{
Assert.True(MailAddressParser.TryParseAddress("a..b_b@example.com", out ParseAddressInfo result, throwExceptionIfFail: true));

Assert.Equal(string.Empty, result.DisplayName);
Assert.Equal("a..b_b", result.User);
Assert.Equal("example.com", result.Host);
}

[Fact]
public void TryParseAddress_WithQuotedDisplayNameandNoAngleAddress_ShouldReadCorrectly()
{
Expand Down Expand Up @@ -518,18 +497,17 @@ public void ParseAddresses_WithOnlyOneAddress_ShouldReadCorrectly()
[Fact]
public void ParseAddresses_WithManyComplexAddresses_ShouldReadCorrectly()
{
string addresses = string.Format("{0},{1},{2},{3},{4},{5},{6}",
string addresses = string.Format("{0},{1},{2},{3},{4},{5}",
"\"Dr M\u00FCller\" test@mail.com",
"(comment)this.test.this(comment)@(comment)this.test.this(comment)",
"jeff@example.com",
"jeff2@example.org",
"(comment)this.test.this(comment)<(comment)this.test.this(comment)@(comment)[ test this ](comment)>",
"\"test\" <a..b_b@example.com>",
"(comment)\" asciin;,oqu o.tesws \"(comment)<(comment)\" asciin;,oqu o.tesws \"(comment)@(comment)this.test.this(comment)>");

IList<MailAddress> result = MailAddressParser.ParseMultipleAddresses(addresses);

Assert.Equal(7, result.Count);
Assert.Equal(6, result.Count);

Assert.Equal("Dr M\u00FCller", result[0].DisplayName);
Assert.Equal("test", result[0].User);
Expand All @@ -551,13 +529,9 @@ public void ParseAddresses_WithManyComplexAddresses_ShouldReadCorrectly()
Assert.Equal("this.test.this", result[4].User);
Assert.Equal("[ test this ]", result[4].Host);

Assert.Equal("test", result[5].DisplayName);
Assert.Equal("a..b_b", result[5].User);
Assert.Equal("example.com", result[5].Host);

Assert.Equal(" asciin;,oqu o.tesws ", result[6].DisplayName);
Assert.Equal("\" asciin;,oqu o.tesws \"", result[6].User);
Assert.Equal("this.test.this", result[6].Host);
Assert.Equal(" asciin;,oqu o.tesws ", result[5].DisplayName);
Assert.Equal("\" asciin;,oqu o.tesws \"", result[5].User);
Assert.Equal("this.test.this", result[5].Host);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ public static IEnumerable<object[]> GetValidEmailTestData()
yield return new object[] { "!def!xyz%abc@example.com" };
yield return new object[] { "_somename@example.com" };
yield return new object[] { "\"te\\@st\"@example.com" };
yield return new object[] { "a..b_b@example.com" };
yield return new object[] { "a..b_b...@example.com" };
yield return new object[] { "\"test display\" test@(comment)[exam\\@ple](comment)" };
yield return new object[] { "NoSpaceBeforeEmail\"a\"@example.com" };
yield return new object[] { "NoSpace BeforeEmail\"a\"@example.com" };
Expand Down Expand Up @@ -134,6 +132,8 @@ public static IEnumerable<object[]> GetInvalidEmailTestData()
yield return new object[] { "\uD800 invalid@unicode.com" }; // D800 is a high surrogate
yield return new object[] { null };
yield return new object[] { "" };
yield return new object[] { "a..b_b@example.com" };
yield return new object[] { "a..b_b...@example.com" };
}

[Theory]
Expand Down

0 comments on commit 0d55c5b

Please sign in to comment.