Coverage Report - org.apache.myfaces.util.IllegalXmlCharacterFilterWriter
 
Classes in this File Line Coverage Branch Coverage Complexity
IllegalXmlCharacterFilterWriter
0%
0/82
0%
0/72
7.714
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.util;
 20  
 
 21  
 import java.io.FilterWriter;
 22  
 import java.io.IOException;
 23  
 import java.io.Writer;
 24  
 import org.apache.myfaces.shared.util.ClassUtils;
 25  
 import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
 26  
 
 27  
 /**
 28  
  * There are unicodes outside the ranges defined in the
 29  
  * <a href="https://www.w3.org/TR/REC-xml/#charsets">XML 1.0 specification</a> that break XML parsers
 30  
  * and therefore must be filtered out when writing partial responses. Otherwise this may lead to
 31  
  * Denial of Service attacks.
 32  
  * @see https://issues.apache.org/jira/browse/MYFACES-4266
 33  
  */
 34  
 public class IllegalXmlCharacterFilterWriter extends FilterWriter
 35  
 {
 36  
     private static final char BLANK_CHAR = ' ';
 37  
 
 38  
     // Java 1.6 doesnt support Character.isSurrogate
 39  
     private static boolean java16;
 40  
     
 41  
     static 
 42  
     {
 43  
         try
 44  
         {
 45  0
             java16 = ClassUtils.classForName("java.util.Objects") == null;
 46  
         }
 47  0
         catch (Throwable e)
 48  
         {
 49  0
             java16 = true;
 50  0
         }
 51  
 
 52  0
         if (java16)
 53  
         {
 54  0
             System.err.println("Skip using " + IllegalXmlCharacterFilterWriter.class.getName()
 55  
                     + ", it's only available in Java 1.6+");
 56  
         }
 57  0
     }
 58  
 
 59  
     public IllegalXmlCharacterFilterWriter(Writer out)
 60  
     {
 61  0
         super(out);
 62  0
     }
 63  
 
 64  
     @Override
 65  
     public void write(int c) throws IOException 
 66  
     {
 67  0
         if (java16)
 68  
         {
 69  0
             super.write(c);
 70  0
             return;
 71  
         }
 72  
 
 73  0
         if (isInvalidChar(c))
 74  
         {
 75  0
             super.write((int) BLANK_CHAR);
 76  
         }
 77  
         else
 78  
         {
 79  0
             super.write(c);
 80  
         }
 81  0
     }
 82  
 
 83  
     @Override
 84  
     public void write(char[] cbuf, int off, int len) throws IOException 
 85  
     {
 86  0
         if (java16)
 87  
         {
 88  0
             super.write(cbuf, off, len);
 89  0
             return;
 90  
         }
 91  
 
 92  0
         super.write(encodeCharArray(cbuf, off, len), off, len);
 93  0
     }
 94  
 
 95  
     @Override
 96  
     public void write(String str, int off, int len) throws IOException 
 97  
     {
 98  0
         if (java16)
 99  
         {
 100  0
             super.write(str, off, len);
 101  0
             return;
 102  
         }
 103  
 
 104  0
         super.write(encodeString(str, off, len), off, len);
 105  0
     }
 106  
 
 107  
     @IgnoreJRERequirement // Java 1.6 doesnt support Character.isSurrogate
 108  
     private static String encodeString(String str, int off, int len)
 109  
     {
 110  0
         if (str == null)
 111  
         {
 112  0
             return null;
 113  
         }
 114  
         
 115  0
         int to = off + len;
 116  0
         boolean surrogateResolved = false;
 117  
         char c;
 118  
         int codePoint;
 119  
         char cNext;
 120  
         
 121  0
         char[] encoded = null;
 122  0
         for (int i = off; i < to; i++)
 123  
         {
 124  0
             if (surrogateResolved == true)
 125  
             {
 126  0
                 surrogateResolved = false;
 127  0
                 continue;
 128  
             }
 129  
             
 130  0
             c = str.charAt(i);
 131  0
             codePoint = c;
 132  
 
 133  0
             if (Character.isHighSurrogate(c) && i + 1 < to)
 134  
             {
 135  0
                 cNext = str.charAt(i + 1);
 136  0
                 if (Character.isLowSurrogate(cNext))
 137  
                 {
 138  0
                     codePoint = Character.toCodePoint(c, cNext);
 139  0
                     surrogateResolved = true;
 140  
                 }
 141  
             }
 142  
 
 143  
             // try to resolve surrogate, this is required e.g. for emojis
 144  0
             if ((!surrogateResolved && Character.isSurrogate(c)) || isInvalidChar(codePoint))
 145  
             {
 146  0
                 if (encoded == null)
 147  
                 {
 148  0
                     encoded = str.toCharArray();
 149  
                 }
 150  0
                 encoded[i] = BLANK_CHAR;
 151  
                 
 152  0
                 if (surrogateResolved)
 153  
                 {
 154  0
                     encoded[i + 1] = BLANK_CHAR;
 155  
                 }
 156  
             }
 157  
         }
 158  
 
 159  0
         if (encoded != null)
 160  
         {
 161  0
             return String.valueOf(encoded);
 162  
         }
 163  
 
 164  0
         return str;
 165  
     }
 166  
 
 167  
     @IgnoreJRERequirement // Java 1.6 doesnt support Character.isSurrogate
 168  
     private static char[] encodeCharArray(char[] cbuf, int off, int len)
 169  
     {
 170  0
         if (cbuf == null)
 171  
         {
 172  0
             return null;
 173  
         }
 174  
 
 175  0
         int to = off + len;
 176  
         
 177  0
         boolean surrogateResolved = false;
 178  
         char c;
 179  
         int codePoint;
 180  
         char cNext;
 181  
 
 182  0
         for (int i = off; i < to; i++)
 183  
         {
 184  0
             if (surrogateResolved == true)
 185  
             {
 186  0
                 surrogateResolved = false;
 187  0
                 continue;
 188  
             }
 189  
             
 190  0
             c = cbuf[i];
 191  0
             codePoint = c;
 192  
 
 193  
             // try to resolve surrogate, this is required e.g. for emojis
 194  0
             if (Character.isHighSurrogate(c) && i + 1 < to)
 195  
             {
 196  0
                 cNext = cbuf[i + 1];
 197  0
                 if (Character.isLowSurrogate(cNext))
 198  
                 {
 199  0
                     codePoint = Character.toCodePoint(c, cNext);
 200  0
                     surrogateResolved = true;
 201  
                 }
 202  
             }
 203  
             
 204  0
             if ((!surrogateResolved && Character.isSurrogate(c)) || isInvalidChar(codePoint))
 205  
             {
 206  0
                 cbuf[i] = BLANK_CHAR;
 207  
                 
 208  0
                 if (surrogateResolved)
 209  
                 {
 210  0
                     cbuf[i + 1] = BLANK_CHAR;
 211  
                 }
 212  
             }
 213  
         }
 214  0
         return cbuf;
 215  
     }
 216  
 
 217  
     private static boolean isInvalidChar(int codePoint)
 218  
     {
 219  0
         if (codePoint == 1113088)
 220  
         {
 221  0
             return true;
 222  
         }
 223  
 
 224  0
         if (codePoint == 0x9 || codePoint == 0xA || codePoint == 0xD) 
 225  
         {
 226  0
             return false;
 227  
         }
 228  0
         if (codePoint >= 0x20 && codePoint <= 0xD7FF) 
 229  
         {
 230  0
             return false;
 231  
         }
 232  0
         if (codePoint >= 0xE000 && codePoint <= 0xFFFD) 
 233  
         {
 234  0
             return false;
 235  
         }
 236  0
         if (codePoint >= 0x10000 && codePoint <= 0x10FFFF) 
 237  
         {
 238  0
             return false;
 239  
         }
 240  
 
 241  0
         return true;
 242  
     }
 243  
 }