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) 2012 Jeremy Long. All Rights Reserved.
17  */
18 package org.owasp.dependencycheck.data.update.nvd;
19 
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import javax.annotation.concurrent.NotThreadSafe;
25 import org.owasp.dependencycheck.dependency.VulnerableSoftware;
26 import org.xml.sax.Attributes;
27 import org.xml.sax.SAXException;
28 import org.xml.sax.SAXNotSupportedException;
29 import org.xml.sax.helpers.DefaultHandler;
30 
31 /**
32  * A SAX Handler that will parse the NVD CVE XML (schema version 1.2). This parses the xml and retrieves a listing of
33  * CPEs that have previous versions specified. The previous version information is not in the 2.0 version of the schema
34  * and is useful to ensure accurate identification (or at least complete).
35  *
36  * @author Jeremy Long
37  */
38 @NotThreadSafe
39 public class NvdCve12Handler extends DefaultHandler {
40 
41     /**
42      * the supported schema version.
43      */
44     private static final String CURRENT_SCHEMA_VERSION = "1.2";
45     /**
46      * the current vulnerability.
47      */
48     private String vulnerability;
49     /**
50      * a list of vulnerable software.
51      */
52     private List<VulnerableSoftware> software;
53     /**
54      * the vendor name.
55      */
56     private String vendor;
57     /**
58      * the product name.
59      */
60     private String product;
61     /**
62      * if the nvd cve should be skipped because it was rejected.
63      */
64     private boolean skip = false;
65     /**
66      * flag indicating if there is a previous version.
67      */
68     private boolean hasPreviousVersion = false;
69     /**
70      * The current element.
71      */
72     private final Element current = new Element();
73     /**
74      * a map of vulnerabilities.
75      */
76     private Map<String, List<VulnerableSoftware>> vulnerabilities;
77 
78     /**
79      * Get the value of vulnerabilities.
80      *
81      * @return the value of vulnerabilities
82      */
getVulnerabilities()83     public Map<String, List<VulnerableSoftware>> getVulnerabilities() {
84         return vulnerabilities;
85     }
86 
87     @Override
startElement(String uri, String localName, String qName, Attributes attributes)88     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
89         current.setNode(qName);
90         if (current.isEntryNode()) {
91             vendor = null;
92             product = null;
93             hasPreviousVersion = false;
94             final String reject = attributes.getValue("reject");
95             skip = "1".equals(reject);
96             if (!skip) {
97                 vulnerability = attributes.getValue("name");
98                 software = new ArrayList<>();
99             } else {
100                 vulnerability = null;
101                 software = null;
102             }
103         } else if (!skip && current.isProdNode()) {
104             vendor = attributes.getValue("vendor");
105             product = attributes.getValue("name");
106         } else if (!skip && current.isVersNode()) {
107             final String prev = attributes.getValue("prev");
108             if (prev != null && "1".equals(prev)) {
109                 hasPreviousVersion = true;
110                 final String edition = attributes.getValue("edition");
111                 final String num = attributes.getValue("num");
112 
113                 /*yes yes, this may not actually be an "a" - it could be an OS, etc. but for our
114                  purposes this is good enough as we won't use this if we don't find a corresponding "a"
115                  in the nvd cve 2.0. */
116                 final int cpeLen = 8 + vendor.length() + product.length()
117                     + (null != num ? (1 + num.length()) : 0)
118                     + (null != edition ? (1 + edition.length()) : 0);
119                 final StringBuilder cpe = new StringBuilder(cpeLen);
120                 cpe.append("cpe:/a:").append(vendor).append(':').append(product);
121                 if (num != null) {
122                     cpe.append(':').append(num);
123                 }
124                 if (edition != null) {
125                     cpe.append(':').append(edition);
126                 }
127                 final VulnerableSoftware vs = new VulnerableSoftware();
128                 vs.setCpe(cpe.toString());
129                 vs.setPreviousVersion(prev);
130                 software.add(vs);
131             }
132         } else if (current.isNVDNode()) {
133             final String nvdVer = attributes.getValue("nvd_xml_version");
134             if (!CURRENT_SCHEMA_VERSION.equals(nvdVer)) {
135                 throw new SAXNotSupportedException("Schema version " + nvdVer + " is not supported");
136             }
137             vulnerabilities = new HashMap<>();
138         }
139     }
140 
141     @Override
endElement(String uri, String localName, String qName)142     public void endElement(String uri, String localName, String qName) throws SAXException {
143         current.setNode(qName);
144         if (current.isEntryNode()) {
145             if (!skip && hasPreviousVersion) {
146                 vulnerabilities.put(vulnerability, software);
147             }
148             vulnerability = null;
149             software = null;
150         }
151     }
152 
153     // <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
154     /**
155      * A simple class to maintain information about the current element while parsing the NVD CVE XML.
156      */
157     protected static class Element {
158 
159         /**
160          * A node type in the NVD CVE Schema 1.2.
161          */
162         public static final String NVD = "nvd";
163         /**
164          * A node type in the NVD CVE Schema 1.2.
165          */
166         public static final String ENTRY = "entry";
167         /**
168          * A node type in the NVD CVE Schema 1.2.
169          */
170         public static final String VULN_SOFTWARE = "vuln_soft";
171         /**
172          * A node type in the NVD CVE Schema 1.2.
173          */
174         public static final String PROD = "prod";
175         /**
176          * A node type in the NVD CVE Schema 1.2.
177          */
178         public static final String VERS = "vers";
179         /**
180          * The name of the current node.
181          */
182         private String node;
183 
184         /**
185          * Gets the value of node.
186          *
187          * @return the value of node
188          */
getNode()189         public String getNode() {
190             return this.node;
191         }
192 
193         /**
194          * Sets the value of node.
195          *
196          * @param node new value of node
197          */
setNode(String node)198         public void setNode(String node) {
199             this.node = node;
200         }
201 
202         /**
203          * Checks if the handler is at the NVD node.
204          *
205          * @return true or false
206          */
isNVDNode()207         public boolean isNVDNode() {
208             return NVD.equals(node);
209         }
210 
211         /**
212          * Checks if the handler is at the ENTRY node.
213          *
214          * @return true or false
215          */
isEntryNode()216         public boolean isEntryNode() {
217             return ENTRY.equals(node);
218         }
219 
220         /**
221          * Checks if the handler is at the VULN_SOFTWARE node.
222          *
223          * @return true or false
224          */
isVulnSoftwareNode()225         public boolean isVulnSoftwareNode() {
226             return VULN_SOFTWARE.equals(node);
227         }
228 
229         /**
230          * Checks if the handler is at the PROD node.
231          *
232          * @return true or false
233          */
isProdNode()234         public boolean isProdNode() {
235             return PROD.equals(node);
236         }
237 
238         /**
239          * Checks if the handler is at the VERS node.
240          *
241          * @return true or false
242          */
isVersNode()243         public boolean isVersNode() {
244             return VERS.equals(node);
245         }
246     }
247     // </editor-fold>
248 }
249