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.io.IOException; 021import java.lang.invoke.MethodHandle; 022import java.util.Objects; 023import java.util.function.Supplier; 024 025import javax.xml.XMLConstants; 026import javax.xml.parsers.DocumentBuilderFactory; 027import javax.xml.parsers.FactoryConfigurationError; 028import javax.xml.parsers.ParserConfigurationException; 029import javax.xml.transform.ErrorListener; 030import javax.xml.transform.Source; 031import javax.xml.transform.Templates; 032import javax.xml.transform.Transformer; 033import javax.xml.transform.TransformerConfigurationException; 034import javax.xml.transform.TransformerException; 035import javax.xml.transform.TransformerFactory; 036import javax.xml.transform.TransformerFactoryConfigurationError; 037import javax.xml.transform.URIResolver; 038import javax.xml.transform.dom.DOMSource; 039import javax.xml.transform.sax.SAXSource; 040import javax.xml.transform.sax.SAXTransformerFactory; 041import javax.xml.transform.sax.TemplatesHandler; 042import javax.xml.transform.sax.TransformerHandler; 043import javax.xml.transform.stream.StreamSource; 044 045import org.w3c.dom.Document; 046import org.xml.sax.InputSource; 047import org.xml.sax.SAXException; 048import org.xml.sax.XMLFilter; 049import org.xml.sax.XMLReader; 050 051/** 052 * Creates new, secure {@link TransformerFactory} instances. 053 * <p> 054 * Beyond the three universal guarantees on {@link org.apache.commons.xml.secure}: {@code xsl:import}, {@code xsl:include} and {@code document()} URIs are not 055 * resolved. 056 * </p> 057 * <p> 058 * The guarantees govern what the transform reads, not what it writes: an output instruction like {@code xsl:result-document} still writes wherever the 059 * stylesheet directs, so an untrusted stylesheet's output destinations must be restricted outside the library. 060 * </p> 061 * <p> 062 * The guarantees apply to every parser the factory creates internally for the standard {@link TransformerFactory} entry points: stylesheet compilation 063 * ({@link TransformerFactory#newTemplates(javax.xml.transform.Source) newTemplates(Source)}, 064 * {@link TransformerFactory#newTransformer(javax.xml.transform.Source) newTransformer(Source)}) and source-document reading at 065 * {@code Transformer.transform(Source, Result)} time. 066 * </p> 067 * <p> 068 * The {@code href} an {@code xml-stylesheet} processing instruction names is content of the document being scanned, so 069 * {@link TransformerFactory#getAssociatedStylesheet(Source, String, String, String) getAssociatedStylesheet} treats it as any other content-named reference: 070 * install a {@link URIResolver} resolving that href to compile the stylesheet it points at. Without one the returned {@link Source} carries empty content 071 * rather than naming the URI, so compiling it cannot fetch a stylesheet the parsed document chose. 072 * </p> 073 * <p> 074 * The {@link javax.xml.transform.sax.SAXTransformerFactory} extension methods ({@code newTransformerHandler(..)}, {@code newTemplatesHandler()}, 075 * {@code newXMLFilter(..)}), if reachable by casting the returned factory, produce objects carrying the same guarantees. 076 * </p> 077 * <p> 078 * This class is not itself a {@link TransformerFactory}, so it inherits none of the static JAXP factory methods. A caller therefore cannot obtain an unsecured 079 * factory through this class by calling a method such as {@code newDefaultInstance()}. The secure factories are instances of a nested, non-public wrapper 080 * class. 081 * </p> 082 * 083 * @see org.apache.commons.xml.secure 084 */ 085public final class SecureTransformerFactory { 086 087 /** 088 * {@link TransformerFactory} wrapper that rewrites every Source-taking entry point through {@link SecureSAXParserFactory#secure(Source, boolean)} before 089 * delegating. 090 * 091 * <p> 092 * Used by providers whose underlying TrAX implementation pulls a new {@code SAXParserFactory.newInstance()} for any Source that is not already a 093 * {@link SAXSource} carrying its own {@link XMLReader}, and only sets {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING FSP} on the resulting reader. 094 * Wrapping the factory and rewriting the Source upstream guarantees the parse runs through an {@link org.apache.commons.xml.secure}-secured reader instead. 095 * </p> 096 * <p> 097 * Three layers cooperate: 098 * </p> 099 * <ol> 100 * <li>{@link SecureTransformerFactory} rewrites the Source on every entry point that compiles a stylesheet or transforms a one-shot input.</li> 101 * <li>{@link SecureTemplates} returns a {@link SecureTransformer} from {@link Templates#newTransformer()} so runtime source parsing is also covered, and 102 * restores the factory's URIResolver onto the produced Transformer (which the underlying implementation typically does not propagate through 103 * {@code Templates}).</li> 104 * <li>{@link SecureTransformer} rewrites the Source on every {@link Transformer#transform(Source, javax.xml.transform.Result)} call.</li> 105 * </ol> 106 * <p> 107 * The {@link SAXTransformerFactory} extension products ride the same wrappers: {@code newTransformerHandler}/{@code newTemplatesHandler} products are 108 * wrapped ({@link SecureTransformerHandler}, {@link SecureTemplatesHandler}) so the {@link Transformer}/{@link Templates} they expose carry the resolver 109 * floor, and {@code newXMLFilter} returns a {@link SecureXMLFilter} composed from these wrappers instead of the implementation's filter, which would 110 * self-provision an unsecured input reader. 111 * </p> 112 * 113 * <h2>Caveats</h2> 114 * <ul> 115 * <li>A {@link SAXSource} that carries its own {@link XMLReader} is trusted as-is: the caller is expected to supply a secure reader (via 116 * {@link SecureSAXParserFactory#newInstance()}) in that case. The same applies to the SAX events a caller feeds into a handler, and to a parent reader a 117 * caller sets on a returned {@link XMLFilter}. The exception is {@code getAssociatedStylesheet} on an engine that drops the reader (Apache Xalan, and 118 * the JDK's XSLTC on Java 8): there the document is pre-parsed into a DOM instead, since the reader would otherwise be replaced by the engine's own.</li> 119 * </ul> 120 */ 121 private static final class Wrapper extends SAXTransformerFactory { 122 123 /** 124 * Whether the delegate is Apache Xalan (either its interpretive or its XSLTC factory), whose {@code getAssociatedStylesheet} ignores a SAXSource reader. 125 * 126 * @param factory The delegate factory. 127 * @return Whether the delegate is an {@code org.apache.xalan.} implementation. 128 */ 129 private static boolean isXalan(final SAXTransformerFactory factory) { 130 return factory.getClass().getName().startsWith("org.apache.xalan."); 131 } 132 133 /** 134 * Whether the delegate recognizes {@value SecureSAXParserFactory#OVERRIDE_DEFAULT_PARSER}, probed with a same-value {@code setFeature}: 135 * {@code TransformerFactory.getFeature} cannot signal an unrecognized name (it returns {@code false}), while every implementation rejects a 136 * {@code setFeature} for a name it does not support (Xalan with {@link TransformerConfigurationException}, Saxon with its own unchecked exception). 137 * 138 * @param factory The delegate factory. 139 * @return Whether the delegate recognizes the feature. 140 */ 141 private static boolean probeOverrideDefaultParser(final SAXTransformerFactory factory) { 142 try { 143 factory.setFeature(SecureSAXParserFactory.OVERRIDE_DEFAULT_PARSER, 144 factory.getFeature(SecureSAXParserFactory.OVERRIDE_DEFAULT_PARSER)); 145 return true; 146 } catch (final Exception e) { 147 return false; 148 } 149 } 150 151 private static Templates unwrap(final Templates templates) { 152 return templates instanceof SecureTemplates ? ((SecureTemplates) templates).getDelegate() : templates; 153 } 154 155 private final SAXTransformerFactory delegate; 156 157 /** 158 * Empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means the default empty DOM. 159 */ 160 private final Supplier<Source> emptySource; 161 162 private final FallbackIgnoreURIResolver floor; 163 164 /** Whether the delegate recognizes {@value SecureSAXParserFactory#OVERRIDE_DEFAULT_PARSER}; its value is read per created product, like the JDK. */ 165 private final boolean supportsOverrideDefaultParser; 166 167 /** 168 * Constructs a new instance. 169 * 170 * @param delegate The delegate to wrap; must not be {@code null}. 171 * @throws NullPointerException Thrown if {@code delegate} is {@code null}. 172 */ 173 private Wrapper(final SAXTransformerFactory delegate) { 174 this(delegate, null); 175 } 176 177 /** 178 * Constructs a new instance. 179 * 180 * @param delegate The delegate to wrap; must not be {@code null}. 181 * @param emptySource The empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means the 182 * default empty DOM. 183 * @throws NullPointerException Thrown if {@code delegate} is {@code null}. 184 */ 185 private Wrapper(final SAXTransformerFactory delegate, final Supplier<Source> emptySource) { 186 this.delegate = Objects.requireNonNull(delegate, "delegate"); 187 this.emptySource = emptySource; 188 this.supportsOverrideDefaultParser = probeOverrideDefaultParser(delegate); 189 this.floor = new FallbackIgnoreURIResolver(null, emptySource, this::overrideDefaultParser); 190 // Compile-time block for xsl:import/xsl:include and document(); a caller-set resolver is routed through the floor rather than replacing it. 191 delegate.setURIResolver(floor); 192 } 193 194 /** 195 * Routes the href an {@code xml-stylesheet} PI yielded through the floor, so a URI distilled from untrusted content is opted in by the caller's 196 * resolver or resolved to empty like any other content-named reference. 197 * 198 * <p>XSLTC-lineage engines resolve the href during the scan, before they install the factory's {@link URIResolver}, and hand back a live 199 * {@link SAXSource} naming the absolutized URI; compiling it, the one documented use of this method, would then fetch it. Saxon already floors the href 200 * itself and returns an empty source, so flooring here is also what makes the engines agree.</p> 201 * 202 * @param associated The delegate's result; {@code null} when no PI matched. 203 * @param base The system id of the scanned document, the base the href was resolved against. 204 * @return The caller resolver's source for an opted-in href, an empty source otherwise, or {@code null} when no PI matched. 205 * @throws TransformerConfigurationException Thrown if the floor rejects the href, which it does when {@value SecureException#THROW_ON_UNRESOLVED} is set. 206 */ 207 private Source floorAssociated(final Source associated, final String base) throws TransformerConfigurationException { 208 if (associated == null || associated.getSystemId() == null) { 209 // No PI matched, or the engine already floored the href to a source that names no URI. 210 return associated; 211 } 212 try { 213 return floor.resolve(associated.getSystemId(), base); 214 } catch (final TransformerException e) { 215 throw new TransformerConfigurationException("Failed to resolve the associated stylesheet " + associated.getSystemId(), e); 216 } 217 } 218 219 /** 220 * {@inheritDoc} 221 * 222 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 223 * configuration error} or if the implementation is not available or cannot be instantiated. 224 */ 225 @Override 226 public Source getAssociatedStylesheet(final Source source, final String media, final String title, final String charset) 227 throws TransformerConfigurationException { 228 // Xalan's getAssociatedStylesheet drops a SAXSource's reader and self-provisions its own to scan for xml-stylesheet PIs (XALANJ-2849), and the 229 // JDK's XSLTC did the same before 8u162; hand those a DOM so no parser but ours ever sees the document. 230 final Source secure = isXalan(delegate) || JAVA_8 ? secureSourceToDom(source) : SecureSAXParserFactory.secure(source, overrideDefaultParser()); 231 return floorAssociated(delegate.getAssociatedStylesheet(secure, media, title, charset), secure.getSystemId()); 232 } 233 234 @Override 235 public Object getAttribute(final String name) { 236 return delegate.getAttribute(name); 237 } 238 239 @Override 240 public ErrorListener getErrorListener() { 241 return delegate.getErrorListener(); 242 } 243 244 @Override 245 public boolean getFeature(final String name) { 246 return delegate.getFeature(name); 247 } 248 249 @Override 250 public URIResolver getURIResolver() { 251 return floor.getDelegate(); 252 } 253 254 /** 255 * {@inheritDoc} 256 * 257 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 258 * configuration error} or if the implementation is not available or cannot be instantiated. 259 */ 260 @Override 261 public Templates newTemplates(final Source source) throws TransformerConfigurationException { 262 // newTemplates() should never return null for a specification-compliant factory. 263 final Templates templates = delegate.newTemplates(SecureSAXParserFactory.secure(source, overrideDefaultParser())); 264 return templates == null ? null : new SecureTemplates(templates, getURIResolver(), emptySource, overrideDefaultParser()); 265 } 266 267 @Override 268 public TemplatesHandler newTemplatesHandler() throws TransformerConfigurationException { 269 // newTemplatesHandler() should never return null for a specification-compliant factory. 270 final TemplatesHandler handler = delegate.newTemplatesHandler(); 271 return handler == null ? null : new SecureTemplatesHandler(handler, getURIResolver(), emptySource, overrideDefaultParser()); 272 } 273 274 @Override 275 public Transformer newTransformer() throws TransformerConfigurationException { 276 // Identity transformer: still parses runtime sources, so wrap it to secure Transformer.transform(Source, Result). 277 // newTransformer() should never return null for a specification-compliant factory. 278 final Transformer transformer = delegate.newTransformer(); 279 return transformer == null ? null : new SecureTransformer(transformer, getURIResolver(), emptySource, overrideDefaultParser()); 280 } 281 282 /** 283 * {@inheritDoc} 284 * 285 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 286 * configuration error} or if the implementation is not available or cannot be instantiated. 287 */ 288 @Override 289 public Transformer newTransformer(final Source source) throws TransformerConfigurationException { 290 // newTransformer() should never return null for a specification-compliant factory. 291 final Transformer transformer = delegate.newTransformer(SecureSAXParserFactory.secure(source, overrideDefaultParser())); 292 return transformer == null ? null : new SecureTransformer(transformer, getURIResolver(), emptySource, overrideDefaultParser()); 293 } 294 295 @Override 296 public TransformerHandler newTransformerHandler() throws TransformerConfigurationException { 297 return secure(delegate.newTransformerHandler()); 298 } 299 300 /** 301 * {@inheritDoc} 302 * 303 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 304 * configuration error} or if the implementation is not available or cannot be instantiated. 305 */ 306 @Override 307 public TransformerHandler newTransformerHandler(final Source source) throws TransformerConfigurationException { 308 return secure(delegate.newTransformerHandler(SecureSAXParserFactory.secure(source, overrideDefaultParser()))); 309 } 310 311 @Override 312 public TransformerHandler newTransformerHandler(final Templates templates) throws TransformerConfigurationException { 313 // Implementations cast templates.newTransformer() to their own Transformer type, so hand them the wrapped implementation Templates, not the wrapper. 314 return secure(delegate.newTransformerHandler(unwrap(templates))); 315 } 316 317 /** 318 * {@inheritDoc} 319 * 320 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 321 * configuration error} or if the implementation is not available or cannot be instantiated. 322 */ 323 @Override 324 public XMLFilter newXMLFilter(final Source source) throws TransformerConfigurationException { 325 final Templates templates = newTemplates(source); 326 return templates == null ? null : new SecureXMLFilter((SecureTemplates) templates); 327 } 328 329 @Override 330 public XMLFilter newXMLFilter(final Templates templates) throws TransformerConfigurationException { 331 return new SecureXMLFilter(templates instanceof SecureTemplates ? (SecureTemplates) templates 332 : new SecureTemplates(templates, getURIResolver(), emptySource, overrideDefaultParser())); 333 } 334 335 /** 336 * Tests whether parsers should be instantiated via {@code newInstance()} instead of {@code newDefaultInstance()}. 337 * 338 * <p>The JDK implementation of {@link TransformerFactory} uses the JDK parsers while {@value SecureSAXParserFactory#OVERRIDE_DEFAULT_PARSER} is unset 339 * or {@code false}.</p> 340 * 341 * @return {@code true} if parsers should be created via {@code newInstance()}. 342 */ 343 private boolean overrideDefaultParser() { 344 return !supportsOverrideDefaultParser || delegate.getFeature(SecureSAXParserFactory.OVERRIDE_DEFAULT_PARSER); 345 } 346 347 private TransformerHandler secure(final TransformerHandler handler) { 348 return handler == null ? null : new SecureTransformerHandler(handler, getURIResolver(), emptySource, overrideDefaultParser()); 349 } 350 351 /** 352 * Parses a stream or SAX source into a DOM through a secure, namespace-aware {@link javax.xml.parsers.DocumentBuilder} and returns a {@link DOMSource} 353 * carrying its system id, so the consumer walks the tree instead of provisioning its own reader. Any other source is left to 354 * {@link SecureSAXParserFactory#secure(Source, boolean)}. 355 * 356 * <p>A {@link SAXSource} carrying the caller's own reader is pre-parsed here too, unlike everywhere else in this class: an engine that reaches this 357 * method drops that reader anyway, so honoring it is not among the options — the choice is only between this parse and the engine's unsecured one.</p> 358 * 359 * @param source The source to scan for an associated stylesheet. 360 * @return A {@link DOMSource} for a stream or SAX source, otherwise the result of {@link SecureSAXParserFactory#secure(Source, boolean)}. 361 * @throws TransformerConfigurationException Thrown if the source cannot be parsed. 362 * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service 363 * configuration error} or if the implementation is not available or cannot be instantiated. 364 * @throws SecureException Thrown if a (non-Android) factory cannot support the secure processing feature {@link XMLConstants#FEATURE_SECURE_PROCESSING}. 365 */ 366 private Source secureSourceToDom(final Source source) throws TransformerConfigurationException { 367 if (source instanceof StreamSource || source instanceof SAXSource) { 368 final InputSource inputSource = SAXSource.sourceToInputSource(source); 369 if (inputSource != null) { 370 try { 371 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newNSInstance(overrideDefaultParser()); 372 final Document document = factory.newDocumentBuilder().parse(inputSource); 373 return new DOMSource(document, inputSource.getSystemId()); 374 } catch (final ParserConfigurationException | SAXException | IOException e) { 375 throw new TransformerConfigurationException("Failed to parse the source for associated-stylesheet lookup", e); 376 } 377 } 378 } 379 return SecureSAXParserFactory.secure(source, overrideDefaultParser()); 380 } 381 382 @Override 383 public void setAttribute(final String name, final Object value) { 384 delegate.setAttribute(name, value); 385 } 386 387 @Override 388 public void setErrorListener(final ErrorListener listener) { 389 delegate.setErrorListener(listener); 390 } 391 392 393 @Override 394 public void setFeature(final String name, final boolean value) throws TransformerConfigurationException { 395 delegate.setFeature(name, value); 396 } 397 398 @Override 399 public void setURIResolver(final URIResolver resolver) { 400 floor.setDelegate(resolver); 401 } 402 } 403 404 /** Class name of the JDK's built-in default implementation, the Java 8 fallback for {@link #newDefaultInstance()}. */ 405 private static final String JDK_TRANSFORMER_FACTORY = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"; 406 407 private static final MethodHandle MH_newDefaultInstance = MethodHandleFactory.findStatic(TransformerFactory.class, "newDefaultInstance"); 408 409 /** 410 * {@code true} on Java 8, detected by the absence of {@code TransformerFactory.newDefaultInstance()}, which arrived in Java 9. 411 * <p> 412 * The JDK's XSLTC only began honoring the {@link XMLReader} carried by a {@link SAXSource} in {@code getAssociatedStylesheet} in 8u162; through 8u152 it 413 * provisions its own parser, exactly as Apache Xalan does. Java 8 as a whole is used as the boundary rather than the patch level: the two are 414 * indistinguishable through any API, and such an old runtime has already chosen correctness of configuration over the cost of a DOM pre-parse. 415 * </p> 416 */ 417 private static final boolean JAVA_8 = MH_newDefaultInstance == null; 418 419 /** 420 * Returns a new, secure {@link TransformerFactory} of the system-default implementation. 421 * <p> 422 * Obtained from {@code TransformerFactory.newDefaultInstance()} where the platform provides it (Java 9 or later), and by instantiating the JDK's built-in 423 * implementation directly on Java 8. 424 * </p> 425 * 426 * @return A secure factory. 427 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 428 * @throws TransformerFactoryConfigurationError Thrown if the running platform provides neither {@code newDefaultInstance()} nor the JDK's built-in 429 * implementation (for example Android). 430 */ 431 public static TransformerFactory newDefaultInstance() { 432 if (MH_newDefaultInstance != null) { 433 return secure(MethodHandleFactory.invokeExact(() -> (TransformerFactory) MH_newDefaultInstance.invokeExact(), TransformerFactoryConfigurationError.class)); 434 } 435 // Java 8: the method does not exist; instantiate the JDK's built-in default by its class name instead. Where that class does not exist either (for 436 // example Android), the lookup miss surfaces as TransformerFactoryConfigurationError, like any newInstance miss. 437 return newInstance(JDK_TRANSFORMER_FACTORY, null); 438 } 439 440 /** 441 * Returns a new, secure {@link TransformerFactory}. 442 * 443 * @return A secure factory. 444 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 445 */ 446 public static TransformerFactory newInstance() { 447 return secure(TransformerFactory.newInstance()); 448 } 449 450 /** 451 * Returns a new, secure {@link TransformerFactory} of the given implementation class. 452 * 453 * @param factoryClassName The fully qualified class name of the {@link TransformerFactory} implementation. 454 * @param classLoader The class loader used to load the factory class; {@code null} means the current thread's context class loader. 455 * @return A secure factory. 456 * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation. 457 * @throws TransformerFactoryConfigurationError Thrown if {@code factoryClassName} is {@code null} or the factory class cannot be loaded or instantiated. 458 */ 459 public static TransformerFactory newInstance(final String factoryClassName, final ClassLoader classLoader) { 460 return secure(TransformerFactory.newInstance(factoryClassName, classLoader)); 461 } 462 463 /** 464 * Capability-driven secure for any {@link TransformerFactory} on the classpath. 465 * 466 * <p> 467 * Rather than branching on the implementation class, this method probes what the factory supports and adapts: 468 * </p> 469 * <ul> 470 * <li><strong>Saxon</strong> ({@code net.sf.saxon}): recognized by package prefix and handed to {@link SaxonProvider#configure(TransformerFactory)} for the 471 * channels the standard JAXP knobs cannot close (reflection-based extension functions, the collection finder, the internal SAX parser). It is then 472 * wrapped like every other implementation to install the {@link FallbackIgnoreURIResolver} floor; the only 473 * difference is the empty-{@link Source} shape the floor returns, {@code EmptySource} for Saxon rather than the default empty DOM document.</li> 474 * <li><strong>FSP</strong> ({@link XMLConstants#FEATURE_SECURE_PROCESSING}): required. On XSLTC it enables the runtime evaluator limits; on Xalan it disables 475 * reflection-based extension functions.</li> 476 * <li><strong>{@link FallbackIgnoreURIResolver} floor</strong>: required. An ignore-all {@link URIResolver} floor, installed by 477 * the nested wrapper and carried onto every produced {@link Transformer}, resolves {@code xsl:import}/{@code xsl:include} at compile 478 * time and {@code document()} at runtime to an empty document, the one channel both XSLTC and Xalan route through. A caller-set {@link URIResolver} is 479 * routed through the floor rather than replacing it, so a caller can opt a specific URI in but cannot reopen the fetch.</li> 480 * <li><strong>The nested wrapper</strong>: required. Both implementations fall back to {@code SAXParserFactory.newInstance()} to parse a 481 * stylesheet or source document that does not carry its own reader, and only set FSP on it; wrapping the factory rewrites every {@link Source} through an 482 * {@link org.apache.commons.xml.secure}-secured reader instead.</li> 483 * </ul> 484 * 485 * @param factory The factory to secure; never {@code null}. 486 * @return a secure factory. 487 */ 488 static TransformerFactory secure(final TransformerFactory factory) { 489 // Required: enables secure processing (XSLTC runtime limits; Xalan's extension-function block). 490 setFeature(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true); 491 if (SaxonProvider.isSaxon(factory.getClass())) { 492 // Saxon keeps its vendor Configuration for the channels JAXP cannot close, 493 // then goes through the same wrapper as every other implementation for the URIResolver floor; 494 // EmptySource is the empty-source shape Saxon's consumers expect. 495 return new Wrapper((SAXTransformerFactory) SaxonProvider.configure(factory), SaxonProvider.emptySourceSupplier()); 496 } 497 // Required: source/stylesheet parsing provisions its own SAX reader otherwise; the wrapper routes every Source through a secure one and installs the 498 // ignore-all URIResolver floor (blocking xsl:import/include at compile time and document() at runtime) that a caller-set resolver cannot remove. 499 return new Wrapper((SAXTransformerFactory) factory); 500 } 501 502 private static void setFeature(final TransformerFactory factory, final String feature, final boolean value) { 503 try { 504 factory.setFeature(feature, value); 505 } catch (final Exception e) { 506 throw SecureException.featureFailed(feature, factory, e); 507 } 508 } 509 510 private SecureTransformerFactory() { 511 // static only 512 } 513}