1 /* 2 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.org.apache.xerces.internal.utils; 27 28 import com.sun.org.apache.xerces.internal.impl.Constants; 29 import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit; 30 import java.util.Formatter; 31 import java.util.HashMap; 32 import java.util.Map; 33 34 /** 35 * A helper for analyzing entity expansion limits 36 * 37 * @author Joe Wang Oracle Corp. 38 * 39 */ 40 public final class XMLLimitAnalyzer { 41 42 /** 43 * Map old property names with the new ones 44 */ 45 public static enum NameMap { 46 ENTITY_EXPANSION_LIMIT(Constants.SP_ENTITY_EXPANSION_LIMIT, Constants.ENTITY_EXPANSION_LIMIT), 47 MAX_OCCUR_NODE_LIMIT(Constants.SP_MAX_OCCUR_LIMIT, Constants.MAX_OCCUR_LIMIT), 48 ELEMENT_ATTRIBUTE_LIMIT(Constants.SP_ELEMENT_ATTRIBUTE_LIMIT, Constants.ELEMENT_ATTRIBUTE_LIMIT); 49 50 final String newName; 51 final String oldName; 52 53 NameMap(String newName, String oldName) { 54 this.newName = newName; 55 this.oldName = oldName; 56 } 57 58 String getOldName(String newName) { 59 if (newName.equals(this.newName)) { 60 return oldName; 61 } 62 return null; 63 } 64 } 65 66 /** 67 * Max value accumulated for each property 68 */ 69 private final int[] values; 70 /** 71 * Names of the entities corresponding to their max values 72 */ 73 private final String[] names; 74 /** 75 * Total value of accumulated entities 76 */ 77 private final int[] totalValue; 78 79 /** 80 * Maintain values of the top 10 elements in the process of parsing 81 */ 82 private final Map<String, Integer>[] caches; 83 84 private String entityStart, entityEnd; 85 /** 86 * Default constructor. Establishes default values for known security 87 * vulnerabilities. 88 */ 89 @SuppressWarnings({"rawtypes", "unchecked"}) 90 public XMLLimitAnalyzer() { 91 values = new int[Limit.values().length]; 92 totalValue = new int[Limit.values().length]; 93 names = new String[Limit.values().length]; 94 caches = new Map[Limit.values().length]; 95 } 96 97 /** 98 * Add the value to the current max count for the specified property 99 * To find the max value of all entities, set no limit 100 * 101 * @param limit the type of the property 102 * @param entityName the name of the entity 103 * @param value the value of the entity 104 */ 105 public void addValue(Limit limit, String entityName, int value) { 106 addValue(limit.ordinal(), entityName, value); 107 } 108 109 /** 110 * Add the value to the current count by the index of the property 111 * @param index the index of the property 112 * @param entityName the name of the entity 113 * @param value the value of the entity 114 */ 115 public void addValue(int index, String entityName, int value) { 116 if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || 117 index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || 118 index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || 119 index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || 120 index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() 121 ) { 122 totalValue[index] += value; 123 return; 124 } 125 if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || 126 index == Limit.MAX_NAME_LIMIT.ordinal()) { 127 values[index] = value; 128 totalValue[index] = value; 129 return; 130 } 131 132 Map<String, Integer> cache; 133 if (caches[index] == null) { 134 cache = new HashMap<>(10); 135 caches[index] = cache; 136 } else { 137 cache = caches[index]; 138 } 139 140 int accumulatedValue = value; 141 if (cache.containsKey(entityName)) { 142 accumulatedValue += cache.get(entityName); 143 cache.put(entityName, accumulatedValue); 144 } else { 145 cache.put(entityName, value); 146 } 147 148 if (accumulatedValue > values[index]) { 149 values[index] = accumulatedValue; 150 names[index] = entityName; 151 } 152 153 154 if (index == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal() || 155 index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) { 156 totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value; 157 } 158 } 159 160 /** 161 * Return the value of the current max count for the specified property 162 * 163 * @param limit the property 164 * @return the value of the property 165 */ 166 public int getValue(Limit limit) { 167 return getValue(limit.ordinal()); 168 } 169 170 public int getValue(int index) { 171 if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) { 172 return totalValue[index]; 173 } 174 return values[index]; 175 } 176 /** 177 * Return the total value accumulated so far 178 * 179 * @param limit the property 180 * @return the accumulated value of the property 181 */ 182 public int getTotalValue(Limit limit) { 183 return totalValue[limit.ordinal()]; 184 } 185 186 public int getTotalValue(int index) { 187 return totalValue[index]; 188 } 189 /** 190 * Return the current max value (count or length) by the index of a property 191 * @param index the index of a property 192 * @return count of a property 193 */ 194 public int getValueByIndex(int index) { 195 return values[index]; 196 } 197 198 public void startEntity(String name) { 199 entityStart = name; 200 } 201 202 public boolean isTracking(String name) { 203 if (entityStart == null) { 204 return false; 205 } 206 return entityStart.equals(name); 207 } 208 /** 209 * Stop tracking the entity 210 * @param limit the limit property 211 * @param name the name of an entity 212 */ 213 public void endEntity(Limit limit, String name) { 214 entityStart = ""; 215 Map<String, Integer> cache = caches[limit.ordinal()]; 216 if (cache != null) { 217 cache.remove(name); 218 } 219 } 220 221 /** 222 * Resets the current value of the specified limit. 223 * @param limit The limit to be reset. 224 */ 225 public void reset(Limit limit) { 226 if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) { 227 totalValue[limit.ordinal()] = 0; 228 } else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) { 229 names[limit.ordinal()] = null; 230 values[limit.ordinal()] = 0; 231 caches[limit.ordinal()] = null; 232 totalValue[limit.ordinal()] = 0; 233 } 234 } 235 236 public void debugPrint(XMLSecurityManager securityManager) { 237 Formatter formatter = new Formatter(); 238 System.out.println(formatter.format("%30s %15s %15s %15s %30s", 239 "Property","Limit","Total size","Size","Entity Name")); 240 241 for (Limit limit : Limit.values()) { 242 formatter = new Formatter(); 243 System.out.println(formatter.format("%30s %15d %15d %15d %30s", 244 limit.name(), 245 securityManager.getLimit(limit), 246 totalValue[limit.ordinal()], 247 values[limit.ordinal()], 248 names[limit.ordinal()])); 249 } 250 } 251 }