diff --git a/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java b/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java index bf6c7952641d..615ff14c571b 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java +++ b/spring-web/src/main/java/org/springframework/web/util/HtmlCharacterEntityDecoder.java @@ -124,7 +124,10 @@ private boolean processNumberedReference() { int value = (!isHexNumberedReference ? Integer.parseInt(getReferenceSubstring(2)) : Integer.parseInt(getReferenceSubstring(3), 16)); - this.decodedMessage.append((char) value); + if (value > Character.MAX_CODE_POINT) { + return false; + } + this.decodedMessage.appendCodePoint(value); return true; } catch (NumberFormatException ex) { diff --git a/spring-web/src/test/java/org/springframework/web/util/HtmlCharacterEntityDecoderTest.java b/spring-web/src/test/java/org/springframework/web/util/HtmlCharacterEntityDecoderTest.java new file mode 100644 index 000000000000..88355eadade2 --- /dev/null +++ b/spring-web/src/test/java/org/springframework/web/util/HtmlCharacterEntityDecoderTest.java @@ -0,0 +1,42 @@ +package org.springframework.web.util; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HtmlCharacterEntityDecoderTest { + + @Test + @DisplayName("Should correctly unescape Unicode supplementary characters") + void unescapeHandlesSupplementaryCharactersCorrectly() { + // Arrange: Prepare test cases with the 'grinning face' emoji (😀, U+1F600). + String expectedCharacter = "😀"; + String decimalEntity = "😀"; + String hexEntity = "😀"; + + // Act: Call the HtmlUtils.htmlUnescape method to get the actual results. + String actualResultFromDecimal = HtmlUtils.htmlUnescape(decimalEntity); + String actualResultFromHex = HtmlUtils.htmlUnescape(hexEntity); + + // Assert: Verify that the actual results match the expected character. + assertEquals(expectedCharacter, actualResultFromDecimal, "Decimal entity was not converted correctly."); + assertEquals(expectedCharacter, actualResultFromHex, "Hexadecimal entity was not converted correctly."); + } + + @Test + @DisplayName("Should correctly unescape basic and named HTML entities") + void unescapeHandlesBasicEntities() { + // Arrange + String input = "<p>Tom & Jerry's "Show"</p>"; + String expectedOutput = "
Tom & Jerry's \"Show\"
"; + + // Act + String actualOutput = HtmlUtils.htmlUnescape(input); + + // Assert + assertEquals(expectedOutput, actualOutput, "Basic HTML entities were not unescaped correctly."); + } + +} +