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 private XMLSecurityManager securityManager; 81 /** 82 * Max value accumulated for each property 83 */ 84 private final int[] values; 85 /** 86 * Names of the entities corresponding to their max values 87 */ 88 private final String[] names; 89 /** 90 * Total value of accumulated entities 91 */ 92 private final int[] totalValue; 93 94 /** 95 * Maintain values of the top 10 elements in the process of parsing 96 */ 97 private final Map[] caches; 98 99 private String entityStart, entityEnd; 100 /** 101 * Default constructor. Establishes default values for known security 102 * vulnerabilities. 103 */ 104 public XMLLimitAnalyzer(XMLSecurityManager securityManager) { 105 this.securityManager = securityManager; 106 values = new int[Limit.values().length]; 107 totalValue = new int[Limit.values().length]; 108 names = new String[Limit.values().length]; 109 caches = new Map[Limit.values().length]; 110 } 111 112 /** 113 * Add the value to the current max count for the specified property 114 * To find the max value of all entities, set no limit 115 * 116 * @param limit the type of the property 117 * @param entityName the name of the entity 118 * @param value the value of the entity 119 */ 120 public void addValue(Limit limit, String entityName, int value) { 121 addValue(limit.ordinal(), entityName, value); 122 } 123 124 /** 125 * Add the value to the current count by the index of the property 126 * @param index the index of the property 127 * @param entityName the name of the entity 128 * @param value the value of the entity 129 */ 130 public void addValue(int index, String entityName, int value) { 131 if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || 132 index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || 133 index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal()) { 134 totalValue[index] += value; 135 return; 136 } 137 138 Map<String, Integer> cache; 139 if (caches[index] == null) { 140 cache = new HashMap<String, Integer>(10); 141 caches[index] = cache; 142 } else { 143 cache = caches[index]; 144 } 145 146 int accumulatedValue = value; 147 if (cache.containsKey(entityName)) { 148 accumulatedValue += cache.get(entityName).intValue(); 149 cache.put(entityName, Integer.valueOf(accumulatedValue)); 150 } else { 151 cache.put(entityName, Integer.valueOf(value)); 152 } 153 154 if (accumulatedValue > values[index]) { 155 values[index] = accumulatedValue; 156 names[index] = entityName; 157 } 158 159 160 if (index == Limit.GENEAL_ENTITY_SIZE_LIMIT.ordinal() || 161 index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) { 162 totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value; 163 } 164 } 165 166 /** 167 * Return the value of the current max count for the specified property 168 * 169 * @param limit the property 170 * @return the value of the property 171 */ 172 public int getValue(Limit limit) { 173 return values[limit.ordinal()]; 174 } 175 176 public int getValue(int index) { 177 return values[index]; 178 } 179 /** 180 * Return the total value accumulated so far 181 * 182 * @param limit the property 183 * @return the accumulated value of the property 184 */ 185 public int getTotalValue(Limit limit) { 186 return totalValue[limit.ordinal()]; 187 } 188 189 public int getTotalValue(int index) { 190 return totalValue[index]; 191 } 192 /** 193 * Return the current max value (count or length) by the index of a property 194 * @param index the index of a property 195 * @return count of a property 196 */ 197 public int getValueByIndex(int index) { 198 return values[index]; 199 } 200 201 public void startEntity(String name) { 202 entityStart = name; 203 } 204 205 public boolean isTracking(String name) { 206 if (entityStart == null) { 207 return false; 208 } 209 return entityStart.equals(name); 210 } 211 /** 212 * Stop tracking the entity 213 * @param limit the limit property 214 * @param name the name of an entity 215 */ 216 public void endEntity(Limit limit, String name) { 217 entityStart = ""; 218 Map<String, Integer> cache = caches[limit.ordinal()]; 219 if (cache != null) { 220 cache.remove(name); 221 } 222 } 223 224 public void debugPrint() { 225 Formatter formatter = new Formatter(); 226 System.out.println(formatter.format("%30s %15s %15s %15s %30s", 227 "Property","Limit","Total size","Size","Entity Name")); 228 229 for (Limit limit : Limit.values()) { 230 formatter = new Formatter(); 231 System.out.println(formatter.format("%30s %15d %15d %15d %30s", 232 limit.name(), 233 securityManager.getLimit(limit), 234 totalValue[limit.ordinal()], 235 values[limit.ordinal()], 236 names[limit.ordinal()])); 237 } 238 } 239 }