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