Merge branch 'master' into swift_support

This commit is contained in:
bjiang
2016-08-12 16:35:12 -04:00
53 changed files with 1778 additions and 536 deletions

18
.github/issue_template.md vendored Normal file
View File

@@ -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
<dependency>
<groupId>org.sample</groupId>
<artifactId>foo</artifactId>
<version>1.0</version>
</dependency>
```

View File

@@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.4.1-SNAPSHOT</version> <version>1.4.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>dependency-check-ant</artifactId> <artifactId>dependency-check-ant</artifactId>

View File

@@ -18,7 +18,6 @@
package org.owasp.dependencycheck.taskdefs; package org.owasp.dependencycheck.taskdefs;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.List; import java.util.List;
import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project; 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.CveDB;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; 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.Dependency;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability; 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;
import org.owasp.dependencycheck.reporting.ReportGenerator.Format; import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
@@ -806,52 +808,67 @@ public class Check extends Update {
engine = new Engine(Check.class.getClassLoader()); engine = new Engine(Check.class.getClassLoader());
if (isUpdateOnly()) { if (isUpdateOnly()) {
log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN); log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN);
engine.doUpdates();
} else {
try { try {
for (Resource resource : path) { engine.doUpdates();
final FileProvider provider = resource.as(FileProvider.class); } catch (UpdateException ex) {
if (provider != null) { if (this.isFailOnError()) {
final File file = provider.getFile(); throw new BuildException(ex);
if (file != null && file.exists()) { }
engine.scan(file); 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(); engine.analyzeDependencies();
DatabaseProperties prop = null; } catch (ExceptionCollection ex) {
CveDB cve = null; if (this.isFailOnError()) {
try { throw new BuildException(ex);
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); 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) { if (this.failBuildOnCVSS <= 10) {
checkForFailure(engine.getDependencies()); checkForFailure(engine.getDependencies());
} }
if (this.showSummary) { if (this.showSummary) {
showSummary(engine.getDependencies()); 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);
} }
} }
} catch (DatabaseException ex) { } 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 { } finally {
Settings.cleanup(true); Settings.cleanup(true);
if (engine != null) { if (engine != null) {

View File

@@ -71,6 +71,30 @@ public class Purge extends Task {
this.dataDirectory = dataDirectory; 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 @Override
public void execute() throws BuildException { public void execute() throws BuildException {
populateSettings(); populateSettings();
@@ -81,30 +105,49 @@ public class Purge extends Task {
if (db.delete()) { if (db.delete()) {
log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO); log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO);
} else { } 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 { } 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) { } 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 { } finally {
Settings.cleanup(true); Settings.cleanup(true);
} }
} }
/** /**
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties * Takes the properties supplied and updates the dependency-check settings.
* required to change the proxy server, port, and connection timeout. * 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(); Settings.initialize();
InputStream taskProperties = null; InputStream taskProperties = null;
try { try {
taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE); taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
Settings.mergeProperties(taskProperties); Settings.mergeProperties(taskProperties);
} catch (IOException ex) { } 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 { } finally {
if (taskProperties != null) { if (taskProperties != null) {
try { try {

View File

@@ -18,14 +18,17 @@
package org.owasp.dependencycheck.taskdefs; package org.owasp.dependencycheck.taskdefs;
import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.impl.StaticLoggerBinder; import org.slf4j.impl.StaticLoggerBinder;
/** /**
* An Ant task definition to execute dependency-check update. This will download the latest data from the National Vulnerability * An Ant task definition to execute dependency-check update. This will download
* Database (NVD) and store a copy in the local database. * the latest data from the National Vulnerability Database (NVD) and store a
* copy in the local database.
* *
* @author Jeremy Long * @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 * Executes the update by initializing the settings, downloads the NVD XML
* local database. * 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 @Override
public void execute() throws BuildException { public void execute() throws BuildException {
@@ -392,9 +396,20 @@ public class Update extends Purge {
Engine engine = null; Engine engine = null;
try { try {
engine = new Engine(Update.class.getClassLoader()); 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) { } 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 { } finally {
Settings.cleanup(true); Settings.cleanup(true);
if (engine != null) { 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 * Takes the properties supplied and updates the dependency-check settings.
* required to change the proxy server, port, and connection timeout. * 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. * @throws BuildException thrown when an invalid setting is configured.
*/ */

View File

@@ -14,6 +14,7 @@ Configuration: dependency-check-purge Task
-------------------- --------------------
The following properties can be set on the dependency-check-purge task. The following properties can be set on the dependency-check-purge task.
Property | Description | Default Value Property | Description | Default Value
----------------------|----------------------------------------------------------------|------------------ ----------------------|------------------------------------------------------------------------|------------------
dataDirectory | Data directory that is used to store the local copy of the NVD | data 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

View File

@@ -24,6 +24,7 @@ proxyPort | The Proxy Port. | &nbsp;
proxyUsername | Defines the proxy user name. | &nbsp; proxyUsername | Defines the proxy user name. | &nbsp;
proxyPassword | Defines the proxy password. | &nbsp; proxyPassword | Defines the proxy password. | &nbsp;
connectionTimeout | The URL Connection Timeout. | &nbsp; connectionTimeout | The URL Connection Timeout. | &nbsp;
failOnError | Whether the build should fail if there is an error executing the update | true
Advanced Configuration Advanced Configuration
==================== ====================

View File

@@ -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 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 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 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 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 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' 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'

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.4.1-SNAPSHOT</version> <version>1.4.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>dependency-check-cli</artifactId> <artifactId>dependency-check-cli</artifactId>

View File

@@ -37,6 +37,10 @@ import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ch.qos.logback.core.FileAppender; 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; import org.slf4j.impl.StaticLoggerBinder;
/** /**
@@ -57,21 +61,26 @@ public class App {
* @param args the command line arguments * @param args the command line arguments
*/ */
public static void main(String[] args) { public static void main(String[] args) {
int exitCode = 0;
try { try {
Settings.initialize(); Settings.initialize();
final App app = new App(); final App app = new App();
app.run(args); exitCode = app.run(args);
LOGGER.debug("Exit code: " + exitCode);
} finally { } finally {
Settings.cleanup(true); Settings.cleanup(true);
} }
System.exit(exitCode);
} }
/** /**
* Main CLI entry-point into the application. * Main CLI entry-point into the application.
* *
* @param args the command line arguments * @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(); final CliParser cli = new CliParser();
try { try {
@@ -79,11 +88,11 @@ public class App {
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
System.err.println(ex.getMessage()); System.err.println(ex.getMessage());
cli.printHelp(); cli.printHelp();
return; return -1;
} catch (ParseException ex) { } catch (ParseException ex) {
System.err.println(ex.getMessage()); System.err.println(ex.getMessage());
cli.printHelp(); cli.printHelp();
return; return -2;
} }
if (cli.getVerboseLog() != null) { if (cli.getVerboseLog() != null) {
@@ -93,8 +102,15 @@ public class App {
if (cli.isPurge()) { if (cli.isPurge()) {
if (cli.getConnectionString() != null) { if (cli.getConnectionString() != null) {
LOGGER.error("Unable to purge the database when using a non-default connection string"); LOGGER.error("Unable to purge the database when using a non-default connection string");
exitCode = -3;
} else { } else {
populateSettings(cli); try {
populateSettings(cli);
} catch (InvalidSettingException ex) {
LOGGER.error(ex.getMessage());
LOGGER.debug("Error loading properties file", ex);
exitCode = -4;
}
File db; File db;
try { try {
db = new File(Settings.getDataDirectory(), "dc.h2.db"); 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"); LOGGER.info("Database file purged; local copy of the NVD has been removed");
} else { } else {
LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath()); LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath());
exitCode = -5;
} }
} else { } else {
LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath()); LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath());
exitCode = -6;
} }
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.error("Unable to delete the database"); LOGGER.error("Unable to delete the database");
exitCode = -7;
} }
} }
} else if (cli.isGetVersion()) { } else if (cli.isGetVersion()) {
cli.printVersionInfo(); cli.printVersionInfo();
} else if (cli.isUpdateOnly()) { } else if (cli.isUpdateOnly()) {
populateSettings(cli); try {
runUpdateOnly(); 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()) { } 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 { try {
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), cli.getScanFiles(), runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), cli.getScanFiles(),
cli.getExcludeList(), cli.getSymLinkDepth()); cli.getExcludeList(), cli.getSymLinkDepth());
} catch (InvalidScanPathException ex) { } catch (InvalidScanPathException ex) {
LOGGER.error("An invalid scan path was detected; unable to scan '//*' paths"); 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 { } else {
cli.printHelp(); 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 outputFormat the output format of the report
* @param applicationName the application name for the report * @param applicationName the application name for the report
* @param files the files/directories to scan * @param files the files/directories to scan
* @param excludes the patterns for files/directories to exclude * @param excludes the patterns for files/directories to exclude
* @param symLinkDepth the depth that symbolic links will be followed * @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, 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; Engine engine = null;
try { try {
engine = new Engine(); engine = new Engine();
@@ -174,8 +240,6 @@ public class App {
include = "**/*"; include = "**/*";
} }
} }
//LOGGER.debug("baseDir: {}", baseDir);
//LOGGER.debug("include: {}", include);
scanner.setBasedir(baseDir); scanner.setBasedir(baseDir);
final String[] includes = {include}; final String[] includes = {include};
scanner.setIncludes(includes); scanner.setIncludes(includes);
@@ -197,7 +261,15 @@ public class App {
} }
engine.scan(paths); engine.scan(paths);
engine.analyzeDependencies(); ExceptionCollection exCol = null;
try {
engine.analyzeDependencies();
} catch (ExceptionCollection ex) {
if (ex.isFatal()) {
throw ex;
}
exCol = ex;
}
final List<Dependency> dependencies = engine.getDependencies(); final List<Dependency> dependencies = engine.getDependencies();
DatabaseProperties prop = null; DatabaseProperties prop = null;
CveDB cve = null; CveDB cve = null;
@@ -205,8 +277,6 @@ public class App {
cve = new CveDB(); cve = new CveDB();
cve.open(); cve.open();
prop = cve.getDatabaseProperties(); prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
LOGGER.debug("Unable to retrieve DB Properties", ex);
} finally { } finally {
if (cve != null) { if (cve != null) {
cve.close(); cve.close();
@@ -215,34 +285,37 @@ public class App {
final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop); final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
try { try {
report.generateReports(reportDirectory, outputFormat); report.generateReports(reportDirectory, outputFormat);
} catch (IOException ex) { } catch (ReportException ex) {
LOGGER.error("There was an IO error while attempting to generate the report."); if (exCol != null) {
LOGGER.debug("", ex); exCol.addException(ex);
} catch (Throwable ex) { throw exCol;
LOGGER.error("There was an error while attempting to generate the report."); } else {
LOGGER.debug("", ex); 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 { } finally {
if (engine != null) { if (engine != null) {
engine.cleanup(); engine.cleanup();
} }
} }
} }
/** /**
* Only executes the update phase of dependency-check. * 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; Engine engine = null;
try { try {
engine = new Engine(); engine = new Engine();
engine.doUpdates(); engine.doUpdates();
} catch (DatabaseException ex) {
LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
LOGGER.debug("", ex);
} finally { } finally {
if (engine != null) { if (engine != null) {
engine.cleanup(); engine.cleanup();
@@ -253,11 +326,13 @@ public class App {
/** /**
* Updates the global Settings. * 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 * @param cli a reference to the CLI Parser that contains the command line
* the core engine. * 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 boolean autoUpdate = cli.isAutoUpdate();
final String connectionTimeout = cli.getConnectionTimeout(); final String connectionTimeout = cli.getConnectionTimeout();
final String proxyServer = cli.getProxyServer(); final String proxyServer = cli.getProxyServer();
@@ -286,11 +361,9 @@ public class App {
try { try {
Settings.mergeProperties(propertiesFile); Settings.mergeProperties(propertiesFile);
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
LOGGER.error("Unable to load properties file '{}'", propertiesFile.getPath()); throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex);
LOGGER.debug("", ex);
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.error("Unable to find properties file '{}'", propertiesFile.getPath()); throw new InvalidSettingException("Error reading properties file '" + propertiesFile.getPath() + "'", ex);
LOGGER.debug("", ex);
} }
} }
// We have to wait until we've merged the properties before attempting to set whether we use // 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 &amp; absolute path. The caveats are that this method will take an Ant style * Takes a path and resolves it to be a canonical &amp; absolute path. The
* file selector path (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at least to the left of the first * * caveats are that this method will take an Ant style file selector path
* or ?). * (../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 * @param path the path to canonicalize
* @return the canonical path * @return the canonical path
*/ */
protected String ensureCanonicalPath(String path) { protected String ensureCanonicalPath(String path) {
String basePath = null; String basePath;
String wildCards = null; String wildCards = null;
final String file = path.replace('\\', '/'); final String file = path.replace('\\', '/');
if (file.contains("*") || file.contains("?")) { if (file.contains("*") || file.contains("?")) {

View File

@@ -966,7 +966,7 @@ public final class CliParser {
*/ */
public void printVersionInfo() { public void printVersionInfo() {
final String version = String.format("%s version %s", 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")); Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown"));
System.out.println(version); System.out.println(version);
} }

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.4.1-SNAPSHOT</version> <version>1.4.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>dependency-check-core</artifactId> <artifactId>dependency-check-core</artifactId>

View File

@@ -30,6 +30,8 @@ import org.owasp.dependencycheck.data.update.UpdateService;
import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.exception.NoDataException; 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.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -47,8 +49,10 @@ import java.util.Map;
import java.util.Set; 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 * Scans files, directories, etc. for Dependencies. Analyzers are loaded and
* file is encountered and an Analyzer is associated with the file type then the file is turned into a dependency. * 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 * @author Jeremy Long
*/ */
@@ -69,7 +73,8 @@ public class Engine implements FileFilter {
private final Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<FileTypeAnalyzer>(); private final Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<FileTypeAnalyzer>();
/** /**
* 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(); private ClassLoader serviceClassLoader = Thread.currentThread().getContextClassLoader();
/** /**
@@ -80,7 +85,8 @@ public class Engine implements FileFilter {
/** /**
* Creates a new Engine. * 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 { public Engine() throws DatabaseException {
initializeEngine(); initializeEngine();
@@ -90,7 +96,8 @@ public class Engine implements FileFilter {
* Creates a new Engine. * Creates a new Engine.
* *
* @param serviceClassLoader a reference the class loader being used * @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 { public Engine(ClassLoader serviceClassLoader) throws DatabaseException {
this.serviceClassLoader = serviceClassLoader; 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 { protected final void initializeEngine() throws DatabaseException {
ConnectionFactory.initialize(); 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() { private void loadAnalyzers() {
if (!analyzers.isEmpty()) { 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 * Scans an array of files or directories. If a directory is specified, it
* identified are added to the dependency collection. * 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 * @param paths an array of paths to files or directories to be analyzed
* @return the list of dependencies scanned * @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 * Scans a given file or directory. If a directory is specified, it will be
* are added to the dependency collection. * scanned recursively. Any dependencies identified are added to the
* dependency collection.
* *
* @param path the path to a file or directory to be analyzed * @param path the path to a file or directory to be analyzed
* @return the list of dependencies scanned * @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 * Scans an array of files or directories. If a directory is specified, it
* identified are added to the dependency collection. * 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. * @param files an array of paths to files or directories to be analyzed.
* @return the list of dependencies * @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 * Scans a collection of files or directories. If a directory is specified,
* identified are added to the dependency collection. * 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 * @param files a set of paths to files or directories to be analyzed
* @return the list of dependencies scanned * @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 * Scans a given file or directory. If a directory is specified, it will be
* are added to the dependency collection. * scanned recursively. Any dependencies identified are added to the
* dependency collection.
* *
* @param file the path to a file or directory to be analyzed * @param file the path to a file or directory to be analyzed
* @return the list of dependencies scanned * @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 * @param dir the directory to scan
* @return the list of Dependency objects scanned * @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 * @param file The file to scan
* @return the scanned dependency * @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 * Runs the analyzers against all of the dependencies. Since the mutable
* {@link #getDependencies()}, this method iterates over a copy of the dependencies list. Thus, the potential for * dependencies list is exposed via {@link #getDependencies()}, this method
* {@link java.util.ConcurrentModificationException}s is avoided, and analyzers may safely add or remove entries from the * iterates over a copy of the dependencies list. Thus, the potential for
* dependencies list. * {@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<Throwable> exceptions = new ArrayList<Throwable>();
boolean autoUpdate = true; boolean autoUpdate = true;
try { try {
autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
} catch (InvalidSettingException ex) { } catch (InvalidSettingException ex) {
LOGGER.debug("Invalid setting for auto-update; using true."); LOGGER.debug("Invalid setting for auto-update; using true.");
exceptions.add(ex);
} }
if (autoUpdate) { 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 //need to ensure that data exists
@@ -323,12 +358,13 @@ public class Engine implements FileFilter {
} catch (NoDataException ex) { } catch (NoDataException ex) {
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage()); LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
LOGGER.debug("", ex); LOGGER.debug("", ex);
return; exceptions.add(ex);
throw new ExceptionCollection("Unable to continue dependency-check analysis.", exceptions, true);
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage()); LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
LOGGER.debug("", ex); 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----------------------------------------------------"); LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------");
@@ -340,7 +376,12 @@ public class Engine implements FileFilter {
final List<Analyzer> analyzerList = analyzers.get(phase); final List<Analyzer> analyzerList = analyzers.get(phase);
for (Analyzer a : analyzerList) { 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 /* need to create a copy of the collection because some of the
* analyzers may modify it. This prevents ConcurrentModificationExceptions. * analyzers may modify it. This prevents ConcurrentModificationExceptions.
@@ -361,10 +402,12 @@ public class Engine implements FileFilter {
} catch (AnalysisException ex) { } catch (AnalysisException ex) {
LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath()); LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath());
LOGGER.debug("", ex); LOGGER.debug("", ex);
exceptions.add(ex);
} catch (Throwable ex) { } catch (Throwable ex) {
//final AnalysisException ax = new AnalysisException(axMsg, ex); //final AnalysisException ax = new AnalysisException(axMsg, ex);
LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath()); LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath());
LOGGER.debug("", ex); LOGGER.debug("", ex);
exceptions.add(ex);
} }
} }
} }
@@ -380,6 +423,9 @@ public class Engine implements FileFilter {
LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------"); LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------");
LOGGER.info("Analysis Complete ({} ms)", System.currentTimeMillis() - analysisStart); 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 * @param analyzer the analyzer to initialize
* @return the initialized analyzer * @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 { try {
LOGGER.debug("Initializing {}", analyzer.getName()); LOGGER.debug("Initializing {}", analyzer.getName());
analyzer.initialize(); analyzer.initialize();
} catch (Throwable ex) { } catch (InitializationException ex) {
LOGGER.error("Exception occurred initializing {}.", analyzer.getName()); LOGGER.error("Exception occurred initializing {}.", analyzer.getName());
LOGGER.debug("", ex); LOGGER.debug("", ex);
try { try {
@@ -400,6 +448,16 @@ public class Engine implements FileFilter {
} catch (Throwable ex1) { } catch (Throwable ex1) {
LOGGER.trace("", 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; 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"); LOGGER.info("Checking for updates");
final long updateStart = System.currentTimeMillis(); final long updateStart = System.currentTimeMillis();
final UpdateService service = new UpdateService(serviceClassLoader); final UpdateService service = new UpdateService(serviceClassLoader);
final Iterator<CachedWebDataSource> iterator = service.getDataSources(); final Iterator<CachedWebDataSource> iterator = service.getDataSources();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final CachedWebDataSource source = iterator.next(); final CachedWebDataSource source = iterator.next();
try { source.update();
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);
}
} }
LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart); 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 * @return a list of Analyzers
*/ */
@@ -457,7 +513,8 @@ public class Engine implements FileFilter {
* Checks all analyzers to see if an extension is supported. * Checks all analyzers to see if an extension is supported.
* *
* @param file a file extension * @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 @Override
public boolean accept(File file) { 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 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 { private void ensureDataExists() throws NoDataException, DatabaseException {
final CveDB cve = new CveDB(); final CveDB cve = new CveDB();

View File

@@ -27,6 +27,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.ScanAgentException; import org.owasp.dependencycheck.exception.ScanAgentException;
import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.reporting.ReportGenerator;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
@@ -34,10 +35,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* This class provides a way to easily conduct a scan solely based on existing evidence metadata rather than collecting evidence * This class provides a way to easily conduct a scan solely based on existing
* from the files themselves. This class is based on the Ant task and Maven plugin with the exception that it takes a list of * evidence metadata rather than collecting evidence from the files themselves.
* dependencies that can be programmatically added from data in a spreadsheet, database or some other datasource and conduct a * This class is based on the Ant task and Maven plugin with the exception that
* scan based on this pre-defined evidence. * 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.
* *
* <h2>Example:</h2> * <h2>Example:</h2>
* <pre> * <pre>
@@ -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; 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 * Specifies if the build should be failed if a CVSS score above a specified
* 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 * level is identified. The default is 11 which means since the CVSS scores
* for the fail build on CVSS is 0 to 11, where anything above 10 will not cause the build to fail. * 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; 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 * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not
* is true. * recommended that this be turned to false. Default is true.
*/ */
private boolean autoUpdate = 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 * The report format to be generated (HTML, XML, VULN, ALL). This
* Site plugin unless the externalReport is set to true. Default is HTML. * 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; private ReportGenerator.Format reportFormat = ReportGenerator.Format.HTML;
@@ -283,7 +290,9 @@ public class DependencyCheckScanAgent {
* Get the value of proxyServer. * Get the value of proxyServer.
* *
* @return 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 @Deprecated
public String getProxyUrl() { 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 * Additional ZIP File extensions to add analyze. This should be a
* files. * comma-separated list of file extensions to treat like ZIP files.
*/ */
private String zipExtensions; private String zipExtensions;
@@ -836,11 +845,17 @@ public class DependencyCheckScanAgent {
* Executes the Dependency-Check on the dependent libraries. * Executes the Dependency-Check on the dependent libraries.
* *
* @return the Engine used to scan the dependencies. * @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(); 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.setDependencies(this.dependencies);
engine.analyzeDependencies(); engine.analyzeDependencies();
return engine; 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 * Takes the properties supplied and updates the dependency-check settings.
* required to change the proxy server, port, and connection timeout. * Additionally, this sets the system properties required to change the
* proxy server, port, and connection timeout.
*/ */
private void populateSettings() { private void populateSettings() {
Settings.initialize(); Settings.initialize();
@@ -925,7 +941,8 @@ public class DependencyCheckScanAgent {
* Executes the dependency-check and generates the report. * Executes the dependency-check and generates the report.
* *
* @return a reference to the engine used to perform the scan. * @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 { public Engine execute() throws ScanAgentException {
Engine engine = null; Engine engine = null;
@@ -940,10 +957,12 @@ public class DependencyCheckScanAgent {
if (this.failBuildOnCVSS <= 10) { if (this.failBuildOnCVSS <= 10) {
checkForFailure(engine.getDependencies()); checkForFailure(engine.getDependencies());
} }
} catch (DatabaseException ex) { } catch (ExceptionCollection ex) {
LOGGER.error( if (ex.isFatal()) {
"Unable to connect to the dependency-check database; analysis has stopped"); LOGGER.error("A fatal exception occurred during analysis; analysis has stopped. Please see the debug log for more details.");
LOGGER.debug("", ex); LOGGER.debug("", ex);
}
throw new ScanAgentException("One or more exceptions occurred during analysis; please see the debug log for more details.", ex);
} finally { } finally {
Settings.cleanup(true); Settings.cleanup(true);
if (engine != null) { 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 * Checks to see if a vulnerability has been identified with a CVSS score
* configuration. * that is above the threshold set in the configuration.
* *
* @param dependencies the list of dependency objects * @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<Dependency> dependencies) throws ScanAgentException { private void checkForFailure(List<Dependency> dependencies) throws ScanAgentException {
final StringBuilder ids = new StringBuilder(); 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 * @param dependencies a list of dependency objects
*/ */

View File

@@ -367,7 +367,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase(); final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
try { try {
if (ZIPPABLES.contains(archiveExt)) { if (ZIPPABLES.contains(archiveExt)) {
BufferedInputStream in = new BufferedInputStream(fis); final BufferedInputStream in = new BufferedInputStream(fis);
ensureReadableJar(archiveExt, in); ensureReadableJar(archiveExt, in);
extractArchive(new ZipArchiveInputStream(in), destination, engine); extractArchive(new ZipArchiveInputStream(in), destination, engine);
} else if ("tar".equals(archiveExt)) { } else if ("tar".equals(archiveExt)) {
@@ -413,7 +413,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
private void ensureReadableJar(final String archiveExt, BufferedInputStream in) throws IOException { private void ensureReadableJar(final String archiveExt, BufferedInputStream in) throws IOException {
if ("jar".equals(archiveExt) && in.markSupported()) { if ("jar".equals(archiveExt) && in.markSupported()) {
in.mark(7); in.mark(7);
byte[] b = new byte[7]; final byte[] b = new byte[7];
in.read(b); in.read(b);
if (b[0] == '#' if (b[0] == '#'
&& b[1] == '!' && b[1] == '!'
@@ -441,6 +441,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
} }
} }
} }
} else {
in.reset();
} }
} }
} }

View File

@@ -574,15 +574,14 @@ public class CPEAnalyzer implements Analyzer {
final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getName(), "UTF-8")); 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); final IdentifierMatch match = new IdentifierMatch("cpe", vs.getName(), url, IdentifierConfidence.EXACT_MATCH, conf);
collected.add(match); collected.add(match);
} else //TODO the following isn't quite right is it? need to think about this guessing game a bit more.
{ //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() } else if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size()
&& evVer.matchesAtLeastThreeLevels(dbVer)) { && evVer.matchesAtLeastThreeLevels(dbVer)) {
if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) { if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) { if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) {
bestGuess = dbVer; bestGuess = dbVer;
bestGuessConf = conf; bestGuessConf = conf;
}
} }
} }
} }

View File

@@ -93,26 +93,27 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
//add version evidence //add version evidence
final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName); final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName);
final String packageName = DependencyVersionUtil.parsePreVersion(fileName);
if (version != null) { if (version != null) {
// If the version number is just a number like 2 or 23, reduce the confidence // 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 // a shade. This should hopefully correct for cases like log4j.jar or
// struts2-core.jar // struts2-core.jar
if (version.getVersionParts() == null || version.getVersionParts().size() < 2) { if (version.getVersionParts() == null || version.getVersionParts().size() < 2) {
dependency.getVersionEvidence().addEvidence("file", "name", dependency.getVersionEvidence().addEvidence("file", "version",
version.toString(), Confidence.MEDIUM); version.toString(), Confidence.MEDIUM);
} else { } else {
dependency.getVersionEvidence().addEvidence("file", "version", dependency.getVersionEvidence().addEvidence("file", "version",
version.toString(), Confidence.HIGHEST); version.toString(), Confidence.HIGHEST);
} }
dependency.getVersionEvidence().addEvidence("file", "name", dependency.getVersionEvidence().addEvidence("file", "name",
fileName, Confidence.MEDIUM); packageName, Confidence.MEDIUM);
} }
if (!IGNORED_FILES.accept(f)) { if (!IGNORED_FILES.accept(f)) {
dependency.getProductEvidence().addEvidence("file", "name", dependency.getProductEvidence().addEvidence("file", "name",
fileName, Confidence.HIGH); packageName, Confidence.HIGH);
dependency.getVendorEvidence().addEvidence("file", "name", dependency.getVendorEvidence().addEvidence("file", "name",
fileName, Confidence.HIGH); packageName, Confidence.HIGH);
} }
} }
} }

View File

@@ -32,7 +32,6 @@ import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Evidence; import org.owasp.dependencycheck.dependency.Evidence;
import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.exception.InitializationException;
import org.owasp.dependencycheck.xml.suppression.PropertyType; 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.DownloadFailedException;
import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.Downloader;
import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.FileUtils;
@@ -279,7 +278,7 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
/** /**
* Loads the hint rules file. * 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 { private void loadHintRules() throws HintParseException {
final HintParser parser = new HintParser(); 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.error("Unable to parse the base hint data file");
LOGGER.debug("Unable to parse the base hint data file", ex); 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) { if (filePath == null) {
return; return;
} }
@@ -327,7 +326,7 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
if (file != null) { if (file != null) {
try { try {
Hints newHints = parser.parseHints(file); final Hints newHints = parser.parseHints(file);
hints.getHintRules().addAll(newHints.getHintRules()); hints.getHintRules().addAll(newHints.getHintRules());
hints.getVendorDuplicatingHintRules().addAll(newHints.getVendorDuplicatingHintRules()); hints.getVendorDuplicatingHintRules().addAll(newHints.getVendorDuplicatingHintRules());
LOGGER.debug("{} hint rules were loaded.", hints.getHintRules().size()); LOGGER.debug("{} hint rules were loaded.", hints.getHintRules().size());

View File

@@ -39,7 +39,6 @@ import java.util.jar.Attributes;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.compress.utils.IOUtils;
@@ -646,9 +645,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
* @return whether evidence was identified parsing the manifest * @return whether evidence was identified parsing the manifest
* @throws IOException if there is an issue reading the JAR file * @throws IOException if there is an issue reading the JAR file
*/ */
protected boolean parseManifest(Dependency dependency, protected boolean parseManifest(Dependency dependency, List<ClassNameInformation> classInformation) throws IOException {
List<ClassNameInformation> classInformation)
throws IOException {
boolean foundSomething = false; boolean foundSomething = false;
JarFile jar = null; JarFile jar = null;
try { try {
@@ -667,7 +664,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
final EvidenceCollection vendorEvidence = dependency.getVendorEvidence(); final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
final EvidenceCollection productEvidence = dependency.getProductEvidence(); final EvidenceCollection productEvidence = dependency.getProductEvidence();
final EvidenceCollection versionEvidence = dependency.getVersionEvidence(); final EvidenceCollection versionEvidence = dependency.getVersionEvidence();
String source = "Manifest"; String source = "Manifest";
String specificationVersion = null; String specificationVersion = null;
boolean hasImplementationVersion = false; boolean hasImplementationVersion = false;
@@ -689,7 +685,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
foundSomething = true; foundSomething = true;
versionEvidence.addEvidence(source, key, value, Confidence.HIGH); versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
} else if ("specification-version".equalsIgnoreCase(key)) { } else if ("specification-version".equalsIgnoreCase(key)) {
specificationVersion = key; specificationVersion = value;
} else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) { } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) {
foundSomething = true; foundSomething = true;
vendorEvidence.addEvidence(source, key, value, Confidence.HIGH); vendorEvidence.addEvidence(source, key, value, Confidence.HIGH);
@@ -784,7 +780,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
} }
} }
} }
for (Map.Entry<String, Attributes> item : manifest.getEntries().entrySet()) { for (Map.Entry<String, Attributes> item : manifest.getEntries().entrySet()) {
final String name = item.getKey(); final String name = item.getKey();
source = "manifest: " + name; source = "manifest: " + name;

View File

@@ -53,6 +53,9 @@ import org.slf4j.LoggerFactory;
@Experimental @Experimental
public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class); private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class);
/** /**
@@ -151,7 +154,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
setEnabled(false); setEnabled(false);
cvedb.close(); cvedb.close();
cvedb = null; 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); throw new InitializationException(msg, ae);
} catch (IOException ex) { } catch (IOException ex) {
setEnabled(false); setEnabled(false);
@@ -163,12 +166,12 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
exitValue = process.waitFor(); exitValue = process.waitFor();
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
setEnabled(false); 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); throw new InitializationException(msg);
} }
if (0 == exitValue) { if (0 == exitValue) {
setEnabled(false); 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); throw new InitializationException(msg);
} else { } else {
BufferedReader reader = null; BufferedReader reader = null;

View File

@@ -38,7 +38,6 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory; 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.LuceneUtils;
import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer; import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer;
import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.CveDB;
@@ -48,8 +47,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* An in memory lucene index that contains the vendor/product combinations from the CPE (application) identifiers within the NVD * An in memory lucene index that contains the vendor/product combinations from
* CVE data. * the CPE (application) identifiers within the NVD CVE data.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -144,19 +143,6 @@ public final class CpeMemoryIndex {
return openState; 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<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
}
/** /**
* Creates an Analyzer for searching the CPE Index. * 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 * @param maxQueryResults the maximum number of documents to return
* @return the TopDocs found by the search * @return the TopDocs found by the search
* @throws ParseException thrown when the searchString is invalid * @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 { public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException {
if (searchString == null || searchString.trim().isEmpty()) { if (searchString == null || searchString.trim().isEmpty()) {

View File

@@ -34,7 +34,7 @@ import org.apache.lucene.util.Version;
* index the CPE fields vendor and product.</p> * index the CPE fields vendor and product.</p>
* *
* @author Jeremy Long * @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. * SearchFieldAnalyzer so that the token analyzing filter is used.
*/ */
@Deprecated @Deprecated

View File

@@ -68,10 +68,9 @@ public class CveDB {
private ResourceBundle statementBundle = null; private ResourceBundle statementBundle = null;
/** /**
* Creates a new CveDB object and opens the database * Creates a new CveDB object and opens the database connection. Note, the
* connection. Note, the connection must be closed by the caller by calling * connection must be closed by the caller by calling the close method.
* the close method. ======= Does the underlying connection support batch * ======= Does the underlying connection support batch operations?
* operations?
*/ */
private boolean batchSupported; private boolean batchSupported;
@@ -659,7 +658,7 @@ public class CveDB {
+ "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please " + "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 " + "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", + "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); LOGGER.debug("", ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);

View File

@@ -43,9 +43,10 @@ public class CPEHandler extends DefaultHandler {
/** /**
* The Starts with expression to filter CVE entries by CPE. * 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; private StringBuilder nodeText = null;
/** /**
@@ -77,7 +78,8 @@ public class CPEHandler extends DefaultHandler {
* @param localName the local name * @param localName the local name
* @param qName the qualified name * @param qName the qualified name
* @param attributes the attributes * @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 @Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 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 ch the char array
* @param start the start position of the data read * @param start the start position of the data read
* @param length the length 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 @Override
public void characters(char[] ch, int start, int length) throws SAXException { 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 uri the element's uri
* @param localName the local name * @param localName the local name
* @param qName the qualified 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 @Override
public void endElement(String uri, String localName, String qName) throws SAXException { public void endElement(String uri, String localName, String qName) throws SAXException {
@@ -182,7 +187,8 @@ public class CPEHandler extends DefaultHandler {
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node"> // <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
/** /**
* 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 { protected static final class Element {

View File

@@ -36,6 +36,9 @@ import org.slf4j.LoggerFactory;
*/ */
public class UpdateableNvdCve implements Iterable<NvdCveInfo>, Iterator<NvdCveInfo> { public class UpdateableNvdCve implements Iterable<NvdCveInfo>, Iterator<NvdCveInfo> {
/**
* A reference to the logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(UpdateableNvdCve.class); private static final Logger LOGGER = LoggerFactory.getLogger(UpdateableNvdCve.class);
/** /**
* A collection of sources of data. * A collection of sources of data.

View File

@@ -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<Throwable> 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<Throwable> 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<Throwable> 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<Throwable> 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<Throwable>();
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<Throwable>();
this.exceptions.add(exception);
this.fatal = false;
}
/**
* Instantiates a new exception collection.
*/
public ExceptionCollection() {
super();
this.exceptions = new ArrayList<Throwable>();
}
/**
* The serial version uid.
*/
private static final long serialVersionUID = 1L;
/**
* A collection of exceptions.
*/
private List<Throwable> exceptions;
/**
* Get the value of exceptions.
*
* @return the value of exceptions
*/
public List<Throwable> 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);
}
}

View File

@@ -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);
}
}

View File

@@ -26,6 +26,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
@@ -37,13 +38,16 @@ import org.apache.velocity.runtime.RuntimeConstants;
import org.owasp.dependencycheck.analyzer.Analyzer; import org.owasp.dependencycheck.analyzer.Analyzer;
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* The ReportGenerator is used to, as the name implies, generate reports. Internally the generator uses the Velocity * The ReportGenerator is used to, as the name implies, generate reports.
* Templating Engine. The ReportGenerator exposes a list of Dependencies to the template when generating the report. * 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 * @author Jeremy Long
*/ */
@@ -79,7 +83,7 @@ public class ReportGenerator {
/** /**
* The Velocity Engine. * The Velocity Engine.
*/ */
private final VelocityEngine engine; private final VelocityEngine velocityEngine;
/** /**
* The Velocity Engine Context. * The Velocity Engine Context.
*/ */
@@ -91,13 +95,14 @@ public class ReportGenerator {
* @param applicationName the application name being analyzed * @param applicationName the application name being analyzed
* @param dependencies the list of dependencies * @param dependencies the list of dependencies
* @param analyzers the list of analyzers used * @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<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) { public ReportGenerator(String applicationName, List<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) {
engine = createVelocityEngine(); velocityEngine = createVelocityEngine();
context = createContext(); context = createContext();
engine.init(); velocityEngine.init();
final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy 'at' HH:mm:ss z"); 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"); 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. * Creates a new Velocity Engine.
* *
* @return a velocity engine. * @return a velocity engine
*/ */
private VelocityEngine createVelocityEngine() { private VelocityEngine createVelocityEngine() {
final VelocityEngine engine = new VelocityEngine(); final VelocityEngine velocity = new VelocityEngine();
// Logging redirection for Velocity - Required by Jenkins and other server applications // Logging redirection for Velocity - Required by Jenkins and other server applications
engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName()); velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName());
return engine; return velocity;
} }
/** /**
* Creates a new Velocity Context. * Creates a new Velocity Context.
* *
* @return a Velocity Context. * @return a Velocity Context
*/ */
private Context createContext() { private Context createContext() {
return new VelocityContext(); return new VelocityContext();
@@ -143,7 +148,7 @@ public class ReportGenerator {
* @param outputStream the OutputStream to send the generated report to * @param outputStream the OutputStream to send the generated report to
* @param format the format the report should be written in * @param format the format the report should be written in
* @throws IOException is thrown when the template file does not exist * @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 { public void generateReports(OutputStream outputStream, Format format) throws IOException, Exception {
if (format == Format.XML || format == Format.ALL) { 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 outputDir the path where the reports should be written
* @param format the format the report should be written in * @param format the format the report should be written in
* @throws IOException is thrown when the template file does not exist * @throws ReportException 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(String outputDir, Format format) throws IOException, Exception { public void generateReports(String outputDir, Format format) throws ReportException {
if (format == Format.XML || format == Format.ALL) { if (format == Format.XML || format == Format.ALL) {
generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml"); generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml");
} }
@@ -181,11 +185,12 @@ public class ReportGenerator {
* Generates the Dependency Reports for the identified dependencies. * Generates the Dependency Reports for the identified dependencies.
* *
* @param outputDir the path where the reports should be written * @param outputDir the path where the reports should be written
* @param outputFormat the format the report should be written in (XML, HTML, ALL) * @param outputFormat the format the report should be written in (XML,
* @throws IOException is thrown when the template file does not exist * HTML, ALL)
* @throws Exception is thrown if there is an error writing out the reports. * @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 format = outputFormat.toUpperCase();
final String pathToCheck = outputDir.toLowerCase(); final String pathToCheck = outputDir.toLowerCase();
if (format.matches("^(XML|HTML|VULN|ALL)$")) { 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 * Generates a report from a given Velocity Template. The template name
* contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a * 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. * template file.
* *
* @param templateName the name of the template to load. * @param templateName the name of the template to load
* @param outputStream the OutputStream to write the report to. * @param outputStream the OutputStream to write the report to
* @throws IOException is thrown when the template file does not exist. * @throws ReportException is thrown when an exception occurs
* @throws Exception 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; InputStream input = null;
String templatePath = null; String templatePath = null;
final File f = new File(templateName); final File f = new File(templateName);
@@ -235,27 +240,30 @@ public class ReportGenerator {
templatePath = templateName; templatePath = templateName;
input = new FileInputStream(f); input = new FileInputStream(f);
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
LOGGER.error("Unable to generate the report, the report template file could not be found."); throw new ReportException("Unable to locate template file: " + templateName, ex);
LOGGER.debug("", ex);
} }
} else { } else {
templatePath = "templates/" + templateName + ".vsl"; templatePath = "templates/" + templateName + ".vsl";
input = this.getClass().getClassLoader().getResourceAsStream(templatePath); input = this.getClass().getClassLoader().getResourceAsStream(templatePath);
} }
if (input == null) { 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; OutputStreamWriter writer = null;
try { try {
reader = new InputStreamReader(input, "UTF-8");
writer = new OutputStreamWriter(outputStream, "UTF-8"); writer = new OutputStreamWriter(outputStream, "UTF-8");
if (!velocityEngine.evaluate(context, writer, templatePath, reader)) {
if (!engine.evaluate(context, writer, templatePath, reader)) { throw new ReportException("Failed to convert the template into html.");
throw new Exception("Failed to convert the template into html.");
} }
writer.flush(); 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 { } finally {
if (writer != null) { if (writer != null) {
try { try {
@@ -271,25 +279,27 @@ public class ReportGenerator {
LOGGER.trace("", ex); LOGGER.trace("", ex);
} }
} }
try { if (reader != null) {
reader.close(); try {
} catch (IOException ex) { reader.close();
LOGGER.trace("", ex); } 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 * Generates a report from a given Velocity Template. The template name
* contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a * 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. * template file.
* *
* @param templateName the name of the template to load. * @param templateName the name of the template to load
* @param outFileName the filename and path to write the report to. * @param outFileName the filename and path to write the report to
* @throws IOException is thrown when the template file does not exist. * @throws ReportException is thrown when the report cannot be generated
* @throws Exception is thrown when an exception occurs.
*/ */
protected void generateReport(String templateName, String outFileName) throws Exception { protected void generateReport(String templateName, String outFileName) throws ReportException {
File outFile = new File(outFileName); File outFile = new File(outFileName);
if (outFile.getParentFile() == null) { if (outFile.getParentFile() == null) {
outFile = new File(".", outFileName); outFile = new File(".", outFileName);
@@ -297,7 +307,7 @@ public class ReportGenerator {
if (!outFile.getParentFile().exists()) { if (!outFile.getParentFile().exists()) {
final boolean created = outFile.getParentFile().mkdirs(); final boolean created = outFile.getParentFile().mkdirs();
if (!created) { 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 { try {
outputSteam = new FileOutputStream(outFile); outputSteam = new FileOutputStream(outFile);
generateReport(templateName, outputSteam); generateReport(templateName, outputSteam);
} catch (FileNotFoundException ex) {
throw new ReportException("Unable to write to file: " + outFile, ex);
} finally { } finally {
if (outputSteam != null) { if (outputSteam != null) {
try { try {

View File

@@ -40,6 +40,11 @@ public final class DependencyVersionUtil {
*/ */
private static final Pattern RX_SINGLE_VERSION = Pattern.compile("\\d+(\\.?([_-](release|beta|alpha)|[a-zA-Z_-]{1,3}\\d{1,8}))?"); 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. * Private constructor for utility class.
*/ */
@@ -95,4 +100,27 @@ public final class DependencyVersionUtil {
} }
return new DependencyVersion(version); return new DependencyVersion(version);
} }
/**
* <p>
* 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.</p>
* <pre>
* Example:
* Give the file name: library-name-1.4.1r2-release.jar
* This function would return: library-name</pre>
*
* @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;
}
} }

View File

@@ -201,7 +201,7 @@ public class HintHandler extends DefaultHandler {
/** /**
* Handles the end element event. * Handles the end element event.
* *
* @param uri the element's uri * @param uri the element's URI
* @param localName the local name * @param localName the local name
* @param qName the qualified name * @param qName the qualified name
* @throws SAXException thrown if there is an exception processing the * @throws SAXException thrown if there is an exception processing the

View File

@@ -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<Evidence> getGivenProduct() { public List<Evidence> getGivenProduct() {
return givenProduct; return givenProduct;

View File

@@ -32,7 +32,7 @@ public class Hints {
private List<HintRule> hintRules; private List<HintRule> hintRules;
/** /**
* Get the value of hintRules * Get the value of hintRules.
* *
* @return 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 * @param hintRules new value of hintRules
*/ */
@@ -55,7 +55,7 @@ public class Hints {
private List<VendorDuplicatingHintRule> vendorDuplicatingHintRules; private List<VendorDuplicatingHintRule> vendorDuplicatingHintRules;
/** /**
* Get the value of vendorDuplicatingHintRules * Get the value of vendorDuplicatingHintRules.
* *
* @return 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 * @param vendorDuplicatingHintRules new value of vendorDuplicatingHintRules
*/ */
public void setVendorDuplicatingHintRules(List<VendorDuplicatingHintRule> vendorDuplicatingHintRules) { public void setVendorDuplicatingHintRules(List<VendorDuplicatingHintRule> vendorDuplicatingHintRules) {
this.vendorDuplicatingHintRules = vendorDuplicatingHintRules; this.vendorDuplicatingHintRules = vendorDuplicatingHintRules;
} }
} }

View File

@@ -70,7 +70,7 @@ public class SuppressionErrorHandler implements ErrorHandler {
*/ */
@Override @Override
public void warning(SAXParseException ex) throws SAXException { public void warning(SAXParseException ex) throws SAXException {
LOGGER.debug("", ex); //LOGGER.debug("", ex);
} }
/** /**

View File

@@ -17,12 +17,19 @@
*/ */
package org.owasp.dependencycheck; 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 static org.junit.Assert.assertTrue;
import org.junit.Test; import org.junit.Test;
import org.owasp.dependencycheck.data.nvdcve.CveDB; 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.nvdcve.DatabaseProperties;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.reporting.ReportGenerator;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
/** /**
@@ -34,10 +41,14 @@ public class EngineIntegrationTest extends BaseDBTestCase {
/** /**
* Test running the entire engine. * 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 @Test
public void testEngine() throws Exception { public void testEngine() throws IOException, InvalidSettingException, DatabaseException, ReportException, ExceptionCollection {
String testClasses = "target/test-classes"; String testClasses = "target/test-classes";
boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
@@ -45,7 +56,23 @@ public class EngineIntegrationTest extends BaseDBTestCase {
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
instance.scan(testClasses); instance.scan(testClasses);
assertTrue(instance.getDependencies().size() > 0); 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 cveDB = new CveDB();
cveDB.open(); cveDB.open();
DatabaseProperties dbProp = cveDB.getDatabaseProperties(); DatabaseProperties dbProp = cveDB.getDatabaseProperties();

View File

@@ -26,6 +26,7 @@ import java.io.File;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import org.junit.After; import org.junit.After;
import org.junit.Assume; 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.Evidence;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -175,6 +177,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
* Test Ruby dependencies and their paths. * Test Ruby dependencies and their paths.
* *
* @throws AnalysisException is thrown when an exception occurs. * @throws AnalysisException is thrown when an exception occurs.
* @throws DatabaseException thrown when an exception occurs
*/ */
@Test @Test
public void testDependenciesPath() throws AnalysisException, DatabaseException { public void testDependenciesPath() throws AnalysisException, DatabaseException {
@@ -186,6 +189,8 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
} catch (NullPointerException ex) { } catch (NullPointerException ex) {
LOGGER.error("NPE", ex); LOGGER.error("NPE", ex);
throw 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<Dependency> dependencies = engine.getDependencies(); List<Dependency> dependencies = engine.getDependencies();
LOGGER.info(dependencies.size() + " dependencies found."); LOGGER.info(dependencies.size() + " dependencies found.");

View File

@@ -20,7 +20,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.4.1-SNAPSHOT</version> <version>1.4.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>dependency-check-maven</artifactId> <artifactId>dependency-check-maven</artifactId>

View File

@@ -36,11 +36,13 @@ import org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.exception.ExceptionCollection;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.utils.Settings; 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 * Maven Plugin that checks project dependencies and the dependencies of all
* vulnerabilities. * child modules to see if they have any known published vulnerabilities.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@@ -55,18 +57,27 @@ import org.owasp.dependencycheck.utils.Settings;
public class AggregateMojo extends BaseDependencyCheckMojo { 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 MojoExecutionException thrown if there is ane exception running
* @throws MojoFailureException thrown if dependency-check is configured to fail the build * the mojo
* @throws MojoFailureException thrown if dependency-check is configured to
* fail the build
*/ */
@Override @Override
public void runCheck() throws MojoExecutionException, MojoFailureException { 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()) { if (getProject() == getLastProject()) {
//ensure that the .ser file was created for each. //ensure that the .ser file was created for each.
for (MavenProject current : getReactorProjects()) { for (MavenProject current : getReactorProjects()) {
final File dataFile = getDataFile(current); final File dataFile = getDataFile(current);
@@ -76,7 +87,6 @@ public class AggregateMojo extends BaseDependencyCheckMojo {
generateDataFile(engine, current); generateDataFile(engine, current);
} }
} }
for (MavenProject current : getReactorProjects()) { for (MavenProject current : getReactorProjects()) {
List<Dependency> dependencies = readDataFile(current); List<Dependency> dependencies = readDataFile(current);
if (dependencies == null) { 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())); getLog().debug(String.format("Adding %d dependencies from %s", childDeps.size(), reportOn.getName()));
} }
dependencies.addAll(childDeps); dependencies.addAll(childDeps);
} else { } else if (getLog().isDebugEnabled()) {
if (getLog().isDebugEnabled()) { getLog().debug(String.format("No dependencies read for %s", reportOn.getName()));
getLog().debug(String.format("No dependencies read for %s", reportOn.getName()));
}
} }
} }
engine.getDependencies().clear(); 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. //we shouldn't write this because nothing is configured to generate this report.
outputDir = new File(current.getBuild().getDirectory()); 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(); 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 * @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 * @param project the project for which all descendants will be returned
* @return the set of descendant projects * @return the set of descendant projects
@@ -232,49 +256,85 @@ public class AggregateMojo extends BaseDependencyCheckMojo {
* Test if the project has pom packaging * Test if the project has pom packaging
* *
* @param mavenProject Project to test * @param mavenProject Project to test
* @return <code>true</code> if it has a pom packaging; otherwise <code>false</code> * @return <code>true</code> if it has a pom packaging; otherwise
* <code>false</code>
*/ */
protected boolean isMultiModule(MavenProject mavenProject) { protected boolean isMultiModule(MavenProject mavenProject) {
return "pom".equals(mavenProject.getPackaging()); 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 * @return the MavenEngine used to execute dependency-check
* @throws MojoExecutionException thrown if there is an exception running the mojo * @throws MojoExecutionException thrown if there is an exception running
* @throws MojoFailureException thrown if dependency-check is configured to fail the build if severe CVEs are identified. * 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 { protected MavenEngine generateDataFile() throws MojoExecutionException, MojoFailureException {
final Engine engine; MavenEngine engine = null;
try { try {
engine = initializeEngine(); engine = initializeEngine();
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
if (getLog().isDebugEnabled()) { if (getLog().isDebugEnabled()) {
getLog().debug("Database connection error", ex); 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()); 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 * @param project the project to scan and generate the data file for
* @return the Engine used to execute dependency-check * @return the MavenEngine used to execute dependency-check
* @throws MojoExecutionException thrown if there is an exception running the mojo * @throws MojoExecutionException thrown if there is an exception running
* @throws MojoFailureException thrown if dependency-check is configured to fail the build if severe CVEs are identified. * 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()) { if (getLog().isDebugEnabled()) {
getLog().debug(String.format("Begin Scanning: %s", project.getName())); getLog().debug(String.format("Begin Scanning: %s", project.getName()));
} }
engine.getDependencies().clear(); engine.getDependencies().clear();
engine.resetFileTypeAnalyzers(); engine.resetFileTypeAnalyzers();
scanArtifacts(project, engine); 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()); final File target = new File(project.getBuild().getDirectory());
writeDataFile(project, target, engine.getDependencies()); writeDataFile(project, target, engine.getDependencies());
showSummary(project, 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 * @param locale The Locale to get the description for
* @return the description * @return the description

View File

@@ -47,6 +47,7 @@ import org.owasp.dependencycheck.dependency.Confidence;
import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.exception.ReportException;
import org.owasp.dependencycheck.reporting.ReportGenerator; import org.owasp.dependencycheck.reporting.ReportGenerator;
import org.owasp.dependencycheck.utils.ExpectedOjectInputStream; import org.owasp.dependencycheck.utils.ExpectedOjectInputStream;
import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.Settings;
@@ -69,14 +70,28 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
* System specific new line character. * System specific new line character.
*/ */
private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern(); private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern();
//</editor-fold>
// <editor-fold defaultstate="collapsed" desc="Maven bound parameters and components">
/** /**
* Sets whether or not the external report format should be used. * Sets whether or not the external report format should be used.
*/ */
@Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true) @Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true)
private String dataFileName; 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;
}
//</editor-fold>
// <editor-fold defaultstate="collapsed" desc="Maven bound parameters and components">
/** /**
* The Maven Project Object. * 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 * 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. * recommended that this be turned to false. Default is true.
*/ */
@SuppressWarnings("CanBeFinal")
@Parameter(property = "autoUpdate") @Parameter(property = "autoUpdate")
private Boolean autoUpdate; private Boolean autoUpdate;
/** /**
* Sets whether Experimental analyzers are enabled. Default is false. * Sets whether Experimental analyzers are enabled. Default is false.
*/ */
@SuppressWarnings("CanBeFinal")
@Parameter(property = "enableExperimental") @Parameter(property = "enableExperimental")
private Boolean enableExperimental; private Boolean enableExperimental;
/** /**
@@ -145,7 +158,6 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
/** /**
* The maven settings proxy id. * The maven settings proxy id.
*/ */
@SuppressWarnings("CanBeFinal")
@Parameter(property = "mavenSettingsProxyId", required = false) @Parameter(property = "mavenSettingsProxyId", required = false)
private String mavenSettingsProxyId; 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. * Flag indicating whether or not to show a summary in the output.
*/ */
@SuppressWarnings("CanBeFinal")
@Parameter(property = "showSummary", defaultValue = "true", required = false) @Parameter(property = "showSummary", defaultValue = "true", required = false)
private boolean showSummary = true; 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 project the project to scan the dependencies of
* @param engine the engine to use to scan the dependencies * @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()) { for (Artifact a : project.getArtifacts()) {
if (excludeFromScan(a)) { if (excludeFromScan(a)) {
continue; continue;
@@ -649,14 +662,14 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
//</editor-fold> //</editor-fold>
/** /**
* Initializes a new <code>Engine</code> that can be used for scanning. * Initializes a new <code>MavenEngine</code> that can be used for scanning.
* *
* @return a newly instantiated <code>Engine</code> * @return a newly instantiated <code>MavenEngine</code>
* @throws DatabaseException thrown if there is a database exception * @throws DatabaseException thrown if there is a database exception
*/ */
protected Engine initializeEngine() throws DatabaseException { protected MavenEngine initializeEngine() throws DatabaseException {
populateSettings(); populateSettings();
return new Engine(this.project, return new MavenEngine(this.project,
this.reactorProjects); this.reactorProjects);
} }
@@ -875,10 +888,11 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
* Generates the reports for a given dependency-check engine. * Generates the reports for a given dependency-check engine.
* *
* @param engine a dependency-check engine * @param engine a dependency-check engine
* @param p the maven project * @param p the Maven project
* @param outputDir the directory path to write the report(s). * @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; DatabaseProperties prop = null;
CveDB cve = null; CveDB cve = null;
try { 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); final ReportGenerator r = new ReportGenerator(p.getName(), engine.getDependencies(), engine.getAnalyzers(), prop);
try { try {
r.generateReports(outputDir.getAbsolutePath(), format); r.generateReports(outputDir.getAbsolutePath(), format);
} catch (IOException ex) { } catch (ReportException ex) {
getLog().error( final String msg = String.format("Error generating the report for %s", p.getName());
"Unexpected exception occurred during analysis; please see the verbose error log for more details."); throw new ReportException(msg, ex);
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);
}
} }
} }
//<editor-fold defaultstate="collapsed" desc="Methods to fail build or show summary"> //<editor-fold defaultstate="collapsed" desc="Methods to fail build or show summary">
@@ -1074,8 +1080,8 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma
* scan data between the "check" and "aggregate" phase. * scan data between the "check" and "aggregate" phase.
* *
* @param project the Maven project to read the data file from * @param project the Maven project to read the data file from
* @return a <code>Engine</code> object populated with dependencies if the * @return a <code>MavenEngine</code> object populated with dependencies if
* serialized data file exists; otherwise <code>null</code> is returned * the serialized data file exists; otherwise <code>null</code> is returned
*/ */
protected List<Dependency> readDataFile(MavenProject project) { protected List<Dependency> readDataFile(MavenProject project) {
final Object oPath = project.getContextValue(this.getDataFileContextKey()); final Object oPath = project.getContextValue(this.getDataFileContextKey());

View File

@@ -26,10 +26,13 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.plugins.annotations.ResolutionScope;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; 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; 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 * @author Jeremy Long
*/ */
@@ -45,7 +48,8 @@ public class CheckMojo extends BaseDependencyCheckMojo {
/** /**
* Returns whether or not a the report can be generated. * Returns whether or not a the report can be generated.
* *
* @return <code>true</code> if the report can be generated; otherwise <code>false</code> * @return <code>true</code> if the report can be generated; otherwise
* <code>false</code>
*/ */
@Override @Override
public boolean canGenerateReport() { 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 MojoExecutionException thrown if there is an exception executing
* @throws MojoFailureException thrown if dependency-check is configured to fail the build * the goal
* @throws MojoFailureException thrown if dependency-check is configured to
* fail the build
*/ */
@Override @Override
public void runCheck() throws MojoExecutionException, MojoFailureException { public void runCheck() throws MojoExecutionException, MojoFailureException {
final Engine engine; MavenEngine engine = null;
try { try {
engine = initializeEngine(); engine = initializeEngine();
} catch (DatabaseException ex) { } catch (DatabaseException ex) {
if (getLog().isDebugEnabled()) { if (getLog().isDebugEnabled()) {
getLog().debug("Database connection error", ex); 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 != null) {
if (engine.getDependencies().isEmpty()) { scanArtifacts(getProject(), engine);
getLog().info("No dependencies were identified that could be analyzed by dependency-check"); if (engine.getDependencies().isEmpty()) {
} else { getLog().info("No dependencies were identified that could be analyzed by dependency-check");
engine.analyzeDependencies(); } else {
writeReports(engine, getProject(), getCorrectOutputDirectory()); ExceptionCollection exCol = null;
writeDataFile(getProject(), null, engine.getDependencies()); try {
showSummary(getProject(), engine.getDependencies()); engine.analyzeDependencies();
checkForFailure(engine.getDependencies()); } 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(); 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 * @param locale The Locale to get the description for
* @return the description * @return the description

View File

@@ -23,22 +23,25 @@ import org.owasp.dependencycheck.analyzer.Analyzer;
import org.owasp.dependencycheck.analyzer.CPEAnalyzer; import org.owasp.dependencycheck.analyzer.CPEAnalyzer;
import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; 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.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* A modified version of the core engine specifically designed to persist some data between multiple executions of a multi-module * A modified version of the core engine specifically designed to persist some
* Maven project. * data between multiple executions of a multi-module Maven project.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public class Engine extends org.owasp.dependencycheck.Engine { public class MavenEngine extends org.owasp.dependencycheck.Engine {
/** /**
* The logger. * 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. * A key used to persist an object in the MavenProject.
*/ */
@@ -52,18 +55,21 @@ public class Engine extends org.owasp.dependencycheck.Engine {
*/ */
private List<MavenProject> reactorProjects; private List<MavenProject> 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"; 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 project the current Maven project
* @param reactorProjects the reactor projects for the current Maven execution * @param reactorProjects the reactor projects for the current Maven
* @throws DatabaseException thrown if there is an issue connecting to the database * execution
* @throws DatabaseException thrown if there is an issue connecting to the
* database
*/ */
public Engine(MavenProject project, List<MavenProject> reactorProjects) throws DatabaseException { public MavenEngine(MavenProject project, List<MavenProject> reactorProjects) throws DatabaseException {
this.currentProject = project; this.currentProject = project;
this.reactorProjects = reactorProjects; this.reactorProjects = reactorProjects;
initializeEngine(); initializeEngine();
@@ -71,9 +77,12 @@ public class Engine extends org.owasp.dependencycheck.Engine {
/** /**
* Runs the analyzers against all of the dependencies. * 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 @Override
public void analyzeDependencies() { public void analyzeDependencies() throws ExceptionCollection {
final MavenProject root = getExecutionRoot(); final MavenProject root = getExecutionRoot();
if (root != null) { if (root != null) {
LOGGER.debug("Checking root project, {}, if updates have already been completed", root.getArtifactId()); 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. * 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(); final MavenProject root = getExecutionRoot();
if (root != null && root.getContextValue(UPDATE_EXECUTED_FLAG) != null) { if (root != null && root.getContextValue(UPDATE_EXECUTED_FLAG) != null) {
System.setProperty(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE.toString()); 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. * 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 * Initializes the given analyzer. This skips the initialization of the
* execution. * CPEAnalyzer if it has been initialized by a previous execution.
* *
* @param analyzer the analyzer to initialize * @param analyzer the analyzer to initialize
* @return the initialized analyzer * @return the initialized analyzer
*/ */
@Override @Override
protected Analyzer initializeAnalyzer(Analyzer analyzer) { protected Analyzer initializeAnalyzer(Analyzer analyzer) throws InitializationException {
if (analyzer instanceof CPEAnalyzer) { if (analyzer instanceof CPEAnalyzer) {
CPEAnalyzer cpe = getPreviouslyLoadedCPEAnalyzer(); CPEAnalyzer cpe = getPreviouslyLoadedCPEAnalyzer();
if (cpe != null && cpe.isOpen()) { 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 @Override
public void cleanup() { public void cleanup() {
@@ -195,7 +208,7 @@ public class Engine extends org.owasp.dependencycheck.Engine {
* *
* @return the root Maven Project * @return the root Maven Project
*/ */
private MavenProject getExecutionRoot() { MavenProject getExecutionRoot() {
if (reactorProjects == null) { if (reactorProjects == null) {
return 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 * Resets the file type analyzers so that they can be re-used to scan
* might be disabled because the first scan/analyze did not identify any files that could be processed by the analyzer. * 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() { public void resetFileTypeAnalyzers() {
for (FileTypeAnalyzer a : getFileTypeAnalyzers()) { for (FileTypeAnalyzer a : getFileTypeAnalyzers()) {

View File

@@ -54,14 +54,20 @@ public class PurgeMojo extends BaseDependencyCheckMojo {
/** /**
* Purges the local copy of the NVD. * Purges the local copy of the NVD.
* *
* @throws MojoExecutionException thrown if there is an exception executing the goal * @throws MojoExecutionException thrown if there is an exception executing
* @throws MojoFailureException thrown if dependency-check is configured to fail the build * the goal
* @throws MojoFailureException thrown if dependency-check is configured to
* fail the build
*/ */
@Override @Override
public void runCheck() throws MojoExecutionException, MojoFailureException { public void runCheck() throws MojoExecutionException, MojoFailureException {
if (getConnectionString() != null && !getConnectionString().isEmpty()) { 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 { } else {
populateSettings(); populateSettings();
File db; File db;
@@ -71,13 +77,25 @@ public class PurgeMojo extends BaseDependencyCheckMojo {
if (db.delete()) { if (db.delete()) {
getLog().info("Database file purged; local copy of the NVD has been removed"); getLog().info("Database file purged; local copy of the NVD has been removed");
} else { } 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 { } 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) { } 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(); 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 * @param locale The Locale to get the description for
* @return the description * @return the description

View File

@@ -24,10 +24,12 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.plugins.annotations.ResolutionScope;
import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
import org.owasp.dependencycheck.data.update.exception.UpdateException;
import org.owasp.dependencycheck.utils.Settings; 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 * @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 MojoExecutionException thrown if there is an exception executing
* @throws MojoFailureException thrown if dependency-check is configured to fail the build * the goal
* @throws MojoFailureException thrown if dependency-check is configured to
* fail the build
*/ */
@Override @Override
public void runCheck() throws MojoExecutionException, MojoFailureException { public void runCheck() throws MojoExecutionException, MojoFailureException {
final Engine engine; MavenEngine engine = null;
try { try {
engine = initializeEngine(); engine = initializeEngine();
engine.update(); engine.update();
@@ -66,9 +71,21 @@ public class UpdateMojo extends BaseDependencyCheckMojo {
if (getLog().isDebugEnabled()) { if (getLog().isDebugEnabled()) {
getLog().debug("Database connection error", ex); 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(); 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 * @param locale The Locale to get the description for
* @return the description * @return the description
@@ -93,5 +111,4 @@ public class UpdateMojo extends BaseDependencyCheckMojo {
public String getDescription(Locale locale) { public String getDescription(Locale locale) {
return "Updates the local cache of the NVD data from NIST."; return "Updates the local cache of the NVD data from NIST.";
} }
} }

View File

@@ -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 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 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 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 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 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' 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'

View File

@@ -53,18 +53,16 @@ Create an aggregated dependency-check report within the site.
<plugins> <plugins>
... ...
<plugin> <plugin>
<plugin> <groupId>org.owasp</groupId>
<groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId>
<artifactId>dependency-check-maven</artifactId> <version>${project.version}</version>
<version>${project.version}</version> <reportSets>
<reportSets> <reportSet>
<reportSet> <reports>
<reports> <report>aggregate</report>
<report>aggregate</report> </reports>
</reports> </reportSet>
</reportSet> </reportSets>
</reportSets>
</plugin>
</plugin> </plugin>
... ...
</plugins> </plugins>

View File

@@ -90,7 +90,7 @@ public class BaseDependencyCheckMojoTest extends BaseTest {
boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); 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); Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
assertTrue(engine.getDependencies().isEmpty()); assertTrue(engine.getDependencies().isEmpty());

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved.
<parent> <parent>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.4.1-SNAPSHOT</version> <version>1.4.3-SNAPSHOT</version>
</parent> </parent>
<artifactId>dependency-check-utils</artifactId> <artifactId>dependency-check-utils</artifactId>

View File

@@ -175,7 +175,7 @@ public final class Downloader {
} }
LOGGER.debug("Download of {} complete", url.toString()); LOGGER.debug("Download of {} complete", url.toString());
} catch (IOException ex) { } catch (IOException ex) {
checkForSslExceptionn(ex); checkForCommonExceptionTypes(ex);
final String msg = format("Error saving '%s' to file '%s'%nConnection Timeout: %d%nEncoding: %s%n", final String msg = format("Error saving '%s' to file '%s'%nConnection Timeout: %d%nEncoding: %s%n",
url.toString(), outputPath.getAbsolutePath(), conn.getConnectTimeout(), encoding); url.toString(), outputPath.getAbsolutePath(), conn.getConnectTimeout(), encoding);
throw new DownloadFailedException(msg, ex); throw new DownloadFailedException(msg, ex);
@@ -261,8 +261,9 @@ public final class Downloader {
} catch (URLConnectionFailureException ex) { } catch (URLConnectionFailureException ex) {
throw new DownloadFailedException(format("Error creating URL Connection for HTTP %s request.", httpMethod), ex); throw new DownloadFailedException(format("Error creating URL Connection for HTTP %s request.", httpMethod), ex);
} catch (IOException ex) { } catch (IOException ex) {
checkForSslExceptionn(ex); checkForCommonExceptionTypes(ex);
LOGGER.debug("IO Exception: " + ex.getMessage(), ex); LOGGER.error("IO Exception: " + ex.getMessage());
LOGGER.debug("Exception details", ex);
if (ex.getCause() != null) { if (ex.getCause() != null) {
LOGGER.debug("IO Exception cause: " + ex.getCause().getMessage(), ex.getCause()); 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 * Analyzes the IOException, logs the appropriate information for debugging
* purposes, and then throws a DownloadFailedException that wraps the IO * 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 * @param ex the original exception
* @throws DownloadFailedException a wrapper exception that contains the * @throws DownloadFailedException a wrapper exception that contains the
* original exception as the cause * original exception as the cause
*/ */
protected static void checkForSslExceptionn(IOException ex) throws DownloadFailedException { protected static void checkForCommonExceptionTypes(IOException ex) throws DownloadFailedException {
Throwable cause = ex; Throwable cause = ex;
while (cause != null) { 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) { if (cause instanceof InvalidAlgorithmParameterException) {
final String keystore = System.getProperty("javax.net.ssl.keyStore"); final String keystore = System.getProperty("javax.net.ssl.keyStore");
final String version = System.getProperty("java.version"); final String version = System.getProperty("java.version");

View File

@@ -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 <a href="http://stackoverflow.com/users/608639/jww">jww</a>
*/
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<String> aa = new ArrayList<String>();
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<String> aa = new ArrayList<String>();
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;
}

View File

@@ -46,7 +46,8 @@ public final class Settings {
public static final class KEYS { 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() { private KEYS() {
//do nothing //do nothing
@@ -54,29 +55,34 @@ public final class Settings {
/** /**
* The key to obtain the application name. * 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. * The key to obtain the application version.
*/ */
public static final String APPLICATION_VERSION = "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"; 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"; 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"; 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"; 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"; public static final String DB_CONNECTION_STRING = "data.connection_string";
/** /**
@@ -103,34 +109,39 @@ public final class Settings {
* The starts with filter used to exclude CVE entries from the database. * 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 * 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 * just those that are related to applications. If this were set to just
* 'cpe:' the OS, hardware, and application related CVEs would be imported. * 'cpe:' the OS, hardware, and application related CVEs would be
* imported.
*/ */
public static final String CVE_CPE_STARTS_WITH_FILTER = "cve.cpe.startswith.filter"; 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"; 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 * The properties key for the URL to retrieve the recently modified and
* schema. * added CVE entries (last 8 days) using the 2.0 schema.
*/ */
public static final String CVE_MODIFIED_20_URL = "cve.url-2.0.modified"; 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 * The properties key for the URL to retrieve the recently modified and
* schema. * added CVE entries (last 8 days) using the 1.2 schema.
*/ */
public static final String CVE_MODIFIED_12_URL = "cve.url-1.2.modified"; 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"; 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"; 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 * The properties key for the telling us how many cve.url.* URLs exists.
* be able to retrieve the URLs for all of the files that make up the NVD CVE listing. * 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"; 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"; 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"; 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. * 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 @Deprecated
public static final String PROXY_URL = "proxy.server"; public static final String PROXY_URL = "proxy.server";
@@ -161,7 +175,8 @@ public final class Settings {
*/ */
public static final String PROXY_SERVER = "proxy.server"; 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"; 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"; 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"; 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"; 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"; 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"; 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"; 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"; 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 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 * @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 * Initializes the thread local settings object. Note, to use the settings
* also call Settings.cleanup() to properly release resources. * object you must call this method. However, you must also call
* Settings.cleanup() to properly release resources.
*/ */
public static void initialize() { public static void initialize() {
LOCAL_SETTINGS.set(new Settings(PROPERTIES_FILE)); 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 * Initializes the thread local settings object. Note, to use the settings
* also call Settings.cleanup() to properly release resources. * 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 * @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. * 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) { public static void cleanup(boolean deleteTemporary) {
if (deleteTemporary && tempDirectory != null && tempDirectory.exists()) { 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 header the header to print with the log message
* @param properties the properties to log * @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 * Merges a new properties file into the current properties. This method
* file.<br><br> * allows for the loading of a user provided properties file.<br><br>
* <b>Note</b>: even if using this method - system properties will be loaded before properties loaded from files. * <b>Note</b>: 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. * @param filePath the path to the properties file to merge.
* @throws FileNotFoundException is thrown when the filePath points to a non-existent file * @throws FileNotFoundException is thrown when the filePath points to a
* @throws IOException is thrown when there is an exception loading/merging the properties * 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 { public static void mergeProperties(File filePath) throws FileNotFoundException, IOException {
FileInputStream fis = null; 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 * Merges a new properties file into the current properties. This method
* file.<br><br> * allows for the loading of a user provided properties file.<br><br>
* Note: even if using this method - system properties will be loaded before properties loaded from files. * 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. * @param filePath the path to the properties file to merge.
* @throws FileNotFoundException is thrown when the filePath points to a non-existent file * @throws FileNotFoundException is thrown when the filePath points to a
* @throws IOException is thrown when there is an exception loading/merging the properties * 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 { public static void mergeProperties(String filePath) throws FileNotFoundException, IOException {
FileInputStream fis = null; 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 * Merges a new properties file into the current properties. This method
* file.<br><br> * allows for the loading of a user provided properties file.<br><br>
* <b>Note</b>: even if using this method - system properties will be loaded before properties loaded from files. * <b>Note</b>: 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 * @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 { public static void mergeProperties(InputStream stream) throws IOException {
LOCAL_SETTINGS.get().props.load(stream); 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 * Returns a value from the properties file as a File object. If the value
* the -Dprop=value argument - this method will return the value from the system properties before the values in the contained * was specified as a system property or passed in via the -Dprop=value
* configuration file. * 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 key the key to lookup within the properties file
* @return the property from the properties file converted to a File object * @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 * Returns a value from the properties file as a File object. If the value
* the -Dprop=value argument - this method will return the value from the system properties before the values in the contained * was specified as a system property or passed in via the -Dprop=value
* configuration file. * 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 * This method will check the configured base directory and will use this as
* base directory begins with a leading "[JAR]\" sequence with the path to the folder containing the JAR file containing this * the base of the file path. Additionally, if the base directory begins
* class. * 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 * @param key the key to lookup within the properties file
* @return the property from the properties file converted to a File object * @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 * @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 * Returns a value from the properties file. If the value was specified as a
* argument - this method will return the value from the system properties before the values in the contained configuration * system property or passed in via the -Dprop=value argument - this method
* file. * 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 key the key to lookup within the properties file
* @param defaultValue the default value for the requested property * @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; private static File tempDirectory = null;
@@ -701,7 +740,8 @@ public final class Settings {
* Returns the temporary directory. * Returns the temporary directory.
* *
* @return 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 { public static File getTempDirectory() throws IOException {
final File tmpDir = new File(Settings.getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir")), "dctemp"); 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 * Returns a value from the properties file. If the value was specified as a
* argument - this method will return the value from the system properties before the values in the contained configuration * system property or passed in via the -Dprop=value argument - this method
* file. * 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 key the key to lookup within the properties file
* @return the property from 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 * @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 * Returns an int value from the properties file. If the value was specified
* -Dprop=value argument - this method will return the value from the system properties before the values in the contained * as a system property or passed in via the -Dprop=value argument - this
* configuration file. * 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 key the key to lookup within the properties file
* @return the property from 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 { public static int getInt(String key) throws InvalidSettingException {
try { 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 * Returns an int value from the properties file. If the value was specified
* -Dprop=value argument - this method will return the value from the system properties before the values in the contained * as a system property or passed in via the -Dprop=value argument - this
* configuration file. * 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 key the key to lookup within the properties file
* @param defaultValue the default value to return * @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 * @return the property from the properties file or the defaultValue if the
* an integer * property does not exist or cannot be converted to an integer
*/ */
public static int getInt(String key, int defaultValue) { public static int getInt(String key, int defaultValue) {
int value; 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 * Returns a long value from the properties file. If the value was specified
* -Dprop=value argument - this method will return the value from the system properties before the values in the contained * as a system property or passed in via the -Dprop=value argument - this
* configuration file. * 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 key the key to lookup within the properties file
* @return the property from 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 { public static long getLong(String key) throws InvalidSettingException {
try { 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 * Returns a boolean value from the properties file. If the value was
* <code>-Dprop=value</code> argument this method will return the value from the system properties before the values in the * specified as a system property or passed in via the
* contained configuration file. * <code>-Dprop=value</code> 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 key the key to lookup within the properties file
* @return the property from 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 { public static boolean getBoolean(String key) throws InvalidSettingException {
return Boolean.parseBoolean(Settings.getString(key)); 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 * Returns a boolean value from the properties file. If the value was
* <code>-Dprop=value</code> argument this method will return the value from the system properties before the values in the * specified as a system property or passed in via the
* contained configuration file. * <code>-Dprop=value</code> 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 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 * @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 { public static boolean getBoolean(String key, boolean defaultValue) throws InvalidSettingException {
return Boolean.parseBoolean(Settings.getString(key, Boolean.toString(defaultValue))); 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 * Returns a connection string from the configured properties. If the
* determine the 'data' directory and replace the %s with the path to the data directory. If the data directory does not * connection string contains a %s, this method will determine the 'data'
* exists it will be created. * 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 * @param dbFileNameKey the settings key for the db filename
* @return the connection string * @return the connection string
* @throws IOException thrown the data directory cannot be created * @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 * Retrieves the directory that the JAR file exists in so that we can ensure
* embedded H2 database. This is public solely for some unit tests; otherwise this should be private. * 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 * @return the data directory to store data files
* @throws IOException is thrown if an IOException occurs of course... * @throws IOException is thrown if an IOException occurs of course...

View File

@@ -28,15 +28,28 @@ import java.net.PasswordAuthentication;
import java.net.Proxy; import java.net.Proxy;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.URL; 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 * A URLConnection Factory to create new connections. This encapsulates several
* uses the correct proxy settings. * configuration checks to ensure that the connection uses the correct proxy
* settings.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public final class URLConnectionFactory { public final class URLConnectionFactory {
/**
* The logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(URLConnectionFactory.class);
/** /**
* Private constructor for this factory. * 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 * Utility method to create an HttpURLConnection. If the application is
* the proxy settings and use them when setting up the connection. * 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 * @param url the url to connect to
* @return an HttpURLConnection * @return an HttpURLConnection
@@ -95,6 +109,7 @@ public final class URLConnectionFactory {
} }
throw new URLConnectionFailureException("Error getting connection.", ex); throw new URLConnectionFailureException("Error getting connection.", ex);
} }
configureTLS(url, conn);
return 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 * Utility method to create an HttpURLConnection. The use of a proxy here is
* configured but we don't want to use it (for example, if there's an internal repository configured) * 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 url the URL to connect to
* @param proxy whether to use the proxy (if configured) * @param proxy whether to use the proxy (if configured)
@@ -161,6 +178,29 @@ public final class URLConnectionFactory {
} catch (IOException ioe) { } catch (IOException ioe) {
throw new URLConnectionFailureException("Error getting connection.", ioe); throw new URLConnectionFailureException("Error getting connection.", ioe);
} }
configureTLS(url, conn);
return 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);
}
}
}
} }

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId> <artifactId>dependency-check-parent</artifactId>
<version>1.4.1-SNAPSHOT</version> <version>1.4.3-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>

View File

@@ -28,11 +28,12 @@ More information about dependency-check can be found here:
OWASP dependency-check's core analysis engine can be used as: 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) - [Ant Task](dependency-check-ant/index.html)
- [Command Line Tool](dependency-check-cli/index.html)
- [Gradle Plugin](dependency-check-gradle/index.html) - [Gradle Plugin](dependency-check-gradle/index.html)
- [Jenkins Plugin](dependency-check-jenkins/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: For help with dependency-check the following resource can be used:

View File

@@ -3,10 +3,12 @@ Modules
OWASP dependency-check's core analysis engine was designed to fit into an applications normal OWASP dependency-check's core analysis engine was designed to fit into an applications normal
build and reporting process: build and reporting process:
- [Maven Plugin](dependency-check-maven/index.html) - [Ant Task](dependency-check-ant/index.html)
- [Ant Task](dependency-check-ant/index.html) - [Command Line Tool](dependency-check-cli/index.html)
- [Gradle Plugin](dependency-check-gradle/index.html) - [Gradle Plugin](dependency-check-gradle/index.html)
- [Jenkins Plugin](dependency-check-jenkins/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). In addition, dependency-check can be executed from the [command line](dependency-check-cli/index.html).