diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 000000000..f2a77a5bc --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,18 @@ +### Reporting Bugs/Errors +When reporting errors, 99% of the time log file output is required. Please post the log file as a [gist](https://gist.github.com/) and provide a link in the new issue. + +### Reporting False Positives +When reporting a false positive please include: +- The location of the dependency (Maven GAV, URL to download the dependency, etc.) +- The CPE that is believed to be false positive + - Please report the CPE not the CVE + +#### Example +False positive on library foo.jar - reported as cpe:/a:apache:tomcat:7.0 +```xml + + org.sample + foo + 1.0 + +``` \ No newline at end of file diff --git a/dependency-check-ant/pom.xml b/dependency-check-ant/pom.xml index 99ec93329..9c6685dd1 100644 --- a/dependency-check-ant/pom.xml +++ b/dependency-check-ant/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.4.1-SNAPSHOT + 1.4.3-SNAPSHOT dependency-check-ant 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..6c38786d4 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 @@ -18,7 +18,6 @@ package org.owasp.dependencycheck.taskdefs; import java.io.File; -import java.io.IOException; import java.util.List; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -32,9 +31,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 +808,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..3bc335fb0 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/pom.xml b/dependency-check-cli/pom.xml index 29689775e..584a4a72b 100644 --- a/dependency-check-cli/pom.xml +++ b/dependency-check-cli/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.4.1-SNAPSHOT + 1.4.3-SNAPSHOT dependency-check-cli 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..5d3a8fa18 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,10 @@ import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.core.FileAppender; +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,21 +61,26 @@ 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); + LOGGER.debug("Exit code: " + exitCode); } finally { Settings.cleanup(true); } + System.exit(exitCode); } /** * Main CLI entry-point into the application. * * @param args the command line arguments + * @return the exit code to return */ - public void run(String[] args) { + public int run(String[] args) { + int exitCode = 0; final CliParser cli = new CliParser(); try { @@ -79,11 +88,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 +102,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 +119,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 +240,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 +261,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 +277,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,34 +285,37 @@ 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; + } + } + if (exCol != null && exCol.getExceptions().size()>0) { + throw exCol; } - } 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(); } } + } /** * Only executes the update phase of dependency-check. + * + * @throws UpdateException thrown if there is an error updating + * @throws DatabaseException thrown if a fatal error occurred and a + * connection to the database could not be established */ - 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 +326,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 +361,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 +458,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-cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java index d2e522834..19d826bf3 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -966,7 +966,7 @@ public final class CliParser { */ public void printVersionInfo() { final String version = String.format("%s version %s", - Settings.getString(Settings.KEYS.APPLICATION_VAME, "dependency-check"), + Settings.getString(Settings.KEYS.APPLICATION_NAME, "dependency-check"), Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown")); System.out.println(version); } diff --git a/dependency-check-core/pom.xml b/dependency-check-core/pom.xml index 387601cdd..dd7419c56 100644 --- a/dependency-check-core/pom.xml +++ b/dependency-check-core/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.4.1-SNAPSHOT + 1.4.3-SNAPSHOT dependency-check-core 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 675eee417..5560c4adc 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 @@ -30,6 +30,8 @@ import org.owasp.dependencycheck.data.update.UpdateService; import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.exception.NoDataException; +import org.owasp.dependencycheck.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; @@ -47,8 +49,10 @@ import java.util.Map; import java.util.Set; /** - * Scans files, directories, etc. for Dependencies. Analyzers are loaded and used to process the files found by the scan, if a - * file is encountered and an Analyzer is associated with the file type then the file is turned into a dependency. + * Scans files, directories, etc. for Dependencies. Analyzers are loaded and + * used to process the files found by the scan, if a file is encountered and an + * Analyzer is associated with the file type then the file is turned into a + * dependency. * * @author Jeremy Long */ @@ -69,7 +73,8 @@ public class Engine implements FileFilter { private final Set fileTypeAnalyzers = new HashSet(); /** - * The ClassLoader to use when dynamically loading Analyzer and Update services. + * The ClassLoader to use when dynamically loading Analyzer and Update + * services. */ private ClassLoader serviceClassLoader = Thread.currentThread().getContextClassLoader(); /** @@ -80,7 +85,8 @@ public class Engine implements FileFilter { /** * Creates a new Engine. * - * @throws DatabaseException thrown if there is an error connecting to the database + * @throws DatabaseException thrown if there is an error connecting to the + * database */ public Engine() throws DatabaseException { initializeEngine(); @@ -90,7 +96,8 @@ public class Engine implements FileFilter { * Creates a new Engine. * * @param serviceClassLoader a reference the class loader being used - * @throws DatabaseException thrown if there is an error connecting to the database + * @throws DatabaseException thrown if there is an error connecting to the + * database */ public Engine(ClassLoader serviceClassLoader) throws DatabaseException { this.serviceClassLoader = serviceClassLoader; @@ -98,9 +105,11 @@ public class Engine implements FileFilter { } /** - * Creates a new Engine using the specified classloader to dynamically load Analyzer and Update services. + * Creates a new Engine using the specified classloader to dynamically load + * Analyzer and Update services. * - * @throws DatabaseException thrown if there is an error connecting to the database + * @throws DatabaseException thrown if there is an error connecting to the + * database */ protected final void initializeEngine() throws DatabaseException { ConnectionFactory.initialize(); @@ -115,7 +124,8 @@ public class Engine implements FileFilter { } /** - * Loads the analyzers specified in the configuration file (or system properties). + * Loads the analyzers specified in the configuration file (or system + * properties). */ private void loadAnalyzers() { if (!analyzers.isEmpty()) { @@ -164,8 +174,9 @@ public class Engine implements FileFilter { } /** - * Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies - * identified are added to the dependency collection. + * Scans an array of files or directories. If a directory is specified, it + * will be scanned recursively. Any dependencies identified are added to the + * dependency collection. * * @param paths an array of paths to files or directories to be analyzed * @return the list of dependencies scanned @@ -183,8 +194,9 @@ public class Engine implements FileFilter { } /** - * Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified - * are added to the dependency collection. + * Scans a given file or directory. If a directory is specified, it will be + * scanned recursively. Any dependencies identified are added to the + * dependency collection. * * @param path the path to a file or directory to be analyzed * @return the list of dependencies scanned @@ -195,8 +207,9 @@ public class Engine implements FileFilter { } /** - * Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies - * identified are added to the dependency collection. + * Scans an array of files or directories. If a directory is specified, it + * will be scanned recursively. Any dependencies identified are added to the + * dependency collection. * * @param files an array of paths to files or directories to be analyzed. * @return the list of dependencies @@ -214,8 +227,9 @@ public class Engine implements FileFilter { } /** - * Scans a collection of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies - * identified are added to the dependency collection. + * Scans a collection of files or directories. If a directory is specified, + * it will be scanned recursively. Any dependencies identified are added to + * the dependency collection. * * @param files a set of paths to files or directories to be analyzed * @return the list of dependencies scanned @@ -233,8 +247,9 @@ public class Engine implements FileFilter { } /** - * Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified - * are added to the dependency collection. + * Scans a given file or directory. If a directory is specified, it will be + * scanned recursively. Any dependencies identified are added to the + * dependency collection. * * @param file the path to a file or directory to be analyzed * @return the list of dependencies scanned @@ -257,7 +272,8 @@ public class Engine implements FileFilter { } /** - * Recursively scans files and directories. Any dependencies identified are added to the dependency collection. + * Recursively scans files and directories. Any dependencies identified are + * added to the dependency collection. * * @param dir the directory to scan * @return the list of Dependency objects scanned @@ -282,7 +298,8 @@ public class Engine implements FileFilter { } /** - * Scans a specified file. If a dependency is identified it is added to the dependency collection. + * Scans a specified file. If a dependency is identified it is added to the + * dependency collection. * * @param file The file to scan * @return the scanned dependency @@ -301,20 +318,38 @@ public class Engine implements FileFilter { } /** - * Runs the analyzers against all of the dependencies. Since the mutable dependencies list is exposed via - * {@link #getDependencies()}, this method iterates over a copy of the dependencies list. Thus, the potential for - * {@link java.util.ConcurrentModificationException}s is avoided, and analyzers may safely add or remove entries from the - * dependencies list. + * Runs the analyzers against all of the dependencies. Since the mutable + * dependencies list is exposed via {@link #getDependencies()}, this method + * iterates over a copy of the dependencies list. Thus, the potential for + * {@link java.util.ConcurrentModificationException}s is avoided, and + * analyzers may safely add or remove entries from the dependencies list. + * + * Every effort is made to complete analysis on the dependencies. In some + * cases an exception will occur with part of the analysis being performed + * which may not affect the entire analysis. If an exception occurs it will + * be included in the thrown exception collection. + * + * @throws ExceptionCollection a collections of any exceptions that occurred + * during analysis */ - public void analyzeDependencies() { + public void analyzeDependencies() throws ExceptionCollection { + final List exceptions = new ArrayList(); boolean autoUpdate = true; try { autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); } catch (InvalidSettingException ex) { LOGGER.debug("Invalid setting for auto-update; using true."); + exceptions.add(ex); } if (autoUpdate) { - doUpdates(); + try { + doUpdates(); + } catch (UpdateException ex) { + exceptions.add(ex); + LOGGER.warn("Unable to update Cached Web DataSource, using local " + + "data instead. Results may not include recent vulnerabilities."); + LOGGER.debug("Update Error", ex); + } } //need to ensure that data exists @@ -323,12 +358,13 @@ public class Engine implements FileFilter { } catch (NoDataException ex) { LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage()); LOGGER.debug("", ex); - return; + exceptions.add(ex); + 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); - return; - + exceptions.add(ex); + throw new ExceptionCollection("Unable to connect to the dependency-check database", exceptions, true); } LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------"); @@ -340,7 +376,12 @@ public class Engine implements FileFilter { final List analyzerList = analyzers.get(phase); for (Analyzer a : analyzerList) { - a = initializeAnalyzer(a); + try { + a = initializeAnalyzer(a); + } catch (InitializationException ex) { + exceptions.add(ex); + continue; + } /* need to create a copy of the collection because some of the * analyzers may modify it. This prevents ConcurrentModificationExceptions. @@ -361,10 +402,12 @@ public class Engine implements FileFilter { } catch (AnalysisException ex) { LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath()); LOGGER.debug("", ex); + exceptions.add(ex); } catch (Throwable ex) { //final AnalysisException ax = new AnalysisException(axMsg, ex); LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath()); LOGGER.debug("", ex); + exceptions.add(ex); } } } @@ -380,6 +423,9 @@ 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("One or more exceptions occured during dependency-check analysis", exceptions); + } } /** @@ -387,12 +433,14 @@ public class Engine implements FileFilter { * * @param analyzer the analyzer to initialize * @return the initialized analyzer + * @throws InitializationException thrown when there is a problem + * initializing the analyzer */ - protected Analyzer initializeAnalyzer(Analyzer analyzer) { + protected Analyzer initializeAnalyzer(Analyzer analyzer) throws InitializationException { try { LOGGER.debug("Initializing {}", analyzer.getName()); analyzer.initialize(); - } catch (Throwable ex) { + } catch (InitializationException ex) { LOGGER.error("Exception occurred initializing {}.", analyzer.getName()); LOGGER.debug("", ex); try { @@ -400,6 +448,16 @@ public class Engine implements FileFilter { } catch (Throwable ex1) { LOGGER.trace("", ex1); } + throw ex; + } catch (Throwable ex) { + LOGGER.error("Unexpected exception occurred initializing {}.", analyzer.getName()); + LOGGER.debug("", ex); + try { + analyzer.close(); + } catch (Throwable ex1) { + LOGGER.trace("", ex1); + } + throw new InitializationException("Unexpected Exception", ex); } return analyzer; } @@ -419,28 +477,26 @@ public class Engine implements FileFilter { } /** - * Cycles through the cached web data sources and calls update on all of them. + * Cycles through the cached web data sources and calls update on all of + * them. + * + * @throws UpdateException thrown if the operation fails */ - public void doUpdates() { + public void doUpdates() throws UpdateException { LOGGER.info("Checking for updates"); final long updateStart = System.currentTimeMillis(); final UpdateService service = new UpdateService(serviceClassLoader); final Iterator iterator = service.getDataSources(); while (iterator.hasNext()) { final CachedWebDataSource source = iterator.next(); - try { - source.update(); - } catch (UpdateException ex) { - LOGGER.warn( - "Unable to update Cached Web DataSource, using local data instead. Results may not include recent vulnerabilities."); - LOGGER.debug("Unable to update details for {}", source.getClass().getName(), ex); - } + source.update(); } LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart); } /** - * Returns a full list of all of the analyzers. This is useful for reporting which analyzers where used. + * Returns a full list of all of the analyzers. This is useful for reporting + * which analyzers where used. * * @return a list of Analyzers */ @@ -457,7 +513,8 @@ public class Engine implements FileFilter { * Checks all analyzers to see if an extension is supported. * * @param file a file extension - * @return true or false depending on whether or not the file extension is supported + * @return true or false depending on whether or not the file extension is + * supported */ @Override public boolean accept(File file) { @@ -483,10 +540,12 @@ public class Engine implements FileFilter { } /** - * Checks the CPE Index to ensure documents exists. If none exist a NoDataException is thrown. + * Checks the CPE Index to ensure documents exists. If none exist a + * NoDataException is thrown. * * @throws NoDataException thrown if no data exists in the CPE Index - * @throws DatabaseException thrown if there is an exception opening the database + * @throws DatabaseException thrown if there is an exception opening the + * database */ private void ensureDataExists() throws NoDataException, DatabaseException { final CveDB cve = new CveDB(); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java index b2b63955e..56ab93810 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java @@ -27,6 +27,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; 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.ScanAgentException; import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.utils.Settings; @@ -34,10 +35,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * This class provides a way to easily conduct a scan solely based on existing evidence metadata rather than collecting evidence - * from the files themselves. This class is based on the Ant task and Maven plugin with the exception that it takes a list of - * dependencies that can be programmatically added from data in a spreadsheet, database or some other datasource and conduct a - * scan based on this pre-defined evidence. + * This class provides a way to easily conduct a scan solely based on existing + * evidence metadata rather than collecting evidence from the files themselves. + * This class is based on the Ant task and Maven plugin with the exception that + * it takes a list of dependencies that can be programmatically added from data + * in a spreadsheet, database or some other datasource and conduct a scan based + * on this pre-defined evidence. * *

Example:

*
@@ -138,7 +141,8 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * Specifies the destination directory for the generated Dependency-Check report.
+     * Specifies the destination directory for the generated Dependency-Check
+     * report.
      */
     private String reportOutputDirectory;
 
@@ -161,9 +165,11 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * 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 and the CVSS score is set to 11. The valid range
-     * for the fail build on CVSS is 0 to 11, where anything above 10 will not cause the build to fail.
+     * 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 and the CVSS score is set
+     * to 11. The valid range for the fail build on CVSS is 0 to 11, where
+     * anything above 10 will not cause the build to fail.
      */
     private float failBuildOnCVSS = 11;
 
@@ -186,8 +192,8 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * 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.
+     * 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.
      */
     private boolean autoUpdate = true;
 
@@ -233,8 +239,9 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * 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.
+     * 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.
      */
     private ReportGenerator.Format reportFormat = ReportGenerator.Format.HTML;
 
@@ -283,7 +290,9 @@ public class DependencyCheckScanAgent {
      * Get the value of proxyServer.
      *
      * @return the value of proxyServer
-     * @deprecated use {@link org.owasp.dependencycheck.agent.DependencyCheckScanAgent#getProxyServer()} instead
+     * @deprecated use
+     * {@link org.owasp.dependencycheck.agent.DependencyCheckScanAgent#getProxyServer()}
+     * instead
      */
     @Deprecated
     public String getProxyUrl() {
@@ -694,8 +703,8 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * Additional ZIP File extensions to add analyze. This should be a comma-separated list of file extensions to treat like ZIP
-     * files.
+     * Additional ZIP File extensions to add analyze. This should be a
+     * comma-separated list of file extensions to treat like ZIP files.
      */
     private String zipExtensions;
 
@@ -836,11 +845,17 @@ public class DependencyCheckScanAgent {
      * Executes the Dependency-Check on the dependent libraries.
      *
      * @return the Engine used to scan the dependencies.
-     * @throws org.owasp.dependencycheck.data.nvdcve.DatabaseException thrown if there is an exception connecting to the database
+     * @throws ExceptionCollection a collection of one or more exceptions that
+     * occurred during analysis.
      */
-    private Engine executeDependencyCheck() throws DatabaseException {
+    private Engine executeDependencyCheck() throws ExceptionCollection {
         populateSettings();
-        final Engine engine = new Engine();
+        final Engine engine;
+        try {
+            engine = new Engine();
+        } catch (DatabaseException ex) {
+            throw new ExceptionCollection(ex, true);
+        }
         engine.setDependencies(this.dependencies);
         engine.analyzeDependencies();
         return engine;
@@ -881,8 +896,9 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * 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.
      */
     private void populateSettings() {
         Settings.initialize();
@@ -925,7 +941,8 @@ public class DependencyCheckScanAgent {
      * Executes the dependency-check and generates the report.
      *
      * @return a reference to the engine used to perform the scan.
-     * @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if there is an exception executing the scan.
+     * @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if
+     * there is an exception executing the scan.
      */
     public Engine execute() throws ScanAgentException {
         Engine engine = null;
@@ -940,10 +957,12 @@ public class DependencyCheckScanAgent {
             if (this.failBuildOnCVSS <= 10) {
                 checkForFailure(engine.getDependencies());
             }
-        } catch (DatabaseException ex) {
-            LOGGER.error(
-                    "Unable to connect to the dependency-check database; analysis has stopped");
-            LOGGER.debug("", ex);
+        } catch (ExceptionCollection ex) {
+            if (ex.isFatal()) {
+                LOGGER.error("A fatal exception occurred during analysis; analysis has stopped. Please see the debug log for more details.");
+                LOGGER.debug("", ex);
+            }
+            throw new ScanAgentException("One or more exceptions occurred during analysis; please see the debug log for more details.", ex);
         } finally {
             Settings.cleanup(true);
             if (engine != null) {
@@ -954,11 +973,12 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the
-     * configuration.
+     * Checks to see if a vulnerability has been identified with a CVSS score
+     * that is above the threshold set in the configuration.
      *
      * @param dependencies the list of dependency objects
-     * @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if there is an exception executing the scan.
+     * @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if
+     * there is an exception executing the scan.
      */
     private void checkForFailure(List dependencies) throws ScanAgentException {
         final StringBuilder ids = new StringBuilder();
@@ -986,7 +1006,8 @@ public class DependencyCheckScanAgent {
     }
 
     /**
-     * Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries.
+     * Generates a warning message listing a summary of dependencies and their
+     * associated CPE and CVE entries.
      *
      * @param dependencies a list of dependency objects
      */
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java
index 5a14171c7..62e502254 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java
@@ -367,7 +367,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
             final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
             try {
                 if (ZIPPABLES.contains(archiveExt)) {
-                    BufferedInputStream in = new BufferedInputStream(fis);
+                    final BufferedInputStream in = new BufferedInputStream(fis);
                     ensureReadableJar(archiveExt, in);
                     extractArchive(new ZipArchiveInputStream(in), destination, engine);
                 } else if ("tar".equals(archiveExt)) {
@@ -413,7 +413,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
     private void ensureReadableJar(final String archiveExt, BufferedInputStream in) throws IOException {
         if ("jar".equals(archiveExt) && in.markSupported()) {
             in.mark(7);
-            byte[] b = new byte[7];
+            final byte[] b = new byte[7];
             in.read(b);
             if (b[0] == '#'
                     && b[1] == '!'
@@ -441,6 +441,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
                         }
                     }
                 }
+            } else {
+                in.reset();
             }
         }
     }
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java
index 815881155..3733809c9 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java
@@ -574,15 +574,14 @@ public class CPEAnalyzer implements Analyzer {
                         final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getName(), "UTF-8"));
                         final IdentifierMatch match = new IdentifierMatch("cpe", vs.getName(), url, IdentifierConfidence.EXACT_MATCH, conf);
                         collected.add(match);
-                    } else //TODO the following isn't quite right is it? need to think about this guessing game a bit more.
-                    {
-                        if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size()
-                                && evVer.matchesAtLeastThreeLevels(dbVer)) {
-                            if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
-                                if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) {
-                                    bestGuess = dbVer;
-                                    bestGuessConf = conf;
-                                }
+
+                        //TODO the following isn't quite right is it? need to think about this guessing game a bit more.
+                    } else if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size()
+                            && evVer.matchesAtLeastThreeLevels(dbVer)) {
+                        if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
+                            if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) {
+                                bestGuess = dbVer;
+                                bestGuessConf = conf;
                             }
                         }
                     }
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java
index fcaaeb102..5e6dee5b8 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java
@@ -93,26 +93,27 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
 
         //add version evidence
         final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName);
+        final String packageName = DependencyVersionUtil.parsePreVersion(fileName);
         if (version != null) {
             // If the version number is just a number like 2 or 23, reduce the confidence
             // a shade. This should hopefully correct for cases like log4j.jar or
             // struts2-core.jar
             if (version.getVersionParts() == null || version.getVersionParts().size() < 2) {
-                dependency.getVersionEvidence().addEvidence("file", "name",
+                dependency.getVersionEvidence().addEvidence("file", "version",
                         version.toString(), Confidence.MEDIUM);
             } else {
                 dependency.getVersionEvidence().addEvidence("file", "version",
                         version.toString(), Confidence.HIGHEST);
             }
             dependency.getVersionEvidence().addEvidence("file", "name",
-                    fileName, Confidence.MEDIUM);
+                    packageName, Confidence.MEDIUM);
         }
 
         if (!IGNORED_FILES.accept(f)) {
             dependency.getProductEvidence().addEvidence("file", "name",
-                    fileName, Confidence.HIGH);
+            		packageName, Confidence.HIGH);
             dependency.getVendorEvidence().addEvidence("file", "name",
-                    fileName, Confidence.HIGH);
+            		packageName, Confidence.HIGH);
         }
     }
 }
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java
index b4ebcbfe0..5c206037b 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java
@@ -32,7 +32,6 @@ import org.owasp.dependencycheck.dependency.Dependency;
 import org.owasp.dependencycheck.dependency.Evidence;
 import org.owasp.dependencycheck.exception.InitializationException;
 import org.owasp.dependencycheck.xml.suppression.PropertyType;
-import org.owasp.dependencycheck.xml.suppression.SuppressionParseException;
 import org.owasp.dependencycheck.utils.DownloadFailedException;
 import org.owasp.dependencycheck.utils.Downloader;
 import org.owasp.dependencycheck.utils.FileUtils;
@@ -255,7 +254,7 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
         if (product.contains(zendframeworkProduct)) {
             dependency.getProductEvidence().addEvidence("hint analyzer", "vendor", "zend_framework", Confidence.HIGHEST);
         }
-        
+
         //sun/oracle problem
         final Iterator itr = dependency.getVendorEvidence().iterator();
         final List newEntries = new ArrayList();
@@ -279,7 +278,7 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
     /**
      * Loads the hint rules file.
      *
-     * @throws SuppressionParseException thrown if the XML cannot be parsed.
+     * @throws HintParseException thrown if the XML cannot be parsed.
      */
     private void loadHintRules() throws HintParseException {
         final HintParser parser = new HintParser();
@@ -293,7 +292,7 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
             LOGGER.error("Unable to parse the base hint data file");
             LOGGER.debug("Unable to parse the base hint data file", ex);
         }
-        final String filePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE);
+        final String filePath = Settings.getString(Settings.KEYS.HINTS_FILE);
         if (filePath == null) {
             return;
         }
@@ -327,7 +326,7 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
 
             if (file != null) {
                 try {
-                    Hints newHints = parser.parseHints(file);
+                    final Hints newHints = parser.parseHints(file);
                     hints.getHintRules().addAll(newHints.getHintRules());
                     hints.getVendorDuplicatingHintRules().addAll(newHints.getVendorDuplicatingHintRules());
                     LOGGER.debug("{} hint rules were loaded.", hints.getHintRules().size());
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java
index 9d952c14c..9edbcf6ab 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java
@@ -39,7 +39,6 @@ import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
-import java.util.logging.Level;
 import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import org.apache.commons.compress.utils.IOUtils;
@@ -646,9 +645,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
      * @return whether evidence was identified parsing the manifest
      * @throws IOException if there is an issue reading the JAR file
      */
-    protected boolean parseManifest(Dependency dependency,
-            List classInformation)
-            throws IOException {
+    protected boolean parseManifest(Dependency dependency, List classInformation) throws IOException {
         boolean foundSomething = false;
         JarFile jar = null;
         try {
@@ -667,7 +664,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
             final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
             final EvidenceCollection productEvidence = dependency.getProductEvidence();
             final EvidenceCollection versionEvidence = dependency.getVersionEvidence();
-
             String source = "Manifest";
             String specificationVersion = null;
             boolean hasImplementationVersion = false;
@@ -689,7 +685,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
                     foundSomething = true;
                     versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
                 } else if ("specification-version".equalsIgnoreCase(key)) {
-                    specificationVersion = key;
+                    specificationVersion = value;
                 } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) {
                     foundSomething = true;
                     vendorEvidence.addEvidence(source, key, value, Confidence.HIGH);
@@ -784,7 +780,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
                     }
                 }
             }
-
             for (Map.Entry item : manifest.getEntries().entrySet()) {
                 final String name = item.getKey();
                 source = "manifest: " + name;
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java
index 5bade7829..56001c9d7 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java
@@ -53,6 +53,9 @@ import org.slf4j.LoggerFactory;
 @Experimental
 public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
 
+    /**
+     * The logger.
+     */
     private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class);
 
     /**
@@ -151,7 +154,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
             setEnabled(false);
             cvedb.close();
             cvedb = null;
-            String msg = String.format("Exception from bundle-audit process: %s. Disabling %s", ae.getCause(), ANALYZER_NAME);
+            final String msg = String.format("Exception from bundle-audit process: %s. Disabling %s", ae.getCause(), ANALYZER_NAME);
             throw new InitializationException(msg, ae);
         } catch (IOException ex) {
             setEnabled(false);
@@ -163,12 +166,12 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
             exitValue = process.waitFor();
         } catch (InterruptedException ex) {
             setEnabled(false);
-            String msg = String.format("Bundle-audit process was interupted. Disabling %s", ANALYZER_NAME);
+            final String msg = String.format("Bundle-audit process was interupted. Disabling %s", ANALYZER_NAME);
             throw new InitializationException(msg);
         }
         if (0 == exitValue) {
             setEnabled(false);
-            String msg = String.format("Unexpected exit code from bundle-audit process. Disabling %s: %s", ANALYZER_NAME, exitValue);
+            final String msg = String.format("Unexpected exit code from bundle-audit process. Disabling %s: %s", ANALYZER_NAME, exitValue);
             throw new InitializationException(msg);
         } else {
             BufferedReader reader = null;
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java
index 666a2ffbe..5caed2e4f 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java
@@ -38,7 +38,6 @@ import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.store.RAMDirectory;
-import org.owasp.dependencycheck.data.lucene.FieldAnalyzer;
 import org.owasp.dependencycheck.data.lucene.LuceneUtils;
 import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer;
 import org.owasp.dependencycheck.data.nvdcve.CveDB;
@@ -48,8 +47,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * An in memory lucene index that contains the vendor/product combinations from the CPE (application) identifiers within the NVD
- * CVE data.
+ * An in memory lucene index that contains the vendor/product combinations from
+ * the CPE (application) identifiers within the NVD CVE data.
  *
  * @author Jeremy Long
  */
@@ -144,19 +143,6 @@ public final class CpeMemoryIndex {
         return openState;
     }
 
-    /**
-     * Creates the indexing analyzer for the CPE Index.
-     *
-     * @return the CPE Analyzer.
-     * @deprecated the search field analyzer must be used to include the token concatenating filter.
-     */
-    @Deprecated
-    private Analyzer createIndexingAnalyzer() {
-        final Map fieldAnalyzers = new HashMap();
-        fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
-        return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
-    }
-
     /**
      * Creates an Analyzer for searching the CPE Index.
      *
@@ -275,7 +261,8 @@ public final class CpeMemoryIndex {
      * @param maxQueryResults the maximum number of documents to return
      * @return the TopDocs found by the search
      * @throws ParseException thrown when the searchString is invalid
-     * @throws IOException is thrown if there is an issue with the underlying Index
+     * @throws IOException is thrown if there is an issue with the underlying
+     * Index
      */
     public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException {
         if (searchString == null || searchString.trim().isEmpty()) {
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/FieldAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/FieldAnalyzer.java
index 534259f07..0736c9fb0 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/FieldAnalyzer.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/FieldAnalyzer.java
@@ -34,7 +34,7 @@ import org.apache.lucene.util.Version;
  * index the CPE fields vendor and product.

* * @author Jeremy Long - * @Deprecated the field analyzer should not be used, instead use the + * @deprecated the field analyzer should not be used, instead use the * SearchFieldAnalyzer so that the token analyzing filter is used. */ @Deprecated diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java index fc920956c..8b770ec66 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java @@ -68,17 +68,16 @@ public class CveDB { private ResourceBundle statementBundle = null; /** - * Creates a new CveDB object and opens the database - * connection. Note, the connection must be closed by the caller by calling - * the close method. ======= Does the underlying connection support batch - * operations? + * Creates a new CveDB object and opens the database connection. Note, the + * connection must be closed by the caller by calling the close method. + * ======= Does the underlying connection support batch operations? */ private boolean batchSupported; /** * Creates a new CveDB object and opens the database connection. Note, the * connection must be closed by the caller by calling the close method. - * + * * @throws DatabaseException thrown if there is an exception opening the * database. */ @@ -659,7 +658,7 @@ public class CveDB { + "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please " + "create a log file (see documentation at http://jeremylong.github.io/DependencyCheck/) and open a ticket at " + "https://github.com/jeremylong/DependencyCheck/issues and include the log file.\n\n", - dd, dd, Settings.getString(Settings.KEYS.APPLICATION_VAME)); + dd, dd, Settings.getString(Settings.KEYS.APPLICATION_NAME)); LOGGER.debug("", ex); } finally { DBUtils.closeResultSet(rs); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java index 24293b969..4c778e7b2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java @@ -43,9 +43,10 @@ public class CPEHandler extends DefaultHandler { /** * The Starts with expression to filter CVE entries by CPE. */ - private static final String CPE_STARTS_WITH = Settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER,"cpe:/a:"); + private static final String CPE_STARTS_WITH = Settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:/a:"); /** - * The text content of the node being processed. This can be used during the end element event. + * The text content of the node being processed. This can be used during the + * end element event. */ private StringBuilder nodeText = null; /** @@ -77,7 +78,8 @@ public class CPEHandler extends DefaultHandler { * @param localName the local name * @param qName the qualified name * @param attributes the attributes - * @throws SAXException thrown if there is an exception processing the element + * @throws SAXException thrown if there is an exception processing the + * element */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { @@ -128,7 +130,8 @@ public class CPEHandler extends DefaultHandler { * @param ch the char array * @param start the start position of the data read * @param length the length of the data read - * @throws SAXException thrown if there is an exception processing the characters + * @throws SAXException thrown if there is an exception processing the + * characters */ @Override public void characters(char[] ch, int start, int length) throws SAXException { @@ -138,12 +141,14 @@ public class CPEHandler extends DefaultHandler { } /** - * Handles the end element event. Stores the CPE data in the Cve Database if the cpe item node is ending. + * Handles the end element event. Stores the CPE data in the Cve Database if + * the cpe item node is ending. * * @param uri the element's uri * @param localName the local name * @param qName the qualified name - * @throws SAXException thrown if there is an exception processing the element + * @throws SAXException thrown if there is an exception processing the + * element */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { @@ -182,7 +187,8 @@ public class CPEHandler extends DefaultHandler { // /** - * A simple class to maintain information about the current element while parsing the CPE XML. + * A simple class to maintain information about the current element while + * parsing the CPE XML. */ protected static final class Element { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java index 0d5762708..6df4e5fa6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java @@ -36,6 +36,9 @@ import org.slf4j.LoggerFactory; */ public class UpdateableNvdCve implements Iterable, Iterator { + /** + * A reference to the logger. + */ private static final Logger LOGGER = LoggerFactory.getLogger(UpdateableNvdCve.class); /** * A collection of sources of data. 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 new file mode 100644 index 000000000..cf0a1015f --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java @@ -0,0 +1,214 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2016 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * A collection of several exceptions. + * + * @author Jeremy Lomg + */ +public class ExceptionCollection extends Exception { + + /** + * Instantiates a new exception collection. + * + * @param exceptions a list of exceptions + */ + public ExceptionCollection(List exceptions) { + 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. + * + * @param exceptions a list of exceptions + * @param fatal indicates if the exception that occurred is fatal - meaning + * that no analysis was performed. + */ + public ExceptionCollection(List exceptions, boolean fatal) { + super(); + 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 + * @param fatal indicates if the exception that occurred is fatal - meaning + * that no analysis was performed. + */ + public ExceptionCollection(Throwable exceptions, boolean fatal) { + super(); + this.exceptions = new ArrayList(); + 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 = new ArrayList(); + this.exceptions.add(exception); + this.fatal = false; + } + + /** + * Instantiates a new exception collection. + */ + public ExceptionCollection() { + super(); + this.exceptions = new ArrayList(); + } + /** + * The serial version uid. + */ + private static final long serialVersionUID = 1L; + + /** + * A collection of exceptions. + */ + private List exceptions; + + /** + * Get the value of exceptions. + * + * @return the value of exceptions + */ + public List getExceptions() { + return exceptions; + } + + /** + * Adds an exception to the collection. + * + * @param ex the exception to add + */ + public void addException(Throwable ex) { + this.exceptions.add(ex); + } + + /** + * Adds an exception to the collection. + * + * @param ex the exception to add + * @param fatal flag indicating if this is a fatal error + */ + public void addException(Throwable ex, boolean fatal) { + addException(ex); + this.fatal = fatal; + } + + /** + * Flag indicating if a fatal exception occurred that would prevent the + * attempt at completing the analysis even if exceptions occurred. + */ + private boolean fatal = false; + + /** + * Get the value of fatal. + * + * @return the value of fatal + */ + public boolean isFatal() { + return fatal; + } + + /** + * Set the value of fatal. + * + * @param fatal new value of fatal + */ + public void setFatal(boolean fatal) { + 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-core/src/main/java/org/owasp/dependencycheck/exception/ReportException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ReportException.java new file mode 100644 index 000000000..7199b3520 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ReportException.java @@ -0,0 +1,66 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2016 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.exception; + +/** + * An exception used when generating reports. + * + * @author Jeremy Long + */ +public class ReportException extends Exception { + + /** + * The serial version uid. + */ + private static final long serialVersionUID = 1L; + + /** + * Creates a new ReportException. + */ + public ReportException() { + super(); + } + + /** + * Creates a new ReportException. + * + * @param msg a message for the exception. + */ + public ReportException(String msg) { + super(msg); + } + + /** + * Creates a new ReportException. + * + * @param ex the cause of the exception. + */ + public ReportException(Throwable ex) { + super(ex); + } + + /** + * Creates a new ReportException. + * + * @param msg a message for the exception. + * @param ex the cause of the exception. + */ + public ReportException(String msg, Throwable ex) { + super(msg, ex); + } +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java index b47f9a8b9..99b7b387d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java @@ -26,6 +26,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -37,13 +38,16 @@ import org.apache.velocity.runtime.RuntimeConstants; import org.owasp.dependencycheck.analyzer.Analyzer; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The ReportGenerator is used to, as the name implies, generate reports. Internally the generator uses the Velocity - * Templating Engine. The ReportGenerator exposes a list of Dependencies to the template when generating the report. + * The ReportGenerator is used to, as the name implies, generate reports. + * Internally the generator uses the Velocity Templating Engine. The + * ReportGenerator exposes a list of Dependencies to the template when + * generating the report. * * @author Jeremy Long */ @@ -79,7 +83,7 @@ public class ReportGenerator { /** * The Velocity Engine. */ - private final VelocityEngine engine; + private final VelocityEngine velocityEngine; /** * The Velocity Engine Context. */ @@ -91,13 +95,14 @@ public class ReportGenerator { * @param applicationName the application name being analyzed * @param dependencies the list of dependencies * @param analyzers the list of analyzers used - * @param properties the database properties (containing timestamps of the NVD CVE data) + * @param properties the database properties (containing timestamps of the + * NVD CVE data) */ public ReportGenerator(String applicationName, List dependencies, List analyzers, DatabaseProperties properties) { - engine = createVelocityEngine(); + velocityEngine = createVelocityEngine(); context = createContext(); - engine.init(); + velocityEngine.init(); final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy 'at' HH:mm:ss z"); final DateFormat dateFormatXML = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); @@ -119,19 +124,19 @@ public class ReportGenerator { /** * Creates a new Velocity Engine. * - * @return a velocity engine. + * @return a velocity engine */ private VelocityEngine createVelocityEngine() { - final VelocityEngine engine = new VelocityEngine(); + final VelocityEngine velocity = new VelocityEngine(); // Logging redirection for Velocity - Required by Jenkins and other server applications - engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName()); - return engine; + velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName()); + return velocity; } /** * Creates a new Velocity Context. * - * @return a Velocity Context. + * @return a Velocity Context */ private Context createContext() { return new VelocityContext(); @@ -143,7 +148,7 @@ public class ReportGenerator { * @param outputStream the OutputStream to send the generated report to * @param format the format the report should be written in * @throws IOException is thrown when the template file does not exist - * @throws Exception is thrown if there is an error writing out the reports. + * @throws Exception is thrown if there is an error writing out the reports */ public void generateReports(OutputStream outputStream, Format format) throws IOException, Exception { if (format == Format.XML || format == Format.ALL) { @@ -162,10 +167,9 @@ public class ReportGenerator { * * @param outputDir the path where the reports should be written * @param format the format the report should be written in - * @throws IOException is thrown when the template file does not exist - * @throws Exception is thrown if there is an error writing out the reports. + * @throws ReportException is thrown if there is an error writing out the reports */ - public void generateReports(String outputDir, Format format) throws IOException, Exception { + public void generateReports(String outputDir, Format format) throws ReportException { if (format == Format.XML || format == Format.ALL) { generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml"); } @@ -181,11 +185,12 @@ public class ReportGenerator { * Generates the Dependency Reports for the identified dependencies. * * @param outputDir the path where the reports should be written - * @param outputFormat the format the report should be written in (XML, HTML, ALL) - * @throws IOException is thrown when the template file does not exist - * @throws Exception is thrown if there is an error writing out the reports. + * @param outputFormat the format the report should be written in (XML, + * HTML, ALL) + * @throws ReportException is thrown if there is an error creating out the + * reports */ - public void generateReports(String outputDir, String outputFormat) throws IOException, Exception { + public void generateReports(String outputDir, String outputFormat) throws ReportException { final String format = outputFormat.toUpperCase(); final String pathToCheck = outputDir.toLowerCase(); if (format.matches("^(XML|HTML|VULN|ALL)$")) { @@ -217,16 +222,16 @@ public class ReportGenerator { } /** - * Generates a report from a given Velocity Template. The template name provided can be the name of a template - * contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a + * Generates a report from a given Velocity Template. The template name + * provided can be the name of a template contained in the jar file, such as + * 'XmlReport' or 'HtmlReport', or the template name can be the path to a * template file. * - * @param templateName the name of the template to load. - * @param outputStream the OutputStream to write the report to. - * @throws IOException is thrown when the template file does not exist. - * @throws Exception is thrown when an exception occurs. + * @param templateName the name of the template to load + * @param outputStream the OutputStream to write the report to + * @throws ReportException is thrown when an exception occurs */ - protected void generateReport(String templateName, OutputStream outputStream) throws IOException, Exception { + protected void generateReport(String templateName, OutputStream outputStream) throws ReportException { InputStream input = null; String templatePath = null; final File f = new File(templateName); @@ -235,27 +240,30 @@ public class ReportGenerator { templatePath = templateName; input = new FileInputStream(f); } catch (FileNotFoundException ex) { - LOGGER.error("Unable to generate the report, the report template file could not be found."); - LOGGER.debug("", ex); + throw new ReportException("Unable to locate template file: " + templateName, ex); } } else { templatePath = "templates/" + templateName + ".vsl"; input = this.getClass().getClassLoader().getResourceAsStream(templatePath); } if (input == null) { - throw new IOException("Template file doesn't exist"); + throw new ReportException("Template file doesn't exist: " + templatePath); } - final InputStreamReader reader = new InputStreamReader(input, "UTF-8"); + InputStreamReader reader = null; OutputStreamWriter writer = null; try { + reader = new InputStreamReader(input, "UTF-8"); writer = new OutputStreamWriter(outputStream, "UTF-8"); - - if (!engine.evaluate(context, writer, templatePath, reader)) { - throw new Exception("Failed to convert the template into html."); + if (!velocityEngine.evaluate(context, writer, templatePath, reader)) { + throw new ReportException("Failed to convert the template into html."); } writer.flush(); + } catch (UnsupportedEncodingException ex) { + throw new ReportException("Unable to generate the report using UTF-8", ex); + } catch (IOException ex) { + throw new ReportException("Unable to write the report", ex); } finally { if (writer != null) { try { @@ -271,25 +279,27 @@ public class ReportGenerator { LOGGER.trace("", ex); } } - try { - reader.close(); - } catch (IOException ex) { - LOGGER.trace("", ex); + if (reader != null) { + try { + reader.close(); + } catch (IOException ex) { + LOGGER.trace("", ex); + } } } } /** - * Generates a report from a given Velocity Template. The template name provided can be the name of a template - * contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a + * Generates a report from a given Velocity Template. The template name + * provided can be the name of a template contained in the jar file, such as + * 'XmlReport' or 'HtmlReport', or the template name can be the path to a * template file. * - * @param templateName the name of the template to load. - * @param outFileName the filename and path to write the report to. - * @throws IOException is thrown when the template file does not exist. - * @throws Exception is thrown when an exception occurs. + * @param templateName the name of the template to load + * @param outFileName the filename and path to write the report to + * @throws ReportException is thrown when the report cannot be generated */ - protected void generateReport(String templateName, String outFileName) throws Exception { + protected void generateReport(String templateName, String outFileName) throws ReportException { File outFile = new File(outFileName); if (outFile.getParentFile() == null) { outFile = new File(".", outFileName); @@ -297,7 +307,7 @@ public class ReportGenerator { if (!outFile.getParentFile().exists()) { final boolean created = outFile.getParentFile().mkdirs(); if (!created) { - throw new Exception("Unable to create directory '" + outFile.getParentFile().getAbsolutePath() + "'."); + throw new ReportException("Unable to create directory '" + outFile.getParentFile().getAbsolutePath() + "'."); } } @@ -305,6 +315,8 @@ public class ReportGenerator { try { outputSteam = new FileOutputStream(outFile); generateReport(templateName, outputSteam); + } catch (FileNotFoundException ex) { + throw new ReportException("Unable to write to file: " + outFile, ex); } finally { if (outputSteam != null) { try { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java index 483413dcb..b91510b1e 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java @@ -39,6 +39,11 @@ public final class DependencyVersionUtil { * are missing a version number using the previous regex. */ private static final Pattern RX_SINGLE_VERSION = Pattern.compile("\\d+(\\.?([_-](release|beta|alpha)|[a-zA-Z_-]{1,3}\\d{1,8}))?"); + + /** + * Regular expression to extract the part before the version numbers if there are any based on RX_VERSION. In most cases, this part represents a more accurate name. + */ + private static final Pattern RX_PRE_VERSION = Pattern.compile("^(.+)[_-](\\d+\\.\\d{1,6})+"); /** * Private constructor for utility class. @@ -95,4 +100,27 @@ public final class DependencyVersionUtil { } return new DependencyVersion(version); } + + /** + *

+ * A utility class to extract the part before version numbers from file names (or other strings containing version numbers. + * In most cases, this part represents a more accurate name than the full file name.

+ *
+     * Example:
+     * Give the file name: library-name-1.4.1r2-release.jar
+     * This function would return: library-name
+ * + * @param text the text being analyzed + * @return the part before the version numbers if any, otherwise return the text itself. + */ + public static String parsePreVersion(String text) { + if(parseVersion(text) == null) + return text; + + Matcher matcher = RX_PRE_VERSION.matcher(text); + if (matcher.find()) { + return matcher.group(1); + } + return text; + } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java index c5bd0f7b3..9634fb3d2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java @@ -197,11 +197,11 @@ public class HintHandler extends DefaultHandler { vendorDuplicatingHintRules.add(new VendorDuplicatingHintRule(attr.getValue(VALUE), attr.getValue(DUPLICATE))); } } - + /** * Handles the end element event. * - * @param uri the element's uri + * @param uri the element's URI * @param localName the local name * @param qName the qualified name * @throws SAXException thrown if there is an exception processing the diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java index ec85d98f2..1d9df8d4d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java @@ -72,9 +72,9 @@ public class HintRule { } /** - * Get the value of givenProduct + * Get the value of givenProduct. * - * @return the value of givenProduct. + * @return the value of givenProduct */ public List getGivenProduct() { return givenProduct; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/Hints.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/Hints.java index 0240d2fc1..34e465004 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/Hints.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/Hints.java @@ -32,7 +32,7 @@ public class Hints { private List hintRules; /** - * Get the value of hintRules + * Get the value of hintRules. * * @return the value of hintRules */ @@ -41,7 +41,7 @@ public class Hints { } /** - * Set the value of hintRules + * Set the value of hintRules. * * @param hintRules new value of hintRules */ @@ -55,7 +55,7 @@ public class Hints { private List vendorDuplicatingHintRules; /** - * Get the value of vendorDuplicatingHintRules + * Get the value of vendorDuplicatingHintRules. * * @return the value of vendorDuplicatingHintRules */ @@ -64,12 +64,11 @@ public class Hints { } /** - * Set the value of vendorDuplicatingHintRules + * Set the value of vendorDuplicatingHintRules. * * @param vendorDuplicatingHintRules new value of vendorDuplicatingHintRules */ public void setVendorDuplicatingHintRules(List vendorDuplicatingHintRules) { this.vendorDuplicatingHintRules = vendorDuplicatingHintRules; } - } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java index bacbcb5a0..e36bc5365 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java @@ -70,7 +70,7 @@ public class SuppressionErrorHandler implements ErrorHandler { */ @Override public void warning(SAXParseException ex) throws SAXException { - LOGGER.debug("", ex); + //LOGGER.debug("", ex); } /** diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java index 1e12e6a82..bceefc23b 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java @@ -17,12 +17,19 @@ */ package org.owasp.dependencycheck; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; import static org.junit.Assert.assertTrue; import org.junit.Test; 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.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.reporting.ReportGenerator; +import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; /** @@ -34,10 +41,14 @@ public class EngineIntegrationTest extends BaseDBTestCase { /** * Test running the entire engine. * - * @throws Exception is thrown when an exception occurs. + * @throws java.io.IOException + * @throws org.owasp.dependencycheck.utils.InvalidSettingException + * @throws org.owasp.dependencycheck.data.nvdcve.DatabaseException + * @throws org.owasp.dependencycheck.exception.ReportException + * @throws org.owasp.dependencycheck.exception.ExceptionCollection */ @Test - public void testEngine() throws Exception { + public void testEngine() throws IOException, InvalidSettingException, DatabaseException, ReportException, ExceptionCollection { String testClasses = "target/test-classes"; boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); @@ -45,7 +56,23 @@ public class EngineIntegrationTest extends BaseDBTestCase { Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); instance.scan(testClasses); assertTrue(instance.getDependencies().size() > 0); - instance.analyzeDependencies(); + try { + instance.analyzeDependencies(); + } catch (ExceptionCollection ex) { + if (ex.getExceptions().size()==1 && + (ex.getExceptions().get(0).getMessage().contains("bundle-audit") || + ex.getExceptions().get(0).getMessage().contains("AssemblyAnalyzer"))) { + //this is fine to ignore + } else if (ex.getExceptions().size()==2 && + ((ex.getExceptions().get(0).getMessage().contains("bundle-audit") && + ex.getExceptions().get(1).getMessage().contains("AssemblyAnalyzer")) || + (ex.getExceptions().get(1).getMessage().contains("bundle-audit") && + ex.getExceptions().get(0).getMessage().contains("AssemblyAnalyzer")))) { + //this is fine to ignore + } else { + throw ex; + } + } CveDB cveDB = new CveDB(); cveDB.open(); DatabaseProperties dbProp = cveDB.getDatabaseProperties(); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java index 6299850ee..c1b57ab66 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java @@ -26,6 +26,7 @@ import java.io.File; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.logging.Level; import org.junit.After; import org.junit.Assume; @@ -40,6 +41,7 @@ import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Evidence; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; +import org.owasp.dependencycheck.exception.ExceptionCollection; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -175,6 +177,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { * Test Ruby dependencies and their paths. * * @throws AnalysisException is thrown when an exception occurs. + * @throws DatabaseException thrown when an exception occurs */ @Test public void testDependenciesPath() throws AnalysisException, DatabaseException { @@ -186,6 +189,8 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { } catch (NullPointerException ex) { LOGGER.error("NPE", ex); throw ex; + } catch (ExceptionCollection ex) { + Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", ex); } List dependencies = engine.getDependencies(); LOGGER.info(dependencies.size() + " dependencies found."); diff --git a/dependency-check-maven/pom.xml b/dependency-check-maven/pom.xml index aa33ce2f8..d6f48ff87 100644 --- a/dependency-check-maven/pom.xml +++ b/dependency-check-maven/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.4.1-SNAPSHOT + 1.4.3-SNAPSHOT dependency-check-maven 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..d17854dd0 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 @@ -36,11 +36,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 +57,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 +87,6 @@ public class AggregateMojo extends BaseDependencyCheckMojo { generateDataFile(engine, current); } } - for (MavenProject current : getReactorProjects()) { List dependencies = readDataFile(current); if (dependencies == null) { @@ -90,10 +100,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 +126,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 +148,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 +175,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 +256,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 +366,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..3df3f6d75 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,28 @@ 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 +126,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 +158,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 +174,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 +554,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 +662,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 +888,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 +911,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,8 +1080,8 @@ 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 - * serialized data file exists; otherwise null is returned + * @return a MavenEngine object populated with dependencies if + * the serialized data file exists; otherwise null is returned */ protected List readDataFile(MavenProject project) { final Object oPath = project.getContextValue(this.getDataFileContextKey()); 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..ccada1b5c 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 81% 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 f849c8a7e..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 @@ -23,22 +23,25 @@ import org.owasp.dependencycheck.analyzer.Analyzer; import org.owasp.dependencycheck.analyzer.CPEAnalyzer; import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; +import org.owasp.dependencycheck.data.update.exception.UpdateException; +import org.owasp.dependencycheck.exception.ExceptionCollection; +import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * A modified version of the core engine specifically designed to persist some data between multiple executions of a multi-module - * Maven project. + * A modified version of the core engine specifically designed to persist some + * data between multiple executions of a multi-module Maven project. * * @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. */ @@ -52,18 +55,21 @@ public class Engine extends org.owasp.dependencycheck.Engine { */ private List reactorProjects; /** - * Key used in the MavenProject context values to note whether or not an update has been executed. + * Key used in the MavenProject context values to note whether or not an + * update has been executed. */ public static final String UPDATE_EXECUTED_FLAG = "dependency-check-update-executed"; /** - * Creates a new Engine to perform anyalsis on dependencies. + * Creates a new Engine to perform analysis on dependencies. * * @param project the current Maven project - * @param reactorProjects the reactor projects for the current Maven execution - * @throws DatabaseException thrown if there is an issue connecting to the database + * @param reactorProjects the reactor projects for the current Maven + * execution + * @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(); @@ -71,9 +77,12 @@ public class Engine extends org.owasp.dependencycheck.Engine { /** * Runs the analyzers against all of the dependencies. + * + * @throws ExceptionCollection thrown if an exception occurred; contains a + * collection of exceptions that occurred during analysis. */ @Override - public void analyzeDependencies() { + public void analyzeDependencies() throws ExceptionCollection { final MavenProject root = getExecutionRoot(); if (root != null) { LOGGER.debug("Checking root project, {}, if updates have already been completed", root.getArtifactId()); @@ -91,8 +100,10 @@ public class Engine extends org.owasp.dependencycheck.Engine { /** * Runs the update steps of dependency-check. + * + * @throws UpdateException thrown if there is an exception */ - public void update() { + public void update() throws UpdateException { final MavenProject root = getExecutionRoot(); if (root != null && root.getContextValue(UPDATE_EXECUTED_FLAG) != null) { System.setProperty(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE.toString()); @@ -103,20 +114,21 @@ public class Engine extends org.owasp.dependencycheck.Engine { /** * This constructor should not be called. Use Engine(MavenProject) instead. * - * @throws DatabaseException thrown if there is an issue connecting to the database + * @throws DatabaseException thrown if there is an issue connecting to the + * database */ - private Engine() throws DatabaseException { + private MavenEngine() throws DatabaseException { } /** - * Initializes the given analyzer. This skips the initialization of the CPEAnalyzer if it has been initialized by a previous - * execution. + * Initializes the given analyzer. This skips the initialization of the + * CPEAnalyzer if it has been initialized by a previous execution. * * @param analyzer the analyzer to initialize * @return the initialized analyzer */ @Override - protected Analyzer initializeAnalyzer(Analyzer analyzer) { + protected Analyzer initializeAnalyzer(Analyzer analyzer) throws InitializationException { if (analyzer instanceof CPEAnalyzer) { CPEAnalyzer cpe = getPreviouslyLoadedCPEAnalyzer(); if (cpe != null && cpe.isOpen()) { @@ -129,7 +141,8 @@ public class Engine extends org.owasp.dependencycheck.Engine { } /** - * Releases resources used by the analyzers by calling close() on each analyzer. + * Releases resources used by the analyzers by calling close() on each + * analyzer. */ @Override public void cleanup() { @@ -195,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; } @@ -216,8 +229,10 @@ public class Engine extends org.owasp.dependencycheck.Engine { } /** - * Resets the file type analyzers so that they can be re-used to scan additional directories. Without the reset the analyzer - * might be disabled because the first scan/analyze did not identify any files that could be processed by the analyzer. + * Resets the file type analyzers so that they can be re-used to scan + * additional directories. Without the reset the analyzer might be disabled + * because the first scan/analyze did not identify any files that could be + * processed by the analyzer. */ public void resetFileTypeAnalyzers() { for (FileTypeAnalyzer a : getFileTypeAnalyzers()) { 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..bedb80a7c 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,4 @@ 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/site/markdown/index.md.vm b/dependency-check-maven/src/site/markdown/index.md.vm index 49312f4ba..c4e5c1c2a 100644 --- a/dependency-check-maven/src/site/markdown/index.md.vm +++ b/dependency-check-maven/src/site/markdown/index.md.vm @@ -53,18 +53,16 @@ Create an aggregated dependency-check report within the site. ... - - org.owasp - dependency-check-maven - ${project.version} - - - - aggregate - - - - + org.owasp + dependency-check-maven + ${project.version} + + + + aggregate + + + ... 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()); diff --git a/dependency-check-utils/pom.xml b/dependency-check-utils/pom.xml index 709ab9f4c..c5c10da90 100644 --- a/dependency-check-utils/pom.xml +++ b/dependency-check-utils/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 1.4.1-SNAPSHOT + 1.4.3-SNAPSHOT dependency-check-utils diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java index 850a302e7..894c2fab2 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java @@ -175,7 +175,7 @@ public final class Downloader { } LOGGER.debug("Download of {} complete", url.toString()); } catch (IOException ex) { - checkForSslExceptionn(ex); + checkForCommonExceptionTypes(ex); final String msg = format("Error saving '%s' to file '%s'%nConnection Timeout: %d%nEncoding: %s%n", url.toString(), outputPath.getAbsolutePath(), conn.getConnectTimeout(), encoding); throw new DownloadFailedException(msg, ex); @@ -261,8 +261,9 @@ public final class Downloader { } catch (URLConnectionFailureException ex) { throw new DownloadFailedException(format("Error creating URL Connection for HTTP %s request.", httpMethod), ex); } catch (IOException ex) { - checkForSslExceptionn(ex); - LOGGER.debug("IO Exception: " + ex.getMessage(), ex); + checkForCommonExceptionTypes(ex); + LOGGER.error("IO Exception: " + ex.getMessage()); + LOGGER.debug("Exception details", ex); if (ex.getCause() != null) { LOGGER.debug("IO Exception cause: " + ex.getCause().getMessage(), ex.getCause()); } @@ -292,15 +293,21 @@ public final class Downloader { /** * Analyzes the IOException, logs the appropriate information for debugging * purposes, and then throws a DownloadFailedException that wraps the IO - * Exception. + * Exception for common IO Exceptions. This is to provide additional details + * to assist in resolution of the exception. * * @param ex the original exception * @throws DownloadFailedException a wrapper exception that contains the * original exception as the cause */ - protected static void checkForSslExceptionn(IOException ex) throws DownloadFailedException { + protected static void checkForCommonExceptionTypes(IOException ex) throws DownloadFailedException { Throwable cause = ex; while (cause != null) { + if (cause instanceof java.net.UnknownHostException) { + final String msg = String.format("Unable to resolve domain '%s'", cause.getMessage()); + LOGGER.error(msg); + throw new DownloadFailedException(msg); + } if (cause instanceof InvalidAlgorithmParameterException) { final String keystore = System.getProperty("javax.net.ssl.keyStore"); final String version = System.getProperty("java.version"); diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/SSLSocketFactoryEx.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/SSLSocketFactoryEx.java new file mode 100644 index 000000000..727eb24f7 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/SSLSocketFactoryEx.java @@ -0,0 +1,385 @@ +package org.owasp.dependencycheck.utils; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used to enable additional ciphers used by the SSL Socket. This + * is specifically because the NVD stopped supporting TLS 1.0 and Java 6 and 7 + * clients by default were unable to connect to download the NVD data feeds. + * + * The following code was copied from + * http://stackoverflow.com/questions/1037590/which-cipher-suites-to-enable-for-ssl-socket/23365536#23365536 + * + * @author jww + */ +public class SSLSocketFactoryEx extends SSLSocketFactory { + + /** + * The Logger for use throughout the class. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(SSLSocketFactoryEx.class); + + /** + * Constructs a new SSLSocketFactory. + * + * @throws NoSuchAlgorithmException thrown when an algorithm is not + * supported + * @throws KeyManagementException thrown if initialization fails + */ + public SSLSocketFactoryEx() throws NoSuchAlgorithmException, KeyManagementException { + initSSLSocketFactoryEx(null, null, null); + } + + /** + * Constructs a new SSLSocketFactory. + * + * @param km the key manager + * @param tm the trust manager + * @param random secure random + * @throws NoSuchAlgorithmException thrown when an algorithm is not + * supported + * @throws KeyManagementException thrown if initialization fails + */ + public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException { + initSSLSocketFactoryEx(km, tm, random); + } + + /** + * Constructs a new SSLSocketFactory. + * + * @param ctx the SSL context + * @throws NoSuchAlgorithmException thrown when an algorithm is not + * supported + * @throws KeyManagementException thrown if initialization fails + */ + public SSLSocketFactoryEx(SSLContext ctx) throws NoSuchAlgorithmException, KeyManagementException { + initSSLSocketFactoryEx(ctx); + } + + /** + * Returns the default cipher suites. + * + * @return the default cipher suites + */ + @Override + public String[] getDefaultCipherSuites() { + return Arrays.copyOf(ciphers, ciphers.length); + } + + /** + * Returns the supported cipher suites. + * + * @return the supported cipher suites + */ + @Override + public String[] getSupportedCipherSuites() { + return Arrays.copyOf(ciphers, ciphers.length); + } + + /** + * Returns the default protocols. + * + * @return the default protocols + */ + public String[] getDefaultProtocols() { + return Arrays.copyOf(protocols, protocols.length); + } + + /** + * Returns the supported protocols. + * + * @return the supported protocols + */ + public String[] getSupportedProtocols() { + return Arrays.copyOf(protocols, protocols.length); + } + + /** + * Creates an SSL Socket. + * + * @param s the base socket + * @param host the host + * @param port the port + * @param autoClose if the socket should auto-close + * @return the SSL Socket + * @throws IOException thrown if the creation fails + */ + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + final SSLSocketFactory factory = sslCtxt.getSocketFactory(); + final SSLSocket ss = (SSLSocket) factory.createSocket(s, host, port, autoClose); + + ss.setEnabledProtocols(protocols); + ss.setEnabledCipherSuites(ciphers); + + return ss; + } + + /** + * Creates a new SSL Socket. + * + * @param address the address to connect to + * @param port the port number + * @param localAddress the local address + * @param localPort the local port + * @return the SSL Socket + * @throws IOException thrown if the creation fails + */ + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + final SSLSocketFactory factory = sslCtxt.getSocketFactory(); + final SSLSocket ss = (SSLSocket) factory.createSocket(address, port, localAddress, localPort); + + ss.setEnabledProtocols(protocols); + ss.setEnabledCipherSuites(ciphers); + + return ss; + } + + /** + * Creates a new SSL Socket. + * + * @param host the host to connect to + * @param port the port to connect to + * @param localHost the local host + * @param localPort the local port + * @return the SSL Socket + * @throws IOException thrown if the creation fails + */ + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + final SSLSocketFactory factory = sslCtxt.getSocketFactory(); + final SSLSocket ss = (SSLSocket) factory.createSocket(host, port, localHost, localPort); + + ss.setEnabledProtocols(protocols); + ss.setEnabledCipherSuites(ciphers); + + return ss; + } + + /** + * Creates a new SSL Socket. + * + * @param host the host to connect to + * @param port the port to connect to + * @return the SSL Socket + * @throws IOException thrown if the creation fails + */ + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + final SSLSocketFactory factory = sslCtxt.getSocketFactory(); + final SSLSocket ss = (SSLSocket) factory.createSocket(host, port); + + ss.setEnabledProtocols(protocols); + ss.setEnabledCipherSuites(ciphers); + + return ss; + } + + /** + * Creates a new SSL Socket. + * + * @param host the host to connect to + * @param port the port to connect to + * @return the SSL Socket + * @throws IOException thrown if the creation fails + */ + @Override + public Socket createSocket(String host, int port) throws IOException { + final SSLSocketFactory factory = sslCtxt.getSocketFactory(); + final SSLSocket ss = (SSLSocket) factory.createSocket(host, port); + + ss.setEnabledProtocols(protocols); + ss.setEnabledCipherSuites(ciphers); + + return ss; + } + + /** + * Initializes the SSL Socket Factory Extension. + * + * @param km the key managers + * @param tm the trust managers + * @param random the secure random number generator + * @throws NoSuchAlgorithmException thrown when an algorithm is not + * supported + * @throws KeyManagementException thrown if initialization fails + */ + private void initSSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) + throws NoSuchAlgorithmException, KeyManagementException { + sslCtxt = SSLContext.getInstance("TLS"); + sslCtxt.init(km, tm, random); + + protocols = getProtocolList(); + ciphers = getCipherList(); + } + + /** + * Initializes the SSL Socket Factory Extension. + * + * @param ctx the SSL context + * @throws NoSuchAlgorithmException thrown when an algorithm is not + * supported + * @throws KeyManagementException thrown if initialization fails + */ + private void initSSLSocketFactoryEx(SSLContext ctx) + throws NoSuchAlgorithmException, KeyManagementException { + sslCtxt = ctx; + + protocols = getProtocolList(); + ciphers = getCipherList(); + } + + /** + * Returns the protocol list. + * + * @return the protocol list + */ + protected String[] getProtocolList() { + final String[] preferredProtocols = {"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"}; + String[] availableProtocols = null; + + SSLSocket socket = null; + + try { + final SSLSocketFactory factory = sslCtxt.getSocketFactory(); + socket = (SSLSocket) factory.createSocket(); + + availableProtocols = socket.getSupportedProtocols(); + Arrays.sort(availableProtocols); + } catch (Exception ex) { + LOGGER.debug("Error getting protocol list, using TLSv1", ex); + return new String[]{"TLSv1"}; + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException ex) { + LOGGER.trace("Error closing socket", ex); + } + } + } + + final List aa = new ArrayList(); + for (String preferredProtocol : preferredProtocols) { + final int idx = Arrays.binarySearch(availableProtocols, preferredProtocol); + if (idx >= 0) { + aa.add(preferredProtocol); + } + } + + return aa.toArray(new String[0]); + } + + /** + * Returns the cipher list. + * + * @return the cipher list + */ + protected String[] getCipherList() { + final String[] preferredCiphers = { + // *_CHACHA20_POLY1305 are 3x to 4x faster than existing cipher suites. + // http://googleonlinesecurity.blogspot.com/2014/04/speeding-up-and-strengthening-https.html + // Use them if available. Normative names can be found at (TLS spec depends on IPSec spec): + // http://tools.ietf.org/html/draft-nir-ipsecme-chacha20-poly1305-01 + // http://tools.ietf.org/html/draft-mavrogiannopoulos-chacha-tls-02 + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_SHA", + "TLS_ECDHE_RSA_WITH_CHACHA20_SHA", + "TLS_DHE_RSA_WITH_CHACHA20_POLY1305", + "TLS_RSA_WITH_CHACHA20_POLY1305", + "TLS_DHE_RSA_WITH_CHACHA20_SHA", + "TLS_RSA_WITH_CHACHA20_SHA", + // Done with bleeding edge, back to TLS v1.2 and below + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", + // TLS v1.0 (with some SSLv3 interop) + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA", + "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA", + // RSA key transport sucks, but they are needed as a fallback. + // For example, microsoft.com fails under all versions of TLS + // if they are not included. If only TLS 1.0 is available at + // the client, then google.com will fail too. TLS v1.3 is + // trying to deprecate them, so it will be interesteng to see + // what happens. + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_CBC_SHA", + }; + + String[] availableCiphers; + + try { + final SSLSocketFactory factory = sslCtxt.getSocketFactory(); + availableCiphers = factory.getSupportedCipherSuites(); + Arrays.sort(availableCiphers); + } catch (Exception e) { + LOGGER.debug("Error retrieving ciphers", e); + return new String[]{ + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", + }; + } + + final List aa = new ArrayList(); + for (String preferredCipher : preferredCiphers) { + final int idx = Arrays.binarySearch(availableCiphers, preferredCipher); + if (idx >= 0) { + aa.add(preferredCipher); + } + } + + aa.add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV"); + + return aa.toArray(new String[0]); + } + + /** + * The SSL context. + */ + private SSLContext sslCtxt; + /** + * The cipher suites. + */ + private String[] ciphers; + /** + * The protocols. + */ + private String[] protocols; +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index 5f42e6f7d..359d24171 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -46,7 +46,8 @@ public final class Settings { public static final class KEYS { /** - * private constructor because this is a "utility" class containing constants + * private constructor because this is a "utility" class containing + * constants */ private KEYS() { //do nothing @@ -54,29 +55,34 @@ public final class Settings { /** * The key to obtain the application name. */ - public static final String APPLICATION_VAME = "application.name"; + public static final String APPLICATION_NAME = "application.name"; /** * The key to obtain the application version. */ public static final String APPLICATION_VERSION = "application.version"; /** - * The key to obtain the URL to retrieve the current release version from. + * The key to obtain the URL to retrieve the current release version + * from. */ public static final String ENGINE_VERSION_CHECK_URL = "engine.version.url"; /** - * The properties key indicating whether or not the cached data sources should be updated. + * The properties key indicating whether or not the cached data sources + * should be updated. */ public static final String AUTO_UPDATE = "autoupdate"; /** - * The database driver class name. If this is not in the properties file the embedded database is used. + * The database driver class name. If this is not in the properties file + * the embedded database is used. */ public static final String DB_DRIVER_NAME = "data.driver_name"; /** - * The database driver class name. If this is not in the properties file the embedded database is used. + * The database driver class name. If this is not in the properties file + * the embedded database is used. */ public static final String DB_DRIVER_PATH = "data.driver_path"; /** - * The database connection string. If this is not in the properties file the embedded database is used. + * The database connection string. If this is not in the properties file + * the embedded database is used. */ public static final String DB_CONNECTION_STRING = "data.connection_string"; /** @@ -101,36 +107,41 @@ public final class Settings { public static final String DB_VERSION = "data.version"; /** * The starts with filter used to exclude CVE entries from the database. - * By default this is set to 'cpe:/a:' which limits the CVEs imported to - * just those that are related to applications. If this were set to just - * 'cpe:' the OS, hardware, and application related CVEs would be imported. + * By default this is set to 'cpe:/a:' which limits the CVEs imported to + * just those that are related to applications. If this were set to just + * 'cpe:' the OS, hardware, and application related CVEs would be + * imported. */ public static final String CVE_CPE_STARTS_WITH_FILTER = "cve.cpe.startswith.filter"; /** - * The properties key for the URL to retrieve the "meta" data from about the CVE entries. + * The properties key for the URL to retrieve the "meta" data from about + * the CVE entries. */ public static final String CVE_META_URL = "cve.url.meta"; /** - * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days) using the 2.0 - * schema. + * The properties key for the URL to retrieve the recently modified and + * added CVE entries (last 8 days) using the 2.0 schema. */ public static final String CVE_MODIFIED_20_URL = "cve.url-2.0.modified"; /** - * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days) using the 1.2 - * schema. + * The properties key for the URL to retrieve the recently modified and + * added CVE entries (last 8 days) using the 1.2 schema. */ public static final String CVE_MODIFIED_12_URL = "cve.url-1.2.modified"; /** - * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days). + * The properties key for the URL to retrieve the recently modified and + * added CVE entries (last 8 days). */ public static final String CVE_MODIFIED_VALID_FOR_DAYS = "cve.url.modified.validfordays"; /** - * The properties key to control the skipping of the check for CVE updates. + * The properties key to control the skipping of the check for CVE + * updates. */ public static final String CVE_CHECK_VALID_FOR_HOURS = "cve.check.validforhours"; /** - * The properties key for the telling us how many cve.url.* URLs exists. This is used in combination with CVE_BASE_URL to - * be able to retrieve the URLs for all of the files that make up the NVD CVE listing. + * The properties key for the telling us how many cve.url.* URLs exists. + * This is used in combination with CVE_BASE_URL to be able to retrieve + * the URLs for all of the files that make up the NVD CVE listing. */ public static final String CVE_START_YEAR = "cve.startyear"; /** @@ -142,7 +153,8 @@ public final class Settings { */ public static final String CVE_SCHEMA_2_0 = "cve.url-2.0.base"; /** - * The properties key that indicates how often the CPE data needs to be updated. + * The properties key that indicates how often the CPE data needs to be + * updated. */ public static final String CPE_MODIFIED_VALID_FOR_DAYS = "cpe.validfordays"; /** @@ -152,7 +164,9 @@ public final class Settings { /** * The properties key for the proxy server. * - * @deprecated use {@link org.owasp.dependencycheck.utils.Settings.KEYS#PROXY_SERVER} instead. + * @deprecated use + * {@link org.owasp.dependencycheck.utils.Settings.KEYS#PROXY_SERVER} + * instead. */ @Deprecated public static final String PROXY_URL = "proxy.server"; @@ -161,7 +175,8 @@ public final class Settings { */ public static final String PROXY_SERVER = "proxy.server"; /** - * The properties key for the proxy port - this must be an integer value. + * The properties key for the proxy port - this must be an integer + * value. */ public static final String PROXY_PORT = "proxy.port"; /** @@ -209,19 +224,23 @@ public final class Settings { */ public static final String ANALYZER_ARCHIVE_ENABLED = "analyzer.archive.enabled"; /** - * The properties key for whether the node.js package analyzer is enabled. + * The properties key for whether the node.js package analyzer is + * enabled. */ public static final String ANALYZER_NODE_PACKAGE_ENABLED = "analyzer.node.package.enabled"; /** - * The properties key for whether the composer lock file analyzer is enabled. + * The properties key for whether the composer lock file analyzer is + * enabled. */ public static final String ANALYZER_COMPOSER_LOCK_ENABLED = "analyzer.composer.lock.enabled"; /** - * The properties key for whether the Python Distribution analyzer is enabled. + * The properties key for whether the Python Distribution analyzer is + * enabled. */ public static final String ANALYZER_PYTHON_DISTRIBUTION_ENABLED = "analyzer.python.distribution.enabled"; /** - * The properties key for whether the Python Package analyzer is enabled. + * The properties key for whether the Python Package analyzer is + * enabled. */ public static final String ANALYZER_PYTHON_PACKAGE_ENABLED = "analyzer.python.package.enabled"; /** @@ -237,7 +256,8 @@ public final class Settings { */ public static final String ANALYZER_CMAKE_ENABLED = "analyzer.cmake.enabled"; /** - * The properties key for whether the Ruby Bundler Audit analyzer is enabled. + * The properties key for whether the Ruby Bundler Audit analyzer is + * enabled. */ public static final String ANALYZER_BUNDLE_AUDIT_ENABLED = "analyzer.bundle.audit.enabled"; /** @@ -339,7 +359,8 @@ public final class Settings { private Properties props = null; /** - * Private constructor for the Settings class. This class loads the properties files. + * Private constructor for the Settings class. This class loads the + * properties files. * * @param propertiesFilePath the path to the base properties file to load */ @@ -365,16 +386,18 @@ public final class Settings { } /** - * Initializes the thread local settings object. Note, to use the settings object you must call this method. However, you must - * also call Settings.cleanup() to properly release resources. + * Initializes the thread local settings object. Note, to use the settings + * object you must call this method. However, you must also call + * Settings.cleanup() to properly release resources. */ public static void initialize() { LOCAL_SETTINGS.set(new Settings(PROPERTIES_FILE)); } /** - * Initializes the thread local settings object. Note, to use the settings object you must call this method. However, you must - * also call Settings.cleanup() to properly release resources. + * Initializes the thread local settings object. Note, to use the settings + * object you must call this method. However, you must also call + * Settings.cleanup() to properly release resources. * * @param propertiesFilePath the path to the base properties file to load */ @@ -393,7 +416,8 @@ public final class Settings { /** * Cleans up resources to prevent memory leaks. * - * @param deleteTemporary flag indicating whether any temporary directories generated should be removed + * @param deleteTemporary flag indicating whether any temporary directories + * generated should be removed */ public static void cleanup(boolean deleteTemporary) { if (deleteTemporary && tempDirectory != null && tempDirectory.exists()) { @@ -433,7 +457,8 @@ public final class Settings { } /** - * Logs the properties. This will not log any properties that contain 'password' in the key. + * Logs the properties. This will not log any properties that contain + * 'password' in the key. * * @param header the header to print with the log message * @param properties the properties to log @@ -549,13 +574,16 @@ public final class Settings { } /** - * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties - * file.

- * Note: even if using this method - system properties will be loaded before properties loaded from files. + * Merges a new properties file into the current properties. This method + * allows for the loading of a user provided properties file.

+ * Note: even if using this method - system properties will be loaded + * before properties loaded from files. * * @param filePath the path to the properties file to merge. - * @throws FileNotFoundException is thrown when the filePath points to a non-existent file - * @throws IOException is thrown when there is an exception loading/merging the properties + * @throws FileNotFoundException is thrown when the filePath points to a + * non-existent file + * @throws IOException is thrown when there is an exception loading/merging + * the properties */ public static void mergeProperties(File filePath) throws FileNotFoundException, IOException { FileInputStream fis = null; @@ -574,13 +602,16 @@ public final class Settings { } /** - * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties - * file.

- * Note: even if using this method - system properties will be loaded before properties loaded from files. + * Merges a new properties file into the current properties. This method + * allows for the loading of a user provided properties file.

+ * Note: even if using this method - system properties will be loaded before + * properties loaded from files. * * @param filePath the path to the properties file to merge. - * @throws FileNotFoundException is thrown when the filePath points to a non-existent file - * @throws IOException is thrown when there is an exception loading/merging the properties + * @throws FileNotFoundException is thrown when the filePath points to a + * non-existent file + * @throws IOException is thrown when there is an exception loading/merging + * the properties */ public static void mergeProperties(String filePath) throws FileNotFoundException, IOException { FileInputStream fis = null; @@ -599,12 +630,14 @@ public final class Settings { } /** - * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties - * file.

- * Note: even if using this method - system properties will be loaded before properties loaded from files. + * Merges a new properties file into the current properties. This method + * allows for the loading of a user provided properties file.

+ * Note: even if using this method - system properties will be loaded + * before properties loaded from files. * * @param stream an Input Stream pointing at a properties file to merge - * @throws IOException is thrown when there is an exception loading/merging the properties + * @throws IOException is thrown when there is an exception loading/merging + * the properties */ public static void mergeProperties(InputStream stream) throws IOException { LOCAL_SETTINGS.get().props.load(stream); @@ -612,9 +645,10 @@ public final class Settings { } /** - * Returns a value from the properties file as a File object. If the value was specified as a system property or passed in via - * the -Dprop=value argument - this method will return the value from the system properties before the values in the contained - * configuration file. + * Returns a value from the properties file as a File object. If the value + * was specified as a system property or passed in via the -Dprop=value + * argument - this method will return the value from the system properties + * before the values in the contained configuration file. * * @param key the key to lookup within the properties file * @return the property from the properties file converted to a File object @@ -628,13 +662,15 @@ public final class Settings { } /** - * Returns a value from the properties file as a File object. If the value was specified as a system property or passed in via - * the -Dprop=value argument - this method will return the value from the system properties before the values in the contained - * configuration file. + * Returns a value from the properties file as a File object. If the value + * was specified as a system property or passed in via the -Dprop=value + * argument - this method will return the value from the system properties + * before the values in the contained configuration file. * - * This method will check the configured base directory and will use this as the base of the file path. Additionally, if the - * base directory begins with a leading "[JAR]\" sequence with the path to the folder containing the JAR file containing this - * class. + * This method will check the configured base directory and will use this as + * the base of the file path. Additionally, if the base directory begins + * with a leading "[JAR]\" sequence with the path to the folder containing + * the JAR file containing this class. * * @param key the key to lookup within the properties file * @return the property from the properties file converted to a File object @@ -657,7 +693,8 @@ public final class Settings { } /** - * Attempts to retrieve the folder containing the Jar file containing the Settings class. + * Attempts to retrieve the folder containing the Jar file containing the + * Settings class. * * @return a File object */ @@ -679,9 +716,10 @@ public final class Settings { } /** - * Returns a value from the properties file. If the value was specified as a system property or passed in via the -Dprop=value - * argument - this method will return the value from the system properties before the values in the contained configuration - * file. + * Returns a value from the properties file. If the value was specified as a + * system property or passed in via the -Dprop=value argument - this method + * will return the value from the system properties before the values in the + * contained configuration file. * * @param key the key to lookup within the properties file * @param defaultValue the default value for the requested property @@ -693,7 +731,8 @@ public final class Settings { } /** - * A reference to the temporary directory; used incase it needs to be deleted during cleanup. + * A reference to the temporary directory; used incase it needs to be + * deleted during cleanup. */ private static File tempDirectory = null; @@ -701,7 +740,8 @@ public final class Settings { * Returns the temporary directory. * * @return the temporary directory - * @throws java.io.IOException thrown if the temporary directory does not exist and cannot be created + * @throws java.io.IOException thrown if the temporary directory does not + * exist and cannot be created */ public static File getTempDirectory() throws IOException { final File tmpDir = new File(Settings.getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir")), "dctemp"); @@ -714,9 +754,10 @@ public final class Settings { } /** - * Returns a value from the properties file. If the value was specified as a system property or passed in via the -Dprop=value - * argument - this method will return the value from the system properties before the values in the contained configuration - * file. + * Returns a value from the properties file. If the value was specified as a + * system property or passed in via the -Dprop=value argument - this method + * will return the value from the system properties before the values in the + * contained configuration file. * * @param key the key to lookup within the properties file * @return the property from the properties file @@ -726,7 +767,8 @@ public final class Settings { } /** - * Removes a property from the local properties collection. This is mainly used in test cases. + * Removes a property from the local properties collection. This is mainly + * used in test cases. * * @param key the property key to remove */ @@ -735,13 +777,15 @@ public final class Settings { } /** - * Returns an int value from the properties file. If the value was specified as a system property or passed in via the - * -Dprop=value argument - this method will return the value from the system properties before the values in the contained - * configuration file. + * Returns an int value from the properties file. If the value was specified + * as a system property or passed in via the -Dprop=value argument - this + * method will return the value from the system properties before the values + * in the contained configuration file. * * @param key the key to lookup within the properties file * @return the property from the properties file - * @throws InvalidSettingException is thrown if there is an error retrieving the setting + * @throws InvalidSettingException is thrown if there is an error retrieving + * the setting */ public static int getInt(String key) throws InvalidSettingException { try { @@ -752,14 +796,15 @@ public final class Settings { } /** - * Returns an int value from the properties file. If the value was specified as a system property or passed in via the - * -Dprop=value argument - this method will return the value from the system properties before the values in the contained - * configuration file. + * Returns an int value from the properties file. If the value was specified + * as a system property or passed in via the -Dprop=value argument - this + * method will return the value from the system properties before the values + * in the contained configuration file. * * @param key the key to lookup within the properties file * @param defaultValue the default value to return - * @return the property from the properties file or the defaultValue if the property does not exist or cannot be converted to - * an integer + * @return the property from the properties file or the defaultValue if the + * property does not exist or cannot be converted to an integer */ public static int getInt(String key, int defaultValue) { int value; @@ -775,13 +820,15 @@ public final class Settings { } /** - * Returns a long value from the properties file. If the value was specified as a system property or passed in via the - * -Dprop=value argument - this method will return the value from the system properties before the values in the contained - * configuration file. + * Returns a long value from the properties file. If the value was specified + * as a system property or passed in via the -Dprop=value argument - this + * method will return the value from the system properties before the values + * in the contained configuration file. * * @param key the key to lookup within the properties file * @return the property from the properties file - * @throws InvalidSettingException is thrown if there is an error retrieving the setting + * @throws InvalidSettingException is thrown if there is an error retrieving + * the setting */ public static long getLong(String key) throws InvalidSettingException { try { @@ -792,38 +839,47 @@ public final class Settings { } /** - * Returns a boolean value from the properties file. If the value was specified as a system property or passed in via the - * -Dprop=value argument this method will return the value from the system properties before the values in the - * contained configuration file. + * Returns a boolean value from the properties file. If the value was + * specified as a system property or passed in via the + * -Dprop=value argument this method will return the value from + * the system properties before the values in the contained configuration + * file. * * @param key the key to lookup within the properties file * @return the property from the properties file - * @throws InvalidSettingException is thrown if there is an error retrieving the setting + * @throws InvalidSettingException is thrown if there is an error retrieving + * the setting */ public static boolean getBoolean(String key) throws InvalidSettingException { return Boolean.parseBoolean(Settings.getString(key)); } /** - * Returns a boolean value from the properties file. If the value was specified as a system property or passed in via the - * -Dprop=value argument this method will return the value from the system properties before the values in the - * contained configuration file. + * Returns a boolean value from the properties file. If the value was + * specified as a system property or passed in via the + * -Dprop=value argument this method will return the value from + * the system properties before the values in the contained configuration + * file. * * @param key the key to lookup within the properties file - * @param defaultValue the default value to return if the setting does not exist + * @param defaultValue the default value to return if the setting does not + * exist * @return the property from the properties file - * @throws InvalidSettingException is thrown if there is an error retrieving the setting + * @throws InvalidSettingException is thrown if there is an error retrieving + * the setting */ public static boolean getBoolean(String key, boolean defaultValue) throws InvalidSettingException { return Boolean.parseBoolean(Settings.getString(key, Boolean.toString(defaultValue))); } /** - * Returns a connection string from the configured properties. If the connection string contains a %s, this method will - * determine the 'data' directory and replace the %s with the path to the data directory. If the data directory does not - * exists it will be created. + * Returns a connection string from the configured properties. If the + * connection string contains a %s, this method will determine the 'data' + * directory and replace the %s with the path to the data directory. If the + * data directory does not exists it will be created. * - * @param connectionStringKey the property file key for the connection string + * @param connectionStringKey the property file key for the connection + * string * @param dbFileNameKey the settings key for the db filename * @return the connection string * @throws IOException thrown the data directory cannot be created @@ -860,8 +916,9 @@ public final class Settings { } /** - * Retrieves the directory that the JAR file exists in so that we can ensure we always use a common data directory for the - * embedded H2 database. This is public solely for some unit tests; otherwise this should be private. + * Retrieves the directory that the JAR file exists in so that we can ensure + * we always use a common data directory for the embedded H2 database. This + * is public solely for some unit tests; otherwise this should be private. * * @return the data directory to store data files * @throws IOException is thrown if an IOException occurs of course... diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java index cbeb00a64..8a50a33cc 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java @@ -28,15 +28,28 @@ import java.net.PasswordAuthentication; import java.net.Proxy; import java.net.SocketAddress; import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import javax.net.ssl.HttpsURLConnection; +import org.apache.commons.lang3.JavaVersion; +import org.apache.commons.lang3.SystemUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * A URLConnection Factory to create new connections. This encapsulates several configuration checks to ensure that the connection - * uses the correct proxy settings. + * A URLConnection Factory to create new connections. This encapsulates several + * configuration checks to ensure that the connection uses the correct proxy + * settings. * * @author Jeremy Long */ public final class URLConnectionFactory { + /** + * The logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(URLConnectionFactory.class); + /** * Private constructor for this factory. */ @@ -44,8 +57,9 @@ public final class URLConnectionFactory { } /** - * Utility method to create an HttpURLConnection. If the application is configured to use a proxy this method will retrieve - * the proxy settings and use them when setting up the connection. + * Utility method to create an HttpURLConnection. If the application is + * configured to use a proxy this method will retrieve the proxy settings + * and use them when setting up the connection. * * @param url the url to connect to * @return an HttpURLConnection @@ -95,6 +109,7 @@ public final class URLConnectionFactory { } throw new URLConnectionFailureException("Error getting connection.", ex); } + configureTLS(url, conn); return conn; } @@ -140,8 +155,10 @@ public final class URLConnectionFactory { } /** - * Utility method to create an HttpURLConnection. The use of a proxy here is optional as there may be cases where a proxy is - * configured but we don't want to use it (for example, if there's an internal repository configured) + * Utility method to create an HttpURLConnection. The use of a proxy here is + * optional as there may be cases where a proxy is configured but we don't + * want to use it (for example, if there's an internal repository + * configured) * * @param url the URL to connect to * @param proxy whether to use the proxy (if configured) @@ -161,6 +178,29 @@ public final class URLConnectionFactory { } catch (IOException ioe) { throw new URLConnectionFailureException("Error getting connection.", ioe); } + configureTLS(url, conn); return conn; } + + /** + * If the protocol is HTTPS, this will configure the cipher suites so that + * connections can be made to the NVD, and others, using older versions of + * Java. + * + * @param url the URL + * @param conn the connection + */ + private static void configureTLS(URL url, HttpURLConnection conn) { + if ("https".equals(url.getProtocol()) && !SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_8)) { + try { + final HttpsURLConnection secCon = (HttpsURLConnection) conn; + final SSLSocketFactoryEx factory = new SSLSocketFactoryEx(); + secCon.setSSLSocketFactory(factory); + } catch (NoSuchAlgorithmException ex) { + LOGGER.debug("Unsupported algorithm in SSLSocketFactoryEx", ex); + } catch (KeyManagementException ex) { + LOGGER.debug("Key mnagement eception in SSLSocketFactoryEx", ex); + } + } + } } diff --git a/pom.xml b/pom.xml index 0576d574c..3df61fcb1 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long org.owasp dependency-check-parent - 1.4.1-SNAPSHOT + 1.4.3-SNAPSHOT pom diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 81273af65..81a187625 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -28,11 +28,12 @@ More information about dependency-check can be found here: OWASP dependency-check's core analysis engine can be used as: -- [Command Line Tool](dependency-check-cli/index.html) -- [Maven Plugin](dependency-check-maven/index.html) - [Ant Task](dependency-check-ant/index.html) +- [Command Line Tool](dependency-check-cli/index.html) - [Gradle Plugin](dependency-check-gradle/index.html) - [Jenkins Plugin](dependency-check-jenkins/index.html) +- [Maven Plugin](dependency-check-maven/index.html) +- [SBT Plugin](https://github.com/albuch/sbt-dependency-check) For help with dependency-check the following resource can be used: diff --git a/src/site/markdown/modules.md b/src/site/markdown/modules.md index 1a0d027fc..012988c08 100644 --- a/src/site/markdown/modules.md +++ b/src/site/markdown/modules.md @@ -3,10 +3,12 @@ Modules OWASP dependency-check's core analysis engine was designed to fit into an applications normal build and reporting process: -- [Maven Plugin](dependency-check-maven/index.html) -- [Ant Task](dependency-check-ant/index.html) -- [Gradle Plugin](dependency-check-gradle/index.html) -- [Jenkins Plugin](dependency-check-jenkins/index.html) +- [Ant Task](dependency-check-ant/index.html) +- [Command Line Tool](dependency-check-cli/index.html) +- [Gradle Plugin](dependency-check-gradle/index.html) +- [Jenkins Plugin](dependency-check-jenkins/index.html) +- [Maven Plugin](dependency-check-maven/index.html) +- [SBT Plugin](https://github.com/albuch/sbt-dependency-check) In addition, dependency-check can be executed from the [command line](dependency-check-cli/index.html).