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 && descendants.add(mod)) {
143 LOGGER.fine(String.format("Decendent module %s added", mod.getName()));
144 }
145 } catch (IOException ex) {
146 LOGGER.log(Level.FINE, "Unable to determine module path", ex);
147 }
148 }
149 }
150 do {
151 size = descendants.size();
152 for (MavenProject p : getReactorProjects()) {
153 if (project.equals(p.getParent()) || descendants.contains(p.getParent())) {
154 if (descendants.add(p)) {
155 LOGGER.fine(String.format("Decendent %s added", p.getName()));
156 }
157 for (MavenProject modTest : getReactorProjects()) {
158 if (p.getModules() != null && p.getModules().contains(modTest.getName())
159 && descendants.add(modTest)) {
160 LOGGER.fine(String.format("Decendent %s added", modTest.getName()));
161 }
162 }
163 }
164 for (MavenProject dec : descendants) {
165 for (String mod : dec.getModules()) {
166 try {
167 File mpp = new File(dec.getBasedir(), mod);
168 mpp = mpp.getCanonicalFile();
169 if (mpp.compareTo(p.getBasedir()) == 0 && descendants.add(p)) {
170 LOGGER.fine(String.format("Decendent module %s added", p.getName()));
171 }
172 } catch (IOException ex) {
173 LOGGER.log(Level.FINE, "Unable to determine module path", ex);
174 }
175 }
176 }
177 }
178 } while (size != 0 && size != descendants.size());
179 LOGGER.fine(String.format("%s has %d children", project, descendants.size()));
180 return descendants;
181 }
182
183
184
185
186
187
188
189 protected boolean isMultiModule(MavenProject mavenProject) {
190 return "pom".equals(mavenProject.getPackaging());
191 }
192
193
194
195
196
197
198
199
200 protected Engine generateDataFile() throws MojoExecutionException, MojoFailureException {
201 final Engine engine;
202 try {
203 engine = initializeEngine();
204 } catch (DatabaseException ex) {
205 LOGGER.log(Level.FINE, "Database connection error", ex);
206 throw new MojoExecutionException("An exception occured connecting to the local database. Please see the log file for more details.", ex);
207 }
208 return generateDataFile(engine, getProject());
209 }
210
211
212
213
214
215
216
217
218
219
220 protected Engine generateDataFile(Engine engine, MavenProject project) throws MojoExecutionException, MojoFailureException {
221 LOGGER.fine(String.format("Begin Scanning: %s", project.getName()));
222 engine.getDependencies().clear();
223 engine.resetFileTypeAnalyzers();
224 scanArtifacts(project, engine);
225 engine.analyzeDependencies();
226 final File target = new File(project.getBuild().getDirectory());
227 writeDataFile(project, target, engine.getDependencies());
228 showSummary(project, engine.getDependencies());
229 checkForFailure(engine.getDependencies());
230 return engine;
231 }
232
233 @Override
234 public boolean canGenerateReport() {
235 return true;
236 }
237
238
239
240
241
242
243
244 public String getName(Locale locale) {
245 return "dependency-check:aggregate";
246 }
247
248
249
250
251
252
253
254 public String getDescription(Locale locale) {
255 return "Generates an aggregate report of all child Maven projects providing details on any "
256 + "published vulnerabilities within project dependencies. This report is a best "
257 + "effort and may contain false positives and false negatives.";
258 }
259 }