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 }