diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java
index be970e3d2..6797df8a1 100644
--- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java
+++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/DependencyCheckMojo.java
@@ -19,9 +19,12 @@ package org.owasp.dependencycheck.maven;
import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
@@ -31,11 +34,8 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.maven.artifact.Artifact;
-import org.apache.maven.doxia.sink.Sink;
-import org.apache.maven.doxia.sink.SinkFactory;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
@@ -45,6 +45,8 @@ import org.apache.maven.reporting.MavenReport;
import org.apache.maven.reporting.MavenReportException;
import org.apache.maven.settings.Proxy;
import org.owasp.dependencycheck.Engine;
+import org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer;
+import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier;
@@ -62,11 +64,11 @@ import org.owasp.dependencycheck.utils.Settings;
requiresOnline = true)
public class DependencyCheckMojo extends ReportAggregationMojo {
+ //
/**
* Logger field reference.
*/
private static final Logger logger = Logger.getLogger(DependencyCheckMojo.class.getName());
-
/**
* The properties file location.
*/
@@ -83,24 +85,14 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
* The dependency-check engine used to scan the project.
*/
private Engine engine = null;
+ //
//
- /**
- * The Maven Project Object.
- */
- @Component
- private MavenProject project;
/**
* The path to the verbose log.
*/
@Parameter(property = "logfile", defaultValue = "")
private String logFile;
- /**
- * Specifies the destination directory for the generated Dependency-Check report. This generally maps to
- * "target/site".
- */
- @Parameter(property = "reportOutputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true)
- private File reportOutputDirectory;
/**
* The output directory. This generally maps to "target".
*/
@@ -124,123 +116,108 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
* The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this
* within the Site plugin unless the externalReport is set to true. Default is HTML.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "format", defaultValue = "HTML", required = true)
private String format = "HTML";
- /**
- * Sets whether or not the external report format should be used.
- */
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
- @Parameter(property = "externalReport", defaultValue = "false", required = true)
- private boolean externalReport = false;
-
/**
* The maven settings.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "mavenSettings", defaultValue = "${settings}", required = false)
private org.apache.maven.settings.Settings mavenSettings;
/**
* The maven settings proxy id.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "mavenSettingsProxyId", required = false)
private String mavenSettingsProxyId;
/**
* The Connection Timeout.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "connectionTimeout", defaultValue = "", required = false)
private String connectionTimeout = null;
/**
* The path to the suppression file.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "suppressionFile", defaultValue = "", required = false)
private String suppressionFile = null;
/**
* Flag indicating whether or not to show a summary in the output.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "showSummary", defaultValue = "true", required = false)
private boolean showSummary = true;
/**
* Whether or not the Jar Analyzer is enabled.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "jarAnalyzerEnabled", defaultValue = "true", required = false)
private boolean jarAnalyzerEnabled = true;
/**
* Whether or not the Archive Analyzer is enabled.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "archiveAnalyzerEnabled", defaultValue = "true", required = false)
private boolean archiveAnalyzerEnabled = true;
/**
* Whether or not the .NET Assembly Analyzer is enabled.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "assemblyAnalyzerEnabled", defaultValue = "true", required = false)
private boolean assemblyAnalyzerEnabled = true;
/**
* Whether or not the .NET Nuspec Analyzer is enabled.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "nuspecAnalyzerEnabled", defaultValue = "true", required = false)
private boolean nuspecAnalyzerEnabled = true;
/**
* Whether or not the Nexus Analyzer is enabled.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "nexusAnalyzerEnabled", defaultValue = "true", required = false)
private boolean nexusAnalyzerEnabled = true;
/**
* Whether or not the Nexus Analyzer is enabled.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "nexusUrl", defaultValue = "", required = false)
private String nexusUrl;
/**
* Whether or not the configured proxy is used to connect to Nexus.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "nexusUsesProxy", defaultValue = "true", required = false)
private boolean nexusUsesProxy = true;
/**
* The database connection string.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "connectionString", defaultValue = "", required = false)
private String connectionString;
/**
* The database driver name. An example would be org.h2.Driver.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "databaseDriverName", defaultValue = "", required = false)
private String databaseDriverName;
/**
* The path to the database driver if it is not on the class path.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "databaseDriverPath", defaultValue = "", required = false)
private String databaseDriverPath;
/**
* The database user name.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "databaseUser", defaultValue = "", required = false)
private String databaseUser;
/**
* The password to use when connecting to the database.
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
@Parameter(property = "databasePassword", defaultValue = "", required = false)
private String databasePassword;
/**
@@ -303,12 +280,20 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
*
* @deprecated Please use mavenSettings instead
*/
- @SuppressWarnings({"CanBeFinal", "FieldCanBeLocal"})
+ @SuppressWarnings("CanBeFinal")
@Parameter(property = "proxyUrl", defaultValue = "", required = false)
@Deprecated
private String proxyUrl = null;
//
+ /**
+ * Constructs a new dependency-check-mojo.
+ */
+ public DependencyCheckMojo() {
+ final InputStream in = DependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
+ LogUtils.prepareLogger(in, logFile);
+ }
+
/**
* Executes the Dependency-Check on the dependent libraries.
*
@@ -316,61 +301,58 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
* @throws DatabaseException thrown if there is an exception connecting to the database
*/
private Engine executeDependencyCheck() throws DatabaseException {
-
- final InputStream in = DependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
- LogUtils.prepareLogger(in, logFile);
-
- populateSettings();
- final Engine engine = new Engine();
-
- final Set artifacts = project.getArtifacts();
- for (Artifact a : artifacts) {
- if (skipTestScope && Artifact.SCOPE_TEST.equals(a.getScope())) {
- continue;
- }
-
- if (skipProvidedScope && Artifact.SCOPE_PROVIDED.equals(a.getScope())) {
- continue;
- }
-
- if (skipRuntimeScope && !Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
- continue;
- }
-
- engine.scan(a.getFile().getAbsolutePath());
- }
- engine.analyzeDependencies();
-
- return engine;
+ return executeDependencyCheck(getProject());
}
/**
- * Returns the maven proxy.
+ * Executes the Dependency-Check on the dependent libraries.
*
- * @return the maven proxy
+ * @param project the project to run dependency-check on
+ * @return the Engine used to scan the dependencies.
+ * @throws DatabaseException thrown if there is an exception connecting to the database
*/
- private Proxy getMavenProxy() {
- if (mavenSettings != null) {
- final List proxies = mavenSettings.getProxies();
- if (proxies != null && proxies.size() > 0) {
- if (mavenSettingsProxyId != null) {
- for (Proxy proxy : proxies) {
- if (mavenSettingsProxyId.equalsIgnoreCase(proxy.getId())) {
- return proxy;
- }
- }
- } else if (proxies.size() == 1) {
- return proxies.get(0);
- } else {
- logger.warning("Multiple proxy defentiions exist in the Maven settings. In the dependency-check "
- + "configuration set the maveSettingsProxyId so that the correct proxy will be used.");
- throw new IllegalStateException("Ambiguous proxy definition");
- }
+ private Engine executeDependencyCheck(MavenProject project) throws DatabaseException {
+ Engine localEngine = initializeEngine();
+
+ final Set artifacts = project.getArtifacts();
+ for (Artifact a : artifacts) {
+ if (excludeFromScan(a)) {
+ continue;
}
+
+ localEngine.scan(a.getFile().getAbsolutePath());
}
- return null;
+ localEngine.analyzeDependencies();
+
+ return localEngine;
}
+ private Engine initializeEngine() throws DatabaseException {
+ populateSettings();
+ final Engine localEngine = new Engine();
+ return localEngine;
+ }
+
+ /**
+ * Tests is the artifact should be included in the scan (i.e. is the dependency in a scope that is being scanned).
+ *
+ * @param a the Artifact to test
+ * @return true if the artifact is in an excluded scope; otherwise false
+ */
+ private boolean excludeFromScan(Artifact a) {
+ if (skipTestScope && Artifact.SCOPE_TEST.equals(a.getScope())) {
+ return true;
+ }
+ if (skipProvidedScope && Artifact.SCOPE_PROVIDED.equals(a.getScope())) {
+ return true;
+ }
+ if (skipRuntimeScope && !Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
+ return true;
+ }
+ return false;
+ }
+
+ //
/**
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system
* properties required to change the proxy url, port, and connection timeout.
@@ -485,6 +467,34 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
}
}
+ /**
+ * Returns the maven proxy.
+ *
+ * @return the maven proxy
+ */
+ private Proxy getMavenProxy() {
+ if (mavenSettings != null) {
+ final List proxies = mavenSettings.getProxies();
+ if (proxies != null && proxies.size() > 0) {
+ if (mavenSettingsProxyId != null) {
+ for (Proxy proxy : proxies) {
+ if (mavenSettingsProxyId.equalsIgnoreCase(proxy.getId())) {
+ return proxy;
+ }
+ }
+ } else if (proxies.size() == 1) {
+ return proxies.get(0);
+ } else {
+ logger.warning("Multiple proxy defentiions exist in the Maven settings. In the dependency-check "
+ + "configuration set the maveSettingsProxyId so that the correct proxy will be used.");
+ throw new IllegalStateException("Ambiguous proxy definition");
+ }
+ }
+ }
+ return null;
+ }
+ //
+
/**
* Executes the dependency-check and generates the report.
*
@@ -495,7 +505,7 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
protected void performExecute() throws MojoExecutionException, MojoFailureException {
try {
engine = executeDependencyCheck();
- ReportingUtil.generateExternalReports(engine, outputDirectory, project.getName(), format);
+ ReportingUtil.generateExternalReports(engine, outputDirectory, getProject().getName(), format);
if (this.showSummary) {
showSummary(engine.getDependencies());
}
@@ -511,50 +521,103 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
@Override
protected void postExecute() throws MojoExecutionException, MojoFailureException {
- super.postExecute();
- Settings.cleanup(true);
- if (engine != null) {
- engine.cleanup();
- engine = null;
+ try {
+ super.postExecute();
+ } finally {
+ cleanupEngine();
}
}
@Override
protected void postGenerate() throws MavenReportException {
- super.postGenerate();
- Settings.cleanup(true);
+ try {
+ super.postGenerate();
+ } finally {
+ cleanupEngine();
+ }
+ }
+
+ private void cleanupEngine() {
if (engine != null) {
engine.cleanup();
engine = null;
}
+ Settings.cleanup(true);
}
/**
* Generates the Dependency-Check Site Report.
*
- * @param sink the sink to write the report to
- * @param sinkFactory the sink factory
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
*/
@Override
- protected void executeNonAggregateReport(Sink sink, SinkFactory sinkFactory, Locale locale) throws MavenReportException {
- try {
- //TODO figure out if the serialized data is present from THIS build and use it instead?
- engine = executeDependencyCheck();
- if (this.externalReport) {
- ReportingUtil.generateExternalReports(engine, reportOutputDirectory, project.getName(), format);
- } else {
- ReportingUtil.generateMavenSiteReport(engine, sink, project.getName());
+ protected void executeNonAggregateReport(Locale locale) throws MavenReportException {
+
+ List deps = readDataFile();
+ if (deps != null) {
+ try {
+ engine = initializeEngine();
+ engine.getDependencies().addAll(deps);
+ } catch (DatabaseException ex) {
+ final String msg = String.format("An unrecoverable exception with the dependency-check initialization occured while scanning %s",
+ getProject().getName());
+ throw new MavenReportException(msg, ex);
}
- } catch (DatabaseException ex) {
- logger.log(Level.SEVERE,
- "Unable to connect to the dependency-check database; analysis has stopped");
- logger.log(Level.FINE, "", ex);
+ } else {
+ try {
+ engine = executeDependencyCheck();
+ } catch (DatabaseException ex) {
+ final String msg = String.format("An unrecoverable exception with the dependency-check scan occured while scanning %s",
+ getProject().getName());
+ throw new MavenReportException(msg, ex);
+ }
+ }
+ ReportingUtil.generateExternalReports(engine, getReportOutputDirectory(), getProject().getName(), format);
+ }
+
+ @Override
+ protected void executeAggregateReport(MavenProject project, Locale locale) throws MavenReportException {
+ List deps = readDataFile(project);
+ if (deps != null) {
+ try {
+ engine = initializeEngine();
+ engine.getDependencies().addAll(deps);
+ } catch (DatabaseException ex) {
+ final String msg = String.format("An unrecoverable exception with the dependency-check initialization occured while scanning %s", project.getName());
+ throw new MavenReportException(msg, ex);
+ }
+ } else {
+ try {
+ engine = executeDependencyCheck(project);
+ } catch (DatabaseException ex) {
+ final String msg = String.format("An unrecoverable exception with the dependency-check scan occured while scanning %s", project.getName());
+ throw new MavenReportException(msg, ex);
+ }
+ }
+ for (MavenProject child : getAllChildren(project)) {
+ deps = readDataFile(child);
+ if (deps == null) {
+ final String msg = String.format("Unable to include information on %s in the dependency-check aggregate report", child.getName());
+ logger.severe(msg);
+ } else {
+ engine.getDependencies().addAll(deps);
+ }
+ }
+ DependencyBundlingAnalyzer bundler = new DependencyBundlingAnalyzer();
+ try {
+ bundler.analyze(null, engine);
+ } catch (AnalysisException ex) {
+ logger.log(Level.WARNING, "An error occured grouping the dependencies; duplicate entries may exist in the report", ex);
+ logger.log(Level.FINE, "Bundling Exception", ex);
+ }
+ File outputDir = getReportOutputDirectory(project);
+ if (outputDir != null) {
+ ReportingUtil.generateExternalReports(engine, outputDir, project.getName(), format);
}
}
- //
+ //
/**
* Returns the output name.
*
@@ -593,24 +656,6 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
return "dependency-check";
}
- /**
- * Sets the Reporting output directory.
- *
- * @param directory the output directory
- */
- public void setReportOutputDirectory(File directory) {
- reportOutputDirectory = directory;
- }
-
- /**
- * Returns the output directory.
- *
- * @return the output directory
- */
- public File getReportOutputDirectory() {
- return reportOutputDirectory;
- }
-
/**
* Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page.
*
@@ -624,24 +669,58 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
}
/**
- * Returns whether this is an external report.
- *
- * @return true or false;
- */
- public boolean isExternalReport() {
- return externalReport;
- }
-
- /**
- * Returns whether or not the plugin can generate a report.
+ * Returns whether or not a report can be generated.
*
* @return true if a report can be generated; otherwise false
*/
public boolean canGenerateReport() {
- return canGenerateNonAggregateReport() || canGenerateAggregateReport();
+ if (canGenerateAggregateReport() || (isAggregate() && isMultiModule())) {
+ return true;
+ }
+ if (canGenerateNonAggregateReport()) {
+ return true;
+ } else {
+ final String msg;
+ if (getProject().getArtifacts().size() > 0) {
+ msg = "No project dependencies exist in the included scope - dependency-check:check is unable to generate a report.";
+ } else {
+ msg = "No project dependencies exist - dependency-check:check is unable to generate a report.";
+ }
+ logger.warning(msg);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether or not a non-aggregate report can be generated.
+ *
+ * @return true if a non-aggregate report can be generated; otherwise false
+ */
+ @Override
+ protected boolean canGenerateNonAggregateReport() {
+ boolean ability = false;
+ for (Artifact a : getProject().getArtifacts()) {
+ if (!excludeFromScan(a)) {
+ ability = true;
+ break;
+ }
+ }
+ return ability;
+ }
+
+ /**
+ * Returns whether or not an aggregate report can be generated.
+ *
+ * @return true if an aggregate report can be generated; otherwise false
+ */
+ @Override
+ protected boolean canGenerateAggregateReport() {
+ return isAggregate() && isLastProject();
}
//
+ //
/**
* Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the
* configuration.
@@ -712,47 +791,86 @@ public class DependencyCheckMojo extends ReportAggregationMojo {
logger.log(Level.WARNING, msg);
}
}
+ //
+ //
+ /**
+ * Writes the scan data to disk. This is used to serialize the scan data between the "check" and "aggregate" phase.
+ *
+ * @return the File object referencing the data file that was written
+ */
@Override
- protected void executeAggregateReport(Sink sink, SinkFactory sinkFactory, Locale locale) throws MavenReportException {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
- }
-
- @Override
- protected boolean canGenerateNonAggregateReport() {
- return true;
- }
-
- @Override
- protected boolean canGenerateAggregateReport() {
- return isAggregate() && isLastProject();
- }
-
- @Override
- protected String getDataFileName() {
- return "dependency-check.ser";
- }
-
- @Override
- protected void writeDataFile() {
- if (engine != null) {
- File file = new File(project.getBuild().getDirectory(), getDataFileName());
+ protected File writeDataFile() {
+ File file = null;
+ if (engine != null && getProject().getContextValue(this.getDataFileContextKey()) == null) {
+ file = new File(getProject().getBuild().getDirectory(), getDataFileName());
try {
OutputStream os = new FileOutputStream(file);
OutputStream bos = new BufferedOutputStream(os);
ObjectOutput out = new ObjectOutputStream(bos);
try {
- out.writeObject(engine);
+ out.writeObject(engine.getDependencies());
out.flush();
} finally {
out.close();
}
- project.setContextValue("dependency-check-path", file.getAbsolutePath());
+ //getProject().setContextValue(this.getDataFileContextKey(), file.getAbsolutePath());
} catch (IOException ex) {
logger.log(Level.WARNING, "Unable to create data file used for report aggregation; "
+ "if report aggregation is being used the results may be incomplete.");
logger.log(Level.FINE, ex.getMessage(), ex);
}
}
+ return file;
}
+
+ /**
+ * Reads the serialized scan data from disk. This is used to serialize the scan data between the "check" and
+ * "aggregate" phase.
+ *
+ * @return a Engine object populated with dependencies if the serialized data file exists; otherwise
+ * null is returned
+ */
+ protected List readDataFile() {
+ return readDataFile(getProject());
+ }
+
+ /**
+ * Reads the serialized scan data from disk. This is used to serialize the scan data between the "check" and
+ * "aggregate" phase.
+ *
+ * @param project the Maven project to read the data file from
+ * @return a Engine object populated with dependencies if the serialized data file exists; otherwise
+ * null is returned
+ */
+ protected List readDataFile(MavenProject project) {
+ Object oPath = project.getContextValue(this.getDataFileContextKey());
+ if (oPath == null) {
+ return null;
+ }
+ List ret = null;
+ String path = (String) oPath;
+ ObjectInputStream ois = null;
+ try {
+ ois = new ObjectInputStream(new FileInputStream(path));
+ ret = (List) ois.readObject();
+ } catch (FileNotFoundException ex) {
+ //TODO fix logging
+ logger.log(Level.SEVERE, null, ex);
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, null, ex);
+ } catch (ClassNotFoundException ex) {
+ logger.log(Level.SEVERE, null, ex);
+ } finally {
+ if (ois != null) {
+ try {
+ ois.close();
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+ return ret;
+ }
+ //
}
diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportAggregationMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportAggregationMojo.java
index 83c00782a..eed113cac 100644
--- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportAggregationMojo.java
+++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/ReportAggregationMojo.java
@@ -21,19 +21,20 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.logging.Logger;
import org.apache.maven.doxia.sink.Sink;
-import org.apache.maven.doxia.sink.SinkFactory;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
-import org.apache.maven.reporting.MavenMultiPageReport;
+import org.apache.maven.reporting.MavenReport;
import org.apache.maven.reporting.MavenReportException;
/**
@@ -56,7 +57,7 @@ import org.apache.maven.reporting.MavenReportException;
*
* @author Jeremy Long
*/
-public abstract class ReportAggregationMojo extends AbstractMojo implements MavenMultiPageReport {
+public abstract class ReportAggregationMojo extends AbstractMojo implements MavenReport {
/**
* The Maven Project Object.
@@ -81,18 +82,80 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
@Parameter(property = "aggregate", defaultValue = "false")
private boolean aggregate;
- private Map< MavenProject, List< MavenProject>> projectChildren;
+ /**
+ * Sets whether or not the external report format should be used.
+ */
+ @Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true)
+ private String dataFileName;
+ /**
+ * Specifies the destination directory for the generated Dependency-Check report. This generally maps to
+ * "target/site".
+ */
+ @Parameter(property = "reportOutputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true)
+ private File reportOutputDirectory;
+
+ /**
+ * Sets the Reporting output directory.
+ *
+ * @param directory the output directory
+ */
+ @Override
+ public void setReportOutputDirectory(File directory) {
+ reportOutputDirectory = directory;
+ }
+
+ /**
+ * Returns the output directory.
+ *
+ * @return the output directory
+ */
+ @Override
+ public File getReportOutputDirectory() {
+ return reportOutputDirectory;
+ }
+
+ public File getReportOutputDirectory(MavenProject project) {
+ Object o = project.getContextValue(getOutputDirectoryContextKey());
+ if (o != null && o instanceof File) {
+ return (File) o;
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether this is an external report. This method always returns true.
+ *
+ * @return true
+ */
+ @Override
+ public final boolean isExternalReport() {
+ return true;
+ }
+
+ /**
+ * The collection of child projects.
+ */
+ private final Map< MavenProject, Set> projectChildren = new HashMap>();
protected void preExecute() throws MojoExecutionException, MojoFailureException {
- if (this.canGenerateAggregateReport()) {
- buildAggregateInfo();
- }
+ buildAggregateInfo();
}
protected abstract void performExecute() throws MojoExecutionException, MojoFailureException;
protected void postExecute() throws MojoExecutionException, MojoFailureException {
- writeDataFile();
+ File written = writeDataFile();
+ if (written != null) {
+ project.setContextValue(getDataFileContextKey(), written.getAbsolutePath());
+ }
+ }
+
+ protected String getDataFileContextKey() {
+ return "dependency-check-path-" + this.getDataFileName();
+ }
+
+ protected String getOutputDirectoryContextKey() {
+ return "dependency-output-dir-" + this.getDataFileName();
}
public final void execute() throws MojoExecutionException, MojoFailureException {
@@ -110,9 +173,9 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
* @throws MavenReportException if a maven report exception occurs
*/
protected void preGenerate() throws MavenReportException {
- if (canGenerateAggregateReport()) {
- buildAggregateInfo();
- }
+ buildAggregateInfo();
+
+ project.setContextValue(getOutputDirectoryContextKey(), getReportOutputDirectory());
}
/**
@@ -121,28 +184,28 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
* @throws MavenReportException if a maven report exception occurs
*/
protected void postGenerate() throws MavenReportException {
- writeDataFile();
+ File written = writeDataFile();
+ if (written != null) {
+ project.setContextValue(getDataFileContextKey(), written.getAbsolutePath());
+ }
}
/**
* Generates the non aggregate report.
*
- * @param sink the sink to write the report to
- * @param sinkFactory the sink factory
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
*/
- protected abstract void executeNonAggregateReport(Sink sink, SinkFactory sinkFactory, Locale locale) throws MavenReportException;
+ protected abstract void executeNonAggregateReport(Locale locale) throws MavenReportException;
/**
* Generates the aggregate Site Report.
*
- * @param sink the sink to write the report to
- * @param sinkFactory the sink factory
+ * @param project the maven project used to generate the aggregate report
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
*/
- protected abstract void executeAggregateReport(Sink sink, SinkFactory sinkFactory, Locale locale) throws MavenReportException;
+ protected abstract void executeAggregateReport(MavenProject project, Locale locale) throws MavenReportException;
/**
* Generates the Dependency-Check Site Report.
@@ -150,11 +213,11 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
* @param sink the sink to write the report to
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
- * @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, org.apache.maven.doxia.sink.SinkFactory, java.util.Locale) instead.
+ * @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, java.util.Locale) instead.
*/
@Deprecated
public final void generate(@SuppressWarnings("deprecation") org.codehaus.doxia.sink.Sink sink, Locale locale) throws MavenReportException {
- generate((Sink) sink, null, locale);
+ generate((Sink) sink, locale);
}
/**
@@ -163,30 +226,21 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
* @param sink the sink to write the report to
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
- * @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, org.apache.maven.doxia.sink.SinkFactory, java.util.Locale) instead.
*/
- @Deprecated
public final void generate(Sink sink, Locale locale) throws MavenReportException {
- generate(sink, null, locale);
- }
-
- /**
- * Generates the Dependency-Check Site Report.
- *
- * @param sink the sink to write the report to
- * @param sinkFactory the sink factory
- * @param locale the locale to use when generating the report
- * @throws MavenReportException if a maven report exception occurs
- */
- public final void generate(Sink sink, SinkFactory sinkFactory, Locale locale) throws MavenReportException {
try {
preGenerate();
if (canGenerateNonAggregateReport()) {
- executeNonAggregateReport(sink, sinkFactory, locale);
+ executeNonAggregateReport(locale);
}
if (canGenerateAggregateReport()) {
- executeAggregateReport(sink, sinkFactory, locale);
+ for (MavenProject proj : reactorProjects) {
+ if (!isMultiModule(proj)) {
+ continue;
+ }
+ executeAggregateReport(proj, locale);
+ }
}
} finally {
postGenerate();
@@ -208,36 +262,30 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
protected abstract boolean canGenerateAggregateReport();
/**
- * Returns the data file's names.
+ * Returns the name of the data file that contains the serialized data.
*
- * @return the data file's name
+ * @return the name of the data file that contains the serialized data
*/
- protected abstract String getDataFileName();
+ protected String getDataFileName() {
+ return dataFileName;
+ }
/**
* Writes the data file to disk in the target directory.
*
+ * @return the File object referencing the data file that was written
*/
- protected abstract void writeDataFile();
+ protected abstract File writeDataFile();
/**
* Collects the information needed for building aggregate reports.
*/
private void buildAggregateInfo() {
- if (projectChildren != null) {
- // already did this work
- return;
- }
- logger.warning("building aggregate info");
- boolean test = reactorProjects == null;
- logger.warning("Reactor is " + test);
-
// build parent-child map
- projectChildren = new HashMap>();
for (MavenProject proj : reactorProjects) {
- List depList = projectChildren.get(proj.getParent());
+ Set depList = projectChildren.get(proj.getParent());
if (depList == null) {
- depList = new ArrayList();
+ depList = new HashSet();
projectChildren.put(proj.getParent(), depList);
}
depList.add(proj);
@@ -259,8 +307,8 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
* @param parentProject the parent project to collect the child project references
* @return a list of child projects
*/
- private List getAllChildren(MavenProject parentProject) {
- List children = projectChildren.get(parentProject);
+ protected List getAllChildren(MavenProject parentProject) {
+ Set children = projectChildren.get(parentProject);
if (children == null) {
return Collections.emptyList();
}
@@ -276,15 +324,9 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
return result;
}
- /**
- * Returns any existing output files from the current projects children.
- *
- * @param projects the list of projects to obtain the output files from
- * @return a list of output files
- */
- protected List getChildDataFiles() {
- List projects = getAllChildren();
- return getDataFiles(projects);
+ protected List getAllChildDataFiles(MavenProject project) {
+ List children = getAllChildren(project);
+ return getDataFiles(children);
}
/**
@@ -296,18 +338,21 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
protected List getDataFiles(List projects) {
List files = new ArrayList();
for (MavenProject proj : projects) {
- if (isMultiModule(proj)) {
- continue;
- }
- //TODO can we get the path from the context?
- File outputFile = new File(proj.getBuild().getDirectory(), getDataFileName());
- if (outputFile.exists()) {
- files.add(outputFile);
+ Object path = project.getContextValue(getDataFileContextKey());
+ if (path == null) {
+ final String msg = String.format("Unable to aggregate data for '%s' - aggregate data file was not generated",
+ proj.getName());
+ logger.warning(msg);
} else {
- if (!isMultiModule(project)) {
- final String msg = String.format("Unable to aggregate data for '%s' - missing data file '%s'",
- proj.getName(), outputFile.getPath());
- logger.warning(msg);
+ File outputFile = new File((String) path);
+ if (outputFile.exists()) {
+ files.add(outputFile);
+ } else {
+ if (!isMultiModule(project)) {
+ final String msg = String.format("Unable to aggregate data for '%s' - missing data file '%s'",
+ proj.getName(), outputFile.getPath());
+ logger.warning(msg);
+ }
}
}
}
@@ -318,20 +363,20 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
* Test if the project has pom packaging
*
* @param mavenProject Project to test
- * @return True if it has a pom packaging
+ * @return true if it has a pom packaging; otherwise false
*/
- private boolean isMultiModule(MavenProject mavenProject) {
+ protected boolean isMultiModule(MavenProject mavenProject) {
return "pom".equals(mavenProject.getPackaging());
}
/**
- * Test if the project has pom packaging
+ * Test if the current project has pom packaging
*
* @param mavenProject Project to test
- * @return True if it has a pom packaging
+ * @return true if it has a pom packaging; otherwise false
*/
protected boolean isMultiModule() {
- return "pom".equals(project.getPackaging());
+ return isMultiModule(project);
}
/**
@@ -353,4 +398,16 @@ public abstract class ReportAggregationMojo extends AbstractMojo implements Mave
public boolean isAggregate() {
return aggregate;
}
+
+ /**
+ * Returns a reference to the current project. This method is used instead of auto-binding the project via component
+ * annotation in concrete implementations of this. If the child has a @Component MavenProject project;
+ * defined then the abstract class (i.e. this class) will not have access to the current project (just the way Maven
+ * works with the binding).
+ *
+ * @return
+ */
+ protected MavenProject getProject() {
+ return project;
+ }
}