HardeningTransformerFactory.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.xml;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import org.xml.sax.XMLReader;
/**
* {@link javax.xml.transform.TransformerFactory} wrapper that rewrites every Source-taking entry point through {@link XmlFactories#harden(Source)} before
* delegating.
*
* <p>Used by providers whose underlying TrAX implementation pulls a fresh {@code SAXParserFactory.newInstance()} for any Source that is not already a
* {@link SAXSource} carrying its own {@link XMLReader}, and only sets {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING FSP} on the resulting reader.
* Wrapping the factory and rewriting the Source upstream guarantees the parse runs through an {@link XmlFactories}-hardened reader instead.</p>
*
* <p>Three layers cooperate:</p>
* <ol>
* <li>{@link HardeningTransformerFactory} rewrites the Source on every entry point that compiles a stylesheet or transforms a one-shot input.</li>
* <li>{@link HardeningTemplates} returns a {@link HardeningTransformer} from {@link Templates#newTransformer()} so runtime source parsing is also covered, and
* restores the factory's URIResolver onto the produced Transformer (which the underlying impl typically does not propagate through {@code Templates}).</li>
* <li>{@link HardeningTransformer} rewrites the Source on every {@link Transformer#transform(Source, javax.xml.transform.Result)} call.</li>
* </ol>
*
* <h2>Caveats</h2>
* <ul>
* <li>A {@link SAXSource} that carries its own {@link XMLReader} is trusted as-is: the caller is expected to supply a hardened reader (via
* {@link XmlFactories#newSAXParserFactory()} or {@link XmlFactories#harden(XMLReader)}) in that case.</li>
* <li>{@link TransformerHandler} returned from {@code newTransformerHandler} is not wrapped: it processes incoming SAX events instead of reading a Source, so
* it has no inner Source-parsing path. A caller who pulls the inner {@link Transformer} via {@link TransformerHandler#getTransformer()} bypasses the
* runtime source rewrite.</li>
* </ul>
*/
final class HardeningTransformerFactory extends DelegatingTransformerFactory {
HardeningTransformerFactory(final SAXTransformerFactory delegate) {
super(delegate);
}
@Override
public Source getAssociatedStylesheet(final Source source, final String media, final String title, final String charset)
throws TransformerConfigurationException {
return super.getAssociatedStylesheet(XmlFactories.harden(source), media, title, charset);
}
@Override
public Templates newTemplates(final Source source) throws TransformerConfigurationException {
final Templates templates = super.newTemplates(XmlFactories.harden(source));
return templates == null ? null : new HardeningTemplates(templates, getURIResolver());
}
@Override
public Transformer newTransformer() throws TransformerConfigurationException {
// Identity transformer: still parses runtime sources, so wrap it to harden Transformer.transform(Source, Result).
final Transformer transformer = super.newTransformer();
return transformer == null ? null : new HardeningTransformer(transformer);
}
@Override
public Transformer newTransformer(final Source source) throws TransformerConfigurationException {
final Transformer transformer = super.newTransformer(XmlFactories.harden(source));
return transformer == null ? null : new HardeningTransformer(transformer);
}
@Override
public TransformerHandler newTransformerHandler(final Source source) throws TransformerConfigurationException {
return super.newTransformerHandler(XmlFactories.harden(source));
}
}