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 }