Skip to content

Commit

Permalink
Fixed logic for validating HTML Tag and Attribute names
Browse files Browse the repository at this point in the history
Apparently, < is a valid tag name character based on the HTML5 tokenizer
specification.

Fixes #1136
  • Loading branch information
jstedfast committed Jan 30, 2025
1 parent 9787693 commit f2b722b
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 11 deletions.
2 changes: 1 addition & 1 deletion MimeKit/Text/HtmlAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public HtmlAttribute (string name, string value)
if (name.Length == 0)
throw new ArgumentException ("The attribute name cannot be empty.", nameof (name));

if (!HtmlUtils.IsValidTokenName (name))
if (!HtmlUtils.IsValidAttributeName (name))
throw new ArgumentException ("Invalid attribute name.", nameof (name));

Value = value;
Expand Down
27 changes: 21 additions & 6 deletions MimeKit/Text/HtmlUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ namespace MimeKit.Text {
/// </remarks>
public static class HtmlUtils
{
// https://dev.w3.org/html5/spec-LC/tokenization.html#attribute-name-state
static readonly string InvalidAttributeNameCharacters = "\0\t\r\n\f /=>\"\'<";

// https://dev.w3.org/html5/spec-LC/tokenization.html#tag-name-state
static readonly string InvalidTagNameCharacters = "\0\t\r\n\f />";

#if NETSTANDARD2_0 || NETFRAMEWORK
static void Write (this TextWriter writer, ReadOnlySpan<char> value)
{
Expand All @@ -51,18 +57,27 @@ static void Write (this TextWriter writer, ReadOnlySpan<char> value)
}
#endif

internal static bool IsValidTokenName (string name)
internal static bool IsValidAttributeName (string name)
{
if (string.IsNullOrEmpty (name))
return false;

for (int i = 0; i < name.Length; i++) {
switch (name[i]) {
case '\t': case '\r': case '\n': case '\f': case ' ':
case '<': case '>': case '\'': case '"':
case '/': case '=':
if (InvalidAttributeNameCharacters.IndexOf (name[i]) != -1)
return false;
}

return true;
}

internal static bool IsValidTagName (string name)
{
if (string.IsNullOrEmpty (name))
return false;

for (int i = 0; i < name.Length; i++) {
if (InvalidTagNameCharacters.IndexOf (name[i]) != -1)
return false;
}
}

return true;
Expand Down
4 changes: 2 additions & 2 deletions MimeKit/Text/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ static void ValidateAttributeName (string name)
if (name.Length == 0)
throw new ArgumentException ("The attribute name cannot be empty.", nameof (name));

if (!HtmlUtils.IsValidTokenName (name))
if (!HtmlUtils.IsValidAttributeName (name))
throw new ArgumentException ($"Invalid attribute name: {name}", nameof (name));
}

Expand All @@ -147,7 +147,7 @@ static void ValidateTagName (string name)
if (name.Length == 0)
throw new ArgumentException ("The tag name cannot be empty.", nameof (name));

if (!HtmlUtils.IsValidTokenName (name))
if (!HtmlUtils.IsValidTagName (name))
throw new ArgumentException ($"Invalid tag name: {name}", nameof (name));
}

Expand Down
10 changes: 8 additions & 2 deletions UnitTests/Text/HtmlUtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,15 @@ public void TestHtmlNamespaces ()
}

[Test]
public void TestIsValidTokenName ()
public void TestIsValidAttributeName ()
{
Assert.That (HtmlUtils.IsValidTokenName (string.Empty), Is.False, "string.Empty");
Assert.That (HtmlUtils.IsValidAttributeName (string.Empty), Is.False, "string.Empty");
}

[Test]
public void TestIsValidTagName ()
{
Assert.That (HtmlUtils.IsValidTagName (string.Empty), Is.False, "string.Empty");
}
}
}

0 comments on commit f2b722b

Please sign in to comment.