Commit db3840b0946e620709579c8cdbec8b521ae88075

Authored by Alan Davis
1 parent 11fefb5999
Exists in master

REPO-480 Platform XXE protection implements OWASP recommendations

- Added configurable enable and disable feature lists.
   - Added configurable class white list.
   - Added unit tests (FactoryHelper may now be instantiated rather than just having static methods).
src/main/java/org/alfresco/xmlfactory/DocumentBuilderFactoryXercesImpl.java
1 1 /*
2   - * Copyright (C) 2005-2014 Alfresco Software Limited.
  2 + * Copyright (C) 2005-2016 Alfresco Software Limited.
3 3 *
4 4 * This file is part of Alfresco
5 5 *
... ... @@ -18,29 +18,25 @@
18 18 */
19 19 package org.alfresco.xmlfactory;
20 20  
21   -import java.util.ArrayList;
22 21 import java.util.List;
23 22  
24 23 import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
25 24  
  25 +import javax.xml.parsers.DocumentBuilderFactory;
  26 +
26 27 public class DocumentBuilderFactoryXercesImpl extends DocumentBuilderFactoryImpl
27 28 {
28   - private static List<String> featuresToEnable = FactoryHelper.DEFAULT_FEATURES_TO_ENABLE;
29   - private static List<String> featuresToDisable = FactoryHelper.DEFAULT_FEATURES_TO_DISABLE;
30   -
  29 + // Static so we only do this configuration lookup once
  30 + private static FactoryHelper factoryHelper = new FactoryHelper();
  31 + private static List<String> FEATURES_TO_ENABLE = factoryHelper.getConfiguration(DocumentBuilderFactory.class,
  32 + FactoryHelper.FEATURES_TO_ENABLE, FactoryHelper.DEFAULT_FEATURES_TO_ENABLE);
  33 + private static List<String> FEATURES_TO_DISABLE = factoryHelper.getConfiguration(DocumentBuilderFactory.class,
  34 + FactoryHelper.FEATURES_TO_DISABLE, FactoryHelper.DEFAULT_FEATURES_TO_DISABLE);
  35 + private static List<String> WHITE_LIST_CALLERS = factoryHelper.getConfiguration(DocumentBuilderFactory.class,
  36 + FactoryHelper.WHITE_LIST_CALLERS, FactoryHelper.DEFAULT_WHITE_LIST_CALLERS);
  37 +
31 38 public DocumentBuilderFactoryXercesImpl()
32 39 {
33   - super();
34   - FactoryHelper.configureFactory(this, featuresToEnable, featuresToDisable);
35   - }
36   -
37   - public void setFeaturesToEnable(List<String> featuresToEnable)
38   - {
39   - DocumentBuilderFactoryXercesImpl.featuresToEnable = new ArrayList<String>(featuresToEnable);
40   - }
41   -
42   - public void setFeaturesToDisable(List<String> featuresToDisable)
43   - {
44   - DocumentBuilderFactoryXercesImpl.featuresToDisable = new ArrayList<String>(featuresToDisable);
  40 + factoryHelper.configureFactory(this, FEATURES_TO_ENABLE, FEATURES_TO_DISABLE, WHITE_LIST_CALLERS);
45 41 }
46 42 }
... ...
src/main/java/org/alfresco/xmlfactory/FactoryHelper.java
... ... @@ -18,10 +18,10 @@
18 18 */
19 19 package org.alfresco.xmlfactory;
20 20  
21   -import java.util.ArrayList;
22   -import java.util.Arrays;
23   -import java.util.Collections;
24   -import java.util.List;
  21 +import java.io.*;
  22 +import java.net.MalformedURLException;
  23 +import java.net.URL;
  24 +import java.util.*;
25 25  
26 26 import javax.xml.XMLConstants;
27 27 import javax.xml.parsers.DocumentBuilderFactory;
... ... @@ -58,7 +58,7 @@ public class FactoryHelper
58 58 FEATURE_DISALLOW_DOCTYPE)));
59 59  
60 60 /* white list of classes that can use the parsers with no security restrictions */
61   - public final static List<String> WHITE_LIST_CALLERS = Collections.unmodifiableList(new ArrayList<String>(
  61 + public final static List<String> DEFAULT_WHITE_LIST_CALLERS = Collections.unmodifiableList(new ArrayList<String>(
62 62 Arrays.asList(
63 63 "com.sun.xml.ws.transport.http.servlet.WSServletContextListener",
64 64 "org.springframework.beans.factory.xml.XmlBeanDefinitionReader",
... ... @@ -69,10 +69,38 @@ public class FactoryHelper
69 69 "org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl"
70 70 )));
71 71  
72   - public static void configureFactory(DocumentBuilderFactory factory,
73   - List<String> featuresToEnable, List<String> featuresToDisable)
  72 + // Property names used to configure the factories
  73 + public static final String FEATURES_TO_ENABLE = "features.to.enable";
  74 + public static final String FEATURES_TO_DISABLE = "features.to.disable";
  75 + public static final String WHITE_LIST_CALLERS = "white.list.callers";
  76 +
  77 + public void configureFactory(DocumentBuilderFactory factory, List<String> featuresToEnable,
  78 + List<String> featuresToDisable, List<String> whiteListCallers)
  79 + {
  80 + if (!isCallInWhiteList(whiteListCallers))
  81 + {
  82 + if (featuresToEnable != null)
  83 + {
  84 + for (String featureToEnable : featuresToEnable)
  85 + {
  86 + setFeature(factory, featureToEnable, true);
  87 + }
  88 + }
  89 + if (featuresToDisable != null)
  90 + {
  91 + for (String featureToDisable : featuresToDisable)
  92 + {
  93 + setFeature(factory, featureToDisable, false);
  94 + }
  95 + }
  96 + applyAdditionalFeatures(factory);
  97 + }
  98 + }
  99 +
  100 + public void configureFactory(SAXParserFactory factory, List<String> featuresToEnable,
  101 + List<String> featuresToDisable, List<String> whiteListCallers)
74 102 {
75   - if (!isCallInWhiteList())
  103 + if (!isCallInWhiteList(whiteListCallers))
76 104 {
77 105 if (featuresToEnable != null)
78 106 {
... ... @@ -92,14 +120,15 @@ public class FactoryHelper
92 120 }
93 121 }
94 122  
95   - private static boolean isCallInWhiteList()
  123 + private boolean isCallInWhiteList(List<String> whiteListCallers)
96 124 {
97 125 StackTraceElement[] currentStackTrace = (new Exception()).getStackTrace();
98 126 for (int i = 0; i < currentStackTrace.length; i++)
99 127 {
100   - for (String className : WHITE_LIST_CALLERS)
  128 + String currentClassName = currentStackTrace[i].getClassName();
  129 + for (String className : whiteListCallers)
101 130 {
102   - if (currentStackTrace[i].getClassName().equals(className))
  131 + if (currentClassName.equals(className))
103 132 {
104 133 logger.debug("Found " + className + " in white list.");
105 134 return true;
... ... @@ -109,7 +138,7 @@ public class FactoryHelper
109 138 return false;
110 139 }
111 140  
112   - private static void applyAdditionalFeatures(DocumentBuilderFactory factory)
  141 + private void applyAdditionalFeatures(DocumentBuilderFactory factory)
113 142 {
114 143 try
115 144 {
... ... @@ -122,7 +151,19 @@ public class FactoryHelper
122 151 }
123 152 }
124 153  
125   - private static void setFeature(DocumentBuilderFactory factory, String feature, boolean enable)
  154 + private void applyAdditionalFeatures(SAXParserFactory factory)
  155 + {
  156 + try
  157 + {
  158 + factory.setXIncludeAware(false);
  159 + }
  160 + catch (Exception e)
  161 + {
  162 + logConfigurationFailure(factory.getClass().getName(), e);
  163 + }
  164 + }
  165 +
  166 + private void setFeature(DocumentBuilderFactory factory, String feature, boolean enable)
126 167 {
127 168 try
128 169 {
... ... @@ -134,7 +175,7 @@ public class FactoryHelper
134 175 }
135 176 }
136 177  
137   - private static void setFeature(SAXParserFactory factory, String feature, boolean enable)
  178 + private void setFeature(SAXParserFactory factory, String feature, boolean enable)
138 179 {
139 180 try
140 181 {
... ... @@ -154,7 +195,7 @@ public class FactoryHelper
154 195 }
155 196 }
156 197  
157   - private static void logConfigurationFailure(String factoryName, String feature, Exception e)
  198 + private void logConfigurationFailure(String factoryName, String feature, Exception e)
158 199 {
159 200 if (logger.isWarnEnabled())
160 201 {
... ... @@ -162,7 +203,7 @@ public class FactoryHelper
162 203 }
163 204 }
164 205  
165   - private static void logConfigurationFailure(String factoryName, Exception e)
  206 + private void logConfigurationFailure(String factoryName, Exception e)
166 207 {
167 208 if (logger.isWarnEnabled())
168 209 {
... ... @@ -170,19 +211,153 @@ public class FactoryHelper
170 211 }
171 212 }
172 213  
173   - public static void configureFactory(SAXParserFactory factory,
174   - List<String> featuresToEnable, List<String> featuresToDisable) {
175   - if (!isCallInWhiteList()) {
176   - if (featuresToEnable != null) {
177   - for (String featureToEnable : featuresToEnable) {
178   - setFeature(factory, featureToEnable, true);
  214 + /**
  215 + * Returns a List of features (to be enabled or disabled) or class names (to be included in a caller white list) for
  216 + * a factory. This method uses a similar approach to the one used to select the JAXP factories in the first place.
  217 + * The following order is used to find a semicolon separated list of values:
  218 + * <li>A system property {@code}&lt;factoryName>.<propertyNameSuffix>{@code} if it exists and is accessible
  219 + * (for example {@code}javax.xml.parsers.SAXParserFactory.enable{@code}=...).</li>
  220 + * <li>A property in {@code}$JAVA_HOME/lib/&lt;factoryName>.properties{@code} if it exists.</li>
  221 + * <li>A property in {@code}META-INF/services/&lt;factoryName>.properties{@code} if it exists.</li>
  222 + * <li>The {@code}deafultFeatures{@code} parameter passed to this method.</li>
  223 + *
  224 + * @param factoryClass used to look up the &lt;factoryName>.
  225 + * @param propertyName used as the property name in files or as the suffix in a sysme property.
  226 + * @param defaultFeatures to be returned if other values are not found.
  227 + * @return the list of features or class names.
  228 + */
  229 + public List<String> getConfiguration(Class<?> factoryClass, String propertyName, List<String> defaultFeatures)
  230 + {
  231 + List<String> features = defaultFeatures;
  232 + ClassLoader loader = null;
  233 +
  234 + String factoryName = factoryClass.getName();
  235 + String extendedPropertyName = factoryName+'.'+propertyName;
  236 +
  237 + // Look for values in <factoryName>.enable or <factoryName>.disable
  238 + String value = null;
  239 + try
  240 + {
  241 + value = getSystemProperty(extendedPropertyName);
  242 + }
  243 + catch (SecurityException e)
  244 + {
  245 + logger.debug("Error reading system property:"+extendedPropertyName, e);
  246 + }
  247 +
  248 + // Look for values in $JAVA_HOME/jre/lib/<factoryName>.properties.
  249 + if (value == null)
  250 + {
  251 + URL url = null;
  252 + String javaHome = getJavaHome();
  253 + if (javaHome != null)
  254 + {
  255 + File file = new File(new File(new File(javaHome), "lib"), factoryName+".properties");
  256 + try
  257 + {
  258 + url = file.toURI().toURL();
  259 + value = getProperty(url, propertyName);
  260 + }
  261 + catch (MalformedURLException e)
  262 + {
  263 + logger.debug("Error creating URL for:"+file, e);
179 264 }
180 265 }
181   - if (featuresToDisable != null) {
182   - for (String featureToDisable : featuresToDisable) {
183   - setFeature(factory, featureToDisable, false);
  266 + }
  267 +
  268 + // Look for values in META-INF/services/<factoryName>.properties.
  269 + if (value == null)
  270 + {
  271 + String resourceName = "META-INF/services/" + factoryName+".properties";
  272 + URL url = getResource(loader, resourceName);
  273 + value = getProperty(url, propertyName);
  274 + }
  275 +
  276 + // Add features to a new List
  277 + if (value != null)
  278 + {
  279 + features = new ArrayList<>();
  280 + value = value.trim();
  281 + if (!value.isEmpty())
  282 + {
  283 + for (String feature: value.split(";"))
  284 + {
  285 + features.add(feature.trim());
  286 + }
  287 + }
  288 + }
  289 +
  290 + return features;
  291 + }
  292 +
  293 + String getJavaHome()
  294 + {
  295 + return System.getenv("JAVA_HOME");
  296 + }
  297 +
  298 + String getSystemProperty(String propertyName)
  299 + {
  300 + return System.getProperty(propertyName);
  301 + }
  302 +
  303 + URL getResource(ClassLoader loader, String resourceName)
  304 + {
  305 + return loader == null
  306 + ? ClassLoader.getSystemResource(resourceName)
  307 + : loader.getResource(resourceName);
  308 + }
  309 +
  310 + String getProperty(URL url, String propertyName)
  311 + {
  312 + String value = null;
  313 + if (url != null)
  314 + {
  315 + Properties properties = getProperties(url);
  316 + value = properties.getProperty(propertyName);
  317 + }
  318 + return value;
  319 + }
  320 +
  321 + private Properties getProperties(URL url)
  322 + {
  323 + Properties properties = new Properties();
  324 + InputStream in = null;
  325 + Reader reader = null;
  326 + try
  327 + {
  328 + in = url.openStream();
  329 + reader = new InputStreamReader(in, "UTF-8");
  330 + properties.load(reader);
  331 + }
  332 + catch (IOException e)
  333 + {
  334 + logger.debug("Error reading :"+url, e);
  335 + }
  336 + finally
  337 + {
  338 + if (reader != null)
  339 + {
  340 + try
  341 + {
  342 + reader.close();
  343 + }
  344 + catch (IOException e)
  345 + {
  346 + // ignore
  347 + }
  348 + }
  349 + if (in != null)
  350 + {
  351 + try
  352 + {
  353 + in.close();
  354 + }
  355 + catch (IOException e)
  356 + {
  357 + // ignore
184 358 }
185 359 }
186 360 }
  361 + return properties;
187 362 }
188 363 }
... ...
src/main/java/org/alfresco/xmlfactory/SAXParserFactoryXercesImpl.java
1 1 /*
2   - * Copyright (C) 2005-2014 Alfresco Software Limited.
  2 + * Copyright (C) 2005-2016 Alfresco Software Limited.
3 3 *
4 4 * This file is part of Alfresco
5 5 *
... ... @@ -18,29 +18,24 @@
18 18 */
19 19 package org.alfresco.xmlfactory;
20 20  
21   -import java.util.ArrayList;
22   -import java.util.List;
23   -
24 21 import org.apache.xerces.jaxp.SAXParserFactoryImpl;
25 22  
  23 +import javax.xml.parsers.SAXParserFactory;
  24 +import java.util.List;
  25 +
26 26 public class SAXParserFactoryXercesImpl extends SAXParserFactoryImpl
27 27 {
28   - private static List<String> featuresToEnable = FactoryHelper.DEFAULT_FEATURES_TO_ENABLE;
29   - private static List<String> featuresToDisable = FactoryHelper.DEFAULT_FEATURES_TO_DISABLE;
30   -
  28 + // Static so we only do this configuration lookup once
  29 + private static FactoryHelper factoryHelper = new FactoryHelper();
  30 + private static List<String> FEATURES_TO_ENABLE = factoryHelper.getConfiguration(SAXParserFactory.class,
  31 + FactoryHelper.FEATURES_TO_ENABLE, FactoryHelper.DEFAULT_FEATURES_TO_ENABLE);
  32 + private static List<String> FEATURES_TO_DISABLE = factoryHelper.getConfiguration(SAXParserFactory.class,
  33 + FactoryHelper.FEATURES_TO_DISABLE, FactoryHelper.DEFAULT_FEATURES_TO_DISABLE);
  34 + private static List<String> WHITE_LIST_CALLERS = factoryHelper.getConfiguration(SAXParserFactory.class,
  35 + FactoryHelper.WHITE_LIST_CALLERS, FactoryHelper.DEFAULT_WHITE_LIST_CALLERS);
  36 +
31 37 public SAXParserFactoryXercesImpl()
32 38 {
33   - super();
34   - FactoryHelper.configureFactory(this, featuresToEnable, featuresToDisable);
35   - }
36   -
37   - public void setFeaturesToEnable(List<String> featuresToEnable)
38   - {
39   - SAXParserFactoryXercesImpl.featuresToEnable = new ArrayList<String>(featuresToEnable);
40   - }
41   -
42   - public void setFeaturesToDisable(List<String> featuresToDisable)
43   - {
44   - SAXParserFactoryXercesImpl.featuresToDisable = new ArrayList<String>(featuresToDisable);
  39 + factoryHelper.configureFactory(this, FEATURES_TO_ENABLE, FEATURES_TO_DISABLE, WHITE_LIST_CALLERS);
45 40 }
46 41 }
... ...
src/test/java/org/alfresco/xmlfactory/AppTest.java
  1 +/*
  2 + * Copyright (C) 2005-2016 Alfresco Software Limited.
  3 + *
  4 + * This file is part of Alfresco
  5 + *
  6 + * Alfresco is free software: you can redistribute it and/or modify
  7 + * it under the terms of the GNU Lesser General Public License as published by
  8 + * the Free Software Foundation, either version 3 of the License, or
  9 + * (at your option) any later version.
  10 + *
  11 + * Alfresco is distributed in the hope that it will be useful,
  12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 + * GNU Lesser General Public License for more details.
  15 + *
  16 + * You should have received a copy of the GNU Lesser General Public License
  17 + * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
  18 + */
1 19 package org.alfresco.xmlfactory;
2 20  
3 21 import junit.framework.Test;
4 22 import junit.framework.TestCase;
5 23 import junit.framework.TestSuite;
  24 +import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
  25 +import org.apache.xerces.jaxp.SAXParserFactoryImpl;
  26 +
  27 +import javax.xml.XMLConstants;
  28 +import javax.xml.parsers.DocumentBuilderFactory;
  29 +import javax.xml.parsers.SAXParserFactory;
  30 +import java.io.File;
  31 +import java.net.MalformedURLException;
  32 +import java.net.URL;
  33 +import java.util.*;
6 34  
7 35 /**
8 36 * Unit test for simple App.
... ... @@ -35,4 +63,219 @@ public class AppTest
35 63 {
36 64 assertTrue( true );
37 65 }
  66 +
  67 + /**
  68 + * Test we have set features the way we expect as defaults.
  69 + */
  70 + public void testDocumentBuilderFactory() throws Throwable {
  71 + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  72 +
  73 + assertTrue(dbf.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
  74 + assertTrue(dbf.getFeature(FactoryHelper.FEATURE_DISALLOW_DOCTYPE));
  75 +
  76 + assertFalse(dbf.getFeature(FactoryHelper.FEATURE_EXTERNAL_GENERAL_ENTITIES));
  77 + assertFalse(dbf.getFeature(FactoryHelper.FEATURE_EXTERNAL_PARAMETER_ENTITIES));
  78 + assertFalse(dbf.getFeature(FactoryHelper.FEATURE_USE_ENTITY_RESOLVER2));
  79 + assertFalse(dbf.getFeature(FactoryHelper.FEATURE_LOAD_EXTERNAL_DTD));
  80 +
  81 + assertFalse(dbf.isExpandEntityReferences());
  82 + assertFalse(dbf.isXIncludeAware());
  83 + }
  84 +
  85 + /**
  86 + * Test we have set features the way we expect as defaults.
  87 + */
  88 + public void testSAXParserFactory() throws Throwable
  89 + {
  90 + SAXParserFactory spf = SAXParserFactory.newInstance();
  91 +
  92 + assertTrue(spf.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
  93 + assertTrue(spf.getFeature(FactoryHelper.FEATURE_DISALLOW_DOCTYPE));
  94 +
  95 + assertFalse(spf.getFeature(FactoryHelper.FEATURE_EXTERNAL_GENERAL_ENTITIES));
  96 + assertFalse(spf.getFeature(FactoryHelper.FEATURE_EXTERNAL_PARAMETER_ENTITIES));
  97 + assertFalse(spf.getFeature(FactoryHelper.FEATURE_USE_ENTITY_RESOLVER2));
  98 + assertFalse(spf.getFeature(FactoryHelper.FEATURE_LOAD_EXTERNAL_DTD));
  99 +
  100 + assertFalse(spf.isXIncludeAware());
  101 + }
  102 +
  103 + private class TestFactoryHelper extends FactoryHelper
  104 + {
  105 + final Map<String, Properties> testValues = new HashMap<>();
  106 +
  107 + /**
  108 + * @param propertyArgs groups of 3: &lt;url>, &lt;propertyName>, &lt;value>. If &lt;url> is null the property
  109 + * is taken to be a system property.
  110 + */
  111 + TestFactoryHelper(String... propertyArgs)
  112 + {
  113 + for (int i=0; i< propertyArgs.length; i+=3)
  114 + {
  115 + String urlString = propertyArgs[i] == null ? "" : getResource(null, propertyArgs[i]).toString();
  116 + String propertyName = propertyArgs[i+1];
  117 + String value = propertyArgs[i+2];
  118 +
  119 + Properties properties = testValues.get(urlString);
  120 + if (properties == null)
  121 + {
  122 + properties = new Properties();
  123 + testValues.put(urlString, properties);
  124 + }
  125 + properties.put(propertyName, value);
  126 + }
  127 + }
  128 +
  129 + private String getTestValue(String urlString, String propertyName)
  130 + {
  131 + Properties properties = testValues.get(urlString);
  132 + return properties == null ? null : (String)properties.get(propertyName);
  133 + }
  134 +
  135 + @Override
  136 + String getJavaHome()
  137 + {
  138 + return "$JAVA_HOME"; // just has to be a non null value.
  139 + }
  140 +
  141 + @Override
  142 + String getSystemProperty(String propertyName)
  143 + {
  144 + return getTestValue("", propertyName);
  145 + }
  146 +
  147 + @Override
  148 + URL getResource(ClassLoader loader, String resourceName)
  149 + {
  150 + try
  151 + {
  152 + // This gets the wrong URI (it just prefixes the current directory), but that is okay for testing
  153 + // as we are not really reading files, but just using String values.
  154 + File file = new File(resourceName);
  155 + return file.toURI().toURL();
  156 + }
  157 + catch (MalformedURLException e)
  158 + {
  159 + fail("Error in test code creating URL");
  160 + return null;
  161 + }
  162 + }
  163 +
  164 + @Override
  165 + String getProperty(URL url, String propertyName)
  166 + {
  167 + return url == null ? null : getTestValue(url.toString(), propertyName);
  168 + }
  169 + }
  170 +
  171 + /**
  172 + * Test we have set features the way we expect as defaults.
  173 + */
  174 + public void testDocumentBuilderFactoryInWhiteList() throws Throwable
  175 + {
  176 + // Using constructor rather than the service locator and then using the helper to configure it.
  177 + DocumentBuilderFactory dbf = new DocumentBuilderFactoryImpl();
  178 + FactoryHelper factoryHelper = new FactoryHelper();
  179 + List<String> whiteListClasses = Collections.singletonList(getClass().getName());
  180 + factoryHelper.configureFactory(dbf, FactoryHelper.DEFAULT_FEATURES_TO_ENABLE,
  181 + FactoryHelper.DEFAULT_FEATURES_TO_DISABLE,
  182 + whiteListClasses);
  183 +
  184 + assertFalse(dbf.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
  185 + assertFalse(dbf.getFeature(FactoryHelper.FEATURE_DISALLOW_DOCTYPE));
  186 +
  187 + assertTrue(dbf.getFeature(FactoryHelper.FEATURE_EXTERNAL_GENERAL_ENTITIES));
  188 + assertTrue(dbf.getFeature(FactoryHelper.FEATURE_EXTERNAL_PARAMETER_ENTITIES));
  189 + assertTrue(dbf.getFeature(FactoryHelper.FEATURE_USE_ENTITY_RESOLVER2));
  190 + assertTrue(dbf.getFeature(FactoryHelper.FEATURE_LOAD_EXTERNAL_DTD));
  191 +
  192 + assertTrue(dbf.isExpandEntityReferences());
  193 + assertFalse(dbf.isXIncludeAware()); // false is the default so is same as the non whitelist test
  194 + }
  195 +
  196 + /**
  197 + * Test we have set features the way we expect as defaults.
  198 + */
  199 + public void testSAXParserFactoryInWhiteList() throws Throwable
  200 + {
  201 + // Using constructor rather than the service locator and then using the helper to configure it.
  202 + SAXParserFactory spf = new SAXParserFactoryImpl();
  203 + FactoryHelper factoryHelper = new FactoryHelper();
  204 + List<String> whiteListClasses = Collections.singletonList(getClass().getName());
  205 + factoryHelper.configureFactory(spf, FactoryHelper.DEFAULT_FEATURES_TO_ENABLE,
  206 + FactoryHelper.DEFAULT_FEATURES_TO_DISABLE,
  207 + whiteListClasses);
  208 +
  209 + assertFalse(spf.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
  210 + assertFalse(spf.getFeature(FactoryHelper.FEATURE_DISALLOW_DOCTYPE));
  211 +
  212 + assertTrue(spf.getFeature(FactoryHelper.FEATURE_EXTERNAL_GENERAL_ENTITIES));
  213 + assertTrue(spf.getFeature(FactoryHelper.FEATURE_EXTERNAL_PARAMETER_ENTITIES));
  214 + assertTrue(spf.getFeature(FactoryHelper.FEATURE_USE_ENTITY_RESOLVER2));
  215 + assertTrue(spf.getFeature(FactoryHelper.FEATURE_LOAD_EXTERNAL_DTD));
  216 +
  217 + assertFalse(spf.isXIncludeAware()); // false is the default so is same as the non whitelist test
  218 + }
  219 +
  220 + /**
  221 + * Test the pick up of configuration properties. We just overload the methods in the helper as putting files in
  222 + * the jre structure or META_INF is not a good idea in an automated test. Has been manually tested. This tests
  223 + * the code around the actual reading of property files and system properties.
  224 + */
  225 + public void testConfigOrder() throws Throwable
  226 + {
  227 + List<String> emptyList = Collections.emptyList();
  228 + List<String> abcDefList = Arrays.asList("abc", "def");
  229 +
  230 + FactoryHelper factoryHelper = new TestFactoryHelper(
  231 + // Test System Properties: <factory>.<propertyName>
  232 + null, "java.lang.String.name1", "sysProp1",
  233 + null, "java.lang.Object.name1", "sysProp2",
  234 + null, "java.lang.String.name2", " sysProp3a ; sysProp3b;sysProp3c ",
  235 +
  236 + // JRE Property file: jre/lib/<factory>.properties
  237 + "$JAVA_HOME/lib/java.lang.String.properties", "name1", "jre1",
  238 + "$JAVA_HOME/lib/java.lang.Object.properties", "name1", "jre2",
  239 + "$JAVA_HOME/lib/java.lang.String.properties", "name3", "jre3",
  240 +
  241 + // META_INF/services Property file: jre/lib/<factory>.properties
  242 + "META-INF/services/java.lang.String.properties", "name1", "metaInf1",
  243 + "META-INF/services/java.lang.Object.properties", "name1", "metaInf2",
  244 + "META-INF/services/java.lang.String.properties", "name3", "metaInf3",
  245 + "META-INF/services/java.lang.Object.properties", "name3", "metaInf4",
  246 + "META-INF/services/java.lang.String.properties", "name5", "metaInf5",
  247 + "META-INF/services/java.lang.String.properties", "name6", ""
  248 + );
  249 +
  250 + // Test System Properties
  251 + assertGetConfiguration(factoryHelper, emptyList, String.class, "name1", "sysProp1");
  252 + assertGetConfiguration(factoryHelper, emptyList, String.class, "name0");
  253 +
  254 + // Test JRE Property file
  255 + assertGetConfiguration(factoryHelper, emptyList, String.class, "name3", "jre3");
  256 +
  257 + // META_INF/services Property file
  258 + assertGetConfiguration(factoryHelper, emptyList, String.class, "name5", "metaInf5");
  259 +
  260 + // Test the default list
  261 + assertGetConfiguration(factoryHelper, abcDefList, String.class, "name7", "abc", "def");
  262 +
  263 + // Test zero length values - they should override the default
  264 + assertGetConfiguration(factoryHelper, emptyList, String.class, "name6");
  265 + assertGetConfiguration(factoryHelper, abcDefList, String.class, "name6");
  266 +
  267 + // Test multiple values in property value and trim of spaces
  268 + assertGetConfiguration(factoryHelper, emptyList, String.class, "name2", "sysProp3a", "sysProp3b", "sysProp3c");
  269 + }
  270 +
  271 + private void assertGetConfiguration(FactoryHelper fh, List<String> defaultValues, Class clazz, String propertyName, String... expected)
  272 + {
  273 + List<String> list = fh.getConfiguration(clazz, propertyName, defaultValues);
  274 +
  275 + for (int i=0; i < expected.length && i < list.size(); i++)
  276 + {
  277 + assertEquals(expected[i], list.get(i));
  278 + }
  279 + assertEquals(list.toString(), expected.length, list.size());
  280 + }
38 281 }
... ...