/*
* This file is part of dependency-check-core.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.data.update.xml;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.index.CorruptIndexException;
import org.owasp.dependencycheck.data.nvdcve.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.dependency.Reference;
import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.helpers.DefaultHandler;
/**
* A SAX Handler that will parse the NVD CVE XML (schema version 2.0).
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
public class NvdCve20Handler extends DefaultHandler {
/**
* The logger.
*/
private static final Logger LOGGER = Logger.getLogger(NvdCve20Handler.class.getName());
/**
* the current supported schema version.
*/
private static final String CURRENT_SCHEMA_VERSION = "2.0";
/**
* the current element.
*/
private final Element current = new Element();
/**
* the text of the node.
*/
private StringBuilder nodeText;
/**
* the vulnerability.
*/
private Vulnerability vulnerability;
/**
* a reference for the cve.
*/
private Reference reference;
/**
* flag indicating whether the application has a cpe.
*/
private boolean hasApplicationCpe = false;
/**
* The total number of entries parsed.
*/
private int totalNumberOfEntries;
/**
* Get the value of totalNumberOfEntries.
*
* @return the value of totalNumberOfEntries
*/
public int getTotalNumberOfEntries() {
return totalNumberOfEntries;
}
/**
* The total number of application entries parsed.
*/
private int totalNumberOfApplicationEntries;
/**
* Get the value of totalNumberOfApplicationEntries.
*
* @return the value of totalNumberOfApplicationEntries
*/
public int getTotalNumberOfApplicationEntries() {
return totalNumberOfApplicationEntries;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
current.setNode(qName);
if (current.isEntryNode()) {
hasApplicationCpe = false;
vulnerability = new Vulnerability();
vulnerability.setName(attributes.getValue("id"));
} else if (current.isVulnProductNode()) {
nodeText = new StringBuilder(100);
} else if (current.isVulnReferencesNode()) {
final String lang = attributes.getValue("xml:lang");
if ("en".equals(lang)) {
reference = new Reference();
} else {
reference = null;
}
} else if (reference != null && current.isVulnReferenceNode()) {
reference.setUrl(attributes.getValue("href"));
nodeText = new StringBuilder(130);
} else if (reference != null && current.isVulnSourceNode()) {
nodeText = new StringBuilder(30);
} else if (current.isVulnSummaryNode()) {
nodeText = new StringBuilder(500);
} else if (current.isNVDNode()) {
final String nvdVer = attributes.getValue("nvd_xml_version");
if (!CURRENT_SCHEMA_VERSION.equals(nvdVer)) {
throw new SAXNotSupportedException("Schema version " + nvdVer + " is not supported");
}
} else if (current.isVulnCWENode()) {
vulnerability.setCwe(attributes.getValue("id"));
} else if (current.isCVSSScoreNode()) {
nodeText = new StringBuilder(5);
} else if (current.isCVSSAccessVectorNode()) {
nodeText = new StringBuilder(20);
} else if (current.isCVSSAccessComplexityNode()) {
nodeText = new StringBuilder(20);
} else if (current.isCVSSAuthenticationNode()) {
nodeText = new StringBuilder(20);
} else if (current.isCVSSAvailabilityImpactNode()) {
nodeText = new StringBuilder(20);
} else if (current.isCVSSConfidentialityImpactNode()) {
nodeText = new StringBuilder(20);
} else if (current.isCVSSIntegrityImpactNode()) {
nodeText = new StringBuilder(20);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (nodeText != null) {
nodeText.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
current.setNode(qName);
if (current.isEntryNode()) {
totalNumberOfEntries += 1;
if (hasApplicationCpe) {
totalNumberOfApplicationEntries += 1;
try {
saveEntry(vulnerability);
} catch (DatabaseException ex) {
throw new SAXException(ex);
} catch (CorruptIndexException ex) {
throw new SAXException(ex);
} catch (IOException ex) {
throw new SAXException(ex);
}
}
vulnerability = null;
} else if (current.isCVSSScoreNode()) {
try {
final float score = Float.parseFloat(nodeText.toString());
vulnerability.setCvssScore(score);
} catch (NumberFormatException ex) {
LOGGER.log(Level.SEVERE, "Error parsing CVSS Score.");
LOGGER.log(Level.FINE, null, ex);
}
nodeText = null;
} else if (current.isCVSSAccessVectorNode()) {
vulnerability.setCvssAccessVector(nodeText.toString());
nodeText = null;
} else if (current.isCVSSAccessComplexityNode()) {
vulnerability.setCvssAccessComplexity(nodeText.toString());
nodeText = null;
} else if (current.isCVSSAuthenticationNode()) {
vulnerability.setCvssAuthentication(nodeText.toString());
nodeText = null;
} else if (current.isCVSSAvailabilityImpactNode()) {
vulnerability.setCvssAvailabilityImpact(nodeText.toString());
nodeText = null;
} else if (current.isCVSSConfidentialityImpactNode()) {
vulnerability.setCvssConfidentialityImpact(nodeText.toString());
nodeText = null;
} else if (current.isCVSSIntegrityImpactNode()) {
vulnerability.setCvssIntegrityImpact(nodeText.toString());
nodeText = null;
} else if (current.isVulnProductNode()) {
final String cpe = nodeText.toString();
if (cpe.startsWith("cpe:/a:")) {
hasApplicationCpe = true;
vulnerability.addVulnerableSoftware(cpe);
}
nodeText = null;
} else if (reference != null && current.isVulnReferencesNode()) {
vulnerability.addReference(reference);
reference = null;
} else if (reference != null && current.isVulnReferenceNode()) {
reference.setName(nodeText.toString());
nodeText = null;
} else if (reference != null && current.isVulnSourceNode()) {
reference.setSource(nodeText.toString());
nodeText = null;
} else if (current.isVulnSummaryNode()) {
vulnerability.setDescription(nodeText.toString());
if (nodeText.indexOf("** REJECT **") >= 0) {
hasApplicationCpe = true; //ensure we process this to delete the vuln
}
nodeText = null;
}
}
/**
* the cve database.
*/
private CveDB cveDB;
/**
* Sets the cveDB.
*
* @param db a reference to the CveDB
*/
public void setCveDB(CveDB db) {
cveDB = db;
}
/**
* A list of CVE entries and associated VulnerableSoftware entries that contain previous entries.
*/
private Map<String, List<VulnerableSoftware>> prevVersionVulnMap;
/**
* Sets the prevVersionVulnMap.
*
* @param map the map of vulnerable software with previous versions being vulnerable
*/
public void setPrevVersionVulnMap(Map<String, List<VulnerableSoftware>> map) {
prevVersionVulnMap = map;
}
/**
* Saves a vulnerability to the CVE Database.
*
* @param vuln the vulnerability to store in the database
* @throws DatabaseException thrown if there is an error writing to the database
* @throws CorruptIndexException is thrown if the CPE Index is corrupt
* @throws IOException thrown if there is an IOException with the CPE Index
*/
private void saveEntry(Vulnerability vuln) throws DatabaseException, CorruptIndexException, IOException {
if (cveDB == null) {
return;
}
final String cveName = vuln.getName();
if (prevVersionVulnMap.containsKey(cveName)) {
final List<VulnerableSoftware> vulnSoftware = prevVersionVulnMap.get(cveName);
for (VulnerableSoftware vs : vulnSoftware) {
vuln.updateVulnerableSoftware(vs);
}
}
cveDB.updateVulnerability(vuln);
}
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
/**
* A simple class to maintain information about the current element while parsing the NVD CVE XML.
*/
protected static class Element {
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String NVD = "nvd";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String ENTRY = "entry";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String VULN_PRODUCT = "vuln:product";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String VULN_REFERENCES = "vuln:references";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String VULN_SOURCE = "vuln:source";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String VULN_REFERENCE = "vuln:reference";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String VULN_SUMMARY = "vuln:summary";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String VULN_CWE = "vuln:cwe";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String CVSS_SCORE = "cvss:score";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String CVSS_ACCESS_VECTOR = "cvss:access-vector";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String CVSS_ACCESS_COMPLEXITY = "cvss:access-complexity";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String CVSS_AUTHENTICATION = "cvss:authentication";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String CVSS_CONFIDENTIALITY_IMPACT = "cvss:confidentiality-impact";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String CVSS_INTEGRITY_IMPACT = "cvss:integrity-impact";
/**
* A node type in the NVD CVE Schema 2.0
*/
public static final String CVSS_AVAILABILITY_IMPACT = "cvss:availability-impact";
/**
* The current node.
*/
private String node;
/**
* Gets the value of node.
*
* @return the value of node
*/
public String getNode() {
return this.node;
}
/**
* Sets the value of node.
*
* @param node new value of node
*/
public void setNode(String node) {
this.node = node;
}
/**
* Checks if the handler is at the NVD node.
*
* @return true or false
*/
public boolean isNVDNode() {
return NVD.equals(node);
}
/**
* Checks if the handler is at the ENTRY node.
*
* @return true or false
*/
public boolean isEntryNode() {
return ENTRY.equals(node);
}
/**
* Checks if the handler is at the VULN_PRODUCT node.
*
* @return true or false
*/
public boolean isVulnProductNode() {
return VULN_PRODUCT.equals(node);
}
/**
* Checks if the handler is at the REFERENCES node.
*
* @return true or false
*/
public boolean isVulnReferencesNode() {
return VULN_REFERENCES.equals(node);
}
/**
* Checks if the handler is at the REFERENCE node.
*
* @return true or false
*/
public boolean isVulnReferenceNode() {
return VULN_REFERENCE.equals(node);
}
/**
* Checks if the handler is at the VULN_SOURCE node.
*
* @return true or false
*/
public boolean isVulnSourceNode() {
return VULN_SOURCE.equals(node);
}
/**
* Checks if the handler is at the VULN_SUMMARY node.
*
* @return true or false
*/
public boolean isVulnSummaryNode() {
return VULN_SUMMARY.equals(node);
}
/**
* Checks if the handler is at the VULN_CWE node.
*
* @return true or false
*/
public boolean isVulnCWENode() {
return VULN_CWE.equals(node);
}
/**
* Checks if the handler is at the CVSS_SCORE node.
*
* @return true or false
*/
public boolean isCVSSScoreNode() {
return CVSS_SCORE.equals(node);
}
/**
* Checks if the handler is at the CVSS_ACCESS_VECTOR node.
*
* @return true or false
*/
public boolean isCVSSAccessVectorNode() {
return CVSS_ACCESS_VECTOR.equals(node);
}
/**
* Checks if the handler is at the CVSS_ACCESS_COMPLEXITY node.
*
* @return true or false
*/
public boolean isCVSSAccessComplexityNode() {
return CVSS_ACCESS_COMPLEXITY.equals(node);
}
/**
* Checks if the handler is at the CVSS_AUTHENTICATION node.
*
* @return true or false
*/
public boolean isCVSSAuthenticationNode() {
return CVSS_AUTHENTICATION.equals(node);
}
/**
* Checks if the handler is at the CVSS_CONFIDENTIALITY_IMPACT node.
*
* @return true or false
*/
public boolean isCVSSConfidentialityImpactNode() {
return CVSS_CONFIDENTIALITY_IMPACT.equals(node);
}
/**
* Checks if the handler is at the CVSS_INTEGRITY_IMPACT node.
*
* @return true or false
*/
public boolean isCVSSIntegrityImpactNode() {
return CVSS_INTEGRITY_IMPACT.equals(node);
}
/**
* Checks if the handler is at the CVSS_AVAILABILITY_IMPACT node.
*
* @return true or false
*/
public boolean isCVSSAvailabilityImpactNode() {
return CVSS_AVAILABILITY_IMPACT.equals(node);
}
}
// </editor-fold>
}