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.collections4.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.collections4.map.AbstractReferenceMap.ReferenceStrength.HARD;
48  import static org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceStrength.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                         final CvssV2Data cvssData = new CvssV2Data(CvssV2Data.Version._2_0, vector, accessVector,
745                                 accessComplexity, authentication, confidentialityImpact,
746                                 integrityImpact, availabilityImpact, rsV.getDouble(11), rsV.getString(3),
747                                 null, null, null, null, null, null, null, null, null, null);
748                         final CvssV2 cvss = new CvssV2(null, CvssV2.Type.PRIMARY, cvssData, rsV.getString(3),
749                                 rsV.getDouble(4), rsV.getDouble(5), rsV.getBoolean(6), rsV.getBoolean(7),
750                                 rsV.getBoolean(8), rsV.getBoolean(9), rsV.getBoolean(10));
751                         vuln.setCvssV2(cvss);
752                     }
753                     //19.v3ExploitabilityScore, 20.v3ImpactScore, 21.v3AttackVector, 22.v3AttackComplexity, 23.v3PrivilegesRequired,
754                     //24.v3UserInteraction, 25.v3Scope, 26.v3ConfidentialityImpact, 27.v3IntegrityImpact, 28.v3AvailabilityImpact,
755                     //29.v3BaseScore, 30.v3BaseSeverity, 31.v3Version
756                     if (rsV.getObject(21) != null) {
757                         //some older test data may not correctly have the version set.
758                         String cveVersion = "3.1";
759                         if (rsV.getString(31) != null) {
760                             cveVersion = rsV.getString(31);
761                         }
762                         final CvssV3Data.Version version = CvssV3Data.Version.fromValue(cveVersion);
763                         final CvssV3Data.AttackVectorType attackVector = CvssV3Data.AttackVectorType.fromValue(rsV.getString(21));
764                         final CvssV3Data.AttackComplexityType attackComplexity = CvssV3Data.AttackComplexityType.fromValue(rsV.getString(22));
765                         final CvssV3Data.PrivilegesRequiredType privilegesRequired = CvssV3Data.PrivilegesRequiredType.fromValue(rsV.getString(23));
766                         final CvssV3Data.UserInteractionType userInteraction = CvssV3Data.UserInteractionType.fromValue(rsV.getString(24));
767                         final CvssV3Data.ScopeType scope = CvssV3Data.ScopeType.fromValue(rsV.getString(25));
768                         final CvssV3Data.CiaType confidentialityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(26));
769                         final CvssV3Data.CiaType integrityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(27));
770                         final CvssV3Data.CiaType availabilityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(28));
771                         final CvssV3Data.SeverityType baseSeverity = CvssV3Data.SeverityType.fromValue(rsV.getString(30));
772                         final String vector = String.format("CVSS:%s/AV:%s/AC:%s/PR:%s/UI:%s/S:%s/C:%s/I:%s/A:%s",
773                                 version == null ? "" : version,
774                                 attackVector == null ? "" : attackVector.value().substring(0, 1),
775                                 attackComplexity == null ? "" : attackComplexity.value().substring(0, 1),
776                                 privilegesRequired == null ? "" : privilegesRequired.value().substring(0, 1),
777                                 userInteraction == null ? "" : userInteraction.value().substring(0, 1),
778                                 scope == null ? "" : scope.value().substring(0, 1),
779                                 confidentialityImpact == null ? "" : confidentialityImpact.value().substring(0, 1),
780                                 integrityImpact == null ? "" : integrityImpact.value().substring(0, 1),
781                                 availabilityImpact == null ? "" : availabilityImpact.value().substring(0, 1));
782 
783                         final CvssV3Data cvssData = new CvssV3Data(version, vector, attackVector, attackComplexity, privilegesRequired,
784                                 userInteraction, scope, confidentialityImpact, integrityImpact, availabilityImpact,
785                                 rsV.getDouble(29), baseSeverity, CvssV3Data.ExploitCodeMaturityType.PROOF_OF_CONCEPT,
786                                 CvssV3Data.RemediationLevelType.NOT_DEFINED, CvssV3Data.ConfidenceType.REASONABLE, 0.0,
787                                 CvssV3Data.SeverityType.MEDIUM, CvssV3Data.CiaRequirementType.NOT_DEFINED,
788                                 CvssV3Data.CiaRequirementType.NOT_DEFINED, CvssV3Data.CiaRequirementType.NOT_DEFINED,
789                                 CvssV3Data.ModifiedAttackVectorType.ADJACENT_NETWORK, CvssV3Data.ModifiedAttackComplexityType.NOT_DEFINED,
790                                 CvssV3Data.ModifiedPrivilegesRequiredType.NOT_DEFINED, CvssV3Data.ModifiedUserInteractionType.NOT_DEFINED,
791                                 CvssV3Data.ModifiedScopeType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED,
792                                 CvssV3Data.ModifiedCiaType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED, 1.0,
793                                 CvssV3Data.SeverityType.NONE);
794                         final CvssV3 cvss = new CvssV3(null, null, cvssData, rsV.getDouble(19), rsV.getDouble(20));
795                         vuln.setCvssV3(cvss);
796                     }
797 //                    32.v4version, 33.v4attackVector, 34.v4attackComplexity, 35.v4attackRequirements, 36.v4privilegesRequired,
798 //                    37.v4userInteraction, 38.v4vulnConfidentialityImpact, 39.v4vulnIntegrityImpact, 40.v4vulnAvailabilityImpact,
799 //                    41.v4subConfidentialityImpact, 42.v4subIntegrityImpact, 43.v4subAvailabilityImpact, 44.v4exploitMaturity,
800 //                    45.v4confidentialityRequirement, 46.v4integrityRequirement, 47.v4availabilityRequirement, 48.v4modifiedAttackVector,
801 //                    49.v4modifiedAttackComplexity, 50.v4modifiedAttackRequirements, 51.v4modifiedPrivilegesRequired, 52.v4modifiedUserInteraction,
802 //                    53.v4modifiedVulnConfidentialityImpact, 54.v4modifiedVulnIntegrityImpact, 55.v4modifiedVulnAvailabilityImpact,
803 //                    56.v4modifiedSubConfidentialityImpact, 57.v4modifiedSubIntegrityImpact, 58.v4modifiedSubAvailabilityImpact,
804 //                    59.v4safety, 60.v4automatable, 61.v4recovery, 62.v4valueDensity, 63.v4vulnerabilityResponseEffort, 64.v4providerUrgency,
805 //                    65.v4baseScore, 66.v4baseSeverity, 67.v4threatScore, 68.v4threatSeverity, 69.v4environmentalScore, 70.v4environmentalSeverity
806 //                    71.v4source, 72.v4type
807                     if (rsV.getObject(33) != null) {
808                         String vectorString = null;
809 
810                         String value = rsV.getString(32);
811                         final CvssV4Data.Version version = CvssV4Data.Version.fromValue(value);
812                         CvssV4Data.AttackVectorType attackVector = null;
813                         value = rsV.getString(33);
814                         if (value != null) {
815                             attackVector = CvssV4Data.AttackVectorType.fromValue(value);
816                         }
817                         CvssV4Data.AttackComplexityType attackComplexity = null;
818                         value = rsV.getString(34);
819                         if (value != null) {
820                             attackComplexity = CvssV4Data.AttackComplexityType.fromValue(value);
821                         }
822                         CvssV4Data.AttackRequirementsType attackRequirements = null;
823                         value = rsV.getString(35);
824                         if (value != null) {
825                             attackRequirements = CvssV4Data.AttackRequirementsType.fromValue(value);
826                         }
827                         CvssV4Data.PrivilegesRequiredType privilegesRequired = null;
828                         value = rsV.getString(36);
829                         if (value != null) {
830                             privilegesRequired = CvssV4Data.PrivilegesRequiredType.fromValue(value);
831                         }
832                         CvssV4Data.UserInteractionType userInteraction = null;
833                         value = rsV.getString(37);
834                         if (value != null) {
835                             userInteraction = CvssV4Data.UserInteractionType.fromValue(value);
836                         }
837                         CvssV4Data.CiaType vulnConfidentialityImpact = null;
838                         value = rsV.getString(38);
839                         if (value != null) {
840                             vulnConfidentialityImpact = CvssV4Data.CiaType.fromValue(value);
841                         }
842                         CvssV4Data.CiaType vulnIntegrityImpact = null;
843                         value = rsV.getString(39);
844                         if (value != null) {
845                             vulnIntegrityImpact = CvssV4Data.CiaType.fromValue(value);
846                         }
847                         CvssV4Data.CiaType vulnAvailabilityImpact = null;
848                         value = rsV.getString(40);
849                         if (value != null) {
850                             vulnAvailabilityImpact = CvssV4Data.CiaType.fromValue(value);
851                         }
852                         CvssV4Data.CiaType subConfidentialityImpact = null;
853                         value = rsV.getString(41);
854                         if (value != null) {
855                             subConfidentialityImpact = CvssV4Data.CiaType.fromValue(value);
856                         }
857                         CvssV4Data.CiaType subIntegrityImpact = null;
858                         value = rsV.getString(42);
859                         if (value != null) {
860                             subIntegrityImpact = CvssV4Data.CiaType.fromValue(value);
861                         }
862                         CvssV4Data.CiaType subAvailabilityImpact = null;
863                         value = rsV.getString(43);
864                         if (value != null) {
865                             subAvailabilityImpact = CvssV4Data.CiaType.fromValue(value);
866                         }
867                         CvssV4Data.ExploitMaturityType exploitMaturity = null;
868                         value = rsV.getString(44);
869                         if (value != null) {
870                             exploitMaturity = CvssV4Data.ExploitMaturityType.fromValue(value);
871                         }
872                         CvssV4Data.CiaRequirementType confidentialityRequirement = null;
873                         value = rsV.getString(45);
874                         if (value != null) {
875                             confidentialityRequirement = CvssV4Data.CiaRequirementType.fromValue(value);
876                         }
877                         CvssV4Data.CiaRequirementType integrityRequirement = null;
878                         value = rsV.getString(46);
879                         if (value != null) {
880                             integrityRequirement = CvssV4Data.CiaRequirementType.fromValue(value);
881                         }
882                         CvssV4Data.CiaRequirementType availabilityRequirement = null;
883                         value = rsV.getString(47);
884                         if (value != null) {
885                             availabilityRequirement = CvssV4Data.CiaRequirementType.fromValue(value);
886                         }
887                         CvssV4Data.ModifiedAttackVectorType modifiedAttackVector = null;
888                         value = rsV.getString(48);
889                         if (value != null) {
890                             modifiedAttackVector = CvssV4Data.ModifiedAttackVectorType.fromValue(value);
891                         }
892                         CvssV4Data.ModifiedAttackComplexityType modifiedAttackComplexity = null;
893                         value = rsV.getString(49);
894                         if (value != null) {
895                             modifiedAttackComplexity = CvssV4Data.ModifiedAttackComplexityType.fromValue(value);
896                         }
897                         CvssV4Data.ModifiedAttackRequirementsType modifiedAttackRequirements = null;
898                         value = rsV.getString(50);
899                         if (value != null) {
900                             modifiedAttackRequirements = CvssV4Data.ModifiedAttackRequirementsType.fromValue(value);
901                         }
902                         CvssV4Data.ModifiedPrivilegesRequiredType modifiedPrivilegesRequired = null;
903                         value = rsV.getString(51);
904                         if (value != null) {
905                             modifiedPrivilegesRequired = CvssV4Data.ModifiedPrivilegesRequiredType.fromValue(value);
906                         }
907                         CvssV4Data.ModifiedUserInteractionType modifiedUserInteraction = null;
908                         value = rsV.getString(52);
909                         if (value != null) {
910                             modifiedUserInteraction = CvssV4Data.ModifiedUserInteractionType.fromValue(value);
911                         }
912                         CvssV4Data.ModifiedCiaType modifiedVulnConfidentialityImpact = null;
913                         value = rsV.getString(53);
914                         if (value != null) {
915                             modifiedVulnConfidentialityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
916                         }
917                         CvssV4Data.ModifiedCiaType modifiedVulnIntegrityImpact = null;
918                         value = rsV.getString(54);
919                         if (value != null) {
920                             modifiedVulnIntegrityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
921                         }
922                         CvssV4Data.ModifiedCiaType modifiedVulnAvailabilityImpact = null;
923                         value = rsV.getString(55);
924                         if (value != null) {
925                             modifiedVulnAvailabilityImpact = CvssV4Data.ModifiedCiaType.fromValue(value);
926                         }
927                         CvssV4Data.ModifiedSubCType modifiedSubConfidentialityImpact = null;
928                         value = rsV.getString(56);
929                         if (value != null) {
930                             modifiedSubConfidentialityImpact = CvssV4Data.ModifiedSubCType.fromValue(value);
931                         }
932                         CvssV4Data.ModifiedSubIaType modifiedSubIntegrityImpact = null;
933                         value = rsV.getString(57);
934                         if (value != null) {
935                             modifiedSubIntegrityImpact = CvssV4Data.ModifiedSubIaType.fromValue(value);
936                         }
937                         CvssV4Data.ModifiedSubIaType modifiedSubAvailabilityImpact = null;
938                         value = rsV.getString(58);
939                         if (value != null) {
940                             modifiedSubAvailabilityImpact = CvssV4Data.ModifiedSubIaType.fromValue(value);
941                         }
942                         CvssV4Data.SafetyType safety = null;
943                         value = rsV.getString(59);
944                         if (value != null) {
945                             safety = CvssV4Data.SafetyType.fromValue(value);
946                         }
947                         CvssV4Data.AutomatableType automatable = null;
948                         value = rsV.getString(60);
949                         if (value != null) {
950                             automatable = CvssV4Data.AutomatableType.fromValue(value);
951                         }
952                         CvssV4Data.RecoveryType recovery = null;
953                         value = rsV.getString(61);
954                         if (value != null) {
955                             recovery = CvssV4Data.RecoveryType.fromValue(value);
956                         }
957                         CvssV4Data.ValueDensityType valueDensity = null;
958                         value = rsV.getString(62);
959                         if (value != null) {
960                             valueDensity = CvssV4Data.ValueDensityType.fromValue(value);
961                         }
962                         CvssV4Data.VulnerabilityResponseEffortType vulnerabilityResponseEffort = null;
963                         value = rsV.getString(63);
964                         if (value != null) {
965                             vulnerabilityResponseEffort = CvssV4Data.VulnerabilityResponseEffortType.fromValue(value);
966                         }
967                         CvssV4Data.ProviderUrgencyType providerUrgency = null;
968                         value = rsV.getString(64);
969                         if (value != null) {
970                             providerUrgency = CvssV4Data.ProviderUrgencyType.fromValue(value);
971                         }
972                         Double baseScore = null;
973                         if (rsV.getObject(65) != null) {
974                             baseScore = rsV.getDouble(65);
975                         }
976                         CvssV4Data.SeverityType baseSeverity = null;
977                         value = rsV.getString(66);
978                         if (value != null) {
979                             baseSeverity = CvssV4Data.SeverityType.fromValue(value);
980                         }
981                         Double threatScore = null;
982                         if (rsV.getObject(67) != null) {
983                             threatScore = rsV.getDouble(67);
984                         }
985                         CvssV4Data.SeverityType threatSeverity = null;
986                         value = rsV.getString(68);
987                         if (value != null) {
988                             threatSeverity = CvssV4Data.SeverityType.fromValue(value);
989                         }
990                         Double environmentalScore = null;
991                         if (rsV.getObject(69) != null) {
992                             environmentalScore = rsV.getDouble(69);
993                         }
994                         CvssV4Data.SeverityType environmentalSeverity = null;
995                         value = rsV.getString(70);
996                         if (value != null) {
997                             environmentalSeverity = CvssV4Data.SeverityType.fromValue(value);
998                         }
999                         //initializing data twice to get the vector string. I really should have designed the object better...
1000                         CvssV4Data data = new CvssV4Data(version, vectorString, attackVector, attackComplexity, attackRequirements, privilegesRequired,
1001                                 userInteraction, vulnConfidentialityImpact, vulnIntegrityImpact, vulnAvailabilityImpact, subConfidentialityImpact,
1002                                 subIntegrityImpact, subAvailabilityImpact, exploitMaturity, confidentialityRequirement, integrityRequirement,
1003                                 availabilityRequirement, modifiedAttackVector, modifiedAttackComplexity, modifiedAttackRequirements,
1004                                 modifiedPrivilegesRequired, modifiedUserInteraction, modifiedVulnConfidentialityImpact, modifiedVulnIntegrityImpact,
1005                                 modifiedVulnAvailabilityImpact, modifiedSubConfidentialityImpact, modifiedSubIntegrityImpact,
1006                                 modifiedSubAvailabilityImpact, safety, automatable, recovery, valueDensity, vulnerabilityResponseEffort,
1007                                 providerUrgency, baseScore, baseSeverity, threatScore, threatSeverity, environmentalScore, environmentalSeverity);
1008                         vectorString = data.toString();
1009                         data = new CvssV4Data(version, vectorString, attackVector, attackComplexity, attackRequirements, privilegesRequired,
1010                                 userInteraction, vulnConfidentialityImpact, vulnIntegrityImpact, vulnAvailabilityImpact, subConfidentialityImpact,
1011                                 subIntegrityImpact, subAvailabilityImpact, exploitMaturity, confidentialityRequirement, integrityRequirement,
1012                                 availabilityRequirement, modifiedAttackVector, modifiedAttackComplexity, modifiedAttackRequirements,
1013                                 modifiedPrivilegesRequired, modifiedUserInteraction, modifiedVulnConfidentialityImpact, modifiedVulnIntegrityImpact,
1014                                 modifiedVulnAvailabilityImpact, modifiedSubConfidentialityImpact, modifiedSubIntegrityImpact,
1015                                 modifiedSubAvailabilityImpact, safety, automatable, recovery, valueDensity, vulnerabilityResponseEffort,
1016                                 providerUrgency, baseScore, baseSeverity, threatScore, threatSeverity, environmentalScore, environmentalSeverity);
1017 
1018                         final String source = rsV.getString(71);
1019                         CvssV4.Type cvssType = null;
1020                         value = rsV.getString(72);
1021                         if (value != null) {
1022                             cvssType = CvssV4.Type.fromValue(value);
1023                         }
1024                         final CvssV4 cvssv4 = new CvssV4(source, cvssType, data);
1025                         vuln.setCvssV4(cvssv4);
1026                     }
1027                 } else {
1028                     LOGGER.debug(cve + " does not exist in the database");
1029                     return null;
1030                 }
1031             }
1032             try (PreparedStatement psCWE = getPreparedStatement(conn, SELECT_VULNERABILITY_CWE, cveId); ResultSet rsC = psCWE.executeQuery()) {
1033                 while (rsC.next()) {
1034                     vuln.addCwe(rsC.getString(1));
1035                 }
1036             }
1037             try (PreparedStatement psR = getPreparedStatement(conn, SELECT_REFERENCES, cveId); ResultSet rsR = psR.executeQuery()) {
1038                 while (rsR.next()) {
1039                     vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3));
1040                 }
1041             }
1042             try (PreparedStatement psS = getPreparedStatement(conn, SELECT_SOFTWARE, cveId); ResultSet rsS = psS.executeQuery()) {
1043                 //1 part, 2 vendor, 3 product, 4 version, 5 update_version, 6 edition, 7 lang,
1044                 //8 sw_edition, 9 target_sw, 10 target_hw, 11 other, 12 versionEndExcluding,
1045                 //13 versionEndIncluding, 14 versionStartExcluding, 15 versionStartIncluding, 16 vulnerable
1046                 while (rsS.next()) {
1047                     vulnerableSoftwareBuilder.part(rsS.getString(1))
1048                             .vendor(rsS.getString(2))
1049                             .product(rsS.getString(3))
1050                             .version(rsS.getString(4))
1051                             .update(rsS.getString(5))
1052                             .edition(rsS.getString(6))
1053                             .language(rsS.getString(7))
1054                             .swEdition(rsS.getString(8))
1055                             .targetSw(rsS.getString(9))
1056                             .targetHw(rsS.getString(10))
1057                             .other(rsS.getString(11))
1058                             .versionEndExcluding(rsS.getString(12))
1059                             .versionEndIncluding(rsS.getString(13))
1060                             .versionStartExcluding(rsS.getString(14))
1061                             .versionStartIncluding(rsS.getString(15))
1062                             .vulnerable(rsS.getBoolean(16));
1063                     vuln.addVulnerableSoftware(vulnerableSoftwareBuilder.build());
1064                 }
1065             }
1066         } catch (SQLException ex) {
1067             throw new DatabaseException("Error retrieving " + cve, ex);
1068         } catch (CpeParsingException | CpeValidationException ex) {
1069             throw new DatabaseException("The database contains an invalid Vulnerable Software Entry", ex);
1070         }
1071         return vuln;
1072     }
1073 
1074     /**
1075      * Updates the vulnerability within the database. If the vulnerability does
1076      * not exist it will be added.
1077      *
1078      * @param cve the vulnerability from the NVD CVE Data Feed to add to the
1079      * database
1080      * @param baseEcosystem the ecosystem the CVE belongs to; this is based off
1081      * of things like the CVE description
1082      * @throws DatabaseException is thrown if the database
1083      */
1084     public void updateVulnerability(DefCveItem cve, String baseEcosystem) {
1085         clearCache();
1086         final String cveId = cve.getCve().getId();
1087         try {
1088             if (cve.getCve().getVulnStatus() != null && cve.getCve().getVulnStatus().toUpperCase().startsWith("REJECT")) {
1089                 deleteVulnerability(cveId);
1090             } else {
1091                 if (cveItemConverter.testCveCpeStartWithFilter(cve)) {
1092                     final String description = cveItemConverter.extractDescription(cve);
1093                     final int vulnerabilityId = updateOrInsertVulnerability(cve, description);
1094                     updateVulnerabilityInsertCwe(vulnerabilityId, cve);
1095                     updateVulnerabilityInsertReferences(vulnerabilityId, cve);
1096 
1097                     final List<VulnerableSoftware> software = parseCpes(cve);
1098                     updateVulnerabilityInsertSoftware(vulnerabilityId, cveId, software, baseEcosystem);
1099                 }
1100             }
1101         } catch (SQLException ex) {
1102             final String msg = String.format("Error updating '%s'; %s", cveId, ex.getMessage());
1103             LOGGER.debug(msg, ex);
1104             throw new DatabaseException(msg);
1105         } catch (CpeValidationException ex) {
1106             final String msg = String.format("Error parsing CPE entry from '%s'; %s", cveId, ex.getMessage());
1107             LOGGER.debug(msg, ex);
1108             throw new DatabaseException(msg);
1109         }
1110     }
1111 
1112     private void loadCpeEcosystemCache() {
1113         final Map<Pair<String, String>, String> map = new HashMap<>();
1114         try (Connection conn = databaseManager.getConnection();
1115                 PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ECOSYSTEM);
1116                 ResultSet rs = ps.executeQuery()) {
1117             while (rs.next()) {
1118                 final Pair<String, String> key = new Pair<>(rs.getString(1), rs.getString(2));
1119                 final String value = rs.getString(3);
1120                 map.put(key, value);
1121             }
1122         } catch (SQLException ex) {
1123             final String msg = String.format("Error loading the Cpe Ecosystem Cache: %s", ex.getMessage());
1124             LOGGER.debug(msg, ex);
1125             throw new DatabaseException(msg, ex);
1126         }
1127         CpeEcosystemCache.setCache(map);
1128     }
1129 
1130     private void saveCpeEcosystemCache() {
1131         final Map<Pair<String, String>, String> map = CpeEcosystemCache.getChanged();
1132         if (map != null && !map.isEmpty()) {
1133             try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, MERGE_CPE_ECOSYSTEM)) {
1134                 for (Map.Entry<Pair<String, String>, String> entry : map.entrySet()) {
1135                     ps.setString(1, entry.getKey().getLeft());
1136                     ps.setString(2, entry.getKey().getRight());
1137                     ps.setString(3, entry.getValue());
1138                     if (isBatchInsertEnabled()) {
1139                         ps.addBatch();
1140                     } else {
1141                         ps.execute();
1142                     }
1143                 }
1144                 if (isBatchInsertEnabled()) {
1145                     ps.executeBatch();
1146                 }
1147             } catch (SQLException ex) {
1148                 final String msg = String.format("Error saving the Cpe Ecosystem Cache: %s", ex.getMessage());
1149                 LOGGER.debug(msg, ex);
1150                 throw new DatabaseException(msg, ex);
1151             }
1152         }
1153     }
1154 
1155     /**
1156      * Used when updating a vulnerability - this method inserts the
1157      * vulnerability entry itself.
1158      *
1159      * @param cve the CVE data
1160      * @param description the description of the CVE entry
1161      * @return the vulnerability ID
1162      */
1163     private int updateOrInsertVulnerability(DefCveItem cve, String description) {
1164         if (CpeEcosystemCache.isEmpty()) {
1165             loadCpeEcosystemCache();
1166         }
1167         final int vulnerabilityId;
1168         try (Connection conn = databaseManager.getConnection(); PreparedStatement callUpdate = getPreparedStatement(conn, UPDATE_VULNERABILITY)) {
1169 //            String 1.cve, String 2.description, String 3.v2Severity, Float 4.v2ExploitabilityScore,
1170 //            Float 5.v2ImpactScore, Boolean 6.v2AcInsufInfo, Boolean 7.v2ObtainAllPrivilege,
1171 //            Boolean 8.v2ObtainUserPrivilege, Boolean 9.v2ObtainOtherPrivilege, Boolean 10.v2UserInteractionRequired,
1172 //            Float 11.v2Score, String 12.v2AccessVector, String 13.v2AccessComplexity,
1173 //            String 14.v2Authentication, String 15.v2ConfidentialityImpact, String 16.v2IntegrityImpact,
1174 //            String 17.v2AvailabilityImpact, String 18.v2Version, Float 19.v3ExploitabilityScore,
1175 //            Float 20.v3ImpactScore, String 21.v3AttackVector, String 22.v3AttackComplexity,
1176 //            String 23.v3PrivilegesRequired, String 24.v3UserInteraction, String 25.v3Scope,
1177 //            String 26.v3ConfidentialityImpact, String 27.v3IntegrityImpact, String 28.v3AvailabilityImpact,
1178 //            Float 29.v3BaseScore, String 30.v3BaseSeverity, String 31.v3Version
1179 // .          String 32.v4version, String 33.v4attackVector, String 34.v4attackComplexity, String 35.v4attackRequirements,
1180 //            String 36.v4privilegesRequired, String 37.v4userInteraction, String 38.v4vulnConfidentialityImpact,
1181 //            String 39.v4vulnIntegrityImpact, String 40.v4vulnAvailabilityImpact, String 41.v4subConfidentialityImpact,
1182 //            String 42.v4subIntegrityImpact, String 43.v4subAvailabilityImpact, String 44.v4exploitMaturity,
1183 //            String 45.v4confidentialityRequirement, String 46.v4integrityRequirement, String 47.v4availabilityRequirement,
1184 //            String 48.v4modifiedAttackVector, String 49.v4modifiedAttackComplexity, String 50.v4modifiedAttackRequirements,
1185 //            String 51.v4modifiedPrivilegesRequired, String 52.v4modifiedUserInteraction, String 53.v4modifiedVulnConfidentialityImpact,
1186 //            String 54.v4modifiedVulnIntegrityImpact, String 55.v4modifiedVulnAvailabilityImpact, String 56.v4modifiedSubConfidentialityImpact,
1187 //            String 57.v4modifiedSubIntegrityImpact, String 58.v4modifiedSubAvailabilityImpact, String 59.v4safety,
1188 //            String 60.v4automatable, String 61.v4recovery, String 62.v4valueDensity, String 63.v4vulnerabilityResponseEffort,
1189 //            String 64.v4providerUrgency, Float 65.v4baseScore, String 66.v4baseSeverity, Float 67.v4threatScore,
1190 //            String 68.v4threatSeverity, Float 69.v4environmentalScore, String 70.v4environmentalSeverity
1191 // .          String 71.v4Source, String 72.v4type
1192             callUpdate.setString(1, cve.getCve().getId());
1193             callUpdate.setString(2, description);
1194             Optional<CvssV2> optCvssv2 = null;
1195             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV2() != null) {
1196                 optCvssv2 = cve.getCve().getMetrics().getCvssMetricV2().stream().sorted(Comparator.comparing(CvssV2::getType)).findFirst();
1197             }
1198             if (optCvssv2 != null && optCvssv2.isPresent()) {
1199                 final CvssV2 cvssv2 = optCvssv2.get();
1200                 setUpdateColumn(callUpdate, 3, cvssv2.getBaseSeverity());
1201                 setUpdateColumn(callUpdate, 4, cvssv2.getExploitabilityScore());
1202                 setUpdateColumn(callUpdate, 5, cvssv2.getImpactScore());
1203                 setUpdateColumn(callUpdate, 6, cvssv2.getAcInsufInfo());
1204                 setUpdateColumn(callUpdate, 7, cvssv2.getObtainAllPrivilege());
1205                 setUpdateColumn(callUpdate, 8, cvssv2.getObtainUserPrivilege());
1206                 setUpdateColumn(callUpdate, 9, cvssv2.getObtainOtherPrivilege());
1207                 setUpdateColumn(callUpdate, 10, cvssv2.getUserInteractionRequired());
1208                 setUpdateColumn(callUpdate, 11, cvssv2.getCvssData().getBaseScore());
1209                 setUpdateColumn(callUpdate, 12, cvssv2.getCvssData().getAccessVector());
1210                 setUpdateColumn(callUpdate, 13, cvssv2.getCvssData().getAccessComplexity());
1211                 setUpdateColumn(callUpdate, 14, cvssv2.getCvssData().getAuthentication());
1212                 setUpdateColumn(callUpdate, 15, cvssv2.getCvssData().getConfidentialityImpact());
1213                 setUpdateColumn(callUpdate, 16, cvssv2.getCvssData().getIntegrityImpact());
1214                 setUpdateColumn(callUpdate, 17, cvssv2.getCvssData().getAvailabilityImpact());
1215                 setUpdateColumn(callUpdate, 18, cvssv2.getCvssData().getVersion());
1216             } else {
1217                 callUpdate.setNull(3, java.sql.Types.VARCHAR);
1218                 callUpdate.setNull(4, java.sql.Types.DOUBLE);
1219                 callUpdate.setNull(5, java.sql.Types.DOUBLE);
1220                 callUpdate.setNull(6, java.sql.Types.VARCHAR);
1221                 //TODO this is may also be an issue for MS SQL, if an issue is created we'll just need
1222                 // to create an isMsSQL flag. See todo below in setUpdateColum
1223                 if (isOracle) {
1224                     callUpdate.setNull(7, java.sql.Types.BIT);
1225                     callUpdate.setNull(8, java.sql.Types.BIT);
1226                     callUpdate.setNull(9, java.sql.Types.BIT);
1227                     callUpdate.setNull(10, java.sql.Types.BIT);
1228                 } else {
1229                     callUpdate.setNull(7, java.sql.Types.BOOLEAN);
1230                     callUpdate.setNull(8, java.sql.Types.BOOLEAN);
1231                     callUpdate.setNull(9, java.sql.Types.BOOLEAN);
1232                     callUpdate.setNull(10, java.sql.Types.BOOLEAN);
1233                 }
1234                 callUpdate.setNull(11, java.sql.Types.DOUBLE);
1235                 callUpdate.setNull(12, java.sql.Types.VARCHAR);
1236                 callUpdate.setNull(13, java.sql.Types.VARCHAR);
1237                 callUpdate.setNull(14, java.sql.Types.VARCHAR);
1238                 callUpdate.setNull(15, java.sql.Types.VARCHAR);
1239                 callUpdate.setNull(16, java.sql.Types.VARCHAR);
1240                 callUpdate.setNull(17, java.sql.Types.VARCHAR);
1241                 callUpdate.setNull(18, java.sql.Types.VARCHAR);
1242             }
1243             Optional<CvssV3> optCvssv30 = Optional.empty();
1244             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV30() != null) {
1245                 optCvssv30 = cve.getCve().getMetrics().getCvssMetricV30().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst();
1246             }
1247             Optional<CvssV3> optCvssv31 = Optional.empty();
1248             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV31() != null) {
1249                 optCvssv31 = cve.getCve().getMetrics().getCvssMetricV31().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst();
1250             }
1251 
1252             CvssV3 cvssv3 = null;
1253             if (optCvssv31.isPresent()) {
1254                 cvssv3 = optCvssv31.get();
1255             } else if (optCvssv30.isPresent()) {
1256                 cvssv3 = optCvssv30.get();
1257             }
1258             if (cvssv3 != null) {
1259                 setUpdateColumn(callUpdate, 19, cvssv3.getExploitabilityScore());
1260                 setUpdateColumn(callUpdate, 20, cvssv3.getImpactScore());
1261                 setUpdateColumn(callUpdate, 21, cvssv3.getCvssData().getAttackVector());
1262                 setUpdateColumn(callUpdate, 22, cvssv3.getCvssData().getAttackComplexity());
1263                 setUpdateColumn(callUpdate, 23, cvssv3.getCvssData().getPrivilegesRequired());
1264                 setUpdateColumn(callUpdate, 24, cvssv3.getCvssData().getUserInteraction());
1265                 setUpdateColumn(callUpdate, 25, cvssv3.getCvssData().getScope());
1266                 setUpdateColumn(callUpdate, 26, cvssv3.getCvssData().getConfidentialityImpact());
1267                 setUpdateColumn(callUpdate, 27, cvssv3.getCvssData().getIntegrityImpact());
1268                 setUpdateColumn(callUpdate, 28, cvssv3.getCvssData().getAvailabilityImpact());
1269                 setUpdateColumn(callUpdate, 29, cvssv3.getCvssData().getBaseScore());
1270                 setUpdateColumn(callUpdate, 30, cvssv3.getCvssData().getBaseSeverity());
1271                 setUpdateColumn(callUpdate, 31, cvssv3.getCvssData().getVersion());
1272             } else {
1273                 callUpdate.setNull(19, java.sql.Types.DOUBLE);
1274                 callUpdate.setNull(20, java.sql.Types.DOUBLE);
1275                 callUpdate.setNull(21, java.sql.Types.VARCHAR);
1276                 callUpdate.setNull(22, java.sql.Types.VARCHAR);
1277                 callUpdate.setNull(23, java.sql.Types.VARCHAR);
1278                 callUpdate.setNull(24, java.sql.Types.VARCHAR);
1279                 callUpdate.setNull(25, java.sql.Types.VARCHAR);
1280                 callUpdate.setNull(26, java.sql.Types.VARCHAR);
1281                 callUpdate.setNull(27, java.sql.Types.VARCHAR);
1282                 callUpdate.setNull(28, java.sql.Types.VARCHAR);
1283                 callUpdate.setNull(29, java.sql.Types.DOUBLE);
1284                 callUpdate.setNull(30, java.sql.Types.VARCHAR);
1285                 callUpdate.setNull(31, java.sql.Types.VARCHAR);
1286             }
1287 
1288             Optional<CvssV4> optCvssv4 = null;
1289             if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV40() != null) {
1290                 optCvssv4 = cve.getCve().getMetrics().getCvssMetricV40().stream().sorted(Comparator.comparing(CvssV4::getType)).findFirst();
1291             }
1292             if (optCvssv4 != null && optCvssv4.isPresent()) {
1293                 final CvssV4 cvssv4 = optCvssv4.get();
1294                 setUpdateColumn(callUpdate, 32, cvssv4.getCvssData().getVersion());
1295                 setUpdateColumn(callUpdate, 33, cvssv4.getCvssData().getAttackVector());
1296                 setUpdateColumn(callUpdate, 34, cvssv4.getCvssData().getAttackComplexity());
1297                 setUpdateColumn(callUpdate, 35, cvssv4.getCvssData().getAttackRequirements());
1298                 setUpdateColumn(callUpdate, 36, cvssv4.getCvssData().getPrivilegesRequired());
1299                 setUpdateColumn(callUpdate, 37, cvssv4.getCvssData().getUserInteraction());
1300                 setUpdateColumn(callUpdate, 38, cvssv4.getCvssData().getVulnConfidentialityImpact());
1301                 setUpdateColumn(callUpdate, 39, cvssv4.getCvssData().getVulnIntegrityImpact());
1302                 setUpdateColumn(callUpdate, 40, cvssv4.getCvssData().getVulnAvailabilityImpact());
1303                 setUpdateColumn(callUpdate, 41, cvssv4.getCvssData().getSubConfidentialityImpact());
1304                 setUpdateColumn(callUpdate, 42, cvssv4.getCvssData().getSubIntegrityImpact());
1305                 setUpdateColumn(callUpdate, 43, cvssv4.getCvssData().getSubAvailabilityImpact());
1306                 setUpdateColumn(callUpdate, 44, cvssv4.getCvssData().getExploitMaturity());
1307                 setUpdateColumn(callUpdate, 45, cvssv4.getCvssData().getConfidentialityRequirement());
1308                 setUpdateColumn(callUpdate, 46, cvssv4.getCvssData().getIntegrityRequirement());
1309                 setUpdateColumn(callUpdate, 47, cvssv4.getCvssData().getAvailabilityRequirement());
1310                 setUpdateColumn(callUpdate, 48, cvssv4.getCvssData().getModifiedAttackVector());
1311                 setUpdateColumn(callUpdate, 49, cvssv4.getCvssData().getModifiedAttackComplexity());
1312                 setUpdateColumn(callUpdate, 50, cvssv4.getCvssData().getModifiedAttackRequirements());
1313                 setUpdateColumn(callUpdate, 51, cvssv4.getCvssData().getModifiedPrivilegesRequired());
1314                 setUpdateColumn(callUpdate, 52, cvssv4.getCvssData().getModifiedUserInteraction());
1315                 setUpdateColumn(callUpdate, 53, cvssv4.getCvssData().getModifiedVulnConfidentialityImpact());
1316                 setUpdateColumn(callUpdate, 54, cvssv4.getCvssData().getModifiedVulnIntegrityImpact());
1317                 setUpdateColumn(callUpdate, 55, cvssv4.getCvssData().getModifiedVulnAvailabilityImpact());
1318                 setUpdateColumn(callUpdate, 56, cvssv4.getCvssData().getModifiedSubConfidentialityImpact());
1319                 setUpdateColumn(callUpdate, 57, cvssv4.getCvssData().getModifiedSubIntegrityImpact());
1320                 setUpdateColumn(callUpdate, 58, cvssv4.getCvssData().getModifiedSubAvailabilityImpact());
1321                 setUpdateColumn(callUpdate, 59, cvssv4.getCvssData().getSafety());
1322                 setUpdateColumn(callUpdate, 60, cvssv4.getCvssData().getAutomatable());
1323                 setUpdateColumn(callUpdate, 61, cvssv4.getCvssData().getRecovery());
1324                 setUpdateColumn(callUpdate, 62, cvssv4.getCvssData().getValueDensity());
1325                 setUpdateColumn(callUpdate, 63, cvssv4.getCvssData().getVulnerabilityResponseEffort());
1326                 setUpdateColumn(callUpdate, 64, cvssv4.getCvssData().getProviderUrgency());
1327                 setUpdateColumn(callUpdate, 65, cvssv4.getCvssData().getBaseScore());
1328                 setUpdateColumn(callUpdate, 66, cvssv4.getCvssData().getBaseSeverity());
1329                 setUpdateColumn(callUpdate, 67, cvssv4.getCvssData().getThreatScore());
1330                 setUpdateColumn(callUpdate, 68, cvssv4.getCvssData().getThreatSeverity());
1331                 setUpdateColumn(callUpdate, 69, cvssv4.getCvssData().getEnvironmentalScore());
1332                 setUpdateColumn(callUpdate, 70, cvssv4.getCvssData().getEnvironmentalSeverity());
1333                 setUpdateColumn(callUpdate, 71, cvssv4.getSource());
1334                 setUpdateColumn(callUpdate, 72, cvssv4.getType());
1335             } else {
1336                 callUpdate.setNull(32, java.sql.Types.VARCHAR);
1337                 callUpdate.setNull(33, java.sql.Types.VARCHAR);
1338                 callUpdate.setNull(34, java.sql.Types.VARCHAR);
1339                 callUpdate.setNull(35, java.sql.Types.VARCHAR);
1340                 callUpdate.setNull(36, java.sql.Types.VARCHAR);
1341                 callUpdate.setNull(37, java.sql.Types.VARCHAR);
1342                 callUpdate.setNull(38, java.sql.Types.VARCHAR);
1343                 callUpdate.setNull(39, java.sql.Types.VARCHAR);
1344                 callUpdate.setNull(40, java.sql.Types.VARCHAR);
1345                 callUpdate.setNull(41, java.sql.Types.VARCHAR);
1346                 callUpdate.setNull(42, java.sql.Types.VARCHAR);
1347                 callUpdate.setNull(43, java.sql.Types.VARCHAR);
1348                 callUpdate.setNull(44, java.sql.Types.VARCHAR);
1349                 callUpdate.setNull(45, java.sql.Types.VARCHAR);
1350                 callUpdate.setNull(46, java.sql.Types.VARCHAR);
1351                 callUpdate.setNull(47, java.sql.Types.VARCHAR);
1352                 callUpdate.setNull(48, java.sql.Types.VARCHAR);
1353                 callUpdate.setNull(49, java.sql.Types.VARCHAR);
1354                 callUpdate.setNull(50, java.sql.Types.VARCHAR);
1355                 callUpdate.setNull(51, java.sql.Types.VARCHAR);
1356                 callUpdate.setNull(52, java.sql.Types.VARCHAR);
1357                 callUpdate.setNull(53, java.sql.Types.VARCHAR);
1358                 callUpdate.setNull(54, java.sql.Types.VARCHAR);
1359                 callUpdate.setNull(55, java.sql.Types.VARCHAR);
1360                 callUpdate.setNull(56, java.sql.Types.VARCHAR);
1361                 callUpdate.setNull(57, java.sql.Types.VARCHAR);
1362                 callUpdate.setNull(58, java.sql.Types.VARCHAR);
1363                 callUpdate.setNull(59, java.sql.Types.VARCHAR);
1364                 callUpdate.setNull(60, java.sql.Types.VARCHAR);
1365                 callUpdate.setNull(61, java.sql.Types.VARCHAR);
1366                 callUpdate.setNull(62, java.sql.Types.VARCHAR);
1367                 callUpdate.setNull(63, java.sql.Types.VARCHAR);
1368                 callUpdate.setNull(64, java.sql.Types.VARCHAR);
1369                 callUpdate.setNull(65, java.sql.Types.DOUBLE);
1370                 callUpdate.setNull(66, java.sql.Types.VARCHAR);
1371                 callUpdate.setNull(67, java.sql.Types.DOUBLE);
1372                 callUpdate.setNull(68, java.sql.Types.VARCHAR);
1373                 callUpdate.setNull(69, java.sql.Types.DOUBLE);
1374                 callUpdate.setNull(70, java.sql.Types.VARCHAR);
1375                 callUpdate.setNull(71, java.sql.Types.VARCHAR);
1376                 callUpdate.setNull(72, java.sql.Types.VARCHAR);
1377             }
1378             if (isOracle) {
1379                 try {
1380                     final CallableStatement cs = (CallableStatement) callUpdate;
1381                     cs.registerOutParameter(73, JDBCType.INTEGER);
1382                     cs.executeUpdate();
1383                     vulnerabilityId = cs.getInt(73);
1384                 } catch (SQLException ex) {
1385                     final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId());
1386                     throw new DatabaseException(msg, ex);
1387                 }
1388             } else {
1389                 try (ResultSet rs = callUpdate.executeQuery()) {
1390                     rs.next();
1391                     vulnerabilityId = rs.getInt(1);
1392                 } catch (SQLException ex) {
1393                     final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId());
1394                     throw new DatabaseException(msg, ex);
1395                 }
1396             }
1397         } catch (SQLException ex) {
1398             throw new UnexpectedAnalysisException(ex);
1399         }
1400         return vulnerabilityId;
1401     }
1402 
1403     /**
1404      * Used when updating a vulnerability - this method inserts the CWE entries.
1405      *
1406      * @param vulnerabilityId the vulnerability ID
1407      * @param cve the CVE entry that contains the CWE entries to insert
1408      * @throws SQLException thrown if there is an error inserting the data
1409      */
1410     private void updateVulnerabilityInsertCwe(int vulnerabilityId, DefCveItem cve) throws SQLException {
1411         if (cve.getCve() != null && cve.getCve().getWeaknesses() != null) {
1412             try (Connection conn = databaseManager.getConnection();
1413                     PreparedStatement insertCWE = getPreparedStatement(conn, INSERT_CWE, vulnerabilityId)) {
1414                 for (Weakness weakness : cve.getCve().getWeaknesses()) {
1415                     for (LangString desc : weakness.getDescription()) {
1416                         if ("en".equals(desc.getLang())) {
1417                             insertCWE.setString(2, desc.getValue());
1418                             if (isBatchInsertEnabled()) {
1419                                 insertCWE.addBatch();
1420                             } else {
1421                                 insertCWE.execute();
1422                             }
1423                         }
1424                     }
1425                 }
1426                 if (isBatchInsertEnabled()) {
1427                     insertCWE.executeBatch();
1428                 }
1429             }
1430         }
1431     }
1432 
1433     /**
1434      * Used when updating a vulnerability - in some cases a CVE needs to be
1435      * removed.
1436      *
1437      * @param cve the vulnerability CVE
1438      * @throws SQLException thrown if there is an error deleting the
1439      * vulnerability
1440      */
1441     private void deleteVulnerability(String cve) throws SQLException {
1442         try (Connection conn = databaseManager.getConnection();
1443                 PreparedStatement deleteVulnerability = getPreparedStatement(conn, DELETE_VULNERABILITY, cve)) {
1444             deleteVulnerability.executeUpdate();
1445         }
1446     }
1447 
1448     /**
1449      * Merges the list of known exploited vulnerabilities into the database.
1450      *
1451      * @param vulnerabilities the list of known exploited vulnerabilities
1452      * @throws DatabaseException thrown if there is an exception... duh..
1453      * @throws SQLException thrown if there is an exception... duh..
1454      */
1455     public void updateKnownExploitedVulnerabilities(
1456             List<org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> vulnerabilities)
1457             throws DatabaseException, SQLException {
1458         try (Connection conn = databaseManager.getConnection();
1459                 PreparedStatement mergeKnownVulnerability = getPreparedStatement(conn, MERGE_KNOWN_EXPLOITED)) {
1460             int ctr = 0;
1461             for (org.owasp.dependencycheck.data.knownexploited.json.Vulnerability v : vulnerabilities) {
1462                 mergeKnownVulnerability.setString(1, v.getCveID());
1463                 addNullableStringParameter(mergeKnownVulnerability, 2, v.getVendorProject());
1464                 addNullableStringParameter(mergeKnownVulnerability, 3, v.getProduct());
1465                 addNullableStringParameter(mergeKnownVulnerability, 4, v.getVulnerabilityName());
1466                 addNullableStringParameter(mergeKnownVulnerability, 5, v.getDateAdded());
1467                 addNullableStringParameter(mergeKnownVulnerability, 6, v.getShortDescription());
1468                 addNullableStringParameter(mergeKnownVulnerability, 7, v.getRequiredAction());
1469                 addNullableStringParameter(mergeKnownVulnerability, 8, v.getDueDate());
1470                 addNullableStringParameter(mergeKnownVulnerability, 9, v.getNotes());
1471                 if (isBatchInsertEnabled()) {
1472                     mergeKnownVulnerability.addBatch();
1473                     ctr++;
1474                     if (ctr >= getBatchSize()) {
1475                         mergeKnownVulnerability.executeBatch();
1476                         ctr = 0;
1477                     }
1478                 } else {
1479                     try {
1480                         mergeKnownVulnerability.execute();
1481                     } catch (SQLException ex) {
1482                         if (ex.getMessage().contains("Duplicate entry")) {
1483                             final String msg = String.format("Duplicate known exploited vulnerability key identified in '%s'", v.getCveID());
1484                             LOGGER.info(msg, ex);
1485                         } else {
1486                             throw ex;
1487                         }
1488                     }
1489                 }
1490             }
1491             if (isBatchInsertEnabled()) {
1492                 mergeKnownVulnerability.executeBatch();
1493             }
1494         }
1495     }
1496 
1497     /**
1498      * Used when updating a vulnerability - this method inserts the list of
1499      * vulnerable software.
1500      *
1501      * @param vulnerabilityId the vulnerability id
1502      * @param cveId the CVE ID - used for reporting
1503      * @param software the list of vulnerable software
1504      * @param baseEcosystem the ecosystem based off of the vulnerability
1505      * description
1506      * @throws DatabaseException thrown if there is an error inserting the data
1507      * @throws SQLException thrown if there is an error inserting the data
1508      */
1509     private void updateVulnerabilityInsertSoftware(int vulnerabilityId, String cveId,
1510             List<VulnerableSoftware> software, String baseEcosystem)
1511             throws DatabaseException, SQLException {
1512         try (Connection conn = databaseManager.getConnection(); PreparedStatement insertSoftware = getPreparedStatement(conn, INSERT_SOFTWARE)) {
1513             for (VulnerableSoftware parsedCpe : software) {
1514                 insertSoftware.setInt(1, vulnerabilityId);
1515                 insertSoftware.setString(2, parsedCpe.getPart().getAbbreviation());
1516                 insertSoftware.setString(3, parsedCpe.getVendor());
1517                 insertSoftware.setString(4, parsedCpe.getProduct());
1518                 insertSoftware.setString(5, parsedCpe.getVersion());
1519                 insertSoftware.setString(6, parsedCpe.getUpdate());
1520                 insertSoftware.setString(7, parsedCpe.getEdition());
1521                 insertSoftware.setString(8, parsedCpe.getLanguage());
1522                 insertSoftware.setString(9, parsedCpe.getSwEdition());
1523                 insertSoftware.setString(10, parsedCpe.getTargetSw());
1524                 insertSoftware.setString(11, parsedCpe.getTargetHw());
1525                 insertSoftware.setString(12, parsedCpe.getOther());
1526                 final String ecosystem = CpeEcosystemCache.getEcosystem(parsedCpe.getVendor(), parsedCpe.getProduct(),
1527                         cveItemConverter.extractEcosystem(baseEcosystem, parsedCpe));
1528 
1529                 addNullableStringParameter(insertSoftware, 13, ecosystem);
1530                 addNullableStringParameter(insertSoftware, 14, parsedCpe.getVersionEndExcluding());
1531                 addNullableStringParameter(insertSoftware, 15, parsedCpe.getVersionEndIncluding());
1532                 addNullableStringParameter(insertSoftware, 16, parsedCpe.getVersionStartExcluding());
1533                 addNullableStringParameter(insertSoftware, 17, parsedCpe.getVersionStartIncluding());
1534                 insertSoftware.setBoolean(18, parsedCpe.isVulnerable());
1535 
1536                 if (isBatchInsertEnabled()) {
1537                     insertSoftware.addBatch();
1538                 } else {
1539                     try {
1540                         insertSoftware.execute();
1541                     } catch (SQLException ex) {
1542                         if (ex.getMessage().contains("Duplicate entry")) {
1543                             final String msg = String.format("Duplicate software key identified in '%s'", cveId);
1544                             LOGGER.info(msg, ex);
1545                         } else {
1546                             throw ex;
1547                         }
1548                     }
1549                 }
1550             }
1551             if (isBatchInsertEnabled()) {
1552                 executeBatch(cveId, insertSoftware);
1553             }
1554         }
1555     }
1556 
1557     /**
1558      * Used when updating a vulnerability - this method inserts the list of
1559      * references.
1560      *
1561      * @param vulnerabilityId the vulnerability id
1562      * @param cve the CVE entry that contains the list of references
1563      * @throws SQLException thrown if there is an error inserting the data
1564      */
1565     private void updateVulnerabilityInsertReferences(int vulnerabilityId, DefCveItem cve) throws SQLException {
1566         try (Connection conn = databaseManager.getConnection(); PreparedStatement insertReference = getPreparedStatement(conn, INSERT_REFERENCE)) {
1567             if (cve.getCve().getReferences() != null) {
1568                 for (Reference r : cve.getCve().getReferences()) {
1569                     insertReference.setInt(1, vulnerabilityId);
1570                     String name = null;
1571                     if (r.getTags() != null) {
1572                         name = r.getTags().stream().sorted().collect(Collectors.joining(",")).toUpperCase().replaceAll("\\s", "_");
1573                     }
1574                     if (name != null) {
1575                         insertReference.setString(2, name);
1576                     } else {
1577                         insertReference.setNull(2, java.sql.Types.VARCHAR);
1578                     }
1579                     if (r.getUrl() != null && !r.getUrl().isEmpty()) {
1580                         insertReference.setString(3, r.getUrl());
1581                     } else {
1582                         insertReference.setNull(3, java.sql.Types.VARCHAR);
1583                     }
1584                     if (r.getSource() != null && !r.getSource().isEmpty()) {
1585                         insertReference.setString(4, r.getSource());
1586                     } else {
1587                         insertReference.setNull(4, java.sql.Types.VARCHAR);
1588                     }
1589                     if (isBatchInsertEnabled()) {
1590                         insertReference.addBatch();
1591                     } else {
1592                         insertReference.execute();
1593                     }
1594                 }
1595             }
1596             if (isBatchInsertEnabled()) {
1597                 insertReference.executeBatch();
1598             }
1599         }
1600     }
1601 
1602     /**
1603      * Parses the configuration entries from the CVE entry into a list of
1604      * VulnerableSoftware objects.
1605      *
1606      * @param cve the CVE to parse the vulnerable software entries from
1607      * @return the list of vulnerable software
1608      * @throws CpeValidationException if an invalid CPE is present
1609      */
1610     private List<VulnerableSoftware> parseCpes(DefCveItem cve) throws CpeValidationException {
1611         final List<VulnerableSoftware> software = new ArrayList<>();
1612 
1613         final List<CpeMatch> cpeEntries = cve.getCve().getConfigurations().stream()
1614                 .filter(config -> config.getNodes() != null)
1615                 .map(Config::getNodes)
1616                 .flatMap(List::stream)
1617                 .map(Node::getCpeMatch)
1618                 .flatMap(List::stream)
1619                 .filter(predicate -> predicate.getCriteria() != null)
1620                 .filter(predicate -> predicate.getCriteria().startsWith(cpeStartsWithFilter))
1621                 //this single CPE entry causes nearly 100% FP - so filtering it at the source.
1622                 .filter(entry -> !("CVE-2009-0754".equals(cve.getCve().getId())
1623                 && "cpe:2.3:a:apache:apache:*:*:*:*:*:*:*:*".equals(entry.getCriteria())))
1624                 .collect(Collectors.toList());
1625         final VulnerableSoftwareBuilder builder = new VulnerableSoftwareBuilder();
1626 
1627         try {
1628             cpeEntries.forEach(entry -> {
1629                 builder.cpe(parseCpe(entry, cve.getCve().getId()))
1630                         .versionEndExcluding(entry.getVersionEndExcluding())
1631                         .versionStartExcluding(entry.getVersionStartExcluding())
1632                         .versionEndIncluding(entry.getVersionEndIncluding())
1633                         .versionStartIncluding(entry.getVersionStartIncluding())
1634                         .vulnerable(entry.getVulnerable());
1635                 try {
1636                     software.add(builder.build());
1637                 } catch (CpeValidationException ex) {
1638                     throw new LambdaExceptionWrapper(ex);
1639                 }
1640             });
1641         } catch (LambdaExceptionWrapper ex) {
1642             throw (CpeValidationException) ex.getCause();
1643         }
1644         return software;
1645     }
1646 
1647     /**
1648      * Helper method to convert a CpeMatch (generated code used in parsing the
1649      * NVD JSON) into a CPE object.
1650      *
1651      * @param cpe the CPE Match
1652      * @param cveId the CVE associated with the CPEMatch - used for error
1653      * reporting
1654      * @return the resulting CPE object
1655      * @throws DatabaseException thrown if there is an error converting the
1656      * CpeMatch into a CPE object
1657      */
1658     private Cpe parseCpe(CpeMatch cpe, String cveId) throws DatabaseException {
1659         final Cpe parsedCpe;
1660         try {
1661             //the replace is a hack as the NVD does not properly escape backslashes in their JSON
1662             parsedCpe = CpeParser.parse(cpe.getCriteria(), true);
1663         } catch (CpeParsingException ex) {
1664             LOGGER.debug("NVD (" + cveId + ") contain an invalid 2.3 CPE: " + cpe.getCriteria());
1665             throw new DatabaseException("Unable to parse CPE: " + cpe.getCriteria(), ex);
1666         }
1667         return parsedCpe;
1668     }
1669 
1670     /**
1671      * Returns the size of the batch.
1672      *
1673      * @return the size of the batch
1674      */
1675     private int getBatchSize() {
1676         int max;
1677         try {
1678             max = settings.getInt(Settings.KEYS.MAX_BATCH_SIZE);
1679         } catch (InvalidSettingException pE) {
1680             max = 1000;
1681         }
1682         return max;
1683     }
1684 
1685     /**
1686      * Determines whether or not batch insert is enabled.
1687      *
1688      * @return <code>true</code> if batch insert is enabled; otherwise
1689      * <code>false</code>
1690      */
1691     private boolean isBatchInsertEnabled() {
1692         boolean batch;
1693         try {
1694             batch = settings.getBoolean(Settings.KEYS.ENABLE_BATCH_UPDATES);
1695         } catch (InvalidSettingException pE) {
1696             //If there's no configuration, default is to not perform batch inserts
1697             batch = false;
1698         }
1699         return batch;
1700     }
1701 
1702     /**
1703      * Executes batch inserts of vulnerabilities when property
1704      * database.batchinsert.maxsize is reached.
1705      *
1706      * @param vulnId the vulnerability ID
1707      * @param statement the prepared statement to batch execute
1708      * @throws SQLException thrown when the batch cannot be executed
1709      */
1710     private void executeBatch(String vulnId, PreparedStatement statement)
1711             throws SQLException {
1712         try {
1713             statement.executeBatch();
1714         } catch (SQLException ex) {
1715             if (ex.getMessage().contains("Duplicate entry")) {
1716                 final String msg = String.format("Duplicate software key identified in '%s'",
1717                         vulnId);
1718                 LOGGER.info(msg, ex);
1719             } else {
1720                 throw ex;
1721             }
1722         }
1723     }
1724 
1725     /**
1726      * Checks to see if data exists so that analysis can be performed.
1727      *
1728      * @return <code>true</code> if data exists; otherwise <code>false</code>
1729      */
1730     public boolean dataExists() {
1731         try (Connection conn = databaseManager.getConnection();
1732                 PreparedStatement cs = getPreparedStatement(conn, COUNT_CPE);
1733                 ResultSet rs = cs.executeQuery()) {
1734             if (rs.next() && rs.getInt(1) > 0) {
1735                 return true;
1736             }
1737         } catch (Exception ex) {
1738             String dd;
1739             try {
1740                 dd = settings.getDataDirectory().getAbsolutePath();
1741             } catch (IOException ex1) {
1742                 dd = settings.getString(Settings.KEYS.DATA_DIRECTORY);
1743             }
1744             LOGGER.error("Unable to access the local database.\n\nEnsure that '{}' is a writable directory. "
1745                     + "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please "
1746                     + "create a log file (see documentation at https://dependency-check.github.io/DependencyCheck/) and open a ticket at "
1747                     + "https://github.com/dependency-check/DependencyCheck/issues and include the log file.\n\n",
1748                     dd, dd, settings.getString(Settings.KEYS.APPLICATION_NAME));
1749             LOGGER.debug("", ex);
1750         }
1751         return false;
1752     }
1753 
1754     /**
1755      * It is possible that orphaned rows may be generated during database
1756      * updates. This should be called after all updates have been completed to
1757      * ensure orphan entries are removed.
1758      */
1759     public void cleanupDatabase() {
1760         LOGGER.info("Begin database maintenance");
1761         final long start = System.currentTimeMillis();
1762         try (Connection conn = databaseManager.getConnection();
1763                 PreparedStatement psOrphans = getPreparedStatement(conn, CLEANUP_ORPHANS);
1764                 PreparedStatement psEcosystem = getPreparedStatement(conn, UPDATE_ECOSYSTEM);
1765                 PreparedStatement psEcosystem2 = getPreparedStatement(conn, UPDATE_ECOSYSTEM2)) {
1766             if (psEcosystem != null) {
1767                 final int count = psEcosystem.executeUpdate();
1768                 if (count > 0) {
1769                     LOGGER.info("Updated the CPE ecosystem on {} NVD records", count);
1770                 }
1771             }
1772             if (psEcosystem2 != null) {
1773                 final int count = psEcosystem2.executeUpdate();
1774                 if (count > 0) {
1775                     LOGGER.info("Removed the CPE ecosystem on {} NVD records", count);
1776                 }
1777             }
1778             if (psOrphans != null) {
1779                 final int count = psOrphans.executeUpdate();
1780                 if (count > 0) {
1781                     LOGGER.info("Cleaned up {} orphaned NVD records", count);
1782                 }
1783             }
1784             final long millis = System.currentTimeMillis() - start;
1785             //final long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
1786             LOGGER.info("End database maintenance ({} ms)", millis);
1787         } catch (SQLException ex) {
1788             LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
1789             LOGGER.debug("", ex);
1790             throw new DatabaseException("Unexpected SQL Exception", ex);
1791         }
1792     }
1793 
1794     /**
1795      * Persist the EcosystemCache into the database.
1796      */
1797     public void persistEcosystemCache() {
1798         saveCpeEcosystemCache();
1799         clearCache();
1800     }
1801 
1802     /**
1803      * If the database is using an H2 file based database calling
1804      * <code>defrag()</code> will de-fragment the database.
1805      */
1806     public void defrag() {
1807         if (isH2) {
1808             final long start = System.currentTimeMillis();
1809             try (Connection conn = databaseManager.getConnection(); CallableStatement psCompaxt = conn.prepareCall("SHUTDOWN DEFRAG")) {
1810                 LOGGER.info("Begin database defrag");
1811                 psCompaxt.execute();
1812                 final long millis = System.currentTimeMillis() - start;
1813                 //final long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
1814                 LOGGER.info("End database defrag ({} ms)", millis);
1815             } catch (SQLException ex) {
1816                 LOGGER.error("An unexpected SQL Exception occurred compacting the database; please see the verbose log for more details.");
1817                 LOGGER.debug("", ex);
1818             }
1819         }
1820     }
1821 
1822     /**
1823      * Determines if the given identifiedVersion is affected by the given cpeId
1824      * and previous version flag. A non-null, non-empty string passed to the
1825      * previous version argument indicates that all previous versions are
1826      * affected.
1827      *
1828      * @param cpe the CPE for the given dependency
1829      * @param vulnerableSoftware a set of the vulnerable software
1830      * @return true if the identified version is affected, otherwise false
1831      */
1832     VulnerableSoftware getMatchingSoftware(Cpe cpe, Set<VulnerableSoftware> vulnerableSoftware) {
1833         VulnerableSoftware matched = null;
1834         for (VulnerableSoftware vs : vulnerableSoftware) {
1835             if (vs.matches(cpe)) {
1836                 if (matched == null) {
1837                     matched = vs;
1838                 } else {
1839                     if ("*".equals(vs.getWellFormedUpdate()) && !"*".equals(matched.getWellFormedUpdate())) {
1840                         matched = vs;
1841                     }
1842                 }
1843             }
1844         }
1845         return matched;
1846     }
1847 
1848     /**
1849      * This method is only referenced in unused code.
1850      * <p>
1851      * Deletes unused dictionary entries from the database.
1852      * </p>
1853      */
1854     public void deleteUnusedCpe() {
1855         clearCache();
1856         try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, DELETE_UNUSED_DICT_CPE)) {
1857             ps.executeUpdate();
1858         } catch (SQLException ex) {
1859             LOGGER.error("Unable to delete CPE dictionary entries", ex);
1860         }
1861     }
1862 
1863     /**
1864      * This method is only referenced in unused code and will likely break on
1865      * MySQL if ever used due to the MERGE statement.
1866      * <p>
1867      * Merges CPE entries into the database.
1868      * </p>
1869      *
1870      * @param cpe the CPE identifier
1871      * @param vendor the CPE vendor
1872      * @param product the CPE product
1873      */
1874     public void addCpe(String cpe, String vendor, String product) {
1875         clearCache();
1876         try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, ADD_DICT_CPE)) {
1877             ps.setString(1, cpe);
1878             ps.setString(2, vendor);
1879             ps.setString(3, product);
1880             ps.executeUpdate();
1881         } catch (SQLException ex) {
1882             LOGGER.error("Unable to add CPE dictionary entry", ex);
1883         }
1884     }
1885 
1886     /**
1887      * Returns a map of known exploited vulnerabilities.
1888      *
1889      * @return a map of known exploited vulnerabilities
1890      */
1891     public Map<String, org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> getknownExploitedVulnerabilities() {
1892         final Map<String, org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> known = new HashMap<>();
1893 
1894         try (Connection conn = databaseManager.getConnection();
1895                 PreparedStatement ps = getPreparedStatement(conn, SELECT_KNOWN_EXPLOITED_VULNERABILITIES);
1896                 ResultSet rs = ps.executeQuery()) {
1897 
1898             while (rs.next()) {
1899                 final org.owasp.dependencycheck.data.knownexploited.json.Vulnerability kev =
1900                         new org.owasp.dependencycheck.data.knownexploited.json.Vulnerability();
1901                 kev.setCveID(rs.getString(1));
1902                 kev.setVendorProject(rs.getString(2));
1903                 kev.setProduct(rs.getString(3));
1904                 kev.setVulnerabilityName(rs.getString(4));
1905                 kev.setDateAdded(rs.getString(5));
1906                 kev.setShortDescription(rs.getString(6));
1907                 kev.setRequiredAction(rs.getString(7));
1908                 kev.setDueDate(rs.getString(8));
1909                 kev.setNotes(rs.getString(9));
1910                 known.put(kev.getCveID(), kev);
1911             }
1912 
1913         } catch (SQLException ex) {
1914             throw new DatabaseException(ex);
1915         }
1916         return known;
1917     }
1918 
1919     /**
1920      * Helper method to add a nullable string parameter.
1921      *
1922      * @param ps the prepared statement
1923      * @param pos the position of the parameter
1924      * @param value the value of the parameter
1925      * @throws SQLException thrown if there is an error setting the parameter.
1926      */
1927     private void addNullableStringParameter(PreparedStatement ps, int pos, String value) throws SQLException {
1928         if (value == null || value.isEmpty()) {
1929             ps.setNull(pos, java.sql.Types.VARCHAR);
1930         } else {
1931             ps.setString(pos, value);
1932         }
1933     }
1934 
1935     private void setUpdateColumn(PreparedStatement ps, int i, Double value) throws SQLException {
1936         if (value == null) {
1937             ps.setNull(i, java.sql.Types.DOUBLE);
1938         } else {
1939             ps.setDouble(i, value);
1940         }
1941     }
1942 
1943     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AuthenticationType value) throws SQLException {
1944         if (value == null) {
1945             ps.setNull(i, java.sql.Types.VARCHAR);
1946         } else {
1947             ps.setString(i, value.value());
1948         }
1949     }
1950 
1951     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.CiaType value) throws SQLException {
1952         if (value == null) {
1953             ps.setNull(i, java.sql.Types.VARCHAR);
1954         } else {
1955             ps.setString(i, value.value());
1956         }
1957     }
1958 
1959     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.Version value) throws SQLException {
1960         if (value == null) {
1961             ps.setNull(i, java.sql.Types.VARCHAR);
1962         } else {
1963             ps.setString(i, value.value());
1964         }
1965     }
1966 
1967     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessComplexityType value) throws SQLException {
1968         if (value == null) {
1969             ps.setNull(i, java.sql.Types.VARCHAR);
1970         } else {
1971             ps.setString(i, value.value());
1972         }
1973     }
1974 
1975     private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessVectorType value) throws SQLException {
1976         if (value == null) {
1977             ps.setNull(i, java.sql.Types.VARCHAR);
1978         } else {
1979             ps.setString(i, value.value());
1980         }
1981     }
1982 
1983     private void setUpdateColumn(PreparedStatement ps, int i, String value) throws SQLException {
1984         if (value == null) {
1985             ps.setNull(i, java.sql.Types.VARCHAR);
1986         } else {
1987             ps.setString(i, value);
1988         }
1989     }
1990 
1991     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4.Type value) throws SQLException {
1992         if (value == null) {
1993             ps.setNull(i, java.sql.Types.VARCHAR);
1994         } else {
1995             ps.setString(i, value.value());
1996         }
1997     }
1998 
1999     private void setUpdateColumn(PreparedStatement ps, int i, Boolean value) throws SQLException {
2000         if (value == null) {
2001             //TODO this is may also be an issue for MS SQL, if an issue is created we'll just need
2002             // to create an isMsSQL flag. See todo above in updateOrInsertVulnerability.
2003             if (isOracle) {
2004                 ps.setNull(i, java.sql.Types.BIT);
2005             } else {
2006                 ps.setNull(i, java.sql.Types.BOOLEAN);
2007             }
2008         } else {
2009             ps.setBoolean(i, value);
2010         }
2011     }
2012 
2013     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackVectorType value) throws SQLException {
2014         if (value == null) {
2015             ps.setNull(i, java.sql.Types.VARCHAR);
2016         } else {
2017             ps.setString(i, value.value());
2018         }
2019     }
2020 
2021     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackComplexityType value) throws SQLException {
2022         if (value == null) {
2023             ps.setNull(i, java.sql.Types.VARCHAR);
2024         } else {
2025             ps.setString(i, value.value());
2026         }
2027     }
2028 
2029     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.PrivilegesRequiredType value) throws SQLException {
2030         if (value == null) {
2031             ps.setNull(i, java.sql.Types.VARCHAR);
2032         } else {
2033             ps.setString(i, value.value());
2034         }
2035     }
2036 
2037     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.UserInteractionType value) throws SQLException {
2038         if (value == null) {
2039             ps.setNull(i, java.sql.Types.VARCHAR);
2040         } else {
2041             ps.setString(i, value.value());
2042         }
2043     }
2044 
2045     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.ScopeType value) throws SQLException {
2046         if (value == null) {
2047             ps.setNull(i, java.sql.Types.VARCHAR);
2048         } else {
2049             ps.setString(i, value.value());
2050         }
2051     }
2052 
2053     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.SeverityType value) throws SQLException {
2054         if (value == null) {
2055             ps.setNull(i, java.sql.Types.VARCHAR);
2056         } else {
2057             ps.setString(i, value.value());
2058         }
2059     }
2060 
2061     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.CiaType value) throws SQLException {
2062         if (value == null) {
2063             ps.setNull(i, java.sql.Types.VARCHAR);
2064         } else {
2065             ps.setString(i, value.value());
2066         }
2067     }
2068 
2069     private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.Version value) throws SQLException {
2070         if (value == null) {
2071             ps.setNull(i, java.sql.Types.VARCHAR);
2072         } else {
2073             ps.setString(i, value.value());
2074         }
2075     }
2076 
2077     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.Version value) throws SQLException  {
2078         if (value == null) {
2079             ps.setNull(i, java.sql.Types.VARCHAR);
2080         } else {
2081             ps.setString(i, value.value());
2082         }
2083     }
2084 
2085     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AttackVectorType value) throws SQLException {
2086         if (value == null) {
2087             ps.setNull(i, java.sql.Types.VARCHAR);
2088         } else {
2089             ps.setString(i, value.value());
2090         }
2091     }
2092 
2093     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AttackComplexityType value) throws SQLException  {
2094         if (value == null) {
2095             ps.setNull(i, java.sql.Types.VARCHAR);
2096         } else {
2097             ps.setString(i, value.value());
2098         }
2099     }
2100 
2101     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AttackRequirementsType value) throws SQLException  {
2102         if (value == null) {
2103             ps.setNull(i, java.sql.Types.VARCHAR);
2104         } else {
2105             ps.setString(i, value.value());
2106         }
2107     }
2108 
2109     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.PrivilegesRequiredType value) throws SQLException  {
2110         if (value == null) {
2111             ps.setNull(i, java.sql.Types.VARCHAR);
2112         } else {
2113             ps.setString(i, value.value());
2114         }
2115     }
2116 
2117     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.UserInteractionType value)  throws SQLException {
2118         if (value == null) {
2119             ps.setNull(i, java.sql.Types.VARCHAR);
2120         } else {
2121             ps.setString(i, value.value());
2122         }
2123     }
2124 
2125     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.CiaType value)  throws SQLException {
2126         if (value == null) {
2127             ps.setNull(i, java.sql.Types.VARCHAR);
2128         } else {
2129             ps.setString(i, value.value());
2130         }
2131     }
2132 
2133     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ExploitMaturityType value)  throws SQLException {
2134         if (value == null) {
2135             ps.setNull(i, java.sql.Types.VARCHAR);
2136         } else {
2137             ps.setString(i, value.value());
2138         }
2139     }
2140 
2141     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.CiaRequirementType value) throws SQLException  {
2142         if (value == null) {
2143             ps.setNull(i, java.sql.Types.VARCHAR);
2144         } else {
2145             ps.setString(i, value.value());
2146         }
2147     }
2148 
2149     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedAttackVectorType value) throws SQLException  {
2150         if (value == null) {
2151             ps.setNull(i, java.sql.Types.VARCHAR);
2152         } else {
2153             ps.setString(i, value.value());
2154         }
2155     }
2156 
2157     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedAttackComplexityType value) throws SQLException  {
2158         if (value == null) {
2159             ps.setNull(i, java.sql.Types.VARCHAR);
2160         } else {
2161             ps.setString(i, value.value());
2162         }
2163     }
2164 
2165     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedAttackRequirementsType value)  throws SQLException {
2166         if (value == null) {
2167             ps.setNull(i, java.sql.Types.VARCHAR);
2168         } else {
2169             ps.setString(i, value.value());
2170         }
2171     }
2172 
2173     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedPrivilegesRequiredType value)  throws SQLException {
2174         if (value == null) {
2175             ps.setNull(i, java.sql.Types.VARCHAR);
2176         } else {
2177             ps.setString(i, value.value());
2178         }
2179     }
2180 
2181     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedUserInteractionType value)  throws SQLException {
2182         if (value == null) {
2183             ps.setNull(i, java.sql.Types.VARCHAR);
2184         } else {
2185             ps.setString(i, value.value());
2186         }
2187     }
2188 
2189     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedCiaType value) throws SQLException  {
2190         if (value == null) {
2191             ps.setNull(i, java.sql.Types.VARCHAR);
2192         } else {
2193             ps.setString(i, value.value());
2194         }
2195     }
2196 
2197     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedSubCType value) throws SQLException  {
2198         if (value == null) {
2199             ps.setNull(i, java.sql.Types.VARCHAR);
2200         } else {
2201             ps.setString(i, value.value());
2202         }
2203     }
2204 
2205     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ModifiedSubIaType value) throws SQLException  {
2206         if (value == null) {
2207             ps.setNull(i, java.sql.Types.VARCHAR);
2208         } else {
2209             ps.setString(i, value.value());
2210         }
2211     }
2212 
2213     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.SafetyType value) throws SQLException  {
2214         if (value == null) {
2215             ps.setNull(i, java.sql.Types.VARCHAR);
2216         } else {
2217             ps.setString(i, value.value());
2218         }
2219     }
2220 
2221     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.AutomatableType value) throws SQLException  {
2222         if (value == null) {
2223             ps.setNull(i, java.sql.Types.VARCHAR);
2224         } else {
2225             ps.setString(i, value.value());
2226         }
2227     }
2228 
2229     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.RecoveryType value)  throws SQLException {
2230         if (value == null) {
2231             ps.setNull(i, java.sql.Types.VARCHAR);
2232         } else {
2233             ps.setString(i, value.value());
2234         }
2235     }
2236 
2237     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ValueDensityType value) throws SQLException  {
2238         if (value == null) {
2239             ps.setNull(i, java.sql.Types.VARCHAR);
2240         } else {
2241             ps.setString(i, value.value());
2242         }
2243     }
2244 
2245     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.VulnerabilityResponseEffortType value) throws SQLException  {
2246         if (value == null) {
2247             ps.setNull(i, java.sql.Types.VARCHAR);
2248         } else {
2249             ps.setString(i, value.value());
2250         }
2251     }
2252 
2253     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.ProviderUrgencyType value) throws SQLException  {
2254         if (value == null) {
2255             ps.setNull(i, java.sql.Types.VARCHAR);
2256         } else {
2257             ps.setString(i, value.value());
2258         }
2259     }
2260 
2261     private void setUpdateColumn(PreparedStatement ps, int i, CvssV4Data.SeverityType value) throws SQLException  {
2262         if (value == null) {
2263             ps.setNull(i, java.sql.Types.VARCHAR);
2264         } else {
2265             ps.setString(i, value.value());
2266         }
2267     }
2268 
2269     /**
2270      * Sets the float parameter on a prepared statement from a properties map.
2271      *
2272      * @param ps a prepared statement
2273      * @param i the index of the property
2274      * @param props the property collection
2275      * @param key the property key
2276      * @throws SQLException thrown if there is an error adding the property
2277      */
2278     private void setFloatValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
2279         if (props != null && props.containsKey(key)) {
2280             try {
2281                 ps.setFloat(i, Float.parseFloat(props.get(key).toString()));
2282             } catch (NumberFormatException nfe) {
2283                 ps.setNull(i, java.sql.Types.FLOAT);
2284             }
2285         } else {
2286             ps.setNull(i, java.sql.Types.FLOAT);
2287         }
2288     }
2289 
2290     /**
2291      * Sets the string parameter on a prepared statement from a properties map.
2292      *
2293      * @param ps a prepared statement
2294      * @param i the index of the property
2295      * @param props the property collection
2296      * @param key the property key
2297      * @throws SQLException thrown if there is an error adding the property
2298      */
2299     private void setStringValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
2300         if (props != null && props.containsKey(key)) {
2301             ps.setString(i, props.get(key).toString());
2302         } else {
2303             ps.setNull(i, java.sql.Types.VARCHAR);
2304         }
2305     }
2306 
2307     /**
2308      * Sets the boolean parameter on a prepared statement from a properties map.
2309      *
2310      * @param ps a prepared statement
2311      * @param i the index of the property
2312      * @param props the property collection
2313      * @param key the property key
2314      * @throws SQLException thrown if there is an error adding the property
2315      */
2316     private void setBooleanValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
2317         if (props != null && props.containsKey(key)) {
2318             ps.setBoolean(i, Boolean.parseBoolean(props.get(key).toString()));
2319         } else {
2320             ps.setNull(i, java.sql.Types.BOOLEAN);
2321         }
2322     }
2323 
2324     /**
2325      * Returns the Boolean value for the given index; if the value is null then
2326      * null is returned.
2327      *
2328      * @param rs the record set
2329      * @param index the parameter index
2330      * @return the Boolean value; or null
2331      * @throws SQLException thrown if there is an error obtaining the value
2332      */
2333     @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL")
2334     private Boolean getBooleanValue(ResultSet rs, int index) throws SQLException {
2335         if (rs.getObject(index) == null) {
2336             return null;
2337         }
2338         return rs.getBoolean(index);
2339     }
2340 
2341     /**
2342      * Returns the Float value for the given index; if the value is null then
2343      * null is returned.
2344      *
2345      * @param rs the record set
2346      * @param index the parameter index
2347      * @return the Float value; or null
2348      * @throws SQLException thrown if there is an error obtaining the value
2349      */
2350     private Float getFloatValue(ResultSet rs, int index) throws SQLException {
2351         if (rs.getObject(index) == null) {
2352             return null;
2353         }
2354         return rs.getFloat(index);
2355     }
2356 }