Path: blob/master/src/java.xml/share/classes/jdk/xml/internal/XMLLimitAnalyzer.java
67862 views
/*1* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package jdk.xml.internal;2526import java.util.Formatter;27import java.util.HashMap;28import java.util.Map;29import jdk.xml.internal.XMLSecurityManager.Limit;303132/**33* A helper for analyzing entity expansion limits34*35*/36public final class XMLLimitAnalyzer {3738/**39* Map old property names with the new ones40*/41public static enum NameMap {42ENTITY_EXPANSION_LIMIT(JdkConstants.SP_ENTITY_EXPANSION_LIMIT, JdkConstants.ENTITY_EXPANSION_LIMIT),43MAX_OCCUR_NODE_LIMIT(JdkConstants.SP_MAX_OCCUR_LIMIT, JdkConstants.MAX_OCCUR_LIMIT),44ELEMENT_ATTRIBUTE_LIMIT(JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, JdkConstants.ELEMENT_ATTRIBUTE_LIMIT);4546final String newName;47final String oldName;4849NameMap(String newName, String oldName) {50this.newName = newName;51this.oldName = oldName;52}5354String getOldName(String newName) {55if (newName.equals(this.newName)) {56return oldName;57}58return null;59}60}6162/**63* Max value accumulated for each property64*/65private final int[] values;66/**67* Names of the entities corresponding to their max values68*/69private final String[] names;70/**71* Total value of accumulated entities72*/73private final int[] totalValue;7475/**76* Maintain values of the top 10 elements in the process of parsing77*/78private final Map<String, Integer>[] caches;7980private String entityStart, entityEnd;81/**82* Default constructor. Establishes default values for known security83* vulnerabilities.84*/85@SuppressWarnings({"rawtypes", "unchecked"})86public XMLLimitAnalyzer() {87values = new int[Limit.values().length];88totalValue = new int[Limit.values().length];89names = new String[Limit.values().length];90caches = new Map[Limit.values().length];91}9293/**94* Add the value to the current max count for the specified property95* To find the max value of all entities, set no limit96*97* @param limit the type of the property98* @param entityName the name of the entity99* @param value the value of the entity100*/101public void addValue(Limit limit, String entityName, int value) {102addValue(limit.ordinal(), entityName, value);103}104105/**106* Add the value to the current count by the index of the property107* @param index the index of the property108* @param entityName the name of the entity109* @param value the value of the entity110*/111public void addValue(int index, String entityName, int value) {112if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||113index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() ||114index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||115index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||116index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()117) {118totalValue[index] += value;119return;120}121if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||122index == Limit.MAX_NAME_LIMIT.ordinal()) {123values[index] = value;124totalValue[index] = value;125return;126}127128Map<String, Integer> cache;129if (caches[index] == null) {130cache = new HashMap<>(10);131caches[index] = cache;132} else {133cache = caches[index];134}135136int accumulatedValue = value;137if (cache.containsKey(entityName)) {138accumulatedValue += cache.get(entityName);139cache.put(entityName, accumulatedValue);140} else {141cache.put(entityName, value);142}143144if (accumulatedValue > values[index]) {145values[index] = accumulatedValue;146names[index] = entityName;147}148149150if (index == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal() ||151index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) {152totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value;153}154}155156/**157* Return the value of the current max count for the specified property158*159* @param limit the property160* @return the value of the property161*/162public int getValue(Limit limit) {163return getValue(limit.ordinal());164}165166public int getValue(int index) {167if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) {168return totalValue[index];169}170return values[index];171}172/**173* Return the total value accumulated so far174*175* @param limit the property176* @return the accumulated value of the property177*/178public int getTotalValue(Limit limit) {179return totalValue[limit.ordinal()];180}181182public int getTotalValue(int index) {183return totalValue[index];184}185/**186* Return the current max value (count or length) by the index of a property187* @param index the index of a property188* @return count of a property189*/190public int getValueByIndex(int index) {191return values[index];192}193194public void startEntity(String name) {195entityStart = name;196}197198public boolean isTracking(String name) {199if (entityStart == null) {200return false;201}202return entityStart.equals(name);203}204/**205* Stop tracking the entity206* @param limit the limit property207* @param name the name of an entity208*/209public void endEntity(Limit limit, String name) {210entityStart = "";211Map<String, Integer> cache = caches[limit.ordinal()];212if (cache != null) {213cache.remove(name);214}215}216217/**218* Resets the current value of the specified limit.219* @param limit The limit to be reset.220*/221public void reset(Limit limit) {222if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) {223totalValue[limit.ordinal()] = 0;224} else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) {225names[limit.ordinal()] = null;226values[limit.ordinal()] = 0;227caches[limit.ordinal()] = null;228totalValue[limit.ordinal()] = 0;229}230}231232public void debugPrint(XMLSecurityManager securityManager) {233Formatter formatter = new Formatter();234System.out.println(formatter.format("%30s %15s %15s %15s %30s",235"Property","Limit","Total size","Size","Entity Name"));236237for (Limit limit : Limit.values()) {238formatter = new Formatter();239System.out.println(formatter.format("%30s %15d %15d %15d %30s",240limit.name(),241securityManager.getLimit(limit),242totalValue[limit.ordinal()],243values[limit.ordinal()],244names[limit.ordinal()]));245}246}247}248249250