1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. 5 * 6 * The contents of this file are subject to the terms of either the GNU 7 * General Public License Version 2 only ("GPL") or the Common Development 8 * and Distribution License("CDDL") (collectively, the "License"). You 9 * may not use this file except in compliance with the License. You can 10 * obtain a copy of the License at 11 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html 12 * or packager/legal/LICENSE.txt. See the License for the specific 13 * language governing permissions and limitations under the License. 14 * 15 * When distributing the software, include this License Header Notice in each 16 * file and include the License file at packager/legal/LICENSE.txt. 17 * 18 * GPL Classpath Exception: 19 * Oracle designates this particular file as subject to the "Classpath" 20 * exception as provided by Oracle in the GPL Version 2 section of the License 21 * file that accompanied this code. 22 * 23 * Modifications: 24 * If applicable, add the following below the License Header, with the fields 25 * enclosed by brackets [] replaced by your own identifying information: 26 * "Portions Copyright [year] [name of copyright owner]" 27 * 28 * Contributor(s): 29 * If you wish your version of this file to be governed by only the CDDL or 30 * only the GPL Version 2, indicate your decision by adding "[Contributor] 31 * elects to include this software in this distribution under the [CDDL or GPL 32 * Version 2] license." If you don't indicate a single choice of license, a 33 * recipient has the option to distribute your version of this file under 34 * either the CDDL, the GPL Version 2 or to extend the choice of license to 35 * its licensees as provided above. However, if you add GPL Version 2 code 36 * and therefore, elected the GPL Version 2 license, then the option applies 37 * only if the new code is made subject to such option by the copyright 38 * holder. 39 */ 40 package com.sun.org.apache.xerces.internal.utils; 41 42 import com.sun.org.apache.xerces.internal.impl.Constants; 43 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit; 44 import java.util.Formatter; 45 import java.util.HashMap; 46 import java.util.Map; 47 48 /** 49 * A helper for analyzing entity expansion limits 50 * 51 * @author Joe Wang Oracle Corp. 52 * 53 */ 54 public final class XMLLimitAnalyzer { 55 56 /** 57 * Map old property names with the new ones 58 */ 59 public static enum NameMap { 60 ENTITY_EXPANSION_LIMIT(Constants.SP_ENTITY_EXPANSION_LIMIT, Constants.ENTITY_EXPANSION_LIMIT), 61 MAX_OCCUR_NODE_LIMIT(Constants.SP_MAX_OCCUR_LIMIT, Constants.MAX_OCCUR_LIMIT), 62 ELEMENT_ATTRIBUTE_LIMIT(Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, Constants.ELEMENT_ATTRIBUTE_LIMIT); 63 64 final String newName; 65 final String oldName; 66 67 NameMap(String newName, String oldName) { 68 this.newName = newName; 69 this.oldName = oldName; 70 } 71 72 String getOldName(String newName) { 73 if (newName.equals(this.newName)) { 74 return oldName; 75 } 76 return null; 77 } 78 } 79 80 /** 81 * Max value accumulated for each property 82 */ 83 private final int[] values; 84 /** 85 * Names of the entities corresponding to their max values 86 */ 87 private final String[] names; 88 /** 89 * Total value of accumulated entities 90 */ 91 private final int[] totalValue; 92 93 /** 94 * Maintain values of the top 10 elements in the process of parsing 95 */ 96 private final Map<String, Integer>[] caches; 97 98 private String entityStart, entityEnd; 99 /** 100 * Default constructor. Establishes default values for known security 101 * vulnerabilities. 102 */ 103 @SuppressWarnings({"rawtypes", "unchecked"}) 104 public XMLLimitAnalyzer() { 105 values = new int[Limit.values().length]; 106 totalValue = new int[Limit.values().length]; 107 names = new String[Limit.values().length]; 108 caches = new Map[Limit.values().length]; 109 } 110 111 /** 112 * Add the value to the current max count for the specified property 113 * To find the max value of all entities, set no limit 114 * 115 * @param limit the type of the property 116 * @param entityName the name of the entity 117 * @param value the value of the entity 118 */ 119 public void addValue(Limit limit, String entityName, int value) { 120 addValue(limit.ordinal(), entityName, value); 121 } 122 123 /** 124 * Add the value to the current count by the index of the property 125 * @param index the index of the property 126 * @param entityName the name of the entity 127 * @param value the value of the entity 128 */ 129 public void addValue(int index, String entityName, int value) { 130 if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || 131 index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || 132 index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || 133 index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || 134 index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() 135 ) { 136 totalValue[index] += value; 137 return; 138 } 139 if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || 140 index == Limit.MAX_NAME_LIMIT.ordinal()) { 141 values[index] = value; 142 totalValue[index] = value; 143 return; 144 } 145 146 Map<String, Integer> cache; 147 if (caches[index] == null) { 148 cache = new HashMap<>(10); 149 caches[index] = cache; 150 } else { 151 cache = caches[index]; 152 } 153 154 int accumulatedValue = value; 155 if (cache.containsKey(entityName)) { 156 accumulatedValue += cache.get(entityName); 157 cache.put(entityName, accumulatedValue); 158 } else { 159 cache.put(entityName, value); 160 } 161 162 if (accumulatedValue > values[index]) { 163 values[index] = accumulatedValue; 164 names[index] = entityName; 165 } 166 167 168 if (index == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal() || 169 index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) { 170 totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value; 171 } 172 } 173 174 /** 175 * Return the value of the current max count for the specified property 176 * 177 * @param limit the property 178 * @return the value of the property 179 */ 180 public int getValue(Limit limit) { 181 return getValue(limit.ordinal()); 182 } 183 184 public int getValue(int index) { 185 if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) { 186 return totalValue[index]; 187 } 188 return values[index]; 189 } 190 /** 191 * Return the total value accumulated so far 192 * 193 * @param limit the property 194 * @return the accumulated value of the property 195 */ 196 public int getTotalValue(Limit limit) { 197 return totalValue[limit.ordinal()]; 198 } 199 200 public int getTotalValue(int index) { 201 return totalValue[index]; 202 } 203 /** 204 * Return the current max value (count or length) by the index of a property 205 * @param index the index of a property 206 * @return count of a property 207 */ 208 public int getValueByIndex(int index) { 209 return values[index]; 210 } 211 212 public void startEntity(String name) { 213 entityStart = name; 214 } 215 216 public boolean isTracking(String name) { 217 if (entityStart == null) { 218 return false; 219 } 220 return entityStart.equals(name); 221 } 222 /** 223 * Stop tracking the entity 224 * @param limit the limit property 225 * @param name the name of an entity 226 */ 227 public void endEntity(Limit limit, String name) { 228 entityStart = ""; 229 Map<String, Integer> cache = caches[limit.ordinal()]; 230 if (cache != null) { 231 cache.remove(name); 232 } 233 } 234 235 /** 236 * Resets the current value of the specified limit. 237 * @param limit The limit to be reset. 238 */ 239 public void reset(Limit limit) { 240 if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) { 241 totalValue[limit.ordinal()] = 0; 242 } else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) { 243 names[limit.ordinal()] = null; 244 values[limit.ordinal()] = 0; 245 caches[limit.ordinal()] = null; 246 totalValue[limit.ordinal()] = 0; 247 } 248 } 249 250 public void debugPrint(XMLSecurityManager securityManager) { 251 Formatter formatter = new Formatter(); 252 System.out.println(formatter.format("%30s %15s %15s %15s %30s", 253 "Property","Limit","Total size","Size","Entity Name")); 254 255 for (Limit limit : Limit.values()) { 256 formatter = new Formatter(); 257 System.out.println(formatter.format("%30s %15d %15d %15d %30s", 258 limit.name(), 259 securityManager.getLimit(limit), 260 totalValue[limit.ordinal()], 261 values[limit.ordinal()], 262 names[limit.ordinal()])); 263 } 264 } 265 }