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         * Tests whether the delegate is Apache Xalan (either its interpretive or its XSLTC factory), whose {@code getAssociatedStylesheet} ignores a SAXSource
125         * reader.
126         *
127         * @param factory The delegate factory.
128         * @return Whether the delegate is an {@code org.apache.xalan.} implementation.
129         */
130        private static boolean isXalan(final SAXTransformerFactory factory) {
131            return factory.getClass().getName().startsWith("org.apache.xalan.");
132        }
133
134        /**
135         * Whether the delegate recognizes {@value SecureSAXParserFactory#OVERRIDE_DEFAULT_PARSER}, probed with a same-value {@code setFeature}:
136         * {@code TransformerFactory.getFeature} cannot signal an unrecognized name (it returns {@code false}), while every implementation rejects a
137         * {@code setFeature} for a name it does not support (Xalan with {@link TransformerConfigurationException}, Saxon with its own unchecked exception).
138         *
139         * @param factory The delegate factory.
140         * @return Whether the delegate recognizes the feature.
141         */
142        private static boolean probeOverrideDefaultParser(final SAXTransformerFactory factory) {
143            try {
144                factory.setFeature(SecureSAXParserFactory.OVERRIDE_DEFAULT_PARSER,
145                        factory.getFeature(SecureSAXParserFactory.OVERRIDE_DEFAULT_PARSER));
146                return true;
147            } catch (final Exception e) {
148                return false;
149            }
150        }
151
152        /**
153         * Returns the value, or reports its absence in the TrAX shape.
154         *
155         * @param <T>   The type of the product.
156         * @param value The value an implementation produced.
157         * @param what  Name of the missing product, for the message.
158         * @return The value, never {@code null}.
159         * @throws TransformerConfigurationException Thrown if {@code value} is {@code null}.
160         */
161        private static <T> T required(final T value, final String what) throws TransformerConfigurationException {
162            // Xalan hands back null instead of throwing when the stylesheet failed to compile: XALANJ-2410.
163            if (value == null) {
164                throw new TransformerConfigurationException("Underlying implementation returned a null " + what + ".");
165            }
166            return value;
167        }
168
169        private static Templates unwrap(final Templates templates) {
170            return templates instanceof SecureTemplates ? ((SecureTemplates) templates).getDelegate() : templates;
171        }
172
173        private final SAXTransformerFactory delegate;
174
175        /**
176         * Empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means the default empty DOM.
177         */
178        private final Supplier<Source> emptySource;
179
180        private final FallbackIgnoreURIResolver floor;
181
182        /**
183         * Whether the delegate recognizes {@value SecureSAXParserFactory#OVERRIDE_DEFAULT_PARSER}; its value is read for each created product, as in the JDK.
184         */
185        private final boolean supportsOverrideDefaultParser;
186
187        /**
188         * Constructs a new instance.
189         *
190         * @param delegate The delegate to wrap; must not be {@code null}.
191         * @throws NullPointerException Thrown if {@code delegate} is {@code null}.
192         */
193        private Wrapper(final SAXTransformerFactory delegate) {
194            this(delegate, null);
195        }
196
197        /**
198         * Constructs a new instance.
199         *
200         * @param delegate    The delegate to wrap; must not be {@code null}.
201         * @param emptySource The empty-{@link Source} supplier for the resolver floor, threaded onto every produced Templates/Transformer; {@code null} means
202         * the                    default empty DOM.
203         * @throws NullPointerException Thrown if {@code delegate} is {@code null}.
204         */
205        private Wrapper(final SAXTransformerFactory delegate, final Supplier<Source> emptySource) {
206            this.delegate = Objects.requireNonNull(delegate, "delegate");
207            this.emptySource = emptySource;
208            this.supportsOverrideDefaultParser = probeOverrideDefaultParser(delegate);
209            this.floor = new FallbackIgnoreURIResolver(null, emptySource, this::overrideDefaultParser);
210            // Compile-time block for xsl:import/xsl:include and document(); a caller-set resolver is routed through the floor rather than replacing it.
211            delegate.setURIResolver(floor);
212        }
213
214        /**
215         * 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
216         * resolver or resolved to empty like any other content-named reference.
217         *
218         * <p>
219         * XSLTC-lineage engines resolve the href during the scan, before they install the factory's {@link URIResolver}, and hand back a live
220         * {@link SAXSource} naming the absolutized URI; compiling it, the one documented use of this method, would then fetch it. Saxon already floors the href
221         * itself and returns an empty source, so flooring here is also what makes the engines agree.
222         * </p>
223         *
224         * @param associated The delegate's result; {@code null} when no PI matched.
225         * @param base       The system ID of the scanned document, the base the href was resolved against.
226         * @return The caller resolver's source for an opted-in href, an empty source otherwise, or {@code null} when no PI matched.
227         * @throws TransformerConfigurationException Thrown if the floor rejects the href, which it does when {@value SecureException#THROW_ON_UNRESOLVED} is
228         * set.
229         */
230        private Source floorAssociated(final Source associated, final String base) throws TransformerConfigurationException {
231            if (associated == null || associated.getSystemId() == null) {
232                // No PI matched, or the engine already floored the href to a source that names no URI.
233                return associated;
234            }
235            try {
236                return floor.resolve(associated.getSystemId(), base);
237            } catch (final TransformerException e) {
238                throw new TransformerConfigurationException("Failed to resolve the associated stylesheet " + associated.getSystemId(), e);
239            }
240        }
241
242        /**
243         * {@inheritDoc}
244         *
245         * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service
246         *                                   configuration error} or if the implementation is not available or cannot be instantiated.
247         */
248        @Override
249        public Source getAssociatedStylesheet(final Source source, final String media, final String title, final String charset)
250                throws TransformerConfigurationException {
251            // Xalan's getAssociatedStylesheet drops a SAXSource's reader and self-provisions its own to scan for xml-stylesheet PIs (XALANJ-2849), and the
252            // JDK's XSLTC did the same before 8u162; hand those a DOM so no parser but ours ever sees the document.
253            final Source secure = isXalan(delegate) || JAVA_8 ? secureSourceToDom(source) : SecureSAXParserFactory.secure(source, overrideDefaultParser());
254            return floorAssociated(delegate.getAssociatedStylesheet(secure, media, title, charset), secure.getSystemId());
255        }
256
257        @Override
258        public Object getAttribute(final String name) {
259            return delegate.getAttribute(name);
260        }
261
262        @Override
263        public ErrorListener getErrorListener() {
264            return delegate.getErrorListener();
265        }
266
267        @Override
268        public boolean getFeature(final String name) {
269            return delegate.getFeature(name);
270        }
271
272        @Override
273        public URIResolver getURIResolver() {
274            return floor.getDelegate();
275        }
276
277        /**
278         * {@inheritDoc}
279         *
280         * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service
281         *                                   configuration error} or if the implementation is not available or cannot be instantiated.
282         */
283        @Override
284        public Templates newTemplates(final Source source) throws TransformerConfigurationException {
285            // newTemplates() should never return null for a specification-compliant factory.
286            final Templates templates = delegate.newTemplates(SecureSAXParserFactory.secure(source, overrideDefaultParser()));
287            return templates == null ? null : new SecureTemplates(templates, getURIResolver(), emptySource, overrideDefaultParser());
288        }
289
290        @Override
291        public TemplatesHandler newTemplatesHandler() throws TransformerConfigurationException {
292            // newTemplatesHandler() should never return null for a specification-compliant factory.
293            final TemplatesHandler handler = delegate.newTemplatesHandler();
294            return handler == null ? null : new SecureTemplatesHandler(handler, getURIResolver(), emptySource, overrideDefaultParser());
295        }
296
297        @Override
298        public Transformer newTransformer() throws TransformerConfigurationException {
299            // Identity transformer: still parses runtime sources, so wrap it to secure Transformer.transform(Source, Result).
300            // newTransformer() should never return null for a specification-compliant factory.
301            final Transformer transformer = delegate.newTransformer();
302            return transformer == null ? null : new SecureTransformer(transformer, getURIResolver(), emptySource, overrideDefaultParser());
303        }
304
305        /**
306         * {@inheritDoc}
307         *
308         * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service
309         *                                   configuration error} or if the implementation is not available or cannot be instantiated.
310         */
311        @Override
312        public Transformer newTransformer(final Source source) throws TransformerConfigurationException {
313            // newTransformer() should never return null for a specification-compliant factory.
314            final Transformer transformer = delegate.newTransformer(SecureSAXParserFactory.secure(source, overrideDefaultParser()));
315            return transformer == null ? null : new SecureTransformer(transformer, getURIResolver(), emptySource, overrideDefaultParser());
316        }
317
318        @Override
319        public TransformerHandler newTransformerHandler() throws TransformerConfigurationException {
320            return secure(delegate.newTransformerHandler());
321        }
322
323        /**
324         * {@inheritDoc}
325         *
326         * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service
327         *                                   configuration error} or if the implementation is not available or cannot be instantiated.
328         */
329        @Override
330        public TransformerHandler newTransformerHandler(final Source source) throws TransformerConfigurationException {
331            return secure(delegate.newTransformerHandler(SecureSAXParserFactory.secure(source, overrideDefaultParser())));
332        }
333
334        /**
335         * {@inheritDoc}
336         *
337         * <p>
338         * Most implementations reject a {@link Templates} they did not compile, some in the TrAX shape, others by casting it or its Transformer to their
339         * own type. Both reach the caller as a {@link TransformerConfigurationException}.
340         * </p>
341         */
342        @Override
343        public TransformerHandler newTransformerHandler(final Templates templates) throws TransformerConfigurationException {
344            // Implementations:
345            // - cast templates.newTransformer() to their own Transformer type, so hand them the wrapped implementation Templates, not the wrapper;
346            // - raise NullPointerException for a null argument, except Saxon; rejecting it here keeps that uniform, as on the other methods.
347            final Templates unwrapped = unwrap(Objects.requireNonNull(templates, "templates"));
348            try {
349                return secure(delegate.newTransformerHandler(unwrapped));
350            } catch (final ClassCastException e) {
351                throw new TransformerConfigurationException("Failed to create a TransformerHandler from a Templates of type "
352                        + unwrapped.getClass().getName(), e);
353            }
354        }
355
356        /**
357         * {@inheritDoc}
358         *
359         * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service
360         *                                   configuration error} or if the implementation is not available or cannot be instantiated.
361         */
362        @Override
363        public XMLFilter newXMLFilter(final Source source) throws TransformerConfigurationException {
364            return newXMLFilter(required(newTemplates(source), "Templates"));
365        }
366
367        @Override
368        public XMLFilter newXMLFilter(final Templates templates) throws TransformerConfigurationException {
369            final Transformer transformer = required(templates.newTransformer(), "Transformer");
370            return new SecureXMLFilter(transformer instanceof SecureTransformer
371                    ? (SecureTransformer) transformer
372                    : new SecureTransformer(transformer, getURIResolver(), emptySource, overrideDefaultParser()));
373        }
374
375        /**
376         * Tests whether parsers should be instantiated via {@code newInstance()} instead of {@code newDefaultInstance()}.
377         *
378         * <p>
379         * The JDK implementation of {@link TransformerFactory} uses the JDK parsers while {@value SecureSAXParserFactory#OVERRIDE_DEFAULT_PARSER} is unset
380         * or {@code false}.
381         * </p>
382         *
383         * @return {@code true} if parsers should be created via {@code newInstance()}.
384         */
385        private boolean overrideDefaultParser() {
386            return !supportsOverrideDefaultParser || delegate.getFeature(SecureSAXParserFactory.OVERRIDE_DEFAULT_PARSER);
387        }
388
389        private TransformerHandler secure(final TransformerHandler handler) {
390            return handler == null ? null : new SecureTransformerHandler(handler, getURIResolver(), emptySource, overrideDefaultParser());
391        }
392
393        /**
394         * Parses a stream or SAX source into a DOM through a secure, namespace-aware {@link javax.xml.parsers.DocumentBuilder} and returns a {@link DOMSource}
395         * carrying its system ID, so the consumer walks the tree instead of provisioning its own reader. Any other source is left to
396         * {@link SecureSAXParserFactory#secure(Source, boolean)}.
397         *
398         * <p>
399         * 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
400         * 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.
401         * </p>
402         *
403         * @param source The source to scan for an associated stylesheet.
404         * @return A {@link DOMSource} for a stream or SAX source, otherwise the result of {@link SecureSAXParserFactory#secure(Source, boolean)}.
405         * @throws TransformerConfigurationException Thrown if the source cannot be parsed.
406         * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service
407         *                                   configuration error} or if the implementation is not available or cannot be instantiated.
408         * @throws SecureException Thrown if a (non-Android) factory cannot support the secure processing feature
409         * {@link XMLConstants#FEATURE_SECURE_PROCESSING}.
410         */
411        private Source secureSourceToDom(final Source source) throws TransformerConfigurationException {
412            if (source instanceof StreamSource || source instanceof SAXSource) {
413                final InputSource inputSource = SAXSource.sourceToInputSource(source);
414                if (inputSource != null) {
415                    try {
416                        final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newNSInstance(overrideDefaultParser());
417                        final Document document = factory.newDocumentBuilder().parse(inputSource);
418                        return new DOMSource(document, inputSource.getSystemId());
419                    } catch (final ParserConfigurationException | SAXException | IOException e) {
420                        throw new TransformerConfigurationException("Failed to parse the source for associated-stylesheet lookup", e);
421                    }
422                }
423            }
424            return SecureSAXParserFactory.secure(source, overrideDefaultParser());
425        }
426
427        @Override
428        public void setAttribute(final String name, final Object value) {
429            delegate.setAttribute(name, value);
430        }
431
432        @Override
433        public void setErrorListener(final ErrorListener listener) {
434            delegate.setErrorListener(listener);
435        }
436
437
438        @Override
439        public void setFeature(final String name, final boolean value) throws TransformerConfigurationException {
440            delegate.setFeature(name, value);
441        }
442
443        @Override
444        public void setURIResolver(final URIResolver resolver) {
445            floor.setDelegate(resolver);
446        }
447    }
448
449    /**
450     * Class name of the JDK's built-in default implementation, the Java 8 fallback for {@link #newDefaultInstance()}.
451     */
452    private static final String JDK_TRANSFORMER_FACTORY = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
453
454    private static final MethodHandle MH_newDefaultInstance = MethodHandleFactory.findStatic(TransformerFactory.class, "newDefaultInstance");
455
456    /**
457     * {@code true} on Java 8, detected by the absence of {@code TransformerFactory.newDefaultInstance()}, which arrived in Java 9.
458     * <p>
459     * The JDK's XSLTC only began honoring the {@link XMLReader} carried by a {@link SAXSource} in {@code getAssociatedStylesheet} in 8u162; through 8u152 it
460     * 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
461     * indistinguishable through any API, and such an old runtime has already chosen correctness of configuration over the cost of a DOM pre-parse.
462     * </p>
463     */
464    private static final boolean JAVA_8 = MH_newDefaultInstance == null;
465
466    /**
467     * Returns a new, secure {@link TransformerFactory} of the system-default implementation.
468     * <p>
469     * Obtained from {@code TransformerFactory.newDefaultInstance()} where the platform provides it (Java 9 or later), and by instantiating the JDK's built-in
470     * implementation directly on Java 8.
471     * </p>
472     *
473     * @return A secure factory.
474     * @throws IllegalStateException                Thrown if a required secure setting cannot be applied to the underlying implementation.
475     * @throws TransformerFactoryConfigurationError Thrown if the running platform provides neither {@code newDefaultInstance()} nor the JDK's built-in
476     *                                                implementation (for example, Android).
477     */
478    public static TransformerFactory newDefaultInstance() {
479        if (MH_newDefaultInstance != null) {
480            return secure(MethodHandleFactory.invokeExact(() -> (TransformerFactory) MH_newDefaultInstance.invokeExact(), TransformerFactoryConfigurationError.class));
481        }
482        // 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
483        // example Android), the lookup miss surfaces as TransformerFactoryConfigurationError, like any newInstance miss.
484        return newInstance(JDK_TRANSFORMER_FACTORY, null);
485    }
486
487    /**
488     * Returns a new, secure {@link TransformerFactory}.
489     *
490     * @return A secure factory.
491     * @throws IllegalStateException Thrown if a required secure setting cannot be applied to the underlying implementation.
492     */
493    public static TransformerFactory newInstance() {
494        return secure(TransformerFactory.newInstance());
495    }
496
497    /**
498     * Returns a new, secure {@link TransformerFactory} of the given implementation class.
499     *
500     * @param factoryClassName The fully qualified class name of the {@link TransformerFactory} implementation.
501     * @param classLoader      The class loader used to load the factory class; {@code null} means the current thread's context class loader.
502     * @return A secure factory.
503     * @throws IllegalStateException                Thrown if a required secure setting cannot be applied to the underlying implementation.
504     * @throws TransformerFactoryConfigurationError Thrown if {@code factoryClassName} is {@code null} or the factory class cannot be loaded or instantiated.
505     */
506    public static TransformerFactory newInstance(final String factoryClassName, final ClassLoader classLoader) {
507        return secure(TransformerFactory.newInstance(factoryClassName, classLoader));
508    }
509
510    /**
511     * Applies capability-driven secure settings to any {@link TransformerFactory} on the classpath.
512     *
513     * <p>
514     * Rather than branching on the implementation class, this method probes what the factory supports and adapts:
515     * </p>
516     * <ul>
517     * <li><strong>Saxon</strong> ({@code net.sf.saxon}): recognized by package prefix and handed to {@link SaxonProvider#configure(TransformerFactory)} for the
518     *         channels the standard JAXP knobs cannot close (reflection-based extension functions, the collection finder, the internal SAX parser). It is then
519     *         wrapped like every other implementation to install the {@link FallbackIgnoreURIResolver} floor; the only
520     *         difference is the empty-{@link Source} shape the floor returns, {@code EmptySource} for Saxon rather than the default empty DOM document.</li>
521     * <li><strong>FSP</strong> ({@link XMLConstants#FEATURE_SECURE_PROCESSING}): required. On XSLTC it enables the runtime evaluator limits; on Xalan it
522     * disables         reflection-based extension functions.</li>
523     *     <li><strong>{@link FallbackIgnoreURIResolver} floor</strong>: required. An ignore-all {@link URIResolver} floor, installed by
524     *         the nested wrapper and carried onto every produced {@link Transformer}, resolves {@code xsl:import}/{@code xsl:include} at compile
525     * 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
526     *         routed through the floor rather than replacing it, so a caller can opt a specific URI in but cannot reopen the fetch.</li>
527     *     <li><strong>The nested wrapper</strong>: required. Both implementations fall back to {@code SAXParserFactory.newInstance()} to parse a
528     * 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
529     *         {@link org.apache.commons.xml.secure}-secured reader instead.</li>
530     * </ul>
531     *
532     * @param factory The factory to secure; never {@code null}.
533     * @return a secure factory.
534     */
535    static TransformerFactory secure(final TransformerFactory factory) {
536        // Required: enables secure processing (XSLTC runtime limits; Xalan's extension-function block).
537        setFeature(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true);
538        if (SaxonProvider.isSaxon(factory.getClass())) {
539            // Saxon keeps its vendor Configuration for the channels JAXP cannot close,
540            // then goes through the same wrapper as every other implementation for the URIResolver floor;
541            // EmptySource is the empty-source shape Saxon's consumers expect.
542            return new Wrapper((SAXTransformerFactory) SaxonProvider.configure(factory), SaxonProvider.emptySourceSupplier());
543        }
544        // Required: source/stylesheet parsing provisions its own SAX reader otherwise; the wrapper routes every Source through a secure one and installs the
545        // ignore-all URIResolver floor (blocking xsl:import/include at compile time and document() at runtime) that a caller-set resolver cannot remove.
546        return new Wrapper((SAXTransformerFactory) factory);
547    }
548
549    private static void setFeature(final TransformerFactory factory, final String feature, final boolean value) {
550        try {
551            factory.setFeature(feature, value);
552        } catch (final Exception e) {
553            throw SecureException.featureFailed(feature, factory, e);
554        }
555    }
556
557    private SecureTransformerFactory() {
558        // static only
559    }
560}