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 20 import java.util.Arrays; 21 import java.util.HashSet; 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.dependency.Dependency; 27 28 /** 29 * <p> 30 * This analyzer ensures dependencies that should be grouped together, to remove 31 * excess noise from the report, are grouped. An example would be Spring, Spring 32 * Beans, Spring MVC, etc. If they are all for the same version and have the 33 * same relative path then these should be grouped into a single dependency 34 * under the core/main library.</p> 35 * <p> 36 * Note, this grouping only works on dependencies with identified CVE 37 * entries</p> 38 * 39 * @author Jeremy Long 40 */ 41 @ThreadSafe 42 public abstract class AbstractDependencyComparingAnalyzer extends AbstractAnalyzer { 43 44 /** 45 * a flag indicating if this analyzer has run. This analyzer only runs once. 46 */ 47 private boolean analyzed = false; 48 49 /** 50 * Returns a flag indicating if this analyzer has run. This analyzer only 51 * runs once. Note this is currently only used in the unit tests. 52 * 53 * @return a flag indicating if this analyzer has run. This analyzer only 54 * runs once 55 */ 56 protected synchronized boolean getAnalyzed() { 57 return analyzed; 58 } 59 60 /** 61 * Does not support parallel processing as it only runs once and then 62 * operates on <em>all</em> dependencies. 63 * 64 * @return whether or not parallel processing is enabled 65 * @see #analyze(Dependency, Engine) 66 */ 67 @Override 68 public final boolean supportsParallelProcessing() { 69 return false; 70 } 71 72 /** 73 * Analyzes a set of dependencies. If they have been found to have the same 74 * base path and the same set of identifiers they are likely related. The 75 * related dependencies are bundled into a single reportable item. 76 * 77 * @param ignore this analyzer ignores the dependency being analyzed 78 * @param engine the engine that is scanning the dependencies 79 * @throws AnalysisException is thrown if there is an error reading the JAR 80 * file. 81 */ 82 @Override 83 protected synchronized void analyzeDependency(Dependency ignore, Engine engine) throws AnalysisException { 84 if (!analyzed) { 85 analyzed = true; 86 final Set<Dependency> dependenciesToRemove = new HashSet<>(); 87 88 final Dependency[] dependencies = engine.getDependencies(); 89 if (dependencies.length < 2) { 90 return; 91 } 92 Arrays.sort(dependencies, Dependency.NAME_COMPARATOR); 93 for (int x = 0; x < dependencies.length - 1; x++) { 94 final Dependency dependency = dependencies[x]; 95 if (!dependenciesToRemove.contains(dependency)) { 96 for (int y = x + 1; y < dependencies.length; y++) { 97 final Dependency nextDependency = dependencies[y]; 98 if (evaluateDependencies(dependency, nextDependency, dependenciesToRemove)) { 99 break; 100 } 101 } 102 } 103 } 104 dependenciesToRemove.forEach(engine::removeDependency); 105 } 106 } 107 108 /** 109 * Evaluates the dependencies 110 * 111 * @param dependency a dependency to compare 112 * @param nextDependency a dependency to compare 113 * @param dependenciesToRemove a set of dependencies that will be removed 114 * @return true if a dependency is removed; otherwise false 115 */ 116 protected abstract boolean evaluateDependencies(Dependency dependency, 117 Dependency nextDependency, Set<Dependency> dependenciesToRemove); 118 }