View Javadoc

    1   /*
    2    * This file is part of dependency-check-core.
    3    *
    4    * Dependency-check-core is free software: you can redistribute it and/or modify it
    5    * under the terms of the GNU General Public License as published by the Free
    6    * Software Foundation, either version 3 of the License, or (at your option) any
    7    * later version.
    8    *
    9    * Dependency-check-core is distributed in the hope that it will be useful, but
    10   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
    12   * details.
    13   *
    14   * You should have received a copy of the GNU General Public License along with
    15   * dependency-check-core. If not, see http://www.gnu.org/licenses/.
    16   *
    17   * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
    18   */
    19  package org.owasp.dependencycheck.data.nvdcve;
    20
    21  import java.io.IOException;
    22  import java.util.List;
    23  import java.util.Map;
    24  import java.util.logging.Level;
    25  import java.util.logging.Logger;
    26  import org.apache.lucene.index.CorruptIndexException;
    27  import org.owasp.dependencycheck.data.cpe.CpeIndexWriter;
    28  import org.owasp.dependencycheck.dependency.Reference;
    29  import org.owasp.dependencycheck.dependency.Vulnerability;
    30  import org.owasp.dependencycheck.dependency.VulnerableSoftware;
    31  import org.xml.sax.Attributes;
    32  import org.xml.sax.SAXException;
    33  import org.xml.sax.SAXNotSupportedException;
    34  import org.xml.sax.helpers.DefaultHandler;
    35
    36  /**
    37   * A SAX Handler that will parse the NVD CVE XML (schema version 2.0).
    38   *
    39   * @author Jeremy Long (jeremy.long@owasp.org)
    40   */
    41  public class NvdCve20Handler extends DefaultHandler {
    42
    43      /**
    44       * the current supported schema version.
    45       */
    46      private static final String CURRENT_SCHEMA_VERSION = "2.0";
    47      /**
    48       * the current element.
    49       */
    50      private final Element current = new Element();
    51      /**
    52       * the text of the node.
    53       */
    54      private StringBuilder nodeText;
    55      /**
    56       * the vulnerability.
    57       */
    58      private Vulnerability vulnerability;
    59      /**
    60       * a reference for the cve.
    61       */
    62      private Reference reference;
    63      /**
    64       * flag indicating whether the application has a cpe.
    65       */
    66      private boolean hasApplicationCpe = false;
    67      /**
    68       * The total number of entries parsed.
    69       */
    70      private int totalNumberOfEntries;
    71
    72      /**
    73       * Get the value of totalNumberOfEntries.
    74       *
    75       * @return the value of totalNumberOfEntries
    76       */
    77      public int getTotalNumberOfEntries() {
    78          return totalNumberOfEntries;
    79      }
    80      /**
    81       * The total number of application entries parsed.
    82       */
    83      private int totalNumberOfApplicationEntries;
    84
    85      /**
    86       * Get the value of totalNumberOfApplicationEntries.
    87       *
    88       * @return the value of totalNumberOfApplicationEntries
    89       */
    90      public int getTotalNumberOfApplicationEntries() {
    91          return totalNumberOfApplicationEntries;
    92      }
    93
    94      @Override
    95      public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    96          current.setNode(qName);
    97          if (current.isEntryNode()) {
    98              hasApplicationCpe = false;
    99              vulnerability = new Vulnerability();
    100             vulnerability.setName(attributes.getValue("id"));
    101         } else if (current.isVulnProductNode()) {
    102             nodeText = new StringBuilder(100);
    103         } else if (current.isVulnReferencesNode()) {
    104             final String lang = attributes.getValue("xml:lang");
    105             if ("en".equals(lang)) {
    106                 reference = new Reference();
    107             } else {
    108                 reference = null;
    109             }
    110         } else if (reference != null && current.isVulnReferenceNode()) {
    111             reference.setUrl(attributes.getValue("href"));
    112             nodeText = new StringBuilder(130);
    113         } else if (reference != null && current.isVulnSourceNode()) {
    114             nodeText = new StringBuilder(30);
    115         } else if (current.isVulnSummaryNode()) {
    116             nodeText = new StringBuilder(500);
    117         } else if (current.isNVDNode()) {
    118             final String nvdVer = attributes.getValue("nvd_xml_version");
    119             if (!CURRENT_SCHEMA_VERSION.equals(nvdVer)) {
    120                 throw new SAXNotSupportedException("Schema version " + nvdVer + " is not supported");
    121             }
    122         } else if (current.isVulnCWENode()) {
    123             vulnerability.setCwe(attributes.getValue("id"));
    124         } else if (current.isCVSSScoreNode()) {
    125             nodeText = new StringBuilder(5);
    126         } else if (current.isCVSSAccessVectorNode()) {
    127             nodeText = new StringBuilder(20);
    128         } else if (current.isCVSSAccessComplexityNode()) {
    129             nodeText = new StringBuilder(20);
    130         } else if (current.isCVSSAuthenticationNode()) {
    131             nodeText = new StringBuilder(20);
    132         } else if (current.isCVSSAvailabilityImpactNode()) {
    133             nodeText = new StringBuilder(20);
    134         } else if (current.isCVSSConfidentialityImpactNode()) {
    135             nodeText = new StringBuilder(20);
    136         } else if (current.isCVSSIntegrityImpactNode()) {
    137             nodeText = new StringBuilder(20);
    138         }
    139     }
    140
    141     @Override
    142     public void characters(char[] ch, int start, int length) throws SAXException {
    143         if (nodeText != null) {
    144             nodeText.append(ch, start, length);
    145         }
    146     }
    147
    148     @Override
    149     public void endElement(String uri, String localName, String qName) throws SAXException {
    150         current.setNode(qName);
    151         if (current.isEntryNode()) {
    152             totalNumberOfEntries += 1;
    153             if (hasApplicationCpe) {
    154                 totalNumberOfApplicationEntries += 1;
    155                 try {
    156                     saveEntry(vulnerability);
    157                 } catch (DatabaseException ex) {
    158                     throw new SAXException(ex);
    159                 } catch (CorruptIndexException ex) {
    160                     throw new SAXException(ex);
    161                 } catch (IOException ex) {
    162                     throw new SAXException(ex);
    163                 }
    164             }
    165             vulnerability = null;
    166         } else if (current.isCVSSScoreNode()) {
    167             try {
    168                 final float score = Float.parseFloat(nodeText.toString());
    169                 vulnerability.setCvssScore(score);
    170             } catch (NumberFormatException ex) {
    171                 Logger.getLogger(NvdCve20Handler.class.getName()).log(Level.SEVERE, "Error parsing CVSS Score.");
    172                 Logger.getLogger(NvdCve20Handler.class.getName()).log(Level.FINE, null, ex);
    173             }
    174             nodeText = null;
    175         } else if (current.isCVSSAccessVectorNode()) {
    176             vulnerability.setCvssAccessVector(nodeText.toString());
    177             nodeText = null;
    178         } else if (current.isCVSSAccessComplexityNode()) {
    179             vulnerability.setCvssAccessComplexity(nodeText.toString());
    180             nodeText = null;
    181         } else if (current.isCVSSAuthenticationNode()) {
    182             vulnerability.setCvssAuthentication(nodeText.toString());
    183             nodeText = null;
    184         } else if (current.isCVSSAvailabilityImpactNode()) {
    185             vulnerability.setCvssAvailabilityImpact(nodeText.toString());
    186             nodeText = null;
    187         } else if (current.isCVSSConfidentialityImpactNode()) {
    188             vulnerability.setCvssConfidentialityImpact(nodeText.toString());
    189             nodeText = null;
    190         } else if (current.isCVSSIntegrityImpactNode()) {
    191             vulnerability.setCvssIntegrityImpact(nodeText.toString());
    192             nodeText = null;
    193         } else if (current.isVulnProductNode()) {
    194             final String cpe = nodeText.toString();
    195             if (cpe.startsWith("cpe:/a:")) {
    196                 hasApplicationCpe = true;
    197                 vulnerability.addVulnerableSoftware(cpe);
    198             }
    199             nodeText = null;
    200         } else if (reference != null && current.isVulnReferencesNode()) {
    201             vulnerability.addReference(reference);
    202             reference = null;
    203         } else if (reference != null && current.isVulnReferenceNode()) {
    204             reference.setName(nodeText.toString());
    205             nodeText = null;
    206         } else if (reference != null && current.isVulnSourceNode()) {
    207             reference.setSource(nodeText.toString());
    208             nodeText = null;
    209         } else if (current.isVulnSummaryNode()) {
    210             vulnerability.setDescription(nodeText.toString());
    211             nodeText = null;
    212         }
    213     }
    214     /**
    215      * the cve database.
    216      */
    217     private CveDB cveDB;
    218
    219     /**
    220      * Sets the cveDB.
    221      *
    222      * @param db a reference to the CveDB
    223      */
    224     public void setCveDB(CveDB db) {
    225         cveDB = db;
    226     }
    227     /**
    228      * A list of CVE entries and associated VulnerableSoftware entries that
    229      * contain previous entries.
    230      */
    231     private Map<String, List<VulnerableSoftware>> prevVersionVulnMap;
    232
    233     /**
    234      * Sets the prevVersionVulnMap.
    235      *
    236      * @param map the map of vulnerable software with previous versions being
    237      * vulnerable
    238      */
    239     public void setPrevVersionVulnMap(Map<String, List<VulnerableSoftware>> map) {
    240         prevVersionVulnMap = map;
    241     }
    242
    243     /**
    244      * Saves a vulnerability to the CVE Database.
    245      *
    246      * @param vuln the vulnerability to store in the database
    247      * @throws DatabaseException thrown if there is an error writing to the
    248      * database
    249      * @throws CorruptIndexException is thrown if the CPE Index is corrupt
    250      * @throws IOException thrown if there is an IOException with the CPE Index
    251      */
    252     private void saveEntry(Vulnerability vuln) throws DatabaseException, CorruptIndexException, IOException {
    253         if (cveDB == null) {
    254             return;
    255         }
    256         final String cveName = vuln.getName();
    257         if (prevVersionVulnMap.containsKey(cveName)) {
    258             final List<VulnerableSoftware> vulnSoftware = prevVersionVulnMap.get(cveName);
    259             for (VulnerableSoftware vs : vulnSoftware) {
    260                 vuln.updateVulnerableSoftware(vs);
    261             }
    262         }
    263         for (VulnerableSoftware vs : vuln.getVulnerableSoftware()) {
    264             if (cpeIndex != null) {
    265                 cpeIndex.saveEntry(vs);
    266             }
    267         }
    268         cveDB.updateVulnerability(vuln);
    269     }
    270     /**
    271      * the cpe index.
    272      */
    273     private CpeIndexWriter cpeIndex;
    274
    275     /**
    276      * Sets the cpe index writer.
    277      *
    278      * @param index the CPE Lucene Index
    279      */
    280     public void setCpeIndex(CpeIndexWriter index) {
    281         cpeIndex = index;
    282     }
    283
    284     // <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
    285     /**
    286      * A simple class to maintain information about the current element while
    287      * parsing the NVD CVE XML.
    288      */
    289     protected static class Element {
    290
    291         /**
    292          * A node type in the NVD CVE Schema 2.0
    293          */
    294         public static final String NVD = "nvd";
    295         /**
    296          * A node type in the NVD CVE Schema 2.0
    297          */
    298         public static final String ENTRY = "entry";
    299         /**
    300          * A node type in the NVD CVE Schema 2.0
    301          */
    302         public static final String VULN_PRODUCT = "vuln:product";
    303         /**
    304          * A node type in the NVD CVE Schema 2.0
    305          */
    306         public static final String VULN_REFERENCES = "vuln:references";
    307         /**
    308          * A node type in the NVD CVE Schema 2.0
    309          */
    310         public static final String VULN_SOURCE = "vuln:source";
    311         /**
    312          * A node type in the NVD CVE Schema 2.0
    313          */
    314         public static final String VULN_REFERENCE = "vuln:reference";
    315         /**
    316          * A node type in the NVD CVE Schema 2.0
    317          */
    318         public static final String VULN_SUMMARY = "vuln:summary";
    319         /**
    320          * A node type in the NVD CVE Schema 2.0
    321          */
    322         public static final String VULN_CWE = "vuln:cwe";
    323         /**
    324          * A node type in the NVD CVE Schema 2.0
    325          */
    326         public static final String CVSS_SCORE = "cvss:score";
    327         /**
    328          * A node type in the NVD CVE Schema 2.0
    329          */
    330         public static final String CVSS_ACCESS_VECTOR = "cvss:access-vector";
    331         /**
    332          * A node type in the NVD CVE Schema 2.0
    333          */
    334         public static final String CVSS_ACCESS_COMPLEXITY = "cvss:access-complexity";
    335         /**
    336          * A node type in the NVD CVE Schema 2.0
    337          */
    338         public static final String CVSS_AUTHENTICATION = "cvss:authentication";
    339         /**
    340          * A node type in the NVD CVE Schema 2.0
    341          */
    342         public static final String CVSS_CONFIDENTIALITY_IMPACT = "cvss:confidentiality-impact";
    343         /**
    344          * A node type in the NVD CVE Schema 2.0
    345          */
    346         public static final String CVSS_INTEGRITY_IMPACT = "cvss:integrity-impact";
    347         /**
    348          * A node type in the NVD CVE Schema 2.0
    349          */
    350         public static final String CVSS_AVAILABILITY_IMPACT = "cvss:availability-impact";
    351         /**
    352          * The current node.
    353          */
    354         private String node;
    355
    356         /**
    357          * Gets the value of node.
    358          *
    359          * @return the value of node
    360          */
    361         public String getNode() {
    362             return this.node;
    363         }
    364
    365         /**
    366          * Sets the value of node.
    367          *
    368          * @param node new value of node
    369          */
    370         public void setNode(String node) {
    371             this.node = node;
    372         }
    373
    374         /**
    375          * Checks if the handler is at the NVD node.
    376          *
    377          * @return true or false
    378          */
    379         public boolean isNVDNode() {
    380             return NVD.equals(node);
    381         }
    382
    383         /**
    384          * Checks if the handler is at the ENTRY node.
    385          *
    386          * @return true or false
    387          */
    388         public boolean isEntryNode() {
    389             return ENTRY.equals(node);
    390         }
    391
    392         /**
    393          * Checks if the handler is at the VULN_PRODUCT node.
    394          *
    395          * @return true or false
    396          */
    397         public boolean isVulnProductNode() {
    398             return VULN_PRODUCT.equals(node);
    399         }
    400
    401         /**
    402          * Checks if the handler is at the REFERENCES node.
    403          *
    404          * @return true or false
    405          */
    406         public boolean isVulnReferencesNode() {
    407             return VULN_REFERENCES.equals(node);
    408         }
    409
    410         /**
    411          * Checks if the handler is at the REFERENCE node.
    412          *
    413          * @return true or false
    414          */
    415         public boolean isVulnReferenceNode() {
    416             return VULN_REFERENCE.equals(node);
    417         }
    418
    419         /**
    420          * Checks if the handler is at the VULN_SOURCE node.
    421          *
    422          * @return true or false
    423          */
    424         public boolean isVulnSourceNode() {
    425             return VULN_SOURCE.equals(node);
    426         }
    427
    428         /**
    429          * Checks if the handler is at the VULN_SUMMARY node.
    430          *
    431          * @return true or false
    432          */
    433         public boolean isVulnSummaryNode() {
    434             return VULN_SUMMARY.equals(node);
    435         }
    436
    437         /**
    438          * Checks if the handler is at the VULN_CWE node.
    439          *
    440          * @return true or false
    441          */
    442         public boolean isVulnCWENode() {
    443             return VULN_CWE.equals(node);
    444         }
    445
    446         /**
    447          * Checks if the handler is at the CVSS_SCORE node.
    448          *
    449          * @return true or false
    450          */
    451         public boolean isCVSSScoreNode() {
    452             return CVSS_SCORE.equals(node);
    453         }
    454
    455         /**
    456          * Checks if the handler is at the CVSS_ACCESS_VECTOR node.
    457          *
    458          * @return true or false
    459          */
    460         public boolean isCVSSAccessVectorNode() {
    461             return CVSS_ACCESS_VECTOR.equals(node);
    462         }
    463
    464         /**
    465          * Checks if the handler is at the CVSS_ACCESS_COMPLEXITY node.
    466          *
    467          * @return true or false
    468          */
    469         public boolean isCVSSAccessComplexityNode() {
    470             return CVSS_ACCESS_COMPLEXITY.equals(node);
    471         }
    472
    473         /**
    474          * Checks if the handler is at the CVSS_AUTHENTICATION node.
    475          *
    476          * @return true or false
    477          */
    478         public boolean isCVSSAuthenticationNode() {
    479             return CVSS_AUTHENTICATION.equals(node);
    480         }
    481
    482         /**
    483          * Checks if the handler is at the CVSS_CONFIDENTIALITY_IMPACT node.
    484          *
    485          * @return true or false
    486          */
    487         public boolean isCVSSConfidentialityImpactNode() {
    488             return CVSS_CONFIDENTIALITY_IMPACT.equals(node);
    489         }
    490
    491         /**
    492          * Checks if the handler is at the CVSS_INTEGRITY_IMPACT node.
    493          *
    494          * @return true or false
    495          */
    496         public boolean isCVSSIntegrityImpactNode() {
    497             return CVSS_INTEGRITY_IMPACT.equals(node);
    498         }
    499
    500         /**
    501          * Checks if the handler is at the CVSS_AVAILABILITY_IMPACT node.
    502          *
    503          * @return true or false
    504          */
    505         public boolean isCVSSAvailabilityImpactNode() {
    506             return CVSS_AVAILABILITY_IMPACT.equals(node);
    507         }
    508     }
    509     // </editor-fold>
    510 }