001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.xml.secure; 019 020import java.lang.invoke.MethodHandle; 021import java.util.Objects; 022 023import javax.xml.XMLConstants; 024import javax.xml.parsers.FactoryConfigurationError; 025import javax.xml.parsers.ParserConfigurationException; 026import javax.xml.parsers.SAXParser; 027import javax.xml.parsers.SAXParserFactory; 028import javax.xml.transform.Source; 029import javax.xml.transform.sax.SAXSource; 030import javax.xml.transform.stream.StreamSource; 031import javax.xml.validation.Schema; 032 033import org.xml.sax.ContentHandler; 034import org.xml.sax.EntityResolver; 035import org.xml.sax.InputSource; 036import org.xml.sax.SAXException; 037import org.xml.sax.SAXNotRecognizedException; 038import org.xml.sax.SAXNotSupportedException; 039import org.xml.sax.XMLReader; 040 041/** 042 * Creates new, secure {@link SAXParserFactory} instances. 043 * <p> 044 * Beyond the three universal guarantees on {@link org.apache.commons.xml.secure}, XInclude resolution is denied by default. When 045 * {@link SAXParserFactory#setXIncludeAware(boolean) setXIncludeAware(true)} is called on the returned factory, the parser will process {@code xi:include} 046 * elements but every external resource lookup is rejected. To permit specific trusted resources, install an {@link org.xml.sax.EntityResolver EntityResolver} 047 * on the {@link org.xml.sax.XMLReader} that allow-lists them; any href the resolver does not explicitly allow stays blocked. 048 * </p> 049 * <p> 050 * This class is not itself a {@link SAXParserFactory}, so it inherits none of the static JAXP factory methods. A caller therefore cannot obtain an unsecured 051 * factory through this class by calling a method such as {@code newDefaultInstance()}. The secure factories are instances of a nested, non-public wrapper 052 * class. 053 * </p> 054 * 055 * @see org.apache.commons.xml.secure 056 */ 057public final class SecureSAXParserFactory { 058 059 /** 060 * {@link SecureXMLReader} for Android's {@code org.apache.harmony.xml.ExpatReader} that additionally surfaces its {@code namespace-prefixes} limitation at 061 * configuration time. 062 * 063 * <p> 064 * ExpatReader does not actually support the {@code namespace-prefixes} feature: enabling it is accepted by {@code setFeature} but fails later, during 065 * {@code parse}, with a {@link SAXNotSupportedException}. Reporting the rejection eagerly from {@link #setFeature(String, boolean)} lets consumers that 066 * probe the feature, such as Xalan's identity transformer, catch the exception and fall back instead of failing the whole parse. 067 * </p> 068 */ 069 static final class SecureExpatXMLReader extends SecureXMLReader { 070 071 private static final String NAMESPACE_PREFIXES_FEATURE = "http://xml.org/sax/features/namespace-prefixes"; 072 073 SecureExpatXMLReader(final XMLReader delegate) { 074 super(delegate); 075 } 076 077 @Override 078 public void setFeature(final String name, final boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { 079 if (value && NAMESPACE_PREFIXES_FEATURE.equals(name)) { 080 throw new SAXNotSupportedException("ExpatReader does not support enabling the '" + NAMESPACE_PREFIXES_FEATURE + "' feature"); 081 } 082 super.setFeature(name, value); 083 } 084 } 085 /** 086 * Universal SAX factory wrapper that funnels every produced parser through {@link SecureSAXParserFactory#secure(XMLReader)}. 087 * <p> 088 * {@link SAXParserFactory} exposes only a feature API and no property API, so the per-parse secure configuration (limits, entity blocking, 089 * implementation-specific fixups) has to run on each {@link XMLReader} the factory produces. This wrapper returns a {@link SecureSAXParser}, which applies 090 * that securing lazily to both the SAX 2 {@link XMLReader} and the SAX 1 {@link org.xml.sax.Parser} it exposes. 091 * </p> 092 */ 093 private static final class Wrapper extends SAXParserFactory { 094 095 private final SAXParserFactory delegate; 096 097 /** 098 * Constructs a new instance. 099 * 100 * @param delegate The delegate to wrap; must not be {@code null}. 101 * @throws NullPointerException Thrown if {@code delegate} is {@code null}. 102 */ 103 private Wrapper(final SAXParserFactory delegate) { 104 this.delegate = Objects.requireNonNull(delegate, "delegate"); 105 } 106 107 @Override 108 public boolean getFeature(final String name) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException { 109 return delegate.getFeature(name); 110 } 111 112 @Override 113 public Schema getSchema() { 114 return delegate.getSchema(); 115 } 116 117 @Override 118 public boolean isNamespaceAware() { 119 return delegate.isNamespaceAware(); 120 } 121 122 @Override 123 public boolean isValidating() { 124 return delegate.isValidating(); 125 } 126 127 @Override 128 public boolean isXIncludeAware() { 129 return delegate.isXIncludeAware(); 130 } 131 132 @Override 133 public SAXParser newSAXParser() throws ParserConfigurationException, SAXException { 134 return new SecureSAXParser(delegate.newSAXParser()); 135 } 136 137 @Override 138 public void setFeature(final String name, final boolean value) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException { 139 delegate.setFeature(name, value); 140 } 141 142 @Override 143 public void setNamespaceAware(final boolean awareness) { 144 delegate.setNamespaceAware(awareness); 145 } 146 147 @Override 148 public void setSchema(final Schema schema) { 149 delegate.setSchema(schema); 150 } 151 152 @Override 153 public void setValidating(final boolean validating) { 154 delegate.setValidating(validating); 155 } 156 157 @Override 158 public void setXIncludeAware(final boolean state) { 159 delegate.setXIncludeAware(state); 160 } 161 } 162 /** 163 * Class name of Android's Expat-backed {@link XMLReader}. 164 */ 165 private static final String ANDROID_EXPAT_READER = "org.apache.harmony.xml.ExpatReader"; 166 167 /** 168 * Class name of Android's Harmony-based {@link SAXParserFactory}, backed by the native Expat parser. 169 */ 170 private static final String ANDROID_SAX_PARSER_FACTORY = "org.apache.harmony.xml.parsers.SAXParserFactoryImpl"; 171 172 /** 173 * Class name of the JDK's built-in default implementation, the Java 8 fallback for {@link #newDefaultInstance()}. 174 */ 175 static final String JDK_SAX_PARSER_FACTORY = "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"; 176 177 /** 178 * The JDK feature governing whether an implementation's internal parser lookup may resolve a third-party parser. The secure wrappers parse every source 179 * themselves, so instead of configuring the implementation the TrAX, XPath and schema wrappers read this feature and pick the rewrite parser accordingly. 180 */ 181 static final String OVERRIDE_DEFAULT_PARSER = "jdk.xml.overrideDefaultParser"; 182 183 /** 184 * System property naming the {@link SAXParserFactory} implementation, the JDK's own mechanism for reconfiguring the default parser. 185 */ 186 private static final String SAX_FACTORY_ID = "javax.xml.parsers.SAXParserFactory"; 187 188 private static final MethodHandle MH_newDefaultInstance = MethodHandleFactory.findStatic(SAXParserFactory.class, "newDefaultInstance"); 189 190 /** 191 * Enables namespace awareness on the given factory; the {@code NSInstance} counterpart of each factory method routes its result through here. 192 * 193 * @param factory The factory to configure; never {@code null}. 194 * @return The given factory, namespace-aware. 195 */ 196 private static SAXParserFactory makeNSAware(final SAXParserFactory factory) { 197 factory.setNamespaceAware(true); 198 return factory; 199 } 200 201 /** 202 * Returns a new, secure {@link SAXParserFactory} of the system-default implementation. 203 * <p> 204 * Obtained from {@code SAXParserFactory.newDefaultInstance()} where the platform provides it (Java 9 or later), 205 * by instantiating the JDK's built-in implementation directly on Java 8, 206 * and by the standard {@link #newInstance()} lookup where the platform provides neither 207 * (for example, Android, whose lookup is itself pinned to the platform implementation). 208 * </p> 209 * 210 * @return A secure factory. 211 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 212 * @throws FactoryConfigurationError Thrown from the {@link #newInstance()} lookup this method falls back to on a platform that provides neither 213 * {@code newDefaultInstance()} nor the JDK's built-in implementation (for example, Android). 214 */ 215 public static SAXParserFactory newDefaultInstance() { 216 if (MH_newDefaultInstance != null) { 217 return secure(MethodHandleFactory.invokeExact(() -> (SAXParserFactory) MH_newDefaultInstance.invokeExact(), FactoryConfigurationError.class)); 218 } 219 try { 220 // Java 8: the method does not exist; instantiate the JDK's built-in default by its class name instead. 221 // Load it from the system class loader: the context class loader may not see it (for example an OSGi bundle class loader). 222 return newInstance(JDK_SAX_PARSER_FACTORY, ClassLoader.getSystemClassLoader()); 223 } catch (final FactoryConfigurationError e) { 224 // Neither exists (for example, Android): degrade to the regular lookup, which such platforms pin to their built-in parser. 225 return newInstance(); 226 } 227 } 228 229 /** 230 * Returns a new, secure, namespace-aware {@link SAXParserFactory} of the system-default implementation, enabling namespace awareness on 231 * {@link #newDefaultInstance()}, the behavior {@code SAXParserFactory.newDefaultNSInstance()} (Java 13 or later) is specified to have. 232 * 233 * @return A secure, namespace-aware factory. 234 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 235 * @throws FactoryConfigurationError Thrown from the {@link #newInstance()} lookup {@link #newDefaultInstance()} falls back to on a platform that provides 236 * neither {@code newDefaultInstance()} nor the JDK's built-in implementation (for example, Android). 237 */ 238 public static SAXParserFactory newDefaultNSInstance() { 239 return makeNSAware(newDefaultInstance()); 240 } 241 242 /** 243 * Returns a new, secure {@link SAXParserFactory}. 244 * 245 * @return A secure factory. 246 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 247 * @throws FactoryConfigurationError Thrown from {@link SAXParserFactory} in case of a {@link java.util.ServiceConfigurationError service configuration 248 * error} or if the implementation is not available or cannot be instantiated. 249 */ 250 public static SAXParserFactory newInstance() { 251 return secure(SAXParserFactory.newInstance()); 252 } 253 254 /** 255 * Returns a new, secure {@link SAXParserFactory} of the given implementation class. 256 * 257 * @param factoryClassName The fully qualified class name of the {@link SAXParserFactory} implementation. 258 * @param classLoader The class loader used to load the factory class; {@code null} means the current thread's context class loader. 259 * @return A secure factory. 260 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 261 * @throws FactoryConfigurationError Thrown if {@code factoryClassName} is {@code null} or the factory class cannot be loaded or instantiated. 262 */ 263 public static SAXParserFactory newInstance(final String factoryClassName, final ClassLoader classLoader) { 264 return secure(SAXParserFactory.newInstance(factoryClassName, classLoader)); 265 } 266 267 /** 268 * Returns a new, secure, namespace-aware {@link SAXParserFactory}, enabling namespace awareness on {@link #newInstance()}, the behavior 269 * {@code SAXParserFactory.newNSInstance()} (Java 13 or later) is specified to have. 270 * 271 * @return A secure, namespace-aware factory. 272 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 273 * @throws FactoryConfigurationError Thrown from {@link SAXParserFactory} in case of a {@link java.util.ServiceConfigurationError service configuration 274 * error} or if the implementation is not available or cannot be instantiated. 275 */ 276 public static SAXParserFactory newNSInstance() { 277 return makeNSAware(newInstance()); 278 } 279 280 /** 281 * Returns the secure, namespace-aware factory the Source-rewriting wrappers parse with. 282 * <p> 283 * While {@code overrideDefaultParser} is {@code false}, the factory is the JDK's "default parser" factory, determined the way the JDK itself determines it: 284 * the built-in parser, unless the {@value #SAX_FACTORY_ID} system property is set. That property is the JDK's own mechanism for reconfiguring the default 285 * parser, so it is honored through the standard lookup rather than bypassed. 286 * </p> 287 * 288 * @param overrideDefaultParser whether {@value #OVERRIDE_DEFAULT_PARSER} on the originating factory asks to override the JDK's default parser. 289 * @return A secure, namespace-aware factory. 290 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 291 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service configuration error} or if the 292 * implementation is not available or cannot be instantiated. 293 */ 294 static SAXParserFactory newNSInstance(final boolean overrideDefaultParser) { 295 return overrideDefaultParser || System.getProperty(SAX_FACTORY_ID) != null ? newNSInstance() : newDefaultNSInstance(); 296 } 297 298 /** 299 * Returns a new, secure, namespace-aware {@link SAXParserFactory} of the given implementation class, enabling namespace awareness on 300 * {@link #newInstance(String, ClassLoader)}, the behavior {@code SAXParserFactory.newNSInstance(String, ClassLoader)} (Java 13 or later) is specified to 301 * have. 302 * 303 * @param factoryClassName The fully qualified class name of the {@link SAXParserFactory} implementation. 304 * @param classLoader The class loader used to load the factory class; {@code null} means the current thread's context class loader. 305 * @return A secure, namespace-aware factory. 306 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 307 * @throws FactoryConfigurationError Thrown if {@code factoryClassName} is {@code null} or the factory class cannot be loaded or instantiated. 308 */ 309 public static SAXParserFactory newNSInstance(final String factoryClassName, final ClassLoader classLoader) { 310 return makeNSAware(newInstance(factoryClassName, classLoader)); 311 } 312 313 /** 314 * Creates a new, secure, namespace-aware {@link SAXParser} from {@link #newNSInstance()}. 315 * <p> 316 * No factory is cached: each call configures a fresh one. To parse many documents, keep the returned parser and call {@link SAXParser#reset()} between 317 * documents. Reusing the parser saves more than caching the factory would, and {@code reset()} costs next to nothing while keeping handler state from 318 * leaking between parses. A parser is not thread-safe, so reuse it within one thread. 319 * </p> 320 * 321 * @return A secure, namespace-aware parser. 322 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation, or if the implementation cannot 323 * create a parser. 324 * @throws FactoryConfigurationError Thrown from {@link SAXParserFactory} in case of a {@link java.util.ServiceConfigurationError service configuration 325 * error} or if the implementation is not available or cannot be instantiated. 326 * @since 1.1.0 327 */ 328 public static SAXParser newNSSAXParser() { 329 try { 330 return newNSInstance().newSAXParser(); 331 } catch (final ParserConfigurationException | SAXException e) { 332 // Implementations reject settings when they are set on the factory, not here: a failure means a broken environment. 333 throw SecureException.creationFailed(SAXParser.class, e); 334 } 335 } 336 337 /** 338 * Creates a new, secure, namespace-aware {@link XMLReader} from {@link #newNSInstance()}, with no content handler registered. 339 * <p> 340 * No factory is cached: each call configures a fresh one. To parse many documents, keep the returned reader and parse each document with it. Reusing the 341 * reader saves more than caching the factory would. Handlers set on the reader stay set between parses, and a reader is not thread-safe, so reuse it within 342 * one thread. 343 * </p> 344 * 345 * @return A secure, namespace-aware reader. 346 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation, or if the implementation cannot 347 * create a reader. 348 * @throws FactoryConfigurationError Thrown from {@link SAXParserFactory} in case of a {@link java.util.ServiceConfigurationError service configuration 349 * error} or if the implementation is not available or cannot be instantiated. 350 * @since 1.1.0 351 */ 352 public static XMLReader newNSXMLReader() { 353 return newNSXMLReader(null); 354 } 355 356 /** 357 * Creates a new, secure, namespace-aware {@link XMLReader} from {@link #newNSInstance()}. 358 * <p> 359 * No factory is cached: each call configures a fresh one. To parse many documents, keep the returned reader and parse each document with it. Reusing the 360 * reader saves more than caching the factory would. Handlers set on the reader stay set between parses, and a reader is not thread-safe, so reuse it within 361 * one thread. 362 * </p> 363 * 364 * @param handler The content handler to register on the reader, or {@code null} to register none. 365 * @return A secure, namespace-aware reader. 366 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation, or if the implementation cannot 367 * create a reader. 368 * @throws FactoryConfigurationError Thrown from {@link SAXParserFactory} in case of a {@link java.util.ServiceConfigurationError service configuration 369 * error} or if the implementation is not available or cannot be instantiated. 370 * @since 1.1.0 371 */ 372 public static XMLReader newNSXMLReader(final ContentHandler handler) { 373 return newXMLReader(newNSInstance(), handler); 374 } 375 376 /** 377 * Creates a new secure, namespace-aware {@link XMLReader} for the TrAX, XPath and schema wrappers to parse sources with, from the factory 378 * {@link #newNSInstance(boolean)} selects. 379 * 380 * @param overrideDefaultParser whether {@value #OVERRIDE_DEFAULT_PARSER} on the originating factory asks to override the JDK's default parser. 381 * @return a secure reader. 382 * @throws IllegalStateException Thrown if the underlying implementation cannot provide a secure reader; providing one is a routine capability of every 383 * supported implementation, so a failure signals a broken environment, not a per-parse condition. 384 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 385 * configuration error} or if the implementation is not available or cannot be instantiated. 386 */ 387 static XMLReader newXMLReader(final boolean overrideDefaultParser) { 388 return newXMLReader(newNSInstance(overrideDefaultParser), null); 389 } 390 391 /** 392 * Creates a new {@link XMLReader} from the given secure factory. 393 * 394 * @param factory The secure factory; never {@code null}. 395 * @param handler The content handler to register on the reader, or {@code null} to register none. 396 * @return A secure reader. 397 * @throws IllegalStateException Thrown if the factory cannot create a reader. 398 */ 399 private static XMLReader newXMLReader(final SAXParserFactory factory, final ContentHandler handler) { 400 final XMLReader reader; 401 try { 402 reader = factory.newSAXParser().getXMLReader(); 403 } catch (final ParserConfigurationException | SAXException e) { 404 // Implementations reject settings when they are set on the factory, not here: a failure means a broken environment. 405 throw SecureException.creationFailed(XMLReader.class, e); 406 } 407 if (handler != null) { 408 reader.setContentHandler(handler); 409 } 410 return reader; 411 } 412 413 /** 414 * Applies capability-driven secure settings to any {@link SAXParserFactory} on the classpath. 415 * 416 * <p> 417 * Rather than branching on the implementation class, this method probes what the factory supports and adapts. Because 418 * {@link SAXParserFactory} exposes only a feature API and no property API, the per-parse configuration runs on each {@link XMLReader} the factory produces, 419 * funneled through the nested wrapper into {@link #secure(XMLReader)}: 420 * </p> 421 * <ul> 422 * <li><strong>Android</strong> (Harmony / Expat): {@link XMLConstants#FEATURE_SECURE_PROCESSING FSP} and the JAXP 1.5 {@code ACCESS_EXTERNAL_*} properties 423 * are not recognized, and libexpat enforces its own Billion Laughs check, so neither is applied. Two fixups are still needed: an ignore-all resolver 424 * (Expat ignores external fetches silently when no resolver is set; the floor keeps that behavior non-bypassable, resolving anything unresolved to 425 * empty), and a {@link SecureExpatXMLReader} so the unsupported {@code namespace-prefixes} feature is rejected at 426 * configuration time rather than mid-parse.</li> 427 * <li><strong>FSP</strong>: required on every other reader. It switches on the implementation's built-in security manager, which is what carries the 428 * processing limits.</li> 429 * <li><strong>Ignore-all resolver floor</strong>: every reader is wrapped in a {@link SecureXMLReader} that keeps an ignore-all {@link EntityResolver} 430 * floor. 431 * That floor blocks external DTD, entity, schema and {@code xi:include} fetches in one place: the stock JDK's XInclude processor ignores 432 * {@code ACCESS_EXTERNAL_*} and consults the {@link EntityResolver} instead, so no {@code ACCESS_EXTERNAL_*} properties are needed here. A caller can 433 * chain its own resolver onto the floor to allow-list resources, but cannot remove it.</li> 434 * </ul> 435 * 436 * @param factory The factory to secure; never {@code null}. 437 * @return a secure factory. 438 */ 439 static SAXParserFactory secure(final SAXParserFactory factory) { 440 // Required: enables the implementation's security manager, which carries the limits. Android's Expat rejects FSP, so it is skipped there. 441 if (!ANDROID_SAX_PARSER_FACTORY.equals(factory.getClass().getName())) { 442 setFeature(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true); 443 } 444 // The per-parse securing (limits, entity blocking, Android fixups) lives in secure(XMLReader) because SAXParserFactory has no property API. 445 return new Wrapper(factory); 446 } 447 448 /** 449 * Rewrites a {@link Source} so that any SAX parsing it triggers runs through a secure {@link XMLReader}. 450 * <p> 451 * Only a {@link StreamSource} or a {@link SAXSource} without a reader is enriched with a secure, namespace-aware reader; other source kinds are returned 452 * as-is. Used by the TrAX and schema wrappers to route every source they parse through the secure SAX path. 453 * </p> 454 * 455 * @param source The source to secure; never {@code null}. 456 * @param overrideDefaultParser whether {@value #OVERRIDE_DEFAULT_PARSER} on the originating factory asks to override the JDK's default parser. 457 * @return a secure source. 458 * @throws IllegalStateException Thrown if the underlying implementation cannot provide a secure reader. 459 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 460 * configuration error} or if the implementation is not available or cannot be instantiated. 461 */ 462 static Source secure(final Source source, final boolean overrideDefaultParser) { 463 if (source instanceof StreamSource || source instanceof SAXSource && ((SAXSource) source).getXMLReader() == null) { 464 final InputSource inputSource = SAXSource.sourceToInputSource(source); 465 return inputSource == null ? source : new SAXSource(newXMLReader(overrideDefaultParser), inputSource); 466 } 467 return source; 468 } 469 470 /** 471 * Secures an existing {@link XMLReader}. 472 * 473 * @param reader The reader to secure; never {@code null}. 474 * @return A secure reader. 475 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 476 */ 477 static XMLReader secure(final XMLReader reader) { 478 if (reader instanceof SecureXMLReader) { 479 // Already secure (for example, a reader from a secure factory passed back through secure(XMLReader)); the floor is already in place. 480 return reader; 481 } 482 if (ANDROID_EXPAT_READER.equals(reader.getClass().getName())) { 483 // Expat ignores external fetches when no resolver is set; the ignore-all floor keeps that behavior non-bypassable (routing a caller-set resolver, 484 // including SAXParser.parse's handler, through it and resolving anything unresolved to empty) and, via SecureExpatXMLReader, rejects the 485 // unsupported namespace-prefixes feature eagerly rather than mid-parse. 486 return new SecureExpatXMLReader(reader); 487 } 488 // Required: enables the JDK XMLSecurityManager / Xerces SecurityManager limits. 489 setFeature(reader, XMLConstants.FEATURE_SECURE_PROCESSING, true); 490 // Required: SecureXMLReader installs an ignore-all EntityResolver floor on the reader. 491 // That floor blocks external DTD, entity, schema and xi:include fetches in one place: no ACCESS_EXTERNAL_* properties are needed here. 492 // Callers can chain their resolvers, but not override the floor. 493 return new SecureXMLReader(reader); 494 } 495 496 private static void setFeature(final SAXParserFactory factory, final String feature, final boolean value) { 497 try { 498 factory.setFeature(feature, value); 499 } catch (final Exception e) { 500 throw SecureException.featureFailed(feature, factory, e); 501 } 502 } 503 504 private static void setFeature(final XMLReader reader, final String feature, final boolean value) { 505 try { 506 reader.setFeature(feature, value); 507 } catch (final Exception e) { 508 throw SecureException.featureFailed(feature, reader, e); 509 } 510 } 511 512 private SecureSAXParserFactory() { 513 // static only 514 } 515}