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