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