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