1/* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 2013, 2016, 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 */ 40package com.sun.org.apache.xerces.internal.utils; 41 42import com.sun.org.apache.xerces.internal.impl.Constants; 43import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit; 44import java.util.Formatter; 45import java.util.HashMap; 46import java.util.Map; 47 48/** 49 * A helper for analyzing entity expansion limits 50 * 51 * @author Joe Wang Oracle Corp. 52 * 53 */ 54public 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[] caches; 97 98 private String entityStart, entityEnd; 99 /** 100 * Default constructor. Establishes default values for known security 101 * vulnerabilities. 102 */ 103 public XMLLimitAnalyzer() { 104 values = new int[Limit.values().length]; 105 totalValue = new int[Limit.values().length]; 106 names = new String[Limit.values().length]; 107 caches = new Map[Limit.values().length]; 108 } 109 110 /** 111 * Add the value to the current max count for the specified property 112 * To find the max value of all entities, set no limit 113 * 114 * @param limit the type of the property 115 * @param entityName the name of the entity 116 * @param value the value of the entity 117 */ 118 public void addValue(Limit limit, String entityName, int value) { 119 addValue(limit.ordinal(), entityName, value); 120 } 121 122 /** 123 * Add the value to the current count by the index of the property 124 * @param index the index of the property 125 * @param entityName the name of the entity 126 * @param value the value of the entity 127 */ 128 public void addValue(int index, String entityName, int value) { 129 if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || 130 index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || 131 index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || 132 index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || 133 index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() 134 ) { 135 totalValue[index] += value; 136 return; 137 } 138 if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || 139 index == Limit.MAX_NAME_LIMIT.ordinal()) { 140 values[index] = value; 141 totalValue[index] = value; 142 return; 143 } 144 145 Map<String, Integer> cache; 146 if (caches[index] == null) { 147 cache = new HashMap<>(10); 148 caches[index] = cache; 149 } else { 150 cache = caches[index]; 151 } 152 153 int accumulatedValue = value; 154 if (cache.containsKey(entityName)) { 155 accumulatedValue += cache.get(entityName); 156 cache.put(entityName, accumulatedValue); 157 } else { 158 cache.put(entityName, value); 159 } 160 161 if (accumulatedValue > values[index]) { 162 values[index] = accumulatedValue; 163 names[index] = entityName; 164 } 165 166 167 if (index == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal() || 168 index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) { 169 totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value; 170 } 171 } 172 173 /** 174 * Return the value of the current max count for the specified property 175 * 176 * @param limit the property 177 * @return the value of the property 178 */ 179 public int getValue(Limit limit) { 180 return getValue(limit.ordinal()); 181 } 182 183 public int getValue(int index) { 184 if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) { 185 return totalValue[index]; 186 } 187 return values[index]; 188 } 189 /** 190 * Return the total value accumulated so far 191 * 192 * @param limit the property 193 * @return the accumulated value of the property 194 */ 195 public int getTotalValue(Limit limit) { 196 return totalValue[limit.ordinal()]; 197 } 198 199 public int getTotalValue(int index) { 200 return totalValue[index]; 201 } 202 /** 203 * Return the current max value (count or length) by the index of a property 204 * @param index the index of a property 205 * @return count of a property 206 */ 207 public int getValueByIndex(int index) { 208 return values[index]; 209 } 210 211 public void startEntity(String name) { 212 entityStart = name; 213 } 214 215 public boolean isTracking(String name) { 216 if (entityStart == null) { 217 return false; 218 } 219 return entityStart.equals(name); 220 } 221 /** 222 * Stop tracking the entity 223 * @param limit the limit property 224 * @param name the name of an entity 225 */ 226 public void endEntity(Limit limit, String name) { 227 entityStart = ""; 228 Map<String, Integer> cache = caches[limit.ordinal()]; 229 if (cache != null) { 230 cache.remove(name); 231 } 232 } 233 234 /** 235 * Resets the current value of the specified limit. 236 * @param limit The limit to be reset. 237 */ 238 public void reset(Limit limit) { 239 if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) { 240 totalValue[limit.ordinal()] = 0; 241 } else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) { 242 names[limit.ordinal()] = null; 243 values[limit.ordinal()] = 0; 244 caches[limit.ordinal()] = null; 245 totalValue[limit.ordinal()] = 0; 246 } 247 } 248 249 public void debugPrint(XMLSecurityManager securityManager) { 250 Formatter formatter = new Formatter(); 251 System.out.println(formatter.format("%30s %15s %15s %15s %30s", 252 "Property","Limit","Total size","Size","Entity Name")); 253 254 for (Limit limit : Limit.values()) { 255 formatter = new Formatter(); 256 System.out.println(formatter.format("%30s %15d %15d %15d %30s", 257 limit.name(), 258 securityManager.getLimit(limit), 259 totalValue[limit.ordinal()], 260 values[limit.ordinal()], 261 names[limit.ordinal()])); 262 } 263 } 264} 265