Coverage Report - org.owasp.dependencycheck.data.update.NvdCveUpdater
 
Classes in this File Line Coverage Branch Coverage Complexity
NvdCveUpdater
0%
0/132
0%
0/50
12.25
 
 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.update;
 19  
 
 20  
 import java.net.MalformedURLException;
 21  
 import java.util.Calendar;
 22  
 import java.util.HashSet;
 23  
 import java.util.Set;
 24  
 import java.util.concurrent.ExecutionException;
 25  
 import java.util.concurrent.ExecutorService;
 26  
 import java.util.concurrent.Executors;
 27  
 import java.util.concurrent.Future;
 28  
 import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
 29  
 import static org.owasp.dependencycheck.data.nvdcve.DatabaseProperties.MODIFIED;
 30  
 import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
 31  
 import org.owasp.dependencycheck.data.update.exception.UpdateException;
 32  
 import org.owasp.dependencycheck.data.update.nvd.DownloadTask;
 33  
 import org.owasp.dependencycheck.data.update.nvd.NvdCveInfo;
 34  
 import org.owasp.dependencycheck.data.update.nvd.ProcessTask;
 35  
 import org.owasp.dependencycheck.data.update.nvd.UpdateableNvdCve;
 36  
 import org.owasp.dependencycheck.utils.DateUtil;
 37  
 import org.owasp.dependencycheck.utils.DownloadFailedException;
 38  
 import org.owasp.dependencycheck.utils.InvalidSettingException;
 39  
 import org.owasp.dependencycheck.utils.Settings;
 40  
 import org.slf4j.Logger;
 41  
 import org.slf4j.LoggerFactory;
 42  
 
 43  
 /**
 44  
  * Class responsible for updating the NVD CVE data.
 45  
  *
 46  
  * @author Jeremy Long
 47  
  */
 48  0
 public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
 49  
 
 50  
     /**
 51  
      * The logger
 52  
      */
 53  0
     private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveUpdater.class);
 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  
      * <p>
 61  
      * Downloads the latest NVD CVE XML file from the web and imports it into the current CVE Database.</p>
 62  
      *
 63  
      * @throws UpdateException is thrown if there is an error updating the database
 64  
      */
 65  
     @Override
 66  
     public void update() throws UpdateException {
 67  
         try {
 68  0
             openDataStores();
 69  0
             final UpdateableNvdCve updateable = getUpdatesNeeded();
 70  0
             if (updateable.isUpdateNeeded()) {
 71  0
                 performUpdate(updateable);
 72  
             }
 73  0
         } catch (MalformedURLException ex) {
 74  0
             LOGGER.warn(
 75  
                     "NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.");
 76  0
             LOGGER.debug("", ex);
 77  0
         } catch (DownloadFailedException ex) {
 78  0
             LOGGER.warn(
 79  
                     "Unable to download the NVD CVE data; the results may not include the most recent CPE/CVEs from the NVD.");
 80  0
             if (Settings.getString(Settings.KEYS.PROXY_SERVER) == null) {
 81  0
                 LOGGER.info(
 82  
                         "If you are behind a proxy you may need to configure dependency-check to use the proxy.");
 83  
             }
 84  0
             LOGGER.debug("", ex);
 85  
         } finally {
 86  0
             closeDataStores();
 87  0
         }
 88  0
     }
 89  
 
 90  
     /**
 91  
      * Downloads the latest NVD CVE XML file from the web and imports it into the current CVE Database.
 92  
      *
 93  
      * @param updateable a collection of NVD CVE data file references that need to be downloaded and processed to update the
 94  
      * database
 95  
      * @throws UpdateException is thrown if there is an error updating the database
 96  
      */
 97  
     public void performUpdate(UpdateableNvdCve updateable) throws UpdateException {
 98  0
         int maxUpdates = 0;
 99  
         try {
 100  0
             for (NvdCveInfo cve : updateable) {
 101  0
                 if (cve.getNeedsUpdate()) {
 102  0
                     maxUpdates += 1;
 103  
                 }
 104  0
             }
 105  0
             if (maxUpdates <= 0) {
 106  
                 return;
 107  
             }
 108  0
             if (maxUpdates > 3) {
 109  0
                 LOGGER.info(
 110  
                         "NVD CVE requires several updates; this could take a couple of minutes.");
 111  
             }
 112  0
             if (maxUpdates > 0) {
 113  0
                 openDataStores();
 114  
             }
 115  
 
 116  0
             final int poolSize = (MAX_THREAD_POOL_SIZE < maxUpdates) ? MAX_THREAD_POOL_SIZE : maxUpdates;
 117  
 
 118  0
             final ExecutorService downloadExecutors = Executors.newFixedThreadPool(poolSize);
 119  0
             final ExecutorService processExecutor = Executors.newSingleThreadExecutor();
 120  0
             final Set<Future<Future<ProcessTask>>> downloadFutures = new HashSet<Future<Future<ProcessTask>>>(maxUpdates);
 121  0
             for (NvdCveInfo cve : updateable) {
 122  0
                 if (cve.getNeedsUpdate()) {
 123  0
                     final DownloadTask call = new DownloadTask(cve, processExecutor, getCveDB(), Settings.getInstance());
 124  0
                     downloadFutures.add(downloadExecutors.submit(call));
 125  
                 }
 126  0
             }
 127  0
             downloadExecutors.shutdown();
 128  
 
 129  
             //next, move the future future processTasks to just future processTasks
 130  0
             final Set<Future<ProcessTask>> processFutures = new HashSet<Future<ProcessTask>>(maxUpdates);
 131  0
             for (Future<Future<ProcessTask>> future : downloadFutures) {
 132  0
                 Future<ProcessTask> task = null;
 133  
                 try {
 134  0
                     task = future.get();
 135  0
                 } catch (InterruptedException ex) {
 136  0
                     downloadExecutors.shutdownNow();
 137  0
                     processExecutor.shutdownNow();
 138  
 
 139  0
                     LOGGER.debug("Thread was interrupted during download", ex);
 140  0
                     throw new UpdateException("The download was interrupted", ex);
 141  0
                 } catch (ExecutionException ex) {
 142  0
                     downloadExecutors.shutdownNow();
 143  0
                     processExecutor.shutdownNow();
 144  
 
 145  0
                     LOGGER.debug("Thread was interrupted during download execution", ex);
 146  0
                     throw new UpdateException("The execution of the download was interrupted", ex);
 147  0
                 }
 148  0
                 if (task == null) {
 149  0
                     downloadExecutors.shutdownNow();
 150  0
                     processExecutor.shutdownNow();
 151  0
                     LOGGER.debug("Thread was interrupted during download");
 152  0
                     throw new UpdateException("The download was interrupted; unable to complete the update");
 153  
                 } else {
 154  0
                     processFutures.add(task);
 155  
                 }
 156  0
             }
 157  
 
 158  0
             for (Future<ProcessTask> future : processFutures) {
 159  
                 try {
 160  0
                     final ProcessTask task = future.get();
 161  0
                     if (task.getException() != null) {
 162  0
                         throw task.getException();
 163  
                     }
 164  0
                 } catch (InterruptedException ex) {
 165  0
                     processExecutor.shutdownNow();
 166  0
                     LOGGER.debug("Thread was interrupted during processing", ex);
 167  0
                     throw new UpdateException(ex);
 168  0
                 } catch (ExecutionException ex) {
 169  0
                     processExecutor.shutdownNow();
 170  0
                     LOGGER.debug("Execution Exception during process", ex);
 171  0
                     throw new UpdateException(ex);
 172  
                 } finally {
 173  0
                     processExecutor.shutdown();
 174  0
                 }
 175  0
             }
 176  
 
 177  0
             if (maxUpdates >= 1) { //ensure the modified file date gets written (we may not have actually updated it)
 178  0
                 getProperties().save(updateable.get(MODIFIED));
 179  0
                 LOGGER.info("Begin database maintenance.");
 180  0
                 getCveDB().cleanupDatabase();
 181  0
                 LOGGER.info("End database maintenance.");
 182  
             }
 183  
         } finally {
 184  0
             closeDataStores();
 185  0
         }
 186  0
     }
 187  
 
 188  
     /**
 189  
      * Determines if the index needs to be updated. This is done by fetching the NVD CVE meta data and checking the last update
 190  
      * date. If the data needs to be refreshed this method will return the NvdCveUrl for the files that need to be updated.
 191  
      *
 192  
      * @return the collection of files that need to be updated
 193  
      * @throws MalformedURLException is thrown if the URL for the NVD CVE Meta data is incorrect
 194  
      * @throws DownloadFailedException is thrown if there is an error. downloading the NVD CVE download data file
 195  
      * @throws UpdateException Is thrown if there is an issue with the last updated properties file
 196  
      */
 197  
     protected final UpdateableNvdCve getUpdatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
 198  0
         UpdateableNvdCve updates = null;
 199  
         try {
 200  0
             updates = retrieveCurrentTimestampsFromWeb();
 201  0
         } catch (InvalidDataException ex) {
 202  0
             final String msg = "Unable to retrieve valid timestamp from nvd cve downloads page";
 203  0
             LOGGER.debug(msg, ex);
 204  0
             throw new DownloadFailedException(msg, ex);
 205  0
         } catch (InvalidSettingException ex) {
 206  0
             LOGGER.debug("Invalid setting found when retrieving timestamps", ex);
 207  0
             throw new DownloadFailedException("Invalid settings", ex);
 208  0
         }
 209  
 
 210  0
         if (updates == null) {
 211  0
             throw new DownloadFailedException("Unable to retrieve the timestamps of the currently published NVD CVE data");
 212  
         }
 213  0
         if (!getProperties().isEmpty()) {
 214  
             try {
 215  0
                 final long lastUpdated = Long.parseLong(getProperties().getProperty(DatabaseProperties.LAST_UPDATED, "0"));
 216  0
                 final long now = System.currentTimeMillis();
 217  0
                 final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7);
 218  0
                 if (lastUpdated == updates.getTimeStamp(MODIFIED)) {
 219  0
                     updates.clear(); //we don't need to update anything.
 220  0
                 } else if (DateUtil.withinDateRange(lastUpdated, now, days)) {
 221  0
                     for (NvdCveInfo entry : updates) {
 222  0
                         if (MODIFIED.equals(entry.getId())) {
 223  0
                             entry.setNeedsUpdate(true);
 224  
                         } else {
 225  0
                             entry.setNeedsUpdate(false);
 226  
                         }
 227  0
                     }
 228  
                 } else { //we figure out which of the several XML files need to be downloaded.
 229  0
                     for (NvdCveInfo entry : updates) {
 230  0
                         if (MODIFIED.equals(entry.getId())) {
 231  0
                             entry.setNeedsUpdate(true);
 232  
                         } else {
 233  0
                             long currentTimestamp = 0;
 234  
                             try {
 235  0
                                 currentTimestamp = Long.parseLong(getProperties().getProperty(DatabaseProperties.LAST_UPDATED_BASE
 236  
                                         + entry.getId(), "0"));
 237  0
                             } catch (NumberFormatException ex) {
 238  0
                                 LOGGER.debug("Error parsing '{}' '{}' from nvdcve.lastupdated",
 239  
                                         DatabaseProperties.LAST_UPDATED_BASE, entry.getId(), ex);
 240  0
                             }
 241  0
                             if (currentTimestamp == entry.getTimestamp()) {
 242  0
                                 entry.setNeedsUpdate(false);
 243  
                             }
 244  
                         }
 245  0
                     }
 246  
                 }
 247  0
             } catch (NumberFormatException ex) {
 248  0
                 LOGGER.warn("An invalid schema version or timestamp exists in the data.properties file.");
 249  0
                 LOGGER.debug("", ex);
 250  0
             }
 251  
         }
 252  0
         return updates;
 253  
     }
 254  
 
 255  
     /**
 256  
      * Retrieves the timestamps from the NVD CVE meta data file.
 257  
      *
 258  
      * @return the timestamp from the currently published nvdcve downloads page
 259  
      * @throws MalformedURLException thrown if the URL for the NVD CCE Meta data is incorrect.
 260  
      * @throws DownloadFailedException thrown if there is an error downloading the nvd cve meta data file
 261  
      * @throws InvalidDataException thrown if there is an exception parsing the timestamps
 262  
      * @throws InvalidSettingException thrown if the settings are invalid
 263  
      */
 264  
     private UpdateableNvdCve retrieveCurrentTimestampsFromWeb()
 265  
             throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException {
 266  
 
 267  0
         final UpdateableNvdCve updates = new UpdateableNvdCve();
 268  0
         updates.add(MODIFIED, Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL),
 269  
                 Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL),
 270  
                 false);
 271  
 
 272  0
         final int start = Settings.getInt(Settings.KEYS.CVE_START_YEAR);
 273  0
         final int end = Calendar.getInstance().get(Calendar.YEAR);
 274  0
         final String baseUrl20 = Settings.getString(Settings.KEYS.CVE_SCHEMA_2_0);
 275  0
         final String baseUrl12 = Settings.getString(Settings.KEYS.CVE_SCHEMA_1_2);
 276  0
         for (int i = start; i <= end; i++) {
 277  0
             updates.add(Integer.toString(i), String.format(baseUrl20, i),
 278  
                     String.format(baseUrl12, i),
 279  
                     true);
 280  
         }
 281  0
         return updates;
 282  
     }
 283  
 
 284  
 }