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.Collections;
23 import java.util.HashSet;
24 import java.util.Locale;
25 import java.util.Set;
26 import org.apache.maven.plugin.MojoExecutionException;
27 import org.apache.maven.plugin.MojoFailureException;
28 import org.apache.maven.plugins.annotations.LifecyclePhase;
29 import org.apache.maven.plugins.annotations.Mojo;
30 import org.apache.maven.plugins.annotations.Parameter;
31 import org.apache.maven.plugins.annotations.ResolutionScope;
32 import org.apache.maven.project.MavenProject;
33 import org.owasp.dependencycheck.Engine;
34 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
35 import org.owasp.dependencycheck.exception.ExceptionCollection;
36 import org.owasp.dependencycheck.exception.ReportException;
37 import org.owasp.dependencycheck.utils.Settings;
38
39
40
41
42
43
44
45 @Mojo(
46 name = "aggregate",
47 defaultPhase = LifecyclePhase.VERIFY,
48 aggregator = true,
49 threadSafe = false,
50 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
51 requiresOnline = true
52 )
53 public class AggregateMojo extends BaseDependencyCheckMojo {
54
55
56
57
58
59
60
61
62
63
64 @Override
65 public void runCheck() throws MojoExecutionException, MojoFailureException {
66 final Engine engine = loadEngine();
67 if (engine == null) {
68 return;
69 }
70
71 ExceptionCollection exCol = scanArtifacts(getProject(), engine);
72
73 for (MavenProject childProject : getDescendants(this.getProject())) {
74 final ExceptionCollection ex = scanArtifacts(childProject, engine);
75 if (ex != null) {
76 if (exCol == null) {
77 exCol = ex;
78 }
79 exCol.getExceptions().addAll(ex.getExceptions());
80 if (ex.isFatal()) {
81 exCol.setFatal(true);
82 }
83 }
84 }
85
86 try {
87 engine.analyzeDependencies();
88 } catch (ExceptionCollection ex) {
89 if (exCol == null) {
90 exCol = ex;
91 } else if (ex.isFatal()) {
92 exCol.setFatal(true);
93 exCol.getExceptions().addAll(ex.getExceptions());
94 }
95 if (exCol.isFatal()) {
96 final String msg = String.format("Fatal exception(s) analyzing %s", getProject().getName());
97 if (this.isFailOnError()) {
98 throw new MojoExecutionException(msg, exCol);
99 }
100 getLog().error(msg);
101 if (getLog().isDebugEnabled()) {
102 getLog().debug(exCol);
103 }
104 return;
105 } else {
106 final String msg = String.format("Exception(s) analyzing %s", getProject().getName());
107 if (getLog().isDebugEnabled()) {
108 getLog().debug(msg, exCol);
109 }
110 }
111 }
112 File outputDir = getCorrectOutputDirectory(this.getProject());
113 if (outputDir == null) {
114
115
116 outputDir = new File(this.getProject().getBuild().getDirectory());
117 }
118 try {
119 writeReports(engine, this.getProject(), outputDir);
120 } catch (ReportException ex) {
121 if (exCol == null) {
122 exCol = new ExceptionCollection("Error writing aggregate report", ex);
123 } else {
124 exCol.addException(ex);
125 }
126 if (this.isFailOnError()) {
127 throw new MojoExecutionException("One or more exceptions occurred during dependency-check analysis", exCol);
128 } else {
129 getLog().debug("One or more exceptions occurred during dependency-check analysis", exCol);
130 }
131 }
132 showSummary(this.getProject(), engine.getDependencies());
133 checkForFailure(engine.getDependencies());
134 engine.cleanup();
135 Settings.cleanup();
136 }
137
138
139
140
141
142
143
144
145 protected Set<MavenProject> getDescendants(MavenProject project) {
146 if (project == null) {
147 return Collections.emptySet();
148 }
149 final Set<MavenProject> descendants = new HashSet<MavenProject>();
150 int size = 0;
151 if (getLog().isDebugEnabled()) {
152 getLog().debug(String.format("Collecting descendants of %s", project.getName()));
153 }
154 for (String m : project.getModules()) {
155 for (MavenProject mod : getReactorProjects()) {
156 try {
157 File mpp = new File(project.getBasedir(), m);
158 mpp = mpp.getCanonicalFile();
159 if (mpp.compareTo(mod.getBasedir()) == 0 && descendants.add(mod)
160 && getLog().isDebugEnabled()) {
161 getLog().debug(String.format("Decendent module %s added", mod.getName()));
162
163 }
164 } catch (IOException ex) {
165 if (getLog().isDebugEnabled()) {
166 getLog().debug("Unable to determine module path", ex);
167 }
168 }
169 }
170 }
171 do {
172 size = descendants.size();
173 for (MavenProject p : getReactorProjects()) {
174 if (project.equals(p.getParent()) || descendants.contains(p.getParent())) {
175 if (descendants.add(p) && getLog().isDebugEnabled()) {
176 getLog().debug(String.format("Decendent %s added", p.getName()));
177
178 }
179 for (MavenProject modTest : getReactorProjects()) {
180 if (p.getModules() != null && p.getModules().contains(modTest.getName())
181 && descendants.add(modTest)
182 && getLog().isDebugEnabled()) {
183 getLog().debug(String.format("Decendent %s added", modTest.getName()));
184 }
185 }
186 }
187 final Set<MavenProject> addedDescendants = new HashSet<MavenProject>();
188 for (MavenProject dec : descendants) {
189 for (String mod : dec.getModules()) {
190 try {
191 File mpp = new File(dec.getBasedir(), mod);
192 mpp = mpp.getCanonicalFile();
193 if (mpp.compareTo(p.getBasedir()) == 0) {
194 addedDescendants.add(p);
195 }
196 } catch (IOException ex) {
197 if (getLog().isDebugEnabled()) {
198 getLog().debug("Unable to determine module path", ex);
199 }
200 }
201 }
202 }
203 for (MavenProject addedDescendant : addedDescendants) {
204 if (descendants.add(addedDescendant) && getLog().isDebugEnabled()) {
205 getLog().debug(String.format("Decendent module %s added", addedDescendant.getName()));
206 }
207 }
208 }
209 } while (size != 0 && size != descendants.size());
210 if (getLog().isDebugEnabled()) {
211 getLog().debug(String.format("%s has %d children", project, descendants.size()));
212 }
213 return descendants;
214 }
215
216
217
218
219
220
221
222
223 protected boolean isMultiModule(MavenProject mavenProject) {
224 return "pom".equals(mavenProject.getPackaging());
225 }
226
227
228
229
230
231
232
233
234
235
236 protected Engine loadEngine() throws MojoExecutionException, MojoFailureException {
237 Engine engine = null;
238 try {
239 engine = initializeEngine();
240 } catch (DatabaseException ex) {
241 if (getLog().isDebugEnabled()) {
242 getLog().debug("Database connection error", ex);
243 }
244 final String msg = "An exception occurred connecting to the local database. Please see the log file for more details.";
245 if (this.isFailOnError()) {
246 throw new MojoExecutionException(msg, ex);
247 }
248 getLog().error(msg, ex);
249 }
250 return engine;
251 }
252
253 @Override
254 public boolean canGenerateReport() {
255 return true;
256 }
257
258
259
260
261 @SuppressWarnings("CanBeFinal")
262 @Parameter(property = "name", defaultValue = "dependency-check:aggregate", required = true)
263 private String name = "dependency-check:aggregate";
264
265
266
267
268
269
270
271 @Override
272 public String getName(Locale locale) {
273 return name;
274 }
275
276
277
278
279
280
281
282
283 @Override
284 public String getDescription(Locale locale) {
285 return "Generates an aggregate report of all child Maven projects providing details on any "
286 + "published vulnerabilities within project dependencies. This report is a best "
287 + "effort and may contain false positives and false negatives.";
288 }
289 }