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