1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.data.nvdcve;
19
20
21 import com.google.common.io.Resources;
22 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23 import io.github.jeremylong.openvulnerability.client.nvd.Config;
24 import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch;
25 import org.apache.commons.collections.map.ReferenceMap;
26 import org.owasp.dependencycheck.dependency.Vulnerability;
27 import org.owasp.dependencycheck.dependency.VulnerableSoftware;
28 import org.owasp.dependencycheck.utils.*;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import javax.annotation.concurrent.ThreadSafe;
33 import java.io.IOException;
34 import java.net.URL;
35 import java.nio.charset.StandardCharsets;
36 import java.sql.CallableStatement;
37 import java.sql.Connection;
38 import java.sql.JDBCType;
39 import java.sql.PreparedStatement;
40 import java.sql.ResultSet;
41 import java.sql.SQLException;
42 import java.sql.Statement;
43 import java.util.*;
44 import java.util.stream.Collectors;
45 import org.anarres.jdiagnostics.DefaultQuery;
46
47 import static org.apache.commons.collections.map.AbstractReferenceMap.HARD;
48 import static org.apache.commons.collections.map.AbstractReferenceMap.SOFT;
49 import org.owasp.dependencycheck.analyzer.exception.LambdaExceptionWrapper;
50 import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException;
51 import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem;
52 import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*;
53 import org.owasp.dependencycheck.data.update.cpe.CpeEcosystemCache;
54 import org.owasp.dependencycheck.data.update.cpe.CpePlus;
55 import io.github.jeremylong.openvulnerability.client.nvd.CvssV2;
56 import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data;
57 import io.github.jeremylong.openvulnerability.client.nvd.CvssV3;
58 import io.github.jeremylong.openvulnerability.client.nvd.CvssV3Data;
59 import io.github.jeremylong.openvulnerability.client.nvd.LangString;
60 import io.github.jeremylong.openvulnerability.client.nvd.Node;
61 import io.github.jeremylong.openvulnerability.client.nvd.Reference;
62 import io.github.jeremylong.openvulnerability.client.nvd.Weakness;
63 import org.owasp.dependencycheck.dependency.VulnerableSoftwareBuilder;
64 import us.springett.parsers.cpe.Cpe;
65 import us.springett.parsers.cpe.CpeBuilder;
66 import us.springett.parsers.cpe.CpeParser;
67 import us.springett.parsers.cpe.exceptions.CpeParsingException;
68 import us.springett.parsers.cpe.exceptions.CpeValidationException;
69
70
71
72
73
74
75
76
77 @ThreadSafe
78 public final class CveDB implements AutoCloseable {
79
80
81
82
83 private static final Logger LOGGER = LoggerFactory.getLogger(CveDB.class);
84
85
86
87
88 public static final String DB_ECOSYSTEM_CACHE = "data/dbEcosystemCacheUpdates.sql";
89
90
91
92
93 private final DatabaseManager databaseManager;
94
95
96
97
98 private ResourceBundle statementBundle;
99
100
101
102
103 private DatabaseProperties databaseProperties;
104
105
106
107
108 private final String cpeStartsWithFilter;
109
110
111
112 @SuppressWarnings("unchecked")
113 private final Map<String, List<Vulnerability>> vulnerabilitiesForCpeCache = Collections.synchronizedMap(new ReferenceMap(HARD, SOFT));
114
115
116
117 private final Settings settings;
118
119
120
121
122
123 private final CveItemOperator cveItemConverter;
124
125
126
127 private boolean isOracle = false;
128
129
130
131 private boolean isH2 = false;
132
133
134
135
136
137
138
139 public int updateEcosystemCache() {
140 LOGGER.debug("Updating the ecosystem cache");
141 int updateCount = 0;
142 try {
143 final URL url = Resources.getResource(DB_ECOSYSTEM_CACHE);
144 final List<String> sql = Resources.readLines(url, StandardCharsets.UTF_8);
145
146 try (Connection conn = databaseManager.getConnection(); Statement statement = conn.createStatement()) {
147 for (String single : sql) {
148 updateCount += statement.executeUpdate(single);
149 }
150 } catch (SQLException ex) {
151 LOGGER.debug("", ex);
152 throw new DatabaseException("Unable to update the ecosystem cache", ex);
153 }
154 } catch (IOException ex) {
155 throw new DatabaseException("Unable to update the ecosystem cache", ex);
156 } catch (LinkageError ex) {
157 LOGGER.debug(new DefaultQuery(ex).call().toString());
158 }
159 return updateCount;
160 }
161
162
163
164
165
166 enum PreparedStatementCveDb {
167
168
169
170 CLEANUP_ORPHANS,
171
172
173
174 UPDATE_ECOSYSTEM,
175
176
177
178 UPDATE_ECOSYSTEM2,
179
180
181
182 COUNT_CPE,
183
184
185
186 DELETE_VULNERABILITY,
187
188
189
190 INSERT_PROPERTY,
191
192
193
194 INSERT_CWE,
195
196
197
198 INSERT_REFERENCE,
199
200
201
202 INSERT_SOFTWARE,
203
204
205
206 MERGE_PROPERTY,
207
208
209
210 SELECT_CPE_ENTRIES,
211
212
213
214 SELECT_CVE_FROM_SOFTWARE,
215
216
217
218 SELECT_PROPERTIES,
219
220
221
222 SELECT_VULNERABILITY_CWE,
223
224
225
226 SELECT_REFERENCES,
227
228
229
230 SELECT_SOFTWARE,
231
232
233
234 SELECT_VENDOR_PRODUCT_LIST,
235
236
237
238 SELECT_VENDOR_PRODUCT_LIST_FOR_NODE,
239
240
241
242 SELECT_VULNERABILITY,
243
244
245
246 UPDATE_PROPERTY,
247
248
249
250 UPDATE_VULNERABILITY,
251
252
253
254 SELECT_CPE_ECOSYSTEM,
255
256
257
258 MERGE_CPE_ECOSYSTEM,
259
260
261
262 DELETE_UNUSED_DICT_CPE,
263
264
265
266 ADD_DICT_CPE,
267
268
269
270 SELECT_KNOWN_EXPLOITED_VULNERABILITIES,
271
272
273
274 MERGE_KNOWN_EXPLOITED
275 }
276
277
278
279
280
281
282
283
284
285 public CveDB(Settings settings) throws DatabaseException {
286 this.settings = settings;
287 this.cpeStartsWithFilter = settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:2.3:a:");
288 this.cveItemConverter = new CveItemOperator(cpeStartsWithFilter);
289 databaseManager = new DatabaseManager(settings);
290 statementBundle = databaseManager.getSqlStatements();
291 isOracle = databaseManager.isOracle();
292 isH2 = databaseManager.isH2Connection();
293 }
294
295
296
297
298 public void open() {
299 databaseManager.open();
300 databaseProperties = new DatabaseProperties(this);
301 }
302
303
304
305
306
307 @Override
308 public void close() {
309 if (isOpen()) {
310 LOGGER.debug("Closing database");
311 clearCache();
312 LOGGER.debug("Cache cleared");
313 try {
314 databaseManager.close();
315 LOGGER.debug("Connection closed");
316 } catch (Throwable ex) {
317 LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details.");
318 LOGGER.debug("", ex);
319 }
320 releaseResources();
321 LOGGER.debug("Resources released");
322 databaseManager.cleanup();
323 }
324 }
325
326
327
328
329 private void releaseResources() {
330 statementBundle = null;
331 databaseProperties = null;
332 }
333
334
335
336
337
338
339 public boolean isOpen() {
340 return databaseManager.isOpen();
341 }
342
343
344
345
346
347
348
349
350
351
352
353
354
355 private PreparedStatement getPreparedStatement(Connection connection, PreparedStatementCveDb key, String parameter)
356 throws DatabaseException, SQLException {
357 final PreparedStatement preparedStatement = getPreparedStatement(connection, key);
358 preparedStatement.setString(1, parameter);
359 return preparedStatement;
360 }
361
362
363
364
365
366
367
368
369
370
371
372
373
374 private PreparedStatement getPreparedStatement(Connection connection, PreparedStatementCveDb key, int parameter)
375 throws DatabaseException, SQLException {
376 final PreparedStatement preparedStatement = getPreparedStatement(connection, key);
377 preparedStatement.setInt(1, parameter);
378 return preparedStatement;
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392 private PreparedStatement getPreparedStatement(Connection connection, PreparedStatementCveDb key) throws DatabaseException {
393 PreparedStatement preparedStatement = null;
394 try {
395 final String statementString = statementBundle.getString(key.name());
396 if (isOracle && key == UPDATE_VULNERABILITY) {
397 preparedStatement = connection.prepareCall(statementString);
398
399
400
401 } else {
402 preparedStatement = connection.prepareStatement(statementString);
403 }
404 if (isOracle) {
405
406
407 preparedStatement.setFetchSize(10_000);
408 }
409 } catch (SQLException ex) {
410 throw new DatabaseException(ex);
411 } catch (MissingResourceException ex) {
412 if (!ex.getMessage().contains("key MERGE_PROPERTY")) {
413 throw new DatabaseException(ex);
414 }
415 }
416 return preparedStatement;
417 }
418
419
420
421
422
423
424 @Override
425 @SuppressWarnings("FinalizeDeclaration")
426 protected void finalize() throws Throwable {
427 LOGGER.debug("Entering finalize");
428 close();
429 super.finalize();
430 }
431
432
433
434
435
436
437 public DatabaseProperties getDatabaseProperties() {
438 return databaseProperties;
439 }
440
441
442
443
444
445
446 DatabaseProperties reloadProperties() {
447 databaseProperties = new DatabaseProperties(this);
448 return databaseProperties;
449 }
450
451
452
453
454
455
456
457
458
459
460
461 public Set<CpePlus> getCPEs(String vendor, String product) {
462 final Set<CpePlus> cpe = new HashSet<>();
463 try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ENTRIES)) {
464
465
466 ps.setString(1, vendor);
467 ps.setString(2, product);
468 try (ResultSet rs = ps.executeQuery()) {
469 final CpeBuilder builder = new CpeBuilder();
470 while (rs.next()) {
471 final Cpe entry = builder
472 .part(rs.getString(1))
473 .vendor(rs.getString(2))
474 .product(rs.getString(3))
475 .version(rs.getString(4))
476 .update(rs.getString(5))
477 .edition(rs.getString(6))
478 .language(rs.getString(7))
479 .swEdition(rs.getString(8))
480 .targetSw(rs.getString(9))
481 .targetHw(rs.getString(10))
482 .other(rs.getString(11)).build();
483 final CpePlus plus = new CpePlus(entry, rs.getString(12));
484 cpe.add(plus);
485 }
486 }
487 } catch (SQLException | CpeParsingException | CpeValidationException ex) {
488 LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
489 LOGGER.debug("", ex);
490 }
491 return cpe;
492 }
493
494
495
496
497
498
499
500
501 public Set<Pair<String, String>> getVendorProductList() throws DatabaseException {
502 final Set<Pair<String, String>> data = new HashSet<>();
503 try (Connection conn = databaseManager.getConnection();
504 PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST);
505 ResultSet rs = ps.executeQuery()) {
506 while (rs.next()) {
507 data.add(new Pair<>(rs.getString(1), rs.getString(2)));
508 }
509 } catch (SQLException ex) {
510 final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
511 throw new DatabaseException(msg, ex);
512 }
513 return data;
514 }
515
516
517
518
519
520
521
522
523
524
525 public Set<Pair<String, String>> getVendorProductListForNode() throws DatabaseException {
526 final Set<Pair<String, String>> data = new HashSet<>();
527 try (Connection conn = databaseManager.getConnection();
528 PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST_FOR_NODE);
529 ResultSet rs = ps.executeQuery()) {
530 while (rs.next()) {
531 data.add(new Pair<>(rs.getString(1), rs.getString(2)));
532 }
533 } catch (SQLException ex) {
534 final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
535 throw new DatabaseException(msg, ex);
536 }
537 return data;
538 }
539
540
541
542
543
544
545 public Properties getProperties() {
546 final Properties prop = new Properties();
547 try (Connection conn = databaseManager.getConnection();
548 PreparedStatement ps = getPreparedStatement(conn, SELECT_PROPERTIES);
549 ResultSet rs = ps.executeQuery()) {
550 while (rs.next()) {
551 prop.setProperty(rs.getString(1), rs.getString(2));
552 }
553 } catch (SQLException ex) {
554 LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
555 LOGGER.debug("", ex);
556 }
557 return prop;
558 }
559
560
561
562
563
564
565
566 public void saveProperty(String key, String value) {
567 clearCache();
568 try (Connection conn = databaseManager.getConnection(); PreparedStatement mergeProperty = getPreparedStatement(conn, MERGE_PROPERTY)) {
569 if (mergeProperty != null) {
570 mergeProperty.setString(1, key);
571 mergeProperty.setString(2, value);
572 mergeProperty.execute();
573 } else {
574
575 try (PreparedStatement updateProperty = getPreparedStatement(conn, UPDATE_PROPERTY)) {
576 updateProperty.setString(1, value);
577 updateProperty.setString(2, key);
578 if (updateProperty.executeUpdate() == 0) {
579 try (PreparedStatement insertProperty = getPreparedStatement(conn, INSERT_PROPERTY)) {
580 insertProperty.setString(1, key);
581 insertProperty.setString(2, value);
582 insertProperty.executeUpdate();
583 }
584 }
585 }
586 }
587 } catch (SQLException ex) {
588 LOGGER.warn("Unable to save property '{}' with a value of '{}' to the database", key, value);
589 LOGGER.debug("", ex);
590 }
591 }
592
593
594
595
596
597
598
599
600
601
602 private void clearCache() {
603 vulnerabilitiesForCpeCache.clear();
604 }
605
606
607
608
609
610
611
612
613 public List<Vulnerability> getVulnerabilities(Cpe cpe) throws DatabaseException {
614 final List<Vulnerability> cachedVulnerabilities = vulnerabilitiesForCpeCache.get(cpe.toCpe23FS());
615 if (cachedVulnerabilities != null) {
616 LOGGER.debug("Cache hit for {}", cpe.toCpe23FS());
617 return cachedVulnerabilities;
618 } else {
619 LOGGER.debug("Cache miss for {}", cpe.toCpe23FS());
620 }
621
622 final List<Vulnerability> vulnerabilities = new ArrayList<>();
623 try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_CVE_FROM_SOFTWARE)) {
624 ps.setString(1, cpe.getVendor());
625 ps.setString(2, cpe.getProduct());
626 try (ResultSet rs = ps.executeQuery()) {
627 String currentCVE = "";
628 final Set<VulnerableSoftware> vulnSoftware = new HashSet<>();
629 final VulnerableSoftwareBuilder vulnerableSoftwareBuilder = new VulnerableSoftwareBuilder();
630 while (rs.next()) {
631 final String cveId = rs.getString(1);
632 if (currentCVE.isEmpty()) {
633
634 currentCVE = cveId;
635 }
636 if (!vulnSoftware.isEmpty() && !currentCVE.equals(cveId)) {
637 final VulnerableSoftware matchedCPE = getMatchingSoftware(cpe, vulnSoftware);
638 if (matchedCPE != null) {
639 final Vulnerability v = getVulnerability(currentCVE, conn);
640 if (v != null) {
641 v.setMatchedVulnerableSoftware(matchedCPE);
642 v.setSource(Vulnerability.Source.NVD);
643 vulnerabilities.add(v);
644 }
645 }
646 vulnSoftware.clear();
647 currentCVE = cveId;
648 }
649
650
651
652 final VulnerableSoftware vs;
653 try {
654 vs = vulnerableSoftwareBuilder.part(rs.getString(2)).vendor(rs.getString(3))
655 .product(rs.getString(4)).version(rs.getString(5)).update(rs.getString(6))
656 .edition(rs.getString(7)).language(rs.getString(8)).swEdition(rs.getString(9))
657 .targetSw(rs.getString(10)).targetHw(rs.getString(11)).other(rs.getString(12))
658 .versionEndExcluding(rs.getString(13)).versionEndIncluding(rs.getString(14))
659 .versionStartExcluding(rs.getString(15)).versionStartIncluding(rs.getString(16))
660 .vulnerable(rs.getBoolean(17)).build();
661 } catch (CpeParsingException | CpeValidationException ex) {
662 throw new DatabaseException("Database contains an invalid Vulnerable Software Entry", ex);
663 }
664 vulnSoftware.add(vs);
665 }
666
667
668 final VulnerableSoftware matchedCPE = getMatchingSoftware(cpe, vulnSoftware);
669 if (matchedCPE != null) {
670 final Vulnerability v = getVulnerability(currentCVE, conn);
671 if (v != null) {
672 v.setMatchedVulnerableSoftware(matchedCPE);
673 v.setSource(Vulnerability.Source.NVD);
674 vulnerabilities.add(v);
675 }
676 }
677 }
678 } catch (SQLException ex) {
679 throw new DatabaseException("Exception retrieving vulnerability for " + cpe.toCpe23FS(), ex);
680 }
681 vulnerabilitiesForCpeCache.put(cpe.toCpe23FS(), vulnerabilities);
682 return vulnerabilities;
683 }
684
685
686
687
688
689
690
691
692 public Vulnerability getVulnerability(String cve) throws DatabaseException {
693 try (Connection conn = databaseManager.getConnection()) {
694 return getVulnerability(cve, conn);
695 } catch (SQLException ex) {
696 throw new DatabaseException("Error retrieving " + cve, ex);
697 }
698 }
699
700
701
702
703
704
705
706
707
708 public Vulnerability getVulnerability(String cve, Connection conn) throws DatabaseException {
709 final int cveId;
710 final VulnerableSoftwareBuilder vulnerableSoftwareBuilder = new VulnerableSoftwareBuilder();
711 Vulnerability vuln = null;
712 try {
713 try (PreparedStatement psV = getPreparedStatement(conn, SELECT_VULNERABILITY, cve); ResultSet rsV = psV.executeQuery()) {
714 if (rsV.next()) {
715
716 cveId = rsV.getInt(1);
717 vuln = new Vulnerability();
718 vuln.setSource(Vulnerability.Source.NVD);
719 vuln.setName(cve);
720 vuln.setDescription(rsV.getString(2));
721
722
723
724
725
726 if (rsV.getObject(11) != null) {
727
728 final CvssV2Data.AccessVectorType accessVector = CvssV2Data.AccessVectorType.fromValue(rsV.getString(12));
729 final CvssV2Data.AccessComplexityType accessComplexity = CvssV2Data.AccessComplexityType.fromValue(rsV.getString(13));
730 final CvssV2Data.AuthenticationType authentication = CvssV2Data.AuthenticationType.fromValue(rsV.getString(14));
731 final CvssV2Data.CiaType confidentialityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(15));
732 final CvssV2Data.CiaType integrityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(16));
733 final CvssV2Data.CiaType availabilityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(17));
734 final String vector = String.format("/AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s",
735 accessVector == null ? "" : accessVector.value().substring(0, 1),
736 accessComplexity == null ? "" : accessComplexity.value().substring(0, 1),
737 authentication == null ? "" : authentication.value().substring(0, 1),
738 confidentialityImpact == null ? "" : confidentialityImpact.value().substring(0, 1),
739 integrityImpact == null ? "" : integrityImpact.value().substring(0, 1),
740 availabilityImpact == null ? "" : availabilityImpact.value().substring(0, 1));
741
742
743 String cveVersion = "2.0";
744 if (rsV.getString(18) != null) {
745 cveVersion = rsV.getString(18);
746 }
747 final CvssV2Data cvssData = new CvssV2Data(cveVersion, vector, accessVector,
748 accessComplexity, authentication, confidentialityImpact,
749 integrityImpact, availabilityImpact, rsV.getDouble(11), rsV.getString(3),
750 null, null, null, null, null, null, null, null, null, null);
751 final CvssV2 cvss = new CvssV2(null, CvssV2.Type.PRIMARY, cvssData, rsV.getString(3),
752 rsV.getDouble(4), rsV.getDouble(5), rsV.getBoolean(6), rsV.getBoolean(7),
753 rsV.getBoolean(8), rsV.getBoolean(9), rsV.getBoolean(10));
754 vuln.setCvssV2(cvss);
755 }
756
757
758
759 if (rsV.getObject(21) != null) {
760
761 String cveVersion = "3.1";
762 if (rsV.getString(31) != null) {
763 cveVersion = rsV.getString(31);
764 }
765 final CvssV3Data.Version version = CvssV3Data.Version.fromValue(cveVersion);
766 final CvssV3Data.AttackVectorType attackVector = CvssV3Data.AttackVectorType.fromValue(rsV.getString(21));
767 final CvssV3Data.AttackComplexityType attackComplexity = CvssV3Data.AttackComplexityType.fromValue(rsV.getString(22));
768 final CvssV3Data.PrivilegesRequiredType privilegesRequired = CvssV3Data.PrivilegesRequiredType.fromValue(rsV.getString(23));
769 final CvssV3Data.UserInteractionType userInteraction = CvssV3Data.UserInteractionType.fromValue(rsV.getString(24));
770 final CvssV3Data.ScopeType scope = CvssV3Data.ScopeType.fromValue(rsV.getString(25));
771 final CvssV3Data.CiaType confidentialityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(26));
772 final CvssV3Data.CiaType integrityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(27));
773 final CvssV3Data.CiaType availabilityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(28));
774 final CvssV3Data.SeverityType baseSeverity = CvssV3Data.SeverityType.fromValue(rsV.getString(30));
775 final String vector = String.format("CVSS:%s/AV:%s/AC:%s/PR:%s/UI:%s/S:%s/C:%s/I:%s/A:%s",
776 version == null ? "" : version,
777 attackVector == null ? "" : attackVector.value().substring(0, 1),
778 attackComplexity == null ? "" : attackComplexity.value().substring(0, 1),
779 privilegesRequired == null ? "" : privilegesRequired.value().substring(0, 1),
780 userInteraction == null ? "" : userInteraction.value().substring(0, 1),
781 scope == null ? "" : scope.value().substring(0, 1),
782 confidentialityImpact == null ? "" : confidentialityImpact.value().substring(0, 1),
783 integrityImpact == null ? "" : integrityImpact.value().substring(0, 1),
784 availabilityImpact == null ? "" : availabilityImpact.value().substring(0, 1));
785
786 final CvssV3Data cvssData = new CvssV3Data(version, vector, attackVector, attackComplexity, privilegesRequired,
787 userInteraction, scope, confidentialityImpact, integrityImpact, availabilityImpact,
788 rsV.getDouble(29), baseSeverity, CvssV3Data.ExploitCodeMaturityType.PROOF_OF_CONCEPT,
789 CvssV3Data.RemediationLevelType.NOT_DEFINED, CvssV3Data.ConfidenceType.REASONABLE, 0.0,
790 CvssV3Data.SeverityType.MEDIUM, CvssV3Data.CiaRequirementType.NOT_DEFINED,
791 CvssV3Data.CiaRequirementType.NOT_DEFINED, CvssV3Data.CiaRequirementType.NOT_DEFINED,
792 CvssV3Data.ModifiedAttackVectorType.ADJACENT_NETWORK, CvssV3Data.ModifiedAttackComplexityType.NOT_DEFINED,
793 CvssV3Data.ModifiedPrivilegesRequiredType.NOT_DEFINED, CvssV3Data.ModifiedUserInteractionType.NOT_DEFINED,
794 CvssV3Data.ModifiedScopeType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED,
795 CvssV3Data.ModifiedCiaType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED, 1.0,
796 CvssV3Data.SeverityType.NONE);
797 final CvssV3 cvss = new CvssV3(null, null, cvssData, rsV.getDouble(19), rsV.getDouble(20));
798 vuln.setCvssV3(cvss);
799 }
800 } else {
801 LOGGER.debug(cve + " does not exist in the database");
802 return null;
803 }
804 }
805 try (PreparedStatement psCWE = getPreparedStatement(conn, SELECT_VULNERABILITY_CWE, cveId); ResultSet rsC = psCWE.executeQuery()) {
806 while (rsC.next()) {
807 vuln.addCwe(rsC.getString(1));
808 }
809 }
810 try (PreparedStatement psR = getPreparedStatement(conn, SELECT_REFERENCES, cveId); ResultSet rsR = psR.executeQuery()) {
811 while (rsR.next()) {
812 vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3));
813 }
814 }
815 try (PreparedStatement psS = getPreparedStatement(conn, SELECT_SOFTWARE, cveId); ResultSet rsS = psS.executeQuery()) {
816
817
818
819 while (rsS.next()) {
820 vulnerableSoftwareBuilder.part(rsS.getString(1))
821 .vendor(rsS.getString(2))
822 .product(rsS.getString(3))
823 .version(rsS.getString(4))
824 .update(rsS.getString(5))
825 .edition(rsS.getString(6))
826 .language(rsS.getString(7))
827 .swEdition(rsS.getString(8))
828 .targetSw(rsS.getString(9))
829 .targetHw(rsS.getString(10))
830 .other(rsS.getString(11))
831 .versionEndExcluding(rsS.getString(12))
832 .versionEndIncluding(rsS.getString(13))
833 .versionStartExcluding(rsS.getString(14))
834 .versionStartIncluding(rsS.getString(15))
835 .vulnerable(rsS.getBoolean(16));
836 vuln.addVulnerableSoftware(vulnerableSoftwareBuilder.build());
837 }
838 }
839 } catch (SQLException ex) {
840 throw new DatabaseException("Error retrieving " + cve, ex);
841 } catch (CpeParsingException | CpeValidationException ex) {
842 throw new DatabaseException("The database contains an invalid Vulnerable Software Entry", ex);
843 }
844 return vuln;
845 }
846
847
848
849
850
851
852
853
854
855
856
857 public void updateVulnerability(DefCveItem cve, String baseEcosystem) {
858 clearCache();
859 final String cveId = cve.getCve().getId();
860 try {
861 if (cve.getCve().getVulnStatus().toUpperCase().startsWith("REJECT")) {
862 deleteVulnerability(cveId);
863 } else {
864 if (cveItemConverter.testCveCpeStartWithFilter(cve)) {
865 final String description = cveItemConverter.extractDescription(cve);
866 final int vulnerabilityId = updateOrInsertVulnerability(cve, description);
867 updateVulnerabilityInsertCwe(vulnerabilityId, cve);
868 updateVulnerabilityInsertReferences(vulnerabilityId, cve);
869
870 final List<VulnerableSoftware> software = parseCpes(cve);
871 updateVulnerabilityInsertSoftware(vulnerabilityId, cveId, software, baseEcosystem);
872 }
873 }
874 } catch (SQLException ex) {
875 final String msg = String.format("Error updating '%s'; %s", cveId, ex.getMessage());
876 LOGGER.debug(msg, ex);
877 throw new DatabaseException(msg);
878 } catch (CpeValidationException ex) {
879 final String msg = String.format("Error parsing CPE entry from '%s'; %s", cveId, ex.getMessage());
880 LOGGER.debug(msg, ex);
881 throw new DatabaseException(msg);
882 }
883 }
884
885 private void loadCpeEcosystemCache() {
886 final Map<Pair<String, String>, String> map = new HashMap<>();
887 try (Connection conn = databaseManager.getConnection();
888 PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ECOSYSTEM);
889 ResultSet rs = ps.executeQuery()) {
890 while (rs.next()) {
891 final Pair<String, String> key = new Pair<>(rs.getString(1), rs.getString(2));
892 final String value = rs.getString(3);
893 map.put(key, value);
894 }
895 } catch (SQLException ex) {
896 final String msg = String.format("Error loading the Cpe Ecosystem Cache: %s", ex.getMessage());
897 LOGGER.debug(msg, ex);
898 throw new DatabaseException(msg, ex);
899 }
900 CpeEcosystemCache.setCache(map);
901 }
902
903 private void saveCpeEcosystemCache() {
904 final Map<Pair<String, String>, String> map = CpeEcosystemCache.getChanged();
905 if (map != null && !map.isEmpty()) {
906 try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, MERGE_CPE_ECOSYSTEM)) {
907 for (Map.Entry<Pair<String, String>, String> entry : map.entrySet()) {
908 ps.setString(1, entry.getKey().getLeft());
909 ps.setString(2, entry.getKey().getRight());
910 ps.setString(3, entry.getValue());
911 if (isBatchInsertEnabled()) {
912 ps.addBatch();
913 } else {
914 ps.execute();
915 }
916 }
917 if (isBatchInsertEnabled()) {
918 ps.executeBatch();
919 }
920 } catch (SQLException ex) {
921 final String msg = String.format("Error saving the Cpe Ecosystem Cache: %s", ex.getMessage());
922 LOGGER.debug(msg, ex);
923 throw new DatabaseException(msg, ex);
924 }
925 }
926 }
927
928
929
930
931
932
933
934
935
936 private int updateOrInsertVulnerability(DefCveItem cve, String description) {
937 if (CpeEcosystemCache.isEmpty()) {
938 loadCpeEcosystemCache();
939 }
940 final int vulnerabilityId;
941 try (Connection conn = databaseManager.getConnection(); PreparedStatement callUpdate = getPreparedStatement(conn, UPDATE_VULNERABILITY)) {
942
943
944
945
946
947
948
949
950
951
952 callUpdate.setString(1, cve.getCve().getId());
953 callUpdate.setString(2, description);
954 Optional<CvssV2> optCvssv2 = null;
955 if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV2() != null) {
956 optCvssv2 = cve.getCve().getMetrics().getCvssMetricV2().stream().sorted(Comparator.comparing(CvssV2::getType)).findFirst();
957 }
958 if (optCvssv2 != null && optCvssv2.isPresent()) {
959 final CvssV2 cvssv2 = optCvssv2.get();
960 setUpdateColumn(callUpdate, 3, cvssv2.getBaseSeverity());
961 setUpdateColumn(callUpdate, 4, cvssv2.getExploitabilityScore());
962 setUpdateColumn(callUpdate, 5, cvssv2.getImpactScore());
963 setUpdateColumn(callUpdate, 6, cvssv2.getAcInsufInfo());
964 setUpdateColumn(callUpdate, 7, cvssv2.getObtainAllPrivilege());
965 setUpdateColumn(callUpdate, 8, cvssv2.getObtainUserPrivilege());
966 setUpdateColumn(callUpdate, 9, cvssv2.getObtainOtherPrivilege());
967 setUpdateColumn(callUpdate, 10, cvssv2.getUserInteractionRequired());
968 setUpdateColumn(callUpdate, 11, cvssv2.getCvssData().getBaseScore());
969 setUpdateColumn(callUpdate, 12, cvssv2.getCvssData().getAccessVector());
970 setUpdateColumn(callUpdate, 13, cvssv2.getCvssData().getAccessComplexity());
971 setUpdateColumn(callUpdate, 14, cvssv2.getCvssData().getAuthentication());
972 setUpdateColumn(callUpdate, 15, cvssv2.getCvssData().getConfidentialityImpact());
973 setUpdateColumn(callUpdate, 16, cvssv2.getCvssData().getIntegrityImpact());
974 setUpdateColumn(callUpdate, 17, cvssv2.getCvssData().getAvailabilityImpact());
975 setUpdateColumn(callUpdate, 18, cvssv2.getCvssData().getVersion());
976 } else {
977 callUpdate.setNull(3, java.sql.Types.VARCHAR);
978 callUpdate.setNull(4, java.sql.Types.DOUBLE);
979 callUpdate.setNull(5, java.sql.Types.DOUBLE);
980 callUpdate.setNull(6, java.sql.Types.VARCHAR);
981
982
983 if (isOracle) {
984 callUpdate.setNull(7, java.sql.Types.BIT);
985 callUpdate.setNull(8, java.sql.Types.BIT);
986 callUpdate.setNull(9, java.sql.Types.BIT);
987 callUpdate.setNull(10, java.sql.Types.BIT);
988 } else {
989 callUpdate.setNull(7, java.sql.Types.BOOLEAN);
990 callUpdate.setNull(8, java.sql.Types.BOOLEAN);
991 callUpdate.setNull(9, java.sql.Types.BOOLEAN);
992 callUpdate.setNull(10, java.sql.Types.BOOLEAN);
993 }
994 callUpdate.setNull(11, java.sql.Types.DOUBLE);
995 callUpdate.setNull(12, java.sql.Types.VARCHAR);
996 callUpdate.setNull(13, java.sql.Types.VARCHAR);
997 callUpdate.setNull(14, java.sql.Types.VARCHAR);
998 callUpdate.setNull(15, java.sql.Types.VARCHAR);
999 callUpdate.setNull(16, java.sql.Types.VARCHAR);
1000 callUpdate.setNull(17, java.sql.Types.VARCHAR);
1001 callUpdate.setNull(18, java.sql.Types.VARCHAR);
1002 }
1003 Optional<CvssV3> optCvssv30 = Optional.empty();
1004 if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV30() != null) {
1005 optCvssv30 = cve.getCve().getMetrics().getCvssMetricV30().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst();
1006 }
1007 Optional<CvssV3> optCvssv31 = Optional.empty();
1008 if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV31() != null) {
1009 optCvssv31 = cve.getCve().getMetrics().getCvssMetricV31().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst();
1010 }
1011
1012 CvssV3 cvssv3 = null;
1013 if (optCvssv31.isPresent()) {
1014 cvssv3 = optCvssv31.get();
1015 } else if (optCvssv30.isPresent()) {
1016 cvssv3 = optCvssv30.get();
1017 }
1018 if (cvssv3 != null) {
1019 setUpdateColumn(callUpdate, 19, cvssv3.getExploitabilityScore());
1020 setUpdateColumn(callUpdate, 20, cvssv3.getImpactScore());
1021 setUpdateColumn(callUpdate, 21, cvssv3.getCvssData().getAttackVector());
1022 setUpdateColumn(callUpdate, 22, cvssv3.getCvssData().getAttackComplexity());
1023 setUpdateColumn(callUpdate, 23, cvssv3.getCvssData().getPrivilegesRequired());
1024 setUpdateColumn(callUpdate, 24, cvssv3.getCvssData().getUserInteraction());
1025 setUpdateColumn(callUpdate, 25, cvssv3.getCvssData().getScope());
1026 setUpdateColumn(callUpdate, 26, cvssv3.getCvssData().getConfidentialityImpact());
1027 setUpdateColumn(callUpdate, 27, cvssv3.getCvssData().getIntegrityImpact());
1028 setUpdateColumn(callUpdate, 28, cvssv3.getCvssData().getAvailabilityImpact());
1029 setUpdateColumn(callUpdate, 29, cvssv3.getCvssData().getBaseScore());
1030 setUpdateColumn(callUpdate, 30, cvssv3.getCvssData().getBaseSeverity());
1031 setUpdateColumn(callUpdate, 31, cvssv3.getCvssData().getVersion());
1032 } else {
1033 callUpdate.setNull(19, java.sql.Types.DOUBLE);
1034 callUpdate.setNull(20, java.sql.Types.DOUBLE);
1035 callUpdate.setNull(21, java.sql.Types.VARCHAR);
1036 callUpdate.setNull(22, java.sql.Types.VARCHAR);
1037 callUpdate.setNull(23, java.sql.Types.VARCHAR);
1038 callUpdate.setNull(24, java.sql.Types.VARCHAR);
1039 callUpdate.setNull(25, java.sql.Types.VARCHAR);
1040 callUpdate.setNull(26, java.sql.Types.VARCHAR);
1041 callUpdate.setNull(27, java.sql.Types.VARCHAR);
1042 callUpdate.setNull(28, java.sql.Types.VARCHAR);
1043 callUpdate.setNull(29, java.sql.Types.DOUBLE);
1044 callUpdate.setNull(30, java.sql.Types.VARCHAR);
1045 callUpdate.setNull(31, java.sql.Types.VARCHAR);
1046 }
1047 if (isOracle) {
1048 try {
1049 final CallableStatement cs = (CallableStatement) callUpdate;
1050 cs.registerOutParameter(32, JDBCType.INTEGER);
1051 cs.executeUpdate();
1052 vulnerabilityId = cs.getInt(32);
1053 } catch (SQLException ex) {
1054 final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId());
1055 throw new DatabaseException(msg, ex);
1056 }
1057 } else {
1058 try (ResultSet rs = callUpdate.executeQuery()) {
1059 rs.next();
1060 vulnerabilityId = rs.getInt(1);
1061 } catch (SQLException ex) {
1062 final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId());
1063 throw new DatabaseException(msg, ex);
1064 }
1065 }
1066 } catch (SQLException ex) {
1067 throw new UnexpectedAnalysisException(ex);
1068 }
1069 return vulnerabilityId;
1070 }
1071
1072
1073
1074
1075
1076
1077
1078
1079 private void updateVulnerabilityInsertCwe(int vulnerabilityId, DefCveItem cve) throws SQLException {
1080 if (cve.getCve() != null && cve.getCve().getWeaknesses() != null) {
1081 try (Connection conn = databaseManager.getConnection();
1082 PreparedStatement insertCWE = getPreparedStatement(conn, INSERT_CWE, vulnerabilityId)) {
1083 for (Weakness weakness : cve.getCve().getWeaknesses()) {
1084 for (LangString desc : weakness.getDescription()) {
1085 if ("en".equals(desc.getLang())) {
1086 insertCWE.setString(2, desc.getValue());
1087 if (isBatchInsertEnabled()) {
1088 insertCWE.addBatch();
1089 } else {
1090 insertCWE.execute();
1091 }
1092 }
1093 }
1094 }
1095 if (isBatchInsertEnabled()) {
1096 insertCWE.executeBatch();
1097 }
1098 }
1099 }
1100 }
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110 private void deleteVulnerability(String cve) throws SQLException {
1111 try (Connection conn = databaseManager.getConnection();
1112 PreparedStatement deleteVulnerability = getPreparedStatement(conn, DELETE_VULNERABILITY, cve)) {
1113 deleteVulnerability.executeUpdate();
1114 }
1115 }
1116
1117
1118
1119
1120
1121
1122
1123
1124 public void updateKnownExploitedVulnerabilities(
1125 List<org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> vulnerabilities)
1126 throws DatabaseException, SQLException {
1127 try (Connection conn = databaseManager.getConnection();
1128 PreparedStatement mergeKnownVulnerability = getPreparedStatement(conn, MERGE_KNOWN_EXPLOITED)) {
1129 int ctr = 0;
1130 for (org.owasp.dependencycheck.data.knownexploited.json.Vulnerability v : vulnerabilities) {
1131 mergeKnownVulnerability.setString(1, v.getCveID());
1132 addNullableStringParameter(mergeKnownVulnerability, 2, v.getVendorProject());
1133 addNullableStringParameter(mergeKnownVulnerability, 3, v.getProduct());
1134 addNullableStringParameter(mergeKnownVulnerability, 4, v.getVulnerabilityName());
1135 addNullableStringParameter(mergeKnownVulnerability, 5, v.getDateAdded());
1136 addNullableStringParameter(mergeKnownVulnerability, 6, v.getShortDescription());
1137 addNullableStringParameter(mergeKnownVulnerability, 7, v.getRequiredAction());
1138 addNullableStringParameter(mergeKnownVulnerability, 8, v.getDueDate());
1139 addNullableStringParameter(mergeKnownVulnerability, 9, v.getNotes());
1140 if (isBatchInsertEnabled()) {
1141 mergeKnownVulnerability.addBatch();
1142 ctr++;
1143 if (ctr >= getBatchSize()) {
1144 mergeKnownVulnerability.executeBatch();
1145 ctr = 0;
1146 }
1147 } else {
1148 try {
1149 mergeKnownVulnerability.execute();
1150 } catch (SQLException ex) {
1151 if (ex.getMessage().contains("Duplicate entry")) {
1152 final String msg = String.format("Duplicate known exploited vulnerability key identified in '%s'", v.getCveID());
1153 LOGGER.info(msg, ex);
1154 } else {
1155 throw ex;
1156 }
1157 }
1158 }
1159 }
1160 if (isBatchInsertEnabled()) {
1161 mergeKnownVulnerability.executeBatch();
1162 }
1163 }
1164 }
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178 private void updateVulnerabilityInsertSoftware(int vulnerabilityId, String cveId,
1179 List<VulnerableSoftware> software, String baseEcosystem)
1180 throws DatabaseException, SQLException {
1181 try (Connection conn = databaseManager.getConnection(); PreparedStatement insertSoftware = getPreparedStatement(conn, INSERT_SOFTWARE)) {
1182 for (VulnerableSoftware parsedCpe : software) {
1183 insertSoftware.setInt(1, vulnerabilityId);
1184 insertSoftware.setString(2, parsedCpe.getPart().getAbbreviation());
1185 insertSoftware.setString(3, parsedCpe.getVendor());
1186 insertSoftware.setString(4, parsedCpe.getProduct());
1187 insertSoftware.setString(5, parsedCpe.getVersion());
1188 insertSoftware.setString(6, parsedCpe.getUpdate());
1189 insertSoftware.setString(7, parsedCpe.getEdition());
1190 insertSoftware.setString(8, parsedCpe.getLanguage());
1191 insertSoftware.setString(9, parsedCpe.getSwEdition());
1192 insertSoftware.setString(10, parsedCpe.getTargetSw());
1193 insertSoftware.setString(11, parsedCpe.getTargetHw());
1194 insertSoftware.setString(12, parsedCpe.getOther());
1195 final String ecosystem = CpeEcosystemCache.getEcosystem(parsedCpe.getVendor(), parsedCpe.getProduct(),
1196 cveItemConverter.extractEcosystem(baseEcosystem, parsedCpe));
1197
1198 addNullableStringParameter(insertSoftware, 13, ecosystem);
1199 addNullableStringParameter(insertSoftware, 14, parsedCpe.getVersionEndExcluding());
1200 addNullableStringParameter(insertSoftware, 15, parsedCpe.getVersionEndIncluding());
1201 addNullableStringParameter(insertSoftware, 16, parsedCpe.getVersionStartExcluding());
1202 addNullableStringParameter(insertSoftware, 17, parsedCpe.getVersionStartIncluding());
1203 insertSoftware.setBoolean(18, parsedCpe.isVulnerable());
1204
1205 if (isBatchInsertEnabled()) {
1206 insertSoftware.addBatch();
1207 } else {
1208 try {
1209 insertSoftware.execute();
1210 } catch (SQLException ex) {
1211 if (ex.getMessage().contains("Duplicate entry")) {
1212 final String msg = String.format("Duplicate software key identified in '%s'", cveId);
1213 LOGGER.info(msg, ex);
1214 } else {
1215 throw ex;
1216 }
1217 }
1218 }
1219 }
1220 if (isBatchInsertEnabled()) {
1221 executeBatch(cveId, insertSoftware);
1222 }
1223 }
1224 }
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234 private void updateVulnerabilityInsertReferences(int vulnerabilityId, DefCveItem cve) throws SQLException {
1235 try (Connection conn = databaseManager.getConnection(); PreparedStatement insertReference = getPreparedStatement(conn, INSERT_REFERENCE)) {
1236 if (cve.getCve().getReferences() != null) {
1237 for (Reference r : cve.getCve().getReferences()) {
1238 insertReference.setInt(1, vulnerabilityId);
1239 String name = null;
1240 if (r.getTags() != null) {
1241 name = r.getTags().stream().sorted().collect(Collectors.joining(",")).toUpperCase().replaceAll("\\s", "_");
1242 }
1243 if (name != null) {
1244 insertReference.setString(2, name);
1245 } else {
1246 insertReference.setNull(2, java.sql.Types.VARCHAR);
1247 }
1248 if (r.getUrl() != null && !r.getUrl().isEmpty()) {
1249 insertReference.setString(3, r.getUrl());
1250 } else {
1251 insertReference.setNull(3, java.sql.Types.VARCHAR);
1252 }
1253 if (r.getSource() != null && !r.getSource().isEmpty()) {
1254 insertReference.setString(4, r.getSource());
1255 } else {
1256 insertReference.setNull(4, java.sql.Types.VARCHAR);
1257 }
1258 if (isBatchInsertEnabled()) {
1259 insertReference.addBatch();
1260 } else {
1261 insertReference.execute();
1262 }
1263 }
1264 }
1265 if (isBatchInsertEnabled()) {
1266 insertReference.executeBatch();
1267 }
1268 }
1269 }
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279 private List<VulnerableSoftware> parseCpes(DefCveItem cve) throws CpeValidationException {
1280 final List<VulnerableSoftware> software = new ArrayList<>();
1281
1282 final List<CpeMatch> cpeEntries = cve.getCve().getConfigurations().stream()
1283 .map(Config::getNodes)
1284 .flatMap(List::stream)
1285 .map(Node::getCpeMatch)
1286 .flatMap(List::stream)
1287 .filter(predicate -> predicate.getCriteria() != null)
1288 .filter(predicate -> predicate.getCriteria().startsWith(cpeStartsWithFilter))
1289
1290 .filter(entry -> !("CVE-2009-0754".equals(cve.getCve().getId())
1291 && "cpe:2.3:a:apache:apache:*:*:*:*:*:*:*:*".equals(entry.getCriteria())))
1292 .collect(Collectors.toList());
1293 final VulnerableSoftwareBuilder builder = new VulnerableSoftwareBuilder();
1294
1295 try {
1296 cpeEntries.forEach(entry -> {
1297 builder.cpe(parseCpe(entry, cve.getCve().getId()))
1298 .versionEndExcluding(entry.getVersionEndExcluding())
1299 .versionStartExcluding(entry.getVersionStartExcluding())
1300 .versionEndIncluding(entry.getVersionEndIncluding())
1301 .versionStartIncluding(entry.getVersionStartIncluding())
1302 .vulnerable(entry.getVulnerable());
1303 try {
1304 software.add(builder.build());
1305 } catch (CpeValidationException ex) {
1306 throw new LambdaExceptionWrapper(ex);
1307 }
1308 });
1309 } catch (LambdaExceptionWrapper ex) {
1310 throw (CpeValidationException) ex.getCause();
1311 }
1312 return software;
1313 }
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326 private Cpe parseCpe(CpeMatch cpe, String cveId) throws DatabaseException {
1327 final Cpe parsedCpe;
1328 try {
1329
1330 parsedCpe = CpeParser.parse(cpe.getCriteria(), true);
1331 } catch (CpeParsingException ex) {
1332 LOGGER.debug("NVD (" + cveId + ") contain an invalid 2.3 CPE: " + cpe.getCriteria());
1333 throw new DatabaseException("Unable to parse CPE: " + cpe.getCriteria(), ex);
1334 }
1335 return parsedCpe;
1336 }
1337
1338
1339
1340
1341
1342
1343 private int getBatchSize() {
1344 int max;
1345 try {
1346 max = settings.getInt(Settings.KEYS.MAX_BATCH_SIZE);
1347 } catch (InvalidSettingException pE) {
1348 max = 1000;
1349 }
1350 return max;
1351 }
1352
1353
1354
1355
1356
1357
1358
1359 private boolean isBatchInsertEnabled() {
1360 boolean batch;
1361 try {
1362 batch = settings.getBoolean(Settings.KEYS.ENABLE_BATCH_UPDATES);
1363 } catch (InvalidSettingException pE) {
1364
1365 batch = false;
1366 }
1367 return batch;
1368 }
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378 private void executeBatch(String vulnId, PreparedStatement statement)
1379 throws SQLException {
1380 try {
1381 statement.executeBatch();
1382 } catch (SQLException ex) {
1383 if (ex.getMessage().contains("Duplicate entry")) {
1384 final String msg = String.format("Duplicate software key identified in '%s'",
1385 vulnId);
1386 LOGGER.info(msg, ex);
1387 } else {
1388 throw ex;
1389 }
1390 }
1391 }
1392
1393
1394
1395
1396
1397
1398 public boolean dataExists() {
1399 try (Connection conn = databaseManager.getConnection();
1400 PreparedStatement cs = getPreparedStatement(conn, COUNT_CPE);
1401 ResultSet rs = cs.executeQuery()) {
1402 if (rs.next() && rs.getInt(1) > 0) {
1403 return true;
1404 }
1405 } catch (Exception ex) {
1406 String dd;
1407 try {
1408 dd = settings.getDataDirectory().getAbsolutePath();
1409 } catch (IOException ex1) {
1410 dd = settings.getString(Settings.KEYS.DATA_DIRECTORY);
1411 }
1412 LOGGER.error("Unable to access the local database.\n\nEnsure that '{}' is a writable directory. "
1413 + "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please "
1414 + "create a log file (see documentation at https://jeremylong.github.io/DependencyCheck/) and open a ticket at "
1415 + "https://github.com/jeremylong/DependencyCheck/issues and include the log file.\n\n",
1416 dd, dd, settings.getString(Settings.KEYS.APPLICATION_NAME));
1417 LOGGER.debug("", ex);
1418 }
1419 return false;
1420 }
1421
1422
1423
1424
1425
1426
1427 public void cleanupDatabase() {
1428 LOGGER.info("Begin database maintenance");
1429 final long start = System.currentTimeMillis();
1430 try (Connection conn = databaseManager.getConnection();
1431 PreparedStatement psOrphans = getPreparedStatement(conn, CLEANUP_ORPHANS);
1432 PreparedStatement psEcosystem = getPreparedStatement(conn, UPDATE_ECOSYSTEM);
1433 PreparedStatement psEcosystem2 = getPreparedStatement(conn, UPDATE_ECOSYSTEM2)) {
1434 if (psEcosystem != null) {
1435 final int count = psEcosystem.executeUpdate();
1436 if (count > 0) {
1437 LOGGER.info("Updated the CPE ecosystem on {} NVD records", count);
1438 }
1439 }
1440 if (psEcosystem2 != null) {
1441 final int count = psEcosystem2.executeUpdate();
1442 if (count > 0) {
1443 LOGGER.info("Removed the CPE ecosystem on {} NVD records", count);
1444 }
1445 }
1446 if (psOrphans != null) {
1447 final int count = psOrphans.executeUpdate();
1448 if (count > 0) {
1449 LOGGER.info("Cleaned up {} orphaned NVD records", count);
1450 }
1451 }
1452 final long millis = System.currentTimeMillis() - start;
1453
1454 LOGGER.info("End database maintenance ({} ms)", millis);
1455 } catch (SQLException ex) {
1456 LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
1457 LOGGER.debug("", ex);
1458 throw new DatabaseException("Unexpected SQL Exception", ex);
1459 }
1460 }
1461
1462
1463
1464
1465 public void persistEcosystemCache() {
1466 saveCpeEcosystemCache();
1467 clearCache();
1468 }
1469
1470
1471
1472
1473
1474 public void defrag() {
1475 if (isH2) {
1476 final long start = System.currentTimeMillis();
1477 try (Connection conn = databaseManager.getConnection(); CallableStatement psCompaxt = conn.prepareCall("SHUTDOWN DEFRAG")) {
1478 LOGGER.info("Begin database defrag");
1479 psCompaxt.execute();
1480 final long millis = System.currentTimeMillis() - start;
1481
1482 LOGGER.info("End database defrag ({} ms)", millis);
1483 } catch (SQLException ex) {
1484 LOGGER.error("An unexpected SQL Exception occurred compacting the database; please see the verbose log for more details.");
1485 LOGGER.debug("", ex);
1486 }
1487 }
1488 }
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500 VulnerableSoftware getMatchingSoftware(Cpe cpe, Set<VulnerableSoftware> vulnerableSoftware) {
1501 VulnerableSoftware matched = null;
1502 for (VulnerableSoftware vs : vulnerableSoftware) {
1503 if (vs.matches(cpe)) {
1504 if (matched == null) {
1505 matched = vs;
1506 } else {
1507 if ("*".equals(vs.getWellFormedUpdate()) && !"*".equals(matched.getWellFormedUpdate())) {
1508 matched = vs;
1509 }
1510 }
1511 }
1512 }
1513 return matched;
1514 }
1515
1516
1517
1518
1519
1520
1521
1522 public void deleteUnusedCpe() {
1523 clearCache();
1524 try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, DELETE_UNUSED_DICT_CPE)) {
1525 ps.executeUpdate();
1526 } catch (SQLException ex) {
1527 LOGGER.error("Unable to delete CPE dictionary entries", ex);
1528 }
1529 }
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542 public void addCpe(String cpe, String vendor, String product) {
1543 clearCache();
1544 try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, ADD_DICT_CPE)) {
1545 ps.setString(1, cpe);
1546 ps.setString(2, vendor);
1547 ps.setString(3, product);
1548 ps.executeUpdate();
1549 } catch (SQLException ex) {
1550 LOGGER.error("Unable to add CPE dictionary entry", ex);
1551 }
1552 }
1553
1554
1555
1556
1557
1558
1559 public Map<String, org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> getknownExploitedVulnerabilities() {
1560 final Map<String, org.owasp.dependencycheck.data.knownexploited.json.Vulnerability> known = new HashMap<>();
1561
1562 try (Connection conn = databaseManager.getConnection();
1563 PreparedStatement ps = getPreparedStatement(conn, SELECT_KNOWN_EXPLOITED_VULNERABILITIES);
1564 ResultSet rs = ps.executeQuery()) {
1565
1566 while (rs.next()) {
1567 final org.owasp.dependencycheck.data.knownexploited.json.Vulnerability kev =
1568 new org.owasp.dependencycheck.data.knownexploited.json.Vulnerability();
1569 kev.setCveID(rs.getString(1));
1570 kev.setVendorProject(rs.getString(2));
1571 kev.setProduct(rs.getString(3));
1572 kev.setVulnerabilityName(rs.getString(4));
1573 kev.setDateAdded(rs.getString(5));
1574 kev.setShortDescription(rs.getString(6));
1575 kev.setRequiredAction(rs.getString(7));
1576 kev.setDueDate(rs.getString(8));
1577 kev.setNotes(rs.getString(9));
1578 known.put(kev.getCveID(), kev);
1579 }
1580
1581 } catch (SQLException ex) {
1582 throw new DatabaseException(ex);
1583 }
1584 return known;
1585 }
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595 private void addNullableStringParameter(PreparedStatement ps, int pos, String value) throws SQLException {
1596 if (value == null || value.isEmpty()) {
1597 ps.setNull(pos, java.sql.Types.VARCHAR);
1598 } else {
1599 ps.setString(pos, value);
1600 }
1601 }
1602
1603 private void setUpdateColumn(PreparedStatement ps, int i, Double value) throws SQLException {
1604 if (value == null) {
1605 ps.setNull(i, java.sql.Types.DOUBLE);
1606 } else {
1607 ps.setDouble(i, value);
1608 }
1609 }
1610
1611 private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AuthenticationType value) throws SQLException {
1612 if (value == null) {
1613 ps.setNull(i, java.sql.Types.VARCHAR);
1614 } else {
1615 ps.setString(i, value.value());
1616 }
1617 }
1618
1619 private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.CiaType value) throws SQLException {
1620 if (value == null) {
1621 ps.setNull(i, java.sql.Types.VARCHAR);
1622 } else {
1623 ps.setString(i, value.value());
1624 }
1625 }
1626
1627 private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.Version value) throws SQLException {
1628 if (value == null) {
1629 ps.setNull(i, java.sql.Types.VARCHAR);
1630 } else {
1631 ps.setString(i, value.value());
1632 }
1633 }
1634
1635 private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessComplexityType value) throws SQLException {
1636 if (value == null) {
1637 ps.setNull(i, java.sql.Types.VARCHAR);
1638 } else {
1639 ps.setString(i, value.value());
1640 }
1641 }
1642
1643 private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessVectorType value) throws SQLException {
1644 if (value == null) {
1645 ps.setNull(i, java.sql.Types.VARCHAR);
1646 } else {
1647 ps.setString(i, value.value());
1648 }
1649 }
1650
1651 private void setUpdateColumn(PreparedStatement ps, int i, String value) throws SQLException {
1652 if (value == null) {
1653 ps.setNull(i, java.sql.Types.VARCHAR);
1654 } else {
1655 ps.setString(i, value);
1656 }
1657 }
1658
1659 private void setUpdateColumn(PreparedStatement ps, int i, Boolean value) throws SQLException {
1660 if (value == null) {
1661
1662
1663 if (isOracle) {
1664 ps.setNull(i, java.sql.Types.BIT);
1665 } else {
1666 ps.setNull(i, java.sql.Types.BOOLEAN);
1667 }
1668 } else {
1669 ps.setBoolean(i, value);
1670 }
1671 }
1672
1673 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackVectorType value) throws SQLException {
1674 if (value == null) {
1675 ps.setNull(i, java.sql.Types.VARCHAR);
1676 } else {
1677 ps.setString(i, value.value());
1678 }
1679 }
1680
1681 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackComplexityType value) throws SQLException {
1682 if (value == null) {
1683 ps.setNull(i, java.sql.Types.VARCHAR);
1684 } else {
1685 ps.setString(i, value.value());
1686 }
1687 }
1688
1689 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.PrivilegesRequiredType value) throws SQLException {
1690 if (value == null) {
1691 ps.setNull(i, java.sql.Types.VARCHAR);
1692 } else {
1693 ps.setString(i, value.value());
1694 }
1695 }
1696
1697 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.UserInteractionType value) throws SQLException {
1698 if (value == null) {
1699 ps.setNull(i, java.sql.Types.VARCHAR);
1700 } else {
1701 ps.setString(i, value.value());
1702 }
1703 }
1704
1705 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.ScopeType value) throws SQLException {
1706 if (value == null) {
1707 ps.setNull(i, java.sql.Types.VARCHAR);
1708 } else {
1709 ps.setString(i, value.value());
1710 }
1711 }
1712
1713 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.SeverityType value) throws SQLException {
1714 if (value == null) {
1715 ps.setNull(i, java.sql.Types.VARCHAR);
1716 } else {
1717 ps.setString(i, value.value());
1718 }
1719 }
1720
1721 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.CiaType value) throws SQLException {
1722 if (value == null) {
1723 ps.setNull(i, java.sql.Types.VARCHAR);
1724 } else {
1725 ps.setString(i, value.value());
1726 }
1727 }
1728
1729 private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.Version value) throws SQLException {
1730 if (value == null) {
1731 ps.setNull(i, java.sql.Types.VARCHAR);
1732 } else {
1733 ps.setString(i, value.value());
1734 }
1735 }
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746 private void setFloatValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
1747 if (props != null && props.containsKey(key)) {
1748 try {
1749 ps.setFloat(i, Float.parseFloat(props.get(key).toString()));
1750 } catch (NumberFormatException nfe) {
1751 ps.setNull(i, java.sql.Types.FLOAT);
1752 }
1753 } else {
1754 ps.setNull(i, java.sql.Types.FLOAT);
1755 }
1756 }
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767 private void setStringValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
1768 if (props != null && props.containsKey(key)) {
1769 ps.setString(i, props.get(key).toString());
1770 } else {
1771 ps.setNull(i, java.sql.Types.VARCHAR);
1772 }
1773 }
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784 private void setBooleanValue(PreparedStatement ps, int i, Map<String, Object> props, String key) throws SQLException {
1785 if (props != null && props.containsKey(key)) {
1786 ps.setBoolean(i, Boolean.parseBoolean(props.get(key).toString()));
1787 } else {
1788 ps.setNull(i, java.sql.Types.BOOLEAN);
1789 }
1790 }
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801 @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL")
1802 private Boolean getBooleanValue(ResultSet rs, int index) throws SQLException {
1803 if (rs.getObject(index) == null) {
1804 return null;
1805 }
1806 return rs.getBoolean(index);
1807 }
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818 private Float getFloatValue(ResultSet rs, int index) throws SQLException {
1819 if (rs.getObject(index) == null) {
1820 return null;
1821 }
1822 return rs.getFloat(index);
1823 }
1824 }