mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-14 07:43:40 +01:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc98d646a0 | ||
|
|
573866feee | ||
|
|
ebf855f2a4 | ||
|
|
595452cf82 | ||
|
|
1439fd6104 | ||
|
|
f8771adbe7 | ||
|
|
4eb76e6da3 | ||
|
|
a84b624fa5 | ||
|
|
9ca198ee41 | ||
|
|
d509523743 | ||
|
|
338c70c289 | ||
|
|
e899ad8caa | ||
|
|
c8c6e0350a | ||
|
|
8faaf6a469 | ||
|
|
1a0bd89c9d | ||
|
|
6a9308b514 | ||
|
|
1b1f5203f1 | ||
|
|
e2c78e546d | ||
|
|
dc02757bc3 | ||
|
|
19a2265792 | ||
|
|
7666ed070a | ||
|
|
d088e4574e | ||
|
|
dd8798e52b | ||
|
|
623d992e34 | ||
|
|
420f9a068d | ||
|
|
864807196c | ||
|
|
a71c8cef83 | ||
|
|
f34a3e421d | ||
|
|
0440a4aa7e | ||
|
|
0faa49d0e5 | ||
|
|
9dfc25559e | ||
|
|
ee6dd0e794 | ||
|
|
10824e9731 | ||
|
|
edcf708945 | ||
|
|
c96375a16c | ||
|
|
5cbf49a3dd | ||
|
|
eebd0491a3 | ||
|
|
8c38a0e6cc | ||
|
|
5b9fe065d7 | ||
|
|
8567610ddc | ||
|
|
52c186868e | ||
|
|
2699f8ee85 | ||
|
|
ebaf33a36f | ||
|
|
b0f3c76f76 | ||
|
|
acd118a58c | ||
|
|
dff0b497b0 | ||
|
|
e34f51a1b0 | ||
|
|
e82e996fe5 | ||
|
|
238abd009d | ||
|
|
25e929c10e | ||
|
|
0e9f5978e1 | ||
|
|
1024b11eeb | ||
|
|
a390418f83 | ||
|
|
182c131ee0 | ||
|
|
1d5d104bbc | ||
|
|
53cf0863d0 | ||
|
|
5bc64c6925 | ||
|
|
c2f9d3f455 | ||
|
|
ddd93f518d | ||
|
|
6d7de79fa9 | ||
|
|
df0f05197a | ||
|
|
e3186e6c4c | ||
|
|
18bca6352d | ||
|
|
fd7299c86f | ||
|
|
f572d32f5b | ||
|
|
e534d41d81 | ||
|
|
a641c9858c | ||
|
|
c8e339a58d | ||
|
|
5cfb83a912 | ||
|
|
85540e6fe3 | ||
|
|
eda770570c | ||
|
|
41476943ef | ||
|
|
68857fea24 | ||
|
|
98911eca05 | ||
|
|
d71e61df8b | ||
|
|
3188b0f6cb | ||
|
|
9885b8d117 | ||
|
|
f868c3d172 | ||
|
|
a169183783 | ||
|
|
415edd2265 | ||
|
|
255c80953d | ||
|
|
bf08aeeaad | ||
|
|
45143ba8d4 | ||
|
|
ffeac233c2 | ||
|
|
6903ecbeb4 | ||
|
|
64f0c37251 | ||
|
|
2331c569df | ||
|
|
34ae6fd089 | ||
|
|
5b58894b02 | ||
|
|
ed5e8e2666 | ||
|
|
f903d91dca | ||
|
|
58cfdd6d05 | ||
|
|
28523c356c | ||
|
|
3553489f2e | ||
|
|
f74efd5b96 | ||
|
|
ba887fdf21 | ||
|
|
3995cd64da | ||
|
|
9fdf22a475 | ||
|
|
5980d0a6fa | ||
|
|
21f8b0b553 | ||
|
|
d98ca9d21f | ||
|
|
fe2cdfe81a | ||
|
|
878d9ad8d9 | ||
|
|
e25961f40c | ||
|
|
7987800567 | ||
|
|
daec4c2e4e | ||
|
|
5ea52b47ab |
@@ -22,7 +22,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.0.3</version>
|
||||
<version>1.0.7</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-ant</artifactId>
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
@@ -38,6 +37,7 @@ import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
|
||||
import org.owasp.dependencycheck.utils.LogUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
@@ -323,6 +323,50 @@ public class DependencyCheckTask extends Task {
|
||||
public void setProxyPort(String proxyPort) {
|
||||
this.proxyPort = proxyPort;
|
||||
}
|
||||
/**
|
||||
* The Proxy username.
|
||||
*/
|
||||
private String proxyUsername;
|
||||
|
||||
/**
|
||||
* Get the value of proxyUsername.
|
||||
*
|
||||
* @return the value of proxyUsername
|
||||
*/
|
||||
public String getProxyUsername() {
|
||||
return proxyUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of proxyUsername.
|
||||
*
|
||||
* @param proxyUsername new value of proxyUsername
|
||||
*/
|
||||
public void setProxyUsername(String proxyUsername) {
|
||||
this.proxyUsername = proxyUsername;
|
||||
}
|
||||
/**
|
||||
* The Proxy password.
|
||||
*/
|
||||
private String proxyPassword;
|
||||
|
||||
/**
|
||||
* Get the value of proxyPassword.
|
||||
*
|
||||
* @return the value of proxyPassword
|
||||
*/
|
||||
public String getProxyPassword() {
|
||||
return proxyPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of proxyPassword.
|
||||
*
|
||||
* @param proxyPassword new value of proxyPassword
|
||||
*/
|
||||
public void setProxyPassword(String proxyPassword) {
|
||||
this.proxyPassword = proxyPassword;
|
||||
}
|
||||
/**
|
||||
* The Connection Timeout.
|
||||
*/
|
||||
@@ -345,41 +389,55 @@ public class DependencyCheckTask extends Task {
|
||||
public void setConnectionTimeout(String connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
/**
|
||||
* The file path used for verbose logging.
|
||||
*/
|
||||
private String logFile = null;
|
||||
|
||||
/**
|
||||
* Configures the logger for use by the application.
|
||||
* Get the value of logFile.
|
||||
*
|
||||
* @return the value of logFile
|
||||
*/
|
||||
private static void prepareLogger() {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = DependencyCheckTask.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
|
||||
LogManager.getLogManager().reset();
|
||||
LogManager.getLogManager().readConfiguration(in);
|
||||
//TODO add code to disable fine grained log file.
|
||||
// Logger logger = LogManager.getLogManager().getLogger("");
|
||||
// for (Handler h : logger.getHandlers()) {
|
||||
// if (h.getFormatter(). h.toString());
|
||||
// }
|
||||
} catch (IOException ex) {
|
||||
System.err.println(ex.toString());
|
||||
Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (SecurityException ex) {
|
||||
Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ex) {
|
||||
//noinspection UnusedAssignment
|
||||
in = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public String getLogFile() {
|
||||
return logFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of logFile.
|
||||
*
|
||||
* @param logFile new value of logFile
|
||||
*/
|
||||
public void setLogFile(String logFile) {
|
||||
this.logFile = logFile;
|
||||
}
|
||||
/**
|
||||
* The path to the suppression file.
|
||||
*/
|
||||
private String suppressionFile;
|
||||
|
||||
/**
|
||||
* Get the value of suppressionFile.
|
||||
*
|
||||
* @return the value of suppressionFile
|
||||
*/
|
||||
public String getSuppressionFile() {
|
||||
return suppressionFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of suppressionFile.
|
||||
*
|
||||
* @param suppressionFile new value of suppressionFile
|
||||
*/
|
||||
public void setSuppressionFile(String suppressionFile) {
|
||||
this.suppressionFile = suppressionFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws BuildException {
|
||||
prepareLogger();
|
||||
final InputStream in = DependencyCheckTask.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
|
||||
LogUtils.prepareLogger(in, logFile);
|
||||
|
||||
dealWithReferences();
|
||||
validateConfiguration();
|
||||
@@ -467,9 +525,18 @@ public class DependencyCheckTask extends Task {
|
||||
if (proxyPort != null && !proxyPort.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
}
|
||||
if (proxyUsername != null && !proxyUsername.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUsername);
|
||||
}
|
||||
if (proxyPassword != null && !proxyPassword.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPassword);
|
||||
}
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
}
|
||||
if (suppressionFile != null && !suppressionFile.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,6 +579,7 @@ public class DependencyCheckTask extends Task {
|
||||
*
|
||||
* @return the list of values for the report format
|
||||
*/
|
||||
@Override
|
||||
public String[] getValues() {
|
||||
int i = 0;
|
||||
final Format[] formats = Format.values();
|
||||
|
||||
@@ -28,8 +28,12 @@ ReportOutputDirectory | The directory where dependency-check will store data use
|
||||
FailBuildOn | If set and a CVE is found that is greater then the specified value the build will fail. The default value is 11 which means that the build will not fail. Valid values are 0-11. | Optional
|
||||
AutoUpdate | If set to false the NVD CVE data is not automatically updated. Setting this to false could result in false negatives. However, this may be required in some environments. The default value is true. | Optional
|
||||
DataDirectory | The directory where dependency-check will store data used for analysis. Defaults to a folder called, called 'dependency-check-data', that is in the same directory as the dependency-check-ant jar file was installed in. *It is not recommended to change this.* | Optional
|
||||
LogFile | The file path to write verbose logging information. | Optional
|
||||
SuppressionFile | An XML file conforming to the suppression schema that suppresses findings; this is used to hide false positives. | Optional
|
||||
ProxyUrl | Defines the proxy used to connect to the Internet. | Optional
|
||||
ProxyPort | Defines the port for the proxy. | Optional
|
||||
ProxyUsername | Defines the proxy user name. | Optional
|
||||
ProxyPassword | Defines the proxy password. | Optional
|
||||
ConnectionTimeout | The connection timeout used when downloading data files from the Internet. | Optional
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.0.3</version>
|
||||
<version>1.0.7</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-cli</artifactId>
|
||||
|
||||
@@ -24,12 +24,12 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.cli.CliParser;
|
||||
import org.owasp.dependencycheck.utils.LogUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/*
|
||||
@@ -67,35 +67,10 @@ public class App {
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
prepareLogger();
|
||||
final App app = new App();
|
||||
app.run(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the logger for use by the application.
|
||||
*/
|
||||
private static void prepareLogger() {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = App.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
|
||||
LogManager.getLogManager().reset();
|
||||
LogManager.getLogManager().readConfiguration(in);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.FINE, "IO Error preparing the logger", ex);
|
||||
} catch (SecurityException ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.FINE, "Error preparing the logger", ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(App.class.getName()).log(Level.FINEST, "Error closing resource stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main CLI entry-point into the application.
|
||||
*
|
||||
@@ -116,10 +91,15 @@ public class App {
|
||||
return;
|
||||
}
|
||||
|
||||
final InputStream in = App.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
|
||||
LogUtils.prepareLogger(in, cli.getVerboseLog());
|
||||
|
||||
if (cli.isGetVersion()) {
|
||||
cli.printVersionInfo();
|
||||
} else if (cli.isRunScan()) {
|
||||
updateSettings(cli.isAutoUpdate(), cli.getConnectionTimeout(), cli.getProxyUrl(), cli.getProxyPort(), cli.getDataDirectory());
|
||||
updateSettings(cli.isAutoUpdate(), cli.getConnectionTimeout(), cli.getProxyUrl(),
|
||||
cli.getProxyPort(), cli.getProxyUsername(), cli.getProxyPassword(),
|
||||
cli.getDataDirectory(), cli.getPropertiesFile(), cli.getSuppressionFile());
|
||||
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getApplicationName(), cli.getScanFiles());
|
||||
} else {
|
||||
cli.printHelp();
|
||||
@@ -167,9 +147,29 @@ public class App {
|
||||
* @param proxyUrl the proxy url (null or blank means no proxy will be used)
|
||||
* @param proxyPort the proxy port (null or blank means no port will be
|
||||
* used)
|
||||
* @param proxyUser the proxy user name
|
||||
* @param proxyPass the password for the proxy
|
||||
* @param dataDirectory the directory to store/retrieve persistent data from
|
||||
* @param propertiesFile the properties file to utilize
|
||||
* @param suppressionFile the path to the suppression file
|
||||
*/
|
||||
private void updateSettings(boolean autoUpdate, String connectionTimeout, String proxyUrl, String proxyPort, String dataDirectory) {
|
||||
private void updateSettings(boolean autoUpdate, String connectionTimeout, String proxyUrl, String proxyPort,
|
||||
String proxyUser, String proxyPass, String dataDirectory, File propertiesFile,
|
||||
String suppressionFile) {
|
||||
|
||||
if (propertiesFile != null) {
|
||||
try {
|
||||
Settings.mergeProperties(propertiesFile);
|
||||
} catch (FileNotFoundException ex) {
|
||||
final String msg = String.format("Unable to load properties file '%s'", propertiesFile.getPath());
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(App.class.getName()).log(Level.FINE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Unable to find properties file '%s'", propertiesFile.getPath());
|
||||
Logger.getLogger(App.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(App.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
if (dataDirectory != null) {
|
||||
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
|
||||
} else if (System.getProperty("basedir") != null) {
|
||||
@@ -182,8 +182,6 @@ public class App {
|
||||
final File dataDir = new File(base, sub);
|
||||
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
|
||||
if (proxyUrl != null && !proxyUrl.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_URL, proxyUrl);
|
||||
@@ -191,8 +189,17 @@ public class App {
|
||||
if (proxyPort != null && !proxyPort.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
}
|
||||
if (proxyUser != null && !proxyUser.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUser);
|
||||
}
|
||||
if (proxyPass != null && !proxyPass.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPass);
|
||||
}
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
}
|
||||
if (suppressionFile != null && !suppressionFile.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +175,14 @@ public final class CliParser {
|
||||
.withDescription("The proxy port to use when downloading resources.")
|
||||
.create(ArgumentName.PROXY_PORT_SHORT);
|
||||
|
||||
final Option proxyUsername = OptionBuilder.withArgName("user").hasArg().withLongOpt(ArgumentName.PROXY_USERNAME)
|
||||
.withDescription("The proxy username to use when downloading resources.")
|
||||
.create(ArgumentName.PROXY_USERNAME_SHORT);
|
||||
|
||||
final Option proxyPassword = OptionBuilder.withArgName("pass").hasArg().withLongOpt(ArgumentName.PROXY_PASSWORD)
|
||||
.withDescription("The proxy password to use when downloading resources.")
|
||||
.create(ArgumentName.PROXY_PASSWORD_SHORT);
|
||||
|
||||
final Option path = OptionBuilder.withArgName("path").hasArg().withLongOpt(ArgumentName.SCAN)
|
||||
.withDescription("The path to scan - this option can be specified multiple times.")
|
||||
.create(ArgumentName.SCAN_SHORT);
|
||||
@@ -195,6 +203,15 @@ public final class CliParser {
|
||||
.withDescription("The output format to write to (XML, HTML, VULN, ALL). The default is HTML.")
|
||||
.create(ArgumentName.OUTPUT_FORMAT_SHORT);
|
||||
|
||||
final Option verboseLog = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.VERBOSE_LOG)
|
||||
.withDescription("The file path to write verbose logging information.")
|
||||
.create(ArgumentName.VERBOSE_LOG_SHORT);
|
||||
|
||||
final Option suppressionFile = OptionBuilder.withArgName("file").hasArg().withLongOpt(ArgumentName.SUPPRESION_FILE)
|
||||
.withDescription("The file path to the suppression XML file.")
|
||||
.create(ArgumentName.SUPPRESION_FILE_SHORT);
|
||||
|
||||
|
||||
final OptionGroup og = new OptionGroup();
|
||||
og.addOption(path);
|
||||
|
||||
@@ -208,8 +225,12 @@ public final class CliParser {
|
||||
opts.addOption(noUpdate);
|
||||
opts.addOption(props);
|
||||
opts.addOption(data);
|
||||
opts.addOption(verboseLog);
|
||||
opts.addOption(suppressionFile);
|
||||
opts.addOption(proxyPort);
|
||||
opts.addOption(proxyUrl);
|
||||
opts.addOption(proxyUsername);
|
||||
opts.addOption(proxyPassword);
|
||||
opts.addOption(connectionTimeout);
|
||||
|
||||
return opts;
|
||||
@@ -325,6 +346,24 @@ public final class CliParser {
|
||||
return line.getOptionValue(ArgumentName.PROXY_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the proxy username.
|
||||
*
|
||||
* @return the proxy username
|
||||
*/
|
||||
public String getProxyUsername() {
|
||||
return line.getOptionValue(ArgumentName.PROXY_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the proxy password.
|
||||
*
|
||||
* @return the proxy password
|
||||
*/
|
||||
public String getProxyPassword() {
|
||||
return line.getOptionValue(ArgumentName.PROXY_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of dataDirectory.
|
||||
*
|
||||
@@ -334,6 +373,37 @@ public final class CliParser {
|
||||
return line.getOptionValue(ArgumentName.DATA_DIRECTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the properties file specified on the command line.
|
||||
*
|
||||
* @return the properties file specified on the command line
|
||||
*/
|
||||
public File getPropertiesFile() {
|
||||
final String path = line.getOptionValue(ArgumentName.PROP);
|
||||
if (path != null) {
|
||||
return new File(path);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the verbose log file.
|
||||
*
|
||||
* @return the path to the verbose log file
|
||||
*/
|
||||
public String getVerboseLog() {
|
||||
return line.getOptionValue(ArgumentName.VERBOSE_LOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the suppression file.
|
||||
*
|
||||
* @return the path to the suppression file
|
||||
*/
|
||||
public String getSuppressionFile() {
|
||||
return line.getOptionValue(ArgumentName.SUPPRESION_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Prints the manifest information to standard output.</p>
|
||||
* <ul><li>Implementation-Title: ${pom.name}</li>
|
||||
@@ -443,11 +513,27 @@ public final class CliParser {
|
||||
*/
|
||||
public static final String PROXY_URL = "proxyurl";
|
||||
/**
|
||||
* The short CLI argument name indicating the proxy url.
|
||||
* The short CLI argument name indicating the proxy username.
|
||||
*/
|
||||
public static final String PROXY_USERNAME_SHORT = "pu";
|
||||
/**
|
||||
* The CLI argument name indicating the proxy username.
|
||||
*/
|
||||
public static final String PROXY_USERNAME = "proxyuser";
|
||||
/**
|
||||
* The short CLI argument name indicating the proxy password.
|
||||
*/
|
||||
public static final String PROXY_PASSWORD_SHORT = "pp";
|
||||
/**
|
||||
* The CLI argument name indicating the proxy password.
|
||||
*/
|
||||
public static final String PROXY_PASSWORD = "proxypass";
|
||||
/**
|
||||
* The short CLI argument name indicating the connection timeout.
|
||||
*/
|
||||
public static final String CONNECTION_TIMEOUT_SHORT = "c";
|
||||
/**
|
||||
* The CLI argument name indicating the proxy url.
|
||||
* The CLI argument name indicating the connection timeout.
|
||||
*/
|
||||
public static final String CONNECTION_TIMEOUT = "connectiontimeout";
|
||||
/**
|
||||
@@ -469,5 +555,24 @@ public final class CliParser {
|
||||
* directory.
|
||||
*/
|
||||
public static final String DATA_DIRECTORY_SHORT = "d";
|
||||
/**
|
||||
* The CLI argument name for setting the location of the data directory.
|
||||
*/
|
||||
public static final String VERBOSE_LOG = "log";
|
||||
/**
|
||||
* The short CLI argument name for setting the location of the data
|
||||
* directory.
|
||||
*/
|
||||
public static final String VERBOSE_LOG_SHORT = "l";
|
||||
/**
|
||||
* The CLI argument name for setting the location of the suppression
|
||||
* file.
|
||||
*/
|
||||
public static final String SUPPRESION_FILE = "suppression";
|
||||
/**
|
||||
* The short CLI argument name for setting the location of the
|
||||
* suppression file.
|
||||
*/
|
||||
public static final String SUPPRESION_FILE_SHORT = "sf";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ handlers=java.util.logging.ConsoleHandler
|
||||
# Configure the ConsoleHandler.
|
||||
java.util.logging.ConsoleHandler.level=INFO
|
||||
|
||||
org.owasp.dependencycheck.data.nvdcve.xml
|
||||
|
||||
# Configure the FileHandler.
|
||||
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
|
||||
java.util.logging.FileHandler.level=FINE
|
||||
@@ -21,4 +19,4 @@ java.util.logging.FileHandler.level=FINE
|
||||
# %g - generation number for rotating logs
|
||||
# %u - unique number to avoid conflicts
|
||||
# FileHandler writes to %h/demo0.log by default.
|
||||
java.util.logging.FileHandler.pattern=./logs/DependencyCheck.log
|
||||
java.util.logging.FileHandler.pattern=./dependency-check.log
|
||||
22
dependency-check-cli/src/site/markdown/arguments.md
Normal file
22
dependency-check-cli/src/site/markdown/arguments.md
Normal file
@@ -0,0 +1,22 @@
|
||||
Command Line Arguments
|
||||
====================
|
||||
|
||||
The following table lists the command line arguments:
|
||||
|
||||
Short | Argument Name | Parameter | Description | Requirement
|
||||
-------|-----------------------|-------------|-------------|------------
|
||||
\-a | \-\-app | \<name\> | The name of the application being scanned. This is a required argument. |
|
||||
\-c | \-\-connectiontimeout | \<timeout\> | The connection timeout (in milliseconds) to use when downloading resources. | Optional
|
||||
\-d | \-\-data | \<path\> | The location of the data directory used to store persistent data. This option should generally not be set. | Optional
|
||||
\-f | \-\-format | \<format\> | The output format to write to (XML, HTML, VULN, ALL). The default is HTML. |
|
||||
\-h | \-\-help | | Print the help message. | Optional
|
||||
\-l | \-\-log | \<file\> | The file path to write verbose logging information. | Optional
|
||||
\-n | \-\-noupdate | | Disables the automatic updating of the CPE data. | Optional
|
||||
\-o | \-\-out | \<folder\> | The folder to write reports to. This defaults to the current directory. | Optional
|
||||
\-p | \-\-proxyport | \<port\> | The proxy port to use when downloading resources. | Optional
|
||||
\-pp | \-\-proxypass | \<pass\> | The proxy password to use when downloading resources. | Optional
|
||||
\-pu | \-\-proxyuser | \<user\> | The proxy username to use when downloading resources. | Optional
|
||||
\-s | \-\-scan | \<path\> | The path to scan \- this option can be specified multiple times. |
|
||||
\-sf | \-\-suppression | \<file\> | The file path to the suppression XML file. | Optional
|
||||
\-u | \-\-proxyurl | \<url\> | The proxy url to use when downloading resources. | Optional
|
||||
\-v | \-\-version | | Print the version information. | Optional
|
||||
@@ -13,4 +13,11 @@ To scan a folder on the system you can run:
|
||||
dependency-check.bat --app "My App Name" --scan "c:\java\application\lib"
|
||||
|
||||
### \*nix
|
||||
dependency-check.sh --app "My App Name" --scan "/java/application/lib"
|
||||
dependency-check.sh --app "My App Name" --scan "/java/application/lib"
|
||||
|
||||
To view the command line arguments, see the <a href="arguments.html">arguments page</a>, or you can run:
|
||||
### Windows
|
||||
dependency-check.bat --help
|
||||
|
||||
### \*nix
|
||||
dependency-check.sh --help
|
||||
@@ -27,6 +27,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
</breadcrumbs>
|
||||
<menu name="Getting Started">
|
||||
<item name="Installation" href="installation.html"/>
|
||||
<item name="Configuration" href="arguments.html"/>
|
||||
</menu>
|
||||
<menu ref="Project Documentation" />
|
||||
<menu ref="reports" />
|
||||
|
||||
@@ -22,7 +22,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.0.3</version>
|
||||
<version>1.0.7</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-core</artifactId>
|
||||
@@ -410,17 +410,17 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-core</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-analyzers-common</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-queryparser</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
|
||||
@@ -20,6 +20,8 @@ package org.owasp.dependencycheck;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -32,8 +34,13 @@ import org.owasp.dependencycheck.analyzer.AnalysisPhase;
|
||||
import org.owasp.dependencycheck.analyzer.Analyzer;
|
||||
import org.owasp.dependencycheck.analyzer.AnalyzerService;
|
||||
import org.owasp.dependencycheck.data.CachedWebDataSource;
|
||||
import org.owasp.dependencycheck.data.NoDataException;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.data.UpdateService;
|
||||
import org.owasp.dependencycheck.data.cpe.CpeMemoryIndex;
|
||||
import org.owasp.dependencycheck.data.cpe.IndexException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
@@ -277,16 +284,28 @@ public class Engine {
|
||||
* Runs the analyzers against all of the dependencies.
|
||||
*/
|
||||
public void analyzeDependencies() {
|
||||
//need to ensure that data exists
|
||||
try {
|
||||
ensureDataExists();
|
||||
} catch (NoDataException ex) {
|
||||
final String msg = String.format("%n%n%s%n%nUnable to continue dependency-check analysis.", ex.getMessage());
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, null, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
//phase one initialize
|
||||
for (AnalysisPhase phase : AnalysisPhase.values()) {
|
||||
final List<Analyzer> analyzerList = analyzers.get(phase);
|
||||
for (Analyzer a : analyzerList) {
|
||||
try {
|
||||
final String msg = String.format("Initializing %s", a.getName());
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, msg);
|
||||
a.initialize();
|
||||
} catch (Exception ex) {
|
||||
final String msg = String.format("\"Exception occurred initializing \"%s\".\"", a.getName());
|
||||
final String msg = String.format("Exception occurred initializing %s.", a.getName());
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.INFO, msg, ex);
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.INFO, null, ex);
|
||||
try {
|
||||
a.close();
|
||||
} catch (Exception ex1) {
|
||||
@@ -305,9 +324,13 @@ public class Engine {
|
||||
* analyzers may modify it. This prevents ConcurrentModificationExceptions.
|
||||
* This is okay for adds/deletes because it happens per analyzer.
|
||||
*/
|
||||
final String msg = String.format("Begin Analyzer '%s'", a.getName());
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, msg);
|
||||
final Set<Dependency> dependencySet = new HashSet<Dependency>();
|
||||
dependencySet.addAll(dependencies);
|
||||
for (Dependency d : dependencySet) {
|
||||
final String msgFile = String.format("Begin Analysis of '%s'", d.getActualFilePath());
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, msgFile);
|
||||
if (a.supportsExtension(d.getFileExtension())) {
|
||||
try {
|
||||
a.analyze(d, this);
|
||||
@@ -323,6 +346,8 @@ public class Engine {
|
||||
for (AnalysisPhase phase : AnalysisPhase.values()) {
|
||||
final List<Analyzer> analyzerList = analyzers.get(phase);
|
||||
for (Analyzer a : analyzerList) {
|
||||
final String msg = String.format("Closing Analyzer '%s'", a.getName());
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, msg);
|
||||
try {
|
||||
a.close();
|
||||
} catch (Exception ex) {
|
||||
@@ -388,4 +413,36 @@ public class Engine {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private void ensureDataExists() throws NoDataException {
|
||||
final CpeMemoryIndex cpe = CpeMemoryIndex.getInstance();
|
||||
final CveDB cve = new CveDB();
|
||||
|
||||
try {
|
||||
cve.open();
|
||||
cpe.open(cve);
|
||||
} catch (IndexException ex) {
|
||||
throw new NoDataException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new NoDataException(ex);
|
||||
} catch (SQLException ex) {
|
||||
throw new NoDataException(ex);
|
||||
} catch (DatabaseException ex) {
|
||||
throw new NoDataException(ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new NoDataException(ex);
|
||||
} finally {
|
||||
cve.close();
|
||||
}
|
||||
if (cpe.numDocs() <= 0) {
|
||||
cpe.close();
|
||||
throw new NoDataException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionParseException;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionParser;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* Abstract base suppression analyzer that contains methods for parsing the
|
||||
* suppression xml file.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implmentation details of Analyzer">
|
||||
/**
|
||||
* Returns a list of file EXTENSIONS supported by this analyzer.
|
||||
*
|
||||
* @return a list of file EXTENSIONS supported by this analyzer.
|
||||
*/
|
||||
public Set<String> getSupportedExtensions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this analyzer can process the given extension.
|
||||
*
|
||||
* @param extension the file extension to test for support.
|
||||
* @return whether or not the specified file extension is supported by this
|
||||
* analyzer.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsExtension(String extension) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//</editor-fold>
|
||||
/**
|
||||
* The initialize method loads the suppression XML file.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception
|
||||
*/
|
||||
@Override
|
||||
public void initialize() throws Exception {
|
||||
super.initialize();
|
||||
loadSuppressionData();
|
||||
}
|
||||
/**
|
||||
* The list of suppression rules
|
||||
*/
|
||||
private List<SuppressionRule> rules;
|
||||
|
||||
/**
|
||||
* Get the value of rules.
|
||||
*
|
||||
* @return the value of rules
|
||||
*/
|
||||
public List<SuppressionRule> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of rules.
|
||||
*
|
||||
* @param rules new value of rules
|
||||
*/
|
||||
public void setRules(List<SuppressionRule> rules) {
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the suppression rules file.
|
||||
*
|
||||
* @throws SuppressionParseException thrown if the XML cannot be parsed.
|
||||
*/
|
||||
private void loadSuppressionData() throws SuppressionParseException {
|
||||
final File file = Settings.getFile(Settings.KEYS.SUPPRESSION_FILE);
|
||||
if (file != null) {
|
||||
final SuppressionParser parser = new SuppressionParser();
|
||||
try {
|
||||
rules = parser.parseSuppressionRules(file);
|
||||
} catch (SuppressionParseException ex) {
|
||||
final String msg = String.format("Unable to parse suppression xml file '%s'", file.getPath());
|
||||
Logger.getLogger(AbstractSuppressionAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(AbstractSuppressionAnalyzer.class.getName()).log(Level.WARNING, ex.getMessage());
|
||||
Logger.getLogger(AbstractSuppressionAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class AnalysisException extends Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DownloadFailedException.
|
||||
* Creates a new AnalysisException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the failure.
|
||||
|
||||
@@ -32,29 +32,21 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
//import java.util.zip.ZipEntry;
|
||||
//import java.util.zip.ZipException;
|
||||
//import java.util.zip.ZipInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||
import org.apache.commons.compress.compressors.CompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipUtils;
|
||||
import org.h2.store.fs.FileUtils;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* <p>An analyzer that works on archive files:
|
||||
* <ul>
|
||||
* <li><b>ZIP</b> - if it is determined to be a JAR, WAR or EAR a copy is made
|
||||
* and the copy is given the correct extension so that it will be correctly
|
||||
* analyzed.</li>
|
||||
* <li><b>WAR</b> - the WAR contents are extracted and added as dependencies to
|
||||
* the scan. The displayed path is relative to the WAR.</li>
|
||||
* <li><b>EAR</b> - the WAR contents are extracted and added as dependencies to
|
||||
* the scan. Any WAR files are also processed so that the contained JAR files
|
||||
* are added to the list of dependencies. The displayed path is relative to the
|
||||
* EAR.</li>
|
||||
* </ul></p>
|
||||
* <p>An analyzer that extracts files from archives and ensures any supported
|
||||
* files contained within the archive are added to the dependency list.</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
@@ -94,7 +86,7 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
/**
|
||||
* The set of file extensions supported by this analyzer.
|
||||
*/
|
||||
private static final Set<String> EXTENSIONS = newHashSet("zip", "ear", "war");
|
||||
private static final Set<String> EXTENSIONS = newHashSet("zip", "ear", "war", "tar", "gz", "tgz");
|
||||
|
||||
/**
|
||||
* Returns a list of file EXTENSIONS supported by this analyzer.
|
||||
@@ -145,14 +137,19 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
public void initialize() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
if (!baseDir.exists()) {
|
||||
baseDir.mkdirs();
|
||||
if (!baseDir.mkdirs()) {
|
||||
final String msg = String.format("Unable to make a temporary folder '%s'", baseDir.getPath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
}
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
throw new AnalysisException("Unable to delete temporary file '" + tempFileLocation.getAbsolutePath() + "'.");
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
throw new AnalysisException("Unable to create directory '" + tempFileLocation.getAbsolutePath() + "'.");
|
||||
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +225,13 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
private File getNextTempDirectory() throws AnalysisException {
|
||||
dirCount += 1;
|
||||
final File directory = new File(tempFileLocation, String.valueOf(dirCount));
|
||||
//getting an exception for some directories not being able to be created; might be because the directory already exists?
|
||||
if (directory.exists()) {
|
||||
return getNextTempDirectory();
|
||||
}
|
||||
if (!directory.mkdirs()) {
|
||||
throw new AnalysisException("Unable to create temp directory '" + directory.getAbsolutePath() + "'.");
|
||||
final String msg = String.format("Unable to create temp directory '%s'.", directory.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
@@ -238,37 +240,75 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
* Extracts the contents of an archive into the specified directory.
|
||||
*
|
||||
* @param archive an archive file such as a WAR or EAR
|
||||
* @param extractTo a directory to extract the contents to
|
||||
* @param destination a directory to extract the contents to
|
||||
* @param engine the scanning engine
|
||||
* @throws AnalysisException thrown if the archive is not found
|
||||
*/
|
||||
private void extractFiles(File archive, File extractTo, Engine engine) throws AnalysisException {
|
||||
if (archive == null || extractTo == null) {
|
||||
private void extractFiles(File archive, File destination, Engine engine) throws AnalysisException {
|
||||
if (archive == null || destination == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileInputStream fis = null;
|
||||
//ZipInputStream zis = null;
|
||||
ZipArchiveInputStream zis = null;
|
||||
|
||||
try {
|
||||
fis = new FileInputStream(archive);
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.INFO, null, ex);
|
||||
throw new AnalysisException("Archive file was not found.", ex);
|
||||
}
|
||||
zis = new ZipArchiveInputStream(new BufferedInputStream(fis));
|
||||
ZipArchiveEntry entry;
|
||||
|
||||
final String archiveExt = org.owasp.dependencycheck.utils.FileUtils.getFileExtension(archive.getName()).toLowerCase();
|
||||
try {
|
||||
while ((entry = zis.getNextZipEntry()) != null) {
|
||||
if ("zip".equals(archiveExt) || "war".equals(archiveExt) || "ear".equals(archiveExt)) {
|
||||
extractArchive(new ZipArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
||||
} else if ("tar".equals(archiveExt)) {
|
||||
extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
||||
} else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
|
||||
final String uncompressedName = GzipUtils.getUncompressedFilename(archive.getName());
|
||||
final String uncompressedExt = org.owasp.dependencycheck.utils.FileUtils.getFileExtension(uncompressedName).toLowerCase();
|
||||
if (engine.supportsExtension(uncompressedExt)) {
|
||||
decompressFile(new GzipCompressorInputStream(new BufferedInputStream(fis)), new File(destination, uncompressedName));
|
||||
}
|
||||
}
|
||||
} catch (ArchiveExtractionException ex) {
|
||||
final String msg = String.format("Exception extracting archive '%s'.", archive.getName());
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Exception reading archive '%s'.", archive.getName());
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts files from an archive.
|
||||
*
|
||||
* @param input the archive to extract files from
|
||||
* @param destination the location to write the files too
|
||||
* @param engine the dependency-check engine
|
||||
* @throws ArchiveExtractionException thrown if there is an exception
|
||||
* extracting files from the archive
|
||||
*/
|
||||
private void extractArchive(ArchiveInputStream input, File destination, Engine engine) throws ArchiveExtractionException {
|
||||
ArchiveEntry entry;
|
||||
try {
|
||||
while ((entry = input.getNextEntry()) != null) {
|
||||
if (entry.isDirectory()) {
|
||||
final File d = new File(extractTo, entry.getName());
|
||||
if (!d.mkdirs()) {
|
||||
throw new AnalysisException("Unable to create '" + d.getAbsolutePath() + "'.");
|
||||
final File d = new File(destination, entry.getName());
|
||||
if (!d.exists()) {
|
||||
if (!d.mkdirs()) {
|
||||
final String msg = String.format("Unable to create '%s'.", d.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final File file = new File(extractTo, entry.getName());
|
||||
final File file = new File(destination, entry.getName());
|
||||
final String ext = org.owasp.dependencycheck.utils.FileUtils.getFileExtension(file.getName());
|
||||
if (engine.supportsExtension(ext)) {
|
||||
BufferedOutputStream bos = null;
|
||||
@@ -278,22 +318,27 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
bos = new BufferedOutputStream(fos, BUFFER_SIZE);
|
||||
int count;
|
||||
final byte data[] = new byte[BUFFER_SIZE];
|
||||
while ((count = zis.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
while ((count = input.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
bos.write(data, 0, count);
|
||||
}
|
||||
bos.flush();
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException("Unable to find file '" + file.getName() + "'.", ex);
|
||||
Logger.getLogger(ArchiveAnalyzer.class
|
||||
.getName()).log(Level.FINE, null, ex);
|
||||
final String msg = String.format("Unable to find file '%s'.", file.getName());
|
||||
throw new AnalysisException(msg, ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException("IO Exception while parsing file '" + file.getName() + "'.", ex);
|
||||
Logger.getLogger(ArchiveAnalyzer.class
|
||||
.getName()).log(Level.FINE, null, ex);
|
||||
final String msg = String.format("IO Exception while parsing file '%s'.", file.getName());
|
||||
throw new AnalysisException(msg, ex);
|
||||
} finally {
|
||||
if (bos != null) {
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINEST, null, ex);
|
||||
Logger.getLogger(ArchiveAnalyzer.class
|
||||
.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,20 +346,50 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Exception reading archive '%s'.", archive.getName());
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException(msg, ex);
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} catch (Throwable ex) {
|
||||
final String msg = String.format("Exception reading archive '%s'.", archive.getName());
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.WARNING, null, ex);
|
||||
throw new AnalysisException(msg, ex);
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} finally {
|
||||
try {
|
||||
zis.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINEST, null, ex);
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompresses a file.
|
||||
*
|
||||
* @param inputStream the compressed file
|
||||
* @param outputFile the location to write the decompressed file
|
||||
* @throws ArchiveExtractionException thrown if there is an exception
|
||||
* decompressing the file
|
||||
*/
|
||||
private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException {
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(outputFile);
|
||||
final byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int n = 0;
|
||||
while (-1 != (n = inputStream.read(buffer))) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new ArchiveExtractionException(ex);
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ArchiveAnalyzer.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
/**
|
||||
* An exception thrown when files in an archive cannot be extracted.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class ArchiveExtractionException extends Exception {
|
||||
|
||||
/**
|
||||
* The serial version UID for serialization.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new ArchiveExtractionException.
|
||||
*/
|
||||
public ArchiveExtractionException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ArchiveExtractionException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
public ArchiveExtractionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ArchiveExtractionException.
|
||||
*
|
||||
* @param ex the cause of the failure.
|
||||
*/
|
||||
public ArchiveExtractionException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ArchiveExtractionException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the failure.
|
||||
*/
|
||||
public ArchiveExtractionException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,10 @@ import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.dependency.Evidence.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.data.cpe.CpeIndexReader;
|
||||
import org.owasp.dependencycheck.data.cpe.CpeMemoryIndex;
|
||||
import org.owasp.dependencycheck.data.cpe.Fields;
|
||||
import org.owasp.dependencycheck.data.cpe.IndexEntry;
|
||||
import org.owasp.dependencycheck.data.cpe.IndexException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
@@ -83,9 +84,9 @@ public class CPEAnalyzer implements Analyzer {
|
||||
*/
|
||||
static final int STRING_BUILDER_BUFFER = 20;
|
||||
/**
|
||||
* The CPE Index Reader.
|
||||
* The CPE in memory index.
|
||||
*/
|
||||
private CpeIndexReader cpe;
|
||||
private CpeMemoryIndex cpe;
|
||||
/**
|
||||
* The CVE Database.
|
||||
*/
|
||||
@@ -100,8 +101,6 @@ public class CPEAnalyzer implements Analyzer {
|
||||
* usually occurs when the database is in use by another process.
|
||||
*/
|
||||
public void open() throws IOException, DatabaseException {
|
||||
cpe = new CpeIndexReader();
|
||||
cpe.open();
|
||||
cve = new CveDB();
|
||||
try {
|
||||
cve.open();
|
||||
@@ -112,10 +111,17 @@ public class CPEAnalyzer implements Analyzer {
|
||||
Logger.getLogger(CPEAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to open the cve db", ex);
|
||||
}
|
||||
cpe = CpeMemoryIndex.getInstance();
|
||||
try {
|
||||
cpe.open(cve);
|
||||
} catch (IndexException ex) {
|
||||
Logger.getLogger(CPEAnalyzer.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new DatabaseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the data source.
|
||||
* Closes the data sources.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
@@ -127,28 +133,6 @@ public class CPEAnalyzer implements Analyzer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the data source - is the index open.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return (cpe != null) && cpe.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the Lucene index is closed.
|
||||
*
|
||||
* @throws Throwable when a throwable is thrown.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the data store of CPE entries, trying to identify the CPE for
|
||||
* the given dependency based on the evidence contained within. The
|
||||
@@ -444,6 +428,9 @@ public class CPEAnalyzer implements Analyzer {
|
||||
//</editor-fold>
|
||||
|
||||
//TODO - likely need to change the split... not sure if this will work for CPE with special chars
|
||||
if (text == null) {
|
||||
return false;
|
||||
}
|
||||
final String[] words = text.split("[\\s_-]");
|
||||
final List<String> list = new ArrayList<String>();
|
||||
String tempWord = null;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
|
||||
/**
|
||||
* The suppression analyzer processes an externally defined XML document that
|
||||
* complies with the suppressions.xsd schema. Any identified CPE entries within
|
||||
* the dependencies that match will be removed.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class CpeSuppressionAnalyzer extends AbstractSuppressionAnalyzer {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implmentation details of Analyzer">
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "Cpe Suppression Analyzer";
|
||||
/**
|
||||
* The phase that this analyzer is intended to run in.
|
||||
*/
|
||||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.POST_IDENTIFIER_ANALYSIS;
|
||||
|
||||
/**
|
||||
* Returns the name of the analyzer.
|
||||
*
|
||||
* @return the name of the analyzer.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return ANALYZER_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the phase that the analyzer is intended to run in.
|
||||
*
|
||||
* @return the phase that the analyzer is intended to run in.
|
||||
*/
|
||||
@Override
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
@Override
|
||||
public void analyze(final Dependency dependency, final Engine engine) throws AnalysisException {
|
||||
|
||||
if (getRules() == null || getRules().size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final SuppressionRule rule : getRules()) {
|
||||
rule.process(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,15 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersion;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
import org.owasp.dependencycheck.utils.LogUtils;
|
||||
|
||||
/**
|
||||
* <p>This analyzer ensures dependencies that should be grouped together, to
|
||||
@@ -144,16 +147,14 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
}
|
||||
dependenciesToRemove.add(nextDependency);
|
||||
} else {
|
||||
if (isCore(nextDependency, dependency)) {
|
||||
nextDependency.addRelatedDependency(dependency);
|
||||
//move any "related dependencies" to the new "parent" dependency
|
||||
final Iterator<Dependency> i = dependency.getRelatedDependencies().iterator();
|
||||
while (i.hasNext()) {
|
||||
nextDependency.addRelatedDependency(i.next());
|
||||
i.remove();
|
||||
}
|
||||
dependenciesToRemove.add(dependency);
|
||||
nextDependency.addRelatedDependency(dependency);
|
||||
//move any "related dependencies" to the new "parent" dependency
|
||||
final Iterator<Dependency> i = dependency.getRelatedDependencies().iterator();
|
||||
while (i.hasNext()) {
|
||||
nextDependency.addRelatedDependency(i.next());
|
||||
i.remove();
|
||||
}
|
||||
dependenciesToRemove.add(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,8 +261,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
|| dependency2 == null || dependency2.getIdentifiers() == null) {
|
||||
return false;
|
||||
}
|
||||
return dependency1.getIdentifiers().size() > 0
|
||||
final boolean matches = dependency1.getIdentifiers().size() > 0
|
||||
&& dependency2.getIdentifiers().equals(dependency1.getIdentifiers());
|
||||
if (LogUtils.isVerboseLoggingEnabled()) {
|
||||
final String msg = String.format("IdentifiersMatch=%s (%s, %s)", matches, dependency1.getFileName(), dependency2.getFileName());
|
||||
Logger.getLogger(DependencyBundlingAnalyzer.class.getName()).log(Level.FINE, msg);
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,10 +305,6 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
* This is likely a very broken attempt at determining if the 'left'
|
||||
* dependency is the 'core' library in comparison to the 'right' library.
|
||||
*
|
||||
* TODO - consider splitting on /\._-\s/ and checking if all of one side is
|
||||
* fully contained in the other With the exception of the word "core". This
|
||||
* might work even on groups when we don't have a CVE.
|
||||
*
|
||||
* @param left the dependency to test
|
||||
* @param right the dependency to test against
|
||||
* @return a boolean indicating whether or not the left dependency should be
|
||||
@@ -311,18 +313,31 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
private boolean isCore(Dependency left, Dependency right) {
|
||||
final String leftName = left.getFileName().toLowerCase();
|
||||
final String rightName = right.getFileName().toLowerCase();
|
||||
|
||||
final boolean returnVal;
|
||||
if (rightName.contains("core") && !leftName.contains("core")) {
|
||||
return false;
|
||||
returnVal = false;
|
||||
} else if (!rightName.contains("core") && leftName.contains("core")) {
|
||||
return true;
|
||||
returnVal = true;
|
||||
} else {
|
||||
//TODO should we be splitting the name on [-_(.\d)+] and seeing if the
|
||||
// parts are contained in the other side?
|
||||
/*
|
||||
* considered splitting the names up and comparing the components,
|
||||
* but decided that the file name length should be sufficient as the
|
||||
* "core" component, if this follows a normal namming protocol should
|
||||
* be shorter:
|
||||
* axis2-saaj-1.4.1.jar
|
||||
* axis2-1.4.1.jar <-----
|
||||
* axis2-kernal-1.4.1.jar
|
||||
*/
|
||||
if (leftName.length() > rightName.length()) {
|
||||
return false;
|
||||
returnVal = false;
|
||||
} else {
|
||||
returnVal = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (LogUtils.isVerboseLoggingEnabled()) {
|
||||
final String msg = String.format("IsCore=%s (%s, %s)", returnVal, left.getFileName(), right.getFileName());
|
||||
Logger.getLogger(DependencyBundlingAnalyzer.class.getName()).log(Level.FINE, msg);
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
removeJreEntries(dependency);
|
||||
removeBadMatches(dependency);
|
||||
removeWrongVersionMatches(dependency);
|
||||
removeSpuriousCPE(dependency);
|
||||
addFalseNegativeCPEs(dependency);
|
||||
}
|
||||
@@ -129,6 +130,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
*/
|
||||
@SuppressWarnings("null")
|
||||
private void removeSpuriousCPE(Dependency dependency) {
|
||||
final List<Identifier> ids = new ArrayList<Identifier>();
|
||||
ids.addAll(dependency.getIdentifiers());
|
||||
@@ -291,6 +293,40 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes CPE matches for the wrong version of a dependency. Currently,
|
||||
* this only covers Axis 1 & 2.
|
||||
*
|
||||
* @param dependency the dependency to analyze
|
||||
*/
|
||||
private void removeWrongVersionMatches(Dependency dependency) {
|
||||
final Set<Identifier> identifiers = dependency.getIdentifiers();
|
||||
final Iterator<Identifier> itr = identifiers.iterator();
|
||||
|
||||
final String fileName = dependency.getFileName();
|
||||
if (fileName != null && fileName.contains("axis2")) {
|
||||
while (itr.hasNext()) {
|
||||
final Identifier i = itr.next();
|
||||
if ("cpe".equals(i.getType())) {
|
||||
final String cpe = i.getValue();
|
||||
if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis:") || "cpe:/a:apache:axis".equals(cpe))) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fileName != null && fileName.contains("axis")) {
|
||||
while (itr.hasNext()) {
|
||||
final Identifier i = itr.next();
|
||||
if ("cpe".equals(i.getType())) {
|
||||
final String cpe = i.getValue();
|
||||
if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis2:") || "cpe:/a:apache:axis2".equals(cpe))) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There are some known CPE entries, specifically regarding sun and oracle
|
||||
* products due to the acquisition and changes in product names, that based
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
|
||||
/**
|
||||
* The suppression analyzer processes an externally defined XML document that
|
||||
* complies with the suppressions.xsd schema. Any identified Vulnerability
|
||||
* entries within the dependencies that match will be removed.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class VulnerabilitySuppressionAnalyzer extends AbstractSuppressionAnalyzer {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implmentation details of Analyzer">
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "Vulnerability Suppression Analyzer";
|
||||
/**
|
||||
* The phase that this analyzer is intended to run in.
|
||||
*/
|
||||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.POST_FINDING_ANALYSIS;
|
||||
|
||||
/**
|
||||
* Returns the name of the analyzer.
|
||||
*
|
||||
* @return the name of the analyzer.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return ANALYZER_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the phase that the analyzer is intended to run in.
|
||||
*
|
||||
* @return the phase that the analyzer is intended to run in.
|
||||
*/
|
||||
@Override
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
@Override
|
||||
public void analyze(final Dependency dependency, final Engine engine) throws AnalysisException {
|
||||
|
||||
if (getRules() == null || getRules().size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final SuppressionRule rule : getRules()) {
|
||||
rule.process(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import static org.owasp.dependencycheck.data.nvdcve.CveDB.DB_SCHEMA_VERSION;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class BaseDB {
|
||||
|
||||
/**
|
||||
* Resource location for SQL file used to create the database schema.
|
||||
*/
|
||||
public static final String DB_STRUCTURE_RESOURCE = "data/initialize.sql";
|
||||
/**
|
||||
* The version of the current DB Schema.
|
||||
*/
|
||||
public static final String DB_SCHEMA_VERSION = "2.7";
|
||||
/**
|
||||
* Database connection
|
||||
*/
|
||||
private Connection conn;
|
||||
|
||||
/**
|
||||
* Returns the database connection.
|
||||
*
|
||||
* @return the database connection
|
||||
*/
|
||||
protected Connection getConnection() {
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the database connection. If the database does not exist, it will
|
||||
* create a new one.
|
||||
*
|
||||
* @throws IOException thrown if there is an IO Exception
|
||||
* @throws SQLException thrown if there is a SQL Exception
|
||||
* @throws DatabaseException thrown if there is an error initializing a new
|
||||
* database
|
||||
* @throws ClassNotFoundException thrown if the h2 database driver cannot be
|
||||
* loaded
|
||||
*/
|
||||
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
|
||||
value = "DMI_EMPTY_DB_PASSWORD",
|
||||
justification = "Yes, I know... Blank password.")
|
||||
public void open() throws IOException, SQLException, DatabaseException, ClassNotFoundException {
|
||||
final String fileName = CveDB.getDataDirectory().getCanonicalPath();
|
||||
final File f = new File(fileName, "cve." + DB_SCHEMA_VERSION);
|
||||
final File check = new File(f.getAbsolutePath() + ".h2.db");
|
||||
final boolean createTables = !check.exists();
|
||||
final String connStr = String.format("jdbc:h2:file:%s;AUTO_SERVER=TRUE", f.getAbsolutePath());
|
||||
Class.forName("org.h2.Driver");
|
||||
conn = DriverManager.getConnection(connStr, "sa", "");
|
||||
if (createTables) {
|
||||
createTables();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the DB4O database. Close should be called on this object when it
|
||||
* is done being used.
|
||||
*/
|
||||
public void close() {
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException ex) {
|
||||
final String msg = "There was an error attempting to close the CveDB, see the log for more details.";
|
||||
Logger.getLogger(BaseDB.class.getName()).log(Level.SEVERE, msg, ex);
|
||||
Logger.getLogger(BaseDB.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits all completed transactions.
|
||||
*
|
||||
* @throws SQLException thrown if a SQL Exception occurs
|
||||
*/
|
||||
public void commit() throws SQLException {
|
||||
if (conn != null) {
|
||||
conn.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the object and ensures that "close" has been called.
|
||||
*
|
||||
* @throws Throwable thrown if there is a problem
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close();
|
||||
super.finalize(); //not necessary if extending Object.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database structure (tables and indexes) to store the CVE data
|
||||
*
|
||||
* @throws SQLException thrown if there is a sql exception
|
||||
* @throws DatabaseException thrown if there is a database exception
|
||||
*/
|
||||
private void createTables() throws SQLException, DatabaseException {
|
||||
InputStream is;
|
||||
InputStreamReader reader;
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
is = this.getClass().getClassLoader().getResourceAsStream(DB_STRUCTURE_RESOURCE);
|
||||
reader = new InputStreamReader(is, "UTF-8");
|
||||
in = new BufferedReader(reader);
|
||||
final StringBuilder sb = new StringBuilder(2110);
|
||||
String tmp;
|
||||
while ((tmp = in.readLine()) != null) {
|
||||
sb.append(tmp);
|
||||
}
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = conn.createStatement();
|
||||
statement.execute(sb.toString());
|
||||
} finally {
|
||||
closeStatement(statement);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new DatabaseException("Unable to create database schema", ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CveDB.class
|
||||
.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the directory that the JAR file exists in so that we can ensure
|
||||
* we always use a common data directory.
|
||||
*
|
||||
* @return the data directory for this index.
|
||||
* @throws IOException is thrown if an IOException occurs of course...
|
||||
*/
|
||||
public static File getDataDirectory() throws IOException {
|
||||
final File path = Settings.getDataFile(Settings.KEYS.CVE_DATA_DIRECTORY);
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
throw new IOException("Unable to create NVD CVE Data directory");
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generated integer primary key for a newly inserted row.
|
||||
*
|
||||
* @param statement a prepared statement that just executed an insert
|
||||
* @return a primary key
|
||||
* @throws DatabaseException thrown if there is an exception obtaining the
|
||||
* key
|
||||
*/
|
||||
protected int getGeneratedKey(PreparedStatement statement) throws DatabaseException {
|
||||
ResultSet rs = null;
|
||||
int id = 0;
|
||||
try {
|
||||
rs = statement.getGeneratedKeys();
|
||||
rs.next();
|
||||
id = rs.getInt(1);
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Unable to get primary key for inserted row");
|
||||
} finally {
|
||||
closeResultSet(rs);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the given statement object ignoring any exceptions that occur.
|
||||
*
|
||||
* @param statement a Statement object
|
||||
*/
|
||||
public void closeStatement(Statement statement) {
|
||||
if (statement != null) {
|
||||
try {
|
||||
statement.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class
|
||||
.getName()).log(Level.FINEST, statement.toString(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the result set capturing and ignoring any SQLExceptions that
|
||||
* occur.
|
||||
*
|
||||
* @param rs a ResultSet to close
|
||||
*/
|
||||
public void closeResultSet(ResultSet rs) {
|
||||
if (rs != null) {
|
||||
try {
|
||||
rs.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class
|
||||
.getName()).log(Level.FINEST, rs.toString(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception used when the data needed does not exist to perform analysis.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class NoDataException extends IOException {
|
||||
|
||||
/**
|
||||
* The serial version uid.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new NoDataException.
|
||||
*/
|
||||
public NoDataException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new NoDataException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
public NoDataException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new NoDataException.
|
||||
*
|
||||
* @param ex the cause of the exception.
|
||||
*/
|
||||
public NoDataException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new NoDataException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the exception.
|
||||
*/
|
||||
public NoDataException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* The Base Index class used to access the CPE Index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public abstract class BaseIndex {
|
||||
|
||||
/**
|
||||
* The Lucene directory containing the index.
|
||||
*/
|
||||
private Directory directory;
|
||||
/**
|
||||
* Indicates whether or not the Lucene Index is open.
|
||||
*/
|
||||
private boolean indexOpen = false;
|
||||
|
||||
/**
|
||||
* Gets the directory.
|
||||
*
|
||||
* @return the directory
|
||||
*/
|
||||
public Directory getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the CPE Index.
|
||||
*
|
||||
* @throws IOException is thrown if an IOException occurs opening the index.
|
||||
*/
|
||||
public void open() throws IOException {
|
||||
directory = this.openDirectory();
|
||||
indexOpen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CPE Index.
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
directory.close();
|
||||
} catch (IOException ex) {
|
||||
final String msg = "Unable to update database due to an IO error.";
|
||||
Logger.getLogger(BaseIndex.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(BaseIndex.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
directory = null;
|
||||
}
|
||||
indexOpen = false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the data source - is the index open.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return indexOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Lucene directory object for the CPE Index.
|
||||
*
|
||||
* @return the Lucene Directory object for the CPE Index.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
protected Directory openDirectory() throws IOException {
|
||||
final File path = getDataDirectory();
|
||||
return FSDirectory.open(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the directory that the JAR file exists in so that we can ensure
|
||||
* we always use a common data directory.
|
||||
*
|
||||
* @return the data directory for this index.
|
||||
* @throws IOException is thrown if an IOException occurs of course...
|
||||
*/
|
||||
public static File getDataDirectory() throws IOException {
|
||||
final File path = Settings.getFile(Settings.KEYS.CPE_DATA_DIRECTORY);
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
throw new IOException("Unable to create CPE Data directory");
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.core.KeywordAnalyzer;
|
||||
import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
import org.apache.lucene.queryparser.classic.QueryParser;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.util.Version;
|
||||
import org.owasp.dependencycheck.data.lucene.FieldAnalyzer;
|
||||
import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class CpeIndexReader extends BaseIndex {
|
||||
|
||||
/**
|
||||
* The Lucene IndexReader.
|
||||
*/
|
||||
private IndexReader indexReader;
|
||||
/**
|
||||
* The Lucene IndexSearcher.
|
||||
*/
|
||||
private IndexSearcher indexSearcher;
|
||||
/**
|
||||
* The Lucene Analyzer used for Searching.
|
||||
*/
|
||||
private Analyzer searchingAnalyzer;
|
||||
/**
|
||||
* The Lucene QueryParser used for Searching.
|
||||
*/
|
||||
private QueryParser queryParser;
|
||||
/**
|
||||
* The search field analyzer for the product field.
|
||||
*/
|
||||
private SearchFieldAnalyzer productSearchFieldAnalyzer;
|
||||
/**
|
||||
* The search field analyzer for the vendor field.
|
||||
*/
|
||||
private SearchFieldAnalyzer vendorSearchFieldAnalyzer;
|
||||
|
||||
/**
|
||||
* Opens the CPE Index.
|
||||
*
|
||||
* @throws IOException is thrown if an IOException occurs opening the index.
|
||||
*/
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
//TODO add spinlock (shared)
|
||||
super.open();
|
||||
indexReader = DirectoryReader.open(getDirectory());
|
||||
indexSearcher = new IndexSearcher(indexReader);
|
||||
searchingAnalyzer = createSearchingAnalyzer();
|
||||
queryParser = new QueryParser(Version.LUCENE_43, Fields.DOCUMENT_KEY, searchingAnalyzer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CPE Index.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
//TODO remove spinlock (shared)
|
||||
if (searchingAnalyzer != null) {
|
||||
searchingAnalyzer.close();
|
||||
searchingAnalyzer = null;
|
||||
}
|
||||
if (indexReader != null) {
|
||||
try {
|
||||
indexReader.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CpeIndexReader.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
indexReader = null;
|
||||
}
|
||||
queryParser = null;
|
||||
indexSearcher = null;
|
||||
super.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the index using the given search string.
|
||||
*
|
||||
* @param searchString the query text
|
||||
* @param maxQueryResults the maximum number of documents to return
|
||||
* @return the TopDocs found by the search
|
||||
* @throws ParseException thrown when the searchString is invalid
|
||||
* @throws IOException is thrown if there is an issue with the underlying
|
||||
* Index
|
||||
*/
|
||||
public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException {
|
||||
if (searchString == null || searchString.trim().isEmpty()) {
|
||||
throw new ParseException("Query is null or empty");
|
||||
}
|
||||
if (queryParser == null) {
|
||||
if (isOpen()) {
|
||||
final String msg = String.format("QueryParser is null for query: '%s'. Attempting to reopen index.",
|
||||
searchString);
|
||||
Logger.getLogger(CpeIndexReader.class.getName()).log(Level.WARNING, msg);
|
||||
close();
|
||||
open();
|
||||
} else {
|
||||
final String msg = String.format("QueryParser is null, but data source is open, for query: '%s'. Attempting to reopen index.",
|
||||
searchString);
|
||||
Logger.getLogger(CpeIndexReader.class.getName()).log(Level.WARNING, msg);
|
||||
close();
|
||||
open();
|
||||
}
|
||||
}
|
||||
final Query query = queryParser.parse(searchString);
|
||||
return indexSearcher.search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the index using the given query.
|
||||
*
|
||||
* @param query the query used to search the index
|
||||
* @param maxQueryResults the max number of results to return
|
||||
* @return the TopDocs found be the query
|
||||
* @throws CorruptIndexException thrown if the Index is corrupt
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public TopDocs search(Query query, int maxQueryResults) throws CorruptIndexException, IOException {
|
||||
resetSearchingAnalyzer();
|
||||
return indexSearcher.search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a document from the Index.
|
||||
*
|
||||
* @param documentId the id of the document to retrieve
|
||||
* @return the Document
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public Document getDocument(int documentId) throws IOException {
|
||||
return indexSearcher.doc(documentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Analyzer for searching the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Analyzer createSearchingAnalyzer() {
|
||||
final Map fieldAnalyzers = new HashMap();
|
||||
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
|
||||
productSearchFieldAnalyzer = new SearchFieldAnalyzer(Version.LUCENE_43);
|
||||
vendorSearchFieldAnalyzer = new SearchFieldAnalyzer(Version.LUCENE_43);
|
||||
fieldAnalyzers.put(Fields.PRODUCT, productSearchFieldAnalyzer);
|
||||
fieldAnalyzers.put(Fields.VENDOR, vendorSearchFieldAnalyzer);
|
||||
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(Version.LUCENE_43), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the searching analyzers
|
||||
*/
|
||||
private void resetSearchingAnalyzer() {
|
||||
if (productSearchFieldAnalyzer != null) {
|
||||
productSearchFieldAnalyzer.clear();
|
||||
}
|
||||
if (vendorSearchFieldAnalyzer != null) {
|
||||
vendorSearchFieldAnalyzer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.core.KeywordAnalyzer;
|
||||
import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.util.Version;
|
||||
import org.owasp.dependencycheck.data.lucene.FieldAnalyzer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class CpeIndexWriter extends BaseIndex {
|
||||
|
||||
/**
|
||||
* The IndexWriter for the Lucene index.
|
||||
*/
|
||||
private IndexWriter indexWriter;
|
||||
/**
|
||||
* The Lucene Analyzer used for Indexing.
|
||||
*/
|
||||
private Analyzer indexingAnalyzer;
|
||||
|
||||
/**
|
||||
* Opens the CPE Index.
|
||||
*
|
||||
* @throws IOException is thrown if an IOException occurs opening the index.
|
||||
*/
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
//TODO add spinlock
|
||||
super.open();
|
||||
indexingAnalyzer = createIndexingAnalyzer();
|
||||
final IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_43, indexingAnalyzer);
|
||||
indexWriter = new IndexWriter(getDirectory(), conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CPE Index.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
//TODO remove spinlock
|
||||
if (indexWriter != null) {
|
||||
commit();
|
||||
try {
|
||||
indexWriter.close(true);
|
||||
} catch (CorruptIndexException ex) {
|
||||
final String msg = "Unable to update database, there is a corrupt index.";
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.FINE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
final String msg = "Unable to update database due to an IO error.";
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
indexWriter = null;
|
||||
}
|
||||
}
|
||||
if (indexingAnalyzer != null) {
|
||||
indexingAnalyzer.close();
|
||||
indexingAnalyzer = null;
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits any pending changes.
|
||||
*/
|
||||
public void commit() {
|
||||
if (indexWriter != null) {
|
||||
try {
|
||||
indexWriter.forceMerge(1);
|
||||
indexWriter.commit();
|
||||
} catch (CorruptIndexException ex) {
|
||||
final String msg = "Unable to update database, there is a corrupt index.";
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.FINE, null, ex);
|
||||
} catch (IOException ex) {
|
||||
final String msg = "Unable to update database due to an IO error.";
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CpeIndexWriter.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the indexing analyzer for the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Analyzer createIndexingAnalyzer() {
|
||||
final Map fieldAnalyzers = new HashMap();
|
||||
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(Version.LUCENE_43), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a CPE IndexEntry into the Lucene index.
|
||||
*
|
||||
* @param entry a CPE entry.
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt.
|
||||
* @throws IOException is thrown if an IOException occurs.
|
||||
*/
|
||||
public void saveEntry(IndexEntry entry) throws CorruptIndexException, IOException {
|
||||
final Document doc = new Document();
|
||||
final Field documentKey = new StringField(Fields.DOCUMENT_KEY, entry.getDocumentId(), Field.Store.NO);
|
||||
final Field vendor = new TextField(Fields.VENDOR, entry.getVendor(), Field.Store.YES);
|
||||
final Field product = new TextField(Fields.PRODUCT, entry.getProduct(), Field.Store.YES);
|
||||
doc.add(documentKey);
|
||||
doc.add(vendor);
|
||||
doc.add(product);
|
||||
|
||||
final Term term = new Term(Fields.DOCUMENT_KEY, entry.getDocumentId());
|
||||
indexWriter.updateDocument(term, doc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.core.KeywordAnalyzer;
|
||||
import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
import org.apache.lucene.queryparser.classic.QueryParser;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.owasp.dependencycheck.data.lucene.FieldAnalyzer;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.owasp.dependencycheck.data.lucene.LuceneUtils;
|
||||
import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer;
|
||||
|
||||
/**
|
||||
* An in memory lucene index that contains the vendor/product combinations from
|
||||
* the CPE (application) identifiers within the NVD CVE data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public final class CpeMemoryIndex {
|
||||
|
||||
/**
|
||||
* singleton instance.
|
||||
*/
|
||||
private static CpeMemoryIndex instance = new CpeMemoryIndex();
|
||||
|
||||
/**
|
||||
* private constructor for singleton.
|
||||
*/
|
||||
private CpeMemoryIndex() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the singleton instance of the CpeMemoryIndex.
|
||||
*
|
||||
* @return the instance of the CpeMemoryIndex
|
||||
*/
|
||||
public static CpeMemoryIndex getInstance() {
|
||||
return instance;
|
||||
}
|
||||
/**
|
||||
* The in memory Lucene index.
|
||||
*/
|
||||
private RAMDirectory index;
|
||||
/**
|
||||
* The Lucene IndexReader.
|
||||
*/
|
||||
private IndexReader indexReader;
|
||||
/**
|
||||
* The Lucene IndexSearcher.
|
||||
*/
|
||||
private IndexSearcher indexSearcher;
|
||||
/**
|
||||
* The Lucene Analyzer used for Searching.
|
||||
*/
|
||||
private Analyzer searchingAnalyzer;
|
||||
/**
|
||||
* The Lucene QueryParser used for Searching.
|
||||
*/
|
||||
private QueryParser queryParser;
|
||||
/**
|
||||
* The search field analyzer for the product field.
|
||||
*/
|
||||
private SearchFieldAnalyzer productSearchFieldAnalyzer;
|
||||
/**
|
||||
* The search field analyzer for the vendor field.
|
||||
*/
|
||||
private SearchFieldAnalyzer vendorSearchFieldAnalyzer;
|
||||
|
||||
/**
|
||||
* Creates and loads data into an in memory index.
|
||||
*
|
||||
* @param cve the data source to retrieve the cpe data
|
||||
* @throws IndexException thrown if there is an error creating the index
|
||||
*/
|
||||
public void open(CveDB cve) throws IndexException {
|
||||
if (!openState) {
|
||||
index = new RAMDirectory();
|
||||
buildIndex(cve);
|
||||
try {
|
||||
indexReader = DirectoryReader.open(index);
|
||||
} catch (IOException ex) {
|
||||
throw new IndexException(ex);
|
||||
}
|
||||
indexSearcher = new IndexSearcher(indexReader);
|
||||
searchingAnalyzer = createSearchingAnalyzer();
|
||||
queryParser = new QueryParser(LuceneUtils.CURRENT_VERSION, Fields.DOCUMENT_KEY, searchingAnalyzer);
|
||||
openState = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A flag indicating whether or not the index is open.
|
||||
*/
|
||||
private boolean openState = false;
|
||||
|
||||
/**
|
||||
* returns whether or not the index is open.
|
||||
*
|
||||
* @return whether or not the index is open
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return openState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the indexing analyzer for the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Analyzer createIndexingAnalyzer() {
|
||||
final Map fieldAnalyzers = new HashMap();
|
||||
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Analyzer for searching the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Analyzer createSearchingAnalyzer() {
|
||||
final Map fieldAnalyzers = new HashMap();
|
||||
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
|
||||
productSearchFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
vendorSearchFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
fieldAnalyzers.put(Fields.PRODUCT, productSearchFieldAnalyzer);
|
||||
fieldAnalyzers.put(Fields.VENDOR, vendorSearchFieldAnalyzer);
|
||||
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a CPE IndexEntry into the Lucene index.
|
||||
*
|
||||
* @param vendor the vendor to index
|
||||
* @param product the product to index
|
||||
* @param indexWriter the index writer to write the entry into
|
||||
* @throws CorruptIndexException is thrown if the index is corrupt
|
||||
* @throws IOException is thrown if an IOException occurs
|
||||
*/
|
||||
public void saveEntry(String vendor, String product, IndexWriter indexWriter) throws CorruptIndexException, IOException {
|
||||
final Document doc = new Document();
|
||||
final Field v = new TextField(Fields.VENDOR, vendor, Field.Store.YES);
|
||||
final Field p = new TextField(Fields.PRODUCT, product, Field.Store.YES);
|
||||
doc.add(v);
|
||||
doc.add(p);
|
||||
indexWriter.addDocument(doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CPE Index.
|
||||
*/
|
||||
public void close() {
|
||||
if (searchingAnalyzer != null) {
|
||||
searchingAnalyzer.close();
|
||||
searchingAnalyzer = null;
|
||||
}
|
||||
if (indexReader != null) {
|
||||
try {
|
||||
indexReader.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CpeMemoryIndex.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
indexReader = null;
|
||||
}
|
||||
queryParser = null;
|
||||
indexSearcher = null;
|
||||
if (index != null) {
|
||||
index.close();
|
||||
index = null;
|
||||
}
|
||||
openState = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the lucene index based off of the data within the CveDB.
|
||||
*
|
||||
* @param cve the data base containing the CPE data
|
||||
* @throws IndexException thrown if there is an issue creating the index
|
||||
*/
|
||||
private void buildIndex(CveDB cve) throws IndexException {
|
||||
Analyzer analyzer = null;
|
||||
IndexWriter indexWriter = null;
|
||||
try {
|
||||
analyzer = createIndexingAnalyzer();
|
||||
final IndexWriterConfig conf = new IndexWriterConfig(LuceneUtils.CURRENT_VERSION, analyzer);
|
||||
indexWriter = new IndexWriter(index, conf);
|
||||
final ResultSet rs = cve.getVendorProductList();
|
||||
if (rs == null) {
|
||||
throw new IndexException("No data exists");
|
||||
}
|
||||
try {
|
||||
while (rs.next()) {
|
||||
saveEntry(rs.getString(1), rs.getString(2), indexWriter);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CpeMemoryIndex.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new IndexException("Error reading CPE data", ex);
|
||||
}
|
||||
} catch (CorruptIndexException ex) {
|
||||
throw new IndexException("Unable to close an in-memory index", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new IndexException("Unable to close an in-memory index", ex);
|
||||
} finally {
|
||||
if (indexWriter != null) {
|
||||
try {
|
||||
try {
|
||||
indexWriter.commit();
|
||||
} finally {
|
||||
indexWriter.close(true);
|
||||
}
|
||||
} catch (CorruptIndexException ex) {
|
||||
throw new IndexException("Unable to close an in-memory index", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new IndexException("Unable to close an in-memory index", ex);
|
||||
}
|
||||
if (analyzer != null) {
|
||||
analyzer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the searching analyzers
|
||||
*/
|
||||
private void resetSearchingAnalyzer() {
|
||||
if (productSearchFieldAnalyzer != null) {
|
||||
productSearchFieldAnalyzer.clear();
|
||||
}
|
||||
if (vendorSearchFieldAnalyzer != null) {
|
||||
vendorSearchFieldAnalyzer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the index using the given search string.
|
||||
*
|
||||
* @param searchString the query text
|
||||
* @param maxQueryResults the maximum number of documents to return
|
||||
* @return the TopDocs found by the search
|
||||
* @throws ParseException thrown when the searchString is invalid
|
||||
* @throws IOException is thrown if there is an issue with the underlying
|
||||
* Index
|
||||
*/
|
||||
public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException {
|
||||
if (searchString == null || searchString.trim().isEmpty()) {
|
||||
throw new ParseException("Query is null or empty");
|
||||
}
|
||||
final Query query = queryParser.parse(searchString);
|
||||
return indexSearcher.search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the index using the given query.
|
||||
*
|
||||
* @param query the query used to search the index
|
||||
* @param maxQueryResults the max number of results to return
|
||||
* @return the TopDocs found be the query
|
||||
* @throws CorruptIndexException thrown if the Index is corrupt
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public TopDocs search(Query query, int maxQueryResults) throws CorruptIndexException, IOException {
|
||||
resetSearchingAnalyzer();
|
||||
return indexSearcher.search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a document from the Index.
|
||||
*
|
||||
* @param documentId the id of the document to retrieve
|
||||
* @return the Document
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public Document getDocument(int documentId) throws IOException {
|
||||
return indexSearcher.doc(documentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of CPE entries stored in the index.
|
||||
*
|
||||
* @return the number of CPE entries stored in the index
|
||||
*/
|
||||
public int numDocs() {
|
||||
if (indexReader == null) {
|
||||
return -1;
|
||||
}
|
||||
return indexReader.numDocs();
|
||||
}
|
||||
}
|
||||
@@ -16,36 +16,52 @@
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
/**
|
||||
* An interface defining an update task.
|
||||
* An exception thrown when the there is an issue using the in-memory CPE Index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public interface UpdateTask {
|
||||
public class IndexException extends Exception {
|
||||
|
||||
/**
|
||||
* <p>Updates the data store to the latest version.</p>
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
* The serial version UID for serialization.
|
||||
*/
|
||||
void update() throws UpdateException;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Get the value of deleteAndRecreate.
|
||||
*
|
||||
* @return the value of deleteAndRecreate
|
||||
* Creates a new IndexException.
|
||||
*/
|
||||
boolean shouldDeleteAndRecreate();
|
||||
public IndexException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not an update is needed.
|
||||
* Creates a new IndexException.
|
||||
*
|
||||
* @return true or false depending on whether an update is needed
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
boolean isUpdateNeeded();
|
||||
public IndexException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new IndexException.
|
||||
*
|
||||
* @param ex the cause of the failure.
|
||||
*/
|
||||
public IndexException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new IndexException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the failure.
|
||||
*/
|
||||
public IndexException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.lucene;
|
||||
|
||||
import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* <p>Lucene utils is a set of utilize written to make constructing Lucene
|
||||
* queries simpler.</p>
|
||||
@@ -26,6 +28,12 @@ package org.owasp.dependencycheck.data.lucene;
|
||||
*/
|
||||
public final class LuceneUtils {
|
||||
|
||||
/**
|
||||
* The current version of Lucene being used. Declaring this one place so an
|
||||
* upgrade doesn't require hunting through the code base.
|
||||
*/
|
||||
public static final Version CURRENT_VERSION = Version.LUCENE_45;
|
||||
|
||||
/**
|
||||
* Private constructor as this is a utility class.
|
||||
*/
|
||||
|
||||
@@ -18,14 +18,7 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@@ -36,84 +29,72 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.data.BaseDB;
|
||||
import org.owasp.dependencycheck.data.cwe.CweDB;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersion;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* The database holding information about the NVD CVE data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class CveDB {
|
||||
public class CveDB extends BaseDB {
|
||||
|
||||
/**
|
||||
* Resource location for SQL file used to create the database schema.
|
||||
*/
|
||||
public static final String DB_STRUCTURE_RESOURCE = "data/initialize.sql";
|
||||
/**
|
||||
* The version of the current DB Schema.
|
||||
*/
|
||||
public static final String DB_SCHEMA_VERSION = "2.6";
|
||||
/**
|
||||
* Database connection
|
||||
*/
|
||||
private Connection conn;
|
||||
//<editor-fold defaultstate="collapsed" desc="Constants to create, maintain, and retrieve data from the CVE Database">
|
||||
/**
|
||||
* SQL Statement to delete references by vulnerability ID.
|
||||
*/
|
||||
public static final String DELETE_REFERENCE = "DELETE FROM reference WHERE cveid = ?";
|
||||
private static final String DELETE_REFERENCE = "DELETE FROM reference WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to delete software by vulnerability ID.
|
||||
*/
|
||||
public static final String DELETE_SOFTWARE = "DELETE FROM software WHERE cveid = ?";
|
||||
private static final String DELETE_SOFTWARE = "DELETE FROM software WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to delete a vulnerability by CVE.
|
||||
*/
|
||||
public static final String DELETE_VULNERABILITY = "DELETE FROM vulnerability WHERE cve = ?";
|
||||
private static final String DELETE_VULNERABILITY = "DELETE FROM vulnerability WHERE id = ?";
|
||||
/**
|
||||
* SQL Statement to cleanup orphan entries. Yes, the db schema could be a
|
||||
* little tighter, but what we have works well to keep the data file size
|
||||
* down a bit.
|
||||
*/
|
||||
public static final String CLEANUP_ORPHANS = "DELETE FROM CpeEntry WHERE id not in (SELECT CPEEntryId FROM Software); ";
|
||||
private static final String CLEANUP_ORPHANS = "DELETE FROM CpeEntry WHERE id not in (SELECT CPEEntryId FROM Software); ";
|
||||
/**
|
||||
* SQL Statement to insert a new reference.
|
||||
*/
|
||||
public static final String INSERT_REFERENCE = "INSERT INTO reference (cveid, name, url, source) VALUES (?, ?, ?, ?)";
|
||||
private static final String INSERT_REFERENCE = "INSERT INTO reference (cveid, name, url, source) VALUES (?, ?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to insert a new software.
|
||||
*/
|
||||
public static final String INSERT_SOFTWARE = "INSERT INTO software (cveid, cpeEntryId, previousVersion) VALUES (?, ?, ?)";
|
||||
private static final String INSERT_SOFTWARE = "INSERT INTO software (cveid, cpeEntryId, previousVersion) VALUES (?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to insert a new cpe.
|
||||
*/
|
||||
public static final String INSERT_CPE = "INSERT INTO cpeEntry (cpe, vendor, product) VALUES (?, ?, ?)";
|
||||
private static final String INSERT_CPE = "INSERT INTO cpeEntry (cpe, vendor, product) VALUES (?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to get a CPEProductID.
|
||||
*/
|
||||
public static final String SELECT_CPE_ID = "SELECT id FROM cpeEntry WHERE cpe = ?";
|
||||
private static final String SELECT_CPE_ID = "SELECT id FROM cpeEntry WHERE cpe = ?";
|
||||
/**
|
||||
* SQL Statement to insert a new vulnerability.
|
||||
*/
|
||||
public static final String INSERT_VULNERABILITY = "INSERT INTO vulnerability (cve, description, cwe, cvssScore, cvssAccessVector, "
|
||||
private static final String INSERT_VULNERABILITY = "INSERT INTO vulnerability (cve, description, cwe, cvssScore, cvssAccessVector, "
|
||||
+ "cvssAccessComplexity, cvssAuthentication, cvssConfidentialityImpact, cvssIntegrityImpact, cvssAvailabilityImpact) "
|
||||
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to update a vulnerability.
|
||||
*/
|
||||
public static final String UPDATE_VULNERABILITY = "UPDATE vulnerability SET description=?, cwe=?, cvssScore=?, cvssAccessVector=?, "
|
||||
private static final String UPDATE_VULNERABILITY = "UPDATE vulnerability SET description=?, cwe=?, cvssScore=?, cvssAccessVector=?, "
|
||||
+ "cvssAccessComplexity=?, cvssAuthentication=?, cvssConfidentialityImpact=?, cvssIntegrityImpact=?, cvssAvailabilityImpact=? "
|
||||
+ "WHERE id=?";
|
||||
/**
|
||||
* SQL Statement to find CVE entries based on CPE data.
|
||||
*/
|
||||
public static final String SELECT_CVE_FROM_SOFTWARE = "SELECT cve, cpe, previousVersion "
|
||||
private static final String SELECT_CVE_FROM_SOFTWARE = "SELECT cve, cpe, previousVersion "
|
||||
+ "FROM software INNER JOIN vulnerability ON vulnerability.id = software.cveId "
|
||||
+ "INNER JOIN cpeEntry ON cpeEntry.id = software.cpeEntryId "
|
||||
+ "WHERE vendor = ? AND product = ?";
|
||||
@@ -123,15 +104,19 @@ public class CveDB {
|
||||
/**
|
||||
* SQL Statement to find the CPE entry based on the vendor and product.
|
||||
*/
|
||||
public static final String SELECT_CPE_ENTRIES = "SELECT cpe FROM cpeEntry WHERE vendor = ? AND product = ?";
|
||||
private static final String SELECT_CPE_ENTRIES = "SELECT cpe FROM cpeEntry WHERE vendor = ? AND product = ?";
|
||||
/**
|
||||
* SQL Statement to select references by CVEID.
|
||||
*/
|
||||
public static final String SELECT_REFERENCE = "SELECT source, name, url FROM reference WHERE cveid = ?";
|
||||
private static final String SELECT_REFERENCE = "SELECT source, name, url FROM reference WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to select vendor and product for lucene index.
|
||||
*/
|
||||
private static final String SELECT_VENDOR_PRODUCT_LIST = "SELECT vendor, product FROM cpeEntry GROUP BY vendor, product";
|
||||
/**
|
||||
* SQL Statement to select software by CVEID.
|
||||
*/
|
||||
public static final String SELECT_SOFTWARE = "SELECT cpe, previousVersion "
|
||||
private static final String SELECT_SOFTWARE = "SELECT cpe, previousVersion "
|
||||
+ "FROM software INNER JOIN cpeEntry ON software.cpeEntryId = cpeEntry.id WHERE cveid = ?";
|
||||
// public static final String SELECT_SOFTWARE = "SELECT part, vendor, product, version, revision, previousVersion "
|
||||
// + "FROM software INNER JOIN cpeProduct ON cpeProduct.id = software.cpeProductId LEFT JOIN cpeVersion ON "
|
||||
@@ -139,80 +124,14 @@ public class CveDB {
|
||||
/**
|
||||
* SQL Statement to select a vulnerability by CVEID.
|
||||
*/
|
||||
public static final String SELECT_VULNERABILITY = "SELECT id, description, cwe, cvssScore, cvssAccessVector, cvssAccessComplexity, "
|
||||
private static final String SELECT_VULNERABILITY = "SELECT id, description, cwe, cvssScore, cvssAccessVector, cvssAccessComplexity, "
|
||||
+ "cvssAuthentication, cvssConfidentialityImpact, cvssIntegrityImpact, cvssAvailabilityImpact FROM vulnerability WHERE cve = ?";
|
||||
/**
|
||||
* SQL Statement to select a vulnerability's primary key.
|
||||
*/
|
||||
public static final String SELECT_VULNERABILITY_ID = "SELECT id FROM vulnerability WHERE cve = ?";
|
||||
private static final String SELECT_VULNERABILITY_ID = "SELECT id FROM vulnerability WHERE cve = ?";
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* Opens the database connection. If the database does not exist, it will
|
||||
* create a new one.
|
||||
*
|
||||
* @throws IOException thrown if there is an IO Exception
|
||||
* @throws SQLException thrown if there is a SQL Exception
|
||||
* @throws DatabaseException thrown if there is an error initializing a new
|
||||
* database
|
||||
* @throws ClassNotFoundException thrown if the h2 database driver cannot be
|
||||
* loaded
|
||||
*/
|
||||
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
|
||||
value = "DMI_EMPTY_DB_PASSWORD",
|
||||
justification = "Yes, I know... Blank password.")
|
||||
public void open() throws IOException, SQLException, DatabaseException, ClassNotFoundException {
|
||||
final String fileName = CveDB.getDataDirectory().getCanonicalPath();
|
||||
final File f = new File(fileName, "cve." + DB_SCHEMA_VERSION);
|
||||
final File check = new File(f.getAbsolutePath() + ".h2.db");
|
||||
final boolean createTables = !check.exists();
|
||||
final String connStr = String.format("jdbc:h2:file:%s;AUTO_SERVER=TRUE", f.getAbsolutePath());
|
||||
Class.forName("org.h2.Driver");
|
||||
conn = DriverManager.getConnection(connStr, "sa", "");
|
||||
if (createTables) {
|
||||
createTables();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits all completed transactions.
|
||||
*
|
||||
* @throws SQLException thrown if a SQL Exception occurs
|
||||
*/
|
||||
public void commit() throws SQLException {
|
||||
if (conn != null) {
|
||||
conn.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the object and ensures that "close" has been called.
|
||||
*
|
||||
* @throws Throwable thrown if there is a problem
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close();
|
||||
super.finalize(); //not necessary if extending Object.
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the DB4O database. Close should be called on this object when it
|
||||
* is done being used.
|
||||
*/
|
||||
public void close() {
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException ex) {
|
||||
final String msg = "There was an error attempting to close the CveDB, see the log for more details.";
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, msg, ex);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the CPE entries in the database and retrieves all entries for a
|
||||
* given vendor and product combination. The returned list will include all
|
||||
@@ -228,7 +147,7 @@ public class CveDB {
|
||||
ResultSet rs = null;
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = conn.prepareStatement(SELECT_CPE_ENTRIES);
|
||||
ps = getConnection().prepareStatement(SELECT_CPE_ENTRIES);
|
||||
ps.setString(1, vendor);
|
||||
ps.setString(2, product);
|
||||
rs = ps.executeQuery();
|
||||
@@ -247,6 +166,22 @@ public class CveDB {
|
||||
return cpe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entire list of vendor/product combinations.
|
||||
*
|
||||
* @return the entire list of vendor/product combinations.
|
||||
*/
|
||||
public ResultSet getVendorProductList() {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
final PreparedStatement ps = getConnection().prepareStatement(SELECT_VENDOR_PRODUCT_LIST);
|
||||
rs = ps.executeQuery();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} // can't close the statement in the PS as the resultset is returned, closing PS would close the resultset
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the vulnerabilities associated with the specified CPE.
|
||||
*
|
||||
@@ -268,7 +203,7 @@ public class CveDB {
|
||||
PreparedStatement ps;
|
||||
final HashSet<String> cveEntries = new HashSet<String>();
|
||||
try {
|
||||
ps = conn.prepareStatement(SELECT_CVE_FROM_SOFTWARE);
|
||||
ps = getConnection().prepareStatement(SELECT_CVE_FROM_SOFTWARE);
|
||||
ps.setString(1, cpe.getVendor());
|
||||
ps.setString(2, cpe.getProduct());
|
||||
rs = ps.executeQuery();
|
||||
@@ -311,7 +246,7 @@ public class CveDB {
|
||||
ResultSet rsS = null;
|
||||
Vulnerability vuln = null;
|
||||
try {
|
||||
psV = conn.prepareStatement(SELECT_VULNERABILITY);
|
||||
psV = getConnection().prepareStatement(SELECT_VULNERABILITY);
|
||||
psV.setString(1, cve);
|
||||
rsV = psV.executeQuery();
|
||||
if (rsV.next()) {
|
||||
@@ -335,13 +270,13 @@ public class CveDB {
|
||||
vuln.setCvssIntegrityImpact(rsV.getString(9));
|
||||
vuln.setCvssAvailabilityImpact(rsV.getString(10));
|
||||
|
||||
psR = conn.prepareStatement(SELECT_REFERENCE);
|
||||
psR = getConnection().prepareStatement(SELECT_REFERENCE);
|
||||
psR.setInt(1, cveId);
|
||||
rsR = psR.executeQuery();
|
||||
while (rsR.next()) {
|
||||
vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3));
|
||||
}
|
||||
psS = conn.prepareStatement(SELECT_SOFTWARE);
|
||||
psS = getConnection().prepareStatement(SELECT_SOFTWARE);
|
||||
psS.setInt(1, cveId);
|
||||
rsS = psS.executeQuery();
|
||||
while (rsS.next()) {
|
||||
@@ -376,6 +311,7 @@ public class CveDB {
|
||||
*/
|
||||
public void updateVulnerability(Vulnerability vuln) throws DatabaseException {
|
||||
PreparedStatement selectVulnerabilityId = null;
|
||||
PreparedStatement deleteVulnerability = null;
|
||||
PreparedStatement deleteReferences = null;
|
||||
PreparedStatement deleteSoftware = null;
|
||||
PreparedStatement updateVulnerability = null;
|
||||
@@ -386,15 +322,16 @@ public class CveDB {
|
||||
PreparedStatement insertSoftware = null;
|
||||
|
||||
try {
|
||||
selectVulnerabilityId = conn.prepareStatement(SELECT_VULNERABILITY_ID);
|
||||
deleteReferences = conn.prepareStatement(DELETE_REFERENCE);
|
||||
deleteSoftware = conn.prepareStatement(DELETE_SOFTWARE);
|
||||
updateVulnerability = conn.prepareStatement(UPDATE_VULNERABILITY);
|
||||
insertVulnerability = conn.prepareStatement(INSERT_VULNERABILITY, Statement.RETURN_GENERATED_KEYS);
|
||||
insertReference = conn.prepareStatement(INSERT_REFERENCE);
|
||||
selectCpeId = conn.prepareStatement(SELECT_CPE_ID);
|
||||
insertCpe = conn.prepareStatement(INSERT_CPE, Statement.RETURN_GENERATED_KEYS);
|
||||
insertSoftware = conn.prepareStatement(INSERT_SOFTWARE);
|
||||
selectVulnerabilityId = getConnection().prepareStatement(SELECT_VULNERABILITY_ID);
|
||||
deleteVulnerability = getConnection().prepareStatement(DELETE_VULNERABILITY);
|
||||
deleteReferences = getConnection().prepareStatement(DELETE_REFERENCE);
|
||||
deleteSoftware = getConnection().prepareStatement(DELETE_SOFTWARE);
|
||||
updateVulnerability = getConnection().prepareStatement(UPDATE_VULNERABILITY);
|
||||
insertVulnerability = getConnection().prepareStatement(INSERT_VULNERABILITY, Statement.RETURN_GENERATED_KEYS);
|
||||
insertReference = getConnection().prepareStatement(INSERT_REFERENCE);
|
||||
selectCpeId = getConnection().prepareStatement(SELECT_CPE_ID);
|
||||
insertCpe = getConnection().prepareStatement(INSERT_CPE, Statement.RETURN_GENERATED_KEYS);
|
||||
insertSoftware = getConnection().prepareStatement(INSERT_SOFTWARE);
|
||||
int vulnerabilityId = 0;
|
||||
selectVulnerabilityId.setString(1, vuln.getName());
|
||||
ResultSet rs = selectVulnerabilityId.executeQuery();
|
||||
@@ -409,17 +346,22 @@ public class CveDB {
|
||||
closeResultSet(rs);
|
||||
rs = null;
|
||||
if (vulnerabilityId != 0) {
|
||||
updateVulnerability.setString(1, vuln.getDescription());
|
||||
updateVulnerability.setString(2, vuln.getCwe());
|
||||
updateVulnerability.setFloat(3, vuln.getCvssScore());
|
||||
updateVulnerability.setString(4, vuln.getCvssAccessVector());
|
||||
updateVulnerability.setString(5, vuln.getCvssAccessComplexity());
|
||||
updateVulnerability.setString(6, vuln.getCvssAuthentication());
|
||||
updateVulnerability.setString(7, vuln.getCvssConfidentialityImpact());
|
||||
updateVulnerability.setString(8, vuln.getCvssIntegrityImpact());
|
||||
updateVulnerability.setString(9, vuln.getCvssAvailabilityImpact());
|
||||
updateVulnerability.setInt(10, vulnerabilityId);
|
||||
updateVulnerability.executeUpdate();
|
||||
if (vuln.getDescription().contains("** REJECT **")) {
|
||||
deleteVulnerability.setInt(1, vulnerabilityId);
|
||||
deleteVulnerability.executeUpdate();
|
||||
} else {
|
||||
updateVulnerability.setString(1, vuln.getDescription());
|
||||
updateVulnerability.setString(2, vuln.getCwe());
|
||||
updateVulnerability.setFloat(3, vuln.getCvssScore());
|
||||
updateVulnerability.setString(4, vuln.getCvssAccessVector());
|
||||
updateVulnerability.setString(5, vuln.getCvssAccessComplexity());
|
||||
updateVulnerability.setString(6, vuln.getCvssAuthentication());
|
||||
updateVulnerability.setString(7, vuln.getCvssConfidentialityImpact());
|
||||
updateVulnerability.setString(8, vuln.getCvssIntegrityImpact());
|
||||
updateVulnerability.setString(9, vuln.getCvssAvailabilityImpact());
|
||||
updateVulnerability.setInt(10, vulnerabilityId);
|
||||
updateVulnerability.executeUpdate();
|
||||
}
|
||||
} else {
|
||||
insertVulnerability.setString(1, vuln.getName());
|
||||
insertVulnerability.setString(2, vuln.getDescription());
|
||||
@@ -496,6 +438,7 @@ public class CveDB {
|
||||
closeStatement(deleteReferences);
|
||||
closeStatement(deleteSoftware);
|
||||
closeStatement(updateVulnerability);
|
||||
closeStatement(deleteVulnerability);
|
||||
closeStatement(insertVulnerability);
|
||||
closeStatement(insertReference);
|
||||
closeStatement(selectCpeId);
|
||||
@@ -504,23 +447,6 @@ public class CveDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the directory that the JAR file exists in so that we can ensure
|
||||
* we always use a common data directory.
|
||||
*
|
||||
* @return the data directory for this index.
|
||||
* @throws IOException is thrown if an IOException occurs of course...
|
||||
*/
|
||||
public static File getDataDirectory() throws IOException {
|
||||
final File path = Settings.getFile(Settings.KEYS.CVE_DATA_DIRECTORY);
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
throw new IOException("Unable to create NVD CVE Data directory");
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* It is possible that orphaned rows may be generated during database
|
||||
* updates. This should be called after all updates have been completed to
|
||||
@@ -529,7 +455,7 @@ public class CveDB {
|
||||
public void cleanupDatabase() {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = conn.prepareStatement(CLEANUP_ORPHANS);
|
||||
ps = getConnection().prepareStatement(CLEANUP_ORPHANS);
|
||||
if (ps != null) {
|
||||
ps.executeUpdate();
|
||||
}
|
||||
@@ -540,102 +466,6 @@ public class CveDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database structure (tables and indexes) to store the CVE data
|
||||
*
|
||||
* @throws SQLException thrown if there is a sql exception
|
||||
* @throws DatabaseException thrown if there is a database exception
|
||||
*/
|
||||
protected void createTables() throws SQLException, DatabaseException {
|
||||
InputStream is;
|
||||
InputStreamReader reader;
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
is = this.getClass().getClassLoader().getResourceAsStream(DB_STRUCTURE_RESOURCE);
|
||||
reader = new InputStreamReader(is, "UTF-8");
|
||||
in = new BufferedReader(reader);
|
||||
final StringBuilder sb = new StringBuilder(2110);
|
||||
String tmp;
|
||||
while ((tmp = in.readLine()) != null) {
|
||||
sb.append(tmp);
|
||||
}
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = conn.createStatement();
|
||||
statement.execute(sb.toString());
|
||||
} finally {
|
||||
closeStatement(statement);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new DatabaseException("Unable to create database schema", ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(CveDB.class
|
||||
.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the given statement object ignoring any exceptions that occur.
|
||||
*
|
||||
* @param statement a Statement object
|
||||
*/
|
||||
private void closeStatement(Statement statement) {
|
||||
if (statement != null) {
|
||||
try {
|
||||
statement.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class
|
||||
.getName()).log(Level.FINEST, statement.toString(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the result set capturing and ignoring any SQLExceptions that
|
||||
* occur.
|
||||
*
|
||||
* @param rs a ResultSet to close
|
||||
*/
|
||||
private void closeResultSet(ResultSet rs) {
|
||||
if (rs != null) {
|
||||
try {
|
||||
rs.close();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class
|
||||
.getName()).log(Level.FINEST, rs.toString(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generated integer primary key for a newly inserted row.
|
||||
*
|
||||
* @param statement a prepared statement that just executed an insert
|
||||
* @return a primary key
|
||||
* @throws DatabaseException thrown if there is an exception obtaining the
|
||||
* key
|
||||
*/
|
||||
private int getGeneratedKey(PreparedStatement statement) throws DatabaseException {
|
||||
ResultSet rs = null;
|
||||
int id = 0;
|
||||
try {
|
||||
rs = statement.getGeneratedKeys();
|
||||
rs.next();
|
||||
id = rs.getInt(1);
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Unable to get primary key for inserted row");
|
||||
} finally {
|
||||
closeResultSet(rs);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given identifiedVersion is affected by the given cpeId
|
||||
* and previous version flag. A non-null, non-empty string passed to the
|
||||
|
||||
@@ -39,13 +39,22 @@ public class DatabaseException extends Exception {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an DatabaseException.
|
||||
*
|
||||
* @param ex the cause of the exception
|
||||
*/
|
||||
public DatabaseException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an DatabaseException.
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param ex the cause of the exception
|
||||
*/
|
||||
public DatabaseException(String msg, Exception ex) {
|
||||
public DatabaseException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.owasp.dependencycheck.data.cpe.CpeIndexWriter;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
@@ -208,6 +207,9 @@ public class NvdCve20Handler extends DefaultHandler {
|
||||
nodeText = null;
|
||||
} else if (current.isVulnSummaryNode()) {
|
||||
vulnerability.setDescription(nodeText.toString());
|
||||
if (nodeText.indexOf("** REJECT **") >= 0) {
|
||||
hasApplicationCpe = true; //ensure we process this to delete the vuln
|
||||
}
|
||||
nodeText = null;
|
||||
}
|
||||
}
|
||||
@@ -260,26 +262,8 @@ public class NvdCve20Handler extends DefaultHandler {
|
||||
vuln.updateVulnerableSoftware(vs);
|
||||
}
|
||||
}
|
||||
for (VulnerableSoftware vs : vuln.getVulnerableSoftware()) {
|
||||
if (cpeIndex != null) {
|
||||
cpeIndex.saveEntry(vs);
|
||||
}
|
||||
}
|
||||
cveDB.updateVulnerability(vuln);
|
||||
}
|
||||
/**
|
||||
* the cpe index.
|
||||
*/
|
||||
private CpeIndexWriter cpeIndex;
|
||||
|
||||
/**
|
||||
* Sets the cpe index writer.
|
||||
*
|
||||
* @param index the CPE Lucene Index
|
||||
*/
|
||||
public void setCpeIndex(CpeIndexWriter index) {
|
||||
cpeIndex = index;
|
||||
}
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
|
||||
/**
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.data.cpe.CpeIndexWriter;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.NvdCve12Handler;
|
||||
import org.owasp.dependencycheck.data.nvdcve.NvdCve20Handler;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Class responsible for updating the CPE and NVDCVE data stores.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public abstract class AbstractUpdateTask implements UpdateTask {
|
||||
|
||||
/**
|
||||
* Initializes the AbstractUpdateTask.
|
||||
*
|
||||
* @param properties information about the data store
|
||||
* @throws MalformedURLException thrown if the configuration contains a
|
||||
* malformed url
|
||||
* @throws DownloadFailedException thrown if the timestamp on a file cannot
|
||||
* be checked
|
||||
* @throws UpdateException thrown if the update fails
|
||||
*/
|
||||
public AbstractUpdateTask(DataStoreMetaInfo properties) throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
this.properties = properties;
|
||||
this.updateable = updatesNeeded();
|
||||
}
|
||||
/**
|
||||
* A collection of updateable NVD CVE items.
|
||||
*/
|
||||
private Updateable updateable;
|
||||
/**
|
||||
* Utility to read and write meta-data about the data.
|
||||
*/
|
||||
private DataStoreMetaInfo properties = null;
|
||||
|
||||
/**
|
||||
* Returns the data store properties.
|
||||
*
|
||||
* @return the data store properties
|
||||
*/
|
||||
protected DataStoreMetaInfo getProperties() {
|
||||
return properties;
|
||||
}
|
||||
/**
|
||||
* Reference to the Cve Database.
|
||||
*/
|
||||
private CveDB cveDB = null;
|
||||
|
||||
/**
|
||||
* Returns the CveDB.
|
||||
*
|
||||
* @return the CveDB
|
||||
*/
|
||||
protected CveDB getCveDB() {
|
||||
return cveDB;
|
||||
}
|
||||
/**
|
||||
* Reference to the Cpe Index.
|
||||
*/
|
||||
private CpeIndexWriter cpeIndex = null;
|
||||
|
||||
/**
|
||||
* Returns the CpeIndex.
|
||||
*
|
||||
* @return the CpeIndex
|
||||
*/
|
||||
protected CpeIndexWriter getCpeIndex() {
|
||||
return cpeIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether or not an update is needed.
|
||||
*
|
||||
* @return true or false depending on whether an update is needed
|
||||
*/
|
||||
public boolean isUpdateNeeded() {
|
||||
return updateable.isUpdateNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the updateable NVD CVE Entries.
|
||||
*
|
||||
* @return an Updateable object containing the NVD CVE entries
|
||||
*/
|
||||
protected Updateable getUpdateable() {
|
||||
return updateable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the index needs to be updated.
|
||||
*
|
||||
* @return a collection of updateable resources.
|
||||
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
|
||||
* data is incorrect.
|
||||
* @throws DownloadFailedException is thrown if there is an error.
|
||||
* downloading the NVD CVE download data file.
|
||||
* @throws UpdateException Is thrown if there is an issue with the last
|
||||
* updated properties file.
|
||||
*/
|
||||
protected abstract Updateable updatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException;
|
||||
|
||||
/**
|
||||
* <p>Updates the data store to the latest version.</p>
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
*/
|
||||
public abstract void update() throws UpdateException;
|
||||
/**
|
||||
* A flag indicating whether or not the current data store should be
|
||||
* deleted.
|
||||
*/
|
||||
private boolean deleteAndRecreate = false;
|
||||
|
||||
/**
|
||||
* Get the value of deleteAndRecreate.
|
||||
*
|
||||
* @return the value of deleteAndRecreate
|
||||
*/
|
||||
public boolean shouldDeleteAndRecreate() {
|
||||
return deleteAndRecreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of deleteAndRecreate.
|
||||
*
|
||||
* @param deleteAndRecreate new value of deleteAndRecreate
|
||||
*/
|
||||
protected void setDeleteAndRecreate(boolean deleteAndRecreate) {
|
||||
this.deleteAndRecreate = deleteAndRecreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the existing data directories.
|
||||
*
|
||||
* @throws IOException thrown if the directory cannot be deleted
|
||||
*/
|
||||
protected void deleteExistingData() throws IOException {
|
||||
File data = Settings.getFile(Settings.KEYS.CVE_DATA_DIRECTORY);
|
||||
if (data.exists()) {
|
||||
FileUtils.delete(data);
|
||||
}
|
||||
data = Settings.getFile(Settings.KEYS.CPE_DATA_DIRECTORY);
|
||||
if (data.exists()) {
|
||||
FileUtils.delete(data);
|
||||
}
|
||||
data = DataStoreMetaInfo.getPropertiesFile();
|
||||
if (data.exists()) {
|
||||
FileUtils.delete(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CVE and CPE data stores.
|
||||
*/
|
||||
protected void closeDataStores() {
|
||||
if (cveDB != null) {
|
||||
try {
|
||||
cveDB.close();
|
||||
} catch (Exception ignore) {
|
||||
Logger.getLogger(AbstractUpdateTask.class.getName()).log(Level.FINEST, "Error closing the cveDB", ignore);
|
||||
}
|
||||
}
|
||||
if (cpeIndex != null) {
|
||||
try {
|
||||
cpeIndex.close();
|
||||
} catch (Exception ignore) {
|
||||
Logger.getLogger(AbstractUpdateTask.class.getName()).log(Level.FINEST, "Error closing the cpeIndex", ignore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the CVE and CPE data stores.
|
||||
*
|
||||
* @throws UpdateException thrown if a data store cannot be opened
|
||||
*/
|
||||
protected void openDataStores() throws UpdateException {
|
||||
//open the cve and cpe data stores
|
||||
try {
|
||||
cveDB = new CveDB();
|
||||
cveDB.open();
|
||||
cpeIndex = new CpeIndexWriter();
|
||||
cpeIndex.open();
|
||||
} catch (IOException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(AbstractUpdateTask.class.getName()).log(Level.FINE, "IO Error opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
} catch (SQLException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(AbstractUpdateTask.class.getName()).log(Level.FINE, "SQL Exception opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
} catch (DatabaseException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(AbstractUpdateTask.class.getName()).log(Level.FINE, "Database Exception opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(AbstractUpdateTask.class.getName()).log(Level.FINE, "Class not found exception opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the epoch date is within the range specified of the
|
||||
* compareTo epoch time. This takes the (compareTo-date)/1000/60/60/24 to
|
||||
* get the number of days. If the calculated days is less then the range the
|
||||
* date is considered valid.
|
||||
*
|
||||
* @param date the date to be checked.
|
||||
* @param compareTo the date to compare to.
|
||||
* @param range the range in days to be considered valid.
|
||||
* @return whether or not the date is within the range.
|
||||
*/
|
||||
protected boolean withinRange(long date, long compareTo, int range) {
|
||||
final double differenceInDays = (compareTo - date) / 1000.0 / 60.0 / 60.0 / 24.0;
|
||||
return differenceInDays < range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the NVD CVE XML File into the Lucene Index.
|
||||
*
|
||||
* @param file the file containing the NVD CVE XML
|
||||
* @param oldVersion contains the file containing the NVD CVE XML 1.2
|
||||
* @throws ParserConfigurationException is thrown if there is a parser
|
||||
* configuration exception
|
||||
* @throws SAXException is thrown if there is a SAXException
|
||||
* @throws IOException is thrown if there is a IO Exception
|
||||
* @throws SQLException is thrown if there is a SQL exception
|
||||
* @throws DatabaseException is thrown if there is a database exception
|
||||
* @throws ClassNotFoundException thrown if the h2 database driver cannot be
|
||||
* loaded
|
||||
*/
|
||||
protected void importXML(File file, File oldVersion)
|
||||
throws ParserConfigurationException, SAXException, IOException, SQLException, DatabaseException, ClassNotFoundException {
|
||||
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
final SAXParser saxParser = factory.newSAXParser();
|
||||
|
||||
final NvdCve12Handler cve12Handler = new NvdCve12Handler();
|
||||
saxParser.parse(oldVersion, cve12Handler);
|
||||
final Map<String, List<VulnerableSoftware>> prevVersionVulnMap = cve12Handler.getVulnerabilities();
|
||||
|
||||
final NvdCve20Handler cve20Handler = new NvdCve20Handler();
|
||||
cve20Handler.setCveDB(cveDB);
|
||||
cve20Handler.setPrevVersionVulnMap(prevVersionVulnMap);
|
||||
cve20Handler.setCpeIndex(cpeIndex);
|
||||
saxParser.parse(file, cve20Handler);
|
||||
}
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import org.owasp.dependencycheck.data.nvdcve.InvalidDataException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import static org.owasp.dependencycheck.data.update.DataStoreMetaInfo.BATCH;
|
||||
import static org.owasp.dependencycheck.data.update.DataStoreMetaInfo.MODIFIED;
|
||||
|
||||
/**
|
||||
* Class responsible for updating the CPE and NVDCVE data stores.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class BatchUpdateTask extends AbstractUpdateTask {
|
||||
|
||||
/**
|
||||
* Constructs a new BatchUpdateTask.
|
||||
*
|
||||
* @param properties information about the data store
|
||||
* @throws MalformedURLException thrown if a configured URL is malformed
|
||||
* @throws DownloadFailedException thrown if a timestamp cannot be checked
|
||||
* on a configured URL
|
||||
* @throws UpdateException thrown if there is an exception generating the
|
||||
* update task
|
||||
*/
|
||||
public BatchUpdateTask(DataStoreMetaInfo properties) throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
super(properties);
|
||||
}
|
||||
/**
|
||||
* A flag indicating whether or not the batch update should be performed.
|
||||
*/
|
||||
private boolean doBatchUpdate;
|
||||
|
||||
/**
|
||||
* Get the value of doBatchUpdate
|
||||
*
|
||||
* @return the value of doBatchUpdate
|
||||
*/
|
||||
protected boolean isDoBatchUpdate() {
|
||||
return doBatchUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of doBatchUpdate
|
||||
*
|
||||
* @param doBatchUpdate new value of doBatchUpdate
|
||||
*/
|
||||
protected void setDoBatchUpdate(boolean doBatchUpdate) {
|
||||
this.doBatchUpdate = doBatchUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Downloads the latest NVD CVE XML file from the web and imports it into
|
||||
* the current CVE Database.</p>
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
*/
|
||||
@Override
|
||||
public void update() throws UpdateException {
|
||||
if (getProperties().isBatchUpdateMode() && doBatchUpdate) {
|
||||
final String batchSrc = Settings.getString(Settings.KEYS.BATCH_UPDATE_URL);
|
||||
File tmp = null;
|
||||
try {
|
||||
deleteExistingData();
|
||||
final File dataDirectory = CveDB.getDataDirectory().getParentFile();
|
||||
final URL batchUrl = new URL(batchSrc);
|
||||
if ("file".equals(batchUrl.getProtocol())) {
|
||||
try {
|
||||
tmp = new File(batchUrl.toURI());
|
||||
} catch (URISyntaxException ex) {
|
||||
final String msg = String.format("Invalid batch update URI: %s", batchSrc);
|
||||
throw new UpdateException(msg, ex);
|
||||
}
|
||||
} else if ("http".equals(batchUrl.getProtocol())
|
||||
|| "https".equals(batchUrl.getProtocol())) {
|
||||
tmp = File.createTempFile("batch_", ".zip");
|
||||
Downloader.fetchFile(batchUrl, tmp);
|
||||
}
|
||||
//TODO add FTP?
|
||||
FileUtils.extractFiles(tmp, dataDirectory);
|
||||
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("IO Exception Occured performing batch update using: %s", batchSrc);
|
||||
throw new UpdateException(msg, ex);
|
||||
} finally {
|
||||
if (tmp != null && !tmp.delete()) {
|
||||
tmp.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the index needs to be updated. This is done by fetching the
|
||||
* NVD CVE meta data and checking the last update date. If the data needs to
|
||||
* be refreshed this method will return the NvdCveUrl for the files that
|
||||
* need to be updated.
|
||||
*
|
||||
* @return the collection of files that need to be updated
|
||||
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
|
||||
* data is incorrect
|
||||
* @throws DownloadFailedException is thrown if there is an error.
|
||||
* downloading the NVD CVE download data file
|
||||
* @throws UpdateException Is thrown if there is an issue with the last
|
||||
* updated properties file
|
||||
*/
|
||||
@Override
|
||||
public Updateable updatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
Updateable updates = null;
|
||||
try {
|
||||
updates = retrieveCurrentTimestampsFromWeb();
|
||||
} catch (InvalidDataException ex) {
|
||||
final String msg = "Unable to retrieve valid timestamp from nvd cve downloads page";
|
||||
Logger.getLogger(BatchUpdateTask.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DownloadFailedException(msg, ex);
|
||||
} catch (InvalidSettingException ex) {
|
||||
Logger.getLogger(BatchUpdateTask.class.getName()).log(Level.FINE, "Invalid setting found when retrieving timestamps", ex);
|
||||
throw new DownloadFailedException("Invalid settings", ex);
|
||||
}
|
||||
|
||||
if (updates == null) {
|
||||
throw new DownloadFailedException("Unable to retrieve the timestamps of the currently published NVD CVE data");
|
||||
}
|
||||
final DataStoreMetaInfo properties = getProperties();
|
||||
if (!properties.isEmpty()) {
|
||||
try {
|
||||
boolean deleteAndRecreate = false;
|
||||
float version;
|
||||
|
||||
if (properties.getProperty("version") == null) {
|
||||
deleteAndRecreate = true;
|
||||
} else {
|
||||
try {
|
||||
version = Float.parseFloat(properties.getProperty("version"));
|
||||
final float currentVersion = Float.parseFloat(CveDB.DB_SCHEMA_VERSION);
|
||||
if (currentVersion > version) {
|
||||
deleteAndRecreate = true;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
deleteAndRecreate = true;
|
||||
}
|
||||
}
|
||||
|
||||
final NvdCveInfo batchInfo = updates.get(BATCH);
|
||||
if (properties.isBatchUpdateMode() && batchInfo != null) {
|
||||
final long lastUpdated = Long.parseLong(properties.getProperty(DataStoreMetaInfo.BATCH, "0"));
|
||||
if (lastUpdated != batchInfo.getTimestamp()) {
|
||||
deleteAndRecreate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteAndRecreate) {
|
||||
setDoBatchUpdate(properties.isBatchUpdateMode());
|
||||
try {
|
||||
deleteExistingData();
|
||||
} catch (IOException ex) {
|
||||
final String msg = "Unable to delete existing data";
|
||||
Logger.getLogger(BatchUpdateTask.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(BatchUpdateTask.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
final long lastUpdated = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED, "0"));
|
||||
final Date now = new Date();
|
||||
final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7);
|
||||
final int start = Settings.getInt(Settings.KEYS.CVE_START_YEAR, 2002);
|
||||
final int end = Calendar.getInstance().get(Calendar.YEAR);
|
||||
if (lastUpdated == updates.get(MODIFIED).getTimestamp()) {
|
||||
updates.clear(); //we don't need to update anything.
|
||||
setDoBatchUpdate(properties.isBatchUpdateMode());
|
||||
} else if (withinRange(lastUpdated, now.getTime(), days)) {
|
||||
updates.get(MODIFIED).setNeedsUpdate(true);
|
||||
if (properties.isBatchUpdateMode()) {
|
||||
setDoBatchUpdate(false);
|
||||
} else {
|
||||
for (int i = start; i <= end; i++) {
|
||||
updates.get(String.valueOf(i)).setNeedsUpdate(false);
|
||||
}
|
||||
}
|
||||
} else if (properties.isBatchUpdateMode()) {
|
||||
updates.get(MODIFIED).setNeedsUpdate(true);
|
||||
setDoBatchUpdate(true);
|
||||
} else { //we figure out which of the several XML files need to be downloaded.
|
||||
updates.get(MODIFIED).setNeedsUpdate(false);
|
||||
for (int i = start; i <= end; i++) {
|
||||
final NvdCveInfo cve = updates.get(String.valueOf(i));
|
||||
long currentTimestamp = 0;
|
||||
try {
|
||||
currentTimestamp = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED_BASE + String.valueOf(i), "0"));
|
||||
} catch (NumberFormatException ex) {
|
||||
final String msg = String.format("Error parsing '%s' '%s' from nvdcve.lastupdated",
|
||||
DataStoreMetaInfo.LAST_UPDATED_BASE, String.valueOf(i));
|
||||
Logger.getLogger(BatchUpdateTask.class.getName()).log(Level.FINE, msg, ex);
|
||||
}
|
||||
if (currentTimestamp == cve.getTimestamp()) {
|
||||
cve.setNeedsUpdate(false); //they default to true.
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
final String msg = "An invalid schema version or timestamp exists in the data.properties file.";
|
||||
Logger.getLogger(BatchUpdateTask.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(BatchUpdateTask.class.getName()).log(Level.FINE, null, ex);
|
||||
setDoBatchUpdate(properties.isBatchUpdateMode());
|
||||
}
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the timestamps from the NVD CVE meta data file.
|
||||
*
|
||||
* @return the timestamp from the currently published nvdcve downloads page
|
||||
* @throws MalformedURLException thrown if the URL for the NVD CCE Meta data
|
||||
* is incorrect.
|
||||
* @throws DownloadFailedException thrown if there is an error downloading
|
||||
* the nvd cve meta data file
|
||||
* @throws InvalidDataException thrown if there is an exception parsing the
|
||||
* timestamps
|
||||
* @throws InvalidSettingException thrown if the settings are invalid
|
||||
*/
|
||||
private Updateable retrieveCurrentTimestampsFromWeb()
|
||||
throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException {
|
||||
final Updateable updates = new Updateable();
|
||||
updates.add(BATCH, Settings.getString(Settings.KEYS.BATCH_UPDATE_URL),
|
||||
null, false);
|
||||
|
||||
final String url = Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL, "");
|
||||
if (!url.isEmpty()) {
|
||||
updates.add(MODIFIED, url,
|
||||
Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL),
|
||||
false);
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
|
||||
/**
|
||||
* A callable object to download two files.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class CallableDownloadTask implements Callable<CallableDownloadTask> {
|
||||
|
||||
/**
|
||||
* Simple constructor for the callable download task.
|
||||
*
|
||||
* @param nvdCveInfo the nvd cve info
|
||||
* @param first the first file
|
||||
* @param second the second file
|
||||
*/
|
||||
public CallableDownloadTask(NvdCveInfo nvdCveInfo, File first, File second) {
|
||||
this.nvdCveInfo = nvdCveInfo;
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
/**
|
||||
* The NVD CVE Meta Data.
|
||||
*/
|
||||
private NvdCveInfo nvdCveInfo;
|
||||
|
||||
/**
|
||||
* Get the value of nvdCveInfo.
|
||||
*
|
||||
* @return the value of nvdCveInfo
|
||||
*/
|
||||
public NvdCveInfo getNvdCveInfo() {
|
||||
return nvdCveInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of nvdCveInfo.
|
||||
*
|
||||
* @param nvdCveInfo new value of nvdCveInfo
|
||||
*/
|
||||
public void setNvdCveInfo(NvdCveInfo nvdCveInfo) {
|
||||
this.nvdCveInfo = nvdCveInfo;
|
||||
}
|
||||
/**
|
||||
* a file.
|
||||
*/
|
||||
private File first;
|
||||
|
||||
/**
|
||||
* Get the value of first.
|
||||
*
|
||||
* @return the value of first
|
||||
*/
|
||||
public File getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of first.
|
||||
*
|
||||
* @param first new value of first
|
||||
*/
|
||||
public void setFirst(File first) {
|
||||
this.first = first;
|
||||
}
|
||||
/**
|
||||
* a file.
|
||||
*/
|
||||
private File second;
|
||||
|
||||
/**
|
||||
* Get the value of second.
|
||||
*
|
||||
* @return the value of second
|
||||
*/
|
||||
public File getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of second.
|
||||
*
|
||||
* @param second new value of second
|
||||
*/
|
||||
public void setSecond(File second) {
|
||||
this.second = second;
|
||||
}
|
||||
/**
|
||||
* A placeholder for an exception.
|
||||
*/
|
||||
private Exception exception = null;
|
||||
|
||||
/**
|
||||
* Get the value of exception.
|
||||
*
|
||||
* @return the value of exception
|
||||
*/
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns whether or not an exception occurred during download.
|
||||
*
|
||||
* @return whether or not an exception occurred during download
|
||||
*/
|
||||
public boolean hasException() {
|
||||
return exception != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableDownloadTask call() throws Exception {
|
||||
try {
|
||||
final URL url1 = new URL(nvdCveInfo.getUrl());
|
||||
final URL url2 = new URL(nvdCveInfo.getOldSchemaVersionUrl());
|
||||
String msg = String.format("Download Started for NVD CVE - %s", nvdCveInfo.getId());
|
||||
Logger.getLogger(CallableDownloadTask.class.getName()).log(Level.INFO, msg);
|
||||
Downloader.fetchFile(url1, first);
|
||||
Downloader.fetchFile(url2, second);
|
||||
msg = String.format("Download Complete for NVD CVE - %s", nvdCveInfo.getId());
|
||||
Logger.getLogger(CallableDownloadTask.class.getName()).log(Level.INFO, msg);
|
||||
} catch (DownloadFailedException ex) {
|
||||
this.exception = ex;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to delete the files that were downloaded.
|
||||
*/
|
||||
public void cleanup() {
|
||||
boolean deleted = false;
|
||||
try {
|
||||
if (first != null && first.exists()) {
|
||||
deleted = first.delete();
|
||||
}
|
||||
} finally {
|
||||
if (first != null && (first.exists() || !deleted)) {
|
||||
first.deleteOnExit();
|
||||
}
|
||||
}
|
||||
try {
|
||||
deleted = false;
|
||||
if (second != null && second.exists()) {
|
||||
deleted = second.delete();
|
||||
}
|
||||
} finally {
|
||||
if (second != null && (second.exists() || !deleted)) {
|
||||
second.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,7 +234,7 @@ public class DataStoreMetaInfo {
|
||||
* @return the properties file
|
||||
*/
|
||||
public static File getPropertiesFile() {
|
||||
final File dataDirectory = Settings.getFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
final File dataDirectory = Settings.getDataFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
final File file = new File(dataDirectory, UPDATE_PROPERTIES_FILE);
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,6 @@ import org.owasp.dependencycheck.data.CachedWebDataSource;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.concurrency.DirectoryLockException;
|
||||
import org.owasp.dependencycheck.concurrency.DirectorySpinLock;
|
||||
import org.owasp.dependencycheck.concurrency.InvalidDirectoryException;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
@@ -48,24 +45,9 @@ public class DatabaseUpdater implements CachedWebDataSource {
|
||||
*/
|
||||
@Override
|
||||
public void update() throws UpdateException {
|
||||
final File dataDir = Settings.getFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
DirectorySpinLock lock = null;
|
||||
try {
|
||||
lock = new DirectorySpinLock(dataDir);
|
||||
} catch (InvalidDirectoryException ex) {
|
||||
throw new UpdateException("Unable to obtain lock on the data directory", ex);
|
||||
} catch (DirectoryLockException ex) {
|
||||
throw new UpdateException("Unable to obtain exclusive lock on the data directory", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
lock.obtainSharedLock();
|
||||
final UpdateTask task = UpdateTaskFactory.getUpdateTask();
|
||||
|
||||
|
||||
final StandardUpdate task = new StandardUpdate();
|
||||
if (task.isUpdateNeeded()) {
|
||||
lock.release();
|
||||
lock.obtainExclusiveLock();
|
||||
if (task.shouldDeleteAndRecreate()) {
|
||||
try {
|
||||
deleteExistingData();
|
||||
@@ -76,10 +58,6 @@ public class DatabaseUpdater implements CachedWebDataSource {
|
||||
}
|
||||
task.update();
|
||||
}
|
||||
} catch (DirectoryLockException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"Unable to obtain lock on data directory, unable to update the data to use the most current data.");
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINE, null, ex);
|
||||
} catch (MalformedURLException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.");
|
||||
@@ -88,10 +66,6 @@ public class DatabaseUpdater implements CachedWebDataSource {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING,
|
||||
"Unable to download the NVD CVE data, unable to update the data to use the most current data.");
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
if (lock != null) {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,11 +75,7 @@ public class DatabaseUpdater implements CachedWebDataSource {
|
||||
* @throws IOException thrown if the directory cannot be deleted
|
||||
*/
|
||||
protected void deleteExistingData() throws IOException {
|
||||
File data = Settings.getFile(Settings.KEYS.CVE_DATA_DIRECTORY);
|
||||
if (data.exists()) {
|
||||
FileUtils.delete(data);
|
||||
}
|
||||
data = Settings.getFile(Settings.KEYS.CPE_DATA_DIRECTORY);
|
||||
File data = Settings.getDataFile(Settings.KEYS.CVE_DATA_DIRECTORY);
|
||||
if (data.exists()) {
|
||||
FileUtils.delete(data);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.NvdCve12Handler;
|
||||
import org.owasp.dependencycheck.data.nvdcve.NvdCve20Handler;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* A callable task that will process a given set of NVD CVE xml files and update
|
||||
* the Cve Database accordingly.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class ProcessTask implements Callable<ProcessTask> {
|
||||
|
||||
/**
|
||||
* A field to store any update exceptions that occur during the "call".
|
||||
*/
|
||||
private UpdateException exception = null;
|
||||
|
||||
/**
|
||||
* Get the value of exception.
|
||||
*
|
||||
* @return the value of exception
|
||||
*/
|
||||
public UpdateException getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of exception.
|
||||
*
|
||||
* @param exception new value of exception
|
||||
*/
|
||||
public void setException(UpdateException exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
private final CveDB cveDB;
|
||||
private final CallableDownloadTask filePair;
|
||||
private final DataStoreMetaInfo properties;
|
||||
|
||||
public ProcessTask(final CveDB cveDB, final DataStoreMetaInfo properties, final CallableDownloadTask filePair) {
|
||||
this.cveDB = cveDB;
|
||||
this.filePair = filePair;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessTask call() throws Exception {
|
||||
try {
|
||||
processFiles();
|
||||
} catch (UpdateException ex) {
|
||||
this.exception = ex;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the NVD CVE XML File into the Lucene Index.
|
||||
*
|
||||
* @param file the file containing the NVD CVE XML
|
||||
* @param oldVersion contains the file containing the NVD CVE XML 1.2
|
||||
* @throws ParserConfigurationException is thrown if there is a parser
|
||||
* configuration exception
|
||||
* @throws SAXException is thrown if there is a SAXException
|
||||
* @throws IOException is thrown if there is a IO Exception
|
||||
* @throws SQLException is thrown if there is a SQL exception
|
||||
* @throws DatabaseException is thrown if there is a database exception
|
||||
* @throws ClassNotFoundException thrown if the h2 database driver cannot be
|
||||
* loaded
|
||||
*/
|
||||
protected void importXML(File file, File oldVersion) throws ParserConfigurationException,
|
||||
SAXException, IOException, SQLException, DatabaseException, ClassNotFoundException {
|
||||
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
final SAXParser saxParser = factory.newSAXParser();
|
||||
|
||||
final NvdCve12Handler cve12Handler = new NvdCve12Handler();
|
||||
saxParser.parse(oldVersion, cve12Handler);
|
||||
final Map<String, List<VulnerableSoftware>> prevVersionVulnMap = cve12Handler.getVulnerabilities();
|
||||
|
||||
final NvdCve20Handler cve20Handler = new NvdCve20Handler();
|
||||
cve20Handler.setCveDB(cveDB);
|
||||
cve20Handler.setPrevVersionVulnMap(prevVersionVulnMap);
|
||||
saxParser.parse(file, cve20Handler);
|
||||
}
|
||||
|
||||
private void processFiles() throws UpdateException {
|
||||
String msg = String.format("Processing Started for NVD CVE - %s", filePair.getNvdCveInfo().getId());
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO, msg);
|
||||
try {
|
||||
importXML(filePair.getFirst(), filePair.getSecond());
|
||||
cveDB.commit();
|
||||
properties.save(filePair.getNvdCveInfo());
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (ParserConfigurationException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SAXException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SQLException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (DatabaseException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
filePair.cleanup();
|
||||
}
|
||||
msg = String.format("Processing Complete for NVD CVE - %s", filePair.getNvdCveInfo().getId());
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import org.owasp.dependencycheck.data.nvdcve.InvalidDataException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import static org.owasp.dependencycheck.data.update.DataStoreMetaInfo.MODIFIED;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
|
||||
/**
|
||||
* Class responsible for updating the NVDCVE data store.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class StandardUpdate {
|
||||
|
||||
/**
|
||||
* The max thread pool size to use when downloading files.
|
||||
*/
|
||||
public static final int MAX_THREAD_POOL_SIZE = Settings.getInt(Settings.KEYS.MAX_DOWNLOAD_THREAD_POOL_SIZE, 3);
|
||||
/**
|
||||
* Information about the timestamps and URLs for data that needs to be
|
||||
* updated.
|
||||
*/
|
||||
private DataStoreMetaInfo properties;
|
||||
/**
|
||||
* A collection of updateable NVD CVE items.
|
||||
*/
|
||||
private Updateable updateable;
|
||||
/**
|
||||
* A flag indicating whether or not the current data store should be
|
||||
* deleted.
|
||||
*/
|
||||
private boolean deleteAndRecreate = false;
|
||||
/**
|
||||
* Reference to the Cve Database.
|
||||
*/
|
||||
private CveDB cveDB = null;
|
||||
|
||||
/**
|
||||
* Gets whether or not an update is needed.
|
||||
*
|
||||
* @return true or false depending on whether an update is needed
|
||||
*/
|
||||
public boolean isUpdateNeeded() {
|
||||
return updateable.isUpdateNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of deleteAndRecreate.
|
||||
*
|
||||
* @param deleteAndRecreate new value of deleteAndRecreate
|
||||
*/
|
||||
protected void setDeleteAndRecreate(boolean deleteAndRecreate) {
|
||||
this.deleteAndRecreate = deleteAndRecreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of deleteAndRecreate.
|
||||
*
|
||||
* @return the value of deleteAndRecreate
|
||||
*/
|
||||
public boolean shouldDeleteAndRecreate() {
|
||||
return deleteAndRecreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Standard Update Task.
|
||||
*
|
||||
* @throws MalformedURLException thrown if a configured URL is malformed
|
||||
* @throws DownloadFailedException thrown if a timestamp cannot be checked
|
||||
* on a configured URL
|
||||
* @throws UpdateException thrown if there is an exception generating the
|
||||
* update task
|
||||
*/
|
||||
public StandardUpdate() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
properties = new DataStoreMetaInfo();
|
||||
updateable = updatesNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Downloads the latest NVD CVE XML file from the web and imports it into
|
||||
* the current CVE Database.</p>
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
*/
|
||||
public void update() throws UpdateException {
|
||||
int maxUpdates = 0;
|
||||
try {
|
||||
for (NvdCveInfo cve : updateable) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
maxUpdates += 1;
|
||||
}
|
||||
}
|
||||
if (maxUpdates <= 0) {
|
||||
return;
|
||||
}
|
||||
if (maxUpdates > 3) {
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO,
|
||||
"NVD CVE requires several updates; this could take a couple of minutes.");
|
||||
}
|
||||
if (maxUpdates > 0) {
|
||||
openDataStores();
|
||||
}
|
||||
|
||||
final int poolSize = (MAX_THREAD_POOL_SIZE > maxUpdates) ? MAX_THREAD_POOL_SIZE : maxUpdates;
|
||||
final ExecutorService downloadExecutor = Executors.newFixedThreadPool(poolSize);
|
||||
final ExecutorService processExecutor = Executors.newSingleThreadExecutor();
|
||||
final Set<Future<CallableDownloadTask>> downloadFutures = new HashSet<Future<CallableDownloadTask>>(maxUpdates);
|
||||
final Set<Future<ProcessTask>> processFutures = new HashSet<Future<ProcessTask>>(maxUpdates);
|
||||
int ctr = 0;
|
||||
for (NvdCveInfo cve : updateable) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
ctr += 1;
|
||||
final File file1;
|
||||
final File file2;
|
||||
try {
|
||||
file1 = File.createTempFile("cve" + cve.getId() + "_", ".xml");
|
||||
file2 = File.createTempFile("cve_1_2_" + cve.getId() + "_", ".xml");
|
||||
} catch (IOException ex) {
|
||||
throw new UpdateException(ex);
|
||||
}
|
||||
final CallableDownloadTask call = new CallableDownloadTask(cve, file1, file2);
|
||||
downloadFutures.add(downloadExecutor.submit(call));
|
||||
|
||||
boolean waitForFuture = ctr % 2 == 0;
|
||||
|
||||
final Iterator<Future<CallableDownloadTask>> itr = downloadFutures.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Future<CallableDownloadTask> future = itr.next();
|
||||
if (waitForFuture) { //only allow two NVD/CVE files to be downloaded at a time
|
||||
spinWaitForFuture(future);
|
||||
}
|
||||
if (future.isDone()) { //if we find something complete, add it to the process queue
|
||||
try {
|
||||
final CallableDownloadTask filePair = future.get();
|
||||
itr.remove();
|
||||
final ProcessTask task = new ProcessTask(cveDB, properties, filePair);
|
||||
processFutures.add(processExecutor.submit(task));
|
||||
} catch (InterruptedException ex) {
|
||||
downloadExecutor.shutdownNow();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Thread was interupted", ex);
|
||||
throw new UpdateException(ex);
|
||||
} catch (ExecutionException ex) {
|
||||
downloadExecutor.shutdownNow();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new UpdateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final Iterator<Future<CallableDownloadTask>> itr = downloadFutures.iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Future<CallableDownloadTask> future = itr.next();
|
||||
final CallableDownloadTask filePair = future.get();
|
||||
final ProcessTask task = new ProcessTask(cveDB, properties, filePair);
|
||||
processFutures.add(processExecutor.submit(task));
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
downloadExecutor.shutdownNow();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Thread was interupted during download", ex);
|
||||
throw new UpdateException(ex);
|
||||
} catch (ExecutionException ex) {
|
||||
downloadExecutor.shutdownNow();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Execution Exception during download", ex);
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
downloadExecutor.shutdown();
|
||||
}
|
||||
|
||||
for (Future<ProcessTask> future : processFutures) {
|
||||
try {
|
||||
final ProcessTask task = future.get();
|
||||
if (task.getException() != null) {
|
||||
throw task.getException();
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
processExecutor.shutdownNow();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Thread was interupted during processing", ex);
|
||||
throw new UpdateException(ex);
|
||||
} catch (ExecutionException ex) {
|
||||
processExecutor.shutdownNow();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Execution Exception during process", ex);
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
processExecutor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
if (maxUpdates >= 1) { //ensure the modified file date gets written
|
||||
properties.save(updateable.get(MODIFIED));
|
||||
cveDB.cleanupDatabase();
|
||||
}
|
||||
} finally {
|
||||
closeDataStores();
|
||||
}
|
||||
}
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="OLD version of update() - not multithreaded">
|
||||
/*
|
||||
* TODO - remove this
|
||||
public void update() throws UpdateException {
|
||||
try {
|
||||
int maxUpdates = 0;
|
||||
for (NvdCveInfo cve : getUpdateable()) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
maxUpdates += 1;
|
||||
}
|
||||
}
|
||||
if (maxUpdates > 3) {
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO,
|
||||
"NVD CVE requires several updates; this could take a couple of minutes.");
|
||||
}
|
||||
if (maxUpdates > 0) {
|
||||
openDataStores();
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (NvdCveInfo cve : getUpdateable()) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
count += 1;
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO,
|
||||
"Updating NVD CVE ({0} of {1})", new Object[]{count, maxUpdates});
|
||||
URL url = new URL(cve.getUrl());
|
||||
File outputPath = null;
|
||||
File outputPath12 = null;
|
||||
try {
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO,
|
||||
"Downloading {0}", cve.getUrl());
|
||||
outputPath = File.createTempFile("cve" + cve.getId() + "_", ".xml");
|
||||
Downloader.fetchFile(url, outputPath);
|
||||
|
||||
url = new URL(cve.getOldSchemaVersionUrl());
|
||||
outputPath12 = File.createTempFile("cve_1_2_" + cve.getId() + "_", ".xml");
|
||||
Downloader.fetchFile(url, outputPath12);
|
||||
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO,
|
||||
"Processing {0}", cve.getUrl());
|
||||
|
||||
importXML(outputPath, outputPath12);
|
||||
|
||||
getCveDB().commit();
|
||||
getProperties().save(cve);
|
||||
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.INFO,
|
||||
"Completed update {0} of {1}", new Object[]{count, maxUpdates});
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (ParserConfigurationException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SAXException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SQLException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (DatabaseException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
boolean deleted = false;
|
||||
try {
|
||||
if (outputPath != null && outputPath.exists()) {
|
||||
deleted = outputPath.delete();
|
||||
}
|
||||
} finally {
|
||||
if (outputPath != null && (outputPath.exists() || !deleted)) {
|
||||
outputPath.deleteOnExit();
|
||||
}
|
||||
}
|
||||
try {
|
||||
deleted = false;
|
||||
if (outputPath12 != null && outputPath12.exists()) {
|
||||
deleted = outputPath12.delete();
|
||||
}
|
||||
} finally {
|
||||
if (outputPath12 != null && (outputPath12.exists() || !deleted)) {
|
||||
outputPath12.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxUpdates >= 1) { //ensure the modified file date gets written
|
||||
getProperties().save(getUpdateable().get(MODIFIED));
|
||||
getCveDB().cleanupDatabase();
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
closeDataStores();
|
||||
}
|
||||
}
|
||||
*/
|
||||
//</editor-fold>
|
||||
/**
|
||||
* Determines if the index needs to be updated. This is done by fetching the
|
||||
* NVD CVE meta data and checking the last update date. If the data needs to
|
||||
* be refreshed this method will return the NvdCveUrl for the files that
|
||||
* need to be updated.
|
||||
*
|
||||
* @return the collection of files that need to be updated
|
||||
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
|
||||
* data is incorrect
|
||||
* @throws DownloadFailedException is thrown if there is an error.
|
||||
* downloading the NVD CVE download data file
|
||||
* @throws UpdateException Is thrown if there is an issue with the last
|
||||
* updated properties file
|
||||
*/
|
||||
protected final Updateable updatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
Updateable updates = null;
|
||||
try {
|
||||
updates = retrieveCurrentTimestampsFromWeb();
|
||||
} catch (InvalidDataException ex) {
|
||||
final String msg = "Unable to retrieve valid timestamp from nvd cve downloads page";
|
||||
Logger
|
||||
.getLogger(StandardUpdate.class
|
||||
.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DownloadFailedException(msg, ex);
|
||||
} catch (InvalidSettingException ex) {
|
||||
Logger.getLogger(StandardUpdate.class
|
||||
.getName()).log(Level.FINE, "Invalid setting found when retrieving timestamps", ex);
|
||||
throw new DownloadFailedException(
|
||||
"Invalid settings", ex);
|
||||
}
|
||||
|
||||
if (updates == null) {
|
||||
throw new DownloadFailedException("Unable to retrieve the timestamps of the currently published NVD CVE data");
|
||||
}
|
||||
if (!properties.isEmpty()) {
|
||||
try {
|
||||
float version;
|
||||
|
||||
if (properties.getProperty("version") == null) {
|
||||
deleteAndRecreate = true;
|
||||
} else {
|
||||
try {
|
||||
version = Float.parseFloat(properties.getProperty("version"));
|
||||
final float currentVersion = Float.parseFloat(CveDB.DB_SCHEMA_VERSION);
|
||||
if (currentVersion > version) {
|
||||
deleteAndRecreate = true;
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
deleteAndRecreate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteAndRecreate) {
|
||||
return updates;
|
||||
}
|
||||
|
||||
final long lastUpdated = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED, "0"));
|
||||
final Date now = new Date();
|
||||
final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7);
|
||||
if (lastUpdated == updates.getTimeStamp(MODIFIED)) {
|
||||
updates.clear(); //we don't need to update anything.
|
||||
} else if (withinRange(lastUpdated, now.getTime(), days)) {
|
||||
for (NvdCveInfo entry : updates) {
|
||||
if (MODIFIED.equals(entry.getId())) {
|
||||
entry.setNeedsUpdate(true);
|
||||
} else {
|
||||
entry.setNeedsUpdate(false);
|
||||
}
|
||||
}
|
||||
} else { //we figure out which of the several XML files need to be downloaded.
|
||||
for (NvdCveInfo entry : updates) {
|
||||
if (MODIFIED.equals(entry.getId())) {
|
||||
entry.setNeedsUpdate(true);
|
||||
} else {
|
||||
long currentTimestamp = 0;
|
||||
try {
|
||||
currentTimestamp = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED_BASE + entry.getId(), "0"));
|
||||
} catch (NumberFormatException ex) {
|
||||
final String msg = String.format("Error parsing '%s' '%s' from nvdcve.lastupdated",
|
||||
DataStoreMetaInfo.LAST_UPDATED_BASE, entry.getId());
|
||||
Logger
|
||||
.getLogger(StandardUpdate.class
|
||||
.getName()).log(Level.FINE, msg, ex);
|
||||
}
|
||||
if (currentTimestamp == entry.getTimestamp()) {
|
||||
entry.setNeedsUpdate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
final String msg = "An invalid schema version or timestamp exists in the data.properties file.";
|
||||
Logger
|
||||
.getLogger(StandardUpdate.class
|
||||
.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(StandardUpdate.class
|
||||
.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the timestamps from the NVD CVE meta data file.
|
||||
*
|
||||
* @return the timestamp from the currently published nvdcve downloads page
|
||||
* @throws MalformedURLException thrown if the URL for the NVD CCE Meta data
|
||||
* is incorrect.
|
||||
* @throws DownloadFailedException thrown if there is an error downloading
|
||||
* the nvd cve meta data file
|
||||
* @throws InvalidDataException thrown if there is an exception parsing the
|
||||
* timestamps
|
||||
* @throws InvalidSettingException thrown if the settings are invalid
|
||||
*/
|
||||
private Updateable retrieveCurrentTimestampsFromWeb()
|
||||
throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException {
|
||||
|
||||
final Updateable updates = new Updateable();
|
||||
updates.add(MODIFIED, Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL),
|
||||
Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL),
|
||||
false);
|
||||
|
||||
final int start = Settings.getInt(Settings.KEYS.CVE_START_YEAR);
|
||||
final int end = Calendar.getInstance().get(Calendar.YEAR);
|
||||
final String baseUrl20 = Settings.getString(Settings.KEYS.CVE_SCHEMA_2_0);
|
||||
final String baseUrl12 = Settings.getString(Settings.KEYS.CVE_SCHEMA_1_2);
|
||||
for (int i = start; i <= end; i++) {
|
||||
updates.add(Integer.toString(i), String.format(baseUrl20, i),
|
||||
String.format(baseUrl12, i),
|
||||
true);
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the existing data directories.
|
||||
*
|
||||
* @throws IOException thrown if the directory cannot be deleted
|
||||
*/
|
||||
protected void deleteExistingData() throws IOException {
|
||||
File data = Settings.getDataFile(Settings.KEYS.CVE_DATA_DIRECTORY);
|
||||
if (data.exists()) {
|
||||
FileUtils.delete(data);
|
||||
}
|
||||
data = DataStoreMetaInfo.getPropertiesFile();
|
||||
if (data.exists()) {
|
||||
FileUtils.delete(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the CVE and CPE data stores.
|
||||
*/
|
||||
protected void closeDataStores() {
|
||||
if (cveDB != null) {
|
||||
try {
|
||||
cveDB.close();
|
||||
} catch (Exception ignore) {
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINEST, "Error closing the cveDB", ignore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the CVE and CPE data stores.
|
||||
*
|
||||
* @throws UpdateException thrown if a data store cannot be opened
|
||||
*/
|
||||
protected void openDataStores() throws UpdateException {
|
||||
//open the cve and cpe data stores
|
||||
try {
|
||||
cveDB = new CveDB();
|
||||
cveDB.open();
|
||||
} catch (IOException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "IO Error opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
} catch (SQLException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "SQL Exception opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
} catch (DatabaseException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Database Exception opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
closeDataStores();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Class not found exception opening databases", ex);
|
||||
throw new UpdateException("Error updating the CPE/CVE data, please see the log file for more details.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the epoch date is within the range specified of the
|
||||
* compareTo epoch time. This takes the (compareTo-date)/1000/60/60/24 to
|
||||
* get the number of days. If the calculated days is less then the range the
|
||||
* date is considered valid.
|
||||
*
|
||||
* @param date the date to be checked.
|
||||
* @param compareTo the date to compare to.
|
||||
* @param range the range in days to be considered valid.
|
||||
* @return whether or not the date is within the range.
|
||||
*/
|
||||
protected boolean withinRange(long date, long compareTo, int range) {
|
||||
final double differenceInDays = (compareTo - date) / 1000.0 / 60.0 / 60.0 / 24.0;
|
||||
return differenceInDays < range;
|
||||
}
|
||||
|
||||
private void spinWaitForFuture(final Future<CallableDownloadTask> future) {
|
||||
//then wait for downloads to finish
|
||||
while (!future.isDone()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import org.owasp.dependencycheck.data.nvdcve.InvalidDataException;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import static org.owasp.dependencycheck.data.update.DataStoreMetaInfo.MODIFIED;
|
||||
|
||||
/**
|
||||
* Class responsible for updating the CPE and NVDCVE data stores.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class StandardUpdateTask extends AbstractUpdateTask {
|
||||
|
||||
/**
|
||||
* Constructs a new Standard Update Task.
|
||||
*
|
||||
* @param properties information about the data store
|
||||
* @throws MalformedURLException thrown if a configured URL is malformed
|
||||
* @throws DownloadFailedException thrown if a timestamp cannot be checked
|
||||
* on a configured URL
|
||||
* @throws UpdateException thrown if there is an exception generating the
|
||||
* update task
|
||||
*/
|
||||
public StandardUpdateTask(DataStoreMetaInfo properties) throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Downloads the latest NVD CVE XML file from the web and imports it into
|
||||
* the current CVE Database.</p>
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
*/
|
||||
@Override
|
||||
public void update() throws UpdateException {
|
||||
try {
|
||||
int maxUpdates = 0;
|
||||
for (NvdCveInfo cve : getUpdateable()) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
maxUpdates += 1;
|
||||
}
|
||||
}
|
||||
if (maxUpdates > 3) {
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
|
||||
"NVD CVE requires several updates; this could take a couple of minutes.");
|
||||
}
|
||||
if (maxUpdates > 0) {
|
||||
openDataStores();
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (NvdCveInfo cve : getUpdateable()) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
count += 1;
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
|
||||
"Updating NVD CVE ({0} of {1})", new Object[]{count, maxUpdates});
|
||||
URL url = new URL(cve.getUrl());
|
||||
File outputPath = null;
|
||||
File outputPath12 = null;
|
||||
try {
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
|
||||
"Downloading {0}", cve.getUrl());
|
||||
outputPath = File.createTempFile("cve" + cve.getId() + "_", ".xml");
|
||||
Downloader.fetchFile(url, outputPath);
|
||||
|
||||
url = new URL(cve.getOldSchemaVersionUrl());
|
||||
outputPath12 = File.createTempFile("cve_1_2_" + cve.getId() + "_", ".xml");
|
||||
Downloader.fetchFile(url, outputPath12);
|
||||
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
|
||||
"Processing {0}", cve.getUrl());
|
||||
|
||||
importXML(outputPath, outputPath12);
|
||||
|
||||
getCveDB().commit();
|
||||
getCpeIndex().commit();
|
||||
getProperties().save(cve);
|
||||
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.INFO,
|
||||
"Completed update {0} of {1}", new Object[]{count, maxUpdates});
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (ParserConfigurationException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SAXException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (IOException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (SQLException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (DatabaseException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
boolean deleted = false;
|
||||
try {
|
||||
if (outputPath != null && outputPath.exists()) {
|
||||
deleted = outputPath.delete();
|
||||
}
|
||||
} finally {
|
||||
if (outputPath != null && (outputPath.exists() || !deleted)) {
|
||||
outputPath.deleteOnExit();
|
||||
}
|
||||
}
|
||||
try {
|
||||
deleted = false;
|
||||
if (outputPath12 != null && outputPath12.exists()) {
|
||||
deleted = outputPath12.delete();
|
||||
}
|
||||
} finally {
|
||||
if (outputPath12 != null && (outputPath12.exists() || !deleted)) {
|
||||
outputPath12.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxUpdates >= 1) { //ensure the modified file date gets written
|
||||
getProperties().save(getUpdateable().get(MODIFIED));
|
||||
getCveDB().cleanupDatabase();
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
closeDataStores();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the index needs to be updated. This is done by fetching the
|
||||
* NVD CVE meta data and checking the last update date. If the data needs to
|
||||
* be refreshed this method will return the NvdCveUrl for the files that
|
||||
* need to be updated.
|
||||
*
|
||||
* @return the collection of files that need to be updated
|
||||
* @throws MalformedURLException is thrown if the URL for the NVD CVE Meta
|
||||
* data is incorrect
|
||||
* @throws DownloadFailedException is thrown if there is an error.
|
||||
* downloading the NVD CVE download data file
|
||||
* @throws UpdateException Is thrown if there is an issue with the last
|
||||
* updated properties file
|
||||
*/
|
||||
@Override
|
||||
protected Updateable updatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
Updateable updates = null;
|
||||
try {
|
||||
updates = retrieveCurrentTimestampsFromWeb();
|
||||
} catch (InvalidDataException ex) {
|
||||
final String msg = "Unable to retrieve valid timestamp from nvd cve downloads page";
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DownloadFailedException(msg, ex);
|
||||
} catch (InvalidSettingException ex) {
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.FINE, "Invalid setting found when retrieving timestamps", ex);
|
||||
throw new DownloadFailedException("Invalid settings", ex);
|
||||
}
|
||||
|
||||
if (updates == null) {
|
||||
throw new DownloadFailedException("Unable to retrieve the timestamps of the currently published NVD CVE data");
|
||||
}
|
||||
final DataStoreMetaInfo properties = getProperties();
|
||||
if (!properties.isEmpty()) {
|
||||
try {
|
||||
float version;
|
||||
|
||||
if (properties.getProperty("version") == null) {
|
||||
setDeleteAndRecreate(true);
|
||||
} else {
|
||||
try {
|
||||
version = Float.parseFloat(properties.getProperty("version"));
|
||||
final float currentVersion = Float.parseFloat(CveDB.DB_SCHEMA_VERSION);
|
||||
if (currentVersion > version) {
|
||||
setDeleteAndRecreate(true);
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
setDeleteAndRecreate(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDeleteAndRecreate()) {
|
||||
return updates;
|
||||
}
|
||||
|
||||
final long lastUpdated = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED, "0"));
|
||||
final Date now = new Date();
|
||||
final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7);
|
||||
if (lastUpdated == updates.getTimeStamp(MODIFIED)) {
|
||||
updates.clear(); //we don't need to update anything.
|
||||
} else if (withinRange(lastUpdated, now.getTime(), days)) {
|
||||
for (NvdCveInfo entry : updates) {
|
||||
if (MODIFIED.equals(entry.getId())) {
|
||||
entry.setNeedsUpdate(true);
|
||||
} else {
|
||||
entry.setNeedsUpdate(false);
|
||||
}
|
||||
}
|
||||
} else { //we figure out which of the several XML files need to be downloaded.
|
||||
for (NvdCveInfo entry : updates) {
|
||||
if (MODIFIED.equals(entry.getId())) {
|
||||
entry.setNeedsUpdate(true);
|
||||
} else {
|
||||
long currentTimestamp = 0;
|
||||
try {
|
||||
currentTimestamp = Long.parseLong(properties.getProperty(DataStoreMetaInfo.LAST_UPDATED_BASE + entry.getId(), "0"));
|
||||
} catch (NumberFormatException ex) {
|
||||
final String msg = String.format("Error parsing '%s' '%s' from nvdcve.lastupdated",
|
||||
DataStoreMetaInfo.LAST_UPDATED_BASE, entry.getId());
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.FINE, msg, ex);
|
||||
}
|
||||
if (currentTimestamp == entry.getTimestamp()) {
|
||||
entry.setNeedsUpdate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
final String msg = "An invalid schema version or timestamp exists in the data.properties file.";
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(StandardUpdateTask.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the timestamps from the NVD CVE meta data file.
|
||||
*
|
||||
* @return the timestamp from the currently published nvdcve downloads page
|
||||
* @throws MalformedURLException thrown if the URL for the NVD CCE Meta data
|
||||
* is incorrect.
|
||||
* @throws DownloadFailedException thrown if there is an error downloading
|
||||
* the nvd cve meta data file
|
||||
* @throws InvalidDataException thrown if there is an exception parsing the
|
||||
* timestamps
|
||||
* @throws InvalidSettingException thrown if the settings are invalid
|
||||
*/
|
||||
private Updateable retrieveCurrentTimestampsFromWeb()
|
||||
throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException {
|
||||
|
||||
final Updateable updates = new Updateable();
|
||||
updates.add(MODIFIED, Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL),
|
||||
Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL),
|
||||
false);
|
||||
|
||||
final int start = Settings.getInt(Settings.KEYS.CVE_START_YEAR);
|
||||
final int end = Calendar.getInstance().get(Calendar.YEAR);
|
||||
final String baseUrl20 = Settings.getString(Settings.KEYS.CVE_SCHEMA_2_0);
|
||||
final String baseUrl12 = Settings.getString(Settings.KEYS.CVE_SCHEMA_1_2);
|
||||
for (int i = start; i <= end; i++) {
|
||||
updates.add(Integer.toString(i), String.format(baseUrl20, i),
|
||||
String.format(baseUrl12, i),
|
||||
true);
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
|
||||
/**
|
||||
* An UpdateTask Factory that instantiates the correct UpdateTask based on the
|
||||
* given configuration.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public final class UpdateTaskFactory {
|
||||
|
||||
/**
|
||||
* private constructor for a utility class.
|
||||
*/
|
||||
private UpdateTaskFactory() {
|
||||
//empty contrusctor for utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the appropriate update task based on configuration.
|
||||
*
|
||||
* @return an UpdateTask
|
||||
* @throws MalformedURLException thrown if a configured URL is malformed
|
||||
* @throws DownloadFailedException thrown if a timestamp cannot be checked
|
||||
* on a configured URL
|
||||
* @throws UpdateException thrown if there is an exception generating the
|
||||
* update task
|
||||
*/
|
||||
public static UpdateTask getUpdateTask() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
final UpdateTask task;
|
||||
final DataStoreMetaInfo properties = new DataStoreMetaInfo();
|
||||
if (properties.isBatchUpdateMode()) {
|
||||
task = new BatchUpdateTask(properties);
|
||||
} else {
|
||||
task = new StandardUpdateTask(properties);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A simple PropertyType used to represent a string value that could be used as
|
||||
* a regular expression or could be case insensitive. The equals method has been
|
||||
* over-ridden so that the object will correctly compare to strings.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class PropertyType {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="properties">
|
||||
/**
|
||||
* The value.
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Gets the value of the value property.
|
||||
*
|
||||
* @return the value of the value property
|
||||
*
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the value property.
|
||||
*
|
||||
* @param value the value of the value property
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
/**
|
||||
* Whether or not the expression is a regex.
|
||||
*/
|
||||
private boolean regex = false;
|
||||
|
||||
/**
|
||||
* Returns whether or not the value is a regex.
|
||||
*
|
||||
* @return true if the value is a regex, otherwise false
|
||||
*
|
||||
*/
|
||||
public boolean isRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the value property is a regex.
|
||||
*
|
||||
* @param value true if the value is a regex, otherwise false
|
||||
*
|
||||
*/
|
||||
public void setRegex(boolean value) {
|
||||
this.regex = value;
|
||||
}
|
||||
/**
|
||||
* Indicates case sensitivity.
|
||||
*/
|
||||
private boolean caseSensitive = false;
|
||||
|
||||
/**
|
||||
* Gets the value of the caseSensitive property.
|
||||
*
|
||||
* @return true if the value is case sensitive
|
||||
*
|
||||
*/
|
||||
public boolean isCaseSensitive() {
|
||||
return caseSensitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the caseSensitive property.
|
||||
*
|
||||
* @param value whether the value is case sensitive
|
||||
*
|
||||
*/
|
||||
public void setCaseSensitive(boolean value) {
|
||||
this.caseSensitive = value;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* Uses the object's properties to determine if the supplied string matches
|
||||
* the value of this property.
|
||||
*
|
||||
* @param text the String to validate
|
||||
* @return whether the text supplied is matched by the value of the property
|
||||
*/
|
||||
public boolean matches(String text) {
|
||||
if (text == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.regex) {
|
||||
Pattern rx;
|
||||
if (this.caseSensitive) {
|
||||
rx = Pattern.compile(this.value);
|
||||
} else {
|
||||
rx = Pattern.compile(this.value, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
return rx.matcher(text).matches();
|
||||
} else {
|
||||
if (this.caseSensitive) {
|
||||
return value.equals(text);
|
||||
} else {
|
||||
return value.equalsIgnoreCase(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="standard implmentations of hashCode, equals, and toString">
|
||||
/**
|
||||
* Default implementation of hashCode.
|
||||
*
|
||||
* @return the hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 59 * hash + (this.value != null ? this.value.hashCode() : 0);
|
||||
hash = 59 * hash + (this.regex ? 1 : 0);
|
||||
hash = 59 * hash + (this.caseSensitive ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of equals.
|
||||
*
|
||||
* @param obj the object to compare
|
||||
* @return whether the objects are equivalent
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final PropertyType other = (PropertyType) obj;
|
||||
if ((this.value == null) ? (other.value != null) : !this.value.equals(other.value)) {
|
||||
return false;
|
||||
}
|
||||
if (this.regex != other.regex) {
|
||||
return false;
|
||||
}
|
||||
if (this.caseSensitive != other.caseSensitive) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of toString().
|
||||
*
|
||||
* @return the string representation of the object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PropertyType{" + "value=" + value + ", regex=" + regex + ", caseSensitive=" + caseSensitive + '}';
|
||||
}
|
||||
//</editor-fold>
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
/**
|
||||
* An XML parsing error handler.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class SuppressionErrorHandler implements ErrorHandler {
|
||||
|
||||
/**
|
||||
* Builds a prettier exception message.
|
||||
*
|
||||
* @param ex the SAXParseException
|
||||
* @return an easier to read exception message
|
||||
*/
|
||||
private String getPrettyParseExceptionInfo(SAXParseException ex) {
|
||||
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
|
||||
if (ex.getSystemId() != null) {
|
||||
sb.append("systemId=").append(ex.getSystemId()).append(", ");
|
||||
}
|
||||
if (ex.getPublicId() != null) {
|
||||
sb.append("publicId=").append(ex.getPublicId()).append(", ");
|
||||
}
|
||||
if (ex.getLineNumber() > 0) {
|
||||
sb.append("Line=").append(ex.getLineNumber());
|
||||
}
|
||||
if (ex.getColumnNumber() > 0) {
|
||||
sb.append(", Column=").append(ex.getColumnNumber());
|
||||
}
|
||||
sb.append(": ").append(ex.getMessage());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs warnings.
|
||||
*
|
||||
* @param ex the warning to log
|
||||
* @throws SAXException is never thrown
|
||||
*/
|
||||
@Override
|
||||
public void warning(SAXParseException ex) throws SAXException {
|
||||
Logger.getLogger(SuppressionErrorHandler.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors.
|
||||
*
|
||||
* @param ex the error to handle
|
||||
* @throws SAXException is always thrown
|
||||
*/
|
||||
@Override
|
||||
public void error(SAXParseException ex) throws SAXException {
|
||||
throw new SAXException(getPrettyParseExceptionInfo(ex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fatal exceptions.
|
||||
*
|
||||
* @param ex a fatal exception
|
||||
* @throws SAXException is always
|
||||
*/
|
||||
@Override
|
||||
public void fatalError(SAXParseException ex) throws SAXException {
|
||||
throw new SAXException(getPrettyParseExceptionInfo(ex));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* A handler to load suppression rules.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class SuppressionHandler extends DefaultHandler {
|
||||
|
||||
/**
|
||||
* The suppress node, indicates the start of a new rule.
|
||||
*/
|
||||
public static final String SUPPRESS = "suppress";
|
||||
/**
|
||||
* The file path element name.
|
||||
*/
|
||||
public static final String FILE_PATH = "filePath";
|
||||
/**
|
||||
* The sha1 hash element name.
|
||||
*/
|
||||
public static final String SHA1 = "sha1";
|
||||
/**
|
||||
* The CVE element name.
|
||||
*/
|
||||
public static final String CVE = "cve";
|
||||
/**
|
||||
* The CPE element name.
|
||||
*/
|
||||
public static final String CPE = "cpe";
|
||||
/**
|
||||
* The CWE element name.
|
||||
*/
|
||||
public static final String CWE = "cwe";
|
||||
/**
|
||||
* The cvssBelow element name.
|
||||
*/
|
||||
public static final String CVSS_BELOW = "cvssBelow";
|
||||
/**
|
||||
* A list of suppression rules.
|
||||
*/
|
||||
private List<SuppressionRule> supressionRules = new ArrayList<SuppressionRule>();
|
||||
|
||||
/**
|
||||
* Get the value of supressionRules.
|
||||
*
|
||||
* @return the value of supressionRules
|
||||
*/
|
||||
public List<SuppressionRule> getSupressionRules() {
|
||||
return supressionRules;
|
||||
}
|
||||
/**
|
||||
* The current rule being read.
|
||||
*/
|
||||
private SuppressionRule rule;
|
||||
/**
|
||||
* The attributes of the node being read.
|
||||
*/
|
||||
private Attributes currentAttributes;
|
||||
/**
|
||||
* The current node text being extracted from the element.
|
||||
*/
|
||||
private StringBuffer currentText;
|
||||
|
||||
/**
|
||||
* Handles the start element event.
|
||||
*
|
||||
* @param uri the uri of the element being processed
|
||||
* @param localName the local name of the element being processed
|
||||
* @param qName the qName of the element being processed
|
||||
* @param attributes the attributes of the element being processed
|
||||
* @throws SAXException thrown if there is an exception processing
|
||||
*/
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
currentAttributes = null;
|
||||
currentText = new StringBuffer();
|
||||
|
||||
if (SUPPRESS.equals(qName)) {
|
||||
rule = new SuppressionRule();
|
||||
} else if (FILE_PATH.equals(qName)) {
|
||||
currentAttributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the end element event.
|
||||
*
|
||||
* @param uri the uri of the element
|
||||
* @param localName the local name of the element
|
||||
* @param qName the qName of the element
|
||||
* @throws SAXException thrown if there is an exception processing
|
||||
*/
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
if (SUPPRESS.equals(qName)) {
|
||||
supressionRules.add(rule);
|
||||
rule = null;
|
||||
} else if (FILE_PATH.equals(qName)) {
|
||||
final PropertyType pt = processPropertyType();
|
||||
rule.setFilePath(pt);
|
||||
} else if (SHA1.equals(qName)) {
|
||||
rule.setSha1(currentText.toString());
|
||||
} else if (CPE.equals(qName)) {
|
||||
final PropertyType pt = processPropertyType();
|
||||
rule.addCpe(pt);
|
||||
} else if (CWE.equals(qName)) {
|
||||
rule.addCwe(currentText.toString());
|
||||
} else if (CVE.equals(qName)) {
|
||||
rule.addCve(currentText.toString());
|
||||
} else if (CVSS_BELOW.equals(qName)) {
|
||||
final float cvss = Float.parseFloat(currentText.toString());
|
||||
rule.addCvssBelow(cvss);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the body text of the node being processed.
|
||||
*
|
||||
* @param ch the char array of text
|
||||
* @param start the start position to copy text from in the char array
|
||||
* @param length the number of characters to copy from the char array
|
||||
* @throws SAXException thrown if there is a parsing exception
|
||||
*/
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
currentText.append(ch, start, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes field members that have been collected during the characters
|
||||
* and startElement method to construct a PropertyType object.
|
||||
*
|
||||
* @return a PropertyType object
|
||||
*/
|
||||
private PropertyType processPropertyType() {
|
||||
final PropertyType pt = new PropertyType();
|
||||
pt.setValue(currentText.toString());
|
||||
if (currentAttributes != null && currentAttributes.getLength() > 0) {
|
||||
final String regex = currentAttributes.getValue("regex");
|
||||
if (regex != null) {
|
||||
pt.setRegex(Boolean.parseBoolean(regex));
|
||||
}
|
||||
final String caseSensitive = currentAttributes.getValue("caseSensitive");
|
||||
if (regex != null) {
|
||||
pt.setCaseSensitive(Boolean.parseBoolean(caseSensitive));
|
||||
}
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception used when parsing a suppression rule file fails.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class SuppressionParseException extends IOException {
|
||||
|
||||
/**
|
||||
* The serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*/
|
||||
public SuppressionParseException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
public SuppressionParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*
|
||||
* @param ex the cause of the download failure.
|
||||
*/
|
||||
public SuppressionParseException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the download failure.
|
||||
*/
|
||||
public SuppressionParseException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
/**
|
||||
* A simple validating parser for XML Suppression Rules.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class SuppressionParser {
|
||||
|
||||
/**
|
||||
* JAXP Schema Language. Source:
|
||||
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
|
||||
*/
|
||||
public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
|
||||
/**
|
||||
* W3C XML Schema. Source:
|
||||
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
|
||||
*/
|
||||
public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
|
||||
/**
|
||||
* JAXP Schema Source. Source:
|
||||
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
|
||||
*/
|
||||
public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
|
||||
|
||||
/**
|
||||
* Parses the given xml file and returns a list of the suppression rules
|
||||
* contained.
|
||||
*
|
||||
* @param file an xml file containing suppression rules
|
||||
* @return a list of suppression rules
|
||||
* @throws SuppressionParseException thrown if the xml file cannot be parsed
|
||||
*/
|
||||
public List<SuppressionRule> parseSuppressionRules(File file) throws SuppressionParseException {
|
||||
try {
|
||||
final InputStream schemaStream = this.getClass().getClassLoader().getResourceAsStream("schema/suppression.xsd");
|
||||
final SuppressionHandler handler = new SuppressionHandler();
|
||||
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
factory.setValidating(true);
|
||||
final SAXParser saxParser = factory.newSAXParser();
|
||||
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_LANGUAGE, SuppressionParser.W3C_XML_SCHEMA);
|
||||
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_SOURCE, new InputSource(schemaStream));
|
||||
final XMLReader xmlReader = saxParser.getXMLReader();
|
||||
xmlReader.setErrorHandler(new SuppressionErrorHandler());
|
||||
xmlReader.setContentHandler(handler);
|
||||
|
||||
final InputStream inputStream = new FileInputStream(file);
|
||||
final Reader reader = new InputStreamReader(inputStream, "UTF-8");
|
||||
final InputSource in = new InputSource(reader);
|
||||
//in.setEncoding("UTF-8");
|
||||
|
||||
xmlReader.parse(in);
|
||||
|
||||
|
||||
return handler.getSupressionRules();
|
||||
} catch (ParserConfigurationException ex) {
|
||||
Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new SuppressionParseException(ex);
|
||||
} catch (SAXException ex) {
|
||||
Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new SuppressionParseException(ex);
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new SuppressionParseException(ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(SuppressionParser.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new SuppressionParseException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class SuppressionRule {
|
||||
|
||||
/**
|
||||
* The file path for the suppression.
|
||||
*/
|
||||
private PropertyType filePath;
|
||||
|
||||
/**
|
||||
* Get the value of filePath.
|
||||
*
|
||||
* @return the value of filePath
|
||||
*/
|
||||
public PropertyType getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of filePath.
|
||||
*
|
||||
* @param filePath new value of filePath
|
||||
*/
|
||||
public void setFilePath(PropertyType filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
/**
|
||||
* The sha1 hash.
|
||||
*/
|
||||
private String sha1;
|
||||
|
||||
/**
|
||||
* Get the value of sha1.
|
||||
*
|
||||
* @return the value of sha1
|
||||
*/
|
||||
public String getSha1() {
|
||||
return sha1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of sha1.
|
||||
*
|
||||
* @param sha1 new value of sha1
|
||||
*/
|
||||
public void setSha1(String sha1) {
|
||||
this.sha1 = sha1;
|
||||
}
|
||||
/**
|
||||
* A list of CPEs to suppression
|
||||
*/
|
||||
private List<PropertyType> cpe = new ArrayList<PropertyType>();
|
||||
|
||||
/**
|
||||
* Get the value of cpe.
|
||||
*
|
||||
* @return the value of cpe
|
||||
*/
|
||||
public List<PropertyType> getCpe() {
|
||||
return cpe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cpe.
|
||||
*
|
||||
* @param cpe new value of cpe
|
||||
*/
|
||||
public void setCpe(List<PropertyType> cpe) {
|
||||
this.cpe = cpe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the cpe to the cpe list.
|
||||
*
|
||||
* @param cpe the cpe to add
|
||||
*/
|
||||
public void addCpe(PropertyType cpe) {
|
||||
this.cpe.add(cpe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this suppression rule as CPE entries.
|
||||
*
|
||||
* @return whether or not this suppression rule as CPE entries
|
||||
*/
|
||||
public boolean hasCpe() {
|
||||
return cpe.size() > 0;
|
||||
}
|
||||
/**
|
||||
* The list of cvssBelow scores.
|
||||
*/
|
||||
private List<Float> cvssBelow = new ArrayList<Float>();
|
||||
|
||||
/**
|
||||
* Get the value of cvssBelow.
|
||||
*
|
||||
* @return the value of cvssBelow
|
||||
*/
|
||||
public List<Float> getCvssBelow() {
|
||||
return cvssBelow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cvssBelow.
|
||||
*
|
||||
* @param cvssBelow new value of cvssBelow
|
||||
*/
|
||||
public void setCvssBelow(List<Float> cvssBelow) {
|
||||
this.cvssBelow = cvssBelow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the cvss to the cvssBelow list.
|
||||
*
|
||||
* @param cvss the cvss to add
|
||||
*/
|
||||
public void addCvssBelow(Float cvss) {
|
||||
this.cvssBelow.add(cvss);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this suppression rule has cvss suppressions.
|
||||
*
|
||||
* @return whether or not this suppression rule has cvss suppressions
|
||||
*/
|
||||
public boolean hasCvssBelow() {
|
||||
return cvssBelow.size() > 0;
|
||||
}
|
||||
/**
|
||||
* The list of cwe entries to suppress.
|
||||
*/
|
||||
private List<String> cwe = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Get the value of cwe.
|
||||
*
|
||||
* @return the value of cwe
|
||||
*/
|
||||
public List<String> getCwe() {
|
||||
return cwe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cwe.
|
||||
*
|
||||
* @param cwe new value of cwe
|
||||
*/
|
||||
public void setCwe(List<String> cwe) {
|
||||
this.cwe = cwe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the cwe to the cwe list.
|
||||
*
|
||||
* @param cwe the cwe to add
|
||||
*/
|
||||
public void addCwe(String cwe) {
|
||||
this.cwe.add(cwe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this suppression rule has CWE entries.
|
||||
*
|
||||
* @return whether this suppression rule has CWE entries
|
||||
*/
|
||||
public boolean hasCwe() {
|
||||
return cwe.size() > 0;
|
||||
}
|
||||
/**
|
||||
* The list of cve entries to suppress.
|
||||
*/
|
||||
private List<String> cve = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* Get the value of cve.
|
||||
*
|
||||
* @return the value of cve
|
||||
*/
|
||||
public List<String> getCve() {
|
||||
return cve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of cve.
|
||||
*
|
||||
* @param cve new value of cve
|
||||
*/
|
||||
public void setCve(List<String> cve) {
|
||||
this.cve = cve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the cve to the cve list.
|
||||
*
|
||||
* @param cve the cve to add
|
||||
*/
|
||||
public void addCve(String cve) {
|
||||
this.cve.add(cve);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this suppression rule has CVE entries.
|
||||
*
|
||||
* @return whether this suppression rule has CVE entries
|
||||
*/
|
||||
public boolean hasCve() {
|
||||
return cve.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a given dependency to determine if any CPE, CVE, CWE, or CVSS
|
||||
* scores should be suppressed. If any should be, they are removed from the
|
||||
* dependency.
|
||||
*
|
||||
* @param dependency a project dependency to analyze
|
||||
*/
|
||||
public void process(Dependency dependency) {
|
||||
if (filePath != null && !filePath.matches(dependency.getFilePath())) {
|
||||
return;
|
||||
}
|
||||
if (sha1 != null && !sha1.equalsIgnoreCase(dependency.getSha1sum())) {
|
||||
return;
|
||||
}
|
||||
if (this.hasCpe()) {
|
||||
final Iterator<Identifier> itr = dependency.getIdentifiers().iterator();
|
||||
while (itr.hasNext()) {
|
||||
final Identifier i = itr.next();
|
||||
for (PropertyType c : this.cpe) {
|
||||
if (cpeMatches(c, i)) {
|
||||
itr.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasCve() || hasCwe() || hasCvssBelow()) {
|
||||
final Iterator<Vulnerability> itr = dependency.getVulnerabilities().iterator();
|
||||
while (itr.hasNext()) {
|
||||
boolean remove = false;
|
||||
final Vulnerability v = itr.next();
|
||||
for (String entry : this.cve) {
|
||||
if (entry.equalsIgnoreCase(v.getName())) {
|
||||
remove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!remove) {
|
||||
for (String entry : this.cwe) {
|
||||
if (v.getCwe() != null) {
|
||||
final String toMatch = String.format("CWE-%s ", entry);
|
||||
final String toTest = v.getCwe().substring(0, toMatch.length()).toUpperCase();
|
||||
if (toTest.equals(toMatch)) {
|
||||
remove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!remove) {
|
||||
for (float cvss : this.cvssBelow) {
|
||||
if (v.getCvssScore() < cvss) {
|
||||
remove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remove) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if the cpe specified by the cpe suppression rule does not
|
||||
* specify a version.
|
||||
*
|
||||
* @param c a suppression rule identifier
|
||||
* @return true if the property type does not specify a version; otherwise
|
||||
* false
|
||||
*/
|
||||
boolean cpeHasNoVersion(PropertyType c) {
|
||||
if (c.isRegex()) {
|
||||
return false;
|
||||
} // cpe:/a:jboss:jboss:1.0.0:
|
||||
if (countCharacter(c.getValue(), ':') == 3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of occurrences of the character found within the
|
||||
* string.
|
||||
*
|
||||
* @param str the string to check
|
||||
* @param c the character to count
|
||||
* @return the number of times the character is found in the string
|
||||
*/
|
||||
int countCharacter(String str, char c) {
|
||||
int count = 0;
|
||||
int pos = str.indexOf(c) + 1;
|
||||
while (pos > 0) {
|
||||
count += 1;
|
||||
pos = str.indexOf(c, pos) + 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the cpeEntry specified as a PropertyType matches the given
|
||||
* Identifier.
|
||||
*
|
||||
* @param cpeEntry a suppression rule entry
|
||||
* @param identifier a CPE identifier to check
|
||||
* @return true if the entry matches; otherwise false
|
||||
*/
|
||||
boolean cpeMatches(PropertyType cpeEntry, Identifier identifier) {
|
||||
if (cpeEntry.matches(identifier.getValue())) {
|
||||
return true;
|
||||
} else if (cpeHasNoVersion(cpeEntry)) {
|
||||
if (cpeEntry.isCaseSensitive()) {
|
||||
if (identifier.getValue().startsWith(cpeEntry.getValue())) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
final String id = identifier.getValue().toLowerCase();
|
||||
final String check = cpeEntry.getValue().toLowerCase();
|
||||
if (id.startsWith(check)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.suppression</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes used to suppress findings.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
@@ -23,8 +23,10 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Authenticator;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -187,6 +189,22 @@ public final class Downloader {
|
||||
if (proxyUrl != null) {
|
||||
final int proxyPort = Settings.getInt(Settings.KEYS.PROXY_PORT);
|
||||
final SocketAddress addr = new InetSocketAddress(proxyUrl, proxyPort);
|
||||
|
||||
final String username = Settings.getString(Settings.KEYS.PROXY_USERNAME);
|
||||
final String password = Settings.getString(Settings.KEYS.PROXY_PASSWORD);
|
||||
if (username != null && password != null) {
|
||||
final Authenticator auth = new Authenticator() {
|
||||
@Override
|
||||
public PasswordAuthentication getPasswordAuthentication() {
|
||||
if (getRequestorType().equals(RequestorType.PROXY)) {
|
||||
return new PasswordAuthentication(username, password.toCharArray());
|
||||
}
|
||||
return super.getPasswordAuthentication();
|
||||
}
|
||||
};
|
||||
Authenticator.setDefault(auth);
|
||||
}
|
||||
|
||||
proxy = new Proxy(Proxy.Type.HTTP, addr);
|
||||
conn = (HttpURLConnection) url.openConnection(proxy);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.utils;
|
||||
|
||||
import java.util.logging.Filter;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* A simple log filter to limit the entries written to the verbose log file. The
|
||||
* verbose log file uses the root logger as I couldn't get anything else to
|
||||
* work; as such, this filter limits the log entries to specific classes.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class LogFilter implements Filter {
|
||||
|
||||
/**
|
||||
* Determines if the record should be logged.
|
||||
*
|
||||
* @param record a log record to examine
|
||||
* @return true if the record should be logged, otherwise false
|
||||
*/
|
||||
@Override
|
||||
public boolean isLoggable(LogRecord record) {
|
||||
final String name = record.getSourceClassName();
|
||||
return name.startsWith("org.owasp.dependencycheck") && !name.contains("generated") && !name.contains("VelocityLoggerRedirect");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.SimpleFormatter;
|
||||
|
||||
/**
|
||||
* A utility class to aide in the setup of the logging mechanism.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public final class LogUtils {
|
||||
|
||||
/**
|
||||
* Private constructor for a utility class.
|
||||
*/
|
||||
private LogUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the logger for use by the application.
|
||||
*
|
||||
* @param in the input stream to read the log settings from
|
||||
* @param verboseLogFile the file path for the verbose log
|
||||
*/
|
||||
public static void prepareLogger(InputStream in, String verboseLogFile) {
|
||||
try {
|
||||
LogManager.getLogManager().reset();
|
||||
LogManager.getLogManager().readConfiguration(in);
|
||||
if (verboseLogFile != null && !verboseLogFile.isEmpty()) {
|
||||
verboseLoggingEnabled = true;
|
||||
final Logger logger = Logger.getLogger("");
|
||||
final FileHandler handler = new FileHandler(verboseLogFile, true);
|
||||
handler.setFormatter(new SimpleFormatter());
|
||||
handler.setLevel(Level.FINE);
|
||||
handler.setFilter(new LogFilter());
|
||||
logger.addHandler(handler);
|
||||
logger.setLevel(Level.FINE);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(LogUtils.class.getName()).log(Level.FINE, "IO Error preparing the logger", ex);
|
||||
} catch (SecurityException ex) {
|
||||
Logger.getLogger(LogUtils.class.getName()).log(Level.FINE, "Error preparing the logger", ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(LogUtils.class.getName()).log(Level.FINEST, "Error closing resource stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Whether or not verbose logging is enabled.
|
||||
*/
|
||||
private static boolean verboseLoggingEnabled = false;
|
||||
|
||||
/**
|
||||
* Get the value of verboseLoggingEnabled.
|
||||
*
|
||||
* @return the value of verboseLoggingEnabled
|
||||
*/
|
||||
public static boolean isVerboseLoggingEnabled() {
|
||||
return verboseLoggingEnabled;
|
||||
}
|
||||
}
|
||||
@@ -72,11 +72,6 @@ public final class Settings {
|
||||
* contains the contents of the data directory.
|
||||
*/
|
||||
public static final String BATCH_UPDATE_URL = "batch.update.url";
|
||||
/**
|
||||
* The properties key for the path where the CPE Lucene Index will be
|
||||
* stored.
|
||||
*/
|
||||
public static final String CPE_DATA_DIRECTORY = "data.cpe";
|
||||
/**
|
||||
* The properties key for the path where the CVE H2 database will be
|
||||
* stored.
|
||||
@@ -125,6 +120,14 @@ public final class Settings {
|
||||
* value.
|
||||
*/
|
||||
public static final String PROXY_PORT = "proxy.port";
|
||||
/**
|
||||
* The properties key for the proxy username.
|
||||
*/
|
||||
public static final String PROXY_USERNAME = "proxy.username";
|
||||
/**
|
||||
* The properties key for the proxy password.
|
||||
*/
|
||||
public static final String PROXY_PASSWORD = "proxy.password";
|
||||
/**
|
||||
* The properties key for the connection timeout.
|
||||
*/
|
||||
@@ -133,6 +136,14 @@ public final class Settings {
|
||||
* The location of the temporary directory.
|
||||
*/
|
||||
public static final String TEMP_DIRECTORY = "temp.directory";
|
||||
/**
|
||||
* The maximum number of threads to allocate when downloading files.
|
||||
*/
|
||||
public static final String MAX_DOWNLOAD_THREAD_POOL_SIZE = "max.download.threads";
|
||||
/**
|
||||
* The key for a list of suppression files.
|
||||
*/
|
||||
public static final String SUPPRESSION_FILE = "suppression.file";
|
||||
}
|
||||
/**
|
||||
* The properties file location.
|
||||
@@ -195,6 +206,23 @@ public final class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a new properties file into the current properties. This method
|
||||
* allows for the loading of a user provided properties file.<br/><br/>
|
||||
* Note: even if using this method - system properties will be loaded before
|
||||
* properties loaded from files.
|
||||
*
|
||||
* @param filePath the path to the properties file to merge.
|
||||
* @throws FileNotFoundException is thrown when the filePath points to a
|
||||
* non-existent file
|
||||
* @throws IOException is thrown when there is an exception loading/merging
|
||||
* the properties
|
||||
*/
|
||||
public static void mergeProperties(File filePath) throws FileNotFoundException, IOException {
|
||||
final FileInputStream fis = new FileInputStream(filePath);
|
||||
mergeProperties(fis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a new properties file into the current properties. This method
|
||||
* allows for the loading of a user provided properties file.<br/><br/>
|
||||
@@ -232,13 +260,32 @@ public final class Settings {
|
||||
* argument - this method will return the value from the system properties
|
||||
* before the values in the contained configuration file.
|
||||
*
|
||||
* This method will also replace a leading "[JAR]\" sequence with the path
|
||||
* to the folder containing the JAR file containing this class.
|
||||
*
|
||||
* @param key the key to lookup within the properties file
|
||||
* @return the property from the properties file converted to a File object
|
||||
*/
|
||||
public static File getFile(String key) {
|
||||
final String file = getString(key);
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
return new File(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value from the properties file as a File object. If the value
|
||||
* was specified as a system property or passed in via the -Dprop=value
|
||||
* argument - this method will return the value from the system properties
|
||||
* before the values in the contained configuration file.
|
||||
*
|
||||
* This method will check the configured base directory and will use this as
|
||||
* the base of the file path. Additionally, if the base directory begins
|
||||
* with a leading "[JAR]\" sequence with the path to the folder containing
|
||||
* the JAR file containing this class.
|
||||
*
|
||||
* @param key the key to lookup within the properties file
|
||||
* @return the property from the properties file converted to a File object
|
||||
*/
|
||||
public static File getDataFile(String key) {
|
||||
final String file = getString(key);
|
||||
final String baseDir = getString(Settings.KEYS.DATA_DIRECTORY);
|
||||
if (baseDir != null) {
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -1,8 +1,10 @@
|
||||
org.owasp.dependencycheck.analyzer.ArchiveAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.JarAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.FileNameAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.JarAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.HintAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.FalsePositiveAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.CPEAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.NvdCveAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.FalsePositiveAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.CpeSuppressionAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.NvdCveAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.VulnerabilitySuppressionAnalyzer
|
||||
@@ -8,6 +8,7 @@ DROP TABLE IF EXISTS vulnerability;
|
||||
DROP TABLE IF EXISTS reference;
|
||||
DROP TABLE IF EXISTS cpeEntry;
|
||||
DROP TABLE IF EXISTS software;
|
||||
DROP TABLE IF EXISTS settings;
|
||||
|
||||
CREATE TABLE settings (id varchar(50) PRIMARY KEY, value varchar(200));
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
application.name=${pom.name}
|
||||
application.version=${pom.version}
|
||||
autoupdate=true
|
||||
max.download.threads=3
|
||||
|
||||
#temp.directory defaults to System.getProperty("java.io.tmpdir")
|
||||
#temp.directory=[path to temp directory]
|
||||
|
||||
# the path to the data directory; if tis
|
||||
data.directory=[JAR]/data
|
||||
# the path to the lucene index to store the cpe data
|
||||
data.cpe=cpe
|
||||
# the path to the h2 database to store the nvd cve data
|
||||
data.cve=cve
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema id="suppressions"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
targetNamespace="https://www.owasp.org/index.php/OWASP_Dependency_Check_Suppression"
|
||||
xmlns:dc="https://www.owasp.org/index.php/OWASP_Dependency_Check_Suppression">
|
||||
|
||||
<xs:complexType name="regexStringType">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="regex" use="optional" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="caseSensitive" use="optional" type="xs:boolean" default="false"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:simpleType name="cvssScoreType">
|
||||
<xs:restriction base="xs:decimal">
|
||||
<xs:minInclusive value="0"/>
|
||||
<xs:maxInclusive value="10"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="cveType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="CVE\-\d\d\d\d\-\d+"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="sha1Type">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="[a-fA-F0-9]{40}"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:element name="suppressions">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="suppress">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="1" maxOccurs="1">
|
||||
<xs:sequence minOccurs="0" maxOccurs="1">
|
||||
<xs:element name="notes" type="xs:string"/>
|
||||
</xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="1">
|
||||
<xs:element name="filePath" type="dc:regexStringType"/>
|
||||
<xs:element name="sha1" type="dc:sha1Type"/>
|
||||
</xs:choice>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="cpe" type="dc:regexStringType"/>
|
||||
<xs:element name="cve" type="dc:cveType"/>
|
||||
<xs:element name="cwe" type="xs:positiveInteger"/>
|
||||
<xs:element name="cvssBelow" type="dc:cvssScoreType"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -51,14 +51,132 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function(){
|
||||
$("#modal-background, #modal-close").click(function () {
|
||||
$("#modal-content,#modal-background").toggleClass("active");
|
||||
});
|
||||
$("#modal-text").bind('copy cut', function() {
|
||||
setTimeout('$("#modal-content,#modal-background").toggleClass("active");',100);
|
||||
});
|
||||
$("#modal-add-header").click(function () {
|
||||
xml = '<?xml version="1.0" encoding="UTF-8"?>\n<suppressions xmlns="https://www.owasp.org/index.php/OWASP_Dependency_Check_Suppression">\n ';
|
||||
xml += $("#modal-text").text().replace(/\n/g,'\n ');
|
||||
xml += '\n</suppressions>';
|
||||
$("#modal-text").text(xml).focus().select();
|
||||
});
|
||||
});
|
||||
|
||||
function copyText(name, sha1, type, val) {
|
||||
xml = '<suppress>\n';
|
||||
xml += ' <notes><!'+'[CDATA[\n file name: ' + name + '\n ]]'+'></notes>\n';
|
||||
xml += ' <sha1>' + sha1 + '</sha1>\n';
|
||||
xml += ' <'+type+'>' + val + '</'+type+'>\n';
|
||||
xml += '</suppress>';
|
||||
$("#modal-text").text(xml);
|
||||
$("#modal-content,#modal-background").toggleClass("active");
|
||||
$("#modal-text").focus();
|
||||
$("#modal-text").select();
|
||||
}
|
||||
|
||||
function toggleVuln() {
|
||||
$(".notvulnerable").toggle();
|
||||
}
|
||||
</script>
|
||||
<style type="text/css">
|
||||
#modal-background {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
opacity: .50;
|
||||
-webkit-opacity: .5;
|
||||
-moz-opacity: .5;
|
||||
filter: alpha(opacity=50);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#modal-content {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
box-shadow: 0 0 20px 0 #222;
|
||||
-webkit-box-shadow: 0 0 20px 0 #222;
|
||||
-moz-box-shadow: 0 0 20px 0 #222;
|
||||
display: none;
|
||||
height: 240px;
|
||||
left: 50%;
|
||||
margin: -120px 0 0 -160px;
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#modal-background.active, #modal-content.active {
|
||||
display: block;
|
||||
}
|
||||
#modal-text {
|
||||
border: 0;
|
||||
overflow: hidden
|
||||
}
|
||||
#modal-text:focus {
|
||||
outline: none;
|
||||
}
|
||||
.copybutton {
|
||||
padding:1px;
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #555555;
|
||||
color:#555555;
|
||||
text-decoration:none;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-khtml-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.copybutton:hover {
|
||||
padding:1px;
|
||||
background-color: #dddddd;
|
||||
border: 1px solid #444444;
|
||||
color:#444444;
|
||||
text-decoration:none;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-khtml-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.modal-button {
|
||||
padding:1px;
|
||||
float:right;
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #555555;
|
||||
color:#555555;
|
||||
text-decoration:none;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-khtml-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.modal-button:hover {
|
||||
padding:1px;
|
||||
float:right;
|
||||
background-color: #dddddd;
|
||||
border: 1px solid #333333;
|
||||
color:#333333;
|
||||
text-decoration:none;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-khtml-border-radius: 3px;
|
||||
-o-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.rounded-corners {
|
||||
-moz-border-radius: 20px;
|
||||
-webkit-border-radius: 20px;
|
||||
@@ -292,6 +410,12 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="modal-background"></div>
|
||||
<div id="modal-content">
|
||||
<div>Press CTR-C to copy XML<button id="modal-add-header" class="modal-button">Complete XML Doc</button></div>
|
||||
<textarea id="modal-text" cols="50" rows="10"></textarea><br/>
|
||||
<button id="modal-close" class="modal-button">Close</button>
|
||||
</div>
|
||||
<div class="wrapper">
|
||||
<h1>Dependency Report</h1>
|
||||
]]#
|
||||
@@ -424,6 +548,8 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
#else
|
||||
<li><b>$esc.html($id.type):</b> $esc.html($id.value)
|
||||
#end
|
||||
##yes, we are HTML Encoding into JavaScript... the escape utils don't have a JS Encode and I haven't written one yet
|
||||
<button class="copybutton" onclick="copyText('$esc.html($dependency.FileName)', '$esc.html($dependency.Sha1sum)', 'cpe', '$esc.html($id.value)')">suppress</button>
|
||||
#if( $id.description )
|
||||
<br/>$esc.html($id.description)
|
||||
#end
|
||||
@@ -437,7 +563,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<h4 id="header$cnt" class="subsectionheader expandable collaspablesubsection white">Published Vulnerabilities</h4>
|
||||
<div id="content$cnt" class="subsectioncontent standardsubsection">
|
||||
#foreach($vuln in $dependency.getVulnerabilities())
|
||||
<p><b><a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$esc.url($vuln.name)">$esc.html($vuln.name)</a></b></p>
|
||||
<p><b><a target="_blank" href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=$esc.url($vuln.name)">$esc.html($vuln.name)</a></b> <button class="copybutton" onclick="copyText('$esc.html($dependency.FileName)', '$esc.html($dependency.Sha1sum)', 'cve', '$esc.html($vuln.name)')">suppress</button></p>
|
||||
<p>Severity:
|
||||
#if ($vuln.cvssScore<4.0)
|
||||
Low
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.data.cpe.BaseIndexTestCase;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
@@ -35,7 +36,7 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class ArchiveAnalyzerTest {
|
||||
public class ArchiveAnalyzerTest extends BaseIndexTestCase {
|
||||
|
||||
public ArchiveAnalyzerTest() {
|
||||
}
|
||||
@@ -49,11 +50,13 @@ public class ArchiveAnalyzerTest {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +69,9 @@ public class ArchiveAnalyzerTest {
|
||||
expResult.add("zip");
|
||||
expResult.add("war");
|
||||
expResult.add("ear");
|
||||
expResult.add("tar");
|
||||
expResult.add("gz");
|
||||
expResult.add("tgz");
|
||||
Set result = instance.getSupportedExtensions();
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
@@ -86,7 +92,7 @@ public class ArchiveAnalyzerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testSupportsExtension() {
|
||||
String extension = "tar"; //not supported
|
||||
String extension = "7z"; //not supported
|
||||
ArchiveAnalyzer instance = new ArchiveAnalyzer();
|
||||
boolean expResult = false;
|
||||
boolean result = instance.supportsExtension(extension);
|
||||
@@ -155,6 +161,83 @@ public class ArchiveAnalyzerTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class ArchiveAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyzeTar() throws Exception {
|
||||
ArchiveAnalyzer instance = new ArchiveAnalyzer();
|
||||
try {
|
||||
instance.initialize();
|
||||
|
||||
File file = new File(this.getClass().getClassLoader().getResource("file.tar").getPath());
|
||||
Dependency dependency = new Dependency(file);
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
|
||||
Engine engine = new Engine();
|
||||
|
||||
int initial_size = engine.getDependencies().size();
|
||||
instance.analyze(dependency, engine);
|
||||
int ending_size = engine.getDependencies().size();
|
||||
|
||||
assertTrue(initial_size < ending_size);
|
||||
|
||||
} finally {
|
||||
instance.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class ArchiveAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyzeTarGz() throws Exception {
|
||||
ArchiveAnalyzer instance = new ArchiveAnalyzer();
|
||||
try {
|
||||
instance.initialize();
|
||||
|
||||
File file = new File(this.getClass().getClassLoader().getResource("file.tar.gz").getPath());
|
||||
//Dependency dependency = new Dependency(file);
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
|
||||
Engine engine = new Engine();
|
||||
|
||||
int initial_size = engine.getDependencies().size();
|
||||
//instance.analyze(dependency, engine);
|
||||
engine.scan(file);
|
||||
engine.analyzeDependencies();
|
||||
int ending_size = engine.getDependencies().size();
|
||||
|
||||
assertTrue(initial_size < ending_size);
|
||||
|
||||
} finally {
|
||||
instance.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class ArchiveAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyzeTgz() throws Exception {
|
||||
ArchiveAnalyzer instance = new ArchiveAnalyzer();
|
||||
try {
|
||||
instance.initialize();
|
||||
|
||||
File file = new File(this.getClass().getClassLoader().getResource("file.tgz").getPath());
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
|
||||
Engine engine = new Engine();
|
||||
|
||||
int initial_size = engine.getDependencies().size();
|
||||
engine.scan(file);
|
||||
engine.analyzeDependencies();
|
||||
int ending_size = engine.getDependencies().size();
|
||||
|
||||
assertTrue(initial_size < ending_size);
|
||||
|
||||
} finally {
|
||||
instance.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class ArchiveAnalyzer.
|
||||
*/
|
||||
@@ -168,13 +251,16 @@ public class ArchiveAnalyzerTest {
|
||||
Dependency dependency = new Dependency(file);
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
|
||||
Engine engine = new Engine();
|
||||
|
||||
int initial_size = engine.getDependencies().size();
|
||||
// boolean failed = false;
|
||||
// try {
|
||||
instance.analyze(dependency, engine);
|
||||
// } catch (java.lang.UnsupportedClassVersionError ex) {
|
||||
// failed = true;
|
||||
// }
|
||||
// assertTrue(failed);
|
||||
int ending_size = engine.getDependencies().size();
|
||||
|
||||
assertTrue(initial_size == ending_size);
|
||||
|
||||
assertEquals(initial_size, ending_size);
|
||||
} finally {
|
||||
instance.close();
|
||||
}
|
||||
|
||||
@@ -101,21 +101,6 @@ public class CPEAnalyzerTest extends BaseIndexTestCase {
|
||||
Assert.assertTrue(expResult.equals(queryText));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of open method, of class CPEAnalyzer.
|
||||
*
|
||||
* @throws Exception is thrown when an exception occurs
|
||||
*/
|
||||
@Test
|
||||
public void testOpen() throws Exception {
|
||||
CPEAnalyzer instance = new CPEAnalyzer();
|
||||
Assert.assertFalse(instance.isOpen());
|
||||
instance.open();
|
||||
Assert.assertTrue(instance.isOpen());
|
||||
instance.close();
|
||||
Assert.assertFalse(instance.isOpen());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of determineCPE method, of class CPEAnalyzer.
|
||||
*
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class IndexIntegrationTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of update method, of class Index.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
//deprecated
|
||||
//Index instance = new Index();
|
||||
//instance.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of updateNeeded method, of class Index.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateNeeded() throws Exception {
|
||||
//deprecated
|
||||
//Index instance = new Index();
|
||||
//instance.updateNeeded();
|
||||
//if an exception is thrown this test fails. However, because it depends on the
|
||||
// order of the tests what this will return I am just testing for the exception.
|
||||
//assertTrue(expResult < result);
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class FieldAnalyzerTest {
|
||||
@Test
|
||||
public void testAnalyzers() throws Exception {
|
||||
|
||||
Analyzer analyzer = new FieldAnalyzer(Version.LUCENE_43);
|
||||
Analyzer analyzer = new FieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
Directory index = new RAMDirectory();
|
||||
|
||||
String field1 = "product";
|
||||
@@ -83,16 +83,16 @@ public class FieldAnalyzerTest {
|
||||
|
||||
createIndex(analyzer, index, field1, text1, field2, text2);
|
||||
|
||||
//Analyzer searchingAnalyzer = new SearchFieldAnalyzer(Version.LUCENE_43);
|
||||
//Analyzer searchingAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
String querystr = "product:\"(Spring Framework Core)\" vendor:(SpringSource)";
|
||||
|
||||
SearchFieldAnalyzer searchAnalyzerProduct = new SearchFieldAnalyzer(Version.LUCENE_43);
|
||||
SearchFieldAnalyzer searchAnalyzerVendor = new SearchFieldAnalyzer(Version.LUCENE_43);
|
||||
SearchFieldAnalyzer searchAnalyzerProduct = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
SearchFieldAnalyzer searchAnalyzerVendor = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
HashMap<String, Analyzer> map = new HashMap<String, Analyzer>();
|
||||
map.put(field1, searchAnalyzerProduct);
|
||||
map.put(field2, searchAnalyzerVendor);
|
||||
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(new StandardAnalyzer(Version.LUCENE_43), map);
|
||||
QueryParser parser = new QueryParser(Version.LUCENE_43, field1, wrapper);
|
||||
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(new StandardAnalyzer(LuceneUtils.CURRENT_VERSION), map);
|
||||
QueryParser parser = new QueryParser(LuceneUtils.CURRENT_VERSION, field1, wrapper);
|
||||
|
||||
Query q = parser.parse(querystr);
|
||||
//System.out.println(q.toString());
|
||||
@@ -116,7 +116,7 @@ public class FieldAnalyzerTest {
|
||||
}
|
||||
|
||||
private void createIndex(Analyzer analyzer, Directory index, String field1, String text1, String field2, String text2) throws IOException {
|
||||
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_43, analyzer);
|
||||
IndexWriterConfig config = new IndexWriterConfig(LuceneUtils.CURRENT_VERSION, analyzer);
|
||||
IndexWriter w = new IndexWriter(index, config);
|
||||
addDoc(w, field1, text1, field2, text2);
|
||||
w.close();
|
||||
|
||||
@@ -53,7 +53,7 @@ public class TokenPairConcatenatingFilterTest extends BaseTokenStreamTestCase {
|
||||
* test some examples
|
||||
*/
|
||||
public void testExamples() throws IOException {
|
||||
Tokenizer wsTokenizer = new WhitespaceTokenizer(Version.LUCENE_43, new StringReader("one two three"));
|
||||
Tokenizer wsTokenizer = new WhitespaceTokenizer(LuceneUtils.CURRENT_VERSION, new StringReader("one two three"));
|
||||
TokenStream filter = new TokenPairConcatenatingFilter(wsTokenizer);
|
||||
assertTokenStreamContents(filter,
|
||||
new String[]{"one", "onetwo", "two", "twothree", "three"});
|
||||
@@ -65,7 +65,7 @@ public class TokenPairConcatenatingFilterTest extends BaseTokenStreamTestCase {
|
||||
@Test
|
||||
public void testClear() throws IOException {
|
||||
|
||||
TokenStream ts = new WhitespaceTokenizer(Version.LUCENE_43, new StringReader("one two three"));
|
||||
TokenStream ts = new WhitespaceTokenizer(LuceneUtils.CURRENT_VERSION, new StringReader("one two three"));
|
||||
TokenPairConcatenatingFilter filter = new TokenPairConcatenatingFilter(ts);
|
||||
assertTokenStreamContents(filter, new String[]{"one", "onetwo", "two", "twothree", "three"});
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public abstract class BaseDBTestCase extends TestCase {
|
||||
|
||||
public static void ensureDBExists() throws Exception {
|
||||
|
||||
java.io.File dataPath = Settings.getFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
java.io.File dataPath = Settings.getDataFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
if (!dataPath.exists() || (dataPath.isDirectory() && dataPath.listFiles().length < 3)) {
|
||||
dataPath.mkdirs();
|
||||
FileInputStream fis = null;
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class BatchUpdateTaskTest {
|
||||
|
||||
public BatchUpdateTaskTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
}
|
||||
private String old12;
|
||||
private String old20;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
old12 = Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL);
|
||||
old20 = Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL);
|
||||
|
||||
File tmp = Settings.getTempDirectory();
|
||||
if (!tmp.exists()) {
|
||||
tmp.mkdirs();
|
||||
}
|
||||
|
||||
File dest = new File(tmp, "data.zip");
|
||||
File file = new File(this.getClass().getClassLoader().getResource("data.zip").toURI());
|
||||
FileUtils.copyFile(file, dest);
|
||||
String path = "file:///" + dest.getCanonicalPath();
|
||||
Settings.setString(Settings.KEYS.BATCH_UPDATE_URL, path);
|
||||
|
||||
dest = new File(tmp, "nvdcve-2012.xml");
|
||||
file = new File(this.getClass().getClassLoader().getResource("nvdcve-2012.xml").toURI());
|
||||
FileUtils.copyFile(file, dest);
|
||||
path = "file:///" + dest.getCanonicalPath();
|
||||
Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, path);
|
||||
|
||||
dest = new File(tmp, "nvdcve-2.0-2012.xml");
|
||||
file = new File(this.getClass().getClassLoader().getResource("nvdcve-2.0-2012.xml").toURI());
|
||||
FileUtils.copyFile(file, dest);
|
||||
path = "file:///" + dest.getCanonicalPath();
|
||||
Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, path);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, old12);
|
||||
Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, old20);
|
||||
Settings.setString(Settings.KEYS.BATCH_UPDATE_URL, "");
|
||||
}
|
||||
|
||||
public BatchUpdateTask getBatchUpdateTask() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
DataStoreMetaInfo props = new DataStoreMetaInfo();
|
||||
BatchUpdateTask instance = new BatchUpdateTask(props);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setDoBatchUpdate method, of class BatchUpdateTask.
|
||||
*/
|
||||
@Test
|
||||
public void testSetDoBatchUpdate() throws DownloadFailedException, MalformedURLException, UpdateException {
|
||||
boolean expected = false;
|
||||
BatchUpdateTask instance = getBatchUpdateTask();
|
||||
instance.setDoBatchUpdate(expected);
|
||||
boolean results = instance.isDoBatchUpdate();
|
||||
assertEquals(results, expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of update method, of class BatchUpdateTask.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
BatchUpdateTask instance = getBatchUpdateTask();
|
||||
|
||||
//do some setup
|
||||
instance.setDoBatchUpdate(true);
|
||||
instance.deleteExistingData();
|
||||
|
||||
instance.update(); //no exceptions it worked?
|
||||
//todo add some actual asserts to check things.
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,9 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
@@ -38,9 +34,9 @@ import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class AbstractUpdateTaskTest {
|
||||
public class StandardUpdateIntegrationTest {
|
||||
|
||||
public AbstractUpdateTaskTest() {
|
||||
public StandardUpdateIntegrationTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
@@ -59,31 +55,30 @@ public class AbstractUpdateTaskTest {
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
public AbstractUpdateTask getAbstractUpdateImpl() throws Exception {
|
||||
DataStoreMetaInfo props = new DataStoreMetaInfo();
|
||||
AbstractUpdateTask instance = new AbstractUpdateImpl(props);
|
||||
public StandardUpdate getStandardUpdateTask() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
StandardUpdate instance = new StandardUpdate();
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setDeleteAndRecreate method, of class AbstractUpdateTask.
|
||||
* Test of setDeleteAndRecreate method, of class StandardUpdate.
|
||||
*/
|
||||
@Test
|
||||
public void testSetDeleteAndRecreate() throws Exception {
|
||||
boolean deleteAndRecreate = false;
|
||||
boolean expResult = false;
|
||||
AbstractUpdateTask instance = getAbstractUpdateImpl();
|
||||
StandardUpdate instance = getStandardUpdateTask();
|
||||
instance.setDeleteAndRecreate(deleteAndRecreate);
|
||||
boolean result = instance.shouldDeleteAndRecreate();
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of deleteExistingData method, of class AbstractUpdateTask.
|
||||
* Test of deleteExistingData method, of class StandardUpdate.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteExistingData() throws Exception {
|
||||
AbstractUpdateTask instance = getAbstractUpdateImpl();
|
||||
StandardUpdate instance = getStandardUpdateTask();
|
||||
Exception result = null;
|
||||
try {
|
||||
instance.deleteExistingData();
|
||||
@@ -94,17 +89,17 @@ public class AbstractUpdateTaskTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of openDataStores method, of class AbstractUpdateTask.
|
||||
* Test of openDataStores method, of class StandardUpdate.
|
||||
*/
|
||||
@Test
|
||||
public void testOpenDataStores() throws Exception {
|
||||
AbstractUpdateTask instance = getAbstractUpdateImpl();
|
||||
StandardUpdate instance = getStandardUpdateTask();
|
||||
instance.openDataStores();
|
||||
instance.closeDataStores();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of withinRange method, of class AbstractUpdateTask.
|
||||
* Test of withinRange method, of class StandardUpdate.
|
||||
*/
|
||||
@Test
|
||||
public void testWithinRange() throws Exception {
|
||||
@@ -113,7 +108,7 @@ public class AbstractUpdateTaskTest {
|
||||
long current = c.getTimeInMillis();
|
||||
long lastRun = c.getTimeInMillis() - (3 * (1000 * 60 * 60 * 24));
|
||||
int range = 7; // 7 days
|
||||
AbstractUpdateTask instance = getAbstractUpdateImpl();
|
||||
StandardUpdate instance = getStandardUpdateTask();
|
||||
boolean expResult = true;
|
||||
boolean result = instance.withinRange(lastRun, current, range);
|
||||
assertEquals(expResult, result);
|
||||
@@ -124,17 +119,23 @@ public class AbstractUpdateTaskTest {
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
public class AbstractUpdateImpl extends AbstractUpdateTask {
|
||||
/**
|
||||
* Test of update method, of class StandardUpdate.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
StandardUpdate instance = getStandardUpdateTask();
|
||||
instance.update();
|
||||
//TODO make this an actual test
|
||||
}
|
||||
|
||||
public AbstractUpdateImpl(DataStoreMetaInfo props) throws Exception {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public Updateable updatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void update() throws UpdateException {
|
||||
}
|
||||
/**
|
||||
* Test of updatesNeeded method, of class StandardUpdate.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdatesNeeded() throws Exception {
|
||||
StandardUpdate instance = getStandardUpdateTask();
|
||||
Updateable result = instance.updatesNeeded();
|
||||
assertNotNull(result);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.owasp.dependencycheck.data.UpdateException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class StandardUpdateTaskIntegrationTest {
|
||||
|
||||
public StandardUpdateTaskIntegrationTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
public StandardUpdateTask getStandardUpdateTask() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
DataStoreMetaInfo props = new DataStoreMetaInfo();
|
||||
StandardUpdateTask instance = new StandardUpdateTask(props);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of update method, of class StandardUpdateTask.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
StandardUpdateTask instance = getStandardUpdateTask();
|
||||
instance.update();
|
||||
//TODO make this an actual test
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of updatesNeeded method, of class StandardUpdateTask.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdatesNeeded() throws Exception {
|
||||
StandardUpdateTask instance = getStandardUpdateTask();
|
||||
Updateable result = instance.updatesNeeded();
|
||||
assertNotNull(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class PropertyTypeTest {
|
||||
|
||||
public PropertyTypeTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of set and getValue method, of class PropertyType.
|
||||
*/
|
||||
@Test
|
||||
public void testSetGetValue() {
|
||||
|
||||
PropertyType instance = new PropertyType();
|
||||
String expResult = "test";
|
||||
instance.setValue(expResult);
|
||||
String result = instance.getValue();
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of isRegex method, of class PropertyType.
|
||||
*/
|
||||
@Test
|
||||
public void testIsRegex() {
|
||||
PropertyType instance = new PropertyType();
|
||||
boolean result = instance.isRegex();
|
||||
assertFalse(instance.isRegex());
|
||||
instance.setRegex(true);
|
||||
assertTrue(instance.isRegex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of isCaseSensitive method, of class PropertyType.
|
||||
*/
|
||||
@Test
|
||||
public void testIsCaseSensitive() {
|
||||
PropertyType instance = new PropertyType();
|
||||
assertFalse(instance.isCaseSensitive());
|
||||
instance.setCaseSensitive(true);
|
||||
assertTrue(instance.isCaseSensitive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of matches method, of class PropertyType.
|
||||
*/
|
||||
@Test
|
||||
public void testMatches() {
|
||||
String text = "Simple";
|
||||
|
||||
PropertyType instance = new PropertyType();
|
||||
instance.setValue("simple");
|
||||
assertTrue(instance.matches(text));
|
||||
instance.setCaseSensitive(true);
|
||||
assertFalse(instance.matches(text));
|
||||
|
||||
instance.setValue("s.*le");
|
||||
instance.setRegex(true);
|
||||
assertFalse(instance.matches(text));
|
||||
instance.setCaseSensitive(false);
|
||||
assertTrue(instance.matches(text));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class SuppressionHandlerTest {
|
||||
|
||||
public SuppressionHandlerTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getSupressionRules method, of class SuppressionHandler.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception....
|
||||
*/
|
||||
@Test
|
||||
public void testHandler() throws Exception {
|
||||
File file = new File(this.getClass().getClassLoader().getResource("suppressions.xml").getPath());
|
||||
|
||||
File schema = new File(this.getClass().getClassLoader().getResource("schema/suppression.xsd").getPath());
|
||||
SuppressionHandler handler = new SuppressionHandler();
|
||||
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
factory.setValidating(true);
|
||||
SAXParser saxParser = factory.newSAXParser();
|
||||
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_LANGUAGE, SuppressionParser.W3C_XML_SCHEMA);
|
||||
saxParser.setProperty(SuppressionParser.JAXP_SCHEMA_SOURCE, schema);
|
||||
XMLReader xmlReader = saxParser.getXMLReader();
|
||||
xmlReader.setErrorHandler(new SuppressionErrorHandler());
|
||||
xmlReader.setContentHandler(handler);
|
||||
|
||||
InputStream inputStream = new FileInputStream(file);
|
||||
Reader reader = new InputStreamReader(inputStream, "UTF-8");
|
||||
InputSource in = new InputSource(reader);
|
||||
//in.setEncoding("UTF-8");
|
||||
|
||||
xmlReader.parse(in);
|
||||
|
||||
List result = handler.getSupressionRules();
|
||||
assertTrue(result.size() > 3);
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import org.owasp.dependencycheck.data.cpe.BaseIndex;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
@@ -28,17 +28,21 @@ import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test of the suppression parser.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class BaseIndexTest {
|
||||
public class SuppressionParserTest {
|
||||
|
||||
public SuppressionParserTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
public static void setUpClass() {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
public static void tearDownClass() {
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -50,14 +54,13 @@ public class BaseIndexTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getDataDirectory method, of class BaseIndex.
|
||||
*
|
||||
* @throws Exception
|
||||
* Test of parseSuppressionRules method, of class SuppressionParser.
|
||||
*/
|
||||
@Test
|
||||
public void testGetDataDirectory() throws Exception {
|
||||
String file = BaseIndex.getDataDirectory().getPath();
|
||||
String exp = File.separatorChar + "target" + File.separatorChar + "data" + File.separatorChar + "cpe";
|
||||
assertTrue(file.contains(exp));
|
||||
public void testParseSuppressionRules() throws Exception {
|
||||
File file = new File(this.getClass().getClassLoader().getResource("suppressions.xml").getPath());
|
||||
SuppressionParser instance = new SuppressionParser();
|
||||
List result = instance.parseSuppressionRules(file);
|
||||
assertTrue(result.size() > 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Dependency-check-core is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* Dependency-check-core is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* dependency-check-core. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
|
||||
/**
|
||||
* Test of the suppression rule.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class SuppressionRuleTest {
|
||||
|
||||
public SuppressionRuleTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="Stupid tests of properties">
|
||||
/**
|
||||
* Test of FilePath property, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testFilePath() {
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
PropertyType expResult = new PropertyType();
|
||||
expResult.setValue("test");
|
||||
instance.setFilePath(expResult);
|
||||
PropertyType result = instance.getFilePath();
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of Sha1 property, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testSha1() {
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
String expResult = "384FAA82E193D4E4B0546059CA09572654BC3970";
|
||||
instance.setSha1(expResult);
|
||||
String result = instance.getSha1();
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of Cpe property, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testCpe() {
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
ArrayList<PropertyType> cpe = new ArrayList<PropertyType>();
|
||||
instance.setCpe(cpe);
|
||||
assertFalse(instance.hasCpe());
|
||||
PropertyType pt = new PropertyType();
|
||||
pt.setValue("one");
|
||||
instance.addCpe(pt);
|
||||
assertTrue(instance.hasCpe());
|
||||
List<PropertyType> result = instance.getCpe();
|
||||
assertEquals(cpe, result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of CvssBelow property, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testGetCvssBelow() {
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
ArrayList<Float> cvss = new ArrayList<Float>();
|
||||
instance.setCvssBelow(cvss);
|
||||
assertFalse(instance.hasCvssBelow());
|
||||
instance.addCvssBelow(0.7f);
|
||||
assertTrue(instance.hasCvssBelow());
|
||||
List<Float> result = instance.getCvssBelow();
|
||||
assertEquals(cvss, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of Cwe property, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testCwe() {
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
ArrayList<String> cwe = new ArrayList<String>();
|
||||
instance.setCwe(cwe);
|
||||
assertFalse(instance.hasCwe());
|
||||
instance.addCwe("2");
|
||||
assertTrue(instance.hasCwe());
|
||||
List<String> result = instance.getCwe();
|
||||
assertEquals(cwe, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of Cve property, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testCve() {
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
ArrayList<String> cve = new ArrayList<String>();
|
||||
instance.setCve(cve);
|
||||
assertFalse(instance.hasCve());
|
||||
instance.addCve("CVE-2013-1337");
|
||||
assertTrue(instance.hasCve());
|
||||
List<String> result = instance.getCve();
|
||||
assertEquals(cve, result);
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="Ignored duplicate tests, left in, as empty tests, so IDE doesn't re-generate them">
|
||||
/**
|
||||
* Test of getFilePath method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testGetFilePath() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setFilePath method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testSetFilePath() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getSha1 method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testGetSha1() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setSha1 method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testSetSha1() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getCpe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testGetCpe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setCpe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testSetCpe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of addCpe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testAddCpe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hasCpe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testHasCpe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setCvssBelow method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testSetCvssBelow() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of addCvssBelow method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testAddCvssBelow() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hasCvssBelow method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testHasCvssBelow() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getCwe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testGetCwe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setCwe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testSetCwe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of addCwe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testAddCwe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hasCwe method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testHasCwe() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getCve method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testGetCve() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of setCve method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testSetCve() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of addCve method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testAddCve() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of hasCve method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testHasCve() {
|
||||
//already tested, this is just left so the IDE doesn't recreate it.
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* Test of cpeHasNoVersion method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testCpeHasNoVersion() {
|
||||
PropertyType c = new PropertyType();
|
||||
c.setValue("cpe:/a:microsoft:.net_framework:4.5");
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
assertFalse(instance.cpeHasNoVersion(c));
|
||||
c.setValue("cpe:/a:microsoft:.net_framework:");
|
||||
assertFalse(instance.cpeHasNoVersion(c));
|
||||
c.setValue("cpe:/a:microsoft:.net_framework");
|
||||
assertTrue(instance.cpeHasNoVersion(c));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of countCharacter method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testCountCharacter() {
|
||||
String str = "cpe:/a:microsoft:.net_framework:4.5";
|
||||
char c = ':';
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
int expResult = 4;
|
||||
int result = instance.countCharacter(str, c);
|
||||
assertEquals(expResult, result);
|
||||
str = "::";
|
||||
expResult = 2;
|
||||
result = instance.countCharacter(str, c);
|
||||
assertEquals(expResult, result);
|
||||
str = "these are not the characters you are looking for";
|
||||
expResult = 0;
|
||||
result = instance.countCharacter(str, c);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of cpeMatches method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testCpeMatches() {
|
||||
Identifier identifier = new Identifier("cwe", "cpe:/a:microsoft:.net_framework:4.5", "some url not needed for this test");
|
||||
|
||||
PropertyType cpe = new PropertyType();
|
||||
cpe.setValue("cpe:/a:microsoft:.net_framework:4.5");
|
||||
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
boolean expResult = true;
|
||||
boolean result = instance.cpeMatches(cpe, identifier);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
cpe.setValue("cpe:/a:microsoft:.net_framework:4.0");
|
||||
expResult = false;
|
||||
result = instance.cpeMatches(cpe, identifier);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
cpe.setValue("CPE:/a:microsoft:.net_framework:4.5");
|
||||
cpe.setCaseSensitive(true);
|
||||
expResult = false;
|
||||
result = instance.cpeMatches(cpe, identifier);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
cpe.setValue("cpe:/a:microsoft:.net_framework");
|
||||
cpe.setCaseSensitive(false);
|
||||
expResult = true;
|
||||
result = instance.cpeMatches(cpe, identifier);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
cpe.setValue("cpe:/a:microsoft:.*");
|
||||
cpe.setRegex(true);
|
||||
expResult = true;
|
||||
result = instance.cpeMatches(cpe, identifier);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
cpe.setValue("CPE:/a:microsoft:.*");
|
||||
cpe.setRegex(true);
|
||||
cpe.setCaseSensitive(true);
|
||||
expResult = false;
|
||||
result = instance.cpeMatches(cpe, identifier);
|
||||
assertEquals(expResult, result);
|
||||
|
||||
cpe.setValue("cpe:/a:apache:.*");
|
||||
cpe.setRegex(true);
|
||||
cpe.setCaseSensitive(false);
|
||||
expResult = false;
|
||||
result = instance.cpeMatches(cpe, identifier);
|
||||
assertEquals(expResult, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of process method, of class SuppressionRule.
|
||||
*/
|
||||
@Test
|
||||
public void testProcess() {
|
||||
File struts = new File(this.getClass().getClassLoader().getResource("struts2-core-2.1.2.jar").getPath());
|
||||
Dependency dependency = new Dependency(struts);
|
||||
dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:4.5", "some url not needed for this test");
|
||||
String sha1 = dependency.getSha1sum();
|
||||
dependency.setSha1sum("384FAA82E193D4E4B0546059CA09572654BC3970");
|
||||
Vulnerability v = createVulnerability();
|
||||
dependency.addVulnerability(v);
|
||||
|
||||
//cwe
|
||||
SuppressionRule instance = new SuppressionRule();
|
||||
instance.setSha1(sha1);
|
||||
instance.addCwe("287");
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getVulnerabilities().size() == 1);
|
||||
dependency.setSha1sum(sha1);
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getVulnerabilities().isEmpty());
|
||||
|
||||
//cvss
|
||||
dependency.addVulnerability(v);
|
||||
instance = new SuppressionRule();
|
||||
instance.addCvssBelow(5f);
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getVulnerabilities().size() == 1);
|
||||
instance.addCvssBelow(8f);
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getVulnerabilities().isEmpty());
|
||||
|
||||
//cve
|
||||
dependency.addVulnerability(v);
|
||||
instance = new SuppressionRule();
|
||||
instance.addCve("CVE-2012-1337");
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getVulnerabilities().size() == 1);
|
||||
instance.addCve("CVE-2013-1337");
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getVulnerabilities().isEmpty());
|
||||
|
||||
//cpe
|
||||
instance = new SuppressionRule();
|
||||
PropertyType pt = new PropertyType();
|
||||
pt.setValue("cpe:/a:microsoft:.net_framework:4.0");
|
||||
instance.addCpe(pt);
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getIdentifiers().size() == 1);
|
||||
pt = new PropertyType();
|
||||
pt.setValue("cpe:/a:microsoft:.net_framework:4.5");
|
||||
instance.addCpe(pt);
|
||||
pt = new PropertyType();
|
||||
pt.setValue(".*");
|
||||
pt.setRegex(true);
|
||||
instance.setFilePath(pt);
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getIdentifiers().isEmpty());
|
||||
|
||||
dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:4.0", "some url not needed for this test");
|
||||
dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:4.5", "some url not needed for this test");
|
||||
dependency.addIdentifier("cwe", "cpe:/a:microsoft:.net_framework:5.0", "some url not needed for this test");
|
||||
pt = new PropertyType();
|
||||
pt.setValue("cpe:/a:microsoft:.net_framework");
|
||||
instance.addCpe(pt);
|
||||
assertTrue(dependency.getIdentifiers().size() == 3);
|
||||
instance.process(dependency);
|
||||
assertTrue(dependency.getIdentifiers().isEmpty());
|
||||
}
|
||||
|
||||
private Vulnerability createVulnerability() {
|
||||
Vulnerability v = new Vulnerability();
|
||||
v.setCwe("CWE-287 Improper Authentication");
|
||||
v.setName("CVE-2013-1337");
|
||||
v.setCvssScore(7.5f);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -57,23 +57,23 @@ public class SettingsTest {
|
||||
*/
|
||||
@Test
|
||||
public void testGetString() {
|
||||
String key = Settings.KEYS.CPE_DATA_DIRECTORY;
|
||||
String expResult = "cpe";
|
||||
String key = Settings.KEYS.CVE_DATA_DIRECTORY;
|
||||
String expResult = "cve";
|
||||
String result = Settings.getString(key);
|
||||
Assert.assertTrue(result.endsWith(expResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getFile method, of class Settings.
|
||||
* Test of getDataFile method, of class Settings.
|
||||
*/
|
||||
@Test
|
||||
public void testGetFile() throws IOException {
|
||||
String key = Settings.KEYS.CPE_DATA_DIRECTORY;
|
||||
String expResult = "data" + File.separator + "cpe";
|
||||
File result = Settings.getFile(key);
|
||||
public void testGetDataFile() throws IOException {
|
||||
String key = Settings.KEYS.CVE_DATA_DIRECTORY;
|
||||
String expResult = "data" + File.separator + "cve";
|
||||
File result = Settings.getDataFile(key);
|
||||
Assert.assertTrue(result.getAbsolutePath().endsWith(expResult));
|
||||
|
||||
result = Settings.getFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
result = Settings.getDataFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
String path = result.getPath();
|
||||
Assert.assertTrue(path.endsWith("data") || path.endsWith("data" + File.separator));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ cve.url.modified.validfordays=7
|
||||
# the path to the modified nvd cve xml file.
|
||||
cve.url-1.2.modified=http://nvd.nist.gov/download/nvdcve-modified.xml
|
||||
cve.url-2.0.modified=http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-modified.xml
|
||||
cve.startyear=2002
|
||||
cve.startyear=2013
|
||||
cve.url-2.0.base=http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml
|
||||
cve.url-1.2.base=http://nvd.nist.gov/download/nvdcve-%d.xml
|
||||
#cve.url-2.0.base=file:///C:/data/xml/nvdcve-2.0-%d.xml
|
||||
|
||||
BIN
dependency-check-core/src/test/resources/file.tar.gz
Normal file
BIN
dependency-check-core/src/test/resources/file.tar.gz
Normal file
Binary file not shown.
BIN
dependency-check-core/src/test/resources/file.tgz
Normal file
BIN
dependency-check-core/src/test/resources/file.tgz
Normal file
Binary file not shown.
40
dependency-check-core/src/test/resources/suppressions.xml
Normal file
40
dependency-check-core/src/test/resources/suppressions.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<suppressions
|
||||
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
||||
xmlns='https://www.owasp.org/index.php/OWASP_Dependency_Check_Suppression'
|
||||
xsi:schemaLocation='https://www.owasp.org/index.php/OWASP_Dependency_Check_Suppression suppression.xsd'>
|
||||
<suppress>
|
||||
<notes><![CDATA[
|
||||
This suppresses cpe:/a:csv:csv:1.0 for some.jar in the "c:\path\to" directory.
|
||||
]]></notes>
|
||||
<filePath>c:\path\to\some.jar</filePath>
|
||||
<cpe>cpe:/a:csv:csv:1.0</cpe>
|
||||
</suppress>
|
||||
<suppress>
|
||||
<notes><![CDATA[
|
||||
This suppresses any jboss:jboss cpe for any test.jar in any directory.
|
||||
]]></notes>
|
||||
<filePath regex="true">.*\btest\.jar</filePath>
|
||||
<cpe>cpe:/a:jboss:jboss</cpe>
|
||||
</suppress>
|
||||
<suppress>
|
||||
<notes><![CDATA[
|
||||
This suppresses a specific cve for any test.jar in any directory.
|
||||
]]></notes>
|
||||
<filePath regex="true">.*\btest\.jar</filePath>
|
||||
<cve>CVE-2013-1337</cve>
|
||||
</suppress>
|
||||
<suppress>
|
||||
<notes><![CDATA[
|
||||
This suppresses a specific cve for any dependency in any directory that has the specified sha1 checksum.
|
||||
]]></notes>
|
||||
<sha1>384FAA82E193D4E4B0546059CA09572654BC3970</sha1>
|
||||
<cve>CVE-2013-1337</cve>
|
||||
</suppress>
|
||||
<suppress>
|
||||
<notes><![CDATA[
|
||||
This suppresses all CVE entries that have a score below CVSS 7.
|
||||
]]></notes>
|
||||
<cvssBelow>7</cvssBelow>
|
||||
</suppress>
|
||||
</suppressions>
|
||||
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.0.3</version>
|
||||
<version>1.0.7</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-jenkins</artifactId>
|
||||
<name>Dependency-Check Jenkins Plugin</name>
|
||||
<packaging>jar</packaging>
|
||||
<packaging>pom</packaging>
|
||||
<inceptionYear>2012</inceptionYear>
|
||||
<organization>
|
||||
<name>OWASP</name>
|
||||
|
||||
@@ -24,7 +24,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.0.3</version>
|
||||
<version>1.0.7</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-maven</artifactId>
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import java.util.Set;
|
||||
import java.util.logging.LogManager;
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
@@ -54,6 +53,7 @@ import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.utils.LogUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
@@ -90,6 +90,11 @@ public class DependencyCheckMojo extends AbstractMojo implements MavenMultiPageR
|
||||
*/
|
||||
@Parameter(property = "report-name", defaultValue = "dependency-check-report")
|
||||
private String reportName;
|
||||
/**
|
||||
* The path to the verbose log
|
||||
*/
|
||||
@Parameter(property = "logfile", defaultValue = "")
|
||||
private String logFile;
|
||||
/**
|
||||
* The name of the report to be displayed in the Maven Generated Reports
|
||||
* page
|
||||
@@ -155,44 +160,31 @@ public class DependencyCheckMojo extends AbstractMojo implements MavenMultiPageR
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
@Parameter(property = "proxyPort", defaultValue = "", required = false)
|
||||
private String proxyPort = null;
|
||||
/**
|
||||
* The Proxy username.
|
||||
*/
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
@Parameter(property = "proxyUsername", defaultValue = "", required = false)
|
||||
private String proxyUsername = null;
|
||||
/**
|
||||
* The Proxy password.
|
||||
*/
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
@Parameter(property = "proxyPassword", defaultValue = "", required = false)
|
||||
private String proxyPassword = null;
|
||||
/**
|
||||
* The Connection Timeout.
|
||||
*/
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
@Parameter(property = "connectionTimeout", defaultValue = "", required = false)
|
||||
private String connectionTimeout = null;
|
||||
|
||||
// </editor-fold>
|
||||
/**
|
||||
* Configures the logger for use by the application.
|
||||
* The Connection Timeout.
|
||||
*/
|
||||
private static void prepareLogger() {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = DependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
|
||||
LogManager.getLogManager().reset();
|
||||
LogManager.getLogManager().readConfiguration(in);
|
||||
//TODO add code to disable fine grained log file.
|
||||
// Logger logger = LogManager.getLogManager().getLogger("");
|
||||
// for (Handler h : logger.getHandlers()) {
|
||||
// if (h.getFormatter(). h.toString());
|
||||
// }
|
||||
} catch (IOException ex) {
|
||||
System.err.println(ex.toString());
|
||||
Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (SecurityException ex) {
|
||||
Logger.getLogger(DependencyCheckMojo.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ex) {
|
||||
//noinspection UnusedAssignment
|
||||
in = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
@Parameter(property = "suppressionFile", defaultValue = "", required = false)
|
||||
private String suppressionFile = null;
|
||||
// </editor-fold>
|
||||
|
||||
/**
|
||||
* Executes the Dependency-Check on the dependent libraries.
|
||||
@@ -200,7 +192,10 @@ public class DependencyCheckMojo extends AbstractMojo implements MavenMultiPageR
|
||||
* @return the Engine used to scan the dependencies.
|
||||
*/
|
||||
private Engine executeDependencyCheck() {
|
||||
prepareLogger();
|
||||
|
||||
final InputStream in = DependencyCheckMojo.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
|
||||
LogUtils.prepareLogger(in, logFile);
|
||||
|
||||
populateSettings();
|
||||
final Engine engine = new Engine();
|
||||
final Set<Artifact> artifacts = project.getArtifacts();
|
||||
@@ -648,9 +643,18 @@ public class DependencyCheckMojo extends AbstractMojo implements MavenMultiPageR
|
||||
if (proxyPort != null && !proxyPort.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
|
||||
}
|
||||
if (proxyUsername != null && !proxyUsername.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUsername);
|
||||
}
|
||||
if (proxyPassword != null && !proxyPassword.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPassword);
|
||||
}
|
||||
if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
|
||||
}
|
||||
if (suppressionFile != null && !suppressionFile.isEmpty()) {
|
||||
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,10 @@ autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enab
|
||||
externalReport | When using as a Site plugin this parameter sets whether or not the external report format should be used. | false
|
||||
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
|
||||
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
|
||||
logFile | The file path to write verbose logging information. |
|
||||
suppressionFile | The file path to the XML suppression file \- used to support suppressing false positives |
|
||||
connectionTimeout | The Connection Timeout. |
|
||||
proxyUrl | The Proxy URL. |
|
||||
proxyPort | The Proxy Port. |
|
||||
proxyUsername | Defines the proxy user name. |
|
||||
proxyPassword | Defines the proxy password. |
|
||||
|
||||
Reference in New Issue
Block a user