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.Properties;
22  import java.util.function.Supplier;
23  
24  import javax.xml.parsers.FactoryConfigurationError;
25  import javax.xml.transform.ErrorListener;
26  import javax.xml.transform.Result;
27  import javax.xml.transform.Source;
28  import javax.xml.transform.Transformer;
29  import javax.xml.transform.TransformerException;
30  import javax.xml.transform.URIResolver;
31  
32  /**
33   * {@link Transformer} wrapper that rewrites the Source on every {@link Transformer#transform(Source, Result)} call through
34   * {@link SecureSAXParserFactory#secure(Source, boolean)} before delegating, and keeps an ignore-all {@link URIResolver} floor so runtime {@code document()}
35   * calls a
36   * caller does not resolve return empty rather than being fetched.
37   * <p>
38   * The floor is installed on the delegate transformer at construction, seeded with the resolver the delegate already carried, or with the factory's
39   * compile-time resolver where it carried none; {@link #setURIResolver(URIResolver)} routes a caller's resolver through it rather than replacing it, so the
40   * block cannot be dropped. {@link #reset()} re-establishes the floor with that same seed, matching the just-constructed state.
41   * </p>
42   */
43  final class SecureTransformer extends Transformer {
44  
45      private final Transformer delegate;
46  
47      /**
48       * URIResolver the floor is seeded with, both at construction and again on {@link #reset()}: the one the delegate carried, else the factory's compile-time
49       * snapshot.
50       */
51      private final URIResolver initialUriResolver;
52  
53      private final FallbackIgnoreURIResolver floor;
54  
55      /**
56       * Snapshot of the factory's {@value SecureSAXParserFactory#OVERRIDE_DEFAULT_PARSER} outcome at creation, just as the JDK copies the feature onto the
57       * transformers it creates.
58       */
59      final boolean overrideDefaultParser;
60  
61      /**
62       * Constructs a new instance.
63       *
64       * @param delegate         The delegate to wrap; must not be {@code null}.
65       * @param factoryUriResolver The factory's compile-time URIResolver snapshot, used where the delegate carries none of its own; may be {@code null}.
66       * @param emptySource      The empty-{@link Source} supplier for the produced Transformers; {@code null} for the default empty DOM document.
67       * @param overrideDefaultParser whether the source rewrites should use the pluggable parser lookup instead of the platform's built-in parser.
68       * @throws NullPointerException Thrown if {@code delegate} is {@code null}.
69       */
70      SecureTransformer(final Transformer delegate, final URIResolver factoryUriResolver, final Supplier<Source> emptySource,
71              final boolean overrideDefaultParser) {
72          this.delegate = Objects.requireNonNull(delegate, "delegate");
73          this.overrideDefaultParser = overrideDefaultParser;
74          // A caller may have configured the delegate before it reached us; chain the floor onto that resolver rather than dropping it. A floor already there
75          // came from this library, so it is the factory's resolver that seeds the new one.
76          final URIResolver carried = delegate.getURIResolver();
77          this.initialUriResolver = carried == null || carried instanceof FallbackIgnoreURIResolver ? factoryUriResolver : carried;
78          this.floor = new FallbackIgnoreURIResolver(initialUriResolver, emptySource, () -> overrideDefaultParser);
79          delegate.setURIResolver(floor);
80      }
81  
82      @Override
83      public void clearParameters() {
84          delegate.clearParameters();
85      }
86  
87      @Override
88      public ErrorListener getErrorListener() {
89          return delegate.getErrorListener();
90      }
91  
92      @Override
93      public Properties getOutputProperties() {
94          return delegate.getOutputProperties();
95      }
96  
97      @Override
98      public String getOutputProperty(final String name) {
99          return delegate.getOutputProperty(name);
100     }
101 
102     @Override
103     public Object getParameter(final String name) {
104         return delegate.getParameter(name);
105     }
106 
107     @Override
108     public URIResolver getURIResolver() {
109         return floor.getDelegate();
110     }
111 
112     @Override
113     public void reset() {
114         delegate.reset();
115         floor.setDelegate(initialUriResolver);
116         delegate.setURIResolver(floor);
117     }
118 
119     @Override
120     public void setErrorListener(final ErrorListener listener) {
121         delegate.setErrorListener(listener);
122     }
123 
124     @Override
125     public void setOutputProperties(final Properties properties) {
126         delegate.setOutputProperties(properties);
127     }
128 
129     @Override
130     public void setOutputProperty(final String name, final String value) {
131         delegate.setOutputProperty(name, value);
132     }
133 
134     @Override
135     public void setParameter(final String name, final Object value) {
136         delegate.setParameter(name, value);
137     }
138 
139     @Override
140     public void setURIResolver(final URIResolver resolver) {
141         floor.setDelegate(resolver);
142     }
143 
144     /**
145      * {@inheritDoc}
146      *
147      * @throws IllegalStateException     Thrown if the underlying implementation cannot provide a secure reader.
148      * @throws FactoryConfigurationError Thrown from a factory in case of a {@link java.util.ServiceConfigurationError service configuration error} or
149      *                                   if the implementation is not available or cannot be instantiated.
150      */
151     @Override
152     public void transform(final Source xmlSource, final Result outputTarget) throws TransformerException {
153         delegate.transform(SecureSAXParserFactory.secure(xmlSource, overrideDefaultParser), outputTarget);
154     }
155 }