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 2018/11/26 09:08:27 UTC

[myfaces] branch 2.2.x updated: MYFACES-4266 Ajax update fails due to invalid characters in response XML (DoS)

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 0938417  MYFACES-4266 Ajax update fails due to invalid characters in response XML (DoS)
0938417 is described below

commit 09384178c2c1b07618d6bfc5ad7443b1a686f281
Author: Thomas Andraschko <ta...@apache.org>
AuthorDate: Mon Nov 26 10:08:31 2018 +0100

    MYFACES-4266 Ajax update fails due to invalid characters in response XML (DoS)
---
 .../myfaces/context/PartialResponseWriterImpl.java |  3 +-
 .../util/IllegalXmlCharacterFilterWriter.java      | 86 ++++++++++++++++++++++
 .../context/PartialResponseWriterImplTest.java     | 40 ++++++++++
 3 files changed, 128 insertions(+), 1 deletion(-)

diff --git a/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java b/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java
index e1113af..698bcc7 100644
--- a/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/context/PartialResponseWriterImpl.java
@@ -29,6 +29,7 @@ import javax.faces.context.PartialResponseWriter;
 import javax.faces.context.ResponseWriter;
 
 import org.apache.myfaces.util.CDataEndEscapeFilterWriter;
+import org.apache.myfaces.util.IllegalXmlCharacterFilterWriter;
 
 /**
  * <p/>
@@ -109,7 +110,7 @@ public class PartialResponseWriterImpl extends PartialResponseWriter
 
     public PartialResponseWriterImpl(ResponseWriter writer)
     {
-        super(writer);
+        super(writer.cloneWithWriter(new IllegalXmlCharacterFilterWriter(writer)));
     }
 
     @Override
diff --git a/impl/src/main/java/org/apache/myfaces/util/IllegalXmlCharacterFilterWriter.java b/impl/src/main/java/org/apache/myfaces/util/IllegalXmlCharacterFilterWriter.java
new file mode 100644
index 0000000..f7011e3
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/util/IllegalXmlCharacterFilterWriter.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * There are unicodes outside the ranges defined in the
+ * <a href="https://www.w3.org/TR/REC-xml/#charsets">XML 1.0 specification</a> that break XML parsers
+ * and therefore must be filtered out when writing partial responses. Otherwise this may lead to
+ * Denial of Service attacks.
+ * @see https://issues.apache.org/jira/browse/MYFACES-4266
+ */
+public class IllegalXmlCharacterFilterWriter extends FilterWriter
+{
+    public IllegalXmlCharacterFilterWriter(Writer out)
+    {
+        super(out);
+    }
+
+    @Override
+    public void write(int c) throws IOException 
+    {
+        super.write(xmlEncode((char) c));
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException 
+    {
+        super.write(xmlEncode(cbuf), off, len);
+    }
+
+    @Override
+    public void write(String str, int off, int len) throws IOException 
+    {
+        super.write(new String(xmlEncode(str.toCharArray())), off, len);
+    }
+
+    private char[] xmlEncode(char[] ca)
+    {
+        for (int i = 0; i < ca.length; i++)
+        {
+            ca[i] = xmlEncode(ca[i]);
+        }
+        return ca;
+    }
+
+    private char xmlEncode(char c)
+    {
+        if (Character.isSurrogate(c)) 
+        {
+            return ' ';
+        }
+        if (c == '\u0009' || c == '\n' || c == '\r') 
+        {
+            return c;
+        }
+        if (c > '\u0020' && c < '\uD7FF') 
+        {
+            return c;
+        }
+        if (c > '\uE000' && c < '\uFFFD') 
+        {
+            return c;
+        }
+        return ' ';
+    }
+}
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 4a19219..b876cbf 100644
--- a/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java
+++ b/impl/src/test/java/org/apache/myfaces/context/PartialResponseWriterImplTest.java
@@ -298,7 +298,47 @@ public class PartialResponseWriterImplTest extends AbstractJsfTestCase {
         }
     }
 
+    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 legalChars = "foo";
+            _writer.write(illegalChars + legalChars);
+            assertEquals("All illegal XML unicode characters should have been replaced by spaces", legalChars, _contentCollector.toString().trim());
+            
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
+
+    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 legalChars = "foo";
+            _writer.writeText(illegalChars + legalChars, null);
+            assertEquals("All illegal XML unicode characters should have been replaced by spaces", legalChars, _contentCollector.toString().trim());
+
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
 
+    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 legalChars = "foo";
+            _writer.startElement(legalChars, null);
+            _writer.writeAttribute(legalChars, illegalChars + legalChars, null);
+            _writer.endElement(legalChars);
+            assertTrue("All illegal XML unicode characters should have been replaced by spaces", 
+                    _contentCollector.toString().matches("<:X: :X:=\"[ ]+:X:\"></:X:>".replace(":X:", legalChars)));
+
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+    }
 
     /**
      * creates a new test probe (aka response writer)