AbstractDependencyComparingAnalyzer.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) 2017 Jeremy Long. All Rights Reserved.
  17.  */
  18. package org.owasp.dependencycheck.analyzer;

  19. import java.util.Arrays;
  20. import java.util.HashSet;
  21. import java.util.Set;
  22. import javax.annotation.concurrent.ThreadSafe;
  23. import org.owasp.dependencycheck.Engine;
  24. import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
  25. import org.owasp.dependencycheck.dependency.Dependency;

  26. /**
  27.  * <p>
  28.  * This analyzer ensures dependencies that should be grouped together, to remove
  29.  * excess noise from the report, are grouped. An example would be Spring, Spring
  30.  * Beans, Spring MVC, etc. If they are all for the same version and have the
  31.  * same relative path then these should be grouped into a single dependency
  32.  * under the core/main library.</p>
  33.  * <p>
  34.  * Note, this grouping only works on dependencies with identified CVE
  35.  * entries</p>
  36.  *
  37.  * @author Jeremy Long
  38.  */
  39. @ThreadSafe
  40. public abstract class AbstractDependencyComparingAnalyzer extends AbstractAnalyzer {

  41.     /**
  42.      * a flag indicating if this analyzer has run. This analyzer only runs once.
  43.      */
  44.     private boolean analyzed = false;

  45.     /**
  46.      * Returns a flag indicating if this analyzer has run. This analyzer only
  47.      * runs once. Note this is currently only used in the unit tests.
  48.      *
  49.      * @return a flag indicating if this analyzer has run. This analyzer only
  50.      * runs once
  51.      */
  52.     protected synchronized boolean getAnalyzed() {
  53.         return analyzed;
  54.     }

  55.     /**
  56.      * Does not support parallel processing as it only runs once and then
  57.      * operates on <em>all</em> dependencies.
  58.      *
  59.      * @return whether or not parallel processing is enabled
  60.      * @see #analyze(Dependency, Engine)
  61.      */
  62.     @Override
  63.     public final boolean supportsParallelProcessing() {
  64.         return false;
  65.     }

  66.     /**
  67.      * Analyzes a set of dependencies. If they have been found to have the same
  68.      * base path and the same set of identifiers they are likely related. The
  69.      * related dependencies are bundled into a single reportable item.
  70.      *
  71.      * @param ignore this analyzer ignores the dependency being analyzed
  72.      * @param engine the engine that is scanning the dependencies
  73.      * @throws AnalysisException is thrown if there is an error reading the JAR
  74.      * file.
  75.      */
  76.     @Override
  77.     protected synchronized void analyzeDependency(Dependency ignore, Engine engine) throws AnalysisException {
  78.         if (!analyzed) {
  79.             analyzed = true;
  80.             final Set<Dependency> dependenciesToRemove = new HashSet<>();

  81.             final Dependency[] dependencies = engine.getDependencies();
  82.             if (dependencies.length < 2) {
  83.                 return;
  84.             }
  85.             Arrays.sort(dependencies, Dependency.NAME_COMPARATOR);
  86.             for (int x = 0; x < dependencies.length - 1; x++) {
  87.                 final Dependency dependency = dependencies[x];
  88.                 if (!dependenciesToRemove.contains(dependency)) {
  89.                     for (int y = x + 1; y < dependencies.length; y++) {
  90.                         final Dependency nextDependency = dependencies[y];
  91.                         if (evaluateDependencies(dependency, nextDependency, dependenciesToRemove)) {
  92.                             break;
  93.                         }
  94.                     }
  95.                 }
  96.             }
  97.             dependenciesToRemove.forEach(engine::removeDependency);
  98.         }
  99.     }

  100.     /**
  101.      * Evaluates the dependencies
  102.      *
  103.      * @param dependency a dependency to compare
  104.      * @param nextDependency a dependency to compare
  105.      * @param dependenciesToRemove a set of dependencies that will be removed
  106.      * @return true if a dependency is removed; otherwise false
  107.      */
  108.     protected abstract boolean evaluateDependencies(Dependency dependency,
  109.             Dependency nextDependency, Set<Dependency> dependenciesToRemove);
  110. }