View Javadoc
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) 2018 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.data.nvdcve;
19  //CSOFF: AvoidStarImport
20  
21  import com.google.common.io.Resources;
22  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23  import io.github.jeremylong.openvulnerability.client.nvd.Config;
24  import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch;
25  import org.apache.commons.collections.map.ReferenceMap;
26  import org.owasp.dependencycheck.dependency.Vulnerability;
27  import org.owasp.dependencycheck.dependency.VulnerableSoftware;
28  import org.owasp.dependencycheck.utils.*;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  import javax.annotation.concurrent.ThreadSafe;
33  import java.io.IOException;
34  import java.net.URL;
35  import java.nio.charset.StandardCharsets;
36  import java.sql.CallableStatement;
37  import java.sql.Connection;
38  import java.sql.JDBCType;
39  import java.sql.PreparedStatement;
40  import java.sql.ResultSet;
41  import java.sql.SQLException;
42  import java.sql.Statement;
43  import java.util.*;
44  import java.util.stream.Collectors;
45  import org.anarres.jdiagnostics.DefaultQuery;
46  
47  import static org.apache.commons.collections.map.AbstractReferenceMap.HARD;
48  import static org.apache.commons.collections.map.AbstractReferenceMap.SOFT;
49  import org.owasp.dependencycheck.analyzer.exception.LambdaExceptionWrapper;
50  import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException;
51  import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem;
52  import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*;
53  import org.owasp.dependencycheck.data.update.cpe.CpeEcosystemCache;
54  import org.owasp.dependencycheck.data.update.cpe.CpePlus;
55  import io.github.jeremylong.openvulnerability.client.nvd.CvssV2;
56  import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data;
57  import io.github.jeremylong.openvulnerability.client.nvd.CvssV3;
58  import io.github.jeremylong.openvulnerability.client.nvd.CvssV3Data;
59  import io.github.jeremylong.openvulnerability.client.nvd.CvssV4;
60  import io.github.jeremylong.openvulnerability.client.nvd.CvssV4Data;
61  import io.github.jeremylong.openvulnerability.client.nvd.LangString;
62  import io.github.jeremylong.openvulnerability.client.nvd.Node;
63  import io.github.jeremylong.openvulnerability.client.nvd.Reference;
64  import io.github.jeremylong.openvulnerability.client.nvd.Weakness;
65  import org.owasp.dependencycheck.dependency.VulnerableSoftwareBuilder;
66  import us.springett.parsers.cpe.Cpe;
67  import us.springett.parsers.cpe.CpeBuilder;
68  import us.springett.parsers.cpe.CpeParser;
69  import us.springett.parsers.cpe.exceptions.CpeParsingException;
70  import us.springett.parsers.cpe.exceptions.CpeValidationException;
71  
72  /**
73   * The database holding information about the NVD CVE data. This class is safe
74   * to be accessed from multiple threads in parallel, however internally only one
75   * connection will be used.
76   *
77   * @author Jeremy Long
78   */
79  @ThreadSafe
80  public final class CveDB implements AutoCloseable {
81  
82      /**
83       * The logger.
84       */
85      private static final Logger LOGGER = LoggerFactory.getLogger(CveDB.class);
86  
87      /**
88       * Resource location for SQL file containing updates to the ecosystem cache.
89       */
90      public static final String DB_ECOSYSTEM_CACHE = "data/dbEcosystemCacheUpdates.sql";
91  
92      /**
93       * The database connection manager.
94       */
95      private final DatabaseManager databaseManager;
96  
97      /**
98       * The bundle of statements used when accessing the database.
99       */
100     private ResourceBundle statementBundle;
101     /**
102      * Database properties object containing the 'properties' from the database
103      * table.
104      */
105     private DatabaseProperties databaseProperties;
106     /**
107      * The filter for 2.3 CPEs in the CVEs - we don't import unless we get a
108      * match.
109      */
110     private final String cpeStartsWithFilter;
111     /**
112      * Cache for CVE lookup; used to speed up the vulnerability search process.
113      */
114     @SuppressWarnings("unchecked")
115     private final Map<String, List<Vulnerability>> vulnerabilitiesForCpeCache = Collections.synchronizedMap(new ReferenceMap(HARD, SOFT));
116     /**
117      * The configured settings
118      */
119     private final Settings settings;
120 
121     /**
122      * Utility to extract information from
123      * {@linkplain org.owasp.dependencycheck.data.nvd.json.DefCveItem}.
124      */
125     private final CveItemOperator cveItemConverter;
126     /**
127      * Flag indicating if the database is Oracle.
128      */
129     private boolean isOracle = false;
130     /**
131      * Flag indicating if the database is H2.
132      */
133     private boolean isH2 = false;
134 
135     /**
136      * Updates the EcoSystem Cache.
137      *
138      * @return The number of records updated by the DB_ECOSYSTEM_CACHE update
139      * script.
140      */
141     public int updateEcosystemCache() {
142         LOGGER.debug("Updating the ecosystem cache");
143         int updateCount = 0;
144         try {
145             final URL url = Resources.getResource(DB_ECOSYSTEM_CACHE);
146             final List<String> sql = Resources.readLines(url, StandardCharsets.UTF_8);
147 
148             try (Connection conn = databaseManager.getConnection(); Statement statement = conn.createStatement()) {
149                 for (String single : sql) {
150                     updateCount += statement.executeUpdate(single);
151                 }
152             } catch (SQLException ex) {
153                 LOGGER.debug("", ex);
154                 throw new DatabaseException("Unable to update the ecosystem cache", ex);
155             }
156         } catch (IOException ex) {
157             throw new DatabaseException("Unable to update the ecosystem cache", ex);
158         } catch (LinkageError ex) {
159             LOGGER.debug(new DefaultQuery(ex).call().toString());
160         }
161         return updateCount;
162     }
163 
164     /**
165      * The enumeration value names must match the keys of the statements in the
166      * statement bundles "dbStatements*.properties".
167      */
168     enum PreparedStatementCveDb {
169         /**
170          * Key for SQL Statement.
171          */
172         CLEANUP_ORPHANS,
173         /**
174          * Key for update ecosystem.
175          */
176         UPDATE_ECOSYSTEM,
177         /**
178          * Key for update ecosystem.
179          */
180         UPDATE_ECOSYSTEM2,
181         /**
182          * Key for SQL Statement.
183          */
184         COUNT_CPE,
185         /**
186          * Key for SQL Statement.
187          */
188         DELETE_VULNERABILITY,
189         /**
190          * Key for SQL Statement.
191          */
192         INSERT_PROPERTY,
193         /**
194          * Key for SQL Statement.
195          */
196         INSERT_CWE,
197         /**
198          * Key for SQL Statement.
199          */
200         INSERT_REFERENCE,
201         /**
202          * Key for SQL Statement.
203          */
204         INSERT_SOFTWARE,
205         /**
206          * Key for SQL Statement.
207          */
208         MERGE_PROPERTY,
209         /**
210          * Key for SQL Statement.
211          */
212         SELECT_CPE_ENTRIES,
213         /**
214          * Key for SQL Statement.
215          */
216         SELECT_CVE_FROM_SOFTWARE,
217         /**
218          * Key for SQL Statement.
219          */
220         SELECT_PROPERTIES,
221         /**
222          * Key for SQL Statement.
223          */
224         SELECT_VULNERABILITY_CWE,
225         /**
226          * Key for SQL Statement.
227          */
228         SELECT_REFERENCES,
229         /**
230          * Key for SQL Statement.
231          */
232         SELECT_SOFTWARE,
233         /**
234          * Key for SQL Statement.
235          */
236         SELECT_VENDOR_PRODUCT_LIST,
237         /**
238          * Key for SQL Statement.
239          */
240         SELECT_VENDOR_PRODUCT_LIST_FOR_NODE,
241         /**
242          * Key for SQL Statement.
243          */
244         SELECT_VULNERABILITY,
245         /**
246          * Key for SQL Statement.
247          */
248         UPDATE_PROPERTY,
249         /**
250          * Key for SQL Statement.
251          */
252         UPDATE_VULNERABILITY,
253         /**
254          * Key for SQL Statement.
255          */
256         SELECT_CPE_ECOSYSTEM,
257         /**
258          * Key for SQL Statement.
259          */
260         MERGE_CPE_ECOSYSTEM,
261         /**
262          * Key for SQL Statement.
263          */
264         DELETE_UNUSED_DICT_CPE,
265         /**
266          * Key for SQL Statement.
267          */
268         ADD_DICT_CPE,
269         /**
270          * Key for SQL Statement.
271          */
272         SELECT_KNOWN_EXPLOITED_VULNERABILITIES,
273         /**
274          * Key for SQL Statement.
275          */
276         MERGE_KNOWN_EXPLOITED
277     }
278 
279     /**
280      * Creates a new CveDB object and opens the database connection. Note, the
281      * connection must be closed by the caller by calling the close method.
282      *
283      * @param settings the configured settings
284      * @throws DatabaseException thrown if there is an exception opening the
285      * database.
286      */
287     public CveDB(Settings settings) throws DatabaseException {
288         this.settings = settings;
289         this.cpeStartsWithFilter = settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:2.3:a:");
290         this.cveItemConverter = new CveItemOperator(cpeStartsWithFilter);
291         databaseManager = new DatabaseManager(settings);
292         statementBundle = databaseManager.getSqlStatements();
293         isOracle = databaseManager.isOracle();
294         isH2 = databaseManager.isH2Connection();
295     }
296 
297     /**
298      * Opens the database connection pool.
299      */
300     public void open() {
301         databaseManager.open();
302         databaseProperties = new DatabaseProperties(this);
303     }
304 
305     /**
306      * Closes the database connection. Close should be called on this object
307      * when it is done being used.
308      */
309     @Override
310     public void close() {
311         if (isOpen()) {
312             LOGGER.debug("Closing database");
313             clearCache();
314             LOGGER.debug("Cache cleared");
315             try {
316                 databaseManager.close();
317                 LOGGER.debug("Connection closed");
318             } catch (Throwable ex) {
319                 LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details.");
320                 LOGGER.debug("", ex);
321             }
322             releaseResources();
323             LOGGER.debug("Resources released");
324             databaseManager.cleanup();
325         }
326     }
327 
328     /**
329      * Releases the resources used by CveDB.
330      */
331     private void releaseResources() {
332         statementBundle = null;
333         databaseProperties = null;
334     }
335 
336     /**
337      * Returns whether the database connection is open or closed.
338      *
339      * @return whether the database connection is open or closed
340      */
341     public boolean isOpen() {
342         return databaseManager.isOpen();
343     }
344 
345     /**
346      * Creates a prepared statement from the given key. The SQL is stored in a
347      * properties file and the key is used to lookup the specific query.
348      *
349      * @param connection the database connection
350      * @param key the key to select the prepared statement from the properties
351      * file
352      * @param parameter the first parameter to pass into the statement
353      * @return the prepared statement
354      * @throws DatabaseException throw if there is an error generating the
355      * prepared statement
356      */
357     private PreparedStatement getPreparedStatement(Connection connection, PreparedStatementCveDb key, String parameter)
358             throws DatabaseException, SQLException {
359         final PreparedStatement preparedStatement = getPreparedStatement(connection, key);
360         preparedStatement.setString(1, parameter);
361         return preparedStatement;
362     }
363 
364     /**
365      * Creates a prepared statement from the given key. The SQL is stored in a
366      * properties file and the key is used to lookup the specific query.
367      *
368      * @param connection the database connection
369      * @param key the key to select the prepared statement from the properties
370      * file
371      * @param parameter the first parameter to pass into the statement
372      * @return the prepared statement
373      * @throws DatabaseException throw if there is an error generating the
374      * prepared statement
375      */
376     private PreparedStatement getPreparedStatement(Connection connection, PreparedStatementCveDb key, int parameter)
377             throws DatabaseException, SQLException {
378         final PreparedStatement preparedStatement = getPreparedStatement(connection, key);
379         preparedStatement.setInt(1, parameter);
380         return preparedStatement;
381     }
382 
383     /**
384      * Creates a prepared statement from the given key. The SQL is stored in a
385      * properties file and the key is used to lookup the specific query.
386      *
387      * @param connection the database connection
388      * @param key the key to select the prepared statement from the properties
389      * file
390      * @return the prepared statement
391      * @throws DatabaseException throw if there is an error generating the
392      * prepared statement
393      */
394     private PreparedStatement getPreparedStatement(Connection connection, PreparedStatementCveDb key) throws DatabaseException {
395         PreparedStatement preparedStatement = null;
396         try {
397             final String statementString = statementBundle.getString(key.name());
398             if (isOracle && key == UPDATE_VULNERABILITY) {
399                 preparedStatement = connection.prepareCall(statementString);
400 //            } else if (key == INSERT_CPE) {
401 //                final String[] returnedColumns = {"id"};
402 //                preparedStatement = connection.prepareStatement(statementString, returnedColumns);
403             } else {
404                 preparedStatement = connection.prepareStatement(statementString);
405             }
406             if (isOracle) {
407                 // Oracle has a default fetch-size of 10; MariaDB, MySQL, SQLServer and PostgreSQL by default cache the full
408                 // resultset at the client https://venkatsadasivam.com/2009/02/01/jdbc-performance-tuning-with-optimal-fetch-size/
409                 preparedStatement.setFetchSize(10_000);
410             }
411         } catch (SQLException ex) {
412             throw new DatabaseException(ex);
413         } catch (MissingResourceException ex) {
414             if (!ex.getMessage().contains("key MERGE_PROPERTY")) {
415                 throw new DatabaseException(ex);
416             }
417         }
418         return preparedStatement;
419     }
420 
421     /**
422      * Cleans up the object and ensures that "close" has been called.
423      *
424      * @throws Throwable thrown if there is a problem
425      */
426     @Override
427     @SuppressWarnings("FinalizeDeclaration")
428     protected void finalize() throws Throwable {
429         LOGGER.debug("Entering finalize");
430         close();
431         super.finalize();
432     }
433 
434     /**
435      * Get the value of databaseProperties.
436      *
437      * @return the value of databaseProperties
438      */
439     public DatabaseProperties getDatabaseProperties() {
440         return databaseProperties;
441     }
442 
443     /**
444      * Used within the unit tests to reload the database properties.
445      *
446      * @return the database properties
447      */
448     DatabaseProperties reloadProperties() {
449         databaseProperties = new DatabaseProperties(this);
450         return databaseProperties;
451     }
452 
453     /**
454      * Searches the CPE entries in the database and retrieves all entries for a
455      * given vendor and product combination. The returned list will include all
456      * versions of the product that are registered in the NVD CVE data.
457      *
458      * @param vendor the identified vendor name of the dependency being analyzed
459      * @param product the identified name of the product of the dependency being
460      * analyzed
461      * @return a set of vulnerable software
462      */
463     public Set<CpePlus> getCPEs(String vendor, String product) {
464         final Set<CpePlus> cpe = new HashSet<>();
465         try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ENTRIES)) {
466             //part, vendor, product, version, update_version, edition,
467             //lang, sw_edition, target_sw, target_hw, other, ecosystem
468             ps.setString(1, vendor);
469             ps.setString(2, product);
470             try (ResultSet rs = ps.executeQuery()) {
471                 final CpeBuilder builder = new CpeBuilder();
472                 while (rs.next()) {
473                     final Cpe entry = builder
474                             .part(rs.getString(1))
475                             .vendor(rs.getString(2))
476                             .product(rs.getString(3))
477                             .version(rs.getString(4))
478                             .update(rs.getString(5))
479                             .edition(rs.getString(6))
480                             .language(rs.getString(7))
481                             .swEdition(rs.getString(8))
482                             .targetSw(rs.getString(9))
483                             .targetHw(rs.getString(10))
484                             .other(rs.getString(11)).build();
485                     final CpePlus plus = new CpePlus(entry, rs.getString(12));
486                     cpe.add(plus);
487                 }
488             }
489         } catch (SQLException | CpeParsingException | CpeValidationException ex) {
490             LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
491             LOGGER.debug("", ex);
492         }
493         return cpe;
494     }
495 
496     /**
497      * Returns the entire list of vendor/product combinations.
498      *
499      * @return the entire list of vendor/product combinations
500      * @throws DatabaseException thrown when there is an error retrieving the
501      * data from the DB
502      */
503     public Set<Pair<String, String>> getVendorProductList() throws DatabaseException {
504         final Set<Pair<String, String>> data = new HashSet<>();
505         try (Connection conn = databaseManager.getConnection();
506                 PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST);
507                 ResultSet rs = ps.executeQuery()) {
508             while (rs.next()) {
509                 data.add(new Pair<>(rs.getString(1), rs.getString(2)));
510             }
511         } catch (SQLException ex) {
512             final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
513             throw new DatabaseException(msg, ex);
514         }
515         return data;
516     }
517 
518     /**
519      * Returns the entire list of vendor/product combinations filtered for just
520      * Node JS related products.
521      *
522      * @return the list of vendor/product combinations that are known to be
523      * related to Node JS
524      * @throws DatabaseException thrown when there is an error retrieving the
525      * data from the DB
526      */
527     public Set<Pair<String, String>> getVendorProductListForNode() throws DatabaseException {
528         final Set<Pair<String, String>> data = new HashSet<>();
529         try (Connection conn = databaseManager.getConnection();
530                 PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST_FOR_NODE);
531                 ResultSet rs = ps.executeQuery()) {
532             while (rs.next()) {
533                 data.add(new Pair<>(rs.getString(1), rs.getString(2)));
534             }
535         } catch (SQLException ex) {
536             final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
537             throw new DatabaseException(msg, ex);
538         }
539         return data;
540     }
541 
542     /**
543      * Returns a set of properties.
544      *
545      * @return the properties from the database
546      */
547     public Properties getProperties() {
548         final Properties prop = new Properties();
549         try (Connection conn = databaseManager.getConnection();
550                 PreparedStatement ps = getPreparedStatement(conn, SELECT_PROPERTIES);
551                 ResultSet rs = ps.executeQuery()) {
552             while (rs.next()) {
553                 prop.setProperty(rs.getString(1), rs.getString(2));
554             }
555         } catch (SQLException ex) {
556             LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
557             LOGGER.debug("", ex);
558         }
559         return prop;
560     }
561 
562     /**
563      * Saves a property to the database.
564      *
565      * @param key the property key
566      * @param value the property value
567      */
568     public void saveProperty(String key, String value) {
569         clearCache();
570         try (Connection conn = databaseManager.getConnection(); PreparedStatement mergeProperty = getPreparedStatement(conn, MERGE_PROPERTY)) {
571             if (mergeProperty != null) {
572                 mergeProperty.setString(1, key);
573                 mergeProperty.setString(2, value);
574                 mergeProperty.execute();
575             } else {
576                 // No Merge statement, so doing an Update/Insert...
577                 try (PreparedStatement updateProperty = getPreparedStatement(conn, UPDATE_PROPERTY)) {
578                     updateProperty.setString(1, value);
579                     updateProperty.setString(2, key);
580                     if (updateProperty.executeUpdate() == 0) {
581                         try (PreparedStatement insertProperty = getPreparedStatement(conn, INSERT_PROPERTY)) {
582                             insertProperty.setString(1, key);
583                             insertProperty.setString(2, value);
584                             insertProperty.executeUpdate();
585                         }
586                     }
587                 }
588             }
589         } catch (SQLException ex) {
590             LOGGER.warn("Unable to save property '{}' with a value of '{}' to the database", key, value);
591             LOGGER.debug("", ex);
592         }
593     }
594 
595     /**
596      * Clears cache. Should be called whenever something is modified. While this
597      * is not the optimal cache eviction strategy, this is good enough for
598      * typical usage (update DB and then only read) and it is easier to maintain
599      * the code.
600      * <p>
601      * It should be also called when DB is closed.
602      * </p>
603      */
604     private void clearCache() {
605         vulnerabilitiesForCpeCache.clear();
606     }
607 
608     /**
609      * Retrieves the vulnerabilities associated with the specified CPE.
610      *
611      * @param cpe the CPE to retrieve vulnerabilities for
612      * @return a list of Vulnerabilities
613      * @throws DatabaseException thrown if there is an exception retrieving data
614      */
615     public List<Vulnerability> getVulnerabilities(Cpe cpe) throws DatabaseException {
616         final List<Vulnerability> cachedVulnerabilities = vulnerabilitiesForCpeCache.get(cpe.toCpe23FS());
617         if (cachedVulnerabilities != null) {
618             LOGGER.debug("Cache hit for {}", cpe.toCpe23FS());
619             return cachedVulnerabilities;
620         } else {
621             LOGGER.debug("Cache miss for {}", cpe.toCpe23FS());
622         }
623 
624         final List<Vulnerability> vulnerabilities = new ArrayList<>();
625         try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_CVE_FROM_SOFTWARE)) {
626             ps.setString(1, cpe.getVendor());
627             ps.setString(2, cpe.getProduct());
628             try (ResultSet rs = ps.executeQuery()) {
629                 String currentCVE = "";
630                 final Set<VulnerableSoftware> vulnSoftware = new HashSet<>();
631                 final VulnerableSoftwareBuilder vulnerableSoftwareBuilder = new VulnerableSoftwareBuilder();
632                 while (rs.next()) {
633                     final String cveId = rs.getString(1);
634                     if (currentCVE.isEmpty()) {
635                         //first loop we don't have the cveId
636                         currentCVE = cveId;
637                     }
638                     if (!vulnSoftware.isEmpty() && !currentCVE.equals(cveId)) { //check for match and add
639                         final VulnerableSoftware matchedCPE = getMatchingSoftware(cpe, vulnSoftware);
640                         if (matchedCPE != null) {
641                             final Vulnerability v = getVulnerability(currentCVE, conn);
642                             if (v != null) {
643                                 v.setMatchedVulnerableSoftware(matchedCPE);
644                                 v.setSource(Vulnerability.Source.NVD);
645                                 vulnerabilities.add(v);
646                             }
647                         }
648                         vulnSoftware.clear();
649                         currentCVE = cveId;
650                     }
651                     // 1 cve, 2 part, 3 vendor, 4 product, 5 version, 6 update_version, 7 edition,
652                     // 8 lang, 9 sw_edition, 10 target_sw, 11 target_hw, 12 other, 13 versionEndExcluding,
653                     //14 versionEndIncluding, 15 versionStartExcluding, 16 versionStartIncluding, 17 vulnerable
654                     final VulnerableSoftware vs;
655                     try {
656                         vs = vulnerableSoftwareBuilder.part(rs.getString(2)).vendor(rs.getString(3))
657                                 .product(rs.getString(4)).version(rs.getString(5)).update(rs.getString(6))
658                                 .edition(rs.getString(7)).language(rs.getString(8)).swEdition(rs.getString(9))
659                                 .targetSw(rs.getString(10)).targetHw(rs.getString(11)).other(rs.getString(12))
660                                 .versionEndExcluding(rs.getString(13)).versionEndIncluding(rs.getString(14))
661                                 .versionStartExcluding(rs.getString(15)).versionStartIncluding(rs.getString(16))
662                                 .vulnerable(rs.getBoolean(17)).build();
663                     } catch (CpeParsingException | CpeValidationException ex) {
664                         throw new DatabaseException("Database contains an invalid Vulnerable Software Entry", ex);
665                     }
666                     vulnSoftware.add(vs);
667                 }
668 
669                 //remember to process the last set of CVE/CPE entries
670                 final VulnerableSoftware matchedCPE = getMatchingSoftware(cpe, vulnSoftware);
671                 if (matchedCPE != null) {
672                     final Vulnerability v = getVulnerability(currentCVE, conn);
673                     if (v != null) {
674                         v.setMatchedVulnerableSoftware(matchedCPE);
675                         v.setSource(Vulnerability.Source.NVD);
676                         vulnerabilities.add(v);
677                     }
678                 }
679             }
680         } catch (SQLException ex) {
681             throw new DatabaseException("Exception retrieving vulnerability for " + cpe.toCpe23FS(), ex);
682         }
683         vulnerabilitiesForCpeCache.put(cpe.toCpe23FS(), vulnerabilities);
684         return vulnerabilities;
685     }
686 
687     /**
688      * Gets a vulnerability for the provided CVE.
689      *
690      * @param cve the CVE to lookup
691      * @return a vulnerability object
692      * @throws DatabaseException if an exception occurs
693      */
694     public Vulnerability getVulnerability(String cve) throws DatabaseException {
695         try (Connection conn = databaseManager.getConnection()) {
696             return getVulnerability(cve, conn);
697         } catch (SQLException ex) {
698             throw new DatabaseException("Error retrieving " + cve, ex);
699         }
700     }
701 
702     /**
703      * Gets a vulnerability for the provided CVE.
704      *
705      * @param cve the CVE to lookup
706      * @param conn already active database connection
707      * @return a vulnerability object
708      * @throws DatabaseException if an exception occurs
709      */
710     public Vulnerability getVulnerability(String cve, Connection conn) throws DatabaseException {
711         final int cveId;
712         final VulnerableSoftwareBuilder vulnerableSoftwareBuilder = new VulnerableSoftwareBuilder();
713         Vulnerability vuln = null;
714         try {
715             try (PreparedStatement psV = getPreparedStatement(conn, SELECT_VULNERABILITY, cve); ResultSet rsV = psV.executeQuery()) {
716                 if (rsV.next()) {
717                     //1.id, 2.description,
718                     cveId = rsV.getInt(1);
719                     vuln = new Vulnerability();
720                     vuln.setSource(Vulnerability.Source.NVD);
721                     vuln.setName(cve);
722                     vuln.setDescription(rsV.getString(2));
723 
724                     //3.v2Severity, 4.v2ExploitabilityScore, 5.v2ImpactScore, 6.v2AcInsufInfo, 7.v2ObtainAllPrivilege,
725                     //8.v2ObtainUserPrivilege, 9.v2ObtainOtherPrivilege, 10.v2UserInteractionRequired, 11.v2Score,
726                     //12.v2AccessVector, 13.v2AccessComplexity, 14.v2Authentication, 15.v2ConfidentialityImpact,
727                     //16.v2IntegrityImpact, 17.v2AvailabilityImpact, 18.v2Version,
728                     if (rsV.getObject(11) != null) {
729 
730                         final CvssV2Data.AccessVectorType accessVector = CvssV2Data.AccessVectorType.fromValue(rsV.getString(12));
731                         final CvssV2Data.AccessComplexityType accessComplexity = CvssV2Data.AccessComplexityType.fromValue(rsV.getString(13));
732                         final CvssV2Data.AuthenticationType authentication = CvssV2Data.AuthenticationType.fromValue(rsV.getString(14));
733                         final CvssV2Data.CiaType confidentialityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(15));
734                         final CvssV2Data.CiaType integrityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(16));
735                         final CvssV2Data.CiaType availabilityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(17));
736                         final String vector = String.format("/AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s",
737                                 accessVector == null ? "" : accessVector.value().substring(0, 1),
738                                 accessComplexity == null ? "" : accessComplexity.value().substring(0, 1),
739                                 authentication == null ? "" : authentication.value().substring(0, 1),
740                                 confidentialityImpact == null ? "" : confidentialityImpact.value().substring(0, 1),
741                                 integrityImpact == null ? "" : integrityImpact.value().substring(0, 1),
742                                 availabilityImpact == null ? "" : availabilityImpact.value().substring(0, 1));
743 
744                         //some older test data may not correctly have the version set.
745                         String cveVersion = "2.0";
746                         if (rsV.getString(18) != null) {
747                             cveVersion = rsV.getString(18);
748                         }
749                         final CvssV2Data cvssData = new CvssV2Data(cveVersion, vector, accessVector,
750                                 accessComplexity, authentication, confidentialityImpact,
751                                 integrityImpact, availabilityImpact, rsV.getDouble(11), rsV.getString(3),
752                                 null, null, null, null, null, null, null, null, null, null);
753                         final CvssV2 cvss = new CvssV2(null, CvssV2.Type.PRIMARY, cvssData, rsV.getString(3),
754                                 rsV.getDouble(4), rsV.getDouble(5), rsV.getBoolean(6), rsV.getBoolean(7),
755                                 rsV.getBoolean(8), rsV.getBoolean(9), rsV.getBoolean(10));
756                         vuln.setCvssV2(cvss);
757                     }
758                     //19.v3ExploitabilityScore, 20.v3ImpactScore, 21.v3AttackVector, 22.v3AttackComplexity, 23.v3PrivilegesRequired,
759                     //24.v3UserInteraction, 25.v3Scope, 26.v3ConfidentialityImpact, 27.v3IntegrityImpact, 28.v3AvailabilityImpact,
760                     //29.v3BaseScore, 30.v3BaseSeverity, 31.v3Version
761                     if (rsV.getObject(21) != null) {
762                         //some older test data may not correctly have the version set.
763                         String cveVersion = "3.1";
764                         if (rsV.getString(31) != null) {
765                             cveVersion = rsV.getString(31);
766                         }
767                         final CvssV3Data.Version version = CvssV3Data.Version.fromValue(cveVersion);
768                         final CvssV3Data.AttackVectorType attackVector = CvssV3Data.AttackVectorType.fromValue(rsV.getString(21));
769                         final CvssV3Data.AttackComplexityType attackComplexity = CvssV3Data.AttackComplexityType.fromValue(rsV.getString(22));
770                         final CvssV3Data.PrivilegesRequiredType privilegesRequired = CvssV3Data.PrivilegesRequiredType.fromValue(rsV.getString(23));
771                         final CvssV3Data.UserInteractionType userInteraction = CvssV3Data.UserInteractionType.fromValue(rsV.getString(24));
772                         final CvssV3Data.ScopeType scope = CvssV3Data.ScopeType.fromValue(rsV.getString(25));
773                         final CvssV3Data.CiaType confidentialityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(26));
774                         final CvssV3Data.CiaType integrityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(27));
775                         final CvssV3Data.CiaType availabilityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(28));
776                         final CvssV3Data.SeverityType baseSeverity = CvssV3Data.SeverityType.fromValue(rsV.getString(30));
777                         final String vector = String.format("CVSS:%s/AV:%s/AC:%s/PR:%s/UI:%s/S:%s/C:%s/I:%s/A:%s",
778                                 version == null ? "" : version,
779                                 attackVector == null ? "" : attackVector.value().substring(0, 1),
780                                 attackComplexity == null ? "" : attackComplexity.value().substring(0, 1),
781                                 privilegesRequired == null ? "" : privilegesRequired.value().substring(0, 1),
782                                 userInteraction == null ? "" : userInteraction.value().substring(0, 1),
783                                 scope == null ? "" : scope.value().substring(0, 1),
784                                 confidentialityImpact == null ? "" : confidentialityImpact.value().substring(0, 1),
785                                 integrityImpact == null ? "" : integrityImpact.value().substring(0, 1),
786                                 availabilityImpact == null ? "" : availabilityImpact.value().substring(0, 1));
787 
788                         final CvssV3Data cvssData = new CvssV3Data(version, vector, attackVector, attackComplexity, privilegesRequired,
789                                 userInteraction, scope, confidentialityImpact, integrityImpact, availabilityImpact,
790                                 rsV.getDouble(29), baseSeverity, CvssV3Data.ExploitCodeMaturityType.PROOF_OF_CONCEPT,
791                                 CvssV3Data.RemediationLevelType.NOT_DEFINED, CvssV3Data.ConfidenceType.REASONABLE, 0.0,
792                                 CvssV3Data.SeverityType.MEDIUM, CvssV3Data.CiaRequirementType.NOT_DEFINED,
793                                 CvssV3Data.CiaRequirementType.NOT_DEFINED, CvssV3Data.CiaRequirementType.NOT_DEFINED,
794                                 CvssV3Data.ModifiedAttackVectorType.ADJACENT_NETWORK, CvssV3Data.ModifiedAttackComplexityType.NOT_DEFINED,
795                                 CvssV3Data.ModifiedPrivilegesRequiredType.NOT_DEFINED, CvssV3Data.ModifiedUserInteractionType.NOT_DEFINED,
796                                 CvssV3Data.ModifiedScopeType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED,
797                                 CvssV3Data.ModifiedCiaType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED, 1.0,
798                                 CvssV3Data.SeverityType.NONE);
799                         final CvssV3 cvss = new CvssV3(null, null, cvssData, rsV.getDouble(19), rsV.getDouble(20));
800                         vuln.setCvssV3(cvss);
801                     }
802 //                    32.v4version, 33.v4attackVector, 34.v4attackComplexity, 35.v4attackRequirements, 36.v4privilegesRequired,
803 //                    37.v4userInteraction, 38.v4vulnConfidentialityImpact, 39.v4vulnIntegrityImpact, 40.v4vulnAvailabilityImpact,
804 //                    41.v4subConfidentialityImpact, 42.v4subIntegrityImpact, 43.v4subAvailabilityImpact, 44.v4exploitMaturity,
805 //                    45.v4confidentialityRequirement, 46.v4integrityRequirement, 47.v4availabilityRequirement, 48.v4modifiedAttackVector,
806 //                    49.v4modifiedAttackComplexity, 50.v4modifiedAttackRequirements, 51.v4modifiedPrivilegesRequired, 52.v4modifiedUserInteraction,
807 //                    53.v4modifiedVulnConfidentialityImpact, 54.v4modifiedVulnIntegrityImpact, 55.v4modifiedVulnAvailabilityImpact,
808 //                    56.v4modifiedSubConfidentialityImpact, 57.v4modifiedSubIntegrityImpact, 58.v4modifiedSubAvailabilityImpact,
809 //                    59.v4safety, 60.v4automatable, 61.v4recovery, 62.v4valueDensity, 63.v4vulnerabilityResponseEffort, 64.v4providerUrgency,
810 //                    65.v4baseScore, 66.v4baseSeverity, 67.v4threatScore, 68.v4threatSeverity, 69.v4environmentalScore, 70.v4environmentalSeverity
811 //                    71.v4source, 72.v4type
812                     if (rsV.getObject(33) != null) {
813                         String vectorString = null;
814 
815                         String value = rsV.getString(32);
816                         final CvssV4Data.Version version = CvssV4Data.Version.fromValue(value);
817                         CvssV4Data.AttackVectorType attackVector = null;
818                         value = rsV.getString(33);
819                         if (value != null) {
820                             attackVector = CvssV4Data.AttackVectorType.fromValue(value);
821                         }
822                         CvssV4Data.AttackComplexityType attackComplexity = null;
823                         value = rsV.getString(34);
824                         if (value != null) {
825                             attackComplexity = CvssV4Data.AttackComplexityType.fromValue(value);
826                         }
827                         CvssV4Data.AttackRequirementsType attackRequirements = null;
828                         value = rsV.getString(35);
829                         if (value != null) {
830                             attackRequirements = CvssV4Data.AttackRequirementsType.fromValue(value);
831                         }
832                         CvssV4Data.PrivilegesRequiredType privilegesRequired = null;
833                         value = rsV.getString(36);
834                         if (value != null) {
835                             privilegesRequired = CvssV4Data.PrivilegesRequiredType.fromValue(value);
836                         }
837                         CvssV4Data.UserInteractionType userInteraction = null;
838                         value = rsV.getString(37);
839                         if (value != null) {
840                             userInteraction = CvssV4Data.UserInteractionType.fromValue(value);
841                         }
842                         CvssV4Data.CiaType vulnConfidentialityImpact = null;
843                         value = rsV.getString(38);
844                         if (value != null) {
845                             vulnConfidentialityImpact = CvssV4Data.CiaType.fromValue(value);
846                         }
847                         CvssV4Data.CiaType vulnIntegrityImpact = null;
848                         value = rsV.getString(39);
849                         if (value != null) {
850                             vulnIntegrityImpact = CvssV4Data.CiaType.fromValue(value);
851                         }
852                         CvssV4Data.CiaType vulnAvailabilityImpact = null;
853                         value = rsV.getString(40);
854                         if (value != null) {
855                             vulnAvailabilityImpact = CvssV4Data.CiaType.fromValue(value);
856                         }
857                         CvssV4Data.CiaType subConfidentialityImpact = null;
858                         value = rsV.getString(41);
859                         if (value != null) {
860                             subConfidentialityImpact = CvssV4Data.CiaType.fromValue(value);
861                         }
862                         CvssV4Data.CiaType subIntegrityImpact = null;
863                         value = rsV.getString(42);
864                         if (value != null) {
865                             subIntegrityImpact = CvssV4Data.CiaType.fromValue(value);
866                         }
867                         CvssV4Data.CiaType subAvailabilityImpact = null;
868                         value = rsV.getString(43);
869                         if (value != null) {
870                             subAvailabilityImpact = CvssV4Data.CiaType.fromValue(value);
871                         }
872                         CvssV4Data.ExploitMaturityType exploitMaturity = null;
873                         value = rsV.getString(44);
874                         if (value != null) {
875                             exploitMaturity = CvssV4Data.ExploitMaturityType.fromValue(value);
876                         }
877                         CvssV4Data.CiaRequirementType confidentialityRequirement = null;
878                         value = rsV.getString(45);
879                         if (value != null) {
880                             confidentialityRequirement = CvssV4Data.CiaRequirementType.fromValue(value);
881                         }
882                         CvssV4Data.CiaRequirementType integrityRequirement = null;
883                         value = rsV.getString(46);
884                         if (value != null) {
885                             integrityRequirement = CvssV4Data.CiaRequirementType.fromValue(value);
886                         }
887                         CvssV4Data.CiaRequirementType availabilityRequirement = null;
888                         value = rsV.getString(47);
889                         if (value != null) {
890                             availabilityRequirement = CvssV4Data.CiaRequirementType.fromValue(value);
891                         }
892                         CvssV4Data.ModifiedAttackVectorType modifiedAttackVector = null;
893                         value = rsV.getString(48);
894                         if (value != null) {
895                             modifiedAttackVector = CvssV4Data.ModifiedAttackVectorType.fromValue(value);
896                         }
897                         CvssV4Data.ModifiedAttackComplexityType modifiedAttackComplexity = null;
898                         value = rsV.getString(49);
899                         if (value != null) {
900                             modifiedAttackComplexity = CvssV4Data.ModifiedAttackComplexityType.fromValue(value);
901                         }
902                         CvssV4Data.ModifiedAttackRequirementsType modifiedAttackRequirements = null;
903                         value = rsV.getString(50);
904                         if (value != null) {
905                             modifiedAttackRequirements = CvssV4Data.ModifiedAttackRequirementsType.fromValue(value);
906                         }
907                         CvssV4Data.ModifiedPrivilegesRequiredType modifiedPrivilegesRequired = null;
908                         value = rsV.getString(51);
909                         if (value != null) {
910                             modifiedPrivilegesRequired = CvssV4Data.ModifiedPrivilegesRequiredType.fromValue(value);
911                         }
912                         CvssV4Data.ModifiedUserInteractionType modifiedUserInteraction = null;
913                         value = rsV.getString(52);
914                         if (value != null) {
915                             modifiedUserInteraction = CvssV4Data.ModifiedUserInteractionType.fromValue(value);
916                         }
917                         CvssV4Data.ModifiedCiaType modifiedVulnConfidentialityImpact = null;
918                         value = rsV.getString(53);
919                         if (value != null) {
920                             modifiedVulnConfidentialityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
921                         }
922                         CvssV4Data.ModifiedCiaType modifiedVulnIntegrityImpact = null;
923                         value = rsV.getString(54);
924                         if (value != null) {
925                             modifiedVulnIntegrityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
926                         }
927                         CvssV4Data.ModifiedCiaType modifiedVulnAvailabilityImpact = null;
928                         value = rsV.getString(55);
929                         if (value != null) {
930                             modifiedVulnAvailabilityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
931                         }
932                         CvssV4Data.ModifiedCiaType modifiedSubConfidentialityImpact = null;
933                         value = rsV.getString(56);
934                         if (value != null) {
935                             modifiedSubConfidentialityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
936                         }
937                         CvssV4Data.ModifiedCiaType modifiedSubIntegrityImpact = null;
938                         value = rsV.getString(57);
939                         if (value != null) {
940                             modifiedSubIntegrityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
941                         }
942                         CvssV4Data.ModifiedCiaType modifiedSubAvailabilityImpact = null;
943                         value = rsV.getString(58);
944                         if (value != null) {
945                             modifiedSubAvailabilityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
946                         }
947                         CvssV4Data.SafetyType safety = null;
948                         value = rsV.getString(59);
949                         if (value != null) {
950                             safety = CvssV4Data.SafetyType.fromValue(value);
951                         }
952                         CvssV4Data.AutomatableType automatable = null;
953                         value = rsV.getString(60);
954                         if (value != null) {
955                             automatable = CvssV4Data.AutomatableType.fromValue(value);
956                         }
957                         CvssV4Data.RecoveryType recovery = null;
958                         value = rsV.getString(61);
959                         if (value != null) {
960                             recovery = CvssV4Data.RecoveryType.fromValue(value);
961                         }
962                         CvssV4Data.ValueDensityType valueDensity = null;
963                         value = rsV.getString(62);
964                         if (value != null) {
965                             valueDensity = CvssV4Data.ValueDensityType.fromValue(value);
966                         }
967                         CvssV4Data.VulnerabilityResponseEffortType vulnerabilityResponseEffort = null;
968                         value = rsV.getString(63);
969                         if (value != null) {
970                             vulnerabilityResponseEffort = CvssV4Data.VulnerabilityResponseEffortType.fromValue(value);
971                         }
972                         CvssV4Data.ProviderUrgencyType providerUrgency = null;
973                         value = rsV.getString(64);
974                         if (value != null) {
975                             providerUrgency = CvssV4Data.ProviderUrgencyType.fromValue(value);
976                         }
977                         Double baseScore = null;
978                         if (rsV.getObject(65) != null) {
979                             baseScore = rsV.getDouble(65);
980                         }
981                         CvssV4Data.SeverityType baseSeverity = null;
982                         value = rsV.getString(66);
983                         if (value != null) {
984                             baseSeverity = CvssV4Data.SeverityType.fromValue(value);
985                         }
986                         Double threatScore = null;
987                         if (rsV.getObject(67) != null) {
988                             threatScore = rsV.getDouble(67);
989                         }
990                         CvssV4Data.SeverityType threatSeverity = null;
991                         value = rsV.getString(68);
992                         if (value != null) {
993                             threatSeverity = CvssV4Data.SeverityType.fromValue(value);
994                         }
995                         Double environmentalScore = null;
996                         if (rsV.getObject(69) != null) {
997                             environmentalScore = rsV.getDouble(69);
998                         }
999                         CvssV4Data.SeverityType environmentalSeverity = null;
1000                         value = rsV.getString(70);
1001                         if (value != null) {
1002                             environmentalSeverity = CvssV4Data.SeverityType.fromValue(value);
1003                         }
1004                         //initializing data twice to get the vector string. I really should have designed the object better...
1005                         CvssV4Data data = new CvssV4Data(version, vectorString, attackVector, attackComplexity, attackRequirements, privilegesRequired,
1006                                 userInteraction, vulnConfidentialityImpact, vulnIntegrityImpact, vulnAvailabilityImpact, subConfidentialityImpact,
1007                                 subIntegrityImpact, subAvailabilityImpact, exploitMaturity, confidentialityRequirement, integrityRequirement,
1008                                 availabilityRequirement, modifiedAttackVector, modifiedAttackComplexity, modifiedAttackRequirements,
1009                                 modifiedPrivilegesRequired, modifiedUserInteraction, modifiedVulnConfidentialityImpact, modifiedVulnIntegrityImpact,
1010                                 modifiedVulnAvailabilityImpact, modifiedSubConfidentialityImpact, modifiedSubIntegrityImpact,
1011                                 modifiedSubAvailabilityImpact, safety, automatable, recovery, valueDensity, vulnerabilityResponseEffort,
1012                                 providerUrgency, baseScore, baseSeverity, threatScore, threatSeverity, environmentalScore, environmentalSeverity);
1013                         vectorString = data.toString();
1014                         data = new CvssV4Data(version, vectorString, attackVector, attackComplexity, attackRequirements, privilegesRequired,
1015                                 userInteraction, vulnConfidentialityImpact, vulnIntegrityImpact, vulnAvailabilityImpact, subConfidentialityImpact,
1016                                 subIntegrityImpact, subAvailabilityImpact, exploitMaturity, confidentialityRequirement, integrityRequirement,
1017                                 availabilityRequirement, modifiedAttackVector, modifiedAttackComplexity, modifiedAttackRequirements,
1018                                 modifiedPrivilegesRequired, modifiedUserInteraction, modifiedVulnConfidentialityImpact, modifiedVulnIntegrityImpact,
1019                                 modifiedVulnAvailabilityImpact, modifiedSubConfidentialityImpact, modifiedSubIntegrityImpact,
1020                                 modifiedSubAvailabilityImpact, safety, automatable, recovery, valueDensity, vulnerabilityResponseEffort,
1021                                 providerUrgency, baseScore, baseSeverity, threatScore, threatSeverity, environmentalScore, environmentalSeverity);
1022 
1023                         final String source = rsV.getString(71);
1024                         CvssV4.Type cvssType = null;
1025                         value = rsV.getString(72);
1026                         if (value != null) {
1027                             cvssType = CvssV4.Type.fromValue(value);
1028                         }
1029                         final CvssV4 cvssv4 = new CvssV4(source, cvssType, data);
1030                         vuln.setCvssV4(cvssv4);
1031                     }
1032                 } else {
1033                     LOGGER.debug(cve + " does not exist in the database");
1034                     return null;
1035                 }
1036             }
1037             try (PreparedStatement psCWE = getPreparedStatement(conn, SELECT_VULNERABILITY_CWE, cveId); ResultSet rsC = psCWE.executeQuery()) {
1038                 while (rsC.next()) {
1039                     vuln.addCwe(rsC.getString(1));
1040                 }
1041             }
1042             try (PreparedStatement psR = getPreparedStatement(conn, SELECT_REFERENCES, cveId); ResultSet rsR = psR.executeQuery()) {
1043                 while (rsR.next()) {
1044                     vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3));
1045                 }
1046             }
1047             try (PreparedStatement psS = getPreparedStatement(conn, SELECT_SOFTWARE, cveId); ResultSet rsS = psS.executeQuery()) {
1048                 //1 part, 2 vendor, 3 product, 4 version, 5 update_version, 6 edition, 7 lang,
1049                 //8 sw_edition, 9 target_sw, 10 target_hw, 11 other, 12 versionEndExcluding,
1050                 //13 versionEndIncluding, 14 versionStartExcluding, 15 versionStartIncluding, 16 vulnerable
1051                 while (rsS.next()) {
1052                     vulnerableSoftwareBuilder.part(rsS.getString(1))
1053                             .vendor(rsS.getString(2))
1054                             .product(rsS.getString(3))
1055                             .version(rsS.getString(4))
1056                             .update(rsS.getString(5))
1057                             .edition(rsS.getString(6))
1058                             .language(rsS.getString(7))
1059                             .swEdition(rsS.getString(8))
1060                             .targetSw(rsS.getString(9))
1061                             .targetHw(rsS.getString(10))
1062                             .other(rsS.getString(11))
1063                             .versionEndExcluding(rsS.getString(12))
1064                             .versionEndIncluding(rsS.getString(13))
1065                             .versionStartExcluding(rsS.getString(14))
1066                             .versionStartIncluding(rsS.getString(15))
1067                             .vulnerable(rsS.getBoolean(16));
1068                     vuln.addVulnerableSoftware(vulnerableSoftwareBuilder.build());
1069                 }
1070             }
1071         } catch (SQLException ex) {
1072             throw new DatabaseException("Error retrieving " + cve, ex);
1073         } catch (CpeParsingException | CpeValidationException ex) {
1074             throw new DatabaseException("The database contains an invalid Vulnerable Software Entry", ex);
1075         }
1076         return vuln;
1077     }
1078 
1079     /**
1080      * Updates the vulnerability within the database. If the vulnerability does
1081      * not exist it will be added.
1082      *
1083      * @param cve the vulnerability from the NVD CVE Data Feed to add to the
1084      * database
1085      * @param baseEcosystem the ecosystem the CVE belongs to; this is based off
1086      * of things like the CVE description
1087      * @throws DatabaseException is thrown if the database
1088      */
1089     public void updateVulnerability(DefCveItem cve, String baseEcosystem) {
1090         clearCache();
1091         final String cveId = cve.getCve().getId();
1092         try {
1093             if (cve.getCve().getVulnStatus().toUpperCase().startsWith("REJECT")) {
1094                 deleteVulnerability(cveId);
1095             } else {
1096                 if (cveItemConverter.testCveCpeStartWithFilter(cve)) {
1097                     final String description = cveItemConverter.extractDescription(cve);
1098                     final int vulnerabilityId = updateOrInsertVulnerability(cve, description);
1099                     updateVulnerabilityInsertCwe(vulnerabilityId, cve);
1100                     updateVulnerabilityInsertReferences(vulnerabilityId, cve);
1101 
1102                     final List<VulnerableSoftware> software = parseCpes(cve);
1103                     updateVulnerabilityInsertSoftware(vulnerabilityId, cveId, software, baseEcosystem);
1104                 }
1105             }
1106         } catch (SQLException ex) {
1107             final String msg = String.format("Error updating '%s'; %s", cveId, ex.getMessage());
1108             LOGGER.debug(msg, ex);
1109             throw new DatabaseException(msg);
1110         } catch (CpeValidationException ex) {
1111             final String msg = String.format("Error parsing CPE entry from '%s'; %s", cveId, ex.getMessage());
1112             LOGGER.debug(msg, ex);
1113             throw new DatabaseException(msg);
1114         }
1115     }
1116 
1117     private void loadCpeEcosystemCache() {
1118         final Map<Pair<String, String>, String> map = new HashMap<>();
1119         try (Connection conn = databaseManager.getConnection();
1120                 PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ECOSYSTEM);
1121                 ResultSet rs = ps.executeQuery()) {
1122             while (rs.next()) {
1123                 final Pair<String, String> key = new Pair<>(rs.getString(1), rs.getString(2));
1124                 final String value = rs.getString(3);
1125                 map.put(key, value);
1126             }
1127         } catch (SQLException ex) {
1128             final String msg = String.format("Error loading the Cpe Ecosystem Cache: %s", ex.getMessage());
1129             LOGGER.debug(msg, ex);
1130             throw new DatabaseException(msg, ex);
1131         }
1132         CpeEcosystemCache.setCache(map);
1133     }
1134 
1135     private void saveCpeEcosystemCache() {
1136         final Map<Pair<String, String>, String> map = CpeEcosystemCache.getChanged();
1137         if (map != null && !map.isEmpty()) {
1138             try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, MERGE_CPE_ECOSYSTEM)) {
1139                 for (Map.Entry<Pair<String, String>, String> entry : map.entrySet()) {
1140                     ps.setString(1, entry.getKey().getLeft());
1141                     ps.setString(2, entry.getKey().getRight());
1142                     ps.setString(3, entry.getValue());
1143                     if (isBatchInsertEnabled()) {
1144                         ps.addBatch();
1145                     } else {
1146                         ps.execute();
1147                     }
1148                 }
1149                 if (isBatchInsertEnabled()) {
1150                     ps.executeBatch();
1151                 }
1152             } catch (SQLException ex) {
1153                 final String msg = String.format("Error saving the Cpe Ecosystem Cache: %s", ex.getMessage());
1154                 LOGGER.debug(msg, ex);
1155                 throw new DatabaseException(msg, ex);
1156             }
1157         }
1158     }
1159 
1160     /**
1161      * Used when updating a vulnerability - this method inserts the
1162      * vulnerability entry itself.
1163      *
1164      * @param cve the CVE data
1165      * @param description the description of the CVE entry
1166      * @return the vulnerability ID
1167      */
1168     private int updateOrInsertVulnerability(DefCveItem cve, String description) {
1169         if (CpeEcosystemCache.isEmpty()) {
1170             loadCpeEcosystemCache();
1171         }
1172         final int vulnerabilityId;
1173         try (Connection conn = databaseManager.getConnection(); PreparedStatement callUpdate = getPreparedStatement(conn, UPDATE_VULNERABILITY)) {
1174 //            String 1.cve, String 2.description, String 3.v2Severity, Float 4.v2ExploitabilityScore,
1175 //            Float 5.v2ImpactScore, Boolean 6.v2AcInsufInfo, Boolean 7.v2ObtainAllPrivilege,
1176 //            Boolean 8.v2ObtainUserPrivilege, Boolean 9.v2ObtainOtherPrivilege, Boolean 10.v2UserInteractionRequired,
1177 //            Float 11.v2Score, String 12.v2AccessVector, String 13.v2AccessComplexity,
1178 //            String 14.v2Authentication, String 15.v2ConfidentialityImpact, String 16.v2IntegrityImpact,
1179 //            String 17.v2AvailabilityImpact, String 18.v2Version, Float 19.v3ExploitabilityScore,
1180 //            Float 20.v3ImpactScore, String 21.v3AttackVector, String 22.v3AttackComplexity,
1181 //            String 23.v3PrivilegesRequired, String 24.v3UserInteraction, String 25.v3Scope,
1182 //            String 26.v3ConfidentialityImpact, String 27.v3IntegrityImpact, String 28.v3AvailabilityImpact,
1183 //            Float 29.v3BaseScore, String 30.v3BaseSeverity, String 31.v3Version
1184 // .          String 32.v4version, String 33.v4attackVector, String 34.v4attackComplexity, String 35.v4attackRequirements,
1185 //            String 36.v4privilegesRequired, String 37.v4userInteraction, String 38.v4vulnConfidentialityImpact,
1186 //            String 39.v4vulnIntegrityImpact, String 40.v4vulnAvailabilityImpact, String 41.v4subConfidentialityImpact,
1187 //            String 42.v4subIntegrityImpact, String 43.v4subAvailabilityImpact, String 44.v4exploitMaturity,
1188 //            String 45.v4confidentialityRequirement, String 46.v4integrityRequirement, String 47.v4availabilityRequirement,
1189 //            String 48.v4modifiedAttackVector, String 49.v4modifiedAttackComplexity, String 50.v4modifiedAttackRequirements,
1190 //            String 51.v4modifiedPrivilegesRequired, String 52.v4modifiedUserInteraction, String 53.v4modifiedVulnConfidentialityImpact,
1191 //            String 54.v4modifiedVulnIntegrityImpact, String 55.v4modifiedVulnAvailabilityImpact, String 56.v4modifiedSubConfidentialityImpact,
1192 //            String 57.v4modifiedSubIntegrityImpact, String 58.v4modifiedSubAvailabilityImpact, String 59.v4safety,
1193 //            String 60.v4automatable, String 61.v4recovery, String 62.v4valueDensity, String 63.v4vulnerabilityResponseEffort,
1194 //            String 64.v4providerUrgency, Float 65.v4baseScore, String 66.v4baseSeverity, Float 67.v4threatScore,
1195 //            String 68.v4threatSeverity, Float 69.v4environmentalScore, String 70.v4environmentalSeverity
1196 // .          String 71.v4Source, String 72.v4type
1197             callUpdate.setString(1, cve.getCve().getId());
1198             callUpdate.setString(2, description);
1199             Optional<CvssV2> optCvssv2 = null;
1200             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV2() != null) {
1201                 optCvssv2 = cve.getCve().getMetrics().getCvssMetricV2().stream().sorted(Comparator.comparing(CvssV2::getType)).findFirst();
1202             }
1203             if (optCvssv2 != null && optCvssv2.isPresent()) {
1204                 final CvssV2 cvssv2 = optCvssv2.get();
1205                 setUpdateColumn(callUpdate, 3, cvssv2.getBaseSeverity());
1206                 setUpdateColumn(callUpdate, 4, cvssv2.getExploitabilityScore());
1207                 setUpdateColumn(callUpdate, 5, cvssv2.getImpactScore());
1208                 setUpdateColumn(callUpdate, 6, cvssv2.getAcInsufInfo());
1209                 setUpdateColumn(callUpdate, 7, cvssv2.getObtainAllPrivilege());
1210                 setUpdateColumn(callUpdate, 8, cvssv2.getObtainUserPrivilege());
1211                 setUpdateColumn(callUpdate, 9, cvssv2.getObtainOtherPrivilege());
1212                 setUpdateColumn(callUpdate, 10, cvssv2.getUserInteractionRequired());
1213                 setUpdateColumn(callUpdate, 11, cvssv2.getCvssData().getBaseScore());
1214                 setUpdateColumn(callUpdate, 12, cvssv2.getCvssData().getAccessVector());
1215                 setUpdateColumn(callUpdate, 13, cvssv2.getCvssData().getAccessComplexity());
1216                 setUpdateColumn(callUpdate, 14, cvssv2.getCvssData().getAuthentication());
1217                 setUpdateColumn(callUpdate, 15, cvssv2.getCvssData().getConfidentialityImpact());
1218                 setUpdateColumn(callUpdate, 16, cvssv2.getCvssData().getIntegrityImpact());
1219                 setUpdateColumn(callUpdate, 17, cvssv2.getCvssData().getAvailabilityImpact());
1220                 setUpdateColumn(callUpdate, 18, cvssv2.getCvssData().getVersion());
1221             } else {
1222                 callUpdate.setNull(3, java.sql.Types.VARCHAR);
1223                 callUpdate.setNull(4, java.sql.Types.DOUBLE);
1224                 callUpdate.setNull(5, java.sql.Types.DOUBLE);
1225                 callUpdate.setNull(6, java.sql.Types.VARCHAR);
1226                 //TODO this is may also be an issue for MS SQL, if an issue is created we'll just need
1227                 // to create an isMsSQL flag. See todo below in setUpdateColum
1228                 if (isOracle) {
1229                     callUpdate.setNull(7, java.sql.Types.BIT);
1230                     callUpdate.setNull(8, java.sql.Types.BIT);
1231                     callUpdate.setNull(9, java.sql.Types.BIT);
1232                     callUpdate.setNull(10, java.sql.Types.BIT);
1233                 } else {
1234                     callUpdate.setNull(7, java.sql.Types.BOOLEAN);
1235                     callUpdate.setNull(8, java.sql.Types.BOOLEAN);
1236                     callUpdate.setNull(9, java.sql.Types.BOOLEAN);
1237                     callUpdate.setNull(10, java.sql.Types.BOOLEAN);
1238                 }
1239                 callUpdate.setNull(11, java.sql.Types.DOUBLE);
1240                 callUpdate.setNull(12, java.sql.Types.VARCHAR);
1241                 callUpdate.setNull(13, java.sql.Types.VARCHAR);
1242                 callUpdate.setNull(14, java.sql.Types.VARCHAR);
1243                 callUpdate.setNull(15, java.sql.Types.VARCHAR);
1244                 callUpdate.setNull(16, java.sql.Types.VARCHAR);
1245                 callUpdate.setNull(17, java.sql.Types.VARCHAR);
1246                 callUpdate.setNull(18, java.sql.Types.VARCHAR);
1247             }
1248             Optional<CvssV3> optCvssv30 = Optional.empty();
1249             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV30() != null) {
1250                 optCvssv30 = cve.getCve().getMetrics().getCvssMetricV30().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst();
1251             }
1252             Optional<CvssV3> optCvssv31 = Optional.empty();
1253             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV31() != null) {
1254                 optCvssv31 = cve.getCve().getMetrics().getCvssMetricV31().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst();
1255             }
1256 
1257             CvssV3 cvssv3 = null;
1258             if (optCvssv31.isPresent()) {
1259                 cvssv3 = optCvssv31.get();
1260             } else if (optCvssv30.isPresent()) {
1261                 cvssv3 = optCvssv30.get();
1262             }
1263             if (cvssv3 != null) {
1264                 setUpdateColumn(callUpdate, 19, cvssv3.getExploitabilityScore());
1265                 setUpdateColumn(callUpdate, 20, cvssv3.getImpactScore());
1266                 setUpdateColumn(callUpdate, 21, cvssv3.getCvssData().getAttackVector());
1267                 setUpdateColumn(callUpdate, 22, cvssv3.getCvssData().getAttackComplexity());
1268                 setUpdateColumn(callUpdate, 23, cvssv3.getCvssData().getPrivilegesRequired());
1269                 setUpdateColumn(callUpdate, 24, cvssv3.getCvssData().getUserInteraction());
1270                 setUpdateColumn(callUpdate, 25, cvssv3.getCvssData().getScope());
1271                 setUpdateColumn(callUpdate, 26, cvssv3.getCvssData().getConfidentialityImpact());
1272                 setUpdateColumn(callUpdate, 27, cvssv3.getCvssData().getIntegrityImpact());
1273                 setUpdateColumn(callUpdate, 28, cvssv3.getCvssData().getAvailabilityImpact());
1274                 setUpdateColumn(callUpdate, 29, cvssv3.getCvssData().getBaseScore());
1275                 setUpdateColumn(callUpdate, 30, cvssv3.getCvssData().getBaseSeverity());
1276                 setUpdateColumn(callUpdate, 31, cvssv3.getCvssData().getVersion());
1277             } else {
1278                 callUpdate.setNull(19, java.sql.Types.DOUBLE);
1279                 callUpdate.setNull(20, java.sql.Types.DOUBLE);
1280                 callUpdate.setNull(21, java.sql.Types.VARCHAR);
1281                 callUpdate.setNull(22, java.sql.Types.VARCHAR);
1282                 callUpdate.setNull(23, java.sql.Types.VARCHAR);
1283                 callUpdate.setNull(24, java.sql.Types.VARCHAR);
1284                 callUpdate.setNull(25, java.sql.Types.VARCHAR);
1285                 callUpdate.setNull(26, java.sql.Types.VARCHAR);
1286                 callUpdate.setNull(27, java.sql.Types.VARCHAR);
1287                 callUpdate.setNull(28, java.sql.Types.VARCHAR);
1288                 callUpdate.setNull(29, java.sql.Types.DOUBLE);
1289                 callUpdate.setNull(30, java.sql.Types.VARCHAR);
1290                 callUpdate.setNull(31, java.sql.Types.VARCHAR);
1291             }
1292 
1293             Optional<CvssV4> optCvssv4 = null;
1294             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV40() != null) {
1295                 optCvssv4 = cve.getCve().getMetrics().getCvssMetricV40().stream().sorted(Comparator.comparing(CvssV4::getType)).findFirst();
1296             }
1297             if (optCvssv4 != null && optCvssv4.isPresent()) {
1298                 final CvssV4 cvssv4 = optCvssv4.get();
1299                 setUpdateColumn(callUpdate, 32, cvssv4.getCvssData().getVersion());
1300                 setUpdateColumn(callUpdate, 33, cvssv4.getCvssData().getAttackVector());
1301                 setUpdateColumn(callUpdate, 34, cvssv4.getCvssData().getAttackComplexity());
1302                 setUpdateColumn(callUpdate, 35, cvssv4.getCvssData().getAttackRequirements());
1303                 setUpdateColumn(callUpdate, 36, cvssv4.getCvssData().getPrivilegesRequired());
1304                 setUpdateColumn(callUpdate, 37, cvssv4.getCvssData().getUserInteraction());
1305                 setUpdateColumn(callUpdate, 38, cvssv4.getCvssData().getVulnConfidentialityImpact());
1306                 setUpdateColumn(callUpdate, 39, cvssv4.getCvssData().getVulnIntegrityImpact());
1307                 setUpdateColumn(callUpdate, 40, cvssv4.getCvssData().getVulnAvailabilityImpact());
1308                 setUpdateColumn(callUpdate, 41, cvssv4.getCvssData().getSubConfidentialityImpact());
1309                 setUpdateColumn(callUpdate, 42, cvssv4.getCvssData().getSubIntegrityImpact());
1310                 setUpdateColumn(callUpdate, 43, cvssv4.getCvssData().getSubAvailabilityImpact());
1311                 setUpdateColumn(callUpdate, 44, cvssv4.getCvssData().getExploitMaturity());
1312                 setUpdateColumn(callUpdate, 45, cvssv4.getCvssData().getConfidentialityRequirement());
1313                 setUpdateColumn(callUpdate, 46, cvssv4.getCvssData().getIntegrityRequirement());
1314                 setUpdateColumn(callUpdate, 47, cvssv4.getCvssData().getAvailabilityRequirement());
1315                 setUpdateColumn(callUpdate, 48, cvssv4.getCvssData().getModifiedAttackVector());
1316                 setUpdateColumn(callUpdate, 49, cvssv4.getCvssData().getModifiedAttackComplexity());
1317                 setUpdateColumn(callUpdate, 50, cvssv4.getCvssData().getModifiedAttackRequirements());
1318                 setUpdateColumn(callUpdate, 51, cvssv4.getCvssData().getModifiedPrivilegesRequired());
1319                 setUpdateColumn(callUpdate, 52, cvssv4.getCvssData().getModifiedUserInteraction());
1320                 setUpdateColumn(callUpdate, 53, cvssv4.getCvssData().getModifiedVulnConfidentialityImpact());
1321                 setUpdateColumn(callUpdate, 54, cvssv4.getCvssData().getModifiedVulnIntegrityImpact());
1322                 setUpdateColumn(callUpdate, 55, cvssv4.getCvssData().getModifiedVulnAvailabilityImpact());
1323                 setUpdateColumn(callUpdate, 56, cvssv4.getCvssData().getModifiedSubConfidentialityImpact());
1324                 setUpdateColumn(callUpdate, 57, cvssv4.getCvssData().getModifiedSubIntegrityImpact());
1325                 setUpdateColumn(callUpdate, 58, cvssv4.getCvssData().getModifiedSubAvailabilityImpact());
1326                 setUpdateColumn(callUpdate, 59, cvssv4.getCvssData().getSafety());
1327                 setUpdateColumn(callUpdate, 60, cvssv4.getCvssData().getAutomatable());
1328                 setUpdateColumn(callUpdate, 61, cvssv4.getCvssData().getRecovery());
1329                 setUpdateColumn(callUpdate, 62, cvssv4.getCvssData().getValueDensity());
1330                 setUpdateColumn(callUpdate, 63, cvssv4.getCvssData().getVulnerabilityResponseEffort());
1331                 setUpdateColumn(callUpdate, 64, cvssv4.getCvssData().getProviderUrgency());
1332                 setUpdateColumn(callUpdate, 65, cvssv4.getCvssData().getBaseScore());
1333                 setUpdateColumn(callUpdate, 66, cvssv4.getCvssData().getBaseSeverity());
1334                 setUpdateColumn(callUpdate, 67, cvssv4.getCvssData().getThreatScore());
1335                 setUpdateColumn(callUpdate, 68, cvssv4.getCvssData().getThreatSeverity());
1336                 setUpdateColumn(callUpdate, 69, cvssv4.getCvssData().getEnvironmentalScore());
1337                 setUpdateColumn(callUpdate, 70, cvssv4.getCvssData().getEnvironmentalSeverity());
1338                 setUpdateColumn(callUpdate, 71, cvssv4.getSource());
1339                 setUpdateColumn(callUpdate, 72, cvssv4.getType());
1340             } else {
1341                 callUpdate.setNull(32, java.sql.Types.VARCHAR);
1342                 callUpdate.setNull(33, java.sql.Types.VARCHAR);
1343                 callUpdate.setNull(34, java.sql.Types.VARCHAR);
1344                 callUpdate.setNull(35, java.sql.Types.VARCHAR);
1345                 callUpdate.setNull(36, java.sql.Types.VARCHAR);
1346                 callUpdate.setNull(37, java.sql.Types.VARCHAR);
1347                 callUpdate.setNull(38, java.sql.Types.VARCHAR);
1348                 callUpdate.setNull(39, java.sql.Types.VARCHAR);
1349                 callUpdate.setNull(40, java.sql.Types.VARCHAR);
1350                 callUpdate.setNull(41, java.sql.Types.VARCHAR);
1351                 callUpdate.setNull(42, java.sql.Types.VARCHAR);
1352                 callUpdate.setNull(43, java.sql.Types.VARCHAR);
1353                 callUpdate.setNull(44, java.sql.Types.VARCHAR);
1354                 callUpdate.setNull(45, java.sql.Types.VARCHAR);
1355                 callUpdate.setNull(46, java.sql.Types.VARCHAR);
1356                 callUpdate.setNull(47, java.sql.Types.VARCHAR);
1357                 callUpdate.setNull(48, java.sql.Types.VARCHAR);
1358                 callUpdate.setNull(49, java.sql.Types.VARCHAR);
1359                 callUpdate.setNull(50, java.sql.Types.VARCHAR);
1360                 callUpdate.setNull(51, java.sql.Types.VARCHAR);
1361                 callUpdate.setNull(52, java.sql.Types.VARCHAR);
1362                 callUpdate.setNull(53, java.sql.Types.VARCHAR);
1363                 callUpdate.setNull(54, java.sql.Types.VARCHAR);
1364                 callUpdate.setNull(55, java.sql.Types.VARCHAR);
1365                 callUpdate.setNull(56, java.sql.Types.VARCHAR);
1366                 callUpdate.setNull(57, java.sql.Types.VARCHAR);
1367                 callUpdate.setNull(58, java.sql.Types.VARCHAR);
1368                 callUpdate.setNull(59, java.sql.Types.VARCHAR);
1369                 callUpdate.setNull(60, java.sql.Types.VARCHAR);
1370                 callUpdate.setNull(61, java.sql.Types.VARCHAR);
1371                 callUpdate.setNull(62, java.sql.Types.VARCHAR);
1372                 callUpdate.setNull(63, java.sql.Types.VARCHAR);
1373                 callUpdate.setNull(64, java.sql.Types.VARCHAR);
1374                 callUpdate.setNull(65, java.sql.Types.DOUBLE);
1375                 callUpdate.setNull(66, java.sql.Types.VARCHAR);
1376                 callUpdate.setNull(67, java.sql.Types.DOUBLE);
1377                 callUpdate.setNull(68, java.sql.Types.VARCHAR);
1378                 callUpdate.setNull(69, java.sql.Types.DOUBLE);
1379                 callUpdate.setNull(70, java.sql.Types.VARCHAR);
1380                 callUpdate.setNull(71, java.sql.Types.VARCHAR);
1381                 callUpdate.setNull(72, java.sql.Types.VARCHAR);
1382             }
1383             if (isOracle) {
1384                 try {
1385                     final CallableStatement cs = (CallableStatement) callUpdate;
1386                     cs.registerOutParameter(73, JDBCType.INTEGER);
1387                     cs.executeUpdate();
1388                     vulnerabilityId = cs.getInt(73);
1389                 } catch (SQLException ex) {
1390                     final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId());
1391                     throw new DatabaseException(msg, ex);
1392                 }
1393             } else {
1394                 try (ResultSet rs = callUpdate.executeQuery()) {
1395                     rs.next();
1396                     vulnerabilityId = rs.getInt(1);
1397                 } catch (SQLException ex) {
1398                     final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId());
1399                     throw new DatabaseException(msg, ex);
1400                 }
1401             }
1402         } catch (SQLException ex) {
1403             throw new UnexpectedAnalysisException(ex);
1404         }
1405         return vulnerabilityId;
1406     }
1407 
1408     /**
1409      * Used when updating a vulnerability - this method inserts the CWE entries.
1410      *
1411      * @param vulnerabilityId the vulnerability ID
1412      * @param cve the CVE entry that contains the CWE entries to insert
1413      * @throws SQLException thrown if there is an error inserting the data
1414      */
1415     private void updateVulnerabilityInsertCwe(int vulnerabilityId, DefCveItem cve) throws SQLException {
1416         if (cve.getCve() != null && cve.getCve().getWeaknesses() != null) {
1417             try (Connection conn = databaseManager.getConnection();
1418                     PreparedStatement insertCWE = getPreparedStatement(conn, INSERT_CWE, vulnerabilityId)) {
1419                 for (Weakness weakness : cve.getCve().getWeaknesses()) {
1420                     for (LangString desc : weakness.getDescription()) {
1421                         if ("en".equals(desc.getLang())) {
1422                             insertCWE.setString(2, desc.getValue());
1423                             if (isBatchInsertEnabled()) {
1424                                 insertCWE.addBatch();
1425                             } else {
1426                                 insertCWE.execute();
1427                             }
1428                         }
1429                     }
1430                 }
1431                 if (isBatchInsertEnabled()) {
1432                     insertCWE.executeBatch();
1433                 }
1434             }
1435         }
1436     }
1437 
1438     /**
1439      * Used when updating a vulnerability - in some cases a CVE needs to be
1440      * removed.
1441      *
1442      * @param cve the vulnerability CVE
1443      * @throws SQLException thrown if there is an error deleting the
1444      * vulnerability
1445      */
1446     private void deleteVulnerability(String cve) throws SQLException {
1447         try (Connection conn = databaseManager.getConnection();
1448                 PreparedStatement deleteVulnerability = getPreparedStatement(conn, DELETE_VULNERABILITY, cve)) {
1449             deleteVulnerability.executeUpdate();
1450         }
1451     }
1452 
1453     /**
1454      * Merges the list of known exploited vulnerabilities into the database.
1455      *
1456      * @param vulnerabilities the list of known exploited vulnerabilities
1457      * @throws DatabaseException thrown if there is an exception... duh..
1458      * @throws SQLException thrown if there is an exception... duh..
1459      */
1460     public void updateKnownExploitedVulnerabilities(
1461             List<org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> vulnerabilities)
1462             throws DatabaseException, SQLException {
1463         try (Connection conn = databaseManager.getConnection();
1464                 PreparedStatement mergeKnownVulnerability = getPreparedStatement(conn, MERGE_KNOWN_EXPLOITED)) {
1465             int ctr = 0;
1466             for (org.owasp.dependencycheck.data.knownexploited.json.Vulnerability v : vulnerabilities) {
1467                 mergeKnownVulnerability.setString(1, v.getCveID());
1468                 addNullableStringParameter(mergeKnownVulnerability, 2, v.getVendorProject());
1469                 addNullableStringParameter(mergeKnownVulnerability, 3, v.getProduct());
1470                 addNullableStringParameter(mergeKnownVulnerability, 4, v.getVulnerabilityName());
1471                 addNullableStringParameter(mergeKnownVulnerability, 5, v.getDateAdded());
1472                 addNullableStringParameter(mergeKnownVulnerability, 6, v.getShortDescription());
1473                 addNullableStringParameter(mergeKnownVulnerability, 7, v.getRequiredAction());
1474                 addNullableStringParameter(mergeKnownVulnerability, 8, v.getDueDate());
1475                 addNullableStringParameter(mergeKnownVulnerability, 9, v.getNotes());
1476                 if (isBatchInsertEnabled()) {
1477                     mergeKnownVulnerability.addBatch();
1478                     ctr++;
1479                     if (ctr >= getBatchSize()) {
1480                         mergeKnownVulnerability.executeBatch();
1481                         ctr = 0;
1482                     }
1483                 } else {
1484                     try {
1485                         mergeKnownVulnerability.execute();
1486                     } catch (SQLException ex) {
1487                         if (ex.getMessage().contains("Duplicate entry")) {
1488                             final String msg = String.format("Duplicate known exploited vulnerability key identified in '%s'", v.getCveID());
1489                             LOGGER.info(msg, ex);
1490                         } else {
1491                             throw ex;
1492                         }
1493                     }
1494                 }
1495             }
1496             if (isBatchInsertEnabled()) {
1497                 mergeKnownVulnerability.executeBatch();
1498             }
1499         }
1500     }
1501 
1502     /**
1503      * Used when updating a vulnerability - this method inserts the list of
1504      * vulnerable software.
1505      *
1506      * @param vulnerabilityId the vulnerability id
1507      * @param cveId the CVE ID - used for reporting
1508      * @param software the list of vulnerable software
1509      * @param baseEcosystem the ecosystem based off of the vulnerability
1510      * description
1511      * @throws DatabaseException thrown if there is an error inserting the data
1512      * @throws SQLException thrown if there is an error inserting the data
1513      */
1514     private void updateVulnerabilityInsertSoftware(int vulnerabilityId, String cveId,
1515             List<VulnerableSoftware> software, String baseEcosystem)
1516             throws DatabaseException, SQLException {
1517         try (Connection conn = databaseManager.getConnection(); PreparedStatement insertSoftware = getPreparedStatement(conn, INSERT_SOFTWARE)) {
1518             for (VulnerableSoftware parsedCpe : software) {
1519                 insertSoftware.setInt(1, vulnerabilityId);
1520                 insertSoftware.setString(2, parsedCpe.getPart().getAbbreviation());
1521                 insertSoftware.setString(3, parsedCpe.getVendor());
1522                 insertSoftware.setString(4, parsedCpe.getProduct());
1523                 insertSoftware.setString(5, parsedCpe.getVersion());
1524                 insertSoftware.setString(6, parsedCpe.getUpdate());
1525                 insertSoftware.setString(7, parsedCpe.getEdition());
1526                 insertSoftware.setString(8, parsedCpe.getLanguage());
1527                 insertSoftware.setString(9, parsedCpe.getSwEdition());
1528                 insertSoftware.setString(10, parsedCpe.getTargetSw());
1529                 insertSoftware.setString(11, parsedCpe.getTargetHw());
1530                 insertSoftware.setString(12, parsedCpe.getOther());
1531                 final String ecosystem = CpeEcosystemCache.getEcosystem(parsedCpe.getVendor(), parsedCpe.getProduct(),
1532                         cveItemConverter.extractEcosystem(baseEcosystem, parsedCpe));
1533 
1534                 addNullableStringParameter(insertSoftware, 13, ecosystem);
1535                 addNullableStringParameter(insertSoftware, 14, parsedCpe.getVersionEndExcluding());
1536                 addNullableStringParameter(insertSoftware, 15, parsedCpe.getVersionEndIncluding());
1537                 addNullableStringParameter(insertSoftware, 16, parsedCpe.getVersionStartExcluding());
1538                 addNullableStringParameter(insertSoftware, 17, parsedCpe.getVersionStartIncluding());
1539                 insertSoftware.setBoolean(18, parsedCpe.isVulnerable());
1540 
1541                 if (isBatchInsertEnabled()) {
1542                     insertSoftware.addBatch();
1543                 } else {
1544                     try {
1545                         insertSoftware.execute();
1546                     } catch (SQLException ex) {
1547                         if (ex.getMessage().contains("Duplicate entry")) {
1548                             final String msg = String.format("Duplicate software key identified in '%s'", cveId);
1549                             LOGGER.info(msg, ex);
1550                         } else {
1551                             throw ex;
1552                         }
1553                     }
1554                 }
1555             }
1556             if (isBatchInsertEnabled()) {
1557                 executeBatch(cveId, insertSoftware);
1558             }
1559         }
1560     }
1561 
1562     /**
1563      * Used when updating a vulnerability - this method inserts the list of
1564      * references.
1565      *
1566      * @param vulnerabilityId the vulnerability id
1567      * @param cve the CVE entry that contains the list of references
1568      * @throws SQLException thrown if there is an error inserting the data
1569      */
1570     private void updateVulnerabilityInsertReferences(int vulnerabilityId, DefCveItem cve) throws SQLException {
1571         try (Connection conn = databaseManager.getConnection(); PreparedStatement insertReference = getPreparedStatement(conn, INSERT_REFERENCE)) {
1572             if (cve.getCve().getReferences() != null) {
1573                 for (Reference r : cve.getCve().getReferences()) {
1574                     insertReference.setInt(1, vulnerabilityId);
1575                     String name = null;
1576                     if (r.getTags() != null) {
1577                         name = r.getTags().stream().sorted().collect(Collectors.joining(",")).toUpperCase().replaceAll("\\s", "_");
1578                     }
1579                     if (name != null) {
1580                         insertReference.setString(2, name);
1581                     } else {
1582                         insertReference.setNull(2, java.sql.Types.VARCHAR);
1583                     }
1584                     if (r.getUrl() != null && !r.getUrl().isEmpty()) {
1585                         insertReference.setString(3, r.getUrl());
1586                     } else {
1587                         insertReference.setNull(3, java.sql.Types.VARCHAR);
1588                     }
1589                     if (r.getSource() != null && !r.getSource().isEmpty()) {
1590                         insertReference.setString(4, r.getSource());
1591                     } else {
1592                         insertReference.setNull(4, java.sql.Types.VARCHAR);
1593                     }
1594                     if (isBatchInsertEnabled()) {
1595                         insertReference.addBatch();
1596                     } else {
1597                         insertReference.execute();
1598                     }
1599                 }
1600             }
1601             if (isBatchInsertEnabled()) {
1602                 insertReference.executeBatch();
1603             }
1604         }
1605     }
1606 
1607     /**
1608      * Parses the configuration entries from the CVE entry into a list of
1609      * VulnerableSoftware objects.
1610      *
1611      * @param cve the CVE to parse the vulnerable software entries from
1612      * @return the list of vulnerable software
1613      * @throws CpeValidationException if an invalid CPE is present
1614      */
1615     private List<VulnerableSoftware> parseCpes(DefCveItem cve) throws CpeValidationException {
1616         final List<VulnerableSoftware> software = new ArrayList<>();
1617 
1618         final List<CpeMatch> cpeEntries = cve.getCve().getConfigurations().stream()
1619                 .map(Config::getNodes)
1620                 .flatMap(List::stream)
1621                 .map(Node::getCpeMatch)
1622                 .flatMap(List::stream)
1623                 .filter(predicate -> predicate.getCriteria() != null)
1624                 .filter(predicate -> predicate.getCriteria().startsWith(cpeStartsWithFilter))
1625                 //this single CPE entry causes nearly 100% FP - so filtering it at the source.
1626                 .filter(entry -> !("CVE-2009-0754".equals(cve.getCve().getId())
1627                 && "cpe:2.3:a:apache:apache:*:*:*:*:*:*:*:*".equals(entry.getCriteria())))
1628                 .collect(Collectors.toList());
1629         final VulnerableSoftwareBuilder builder = new VulnerableSoftwareBuilder();
1630 
1631         try {
1632             cpeEntries.forEach(entry -> {
1633                 builder.cpe(parseCpe(entry, cve.getCve().getId()))
1634                         .versionEndExcluding(entry.getVersionEndExcluding())
1635                         .versionStartExcluding(entry.getVersionStartExcluding())
1636                         .versionEndIncluding(entry.getVersionEndIncluding())
1637                         .versionStartIncluding(entry.getVersionStartIncluding())
1638                         .vulnerable(entry.getVulnerable());
1639                 try {
1640                     software.add(builder.build());
1641                 } catch (CpeValidationException ex) {
1642                     throw new LambdaExceptionWrapper(ex);
1643                 }
1644             });
1645         } catch (LambdaExceptionWrapper ex) {
1646             throw (CpeValidationException) ex.getCause();
1647         }
1648         return software;
1649     }
1650 
1651     /**
1652      * Helper method to convert a CpeMatch (generated code used in parsing the
1653      * NVD JSON) into a CPE object.
1654      *
1655      * @param cpe the CPE Match
1656      * @param cveId the CVE associated with the CPEMatch - used for error
1657      * reporting
1658      * @return the resulting CPE object
1659      * @throws DatabaseException thrown if there is an error converting the
1660      * CpeMatch into a CPE object
1661      */
1662     private Cpe parseCpe(CpeMatch cpe, String cveId) throws DatabaseException {
1663         final Cpe parsedCpe;
1664         try {
1665             //the replace is a hack as the NVD does not properly escape backslashes in their JSON
1666             parsedCpe = CpeParser.parse(cpe.getCriteria(), true);
1667         } catch (CpeParsingException ex) {
1668             LOGGER.debug("NVD (" + cveId + ") contain an invalid 2.3 CPE: " + cpe.getCriteria());
1669             throw new DatabaseException("Unable to parse CPE: " + cpe.getCriteria(), ex);
1670         }
1671         return parsedCpe;
1672     }
1673 
1674     /**
1675      * Returns the size of the batch.
1676      *
1677      * @return the size of the batch
1678      */
1679     private int getBatchSize() {
1680         int max;
1681         try {
1682             max = settings.getInt(Settings.KEYS.MAX_BATCH_SIZE);
1683         } catch (InvalidSettingException pE) {
1684             max = 1000;
1685         }
1686         return max;
1687     }
1688 
1689     /**
1690      * Determines whether or not batch insert is enabled.
1691      *
1692      * @return <code>true</code> if batch insert is enabled; otherwise
1693      * <code>false</code>
1694      */
1695     private boolean isBatchInsertEnabled() {
1696         boolean batch;
1697         try {
1698             batch = settings.getBoolean(Settings.KEYS.ENABLE_BATCH_UPDATES);
1699         } catch (InvalidSettingException pE) {
1700             //If there's no configuration, default is to not perform batch inserts
1701             batch = false;
1702         }
1703         return batch;
1704     }
1705 
1706     /**
1707      * Executes batch inserts of vulnerabilities when property
1708      * database.batchinsert.maxsize is reached.
1709      *
1710      * @param vulnId the vulnerability ID
1711      * @param statement the prepared statement to batch execute
1712      * @throws SQLException thrown when the batch cannot be executed
1713      */
1714     private void executeBatch(String vulnId, PreparedStatement statement)
1715             throws SQLException {
1716         try {
1717             statement.executeBatch();
1718         } catch (SQLException ex) {
1719             if (ex.getMessage().contains("Duplicate entry")) {
1720                 final String msg = String.format("Duplicate software key identified in '%s'",
1721                         vulnId);
1722                 LOGGER.info(msg, ex);
1723             } else {
1724                 throw ex;
1725             }
1726         }
1727     }
1728 
1729     /**
1730      * Checks to see if data exists so that analysis can be performed.
1731      *
1732      * @return <code>true</code> if data exists; otherwise <code>false</code>
1733      */
1734     public boolean dataExists() {
1735         try (Connection conn = databaseManager.getConnection();
1736                 PreparedStatement cs = getPreparedStatement(conn, COUNT_CPE);
1737                 ResultSet rs = cs.executeQuery()) {
1738             if (rs.next() && rs.getInt(1) > 0) {
1739                 return true;
1740             }
1741         } catch (Exception ex) {
1742             String dd;
1743             try {
1744                 dd = settings.getDataDirectory().getAbsolutePath();
1745             } catch (IOException ex1) {
1746                 dd = settings.getString(Settings.KEYS.DATA_DIRECTORY);
1747             }
1748             LOGGER.error("Unable to access the local database.\n\nEnsure that '{}' is a writable directory. "
1749                     + "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please "
1750                     + "create a log file (see documentation at https://jeremylong.github.io/DependencyCheck/) and open a ticket at "
1751                     + "https://github.com/jeremylong/DependencyCheck/issues and include the log file.\n\n",
1752                     dd, dd, settings.getString(Settings.KEYS.APPLICATION_NAME));
1753             LOGGER.debug("", ex);
1754         }
1755         return false;
1756     }
1757 
1758     /**
1759      * It is possible that orphaned rows may be generated during database
1760      * updates. This should be called after all updates have been completed to
1761      * ensure orphan entries are removed.
1762      */
1763     public void cleanupDatabase() {
1764         LOGGER.info("Begin database maintenance");
1765         final long start = System.currentTimeMillis();
1766         try (Connection conn = databaseManager.getConnection();
1767                 PreparedStatement psOrphans = getPreparedStatement(conn, CLEANUP_ORPHANS);
1768                 PreparedStatement psEcosystem = getPreparedStatement(conn, UPDATE_ECOSYSTEM);
1769                 PreparedStatement psEcosystem2 = getPreparedStatement(conn, UPDATE_ECOSYSTEM2)) {
1770             if (psEcosystem != null) {
1771                 final int count = psEcosystem.executeUpdate();
1772                 if (count > 0) {
1773                     LOGGER.info("Updated the CPE ecosystem on {} NVD records", count);
1774                 }
1775             }
1776             if (psEcosystem2 != null) {
1777                 final int count = psEcosystem2.executeUpdate();
1778                 if (count > 0) {
1779                     LOGGER.info("Removed the CPE ecosystem on {} NVD records", count);
1780                 }
1781             }
1782             if (psOrphans != null) {
1783                 final int count = psOrphans.executeUpdate();
1784                 if (count > 0) {
1785                     LOGGER.info("Cleaned up {} orphaned NVD records", count);
1786                 }
1787             }
1788             final long millis = System.currentTimeMillis() - start;
1789             //final long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
1790             LOGGER.info("End database maintenance ({} ms)", millis);
1791         } catch (SQLException ex) {
1792             LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
1793             LOGGER.debug("", ex);
1794             throw new DatabaseException("Unexpected SQL Exception", ex);
1795         }
1796     }
1797 
1798     /**
1799      * Persist the EcosystemCache into the database.
1800      */
1801     public void persistEcosystemCache() {
1802         saveCpeEcosystemCache();
1803         clearCache();
1804     }
1805 
1806     /**
1807      * If the database is using an H2 file based database calling
1808      * <code>defrag()</code> will de-fragment the database.
1809      */
1810     public void defrag() {
1811         if (isH2) {
1812             final long start = System.currentTimeMillis();
1813             try (Connection conn = databaseManager.getConnection(); CallableStatement psCompaxt = conn.prepareCall("SHUTDOWN DEFRAG")) {
1814                 LOGGER.info("Begin database defrag");
1815                 psCompaxt.execute();
1816                 final long millis = System.currentTimeMillis() - start;
1817                 //final long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
1818                 LOGGER.info("End database defrag ({} ms)", millis);
1819             } catch (SQLException ex) {
1820                 LOGGER.error("An unexpected SQL Exception occurred compacting the database; please see the verbose log for more details.");
1821                 LOGGER.debug("", ex);
1822             }
1823         }
1824     }
1825 
1826     /**
1827      * Determines if the given identifiedVersion is affected by the given cpeId
1828      * and previous version flag. A non-null, non-empty string passed to the
1829      * previous version argument indicates that all previous versions are
1830      * affected.
1831      *
1832      * @param cpe the CPE for the given dependency
1833      * @param vulnerableSoftware a set of the vulnerable software
1834      * @return true if the identified version is affected, otherwise false
1835      */
1836     VulnerableSoftware getMatchingSoftware(Cpe cpe, Set<VulnerableSoftware> vulnerableSoftware) {
1837         VulnerableSoftware matched = null;
1838         for (VulnerableSoftware vs : vulnerableSoftware) {
1839             if (vs.matches(cpe)) {
1840                 if (matched == null) {
1841                     matched = vs;
1842                 } else {
1843                     if ("*".equals(vs.getWellFormedUpdate()) && !"*".equals(matched.getWellFormedUpdate())) {
1844                         matched = vs;
1845                     }
1846                 }
1847             }
1848         }
1849         return matched;
1850     }
1851 
1852     /**
1853      * This method is only referenced in unused code.
1854      * <p>
1855      * Deletes unused dictionary entries from the database.
1856      * </p>
1857      */
1858     public void deleteUnusedCpe() {
1859         clearCache();
1860         try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, DELETE_UNUSED_DICT_CPE)) {
1861             ps.executeUpdate();
1862         } catch (SQLException ex) {
1863             LOGGER.error("Unable to delete CPE dictionary entries", ex);
1864         }
1865     }
1866 
1867     /**
1868      * This method is only referenced in unused code and will likely break on
1869      * MySQL if ever used due to the MERGE statement.
1870      * <p>
1871      * Merges CPE entries into the database.
1872      * </p>
1873      *
1874      * @param cpe the CPE identifier
1875      * @param vendor the CPE vendor
1876      * @param product the CPE product
1877      */
1878     public void addCpe(String cpe, String vendor, String product) {
1879         clearCache();
1880         try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, ADD_DICT_CPE)) {
1881             ps.setString(1, cpe);
1882             ps.setString(2, vendor);
1883             ps.setString(3, product);
1884             ps.executeUpdate();
1885         } catch (SQLException ex) {
1886             LOGGER.error("Unable to add CPE dictionary entry", ex);
1887         }
1888     }
1889 
1890     /**
1891      * Returns a map of known exploited vulnerabilities.
1892      *
1893      * @return a map of known exploited vulnerabilities
1894      */
1895     public Map<String, org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> getknownExploitedVulnerabilities() {
1896         final Map<String, org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> known = new HashMap<>();
1897 
1898         try (Connection conn = databaseManager.getConnection();
1899                 PreparedStatement ps = getPreparedStatement(conn, SELECT_KNOWN_EXPLOITED_VULNERABILITIES);
1900                 ResultSet rs = ps.executeQuery()) {
1901 
1902             while (rs.next()) {
1903                 final org.owasp.dependencycheck.data.knownexploited.json.Vulnerability kev =
1904                         new org.owasp.dependencycheck.data.knownexploited.json.Vulnerability();
1905                 kev.setCveID(rs.getString(1));
1906                 kev.setVendorProject(rs.getString(2));
1907                 kev.setProduct(rs.getString(3));
1908                 kev.setVulnerabilityName(rs.getString(4));
1909                 kev.setDateAdded(rs.getString(5));
1910                 kev.setShortDescription(rs.getString(6));
1911                 kev.setRequiredAction(rs.getString(7));
1912                 kev.setDueDate(rs.getString(8));
1913                 kev.setNotes(rs.getString(9));
1914                 known.put(kev.getCveID(), kev);
1915             }
1916 
1917         } catch (SQLException ex) {
1918             throw new DatabaseException(ex);
1919         }
1920         return known;
1921     }
1922 
1923     /**
1924      * Helper method to add a nullable string parameter.
1925      *
1926      * @param ps the prepared statement
1927      * @param pos the position of the parameter
1928      * @param value the value of the parameter
1929      * @throws SQLException thrown if there is an error setting the parameter.
1930      */
1931     private void addNullableStringParameter(PreparedStatement ps, int pos, String value) throws SQLException {
1932         if (value == null || value.isEmpty()) {
1933             ps.setNull(pos, java.sql.Types.VARCHAR);
1934         } else {
1935             ps.setString(pos, value);
1936         }
1937     }
1938 
1939     private void setUpdateColumn(PreparedStatement ps, int i, Double value) throws SQLException {
1940         if (value == null) {
1941             ps.setNull(i, java.sql.Types.DOUBLE);
1942         } else {
1943             ps.setDouble(i, value);
1944         }
1945     }
1946 
1947     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AuthenticationType value) throws SQLException {
1948         if (value == null) {
1949             ps.setNull(i, java.sql.Types.VARCHAR);
1950         } else {
1951             ps.setString(i, value.value());
1952         }
1953     }
1954 
1955     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.CiaType value) throws SQLException {
1956         if (value == null) {
1957             ps.setNull(i, java.sql.Types.VARCHAR);
1958         } else {
1959             ps.setString(i, value.value());
1960         }
1961     }
1962 
1963     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.Version value) throws SQLException {
1964         if (value == null) {
1965             ps.setNull(i, java.sql.Types.VARCHAR);
1966         } else {
1967             ps.setString(i, value.value());
1968         }
1969     }
1970 
1971     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessComplexityType value) throws SQLException {
1972         if (value == null) {
1973             ps.setNull(i, java.sql.Types.VARCHAR);
1974         } else {
1975             ps.setString(i, value.value());
1976         }
1977     }
1978 
1979     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessVectorType value) throws SQLException {
1980         if (value == null) {
1981             ps.setNull(i, java.sql.Types.VARCHAR);
1982         } else {
1983             ps.setString(i, value.value());
1984         }
1985     }
1986 
1987     private void setUpdateColumn(PreparedStatement ps, int i, String value) throws SQLException {
1988         if (value == null) {
1989             ps.setNull(i, java.sql.Types.VARCHAR);
1990         } else {
1991             ps.setString(i, value);
1992         }
1993     }
1994 
1995     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4.Type value) throws SQLException {
1996         if (value == null) {
1997             ps.setNull(i, java.sql.Types.VARCHAR);
1998         } else {
1999             ps.setString(i, value.value());
2000         }
2001     }
2002 
2003     private void setUpdateColumn(PreparedStatement ps, int i, Boolean value) throws SQLException {
2004         if (value == null) {
2005             //TODO this is may also be an issue for MS SQL, if an issue is created we'll just need
2006             // to create an isMsSQL flag. See todo above in updateOrInsertVulnerability.
2007             if (isOracle) {
2008                 ps.setNull(i, java.sql.Types.BIT);
2009             } else {
2010                 ps.setNull(i, java.sql.Types.BOOLEAN);
2011             }
2012         } else {
2013             ps.setBoolean(i, value);
2014         }
2015     }
2016 
2017     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackVectorType value) throws SQLException {
2018         if (value == null) {
2019             ps.setNull(i, java.sql.Types.VARCHAR);
2020         } else {
2021             ps.setString(i, value.value());
2022         }
2023     }
2024 
2025     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackComplexityType value) throws SQLException {
2026         if (value == null) {
2027             ps.setNull(i, java.sql.Types.VARCHAR);
2028         } else {
2029             ps.setString(i, value.value());
2030         }
2031     }
2032 
2033     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.PrivilegesRequiredType value) throws SQLException {
2034         if (value == null) {
2035             ps.setNull(i, java.sql.Types.VARCHAR);
2036         } else {
2037             ps.setString(i, value.value());
2038         }
2039     }
2040 
2041     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.UserInteractionType value) throws SQLException {
2042         if (value == null) {
2043             ps.setNull(i, java.sql.Types.VARCHAR);
2044         } else {
2045             ps.setString(i, value.value());
2046         }
2047     }
2048 
2049     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.ScopeType value) throws SQLException {
2050         if (value == null) {
2051             ps.setNull(i, java.sql.Types.VARCHAR);
2052         } else {
2053             ps.setString(i, value.value());
2054         }
2055     }
2056 
2057     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.SeverityType value) throws SQLException {
2058         if (value == null) {
2059             ps.setNull(i, java.sql.Types.VARCHAR);
2060         } else {
2061             ps.setString(i, value.value());
2062         }
2063     }
2064 
2065     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.CiaType value) throws SQLException {
2066         if (value == null) {
2067             ps.setNull(i, java.sql.Types.VARCHAR);
2068         } else {
2069             ps.setString(i, value.value());
2070         }
2071     }
2072 
2073     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.Version value) throws SQLException {
2074         if (value == null) {
2075             ps.setNull(i, java.sql.Types.VARCHAR);
2076         } else {
2077             ps.setString(i, value.value());
2078         }
2079     }
2080 
2081     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.Version value) throws SQLException  {
2082         if (value == null) {
2083             ps.setNull(i, java.sql.Types.VARCHAR);
2084         } else {
2085             ps.setString(i, value.value());
2086         }
2087     }
2088 
2089     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AttackVectorType value) throws SQLException {
2090         if (value == null) {
2091             ps.setNull(i, java.sql.Types.VARCHAR);
2092         } else {
2093             ps.setString(i, value.value());
2094         }
2095     }
2096 
2097     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AttackComplexityType value) throws SQLException  {
2098         if (value == null) {
2099             ps.setNull(i, java.sql.Types.VARCHAR);
2100         } else {
2101             ps.setString(i, value.value());
2102         }
2103     }
2104 
2105     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AttackRequirementsType value) throws SQLException  {
2106         if (value == null) {
2107             ps.setNull(i, java.sql.Types.VARCHAR);
2108         } else {
2109             ps.setString(i, value.value());
2110         }
2111     }
2112 
2113     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.PrivilegesRequiredType value) throws SQLException  {
2114         if (value == null) {
2115             ps.setNull(i, java.sql.Types.VARCHAR);
2116         } else {
2117             ps.setString(i, value.value());
2118         }
2119     }
2120 
2121     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.UserInteractionType value)  throws SQLException {
2122         if (value == null) {
2123             ps.setNull(i, java.sql.Types.VARCHAR);
2124         } else {
2125             ps.setString(i, value.value());
2126         }
2127     }
2128 
2129     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.CiaType value)  throws SQLException {
2130         if (value == null) {
2131             ps.setNull(i, java.sql.Types.VARCHAR);
2132         } else {
2133             ps.setString(i, value.value());
2134         }
2135     }
2136 
2137     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ExploitMaturityType value)  throws SQLException {
2138         if (value == null) {
2139             ps.setNull(i, java.sql.Types.VARCHAR);
2140         } else {
2141             ps.setString(i, value.value());
2142         }
2143     }
2144 
2145     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.CiaRequirementType value) throws SQLException  {
2146         if (value == null) {
2147             ps.setNull(i, java.sql.Types.VARCHAR);
2148         } else {
2149             ps.setString(i, value.value());
2150         }
2151     }
2152 
2153     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedAttackVectorType value) throws SQLException  {
2154         if (value == null) {
2155             ps.setNull(i, java.sql.Types.VARCHAR);
2156         } else {
2157             ps.setString(i, value.value());
2158         }
2159     }
2160 
2161     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedAttackComplexityType value) throws SQLException  {
2162         if (value == null) {
2163             ps.setNull(i, java.sql.Types.VARCHAR);
2164         } else {
2165             ps.setString(i, value.value());
2166         }
2167     }
2168 
2169     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedAttackRequirementsType value)  throws SQLException {
2170         if (value == null) {
2171             ps.setNull(i, java.sql.Types.VARCHAR);
2172         } else {
2173             ps.setString(i, value.value());
2174         }
2175     }
2176 
2177     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedPrivilegesRequiredType value)  throws SQLException {
2178         if (value == null) {
2179             ps.setNull(i, java.sql.Types.VARCHAR);
2180         } else {
2181             ps.setString(i, value.value());
2182         }
2183     }
2184 
2185     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedUserInteractionType value)  throws SQLException {
2186         if (value == null) {
2187             ps.setNull(i, java.sql.Types.VARCHAR);
2188         } else {
2189             ps.setString(i, value.value());
2190         }
2191     }
2192 
2193     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedCiaType value) throws SQLException  {
2194         if (value == null) {
2195             ps.setNull(i, java.sql.Types.VARCHAR);
2196         } else {
2197             ps.setString(i, value.value());
2198         }
2199     }
2200 
2201     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.SafetyType value) throws SQLException  {
2202         if (value == null) {
2203             ps.setNull(i, java.sql.Types.VARCHAR);
2204         } else {
2205             ps.setString(i, value.value());
2206         }
2207     }
2208 
2209     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AutomatableType value) throws SQLException  {
2210         if (value == null) {
2211             ps.setNull(i, java.sql.Types.VARCHAR);
2212         } else {
2213             ps.setString(i, value.value());
2214         }
2215     }
2216 
2217     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.RecoveryType value)  throws SQLException {
2218         if (value == null) {
2219             ps.setNull(i, java.sql.Types.VARCHAR);
2220         } else {
2221             ps.setString(i, value.value());
2222         }
2223     }
2224 
2225     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ValueDensityType value) throws SQLException  {
2226         if (value == null) {
2227             ps.setNull(i, java.sql.Types.VARCHAR);
2228         } else {
2229             ps.setString(i, value.value());
2230         }
2231     }
2232 
2233     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.VulnerabilityResponseEffortType value) throws SQLException  {
2234         if (value == null) {
2235             ps.setNull(i, java.sql.Types.VARCHAR);
2236         } else {
2237             ps.setString(i, value.value());
2238         }
2239     }
2240 
2241     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ProviderUrgencyType value) throws SQLException  {
2242         if (value == null) {
2243             ps.setNull(i, java.sql.Types.VARCHAR);
2244         } else {
2245             ps.setString(i, value.value());
2246         }
2247     }
2248 
2249     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.SeverityType value) throws SQLException  {
2250         if (value == null) {
2251             ps.setNull(i, java.sql.Types.VARCHAR);
2252         } else {
2253             ps.setString(i, value.value());
2254         }
2255     }
2256 
2257     /**
2258      * Sets the float parameter on a prepared statement from a properties map.
2259      *
2260      * @param ps a prepared statement
2261      * @param i the index of the property
2262      * @param props the property collection
2263      * @param key the property key
2264      * @throws SQLException thrown if there is an error adding the property
2265      */
2266     private void setFloatValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
2267         if (props != null && props.containsKey(key)) {
2268             try {
2269                 ps.setFloat(i, Float.parseFloat(props.get(key).toString()));
2270             } catch (NumberFormatException nfe) {
2271                 ps.setNull(i, java.sql.Types.FLOAT);
2272             }
2273         } else {
2274             ps.setNull(i, java.sql.Types.FLOAT);
2275         }
2276     }
2277 
2278     /**
2279      * Sets the string parameter on a prepared statement from a properties map.
2280      *
2281      * @param ps a prepared statement
2282      * @param i the index of the property
2283      * @param props the property collection
2284      * @param key the property key
2285      * @throws SQLException thrown if there is an error adding the property
2286      */
2287     private void setStringValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
2288         if (props != null && props.containsKey(key)) {
2289             ps.setString(i, props.get(key).toString());
2290         } else {
2291             ps.setNull(i, java.sql.Types.VARCHAR);
2292         }
2293     }
2294 
2295     /**
2296      * Sets the boolean parameter on a prepared statement from a properties map.
2297      *
2298      * @param ps a prepared statement
2299      * @param i the index of the property
2300      * @param props the property collection
2301      * @param key the property key
2302      * @throws SQLException thrown if there is an error adding the property
2303      */
2304     private void setBooleanValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
2305         if (props != null && props.containsKey(key)) {
2306             ps.setBoolean(i, Boolean.parseBoolean(props.get(key).toString()));
2307         } else {
2308             ps.setNull(i, java.sql.Types.BOOLEAN);
2309         }
2310     }
2311 
2312     /**
2313      * Returns the Boolean value for the given index; if the value is null then
2314      * null is returned.
2315      *
2316      * @param rs the record set
2317      * @param index the parameter index
2318      * @return the Boolean value; or null
2319      * @throws SQLException thrown if there is an error obtaining the value
2320      */
2321     @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL")
2322     private Boolean getBooleanValue(ResultSet rs, int index) throws SQLException {
2323         if (rs.getObject(index) == null) {
2324             return null;
2325         }
2326         return rs.getBoolean(index);
2327     }
2328 
2329     /**
2330      * Returns the Float value for the given index; if the value is null then
2331      * null is returned.
2332      *
2333      * @param rs the record set
2334      * @param index the parameter index
2335      * @return the Float value; or null
2336      * @throws SQLException thrown if there is an error obtaining the value
2337      */
2338     private Float getFloatValue(ResultSet rs, int index) throws SQLException {
2339         if (rs.getObject(index) == null) {
2340             return null;
2341         }
2342         return rs.getFloat(index);
2343     }
2344 }