From 56dba32abfb8a1097f91d3a4051f5a8b989a804f Mon Sep 17 00:00:00 2001 From: dualfroz Date: Fri, 4 Sep 2026 09:42:55 +0200 Subject: [PATCH] Fix HtmlEntity.DeEntitize duplicating '#' on invalid numeric entities When a numeric character reference (&#...;) fails to parse, the catch block restored the text as "&#" + e where e already begins with '#', producing a doubled '#' (for example "&#foo;" became "&##foo;"). Restore the original text as "&" + e + ";" to match the named-entity fallback and keep DeEntitize idempotent for unparseable numeric entities. --- src/HtmlAgilityPack.Shared/HtmlEntity.cs | 3 ++- .../HtmlEntityDeEntitizeTests.cs | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/Tests/HtmlAgilityPack.Tests.NetStandard2_0/HtmlEntityDeEntitizeTests.cs diff --git a/src/HtmlAgilityPack.Shared/HtmlEntity.cs b/src/HtmlAgilityPack.Shared/HtmlEntity.cs index 52cea1b..261701f 100644 --- a/src/HtmlAgilityPack.Shared/HtmlEntity.cs +++ b/src/HtmlAgilityPack.Shared/HtmlEntity.cs @@ -663,7 +663,8 @@ namespace HtmlAgilityPack } catch { - sb.Append("&#" + e + ";"); + // 'e' already starts with '#', so prepending "&#" would duplicate it. + sb.Append("&" + e + ";"); } } else diff --git a/src/Tests/HtmlAgilityPack.Tests.NetStandard2_0/HtmlEntityDeEntitizeTests.cs b/src/Tests/HtmlAgilityPack.Tests.NetStandard2_0/HtmlEntityDeEntitizeTests.cs new file mode 100644 index 0000000..52535c2 --- /dev/null +++ b/src/Tests/HtmlAgilityPack.Tests.NetStandard2_0/HtmlEntityDeEntitizeTests.cs @@ -0,0 +1,25 @@ +using Xunit; + +namespace HtmlAgilityPack.Tests.NetStandard2_0 +{ + public class HtmlEntityDeEntitizeTests + { + [Theory] + // Invalid numeric entities must be left untouched; "&#foo;" used to become + // "&##foo;". + [InlineData("&#foo;", "&#foo;")] + [InlineData("&#xGG;", "&#xGG;")] + [InlineData("&#;", "&#;")] + [InlineData("prefix &#foo; suffix", "prefix &#foo; suffix")] + // Valid numeric entities keep decoding correctly. + [InlineData("A", "A")] + [InlineData("A", "A")] + // Named entities are unaffected. + [InlineData("&", "&")] + [InlineData("¬real;", "¬real;")] + public void DeEntitize_HandlesInvalidNumericEntitiesWithoutDuplicatingHash(string input, string expected) + { + Assert.Equal(expected, HtmlEntity.DeEntitize(input)); + } + } +}