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 }