From 4c26404fdafd9799c6350ae66732ee3ea74c295d Mon Sep 17 00:00:00 2001 From: Jeremy Long Date: Thu, 28 Sep 2023 08:51:03 -0400 Subject: [PATCH 01/61] feat: utilize NVD API --- ant/pom.xml | 2 +- .../owasp/dependencycheck/taskdefs/Check.java | 4 +- archetype/pom.xml | 2 +- cli/pom.xml | 2 +- .../java/org/owasp/dependencycheck/App.java | 14 +- core/pom.xml | 10 +- .../agent/DependencyCheckScanAgent.java | 12 +- .../analyzer/OssIndexAnalyzer.java | 61 ++- .../data/nodeaudit/Advisory.java | 3 +- .../data/nodeaudit/NpmAuditParser.java | 12 +- .../nvd/ecosystem/CveEcosystemMapper.java | 28 +- .../ecosystem/DescriptionEcosystemMapper.java | 4 +- .../nvd/ecosystem/UrlEcosystemMapper.java | 15 +- .../nvd/json/CpeMatchStreamCollector.java | 85 ---- .../nvd/json/NodeFlatteningCollector.java | 112 ----- .../dependencycheck/data/nvdcve/CveDB.java | 475 ++++++++++++------ .../data/nvdcve/CveItemOperator.java | 23 +- .../data/nvdcve/DatabaseProperties.java | 42 ++ .../data/update/NvdApiDataSource.java | 197 ++++++++ .../data/update/cpe/CpeEcosystemCache.java | 4 +- .../data/update/nvd/NvdCveParser.java | 150 ------ .../data/update/nvd/ProcessTask.java | 4 +- .../data/update/nvd/api/NvdApiProcessor.java | 50 ++ .../dependencycheck/dependency/CvssV2.java | 318 ------------ .../dependencycheck/dependency/CvssV3.java | 330 ------------ .../dependency/Vulnerability.java | 14 +- .../processing/BundlerAuditProcessor.java | 22 +- .../processing/MixAuditProcessor.java | 4 +- .../dependencycheck/reporting/ReportTool.java | 26 +- .../dependencycheck/reporting/SarifRule.java | 104 ++-- .../owasp/dependencycheck/utils/CvssUtil.java | 214 ++++++++ .../dependencycheck/utils/SeverityUtil.java | 36 +- .../xml/suppression/SuppressionHandler.java | 4 +- .../xml/suppression/SuppressionRule.java | 32 +- ...dencycheck.data.update.CachedWebDataSource | 2 +- .../main/resources/templates/csvReport.vsl | 2 +- .../main/resources/templates/htmlReport.vsl | 12 +- .../resources/templates/jenkinsReport.vsl | 8 +- .../main/resources/templates/jsonReport.vsl | 124 ++--- .../main/resources/templates/junitReport.vsl | 16 +- .../main/resources/templates/sarifReport.vsl | 2 +- .../main/resources/templates/xmlReport.vsl | 124 ++--- .../analyzer/ElixirMixAuditAnalyzerIT.java | 2 +- .../analyzer/RubyBundleAuditAnalyzerIT.java | 2 +- .../nvd/ecosystem/CveEcosystemMapperTest.java | 58 +-- .../DescriptionEcosystemMapperTest.java | 32 +- .../nvd/ecosystem/UrlEcosystemMapperTest.java | 35 +- .../nvd/json/CpeMatchStreamCollectorTest.java | 58 --- .../nvd/json/NodeFlatteningCollectorTest.java | 63 --- .../dependency/VulnerabilityTest.java | 58 ++- .../dependencycheck/utils/CvssUtilTest.java | 183 +++++++ .../utils/SeverityUtilTest.java | 24 +- .../xml/suppression/SuppressionRuleTest.java | 18 +- maven/pom.xml | 2 +- .../maven/BaseDependencyCheckMojo.java | 14 +- pom.xml | 7 +- utils/pom.xml | 2 +- .../owasp/dependencycheck/utils/Settings.java | 37 ++ 58 files changed, 1586 insertions(+), 1714 deletions(-) delete mode 100644 core/src/main/java/org/owasp/dependencycheck/data/nvd/json/CpeMatchStreamCollector.java delete mode 100644 core/src/main/java/org/owasp/dependencycheck/data/nvd/json/NodeFlatteningCollector.java create mode 100644 core/src/main/java/org/owasp/dependencycheck/data/update/NvdApiDataSource.java delete mode 100644 core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveParser.java create mode 100644 core/src/main/java/org/owasp/dependencycheck/data/update/nvd/api/NvdApiProcessor.java delete mode 100644 core/src/main/java/org/owasp/dependencycheck/dependency/CvssV2.java delete mode 100644 core/src/main/java/org/owasp/dependencycheck/dependency/CvssV3.java create mode 100644 core/src/main/java/org/owasp/dependencycheck/utils/CvssUtil.java delete mode 100644 core/src/test/java/org/owasp/dependencycheck/data/nvd/json/CpeMatchStreamCollectorTest.java delete mode 100644 core/src/test/java/org/owasp/dependencycheck/data/nvd/json/NodeFlatteningCollectorTest.java create mode 100644 core/src/test/java/org/owasp/dependencycheck/utils/CvssUtilTest.java diff --git a/ant/pom.xml b/ant/pom.xml index 94a194174ab..748cd1e5812 100644 --- a/ant/pom.xml +++ b/ant/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 8.4.1-SNAPSHOT + 9.0.0-SNAPSHOT dependency-check-ant diff --git a/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java b/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java index fbfe1c05b86..ebec4f686bd 100644 --- a/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java +++ b/ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java @@ -2217,8 +2217,8 @@ private void checkForFailure(Dependency[] dependencies) throws BuildException { for (Dependency d : dependencies) { boolean addName = true; for (Vulnerability v : d.getVulnerabilities()) { - if ((v.getCvssV2() != null && v.getCvssV2().getScore() >= failBuildOnCVSS) - || (v.getCvssV3() != null && v.getCvssV3().getBaseScore() >= failBuildOnCVSS) + if ((v.getCvssV2() != null && v.getCvssV2().getCvssData().getBaseScore() >= failBuildOnCVSS) + || (v.getCvssV3() != null && v.getCvssV3().getCvssData().getBaseScore() >= failBuildOnCVSS) || (v.getUnscoredSeverity() != null && SeverityUtil.estimateCvssV2(v.getUnscoredSeverity()) >= failBuildOnCVSS) //safety net to fail on any if for some reason the above misses on 0 || (failBuildOnCVSS <= 0.0f)) { diff --git a/archetype/pom.xml b/archetype/pom.xml index 329bfd645bc..d522f49ac8b 100644 --- a/archetype/pom.xml +++ b/archetype/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2017 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 8.4.1-SNAPSHOT + 9.0.0-SNAPSHOT dependency-check-plugin Dependency-Check Plugin Archetype diff --git a/cli/pom.xml b/cli/pom.xml index 7130074a552..60508c9b68f 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 8.4.1-SNAPSHOT + 9.0.0-SNAPSHOT dependency-check-cli diff --git a/cli/src/main/java/org/owasp/dependencycheck/App.java b/cli/src/main/java/org/owasp/dependencycheck/App.java index ab7313d520f..3176c5e6530 100644 --- a/cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/cli/src/main/java/org/owasp/dependencycheck/App.java @@ -303,21 +303,21 @@ private int determineReturnCode(Engine engine, float cvssFailScore) { for (Dependency d : engine.getDependencies()) { boolean addName = true; for (Vulnerability v : d.getVulnerabilities()) { - final float cvssV2 = v.getCvssV2() != null ? v.getCvssV2().getScore() : -1; - final float cvssV3 = v.getCvssV3() != null ? v.getCvssV3().getBaseScore() : -1; - final float unscoredCvss = v.getUnscoredSeverity() != null ? SeverityUtil.estimateCvssV2(v.getUnscoredSeverity()) : -1; + final Double cvssV2 = v.getCvssV2() != null && v.getCvssV2().getCvssData() != null && v.getCvssV2().getCvssData().getBaseScore() != null ? v.getCvssV2().getCvssData().getBaseScore() : -1; + final Double cvssV3 = v.getCvssV3() != null && v.getCvssV3().getCvssData() != null && v.getCvssV3().getCvssData().getBaseScore() != null ? v.getCvssV3().getCvssData().getBaseScore() : -1; + final Double unscoredCvss = v.getUnscoredSeverity() != null ? SeverityUtil.estimateCvssV2(v.getUnscoredSeverity()) : -1; if (cvssV2 >= cvssFailScore || cvssV3 >= cvssFailScore || unscoredCvss >= cvssFailScore //safety net to fail on any if for some reason the above misses on 0 || (cvssFailScore <= 0.0f)) { - float score = 0.0f; - if (cvssV3 >= 0.0f) { + double score = 0.0; + if (cvssV3 >= 0.0) { score = cvssV3; - } else if (cvssV2 >= 0.0f) { + } else if (cvssV2 >= 0.0) { score = cvssV2; - } else if (unscoredCvss >= 0.0f) { + } else if (unscoredCvss >= 0.0) { score = unscoredCvss; } if (addName) { diff --git a/core/pom.xml b/core/pom.xml index 04bf4bf3d94..3df2083e0f9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 8.4.1-SNAPSHOT + 9.0.0-SNAPSHOT dependency-check-core @@ -105,7 +105,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. jsonschema2pojo-maven-plugin - + generate-knownexploited generate-sources @@ -184,6 +184,10 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. + + io.github.jeremylong + open-vulnerability-clients + org.anarres.jdiagnostics jdiagnostics diff --git a/core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java b/core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java index b7f893fb85d..c45637b6b4d 100644 --- a/core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java +++ b/core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java @@ -101,7 +101,7 @@ public class DependencyCheckScanAgent { * to 11. The valid range for the fail build on CVSS is 0 to 11, where * anything above 10 will not cause the build to fail. */ - private float failBuildOnCVSS = 11; + private Double failBuildOnCVSS = 11.0; /** * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not * recommended that this be turned to false. Default is true. @@ -310,7 +310,7 @@ public void setReportOutputDirectory(String reportOutputDirectory) { * * @return the value of failBuildOnCVSS */ - public float getFailBuildOnCVSS() { + public Double getFailBuildOnCVSS() { return failBuildOnCVSS; } @@ -319,7 +319,7 @@ public float getFailBuildOnCVSS() { * * @param failBuildOnCVSS new value of failBuildOnCVSS */ - public void setFailBuildOnCVSS(float failBuildOnCVSS) { + public void setFailBuildOnCVSS(Double failBuildOnCVSS) { this.failBuildOnCVSS = failBuildOnCVSS; } @@ -993,7 +993,7 @@ public Engine execute() throws ScanAgentException { if (this.showSummary) { showSummary(engine.getDependencies()); } - if (this.failBuildOnCVSS <= 10) { + if (this.failBuildOnCVSS <= 10.0) { checkForFailure(engine.getDependencies()); } } @@ -1025,8 +1025,8 @@ private void checkForFailure(Dependency[] dependencies) throws ScanAgentExceptio for (Dependency d : dependencies) { boolean addName = true; for (Vulnerability v : d.getVulnerabilities()) { - if ((v.getCvssV2() != null && v.getCvssV2().getScore() >= failBuildOnCVSS) - || (v.getCvssV3() != null && v.getCvssV3().getBaseScore() >= failBuildOnCVSS) + if ((v.getCvssV2() != null && v.getCvssV2().getCvssData().getBaseScore() >= failBuildOnCVSS) + || (v.getCvssV3() != null && v.getCvssV3().getCvssData().getBaseScore() >= failBuildOnCVSS) || (v.getUnscoredSeverity() != null && SeverityUtil.estimateCvssV2(v.getUnscoredSeverity()) >= failBuildOnCVSS) //safety net to fail on any if for some reason the above misses on 0 || (failBuildOnCVSS <= 0.0f)) { diff --git a/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java b/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java index be16d18cc8c..2b461b0ab47 100644 --- a/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java +++ b/core/src/main/java/org/owasp/dependencycheck/analyzer/OssIndexAnalyzer.java @@ -17,6 +17,8 @@ */ package org.owasp.dependencycheck.analyzer; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data; import org.sonatype.ossindex.service.api.componentreport.ComponentReport; import org.sonatype.ossindex.service.api.componentreport.ComponentReportVulnerability; import org.sonatype.ossindex.service.api.cvss.Cvss2Severity; @@ -29,8 +31,7 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.data.ossindex.OssindexClientFactory; -import org.owasp.dependencycheck.dependency.CvssV2; -import org.owasp.dependencycheck.dependency.CvssV3; + import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.VulnerableSoftware; @@ -58,6 +59,7 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; +import org.owasp.dependencycheck.utils.CvssUtil; import org.sonatype.goodies.packageurl.InvalidException; import org.sonatype.ossindex.service.client.transport.Transport.TransportException; @@ -318,34 +320,61 @@ private Vulnerability transform(final ComponentReport report, final ComponentRep result.setDescription(source.getDescription()); result.addCwe(source.getCwe()); - final float cvssScore = source.getCvssScore() != null ? source.getCvssScore() : -1; + final double cvssScore = source.getCvssScore() != null ? source.getCvssScore() : -1; if (source.getCvssVector() != null) { if (source.getCvssVector().startsWith("CVSS:3")) { - result.setCvssV3(new CvssV3(source.getCvssVector(), cvssScore)); + result.setCvssV3(CvssUtil.vectorToCvssV3(source.getCvssVector(), cvssScore)); } else { // convert cvss details final CvssVector cvssVector = CvssVectorFactory.create(source.getCvssVector()); final Map metrics = cvssVector.getMetrics(); if (cvssVector instanceof Cvss2Vector) { - result.setCvssV2(new CvssV2( - cvssScore, - metrics.get(Cvss2Vector.ACCESS_VECTOR), - metrics.get(Cvss2Vector.ACCESS_COMPLEXITY), - metrics.get(Cvss2Vector.AUTHENTICATION), - metrics.get(Cvss2Vector.CONFIDENTIALITY_IMPACT), - metrics.get(Cvss2Vector.INTEGRITY_IMPACT), - metrics.get(Cvss2Vector.AVAILABILITY_IMPACT), - Cvss2Severity.of(cvssScore).name() - )); + String tmp = metrics.get(Cvss2Vector.ACCESS_VECTOR); + CvssV2Data.AccessVectorType accessVector = null; + if (tmp!=null) { + accessVector = CvssV2Data.AccessVectorType.fromValue(tmp); + } + tmp = metrics.get(Cvss2Vector.ACCESS_COMPLEXITY); + CvssV2Data.AccessComplexityType accessComplexity = null; + if (tmp!=null) { + accessComplexity = CvssV2Data.AccessComplexityType.fromValue(tmp); + } + tmp = metrics.get(Cvss2Vector.AUTHENTICATION); + CvssV2Data.AuthenticationType authentication = null; + if (tmp!=null) { + authentication = CvssV2Data.AuthenticationType.fromValue(tmp); + } + tmp = metrics.get(Cvss2Vector.CONFIDENTIALITY_IMPACT); + CvssV2Data.CiaType confidentialityImpact = null; + if (tmp!=null) { + confidentialityImpact = CvssV2Data.CiaType.fromValue(tmp); + } + tmp = metrics.get(Cvss2Vector.INTEGRITY_IMPACT); + CvssV2Data.CiaType integrityImpact = null; + if (tmp!=null) { + integrityImpact = CvssV2Data.CiaType.fromValue(tmp); + } + tmp = metrics.get(Cvss2Vector.AVAILABILITY_IMPACT); + CvssV2Data.CiaType availabilityImpact = null; + if (tmp!=null) { + availabilityImpact = CvssV2Data.CiaType.fromValue(tmp); + } + String severity = Cvss2Severity.of((float)cvssScore).name().toUpperCase(); + CvssV2Data cvssData = new CvssV2Data("2.0", source.getCvssVector(), accessVector, + accessComplexity, authentication, confidentialityImpact, + integrityImpact, availabilityImpact, cvssScore, + severity, null, null, null, null, null, null, null, null, null, null); + CvssV2 cvssV2 = new CvssV2(null, null, cvssData, severity, null, null, null, null, null, null, null); + result.setCvssV2(cvssV2); } else { LOG.warn("Unsupported CVSS vector: {}", cvssVector); - result.setUnscoredSeverity(Float.toString(cvssScore)); + result.setUnscoredSeverity(Double.toString(cvssScore)); } } } else { LOG.debug("OSS has no vector for {}", result.getName()); - result.setUnscoredSeverity(Float.toString(cvssScore)); + result.setUnscoredSeverity(Double.toString(cvssScore)); } // generate a reference to the vulnerability details on OSS Index result.addReference(REFERENCE_TYPE, source.getTitle(), source.getReference().toString()); diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/Advisory.java b/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/Advisory.java index a6f044f9dbe..97fab27f268 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/Advisory.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/Advisory.java @@ -17,8 +17,9 @@ */ package org.owasp.dependencycheck.data.nodeaudit; -import org.owasp.dependencycheck.dependency.CvssV3; + +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3; import java.io.Serializable; import java.util.List; import javax.annotation.concurrent.ThreadSafe; diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmAuditParser.java b/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmAuditParser.java index 5f0cf87d155..84ef144374f 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmAuditParser.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmAuditParser.java @@ -17,15 +17,21 @@ */ package org.owasp.dependencycheck.data.nodeaudit; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3Data; import org.json.JSONArray; import org.json.JSONObject; -import org.owasp.dependencycheck.dependency.CvssV3; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.json.JSONException; +import org.owasp.dependencycheck.utils.CvssUtil; +import org.sonatype.ossindex.service.api.cvss.Cvss3Severity; /** * Parser for NPM Audit API response. This parser is derived from: @@ -118,7 +124,7 @@ private Advisory parseAdvisory(JSONObject object) throws JSONException { } final JSONObject jsonCvss = object.optJSONObject("cvss"); if (jsonCvss != null) { - float baseScore = -1.0f; + double baseScore = -1.0; final String score = jsonCvss.optString("score"); if (score != null) { try { @@ -133,7 +139,7 @@ private Advisory parseAdvisory(JSONObject object) throws JSONException { if (vector != null) { if (vector.startsWith("CVSS:3") && baseScore >= 0.0) { try { - final CvssV3 cvss = new CvssV3(vector, baseScore); + final CvssV3 cvss = CvssUtil.vectorToCvssV3(vector, baseScore); advisory.setCvssV3(cvss); } catch (IllegalArgumentException iae) { LOGGER.warn("Invalid CVSS vector format encountered in NPM Audit results '{}' ", vector, iae); diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/CveEcosystemMapper.java b/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/CveEcosystemMapper.java index 8424707b425..de429948225 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/CveEcosystemMapper.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/CveEcosystemMapper.java @@ -13,18 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * Copyright (c) 2020 The OWASP Foundation. All Rights Reserved. + * Copyright (c) 2023 Jeremy Long. All Rights Reserved. */ package org.owasp.dependencycheck.data.nvd.ecosystem; +import io.github.jeremylong.openvulnerability.client.nvd.Config; +import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch; +import io.github.jeremylong.openvulnerability.client.nvd.Node; +import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem; import java.util.List; import java.util.stream.Collectors; import javax.annotation.concurrent.NotThreadSafe; -import org.owasp.dependencycheck.data.nvd.json.CpeMatchStreamCollector; -import org.owasp.dependencycheck.data.nvd.json.DefCpeMatch; - -import org.owasp.dependencycheck.data.nvd.json.DefCveItem; -import org.owasp.dependencycheck.data.nvd.json.NodeFlatteningCollector; /** * Utility for mapping CVEs to their ecosystems. @@ -83,17 +82,20 @@ public String getEcosystem(DefCveItem cve) { * null */ private boolean hasMultipleVendorProductConfigurations(DefCveItem cve) { - final List cpeEntries = cve.getConfigurations().getNodes().stream() - .collect(NodeFlatteningCollector.getInstance()) - .collect(CpeMatchStreamCollector.getInstance()) - .filter(defCpeMatch -> defCpeMatch.getCpe23Uri() != null) + final List cpeEntries = cve.getCve().getConfigurations().stream() + .map(Config::getNodes) + .flatMap(List::stream) + .map(Node::getCpeMatch) + .flatMap(List::stream) + + .filter(match -> match.getCriteria() != null) .collect(Collectors.toList()); if (!cpeEntries.isEmpty() && cpeEntries.size() > 1) { - final DefCpeMatch firstMatch = cpeEntries.get(0); - final String uri = firstMatch.getCpe23Uri(); + final CpeMatch firstMatch = cpeEntries.get(0); + final String uri = firstMatch.getCriteria(); final int pos = uri.indexOf(":", uri.indexOf(":", 10) + 1); final String match = uri.substring(0, pos + 1); - return !cpeEntries.stream().allMatch(e -> e.getCpe23Uri().startsWith(match)); + return !cpeEntries.stream().allMatch(e -> e.getCriteria().startsWith(match)); } return false; } diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/DescriptionEcosystemMapper.java b/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/DescriptionEcosystemMapper.java index d4dae989047..1d325ec8bff 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/DescriptionEcosystemMapper.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/DescriptionEcosystemMapper.java @@ -18,7 +18,7 @@ package org.owasp.dependencycheck.data.nvd.ecosystem; import org.apache.commons.lang3.StringUtils; -import org.owasp.dependencycheck.data.nvd.json.DefCveItem; +import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem; import java.util.HashMap; import java.util.Map; @@ -179,7 +179,7 @@ protected void increment(int i, int[] ecosystemMap) { */ public String getEcosystem(DefCveItem cve) { final int[] ecosystemMap = new int[ECOSYSTEMS.length]; - cve.getCve().getDescription().getDescriptionData().stream() + cve.getCve().getDescriptions().stream() .filter((langString) -> (langString.getLang().equals("en"))) .forEachOrdered((langString) -> search(langString.getValue(), ecosystemMap)); return getResult(ecosystemMap); diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/UrlEcosystemMapper.java b/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/UrlEcosystemMapper.java index 8721ed54b1c..bd48613e95f 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/UrlEcosystemMapper.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nvd/ecosystem/UrlEcosystemMapper.java @@ -23,13 +23,13 @@ import javax.annotation.concurrent.NotThreadSafe; -import org.owasp.dependencycheck.data.nvd.json.CVEJSON40Min11; -import org.owasp.dependencycheck.data.nvd.json.DefCveItem; -import org.owasp.dependencycheck.data.nvd.json.Reference; -import org.owasp.dependencycheck.data.nvd.json.References; +import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem; +import io.github.jeremylong.openvulnerability.client.nvd.Reference; import com.hankcs.algorithm.AhoCorasickDoubleArrayTrie; import com.hankcs.algorithm.AhoCorasickDoubleArrayTrie.Hit; +import io.github.jeremylong.openvulnerability.client.nvd.CveItem; +import java.util.List; @NotThreadSafe public class UrlEcosystemMapper { @@ -69,14 +69,13 @@ public UrlEcosystemMapper() { * @return the ecosystem */ public String getEcosystem(DefCveItem cve) { - final References references = Optional.ofNullable(cve) + final List references = Optional.ofNullable(cve) .map(DefCveItem::getCve) - .map(CVEJSON40Min11::getReferences) + .map(CveItem::getReferences) .orElse(null); if (Objects.nonNull(references)) { - for (Reference r : references.getReferenceData()) { - + for (Reference r : references) { final Hit ecosystem = search.findFirst(r.getUrl()); if (ecosystem != null) { return ecosystem.value; diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvd/json/CpeMatchStreamCollector.java b/core/src/main/java/org/owasp/dependencycheck/data/nvd/json/CpeMatchStreamCollector.java deleted file mode 100644 index 652e45a2ef2..00000000000 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvd/json/CpeMatchStreamCollector.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of dependency-check-core. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright (c) 2018 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.data.nvd.json; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collector; -import java.util.stream.Stream; - -import javax.annotation.concurrent.ThreadSafe; - -/** - * - * @author Jeremy Long - * - */ -@ThreadSafe -public final class CpeMatchStreamCollector implements Collector, Stream> { - - /** - * The singleton instance. - */ - private static final CpeMatchStreamCollector INSTANCE; - - static { - INSTANCE = new CpeMatchStreamCollector(); - } - - public static CpeMatchStreamCollector getInstance() { - return INSTANCE; - } - - private CpeMatchStreamCollector() { - } - - @Override - public Supplier> supplier() { - return ArrayList::new; - } - - @Override - public BiConsumer, DefNode> accumulator() { - return (match, nodes) -> match.addAll(nodes.getCpeMatch()); - } - - @Override - public BinaryOperator> combiner() { - return (map, other) -> { - map.addAll(other); - return map; - }; - } - - @Override - public Function, Stream> finisher() { - return Collection::stream; - } - - @Override - public Set characteristics() { - return EnumSet.of(Characteristics.UNORDERED); - } - -} diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvd/json/NodeFlatteningCollector.java b/core/src/main/java/org/owasp/dependencycheck/data/nvd/json/NodeFlatteningCollector.java deleted file mode 100644 index 7dfe7e291ff..00000000000 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvd/json/NodeFlatteningCollector.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of dependency-check-core. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright (c) 2018 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.data.nvd.json; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collector; -import java.util.stream.Stream; - -import javax.annotation.concurrent.ThreadSafe; - -/** - * Used to flatten a hierarchical list of nodes with children. - * - * @author Jeremy Long - */ -@ThreadSafe -public final class NodeFlatteningCollector implements Collector, Stream> { - - /** - * Singleton instance variable. - */ - private static final NodeFlatteningCollector INSTANCE; - - static { - INSTANCE = new NodeFlatteningCollector(); - } - - public static NodeFlatteningCollector getInstance() { - return INSTANCE; - } - - private NodeFlatteningCollector() { - } - - /** - * Flattens the hierarchical list of nodes. - * - * @param node the node with children to flatten - * @return the flattened list of nodes - */ - private List flatten(DefNode node) { - final List result = new ArrayList<>(); - result.add(node); - return flatten(result, node.getChildren()); - } - - /** - * Flattens the hierarchical list of nodes. - * - * @param result the results - * @param nodes the nodes - * @return the flattened list of nodes - */ - private List flatten(List result, List nodes) { - nodes.forEach(n -> { - flatten(result, n.getChildren()); - result.add(n); - }); - return result; - } - - @Override - public Supplier> supplier() { - return ArrayList::new; - } - - @Override - public BiConsumer, DefNode> accumulator() { - return (nodes, n) -> nodes.addAll(flatten(n)); - } - - @Override - public BinaryOperator> combiner() { - return (map, other) -> { - map.addAll(other); - return map; - }; - } - - @Override - public Function, Stream> finisher() { - return Collection::stream; - } - - @Override - public Set characteristics() { - return EnumSet.of(Characteristics.UNORDERED); - } -} diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java index 752597623ef..d93506ebbf4 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java @@ -20,6 +20,8 @@ import com.google.common.io.Resources; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.github.jeremylong.openvulnerability.client.nvd.Config; +import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch; import org.apache.commons.collections.map.ReferenceMap; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.VulnerableSoftware; @@ -46,20 +48,19 @@ import static org.apache.commons.collections.map.AbstractReferenceMap.SOFT; import org.owasp.dependencycheck.analyzer.exception.LambdaExceptionWrapper; import org.owasp.dependencycheck.analyzer.exception.UnexpectedAnalysisException; -import org.owasp.dependencycheck.data.nvd.json.BaseMetricV2; -import org.owasp.dependencycheck.data.nvd.json.BaseMetricV3; -import org.owasp.dependencycheck.data.nvd.json.CpeMatchStreamCollector; -import org.owasp.dependencycheck.data.nvd.json.DefCpeMatch; -import org.owasp.dependencycheck.data.nvd.json.DefCveItem; -import org.owasp.dependencycheck.data.nvd.json.LangString; -import org.owasp.dependencycheck.data.nvd.json.NodeFlatteningCollector; -import org.owasp.dependencycheck.data.nvd.json.ProblemtypeDatum; -import org.owasp.dependencycheck.data.nvd.json.Reference; +import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem; import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*; import org.owasp.dependencycheck.data.update.cpe.CpeEcosystemCache; import org.owasp.dependencycheck.data.update.cpe.CpePlus; -import org.owasp.dependencycheck.dependency.CvssV2; -import org.owasp.dependencycheck.dependency.CvssV3; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3Data; +import io.github.jeremylong.openvulnerability.client.nvd.LangString; +import io.github.jeremylong.openvulnerability.client.nvd.Metrics; +import io.github.jeremylong.openvulnerability.client.nvd.Node; +import io.github.jeremylong.openvulnerability.client.nvd.Reference; +import io.github.jeremylong.openvulnerability.client.nvd.Weakness; import org.owasp.dependencycheck.dependency.VulnerableSoftwareBuilder; import us.springett.parsers.cpe.Cpe; import us.springett.parsers.cpe.CpeBuilder; @@ -143,8 +144,7 @@ public int updateEcosystemCache() { final URL url = Resources.getResource(DB_ECOSYSTEM_CACHE); final List sql = Resources.readLines(url, StandardCharsets.UTF_8); - try (Connection conn = databaseManager.getConnection(); - Statement statement = conn.createStatement()) { + try (Connection conn = databaseManager.getConnection(); Statement statement = conn.createStatement()) { for (String single : sql) { updateCount += statement.executeUpdate(single); } @@ -461,8 +461,7 @@ DatabaseProperties reloadProperties() { */ public Set getCPEs(String vendor, String product) { final Set cpe = new HashSet<>(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ENTRIES)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ENTRIES)) { //part, vendor, product, version, update_version, edition, //lang, sw_edition, target_sw, target_hw, other, ecosystem ps.setString(1, vendor); @@ -502,9 +501,7 @@ public Set getCPEs(String vendor, String product) { */ public Set> getVendorProductList() throws DatabaseException { final Set> data = new HashSet<>(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST); - ResultSet rs = ps.executeQuery()) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST); ResultSet rs = ps.executeQuery()) { while (rs.next()) { data.add(new Pair<>(rs.getString(1), rs.getString(2))); } @@ -526,9 +523,7 @@ public Set> getVendorProductList() throws DatabaseException */ public Set> getVendorProductListForNode() throws DatabaseException { final Set> data = new HashSet<>(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST_FOR_NODE); - ResultSet rs = ps.executeQuery()) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_VENDOR_PRODUCT_LIST_FOR_NODE); ResultSet rs = ps.executeQuery()) { while (rs.next()) { data.add(new Pair<>(rs.getString(1), rs.getString(2))); } @@ -546,9 +541,7 @@ public Set> getVendorProductListForNode() throws DatabaseEx */ public Properties getProperties() { final Properties prop = new Properties(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, SELECT_PROPERTIES); - ResultSet rs = ps.executeQuery()) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_PROPERTIES); ResultSet rs = ps.executeQuery()) { while (rs.next()) { prop.setProperty(rs.getString(1), rs.getString(2)); } @@ -567,8 +560,7 @@ public Properties getProperties() { */ public void saveProperty(String key, String value) { clearCache(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement mergeProperty = getPreparedStatement(conn, MERGE_PROPERTY)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement mergeProperty = getPreparedStatement(conn, MERGE_PROPERTY)) { if (mergeProperty != null) { mergeProperty.setString(1, key); mergeProperty.setString(2, value); @@ -623,8 +615,7 @@ public List getVulnerabilities(Cpe cpe) throws DatabaseException } final List vulnerabilities = new ArrayList<>(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, SELECT_CVE_FROM_SOFTWARE)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_CVE_FROM_SOFTWARE)) { ps.setString(1, cpe.getVendor()); ps.setString(2, cpe.getProduct()); try (ResultSet rs = ps.executeQuery()) { @@ -714,8 +705,7 @@ public Vulnerability getVulnerability(String cve, Connection conn) throws Databa final VulnerableSoftwareBuilder vulnerableSoftwareBuilder = new VulnerableSoftwareBuilder(); Vulnerability vuln = null; try { - try (PreparedStatement psV = getPreparedStatement(conn, SELECT_VULNERABILITY, cve); - ResultSet rsV = psV.executeQuery()) { + try (PreparedStatement psV = getPreparedStatement(conn, SELECT_VULNERABILITY, cve); ResultSet rsV = psV.executeQuery()) { if (rsV.next()) { //1.id, 2.description, cveId = rsV.getInt(1); @@ -729,23 +719,69 @@ public Vulnerability getVulnerability(String cve, Connection conn) throws Databa //12.v2AccessVector, 13.v2AccessComplexity, 14.v2Authentication, 15.v2ConfidentialityImpact, //16.v2IntegrityImpact, 17.v2AvailabilityImpact, 18.v2Version, if (rsV.getObject(11) != null) { - final CvssV2 cvss = new CvssV2(rsV.getFloat(11), rsV.getString(12), - rsV.getString(13), rsV.getString(14), rsV.getString(15), - rsV.getString(16), rsV.getString(17), rsV.getString(3), - getFloatValue(rsV, 4), getFloatValue(rsV, 5), - getBooleanValue(rsV, 6), getBooleanValue(rsV, 7), getBooleanValue(rsV, 8), - getBooleanValue(rsV, 9), getBooleanValue(rsV, 10), rsV.getString(18)); + + final CvssV2Data.AccessVectorType accessVector = CvssV2Data.AccessVectorType.fromValue(rsV.getString(12)); + final CvssV2Data.AccessComplexityType accessComplexity = CvssV2Data.AccessComplexityType.fromValue(rsV.getString(13)); + final CvssV2Data.AuthenticationType authentication = CvssV2Data.AuthenticationType.fromValue(rsV.getString(14)); + final CvssV2Data.CiaType confidentialityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(15)); + final CvssV2Data.CiaType integrityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(16)); + final CvssV2Data.CiaType availabilityImpact = CvssV2Data.CiaType.fromValue(rsV.getString(17)); + final String vector = String.format("/AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s", + accessVector == null ? "" : accessVector.value().substring(0, 1), + accessComplexity == null ? "" : accessComplexity.value().substring(0, 1), + authentication == null ? "" : authentication.value().substring(0, 1), + confidentialityImpact == null ? "" : confidentialityImpact.value().substring(0, 1), + integrityImpact == null ? "" : integrityImpact.value().substring(0, 1), + availabilityImpact == null ? "" : availabilityImpact.value().substring(0, 1)); + + //some older test data may not correctly have the version set. + String cveVersion = "2.0"; + if (rsV.getString(18) != null) { + cveVersion = rsV.getString(18); + } + final CvssV2Data cvssData = new CvssV2Data(cveVersion, vector, accessVector, + accessComplexity, authentication, confidentialityImpact, + integrityImpact, availabilityImpact, rsV.getDouble(11), rsV.getString(3), + null, null, null, null, null, null, null, null, null, null); + final CvssV2 cvss = new CvssV2(null, CvssV2.Type.PRIMARY, cvssData, rsV.getString(3), + rsV.getDouble(4), rsV.getDouble(5), rsV.getBoolean(6), rsV.getBoolean(7), + rsV.getBoolean(8), rsV.getBoolean(9), rsV.getBoolean(10)); vuln.setCvssV2(cvss); } //19.v3ExploitabilityScore, 20.v3ImpactScore, 21.v3AttackVector, 22.v3AttackComplexity, 23.v3PrivilegesRequired, //24.v3UserInteraction, 25.v3Scope, 26.v3ConfidentialityImpact, 27.v3IntegrityImpact, 28.v3AvailabilityImpact, - //29.v3BaseScore, 30.v3BaseSeverity, 21.v3Version + //29.v3BaseScore, 30.v3BaseSeverity, 31.v3Version if (rsV.getObject(21) != null) { - final CvssV3 cvss = new CvssV3(rsV.getString(21), rsV.getString(22), - rsV.getString(23), rsV.getString(24), rsV.getString(25), - rsV.getString(26), rsV.getString(27), rsV.getString(28), - rsV.getFloat(29), rsV.getString(30), getFloatValue(rsV, 19), - getFloatValue(rsV, 20), rsV.getString(31)); + //some older test data may not correctly have the version set. + String cveVersion = "3.1"; + if (rsV.getString(31) != null) { + cveVersion = rsV.getString(31); + } + final CvssV3Data.Version version = CvssV3Data.Version.fromValue(cveVersion); + final CvssV3Data.AttackVectorType attackVector = CvssV3Data.AttackVectorType.fromValue(rsV.getString(21)); + final CvssV3Data.AttackComplexityType attackComplexity = CvssV3Data.AttackComplexityType.fromValue(rsV.getString(22)); + final CvssV3Data.PrivilegesRequiredType privilegesRequired = CvssV3Data.PrivilegesRequiredType.fromValue(rsV.getString(23)); + final CvssV3Data.UserInteractionType userInteraction = CvssV3Data.UserInteractionType.fromValue(rsV.getString(24)); + final CvssV3Data.ScopeType scope = CvssV3Data.ScopeType.fromValue(rsV.getString(25)); + final CvssV3Data.CiaType confidentialityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(26)); + final CvssV3Data.CiaType integrityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(27)); + final CvssV3Data.CiaType availabilityImpact = CvssV3Data.CiaType.fromValue(rsV.getString(28)); + final CvssV3Data.SeverityType baseSeverity = CvssV3Data.SeverityType.fromValue(rsV.getString(30)); + final String vector = String.format("CVSS:%s/AV:%s/AC:%s/PR:%s/UI:%s/S:%s/C:%s/I:%s/A:%s", + version == null ? "" : version, + attackVector == null ? "" : attackVector.value().substring(0, 1), + attackComplexity == null ? "" : attackComplexity.value().substring(0, 1), + privilegesRequired == null ? "" : privilegesRequired.value().substring(0, 1), + userInteraction == null ? "" : userInteraction.value().substring(0, 1), + scope == null ? "" : scope.value().substring(0, 1), + confidentialityImpact == null ? "" : confidentialityImpact.value().substring(0, 1), + integrityImpact == null ? "" : integrityImpact.value().substring(0, 1), + availabilityImpact == null ? "" : availabilityImpact.value().substring(0, 1)); + + final CvssV3Data cvssData = new CvssV3Data(version, vector, attackVector, attackComplexity, privilegesRequired, + userInteraction, scope, confidentialityImpact, integrityImpact, availabilityImpact, + rsV.getDouble(29), baseSeverity, CvssV3Data.ExploitCodeMaturityType.PROOF_OF_CONCEPT, CvssV3Data.RemediationLevelType.NOT_DEFINED, CvssV3Data.ConfidenceType.REASONABLE, 0.0, CvssV3Data.SeverityType.MEDIUM, CvssV3Data.CiaRequirementType.NOT_DEFINED, CvssV3Data.CiaRequirementType.NOT_DEFINED, CvssV3Data.CiaRequirementType.NOT_DEFINED, CvssV3Data.ModifiedAttackVectorType.ADJACENT_NETWORK, CvssV3Data.ModifiedAttackComplexityType.NOT_DEFINED, CvssV3Data.ModifiedPrivilegesRequiredType.NOT_DEFINED, CvssV3Data.ModifiedUserInteractionType.NOT_DEFINED, CvssV3Data.ModifiedScopeType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED, CvssV3Data.ModifiedCiaType.NOT_DEFINED, 1.0, CvssV3Data.SeverityType.NONE); + final CvssV3 cvss = new CvssV3(null, null, cvssData, rsV.getDouble(19), rsV.getDouble(20)); vuln.setCvssV3(cvss); } } else { @@ -753,20 +789,17 @@ public Vulnerability getVulnerability(String cve, Connection conn) throws Databa return null; } } - try (PreparedStatement psCWE = getPreparedStatement(conn, SELECT_VULNERABILITY_CWE, cveId); - ResultSet rsC = psCWE.executeQuery()) { + try (PreparedStatement psCWE = getPreparedStatement(conn, SELECT_VULNERABILITY_CWE, cveId); ResultSet rsC = psCWE.executeQuery()) { while (rsC.next()) { vuln.addCwe(rsC.getString(1)); } } - try (PreparedStatement psR = getPreparedStatement(conn, SELECT_REFERENCES, cveId); - ResultSet rsR = psR.executeQuery()) { + try (PreparedStatement psR = getPreparedStatement(conn, SELECT_REFERENCES, cveId); ResultSet rsR = psR.executeQuery()) { while (rsR.next()) { vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3)); } } - try (PreparedStatement psS = getPreparedStatement(conn, SELECT_SOFTWARE, cveId); - ResultSet rsS = psS.executeQuery()) { + try (PreparedStatement psS = getPreparedStatement(conn, SELECT_SOFTWARE, cveId); ResultSet rsS = psS.executeQuery()) { //1 part, 2 vendor, 3 product, 4 version, 5 update_version, 6 edition, 7 lang, //8 sw_edition, 9 target_sw, 10 target_hw, 11 other, 12 versionEndExcluding, //13 versionEndIncluding, 14 versionStartExcluding, 15 versionStartIncluding, 16 vulnerable @@ -810,13 +843,13 @@ public Vulnerability getVulnerability(String cve, Connection conn) throws Databa */ public void updateVulnerability(DefCveItem cve, String baseEcosystem) { clearCache(); - final String cveId = cve.getCve().getCVEDataMeta().getId(); + final String cveId = cve.getCve().getId(); try { - final String description = cveItemConverter.extractDescription(cve); - if (cveItemConverter.isRejected(description)) { + if (cve.getCve().getVulnStatus().toUpperCase().startsWith("REJECT")) { deleteVulnerability(cveId); } else { if (cveItemConverter.testCveCpeStartWithFilter(cve)) { + final String description = cveItemConverter.extractDescription(cve); final int vulnerabilityId = updateOrInsertVulnerability(cve, description); updateVulnerabilityInsertCwe(vulnerabilityId, cve); updateVulnerabilityInsertReferences(vulnerabilityId, cve); @@ -825,7 +858,6 @@ public void updateVulnerability(DefCveItem cve, String baseEcosystem) { updateVulnerabilityInsertSoftware(vulnerabilityId, cveId, software, baseEcosystem); } } - } catch (SQLException ex) { final String msg = String.format("Error updating '%s'", cveId); LOGGER.debug(msg, ex); @@ -839,9 +871,7 @@ public void updateVulnerability(DefCveItem cve, String baseEcosystem) { private void loadCpeEcosystemCache() { final Map, String> map = new HashMap<>(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ECOSYSTEM); - ResultSet rs = ps.executeQuery()) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_CPE_ECOSYSTEM); ResultSet rs = ps.executeQuery()) { while (rs.next()) { final Pair key = new Pair<>(rs.getString(1), rs.getString(2)); final String value = rs.getString(3); @@ -858,8 +888,7 @@ private void loadCpeEcosystemCache() { private void saveCpeEcosystemCache() { final Map, String> map = CpeEcosystemCache.getChanged(); if (map != null && !map.isEmpty()) { - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, MERGE_CPE_ECOSYSTEM)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, MERGE_CPE_ECOSYSTEM)) { for (Map.Entry, String> entry : map.entrySet()) { ps.setString(1, entry.getKey().getLeft()); ps.setString(2, entry.getKey().getRight()); @@ -894,8 +923,7 @@ private int updateOrInsertVulnerability(DefCveItem cve, String description) { loadCpeEcosystemCache(); } final int vulnerabilityId; - try (Connection conn = databaseManager.getConnection(); - PreparedStatement callUpdate = getPreparedStatement(conn, UPDATE_VULNERABILITY)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement callUpdate = getPreparedStatement(conn, UPDATE_VULNERABILITY)) { // String 1.cve, String 2.description, String 3.v2Severity, Float 4.v2ExploitabilityScore, // Float 5.v2ImpactScore, Boolean 6.v2AcInsufInfo, Boolean 7.v2ObtainAllPrivilege, // Boolean 8.v2ObtainUserPrivilege, Boolean 9.v2ObtainOtherPrivilege, Boolean 10.v2UserInteractionRequired, @@ -906,28 +934,30 @@ private int updateOrInsertVulnerability(DefCveItem cve, String description) { // String 23.v3PrivilegesRequired, String 24.v3UserInteraction, String 25.v3Scope, // String 26.v3ConfidentialityImpact, String 27.v3IntegrityImpact, String 28.v3AvailabilityImpact, // Float 29.v3BaseScore, String 30.v3BaseSeverity, String 31.v3Version - callUpdate.setString(1, cve.getCve().getCVEDataMeta().getId()); + callUpdate.setString(1, cve.getCve().getId()); callUpdate.setString(2, description); - if (cve.getImpact().getBaseMetricV2() != null) { - final BaseMetricV2 cvssv2 = cve.getImpact().getBaseMetricV2(); - Map props = cvssv2.getAdditionalProperties(); - callUpdate.setString(3, cvssv2.getSeverity()); - setFloatValue(callUpdate, 4, props, "exploitabilityScore"); - setFloatValue(callUpdate, 5, props, "impactScore"); - setBooleanValue(callUpdate, 6, props, "acInsufInfo"); - setBooleanValue(callUpdate, 7, props, "obtainAllPrivilege"); - setBooleanValue(callUpdate, 8, props, "obtainUserPrivilege"); - setBooleanValue(callUpdate, 9, props, "obtainOtherPrivilege"); - setBooleanValue(callUpdate, 10, props, "userInteractionRequired"); - callUpdate.setFloat(11, cvssv2.getCvssV2().getBaseScore().floatValue()); - callUpdate.setString(12, cvssv2.getCvssV2().getAccessVector().value()); - callUpdate.setString(13, cvssv2.getCvssV2().getAccessComplexity().value()); - callUpdate.setString(14, cvssv2.getCvssV2().getAuthentication().value()); - callUpdate.setString(15, cvssv2.getCvssV2().getConfidentialityImpact().value()); - callUpdate.setString(16, cvssv2.getCvssV2().getIntegrityImpact().value()); - callUpdate.setString(17, cvssv2.getCvssV2().getAvailabilityImpact().value()); - props = cvssv2.getCvssV2().getAdditionalProperties(); - setStringValue(callUpdate, 18, props, "version"); + Optional optCvssv2 = null; + if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV2() != null) { + optCvssv2 = cve.getCve().getMetrics().getCvssMetricV2().stream().sorted(Comparator.comparing(CvssV2::getType)).findFirst(); + } + if (optCvssv2 != null && optCvssv2.isPresent()) { + CvssV2 cvssv2 = optCvssv2.get(); + setUpdateColumn(callUpdate, 3, cvssv2.getBaseSeverity()); + setUpdateColumn(callUpdate, 4, cvssv2.getExploitabilityScore()); + setUpdateColumn(callUpdate, 5, cvssv2.getImpactScore()); + setUpdateColumn(callUpdate, 6, cvssv2.getAcInsufInfo()); + setUpdateColumn(callUpdate, 7, cvssv2.getObtainAllPrivilege()); + setUpdateColumn(callUpdate, 8, cvssv2.getObtainUserPrivilege()); + setUpdateColumn(callUpdate, 9, cvssv2.getObtainOtherPrivilege()); + setUpdateColumn(callUpdate, 10, cvssv2.getUserInteractionRequired()); + setUpdateColumn(callUpdate, 11, cvssv2.getCvssData().getBaseScore()); + setUpdateColumn(callUpdate, 12, cvssv2.getCvssData().getAccessVector()); + setUpdateColumn(callUpdate, 13, cvssv2.getCvssData().getAccessComplexity()); + setUpdateColumn(callUpdate, 14, cvssv2.getCvssData().getAuthentication()); + setUpdateColumn(callUpdate, 15, cvssv2.getCvssData().getConfidentialityImpact()); + setUpdateColumn(callUpdate, 16, cvssv2.getCvssData().getIntegrityImpact()); + setUpdateColumn(callUpdate, 17, cvssv2.getCvssData().getAvailabilityImpact()); + setUpdateColumn(callUpdate, 18, cvssv2.getCvssData().getVersion()); } else { callUpdate.setNull(3, java.sql.Types.NULL); callUpdate.setNull(4, java.sql.Types.NULL); @@ -946,25 +976,36 @@ private int updateOrInsertVulnerability(DefCveItem cve, String description) { callUpdate.setNull(17, java.sql.Types.NULL); callUpdate.setNull(18, java.sql.Types.NULL); } - if (cve.getImpact().getBaseMetricV3() != null) { - final BaseMetricV3 cvssv3 = cve.getImpact().getBaseMetricV3(); - Map props = cvssv3.getAdditionalProperties(); - setFloatValue(callUpdate, 19, props, "exploitabilityScore"); - setFloatValue(callUpdate, 20, props, "impactScore"); - - callUpdate.setString(21, cvssv3.getCvssV3().getAttackVector().value()); - callUpdate.setString(22, cvssv3.getCvssV3().getAttackComplexity().value()); - callUpdate.setString(23, cvssv3.getCvssV3().getPrivilegesRequired().value()); - callUpdate.setString(24, cvssv3.getCvssV3().getUserInteraction().value()); - callUpdate.setString(25, cvssv3.getCvssV3().getScope().value()); - callUpdate.setString(26, cvssv3.getCvssV3().getConfidentialityImpact().value()); - callUpdate.setString(27, cvssv3.getCvssV3().getIntegrityImpact().value()); - callUpdate.setString(28, cvssv3.getCvssV3().getAvailabilityImpact().value()); - callUpdate.setFloat(29, cvssv3.getCvssV3().getBaseScore().floatValue()); - callUpdate.setString(30, cvssv3.getCvssV3().getBaseSeverity().value()); - - props = cvssv3.getCvssV3().getAdditionalProperties(); - setStringValue(callUpdate, 31, props, "version"); + Optional optCvssv30 = null; + if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV30() != null) { + optCvssv30 = cve.getCve().getMetrics().getCvssMetricV30().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst(); + } + Optional optCvssv31 = null; + if (cve.getCve().getMetrics() != null && cve.getCve().getMetrics().getCvssMetricV31() != null) { + optCvssv31 = cve.getCve().getMetrics().getCvssMetricV31().stream().sorted(Comparator.comparing(CvssV3::getType)).findFirst(); + } + + CvssV3 cvssv3 = null; + if (optCvssv31 != null && optCvssv31.isPresent()) { + cvssv3 = optCvssv31.get(); + } else if (optCvssv30 != null && optCvssv30.isPresent()) { + cvssv3 = optCvssv30.get(); + } + if (cvssv3 != null) { + setUpdateColumn(callUpdate, 19, cvssv3.getExploitabilityScore()); + setUpdateColumn(callUpdate, 20, cvssv3.getImpactScore()); + + setUpdateColumn(callUpdate, 21, cvssv3.getCvssData().getAttackVector()); + setUpdateColumn(callUpdate, 22, cvssv3.getCvssData().getAttackComplexity()); + setUpdateColumn(callUpdate, 23, cvssv3.getCvssData().getPrivilegesRequired()); + setUpdateColumn(callUpdate, 24, cvssv3.getCvssData().getUserInteraction()); + setUpdateColumn(callUpdate, 25, cvssv3.getCvssData().getScope()); + setUpdateColumn(callUpdate, 26, cvssv3.getCvssData().getConfidentialityImpact()); + setUpdateColumn(callUpdate, 27, cvssv3.getCvssData().getIntegrityImpact()); + setUpdateColumn(callUpdate, 28, cvssv3.getCvssData().getAvailabilityImpact()); + setUpdateColumn(callUpdate, 29, cvssv3.getCvssData().getBaseScore()); + setUpdateColumn(callUpdate, 30, cvssv3.getCvssData().getBaseSeverity()); + setUpdateColumn(callUpdate, 30, cvssv3.getCvssData().getVersion()); } else { callUpdate.setNull(19, java.sql.Types.NULL); callUpdate.setNull(20, java.sql.Types.NULL); @@ -987,7 +1028,7 @@ private int updateOrInsertVulnerability(DefCveItem cve, String description) { cs.executeUpdate(); vulnerabilityId = cs.getInt(32); } catch (SQLException ex) { - final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getCVEDataMeta().getId()); + final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId()); throw new DatabaseException(msg, ex); } } else { @@ -995,7 +1036,7 @@ private int updateOrInsertVulnerability(DefCveItem cve, String description) { rs.next(); vulnerabilityId = rs.getInt(1); } catch (SQLException ex) { - final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getCVEDataMeta().getId()); + final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", cve.getCve().getId()); throw new DatabaseException(msg, ex); } } @@ -1013,10 +1054,9 @@ private int updateOrInsertVulnerability(DefCveItem cve, String description) { * @throws SQLException thrown if there is an error inserting the data */ private void updateVulnerabilityInsertCwe(int vulnerabilityId, DefCveItem cve) throws SQLException { - try (Connection conn = databaseManager.getConnection(); - PreparedStatement insertCWE = getPreparedStatement(conn, INSERT_CWE, vulnerabilityId)) { - for (ProblemtypeDatum datum : cve.getCve().getProblemtype().getProblemtypeData()) { - for (LangString desc : datum.getDescription()) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement insertCWE = getPreparedStatement(conn, INSERT_CWE, vulnerabilityId)) { + for (Weakness weakness : cve.getCve().getWeaknesses()) { + for (LangString desc : weakness.getDescription()) { if ("en".equals(desc.getLang())) { insertCWE.setString(2, desc.getValue()); if (isBatchInsertEnabled()) { @@ -1042,8 +1082,7 @@ private void updateVulnerabilityInsertCwe(int vulnerabilityId, DefCveItem cve) t * vulnerability */ private void deleteVulnerability(String cve) throws SQLException { - try (Connection conn = databaseManager.getConnection(); - PreparedStatement deleteVulnerability = getPreparedStatement(conn, DELETE_VULNERABILITY, cve)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement deleteVulnerability = getPreparedStatement(conn, DELETE_VULNERABILITY, cve)) { deleteVulnerability.executeUpdate(); } } @@ -1058,8 +1097,7 @@ private void deleteVulnerability(String cve) throws SQLException { public void updateKnownExploitedVulnerabilities( List vulnerabilities) throws DatabaseException, SQLException { - try (Connection conn = databaseManager.getConnection(); - PreparedStatement mergeKnownVulnerability = getPreparedStatement(conn, MERGE_KNOWN_EXPLOITED)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement mergeKnownVulnerability = getPreparedStatement(conn, MERGE_KNOWN_EXPLOITED)) { int ctr = 0; for (org.owasp.dependencycheck.data.knownexploited.json.Vulnerability v : vulnerabilities) { mergeKnownVulnerability.setString(1, v.getCveID()); @@ -1112,8 +1150,7 @@ public void updateKnownExploitedVulnerabilities( private void updateVulnerabilityInsertSoftware(int vulnerabilityId, String cveId, List software, String baseEcosystem) throws DatabaseException, SQLException { - try (Connection conn = databaseManager.getConnection(); - PreparedStatement insertSoftware = getPreparedStatement(conn, INSERT_SOFTWARE)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement insertSoftware = getPreparedStatement(conn, INSERT_SOFTWARE)) { for (VulnerableSoftware parsedCpe : software) { insertSoftware.setInt(1, vulnerabilityId); insertSoftware.setString(2, parsedCpe.getPart().getAbbreviation()); @@ -1167,14 +1204,29 @@ private void updateVulnerabilityInsertSoftware(int vulnerabilityId, String cveId * @throws SQLException thrown if there is an error inserting the data */ private void updateVulnerabilityInsertReferences(int vulnerabilityId, DefCveItem cve) throws SQLException { - try (Connection conn = databaseManager.getConnection(); - PreparedStatement insertReference = getPreparedStatement(conn, INSERT_REFERENCE)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement insertReference = getPreparedStatement(conn, INSERT_REFERENCE)) { if (cve.getCve().getReferences() != null) { - for (Reference r : cve.getCve().getReferences().getReferenceData()) { + for (Reference r : cve.getCve().getReferences()) { insertReference.setInt(1, vulnerabilityId); - insertReference.setString(2, r.getName()); - insertReference.setString(3, r.getUrl()); - insertReference.setString(4, r.getRefsource()); + Optional name = null; + if (r.getTags() != null) { + name = r.getTags().stream().sorted().findFirst(); + } + if (name != null && name.isPresent()) { + insertReference.setString(2, name.get()); + } else { + insertReference.setNull(2, java.sql.Types.VARCHAR); + } + if (r.getUrl() != null && !r.getUrl().isEmpty()) { + insertReference.setString(3, r.getUrl()); + } else { + insertReference.setNull(3, java.sql.Types.VARCHAR); + } + if (r.getSource() != null && !r.getSource().isEmpty()) { + insertReference.setString(4, r.getSource()); + } else { + insertReference.setNull(4, java.sql.Types.VARCHAR); + } if (isBatchInsertEnabled()) { insertReference.addBatch(); } else { @@ -1198,20 +1250,23 @@ private void updateVulnerabilityInsertReferences(int vulnerabilityId, DefCveItem */ private List parseCpes(DefCveItem cve) throws CpeValidationException { final List software = new ArrayList<>(); - final List cpeEntries = cve.getConfigurations().getNodes().stream() - .collect(NodeFlatteningCollector.getInstance()) - .collect(CpeMatchStreamCollector.getInstance()) - .filter(predicate -> predicate.getCpe23Uri() != null) - .filter(predicate -> predicate.getCpe23Uri().startsWith(cpeStartsWithFilter)) + + final List cpeEntries = cve.getCve().getConfigurations().stream() + .map(Config::getNodes) + .flatMap(List::stream) + .map(Node::getCpeMatch) + .flatMap(List::stream) + .filter(predicate -> predicate.getCriteria() != null) + .filter(predicate -> predicate.getCriteria().startsWith(cpeStartsWithFilter)) //this single CPE entry causes nearly 100% FP - so filtering it at the source. - .filter(entry -> !("CVE-2009-0754".equals(cve.getCve().getCVEDataMeta().getId()) - && "cpe:2.3:a:apache:apache:*:*:*:*:*:*:*:*".equals(entry.getCpe23Uri()))) + .filter(entry -> !("CVE-2009-0754".equals(cve.getCve().getId()) + && "cpe:2.3:a:apache:apache:*:*:*:*:*:*:*:*".equals(entry.getCriteria()))) .collect(Collectors.toList()); final VulnerableSoftwareBuilder builder = new VulnerableSoftwareBuilder(); try { cpeEntries.forEach(entry -> { - builder.cpe(parseCpe(entry, cve.getCve().getCVEDataMeta().getId())) + builder.cpe(parseCpe(entry, cve.getCve().getId())) .versionEndExcluding(entry.getVersionEndExcluding()) .versionStartExcluding(entry.getVersionStartExcluding()) .versionEndIncluding(entry.getVersionEndIncluding()) @@ -1240,22 +1295,14 @@ private List parseCpes(DefCveItem cve) throws CpeValidationE * @throws DatabaseException thrown if there is an error converting the * CpeMatch into a CPE object */ - private Cpe parseCpe(DefCpeMatch cpe, String cveId) throws DatabaseException { + private Cpe parseCpe(CpeMatch cpe, String cveId) throws DatabaseException { Cpe parsedCpe; try { //the replace is a hack as the NVD does not properly escape backslashes in their JSON - parsedCpe = CpeParser.parse(cpe.getCpe23Uri(), true); + parsedCpe = CpeParser.parse(cpe.getCriteria(), true); } catch (CpeParsingException ex) { - LOGGER.debug("NVD (" + cveId + ") contain an invalid 2.3 CPE: " + cpe.getCpe23Uri()); - if (cpe.getCpe22Uri() != null && !cpe.getCpe22Uri().isEmpty()) { - try { - parsedCpe = CpeParser.parse(cpe.getCpe22Uri(), true); - } catch (CpeParsingException ex2) { - throw new DatabaseException("Unable to parse CPE: " + cpe.getCpe23Uri(), ex); - } - } else { - throw new DatabaseException("Unable to parse CPE: " + cpe.getCpe23Uri(), ex); - } + LOGGER.debug("NVD (" + cveId + ") contain an invalid 2.3 CPE: " + cpe.getCriteria()); + throw new DatabaseException("Unable to parse CPE: " + cpe.getCriteria(), ex); } return parsedCpe; } @@ -1321,9 +1368,7 @@ private void executeBatch(String vulnId, PreparedStatement statement) * @return true if data exists; otherwise false */ public boolean dataExists() { - try (Connection conn = databaseManager.getConnection(); - PreparedStatement cs = getPreparedStatement(conn, COUNT_CPE); - ResultSet rs = cs.executeQuery()) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement cs = getPreparedStatement(conn, COUNT_CPE); ResultSet rs = cs.executeQuery()) { if (rs.next() && rs.getInt(1) > 0) { return true; } @@ -1352,10 +1397,7 @@ public boolean dataExists() { public void cleanupDatabase() { LOGGER.info("Begin database maintenance"); final long start = System.currentTimeMillis(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement psOrphans = getPreparedStatement(conn, CLEANUP_ORPHANS); - PreparedStatement psEcosystem = getPreparedStatement(conn, UPDATE_ECOSYSTEM); - PreparedStatement psEcosystem2 = getPreparedStatement(conn, UPDATE_ECOSYSTEM2)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement psOrphans = getPreparedStatement(conn, CLEANUP_ORPHANS); PreparedStatement psEcosystem = getPreparedStatement(conn, UPDATE_ECOSYSTEM); PreparedStatement psEcosystem2 = getPreparedStatement(conn, UPDATE_ECOSYSTEM2)) { if (psEcosystem != null) { final int count = psEcosystem.executeUpdate(); if (count > 0) { @@ -1399,8 +1441,7 @@ public void persistEcosystemCache() { public void defrag() { if (isH2) { final long start = System.currentTimeMillis(); - try (Connection conn = databaseManager.getConnection(); - CallableStatement psCompaxt = conn.prepareCall("SHUTDOWN DEFRAG")) { + try (Connection conn = databaseManager.getConnection(); CallableStatement psCompaxt = conn.prepareCall("SHUTDOWN DEFRAG")) { LOGGER.info("Begin database defrag"); psCompaxt.execute(); final long millis = System.currentTimeMillis() - start; @@ -1447,8 +1488,7 @@ VulnerableSoftware getMatchingSoftware(Cpe cpe, Set vulnerab */ public void deleteUnusedCpe() { clearCache(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, DELETE_UNUSED_DICT_CPE)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, DELETE_UNUSED_DICT_CPE)) { ps.executeUpdate(); } catch (SQLException ex) { LOGGER.error("Unable to delete CPE dictionary entries", ex); @@ -1468,8 +1508,7 @@ public void deleteUnusedCpe() { */ public void addCpe(String cpe, String vendor, String product) { clearCache(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, ADD_DICT_CPE)) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, ADD_DICT_CPE)) { ps.setString(1, cpe); ps.setString(2, vendor); ps.setString(3, product); @@ -1487,13 +1526,11 @@ public void addCpe(String cpe, String vendor, String product) { public Map getknownExploitedVulnerabilities() { final Map known = new HashMap<>(); - try (Connection conn = databaseManager.getConnection(); - PreparedStatement ps = getPreparedStatement(conn, SELECT_KNOWN_EXPLOITED_VULNERABILITIES); - ResultSet rs = ps.executeQuery()) { + try (Connection conn = databaseManager.getConnection(); PreparedStatement ps = getPreparedStatement(conn, SELECT_KNOWN_EXPLOITED_VULNERABILITIES); ResultSet rs = ps.executeQuery()) { while (rs.next()) { - final org.owasp.dependencycheck.data.knownexploited.json.Vulnerability kev = - new org.owasp.dependencycheck.data.knownexploited.json.Vulnerability(); + final org.owasp.dependencycheck.data.knownexploited.json.Vulnerability kev + = new org.owasp.dependencycheck.data.knownexploited.json.Vulnerability(); kev.setCveID(rs.getString(1)); kev.setVendorProject(rs.getString(2)); kev.setProduct(rs.getString(3)); @@ -1528,6 +1565,134 @@ private void addNullableStringParameter(PreparedStatement ps, int pos, String va } } + private void setUpdateColumn(PreparedStatement ps, int i, Double value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setDouble(i, value); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AuthenticationType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.CiaType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.Version value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessComplexityType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV2Data.AccessVectorType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, String value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, Boolean value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setBoolean(i, value); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackVectorType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.AttackComplexityType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.PrivilegesRequiredType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.UserInteractionType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.ScopeType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.SeverityType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.CiaType value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + + private void setUpdateColumn(PreparedStatement ps, int i, CvssV3Data.Version value) throws SQLException { + if (value == null) { + ps.setNull(i, java.sql.Types.NULL); + } else { + ps.setString(i, value.value()); + } + } + /** * Sets the float parameter on a prepared statement from a properties map. * diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveItemOperator.java b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveItemOperator.java index 87866186e72..092b1904def 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveItemOperator.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveItemOperator.java @@ -17,13 +17,14 @@ */ package org.owasp.dependencycheck.data.nvdcve; +import io.github.jeremylong.openvulnerability.client.nvd.Config; import java.util.stream.Collectors; import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem; -import org.owasp.dependencycheck.data.nvd.json.CpeMatchStreamCollector; -import org.owasp.dependencycheck.data.nvd.json.DefCveItem; -import org.owasp.dependencycheck.data.nvd.json.LangString; -import org.owasp.dependencycheck.data.nvd.json.NodeFlatteningCollector; +import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem; +import io.github.jeremylong.openvulnerability.client.nvd.LangString; +import io.github.jeremylong.openvulnerability.client.nvd.Node; +import java.util.List; import org.owasp.dependencycheck.dependency.VulnerableSoftware; /** @@ -57,7 +58,7 @@ public CveItemOperator(String cpeStartsWithFilter) { * @return the English descriptions from the CVE object */ public String extractDescription(DefCveItem cve) { - return cve.getCve().getDescription().getDescriptionData().stream().filter((desc) + return cve.getCve().getDescriptions().stream().filter((desc) -> "en".equals(desc.getLang())).map(LangString::getValue).collect(Collectors.joining(" ")); } @@ -217,10 +218,12 @@ public boolean isRejected(String description) { */ protected boolean testCveCpeStartWithFilter(final DefCveItem cve) { //cycle through to see if this is a CPE we care about (use the CPE filters - return cve.getConfigurations().getNodes().stream() - .collect(NodeFlatteningCollector.getInstance()) - .collect(CpeMatchStreamCollector.getInstance()) - .filter(cpe -> cpe.getCpe23Uri() != null) - .anyMatch(cpe -> cpe.getCpe23Uri().startsWith(cpeStartsWithFilter)); + return cve.getCve().getConfigurations().stream() + .map(Config::getNodes) + .flatMap(List::stream) + .map(Node::getCpeMatch) + .flatMap(List::stream) + .filter(cpe -> cpe.getCriteria()!= null) + .anyMatch(cpe -> cpe.getCriteria().startsWith(cpeStartsWithFilter)); } } diff --git a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java index f1ef704d3f1..a039ff96aa8 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java @@ -45,6 +45,20 @@ public class DatabaseProperties { * The Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseProperties.class); + + /** + * The last modified request data for the NVD API. + */ + public static final String NVD_API_LAST_MODIFIED = "nvd.api.last.modified"; + /** + * The properties file key for the last checked field - used to store the + * last check time. + */ + public static final String NVD_LAST_CHECKED = "nvd.api.last.checked"; + + public static final String NVD_CACHE_LAST_MODIFIED_BASE = "nvd.cache.last.modified"; + + // TODO DELETE START-------------------------------------------------------- /** * Modified key word, used as a key to store information about the modified * file (i.e. the containing the last 8 days of updates).. @@ -66,6 +80,8 @@ public class DatabaseProperties { * days of the last update. */ public static final String LAST_UPDATED_BASE = "NVD CVE "; + + // TODO DELETE END--------------------------------------------------------- /** * The key for the last time the CPE data was updated. */ @@ -198,4 +214,30 @@ public synchronized Map getMetaData() { } return map; } + + /** + * Retrieves a zoned date time. + * + * @param key the property key + * @return the zoned date time + */ + public ZonedDateTime getTimestamp(String key) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX"); + if (properties.contains(key)) { + String value = properties.getProperty(key); + return ZonedDateTime.parse(value, dtf); + } + return null; + } + + /** + * Stores a timestamp. + * + * @param key the property key + * @param timestamp the zoned date time + */ + public void save(String key, ZonedDateTime timestamp) throws UpdateException { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX"); + save(key, dtf.format(timestamp)); + } } diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/NvdApiDataSource.java b/core/src/main/java/org/owasp/dependencycheck/data/update/NvdApiDataSource.java new file mode 100644 index 00000000000..6250e5a0ee2 --- /dev/null +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/NvdApiDataSource.java @@ -0,0 +1,197 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2023 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.data.update; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem; +import io.github.jeremylong.openvulnerability.client.nvd.NvdApiException; +import io.github.jeremylong.openvulnerability.client.nvd.NvdCveClient; +import io.github.jeremylong.openvulnerability.client.nvd.NvdCveClientBuilder; +import java.io.File; +import java.io.IOException; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.data.nvdcve.CveDB; +import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; +import org.owasp.dependencycheck.data.update.exception.UpdateException; +import org.owasp.dependencycheck.data.update.nvd.api.NvdApiProcessor; +import org.owasp.dependencycheck.utils.InvalidSettingException; +import org.owasp.dependencycheck.utils.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Jeremy Long + */ +public class NvdApiDataSource implements CachedWebDataSource { + + /** + * The logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(NvdApiDataSource.class); + /** + * The thread pool size to use for CPU-intense tasks. + */ + private static final int PROCESSING_THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors(); + /** + * The configured settings. + */ + private Settings settings; + /** + * Reference to the DAO. + */ + private CveDB cveDb = null; + /** + * The properties obtained from the database. + */ + private DatabaseProperties dbProperties = null; + + @Override + public boolean update(Engine engine) throws UpdateException { + this.settings = engine.getSettings(); + this.cveDb = engine.getDatabase(); + if (isUpdateConfiguredFalse()) { + return false; + } + dbProperties = cveDb.getDatabaseProperties(); + + ZonedDateTime lastModifiedRequest = dbProperties.getTimestamp(DatabaseProperties.NVD_API_LAST_MODIFIED); + NvdCveClientBuilder builder = NvdCveClientBuilder.aNvdCveApi(); + if (lastModifiedRequest != null) { + ZonedDateTime end = lastModifiedRequest.minusDays(-120); + builder.withLastModifiedFilter(lastModifiedRequest, end); + } + String key = settings.getString(Settings.KEYS.NVD_API_KEY); + if (key != null) { + builder.withApiKey(key) + .withDelay(3000) + .withThreadCount(4); + } + ExecutorService processingExecutorService = null; + try { + processingExecutorService = Executors.newFixedThreadPool(PROCESSING_THREAD_POOL_SIZE); + List> submitted = new ArrayList<>(); + int errorCount = 0; + try (NvdCveClient api = builder.build()) { + while (api.hasNext()) { + try { + Collection items = api.next(); + if (items != null && !items.isEmpty()) { + Future f = processingExecutorService.submit(new NvdApiProcessor(cveDb, items)); + submitted.add(f); + errorCount = 0; + } + } catch (NvdApiException ex) { + if (ex.getCause() instanceof JsonProcessingException) { + errorCount += 1; + if (errorCount > 10) { + throw ex; + } + } else { + throw ex; + } + } + } + lastModifiedRequest = api.getLastUpdated(); + } catch (Exception e) { + throw new UpdateException("Error updating the NVD Data", e); + } + + try { + for (Future f : submitted) { + LOGGER.error("updating"); + while (!f.isDone()) { + Thread.sleep(500); + } + } + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new UpdateException(ex); + } + dbProperties.save(DatabaseProperties.NVD_API_LAST_MODIFIED, lastModifiedRequest); + return true; + } finally { + if (processingExecutorService != null) { + processingExecutorService.shutdownNow(); + } + } + } + + /** + * Checks if the system is configured NOT to update. + * + * @return false if the system is configured to perform an update; otherwise + * true + */ + private boolean isUpdateConfiguredFalse() { + if (!settings.getBoolean(Settings.KEYS.UPDATE_NVDCVE_ENABLED, true)) { + return true; + } + boolean autoUpdate = true; + try { + autoUpdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE); + } catch (InvalidSettingException ex) { + LOGGER.debug("Invalid setting for auto-update; using true."); + } + return !autoUpdate; + } + + @Override + public boolean purge(Engine engine) { + boolean result = true; + try { + final File dataDir = engine.getSettings().getDataDirectory(); + final File db = new File(dataDir, engine.getSettings().getString(Settings.KEYS.DB_FILE_NAME, "odc.mv.db")); + if (db.exists()) { + if (db.delete()) { + LOGGER.info("Database file purged; local copy of the NVD has been removed"); + } else { + LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath()); + result = false; + } + } else { + LOGGER.info("Unable to purge database; the database file does not exist: {}", db.getAbsolutePath()); + result = false; + } + final File traceFile = new File(dataDir, "odc.trace.db"); + if (traceFile.exists() && !traceFile.delete()) { + LOGGER.error("Unable to delete '{}'; please delete the file manually", traceFile.getAbsolutePath()); + result = false; + } + final File lockFile = new File(dataDir, "odc.update.lock"); + if (lockFile.exists() && !lockFile.delete()) { + LOGGER.error("Unable to delete '{}'; please delete the file manually", lockFile.getAbsolutePath()); + result = false; + } + } catch (IOException ex) { + final String msg = "Unable to delete the database"; + LOGGER.error(msg, ex); + result = false; + } + return result; + } + +} diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CpeEcosystemCache.java b/core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CpeEcosystemCache.java index 671b7bd009b..71d61cf2185 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CpeEcosystemCache.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CpeEcosystemCache.java @@ -20,7 +20,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; -import org.owasp.dependencycheck.data.update.nvd.NvdCveParser; + import org.owasp.dependencycheck.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +50,7 @@ private CpeEcosystemCache() { /** * The logger. */ - private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveParser.class); + private static final Logger LOGGER = LoggerFactory.getLogger(CpeEcosystemCache.class); //CSOFF: EmptyBlock /** diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveParser.java b/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveParser.java deleted file mode 100644 index a1451eaf919..00000000000 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveParser.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This file is part of dependency-check-core. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright (c) 2018 Steve Springett. All Rights Reserved. - */ -package org.owasp.dependencycheck.data.update.nvd; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.module.blackbird.BlackbirdModule; -import com.fasterxml.jackson.module.afterburner.AfterburnerModule; - -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import static java.nio.charset.StandardCharsets.UTF_8; -import java.util.zip.GZIPInputStream; -import java.util.zip.ZipException; - -import org.owasp.dependencycheck.data.nvdcve.CveDB; -import org.owasp.dependencycheck.data.update.exception.CorruptedDatastreamException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.owasp.dependencycheck.data.nvd.json.DefCveItem; -import org.owasp.dependencycheck.data.nvd.ecosystem.CveEcosystemMapper; -import org.owasp.dependencycheck.data.update.exception.UpdateException; -import org.owasp.dependencycheck.utils.Settings; -import org.owasp.dependencycheck.utils.Utils; - -/** - * Parser and processor of NVD CVE JSON data feeds. - * - * @author Jeremy Long - */ -public final class NvdCveParser { - - /** - * The logger. - */ - private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveParser.class); - /** - * A reference to the CVE DB. - */ - private final CveDB cveDB; - /** - * A reference to the ODC settings. - */ - private final Settings settings; - - /** - * Creates a new NVD CVE JSON Parser. - * - * @param settings the dependency-check settings - * @param db a reference to the database - */ - public NvdCveParser(Settings settings, CveDB db) { - this.settings = settings; - this.cveDB = db; - } - - /** - * Parses the NVD JSON file and inserts/updates data into the database. - * - * @param file the NVD JSON file to parse - * @throws UpdateException thrown if the file could not be read - * @throws CorruptedDatastreamException thrown if the file was found to be a - * corrupted download (ZipException or premature EOF) - */ - public void parse(File file) throws UpdateException, CorruptedDatastreamException { - LOGGER.debug("Parsing " + file.getName()); - - final Module module; - if (Utils.getJavaVersion() <= 8) { - module = new AfterburnerModule(); - } else { - module = new BlackbirdModule(); - } - final ObjectMapper objectMapper = JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .addModule(module) - .build(); - - final ObjectReader objectReader = objectMapper.readerFor(DefCveItem.class); - - try (InputStream fin = new FileInputStream(file); - InputStream in = new GZIPInputStream(fin); - InputStreamReader isr = new InputStreamReader(in, UTF_8); - JsonParser parser = objectReader.getFactory().createParser(isr)) { - - final CveEcosystemMapper mapper = new CveEcosystemMapper(); - init(parser); - while (parser.nextToken() == JsonToken.START_OBJECT) { - final DefCveItem cve = objectReader.readValue(parser); - cveDB.updateVulnerability(cve, mapper.getEcosystem(cve)); - } - } catch (FileNotFoundException ex) { - LOGGER.error(ex.getMessage()); - throw new UpdateException("Unable to find the NVD CVE file, `" + file + "`, to parse", ex); - } catch (ZipException | EOFException ex) { - throw new CorruptedDatastreamException("Error parsing NVD CVE file", ex); - } catch (IOException ex) { - LOGGER.error("Error reading NVD JSON data: {}", file); - LOGGER.debug("Error extracting the NVD JSON data from: " + file, ex); - throw new UpdateException("Unable to find the NVD CVE file to parse", ex); - } - } - - void init(JsonParser parser) throws IOException { - JsonToken nextToken = parser.nextToken(); - if (nextToken != JsonToken.START_OBJECT) { - throw new IOException("Expected " + JsonToken.START_OBJECT + ", got " + nextToken); - } - - do { - nextToken = parser.nextToken(); - if (nextToken == null) { - break; - } - - if (nextToken.isStructStart()) { - if (nextToken == JsonToken.START_ARRAY) { - break; - } else { - parser.skipChildren(); - } - } - } while (true); - } -} diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java b/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java index a3f2e70f1d4..31b77c09f51 100644 --- a/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java @@ -137,8 +137,8 @@ public ProcessTask call() throws Exception { protected void importJSON(File file) throws ParserConfigurationException, IOException, SQLException, DatabaseException, ClassNotFoundException, UpdateException, CorruptedDatastreamException { - final NvdCveParser parser = new NvdCveParser(settings, cveDB); - parser.parse(file); +// final NvdCveParser parser = new NvdCveParser(settings, cveDB); +// parser.parse(file); } /** diff --git a/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/api/NvdApiProcessor.java b/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/api/NvdApiProcessor.java new file mode 100644 index 00000000000..46306de8cc1 --- /dev/null +++ b/core/src/main/java/org/owasp/dependencycheck/data/update/nvd/api/NvdApiProcessor.java @@ -0,0 +1,50 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2023 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.data.update.nvd.api; + +import io.github.jeremylong.openvulnerability.client.nvd.DefCveItem; +import java.util.Collection; +import java.util.concurrent.Callable; +import org.owasp.dependencycheck.data.nvd.ecosystem.CveEcosystemMapper; +import org.owasp.dependencycheck.data.nvdcve.CveDB; + +/** + * Stores a collection of NVD CVE Data from the NVD API into the database. + * + * @author Jeremy Long + */ +public class NvdApiProcessor implements Callable { + + private final CveDB cveDB; + private final Collection data; + private final CveEcosystemMapper mapper = new CveEcosystemMapper(); + + public NvdApiProcessor(final CveDB cveDB, Collection data) { + this.cveDB = cveDB; + this.data = data; + } + + @Override + public NvdApiProcessor call() throws Exception { + for (DefCveItem entry : data) { + cveDB.updateVulnerability(entry, mapper.getEcosystem(entry)); + } + return this; + } + +} diff --git a/core/src/main/java/org/owasp/dependencycheck/dependency/CvssV2.java b/core/src/main/java/org/owasp/dependencycheck/dependency/CvssV2.java deleted file mode 100644 index e82bcaf864a..00000000000 --- a/core/src/main/java/org/owasp/dependencycheck/dependency/CvssV2.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * This file is part of dependency-check-core. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright (c) 2018 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.dependency; - -import java.io.Serializable; - -/** - * CVSS V2 scoring information. - * - * @author Jeremy Long - */ -public class CvssV2 implements Serializable { - - /** - * Serial version UID. - */ - private static final long serialVersionUID = -2203955879356702367L; - - /** - * CVSS Score. - */ - private final float score; - /** - * CVSS Access Vector. - */ - private final String accessVector; - /** - * CVSS Access Complexity. - */ - private final String accessComplexity; - /** - * CVSS Authentication. - */ - private final String authentication; - /** - * CVSS Confidentiality Impact. - */ - private final String confidentialityImpact; - /** - * CVSS Integrity Impact. - */ - private final String integrityImpact; - /** - * CVSS Availability Impact. - */ - private final String availabilityImpact; - /** - * CVSS version. - */ - private final String version; - - /** - * CVSSv2 Base Metric severity. - */ - private final String severity; - /** - * CVSSv2 Base Metric exploitability score. - */ - private final Float exploitabilityScore; - /** - * CVSSv2 Base Metric impact score. - */ - private final Float impactScore; - /** - * CVSSv2 Base Metric acInsufInfo. - */ - private final Boolean acInsufInfo; - /** - * CVSSv2 Base Metric obtain all privilege. - */ - private final Boolean obtainAllPrivilege; - /** - * CVSSv2 Base Metric obtain user privilege. - */ - private final Boolean obtainUserPrivilege; - /** - * CVSSv2 Base Metric obtain other privilege. - */ - private final Boolean obtainOtherPrivilege; - /** - * CVSSv2 Base Metric user interaction required. - */ - private final Boolean userInteractionRequired; - - /** - * Constructs a new CVSS V2 object. - * - * @param score the score - * @param accessVector the access vector - * @param accessComplexity the access complexity - * @param authentication the authentication - * @param confidentialityImpact the confidentiality impact - * @param integrityImpact the integrity impact - * @param availabilityImpact the availability impact - * @param severity the severity - */ - //CSOFF: ParameterNumber - public CvssV2(float score, String accessVector, String accessComplexity, String authentication, - String confidentialityImpact, String integrityImpact, String availabilityImpact, String severity) { - this(score, accessVector, accessComplexity, authentication, confidentialityImpact, - integrityImpact, availabilityImpact, severity, null, null, null, null, null, null, null, null); - } - - /** - * Constructs a new CVSS V2 object. - * - * @param score the score - * @param accessVector the access vector - * @param accessComplexity the access complexity - * @param authentication the authentication - * @param confidentialityImpact the confidentiality impact - * @param integrityImpact the integrity impact - * @param availabilityImpact the availability impact - * @param severity the severity - * @param exploitabilityScore the exploitability score - * @param impactScore the impact score - * @param acInsufInfo the acInsufInfo - * @param obtainAllPrivilege whether or not the vulnerability allows one to obtain all privileges - * @param obtainUserPrivilege whether or not the vulnerability allows one to obtain user privileges - * @param obtainOtherPrivilege whether or not the vulnerability allows one to obtain other privileges - * @param userInteractionRequired whether or not user interaction is required - * @param version the CVSS version - */ - //CSOFF: ParameterNumber - public CvssV2(float score, String accessVector, String accessComplexity, String authentication, - String confidentialityImpact, String integrityImpact, String availabilityImpact, String severity, - Float exploitabilityScore, Float impactScore, Boolean acInsufInfo, Boolean obtainAllPrivilege, - Boolean obtainUserPrivilege, Boolean obtainOtherPrivilege, Boolean userInteractionRequired, String version) { - this.score = score; - this.accessVector = accessVector; - this.accessComplexity = accessComplexity; - this.authentication = authentication; - this.confidentialityImpact = confidentialityImpact; - this.integrityImpact = integrityImpact; - this.availabilityImpact = availabilityImpact; - - this.severity = severity; - this.exploitabilityScore = exploitabilityScore; - this.impactScore = impactScore; - this.acInsufInfo = acInsufInfo; - this.obtainAllPrivilege = obtainAllPrivilege; - this.obtainUserPrivilege = obtainUserPrivilege; - this.obtainOtherPrivilege = obtainOtherPrivilege; - this.userInteractionRequired = userInteractionRequired; - this.version = version; - } - //CSON: ParameterNumber - - /** - * Get the value of score. - * - * @return the value of score - */ - public float getScore() { - return score; - } - - /** - * Get the value of accessVector. - * - * @return the value of accessVector - */ - public String getAccessVector() { - return accessVector; - } - - /** - * Get the value of accessComplexity. - * - * @return the value of accessComplexity - */ - public String getAccessComplexity() { - return accessComplexity; - } - - /** - * Get the value of authentication. - * - * @return the value of authentication - */ - public String getAuthentication() { - return authentication; - } - - /** - * Get the value of confidentialityImpact. - * - * @return the value of confidentialityImpact - */ - public String getConfidentialityImpact() { - return confidentialityImpact; - } - - /** - * Get the value of integrityImpact. - * - * @return the value of integrityImpact - */ - public String getIntegrityImpact() { - return integrityImpact; - } - - /** - * Get the value of availabilityImpact. - * - * @return the value of availabilityImpact - */ - public String getAvailabilityImpact() { - return availabilityImpact; - } - - /** - * Get the value of version. - * - * @return the value of version - */ - public String getVersion() { - return version; - } - - /** - * Returns the severity for the vulnerability. - * - * @return the severity - */ - public String getSeverity() { - return severity; - } - - /** - * Returns the exploitabilityScore for the vulnerability. - * - * @return the exploitabilityScore - */ - public Float getExploitabilityScore() { - return exploitabilityScore; - } - - /** - * Returns the impactScore for the vulnerability. - * - * @return the impactScore - */ - public Float getImpactScore() { - return impactScore; - } - - /** - * Returns the acInsufInfo for the vulnerability. - * - * @return the acInsufInfo - */ - public Boolean isAcInsufInfo() { - return acInsufInfo; - } - - /** - * Returns the obtainAllPrivilege for the vulnerability. - * - * @return the obtainAllPrivilege - */ - public Boolean isObtainAllPrivilege() { - return obtainAllPrivilege; - } - - /** - * Returns the obtainUserPrivilege for the vulnerability. - * - * @return the obtainUserPrivilege - */ - public Boolean isObtainUserPrivilege() { - return obtainUserPrivilege; - } - - /** - * Returns the obtainOtherPrivilege for the vulnerability. - * - * @return the obtainOtherPrivilege - */ - public Boolean isObtainOtherPrivilege() { - return obtainOtherPrivilege; - } - - /** - * Returns the userInteractionRequired for the vulnerability. - * - * @return the userInteractionRequired - */ - public Boolean isUserInteractionRequired() { - return userInteractionRequired; - } - - @Override - public String toString() { - return String.format("/AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s", - accessVector == null ? "" : accessVector.substring(0, 1), - accessComplexity == null ? "" : accessComplexity.substring(0, 1), - authentication == null ? "" : authentication.substring(0, 1), - confidentialityImpact == null ? "" : confidentialityImpact.substring(0, 1), - integrityImpact == null ? "" : integrityImpact.substring(0, 1), - availabilityImpact == null ? "" : availabilityImpact.substring(0, 1)); - } -} diff --git a/core/src/main/java/org/owasp/dependencycheck/dependency/CvssV3.java b/core/src/main/java/org/owasp/dependencycheck/dependency/CvssV3.java deleted file mode 100644 index bf6c0814c2e..00000000000 --- a/core/src/main/java/org/owasp/dependencycheck/dependency/CvssV3.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * This file is part of dependency-check-core. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright (c) 2018 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.dependency; - -import org.sonatype.ossindex.service.api.cvss.Cvss3Severity; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -/** - * CVSS V3 scoring information. - * - * @author Jeremy Long - */ -public class CvssV3 implements Serializable { - - /** - * Serial version UID. - */ - private static final long serialVersionUID = -315810090425928920L; - - /** - * The CVSS v3 Base Metrics (that are required by the spec for any CVSS v3 Vector String) - */ - private static final List BASE_METRICS = Arrays.asList("AV", "AC", "PR", "UI", "S", "C", "I", "A"); - - /** - * CVSS Availability Impact. - */ - private final String attackVector; - /** - * CVSS Availability Impact. - */ - private final String attackComplexity; - /** - * CVSS Availability Impact. - */ - private final String privilegesRequired; - /** - * CVSS Availability Impact. - */ - private final String userInteraction; - /** - * CVSS Availability Impact. - */ - private final String scope; - /** - * CVSS Availability Impact. - */ - private final String confidentialityImpact; - /** - * CVSS Availability Impact. - */ - private final String integrityImpact; - /** - * CVSS Availability Impact. - */ - private final String availabilityImpact; - /** - * CVSS Base Score. - */ - private final float baseScore; - /** - * CVSS Base Severity. - */ - private final String baseSeverity; - /** - * CVSSv3 Base Metric exploitability score. - */ - private final Float exploitabilityScore; - /** - * CVSSv3 Base Metric impact score. - */ - private final Float impactScore; - /** - * CVSS version. - */ - private final String version; - - /** - * Constructs a new CVSS V3 object. - * - * @param attackVector the attack vector value - * @param attackComplexity the attack complexity value - * @param privilegesRequired the privileges required value - * @param userInteraction the user interaction value - * @param scope the scope value - * @param confidentialityImpact the confidentiality impact value - * @param integrityImpact the integrity impact value - * @param availabilityImpact the availability impact value - * @param baseScore the base score - * @param baseSeverity the base severity - */ - //CSOFF: ParameterNumber - public CvssV3(String attackVector, String attackComplexity, String privilegesRequired, - String userInteraction, String scope, String confidentialityImpact, String integrityImpact, - String availabilityImpact, float baseScore, String baseSeverity) { - this(attackVector, attackComplexity, privilegesRequired, userInteraction, scope, confidentialityImpact, - integrityImpact, availabilityImpact, baseScore, baseSeverity, null, null, null); - } - - /** - * Constructs a new CVSS V3 object. - * - * @param attackVector the attack vector value - * @param attackComplexity the attack complexity value - * @param privilegesRequired the privileges required value - * @param userInteraction the user interaction value - * @param scope the scope value - * @param confidentialityImpact the confidentiality impact value - * @param integrityImpact the integrity impact value - * @param availabilityImpact the availability impact value - * @param baseScore the base score - * @param baseSeverity the base severity - * @param exploitabilityScore the exploitability score - * @param impactScore the impact score - * @param version the CVSS version - */ - public CvssV3(String attackVector, String attackComplexity, String privilegesRequired, - String userInteraction, String scope, String confidentialityImpact, String integrityImpact, - String availabilityImpact, float baseScore, String baseSeverity, Float exploitabilityScore, Float impactScore, String version) { - this.attackVector = attackVector; - this.attackComplexity = attackComplexity; - this.privilegesRequired = privilegesRequired; - this.userInteraction = userInteraction; - this.scope = scope; - this.confidentialityImpact = confidentialityImpact; - this.integrityImpact = integrityImpact; - this.availabilityImpact = availabilityImpact; - this.baseScore = baseScore; - this.baseSeverity = baseSeverity; - this.exploitabilityScore = exploitabilityScore; - this.impactScore = impactScore; - this.version = version; - } - //CSON: ParameterNumber - - /** - * Constructs a new CVSS V3 object from a CVSS v3.x Vector String representation and - * a CVSS V3 Base score. - * - * @param vectorString a CVSS v3 Vector String - * @param baseScore the CVSS v3 base score - * @throws IllegalArgumentException when the provided vectorString is not a valid - * CVSS v3.x vector string. - */ - public CvssV3(String vectorString, float baseScore) { - if (!vectorString.startsWith("CVSS:3")) { - throw new IllegalArgumentException("Not a valid CVSSv3 vector string: " + vectorString); - } - this.version = vectorString.substring(5, vectorString.indexOf('/')); - final String[] metricStrings = vectorString.substring(vectorString.indexOf('/') + 1).split("/"); - final HashMap metrics = new HashMap<>(); - for (int i = 0; i < metricStrings.length; i++) { - final String[] metricKeyVal = metricStrings[i].split(":"); - if (metricKeyVal.length != 2) { - throw new IllegalArgumentException( - String.format("Not a valid CVSSv3 vector string '%s', invalid metric component '%s'", - vectorString, metricStrings[i])); - } - metrics.put(metricKeyVal[0], metricKeyVal[1]); - } - if (!metrics.keySet().containsAll(BASE_METRICS)) { - throw new IllegalArgumentException( - String.format("Not a valid CVSSv3 vector string '%s'; missing one or more required Base Metrics;", - vectorString)); - } - this.attackVector = metrics.get("AV"); - this.attackComplexity = metrics.get("AC"); - this.privilegesRequired = metrics.get("PR"); - this.userInteraction = metrics.get("UI"); - this.scope = metrics.get("S"); - this.confidentialityImpact = metrics.get("C"); - this.integrityImpact = metrics.get("I"); - this.availabilityImpact = metrics.get("A"); - this.baseScore = baseScore; - this.baseSeverity = Cvss3Severity.of(baseScore).name(); - this.exploitabilityScore = null; - this.impactScore = null; - } - - /** - * Get the value of attackVector. - * - * @return the value of attackVector - */ - public String getAttackVector() { - return attackVector; - } - - /** - * Get the value of attackComplexity. - * - * @return the value of attackComplexity - */ - public String getAttackComplexity() { - return attackComplexity; - } - - /** - * Get the value of privilegesRequired. - * - * @return the value of privilegesRequired - */ - public String getPrivilegesRequired() { - return privilegesRequired; - } - - /** - * Get the value of userInteraction. - * - * @return the value of userInteraction - */ - public String getUserInteraction() { - return userInteraction; - } - - /** - * Get the value of scope. - * - * @return the value of scope - */ - public String getScope() { - return scope; - } - - /** - * Get the value of confidentialityImpact. - * - * @return the value of confidentialityImpact - */ - public String getConfidentialityImpact() { - return confidentialityImpact; - } - - /** - * Get the value of integrityImpact. - * - * @return the value of integrityImpact - */ - public String getIntegrityImpact() { - return integrityImpact; - } - - /** - * Get the value of availabilityImpact. - * - * @return the value of availabilityImpact - */ - public String getAvailabilityImpact() { - return availabilityImpact; - } - - /** - * Get the value of baseScore. - * - * @return the value of baseScore - */ - public float getBaseScore() { - return baseScore; - } - - /** - * Get the value of baseSeverity. - * - * @return the value of baseSeverity - */ - public String getBaseSeverity() { - return baseSeverity; - } - - /** - * Get the value of version. - * - * @return the value of version - */ - public String getVersion() { - return version; - } - - /** - * Returns the exploitabilityScore for the vulnerability. - * - * @return the exploitabilityScore - */ - public Float getexploitabilityScore() { - return exploitabilityScore; - } - - /** - * Returns the impactScore for the vulnerability. - * - * @return the impactScore - */ - public Float getimpactScore() { - return impactScore; - } - - @Override - public String toString() { - return String.format("CVSS:%s/AV:%s/AC:%s/PR:%s/UI:%s/S:%s/C:%s/I:%s/A:%s", - version == null ? "" : version, - attackVector == null ? "" : attackVector.substring(0, 1), - attackComplexity == null ? "" : attackComplexity.substring(0, 1), - privilegesRequired == null ? "" : privilegesRequired.substring(0, 1), - userInteraction == null ? "" : userInteraction.substring(0, 1), - scope == null ? "" : scope.substring(0, 1), - confidentialityImpact == null ? "" : confidentialityImpact.substring(0, 1), - integrityImpact == null ? "" : integrityImpact.substring(0, 1), - availabilityImpact == null ? "" : availabilityImpact.substring(0, 1)); - } - -} diff --git a/core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java b/core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java index c9e1c8a71fc..e13a8257270 100644 --- a/core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java +++ b/core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java @@ -17,6 +17,8 @@ */ package org.owasp.dependencycheck.dependency; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -481,7 +483,7 @@ public int compareTo(@NotNull Vulnerability o) { * be reported as the highest severity after sorting on descending severity. *
* For vulnerabilities not scored with a CVSS score we estimate a score from - * the severity text. For textual severities assumed or sementically + * the severity text. For textual severities assumed or semantically * confirmed to be of a critical nature we assign a value in between the * highest CVSSv2 HIGH and the lowest CVSSv3 CRITICAL severity level. * @@ -490,12 +492,12 @@ public int compareTo(@NotNull Vulnerability o) { * @return A float value that allows for best-effort sorting on * vulnerability severity */ - private float bestEffortSeverityLevelForSorting() { + private Double bestEffortSeverityLevelForSorting() { if (this.cvssV3 != null) { - return SeverityUtil.sortAdjustedCVSSv3BaseScore(this.cvssV3.getBaseScore()); + return SeverityUtil.sortAdjustedCVSSv3BaseScore(this.cvssV3.getCvssData().getBaseScore()); } if (this.cvssV2 != null) { - return this.cvssV2.getScore(); + return this.cvssV2.getCvssData().getBaseScore(); } return SeverityUtil.estimatedSortAdjustedCVSSv3(this.unscoredSeverity); } @@ -509,10 +511,10 @@ private float bestEffortSeverityLevelForSorting() { */ public String getHighestSeverityText() { if (this.cvssV3 != null) { - return this.cvssV3.getBaseSeverity().toUpperCase(); + return this.cvssV3.getCvssData().getBaseSeverity().value().toUpperCase(); } if (this.cvssV2 != null) { - return this.cvssV2.getSeverity().toUpperCase(); + return this.cvssV2.getCvssData().getBaseSeverity().toUpperCase(); } return SeverityUtil.unscoredToSeveritytext(this.unscoredSeverity).toUpperCase(); } diff --git a/core/src/main/java/org/owasp/dependencycheck/processing/BundlerAuditProcessor.java b/core/src/main/java/org/owasp/dependencycheck/processing/BundlerAuditProcessor.java index 973a5711df4..7a94c855344 100644 --- a/core/src/main/java/org/owasp/dependencycheck/processing/BundlerAuditProcessor.java +++ b/core/src/main/java/org/owasp/dependencycheck/processing/BundlerAuditProcessor.java @@ -20,6 +20,8 @@ import com.github.packageurl.MalformedPackageURLException; import com.github.packageurl.PackageURL; import com.github.packageurl.PackageURLBuilder; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -40,7 +42,6 @@ import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.dependency.Confidence; -import org.owasp.dependencycheck.dependency.CvssV2; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.dependency.Reference; @@ -124,8 +125,7 @@ public void run() { final Map map = new HashMap<>(); boolean appendToDescription = false; - try (InputStreamReader ir = new InputStreamReader(getInput(), StandardCharsets.UTF_8); - BufferedReader br = new BufferedReader(ir)) { + try (InputStreamReader ir = new InputStreamReader(getInput(), StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(ir)) { String nextLine; while ((nextLine = br.readLine()) != null) { @@ -216,7 +216,7 @@ private void addReferenceToVulnerability(String parentName, Vulnerability vulner private void addCriticalityToVulnerability(String parentName, Vulnerability vulnerability, String nextLine) { if (null != vulnerability) { final String criticality = nextLine.substring(CRITICALITY.length()).trim(); - float score = -1.0f; + Double score = -1.0; Vulnerability v = null; final CveDB cvedb = engine.getDatabase(); if (cvedb != null) { @@ -235,13 +235,16 @@ private void addCriticalityToVulnerability(String parentName, Vulnerability vuln } } else { if ("High".equalsIgnoreCase(criticality)) { - score = 8.5f; + score = 8.5; } else if ("Medium".equalsIgnoreCase(criticality)) { - score = 5.5f; + score = 5.5; } else if ("Low".equalsIgnoreCase(criticality)) { - score = 2.0f; + score = 2.0; } - vulnerability.setCvssV2(new CvssV2(score, "-", "-", "-", "-", "-", "-", criticality)); + CvssV2Data cvssData = new CvssV2Data(null, null, null, null, null, null, null, null, score, criticality.toUpperCase(), null, null, null, null, null, null, null, null, null, null); + CvssV2 cvssV2 = new CvssV2(null, null, cvssData, criticality.toUpperCase(), null, null, null, null, null, null, null); + vulnerability.setCvssV2(cvssV2); + vulnerability.setUnscoredSeverity(null); } } LOGGER.debug("bundle-audit ({}): {}", parentName, nextLine); @@ -289,7 +292,8 @@ private Vulnerability createVulnerability(String parentName, Dependency dependen .version(version).build(); vulnerability.addVulnerableSoftware(vs); vulnerability.setMatchedVulnerableSoftware(vs); - vulnerability.setCvssV2(new CvssV2(-1, "-", "-", "-", "-", "-", "-", "unknown")); + Double score = -1.0; + vulnerability.setUnscoredSeverity("UNKNOWN"); } LOGGER.debug("bundle-audit ({}): {}", parentName, nextLine); return vulnerability; diff --git a/core/src/main/java/org/owasp/dependencycheck/processing/MixAuditProcessor.java b/core/src/main/java/org/owasp/dependencycheck/processing/MixAuditProcessor.java index c4661c36894..3dda31c7b61 100644 --- a/core/src/main/java/org/owasp/dependencycheck/processing/MixAuditProcessor.java +++ b/core/src/main/java/org/owasp/dependencycheck/processing/MixAuditProcessor.java @@ -31,7 +31,7 @@ import org.owasp.dependencycheck.data.elixir.MixAuditJsonParser; import org.owasp.dependencycheck.data.elixir.MixAuditResult; import org.owasp.dependencycheck.dependency.Confidence; -import org.owasp.dependencycheck.dependency.CvssV2; + import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.dependency.Vulnerability; @@ -197,7 +197,7 @@ private Vulnerability createVulnerability(MixAuditResult result) throws CpeValid vulnerability.addVulnerableSoftware(vs); vulnerability.setMatchedVulnerableSoftware(vs); - vulnerability.setCvssV2(new CvssV2(-1, "-", "-", "-", "-", "-", "-", "unknown")); + vulnerability.setUnscoredSeverity("UNKOWN"); vulnerability.setDescription(result.getDescription()); vulnerability.setName(result.getCve()); diff --git a/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java b/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java index cc553298e91..7f44c766bbe 100644 --- a/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java +++ b/core/src/main/java/org/owasp/dependencycheck/reporting/ReportTool.java @@ -79,7 +79,7 @@ public String identifierToSuppressionId(Identifier id) { * @param severity the text representation of a score * @return the estimated score */ - public float estimateSeverity(String severity) { + public Double estimateSeverity(String severity) { return SeverityUtil.estimateCvssV2(severity); } @@ -110,33 +110,33 @@ public Collection convertToSarifRules(List dependencies) private String determineScore(Vulnerability vuln) { if (vuln.getUnscoredSeverity() != null) { if ("0.0".equals(vuln.getUnscoredSeverity())) { - return "Unknown"; + return "unknown"; } else { return normalizeSeverity(vuln.getUnscoredSeverity().toLowerCase()); } - } else if (vuln.getCvssV3() != null && vuln.getCvssV3().getBaseSeverity() != null) { - return normalizeSeverity(vuln.getCvssV3().getBaseSeverity().toLowerCase()); - } else if (vuln.getCvssV2() != null && vuln.getCvssV2().getSeverity() != null) { - return normalizeSeverity(vuln.getCvssV2().getSeverity()); + } else if (vuln.getCvssV3() != null && vuln.getCvssV3().getCvssData().getBaseSeverity() != null) { + return normalizeSeverity(vuln.getCvssV3().getCvssData().getBaseSeverity().value().toLowerCase()); + } else if (vuln.getCvssV2() != null && vuln.getCvssV2().getCvssData().getBaseSeverity() != null) { + return normalizeSeverity(vuln.getCvssV2().getCvssData().getBaseSeverity()); } - return "Unknown"; + return "unknown"; } private String normalizeSeverity(String sev) { - switch (sev) { + switch (sev.toLowerCase()) { case "critical": - return "Critical"; + return "critical"; case "high": - return "High"; + return "high"; case "medium": case "moderate": - return "Medium"; + return "medium"; case "low": case "informational": case "info": - return "Low"; + return "low"; default: - return "Unknown"; + return "unknown"; } } diff --git a/core/src/main/java/org/owasp/dependencycheck/reporting/SarifRule.java b/core/src/main/java/org/owasp/dependencycheck/reporting/SarifRule.java index 781f5158ce9..bca3a672c81 100644 --- a/core/src/main/java/org/owasp/dependencycheck/reporting/SarifRule.java +++ b/core/src/main/java/org/owasp/dependencycheck/reporting/SarifRule.java @@ -17,8 +17,8 @@ */ package org.owasp.dependencycheck.reporting; -import org.owasp.dependencycheck.dependency.CvssV2; -import org.owasp.dependencycheck.dependency.CvssV3; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3; /** * @@ -61,7 +61,7 @@ public class SarifRule { /** * CVSS V2 field. */ - private String cvssv2ConfidentialImpact; + private String cvssv2ConfidentialityImpact; /** * CVSS V2 field. */ @@ -161,40 +161,76 @@ public SarifRule(String name, String shortDescription, String fullDescription, this.fullDescription = fullDescription; this.source = source; if (cvssV2 != null) { - this.cvssv2Score = Float.toString(cvssV2.getScore()); - this.cvssv2AccessVector = cvssV2.getAccessVector(); - this.cvssv2AccessComplexity = cvssV2.getAccessComplexity(); - this.cvssv2Authentication = cvssV2.getAuthentication(); - this.cvssv2ConfidentialImpact = cvssV2.getConfidentialityImpact(); - this.cvssv2IntegrityImpact = cvssV2.getIntegrityImpact(); - this.cvssv2AvailabilityImpact = cvssV2.getAvailabilityImpact(); - this.cvssv2Severity = cvssV2.getSeverity(); - this.cvssv2Version = cvssV2.getVersion(); + if (cvssV2.getCvssData().getBaseScore() != null) { + this.cvssv2Score = cvssV2.getCvssData().getBaseScore().toString(); + } + if (cvssV2.getCvssData().getAccessVector() != null) { + this.cvssv2AccessVector = cvssV2.getCvssData().getAccessVector().name(); + } + if (cvssV2.getCvssData().getAccessComplexity() != null) { + this.cvssv2AccessComplexity = cvssV2.getCvssData().getAccessComplexity().name(); + } + if (cvssV2.getCvssData().getAuthentication() != null) { + this.cvssv2Authentication = cvssV2.getCvssData().getAuthentication().name(); + } + if (cvssV2.getCvssData().getConfidentialityImpact() != null) { + this.cvssv2ConfidentialityImpact = cvssV2.getCvssData().getConfidentialityImpact().name(); + } + if (cvssV2.getCvssData().getIntegrityImpact() != null) { + this.cvssv2IntegrityImpact = cvssV2.getCvssData().getIntegrityImpact().name(); + } + if (cvssV2.getCvssData().getAvailabilityImpact() != null) { + this.cvssv2AvailabilityImpact = cvssV2.getCvssData().getAvailabilityImpact().name(); + } + this.cvssv2Severity = cvssV2.getCvssData().getBaseSeverity(); + if (cvssV2.getCvssData().getVersion() != null) { + this.cvssv2Version = cvssV2.getCvssData().getVersion().name(); + } if (cvssV2.getExploitabilityScore() != null) { - this.cvssv2ExploitabilityScore = Float.toString(cvssV2.getExploitabilityScore()); + this.cvssv2ExploitabilityScore = cvssV2.getExploitabilityScore().toString(); } if (cvssV2.getImpactScore() != null) { - this.cvssv2ImpactScore = Float.toString(cvssV2.getImpactScore()); + this.cvssv2ImpactScore = cvssV2.getImpactScore().toString(); } } if (cvssV3 != null) { - this.cvssv3BaseScore = Float.toString(cvssV3.getBaseScore()); - this.cvssv3AttackVector = cvssV3.getAttackVector(); - this.cvssv3AttackComplexity = cvssV3.getAttackComplexity(); - this.cvssv3PrivilegesRequired = cvssV3.getPrivilegesRequired(); - this.cvssv3UserInteraction = cvssV3.getUserInteraction(); - this.cvssv3Scope = cvssV3.getScope(); - this.cvssv3ConfidentialityImpact = cvssV3.getConfidentialityImpact(); - this.cvssv3IntegrityImpact = cvssV3.getIntegrityImpact(); - this.cvssv3AvailabilityImpact = cvssV3.getAvailabilityImpact(); - this.cvssv3BaseSeverity = cvssV3.getBaseSeverity(); - if (cvssV3.getexploitabilityScore() != null) { - this.cvssv3ExploitabilityScore = Float.toString(cvssV3.getexploitabilityScore()); + if (cvssV3.getCvssData().getBaseScore() != null) { + this.cvssv3BaseScore = cvssV3.getCvssData().getBaseScore().toString(); + } + if (cvssV3.getCvssData().getAttackVector() != null) { + this.cvssv3AttackVector = cvssV3.getCvssData().getAttackVector().name(); + } + if (cvssV3.getCvssData().getAttackComplexity() != null) { + this.cvssv3AttackComplexity = cvssV3.getCvssData().getAttackComplexity().name(); + } + if (cvssV3.getCvssData().getPrivilegesRequired() != null) { + this.cvssv3PrivilegesRequired = cvssV3.getCvssData().getPrivilegesRequired().name(); + } + if (cvssV3.getCvssData().getUserInteraction() != null) { + this.cvssv3UserInteraction = cvssV3.getCvssData().getUserInteraction().name(); + } + if (cvssV3.getCvssData().getScope() != null) { + this.cvssv3Scope = cvssV3.getCvssData().getScope().name(); + } + if (cvssV3.getCvssData().getConfidentialityImpact() != null) { + this.cvssv3ConfidentialityImpact = cvssV3.getCvssData().getConfidentialityImpact().name(); + } + if (cvssV3.getCvssData().getIntegrityImpact() != null) { + this.cvssv3IntegrityImpact = cvssV3.getCvssData().getIntegrityImpact().name(); + } + if (cvssV3.getCvssData().getAvailabilityImpact() != null) { + this.cvssv3AvailabilityImpact = cvssV3.getCvssData().getAvailabilityImpact().name(); + } + if (cvssV3.getCvssData().getBaseSeverity() != null) { + this.cvssv3BaseSeverity = cvssV3.getCvssData().getBaseSeverity().name(); + } + if (cvssV3.getExploitabilityScore() != null) { + this.cvssv3ExploitabilityScore = cvssV3.getExploitabilityScore().toString(); } - if (cvssV3.getimpactScore() != null) { - this.cvssv3ImpactScore = Float.toString(cvssV3.getimpactScore()); + if (cvssV3.getImpactScore() != null) { + this.cvssv3ImpactScore = cvssV3.getImpactScore().toString(); } - this.cvssv3Version = cvssV3.getVersion(); + this.cvssv3Version = cvssV3.getCvssData().getVersion().name(); } } @@ -564,17 +600,17 @@ public void setCvssv2IntegrityImpact(String cvssv2IntegrityImpact) { * * @return the value of CVSS2 Confidential Impact */ - public String getCvssv2ConfidentialImpact() { - return cvssv2ConfidentialImpact; + public String getCvssv2ConfidentialityImpact() { + return cvssv2ConfidentialityImpact; } /** * Set the value of CVSS2 Confidential Impact. * - * @param cvssv2ConfidentialImpact new value of CVSS2 Confidential Impact + * @param cvssv2ConfidentialityImpact new value of CVSS2 Confidential Impact */ - public void setCvssv2ConfidentialImpact(String cvssv2ConfidentialImpact) { - this.cvssv2ConfidentialImpact = cvssv2ConfidentialImpact; + public void setCvssv2ConfidentialityImpact(String cvssv2ConfidentialityImpact) { + this.cvssv2ConfidentialityImpact = cvssv2ConfidentialityImpact; } /** diff --git a/core/src/main/java/org/owasp/dependencycheck/utils/CvssUtil.java b/core/src/main/java/org/owasp/dependencycheck/utils/CvssUtil.java new file mode 100644 index 00000000000..bfd2c295b5e --- /dev/null +++ b/core/src/main/java/org/owasp/dependencycheck/utils/CvssUtil.java @@ -0,0 +1,214 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2023 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.utils; + +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV2Data; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3; +import io.github.jeremylong.openvulnerability.client.nvd.CvssV3Data; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import org.sonatype.ossindex.service.api.cvss.Cvss3Severity; + +/** + * Utility class to create CVSS Objects. + * + * @author Jeremy Long + */ +public final class CvssUtil { + + private CvssUtil() { + //empty constructor for utility class. + } + /** + * The CVSS v3 Base Metrics (that are required by the spec for any CVSS v3 + * Vector String) + */ + private static final List BASE_METRICS_V3 = Arrays.asList("AV", "AC", "PR", "UI", "S", "C", "I", "A"); + + /** + * The CVSS V2 Metrics required for the vector string to be complete. + */ + private static final List BASE_METRICS_V2 = Arrays.asList("AV", "AC", "Au", "C", "I", "A"); + /** + * ZERO. + */ + private static final Double ZERO = 0.0; + /** + * ONE. + */ + private static final Double ONE = 1.0; + /** + * FOUR. + */ + private static final Double FOUR = 4.0; + /** + * SEVEN. + */ + private static final Double SEVEN = 7.0; + /** + * NINE. + */ + private static final Double NINE = 9.0; + /** + * TEN. + */ + private static final Double TEN = 10.0; + /** + * UNKNOWN. + */ + private static final String UNKNOWN = "UNKNOWN"; + /** + * HIGH. + */ + private static final String HIGH = "HIGH"; + /** + * MEDIUM. + */ + private static final String MEDIUM = "MEDIUM"; + /** + * LOW. + */ + private static final String LOW = "LOW"; + + /** + * Convert a CVSSv2 vector String into a CvssV3 Object. + * + * @param vectorString the vector string + * @param baseScore the base score + * @return the CVSSv2 object + */ + public static CvssV2 vectorToCvssV2(String vectorString, Double baseScore) { + if (vectorString.startsWith("CVSS:")) { + throw new IllegalArgumentException("Not a valid CVSSv2 vector string: " + vectorString); + } + final String[] metricStrings = vectorString.substring(vectorString.indexOf('/') + 1).split("/"); + final HashMap metrics = new HashMap<>(); + for (int i = 0; i < metricStrings.length; i++) { + final String[] metricKeyVal = metricStrings[i].split(":"); + if (metricKeyVal.length != 2) { + throw new IllegalArgumentException( + String.format("Not a valid CVSSv2 vector string '%s', invalid metric component '%s'", + vectorString, metricStrings[i])); + } + metrics.put(metricKeyVal[0], metricKeyVal[1]); + } + if (!metrics.keySet().containsAll(BASE_METRICS_V2)) { + throw new IllegalArgumentException( + String.format("Not a valid CVSSv2 vector string '%s'; missing one or more required Metrics;", + vectorString)); + } + + String version = CvssV2Data.Version._2_0.value(); + //"AV:L/AC:L/Au:N/C:N/I:N/A:C" + CvssV2Data.AccessVectorType accessVector = CvssV2Data.AccessVectorType.fromValue(metrics.get("AV")); + CvssV2Data.AccessComplexityType attackComplexity = CvssV2Data.AccessComplexityType.fromValue(metrics.get("AC")); + CvssV2Data.AuthenticationType authentication = CvssV2Data.AuthenticationType.fromValue(metrics.get("Au")); + CvssV2Data.CiaType confidentialityImpact = CvssV2Data.CiaType.fromValue(metrics.get("C")); + CvssV2Data.CiaType integrityImpact = CvssV2Data.CiaType.fromValue(metrics.get("I")); + CvssV2Data.CiaType availabilityImpact = CvssV2Data.CiaType.fromValue(metrics.get("A")); + + String baseSeverity = cvssV2ScoreToSeverity(baseScore); + CvssV2Data data = new CvssV2Data(version, vectorString, accessVector, attackComplexity, + authentication, confidentialityImpact, integrityImpact, availabilityImpact, baseScore, baseSeverity, null, null, null, null, null, null, null, null, null, null); + CvssV2 cvss = new CvssV2(null, null, data, baseSeverity, null, null, null, null, null, null, null); + return cvss; + + } + + public static String cvssV2ScoreToSeverity(Double score) { + if (score != null) { + if (ZERO.compareTo(score) <= 0 && FOUR.compareTo(score) > 0) { + return LOW; + } else if (FOUR.compareTo(score) <= 0 && SEVEN.compareTo(score) > 0) { + return MEDIUM; + } else if (SEVEN.compareTo(score) <= 0 && TEN.compareTo(score) >= 0) { + return HIGH; + } + } + return UNKNOWN; + } + + public static CvssV3Data.SeverityType cvssV3ScoreToSeverity(Double score) { + if (score != null) { + if (ZERO.compareTo(score) == 0) { + return CvssV3Data.SeverityType.NONE; + } else if (ZERO.compareTo(score) <= 0 && FOUR.compareTo(score) > 0) { + return CvssV3Data.SeverityType.LOW; + } else if (FOUR.compareTo(score) <= 0 && SEVEN.compareTo(score) > 0) { + return CvssV3Data.SeverityType.MEDIUM; + } else if (SEVEN.compareTo(score) <= 0 && NINE.compareTo(score) > 0) { + return CvssV3Data.SeverityType.HIGH; + } else if (NINE.compareTo(score) <= 0 && TEN.compareTo(score) >= 0) { + return CvssV3Data.SeverityType.CRITICAL; + } + } + return null; + } + + /** + * Convert a CVSSv3 vector String into a CvssV3 Object. + * + * @param vectorString the vector string + * @param baseScore the base score + * @return the CVSSv3 object + */ + public static CvssV3 vectorToCvssV3(String vectorString, Double baseScore) { + if (!vectorString.startsWith("CVSS:3")) { + throw new IllegalArgumentException("Not a valid CVSSv3 vector string: " + vectorString); + } + final String versionString = vectorString.substring(5, vectorString.indexOf('/')); + final String[] metricStrings = vectorString.substring(vectorString.indexOf('/') + 1).split("/"); + final HashMap metrics = new HashMap<>(); + for (int i = 0; i < metricStrings.length; i++) { + final String[] metricKeyVal = metricStrings[i].split(":"); + if (metricKeyVal.length != 2) { + throw new IllegalArgumentException( + String.format("Not a valid CVSSv3 vector string '%s', invalid metric component '%s'", + vectorString, metricStrings[i])); + } + metrics.put(metricKeyVal[0], metricKeyVal[1]); + } + if (!metrics.keySet().containsAll(BASE_METRICS_V3)) { + throw new IllegalArgumentException( + String.format("Not a valid CVSSv3 vector string '%s'; missing one or more required Base Metrics;", + vectorString)); + } + + CvssV3Data.Version version = CvssV3Data.Version.fromValue(versionString); + //"CVSS:3.1\/AV:L\/AC:L\/PR:L\/UI:N\/S:U\/C:N\/I:N\/A:H" + CvssV3Data.AttackVectorType attackVector = CvssV3Data.AttackVectorType.fromValue(metrics.get("AV")); + CvssV3Data.AttackComplexityType attackComplexity = CvssV3Data.AttackComplexityType.fromValue(metrics.get("AC")); + CvssV3Data.PrivilegesRequiredType privilegesRequired = CvssV3Data.PrivilegesRequiredType.fromValue(metrics.get("PR")); + CvssV3Data.UserInteractionType userInteraction = CvssV3Data.UserInteractionType.fromValue(metrics.get("UI")); + CvssV3Data.ScopeType scope = CvssV3Data.ScopeType.fromValue(metrics.get("S")); + CvssV3Data.CiaType confidentialityImpact = CvssV3Data.CiaType.fromValue(metrics.get("C")); + CvssV3Data.CiaType integrityImpact = CvssV3Data.CiaType.fromValue(metrics.get("I")); + CvssV3Data.CiaType availabilityImpact = CvssV3Data.CiaType.fromValue(metrics.get("A")); + + String baseSeverityString = Cvss3Severity.of(baseScore.floatValue()).name(); + CvssV3Data.SeverityType baseSeverity = CvssV3Data.SeverityType.fromValue(baseSeverityString); + CvssV3Data data = new CvssV3Data(version, vectorString, attackVector, attackComplexity, + privilegesRequired, userInteraction, scope, confidentialityImpact, integrityImpact, availabilityImpact, baseScore, + baseSeverity, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); + CvssV3 cvss = new CvssV3(null, null, data, null, null); + return cvss; + + } +} diff --git a/core/src/main/java/org/owasp/dependencycheck/utils/SeverityUtil.java b/core/src/main/java/org/owasp/dependencycheck/utils/SeverityUtil.java index ed3283bfe37..d256a280b29 100644 --- a/core/src/main/java/org/owasp/dependencycheck/utils/SeverityUtil.java +++ b/core/src/main/java/org/owasp/dependencycheck/utils/SeverityUtil.java @@ -27,6 +27,10 @@ */ public final class SeverityUtil { + private static final Double HIGH = 10.0; + private static final Double MEDIUM = 6.9; + private static final Double LOW = 3.9; + private static final Double NONE = 0.0; /** * The logger. */ @@ -47,22 +51,22 @@ private SeverityUtil() { * @param severity the severity text (e.g. "medium") * @return a score from 0 to 10 */ - public static float estimateCvssV2(String severity) { + public static Double estimateCvssV2(String severity) { switch (severity == null ? "none" : severity.toLowerCase()) { case "critical": case "high": - return 10.0f; + return HIGH; case "moderate": case "medium": - return 6.9f; + return MEDIUM; case "info": + case "none": case "informational": - return 0.0f; + return NONE; case "low": case "unknown": - case "none": default: - return 3.9f; + return LOW; } } @@ -108,24 +112,24 @@ public static String unscoredToSeveritytext(final String severity) { * highest severity). * @see #sortAdjustedCVSSv3BaseScore(float) */ - public static float estimatedSortAdjustedCVSSv3(final String severity) { + public static Double estimatedSortAdjustedCVSSv3(final String severity) { switch (Severity.forUnscored(severity)) { case CRITICAL: - return 10.2f; + return 10.2; case HIGH: - return 7.0f; + return 7.0; case MEDIUM: - return 4.0f; + return 4.0; case LOW: - return 0.1f; + return 0.1; case INFO: - return 0.0f; + return 0.0; case ASSUMED_CRITICAL: default: SeverityUtil.LOGGER.debug("Unrecognized unscored textual severity: {}, assuming critical score as worst-case " + "estimate for sorting", severity); - return 10.1f; + return 10.1; } } @@ -140,9 +144,9 @@ public static float estimatedSortAdjustedCVSSv3(final String severity) { * critical nature) * @see #estimatedSortAdjustedCVSSv3(String) */ - public static float sortAdjustedCVSSv3BaseScore(final float cvssV3BaseScore) { - if (cvssV3BaseScore >= 9.0f) { - return cvssV3BaseScore + 1.3f; + public static Double sortAdjustedCVSSv3BaseScore(final Double cvssV3BaseScore) { + if (cvssV3BaseScore.floatValue() >= 9.0f) { + return cvssV3BaseScore + 1.3; } return cvssV3BaseScore; } diff --git a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java index 3ea7aebbf3c..50f4df32a9f 100644 --- a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java +++ b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java @@ -191,10 +191,10 @@ public void endElement(String uri, String localName, String qName) throws SAXExc rule.addVulnerabilityName(processPropertyType()); break; case NOTES: - rule.addNotes(currentText.toString().trim()); + rule.setNotes(currentText.toString().trim()); break; case CVSS_BELOW: - final float cvss = Float.parseFloat(currentText.toString().trim()); + final Double cvss = Double.valueOf(currentText.toString().trim()); rule.addCvssBelow(cvss); break; default: diff --git a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java index 3fd508ab05e..9b4a0b662d8 100644 --- a/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java +++ b/core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java @@ -62,7 +62,7 @@ public class SuppressionRule { /** * The list of cvssBelow scores. */ - private List cvssBelow = new ArrayList<>(); + private List cvssBelow = new ArrayList<>(); /** * The list of CWE entries to suppress. */ @@ -230,7 +230,7 @@ public boolean hasCpe() { * * @return the value of cvssBelow */ - public List getCvssBelow() { + public List getCvssBelow() { return cvssBelow; } @@ -239,7 +239,7 @@ public List getCvssBelow() { * * @param cvssBelow new value of cvssBelow */ - public void setCvssBelow(List cvssBelow) { + public void setCvssBelow(List cvssBelow) { this.cvssBelow = cvssBelow; } @@ -248,14 +248,14 @@ public void setCvssBelow(List cvssBelow) { * * @param cvss the CVSS to add */ - public void addCvssBelow(Float cvss) { + public void addCvssBelow(Double cvss) { this.cvssBelow.add(cvss); } /** - * Returns whether or not this suppression rule has CVSS suppressions. + * Returns whether or not this suppression rule has CVSS suppression criteria. * - * @return whether or not this suppression rule has CVSS suppressions + * @return whether or not this suppression rule has CVSS suppression criteria. */ public boolean hasCvssBelow() { return !cvssBelow.isEmpty(); @@ -273,28 +273,19 @@ public String getNotes() { /** * Set the value of notes. * - * @param notes new value of cve + * @param notes new value of notes */ public void setNotes(String notes) { this.notes = notes; } - /** - * Adds the notes to the cve list. - * - * @param notes the cve to add - */ - public void addNotes(String notes) { - this.notes = notes; - } - /** * Returns whether this suppression rule has notes entries. * * @return whether this suppression rule has notes entries */ public boolean hasNotes() { - return !cve.isEmpty(); + return !notes.isEmpty(); } /** @@ -534,13 +525,14 @@ public void process(Dependency dependency) { } } if (!remove) { - for (float cvss : this.cvssBelow) { - if (v.getCvssV2() != null && v.getCvssV2().getScore() < cvss) { + for (Double cvss : this.cvssBelow) { + //TODO validate this comparison + if (v.getCvssV2() != null && v.getCvssV2().getCvssData().getBaseScore().compareTo(cvss) < 0) { remove = true; removeVulns.add(v); break; } - if (v.getCvssV3() != null && v.getCvssV3().getBaseScore() < cvss) { + if (v.getCvssV3() != null && v.getCvssV3().getCvssData().getBaseScore().compareTo(cvss) < 0) { remove = true; removeVulns.add(v); break; diff --git a/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.data.update.CachedWebDataSource b/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.data.update.CachedWebDataSource index 8e10a07f8c9..06b30b96fc5 100644 --- a/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.data.update.CachedWebDataSource +++ b/core/src/main/resources/META-INF/services/org.owasp.dependencycheck.data.update.CachedWebDataSource @@ -1,4 +1,4 @@ -org.owasp.dependencycheck.data.update.NvdCveUpdater +org.owasp.dependencycheck.data.update.NvdApiDataSource org.owasp.dependencycheck.data.update.EngineVersionCheck org.owasp.dependencycheck.data.update.RetireJSDataSource org.owasp.dependencycheck.data.update.HostedSuppressionsDataSource diff --git a/core/src/main/resources/templates/csvReport.vsl b/core/src/main/resources/templates/csvReport.vsl index 891b1572b4a..e421db6b8b9 100644 --- a/core/src/main/resources/templates/csvReport.vsl +++ b/core/src/main/resources/templates/csvReport.vsl @@ -19,5 +19,5 @@ Copyright (c) 2017 Jeremy Long. All Rights Reserved. @version 1 *### "Project","ScanDate","DependencyName","DependencyPath","Description","License","Md5","Sha1","Identifiers","CPE","CVE","CWE","Vulnerability","Source","CVSSv2_Severity","CVSSv2_Score","CVSSv2","CVSSv3_BaseSeverity","CVSSv3_BaseScore","CVSSv3","CPE Confidence","Evidence Count","VendorProject","Product","Name","DateAdded","ShortDescription","RequiredAction","DueDate","Notes"#[[ ]]##foreach($dependency in $dependencies)#if($dependency.getVulnerabilities().size()>0)#foreach($vuln in $dependency.getVulnerabilities(true)) -$enc.csv($applicationName),$enc.csv($scanDate),$enc.csv($dependency.DisplayFileName),$enc.csv($dependency.FilePath),$enc.csv($dependency.description),$enc.csv($dependency.license),#if(!$dependency.isVirtual())$enc.csv($dependency.Md5sum)#else""#end,#if(!$dependency.isVirtual())$enc.csv($dependency.Sha1sum)#else""#end,$enc.csvIdentifiers($dependency.softwareIdentifiers),$enc.csvIdentifiers($dependency.vulnerableSoftwareIdentifiers),$enc.csv($vuln.name),$enc.csv($vuln.getCwes().toString()),$enc.csv($vuln.description),$enc.csv($vuln.getSource().name()),#if($vuln.cvssV2)$enc.csv($vuln.cvssV2.severity)#else""#end,#if($vuln.cvssV2)$enc.csv($vuln.cvssV2.score)#else""#end,#if($vuln.cvssV2)$enc.csv($vuln.cvssV2.toString())#else""#end,#if($vuln.cvssV3)$enc.csv($vuln.cvssV3.baseSeverity)#else""#end,#if($vuln.cvssV3)$enc.csv($vuln.cvssV3.baseScore)#else""#end,#if($vuln.cvssV3)$enc.csv($vuln.cvssV3.toString())#else""#end,$enc.csvCpeConfidence($dependency.softwareIdentifiers),$dependency.size(),#if($vuln.getKnownExploitedVulnerability())$enc.csv($vuln.getKnownExploitedVulnerability().getVendorProject()),$enc.csv($vuln.getKnownExploitedVulnerability().getProduct()),$enc.csv($vuln.getKnownExploitedVulnerability().getVulnerabilityName()),$enc.csv($vuln.getKnownExploitedVulnerability().getDateAdded()),$enc.csv($vuln.getKnownExploitedVulnerability().getShortDescription()),$enc.csv($vuln.getKnownExploitedVulnerability().getRequiredAction()),$enc.csv($vuln.getKnownExploitedVulnerability().getDueDate()),$enc.csv($vuln.getKnownExploitedVulnerability().getNotes())#else"","","","","","","",""#end +$enc.csv($applicationName),$enc.csv($scanDate),$enc.csv($dependency.DisplayFileName),$enc.csv($dependency.FilePath),$enc.csv($dependency.description),$enc.csv($dependency.license),#if(!$dependency.isVirtual())$enc.csv($dependency.Md5sum)#else""#end,#if(!$dependency.isVirtual())$enc.csv($dependency.Sha1sum)#else""#end,$enc.csvIdentifiers($dependency.softwareIdentifiers),$enc.csvIdentifiers($dependency.vulnerableSoftwareIdentifiers),$enc.csv($vuln.name),$enc.csv($vuln.getCwes().toString()),$enc.csv($vuln.description),$enc.csv($vuln.getSource().name()),#if($vuln.cvssV2)$enc.csv($vuln.cvssV2.cvssData.baseSeverity)#else""#end,#if($vuln.cvssV2)$enc.csv($vuln.cvssV2.cvssData.baseScore)#else""#end,#if($vuln.cvssV2)$enc.csv($vuln.cvssV2.toString())#else""#end,#if($vuln.cvssV3)$enc.csv($vuln.cvssV3.cvssData.baseSeverity)#else""#end,#if($vuln.cvssV3)$enc.csv($vuln.cvssV3.cvssData.baseScore)#else""#end,#if($vuln.cvssV3)$enc.csv($vuln.cvssV3.toString())#else""#end,$enc.csvCpeConfidence($dependency.softwareIdentifiers),$dependency.size(),#if($vuln.getKnownExploitedVulnerability())$enc.csv($vuln.getKnownExploitedVulnerability().getVendorProject()),$enc.csv($vuln.getKnownExploitedVulnerability().getProduct()),$enc.csv($vuln.getKnownExploitedVulnerability().getVulnerabilityName()),$enc.csv($vuln.getKnownExploitedVulnerability().getDateAdded()),$enc.csv($vuln.getKnownExploitedVulnerability().getShortDescription()),$enc.csv($vuln.getKnownExploitedVulnerability().getRequiredAction()),$enc.csv($vuln.getKnownExploitedVulnerability().getDueDate()),$enc.csv($vuln.getKnownExploitedVulnerability().getNotes())#else"","","","","","","",""#end #end#end#end \ No newline at end of file diff --git a/core/src/main/resources/templates/htmlReport.vsl b/core/src/main/resources/templates/htmlReport.vsl index f1857f9ad52..784b3c66b32 100644 --- a/core/src/main/resources/templates/htmlReport.vsl +++ b/core/src/main/resources/templates/htmlReport.vsl @@ -826,9 +826,9 @@ Getting Help:
  • Base Score: $enc.html($vuln.getCvssV2().getSeverity()) ($vuln.getCvssV2().getScore())
  • +
    • Base Score: $enc.html($vuln.getCvssV2().getCvssData().getBaseSeverity()) ($vuln.getCvssV2().getCvssData().getBaseScore())
    • Vector: $enc.html($vuln.getCvssV2().toString())
    #end #if($vuln.getCvssV3()) CVSSv3: -