mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-14 15:53:36 +01:00
Compare commits
202 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3002c9e430 | ||
|
|
749d70ca94 | ||
|
|
70b4adeda0 | ||
|
|
7eb86d0a58 | ||
|
|
5545d43417 | ||
|
|
2e3f68bd2c | ||
|
|
539babed18 | ||
|
|
300752ab47 | ||
|
|
2c34c10c7e | ||
|
|
e2fb261e5b | ||
|
|
6399978168 | ||
|
|
4f94765156 | ||
|
|
d58e2aa701 | ||
|
|
000f382143 | ||
|
|
cede2e8843 | ||
|
|
66c3450d46 | ||
|
|
9e6e6701be | ||
|
|
e60ec5df3c | ||
|
|
3eae185ae2 | ||
|
|
053057fdd2 | ||
|
|
0af856d566 | ||
|
|
76f2b39ce6 | ||
|
|
7b646c04d5 | ||
|
|
45ae3209b6 | ||
|
|
9b04ceedf1 | ||
|
|
41a31cdf4c | ||
|
|
fe072cd2e3 | ||
|
|
525933be99 | ||
|
|
98792fde85 | ||
|
|
81733d9f81 | ||
|
|
03a753b82c | ||
|
|
6938b0f4ae | ||
|
|
a50c61e5c5 | ||
|
|
5450bdbc55 | ||
|
|
11a634b9be | ||
|
|
801102d379 | ||
|
|
8eaba18e91 | ||
|
|
d1cb88b5c5 | ||
|
|
940889f96f | ||
|
|
3d5b934f54 | ||
|
|
155f62fd22 | ||
|
|
6c3025c487 | ||
|
|
4e62a2ee4f | ||
|
|
712a076be8 | ||
|
|
97f1ff02a0 | ||
|
|
2b36eb42a8 | ||
|
|
ada2972669 | ||
|
|
5822dcccec | ||
|
|
843fd4abec | ||
|
|
8d840bfe0e | ||
|
|
a2d0c335d0 | ||
|
|
645735a048 | ||
|
|
c58d27fe2d | ||
|
|
b9a2bcb0bf | ||
|
|
8eb2f738ef | ||
|
|
af77ab01c2 | ||
|
|
6022b13285 | ||
|
|
735660c830 | ||
|
|
27daccfc35 | ||
|
|
2a54077ab1 | ||
|
|
8e2e258518 | ||
|
|
a4771090ed | ||
|
|
133a18b70c | ||
|
|
4286c79173 | ||
|
|
97238b764f | ||
|
|
b9f3c41a9d | ||
|
|
e8e951e7ff | ||
|
|
f4928dd0b4 | ||
|
|
8e14a54815 | ||
|
|
52b2bfcd41 | ||
|
|
a383fe09f6 | ||
|
|
00446f7093 | ||
|
|
7b0d04ef72 | ||
|
|
66996ec1d3 | ||
|
|
71f40856dc | ||
|
|
2c19cc3dff | ||
|
|
40a1b6fde0 | ||
|
|
ca5ce25dee | ||
|
|
ca08887dca | ||
|
|
b7804a4c83 | ||
|
|
5460645d4a | ||
|
|
0fbfbfb8f7 | ||
|
|
998f916cdc | ||
|
|
1895af9f39 | ||
|
|
720870675c | ||
|
|
a58eba37a9 | ||
|
|
9a2ec6b110 | ||
|
|
389852c979 | ||
|
|
bbee8e7d81 | ||
|
|
161cd1ee13 | ||
|
|
3adc6a646f | ||
|
|
13ba54183a | ||
|
|
8be331ddcb | ||
|
|
a896566a14 | ||
|
|
58ebcbce3d | ||
|
|
06cff0b2a6 | ||
|
|
74c0e3a659 | ||
|
|
076ad8ef7e | ||
|
|
64ebc35dbd | ||
|
|
ee5146273e | ||
|
|
2c82711476 | ||
|
|
897650cf27 | ||
|
|
8e4a3c705a | ||
|
|
01bf6a7eee | ||
|
|
4ea9445a8f | ||
|
|
b3980acbf5 | ||
|
|
d3a1f73d3e | ||
|
|
7faa9adf79 | ||
|
|
1a5ce8f2e9 | ||
|
|
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 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -11,4 +11,6 @@
|
||||
nb-configuration.xml
|
||||
/target/
|
||||
#maven-shade-plugin generated pom
|
||||
dependency-reduced-pom.xml
|
||||
dependency-reduced-pom.xml
|
||||
# vim files
|
||||
*.swp
|
||||
|
||||
@@ -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.4</version>
|
||||
<version>1.0.8</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-ant</artifactId>
|
||||
@@ -268,7 +268,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.14</version>
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
@@ -286,9 +286,11 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<showDeprecation>false</showDeprecation>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -300,7 +302,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.doxia</groupId>
|
||||
<artifactId>doxia-module-markdown</artifactId>
|
||||
<version>1.4</version>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
@@ -309,7 +311,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>2.7</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -324,7 +326,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<version>2.9.1</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>default</id>
|
||||
@@ -337,7 +339,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.1</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -350,7 +352,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<version>2.4</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
@@ -360,7 +362,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<version>2.14</version>
|
||||
<version>2.16</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -396,7 +398,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.10</version>
|
||||
<version>2.11</version>
|
||||
<configuration>
|
||||
<enableRulesSummary>false</enableRulesSummary>
|
||||
<configLocation>${basedir}/config/checkstyle-checks.xml</configLocation>
|
||||
@@ -418,7 +420,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<version>2.5.3</version>
|
||||
</plugin>
|
||||
</reportPlugins>
|
||||
</configuration>
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.apache.tools.ant.types.resources.FileProvider;
|
||||
import org.apache.tools.ant.types.resources.Resources;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
|
||||
@@ -43,7 +44,7 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
/**
|
||||
* An Ant task definition to execute dependency-check during an Ant build.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DependencyCheckTask extends Task {
|
||||
|
||||
@@ -55,6 +56,10 @@ public class DependencyCheckTask extends Task {
|
||||
* Name of the logging properties file.
|
||||
*/
|
||||
private static final String LOG_PROPERTIES_FILE = "log.properties";
|
||||
/**
|
||||
* System specific new line character.
|
||||
*/
|
||||
private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern();
|
||||
|
||||
/**
|
||||
* Construct a new DependencyCheckTask.
|
||||
@@ -323,6 +328,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.
|
||||
*/
|
||||
@@ -367,6 +416,50 @@ public class DependencyCheckTask extends Task {
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* flag indicating whether or not to show a summary of findings.
|
||||
*/
|
||||
private boolean showSummary = true;
|
||||
|
||||
/**
|
||||
* Get the value of showSummary.
|
||||
*
|
||||
* @return the value of showSummary
|
||||
*/
|
||||
public boolean isShowSummary() {
|
||||
return showSummary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of showSummary.
|
||||
*
|
||||
* @param showSummary new value of showSummary
|
||||
*/
|
||||
public void setShowSummary(boolean showSummary) {
|
||||
this.showSummary = showSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws BuildException {
|
||||
@@ -395,12 +488,15 @@ public class DependencyCheckTask extends Task {
|
||||
if (this.failBuildOnCVSS <= 10) {
|
||||
checkForFailure(engine.getDependencies());
|
||||
}
|
||||
if (this.showSummary) {
|
||||
showSummary(engine.getDependencies());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, null, ex);
|
||||
Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, "Unable to generate dependency-check report", ex);
|
||||
throw new BuildException("Unable to generate dependency-check report", ex);
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
|
||||
throw new BuildException("An exception occured; unable to continue task", ex);
|
||||
Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, "An exception occurred; unable to continue task", ex);
|
||||
throw new BuildException("An exception occurred; unable to continue task", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,9 +555,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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,6 +598,47 @@ public class DependencyCheckTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a warning message listing a summary of dependencies and their
|
||||
* associated CPE and CVE entries.
|
||||
*
|
||||
* @param dependencies a list of dependency objects
|
||||
*/
|
||||
private void showSummary(List<Dependency> dependencies) {
|
||||
final StringBuilder summary = new StringBuilder();
|
||||
for (Dependency d : dependencies) {
|
||||
boolean firstEntry = true;
|
||||
final StringBuilder ids = new StringBuilder();
|
||||
for (Vulnerability v : d.getVulnerabilities()) {
|
||||
if (firstEntry) {
|
||||
firstEntry = false;
|
||||
} else {
|
||||
ids.append(", ");
|
||||
}
|
||||
ids.append(v.getName());
|
||||
}
|
||||
if (ids.length() > 0) {
|
||||
summary.append(d.getFileName()).append(" (");
|
||||
firstEntry = true;
|
||||
for (Identifier id : d.getIdentifiers()) {
|
||||
if (firstEntry) {
|
||||
firstEntry = false;
|
||||
} else {
|
||||
summary.append(", ");
|
||||
}
|
||||
summary.append(id.getValue());
|
||||
}
|
||||
summary.append(") : ").append(ids).append(NEW_LINE);
|
||||
}
|
||||
}
|
||||
if (summary.length() > 0) {
|
||||
final String msg = String.format("%n%n"
|
||||
+ "One or more dependencies were identified with known vulnerabilities:%n%n%s"
|
||||
+ "%n%nSee the dependency-check report for more details.%n%n", summary.toString());
|
||||
Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.WARNING, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration of supported report formats: "ALL", "HTML", "XML", "VULN",
|
||||
* etc..
|
||||
|
||||
@@ -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](../suppression.html). | 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
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.owasp.dependencycheck.data.nvdcve.BaseDBTestCase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DependencyCheckTaskTest extends BuildFileTest {
|
||||
|
||||
|
||||
@@ -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.4</version>
|
||||
<version>1.0.8</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-cli</artifactId>
|
||||
@@ -113,7 +113,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.14</version>
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
@@ -137,9 +137,11 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<showDeprecation>false</showDeprecation>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
@@ -150,7 +152,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.doxia</groupId>
|
||||
<artifactId>doxia-module-markdown</artifactId>
|
||||
<version>1.4</version>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
@@ -159,7 +161,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>2.7</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -174,7 +176,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<version>2.9.1</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>default</id>
|
||||
@@ -187,7 +189,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.1</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -200,7 +202,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<version>2.4</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
@@ -210,7 +212,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<version>2.14</version>
|
||||
<version>2.16</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -246,7 +248,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.10</version>
|
||||
<version>2.11</version>
|
||||
<configuration>
|
||||
<enableRulesSummary>false</enableRulesSummary>
|
||||
<configLocation>${basedir}/config/checkstyle-checks.xml</configLocation>
|
||||
@@ -271,7 +273,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<version>2.5.3</version>
|
||||
</plugin>
|
||||
</reportPlugins>
|
||||
</configuration>
|
||||
@@ -279,7 +281,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>appassembler-maven-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<version>1.7</version>
|
||||
<configuration>
|
||||
<programs>
|
||||
<program>
|
||||
|
||||
@@ -52,7 +52,7 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
/**
|
||||
* The command line interface for the DependencyCheck application.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class App {
|
||||
|
||||
@@ -98,7 +98,8 @@ public class App {
|
||||
cli.printVersionInfo();
|
||||
} else if (cli.isRunScan()) {
|
||||
updateSettings(cli.isAutoUpdate(), cli.getConnectionTimeout(), cli.getProxyUrl(),
|
||||
cli.getProxyPort(), cli.getDataDirectory(), cli.getPropertiesFile());
|
||||
cli.getProxyPort(), cli.getProxyUsername(), cli.getProxyPassword(),
|
||||
cli.getDataDirectory(), cli.getPropertiesFile(), cli.getSuppressionFile());
|
||||
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getApplicationName(), cli.getScanFiles());
|
||||
} else {
|
||||
cli.printHelp();
|
||||
@@ -146,11 +147,15 @@ 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, File propertiesFile) {
|
||||
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 {
|
||||
@@ -184,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
/**
|
||||
* A utility to parse command line arguments for the DependencyCheck.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class CliParser {
|
||||
|
||||
@@ -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);
|
||||
@@ -199,6 +207,11 @@ public final class CliParser {
|
||||
.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);
|
||||
|
||||
@@ -213,8 +226,11 @@ public final class CliParser {
|
||||
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;
|
||||
@@ -330,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.
|
||||
*
|
||||
@@ -361,6 +395,15 @@ public final class CliParser {
|
||||
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>
|
||||
@@ -470,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";
|
||||
/**
|
||||
@@ -505,5 +564,15 @@ public final class CliParser {
|
||||
* 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";
|
||||
}
|
||||
}
|
||||
|
||||
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; used to suppress [false positives](../suppression.html). | 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" />
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class CliParserTest {
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
^ \* You should have received a copy of the GNU General Public License along with\s*$
|
||||
^ \* dependency-check-core\. If not, see http://www.gnu.org/licenses/\.\s*$
|
||||
^ \*\s*$
|
||||
^ \* Copyright \(c\) 201[23] (Jeremy Long|Steve Springett)\. All Rights Reserved\.\s*$
|
||||
^ \* Copyright \(c\) 201[234] (Jeremy Long|Steve Springett)\. All Rights Reserved\.\s*$
|
||||
^ \*/\s*$
|
||||
^package
|
||||
|
||||
@@ -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.4</version>
|
||||
<version>1.0.8</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-core</artifactId>
|
||||
@@ -77,6 +77,9 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
</testResource>
|
||||
<testResource>
|
||||
<directory>${basedir}/src/test/resources</directory>
|
||||
<excludes>
|
||||
<exclude>**/mysql-connector-java-5.1.27-bin.jar</exclude>
|
||||
</excludes>
|
||||
<filtering>false</filtering>
|
||||
</testResource>
|
||||
</testResources>
|
||||
@@ -132,7 +135,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<version>2.6</version>
|
||||
<configuration>
|
||||
<instrumentation>
|
||||
<ignores>
|
||||
@@ -182,14 +185,9 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.14</version>
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>net.sourceforge.cobertura.datafile</name>
|
||||
<value>${project.build.directory}/cobertura/cobertura.ser</value>
|
||||
<workingDirectory>target</workingDirectory>
|
||||
</property>
|
||||
<property>
|
||||
<name>data.directory</name>
|
||||
<value>${project.build.directory}/data</value>
|
||||
@@ -201,13 +199,14 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
</systemProperties>
|
||||
<excludes>
|
||||
<exclude>**/*IntegrationTest.java</exclude>
|
||||
<exclude>**/*MySQLTest.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.14</version>
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
@@ -236,7 +235,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.doxia</groupId>
|
||||
<artifactId>doxia-module-markdown</artifactId>
|
||||
<version>1.4</version>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
@@ -245,7 +244,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>2.7</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -260,7 +259,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<version>2.9.1</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>default</id>
|
||||
@@ -273,7 +272,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.1</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -286,17 +285,17 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>2.3</version>
|
||||
<version>2.4</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<version>2.6</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<version>2.14</version>
|
||||
<version>2.16</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
@@ -339,7 +338,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>2.10</version>
|
||||
<version>2.11</version>
|
||||
<configuration>
|
||||
<enableRulesSummary>false</enableRulesSummary>
|
||||
<configLocation>${basedir}/config/checkstyle-checks.xml</configLocation>
|
||||
@@ -364,7 +363,7 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<version>2.5.3</version>
|
||||
</plugin>
|
||||
</reportPlugins>
|
||||
</configuration>
|
||||
@@ -372,9 +371,11 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<showDeprecation>false</showDeprecation>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
@@ -410,17 +411,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>
|
||||
@@ -569,4 +570,59 @@ along with Dependency-Check. If not, see <http://www.gnu.org/licenses />.
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>MySQL-IntegrationTest</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>mysql</name>
|
||||
<!--value>test</value-->
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<property>
|
||||
<name>data.driver_path</name>
|
||||
<value>${basedir}/${driver_path}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>data.driver_name</name>
|
||||
<value>${driver_name}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>data.connection_string</name>
|
||||
<value>${connection_string}</value>
|
||||
</property>
|
||||
</systemProperties>
|
||||
<includes>
|
||||
<include>**/*MySQLTest.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -31,21 +31,25 @@ import org.owasp.dependencycheck.analyzer.AnalysisException;
|
||||
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.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.data.update.CachedWebDataSource;
|
||||
import org.owasp.dependencycheck.data.update.UpdateService;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.NoDataException;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and
|
||||
* used to process the files found by the scan, if a file is encountered and an
|
||||
* Analyzer is associated with the file type then the file is turned into a
|
||||
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and used to process the files found by the
|
||||
* scan, if a file is encountered and an Analyzer is associated with the file type then the file is turned into a
|
||||
* dependency.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class Engine {
|
||||
|
||||
@@ -56,8 +60,8 @@ public class Engine {
|
||||
/**
|
||||
* A Map of analyzers grouped by Analysis phase.
|
||||
*/
|
||||
private final EnumMap<AnalysisPhase, List<Analyzer>> analyzers =
|
||||
new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class);
|
||||
private final EnumMap<AnalysisPhase, List<Analyzer>> analyzers
|
||||
= new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class);
|
||||
/**
|
||||
* A set of extensions supported by the analyzers.
|
||||
*/
|
||||
@@ -82,10 +86,8 @@ public class Engine {
|
||||
/**
|
||||
* Creates a new Engine.
|
||||
*
|
||||
* @param autoUpdate indicates whether or not data should be updated from
|
||||
* the Internet
|
||||
* @deprecated This function should no longer be used; the autoupdate flag
|
||||
* should be set using:
|
||||
* @param autoUpdate indicates whether or not data should be updated from the Internet
|
||||
* @deprecated This function should no longer be used; the autoupdate flag should be set using:
|
||||
* <code>Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, value);</code>
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -97,8 +99,7 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the analyzers specified in the configuration file (or system
|
||||
* properties).
|
||||
* Loads the analyzers specified in the configuration file (or system properties).
|
||||
*/
|
||||
private void loadAnalyzers() {
|
||||
|
||||
@@ -137,9 +138,8 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* will be scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any
|
||||
* dependencies identified are added to the dependency collection.
|
||||
*
|
||||
* @since v0.3.2.5
|
||||
*
|
||||
@@ -153,9 +153,8 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
*
|
||||
* @param path the path to a file or directory to be analyzed.
|
||||
*/
|
||||
@@ -165,9 +164,8 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* will be scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any
|
||||
* dependencies identified are added to the dependency collection.
|
||||
*
|
||||
* @since v0.3.2.5
|
||||
*
|
||||
@@ -180,9 +178,8 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a list of files or directories. If a directory is specified, it
|
||||
* will be scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
* Scans a list of files or directories. If a directory is specified, it will be scanned recursively. Any
|
||||
* dependencies identified are added to the dependency collection.
|
||||
*
|
||||
* @since v0.3.2.5
|
||||
*
|
||||
@@ -195,9 +192,8 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a list of files or directories. If a directory is specified, it
|
||||
* will be scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
* Scans a list of files or directories. If a directory is specified, it will be scanned recursively. Any
|
||||
* dependencies identified are added to the dependency collection.
|
||||
*
|
||||
* @since v0.3.2.5
|
||||
*
|
||||
@@ -210,9 +206,8 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
*
|
||||
* @since v0.3.2.4
|
||||
*
|
||||
@@ -229,8 +224,7 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scans files and directories. Any dependencies identified are
|
||||
* added to the dependency collection.
|
||||
* Recursively scans files and directories. Any dependencies identified are added to the dependency collection.
|
||||
*
|
||||
* @param dir the directory to scan.
|
||||
*/
|
||||
@@ -248,8 +242,7 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a specified file. If a dependency is identified it is added to the
|
||||
* dependency collection.
|
||||
* Scans a specified file. If a dependency is identified it is added to the dependency collection.
|
||||
*
|
||||
* @param file The file to scan.
|
||||
*/
|
||||
@@ -277,6 +270,29 @@ 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("%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;
|
||||
} catch (DatabaseException ex) {
|
||||
final String msg = String.format("%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;
|
||||
|
||||
}
|
||||
|
||||
final String logHeader = String.format("%n"
|
||||
+ "----------------------------------------------------%n"
|
||||
+ "BEGIN ANALYSIS%n"
|
||||
+ "----------------------------------------------------");
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, logHeader);
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.INFO, "Analysis Starting");
|
||||
|
||||
//phase one initialize
|
||||
for (AnalysisPhase phase : AnalysisPhase.values()) {
|
||||
final List<Analyzer> analyzerList = analyzers.get(phase);
|
||||
@@ -312,13 +328,19 @@ public class Engine {
|
||||
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())) {
|
||||
final String msgFile = String.format("Begin Analysis of '%s'", d.getActualFilePath());
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, msgFile);
|
||||
try {
|
||||
a.analyze(d, this);
|
||||
} catch (AnalysisException ex) {
|
||||
d.addAnalysisException(ex);
|
||||
} catch (Throwable ex) {
|
||||
final String axMsg = String.format("An unexpected error occurred during analysis of '%s'", d.getActualFilePath());
|
||||
final AnalysisException ax = new AnalysisException(axMsg, ex);
|
||||
d.addAnalysisException(ax);
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, axMsg);
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, axMsg, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,11 +360,16 @@ public class Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String logFooter = String.format("%n"
|
||||
+ "----------------------------------------------------%n"
|
||||
+ "END ANALYSIS%n"
|
||||
+ "----------------------------------------------------");
|
||||
Logger.getLogger(Engine.class.getName()).log(Level.FINE, logFooter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through the cached web data sources and calls update on all of
|
||||
* them.
|
||||
* Cycles through the cached web data sources and calls update on all of them.
|
||||
*/
|
||||
private void doUpdates() {
|
||||
final UpdateService service = UpdateService.getInstance();
|
||||
@@ -361,8 +388,7 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a full list of all of the analyzers. This is useful for reporting
|
||||
* which analyzers where used.
|
||||
* Returns a full list of all of the analyzers. This is useful for reporting which analyzers where used.
|
||||
*
|
||||
* @return a list of Analyzers
|
||||
*/
|
||||
@@ -379,8 +405,7 @@ public class Engine {
|
||||
* Checks all analyzers to see if an extension is supported.
|
||||
*
|
||||
* @param ext a file extension
|
||||
* @return true or false depending on whether or not the file extension is
|
||||
* supported
|
||||
* @return true or false depending on whether or not the file extension is supported
|
||||
*/
|
||||
public boolean supportsExtension(String ext) {
|
||||
if (ext == null) {
|
||||
@@ -396,4 +421,30 @@ 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
|
||||
* @throws DatabaseException thrown if there is an exception opening the database
|
||||
*/
|
||||
private void ensureDataExists() throws NoDataException, DatabaseException {
|
||||
final CpeMemoryIndex cpe = CpeMemoryIndex.getInstance();
|
||||
final CveDB cve = new CveDB();
|
||||
|
||||
try {
|
||||
cve.open();
|
||||
cpe.open(cve);
|
||||
} catch (IndexException ex) {
|
||||
throw new NoDataException(ex.getMessage(), ex);
|
||||
} catch (DatabaseException ex) {
|
||||
throw new NoDataException(ex.getMessage(), ex);
|
||||
} finally {
|
||||
cve.close();
|
||||
}
|
||||
if (cpe.numDocs() <= 0) {
|
||||
cpe.close();
|
||||
throw new NoDataException("No documents exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public abstract class AbstractAnalyzer implements Analyzer {
|
||||
|
||||
|
||||
@@ -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 implementation 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ package org.owasp.dependencycheck.analyzer;
|
||||
/**
|
||||
* An exception thrown when the analysis of a dependency fails.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class AnalysisException extends Exception {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -21,7 +21,7 @@ package org.owasp.dependencycheck.analyzer;
|
||||
/**
|
||||
* An enumeration defining the phases of analysis.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public enum AnalysisPhase {
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.owasp.dependencycheck.dependency.Dependency;
|
||||
* An analyzer will collect information about the dependency in the form of
|
||||
* Evidence.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public interface Analyzer {
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class AnalyzerService {
|
||||
|
||||
|
||||
@@ -32,31 +32,23 @@ 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)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
|
||||
@@ -82,7 +74,7 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
* Tracks the current scan/extraction depth for nested archives.
|
||||
*/
|
||||
private int scanDepth = 0;
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implmentation details of Analyzer">
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
@@ -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.
|
||||
@@ -152,10 +144,12 @@ public class ArchiveAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,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;
|
||||
}
|
||||
@@ -241,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;
|
||||
@@ -281,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,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);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -40,9 +39,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;
|
||||
@@ -55,7 +55,7 @@ import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
* to discern if there is an associated CPE. It uses the evidence contained
|
||||
* within the dependency to search the Lucene index.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class CPEAnalyzer implements Analyzer {
|
||||
|
||||
@@ -83,9 +83,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,22 +100,21 @@ 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();
|
||||
Logger.getLogger(CPEAnalyzer.class.getName()).log(Level.FINE, "Opening the CVE Database");
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
Logger.getLogger(CPEAnalyzer.class.getName()).log(Level.FINE, "Creating the Lucene CPE Index");
|
||||
cpe = CpeMemoryIndex.getInstance();
|
||||
try {
|
||||
cve.open();
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CPEAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to open the cve db", ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Logger.getLogger(CPEAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to open the cve db", ex);
|
||||
cpe.open(cve);
|
||||
} catch (IndexException ex) {
|
||||
Logger.getLogger(CPEAnalyzer.class.getName()).log(Level.FINE, "IndexException", ex);
|
||||
throw new DatabaseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the data source.
|
||||
* Closes the data sources.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
@@ -127,28 +126,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
|
||||
@@ -160,11 +137,13 @@ public class CPEAnalyzer implements Analyzer {
|
||||
* @throws ParseException is thrown when the Lucene query cannot be parsed.
|
||||
*/
|
||||
protected void determineCPE(Dependency dependency) throws CorruptIndexException, IOException, ParseException {
|
||||
Confidence vendorConf = Confidence.HIGHEST;
|
||||
Confidence productConf = Confidence.HIGHEST;
|
||||
Confidence confidence = Confidence.HIGHEST;
|
||||
|
||||
String vendors = addEvidenceWithoutDuplicateTerms("", dependency.getVendorEvidence(), vendorConf);
|
||||
String products = addEvidenceWithoutDuplicateTerms("", dependency.getProductEvidence(), productConf);
|
||||
String vendors = addEvidenceWithoutDuplicateTerms("", dependency.getVendorEvidence(), confidence);
|
||||
String products = addEvidenceWithoutDuplicateTerms("", dependency.getProductEvidence(), confidence);
|
||||
/* bug fix for #40 - version evidence is not showing up as "used" in the reports if there is no
|
||||
* CPE identified. As such, we are "using" the evidence and ignoring the results. */
|
||||
addEvidenceWithoutDuplicateTerms("", dependency.getVersionEvidence(), confidence);
|
||||
|
||||
int ctr = 0;
|
||||
do {
|
||||
@@ -180,13 +159,17 @@ public class CPEAnalyzer implements Analyzer {
|
||||
}
|
||||
}
|
||||
}
|
||||
vendorConf = reduceConfidence(vendorConf);
|
||||
if (dependency.getVendorEvidence().contains(vendorConf)) {
|
||||
vendors = addEvidenceWithoutDuplicateTerms(vendors, dependency.getVendorEvidence(), vendorConf);
|
||||
confidence = reduceConfidence(confidence);
|
||||
if (dependency.getVendorEvidence().contains(confidence)) {
|
||||
vendors = addEvidenceWithoutDuplicateTerms(vendors, dependency.getVendorEvidence(), confidence);
|
||||
}
|
||||
productConf = reduceConfidence(productConf);
|
||||
if (dependency.getProductEvidence().contains(productConf)) {
|
||||
products = addEvidenceWithoutDuplicateTerms(products, dependency.getProductEvidence(), productConf);
|
||||
if (dependency.getProductEvidence().contains(confidence)) {
|
||||
products = addEvidenceWithoutDuplicateTerms(products, dependency.getProductEvidence(), confidence);
|
||||
}
|
||||
/* bug fix for #40 - version evidence is not showing up as "used" in the reports if there is no
|
||||
* CPE identified. As such, we are "using" the evidence and ignoring the results. */
|
||||
if (dependency.getVersionEvidence().contains(confidence)) {
|
||||
addEvidenceWithoutDuplicateTerms("", dependency.getVersionEvidence(), confidence);
|
||||
}
|
||||
} while ((++ctr) < 4);
|
||||
}
|
||||
@@ -451,8 +434,10 @@ public class CPEAnalyzer implements Analyzer {
|
||||
final List<String> list = new ArrayList<String>();
|
||||
String tempWord = null;
|
||||
for (String word : words) {
|
||||
//single letter words should be concatonated with the next word.
|
||||
// so { "m", "core", "sample" } -> { "mcore", "sample" }
|
||||
/*
|
||||
single letter words should be concatenated with the next word.
|
||||
so { "m", "core", "sample" } -> { "mcore", "sample" }
|
||||
*/
|
||||
if (tempWord != null) {
|
||||
list.add(tempWord + word);
|
||||
tempWord = null;
|
||||
@@ -577,7 +562,7 @@ public class CPEAnalyzer implements Analyzer {
|
||||
dbVer = DependencyVersionUtil.parseVersion(vs.getVersion());
|
||||
}
|
||||
if (dbVer == null //special case, no version specified - everything is vulnerable
|
||||
|| evVer.equals(dbVer)) { //woot exect match
|
||||
|| evVer.equals(dbVer)) { //yeah! exact match
|
||||
final String url = String.format("http://web.nvd.nist.gov/view/vuln/search?cpe=%s", URLEncoder.encode(vs.getName(), "UTF-8"));
|
||||
final IdentifierMatch match = new IdentifierMatch("cpe", vs.getName(), url, IdentifierConfidence.EXACT_MATCH, conf);
|
||||
collected.add(match);
|
||||
@@ -643,7 +628,7 @@ public class CPEAnalyzer implements Analyzer {
|
||||
private static class IdentifierMatch implements Comparable<IdentifierMatch> {
|
||||
|
||||
/**
|
||||
* Constructs an IdentiferMatch.
|
||||
* Constructs an IdentifierMatch.
|
||||
*
|
||||
* @param type the type of identifier (such as CPE)
|
||||
* @param value the value of the identifier
|
||||
|
||||
@@ -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
|
||||
@@ -39,7 +42,7 @@ import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
* <p>Note, this grouping only works on dependencies with identified CVE
|
||||
* entries</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
|
||||
@@ -53,7 +56,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
*/
|
||||
private boolean analyzed = false;
|
||||
//</editor-fold>
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implmentation details of Analyzer">
|
||||
//<editor-fold defaultstate="collapsed" desc="All standard implementation details of Analyzer">
|
||||
/**
|
||||
* The set of file extensions supported by this analyzer.
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +196,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
|
||||
/**
|
||||
* Returns true if the file names (and version if it exists) of the two
|
||||
* dependencies are sufficiently similiar.
|
||||
* dependencies are sufficiently similar.
|
||||
*
|
||||
* @param dependency1 a dependency2 to compare
|
||||
* @param dependency2 a dependency2 to compare
|
||||
@@ -217,7 +218,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
final String oneParent = one.getParent();
|
||||
final String twoParent = two.getParent();
|
||||
if (oneParent != null) {
|
||||
if (twoParent != null && oneParent.equals(twoParent)) {
|
||||
if (oneParent.equals(twoParent)) {
|
||||
fileName1 = one.getName();
|
||||
fileName2 = two.getName();
|
||||
} else {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,10 +286,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
final File rFile = new File(dependency2.getFilePath());
|
||||
String right = rFile.getParent();
|
||||
if (left == null) {
|
||||
if (right == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return right == null;
|
||||
}
|
||||
if (left.equalsIgnoreCase(right)) {
|
||||
return true;
|
||||
@@ -299,10 +302,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 +310,27 @@ 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?
|
||||
if (leftName.length() > rightName.length()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
/*
|
||||
* 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 naming protocol should
|
||||
* be shorter:
|
||||
* axis2-saaj-1.4.1.jar
|
||||
* axis2-1.4.1.jar <-----
|
||||
* axis2-kernal-1.4.1.jar
|
||||
*/
|
||||
returnVal = leftName.length() <= rightName.length();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
* This analyzer attempts to remove some well known false positives -
|
||||
* specifically regarding the java runtime.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class FalsePositiveAnalyzer extends AbstractAnalyzer {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
*
|
||||
* Takes a dependency and analyzes the filename and determines the hashes.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.owasp.dependencycheck.dependency.Evidence;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ import org.xml.sax.XMLReader;
|
||||
* Used to load a JAR file and collect information that can be used to determine
|
||||
* the associated CPE.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class JarAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
|
||||
@@ -281,18 +281,10 @@ public class JarAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
Model pom = null;
|
||||
try {
|
||||
pom = retrievePom(path, jar);
|
||||
} catch (JAXBException ex) {
|
||||
final String msg = String.format("Unable to parse POM '%s' in '%s'",
|
||||
path, dependency.getFilePath());
|
||||
final AnalysisException ax = new AnalysisException(msg, ex);
|
||||
dependency.getAnalysisExceptions().add(ax);
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, msg, ax);
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Unable to retrieve POM '%s' in '%s'",
|
||||
path, dependency.getFilePath());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, msg, ex);
|
||||
foundSomething = setPomEvidence(dependency, pom, pomProperties, classes) || foundSomething;
|
||||
} catch (AnalysisException ex) {
|
||||
dependency.addAnalysisException(ex);
|
||||
}
|
||||
foundSomething = setPomEvidence(dependency, pom, pomProperties, classes) || foundSomething;
|
||||
}
|
||||
return foundSomething;
|
||||
}
|
||||
@@ -348,14 +340,14 @@ public class JarAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
* @param path the path to the pom.xml file within the jar file
|
||||
* @param jar the jar file to extract the pom from
|
||||
* @return returns a
|
||||
* {@link org.owasp.dependencycheck.analyzer.pom.generated.Model} object
|
||||
* @throws JAXBException is thrown if there is an exception parsing the pom
|
||||
* @throws IOException is thrown if there is an exception reading the jar
|
||||
* @throws AnalysisException is thrown if there is an exception extracting
|
||||
* or parsing the POM
|
||||
* {@link org.owasp.dependencycheck.jaxb.pom.generated.Model} object
|
||||
*/
|
||||
private Model retrievePom(String path, JarFile jar) throws JAXBException, IOException {
|
||||
private Model retrievePom(String path, JarFile jar) throws AnalysisException {
|
||||
final ZipEntry entry = jar.getEntry(path);
|
||||
Model model = null;
|
||||
if (entry != null) { //should never be null
|
||||
Model m = null;
|
||||
try {
|
||||
final XMLFilter filter = new MavenNamespaceFilter();
|
||||
final SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
@@ -367,17 +359,40 @@ public class JarAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
final InputSource xml = new InputSource(reader);
|
||||
final SAXSource source = new SAXSource(filter, xml);
|
||||
final JAXBElement<Model> el = pomUnmarshaller.unmarshal(source, Model.class);
|
||||
m = el.getValue();
|
||||
model = el.getValue();
|
||||
} catch (SecurityException ex) {
|
||||
final String msg = String.format("Unable to parse pom '%s' in jar '%s'; invalid signature", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException(ex);
|
||||
} catch (ParserConfigurationException ex) {
|
||||
final String msg = String.format("Unable to parse pom '%s' in jar '%s'", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, msg, ex);
|
||||
final String msg = String.format("Unable to parse pom '%s' in jar '%s' (Parser Configuration Error)", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException(ex);
|
||||
} catch (SAXException ex) {
|
||||
final String msg = String.format("Unable to parse pom '%s' in jar '%s'", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, msg, ex);
|
||||
final String msg = String.format("Unable to parse pom '%s' in jar '%s' (SAX Error)", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException(ex);
|
||||
} catch (JAXBException ex) {
|
||||
final String msg = String.format("Unable to parse pom '%s' in jar '%s' (JAXB Exception)", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException(ex);
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Unable to parse pom '%s' in jar '%s' (IO Exception)", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException(ex);
|
||||
} catch (Throwable ex) {
|
||||
final String msg = String.format("Unexpected error during parsing of the pom '%s' in jar '%s'", path, jar.getName());
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(JarAnalyzer.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new AnalysisException(ex);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
return null;
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@ import java.util.regex.Pattern;
|
||||
* Used to load a JAR file and collect information that can be used to determine
|
||||
* the associated CPE.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class JavaScriptAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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.analyzer;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.data.nexus.MavenArtifact;
|
||||
import org.owasp.dependencycheck.data.nexus.NexusSearch;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* Analyzer which will attempt to locate a dependency on a Nexus service
|
||||
* by SHA-1 digest of the dependency.
|
||||
*
|
||||
* There are two settings which govern this behavior:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_ENABLED}
|
||||
* determines whether this analyzer is even enabled. This can be overridden by
|
||||
* setting the system property.</li>
|
||||
* <li>{@link org.owasp.dependencycheck.utils.Settings.KEYS#ANALYZER_NEXUS_URL}
|
||||
* the URL to a Nexus service to search by SHA-1. There is an expected <code>%s</code>
|
||||
* in this where the SHA-1 will get entered.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
public class NexusAnalyzer extends AbstractAnalyzer {
|
||||
/**
|
||||
* The logger
|
||||
*/
|
||||
private static final Logger LOGGER = Logger.getLogger(NexusAnalyzer.class.getName());
|
||||
|
||||
/**
|
||||
* The name of the analyzer
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "Nexus Analyzer";
|
||||
|
||||
/**
|
||||
* The phase in which the analyzer runs
|
||||
*/
|
||||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
|
||||
|
||||
/**
|
||||
* The types of files on which this will work.
|
||||
*/
|
||||
private static final Set<String> SUPPORTED_EXTENSIONS = newHashSet("jar");
|
||||
|
||||
/**
|
||||
* Whether this is actually enabled. Will get set during initialization
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* The Nexus Search to be set up for this analyzer.
|
||||
*/
|
||||
private NexusSearch searcher;
|
||||
|
||||
/**
|
||||
* Initializes the analyzer once before any analysis is performed.
|
||||
*
|
||||
* @throws Exception if there's an error during initialization.
|
||||
*/
|
||||
public void initialize() throws Exception {
|
||||
enabled = Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED);
|
||||
|
||||
final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL);
|
||||
|
||||
if (enabled) {
|
||||
try {
|
||||
searcher = new NexusSearch(new URL(searchUrl));
|
||||
} catch (MalformedURLException mue) {
|
||||
// I know that initialize can throw an exception, but we'll
|
||||
// just disable the analyzer if the URL isn't valid
|
||||
LOGGER.warning(String.format("Property %s not a valid URL. Nexus searching disabled",
|
||||
searchUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the analyzer's name.
|
||||
*
|
||||
* @return the name of the analyzer
|
||||
*/
|
||||
public String getName() {
|
||||
return ANALYZER_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the analysis phase under which the analyzer runs.
|
||||
*
|
||||
* @return the phase under which this analyzer runs
|
||||
*/
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extensions for which this Analyzer runs.
|
||||
*
|
||||
* @return the extensions for which this Analyzer runs
|
||||
*/
|
||||
public Set<String> getSupportedExtensions() {
|
||||
return SUPPORTED_EXTENSIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the incoming extension is supported.
|
||||
*
|
||||
* @param extension the extension to check for support
|
||||
* @return whether the extension is supported
|
||||
*/
|
||||
public boolean supportsExtension(String extension) {
|
||||
return SUPPORTED_EXTENSIONS.contains(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the analysis.
|
||||
*
|
||||
* @param dependency the dependency to analyze
|
||||
* @param engine the engine
|
||||
* @throws AnalysisException when there's an exception during analysis
|
||||
*/
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
// Make a quick exit if this analyzer is disabled
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final MavenArtifact ma = searcher.searchSha1(dependency.getSha1sum());
|
||||
if (ma.getGroupId() != null && !"".equals(ma.getGroupId())) {
|
||||
dependency.getVendorEvidence().addEvidence("nexus", "groupid", ma.getGroupId(),
|
||||
Evidence.Confidence.HIGH);
|
||||
}
|
||||
if (ma.getArtifactId() != null && !"".equals(ma.getArtifactId())) {
|
||||
dependency.getProductEvidence().addEvidence("nexus", "artifactid", ma.getArtifactId(),
|
||||
Evidence.Confidence.HIGH);
|
||||
}
|
||||
if (ma.getVersion() != null && !"".equals(ma.getVersion())) {
|
||||
dependency.getVersionEvidence().addEvidence("nexus", "version", ma.getVersion(),
|
||||
Evidence.Confidence.HIGH);
|
||||
}
|
||||
if (ma.getArtifactUrl() != null && !"".equals(ma.getArtifactUrl())) {
|
||||
dependency.addIdentifier("maven", ma.toString(), ma.getArtifactUrl());
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
dependency.addAnalysisException(new AnalysisException("Invalid SHA-1"));
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
dependency.addAnalysisException(new AnalysisException("Artifact not found on repository"));
|
||||
} catch (IOException ioe) {
|
||||
dependency.addAnalysisException(new AnalysisException("Could not connect to repository", ioe));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim: cc=120:sw=4:ts=4:sts=4
|
||||
@@ -34,7 +34,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
* attempts to discern if there is an associated CVEs. It uses the the
|
||||
* identifiers found by other analyzers to lookup the CVE data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class NvdCveAnalyzer implements Analyzer {
|
||||
|
||||
@@ -107,13 +107,6 @@ public class NvdCveAnalyzer implements Analyzer {
|
||||
final String value = id.getValue();
|
||||
final List<Vulnerability> vulns = cveDB.getVulnerabilities(value);
|
||||
dependency.getVulnerabilities().addAll(vulns);
|
||||
//TODO - remove this comment block after additional testing is completed
|
||||
//note - valid match functionality has been moved into the CveDB class.
|
||||
//// for (Vulnerability v : vulns) {
|
||||
//// if (isValidMatch(dependency, v)) {
|
||||
//// dependency.addVulnerability(v);
|
||||
//// }
|
||||
//// }
|
||||
} catch (DatabaseException ex) {
|
||||
throw new AnalysisException(ex);
|
||||
}
|
||||
@@ -166,57 +159,4 @@ public class NvdCveAnalyzer implements Analyzer {
|
||||
public void initialize() throws Exception {
|
||||
this.open();
|
||||
}
|
||||
//TODO - remove this comment block after additional testing is completed
|
||||
// The following check has been moved into the CveDB class.
|
||||
//// /**
|
||||
//// * <p>Determines if this is a valid vulnerability match for the given
|
||||
//// * dependency. Specifically, this is concerned with ensuring the version
|
||||
//// * numbers are correct.</p>
|
||||
//// * <p>Currently, this is focused on the issues with the versions for Struts
|
||||
//// * 1 and Struts 2. In the future this will due better matching on more
|
||||
//// * version numbers.</p>
|
||||
//// *
|
||||
//// * @param dependency the dependency
|
||||
//// * @param v the vulnerability
|
||||
//// * @return returns true if the vulnerability is for the given dependency
|
||||
//// */
|
||||
//// private boolean isValidMatch(final Dependency dependency, final Vulnerability v) {
|
||||
//// //right now I only know of the issue with Struts1/2
|
||||
//// // start with fixing this problem.
|
||||
////
|
||||
//// //TODO extend this solution to do better version matching for the vulnerable software.
|
||||
//// boolean struts1 = false;
|
||||
//// boolean struts2 = false;
|
||||
//// for (Identifier i : dependency.getIdentifiers()) {
|
||||
//// if (i.getValue().startsWith("cpe:/a:apache:struts:")) {
|
||||
//// final char version = i.getValue().charAt(21);
|
||||
//// if (version == '1') {
|
||||
//// struts1 = true;
|
||||
//// }
|
||||
//// if (version == '2') {
|
||||
//// struts2 = true;
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// if (!struts1 && !struts2) {
|
||||
//// return true; //we are not looking at struts, so return true.
|
||||
//// }
|
||||
//// if (struts1 && struts2) {
|
||||
//// return true; //there is a mismatch here, but we can't solve it here so we return valid.
|
||||
//// }
|
||||
//// if (struts1) {
|
||||
//// boolean hasStruts1Vuln = false;
|
||||
//// boolean hasStruts2PreviousVersion = false;
|
||||
//// for (VulnerableSoftware vs : v.getVulnerableSoftware()) {
|
||||
//// //TODO FIX THIS
|
||||
//// //hasStruts2PreviousVersion |= vs.hasPreviousVersion() && vs.getName().charAt(21) == '2';
|
||||
//// //hasStruts1Vuln |= vs.getName().charAt(21) == '1';
|
||||
//// }
|
||||
//// if (!hasStruts1Vuln && hasStruts2PreviousVersion) {
|
||||
//// return false;
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// return true;
|
||||
//// }
|
||||
}
|
||||
|
||||
@@ -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 implementation 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ package org.owasp.dependencycheck.concurrency;
|
||||
/**
|
||||
* If thrown, indicates that a problem occurred when locking a directory.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DirectoryLockException extends Exception {
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.util.logging.Logger;
|
||||
* the process will "spin" waiting for an opportunity to obtain the lock
|
||||
* requested.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DirectorySpinLock implements Closeable /*, AutoCloseable*/ {
|
||||
|
||||
@@ -258,7 +258,7 @@ public class DirectorySpinLock implements Closeable /*, AutoCloseable*/ {
|
||||
try {
|
||||
lock.release();
|
||||
} catch (ClosedChannelException ex) {
|
||||
Logger.getLogger(DirectorySpinLock.class.getName()).log(Level.FINEST, "Uable to release file lock", ex);
|
||||
Logger.getLogger(DirectorySpinLock.class.getName()).log(Level.FINEST, "Unable to release file lock", ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DirectorySpinLock.class.getName()).log(Level.FINEST, "Unable to release file lock due to IO Exception", ex);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ package org.owasp.dependencycheck.concurrency;
|
||||
/**
|
||||
* If thrown, indicates that there is a problem with a directory.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class InvalidDirectoryException extends Exception {
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ package org.owasp.dependencycheck.data.cpe;
|
||||
* Fields is a collection of field names used within the Lucene index for CPE
|
||||
* entries.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public abstract class Fields {
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import java.net.URLDecoder;
|
||||
/**
|
||||
* A CPE entry containing the name, vendor, product, and version.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class IndexEntry implements Serializable {
|
||||
|
||||
|
||||
@@ -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) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cpe;
|
||||
|
||||
/**
|
||||
* An exception thrown when the there is an issue using the in-memory CPE Index.
|
||||
*
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class IndexException extends Exception {
|
||||
|
||||
/**
|
||||
* The serial version UID for serialization.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new IndexException.
|
||||
*/
|
||||
public IndexException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new IndexException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class CweDB {
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.xml.sax.helpers.DefaultHandler;
|
||||
/**
|
||||
* A SAX Handler that will parse the CWE XML.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class CweHandler extends DefaultHandler {
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
* An abstract tokenizing filter that can be used as the base for a tokenizing
|
||||
* filter.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public abstract class AbstractTokenizingFilter extends TokenFilter {
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.apache.lucene.util.Version;
|
||||
* Tokenizes the input breaking it into tokens when non-alpha/numeric characters
|
||||
* are found.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class AlphaNumericTokenizer extends CharTokenizer {
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.apache.lucene.search.similarities.DefaultSimilarity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DependencySimilarity extends DefaultSimilarity {
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.apache.lucene.util.Version;
|
||||
* WordDelimiterFilter, LowerCaseFilter, and StopFilter. The intended purpose of
|
||||
* this Analyzer is to index the CPE fields vendor and product.</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class FieldAnalyzer extends Analyzer {
|
||||
|
||||
|
||||
@@ -18,14 +18,22 @@
|
||||
*/
|
||||
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>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.apache.lucene.util.Version;
|
||||
/**
|
||||
* A Lucene field analyzer used to analyzer queries against the CPE data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class SearchFieldAnalyzer extends Analyzer {
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.apache.lucene.util.Version;
|
||||
* SearchVersionAnalyzer is a Lucene Analyzer used to analyze version
|
||||
* information.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
* @deprecated version information is no longer stored in lucene
|
||||
*/
|
||||
@Deprecated
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
* <p><b>Example:</b> "Spring Framework Core" -> "Spring SpringFramework
|
||||
* Framework FrameworkCore Core".</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class TokenPairConcatenatingFilter extends TokenFilter {
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
|
||||
* <p><b>Example:</b> "3.0.0.RELEASE" -> "3 3.0 3.0.0 RELEASE
|
||||
* 3.0.0.RELEASE".</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class UrlTokenizingFilter extends AbstractTokenizingFilter {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.apache.lucene.util.Version;
|
||||
/**
|
||||
* VersionAnalyzer is a Lucene Analyzer used to analyze version information.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
* @deprecated version information is no longer stored in lucene
|
||||
*/
|
||||
@Deprecated
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
* <p><b>Example:</b> "3.0.0.RELEASE" -> "3 3.0 3.0.0 RELEASE
|
||||
* 3.0.0.RELEASE".</p>
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
* @deprecated version information is no longer stored in lucene
|
||||
*/
|
||||
@Deprecated
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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.nexus;
|
||||
|
||||
/**
|
||||
* Simple bean representing a Maven Artifact.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
public class MavenArtifact {
|
||||
/**
|
||||
* The groupId
|
||||
*/
|
||||
private String groupId;
|
||||
|
||||
/**
|
||||
* The artifactId
|
||||
*/
|
||||
private String artifactId;
|
||||
|
||||
/**
|
||||
* The version
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* The artifact url. This may change depending on which Nexus
|
||||
* server the search took place.
|
||||
*/
|
||||
private String artifactUrl;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an empty MavenArtifact.
|
||||
*/
|
||||
public MavenArtifact() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MavenArtifact with the given attributes.
|
||||
*
|
||||
* @param groupId the groupId
|
||||
* @param artifactId the artifactId
|
||||
* @param version the version
|
||||
*/
|
||||
public MavenArtifact(String groupId, String artifactId, String version) {
|
||||
setGroupId(groupId);
|
||||
setArtifactId(artifactId);
|
||||
setVersion(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MavenArtifact with the given attributes.
|
||||
*
|
||||
* @param groupId the groupId
|
||||
* @param artifactId the artifactId
|
||||
* @param version the version
|
||||
* @param url the artifactLink url
|
||||
*/
|
||||
public MavenArtifact(String groupId, String artifactId, String version, String url) {
|
||||
setGroupId(groupId);
|
||||
setArtifactId(artifactId);
|
||||
setVersion(version);
|
||||
setArtifactUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Artifact coordinates as a String.
|
||||
*
|
||||
* @return the String representation of the artifact coordinates
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s:%s:%s", groupId, artifactId, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the groupId.
|
||||
*
|
||||
* @param groupId the groupId
|
||||
*/
|
||||
public void setGroupId(String groupId) { this.groupId = groupId; }
|
||||
|
||||
/**
|
||||
* Gets the groupId.
|
||||
*
|
||||
* @return the groupId
|
||||
*/
|
||||
public String getGroupId() { return groupId; }
|
||||
|
||||
/**
|
||||
* Sets the artifactId.
|
||||
*
|
||||
* @param artifactId the artifactId
|
||||
*/
|
||||
public void setArtifactId(String artifactId) { this.artifactId = artifactId; }
|
||||
|
||||
/**
|
||||
* Gets the artifactId.
|
||||
*
|
||||
* @return the artifactId
|
||||
*/
|
||||
public String getArtifactId() { return artifactId; }
|
||||
|
||||
/**
|
||||
* Sets the version.
|
||||
*
|
||||
* @param version the version
|
||||
*/
|
||||
public void setVersion(String version) { this.version = version; }
|
||||
|
||||
/**
|
||||
* Gets the version.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
public String getVersion() { return version; }
|
||||
|
||||
/**
|
||||
* Sets the artifactUrl.
|
||||
*
|
||||
* @param artifactUrl the artifactUrl
|
||||
*/
|
||||
public void setArtifactUrl(String artifactUrl) {
|
||||
this.artifactUrl = artifactUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the artifactUrl.
|
||||
*
|
||||
* @return the artifactUrl
|
||||
*/
|
||||
public String getArtifactUrl() {
|
||||
return artifactUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: cc=120:sw=4:ts=4:sts=4
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.nexus;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.logging.Logger;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* Class of methods to search Nexus repositories.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
public class NexusSearch {
|
||||
/**
|
||||
* The root URL for the Nexus repository service
|
||||
*/
|
||||
private final URL rootURL;
|
||||
|
||||
/**
|
||||
* Used for logging.
|
||||
*/
|
||||
private static final Logger LOGGER = Logger.getLogger(NexusSearch.class.getName());
|
||||
|
||||
/**
|
||||
* Creates a NexusSearch for the given repository URL.
|
||||
*
|
||||
* @param rootURL the root URL of the repository on which searches should execute.
|
||||
* full URL's are calculated relative to this URL, so it should end with a /
|
||||
*/
|
||||
public NexusSearch(URL rootURL) {
|
||||
this.rootURL = rootURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the configured Nexus repository for the given sha1
|
||||
* hash. If the artifact is found, a <code>MavenArtifact</code> is populated
|
||||
* with the coordinate information.
|
||||
*
|
||||
* @param sha1 The SHA-1 hash string for which to search
|
||||
* @return the populated Maven coordinates
|
||||
* @throws IOException if it's unable to connect to the specified repositor or
|
||||
* if the specified artifact is not found.
|
||||
*/
|
||||
public MavenArtifact searchSha1(String sha1) throws IOException {
|
||||
if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) {
|
||||
throw new IllegalArgumentException("Invalid SHA1 format");
|
||||
}
|
||||
|
||||
final URL url = new URL(rootURL, String.format("identify/sha1/%s", sha1.toLowerCase()));
|
||||
|
||||
LOGGER.fine(String.format("Searching Nexus url %s", url.toString()));
|
||||
|
||||
final URLConnection conn = url.openConnection();
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// JSON would be more elegant, but there's not currently a dependency
|
||||
// on JSON, so don't want to add one just for this
|
||||
conn.addRequestProperty("Accept", "application/xml");
|
||||
conn.connect();
|
||||
|
||||
try {
|
||||
final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
final Document doc = builder.parse(conn.getInputStream());
|
||||
final XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
final String groupId = xpath.evaluate("/org.sonatype.nexus.rest.model.NexusArtifact/groupId", doc);
|
||||
final String artifactId = xpath.evaluate("/org.sonatype.nexus.rest.model.NexusArtifact/artifactId", doc);
|
||||
final String version = xpath.evaluate("/org.sonatype.nexus.rest.model.NexusArtifact/version", doc);
|
||||
final String link = xpath.evaluate("/org.sonatype.nexus.rest.model.NexusArtifact/artifactLink", doc);
|
||||
return new MavenArtifact(groupId, artifactId, version, link);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
// This is what we get when the SHA1 they sent doesn't exist in Nexus. This
|
||||
// is useful upstream for recovery, so we just re-throw it
|
||||
throw fnfe;
|
||||
} catch (Exception e) {
|
||||
// Anything else is jacked-up XML stuff that we really can't recover from well
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim: cc=120:sw=4:ts=4:sts=4
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.nexus</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* <p>Contains classes related to searching a Nexus repository.</p>
|
||||
* <p>These are used to abstract Nexus searching away from
|
||||
* OWASP Dependency Check so they can be reused elsewhere.</p>
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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) 2014 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
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.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
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.utils.DBUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* Loads the configured database driver and returns the database connection. If the embedded H2 database is used
|
||||
* obtaining a connection will ensure the database file exists and that the appropriate table structure has been
|
||||
* created.
|
||||
*
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class ConnectionFactory {
|
||||
|
||||
/**
|
||||
* The version of the current DB Schema.
|
||||
*/
|
||||
public static final String DB_SCHEMA_VERSION = "2.8";
|
||||
/**
|
||||
* Resource location for SQL file used to create the database schema.
|
||||
*/
|
||||
public static final String DB_STRUCTURE_RESOURCE = "data/initialize.sql";
|
||||
|
||||
/**
|
||||
* Private constructor for this factory class; no instance is ever needed.
|
||||
*/
|
||||
private ConnectionFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new database connection object per the database configuration. This will load the appropriate
|
||||
* database driver, via the DriverManager, if configured.
|
||||
*
|
||||
* @return a database connection object
|
||||
* @throws DatabaseException thrown if there is an exception loading the database connection
|
||||
*/
|
||||
public static Connection getConnection() throws DatabaseException {
|
||||
Connection conn = null;
|
||||
try {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Loading database connection");
|
||||
|
||||
final String connStr = getConnectionString();
|
||||
final String user = Settings.getString(Settings.KEYS.DB_USER, "dcuser");
|
||||
//yes, yes - hard-coded password - only if there isn't one in the properties file.
|
||||
final String pass = Settings.getString(Settings.KEYS.DB_PASSWORD, "DC-Pass1337!");
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Connection String: {0}", connStr);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Database User: {0}", user);
|
||||
boolean createTables = false;
|
||||
if (connStr.startsWith("jdbc:h2:file:")) { //H2
|
||||
createTables = needToCreateDatabaseStructure();
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Need to create DB Structure: {0}", createTables);
|
||||
}
|
||||
final String driverName = Settings.getString(Settings.KEYS.DB_DRIVER_NAME, "");
|
||||
if (!driverName.isEmpty()) { //likely need to load the correct driver
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Loading driver: {0}", driverName);
|
||||
final String driverPath = Settings.getString(Settings.KEYS.DB_DRIVER_PATH, "");
|
||||
if (!driverPath.isEmpty()) { //ugh, driver is not on classpath?
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Loading driver from: {0}", driverPath);
|
||||
DriverLoader.load(driverName, driverPath);
|
||||
} else {
|
||||
DriverLoader.load(driverName);
|
||||
}
|
||||
}
|
||||
|
||||
//JDBC4 drivers don't need this call.
|
||||
//Class.forName("org.h2.Driver");
|
||||
conn = DriverManager.getConnection(connStr, user, pass);
|
||||
if (createTables) {
|
||||
createTables(conn);
|
||||
} else {
|
||||
ensureSchemaVersion(conn);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to load database");
|
||||
} catch (DriverLoadException ex) {
|
||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to load database driver");
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to connect to the database");
|
||||
} catch (DatabaseException ex) {
|
||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to create the database structure");
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured connection string. If using the embedded H2 database this function will also ensure the
|
||||
* data directory exists and if not create it.
|
||||
*
|
||||
* @return the connection string
|
||||
* @throws IOException thrown the data directory cannot be created
|
||||
*/
|
||||
private static String getConnectionString() throws IOException {
|
||||
final String connStr = Settings.getString(Settings.KEYS.DB_CONNECTION_STRING, "jdbc:h2:file:%s;AUTO_SERVER=TRUE");
|
||||
if (connStr.contains("%s")) {
|
||||
final String fileName = getDataDirectory().getCanonicalPath();
|
||||
final File file = new File(fileName, "cve." + DB_SCHEMA_VERSION);
|
||||
return String.format(connStr, file.getAbsolutePath());
|
||||
}
|
||||
return connStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the directory that the JAR file exists in so that we can ensure we always use a common data directory
|
||||
* for the embedded H2 database. This is public solely for some unit tests; otherwise this should be private.
|
||||
*
|
||||
* @return the data directory to store data files
|
||||
* @throws IOException is thrown if an IOException occurs of course...
|
||||
*/
|
||||
public static File getDataDirectory() throws IOException {
|
||||
final File path = Settings.getDataFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
if (!path.exists()) {
|
||||
if (!path.mkdirs()) {
|
||||
throw new IOException("Unable to create NVD CVE Data directory");
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the H2 database file exists. If it does not exist then the data structure will need to be created.
|
||||
*
|
||||
* @return true if the H2 database file does not exist; otherwise false
|
||||
* @throws IOException thrown if the data directory does not exist and cannot be created
|
||||
*/
|
||||
private static boolean needToCreateDatabaseStructure() throws IOException {
|
||||
final File dir = getDataDirectory();
|
||||
final String name = String.format("cve.%s.h2.db", DB_SCHEMA_VERSION);
|
||||
final File file = new File(dir, name);
|
||||
return !file.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database structure (tables and indexes) to store the CVE data.
|
||||
*
|
||||
* @param conn the database connection
|
||||
* @throws DatabaseException thrown if there is a Database Exception
|
||||
*/
|
||||
private static void createTables(Connection conn) throws DatabaseException {
|
||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.FINE, "Creating database structure");
|
||||
InputStream is;
|
||||
InputStreamReader reader;
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
is = ConnectionFactory.class.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());
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to create database statement", ex);
|
||||
} finally {
|
||||
DBUtils.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(ConnectionFactory.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the provided connection to check the specified schema version within the database.
|
||||
*
|
||||
* @param conn the database connection object
|
||||
* @throws DatabaseException thrown if the schema version is not compatible with this version of dependency-check
|
||||
*/
|
||||
private static void ensureSchemaVersion(Connection conn) throws DatabaseException {
|
||||
ResultSet rs = null;
|
||||
CallableStatement cs = null;
|
||||
try {
|
||||
cs = conn.prepareCall("SELECT value FROM properties WHERE id = 'version'");
|
||||
rs = cs.executeQuery();
|
||||
if (rs.next()) {
|
||||
final boolean isWrongSchema = !DB_SCHEMA_VERSION.equals(rs.getString(1));
|
||||
if (isWrongSchema) {
|
||||
throw new DatabaseException("Incorrect database schema; unable to continue");
|
||||
}
|
||||
} else {
|
||||
throw new DatabaseException("Database schema is missing");
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException("Unable to check the database schema version");
|
||||
} finally {
|
||||
DBUtils.closeResultSet(rs);
|
||||
DBUtils.closeStatement(cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ package org.owasp.dependencycheck.data.nvdcve;
|
||||
* An exception used to indicate the db4o database is corrupt. This could be due
|
||||
* to invalid data or a complete failure of the db.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
class CorruptDatabaseException extends DatabaseException {
|
||||
|
||||
|
||||
@@ -18,14 +18,8 @@
|
||||
*/
|
||||
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;
|
||||
@@ -33,6 +27,8 @@ import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -40,140 +36,81 @@ 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.DBUtils;
|
||||
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)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class CveDB {
|
||||
|
||||
/**
|
||||
* 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 = ?";
|
||||
/**
|
||||
* SQL Statement to delete software by vulnerability ID.
|
||||
*/
|
||||
public 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 = ?";
|
||||
/**
|
||||
* 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); ";
|
||||
/**
|
||||
* SQL Statement to insert a new reference.
|
||||
*/
|
||||
public 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 (?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to insert a new cpe.
|
||||
*/
|
||||
public 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 = ?";
|
||||
/**
|
||||
* SQL Statement to insert a new vulnerability.
|
||||
*/
|
||||
public 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=?, "
|
||||
+ "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 "
|
||||
+ "FROM software INNER JOIN vulnerability ON vulnerability.id = software.cveId "
|
||||
+ "INNER JOIN cpeEntry ON cpeEntry.id = software.cpeEntryId "
|
||||
+ "WHERE vendor = ? AND product = ?";
|
||||
//unfortunately, the version info is too complicated to do in a select. Need to filter this afterwards
|
||||
// + " AND (version = '-' OR previousVersion IS NOT NULL OR version=?)";
|
||||
//
|
||||
/**
|
||||
* 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 = ?";
|
||||
/**
|
||||
* SQL Statement to select references by CVEID.
|
||||
*/
|
||||
public static final String SELECT_REFERENCE = "SELECT source, name, url FROM reference WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to select software by CVEID.
|
||||
*/
|
||||
public 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 "
|
||||
// + "software.cpeVersionId = cpeVersion.id LEFT JOIN Version ON cpeVersion.versionId = version.id WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to select a vulnerability by CVEID.
|
||||
*/
|
||||
public 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 = ?";
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* Opens the database connection. If the database does not exist, it will
|
||||
* create a new one.
|
||||
* Creates a new CveDB object and opens the database connection. Note, the connection must be closed by the caller
|
||||
* by calling the close method.
|
||||
*
|
||||
* @throws 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
|
||||
* @throws DatabaseException thrown if there is an exception opening the database.
|
||||
*/
|
||||
@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();
|
||||
public CveDB() throws DatabaseException {
|
||||
super();
|
||||
try {
|
||||
open();
|
||||
databaseProperties = new DatabaseProperties(this);
|
||||
} catch (DatabaseException ex) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 DatabaseException thrown if there is an error opening the database connection
|
||||
*/
|
||||
public final void open() throws DatabaseException {
|
||||
conn = ConnectionFactory.getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(DBUtils.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(DBUtils.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the database connection is open or closed.
|
||||
*
|
||||
* @return whether the database connection is open or closed
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return conn != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits all completed transactions.
|
||||
*
|
||||
@@ -181,7 +118,8 @@ public class CveDB {
|
||||
*/
|
||||
public void commit() throws SQLException {
|
||||
if (conn != null) {
|
||||
conn.commit();
|
||||
//temporary remove this as autocommit is on.
|
||||
//conn.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,34 +131,134 @@ public class CveDB {
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
close();
|
||||
super.finalize(); //not necessary if extending Object.
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the DB4O database. Close should be called on this object when it
|
||||
* is done being used.
|
||||
* Database properties object containing the 'properties' from the database table.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
private DatabaseProperties databaseProperties;
|
||||
|
||||
/**
|
||||
* Searches the CPE entries in the database and retrieves all entries for a
|
||||
* given vendor and product combination. The returned list will include all
|
||||
* versions of the product that are registered in the NVD CVE data.
|
||||
* Get the value of databaseProperties.
|
||||
*
|
||||
* @return the value of databaseProperties
|
||||
*/
|
||||
public DatabaseProperties getDatabaseProperties() {
|
||||
return databaseProperties;
|
||||
}
|
||||
//<editor-fold defaultstate="collapsed" desc="Constants to create, maintain, and retrieve data from the CVE Database">
|
||||
/**
|
||||
* SQL Statement to delete references by vulnerability ID.
|
||||
*/
|
||||
private static final String DELETE_REFERENCE = "DELETE FROM reference WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to delete software by vulnerability ID.
|
||||
*/
|
||||
private static final String DELETE_SOFTWARE = "DELETE FROM software WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to delete a vulnerability by 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.
|
||||
*/
|
||||
private static final String CLEANUP_ORPHANS = "DELETE FROM CpeEntry WHERE id not in (SELECT CPEEntryId FROM Software); ";
|
||||
/**
|
||||
* SQL Statement to insert a new reference.
|
||||
*/
|
||||
private static final String INSERT_REFERENCE = "INSERT INTO reference (cveid, name, url, source) VALUES (?, ?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to insert a new software.
|
||||
*/
|
||||
private static final String INSERT_SOFTWARE = "INSERT INTO software (cveid, cpeEntryId, previousVersion) VALUES (?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to insert a new cpe.
|
||||
*/
|
||||
private static final String INSERT_CPE = "INSERT INTO cpeEntry (cpe, vendor, product) VALUES (?, ?, ?)";
|
||||
/**
|
||||
* SQL Statement to get a CPEProductID.
|
||||
*/
|
||||
private static final String SELECT_CPE_ID = "SELECT id FROM cpeEntry WHERE cpe = ?";
|
||||
/**
|
||||
* SQL Statement to insert a new vulnerability.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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 = ?";
|
||||
//unfortunately, the version info is too complicated to do in a select. Need to filter this afterwards
|
||||
// + " AND (version = '-' OR previousVersion IS NOT NULL OR version=?)";
|
||||
//
|
||||
/**
|
||||
* SQL Statement to find the CPE entry based on the 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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 "
|
||||
// + "software.cpeVersionId = cpeVersion.id LEFT JOIN Version ON cpeVersion.versionId = version.id WHERE cveid = ?";
|
||||
/**
|
||||
* SQL Statement to select a vulnerability by CVEID.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
private static final String SELECT_VULNERABILITY_ID = "SELECT id FROM vulnerability WHERE cve = ?";
|
||||
/**
|
||||
* SQL Statement to retrieve the properties from the database.
|
||||
*/
|
||||
private static final String SELECT_PROPERTIES = "SELECT id, value FROM properties";
|
||||
/**
|
||||
* SQL Statement to retrieve a property from the database.
|
||||
*/
|
||||
private static final String SELECT_PROPERTY = "SELECT id, value FROM properties WHERE id = ?";
|
||||
/**
|
||||
* SQL Statement to insert a new property.
|
||||
*/
|
||||
private static final String INSERT_PROPERTY = "INSERT INTO properties (id, value) VALUES (?, ?)";
|
||||
/**
|
||||
* SQL Statement to update a property.
|
||||
*/
|
||||
private static final String UPDATE_PROPERTY = "UPDATE properties SET value = ? WHERE id = ?";
|
||||
/**
|
||||
* SQL Statement to delete a property.
|
||||
*/
|
||||
private static final String DELETE_PROPERTY = "DELETE FROM properties WHERE id = ?";
|
||||
|
||||
//</editor-fold>
|
||||
/**
|
||||
* Searches the CPE entries in the database and retrieves all entries for a given vendor and product combination.
|
||||
* The returned list will include all versions of the product that are registered in the NVD CVE data.
|
||||
*
|
||||
* @param vendor the identified vendor name of the dependency being analyzed
|
||||
* @param product the identified name of the product of the dependency being
|
||||
* analyzed
|
||||
* @param product the identified name of the product of the dependency being analyzed
|
||||
* @return a set of vulnerable software
|
||||
*/
|
||||
public Set<VulnerableSoftware> getCPEs(String vendor, String product) {
|
||||
@@ -228,7 +266,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();
|
||||
@@ -239,14 +277,142 @@ public class CveDB {
|
||||
cpe.add(vs);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
closeResultSet(rs);
|
||||
closeStatement(ps);
|
||||
DBUtils.closeResultSet(rs);
|
||||
DBUtils.closeStatement(ps);
|
||||
}
|
||||
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) {
|
||||
final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
} // can't close the statement in the PS as the resultset is returned, closing PS would close the resultset
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of properties.
|
||||
*
|
||||
* @return the properties from the database
|
||||
*/
|
||||
Properties getProperties() {
|
||||
final Properties prop = new Properties();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
ps = getConnection().prepareStatement(SELECT_PROPERTIES);
|
||||
rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
prop.setProperty(rs.getString(1), rs.getString(2));
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
DBUtils.closeStatement(ps);
|
||||
DBUtils.closeResultSet(rs);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a set of properties to the database.
|
||||
*
|
||||
* @param props a collection of properties
|
||||
*/
|
||||
void saveProperties(Properties props) {
|
||||
PreparedStatement updateProperty = null;
|
||||
PreparedStatement insertProperty = null;
|
||||
try {
|
||||
try {
|
||||
updateProperty = getConnection().prepareStatement(UPDATE_PROPERTY);
|
||||
insertProperty = getConnection().prepareStatement(INSERT_PROPERTY);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.WARNING, "Unable to save properties to the database");
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Unable to save properties to the database", ex);
|
||||
return;
|
||||
}
|
||||
for (Entry<Object, Object> entry : props.entrySet()) {
|
||||
final String key = entry.getKey().toString();
|
||||
final String value = entry.getValue().toString();
|
||||
try {
|
||||
updateProperty.setString(1, value);
|
||||
updateProperty.setString(2, key);
|
||||
if (updateProperty.executeUpdate() == 0) {
|
||||
insertProperty.setString(1, key);
|
||||
insertProperty.setString(2, value);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
final String msg = String.format("Unable to save property '%s' with a value of '%s' to the database", key, value);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
DBUtils.closeStatement(updateProperty);
|
||||
DBUtils.closeStatement(insertProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a property to the database.
|
||||
*
|
||||
* @param key the property key
|
||||
* @param value the property value
|
||||
*/
|
||||
void saveProperty(String key, String value) {
|
||||
PreparedStatement updateProperty = null;
|
||||
PreparedStatement insertProperty = null;
|
||||
try {
|
||||
try {
|
||||
updateProperty = getConnection().prepareStatement(UPDATE_PROPERTY);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.WARNING, "Unable to save properties to the database");
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Unable to save properties to the database", ex);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
updateProperty.setString(1, value);
|
||||
updateProperty.setString(2, key);
|
||||
if (updateProperty.executeUpdate() == 0) {
|
||||
try {
|
||||
insertProperty = getConnection().prepareStatement(INSERT_PROPERTY);
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.WARNING, "Unable to save properties to the database");
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, "Unable to save properties to the database", ex);
|
||||
return;
|
||||
}
|
||||
insertProperty.setString(1, key);
|
||||
insertProperty.setString(2, value);
|
||||
insertProperty.execute();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
final String msg = String.format("Unable to save property '%s' with a value of '%s' to the database", key, value);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
} finally {
|
||||
DBUtils.closeStatement(updateProperty);
|
||||
DBUtils.closeStatement(insertProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the vulnerabilities associated with the specified CPE.
|
||||
*
|
||||
@@ -268,7 +434,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();
|
||||
@@ -280,8 +446,8 @@ public class CveDB {
|
||||
cveEntries.add(cveId);
|
||||
}
|
||||
}
|
||||
closeResultSet(rs);
|
||||
closeStatement(ps);
|
||||
DBUtils.closeResultSet(rs);
|
||||
DBUtils.closeStatement(ps);
|
||||
for (String cve : cveEntries) {
|
||||
final Vulnerability v = getVulnerability(cve);
|
||||
vulnerabilities.add(v);
|
||||
@@ -290,7 +456,7 @@ public class CveDB {
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Exception retrieving vulnerability for " + cpeStr, ex);
|
||||
} finally {
|
||||
closeResultSet(rs);
|
||||
DBUtils.closeResultSet(rs);
|
||||
}
|
||||
return vulnerabilities;
|
||||
}
|
||||
@@ -311,7 +477,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 +501,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()) {
|
||||
@@ -357,25 +523,25 @@ public class CveDB {
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Error retrieving " + cve, ex);
|
||||
} finally {
|
||||
closeResultSet(rsV);
|
||||
closeResultSet(rsR);
|
||||
closeResultSet(rsS);
|
||||
closeStatement(psV);
|
||||
closeStatement(psR);
|
||||
closeStatement(psS);
|
||||
DBUtils.closeResultSet(rsV);
|
||||
DBUtils.closeResultSet(rsR);
|
||||
DBUtils.closeResultSet(rsS);
|
||||
DBUtils.closeStatement(psV);
|
||||
DBUtils.closeStatement(psR);
|
||||
DBUtils.closeStatement(psS);
|
||||
}
|
||||
return vuln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the vulnerability within the database. If the vulnerability does
|
||||
* not exist it will be added.
|
||||
* Updates the vulnerability within the database. If the vulnerability does not exist it will be added.
|
||||
*
|
||||
* @param vuln the vulnerability to add to the database
|
||||
* @throws DatabaseException is thrown if the database
|
||||
*/
|
||||
public void updateVulnerability(Vulnerability vuln) throws DatabaseException {
|
||||
PreparedStatement selectVulnerabilityId = null;
|
||||
PreparedStatement deleteVulnerability = null;
|
||||
PreparedStatement deleteReferences = null;
|
||||
PreparedStatement deleteSoftware = null;
|
||||
PreparedStatement updateVulnerability = null;
|
||||
@@ -386,15 +552,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();
|
||||
@@ -406,20 +573,25 @@ public class CveDB {
|
||||
deleteSoftware.setInt(1, vulnerabilityId);
|
||||
deleteSoftware.execute();
|
||||
}
|
||||
closeResultSet(rs);
|
||||
DBUtils.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());
|
||||
@@ -440,7 +612,7 @@ public class CveDB {
|
||||
final String msg = String.format("Unable to retrieve id for new vulnerability for '%s'", vuln.getName());
|
||||
throw new DatabaseException(msg, ex);
|
||||
} finally {
|
||||
closeResultSet(rs);
|
||||
DBUtils.closeResultSet(rs);
|
||||
rs = null;
|
||||
}
|
||||
}
|
||||
@@ -462,7 +634,7 @@ public class CveDB {
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Unable to get primary key for new cpe: " + s.getName(), ex);
|
||||
} finally {
|
||||
closeResultSet(rs);
|
||||
DBUtils.closeResultSet(rs);
|
||||
rs = null;
|
||||
}
|
||||
|
||||
@@ -471,7 +643,7 @@ public class CveDB {
|
||||
insertCpe.setString(2, s.getVendor());
|
||||
insertCpe.setString(3, s.getProduct());
|
||||
insertCpe.executeUpdate();
|
||||
cpeProductId = getGeneratedKey(insertCpe);
|
||||
cpeProductId = DBUtils.getGeneratedKey(insertCpe);
|
||||
}
|
||||
if (cpeProductId == 0) {
|
||||
throw new DatabaseException("Unable to retrieve cpeProductId - no data returned");
|
||||
@@ -492,171 +664,55 @@ public class CveDB {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new DatabaseException(msg, ex);
|
||||
} finally {
|
||||
closeStatement(selectVulnerabilityId);
|
||||
closeStatement(deleteReferences);
|
||||
closeStatement(deleteSoftware);
|
||||
closeStatement(updateVulnerability);
|
||||
closeStatement(insertVulnerability);
|
||||
closeStatement(insertReference);
|
||||
closeStatement(selectCpeId);
|
||||
closeStatement(insertCpe);
|
||||
closeStatement(insertSoftware);
|
||||
DBUtils.closeStatement(selectVulnerabilityId);
|
||||
DBUtils.closeStatement(deleteReferences);
|
||||
DBUtils.closeStatement(deleteSoftware);
|
||||
DBUtils.closeStatement(updateVulnerability);
|
||||
DBUtils.closeStatement(deleteVulnerability);
|
||||
DBUtils.closeStatement(insertVulnerability);
|
||||
DBUtils.closeStatement(insertReference);
|
||||
DBUtils.closeStatement(selectCpeId);
|
||||
DBUtils.closeStatement(insertCpe);
|
||||
DBUtils.closeStatement(insertSoftware);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* ensure orphan entries are removed.
|
||||
* It is possible that orphaned rows may be generated during database updates. This should be called after all
|
||||
* updates have been completed to ensure orphan entries are removed.
|
||||
*/
|
||||
public void cleanupDatabase() {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = conn.prepareStatement(CLEANUP_ORPHANS);
|
||||
ps = getConnection().prepareStatement(CLEANUP_ORPHANS);
|
||||
if (ps != null) {
|
||||
ps.executeUpdate();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, null, ex);
|
||||
final String msg = "An unexpected SQL Exception occurred; please see the verbose log for more details.";
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.SEVERE, msg);
|
||||
Logger.getLogger(CveDB.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
closeStatement(ps);
|
||||
DBUtils.closeStatement(ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* previous version argument indicates that all previous versions are
|
||||
* affected.
|
||||
* Determines if the given identifiedVersion is affected by the given cpeId and previous version flag. A non-null,
|
||||
* non-empty string passed to the previous version argument indicates that all previous versions are affected.
|
||||
*
|
||||
* @param vendor the vendor of the dependency being analyzed
|
||||
* @param product the product name of the dependency being analyzed
|
||||
* @param identifiedVersion the identified version of the dependency being
|
||||
* analyzed
|
||||
* @param cpeId the cpe identifier of software that has a known
|
||||
* vulnerability
|
||||
* @param previous a flag indicating if previous versions of the product are
|
||||
* vulnerable
|
||||
* @param identifiedVersion the identified version of the dependency being analyzed
|
||||
* @param cpeId the cpe identifier of software that has a known vulnerability
|
||||
* @param previous a flag indicating if previous versions of the product are vulnerable
|
||||
* @return true if the identified version is affected, otherwise false
|
||||
*/
|
||||
private boolean isAffected(String vendor, String product, DependencyVersion identifiedVersion, String cpeId, String previous) {
|
||||
boolean affected = false;
|
||||
final boolean isStruts = "apache".equals(vendor) && "struts".equals(product);
|
||||
final DependencyVersion v = parseDependencyVersion(cpeId);
|
||||
final boolean prevAffected = previous == null ? false : !previous.isEmpty();
|
||||
final boolean prevAffected = previous != null && !previous.isEmpty();
|
||||
if (identifiedVersion == null || "-".equals(identifiedVersion.toString())) {
|
||||
if (v == null || "-".equals(v.toString())) {
|
||||
affected = true;
|
||||
@@ -678,8 +734,8 @@ public class CveDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the version (including revision) from a CPE identifier. If no
|
||||
* version is identified then a '-' is returned.
|
||||
* Parses the version (including revision) from a CPE identifier. If no version is identified then a '-' is
|
||||
* returned.
|
||||
*
|
||||
* @param cpeStr a cpe identifier
|
||||
* @return a dependency version
|
||||
@@ -696,8 +752,7 @@ public class CveDB {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a CPE and parses out the version number. If no version is
|
||||
* identified then a '-' is returned.
|
||||
* Takes a CPE and parses out the version number. If no version is identified then a '-' is returned.
|
||||
*
|
||||
* @param cpe a cpe object
|
||||
* @return a dependency version
|
||||
|
||||
@@ -21,7 +21,7 @@ package org.owasp.dependencycheck.data.nvdcve;
|
||||
/**
|
||||
* An exception thrown if an operation against the database fails.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DatabaseException extends Exception {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.nvdcve;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.data.update.NvdCveInfo;
|
||||
|
||||
/**
|
||||
* This is a wrapper around a set of properties that are stored in the database.
|
||||
*
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DatabaseProperties {
|
||||
|
||||
/**
|
||||
* Modified key word, used as a key to store information about the modified
|
||||
* file (i.e. the containing the last 8 days of updates)..
|
||||
*/
|
||||
public static final String MODIFIED = "modified";
|
||||
/**
|
||||
* The properties file key for the last updated field - used to store the
|
||||
* last updated time of the Modified NVD CVE xml file.
|
||||
*/
|
||||
public static final String LAST_UPDATED = "lastupdated.modified";
|
||||
/**
|
||||
* Stores the last updated time for each of the NVD CVE files. These
|
||||
* timestamps should be updated if we process the modified file within 7
|
||||
* days of the last update.
|
||||
*/
|
||||
public static final String LAST_UPDATED_BASE = "lastupdated.";
|
||||
/**
|
||||
* A collection of properties about the data.
|
||||
*/
|
||||
private Properties properties;
|
||||
/**
|
||||
* A reference to the database.
|
||||
*/
|
||||
private CveDB cveDB;
|
||||
|
||||
/**
|
||||
* Constructs a new data properties object.
|
||||
*
|
||||
* @param cveDB the database object holding the properties
|
||||
*/
|
||||
DatabaseProperties(CveDB cveDB) {
|
||||
this.cveDB = cveDB;
|
||||
loadProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the properties from the database.
|
||||
*/
|
||||
private void loadProperties() {
|
||||
this.properties = cveDB.getProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not any properties are set.
|
||||
*
|
||||
* @return whether or not any properties are set
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return properties == null || properties.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a properties file containing the last updated date to the
|
||||
* VULNERABLE_CPE directory.
|
||||
*
|
||||
* @param updatedValue the updated NVD CVE entry
|
||||
* @throws UpdateException is thrown if there is an update exception
|
||||
*/
|
||||
public void save(NvdCveInfo updatedValue) throws UpdateException {
|
||||
if (updatedValue == null) {
|
||||
return;
|
||||
}
|
||||
properties.put(LAST_UPDATED_BASE + updatedValue.getId(), String.valueOf(updatedValue.getTimestamp()));
|
||||
cveDB.saveProperty(LAST_UPDATED_BASE + updatedValue.getId(), String.valueOf(updatedValue.getTimestamp()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property value for the given key. If the key is not contained
|
||||
* in the underlying properties null is returned.
|
||||
*
|
||||
* @param key the property key
|
||||
* @return the value of the property
|
||||
*/
|
||||
public String getProperty(String key) {
|
||||
return properties.getProperty(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property value for the given key. If the key is not contained
|
||||
* in the underlying properties the default value is returned.
|
||||
*
|
||||
* @param key the property key
|
||||
* @param defaultValue the default value
|
||||
* @return the value of the property
|
||||
*/
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
return properties.getProperty(key, defaultValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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) 2014 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
/**
|
||||
* An exception thrown the database driver is unable to be loaded.
|
||||
*
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class DriverLoadException extends Exception {
|
||||
|
||||
/**
|
||||
* the serial version uid.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates an DriverLoadException.
|
||||
*
|
||||
* @param msg the exception message
|
||||
*/
|
||||
public DriverLoadException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an DriverLoadException.
|
||||
*
|
||||
* @param ex the cause of the exception
|
||||
*/
|
||||
public DriverLoadException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an DriverLoadException.
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param ex the cause of the exception
|
||||
*/
|
||||
public DriverLoadException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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) 2014 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.sql.Driver;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* DriverLoader is a utility class that is used to load database drivers.
|
||||
*
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class DriverLoader {
|
||||
|
||||
/**
|
||||
* Private constructor for a utility class.
|
||||
*/
|
||||
private DriverLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class using the system class loader and registers the driver with the driver manager.
|
||||
*
|
||||
* @param className the fully qualified name of the desired class
|
||||
* @throws DriverLoadException thrown if the driver cannot be loaded
|
||||
*/
|
||||
public static void load(String className) throws DriverLoadException {
|
||||
final ClassLoader loader = ClassLoader.getSystemClassLoader();
|
||||
load(className, loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class by registering the supplied paths to the class loader and then registers the driver
|
||||
* with the driver manager. The pathToDriver argument is added to the class loader so that an external driver can be
|
||||
* loaded. Note, the pathTodriver can contain a semi-colon separated list of paths so any dependencies can be added
|
||||
* as needed. If a path in the pathToDriver argument is a directory all files in the directory are added to the
|
||||
* class path.
|
||||
*
|
||||
* @param className the fully qualified name of the desired class
|
||||
* @param pathToDriver the path to the JAR file containing the driver; note, this can be a semi-colon separated list
|
||||
* of paths
|
||||
* @throws DriverLoadException thrown if the driver cannot be loaded
|
||||
*/
|
||||
public static void load(String className, String pathToDriver) throws DriverLoadException {
|
||||
final URLClassLoader parent = (URLClassLoader) ClassLoader.getSystemClassLoader();
|
||||
final ArrayList<URL> urls = new ArrayList<URL>();
|
||||
final String[] paths = pathToDriver.split(File.pathSeparator);
|
||||
for (String path : paths) {
|
||||
final File file = new File(path);
|
||||
if (file.isDirectory()) {
|
||||
final File[] files = file.listFiles();
|
||||
|
||||
for (File f : files) {
|
||||
try {
|
||||
urls.add(f.toURI().toURL());
|
||||
} catch (MalformedURLException ex) {
|
||||
final String msg = String.format("Unable to load database driver '%s'; invalid path provided '%s'",
|
||||
className, f.getAbsoluteFile());
|
||||
Logger.getLogger(DriverLoader.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DriverLoadException(msg, ex);
|
||||
}
|
||||
}
|
||||
} else if (file.exists()) {
|
||||
try {
|
||||
urls.add(file.toURI().toURL());
|
||||
} catch (MalformedURLException ex) {
|
||||
final String msg = String.format("Unable to load database driver '%s'; invalid path provided '%s'",
|
||||
className, file.getAbsoluteFile());
|
||||
Logger.getLogger(DriverLoader.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DriverLoadException(msg, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
final URLClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
|
||||
@Override
|
||||
public URLClassLoader run() {
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]), parent);
|
||||
}
|
||||
});
|
||||
|
||||
load(className, loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified class using the supplied class loader and registers the driver with the driver manager.
|
||||
*
|
||||
* @param className the fully qualified name of the desired class
|
||||
* @param loader the class loader to use when loading the driver
|
||||
* @throws DriverLoadException thrown if the driver cannot be loaded
|
||||
*/
|
||||
private static void load(String className, ClassLoader loader) throws DriverLoadException {
|
||||
try {
|
||||
final Class c = loader.loadClass(className);
|
||||
final Driver driver = (Driver) c.newInstance();
|
||||
//using the DriverShim to get around the fact that the DriverManager won't register a driver not in the base class path
|
||||
DriverManager.registerDriver(new DriverShim(driver));
|
||||
} catch (ClassNotFoundException ex) {
|
||||
final String msg = String.format("Unable to load database driver '%s'", className);
|
||||
Logger.getLogger(DriverLoader.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DriverLoadException(msg, ex);
|
||||
} catch (InstantiationException ex) {
|
||||
final String msg = String.format("Unable to load database driver '%s'", className);
|
||||
Logger.getLogger(DriverLoader.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DriverLoadException(msg, ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
final String msg = String.format("Unable to load database driver '%s'", className);
|
||||
Logger.getLogger(DriverLoader.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DriverLoadException(msg, ex);
|
||||
} catch (SQLException ex) {
|
||||
final String msg = String.format("Unable to load database driver '%s'", className);
|
||||
Logger.getLogger(DriverLoader.class.getName()).log(Level.FINE, msg, ex);
|
||||
throw new DriverLoadException(msg, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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) 2014 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.DriverPropertyInfo;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Driver shim to get around the class loader issue with the DriverManager. The following code is a nearly identical
|
||||
* copy (with more comments and a few more methods implemented) of the DriverShim from:</p>
|
||||
* <blockquote>http://www.kfu.com/~nsayer/Java/dyn-jdbc.html</blockquote>
|
||||
*
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
* @see java.sql.Driver
|
||||
*/
|
||||
class DriverShim implements Driver {
|
||||
|
||||
/**
|
||||
* The database driver being wrapped.
|
||||
*/
|
||||
private final Driver driver;
|
||||
|
||||
/**
|
||||
* Constructs a new wrapper around a Driver.
|
||||
*
|
||||
* @param driver the database driver to wrap
|
||||
*/
|
||||
DriverShim(Driver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the underlying driver's call to acceptsURL. Returns whether or not the driver can open a connection to the
|
||||
* given URL.
|
||||
*
|
||||
* @param url the URL of the database
|
||||
* @return true if the wrapped driver can connect to the specified URL
|
||||
* @throws SQLException thrown if there is an error connecting to the database
|
||||
* @see java.sql.Driver#acceptsURL(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public boolean acceptsURL(String url) throws SQLException {
|
||||
return this.driver.acceptsURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapped driver's major version number.
|
||||
*
|
||||
* @return the wrapped driver's major version number
|
||||
* @see java.sql.Driver#getMajorVersion()
|
||||
*/
|
||||
@Override
|
||||
public int getMajorVersion() {
|
||||
return this.driver.getMajorVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapped driver's minor version number.
|
||||
*
|
||||
* @return the wrapped driver's minor version number
|
||||
* @see java.sql.Driver#getMinorVersion()
|
||||
*/
|
||||
@Override
|
||||
public int getMinorVersion() {
|
||||
return this.driver.getMinorVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the wrapped driver is jdbcCompliant.
|
||||
*
|
||||
* @return true if the wrapped driver is JDBC compliant; otherwise false
|
||||
* @see java.sql.Driver#jdbcCompliant()
|
||||
*/
|
||||
@Override
|
||||
public boolean jdbcCompliant() {
|
||||
return this.driver.jdbcCompliant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the call to the underlying driver's connect method.
|
||||
*
|
||||
* @param url the URL of the database
|
||||
* @param info a collection of string/value pairs
|
||||
* @return a Connection object
|
||||
* @throws SQLException thrown if there is an error connecting to the database
|
||||
* @see java.sql.Driver#connect(java.lang.String, java.util.Properties)
|
||||
*/
|
||||
@Override
|
||||
public Connection connect(String url, Properties info) throws SQLException {
|
||||
return this.driver.connect(url, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the call to the underlying driver's getPropertyInfo method.
|
||||
*
|
||||
* @param url the URL of the database
|
||||
* @param info a collection of string/value pairs
|
||||
* @return an array of DriverPropertyInfo objects
|
||||
* @throws SQLException thrown if there is an error accessing the database
|
||||
* @see java.sql.Driver#getPropertyInfo(java.lang.String, java.util.Properties)
|
||||
*/
|
||||
@Override
|
||||
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
|
||||
return this.driver.getPropertyInfo(url, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the call to the underlying driver's getParentLogger method.
|
||||
*
|
||||
* @return the parent's Logger
|
||||
* @throws SQLFeatureNotSupportedException thrown if the feature is not supported
|
||||
* @see java.sql.Driver#getParentLogger()
|
||||
*/
|
||||
@Override
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
return this.driver.getParentLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard implementation of hashCode.
|
||||
*
|
||||
* @return the hashCode of the object
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 97 * hash + (this.driver != null ? this.driver.hashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard implementation of equals.
|
||||
*
|
||||
* @param obj the object to compare
|
||||
* @return returns true if the objects are equal; otherwise false
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final DriverShim other = (DriverShim) obj;
|
||||
return this.driver == other.driver || (this.driver != null && this.driver.equals(other.driver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard implementation of toString().
|
||||
*
|
||||
* @return the String representation of the object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DriverShim{" + "driver=" + driver + '}';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* Contains classes used to work with the data sources.
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
package org.owasp.dependencycheck.data;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -16,23 +16,25 @@
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
|
||||
/**
|
||||
* Defines an Index who's data is retrieved from the Internet. This data can be
|
||||
* downloaded and the index updated.
|
||||
* Defines a data source who's data is retrieved from the Internet. This data
|
||||
* can be downloaded and the local cache updated.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public interface CachedWebDataSource {
|
||||
|
||||
/**
|
||||
* Determines if an update to the current index is needed, if it is the new
|
||||
* data is downloaded from the Internet and imported into the current Lucene
|
||||
* Index.
|
||||
* Determines if an update to the current data store is needed, if it is the
|
||||
* new data is downloaded from the Internet and imported into the current
|
||||
* cached data store.
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an exception updating the
|
||||
* index.
|
||||
* @throws UpdateException is thrown if there is an exception downloading
|
||||
* the data or updating the data store.
|
||||
*/
|
||||
void update() throws UpdateException;
|
||||
}
|
||||
@@ -1,241 +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.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Properties;
|
||||
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.Settings;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class DataStoreMetaInfo {
|
||||
|
||||
/**
|
||||
* Batch key word, used as key to store information about batch mode.
|
||||
*/
|
||||
public static final String BATCH = "batch";
|
||||
/**
|
||||
* Modified key word, used as a key to store information about the modified
|
||||
* file (i.e. the containing the last 8 days of updates)..
|
||||
*/
|
||||
public static final String MODIFIED = "modified";
|
||||
/**
|
||||
* The name of the properties file containing the timestamp of the last
|
||||
* update.
|
||||
*/
|
||||
private static final String UPDATE_PROPERTIES_FILE = "data.properties";
|
||||
/**
|
||||
* The properties file key for the last updated field - used to store the
|
||||
* last updated time of the Modified NVD CVE xml file.
|
||||
*/
|
||||
public static final String LAST_UPDATED = "lastupdated.modified";
|
||||
/**
|
||||
* Stores the last updated time for each of the NVD CVE files. These
|
||||
* timestamps should be updated if we process the modified file within 7
|
||||
* days of the last update.
|
||||
*/
|
||||
public static final String LAST_UPDATED_BASE = "lastupdated.";
|
||||
/**
|
||||
* A collection of properties about the data.
|
||||
*/
|
||||
private Properties properties = new Properties();
|
||||
/**
|
||||
* Indicates whether or not the updates are using a batch update mode or
|
||||
* not.
|
||||
*/
|
||||
private boolean batchUpdateMode;
|
||||
|
||||
/**
|
||||
* Get the value of batchUpdateMode.
|
||||
*
|
||||
* @return the value of batchUpdateMode
|
||||
*/
|
||||
protected boolean isBatchUpdateMode() {
|
||||
return batchUpdateMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of batchUpdateMode.
|
||||
*
|
||||
* @param batchUpdateMode new value of batchUpdateMode
|
||||
*/
|
||||
protected void setBatchUpdateMode(boolean batchUpdateMode) {
|
||||
this.batchUpdateMode = batchUpdateMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new data properties object.
|
||||
*/
|
||||
public DataStoreMetaInfo() {
|
||||
batchUpdateMode = !Settings.getString(Settings.KEYS.BATCH_UPDATE_URL, "").isEmpty();
|
||||
loadProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the data's meta properties.
|
||||
*/
|
||||
private void loadProperties() {
|
||||
final File file = getPropertiesFile();
|
||||
if (file.exists()) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
} catch (FileNotFoundException ignore) {
|
||||
//we will never get here as we check for existence above.
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINEST, null, ignore);
|
||||
}
|
||||
try {
|
||||
properties.load(is);
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Unable to load properties file '%s'", file.getPath());
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINE, null, ex);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ex) {
|
||||
final String msg = String.format("Unable to close properties file '%s'", file.getPath());
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not any properties are set.
|
||||
*
|
||||
* @return whether or not any properties are set
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return properties.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a properties file containing the last updated date to the
|
||||
* VULNERABLE_CPE directory.
|
||||
*
|
||||
* @param updatedValue the updated nvdcve entry
|
||||
* @throws UpdateException is thrown if there is an update exception
|
||||
*/
|
||||
public void save(NvdCveInfo updatedValue) throws UpdateException {
|
||||
if (updatedValue == null) {
|
||||
return;
|
||||
}
|
||||
final File cveProp = getPropertiesFile();
|
||||
final Properties prop = new Properties();
|
||||
if (cveProp.exists()) {
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(cveProp);
|
||||
prop.load(in);
|
||||
} catch (Exception ignoreMe) {
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINEST, null, ignoreMe);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ignoreMeToo) {
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINEST, null, ignoreMeToo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prop.put("version", CveDB.DB_SCHEMA_VERSION);
|
||||
prop.put(LAST_UPDATED_BASE + updatedValue.getId(), String.valueOf(updatedValue.getTimestamp()));
|
||||
|
||||
OutputStream os = null;
|
||||
OutputStreamWriter out = null;
|
||||
try {
|
||||
os = new FileOutputStream(cveProp);
|
||||
out = new OutputStreamWriter(os, "UTF-8");
|
||||
prop.store(out, "Meta data about data and data sources used by dependency-check");
|
||||
} catch (FileNotFoundException ex) {
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new UpdateException("Unable to find last updated properties file.", ex);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINE, null, ex);
|
||||
throw new UpdateException("Unable to update last updated properties file.", ex);
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DataStoreMetaInfo.class.getName()).log(Level.FINEST, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property value for the given key. If the key is not contained
|
||||
* in the underlying properties null is returned.
|
||||
*
|
||||
* @param key the property key
|
||||
* @return the value of the property
|
||||
*/
|
||||
public String getProperty(String key) {
|
||||
return properties.getProperty(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the property value for the given key. If the key is not contained
|
||||
* in the underlying properties the default value is returned.
|
||||
*
|
||||
* @param key the property key
|
||||
* @param defaultValue the default value
|
||||
* @return the value of the property
|
||||
*/
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
return properties.getProperty(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the properties file.
|
||||
*
|
||||
* @return the properties file
|
||||
*/
|
||||
public static File getPropertiesFile() {
|
||||
final File dataDirectory = Settings.getFile(Settings.KEYS.DATA_DIRECTORY);
|
||||
final File file = new File(dataDirectory, UPDATE_PROPERTIES_FILE);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -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) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
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;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* Class responsible for updating the CPE and NVDCVE data stores.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public class DatabaseUpdater implements CachedWebDataSource {
|
||||
|
||||
/**
|
||||
* <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 {
|
||||
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();
|
||||
|
||||
|
||||
if (task.isUpdateNeeded()) {
|
||||
lock.release();
|
||||
lock.obtainExclusiveLock();
|
||||
if (task.shouldDeleteAndRecreate()) {
|
||||
try {
|
||||
deleteExistingData();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.WARNING, "Unable to delete the existing data directory");
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
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.");
|
||||
Logger.getLogger(DatabaseUpdater.class.getName()).log(Level.FINE, null, ex);
|
||||
} catch (DownloadFailedException ex) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ package org.owasp.dependencycheck.data.update;
|
||||
/**
|
||||
* A pojo that contains the Url and timestamp of the current NvdCve XML files.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class NvdCveInfo {
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.update.exception.UpdateException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
|
||||
/**
|
||||
* Class responsible for updating the NVD CVE and CPE data stores.
|
||||
*
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class NvdCveUpdater implements CachedWebDataSource {
|
||||
|
||||
/**
|
||||
* <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 {
|
||||
final StandardUpdate task = new StandardUpdate();
|
||||
if (task.isUpdateNeeded()) {
|
||||
task.update();
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
Logger.getLogger(NvdCveUpdater.class.getName()).log(Level.WARNING,
|
||||
"NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.");
|
||||
Logger.getLogger(NvdCveUpdater.class.getName()).log(Level.FINE, null, ex);
|
||||
} catch (DownloadFailedException ex) {
|
||||
Logger.getLogger(NvdCveUpdater.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(NvdCveUpdater.class.getName()).log(Level.FINE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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.update.task.ProcessTask;
|
||||
import org.owasp.dependencycheck.data.update.task.CallableDownloadTask;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
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.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.nvdcve.DatabaseProperties.MODIFIED;
|
||||
|
||||
/**
|
||||
* 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 DatabaseProperties properties;
|
||||
/**
|
||||
* A collection of updateable NVD CVE items.
|
||||
*/
|
||||
private UpdateableNvdCve updateable;
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
openDataStores();
|
||||
properties = cveDB.getDatabaseProperties();
|
||||
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 downloadExecutors = Executors.newFixedThreadPool(poolSize);
|
||||
final ExecutorService processExecutor = Executors.newSingleThreadExecutor();
|
||||
final Set<Future<Future<ProcessTask>>> downloadFutures = new HashSet<Future<Future<ProcessTask>>>(maxUpdates);
|
||||
for (NvdCveInfo cve : updateable) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
final CallableDownloadTask call = new CallableDownloadTask(cve, processExecutor, cveDB);
|
||||
downloadFutures.add(downloadExecutors.submit(call));
|
||||
}
|
||||
}
|
||||
downloadExecutors.shutdown();
|
||||
|
||||
//next, move the future future processTasks to just future processTasks
|
||||
final Set<Future<ProcessTask>> processFutures = new HashSet<Future<ProcessTask>>(maxUpdates);
|
||||
for (Future<Future<ProcessTask>> future : downloadFutures) {
|
||||
Future<ProcessTask> task = null;
|
||||
try {
|
||||
task = future.get();
|
||||
} catch (InterruptedException ex) {
|
||||
downloadExecutors.shutdownNow();
|
||||
processExecutor.shutdownNow();
|
||||
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Thread was interupted during download", ex);
|
||||
throw new UpdateException("The download was interupted", ex);
|
||||
} catch (ExecutionException ex) {
|
||||
downloadExecutors.shutdownNow();
|
||||
processExecutor.shutdownNow();
|
||||
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Thread was interupted during download execution", ex);
|
||||
throw new UpdateException("The execution of the download was interupted", ex);
|
||||
}
|
||||
if (task == null) {
|
||||
downloadExecutors.shutdownNow();
|
||||
processExecutor.shutdownNow();
|
||||
Logger.getLogger(StandardUpdate.class.getName()).log(Level.FINE, "Thread was interupted during download");
|
||||
throw new UpdateException("The download was interupted; unable to complete the update");
|
||||
} else {
|
||||
processFutures.add(task);
|
||||
}
|
||||
}
|
||||
|
||||
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 (we may not have actually updated it)
|
||||
properties.save(updateable.get(MODIFIED));
|
||||
cveDB.cleanupDatabase();
|
||||
}
|
||||
} 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
|
||||
*/
|
||||
protected final UpdateableNvdCve updatesNeeded() throws MalformedURLException, DownloadFailedException, UpdateException {
|
||||
UpdateableNvdCve 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 {
|
||||
final long lastUpdated = Long.parseLong(properties.getProperty(DatabaseProperties.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(DatabaseProperties.LAST_UPDATED_BASE + entry.getId(), "0"));
|
||||
} catch (NumberFormatException ex) {
|
||||
final String msg = String.format("Error parsing '%s' '%s' from nvdcve.lastupdated",
|
||||
DatabaseProperties.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 UpdateableNvdCve retrieveCurrentTimestampsFromWeb()
|
||||
throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException {
|
||||
|
||||
final UpdateableNvdCve updates = new UpdateableNvdCve();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 final void openDataStores() throws UpdateException {
|
||||
if (cveDB != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
cveDB = new CveDB();
|
||||
cveDB.open();
|
||||
} 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.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,14 @@
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public final class UpdateService {
|
||||
|
||||
@@ -1,51 +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.UpdateException;
|
||||
|
||||
/**
|
||||
* An interface defining an update task.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
*/
|
||||
public interface UpdateTask {
|
||||
|
||||
/**
|
||||
* <p>Updates the data store to the latest version.</p>
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
*/
|
||||
void update() throws UpdateException;
|
||||
|
||||
/**
|
||||
* Get the value of deleteAndRecreate.
|
||||
*
|
||||
* @return the value of deleteAndRecreate
|
||||
*/
|
||||
boolean shouldDeleteAndRecreate();
|
||||
|
||||
/**
|
||||
* Gets whether or not an update is needed.
|
||||
*
|
||||
* @return true or false depending on whether an update is needed
|
||||
*/
|
||||
boolean isUpdateNeeded();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,9 @@ import org.owasp.dependencycheck.utils.Downloader;
|
||||
* Contains a collection of updateable NvdCveInfo objects. This is used to
|
||||
* determine which files need to be downloaded and processed.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class Updateable implements java.lang.Iterable<NvdCveInfo>, Iterator<NvdCveInfo> {
|
||||
public class UpdateableNvdCve implements java.lang.Iterable<NvdCveInfo>, Iterator<NvdCveInfo> {
|
||||
|
||||
/**
|
||||
* A collection of sources of data.
|
||||
@@ -16,18 +16,18 @@
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
package org.owasp.dependencycheck.data.update.exception;
|
||||
|
||||
/**
|
||||
* An InvalidDataDataException is a generic exception used when trying to load
|
||||
* the nvd cve meta data.
|
||||
* the NVD CVE meta data.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class InvalidDataException extends Exception {
|
||||
|
||||
/**
|
||||
* the serial version uid.
|
||||
* The serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data;
|
||||
package org.owasp.dependencycheck.data.update.exception;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception used when an error occurs reading a setting.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class UpdateException extends IOException {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.update.exception</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* <p>A collection of exception classes used within the application.</p>
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update.exception;
|
||||
@@ -1,18 +1,15 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.nvdcve.xml</title>
|
||||
* <title>org.owasp.dependencycheck.data.update</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* <p>Contains classes used to parse the NVD CVE XML file.</p>
|
||||
* <p>The basic use is that the Importer is called to import
|
||||
* an NVD CVE file. The Importer instantiates an Indexer object
|
||||
* (which extends Index). The Indexer creates a partial-unmarshalling
|
||||
* SAX parser (implemented in the NvdCveXmlFilter) that extracts
|
||||
* VulnerabilityTypes (aka Entry) from the NVD CVE data file and
|
||||
* stores these into a Lucene Index.</p>
|
||||
* <p>Contains classes used to update the data stores.</p>
|
||||
* <p>The UpdateService will load, any correctly defined CachedWebDataSource(s)
|
||||
* and call update() on them. The Cached Data Source must determine if it needs
|
||||
* to be updated and if so perform the update. The sub packages contain classes
|
||||
* used to perform the actual updates.</p>
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update;
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.task;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.update.NvdCveInfo;
|
||||
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<Future<ProcessTask>> {
|
||||
|
||||
/**
|
||||
* Simple constructor for the callable download task.
|
||||
*
|
||||
* @param nvdCveInfo the NVD CVE info
|
||||
* @param processor the processor service to submit the downloaded files to
|
||||
* @param cveDB the CVE DB to use to store the vulnerability data
|
||||
*/
|
||||
public CallableDownloadTask(NvdCveInfo nvdCveInfo, ExecutorService processor, CveDB cveDB) {
|
||||
this.nvdCveInfo = nvdCveInfo;
|
||||
this.processorService = processor;
|
||||
this.cveDB = cveDB;
|
||||
|
||||
final File file1;
|
||||
final File file2;
|
||||
|
||||
|
||||
try {
|
||||
file1 = File.createTempFile("cve" + nvdCveInfo.getId() + "_", ".xml");
|
||||
file2 = File.createTempFile("cve_1_2_" + nvdCveInfo.getId() + "_", ".xml");
|
||||
} catch (IOException ex) {
|
||||
return;
|
||||
}
|
||||
this.first = file1;
|
||||
this.second = file2;
|
||||
|
||||
}
|
||||
/**
|
||||
* The CVE DB to use when processing the files.
|
||||
*/
|
||||
private CveDB cveDB;
|
||||
/**
|
||||
* The processor service to pass the results of the download to.
|
||||
*/
|
||||
private ExecutorService processorService;
|
||||
/**
|
||||
* 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 Future<ProcessTask> 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);
|
||||
try {
|
||||
Downloader.fetchFile(url1, first);
|
||||
Downloader.fetchFile(url2, second);
|
||||
} catch (DownloadFailedException ex) {
|
||||
msg = String.format("Download Failed for NVD CVE - %s%nSome CVEs may not be reported.", nvdCveInfo.getId());
|
||||
Logger.getLogger(CallableDownloadTask.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(CallableDownloadTask.class.getName()).log(Level.FINE, null, ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
msg = String.format("Download Complete for NVD CVE - %s", nvdCveInfo.getId());
|
||||
Logger.getLogger(CallableDownloadTask.class.getName()).log(Level.INFO, msg);
|
||||
|
||||
final ProcessTask task = new ProcessTask(cveDB, this);
|
||||
return this.processorService.submit(task);
|
||||
|
||||
} catch (Throwable ex) {
|
||||
final String msg = String.format("An exception occurred downloading NVD CVE - %s%nSome CVEs may not be reported.", nvdCveInfo.getId());
|
||||
Logger.getLogger(CallableDownloadTask.class.getName()).log(Level.WARNING, msg);
|
||||
Logger.getLogger(CallableDownloadTask.class.getName()).log(Level.FINE, "Download Task Failed", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.task;
|
||||
|
||||
import org.owasp.dependencycheck.data.update.xml.NvdCve20Handler;
|
||||
import org.owasp.dependencycheck.data.update.xml.NvdCve12Handler;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
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.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.data.update.StandardUpdate;
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* A reference to the CveDB.
|
||||
*/
|
||||
private final CveDB cveDB;
|
||||
/**
|
||||
* A reference to the callable download task.
|
||||
*/
|
||||
private final CallableDownloadTask filePair;
|
||||
/**
|
||||
* A reference to the properties.
|
||||
*/
|
||||
private final DatabaseProperties properties;
|
||||
|
||||
/**
|
||||
* Constructs a new ProcessTask used to process an NVD CVE update.
|
||||
*
|
||||
* @param cveDB the data store object
|
||||
* @param filePair the download task that contains the URL references to
|
||||
* download
|
||||
*/
|
||||
public ProcessTask(final CveDB cveDB, final CallableDownloadTask filePair) {
|
||||
this.cveDB = cveDB;
|
||||
this.filePair = filePair;
|
||||
this.properties = cveDB.getDatabaseProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the callable interface.
|
||||
*
|
||||
* @return this object
|
||||
* @throws Exception thrown if there is an exception; note that any
|
||||
* UpdateExceptions are simply added to the tasks exception collection
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the NVD CVE XML file and imports the data into the DB.
|
||||
*
|
||||
* @throws UpdateException thrown if there is an error loading the data into
|
||||
* the database
|
||||
*/
|
||||
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,12 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.update.task</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* <p>A collection of callable/runnable tasks used to speed up the update
|
||||
* process.</p>
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update.task;
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
package org.owasp.dependencycheck.data.update.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -35,7 +35,7 @@ import org.xml.sax.helpers.DefaultHandler;
|
||||
* schema and is useful to ensure accurate identification (or at least
|
||||
* complete).
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class NvdCve12Handler extends DefaultHandler {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
package org.owasp.dependencycheck.data.update.xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -24,7 +24,8 @@ 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.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
@@ -36,7 +37,7 @@ import org.xml.sax.helpers.DefaultHandler;
|
||||
/**
|
||||
* A SAX Handler that will parse the NVD CVE XML (schema version 2.0).
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class NvdCve20Handler extends DefaultHandler {
|
||||
|
||||
@@ -208,6 +209,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 +264,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">
|
||||
/**
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* <html>
|
||||
* <head>
|
||||
* <title>org.owasp.dependencycheck.data.nvdcve.xml</title>
|
||||
* </head>
|
||||
* <body>
|
||||
* <p>Contains classes used to parse the NVD CVE XML file.</p>
|
||||
* <p>The basic use is that the Importer is called to import an NVD CVE file.
|
||||
* The Importer instantiates an Indexer object (which extends Index). The
|
||||
* Indexer creates a partial-unmarshalling SAX parser (implemented in the
|
||||
* NvdCveXmlFilter) that extracts VulnerabilityTypes (aka Entry) from the NVD
|
||||
* CVE data file and stores these into a Lucene Index.</p>
|
||||
* </body>
|
||||
* </html>
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update.xml;
|
||||
@@ -37,7 +37,7 @@ import org.owasp.dependencycheck.utils.FileUtils;
|
||||
* the form of evidence. The Evidence is then used to determine if there are any
|
||||
* known, published, vulnerabilities associated with the program dependency.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class Dependency implements Comparable<Dependency> {
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ package org.owasp.dependencycheck.dependency;
|
||||
/**
|
||||
* Evidence is a piece of information about a Dependency.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class Evidence implements Comparable<Evidence> {
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
|
||||
/**
|
||||
* Used to maintain a collection of Evidence.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class EvidenceCollection implements Iterable<Evidence> {
|
||||
|
||||
@@ -89,7 +89,7 @@ public class EvidenceCollection implements Iterable<Evidence> {
|
||||
*
|
||||
* @param confidence the confidence level for the evidence to be iterated
|
||||
* over.
|
||||
* @return Iterable<Evidence> an iterable collectoin of evidence
|
||||
* @return Iterable<Evidence> an iterable collection of evidence
|
||||
*/
|
||||
public final Iterable<Evidence> iterator(Evidence.Confidence confidence) {
|
||||
if (confidence == Evidence.Confidence.HIGHEST) {
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.dependency;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class Identifier implements Comparable<Identifier> {
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.io.Serializable;
|
||||
* An external reference for a vulnerability. This contains a name, URL, and a
|
||||
* source.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class Reference implements Serializable, Comparable<Reference> {
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.TreeSet;
|
||||
/**
|
||||
* Contains the information about a vulnerability.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class Vulnerability implements Serializable, Comparable<Vulnerability> {
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.util.Comparator;
|
||||
/**
|
||||
* Comparator for Vulnerability objects.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class VulnerabilityComparator implements Comparator<Vulnerability>, Serializable {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.owasp.dependencycheck.data.cpe.IndexEntry;
|
||||
* A record containing information about vulnerable software. This is referenced
|
||||
* from a vulnerability.
|
||||
*
|
||||
* @author Jeremy Long (jeremy.long@owasp.org)
|
||||
* @author Jeremy Long <jeremy.long@owasp.org>
|
||||
*/
|
||||
public class VulnerableSoftware extends IndexEntry implements Serializable, Comparable<VulnerableSoftware> {
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user