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