Coverage Report - org.owasp.dependencycheck.data.update.StandardUpdateTask
 
Classes in this File Line Coverage Branch Coverage Complexity
StandardUpdateTask
0%
0/129
0%
0/46
14.25
 
 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.update;
 20  
 
 21  
 import org.owasp.dependencycheck.data.nvdcve.InvalidDataException;
 22  
 import java.io.File;
 23  
 import java.io.FileNotFoundException;
 24  
 import java.io.IOException;
 25  
 import javax.xml.parsers.ParserConfigurationException;
 26  
 import org.xml.sax.SAXException;
 27  
 import java.net.MalformedURLException;
 28  
 import java.sql.SQLException;
 29  
 import java.util.Calendar;
 30  
 import java.util.Date;
 31  
 import java.util.HashSet;
 32  
 import java.util.Set;
 33  
 import java.util.concurrent.ExecutionException;
 34  
 import java.util.concurrent.ExecutorService;
 35  
 import java.util.concurrent.Executors;
 36  
 import java.util.concurrent.Future;
 37  
 import java.util.logging.Level;
 38  
 import java.util.logging.Logger;
 39  
 import org.owasp.dependencycheck.data.UpdateException;
 40  
 import org.owasp.dependencycheck.data.nvdcve.CveDB;
 41  
 import org.owasp.dependencycheck.utils.DownloadFailedException;
 42  
 import org.owasp.dependencycheck.utils.Settings;
 43  
 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
 44  
 import org.owasp.dependencycheck.utils.InvalidSettingException;
 45  
 import static org.owasp.dependencycheck.data.update.DataStoreMetaInfo.MODIFIED;
 46  
 
 47  
 /**
 48  
  * Class responsible for updating the CPE and NVDCVE data stores.
 49  
  *
 50  
  * @author Jeremy Long (jeremy.long@owasp.org)
 51  
  */
 52  
 public class StandardUpdateTask extends AbstractUpdateTask {
 53  
 
 54  
     /**
 55  
      * The max thread pool size to use when downloading files.
 56  
      */
 57  0
     public static final int MAX_THREAD_POOL_SIZE = Settings.getInt(Settings.KEYS.MAX_DOWNLOAD_THREAD_POOL_SIZE, 3);
 58  
 
 59  
     /**
 60  
      * Constructs a new Standard Update Task.
 61  
      *
 62  
      * @param properties information about the data store
 63  
      * @throws MalformedURLException thrown if a configured URL is malformed
 64  
      * @throws DownloadFailedException thrown if a timestamp cannot be checked
 65  
      * on a configured URL
 66  
      * @throws UpdateException thrown if there is an exception generating the
 67  
      * update task
 68  
      */
 69  
     public StandardUpdateTask(DataStoreMetaInfo properties) throws MalformedURLException, DownloadFailedException, UpdateException {
 70  0
         super(properties);
 71  0
     }
 72  
 
 73  
     /**
 74  
      * <p>Downloads the latest NVD CVE XML file from the web and imports it into
 75  
      * the current CVE Database.</p>
 76  
      *
 77  
      * @throws UpdateException is thrown if there is an error updating the
 78  
      * database
 79  
      */
 80  
     @Override
 81  
     public void update() throws UpdateException {
 82  0
         int maxUpdates = 0;
 83  
         try {
 84  0
             for (NvdCveInfo cve : getUpdateable()) {
 85  0
                 if (cve.getNeedsUpdate()) {
 86  0
                     maxUpdates += 1;
 87  
                 }
 88  
             }
 89  0
             if (maxUpdates <= 0) {
 90  
                 return;
 91  
             }
 92  0
             if (maxUpdates > 3) {
 93  0
                 Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
 94  
                         "NVD CVE requires several updates; this could take a couple of minutes.");
 95  
             }
 96  0
             if (maxUpdates > 0) {
 97  0
                 openDataStores();
 98  
             }
 99  
 
 100  0
             final int poolSize = (MAX_THREAD_POOL_SIZE > maxUpdates) ? MAX_THREAD_POOL_SIZE : maxUpdates;
 101  0
             final ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
 102  0
             final Set<Future<CallableDownloadTask>> futures = new HashSet<Future<CallableDownloadTask>>(maxUpdates);
 103  
 
 104  0
             for (NvdCveInfo cve : getUpdateable()) {
 105  0
                 if (cve.getNeedsUpdate()) {
 106  
                     final File file1;
 107  
                     final File file2;
 108  
                     try {
 109  0
                         file1 = File.createTempFile("cve" + cve.getId() + "_", ".xml");
 110  0
                         file2 = File.createTempFile("cve_1_2_" + cve.getId() + "_", ".xml");
 111  0
                     } catch (IOException ex) {
 112  0
                         throw new UpdateException(ex);
 113  0
                     }
 114  0
                     final CallableDownloadTask call = new CallableDownloadTask(cve, file1, file2);
 115  0
                     futures.add(executorService.submit(call));
 116  0
                 }
 117  
             }
 118  
 
 119  
             try {
 120  0
                 for (Future<CallableDownloadTask> future : futures) {
 121  0
                     final CallableDownloadTask filePair = future.get();
 122  0
                     String msg = String.format("Processing Started for NVD CVE - %s", filePair.getNvdCveInfo().getId());
 123  0
                     Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO, msg);
 124  
                     try {
 125  0
                         importXML(filePair.getFirst(), filePair.getSecond());
 126  0
                         getCveDB().commit();
 127  0
                         getProperties().save(filePair.getNvdCveInfo());
 128  0
                     } catch (FileNotFoundException ex) {
 129  0
                         throw new UpdateException(ex);
 130  0
                     } catch (ParserConfigurationException ex) {
 131  0
                         throw new UpdateException(ex);
 132  0
                     } catch (SAXException ex) {
 133  0
                         throw new UpdateException(ex);
 134  0
                     } catch (IOException ex) {
 135  0
                         throw new UpdateException(ex);
 136  0
                     } catch (SQLException ex) {
 137  0
                         throw new UpdateException(ex);
 138  0
                     } catch (DatabaseException ex) {
 139  0
                         throw new UpdateException(ex);
 140  0
                     } catch (ClassNotFoundException ex) {
 141  0
                         throw new UpdateException(ex);
 142  
                     } finally {
 143  0
                         filePair.cleanup();
 144  0
                     }
 145  0
                     msg = String.format("Processing Complete for NVD CVE - %s", filePair.getNvdCveInfo().getId());
 146  0
                     Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO, msg);
 147  0
                 }
 148  0
             } catch (InterruptedException ex) {
 149  0
                 executorService.shutdownNow();
 150  0
                 Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.FINE, "Thread was interupted", ex);
 151  0
                 throw new UpdateException(ex);
 152  0
             } catch (ExecutionException ex) {
 153  0
                 executorService.shutdownNow();
 154  0
                 Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.SEVERE, null, ex);
 155  0
                 throw new UpdateException(ex);
 156  
             } finally {
 157  
                 //yes, this should likely not be in the finally because of the shutdownNow above.
 158  0
                 executorService.shutdown();
 159  0
             }
 160  
 
 161  0
             if (maxUpdates >= 1) { //ensure the modified file date gets written
 162  0
                 getProperties().save(getUpdateable().get(MODIFIED));
 163  0
                 getCveDB().cleanupDatabase();
 164  
             }
 165  
         } finally {
 166  0
             closeDataStores();
 167  0
         }
 168  0
     }
 169  
 
 170  
     //<editor-fold defaultstate="collapsed" desc="OLD version of update() - not multithreaded">
 171  
     /*
 172  
      * TODO - remove this
 173  
      public void update() throws UpdateException {
 174  
      try {
 175  
      int maxUpdates = 0;
 176  
      for (NvdCveInfo cve : getUpdateable()) {
 177  
      if (cve.getNeedsUpdate()) {
 178  
      maxUpdates += 1;
 179  
      }
 180  
      }
 181  
      if (maxUpdates > 3) {
 182  
      Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
 183  
      "NVD CVE requires several updates; this could take a couple of minutes.");
 184  
      }
 185  
      if (maxUpdates > 0) {
 186  
      openDataStores();
 187  
      }
 188  
 
 189  
      int count = 0;
 190  
      for (NvdCveInfo cve : getUpdateable()) {
 191  
      if (cve.getNeedsUpdate()) {
 192  
      count += 1;
 193  
      Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
 194  
      "Updating NVD CVE ({0} of {1})", new Object[]{count, maxUpdates});
 195  
      URL url = new URL(cve.getUrl());
 196  
      File outputPath = null;
 197  
      File outputPath12 = null;
 198  
      try {
 199  
      Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
 200  
      "Downloading {0}", cve.getUrl());
 201  
      outputPath = File.createTempFile("cve" + cve.getId() + "_", ".xml");
 202  
      Downloader.fetchFile(url, outputPath);
 203  
 
 204  
      url = new URL(cve.getOldSchemaVersionUrl());
 205  
      outputPath12 = File.createTempFile("cve_1_2_" + cve.getId() + "_", ".xml");
 206  
      Downloader.fetchFile(url, outputPath12);
 207  
 
 208  
      Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
 209  
      "Processing {0}", cve.getUrl());
 210  
 
 211  
      importXML(outputPath, outputPath12);
 212  
 
 213  
      getCveDB().commit();
 214  
      getProperties().save(cve);
 215  
 
 216  
      Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
 217  
      "Completed update {0} of {1}", new Object[]{count, maxUpdates});
 218  
      } catch (FileNotFoundException ex) {
 219  
      throw new UpdateException(ex);
 220  
      } catch (ParserConfigurationException ex) {
 221  
      throw new UpdateException(ex);
 222  
      } catch (SAXException ex) {
 223  
      throw new UpdateException(ex);
 224  
      } catch (IOException ex) {
 225  
      throw new UpdateException(ex);
 226  
      } catch (SQLException ex) {
 227  
      throw new UpdateException(ex);
 228  
      } catch (DatabaseException ex) {
 229  
      throw new UpdateException(ex);
 230  
      } catch (ClassNotFoundException ex) {
 231  
      throw new UpdateException(ex);
 232  
      } finally {
 233  
      boolean deleted = false;
 234  
      try {
 235  
      if (outputPath != null && outputPath.exists()) {
 236  
      deleted = outputPath.delete();
 237  
      }
 238  
      } finally {
 239  
      if (outputPath != null && (outputPath.exists() || !deleted)) {
 240  
      outputPath.deleteOnExit();
 241  
      }
 242  
      }
 243  
      try {
 244  
      deleted = false;
 245  
      if (outputPath12 != null && outputPath12.exists()) {
 246  
      deleted = outputPath12.delete();
 247  
      }
 248  
      } finally {
 249  
      if (outputPath12 != null && (outputPath12.exists() || !deleted)) {
 250  
      outputPath12.deleteOnExit();
 251  
      }
 252  
      }
 253  
      }
 254  
      }
 255  
      }
 256  
      if (maxUpdates >= 1) { //ensure the modified file date gets written
 257  
      getProperties().save(getUpdateable().get(MODIFIED));
 258  
      getCveDB().cleanupDatabase();
 259  
      }
 260  
      } catch (MalformedURLException ex) {
 261  
      throw new UpdateException(ex);
 262  
      } finally {
 263  
      closeDataStores();
 264  
      }
 265  
      }
 266  
      */
 267  
     //</editor-fold>
 268  
     /**
 269  
      * Determines if the index needs to be updated. This is done by fetching the
 270  
      * NVD CVE meta data and checking the last update date. If the data needs to
 271  
      * be refreshed this method will return the NvdCveUrl for the files that
 272  
      * need to be updated.
 273  
      *
 274  
      * @return the collection of files that need to be updated
 275  
      * @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
 276  
      * data is incorrect
 277  
      * @throws DownloadFailedException is thrown if there is an error.
 278  
      * downloading the NVD CVE download data file
 279  
      * @throws UpdateException Is thrown if there is an issue with the last
 280  
      * updated properties file
 281  
      */
 282  
     @Override
 283  
     protected Updateable updatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
 284  0
         Updateable updates = null;
 285  
         try {
 286  0
             updates = retrieveCurrentTimestampsFromWeb();
 287  0
         } catch (InvalidDataException ex) {
 288  0
             final String msg = "Unable to retrieve valid timestamp from nvd cve downloads page";
 289  0
             Logger
 290  
                     .getLogger(StandardUpdateTask.class
 291  
                     .getName()).log(Level.FINE, msg, ex);
 292  0
             throw new DownloadFailedException(msg, ex);
 293  0
         } catch (InvalidSettingException ex) {
 294  0
             Logger.getLogger(StandardUpdateTask.class
 295  
                     .getName()).log(Level.FINE, "Invalid setting found when retrieving timestamps", ex);
 296  0
             throw new DownloadFailedException(
 297  
                     "Invalid settings", ex);
 298  0
         }
 299  
 
 300  0
         if (updates == null) {
 301  0
             throw new DownloadFailedException("Unable to retrieve the timestamps of the currently published NVD CVE data");
 302  
         }
 303  0
         final DataStoreMetaInfo properties = getProperties();
 304  0
         if (!properties.isEmpty()) {
 305  
             try {
 306  
                 float version;
 307  
 
 308  0
                 if (properties.getProperty("version") == null) {
 309  0
                     setDeleteAndRecreate(true);
 310  
                 } else {
 311  
                     try {
 312  0
                         version = Float.parseFloat(properties.getProperty("version"));
 313  0
                         final float currentVersion = Float.parseFloat(CveDB.DB_SCHEMA_VERSION);
 314  0
                         if (currentVersion > version) {
 315  0
                             setDeleteAndRecreate(true);
 316  
                         }
 317  0
                     } catch (NumberFormatException ex) {
 318  0
                         setDeleteAndRecreate(true);
 319  0
                     }
 320  
                 }
 321  
 
 322  0
                 if (shouldDeleteAndRecreate()) {
 323  0
                     return updates;
 324  
                 }
 325  
 
 326  0
                 final long lastUpdated = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED, "0"));
 327  0
                 final Date now = new Date();
 328  0
                 final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7);
 329  0
                 if (lastUpdated == updates.getTimeStamp(MODIFIED)) {
 330  0
                     updates.clear(); //we don't need to update anything.
 331  0
                 } else if (withinRange(lastUpdated, now.getTime(), days)) {
 332  0
                     for (NvdCveInfo entry : updates) {
 333  0
                         if (MODIFIED.equals(entry.getId())) {
 334  0
                             entry.setNeedsUpdate(true);
 335  
                         } else {
 336  0
                             entry.setNeedsUpdate(false);
 337  
                         }
 338  
                     }
 339  
                 } else { //we figure out which of the several XML files need to be downloaded.
 340  0
                     for (NvdCveInfo entry : updates) {
 341  0
                         if (MODIFIED.equals(entry.getId())) {
 342  0
                             entry.setNeedsUpdate(true);
 343  
                         } else {
 344  0
                             long currentTimestamp = 0;
 345  
                             try {
 346  0
                                 currentTimestamp = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED_BASE + entry.getId(), "0"));
 347  0
                             } catch (NumberFormatException ex) {
 348  0
                                 final String msg = String.format("Error parsing '%s' '%s' from nvdcve.lastupdated",
 349  
                                         DataStoreMetaInfo.LAST_UPDATED_BASE, entry.getId());
 350  0
                                 Logger
 351  
                                         .getLogger(StandardUpdateTask.class
 352  
                                         .getName()).log(Level.FINE, msg, ex);
 353  0
                             }
 354  0
                             if (currentTimestamp == entry.getTimestamp()) {
 355  0
                                 entry.setNeedsUpdate(false);
 356  
                             }
 357  0
                         }
 358  
                     }
 359  
                 }
 360  0
             } catch (NumberFormatException ex) {
 361  0
                 final String msg = "An invalid schema version or timestamp exists in the data.properties file.";
 362  0
                 Logger
 363  
                         .getLogger(StandardUpdateTask.class
 364  
                         .getName()).log(Level.WARNING, msg);
 365  0
                 Logger.getLogger(StandardUpdateTask.class
 366  
                         .getName()).log(Level.FINE, null, ex);
 367  0
             }
 368  
         }
 369  0
         return updates;
 370  
     }
 371  
 
 372  
     /**
 373  
      * Retrieves the timestamps from the NVD CVE meta data file.
 374  
      *
 375  
      * @return the timestamp from the currently published nvdcve downloads page
 376  
      * @throws MalformedURLException thrown if the URL for the NVD CCE Meta data
 377  
      * is incorrect.
 378  
      * @throws DownloadFailedException thrown if there is an error downloading
 379  
      * the nvd cve meta data file
 380  
      * @throws InvalidDataException thrown if there is an exception parsing the
 381  
      * timestamps
 382  
      * @throws InvalidSettingException thrown if the settings are invalid
 383  
      */
 384  
     private Updateable retrieveCurrentTimestampsFromWeb()
 385  
             throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException {
 386  
 
 387  0
         final Updateable updates = new Updateable();
 388  0
         updates.add(MODIFIED, Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL),
 389  
                 Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL),
 390  
                 false);
 391  
 
 392  0
         final int start = Settings.getInt(Settings.KEYS.CVE_START_YEAR);
 393  0
         final int end = Calendar.getInstance().get(Calendar.YEAR);
 394  0
         final String baseUrl20 = Settings.getString(Settings.KEYS.CVE_SCHEMA_2_0);
 395  0
         final String baseUrl12 = Settings.getString(Settings.KEYS.CVE_SCHEMA_1_2);
 396  0
         for (int i = start; i <= end; i++) {
 397  0
             updates.add(Integer.toString(i), String.format(baseUrl20, i),
 398  
                     String.format(baseUrl12, i),
 399  
                     true);
 400  
         }
 401  
 
 402  0
         return updates;
 403  
     }
 404  
 }