View Javadoc
1   /*
2    * This file is part of dependency-check-core.
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    *     http://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   * Copyright (c) 2016 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.xml.hints;
19  
20  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.Reader;
27  import java.nio.charset.StandardCharsets;
28  import java.util.List;
29  import javax.annotation.concurrent.NotThreadSafe;
30  import javax.xml.parsers.ParserConfigurationException;
31  import javax.xml.parsers.SAXParser;
32  import org.apache.commons.io.ByteOrderMark;
33  import org.apache.commons.io.input.BOMInputStream;
34  
35  import org.owasp.dependencycheck.utils.FileUtils;
36  import org.owasp.dependencycheck.utils.XmlUtils;
37  
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  import org.xml.sax.InputSource;
41  import org.xml.sax.SAXException;
42  import org.xml.sax.XMLReader;
43  
44  /**
45   * A simple validating parser for XML Hint Rules.
46   *
47   * @author Jeremy Long
48   */
49  @NotThreadSafe
50  public class HintParser {
51  
52      /**
53       * The logger.
54       */
55      private static final Logger LOGGER = LoggerFactory.getLogger(HintParser.class);
56      /**
57       * JAXP Schema Language. Source:
58       * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
59       */
60      public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
61      /**
62       * W3C XML Schema. Source:
63       * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
64       */
65      public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
66      /**
67       * JAXP Schema Source. Source:
68       * http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
69       */
70      public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
71  
72      /**
73       * The schema for the hint XML files.
74       */
75      private static final String HINT_SCHEMA_1_2 = "schema/dependency-hint.1.2.xsd";
76  
77      /**
78       * The schema for the hint XML files.
79       */
80      private static final String HINT_SCHEMA_1_1 = "schema/dependency-hint.1.1.xsd";
81  
82      /**
83       * The schema for the hint XML files.
84       */
85      private static final String HINT_SCHEMA_1_3 = "schema/dependency-hint.1.3.xsd";
86  
87      /**
88       * The schema for the hint XML files.
89       */
90      private static final String HINT_SCHEMA_1_4 = "schema/dependency-hint.1.4.xsd";
91  
92      /**
93       * The hint rules.
94       */
95      private List<HintRule> hintRules;
96      /**
97       * The vendor duplicating hint rules.
98       */
99      private List<VendorDuplicatingHintRule> vendorDuplicatingHintRules;
100 
101     /**
102      * Returns the hint rules.
103      *
104      * @return the hint rules
105      */
106     @SuppressWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
107     public List<HintRule> getHintRules() {
108         return hintRules;
109     }
110 
111     /**
112      * Returns the vendor duplicating hint rules.
113      *
114      * @return the vendor duplicating hint rules
115      */
116     public List<VendorDuplicatingHintRule> getVendorDuplicatingHintRules() {
117         return vendorDuplicatingHintRules;
118     }
119 
120     /**
121      * Parses the given XML file and returns a list of the hints contained.
122      *
123      * @param file an XML file containing hints
124      * @throws HintParseException thrown if the XML file cannot be parsed
125      */
126     @SuppressFBWarnings(justification = "try with resources will clean up the input stream", value = {"OBL_UNSATISFIED_OBLIGATION"})
127     public void parseHints(File file) throws HintParseException {
128         try (InputStream fis = new FileInputStream(file)) {
129             parseHints(fis);
130         } catch (SAXException | IOException ex) {
131             LOGGER.debug("", ex);
132             throw new HintParseException(ex);
133         }
134     }
135 
136     /**
137      * Parses the given XML stream and returns a list of the hint rules
138      * contained.
139      *
140      * @param inputStream an InputStream containing hint rules
141      * @throws HintParseException thrown if the XML cannot be parsed
142      * @throws SAXException thrown if the XML cannot be parsed
143      */
144     public void parseHints(InputStream inputStream) throws HintParseException, SAXException {
145         try (
146                 InputStream schemaStream14 = FileUtils.getResourceAsStream(HINT_SCHEMA_1_4);
147                 InputStream schemaStream13 = FileUtils.getResourceAsStream(HINT_SCHEMA_1_3);
148                 InputStream schemaStream12 = FileUtils.getResourceAsStream(HINT_SCHEMA_1_2);
149                 InputStream schemaStream11 = FileUtils.getResourceAsStream(HINT_SCHEMA_1_1)) {
150 
151             final BOMInputStream bomStream = BOMInputStream.builder().setInputStream(inputStream).get();
152             final ByteOrderMark bom = bomStream.getBOM();
153             final String defaultEncoding = StandardCharsets.UTF_8.name();
154             final String charsetName = bom == null ? defaultEncoding : bom.getCharsetName();
155 
156             final HintHandler handler = new HintHandler();
157             final SAXParser saxParser = XmlUtils.buildSecureSaxParser(schemaStream14, schemaStream13, schemaStream12, schemaStream11);
158             final XMLReader xmlReader = saxParser.getXMLReader();
159             xmlReader.setErrorHandler(new HintErrorHandler());
160             xmlReader.setContentHandler(handler);
161             try (Reader reader = new InputStreamReader(bomStream, charsetName)) {
162                 final InputSource in = new InputSource(reader);
163                 xmlReader.parse(in);
164                 this.hintRules = handler.getHintRules();
165                 this.vendorDuplicatingHintRules = handler.getVendorDuplicatingHintRules();
166             }
167         } catch (ParserConfigurationException | IOException ex) {
168             LOGGER.debug("", ex);
169             throw new HintParseException(ex);
170         } catch (SAXException ex) {
171             if (ex.getMessage().contains("Cannot find the declaration of element 'hints'.")) {
172                 throw ex;
173             } else {
174                 LOGGER.debug("", ex);
175                 throw new HintParseException(ex);
176             }
177         }
178     }
179 }