View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.xml.secure;
19  
20  import java.util.Objects;
21  import java.util.function.Supplier;
22  
23  import javax.xml.transform.Result;
24  import javax.xml.transform.Source;
25  import javax.xml.transform.Transformer;
26  import javax.xml.transform.URIResolver;
27  import javax.xml.transform.sax.TransformerHandler;
28  
29  import org.xml.sax.Attributes;
30  import org.xml.sax.Locator;
31  import org.xml.sax.SAXException;
32  
33  /**
34   * {@link TransformerHandler} wrapper that keeps an ignore-all {@link URIResolver} floor on the live transformer the handler transforms with.
35   *
36   * <p>
37   * The handler's input is SAX events the caller drives, so it has no inner source-parsing path of its own. What needs the floor is its transformer: the
38   * handler runs the transformation on the object {@link TransformerHandler#getTransformer()} exposes, and not every implementation seeds that transformer with
39   * the factory's resolver (the stock JDK's {@code newTransformerHandler(Templates)} does not). Wrapping that transformer in a {@link SecureTransformer} at
40   * construction installs the floor on the live instance, so runtime {@code document()} during the handler's transform is covered, and so is a caller who pulls
41   * the transformer out through {@code getTransformer()}.
42   * </p>
43   */
44  final class SecureTransformerHandler implements TransformerHandler {
45  
46      private final TransformerHandler delegate;
47  
48      /**
49       * Wraps the handler's live transformer; constructing it installs the resolver floor that the handler's own transform then runs under.
50       */
51      private final SecureTransformer transformer;
52  
53      /**
54       * Constructs a new instance.
55       *
56       * @param delegate              The delegate to wrap; must not be {@code null}.
57       * @param factoryUriResolver    The factory's compile-time URIResolver snapshot to restore onto the live transformer; may be {@code null}.
58       * @param emptySource           The empty-{@link Source} supplier for the produced Transformer's floor; {@code null} means the default empty DOM.
59       * @param overrideDefaultParser whether the live transformer's source rewrites should use the pluggable parser lookup instead of the platform's built-in
60       *                              parser.
61       * @throws NullPointerException Thrown if {@code delegate} is {@code null}.
62       */
63      SecureTransformerHandler(final TransformerHandler delegate, final URIResolver factoryUriResolver, final Supplier<Source> emptySource,
64              final boolean overrideDefaultParser) {
65          this.delegate = Objects.requireNonNull(delegate, "delegate");
66          this.transformer = new SecureTransformer(delegate.getTransformer(), factoryUriResolver, emptySource, overrideDefaultParser);
67      }
68  
69      @Override
70      public void characters(final char[] ch, final int start, final int length) throws SAXException {
71          delegate.characters(ch, start, length);
72      }
73  
74      @Override
75      public void comment(final char[] ch, final int start, final int length) throws SAXException {
76          delegate.comment(ch, start, length);
77      }
78  
79  
80      @Override
81      public void endCDATA() throws SAXException {
82          delegate.endCDATA();
83      }
84  
85      @Override
86      public void endDocument() throws SAXException {
87          delegate.endDocument();
88      }
89  
90      @Override
91      public void endDTD() throws SAXException {
92          delegate.endDTD();
93      }
94  
95      @Override
96      public void endElement(final String uri, final String localName, final String qName) throws SAXException {
97          delegate.endElement(uri, localName, qName);
98      }
99  
100     @Override
101     public void endEntity(final String name) throws SAXException {
102         delegate.endEntity(name);
103     }
104 
105     @Override
106     public void endPrefixMapping(final String prefix) throws SAXException {
107         delegate.endPrefixMapping(prefix);
108     }
109 
110     @Override
111     public String getSystemId() {
112         return delegate.getSystemId();
113     }
114 
115     @Override
116     public Transformer getTransformer() {
117         return transformer;
118     }
119 
120     @Override
121     public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
122         delegate.ignorableWhitespace(ch, start, length);
123     }
124 
125     @Override
126     public void notationDecl(final String name, final String publicId, final String systemId) throws SAXException {
127         delegate.notationDecl(name, publicId, systemId);
128     }
129 
130     @Override
131     public void processingInstruction(final String target, final String data) throws SAXException {
132         delegate.processingInstruction(target, data);
133     }
134 
135     @Override
136     public void setDocumentLocator(final Locator locator) {
137         delegate.setDocumentLocator(locator);
138     }
139 
140     @Override
141     public void setResult(final Result result) {
142         delegate.setResult(result);
143     }
144 
145     @Override
146     public void setSystemId(final String systemID) {
147         delegate.setSystemId(systemID);
148     }
149 
150     @Override
151     public void skippedEntity(final String name) throws SAXException {
152         delegate.skippedEntity(name);
153     }
154 
155     @Override
156     public void startCDATA() throws SAXException {
157         delegate.startCDATA();
158     }
159 
160     @Override
161     public void startDocument() throws SAXException {
162         delegate.startDocument();
163     }
164 
165     @Override
166     public void startDTD(final String name, final String publicId, final String systemId) throws SAXException {
167         delegate.startDTD(name, publicId, systemId);
168     }
169 
170     @Override
171     public void startElement(final String uri, final String localName, final String qName, final Attributes atts) throws SAXException {
172         delegate.startElement(uri, localName, qName, atts);
173     }
174 
175     @Override
176     public void startEntity(final String name) throws SAXException {
177         delegate.startEntity(name);
178     }
179 
180     @Override
181     public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
182         delegate.startPrefixMapping(prefix, uri);
183     }
184 
185     @Override
186     public void unparsedEntityDecl(final String name, final String publicId, final String systemId, final String notationName) throws SAXException {
187         delegate.unparsedEntityDecl(name, publicId, systemId, notationName);
188     }
189 }