1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.maven;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Set;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
30 import org.apache.maven.plugin.MojoExecutionException;
31 import org.apache.maven.plugin.MojoFailureException;
32 import org.apache.maven.plugins.annotations.LifecyclePhase;
33 import org.apache.maven.plugins.annotations.Mojo;
34 import org.apache.maven.plugins.annotations.ResolutionScope;
35 import org.apache.maven.project.MavenProject;
36 import org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer;
37 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
38 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
39 import org.owasp.dependencycheck.dependency.Dependency;
40 import org.owasp.dependencycheck.utils.Settings;
41
42
43
44
45
46
47
48 @Mojo(
49 name = "aggregate",
50 defaultPhase = LifecyclePhase.SITE,
51 aggregator = true,
52 threadSafe = true,
53 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
54 requiresOnline = true
55 )
56 public class AggregateMojo extends BaseDependencyCheckMojo {
57
58
59
60
61 private static final Logger LOGGER = Logger.getLogger(AggregateMojo.class.getName());
62
63
64
65
66
67
68
69 @Override
70 public void runCheck() throws MojoExecutionException, MojoFailureException {
71 final Engine engine = generateDataFile();
72
73 if (getProject() == getReactorProjects().get(getReactorProjects().size() - 1)) {
74
75
76 for (MavenProject current : getReactorProjects()) {
77 final File dataFile = getDataFile(current);
78 if (dataFile == null) {
79 LOGGER.fine(String.format("Executing dependency-check on %s", current.getName()));
80 generateDataFile(engine, current);
81 }
82 }
83
84 for (MavenProject current : getReactorProjects()) {
85 List<Dependency> dependencies = readDataFile(current);
86 if (dependencies == null) {
87 dependencies = new ArrayList<Dependency>();
88 }
89 final Set<MavenProject> childProjects = getDescendants(current);
90 for (MavenProject reportOn : childProjects) {
91 final List<Dependency> childDeps = readDataFile(reportOn);
92 if (childDeps != null && !childDeps.isEmpty()) {
93 LOGGER.fine(String.format("Adding %d dependencies from %s", childDeps.size(), reportOn.getName()));
94 dependencies.addAll(childDeps);
95 } else {
96 LOGGER.fine(String.format("No dependencies read for %s", reportOn.getName()));
97 }
98 }
99 engine.getDependencies().clear();
100 engine.getDependencies().addAll(dependencies);
101 final DependencyBundlingAnalyzer bundler = new DependencyBundlingAnalyzer();
102 try {
103 LOGGER.fine(String.format("Dependency count pre-bundler: %s", engine.getDependencies().size()));
104 bundler.analyze(null, engine);
105 LOGGER.fine(String.format("Dependency count post-bundler: %s", engine.getDependencies().size()));
106 } catch (AnalysisException ex) {
107 LOGGER.log(Level.WARNING, "An error occured grouping the dependencies; duplicate entries may exist in the report", ex);
108 LOGGER.log(Level.FINE, "Bundling Exception", ex);
109 }
110
111 File outputDir = getCorrectOutputDirectory(current);
112 if (outputDir == null) {
113
114
115 outputDir = new File(current.getBuild().getDirectory());
116 }
117 writeReports(engine, current, outputDir);
118 }
119 }
120 engine.cleanup();
121 Settings.cleanup();
122 }
123
124
125
126
127
128
129
130 protected Set<MavenProject> getDescendants(MavenProject project) {
131 if (project == null) {
132 return Collections.emptySet();
133 }
134 final Set<MavenProject> descendants = new HashSet<MavenProject>();
135 int size = 0;
136 LOGGER.fine(String.format("Collecting descendants of %s", project.getName()));
137 for (String m : project.getModules()) {
138 for (MavenProject mod : getReactorProjects()) {
139 try {
140 File mpp = new File(project.getBasedir(), m);
141 mpp = mpp.getCanonicalFile();
142 if (mpp.compareTo(mod.getBasedir()) == 0) {
143 if (descendants.add(mod)) {
144 LOGGER.fine(String.format("Decendent module %s added", mod.getName()));
145 }
146 }
147 } catch (IOException ex) {
148 LOGGER.log(Level.FINE, "Unable to determine module path", ex);
149 }
150 }
151 }
152 do {
153 size = descendants.size();
154 for (MavenProject p : getReactorProjects()) {
155 if (project.equals(p.getParent()) || descendants.contains(p.getParent())) {
156 if (descendants.add(p)) {
157 LOGGER.fine(String.format("Decendent %s added", p.getName()));
158 }
159 for (MavenProject modTest : getReactorProjects()) {
160 if (p.getModules() != null && p.getModules().contains(modTest.getName())) {
161 if (descendants.add(modTest)) {
162 LOGGER.fine(String.format("Decendent %s added", modTest.getName()));
163 }
164 }
165 }
166 }
167 for (MavenProject dec : descendants) {
168 for (String mod : dec.getModules()) {
169 try {
170 File mpp = new File(dec.getBasedir(), mod);
171 mpp = mpp.getCanonicalFile();
172 if (mpp.compareTo(p.getBasedir()) == 0) {
173 if (descendants.add(p)) {
174 LOGGER.fine(String.format("Decendent module %s added", p.getName()));
175 }
176 }
177 } catch (IOException ex) {
178 LOGGER.log(Level.FINE, "Unable to determine module path", ex);
179 }
180 }
181 }
182 }
183 } while (size != 0 && size != descendants.size());
184 LOGGER.fine(String.format("%s has %d children", project, descendants.size()));
185 return descendants;
186 }
187
188
189
190
191
192
193
194 protected boolean isMultiModule(MavenProject mavenProject) {
195 return "pom".equals(mavenProject.getPackaging());
196 }
197
198
199
200
201
202
203
204
205 protected Engine generateDataFile() throws MojoExecutionException, MojoFailureException {
206 final Engine engine;
207 try {
208 engine = initializeEngine();
209 } catch (DatabaseException ex) {
210 LOGGER.log(Level.FINE, "Database connection error", ex);
211 throw new MojoExecutionException("An exception occured connecting to the local database. Please see the log file for more details.", ex);
212 }
213 return generateDataFile(engine, getProject());
214 }
215
216
217
218
219
220
221
222
223
224
225 protected Engine generateDataFile(Engine engine, MavenProject project) throws MojoExecutionException, MojoFailureException {
226 LOGGER.fine(String.format("Begin Scanning: %s", project.getName()));
227 engine.getDependencies().clear();
228 engine.resetFileTypeAnalyzers();
229 scanArtifacts(project, engine);
230 engine.analyzeDependencies();
231 final File target = new File(project.getBuild().getDirectory());
232 writeDataFile(project, target, engine.getDependencies());
233 showSummary(project, engine.getDependencies());
234 checkForFailure(engine.getDependencies());
235 return engine;
236 }
237
238 @Override
239 public boolean canGenerateReport() {
240 return true;
241 }
242
243
244
245
246
247
248
249 public String getName(Locale locale) {
250 return "dependency-check:aggregate";
251 }
252
253
254
255
256
257
258
259 public String getDescription(Locale locale) {
260 return "Generates an aggregate report of all child Maven projects providing details on any "
261 + "published vulnerabilities within project dependencies. This report is a best "
262 + "effort and may contain false positives and false negatives.";
263 }
264 }