diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java index 78f6e823a..86268a2d2 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java @@ -32,9 +32,12 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; +import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; +import org.owasp.dependencycheck.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.reporting.ReportGenerator.Format; import org.owasp.dependencycheck.utils.Settings; @@ -806,52 +809,67 @@ public class Check extends Update { engine = new Engine(Check.class.getClassLoader()); if (isUpdateOnly()) { log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN); - engine.doUpdates(); - } else { try { - for (Resource resource : path) { - final FileProvider provider = resource.as(FileProvider.class); - if (provider != null) { - final File file = provider.getFile(); - if (file != null && file.exists()) { - engine.scan(file); - } + engine.doUpdates(); + } catch (UpdateException ex) { + if (this.isFailOnError()) { + throw new BuildException(ex); + } + log(ex.getMessage(), Project.MSG_ERR); + } + } else { + for (Resource resource : path) { + final FileProvider provider = resource.as(FileProvider.class); + if (provider != null) { + final File file = provider.getFile(); + if (file != null && file.exists()) { + engine.scan(file); } } + } + try { engine.analyzeDependencies(); - DatabaseProperties prop = null; - CveDB cve = null; - try { - cve = new CveDB(); - cve.open(); - prop = cve.getDatabaseProperties(); - } catch (DatabaseException ex) { - log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG); - } finally { - if (cve != null) { - cve.close(); - } + } catch (ExceptionCollection ex) { + if (this.isFailOnError()) { + throw new BuildException(ex); } - final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop); - reporter.generateReports(reportOutputDirectory, reportFormat); + } + DatabaseProperties prop = null; + CveDB cve = null; + try { + cve = new CveDB(); + cve.open(); + prop = cve.getDatabaseProperties(); + } catch (DatabaseException ex) { + log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG); + } finally { + if (cve != null) { + cve.close(); + } + } + final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop); + reporter.generateReports(reportOutputDirectory, reportFormat); - if (this.failBuildOnCVSS <= 10) { - checkForFailure(engine.getDependencies()); - } - if (this.showSummary) { - showSummary(engine.getDependencies()); - } - } catch (IOException ex) { - log("Unable to generate dependency-check report", ex, Project.MSG_DEBUG); - throw new BuildException("Unable to generate dependency-check report", ex); - } catch (Exception ex) { - log("An exception occurred; unable to continue task", ex, Project.MSG_DEBUG); - throw new BuildException("An exception occurred; unable to continue task", ex); + if (this.failBuildOnCVSS <= 10) { + checkForFailure(engine.getDependencies()); + } + if (this.showSummary) { + showSummary(engine.getDependencies()); } } } catch (DatabaseException ex) { - log("Unable to connect to the dependency-check database; analysis has stopped", ex, Project.MSG_ERR); + final String msg = "Unable to connect to the dependency-check database; analysis has stopped"; + if (this.isFailOnError()) { + throw new BuildException(msg, ex); + } + log(msg, ex, Project.MSG_ERR); + } catch (ReportException ex) { + final String msg = "Unable to generate the dependency-check report"; + if (this.isFailOnError()) { + throw new BuildException(msg, ex); + } + log(msg, ex, Project.MSG_ERR); } finally { Settings.cleanup(true); if (engine != null) { diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java index ce28b0645..5316a44c8 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java @@ -71,6 +71,30 @@ public class Purge extends Task { this.dataDirectory = dataDirectory; } + /** + * Indicates if dependency-check should fail the build if an exception + * occurs. + */ + private boolean failOnError = true; + + /** + * Get the value of failOnError + * + * @return the value of failOnError + */ + public boolean isFailOnError() { + return failOnError; + } + + /** + * Set the value of failOnError + * + * @param failOnError new value of failOnError + */ + public void setFailOnError(boolean failOnError) { + this.failOnError = failOnError; + } + @Override public void execute() throws BuildException { populateSettings(); @@ -81,30 +105,49 @@ public class Purge extends Task { if (db.delete()) { log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO); } else { - log(String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath()), Project.MSG_ERR); + final String msg = String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath()); + if (this.failOnError) { + throw new BuildException(msg); + } + log(msg, Project.MSG_ERR); } } else { - log(String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath()), Project.MSG_ERR); + final String msg = String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath()); + if (this.failOnError) { + throw new BuildException(msg); + } + log(msg, Project.MSG_ERR); } } catch (IOException ex) { - log("Unable to delete the database", Project.MSG_ERR); + final String msg = "Unable to delete the database"; + if (this.failOnError) { + throw new BuildException(msg); + } + log(msg, Project.MSG_ERR); } finally { Settings.cleanup(true); } } /** - * Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties - * required to change the proxy server, port, and connection timeout. + * Takes the properties supplied and updates the dependency-check settings. + * Additionally, this sets the system properties required to change the + * proxy server, port, and connection timeout. + * + * @throws BuildException thrown if the properties file cannot be read. */ - protected void populateSettings() { + protected void populateSettings() throws BuildException { Settings.initialize(); InputStream taskProperties = null; try { taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE); Settings.mergeProperties(taskProperties); } catch (IOException ex) { - log("Unable to load the dependency-check ant task.properties file.", ex, Project.MSG_WARN); + final String msg = "Unable to load the dependency-check ant task.properties file."; + if (this.failOnError) { + throw new BuildException(msg, ex); + } + log(msg, ex, Project.MSG_WARN); } finally { if (taskProperties != null) { try { diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java index d121f21c1..5648c7c3e 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java @@ -18,14 +18,17 @@ package org.owasp.dependencycheck.taskdefs; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.impl.StaticLoggerBinder; /** - * An Ant task definition to execute dependency-check update. This will download the latest data from the National Vulnerability - * Database (NVD) and store a copy in the local database. + * An Ant task definition to execute dependency-check update. This will download + * the latest data from the National Vulnerability Database (NVD) and store a + * copy in the local database. * * @author Jeremy Long */ @@ -381,10 +384,11 @@ public class Update extends Purge { } /** - * Executes the update by initializing the settings, downloads the NVD XML data, and then processes the data storing it in the - * local database. + * Executes the update by initializing the settings, downloads the NVD XML + * data, and then processes the data storing it in the local database. * - * @throws BuildException thrown if a connection to the local database cannot be made. + * @throws BuildException thrown if a connection to the local database + * cannot be made. */ @Override public void execute() throws BuildException { @@ -392,9 +396,20 @@ public class Update extends Purge { Engine engine = null; try { engine = new Engine(Update.class.getClassLoader()); - engine.doUpdates(); + try { + engine.doUpdates(); + } catch (UpdateException ex) { + if (this.isFailOnError()) { + throw new BuildException(ex); + } + log(ex.getMessage(), Project.MSG_ERR); + } } catch (DatabaseException ex) { - throw new BuildException("Unable to connect to the dependency-check database; unable to update the NVD data", ex); + final String msg = "Unable to connect to the dependency-check database; unable to update the NVD data"; + if (this.isFailOnError()) { + throw new BuildException(msg, ex); + } + log(msg, Project.MSG_ERR); } finally { Settings.cleanup(true); if (engine != null) { @@ -404,8 +419,9 @@ public class Update extends Purge { } /** - * Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties - * required to change the proxy server, port, and connection timeout. + * Takes the properties supplied and updates the dependency-check settings. + * Additionally, this sets the system properties required to change the + * proxy server, port, and connection timeout. * * @throws BuildException thrown when an invalid setting is configured. */ diff --git a/dependency-check-ant/src/site/markdown/config-purge.md b/dependency-check-ant/src/site/markdown/config-purge.md index 80c9f5d91..e7026070a 100644 --- a/dependency-check-ant/src/site/markdown/config-purge.md +++ b/dependency-check-ant/src/site/markdown/config-purge.md @@ -14,6 +14,7 @@ Configuration: dependency-check-purge Task -------------------- The following properties can be set on the dependency-check-purge task. -Property | Description | Default Value -----------------------|----------------------------------------------------------------|------------------ -dataDirectory | Data directory that is used to store the local copy of the NVD | data +Property | Description | Default Value +----------------------|------------------------------------------------------------------------|------------------ +dataDirectory | Data directory that is used to store the local copy of the NVD | data +failOnError | Whether the build should fail if there is an error executing the purge | true diff --git a/dependency-check-ant/src/site/markdown/config-update.md b/dependency-check-ant/src/site/markdown/config-update.md index dd62e3dbe..87c772b80 100644 --- a/dependency-check-ant/src/site/markdown/config-update.md +++ b/dependency-check-ant/src/site/markdown/config-update.md @@ -24,6 +24,7 @@ proxyPort | The Proxy Port. |   proxyUsername | Defines the proxy user name. |   proxyPassword | Defines the proxy password. |   connectionTimeout | The URL Connection Timeout. |   +failOnError | Whether the build should fail if there is an error executing the update | true Advanced Configuration ==================== diff --git a/dependency-check-ant/src/site/markdown/configuration.md b/dependency-check-ant/src/site/markdown/configuration.md index e2bf57a24..a711cd013 100644 --- a/dependency-check-ant/src/site/markdown/configuration.md +++ b/dependency-check-ant/src/site/markdown/configuration.md @@ -34,6 +34,7 @@ Property | Description autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true cveValidForHours | Sets the number of hours to wait before checking for new updates from the NVD | 4 failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which means since the CVSS scores are 0-10, by default the build will never fail. | 11 +failOnError | Whether the build should fail if there is an error executing the dependency-check analysis | true projectName | The name of the project being scanned. | Dependency-Check reportFormat | 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. | HTML reportOutputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target' diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java index c8bc71cd6..6e1d0f8dd 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java @@ -37,6 +37,11 @@ import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.core.FileAppender; +import java.util.logging.Level; +import org.owasp.dependencycheck.data.update.exception.UpdateException; +import org.owasp.dependencycheck.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.ReportException; +import org.owasp.dependencycheck.utils.InvalidSettingException; import org.slf4j.impl.StaticLoggerBinder; /** @@ -57,13 +62,15 @@ public class App { * @param args the command line arguments */ public static void main(String[] args) { + int exitCode = 0; try { Settings.initialize(); final App app = new App(); - app.run(args); + exitCode = app.run(args); } finally { Settings.cleanup(true); } + System.exit(exitCode); } /** @@ -71,7 +78,8 @@ public class App { * * @param args the command line arguments */ - public void run(String[] args) { + public int run(String[] args) { + int exitCode = 0; final CliParser cli = new CliParser(); try { @@ -79,11 +87,11 @@ public class App { } catch (FileNotFoundException ex) { System.err.println(ex.getMessage()); cli.printHelp(); - return; + return -1; } catch (ParseException ex) { System.err.println(ex.getMessage()); cli.printHelp(); - return; + return -2; } if (cli.getVerboseLog() != null) { @@ -93,8 +101,15 @@ public class App { if (cli.isPurge()) { if (cli.getConnectionString() != null) { LOGGER.error("Unable to purge the database when using a non-default connection string"); + exitCode = -3; } else { - populateSettings(cli); + try { + populateSettings(cli); + } catch (InvalidSettingException ex) { + LOGGER.error(ex.getMessage()); + LOGGER.debug("Error loading properties file", ex); + exitCode = -4; + } File db; try { db = new File(Settings.getDataDirectory(), "dc.h2.db"); @@ -103,46 +118,96 @@ public class App { LOGGER.info("Database file purged; local copy of the NVD has been removed"); } else { LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath()); + exitCode = -5; } } else { LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath()); + exitCode = -6; } } catch (IOException ex) { LOGGER.error("Unable to delete the database"); + exitCode = -7; } } } else if (cli.isGetVersion()) { cli.printVersionInfo(); } else if (cli.isUpdateOnly()) { - populateSettings(cli); - runUpdateOnly(); + try { + populateSettings(cli); + } catch (InvalidSettingException ex) { + LOGGER.error(ex.getMessage()); + LOGGER.debug("Error loading properties file", ex); + exitCode = -4; + } + try { + runUpdateOnly(); + } catch (UpdateException ex) { + LOGGER.error(ex.getMessage()); + exitCode = -8; + } catch (DatabaseException ex) { + LOGGER.error(ex.getMessage()); + exitCode = -9; + } } else if (cli.isRunScan()) { - populateSettings(cli); + try { + populateSettings(cli); + } catch (InvalidSettingException ex) { + LOGGER.error(ex.getMessage()); + LOGGER.debug("Error loading properties file", ex); + exitCode = -4; + } try { runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), cli.getScanFiles(), cli.getExcludeList(), cli.getSymLinkDepth()); } catch (InvalidScanPathException ex) { LOGGER.error("An invalid scan path was detected; unable to scan '//*' paths"); + exitCode = -10; + } catch (DatabaseException ex) { + LOGGER.error(ex.getMessage()); + exitCode = -11; + } catch (ReportException ex) { + LOGGER.error(ex.getMessage()); + exitCode = -12; + } catch (ExceptionCollection ex) { + if (ex.isFatal()) { + exitCode =-13; + LOGGER.error("One or more fatal errors occured"); + } else { + exitCode =-14; + } + for (Throwable e : ex.getExceptions()) { + LOGGER.error(e.getMessage()); + } } } else { cli.printHelp(); } + return exitCode; } /** - * Scans the specified directories and writes the dependency reports to the reportDirectory. + * Scans the specified directories and writes the dependency reports to the + * reportDirectory. * - * @param reportDirectory the path to the directory where the reports will be written + * @param reportDirectory the path to the directory where the reports will + * be written * @param outputFormat the output format of the report * @param applicationName the application name for the report * @param files the files/directories to scan * @param excludes the patterns for files/directories to exclude * @param symLinkDepth the depth that symbolic links will be followed * - * @throws InvalidScanPathException thrown if the path to scan starts with "//" + * @throws InvalidScanPathException thrown if the path to scan starts with + * "//" + * @throws ReportException thrown when the report cannot be generated + * @throws DatabaseException thrown when there is an error connecting to the + * database + * @throws ExceptionCollection thrown when an exception occurs during + * analysis; there may be multiple exceptions contained within the + * collection. */ private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files, - String[] excludes, int symLinkDepth) throws InvalidScanPathException { + String[] excludes, int symLinkDepth) throws InvalidScanPathException, DatabaseException, ExceptionCollection, ReportException { Engine engine = null; try { engine = new Engine(); @@ -174,8 +239,6 @@ public class App { include = "**/*"; } } - //LOGGER.debug("baseDir: {}", baseDir); - //LOGGER.debug("include: {}", include); scanner.setBasedir(baseDir); final String[] includes = {include}; scanner.setIncludes(includes); @@ -197,7 +260,15 @@ public class App { } engine.scan(paths); - engine.analyzeDependencies(); + ExceptionCollection exCol = null; + try { + engine.analyzeDependencies(); + } catch (ExceptionCollection ex) { + if (ex.isFatal()) { + throw ex; + } + exCol = ex; + } final List dependencies = engine.getDependencies(); DatabaseProperties prop = null; CveDB cve = null; @@ -205,8 +276,6 @@ public class App { cve = new CveDB(); cve.open(); prop = cve.getDatabaseProperties(); - } catch (DatabaseException ex) { - LOGGER.debug("Unable to retrieve DB Properties", ex); } finally { if (cve != null) { cve.close(); @@ -215,16 +284,14 @@ public class App { final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop); try { report.generateReports(reportDirectory, outputFormat); - } catch (IOException ex) { - LOGGER.error("There was an IO error while attempting to generate the report."); - LOGGER.debug("", ex); - } catch (Throwable ex) { - LOGGER.error("There was an error while attempting to generate the report."); - LOGGER.debug("", ex); + } catch (ReportException ex) { + if (exCol != null) { + exCol.addException(ex); + throw exCol; + } else { + throw ex; + } } - } catch (DatabaseException ex) { - LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped"); - LOGGER.debug("", ex); } finally { if (engine != null) { engine.cleanup(); @@ -235,14 +302,11 @@ public class App { /** * Only executes the update phase of dependency-check. */ - private void runUpdateOnly() { + private void runUpdateOnly() throws UpdateException, DatabaseException { Engine engine = null; try { engine = new Engine(); engine.doUpdates(); - } catch (DatabaseException ex) { - LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped"); - LOGGER.debug("", ex); } finally { if (engine != null) { engine.cleanup(); @@ -253,11 +317,13 @@ public class App { /** * Updates the global Settings. * - * @param cli a reference to the CLI Parser that contains the command line arguments used to set the corresponding settings in - * the core engine. + * @param cli a reference to the CLI Parser that contains the command line + * arguments used to set the corresponding settings in the core engine. + * + * @throws InvalidSettingException thrown when a user defined properties + * file is unable to be loaded. */ - private void populateSettings(CliParser cli) { - + private void populateSettings(CliParser cli) throws InvalidSettingException { final boolean autoUpdate = cli.isAutoUpdate(); final String connectionTimeout = cli.getConnectionTimeout(); final String proxyServer = cli.getProxyServer(); @@ -286,11 +352,9 @@ public class App { try { Settings.mergeProperties(propertiesFile); } catch (FileNotFoundException ex) { - LOGGER.error("Unable to load properties file '{}'", propertiesFile.getPath()); - LOGGER.debug("", ex); + throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex); } catch (IOException ex) { - LOGGER.error("Unable to find properties file '{}'", propertiesFile.getPath()); - LOGGER.debug("", ex); + throw new InvalidSettingException("Error reading properties file '" + propertiesFile.getPath() + "'", ex); } } // We have to wait until we've merged the properties before attempting to set whether we use @@ -385,15 +449,16 @@ public class App { } /** - * Takes a path and resolves it to be a canonical & absolute path. The caveats are that this method will take an Ant style - * file selector path (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at least to the left of the first * - * or ?). + * Takes a path and resolves it to be a canonical & absolute path. The + * caveats are that this method will take an Ant style file selector path + * (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at + * least to the left of the first * or ?). * * @param path the path to canonicalize * @return the canonical path */ protected String ensureCanonicalPath(String path) { - String basePath = null; + String basePath; String wildCards = null; final String file = path.replace('\\', '/'); if (file.contains("*") || file.contains("?")) { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java index 824f5103a..82e60467b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java @@ -359,12 +359,12 @@ public class Engine implements FileFilter { LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage()); LOGGER.debug("", ex); exceptions.add(ex); - throw new ExceptionCollection(exceptions, true); + throw new ExceptionCollection("Unable to continue dependency-check analysis.",exceptions, true); } catch (DatabaseException ex) { LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage()); LOGGER.debug("", ex); exceptions.add(ex); - throw new ExceptionCollection(exceptions, true); + throw new ExceptionCollection("Unable to connect to the dependency-check database", exceptions, true); } LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------"); @@ -424,7 +424,7 @@ public class Engine implements FileFilter { LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------"); LOGGER.info("Analysis Complete ({} ms)", System.currentTimeMillis() - analysisStart); if (exceptions.size() > 0) { - throw new ExceptionCollection(exceptions); + throw new ExceptionCollection("One or more exceptions occured during dependency-check analysis", exceptions); } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java index 67e426a0d..1d7c65afc 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java @@ -17,6 +17,8 @@ */ package org.owasp.dependencycheck.exception; +import java.io.PrintStream; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -36,6 +38,18 @@ public class ExceptionCollection extends Exception { super(); this.exceptions = exceptions; } + + /** + * Instantiates a new exception collection. + * + * @param msg the exception message + * @param exceptions a list of exceptions + */ + public ExceptionCollection(String msg, List exceptions) { + super(msg); + this.exceptions = exceptions; + } + /** * Instantiates a new exception collection. * @@ -48,7 +62,22 @@ public class ExceptionCollection extends Exception { this.exceptions = exceptions; this.fatal = fatal; } - /** + + /** + * Instantiates a new exception collection. + * + * @param msg the exception message + * @param exceptions a list of exceptions + * @param fatal indicates if the exception that occurred is fatal - meaning + * that no analysis was performed. + */ + public ExceptionCollection(String msg, List exceptions, boolean fatal) { + super(msg); + this.exceptions = exceptions; + this.fatal = fatal; + } + + /** * Instantiates a new exception collection. * * @param exceptions a list of exceptions @@ -60,6 +89,18 @@ public class ExceptionCollection extends Exception { this.exceptions.add(exceptions); this.fatal = fatal; } + /** + * Instantiates a new exception collection. + * + * @param msg the exception message + * @param exception a list of exceptions + */ + public ExceptionCollection(String msg, Throwable exception) { + super(msg); + this.exceptions.add(exception); + this.fatal = false; + } + /** * Instantiates a new exception collection. */ @@ -94,7 +135,8 @@ public class ExceptionCollection extends Exception { public void addException(Throwable ex) { this.exceptions.add(ex); } - /** + + /** * Adds an exception to the collection. * * @param ex the exception to add @@ -105,8 +147,8 @@ public class ExceptionCollection extends Exception { } /** - * Flag indicating if a fatal exception occurred that would prevent the attempt - * at completing the analysis even if exceptions occurred. + * Flag indicating if a fatal exception occurred that would prevent the + * attempt at completing the analysis even if exceptions occurred. */ private boolean fatal = false; @@ -128,4 +170,42 @@ public class ExceptionCollection extends Exception { this.fatal = fatal; } + /** + * Prints the stack trace. + * + * @param s the writer to print to + */ + @Override + public void printStackTrace(PrintWriter s) { + s.println("Multiple Exceptions Occured"); + super.printStackTrace(s); + for (Throwable t : this.exceptions) { + s.println("Next Exception:"); + t.printStackTrace(s); + } + } + + /** + * Prints the stack trace. + * + * @param s the stream to write the stack trace to + */ + @Override + public void printStackTrace(PrintStream s) { + s.println("Multiple Exceptions Occured"); + super.printStackTrace(s); + for (Throwable t : this.exceptions) { + s.println("Next Exception:"); + t.printStackTrace(s); + } + } + + /** + * Prints the stack trace to standard error. + */ + @Override + public void printStackTrace() { + this.printStackTrace(System.err); + } + } diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java index f801f000d..4b1d35b42 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; +import org.apache.maven.MavenExecutionException; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -36,11 +37,13 @@ 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.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.utils.Settings; /** - * Maven Plugin that checks project dependencies and the dependencies of all child modules to see if they have any known published - * vulnerabilities. + * Maven Plugin that checks project dependencies and the dependencies of all + * child modules to see if they have any known published vulnerabilities. * * @author Jeremy Long */ @@ -55,18 +58,27 @@ import org.owasp.dependencycheck.utils.Settings; public class AggregateMojo extends BaseDependencyCheckMojo { /** - * Executes the aggregate dependency-check goal. This runs dependency-check and generates the subsequent reports. + * The key to store aggregate exception in the root Maven execution context. + */ + private static final String AGGREGATE_EXCEPTIONS = "AggregateExceptions"; + + /** + * Executes the aggregate dependency-check goal. This runs dependency-check + * and generates the subsequent reports. * - * @throws MojoExecutionException thrown if there is ane exception running the mojo - * @throws MojoFailureException thrown if dependency-check is configured to fail the build + * @throws MojoExecutionException thrown if there is ane exception running + * the mojo + * @throws MojoFailureException thrown if dependency-check is configured to + * fail the build */ @Override public void runCheck() throws MojoExecutionException, MojoFailureException { - final Engine engine = generateDataFile(); + final MavenEngine engine = generateDataFile(); + if (engine == null) { + return; + } - //if (getProject() == getReactorProjects().get(getReactorProjects().size() - 1)) { if (getProject() == getLastProject()) { - //ensure that the .ser file was created for each. for (MavenProject current : getReactorProjects()) { final File dataFile = getDataFile(current); @@ -76,7 +88,6 @@ public class AggregateMojo extends BaseDependencyCheckMojo { generateDataFile(engine, current); } } - for (MavenProject current : getReactorProjects()) { List dependencies = readDataFile(current); if (dependencies == null) { @@ -90,10 +101,8 @@ public class AggregateMojo extends BaseDependencyCheckMojo { getLog().debug(String.format("Adding %d dependencies from %s", childDeps.size(), reportOn.getName())); } dependencies.addAll(childDeps); - } else { - if (getLog().isDebugEnabled()) { - getLog().debug(String.format("No dependencies read for %s", reportOn.getName())); - } + } else if (getLog().isDebugEnabled()) { + getLog().debug(String.format("No dependencies read for %s", reportOn.getName())); } } engine.getDependencies().clear(); @@ -118,7 +127,21 @@ public class AggregateMojo extends BaseDependencyCheckMojo { //we shouldn't write this because nothing is configured to generate this report. outputDir = new File(current.getBuild().getDirectory()); } - writeReports(engine, current, outputDir); + try { + writeReports(engine, current, outputDir); + } catch (ReportException ex) { + ExceptionCollection exCol = (ExceptionCollection) engine.getExecutionRoot().getContextValue(AGGREGATE_EXCEPTIONS); + if (exCol == null) { + exCol = new ExceptionCollection("Error writing aggregate report",ex); + } else { + exCol.addException(ex); + } + if (this.isFailOnError()) { + throw new MojoExecutionException("One or more exceptions occured during dependency-check analysis", exCol); + } else { + getLog().debug("One or more exceptions occured during dependency-check analysis", exCol); + } + } } } engine.cleanup(); @@ -126,7 +149,8 @@ public class AggregateMojo extends BaseDependencyCheckMojo { } /** - * Gets the last project in the reactor - taking into account skipped projects. + * Gets the last project in the reactor - taking into account skipped + * projects. * * @return the last project in the reactor */ @@ -152,7 +176,8 @@ public class AggregateMojo extends BaseDependencyCheckMojo { } /** - * Returns a set containing all the descendant projects of the given project. + * Returns a set containing all the descendant projects of the given + * project. * * @param project the project for which all descendants will be returned * @return the set of descendant projects @@ -232,49 +257,85 @@ public class AggregateMojo extends BaseDependencyCheckMojo { * Test if the project has pom packaging * * @param mavenProject Project to test - * @return true if it has a pom packaging; otherwise false + * @return true if it has a pom packaging; otherwise + * false */ protected boolean isMultiModule(MavenProject mavenProject) { return "pom".equals(mavenProject.getPackaging()); } /** - * Initializes the engine, runs a scan, and writes the serialized dependencies to disk. + * Initializes the engine, runs a scan, and writes the serialized + * dependencies to disk. * - * @return the Engine used to execute dependency-check - * @throws MojoExecutionException thrown if there is an exception running the mojo - * @throws MojoFailureException thrown if dependency-check is configured to fail the build if severe CVEs are identified. + * @return the MavenEngine used to execute dependency-check + * @throws MojoExecutionException thrown if there is an exception running + * the mojo + * @throws MojoFailureException thrown if dependency-check is configured to + * fail the build if severe CVEs are identified. */ - protected Engine generateDataFile() throws MojoExecutionException, MojoFailureException { - final Engine engine; + protected MavenEngine generateDataFile() throws MojoExecutionException, MojoFailureException { + MavenEngine engine = null; try { engine = initializeEngine(); } catch (DatabaseException ex) { if (getLog().isDebugEnabled()) { getLog().debug("Database connection error", ex); } - throw new MojoExecutionException("An exception occured connecting to the local database. Please see the log file for more details.", ex); + final String msg = "An exception occured connecting to the local database. Please see the log file for more details."; + if (this.isFailOnError()) { + throw new MojoExecutionException(msg, ex); + } + getLog().error(msg, ex); + return null; } return generateDataFile(engine, getProject()); } /** - * Runs dependency-check's Engine and writes the serialized dependencies to disk. + * Runs dependency-check's MavenEngine and writes the serialized + * dependencies to disk. * - * @param engine the Engine to use when scanning. + * @param engine the MavenEngine to use when scanning. * @param project the project to scan and generate the data file for - * @return the Engine used to execute dependency-check - * @throws MojoExecutionException thrown if there is an exception running the mojo - * @throws MojoFailureException thrown if dependency-check is configured to fail the build if severe CVEs are identified. + * @return the MavenEngine used to execute dependency-check + * @throws MojoExecutionException thrown if there is an exception running + * the mojo + * @throws MojoFailureException thrown if dependency-check is configured to + * fail the build if severe CVEs are identified. */ - protected Engine generateDataFile(Engine engine, MavenProject project) throws MojoExecutionException, MojoFailureException { + protected MavenEngine generateDataFile(MavenEngine engine, MavenProject project) throws MojoExecutionException, MojoFailureException { if (getLog().isDebugEnabled()) { getLog().debug(String.format("Begin Scanning: %s", project.getName())); } engine.getDependencies().clear(); engine.resetFileTypeAnalyzers(); scanArtifacts(project, engine); - engine.analyzeDependencies(); + try { + engine.analyzeDependencies(); + } catch (ExceptionCollection ex) { + ExceptionCollection col = (ExceptionCollection) engine.getExecutionRoot().getContextValue(AGGREGATE_EXCEPTIONS); + if (col == null) { + col = ex; + } else if (ex.isFatal()) { + col.setFatal(true); + col.getExceptions().addAll(ex.getExceptions()); + } + if (col.isFatal()) { + final String msg = String.format("Fatal exception(s) analyzing %s", project.getName()); + if (this.isFailOnError()) { + throw new MojoExecutionException(msg, ex); + } + getLog().error(msg, col); + return null; + } else { + final String msg = String.format("Exception(s) analyzing %s", project.getName()); + if (getLog().isDebugEnabled()) { + getLog().debug(msg, ex); + } + engine.getExecutionRoot().setContextValue(AGGREGATE_EXCEPTIONS, col); + } + } final File target = new File(project.getBuild().getDirectory()); writeDataFile(project, target, engine.getDependencies()); showSummary(project, engine.getDependencies()); @@ -306,7 +367,8 @@ public class AggregateMojo extends BaseDependencyCheckMojo { } /** - * Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page. + * Gets the description of the Dependency-Check report to be displayed in + * the Maven Generated Reports page. * * @param locale The Locale to get the description for * @return the description diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java index aff052420..8869a35ed 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java @@ -47,6 +47,7 @@ import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; +import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.utils.ExpectedOjectInputStream; import org.owasp.dependencycheck.utils.Settings; @@ -69,14 +70,27 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * System specific new line character. */ private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern(); + // + // /** * Sets whether or not the external report format should be used. */ @Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true) private String dataFileName; + /** + * Sets whether or not the external report format should be used. + */ + @Parameter(property = "failOnError", defaultValue = "true", required = true) + private boolean failOnError; - // - // + /** + * Returns if the mojo should fail the build if an exception occurs. + * @return whether or not the mojo should fail the build + */ + protected boolean isFailOnError() { + return failOnError; + } + /** * The Maven Project Object. */ @@ -111,13 +125,11 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not * recommended that this be turned to false. Default is true. */ - @SuppressWarnings("CanBeFinal") @Parameter(property = "autoUpdate") private Boolean autoUpdate; /** * Sets whether Experimental analyzers are enabled. Default is false. */ - @SuppressWarnings("CanBeFinal") @Parameter(property = "enableExperimental") private Boolean enableExperimental; /** @@ -145,7 +157,6 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * The maven settings proxy id. */ - @SuppressWarnings("CanBeFinal") @Parameter(property = "mavenSettingsProxyId", required = false) private String mavenSettingsProxyId; @@ -162,6 +173,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * Flag indicating whether or not to show a summary in the output. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "showSummary", defaultValue = "true", required = false) private boolean showSummary = true; @@ -541,7 +553,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * @param project the project to scan the dependencies of * @param engine the engine to use to scan the dependencies */ - protected void scanArtifacts(MavenProject project, Engine engine) { + protected void scanArtifacts(MavenProject project, MavenEngine engine) { for (Artifact a : project.getArtifacts()) { if (excludeFromScan(a)) { continue; @@ -649,14 +661,14 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma // /** - * Initializes a new Engine that can be used for scanning. + * Initializes a new MavenEngine that can be used for scanning. * - * @return a newly instantiated Engine + * @return a newly instantiated MavenEngine * @throws DatabaseException thrown if there is a database exception */ - protected Engine initializeEngine() throws DatabaseException { + protected MavenEngine initializeEngine() throws DatabaseException { populateSettings(); - return new Engine(this.project, + return new MavenEngine(this.project, this.reactorProjects); } @@ -875,10 +887,11 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * Generates the reports for a given dependency-check engine. * * @param engine a dependency-check engine - * @param p the maven project - * @param outputDir the directory path to write the report(s). + * @param p the Maven project + * @param outputDir the directory path to write the report(s) + * @throws ReportException thrown if there is an error writing the report */ - protected void writeReports(Engine engine, MavenProject p, File outputDir) { + protected void writeReports(MavenEngine engine, MavenProject p, File outputDir) throws ReportException { DatabaseProperties prop = null; CveDB cve = null; try { @@ -897,19 +910,11 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma final ReportGenerator r = new ReportGenerator(p.getName(), engine.getDependencies(), engine.getAnalyzers(), prop); try { r.generateReports(outputDir.getAbsolutePath(), format); - } catch (IOException ex) { - getLog().error( - "Unexpected exception occurred during analysis; please see the verbose error log for more details."); - if (getLog().isDebugEnabled()) { - getLog().debug("", ex); - } - } catch (Throwable ex) { - getLog().error( - "Unexpected exception occurred during analysis; please see the verbose error log for more details."); - if (getLog().isDebugEnabled()) { - getLog().debug("", ex); - } + } catch (ReportException ex) { + final String msg = String.format("Error generating the report for %s", p.getName()); + throw new ReportException(msg, ex); } + } // @@ -1074,7 +1079,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * 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 + * @return a MavenEngine object populated with dependencies if the * serialized data file exists; otherwise null is returned */ protected List readDataFile(MavenProject project) { diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java index 17ba52bf1..270ceba39 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java @@ -26,10 +26,13 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.utils.Settings; /** - * Maven Plugin that checks the project dependencies to see if they have any known published vulnerabilities. + * Maven Plugin that checks the project dependencies to see if they have any + * known published vulnerabilities. * * @author Jeremy Long */ @@ -45,7 +48,8 @@ public class CheckMojo extends BaseDependencyCheckMojo { /** * Returns whether or not a the report can be generated. * - * @return true if the report can be generated; otherwise false + * @return true if the report can be generated; otherwise + * false */ @Override public boolean canGenerateReport() { @@ -60,33 +64,65 @@ public class CheckMojo extends BaseDependencyCheckMojo { } /** - * Executes the dependency-check engine on the project's dependencies and generates the report. + * Executes the dependency-check engine on the project's dependencies and + * generates the report. * - * @throws MojoExecutionException thrown if there is an exception executing the goal - * @throws MojoFailureException thrown if dependency-check is configured to fail the build + * @throws MojoExecutionException thrown if there is an exception executing + * the goal + * @throws MojoFailureException thrown if dependency-check is configured to + * fail the build */ @Override public void runCheck() throws MojoExecutionException, MojoFailureException { - final Engine engine; + MavenEngine engine = null; try { engine = initializeEngine(); } catch (DatabaseException ex) { if (getLog().isDebugEnabled()) { getLog().debug("Database connection error", ex); } - throw new MojoExecutionException("An exception occured connecting to the local database. Please see the log file for more details.", ex); + final String msg = "An exception occured connecting to the local database. Please see the log file for more details."; + if (this.isFailOnError()) { + throw new MojoExecutionException(msg, ex); + } + getLog().error(msg); } - scanArtifacts(getProject(), engine); - if (engine.getDependencies().isEmpty()) { - getLog().info("No dependencies were identified that could be analyzed by dependency-check"); - } else { - engine.analyzeDependencies(); - writeReports(engine, getProject(), getCorrectOutputDirectory()); - writeDataFile(getProject(), null, engine.getDependencies()); - showSummary(getProject(), engine.getDependencies()); - checkForFailure(engine.getDependencies()); + if (engine != null) { + scanArtifacts(getProject(), engine); + if (engine.getDependencies().isEmpty()) { + getLog().info("No dependencies were identified that could be analyzed by dependency-check"); + } else { + ExceptionCollection exCol = null; + try { + engine.analyzeDependencies(); + } catch (ExceptionCollection ex) { + if (this.isFailOnError() && ex.isFatal()) { + throw new MojoExecutionException("One or more exceptions occured during analysis", ex); + } + exCol = ex; + } + if (exCol == null || !exCol.isFatal()) { + try { + writeReports(engine, getProject(), getCorrectOutputDirectory()); + } catch (ReportException ex) { + if (this.isFailOnError()) { + if (exCol!= null) { + exCol.addException(ex); + } else { + exCol = new ExceptionCollection("Unable to write the dependency-check report", ex); + } + } + } + writeDataFile(getProject(), null, engine.getDependencies()); + showSummary(getProject(), engine.getDependencies()); + checkForFailure(engine.getDependencies()); + if (exCol != null && this.isFailOnError()) { + throw new MojoExecutionException("One or more exceptions occured during dependency-check analysis", exCol); + } + } + } + engine.cleanup(); } - engine.cleanup(); Settings.cleanup(); } @@ -109,7 +145,8 @@ public class CheckMojo extends BaseDependencyCheckMojo { } /** - * Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page. + * Gets the description of the Dependency-Check report to be displayed in + * the Maven Generated Reports page. * * @param locale The Locale to get the description for * @return the description diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/Engine.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/MavenEngine.java similarity index 96% rename from dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/Engine.java rename to dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/MavenEngine.java index b691ccde5..9edf53ae4 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/Engine.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/MavenEngine.java @@ -36,12 +36,12 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ -public class Engine extends org.owasp.dependencycheck.Engine { +public class MavenEngine extends org.owasp.dependencycheck.Engine { /** * The logger. */ - private static final transient Logger LOGGER = LoggerFactory.getLogger(Engine.class); + private static final transient Logger LOGGER = LoggerFactory.getLogger(MavenEngine.class); /** * A key used to persist an object in the MavenProject. */ @@ -69,7 +69,7 @@ public class Engine extends org.owasp.dependencycheck.Engine { * @throws DatabaseException thrown if there is an issue connecting to the * database */ - public Engine(MavenProject project, List reactorProjects) throws DatabaseException { + public MavenEngine(MavenProject project, List reactorProjects) throws DatabaseException { this.currentProject = project; this.reactorProjects = reactorProjects; initializeEngine(); @@ -117,7 +117,7 @@ public class Engine extends org.owasp.dependencycheck.Engine { * @throws DatabaseException thrown if there is an issue connecting to the * database */ - private Engine() throws DatabaseException { + private MavenEngine() throws DatabaseException { } /** @@ -208,7 +208,7 @@ public class Engine extends org.owasp.dependencycheck.Engine { * * @return the root Maven Project */ - private MavenProject getExecutionRoot() { + MavenProject getExecutionRoot() { if (reactorProjects == null) { return null; } diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java index 2075a2c0c..62c14a0dd 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java @@ -54,14 +54,20 @@ public class PurgeMojo extends BaseDependencyCheckMojo { /** * Purges the local copy of the NVD. * - * @throws MojoExecutionException thrown if there is an exception executing the goal - * @throws MojoFailureException thrown if dependency-check is configured to fail the build + * @throws MojoExecutionException thrown if there is an exception executing + * the goal + * @throws MojoFailureException thrown if dependency-check is configured to + * fail the build */ @Override public void runCheck() throws MojoExecutionException, MojoFailureException { if (getConnectionString() != null && !getConnectionString().isEmpty()) { - getLog().error("Unable to purge the local NVD when using a non-default connection string"); + final String msg = "Unable to purge the local NVD when using a non-default connection string"; + if (this.isFailOnError()) { + throw new MojoFailureException(msg); + } + getLog().error(msg); } else { populateSettings(); File db; @@ -71,13 +77,25 @@ public class PurgeMojo extends BaseDependencyCheckMojo { if (db.delete()) { getLog().info("Database file purged; local copy of the NVD has been removed"); } else { - getLog().error(String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath())); + final String msg = String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath()); + if (this.isFailOnError()) { + throw new MojoFailureException(msg); + } + getLog().error(msg); } } else { - getLog().error(String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath())); + final String msg = String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath()); + if (this.isFailOnError()) { + throw new MojoFailureException(msg); + } + getLog().error(msg); } } catch (IOException ex) { - getLog().error("Unable to delete the database"); + final String msg = "Unable to delete the database"; + if (this.isFailOnError()) { + throw new MojoExecutionException(msg, ex); + } + getLog().error(msg); } Settings.cleanup(); } @@ -95,7 +113,8 @@ public class PurgeMojo extends BaseDependencyCheckMojo { } /** - * Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page. + * Gets the description of the Dependency-Check report to be displayed in + * the Maven Generated Reports page. * * @param locale The Locale to get the description for * @return the description diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java index 8bb457b9b..33f8f172d 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java @@ -24,10 +24,12 @@ import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.ResolutionScope; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.utils.Settings; /** - * Maven Plugin that checks the project dependencies to see if they have any known published vulnerabilities. + * Maven Plugin that checks the project dependencies to see if they have any + * known published vulnerabilities. * * @author Jeremy Long */ @@ -51,14 +53,17 @@ public class UpdateMojo extends BaseDependencyCheckMojo { } /** - * Executes the dependency-check engine on the project's dependencies and generates the report. + * Executes the dependency-check engine on the project's dependencies and + * generates the report. * - * @throws MojoExecutionException thrown if there is an exception executing the goal - * @throws MojoFailureException thrown if dependency-check is configured to fail the build + * @throws MojoExecutionException thrown if there is an exception executing + * the goal + * @throws MojoFailureException thrown if dependency-check is configured to + * fail the build */ @Override public void runCheck() throws MojoExecutionException, MojoFailureException { - final Engine engine; + MavenEngine engine = null; try { engine = initializeEngine(); engine.update(); @@ -66,9 +71,21 @@ public class UpdateMojo extends BaseDependencyCheckMojo { if (getLog().isDebugEnabled()) { getLog().debug("Database connection error", ex); } - throw new MojoExecutionException("An exception occured connecting to the local database. Please see the log file for more details.", ex); + final String msg = "An exception occured connecting to the local database. Please see the log file for more details."; + if (this.isFailOnError()) { + throw new MojoExecutionException(msg, ex); + } + getLog().error(msg); + } catch (UpdateException ex) { + final String msg = "An exception occured while downloading updates. Please see the log file for more details."; + if (this.isFailOnError()) { + throw new MojoExecutionException(msg, ex); + } + getLog().error(msg); + } + if (engine != null) { + engine.cleanup(); } - engine.cleanup(); Settings.cleanup(); } @@ -84,7 +101,8 @@ public class UpdateMojo extends BaseDependencyCheckMojo { } /** - * Gets the description of the Dependency-Check report to be displayed in the Maven Generated Reports page. + * Gets the description of the Dependency-Check report to be displayed in + * the Maven Generated Reports page. * * @param locale The Locale to get the description for * @return the description @@ -93,5 +111,5 @@ public class UpdateMojo extends BaseDependencyCheckMojo { public String getDescription(Locale locale) { return "Updates the local cache of the NVD data from NIST."; } - + } diff --git a/dependency-check-maven/src/site/markdown/configuration.md b/dependency-check-maven/src/site/markdown/configuration.md index 44b68a512..2707e091c 100644 --- a/dependency-check-maven/src/site/markdown/configuration.md +++ b/dependency-check-maven/src/site/markdown/configuration.md @@ -17,6 +17,7 @@ Property | Description | Default Value autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true cveValidForHours | Sets the number of hours to wait before checking for new updates from the NVD. | 4 failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which means since the CVSS scores are 0-10, by default the build will never fail. | 11 +failOnError | Whether the build should fail if there is an error executing the dependency-check analysis | true format | 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. | HTML name | The name of the report in the site | dependency-check or dependency-check:aggregate outputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target' diff --git a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java index f66e34939..d103d0d9e 100644 --- a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java +++ b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java @@ -90,7 +90,7 @@ public class BaseDependencyCheckMojoTest extends BaseTest { boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Engine engine = new Engine(null, null); + MavenEngine engine = new MavenEngine(null, null); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); assertTrue(engine.getDependencies().isEmpty());