You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2019/07/23 13:24:52 UTC

[myfaces] branch 2.2.x updated: MYFACES-4294 Emoji characters are filtered in ajax requests

This is an automated email from the ASF dual-hosted git repository.

tandraschko pushed a commit to branch 2.2.x
in repository https://gitbox.apache.org/repos/asf/myfaces.git


The following commit(s) were added to refs/heads/2.2.x by this push:
     new 946b8c9  MYFACES-4294 Emoji characters are filtered in ajax requests
946b8c9 is described below

commit 946b8c9819c22c29a4dc21f77cc0f53e63838e54
Author: Thomas Andraschko <ta...@apache.org>
AuthorDate: Tue Jul 23 15:24:44 2019 +0200

    MYFACES-4294 Emoji characters are filtered in ajax requests
---
 .../util/IllegalXmlCharacterFilterWriter.java      | 101 +++++++++++++++++----
 .../context/PartialResponseWriterImplTest.java     |  38 +++++++-
 2 files changed, 116 insertions(+), 23 deletions(-)

diff --git a/impl/src/main/java/org/apache/myfaces/util/IllegalXmlCharacterFilterWriter.java b/impl/src/main/java/org/apache/myfaces/util/IllegalXmlCharacterFilterWriter.java
index 50953c8..3b6f5e1 100644
--- a/impl/src/main/java/org/apache/myfaces/util/IllegalXmlCharacterFilterWriter.java
+++ b/impl/src/main/java/org/apache/myfaces/util/IllegalXmlCharacterFilterWriter.java
@@ -31,7 +31,6 @@ import java.io.Writer;
  */
 public class IllegalXmlCharacterFilterWriter extends FilterWriter
 {
-    private static final char[] EMPTY_CHAR_ARRAY = new char[0];
     private static final char BLANK_CHAR = ' ';
     
     public IllegalXmlCharacterFilterWriter(Writer out)
@@ -42,7 +41,7 @@ public class IllegalXmlCharacterFilterWriter extends FilterWriter
     @Override
     public void write(int c) throws IOException 
     {
-        if (isInvalidChar((char) c))
+        if (isInvalidChar(c))
         {
             super.write((int) BLANK_CHAR);
         }
@@ -71,26 +70,53 @@ public class IllegalXmlCharacterFilterWriter extends FilterWriter
             return null;
         }
         
-        boolean containsInvalidChar = false;
-        char[] encodedCharArray = EMPTY_CHAR_ARRAY;
-        
         int to = off + len;
+        boolean surrogateResolved = false;
+        char c;
+        int codePoint;
+        char cNext;
+        
+        char[] encoded = null;
         for (int i = off; i < to; i++)
         {
-            if (isInvalidChar(str.charAt(i)))
+            if (surrogateResolved == true)
+            {
+                surrogateResolved = false;
+                continue;
+            }
+            
+            c = str.charAt(i);
+            codePoint = c;
+
+            if (Character.isHighSurrogate(c) && i + 1 < to)
             {
-                if (!containsInvalidChar)
+                cNext = str.charAt(i + 1);
+                if (Character.isLowSurrogate(cNext))
                 {
-                    containsInvalidChar = true;
-                    encodedCharArray = str.toCharArray();
+                    codePoint = Character.toCodePoint(c, cNext);
+                    surrogateResolved = true;
+                }
+            }
+
+            // try to resolve surrogate, this is required e.g. for emojis
+            if ((!surrogateResolved && Character.isSurrogate(c)) || isInvalidChar(codePoint))
+            {
+                if (encoded == null)
+                {
+                    encoded = str.toCharArray();
+                }
+                encoded[i] = BLANK_CHAR;
+                
+                if (surrogateResolved)
+                {
+                    encoded[i + 1] = BLANK_CHAR;
                 }
-                encodedCharArray[i] = BLANK_CHAR;
             }
         }
 
-        if (containsInvalidChar)
+        if (encoded != null)
         {
-            return String.valueOf(encodedCharArray);
+            return String.valueOf(encoded);
         }
 
         return str;
@@ -102,36 +128,73 @@ public class IllegalXmlCharacterFilterWriter extends FilterWriter
         {
             return null;
         }
-        
+
         int to = off + len;
+        
+        boolean surrogateResolved = false;
+        char c;
+        int codePoint;
+        char cNext;
+
         for (int i = off; i < to; i++)
         {
-            if (isInvalidChar(cbuf[i]))
+            if (surrogateResolved == true)
+            {
+                surrogateResolved = false;
+                continue;
+            }
+            
+            c = cbuf[i];
+            codePoint = c;
+
+            // try to resolve surrogate, this is required e.g. for emojis
+            if (Character.isHighSurrogate(c) && i + 1 < to)
+            {
+                cNext = cbuf[i + 1];
+                if (Character.isLowSurrogate(cNext))
+                {
+                    codePoint = Character.toCodePoint(c, cNext);
+                    surrogateResolved = true;
+                }
+            }
+            
+            if ((!surrogateResolved && Character.isSurrogate(c)) || isInvalidChar(codePoint))
             {
                 cbuf[i] = BLANK_CHAR;
+                
+                if (surrogateResolved)
+                {
+                    cbuf[i + 1] = BLANK_CHAR;
+                }
             }
         }
         return cbuf;
     }
 
-    private static boolean isInvalidChar(char c)
+    private static boolean isInvalidChar(int codePoint)
     {
-        if (Character.isSurrogate(c)) 
+        if (codePoint == 1113088)
         {
             return true;
         }
-        if (c == '\u0009' || c == '\n' || c == '\r') 
+
+        if (codePoint == 0x9 || codePoint == 0xA || codePoint == 0xD) 
         {
             return false;
         }
-        if (c > '\u0020' && c < '\uD7FF') 
+        if (codePoint >= 0x20 && codePoint <= 0xD7FF) 
         {
             return false;
         }
-        if (c > '\uE000' && c < '\uFFFD') 
+        if (codePoint >= 0xE000 && codePoint <= 0xFFFD) 
         {
             return false;
         }
+        if (codePoint >= 0x10000 && codePoint <= 0x10FFFF) 
+        {
+            return false;
+        }
+
         return true;
     }
 }
diff --git a/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java b/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java
index b876cbf..a505316 100644
--- a/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java
+++ b/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java
@@ -297,11 +297,11 @@ public class PartialResponseWriterImplTest extends AbstractJsfTestCase {
             fail(e.toString());
         }
     }
-
+    
     public void testWriteIllegalXmlUnicodeCharacters() {
         _writer = createTestProbe();
         try {
-            String illegalChars = " \u0001\u0002\u0003\u0004\u0005\u0006\u000B\f\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F \uD7FF\uDBFF\uDC00\uE000��";
+            String illegalChars = "\u0001\u0002\u0003\u0004\u0005\u0006\u000B\f\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\uDBFF\uDC00";
             String legalChars = "foo";
             _writer.write(illegalChars + legalChars);
             assertEquals("All illegal XML unicode characters should have been replaced by spaces", legalChars, _contentCollector.toString().trim());
@@ -314,7 +314,7 @@ public class PartialResponseWriterImplTest extends AbstractJsfTestCase {
     public void testWriteTextIllegalXmlUnicodeCharacters() {
         _writer = createTestProbe();
         try {
-            String illegalChars = " \u0001\u0002\u0003\u0004\u0005\u0006\u000B\f\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F \uD7FF\uDBFF\uDC00\uE000��";
+            String illegalChars = "\u0001\u0002\u0003\u0004\u0005\u0006\u000B\f\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\uDBFF\uDC00";
             String legalChars = "foo";
             _writer.writeText(illegalChars + legalChars, null);
             assertEquals("All illegal XML unicode characters should have been replaced by spaces", legalChars, _contentCollector.toString().trim());
@@ -327,7 +327,7 @@ public class PartialResponseWriterImplTest extends AbstractJsfTestCase {
     public void testWriteAttributeIllegalXmlUnicodeCharacters() {
         _writer = createTestProbe();
         try {
-            String illegalChars = " \u0001\u0002\u0003\u0004\u0005\u0006\u000B\f\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F \uD7FF\uDBFF\uDC00\uE000��";
+            String illegalChars = "\u0001\u0002\u0003\u0004\u0005\u0006\u000B\f\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\uDBFF\uDC00";
             String legalChars = "foo";
             _writer.startElement(legalChars, null);
             _writer.writeAttribute(legalChars, illegalChars + legalChars, null);
@@ -340,6 +340,36 @@ public class PartialResponseWriterImplTest extends AbstractJsfTestCase {
         }
     }
 
+    public void testWriteSkipEmoji() {
+        _writer = createTestProbe();
+        try {
+            String input = "foo😀";
+            _writer.writeText(input, null);
+            
+            String escaped = _contentCollector.toString();
+            
+            assertEquals("All illegal XML unicode characters should have been replaced by spaces", input, escaped.trim());
+
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+    
+    public void testWriteSkipPictographs() {
+        _writer = createTestProbe();
+        try {
+            String input = "foo🏺";
+            _writer.writeText(input, null);
+            
+            String escaped = _contentCollector.toString();
+            
+            assertEquals("All illegal XML unicode characters should have been replaced by spaces", input, escaped.trim());
+
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+    
     /**
      * creates a new test probe (aka response writer)
      *