NvdCveAnalyzer.java

  1. /*
  2.  * This file is part of dependency-check-core.
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  *
  16.  * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
  17.  */
  18. package org.owasp.dependencycheck.analyzer;

  19. import java.util.ArrayList;
  20. import java.util.HashSet;
  21. import java.util.List;
  22. import java.util.Set;
  23. import javax.annotation.concurrent.ThreadSafe;

  24. import org.owasp.dependencycheck.Engine;
  25. import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
  26. import org.owasp.dependencycheck.analyzer.exception.LambdaExceptionWrapper;
  27. import org.owasp.dependencycheck.data.nvd.ecosystem.Ecosystem;
  28. import org.owasp.dependencycheck.data.nvdcve.CveDB;
  29. import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
  30. import org.owasp.dependencycheck.dependency.Dependency;
  31. import org.owasp.dependencycheck.dependency.Vulnerability;
  32. import org.owasp.dependencycheck.dependency.Vulnerability.Source;
  33. import org.owasp.dependencycheck.dependency.VulnerableSoftware;
  34. import org.owasp.dependencycheck.dependency.naming.CpeIdentifier;
  35. import org.owasp.dependencycheck.utils.Settings;

  36. /**
  37.  * NvdCveAnalyzer is a utility class that takes a project dependency and
  38.  * attempts to discern if there is an associated CVEs. It uses the the
  39.  * identifiers found by other analyzers to lookup the CVE data.
  40.  *
  41.  * @author Jeremy Long
  42.  */
  43. @ThreadSafe
  44. public class NvdCveAnalyzer extends AbstractAnalyzer {

  45.     /**
  46.      * Analyzes a dependency and attempts to determine if there are any CPE
  47.      * identifiers for this dependency.
  48.      *
  49.      * @param dependency The Dependency to analyze
  50.      * @param engine The analysis engine
  51.      * @throws AnalysisException thrown if there is an issue analyzing the
  52.      * dependency
  53.      */
  54.     @Override
  55.     protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException {
  56.         final CveDB cveDB = engine.getDatabase();
  57.         try {
  58.             dependency.getVulnerableSoftwareIdentifiers().stream()
  59.                     .filter((i) -> (i instanceof CpeIdentifier))
  60.                     .map(i -> (CpeIdentifier) i)
  61.                     .forEach(i -> {
  62.                         try {
  63.                             final List<Vulnerability> vulns = filterEcosystem(dependency.getEcosystem(), cveDB.getVulnerabilities(i.getCpe()));

  64.                             if (Ecosystem.NODEJS.equals(dependency.getEcosystem())) {
  65.                                 replaceOrAddVulnerability(dependency, vulns);
  66.                             } else {
  67.                                 dependency.addVulnerabilities(vulns);
  68.                             }
  69.                         } catch (DatabaseException ex) {
  70.                             throw new LambdaExceptionWrapper(new AnalysisException(ex));
  71.                         }
  72.                     });
  73.             dependency.getSuppressedIdentifiers().stream()
  74.                     .filter((i) -> (i instanceof CpeIdentifier))
  75.                     .map(i -> (CpeIdentifier) i)
  76.                     .forEach(i -> {
  77.                         try {
  78.                             final List<Vulnerability> vulns = cveDB.getVulnerabilities(i.getCpe());
  79.                             dependency.addSuppressedVulnerabilities(vulns);
  80.                         } catch (DatabaseException ex) {
  81.                             throw new LambdaExceptionWrapper(new AnalysisException(ex));
  82.                         }
  83.                     });
  84.         } catch (LambdaExceptionWrapper ex) {
  85.             throw (AnalysisException) ex.getCause();
  86.         }
  87.     }

  88.     /**
  89.      * Returns the name of this analyzer.
  90.      *
  91.      * @return the name of this analyzer.
  92.      */
  93.     @Override
  94.     public String getName() {
  95.         return "NVD CVE Analyzer";
  96.     }

  97.     /**
  98.      * Returns the analysis phase that this analyzer should run in.
  99.      *
  100.      * @return the analysis phase that this analyzer should run in.
  101.      */
  102.     @Override
  103.     public AnalysisPhase getAnalysisPhase() {
  104.         return AnalysisPhase.FINDING_ANALYSIS;
  105.     }

  106.     /**
  107.      * <p>
  108.      * Returns the setting key to determine if the analyzer is enabled.</p>
  109.      *
  110.      * @return the key for the analyzer's enabled property
  111.      */
  112.     @Override
  113.     protected String getAnalyzerEnabledSettingKey() {
  114.         return Settings.KEYS.ANALYZER_NVD_CVE_ENABLED;
  115.     }

  116.     /**
  117.      * Evaluates if the vulnerability is already present; if it is the
  118.      * vulnerability is not added.
  119.      *
  120.      * @param dependency a reference to the dependency being analyzed
  121.      * @param vulns the vulnerability to add
  122.      */
  123.     private void replaceOrAddVulnerability(Dependency dependency, List<Vulnerability> vulns) {
  124.         vulns.forEach(v -> v.getReferences().forEach(ref -> dependency.getVulnerabilities().forEach(existing -> {
  125.                 if (existing.getSource() == Source.NPM
  126.                         && ref.getName() != null
  127.                         && ref.getName().equals("https://nodesecurity.io/advisories/" + existing.getName())) {
  128.                     dependency.removeVulnerability(existing);
  129.                 }
  130.             })));
  131.         dependency.addVulnerabilities(vulns);
  132.     }

  133.     /**
  134.      * Filters the list of vulnerabilities for the given ecosystem compared to
  135.      * the target software from the NVD.
  136.      *
  137.      * @param ecosystem the dependency's ecosystem
  138.      * @param vulnerabilities the list of vulnerabilities to filter
  139.      * @return the filtered list of vulnerabilities
  140.      */
  141.     private synchronized List<Vulnerability> filterEcosystem(String ecosystem, List<Vulnerability> vulnerabilities) {
  142.         final List<Vulnerability> remove = new ArrayList<>();
  143.         vulnerabilities.forEach((v) -> {
  144.             boolean found = false;
  145.             final Set<VulnerableSoftware> removeSoftware = new HashSet<>();
  146.             for (VulnerableSoftware s : v.getVulnerableSoftware()) {
  147.                 if (ecosystemMatchesTargetSoftware(ecosystem, s.getTargetSw())) {
  148.                     found = true;
  149.                 } else {
  150.                     removeSoftware.add(s);
  151.                 }
  152.             }
  153.             if (found) {
  154.                 if (!removeSoftware.isEmpty()) {
  155.                     v.removeVulnerableSoftware(removeSoftware);
  156.                 }
  157.             } else {
  158.                 remove.add(v);
  159.             }
  160.         });
  161.         if (!remove.isEmpty()) {
  162.             vulnerabilities.removeAll(remove);
  163.         }
  164.         return vulnerabilities;
  165.     }

  166.     /**
  167.      * Determines if the target software matches the given ecosystem. Currently,
  168.      * this is very Node JS specific and broadly returns matches for everything
  169.      * else.
  170.      *
  171.      * @param ecosystem the ecosystem to match against
  172.      * @param targetSoftware the target software from the NVD
  173.      * @return <code>true</code> if there is a match; otherwise
  174.      * <code>false</code>
  175.      */
  176.     private boolean ecosystemMatchesTargetSoftware(String ecosystem, String targetSoftware) {
  177.         if ("*".equals(targetSoftware) || "-".equals(targetSoftware)) {
  178.             return true;
  179.         }
  180.         if (Ecosystem.NODEJS.equals(ecosystem)) {
  181.             switch (targetSoftware.toLowerCase()) {
  182.                 case "nodejs":
  183.                 case "node.js":
  184.                 case "node_js":
  185.                 case "npm":
  186.                 case "node-js":
  187.                     return true;
  188.                 default:
  189.                     return false;
  190.             }
  191.         }
  192.         return true;
  193.     }
  194. }