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) 2015 Jeremy Long. All Rights Reserved.
17  */
18 package org.owasp.dependencycheck.data.update.cpe;
19 
20 import java.io.UnsupportedEncodingException;
21 import java.util.ArrayList;
22 import java.util.List;
23 import javax.annotation.concurrent.NotThreadSafe;
24 import org.owasp.dependencycheck.data.update.NvdCveUpdater;
25 import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
26 import org.owasp.dependencycheck.utils.Settings;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.xml.sax.Attributes;
30 import org.xml.sax.SAXException;
31 import org.xml.sax.helpers.DefaultHandler;
32 
33 /**
34  * A SAX Handler that will parse the CPE XML and load it into the database.
35  *
36  * @author Jeremy Long
37  */
38 @NotThreadSafe
39 public class CPEHandler extends DefaultHandler {
40 
41     /**
42      * The current CPE schema.
43      */
44     private static final String CURRENT_SCHEMA_VERSION = "2.3";
45     /**
46      * The Starts with expression to filter CVE entries by CPE.
47      */
48     private final String cpeStartsWith;
49     /**
50      * The text content of the node being processed. This can be used during the
51      * end element event.
52      */
53     private StringBuilder nodeText = null;
54     /**
55      * A reference to the current element.
56      */
57     private final Element current = new Element();
58     /**
59      * The logger.
60      */
61     private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveUpdater.class);
62     /**
63      * The list of CPE values.
64      */
65     private final List<Cpe> data = new ArrayList<>();
66 
67     /**
68      * Constructs a new CPE Handler object with the configured settings.
69      *
70      * @param settings the configured settings
71      */
CPEHandler(Settings settings)72     public CPEHandler(Settings settings) {
73         cpeStartsWith = settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:/a:");
74     }
75 
76     /**
77      * Returns the list of CPE values.
78      *
79      * @return the list of CPE values
80      */
getData()81     public List<Cpe> getData() {
82         return data;
83     }
84 
85     /**
86      * Handles the start element event.
87      *
88      * @param uri the elements uri
89      * @param localName the local name
90      * @param qName the qualified name
91      * @param attributes the attributes
92      * @throws SAXException thrown if there is an exception processing the
93      * element
94      */
95     @Override
startElement(String uri, String localName, String qName, Attributes attributes)96     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
97         nodeText = null;
98         current.setNode(qName);
99         if (current.isCpeItemNode()) {
100             final String temp = attributes.getValue("deprecated");
101             final String value = attributes.getValue("name");
102             final boolean delete = "true".equalsIgnoreCase(temp);
103             if (!delete && value.startsWith(cpeStartsWith) && value.length() > 7) {
104                 try {
105                     final Cpe cpe = new Cpe(value);
106                     data.add(cpe);
107                 } catch (UnsupportedEncodingException ex) {
108                     LOGGER.debug("Unable to parse the CPE", ex);
109                 } catch (InvalidDataException ex) {
110                     LOGGER.debug("CPE is not the correct format", ex);
111                 }
112             }
113         } else if (current.isSchemaVersionNode()) {
114             nodeText = new StringBuilder(3);
115         }
116 //        } else if (current.isTitleNode()) {
117 //            //do nothing
118 //        } else if (current.isMetaNode()) {
119 //            //do nothing
120 //        } else if (current.isTimestampNode()) {
121 //            //do nothing
122 //        } else if (current.isCpeListNode()) {
123 //            //do nothing
124 //        } else if (current.isNotesNode()) {
125 //            //do nothing
126 //        } else if (current.isNoteNode()) {
127 //            //do nothing
128 //        } else if (current.isCheckNode()) {
129 //            //do nothing
130 //        } else if (current.isGeneratorNode()) {
131 //            //do nothing
132 //        } else if (current.isProductNameNode()) {
133 //            //do nothing
134 //        } else if (current.isProductVersionNode()) {
135 //            //do nothing
136     }
137 
138     /**
139      * Reads the characters in the current node.
140      *
141      * @param ch the char array
142      * @param start the start position of the data read
143      * @param length the length of the data read
144      * @throws SAXException thrown if there is an exception processing the
145      * characters
146      */
147     @Override
characters(char[] ch, int start, int length)148     public void characters(char[] ch, int start, int length) throws SAXException {
149         if (nodeText != null) {
150             nodeText.append(ch, start, length);
151         }
152     }
153 
154     /**
155      * Handles the end element event. Stores the CPE data in the Cve Database if
156      * the cpe item node is ending.
157      *
158      * @param uri the element's uri
159      * @param localName the local name
160      * @param qName the qualified name
161      * @throws SAXException thrown if there is an exception processing the
162      * element
163      */
164     @Override
endElement(String uri, String localName, String qName)165     public void endElement(String uri, String localName, String qName) throws SAXException {
166         current.setNode(qName);
167         if (current.isSchemaVersionNode() && !CURRENT_SCHEMA_VERSION.equals(nodeText.toString())) {
168             throw new SAXException("ERROR: Unexpected CPE Schema Version, expected: "
169                     + CURRENT_SCHEMA_VERSION + ", file is: " + nodeText);
170 
171         }
172     }
173 
174     // <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
175     /**
176      * A simple class to maintain information about the current element while
177      * parsing the CPE XML.
178      */
179     protected static final class Element {
180 
181         /**
182          * A node type in the CPE Schema 2.2
183          */
184         public static final String CPE_LIST = "cpe-list";
185         /**
186          * A node type in the CPE Schema 2.2
187          */
188         public static final String CPE_ITEM = "cpe-item";
189         /**
190          * A node type in the CPE Schema 2.2
191          */
192         public static final String TITLE = "title";
193         /**
194          * A node type in the CPE Schema 2.2
195          */
196         public static final String NOTES = "notes";
197         /**
198          * A node type in the CPE Schema 2.2
199          */
200         public static final String NOTE = "note";
201         /**
202          * A node type in the CPE Schema 2.2
203          */
204         public static final String CHECK = "check";
205         /**
206          * A node type in the CPE Schema 2.2
207          */
208         public static final String META = "meta:item-metadata";
209         /**
210          * A node type in the CPE Schema 2.2
211          */
212         public static final String GENERATOR = "generator";
213         /**
214          * A node type in the CPE Schema 2.2
215          */
216         public static final String PRODUCT_NAME = "product_name";
217         /**
218          * A node type in the CPE Schema 2.2
219          */
220         public static final String PRODUCT_VERSION = "product_version";
221         /**
222          * A node type in the CPE Schema 2.2
223          */
224         public static final String SCHEMA_VERSION = "schema_version";
225         /**
226          * A node type in the CPE Schema 2.2
227          */
228         public static final String TIMESTAMP = "timestamp";
229         /**
230          * A reference to the current node.
231          */
232         private String node = null;
233 
234         /**
235          * Gets the value of node
236          *
237          * @return the value of node
238          */
getNode()239         public String getNode() {
240             return this.node;
241         }
242 
243         /**
244          * Sets the value of node
245          *
246          * @param node new value of node
247          */
setNode(String node)248         public void setNode(String node) {
249             this.node = node;
250         }
251 
252         /**
253          * Checks if the handler is at the CPE_LIST node
254          *
255          * @return true or false
256          */
isCpeListNode()257         public boolean isCpeListNode() {
258             return CPE_LIST.equals(node);
259         }
260 
261         /**
262          * Checks if the handler is at the CPE_ITEM node
263          *
264          * @return true or false
265          */
isCpeItemNode()266         public boolean isCpeItemNode() {
267             return CPE_ITEM.equals(node);
268         }
269 
270         /**
271          * Checks if the handler is at the TITLE node
272          *
273          * @return true or false
274          */
isTitleNode()275         public boolean isTitleNode() {
276             return TITLE.equals(node);
277         }
278 
279         /**
280          * Checks if the handler is at the NOTES node
281          *
282          * @return true or false
283          */
isNotesNode()284         public boolean isNotesNode() {
285             return NOTES.equals(node);
286         }
287 
288         /**
289          * Checks if the handler is at the NOTE node
290          *
291          * @return true or false
292          */
isNoteNode()293         public boolean isNoteNode() {
294             return NOTE.equals(node);
295         }
296 
297         /**
298          * Checks if the handler is at the CHECK node
299          *
300          * @return true or false
301          */
isCheckNode()302         public boolean isCheckNode() {
303             return CHECK.equals(node);
304         }
305 
306         /**
307          * Checks if the handler is at the META node
308          *
309          * @return true or false
310          */
isMetaNode()311         public boolean isMetaNode() {
312             return META.equals(node);
313         }
314 
315         /**
316          * Checks if the handler is at the GENERATOR node
317          *
318          * @return true or false
319          */
isGeneratorNode()320         public boolean isGeneratorNode() {
321             return GENERATOR.equals(node);
322         }
323 
324         /**
325          * Checks if the handler is at the PRODUCT_NAME node
326          *
327          * @return true or false
328          */
isProductNameNode()329         public boolean isProductNameNode() {
330             return PRODUCT_NAME.equals(node);
331         }
332 
333         /**
334          * Checks if the handler is at the PRODUCT_VERSION node
335          *
336          * @return true or false
337          */
isProductVersionNode()338         public boolean isProductVersionNode() {
339             return PRODUCT_VERSION.equals(node);
340         }
341 
342         /**
343          * Checks if the handler is at the SCHEMA_VERSION node
344          *
345          * @return true or false
346          */
isSchemaVersionNode()347         public boolean isSchemaVersionNode() {
348             return SCHEMA_VERSION.equals(node);
349         }
350 
351         /**
352          * Checks if the handler is at the TIMESTAMP node
353          *
354          * @return true or false
355          */
isTimestampNode()356         public boolean isTimestampNode() {
357             return TIMESTAMP.equals(node);
358         }
359     }
360     // </editor-fold>
361 }
362