1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 2013 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[] 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 totalValue[index] += value; 133 return; 134 } 135 if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal()) { 136 totalValue[index] = value; 137 return; 138 } 139 140 Map<String, Integer> cache; 141 if (caches[index] == null) { 142 cache = new HashMap<String, Integer>(10); 143 caches[index] = cache; 144 } else { 145 cache = caches[index]; 146 } 147 148 int accumulatedValue = value; 149 if (cache.containsKey(entityName)) { 150 accumulatedValue += cache.get(entityName).intValue(); 151 cache.put(entityName, Integer.valueOf(accumulatedValue)); 152 } else { 153 cache.put(entityName, Integer.valueOf(value)); 154 } 155 156 if (accumulatedValue > values[index]) { 157 values[index] = accumulatedValue; 158 names[index] = entityName; 159 } 160 161 162 if (index == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal() || 163 index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) { 164 totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value; 165 } 166 } 167 168 /** 169 * Return the value of the current max count for the specified property 170 * 171 * @param limit the property 172 * @return the value of the property 173 */ 174 public int getValue(Limit limit) { 175 return values[limit.ordinal()]; 176 } 177 178 public int getValue(int index) { 179 return values[index]; 180 } 181 /** 182 * Return the total value accumulated so far 183 * 184 * @param limit the property 185 * @return the accumulated value of the property 186 */ 187 public int getTotalValue(Limit limit) { 188 return totalValue[limit.ordinal()]; 189 } 190 191 public int getTotalValue(int index) { 192 return totalValue[index]; 193 } 194 /** 195 * Return the current max value (count or length) by the index of a property 196 * @param index the index of a property 197 * @return count of a property 198 */ 199 public int getValueByIndex(int index) { 200 return values[index]; 201 } 202 203 public void startEntity(String name) { 204 entityStart = name; 205 } 206 207 public boolean isTracking(String name) { 208 if (entityStart == null) { 209 return false; 210 } 211 return entityStart.equals(name); 212 } 213 /** 214 * Stop tracking the entity 215 * @param limit the limit property 216 * @param name the name of an entity 217 */ 218 public void endEntity(Limit limit, String name) { 219 entityStart = ""; 220 Map<String, Integer> cache = caches[limit.ordinal()]; 221 if (cache != null) { 222 cache.remove(name); 223 } 224 } 225 226 public void debugPrint(XMLSecurityManager securityManager) { 227 Formatter formatter = new Formatter(); 228 System.out.println(formatter.format("%30s %15s %15s %15s %30s", 229 "Property","Limit","Total size","Size","Entity Name")); 230 231 for (Limit limit : Limit.values()) { 232 formatter = new Formatter(); 233 System.out.println(formatter.format("%30s %15d %15d %15d %30s", 234 limit.name(), 235 securityManager.getLimit(limit), 236 totalValue[limit.ordinal()], 237 values[limit.ordinal()], 238 names[limit.ordinal()])); 239 } 240 } 241 }