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