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 }