Coverage Report - org.owasp.dependencycheck.data.nvdcve.CveDB
 
Classes in this File Line Coverage Branch Coverage Complexity
CveDB
46%
147/319
51%
48/94
5
 
 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.BufferedReader;
 22  
 import java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.io.InputStreamReader;
 26  
 import java.io.UnsupportedEncodingException;
 27  
 import java.sql.Connection;
 28  
 import java.sql.DriverManager;
 29  
 import java.sql.PreparedStatement;
 30  
 import java.sql.ResultSet;
 31  
 import java.sql.SQLException;
 32  
 import java.sql.Statement;
 33  
 import java.util.ArrayList;
 34  
 import java.util.HashSet;
 35  
 import java.util.List;
 36  
 import java.util.Set;
 37  
 import java.util.logging.Level;
 38  
 import java.util.logging.Logger;
 39  
 import org.owasp.dependencycheck.data.cwe.CweDB;
 40  
 import org.owasp.dependencycheck.dependency.Reference;
 41  
 import org.owasp.dependencycheck.dependency.Vulnerability;
 42  
 import org.owasp.dependencycheck.dependency.VulnerableSoftware;
 43  
 import org.owasp.dependencycheck.utils.DependencyVersion;
 44  
 import org.owasp.dependencycheck.utils.DependencyVersionUtil;
 45  
 import org.owasp.dependencycheck.utils.Settings;
 46  
 
 47  
 /**
 48  
  * The database holding information about the NVD CVE data.
 49  
  *
 50  
  * @author Jeremy Long (jeremy.long@owasp.org)
 51  
  */
 52  33
 public class CveDB {
 53  
 
 54  
     /**
 55  
      * Resource location for SQL file used to create the database schema.
 56  
      */
 57  
     public static final String DB_STRUCTURE_RESOURCE = "data/initialize.sql";
 58  
     /**
 59  
      * The version of the current DB Schema.
 60  
      */
 61  
     public static final String DB_SCHEMA_VERSION = "2.5";
 62  
     /**
 63  
      * Database connection
 64  
      */
 65  
     private Connection conn;
 66  
     //<editor-fold defaultstate="collapsed" desc="Constants to create, maintain, and retrieve data from the CVE Database">
 67  
     /**
 68  
      * SQL Statement to delete references by vulnerability ID.
 69  
      */
 70  
     public static final String DELETE_REFERENCE = "DELETE FROM reference WHERE cveid = ?";
 71  
     /**
 72  
      * SQL Statement to delete software by vulnerability ID.
 73  
      */
 74  
     public static final String DELETE_SOFTWARE = "DELETE FROM software WHERE cveid = ?";
 75  
     /**
 76  
      * SQL Statement to delete a vulnerability by CVE.
 77  
      */
 78  
     public static final String DELETE_VULNERABILITY = "DELETE FROM vulnerability WHERE cve = ?";
 79  
     /**
 80  
      * SQL Statement to cleanup orphan entries. Yes, the db schema could be a
 81  
      * little tighter, but what we have works well to keep the data file size
 82  
      * down a bit.
 83  
      */
 84  
     public static final String CLEANUP_ORPHANS = "DELETE FROM CpeEntry WHERE id not in (SELECT CPEEntryId FROM Software); ";
 85  
     /**
 86  
      * SQL Statement to insert a new reference.
 87  
      */
 88  
     public static final String INSERT_REFERENCE = "INSERT INTO reference (cveid, name, url, source) VALUES (?, ?, ?, ?)";
 89  
     /**
 90  
      * SQL Statement to insert a new software.
 91  
      */
 92  
     public static final String INSERT_SOFTWARE = "INSERT INTO software (cveid, cpeEntryId, previousVersion) VALUES (?, ?, ?)";
 93  
     /**
 94  
      * SQL Statement to insert a new cpe.
 95  
      */
 96  
     public static final String INSERT_CPE = "INSERT INTO cpeEntry (cpe, vendor, product) VALUES (?, ?, ?)";
 97  
     /**
 98  
      * SQL Statement to get a CPEProductID.
 99  
      */
 100  
     public static final String SELECT_CPE_ID = "SELECT id FROM cpeEntry WHERE cpe = ?";
 101  
     /**
 102  
      * SQL Statement to insert a new vulnerability.
 103  
      */
 104  
     public static final String INSERT_VULNERABILITY = "INSERT INTO vulnerability (cve, description, cwe, cvssScore, cvssAccessVector, "
 105  
             + "cvssAccessComplexity, cvssAuthentication, cvssConfidentialityImpact, cvssIntegrityImpact, cvssAvailabilityImpact) "
 106  
             + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
 107  
     /**
 108  
      * SQL Statement to update a vulnerability.
 109  
      */
 110  
     public static final String UPDATE_VULNERABILITY = "UPDATE vulnerability SET description=?, cwe=?, cvssScore=?, cvssAccessVector=?, "
 111  
             + "cvssAccessComplexity=?, cvssAuthentication=?, cvssConfidentialityImpact=?, cvssIntegrityImpact=?, cvssAvailabilityImpact=? "
 112  
             + "WHERE id=?";
 113  
     /**
 114  
      * SQL Statement to find CVE entries based on CPE data.
 115  
      */
 116  
     public static final String SELECT_CVE_FROM_SOFTWARE = "SELECT cve, cpe, previousVersion "
 117  
             + "FROM software INNER JOIN vulnerability ON vulnerability.id = software.cveId "
 118  
             + "INNER JOIN cpeEntry ON cpeEntry.id = software.cpeEntryId "
 119  
             + "WHERE vendor = ? AND product = ?";
 120  
     //unfortunately, the version info is too complicated to do in a select. Need to filter this afterwards
 121  
     //        + " AND (version = '-' OR previousVersion IS NOT NULL OR version=?)";
 122  
     //
 123  
     /**
 124  
      * SQL Statement to find the CPE entry based on the vendor and product.
 125  
      */
 126  
     public static final String SELECT_CPE_ENTRIES = "SELECT cpe FROM cpeEntry WHERE vendor = ? AND product = ?";
 127  
     /**
 128  
      * SQL Statement to select references by CVEID.
 129  
      */
 130  
     public static final String SELECT_REFERENCE = "SELECT source, name, url FROM reference WHERE cveid = ?";
 131  
     /**
 132  
      * SQL Statement to select software by CVEID.
 133  
      */
 134  
     public static final String SELECT_SOFTWARE = "SELECT cpe, previousVersion "
 135  
             + "FROM software INNER JOIN cpeEntry ON software.cpeEntryId = cpeEntry.id WHERE cveid = ?";
 136  
 //    public static final String SELECT_SOFTWARE = "SELECT part, vendor, product, version, revision, previousVersion "
 137  
 //            + "FROM software INNER JOIN cpeProduct ON cpeProduct.id = software.cpeProductId LEFT JOIN cpeVersion ON "
 138  
 //            + "software.cpeVersionId = cpeVersion.id LEFT JOIN Version ON cpeVersion.versionId = version.id WHERE cveid = ?";
 139  
     /**
 140  
      * SQL Statement to select a vulnerability by CVEID.
 141  
      */
 142  
     public static final String SELECT_VULNERABILITY = "SELECT id, description, cwe, cvssScore, cvssAccessVector, cvssAccessComplexity, "
 143  
             + "cvssAuthentication, cvssConfidentialityImpact, cvssIntegrityImpact, cvssAvailabilityImpact FROM vulnerability WHERE cve = ?";
 144  
     /**
 145  
      * SQL Statement to select a vulnerability's primary key.
 146  
      */
 147  
     public static final String SELECT_VULNERABILITY_ID = "SELECT id FROM vulnerability WHERE cve = ?";
 148  
     //</editor-fold>
 149  
 
 150  
     /**
 151  
      * Opens the database connection. If the database does not exist, it will
 152  
      * create a new one.
 153  
      *
 154  
      * @throws IOException thrown if there is an IO Exception
 155  
      * @throws SQLException thrown if there is a SQL Exception
 156  
      * @throws DatabaseException thrown if there is an error initializing a new
 157  
      * database
 158  
      * @throws ClassNotFoundException thrown if the h2 database driver cannot be
 159  
      * loaded
 160  
      */
 161  
     @edu.umd.cs.findbugs.annotations.SuppressWarnings(
 162  
             value = "DMI_EMPTY_DB_PASSWORD",
 163  
             justification = "Yes, I know... Blank password.")
 164  
     public void open() throws IOException, SQLException, DatabaseException, ClassNotFoundException {
 165  
         /*
 166  
          * TODO - make it so we can exteralize the database (lucene index is a problem), could I store it as a blob
 167  
          *        and just download it when needed?
 168  
          */
 169  
 //        String dbDriver = Settings.getString(Settings.KEYS.DB_DRIVER);
 170  
 //        String dbConnStr = Settings.getString(Settings.KEYS.DB_CONNECTION_STRING);
 171  
 //        if (dbDriver != null && dbConnStr != null) {
 172  
 //            Class.forName(dbDriver);
 173  
 //            conn = DriverManager.getConnection(dbConnStr);
 174  
 //        } else { //use the embeded version
 175  33
         final String fileName = CveDB.getDataDirectory().getCanonicalPath();
 176  33
         final File f = new File(fileName, "cve." + DB_SCHEMA_VERSION);
 177  33
         final File check = new File(f.getAbsolutePath() + ".h2.db");
 178  33
         final boolean createTables = !check.exists();
 179  33
         final String connStr = "jdbc:h2:file:" + f.getAbsolutePath();
 180  33
         Class.forName("org.h2.Driver");
 181  33
         conn = DriverManager.getConnection(connStr, "sa", "");
 182  33
         if (createTables) {
 183  0
             createTables();
 184  
         }
 185  
 //        }
 186  33
     }
 187  
 
 188  
     /**
 189  
      * Commits all completed transactions.
 190  
      *
 191  
      * @throws SQLException thrown if a SQL Exception occurs
 192  
      */
 193  
     public void commit() throws SQLException {
 194  3
         if (conn != null) {
 195  3
             conn.commit();
 196  
         }
 197  3
     }
 198  
 
 199  
     /**
 200  
      * Cleans up the object and ensures that "close" has been called.
 201  
      *
 202  
      * @throws Throwable thrown if there is a problem
 203  
      */
 204  
     @Override
 205  
     protected void finalize() throws Throwable {
 206  15
         close();
 207  15
         super.finalize(); //not necessary if extending Object.
 208  15
     }
 209  
 
 210  
     /**
 211  
      * Closes the DB4O database. Close should be called on this object when it
 212  
      * is done being used.
 213  
      */
 214  
     public void close() {
 215  48
         if (conn != null) {
 216  
             try {
 217  33
                 conn.close();
 218  0
             } catch (SQLException ex) {
 219  0
                 final String msg = "There was an error attempting to close the CveDB, see the log for more details.";
 220  0
                 Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, msg, ex);
 221  0
                 Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
 222  33
             }
 223  33
             conn = null;
 224  
         }
 225  48
     }
 226  
 
 227  
     /**
 228  
      * Searches the CPE entries in the database and retrieves all entries for a
 229  
      * given vendor and product combination. The returned list will include all
 230  
      * versions of the product that are registered in the NVD CVE data.
 231  
      *
 232  
      * @param vendor the identified vendor name of the dependency being analyzed
 233  
      * @param product the identified name of the product of the dependency being
 234  
      * analyzed
 235  
      * @return a set of vulnerable software
 236  
      */
 237  
     public Set<VulnerableSoftware> getCPEs(String vendor, String product) {
 238  42
         final Set<VulnerableSoftware> cpe = new HashSet<VulnerableSoftware>();
 239  42
         ResultSet rs = null;
 240  42
         PreparedStatement ps = null;
 241  
         try {
 242  42
             ps = conn.prepareStatement(SELECT_CPE_ENTRIES);
 243  42
             ps.setString(1, vendor);
 244  42
             ps.setString(2, product);
 245  42
             rs = ps.executeQuery();
 246  
 
 247  1088
             while (rs.next()) {
 248  1046
                 final VulnerableSoftware vs = new VulnerableSoftware();
 249  1046
                 vs.setCpe(rs.getString(1));
 250  1046
                 cpe.add(vs);
 251  1046
             }
 252  0
         } catch (SQLException ex) {
 253  0
             Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
 254  
         } finally {
 255  42
             closeResultSet(rs);
 256  42
             closeStatement(ps);
 257  42
         }
 258  42
         return cpe;
 259  
     }
 260  
 
 261  
     /**
 262  
      * Retrieves the vulnerabilities associated with the specified CPE.
 263  
      *
 264  
      * @param cpeStr the CPE name
 265  
      * @return a list of Vulnerabilities
 266  
      * @throws DatabaseException thrown if there is an exception retrieving data
 267  
      */
 268  
     public List<Vulnerability> getVulnerabilities(String cpeStr) throws DatabaseException {
 269  3
         ResultSet rs = null;
 270  3
         final VulnerableSoftware cpe = new VulnerableSoftware();
 271  
         try {
 272  3
             cpe.parseName(cpeStr);
 273  0
         } catch (UnsupportedEncodingException ex) {
 274  0
             Logger.getLogger(CveDB.class.getName()).log(Level.FINEST, null, ex);
 275  3
         }
 276  3
         final DependencyVersion detectedVersion = parseDependencyVersion(cpe);
 277  3
         final List<Vulnerability> vulnerabilities = new ArrayList<Vulnerability>();
 278  
 
 279  
         PreparedStatement ps;
 280  3
         final HashSet<String> cveEntries = new HashSet<String>();
 281  
         try {
 282  3
             ps = conn.prepareStatement(SELECT_CVE_FROM_SOFTWARE);
 283  3
             ps.setString(1, cpe.getVendor());
 284  3
             ps.setString(2, cpe.getProduct());
 285  3
             rs = ps.executeQuery();
 286  1901
             while (rs.next()) {
 287  1898
                 final String cveId = rs.getString(1);
 288  1898
                 final String cpeId = rs.getString(2);
 289  1898
                 final String previous = rs.getString(3);
 290  1898
                 if (!cveEntries.contains(cveId) && isAffected(cpe.getVendor(), cpe.getProduct(), detectedVersion, cpeId, previous)) {
 291  54
                     cveEntries.add(cveId);
 292  
                 }
 293  1898
             }
 294  3
             closeResultSet(rs);
 295  3
             closeStatement(ps);
 296  3
             for (String cve : cveEntries) {
 297  54
                 final Vulnerability v = getVulnerability(cve);
 298  54
                 vulnerabilities.add(v);
 299  54
             }
 300  
 
 301  0
         } catch (SQLException ex) {
 302  0
             throw new DatabaseException("Exception retrieving vulnerability for " + cpeStr, ex);
 303  
         } finally {
 304  3
             closeResultSet(rs);
 305  3
         }
 306  3
         return vulnerabilities;
 307  
     }
 308  
 
 309  
     /**
 310  
      * Gets a vulnerability for the provided CVE.
 311  
      *
 312  
      * @param cve the CVE to lookup
 313  
      * @return a vulnerability object
 314  
      * @throws DatabaseException if an exception occurs
 315  
      */
 316  
     private Vulnerability getVulnerability(String cve) throws DatabaseException {
 317  54
         PreparedStatement psV = null;
 318  54
         PreparedStatement psR = null;
 319  54
         PreparedStatement psS = null;
 320  54
         ResultSet rsV = null;
 321  54
         ResultSet rsR = null;
 322  54
         ResultSet rsS = null;
 323  54
         Vulnerability vuln = null;
 324  
         try {
 325  54
             psV = conn.prepareStatement(SELECT_VULNERABILITY);
 326  54
             psV.setString(1, cve);
 327  54
             rsV = psV.executeQuery();
 328  54
             if (rsV.next()) {
 329  54
                 vuln = new Vulnerability();
 330  54
                 vuln.setName(cve);
 331  54
                 vuln.setDescription(rsV.getString(2));
 332  54
                 String cwe = rsV.getString(3);
 333  54
                 if (cwe != null) {
 334  51
                     final String name = CweDB.getCweName(cwe);
 335  51
                     if (name != null) {
 336  48
                         cwe += " " + name;
 337  
                     }
 338  
                 }
 339  54
                 final int cveId = rsV.getInt(1);
 340  54
                 vuln.setCwe(cwe);
 341  54
                 vuln.setCvssScore(rsV.getFloat(4));
 342  54
                 vuln.setCvssAccessVector(rsV.getString(5));
 343  54
                 vuln.setCvssAccessComplexity(rsV.getString(6));
 344  54
                 vuln.setCvssAuthentication(rsV.getString(7));
 345  54
                 vuln.setCvssConfidentialityImpact(rsV.getString(8));
 346  54
                 vuln.setCvssIntegrityImpact(rsV.getString(9));
 347  54
                 vuln.setCvssAvailabilityImpact(rsV.getString(10));
 348  
 
 349  54
                 psR = conn.prepareStatement(SELECT_REFERENCE);
 350  54
                 psR.setInt(1, cveId);
 351  54
                 rsR = psR.executeQuery();
 352  312
                 while (rsR.next()) {
 353  258
                     vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3));
 354  
                 }
 355  54
                 psS = conn.prepareStatement(SELECT_SOFTWARE);
 356  54
                 psS.setInt(1, cveId);
 357  54
                 rsS = psS.executeQuery();
 358  1881
                 while (rsS.next()) {
 359  1827
                     final String cpe = rsS.getString(1);
 360  1827
                     final String prevVersion = rsS.getString(2);
 361  1827
                     if (prevVersion == null) {
 362  1794
                         vuln.addVulnerableSoftware(cpe);
 363  
                     } else {
 364  33
                         vuln.addVulnerableSoftware(cpe, prevVersion);
 365  
                     }
 366  1827
                 }
 367  
             }
 368  0
         } catch (SQLException ex) {
 369  0
             throw new DatabaseException("Error retrieving " + cve, ex);
 370  
         } finally {
 371  54
             closeResultSet(rsV);
 372  54
             closeResultSet(rsR);
 373  54
             closeResultSet(rsS);
 374  54
             closeStatement(psV);
 375  54
             closeStatement(psR);
 376  54
             closeStatement(psS);
 377  54
         }
 378  54
         return vuln;
 379  
     }
 380  
 
 381  
     /**
 382  
      * Updates the vulnerability within the database. If the vulnerability does
 383  
      * not exist it will be added.
 384  
      *
 385  
      * @param vuln the vulnerability to add to the database
 386  
      * @throws DatabaseException is thrown if the database
 387  
      */
 388  
     public void updateVulnerability(Vulnerability vuln) throws DatabaseException {
 389  0
         PreparedStatement selectVulnerabilityId = null;
 390  0
         PreparedStatement deleteReferences = null;
 391  0
         PreparedStatement deleteSoftware = null;
 392  0
         PreparedStatement updateVulnerability = null;
 393  0
         PreparedStatement insertVulnerability = null;
 394  0
         PreparedStatement insertReference = null;
 395  0
         PreparedStatement selectCpeId = null;
 396  0
         PreparedStatement insertCpe = null;
 397  0
         PreparedStatement insertSoftware = null;
 398  
 
 399  
         try {
 400  0
             selectVulnerabilityId = conn.prepareStatement(SELECT_VULNERABILITY_ID);
 401  0
             deleteReferences = conn.prepareStatement(DELETE_REFERENCE);
 402  0
             deleteSoftware = conn.prepareStatement(DELETE_SOFTWARE);
 403  0
             updateVulnerability = conn.prepareStatement(UPDATE_VULNERABILITY);
 404  0
             insertVulnerability = conn.prepareStatement(INSERT_VULNERABILITY, Statement.RETURN_GENERATED_KEYS);
 405  0
             insertReference = conn.prepareStatement(INSERT_REFERENCE);
 406  0
             selectCpeId = conn.prepareStatement(SELECT_CPE_ID);
 407  0
             insertCpe = conn.prepareStatement(INSERT_CPE, Statement.RETURN_GENERATED_KEYS);
 408  0
             insertSoftware = conn.prepareStatement(INSERT_SOFTWARE);
 409  0
             int vulnerabilityId = 0;
 410  0
             selectVulnerabilityId.setString(1, vuln.getName());
 411  0
             ResultSet rs = selectVulnerabilityId.executeQuery();
 412  0
             if (rs.next()) {
 413  0
                 vulnerabilityId = rs.getInt(1);
 414  
                 // first delete any existing vulnerability info. We don't know what was updated. yes, slower but atm easier.
 415  0
                 deleteReferences.setInt(1, vulnerabilityId);
 416  0
                 deleteReferences.execute();
 417  0
                 deleteSoftware.setInt(1, vulnerabilityId);
 418  0
                 deleteSoftware.execute();
 419  
             }
 420  0
             closeResultSet(rs);
 421  0
             rs = null;
 422  0
             if (vulnerabilityId != 0) {
 423  0
                 updateVulnerability.setString(1, vuln.getDescription());
 424  0
                 updateVulnerability.setString(2, vuln.getCwe());
 425  0
                 updateVulnerability.setFloat(3, vuln.getCvssScore());
 426  0
                 updateVulnerability.setString(4, vuln.getCvssAccessVector());
 427  0
                 updateVulnerability.setString(5, vuln.getCvssAccessComplexity());
 428  0
                 updateVulnerability.setString(6, vuln.getCvssAuthentication());
 429  0
                 updateVulnerability.setString(7, vuln.getCvssConfidentialityImpact());
 430  0
                 updateVulnerability.setString(8, vuln.getCvssIntegrityImpact());
 431  0
                 updateVulnerability.setString(9, vuln.getCvssAvailabilityImpact());
 432  0
                 updateVulnerability.setInt(10, vulnerabilityId);
 433  0
                 updateVulnerability.executeUpdate();
 434  
             } else {
 435  0
                 insertVulnerability.setString(1, vuln.getName());
 436  0
                 insertVulnerability.setString(2, vuln.getDescription());
 437  0
                 insertVulnerability.setString(3, vuln.getCwe());
 438  0
                 insertVulnerability.setFloat(4, vuln.getCvssScore());
 439  0
                 insertVulnerability.setString(5, vuln.getCvssAccessVector());
 440  0
                 insertVulnerability.setString(6, vuln.getCvssAccessComplexity());
 441  0
                 insertVulnerability.setString(7, vuln.getCvssAuthentication());
 442  0
                 insertVulnerability.setString(8, vuln.getCvssConfidentialityImpact());
 443  0
                 insertVulnerability.setString(9, vuln.getCvssIntegrityImpact());
 444  0
                 insertVulnerability.setString(10, vuln.getCvssAvailabilityImpact());
 445  0
                 insertVulnerability.execute();
 446  
                 try {
 447  0
                     rs = insertVulnerability.getGeneratedKeys();
 448  0
                     rs.next();
 449  0
                     vulnerabilityId = rs.getInt(1);
 450  0
                 } catch (SQLException ex) {
 451  0
                     final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", vuln.getName());
 452  0
                     throw new DatabaseException(msg, ex);
 453  
                 } finally {
 454  0
                     closeResultSet(rs);
 455  0
                     rs = null;
 456  0
                 }
 457  
             }
 458  0
             insertReference.setInt(1, vulnerabilityId);
 459  0
             for (Reference r : vuln.getReferences()) {
 460  0
                 insertReference.setString(2, r.getName());
 461  0
                 insertReference.setString(3, r.getUrl());
 462  0
                 insertReference.setString(4, r.getSource());
 463  0
                 insertReference.execute();
 464  
             }
 465  0
             for (VulnerableSoftware s : vuln.getVulnerableSoftware()) {
 466  0
                 int cpeProductId = 0;
 467  0
                 selectCpeId.setString(1, s.getName());
 468  
                 try {
 469  0
                     rs = selectCpeId.executeQuery();
 470  0
                     if (rs.next()) {
 471  0
                         cpeProductId = rs.getInt(1);
 472  
                     }
 473  0
                 } catch (SQLException ex) {
 474  0
                     throw new DatabaseException("Unable to get primary key for new cpe: " + s.getName(), ex);
 475  
                 } finally {
 476  0
                     closeResultSet(rs);
 477  0
                     rs = null;
 478  0
                 }
 479  
 
 480  0
                 if (cpeProductId == 0) {
 481  0
                     insertCpe.setString(1, s.getName());
 482  0
                     insertCpe.setString(2, s.getVendor());
 483  0
                     insertCpe.setString(3, s.getProduct());
 484  0
                     insertCpe.executeUpdate();
 485  0
                     cpeProductId = getGeneratedKey(insertCpe);
 486  
                 }
 487  0
                 if (cpeProductId == 0) {
 488  0
                     throw new DatabaseException("Unable to retrieve cpeProductId - no data returned");
 489  
                 }
 490  
 
 491  0
                 insertSoftware.setInt(1, vulnerabilityId);
 492  0
                 insertSoftware.setInt(2, cpeProductId);
 493  0
                 if (s.getPreviousVersion() == null) {
 494  0
                     insertSoftware.setNull(3, java.sql.Types.VARCHAR);
 495  
                 } else {
 496  0
                     insertSoftware.setString(3, s.getPreviousVersion());
 497  
                 }
 498  0
                 insertSoftware.execute();
 499  0
             }
 500  
 
 501  0
         } catch (SQLException ex) {
 502  0
             final String msg = String.format("Error updating '%s'", vuln.getName());
 503  0
             Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
 504  0
             throw new DatabaseException(msg, ex);
 505  
         } finally {
 506  0
             closeStatement(selectVulnerabilityId);
 507  0
             closeStatement(deleteReferences);
 508  0
             closeStatement(deleteSoftware);
 509  0
             closeStatement(updateVulnerability);
 510  0
             closeStatement(insertVulnerability);
 511  0
             closeStatement(insertReference);
 512  0
             closeStatement(selectCpeId);
 513  0
             closeStatement(insertCpe);
 514  0
             closeStatement(insertSoftware);
 515  0
         }
 516  0
     }
 517  
 
 518  
     /**
 519  
      * Retrieves the directory that the JAR file exists in so that we can ensure
 520  
      * we always use a common data directory.
 521  
      *
 522  
      * @return the data directory for this index.
 523  
      * @throws IOException is thrown if an IOException occurs of course...
 524  
      */
 525  
     public static File getDataDirectory() throws IOException {
 526  33
         final File path = Settings.getFile(Settings.KEYS.CVE_DATA_DIRECTORY);
 527  33
         if (!path.exists()) {
 528  0
             if (!path.mkdirs()) {
 529  0
                 throw new IOException("Unable to create NVD CVE Data directory");
 530  
             }
 531  
         }
 532  33
         return path;
 533  
     }
 534  
 
 535  
     /**
 536  
      * It is possible that orphaned rows may be generated during database
 537  
      * updates. This should be called after all updates have been completed to
 538  
      * ensure orphan entries are removed.
 539  
      */
 540  
     public void cleanupDatabase() {
 541  0
         PreparedStatement ps = null;
 542  
         try {
 543  0
             ps = conn.prepareStatement(CLEANUP_ORPHANS);
 544  0
             if (ps != null) {
 545  0
                 ps.executeUpdate();
 546  
             }
 547  0
         } catch (SQLException ex) {
 548  0
             Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
 549  
         } finally {
 550  0
             closeStatement(ps);
 551  0
         }
 552  0
     }
 553  
 
 554  
     /**
 555  
      * Creates the database structure (tables and indexes) to store the CVE data
 556  
      *
 557  
      * @throws SQLException thrown if there is a sql exception
 558  
      * @throws DatabaseException thrown if there is a database exception
 559  
      */
 560  
     protected void createTables() throws SQLException, DatabaseException {
 561  
         InputStream is;
 562  
         InputStreamReader reader;
 563  0
         BufferedReader in = null;
 564  
         try {
 565  0
             is = this.getClass().getClassLoader().getResourceAsStream(DB_STRUCTURE_RESOURCE);
 566  0
             reader = new InputStreamReader(is, "UTF-8");
 567  0
             in = new BufferedReader(reader);
 568  0
             final StringBuilder sb = new StringBuilder(2110);
 569  
             String tmp;
 570  0
             while ((tmp = in.readLine()) != null) {
 571  0
                 sb.append(tmp);
 572  
             }
 573  0
             Statement statement = null;
 574  
             try {
 575  0
                 statement = conn.createStatement();
 576  0
                 statement.execute(sb.toString());
 577  
             } finally {
 578  0
                 closeStatement(statement);
 579  0
             }
 580  0
         } catch (IOException ex) {
 581  0
             throw new DatabaseException("Unable to create database schema", ex);
 582  
         } finally {
 583  0
             if (in != null) {
 584  
                 try {
 585  0
                     in.close();
 586  0
                 } catch (IOException ex) {
 587  0
                     Logger.getLogger(CveDB.class
 588  
                             .getName()).log(Level.FINEST, null, ex);
 589  0
                 }
 590  
             }
 591  
         }
 592  0
     }
 593  
 
 594  
     /**
 595  
      * Closes the given statement object ignoring any exceptions that occur.
 596  
      *
 597  
      * @param statement a Statement object
 598  
      */
 599  
     private void closeStatement(Statement statement) {
 600  207
         if (statement != null) {
 601  
             try {
 602  207
                 statement.close();
 603  0
             } catch (SQLException ex) {
 604  0
                 Logger.getLogger(CveDB.class
 605  
                         .getName()).log(Level.FINEST, statement.toString(), ex);
 606  207
             }
 607  
         }
 608  207
     }
 609  
 
 610  
     /**
 611  
      * Closes the result set capturing and ignoring any SQLExceptions that
 612  
      * occur.
 613  
      *
 614  
      * @param rs a ResultSet to close
 615  
      */
 616  
     private void closeResultSet(ResultSet rs) {
 617  210
         if (rs != null) {
 618  
             try {
 619  210
                 rs.close();
 620  0
             } catch (SQLException ex) {
 621  0
                 Logger.getLogger(CveDB.class
 622  
                         .getName()).log(Level.FINEST, rs.toString(), ex);
 623  210
             }
 624  
         }
 625  210
     }
 626  
 
 627  
     /**
 628  
      * Returns the generated integer primary key for a newly inserted row.
 629  
      *
 630  
      * @param statement a prepared statement that just executed an insert
 631  
      * @return a primary key
 632  
      * @throws DatabaseException thrown if there is an exception obtaining the
 633  
      * key
 634  
      */
 635  
     private int getGeneratedKey(PreparedStatement statement) throws DatabaseException {
 636  0
         ResultSet rs = null;
 637  0
         int id = 0;
 638  
         try {
 639  0
             rs = statement.getGeneratedKeys();
 640  0
             rs.next();
 641  0
             id = rs.getInt(1);
 642  0
         } catch (SQLException ex) {
 643  0
             throw new DatabaseException("Unable to get primary key for inserted row");
 644  
         } finally {
 645  0
             closeResultSet(rs);
 646  0
         }
 647  0
         return id;
 648  
     }
 649  
 
 650  
     /**
 651  
      * Determines if the given identifiedVersion is affected by the given cpeId
 652  
      * and previous version flag. A non-null, non-empty string passed to the
 653  
      * previous version argument indicates that all previous versions are
 654  
      * affected.
 655  
      *
 656  
      * @param vendor the vendor of the dependency being analyzed
 657  
      * @param product the product name of the dependency being analyzed
 658  
      * @param identifiedVersion the identified version of the dependency being
 659  
      * analyzed
 660  
      * @param cpeId the cpe identifier of software that has a known
 661  
      * vulnerability
 662  
      * @param previous a flag indicating if previous versions of the product are
 663  
      * vulnerable
 664  
      * @return true if the identified version is affected, otherwise false
 665  
      */
 666  
     private boolean isAffected(String vendor, String product, DependencyVersion identifiedVersion, String cpeId, String previous) {
 667  1145
         boolean affected = false;
 668  1145
         final boolean isStruts = "apache".equals(vendor) && "struts".equals(product);
 669  1145
         final DependencyVersion v = parseDependencyVersion(cpeId);
 670  1145
         final boolean prevAffected = previous == null ? false : !previous.isEmpty();
 671  1145
         if (identifiedVersion == null || "-".equals(identifiedVersion.toString())) {
 672  0
             if (v == null || "-".equals(v.toString())) {
 673  0
                 affected = true;
 674  
             }
 675  1145
         } else if (identifiedVersion.equals(v) || (prevAffected && identifiedVersion.compareTo(v) < 0)) {
 676  54
             if (isStruts) { //struts 2 vulns don't affect struts 1
 677  54
                 if (identifiedVersion.getVersionParts().get(0).equals(v.getVersionParts().get(0))) {
 678  54
                     affected = true;
 679  
                 }
 680  
             } else {
 681  0
                 affected = true;
 682  
             }
 683  
         }
 684  
         /*
 685  
          * TODO consider utilizing the matchThreeVersion method to get additional results. However, this
 686  
          *      might also introduce false positives.
 687  
          */
 688  1145
         return affected;
 689  
     }
 690  
 
 691  
     /**
 692  
      * Parses the version (including revision) from a CPE identifier. If no
 693  
      * version is identified then a '-' is returned.
 694  
      *
 695  
      * @param cpeStr a cpe identifier
 696  
      * @return a dependency version
 697  
      */
 698  
     private DependencyVersion parseDependencyVersion(String cpeStr) {
 699  1145
         final VulnerableSoftware cpe = new VulnerableSoftware();
 700  
         try {
 701  1145
             cpe.parseName(cpeStr);
 702  0
         } catch (UnsupportedEncodingException ex) {
 703  
             //never going to happen.
 704  0
             Logger.getLogger(CveDB.class.getName()).log(Level.FINEST, null, ex);
 705  1145
         }
 706  1145
         return parseDependencyVersion(cpe);
 707  
     }
 708  
 
 709  
     /**
 710  
      * Takes a CPE and parses out the version number. If no version is
 711  
      * identified then a '-' is returned.
 712  
      *
 713  
      * @param cpe a cpe object
 714  
      * @return a dependency version
 715  
      */
 716  
     private DependencyVersion parseDependencyVersion(VulnerableSoftware cpe) {
 717  
         DependencyVersion cpeVersion;
 718  1148
         if (cpe.getVersion() != null && cpe.getVersion().length() > 0) {
 719  
             String versionText;
 720  1146
             if (cpe.getRevision() != null && cpe.getRevision().length() > 0) {
 721  0
                 versionText = String.format("%s.%s", cpe.getVersion(), cpe.getRevision());
 722  
             } else {
 723  1146
                 versionText = cpe.getVersion();
 724  
             }
 725  1146
             cpeVersion = DependencyVersionUtil.parseVersion(versionText);
 726  1146
         } else {
 727  2
             cpeVersion = new DependencyVersion("-");
 728  
         }
 729  1148
         return cpeVersion;
 730  
     }
 731  
 }