• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / ibatis-2 / #516

25 Sep 2024 10:54PM UTC coverage: 65.152%. Remained the same
#516

Pull #252

github

web-flow
Merge f885bad98 into 67e21de4d
Pull Request #252: [secure] Make sure secure processing on document builder factory

1609 of 2820 branches covered (57.06%)

2 of 3 new or added lines in 2 files covered. (66.67%)

1 existing line in 1 file now uncovered.

5048 of 7748 relevant lines covered (65.15%)

0.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.87
/src/main/java/com/ibatis/common/xml/NodeletParser.java
1
/*
2
 * Copyright 2004-2024 the original author or authors.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *    https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.ibatis.common.xml;
17

18
import java.io.IOException;
19
import java.io.InputStream;
20
import java.io.Reader;
21
import java.util.*;
22

23
import javax.xml.XMLConstants;
24
import javax.xml.parsers.DocumentBuilder;
25
import javax.xml.parsers.DocumentBuilderFactory;
26
import javax.xml.parsers.FactoryConfigurationError;
27
import javax.xml.parsers.ParserConfigurationException;
28

29
import org.w3c.dom.*;
30
import org.xml.sax.*;
31

32
/**
33
 * The NodeletParser is a callback based parser similar to SAX. The big difference is that rather than having a single
34
 * callback for all nodes, the NodeletParser has a number of callbacks mapped to various nodes. The callback is called a
35
 * Nodelet and it is registered with the NodeletParser against a specific XPath.
36
 */
37
public class NodeletParser {
1✔
38

39
  /** The let map. */
40
  private Map letMap = new HashMap();
1✔
41

42
  /** The validation. */
43
  private boolean validation;
44

45
  /** The entity resolver. */
46
  private EntityResolver entityResolver;
47

48
  /**
49
   * Registers a nodelet for the specified XPath. Current XPaths supported are:
50
   * <ul>
51
   * <li>Text Path - /rootElement/childElement/text()
52
   * <li>Attribute Path - /rootElement/childElement/@theAttribute
53
   * <li>Element Path - /rootElement/childElement/theElement
54
   * <li>All Elements Named - //theElement
55
   * </ul>
56
   *
57
   * @param xpath
58
   *          the xpath
59
   * @param nodelet
60
   *          the nodelet
61
   */
62
  public void addNodelet(String xpath, Nodelet nodelet) {
63
    letMap.put(xpath, nodelet);
1✔
64
  }
1✔
65

66
  /**
67
   * Begins parsing from the provided Reader.
68
   *
69
   * @param reader
70
   *          the reader
71
   *
72
   * @throws NodeletException
73
   *           the nodelet exception
74
   */
75
  public void parse(Reader reader) throws NodeletException {
76
    try {
77
      Document doc = createDocument(reader);
1✔
78
      parse(doc.getLastChild());
1✔
79
    } catch (Exception e) {
×
80
      throw new NodeletException("Error parsing XML.  Cause: " + e, e);
×
81
    }
1✔
82
  }
1✔
83

84
  /**
85
   * Parses the.
86
   *
87
   * @param inputStream
88
   *          the input stream
89
   *
90
   * @throws NodeletException
91
   *           the nodelet exception
92
   */
93
  public void parse(InputStream inputStream) throws NodeletException {
94
    try {
95
      Document doc = createDocument(inputStream);
×
96
      parse(doc.getLastChild());
×
97
    } catch (Exception e) {
×
98
      throw new NodeletException("Error parsing XML.  Cause: " + e, e);
×
99
    }
×
100
  }
×
101

102
  /**
103
   * Begins parsing from the provided Node.
104
   *
105
   * @param node
106
   *          the node
107
   */
108
  public void parse(Node node) {
109
    Path path = new Path();
1✔
110
    processNodelet(node, "/");
1✔
111
    process(node, path);
1✔
112
  }
1✔
113

114
  /**
115
   * A recursive method that walkes the DOM tree, registers XPaths and calls Nodelets registered under those XPaths.
116
   *
117
   * @param node
118
   *          the node
119
   * @param path
120
   *          the path
121
   */
122
  private void process(Node node, Path path) {
123
    if (node instanceof Element) {
1✔
124
      // Element
125
      String elementName = node.getNodeName();
1✔
126
      path.add(elementName);
1✔
127
      processNodelet(node, path.toString());
1✔
128
      processNodelet(node, new StringBuilder("//").append(elementName).toString());
1✔
129

130
      // Attribute
131
      NamedNodeMap attributes = node.getAttributes();
1✔
132
      int n = attributes.getLength();
1✔
133
      for (int i = 0; i < n; i++) {
1✔
134
        Node att = attributes.item(i);
1✔
135
        String attrName = att.getNodeName();
1✔
136
        path.add("@" + attrName);
1✔
137
        processNodelet(att, path.toString());
1✔
138
        processNodelet(node, new StringBuilder("//@").append(attrName).toString());
1✔
139
        path.remove();
1✔
140
      }
141

142
      // Children
143
      NodeList children = node.getChildNodes();
1✔
144
      for (int i = 0; i < children.getLength(); i++) {
1✔
145
        process(children.item(i), path);
1✔
146
      }
147
      path.add("end()");
1✔
148
      processNodelet(node, path.toString());
1✔
149
      path.remove();
1✔
150
      path.remove();
1✔
151
    } else if (node instanceof Text) {
1!
152
      // Text
153
      path.add("text()");
1✔
154
      processNodelet(node, path.toString());
1✔
155
      processNodelet(node, "//text()");
1✔
156
      path.remove();
1✔
157
    }
158
  }
1✔
159

160
  /**
161
   * Process nodelet.
162
   *
163
   * @param node
164
   *          the node
165
   * @param pathString
166
   *          the path string
167
   */
168
  private void processNodelet(Node node, String pathString) {
169
    Nodelet nodelet = (Nodelet) letMap.get(pathString);
1✔
170
    if (nodelet != null) {
1✔
171
      try {
172
        nodelet.process(node);
1✔
173
      } catch (Exception e) {
×
174
        throw new RuntimeException("Error parsing XPath '" + pathString + "'.  Cause: " + e, e);
×
175
      }
1✔
176
    }
177
  }
1✔
178

179
  /**
180
   * Creates a JAXP Document from a reader.
181
   *
182
   * @param reader
183
   *          the reader
184
   *
185
   * @return the document
186
   *
187
   * @throws ParserConfigurationException
188
   *           the parser configuration exception
189
   * @throws FactoryConfigurationError
190
   *           the factory configuration error
191
   * @throws SAXException
192
   *           the SAX exception
193
   * @throws IOException
194
   *           Signals that an I/O exception has occurred.
195
   */
196
  private Document createDocument(Reader reader)
197
      throws ParserConfigurationException, FactoryConfigurationError, SAXException, IOException {
198
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1✔
199
    factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
1✔
200
    factory.setValidating(validation);
1✔
201

202
    factory.setNamespaceAware(false);
1✔
203
    factory.setIgnoringComments(true);
1✔
204
    factory.setIgnoringElementContentWhitespace(false);
1✔
205
    factory.setCoalescing(false);
1✔
206
    factory.setExpandEntityReferences(true);
1✔
207

208
    DocumentBuilder builder = factory.newDocumentBuilder();
1✔
209
    builder.setEntityResolver(entityResolver);
1✔
210
    builder.setErrorHandler(new ErrorHandler() {
1✔
211
      public void error(SAXParseException exception) throws SAXException {
212
        throw exception;
×
213
      }
214

215
      public void fatalError(SAXParseException exception) throws SAXException {
216
        throw exception;
×
217
      }
218

219
      public void warning(SAXParseException exception) throws SAXException {
220
      }
×
221
    });
222

223
    return builder.parse(new InputSource(reader));
1✔
224
  }
225

226
  /**
227
   * Creates a JAXP Document from an InoutStream.
228
   *
229
   * @param inputStream
230
   *          the input stream
231
   *
232
   * @return the document
233
   *
234
   * @throws ParserConfigurationException
235
   *           the parser configuration exception
236
   * @throws FactoryConfigurationError
237
   *           the factory configuration error
238
   * @throws SAXException
239
   *           the SAX exception
240
   * @throws IOException
241
   *           Signals that an I/O exception has occurred.
242
   */
243
  private Document createDocument(InputStream inputStream)
244
      throws ParserConfigurationException, FactoryConfigurationError, SAXException, IOException {
245
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
×
NEW
246
    factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
×
UNCOV
247
    factory.setValidating(validation);
×
248

249
    factory.setNamespaceAware(false);
×
250
    factory.setIgnoringComments(true);
×
251
    factory.setIgnoringElementContentWhitespace(false);
×
252
    factory.setCoalescing(false);
×
253
    factory.setExpandEntityReferences(true);
×
254

255
    DocumentBuilder builder = factory.newDocumentBuilder();
×
256
    builder.setEntityResolver(entityResolver);
×
257
    builder.setErrorHandler(new ErrorHandler() {
×
258
      public void error(SAXParseException exception) throws SAXException {
259
        throw exception;
×
260
      }
261

262
      public void fatalError(SAXParseException exception) throws SAXException {
263
        throw exception;
×
264
      }
265

266
      public void warning(SAXParseException exception) throws SAXException {
267
      }
×
268
    });
269

270
    return builder.parse(new InputSource(inputStream));
×
271
  }
272

273
  /**
274
   * Sets the validation.
275
   *
276
   * @param validation
277
   *          the new validation
278
   */
279
  public void setValidation(boolean validation) {
280
    this.validation = validation;
1✔
281
  }
1✔
282

283
  /**
284
   * Sets the entity resolver.
285
   *
286
   * @param resolver
287
   *          the new entity resolver
288
   */
289
  public void setEntityResolver(EntityResolver resolver) {
290
    this.entityResolver = resolver;
1✔
291
  }
1✔
292

293
  /**
294
   * Inner helper class that assists with building XPath paths.
295
   * <p>
296
   * Note: Currently this is a bit slow and could be optimized.
297
   */
298
  private static class Path {
299

300
    /** The node list. */
301
    private List nodeList = new ArrayList();
1✔
302

303
    /**
304
     * Instantiates a new path.
305
     */
306
    public Path() {
1✔
307
    }
1✔
308

309
    /**
310
     * Instantiates a new path.
311
     *
312
     * @param path
313
     *          the path
314
     */
315
    public Path(String path) {
×
316
      StringTokenizer parser = new StringTokenizer(path, "/", false);
×
317
      while (parser.hasMoreTokens()) {
×
318
        nodeList.add(parser.nextToken());
×
319
      }
320
    }
×
321

322
    /**
323
     * Adds the.
324
     *
325
     * @param node
326
     *          the node
327
     */
328
    public void add(String node) {
329
      nodeList.add(node);
1✔
330
    }
1✔
331

332
    /**
333
     * Removes the.
334
     */
335
    public void remove() {
336
      nodeList.remove(nodeList.size() - 1);
1✔
337
    }
1✔
338

339
    @Override
340
    public String toString() {
341
      StringBuilder builder = new StringBuilder("/");
1✔
342
      for (int i = 0; i < nodeList.size(); i++) {
1✔
343
        builder.append(nodeList.get(i));
1✔
344
        if (i < nodeList.size() - 1) {
1✔
345
          builder.append("/");
1✔
346
        }
347
      }
348
      return builder.toString();
1✔
349
    }
350
  }
351

352
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc