mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-01-14 15:53:36 +01:00
Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5574f1c24f | ||
|
|
9457744571 | ||
|
|
19243c479c | ||
|
|
e868ce8328 | ||
|
|
ffa846c05a | ||
|
|
dde1791476 | ||
|
|
45438a7f06 | ||
|
|
c980e77ea3 | ||
|
|
176d3ddefa | ||
|
|
98d783d448 | ||
|
|
bcd6634d8a | ||
|
|
0b260cef2a | ||
|
|
6a68abbd67 | ||
|
|
9fcf23c802 | ||
|
|
5c2c08e051 | ||
|
|
1f254997e1 | ||
|
|
4f95af0864 | ||
|
|
6ff39be9d2 | ||
|
|
6cf5a47971 | ||
|
|
56da53c700 | ||
|
|
7091e10795 | ||
|
|
34765c5741 | ||
|
|
36c139872a | ||
|
|
1e77cec677 | ||
|
|
e95e3fb2d0 | ||
|
|
39c2234e38 | ||
|
|
f4fff5d9cb | ||
|
|
659785f972 | ||
|
|
85c04f6e3e | ||
|
|
bef117cbe8 | ||
|
|
46dd7cf86e | ||
|
|
9ed5a97267 | ||
|
|
cc2da70db2 | ||
|
|
cedd93e774 | ||
|
|
632e1692eb | ||
|
|
4861592d2a | ||
|
|
22e6d4edf3 | ||
|
|
e9bd7ff72f | ||
|
|
e7228fb489 | ||
|
|
96c03a68f2 | ||
|
|
4f6f248421 | ||
|
|
a8f14c86fd | ||
|
|
36de3d1e25 | ||
|
|
48bc4570e1 | ||
|
|
94b272dbae | ||
|
|
c093edf459 | ||
|
|
0164feffcc | ||
|
|
8cd377b99f | ||
|
|
74282c8ac5 | ||
|
|
d2158e5e44 | ||
|
|
9ea16ad1d1 | ||
|
|
45941adb71 | ||
|
|
c4d662fd2b | ||
|
|
d9ce3cda66 | ||
|
|
6bd7d6b078 | ||
|
|
84c6dd5dfa | ||
|
|
71e7412f15 | ||
|
|
d22c920b35 | ||
|
|
f7a0982ca0 | ||
|
|
bed04150e1 | ||
|
|
ba15de2218 | ||
|
|
e9ec89dc9c | ||
|
|
d09f75658c | ||
|
|
62f92db181 | ||
|
|
27a98f4244 | ||
|
|
f0a3482eda | ||
|
|
5f76843c4a | ||
|
|
c6ea92cff9 | ||
|
|
c253308284 | ||
|
|
9ae9c111e3 | ||
|
|
4894372eee | ||
|
|
7cf040653f | ||
|
|
034bd4dba0 | ||
|
|
af12a2161c | ||
|
|
57fcf6fde3 | ||
|
|
c5757dc5f4 | ||
|
|
6d5d5ceb7b | ||
|
|
2fa8507d69 | ||
|
|
f23003ead3 | ||
|
|
c996f6b436 | ||
|
|
d2ee66a1c4 | ||
|
|
26b0dd5ef5 | ||
|
|
ad4149a259 | ||
|
|
9611c3b478 | ||
|
|
cead88d221 | ||
|
|
c1e1a6bb4f | ||
|
|
6212a5f740 | ||
|
|
b3d9ea3c47 | ||
|
|
cd51989354 | ||
|
|
b705ae5f0c | ||
|
|
13b53537fa | ||
|
|
7d05aa6073 | ||
|
|
85de173086 | ||
|
|
d264d804c8 | ||
|
|
8272da615e | ||
|
|
857b993d51 | ||
|
|
a71edf584e | ||
|
|
461d7fec0e | ||
|
|
5e3da035dd | ||
|
|
ebb52995a5 | ||
|
|
519b82c620 | ||
|
|
84682d07c6 | ||
|
|
960eeb19af | ||
|
|
ab3920f8f1 | ||
|
|
f5f5857897 | ||
|
|
1c400b410e | ||
|
|
cc751aa224 | ||
|
|
c20892ee3e | ||
|
|
32ab53c9e1 | ||
|
|
d0a7d9eb42 | ||
|
|
a1a9602509 | ||
|
|
cf97c89fe0 | ||
|
|
8895bc85ea | ||
|
|
1a9976c6ca | ||
|
|
f47ebf6145 | ||
|
|
0380715311 | ||
|
|
80ad16c7fa | ||
|
|
e56e9035b6 | ||
|
|
73f22d32d2 | ||
|
|
c3bc56eebc | ||
|
|
35cc14815e | ||
|
|
9be91474f6 | ||
|
|
adf949bf08 | ||
|
|
c6bf41b8ba | ||
|
|
00d4ee47de | ||
|
|
413c71eb0a | ||
|
|
2b761279e4 | ||
|
|
1e7bbfa7c1 | ||
|
|
dc7245ff6e | ||
|
|
ffaf7b40e9 | ||
|
|
99355d993a | ||
|
|
d25f6e813c | ||
|
|
043f8e0523 | ||
|
|
5fcf2a2623 | ||
|
|
f1422adf75 | ||
|
|
c2b1742582 |
18
.github/issue_template.md
vendored
Normal file
18
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
### Reporting Bugs/Errors
|
||||
When reporting errors, 99% of the time log file output is required. Please post the log file as a [gist](https://gist.github.com/) and provide a link in the new issue.
|
||||
|
||||
### Reporting False Positives
|
||||
When reporting a false positive please include:
|
||||
- The location of the dependency (Maven GAV, URL to download the dependency, etc.)
|
||||
- The CPE that is believed to be false positive
|
||||
- Please report the CPE not the CVE
|
||||
|
||||
#### Example
|
||||
False positive on library foo.jar - reported as cpe:/a:apache:tomcat:7.0
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.sample</groupId>
|
||||
<artifactId>foo</artifactId>
|
||||
<version>1.0</version>
|
||||
</dependency>
|
||||
```
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,4 +26,5 @@ _site/**
|
||||
.LCKpom.xml~
|
||||
#coverity
|
||||
/cov-int/
|
||||
/dependency-check-core/nbproject/
|
||||
/dependency-check-core/nbproject/
|
||||
cov-scan.bat
|
||||
@@ -1,4 +1,5 @@
|
||||
[](https://travis-ci.org/jeremylong/DependencyCheck) [](https://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
[](https://travis-ci.org/jeremylong/DependencyCheck) [](https://www.apache.org/licenses/LICENSE-2.0.txt) [](https://scan.coverity.com/projects/dependencycheck)
|
||||
|
||||
Dependency-Check
|
||||
================
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.4.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-ant</artifactId>
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.owasp.dependencycheck.taskdefs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
@@ -32,9 +31,12 @@ import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.exception.ReportException;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
@@ -806,52 +808,67 @@ public class Check extends Update {
|
||||
engine = new Engine(Check.class.getClassLoader());
|
||||
if (isUpdateOnly()) {
|
||||
log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN);
|
||||
engine.doUpdates();
|
||||
} else {
|
||||
try {
|
||||
for (Resource resource : path) {
|
||||
final FileProvider provider = resource.as(FileProvider.class);
|
||||
if (provider != null) {
|
||||
final File file = provider.getFile();
|
||||
if (file != null && file.exists()) {
|
||||
engine.scan(file);
|
||||
}
|
||||
engine.doUpdates();
|
||||
} catch (UpdateException ex) {
|
||||
if (this.isFailOnError()) {
|
||||
throw new BuildException(ex);
|
||||
}
|
||||
log(ex.getMessage(), Project.MSG_ERR);
|
||||
}
|
||||
} else {
|
||||
for (Resource resource : path) {
|
||||
final FileProvider provider = resource.as(FileProvider.class);
|
||||
if (provider != null) {
|
||||
final File file = provider.getFile();
|
||||
if (file != null && file.exists()) {
|
||||
engine.scan(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
engine.analyzeDependencies();
|
||||
DatabaseProperties prop = null;
|
||||
CveDB cve = null;
|
||||
try {
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
prop = cve.getDatabaseProperties();
|
||||
} catch (DatabaseException ex) {
|
||||
log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
|
||||
} finally {
|
||||
if (cve != null) {
|
||||
cve.close();
|
||||
}
|
||||
} catch (ExceptionCollection ex) {
|
||||
if (this.isFailOnError()) {
|
||||
throw new BuildException(ex);
|
||||
}
|
||||
final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop);
|
||||
reporter.generateReports(reportOutputDirectory, reportFormat);
|
||||
}
|
||||
DatabaseProperties prop = null;
|
||||
CveDB cve = null;
|
||||
try {
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
prop = cve.getDatabaseProperties();
|
||||
} catch (DatabaseException ex) {
|
||||
log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
|
||||
} finally {
|
||||
if (cve != null) {
|
||||
cve.close();
|
||||
}
|
||||
}
|
||||
final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop);
|
||||
reporter.generateReports(reportOutputDirectory, reportFormat);
|
||||
|
||||
if (this.failBuildOnCVSS <= 10) {
|
||||
checkForFailure(engine.getDependencies());
|
||||
}
|
||||
if (this.showSummary) {
|
||||
showSummary(engine.getDependencies());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log("Unable to generate dependency-check report", ex, Project.MSG_DEBUG);
|
||||
throw new BuildException("Unable to generate dependency-check report", ex);
|
||||
} catch (Exception ex) {
|
||||
log("An exception occurred; unable to continue task", ex, Project.MSG_DEBUG);
|
||||
throw new BuildException("An exception occurred; unable to continue task", ex);
|
||||
if (this.failBuildOnCVSS <= 10) {
|
||||
checkForFailure(engine.getDependencies());
|
||||
}
|
||||
if (this.showSummary) {
|
||||
showSummary(engine.getDependencies());
|
||||
}
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
log("Unable to connect to the dependency-check database; analysis has stopped", ex, Project.MSG_ERR);
|
||||
final String msg = "Unable to connect to the dependency-check database; analysis has stopped";
|
||||
if (this.isFailOnError()) {
|
||||
throw new BuildException(msg, ex);
|
||||
}
|
||||
log(msg, ex, Project.MSG_ERR);
|
||||
} catch (ReportException ex) {
|
||||
final String msg = "Unable to generate the dependency-check report";
|
||||
if (this.isFailOnError()) {
|
||||
throw new BuildException(msg, ex);
|
||||
}
|
||||
log(msg, ex, Project.MSG_ERR);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
if (engine != null) {
|
||||
|
||||
@@ -71,6 +71,30 @@ public class Purge extends Task {
|
||||
this.dataDirectory = dataDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if dependency-check should fail the build if an exception
|
||||
* occurs.
|
||||
*/
|
||||
private boolean failOnError = true;
|
||||
|
||||
/**
|
||||
* Get the value of failOnError.
|
||||
*
|
||||
* @return the value of failOnError
|
||||
*/
|
||||
public boolean isFailOnError() {
|
||||
return failOnError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of failOnError.
|
||||
*
|
||||
* @param failOnError new value of failOnError
|
||||
*/
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws BuildException {
|
||||
populateSettings();
|
||||
@@ -81,30 +105,49 @@ public class Purge extends Task {
|
||||
if (db.delete()) {
|
||||
log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO);
|
||||
} else {
|
||||
log(String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath()), Project.MSG_ERR);
|
||||
final String msg = String.format("Unable to delete '%s'; please delete the file manually", db.getAbsolutePath());
|
||||
if (this.failOnError) {
|
||||
throw new BuildException(msg);
|
||||
}
|
||||
log(msg, Project.MSG_ERR);
|
||||
}
|
||||
} else {
|
||||
log(String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath()), Project.MSG_ERR);
|
||||
final String msg = String.format("Unable to purge database; the database file does not exists: %s", db.getAbsolutePath());
|
||||
if (this.failOnError) {
|
||||
throw new BuildException(msg);
|
||||
}
|
||||
log(msg, Project.MSG_ERR);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log("Unable to delete the database", Project.MSG_ERR);
|
||||
final String msg = "Unable to delete the database";
|
||||
if (this.failOnError) {
|
||||
throw new BuildException(msg);
|
||||
}
|
||||
log(msg, Project.MSG_ERR);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties
|
||||
* required to change the proxy server, port, and connection timeout.
|
||||
* Takes the properties supplied and updates the dependency-check settings.
|
||||
* Additionally, this sets the system properties required to change the
|
||||
* proxy server, port, and connection timeout.
|
||||
*
|
||||
* @throws BuildException thrown if the properties file cannot be read.
|
||||
*/
|
||||
protected void populateSettings() {
|
||||
protected void populateSettings() throws BuildException {
|
||||
Settings.initialize();
|
||||
InputStream taskProperties = null;
|
||||
try {
|
||||
taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
|
||||
Settings.mergeProperties(taskProperties);
|
||||
} catch (IOException ex) {
|
||||
log("Unable to load the dependency-check ant task.properties file.", ex, Project.MSG_WARN);
|
||||
final String msg = "Unable to load the dependency-check ant task.properties file.";
|
||||
if (this.failOnError) {
|
||||
throw new BuildException(msg, ex);
|
||||
}
|
||||
log(msg, ex, Project.MSG_WARN);
|
||||
} finally {
|
||||
if (taskProperties != null) {
|
||||
try {
|
||||
|
||||
@@ -18,14 +18,17 @@
|
||||
package org.owasp.dependencycheck.taskdefs;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
|
||||
/**
|
||||
* An Ant task definition to execute dependency-check update. This will download the latest data from the National Vulnerability
|
||||
* Database (NVD) and store a copy in the local database.
|
||||
* An Ant task definition to execute dependency-check update. This will download
|
||||
* the latest data from the National Vulnerability Database (NVD) and store a
|
||||
* copy in the local database.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -381,10 +384,11 @@ public class Update extends Purge {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the update by initializing the settings, downloads the NVD XML data, and then processes the data storing it in the
|
||||
* local database.
|
||||
* Executes the update by initializing the settings, downloads the NVD XML
|
||||
* data, and then processes the data storing it in the local database.
|
||||
*
|
||||
* @throws BuildException thrown if a connection to the local database cannot be made.
|
||||
* @throws BuildException thrown if a connection to the local database
|
||||
* cannot be made.
|
||||
*/
|
||||
@Override
|
||||
public void execute() throws BuildException {
|
||||
@@ -392,9 +396,20 @@ public class Update extends Purge {
|
||||
Engine engine = null;
|
||||
try {
|
||||
engine = new Engine(Update.class.getClassLoader());
|
||||
engine.doUpdates();
|
||||
try {
|
||||
engine.doUpdates();
|
||||
} catch (UpdateException ex) {
|
||||
if (this.isFailOnError()) {
|
||||
throw new BuildException(ex);
|
||||
}
|
||||
log(ex.getMessage(), Project.MSG_ERR);
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
throw new BuildException("Unable to connect to the dependency-check database; unable to update the NVD data", ex);
|
||||
final String msg = "Unable to connect to the dependency-check database; unable to update the NVD data";
|
||||
if (this.isFailOnError()) {
|
||||
throw new BuildException(msg, ex);
|
||||
}
|
||||
log(msg, Project.MSG_ERR);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
if (engine != null) {
|
||||
@@ -404,8 +419,9 @@ public class Update extends Purge {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties
|
||||
* required to change the proxy server, port, and connection timeout.
|
||||
* Takes the properties supplied and updates the dependency-check settings.
|
||||
* Additionally, this sets the system properties required to change the
|
||||
* proxy server, port, and connection timeout.
|
||||
*
|
||||
* @throws BuildException thrown when an invalid setting is configured.
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,7 @@ Configuration: dependency-check-purge Task
|
||||
--------------------
|
||||
The following properties can be set on the dependency-check-purge task.
|
||||
|
||||
Property | Description | Default Value
|
||||
----------------------|----------------------------------------------------------------|------------------
|
||||
dataDirectory | Data directory that is used to store the local copy of the NVD | data
|
||||
Property | Description | Default Value
|
||||
----------------------|------------------------------------------------------------------------|------------------
|
||||
dataDirectory | Data directory that is used to store the local copy of the NVD | data
|
||||
failOnError | Whether the build should fail if there is an error executing the purge | true
|
||||
|
||||
@@ -24,6 +24,7 @@ proxyPort | The Proxy Port. |
|
||||
proxyUsername | Defines the proxy user name. |
|
||||
proxyPassword | Defines the proxy password. |
|
||||
connectionTimeout | The URL Connection Timeout. |
|
||||
failOnError | Whether the build should fail if there is an error executing the update | true
|
||||
|
||||
Advanced Configuration
|
||||
====================
|
||||
|
||||
@@ -34,6 +34,7 @@ Property | Description
|
||||
autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true
|
||||
cveValidForHours | Sets the number of hours to wait before checking for new updates from the NVD | 4
|
||||
failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which means since the CVSS scores are 0-10, by default the build will never fail. | 11
|
||||
failOnError | Whether the build should fail if there is an error executing the dependency-check analysis | true
|
||||
projectName | The name of the project being scanned. | Dependency-Check
|
||||
reportFormat | The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the Site plugin unless the externalReport is set to true. | HTML
|
||||
reportOutputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target'
|
||||
|
||||
@@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.4.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-cli</artifactId>
|
||||
|
||||
@@ -37,6 +37,10 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ch.qos.logback.core.FileAppender;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.exception.ReportException;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
|
||||
/**
|
||||
@@ -57,21 +61,26 @@ public class App {
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
int exitCode = 0;
|
||||
try {
|
||||
Settings.initialize();
|
||||
final App app = new App();
|
||||
app.run(args);
|
||||
exitCode = app.run(args);
|
||||
LOGGER.debug("Exit code: " + exitCode);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
}
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main CLI entry-point into the application.
|
||||
*
|
||||
* @param args the command line arguments
|
||||
* @return the exit code to return
|
||||
*/
|
||||
public void run(String[] args) {
|
||||
public int run(String[] args) {
|
||||
int exitCode = 0;
|
||||
final CliParser cli = new CliParser();
|
||||
|
||||
try {
|
||||
@@ -79,11 +88,11 @@ public class App {
|
||||
} catch (FileNotFoundException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
cli.printHelp();
|
||||
return;
|
||||
return -1;
|
||||
} catch (ParseException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
cli.printHelp();
|
||||
return;
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (cli.getVerboseLog() != null) {
|
||||
@@ -93,8 +102,15 @@ public class App {
|
||||
if (cli.isPurge()) {
|
||||
if (cli.getConnectionString() != null) {
|
||||
LOGGER.error("Unable to purge the database when using a non-default connection string");
|
||||
exitCode = -3;
|
||||
} else {
|
||||
populateSettings(cli);
|
||||
try {
|
||||
populateSettings(cli);
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
LOGGER.debug("Error loading properties file", ex);
|
||||
exitCode = -4;
|
||||
}
|
||||
File db;
|
||||
try {
|
||||
db = new File(Settings.getDataDirectory(), "dc.h2.db");
|
||||
@@ -103,46 +119,101 @@ public class App {
|
||||
LOGGER.info("Database file purged; local copy of the NVD has been removed");
|
||||
} else {
|
||||
LOGGER.error("Unable to delete '{}'; please delete the file manually", db.getAbsolutePath());
|
||||
exitCode = -5;
|
||||
}
|
||||
} else {
|
||||
LOGGER.error("Unable to purge database; the database file does not exists: {}", db.getAbsolutePath());
|
||||
exitCode = -6;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error("Unable to delete the database");
|
||||
exitCode = -7;
|
||||
}
|
||||
}
|
||||
} else if (cli.isGetVersion()) {
|
||||
cli.printVersionInfo();
|
||||
} else if (cli.isUpdateOnly()) {
|
||||
populateSettings(cli);
|
||||
runUpdateOnly();
|
||||
} else if (cli.isRunScan()) {
|
||||
populateSettings(cli);
|
||||
try {
|
||||
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), cli.getScanFiles(),
|
||||
cli.getExcludeList(), cli.getSymLinkDepth());
|
||||
populateSettings(cli);
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
LOGGER.debug("Error loading properties file", ex);
|
||||
exitCode = -4;
|
||||
}
|
||||
try {
|
||||
runUpdateOnly();
|
||||
} catch (UpdateException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
exitCode = -8;
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
exitCode = -9;
|
||||
}
|
||||
} else if (cli.isRunScan()) {
|
||||
try {
|
||||
populateSettings(cli);
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
LOGGER.debug("Error loading properties file", ex);
|
||||
exitCode = -4;
|
||||
}
|
||||
try {
|
||||
final String[] scanFiles = cli.getScanFiles();
|
||||
if (scanFiles != null) {
|
||||
runScan(cli.getReportDirectory(), cli.getReportFormat(), cli.getProjectName(), scanFiles,
|
||||
cli.getExcludeList(), cli.getSymLinkDepth());
|
||||
} else {
|
||||
LOGGER.error("No scan files configured");
|
||||
}
|
||||
} catch (InvalidScanPathException ex) {
|
||||
LOGGER.error("An invalid scan path was detected; unable to scan '//*' paths");
|
||||
exitCode = -10;
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
exitCode = -11;
|
||||
} catch (ReportException ex) {
|
||||
LOGGER.error(ex.getMessage());
|
||||
exitCode = -12;
|
||||
} catch (ExceptionCollection ex) {
|
||||
if (ex.isFatal()) {
|
||||
exitCode = -13;
|
||||
LOGGER.error("One or more fatal errors occured");
|
||||
} else {
|
||||
exitCode = -14;
|
||||
}
|
||||
for (Throwable e : ex.getExceptions()) {
|
||||
LOGGER.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cli.printHelp();
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the specified directories and writes the dependency reports to the reportDirectory.
|
||||
* Scans the specified directories and writes the dependency reports to the
|
||||
* reportDirectory.
|
||||
*
|
||||
* @param reportDirectory the path to the directory where the reports will be written
|
||||
* @param reportDirectory the path to the directory where the reports will
|
||||
* be written
|
||||
* @param outputFormat the output format of the report
|
||||
* @param applicationName the application name for the report
|
||||
* @param files the files/directories to scan
|
||||
* @param excludes the patterns for files/directories to exclude
|
||||
* @param symLinkDepth the depth that symbolic links will be followed
|
||||
*
|
||||
* @throws InvalidScanPathException thrown if the path to scan starts with "//"
|
||||
* @throws InvalidScanPathException thrown if the path to scan starts with
|
||||
* "//"
|
||||
* @throws ReportException thrown when the report cannot be generated
|
||||
* @throws DatabaseException thrown when there is an error connecting to the
|
||||
* database
|
||||
* @throws ExceptionCollection thrown when an exception occurs during
|
||||
* analysis; there may be multiple exceptions contained within the
|
||||
* collection.
|
||||
*/
|
||||
private void runScan(String reportDirectory, String outputFormat, String applicationName, String[] files,
|
||||
String[] excludes, int symLinkDepth) throws InvalidScanPathException {
|
||||
String[] excludes, int symLinkDepth) throws InvalidScanPathException, DatabaseException, ExceptionCollection, ReportException {
|
||||
Engine engine = null;
|
||||
try {
|
||||
engine = new Engine();
|
||||
@@ -174,8 +245,6 @@ public class App {
|
||||
include = "**/*";
|
||||
}
|
||||
}
|
||||
//LOGGER.debug("baseDir: {}", baseDir);
|
||||
//LOGGER.debug("include: {}", include);
|
||||
scanner.setBasedir(baseDir);
|
||||
final String[] includes = {include};
|
||||
scanner.setIncludes(includes);
|
||||
@@ -197,7 +266,15 @@ public class App {
|
||||
}
|
||||
engine.scan(paths);
|
||||
|
||||
engine.analyzeDependencies();
|
||||
ExceptionCollection exCol = null;
|
||||
try {
|
||||
engine.analyzeDependencies();
|
||||
} catch (ExceptionCollection ex) {
|
||||
if (ex.isFatal()) {
|
||||
throw ex;
|
||||
}
|
||||
exCol = ex;
|
||||
}
|
||||
final List<Dependency> dependencies = engine.getDependencies();
|
||||
DatabaseProperties prop = null;
|
||||
CveDB cve = null;
|
||||
@@ -205,8 +282,6 @@ public class App {
|
||||
cve = new CveDB();
|
||||
cve.open();
|
||||
prop = cve.getDatabaseProperties();
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("Unable to retrieve DB Properties", ex);
|
||||
} finally {
|
||||
if (cve != null) {
|
||||
cve.close();
|
||||
@@ -215,34 +290,37 @@ public class App {
|
||||
final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop);
|
||||
try {
|
||||
report.generateReports(reportDirectory, outputFormat);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error("There was an IO error while attempting to generate the report.");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.error("There was an error while attempting to generate the report.");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (ReportException ex) {
|
||||
if (exCol != null) {
|
||||
exCol.addException(ex);
|
||||
throw exCol;
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
if (exCol != null && exCol.getExceptions().size() > 0) {
|
||||
throw exCol;
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
|
||||
LOGGER.debug("", ex);
|
||||
} finally {
|
||||
if (engine != null) {
|
||||
engine.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Only executes the update phase of dependency-check.
|
||||
*
|
||||
* @throws UpdateException thrown if there is an error updating
|
||||
* @throws DatabaseException thrown if a fatal error occurred and a
|
||||
* connection to the database could not be established
|
||||
*/
|
||||
private void runUpdateOnly() {
|
||||
private void runUpdateOnly() throws UpdateException, DatabaseException {
|
||||
Engine engine = null;
|
||||
try {
|
||||
engine = new Engine();
|
||||
engine.doUpdates();
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error("Unable to connect to the dependency-check database; analysis has stopped");
|
||||
LOGGER.debug("", ex);
|
||||
} finally {
|
||||
if (engine != null) {
|
||||
engine.cleanup();
|
||||
@@ -253,11 +331,13 @@ public class App {
|
||||
/**
|
||||
* Updates the global Settings.
|
||||
*
|
||||
* @param cli a reference to the CLI Parser that contains the command line arguments used to set the corresponding settings in
|
||||
* the core engine.
|
||||
* @param cli a reference to the CLI Parser that contains the command line
|
||||
* arguments used to set the corresponding settings in the core engine.
|
||||
*
|
||||
* @throws InvalidSettingException thrown when a user defined properties
|
||||
* file is unable to be loaded.
|
||||
*/
|
||||
private void populateSettings(CliParser cli) {
|
||||
|
||||
private void populateSettings(CliParser cli) throws InvalidSettingException {
|
||||
final boolean autoUpdate = cli.isAutoUpdate();
|
||||
final String connectionTimeout = cli.getConnectionTimeout();
|
||||
final String proxyServer = cli.getProxyServer();
|
||||
@@ -286,11 +366,9 @@ public class App {
|
||||
try {
|
||||
Settings.mergeProperties(propertiesFile);
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.error("Unable to load properties file '{}'", propertiesFile.getPath());
|
||||
LOGGER.debug("", ex);
|
||||
throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.error("Unable to find properties file '{}'", propertiesFile.getPath());
|
||||
LOGGER.debug("", ex);
|
||||
throw new InvalidSettingException("Error reading properties file '" + propertiesFile.getPath() + "'", ex);
|
||||
}
|
||||
}
|
||||
// We have to wait until we've merged the properties before attempting to set whether we use
|
||||
@@ -385,15 +463,16 @@ public class App {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a path and resolves it to be a canonical & absolute path. The caveats are that this method will take an Ant style
|
||||
* file selector path (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at least to the left of the first *
|
||||
* or ?).
|
||||
* Takes a path and resolves it to be a canonical & absolute path. The
|
||||
* caveats are that this method will take an Ant style file selector path
|
||||
* (../someDir/**\/*.jar) and convert it to an absolute/canonical path (at
|
||||
* least to the left of the first * or ?).
|
||||
*
|
||||
* @param path the path to canonicalize
|
||||
* @return the canonical path
|
||||
*/
|
||||
protected String ensureCanonicalPath(String path) {
|
||||
String basePath = null;
|
||||
String basePath;
|
||||
String wildCards = null;
|
||||
final String file = path.replace('\\', '/');
|
||||
if (file.contains("*") || file.contains("?")) {
|
||||
|
||||
@@ -196,6 +196,10 @@ public final class CliParser {
|
||||
isValid = false;
|
||||
final String msg = String.format("Invalid '%s' argument: '%s'%nUnable to scan paths that start with '//'.", argumentName, path);
|
||||
throw new FileNotFoundException(msg);
|
||||
} else if ((path.endsWith("/*") && !path.endsWith("**/*")) || (path.endsWith("\\*") && path.endsWith("**\\*"))) {
|
||||
final String msg = String.format("Possibly incorrect path '%s' from argument '%s' because it ends with a slash star; "
|
||||
+ "dependency-check uses ant-style paths", path, argumentName);
|
||||
LOGGER.warn(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -966,7 +970,7 @@ public final class CliParser {
|
||||
*/
|
||||
public void printVersionInfo() {
|
||||
final String version = String.format("%s version %s",
|
||||
Settings.getString(Settings.KEYS.APPLICATION_VAME, "dependency-check"),
|
||||
Settings.getString(Settings.KEYS.APPLICATION_NAME, "dependency-check"),
|
||||
Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown"));
|
||||
System.out.println(version);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<parent>
|
||||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-parent</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>1.4.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dependency-check-core</artifactId>
|
||||
@@ -459,6 +459,13 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.8</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
@@ -587,13 +594,6 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>1.4.2</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ws.security</groupId>
|
||||
<artifactId>wss4j</artifactId>
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.owasp.dependencycheck.data.update.UpdateService;
|
||||
import org.owasp.dependencycheck.data.update.exception.UpdateException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.NoDataException;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
@@ -47,8 +49,10 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and used to process the files found by the scan, if a
|
||||
* file is encountered and an Analyzer is associated with the file type then the file is turned into a dependency.
|
||||
* Scans files, directories, etc. for Dependencies. Analyzers are loaded and
|
||||
* used to process the files found by the scan, if a file is encountered and an
|
||||
* Analyzer is associated with the file type then the file is turned into a
|
||||
* dependency.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -61,15 +65,16 @@ public class Engine implements FileFilter {
|
||||
/**
|
||||
* A Map of analyzers grouped by Analysis phase.
|
||||
*/
|
||||
private Map<AnalysisPhase, List<Analyzer>> analyzers = new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class);
|
||||
private final Map<AnalysisPhase, List<Analyzer>> analyzers = new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class);
|
||||
|
||||
/**
|
||||
* A Map of analyzers grouped by Analysis phase.
|
||||
*/
|
||||
private Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<FileTypeAnalyzer>();
|
||||
private final Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<FileTypeAnalyzer>();
|
||||
|
||||
/**
|
||||
* The ClassLoader to use when dynamically loading Analyzer and Update services.
|
||||
* The ClassLoader to use when dynamically loading Analyzer and Update
|
||||
* services.
|
||||
*/
|
||||
private ClassLoader serviceClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
/**
|
||||
@@ -80,7 +85,8 @@ public class Engine implements FileFilter {
|
||||
/**
|
||||
* Creates a new Engine.
|
||||
*
|
||||
* @throws DatabaseException thrown if there is an error connecting to the database
|
||||
* @throws DatabaseException thrown if there is an error connecting to the
|
||||
* database
|
||||
*/
|
||||
public Engine() throws DatabaseException {
|
||||
initializeEngine();
|
||||
@@ -90,7 +96,8 @@ public class Engine implements FileFilter {
|
||||
* Creates a new Engine.
|
||||
*
|
||||
* @param serviceClassLoader a reference the class loader being used
|
||||
* @throws DatabaseException thrown if there is an error connecting to the database
|
||||
* @throws DatabaseException thrown if there is an error connecting to the
|
||||
* database
|
||||
*/
|
||||
public Engine(ClassLoader serviceClassLoader) throws DatabaseException {
|
||||
this.serviceClassLoader = serviceClassLoader;
|
||||
@@ -98,9 +105,11 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Engine using the specified classloader to dynamically load Analyzer and Update services.
|
||||
* Creates a new Engine using the specified classloader to dynamically load
|
||||
* Analyzer and Update services.
|
||||
*
|
||||
* @throws DatabaseException thrown if there is an error connecting to the database
|
||||
* @throws DatabaseException thrown if there is an error connecting to the
|
||||
* database
|
||||
*/
|
||||
protected final void initializeEngine() throws DatabaseException {
|
||||
ConnectionFactory.initialize();
|
||||
@@ -115,7 +124,8 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the analyzers specified in the configuration file (or system properties).
|
||||
* Loads the analyzers specified in the configuration file (or system
|
||||
* properties).
|
||||
*/
|
||||
private void loadAnalyzers() {
|
||||
if (!analyzers.isEmpty()) {
|
||||
@@ -164,8 +174,9 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* will be scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param paths an array of paths to files or directories to be analyzed
|
||||
* @return the list of dependencies scanned
|
||||
@@ -183,8 +194,9 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified
|
||||
* are added to the dependency collection.
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param path the path to a file or directory to be analyzed
|
||||
* @return the list of dependencies scanned
|
||||
@@ -195,8 +207,9 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans an array of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
* Scans an array of files or directories. If a directory is specified, it
|
||||
* will be scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param files an array of paths to files or directories to be analyzed.
|
||||
* @return the list of dependencies
|
||||
@@ -214,8 +227,9 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a collection of files or directories. If a directory is specified, it will be scanned recursively. Any dependencies
|
||||
* identified are added to the dependency collection.
|
||||
* Scans a collection of files or directories. If a directory is specified,
|
||||
* it will be scanned recursively. Any dependencies identified are added to
|
||||
* the dependency collection.
|
||||
*
|
||||
* @param files a set of paths to files or directories to be analyzed
|
||||
* @return the list of dependencies scanned
|
||||
@@ -233,8 +247,9 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a given file or directory. If a directory is specified, it will be scanned recursively. Any dependencies identified
|
||||
* are added to the dependency collection.
|
||||
* Scans a given file or directory. If a directory is specified, it will be
|
||||
* scanned recursively. Any dependencies identified are added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param file the path to a file or directory to be analyzed
|
||||
* @return the list of dependencies scanned
|
||||
@@ -257,7 +272,8 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scans files and directories. Any dependencies identified are added to the dependency collection.
|
||||
* Recursively scans files and directories. Any dependencies identified are
|
||||
* added to the dependency collection.
|
||||
*
|
||||
* @param dir the directory to scan
|
||||
* @return the list of Dependency objects scanned
|
||||
@@ -282,7 +298,8 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a specified file. If a dependency is identified it is added to the dependency collection.
|
||||
* Scans a specified file. If a dependency is identified it is added to the
|
||||
* dependency collection.
|
||||
*
|
||||
* @param file The file to scan
|
||||
* @return the scanned dependency
|
||||
@@ -301,20 +318,38 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the analyzers against all of the dependencies. Since the mutable dependencies list is exposed via
|
||||
* {@link #getDependencies()}, this method iterates over a copy of the dependencies list. Thus, the potential for
|
||||
* {@link java.util.ConcurrentModificationException}s is avoided, and analyzers may safely add or remove entries from the
|
||||
* dependencies list.
|
||||
* Runs the analyzers against all of the dependencies. Since the mutable
|
||||
* dependencies list is exposed via {@link #getDependencies()}, this method
|
||||
* iterates over a copy of the dependencies list. Thus, the potential for
|
||||
* {@link java.util.ConcurrentModificationException}s is avoided, and
|
||||
* analyzers may safely add or remove entries from the dependencies list.
|
||||
*
|
||||
* Every effort is made to complete analysis on the dependencies. In some
|
||||
* cases an exception will occur with part of the analysis being performed
|
||||
* which may not affect the entire analysis. If an exception occurs it will
|
||||
* be included in the thrown exception collection.
|
||||
*
|
||||
* @throws ExceptionCollection a collections of any exceptions that occurred
|
||||
* during analysis
|
||||
*/
|
||||
public void analyzeDependencies() {
|
||||
public void analyzeDependencies() throws ExceptionCollection {
|
||||
final List<Throwable> exceptions = new ArrayList<Throwable>();
|
||||
boolean autoUpdate = true;
|
||||
try {
|
||||
autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
|
||||
} catch (InvalidSettingException ex) {
|
||||
LOGGER.debug("Invalid setting for auto-update; using true.");
|
||||
exceptions.add(ex);
|
||||
}
|
||||
if (autoUpdate) {
|
||||
doUpdates();
|
||||
try {
|
||||
doUpdates();
|
||||
} catch (UpdateException ex) {
|
||||
exceptions.add(ex);
|
||||
LOGGER.warn("Unable to update Cached Web DataSource, using local "
|
||||
+ "data instead. Results may not include recent vulnerabilities.");
|
||||
LOGGER.debug("Update Error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
//need to ensure that data exists
|
||||
@@ -323,16 +358,17 @@ public class Engine implements FileFilter {
|
||||
} catch (NoDataException ex) {
|
||||
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
return;
|
||||
exceptions.add(ex);
|
||||
throw new ExceptionCollection("Unable to continue dependency-check analysis.", exceptions, true);
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
return;
|
||||
|
||||
exceptions.add(ex);
|
||||
throw new ExceptionCollection("Unable to connect to the dependency-check database", exceptions, true);
|
||||
}
|
||||
|
||||
LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------");
|
||||
LOGGER.info("Analysis Starting");
|
||||
LOGGER.info("Analysis Started");
|
||||
final long analysisStart = System.currentTimeMillis();
|
||||
|
||||
// analysis phases
|
||||
@@ -340,7 +376,12 @@ public class Engine implements FileFilter {
|
||||
final List<Analyzer> analyzerList = analyzers.get(phase);
|
||||
|
||||
for (Analyzer a : analyzerList) {
|
||||
a = initializeAnalyzer(a);
|
||||
try {
|
||||
a = initializeAnalyzer(a);
|
||||
} catch (InitializationException ex) {
|
||||
exceptions.add(ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* need to create a copy of the collection because some of the
|
||||
* analyzers may modify it. This prevents ConcurrentModificationExceptions.
|
||||
@@ -361,10 +402,12 @@ public class Engine implements FileFilter {
|
||||
} catch (AnalysisException ex) {
|
||||
LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath());
|
||||
LOGGER.debug("", ex);
|
||||
exceptions.add(ex);
|
||||
} catch (Throwable ex) {
|
||||
//final AnalysisException ax = new AnalysisException(axMsg, ex);
|
||||
LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath());
|
||||
LOGGER.debug("", ex);
|
||||
exceptions.add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,6 +423,9 @@ public class Engine implements FileFilter {
|
||||
|
||||
LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------");
|
||||
LOGGER.info("Analysis Complete ({} ms)", System.currentTimeMillis() - analysisStart);
|
||||
if (exceptions.size() > 0) {
|
||||
throw new ExceptionCollection("One or more exceptions occured during dependency-check analysis", exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,12 +433,14 @@ public class Engine implements FileFilter {
|
||||
*
|
||||
* @param analyzer the analyzer to initialize
|
||||
* @return the initialized analyzer
|
||||
* @throws InitializationException thrown when there is a problem
|
||||
* initializing the analyzer
|
||||
*/
|
||||
protected Analyzer initializeAnalyzer(Analyzer analyzer) {
|
||||
protected Analyzer initializeAnalyzer(Analyzer analyzer) throws InitializationException {
|
||||
try {
|
||||
LOGGER.debug("Initializing {}", analyzer.getName());
|
||||
analyzer.initialize();
|
||||
} catch (Throwable ex) {
|
||||
} catch (InitializationException ex) {
|
||||
LOGGER.error("Exception occurred initializing {}.", analyzer.getName());
|
||||
LOGGER.debug("", ex);
|
||||
try {
|
||||
@@ -400,6 +448,16 @@ public class Engine implements FileFilter {
|
||||
} catch (Throwable ex1) {
|
||||
LOGGER.trace("", ex1);
|
||||
}
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
LOGGER.error("Unexpected exception occurred initializing {}.", analyzer.getName());
|
||||
LOGGER.debug("", ex);
|
||||
try {
|
||||
analyzer.close();
|
||||
} catch (Throwable ex1) {
|
||||
LOGGER.trace("", ex1);
|
||||
}
|
||||
throw new InitializationException("Unexpected Exception", ex);
|
||||
}
|
||||
return analyzer;
|
||||
}
|
||||
@@ -419,28 +477,26 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through the cached web data sources and calls update on all of them.
|
||||
* Cycles through the cached web data sources and calls update on all of
|
||||
* them.
|
||||
*
|
||||
* @throws UpdateException thrown if the operation fails
|
||||
*/
|
||||
public void doUpdates() {
|
||||
public void doUpdates() throws UpdateException {
|
||||
LOGGER.info("Checking for updates");
|
||||
final long updateStart = System.currentTimeMillis();
|
||||
final UpdateService service = new UpdateService(serviceClassLoader);
|
||||
final Iterator<CachedWebDataSource> iterator = service.getDataSources();
|
||||
while (iterator.hasNext()) {
|
||||
final CachedWebDataSource source = iterator.next();
|
||||
try {
|
||||
source.update();
|
||||
} catch (UpdateException ex) {
|
||||
LOGGER.warn(
|
||||
"Unable to update Cached Web DataSource, using local data instead. Results may not include recent vulnerabilities.");
|
||||
LOGGER.debug("Unable to update details for {}", source.getClass().getName(), ex);
|
||||
}
|
||||
source.update();
|
||||
}
|
||||
LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a full list of all of the analyzers. This is useful for reporting which analyzers where used.
|
||||
* Returns a full list of all of the analyzers. This is useful for reporting
|
||||
* which analyzers where used.
|
||||
*
|
||||
* @return a list of Analyzers
|
||||
*/
|
||||
@@ -457,7 +513,8 @@ public class Engine implements FileFilter {
|
||||
* Checks all analyzers to see if an extension is supported.
|
||||
*
|
||||
* @param file a file extension
|
||||
* @return true or false depending on whether or not the file extension is supported
|
||||
* @return true or false depending on whether or not the file extension is
|
||||
* supported
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
@@ -483,10 +540,12 @@ public class Engine implements FileFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the CPE Index to ensure documents exists. If none exist a NoDataException is thrown.
|
||||
* Checks the CPE Index to ensure documents exists. If none exist a
|
||||
* NoDataException is thrown.
|
||||
*
|
||||
* @throws NoDataException thrown if no data exists in the CPE Index
|
||||
* @throws DatabaseException thrown if there is an exception opening the database
|
||||
* @throws DatabaseException thrown if there is an exception opening the
|
||||
* database
|
||||
*/
|
||||
private void ensureDataExists() throws NoDataException, DatabaseException {
|
||||
final CveDB cve = new CveDB();
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.exception.ScanAgentException;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
@@ -34,10 +35,12 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class provides a way to easily conduct a scan solely based on existing evidence metadata rather than collecting evidence
|
||||
* from the files themselves. This class is based on the Ant task and Maven plugin with the exception that it takes a list of
|
||||
* dependencies that can be programmatically added from data in a spreadsheet, database or some other datasource and conduct a
|
||||
* scan based on this pre-defined evidence.
|
||||
* This class provides a way to easily conduct a scan solely based on existing
|
||||
* evidence metadata rather than collecting evidence from the files themselves.
|
||||
* This class is based on the Ant task and Maven plugin with the exception that
|
||||
* it takes a list of dependencies that can be programmatically added from data
|
||||
* in a spreadsheet, database or some other datasource and conduct a scan based
|
||||
* on this pre-defined evidence.
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <pre>
|
||||
@@ -138,7 +141,8 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the destination directory for the generated Dependency-Check report.
|
||||
* Specifies the destination directory for the generated Dependency-Check
|
||||
* report.
|
||||
*/
|
||||
private String reportOutputDirectory;
|
||||
|
||||
@@ -161,9 +165,11 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which
|
||||
* means since the CVSS scores are 0-10, by default the build will never fail and the CVSS score is set to 11. The valid range
|
||||
* for the fail build on CVSS is 0 to 11, where anything above 10 will not cause the build to fail.
|
||||
* Specifies if the build should be failed if a CVSS score above a specified
|
||||
* level is identified. The default is 11 which means since the CVSS scores
|
||||
* are 0-10, by default the build will never fail and the CVSS score is set
|
||||
* to 11. The valid range for the fail build on CVSS is 0 to 11, where
|
||||
* anything above 10 will not cause the build to fail.
|
||||
*/
|
||||
private float failBuildOnCVSS = 11;
|
||||
|
||||
@@ -186,8 +192,8 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. Default
|
||||
* is true.
|
||||
* Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not
|
||||
* recommended that this be turned to false. Default is true.
|
||||
*/
|
||||
private boolean autoUpdate = true;
|
||||
|
||||
@@ -233,8 +239,9 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the
|
||||
* Site plugin unless the externalReport is set to true. Default is HTML.
|
||||
* The report format to be generated (HTML, XML, VULN, ALL). This
|
||||
* configuration option has no affect if using this within the Site plugin
|
||||
* unless the externalReport is set to true. Default is HTML.
|
||||
*/
|
||||
private ReportGenerator.Format reportFormat = ReportGenerator.Format.HTML;
|
||||
|
||||
@@ -283,7 +290,9 @@ public class DependencyCheckScanAgent {
|
||||
* Get the value of proxyServer.
|
||||
*
|
||||
* @return the value of proxyServer
|
||||
* @deprecated use {@link org.owasp.dependencycheck.agent.DependencyCheckScanAgent#getProxyServer()} instead
|
||||
* @deprecated use
|
||||
* {@link org.owasp.dependencycheck.agent.DependencyCheckScanAgent#getProxyServer()}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public String getProxyUrl() {
|
||||
@@ -694,8 +703,8 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional ZIP File extensions to add analyze. This should be a comma-separated list of file extensions to treat like ZIP
|
||||
* files.
|
||||
* Additional ZIP File extensions to add analyze. This should be a
|
||||
* comma-separated list of file extensions to treat like ZIP files.
|
||||
*/
|
||||
private String zipExtensions;
|
||||
|
||||
@@ -836,11 +845,17 @@ public class DependencyCheckScanAgent {
|
||||
* Executes the Dependency-Check on the dependent libraries.
|
||||
*
|
||||
* @return the Engine used to scan the dependencies.
|
||||
* @throws org.owasp.dependencycheck.data.nvdcve.DatabaseException thrown if there is an exception connecting to the database
|
||||
* @throws ExceptionCollection a collection of one or more exceptions that
|
||||
* occurred during analysis.
|
||||
*/
|
||||
private Engine executeDependencyCheck() throws DatabaseException {
|
||||
private Engine executeDependencyCheck() throws ExceptionCollection {
|
||||
populateSettings();
|
||||
final Engine engine = new Engine();
|
||||
final Engine engine;
|
||||
try {
|
||||
engine = new Engine();
|
||||
} catch (DatabaseException ex) {
|
||||
throw new ExceptionCollection(ex, true);
|
||||
}
|
||||
engine.setDependencies(this.dependencies);
|
||||
engine.analyzeDependencies();
|
||||
return engine;
|
||||
@@ -881,8 +896,9 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the properties supplied and updates the dependency-check settings. Additionally, this sets the system properties
|
||||
* required to change the proxy server, port, and connection timeout.
|
||||
* Takes the properties supplied and updates the dependency-check settings.
|
||||
* Additionally, this sets the system properties required to change the
|
||||
* proxy server, port, and connection timeout.
|
||||
*/
|
||||
private void populateSettings() {
|
||||
Settings.initialize();
|
||||
@@ -925,7 +941,8 @@ public class DependencyCheckScanAgent {
|
||||
* Executes the dependency-check and generates the report.
|
||||
*
|
||||
* @return a reference to the engine used to perform the scan.
|
||||
* @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if there is an exception executing the scan.
|
||||
* @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if
|
||||
* there is an exception executing the scan.
|
||||
*/
|
||||
public Engine execute() throws ScanAgentException {
|
||||
Engine engine = null;
|
||||
@@ -940,10 +957,12 @@ public class DependencyCheckScanAgent {
|
||||
if (this.failBuildOnCVSS <= 10) {
|
||||
checkForFailure(engine.getDependencies());
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.error(
|
||||
"Unable to connect to the dependency-check database; analysis has stopped");
|
||||
LOGGER.debug("", ex);
|
||||
} catch (ExceptionCollection ex) {
|
||||
if (ex.isFatal()) {
|
||||
LOGGER.error("A fatal exception occurred during analysis; analysis has stopped. Please see the debug log for more details.");
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
throw new ScanAgentException("One or more exceptions occurred during analysis; please see the debug log for more details.", ex);
|
||||
} finally {
|
||||
Settings.cleanup(true);
|
||||
if (engine != null) {
|
||||
@@ -954,11 +973,12 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a vulnerability has been identified with a CVSS score that is above the threshold set in the
|
||||
* configuration.
|
||||
* Checks to see if a vulnerability has been identified with a CVSS score
|
||||
* that is above the threshold set in the configuration.
|
||||
*
|
||||
* @param dependencies the list of dependency objects
|
||||
* @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if there is an exception executing the scan.
|
||||
* @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if
|
||||
* there is an exception executing the scan.
|
||||
*/
|
||||
private void checkForFailure(List<Dependency> dependencies) throws ScanAgentException {
|
||||
final StringBuilder ids = new StringBuilder();
|
||||
@@ -986,7 +1006,8 @@ public class DependencyCheckScanAgent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a warning message listing a summary of dependencies and their associated CPE and CVE entries.
|
||||
* Generates a warning message listing a summary of dependencies and their
|
||||
* associated CPE and CVE entries.
|
||||
*
|
||||
* @param dependencies a list of dependency objects
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* Base class for analyzers to avoid code duplication of initialize and close
|
||||
* as most analyzers do not need these methods.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -26,10 +30,10 @@ public abstract class AbstractAnalyzer implements Analyzer {
|
||||
/**
|
||||
* The initialize method does nothing for this Analyzer.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception
|
||||
* @throws InitializationException thrown if there is an exception
|
||||
*/
|
||||
@Override
|
||||
public void initialize() throws Exception {
|
||||
public void initialize() throws InitializationException {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,11 @@ import java.io.FileFilter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* The base FileTypeAnalyzer that all analyzers that have specific file types they analyze should extend.
|
||||
* The base FileTypeAnalyzer that all analyzers that have specific file types
|
||||
* they analyze should extend.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -40,7 +42,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="Constructor">
|
||||
/**
|
||||
* Base constructor that all children must call. This checks the configuration to determine if the analyzer is enabled.
|
||||
* Base constructor that all children must call. This checks the
|
||||
* configuration to determine if the analyzer is enabled.
|
||||
*/
|
||||
public AbstractFileTypeAnalyzer() {
|
||||
reset();
|
||||
@@ -58,7 +61,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
private boolean filesMatched = false;
|
||||
|
||||
/**
|
||||
* Get the value of filesMatched. A flag indicating whether the scan included any file types this analyzer supports.
|
||||
* Get the value of filesMatched. A flag indicating whether the scan
|
||||
* included any file types this analyzer supports.
|
||||
*
|
||||
* @return the value of filesMatched
|
||||
*/
|
||||
@@ -67,7 +71,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of filesMatched. A flag indicating whether the scan included any file types this analyzer supports.
|
||||
* Set the value of filesMatched. A flag indicating whether the scan
|
||||
* included any file types this analyzer supports.
|
||||
*
|
||||
* @param filesMatched new value of filesMatched
|
||||
*/
|
||||
@@ -102,11 +107,13 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
//<editor-fold defaultstate="collapsed" desc="Abstract methods children must implement">
|
||||
/**
|
||||
* <p>
|
||||
* Returns the {@link java.io.FileFilter} used to determine which files are to be analyzed. An example would be an analyzer
|
||||
* that inspected Java jar files. Implementors may use {@link org.owasp.dependencycheck.utils.FileFilterBuilder}.</p>
|
||||
* Returns the {@link java.io.FileFilter} used to determine which files are
|
||||
* to be analyzed. An example would be an analyzer that inspected Java jar
|
||||
* files. Implementors may use
|
||||
* {@link org.owasp.dependencycheck.utils.FileFilterBuilder}.</p>
|
||||
* <p>
|
||||
* If the analyzer returns null it will not cause additional files to be analyzed, but will be executed against every file
|
||||
* loaded.</p>
|
||||
* If the analyzer returns null it will not cause additional files to be
|
||||
* analyzed, but will be executed against every file loaded.</p>
|
||||
*
|
||||
* @return the file filter used to determine which files are to be analyzed
|
||||
*/
|
||||
@@ -115,13 +122,15 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
/**
|
||||
* Initializes the file type analyzer.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception during initialization
|
||||
* @throws InitializationException thrown if there is an exception during
|
||||
* initialization
|
||||
*/
|
||||
protected abstract void initializeFileTypeAnalyzer() throws Exception;
|
||||
protected abstract void initializeFileTypeAnalyzer() throws InitializationException;
|
||||
|
||||
/**
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned,
|
||||
* and added to the list of dependencies within the engine.
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a
|
||||
* WAR or EAR, the contents are extracted, scanned, and added to the list of
|
||||
* dependencies within the engine.
|
||||
*
|
||||
* @param dependency the dependency to analyze
|
||||
* @param engine the engine scanning
|
||||
@@ -142,10 +151,11 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
/**
|
||||
* Initializes the analyzer.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception during initialization
|
||||
* @throws InitializationException thrown if there is an exception during
|
||||
* initialization
|
||||
*/
|
||||
@Override
|
||||
public final void initialize() throws Exception {
|
||||
public final void initialize() throws InitializationException {
|
||||
if (filesMatched) {
|
||||
initializeFileTypeAnalyzer();
|
||||
} else {
|
||||
@@ -169,8 +179,9 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned,
|
||||
* and added to the list of dependencies within the engine.
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a
|
||||
* WAR or EAR, the contents are extracted, scanned, and added to the list of
|
||||
* dependencies within the engine.
|
||||
*
|
||||
* @param dependency the dependency to analyze
|
||||
* @param engine the engine scanning
|
||||
@@ -202,8 +213,8 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen
|
||||
//<editor-fold defaultstate="collapsed" desc="Static utility methods">
|
||||
/**
|
||||
* <p>
|
||||
* Utility method to help in the creation of the extensions set. This constructs a new Set that can be used in a final static
|
||||
* declaration.</p>
|
||||
* Utility method to help in the creation of the extensions set. This
|
||||
* constructs a new Set that can be used in a final static declaration.</p>
|
||||
* <p>
|
||||
* This implementation was copied from
|
||||
* http://stackoverflow.com/questions/2041778/initialize-java-hashset-values-by-construction</p>
|
||||
|
||||
@@ -25,9 +25,10 @@ import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionParseException;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionParser;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.xml.suppression.SuppressionParseException;
|
||||
import org.owasp.dependencycheck.xml.suppression.SuppressionParser;
|
||||
import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
@@ -63,12 +64,16 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
/**
|
||||
* The initialize method loads the suppression XML file.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception
|
||||
* @throws InitializationException thrown if there is an exception
|
||||
*/
|
||||
@Override
|
||||
public void initialize() throws Exception {
|
||||
public void initialize() throws InitializationException {
|
||||
super.initialize();
|
||||
loadSuppressionData();
|
||||
try {
|
||||
loadSuppressionData();
|
||||
} catch (SuppressionParseException ex) {
|
||||
throw new InitializationException("Error initializing the suppression analyzer", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,12 +109,8 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
File file = null;
|
||||
try {
|
||||
rules = parser.parseSuppressionRules(this.getClass().getClassLoader().getResourceAsStream("dependencycheck-base-suppression.xml"));
|
||||
} catch (SuppressionParseException ex) {
|
||||
LOGGER.error("Unable to parse the base suppression data file");
|
||||
LOGGER.debug("Unable to parse the base suppression data file", ex);
|
||||
} catch (SAXException ex) {
|
||||
LOGGER.error("Unable to parse the base suppression data file");
|
||||
LOGGER.debug("Unable to parse the base suppression data file", ex);
|
||||
throw new SuppressionParseException("Unable to parse the base suppression data file", ex);
|
||||
}
|
||||
final String suppressionFilePath = Settings.getString(Settings.KEYS.SUPPRESSION_FILE);
|
||||
if (suppressionFilePath == null) {
|
||||
@@ -129,29 +130,37 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
} else {
|
||||
file = new File(suppressionFilePath);
|
||||
InputStream suppressionsFromClasspath = null;
|
||||
if (!file.exists()) {
|
||||
final InputStream suppressionsFromClasspath = this.getClass().getClassLoader().getResourceAsStream(suppressionFilePath);
|
||||
if (suppressionsFromClasspath != null) {
|
||||
deleteTempFile = true;
|
||||
file = FileUtils.getTempFile("suppression", "xml");
|
||||
try {
|
||||
org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
|
||||
} catch (IOException ex) {
|
||||
throwSuppressionParseException("Unable to locate suppressions file in classpath", ex);
|
||||
try {
|
||||
suppressionsFromClasspath = this.getClass().getClassLoader().getResourceAsStream(suppressionFilePath);
|
||||
if (suppressionsFromClasspath != null) {
|
||||
deleteTempFile = true;
|
||||
file = FileUtils.getTempFile("suppression", "xml");
|
||||
try {
|
||||
org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file);
|
||||
} catch (IOException ex) {
|
||||
throwSuppressionParseException("Unable to locate suppressions file in classpath", ex);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (suppressionsFromClasspath != null) {
|
||||
try {
|
||||
suppressionsFromClasspath.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Failed to close stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
try {
|
||||
//rules = parser.parseSuppressionRules(file);
|
||||
rules.addAll(parser.parseSuppressionRules(file));
|
||||
LOGGER.debug("{} suppression rules were loaded.", rules.size());
|
||||
} catch (SuppressionParseException ex) {
|
||||
LOGGER.warn("Unable to parse suppression xml file '{}'", file.getPath());
|
||||
LOGGER.warn(ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,24 +20,28 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* An interface that defines an Analyzer that is used to identify Dependencies. An analyzer will collect information
|
||||
* about the dependency in the form of Evidence.
|
||||
* An interface that defines an Analyzer that is used to identify Dependencies.
|
||||
* An analyzer will collect information about the dependency in the form of
|
||||
* Evidence.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public interface Analyzer {
|
||||
|
||||
/**
|
||||
* Analyzes the given dependency. The analysis could be anything from identifying an Identifier for the dependency,
|
||||
* to finding vulnerabilities, etc. Additionally, if the analyzer collects enough information to add a description
|
||||
* or license information for the dependency it should be added.
|
||||
* Analyzes the given dependency. The analysis could be anything from
|
||||
* identifying an Identifier for the dependency, to finding vulnerabilities,
|
||||
* etc. Additionally, if the analyzer collects enough information to add a
|
||||
* description or license information for the dependency it should be added.
|
||||
*
|
||||
* @param dependency a dependency to analyze.
|
||||
* @param engine the engine that is scanning the dependencies - this is useful if we need to check other
|
||||
* dependencies
|
||||
* @throws AnalysisException is thrown if there is an error analyzing the dependency file
|
||||
* @param engine the engine that is scanning the dependencies - this is
|
||||
* useful if we need to check other dependencies
|
||||
* @throws AnalysisException is thrown if there is an error analyzing the
|
||||
* dependency file
|
||||
*/
|
||||
void analyze(Dependency dependency, Engine engine) throws AnalysisException;
|
||||
|
||||
@@ -56,14 +60,17 @@ public interface Analyzer {
|
||||
AnalysisPhase getAnalysisPhase();
|
||||
|
||||
/**
|
||||
* The initialize method is called (once) prior to the analyze method being called on all of the dependencies.
|
||||
* The initialize method is called (once) prior to the analyze method being
|
||||
* called on all of the dependencies.
|
||||
*
|
||||
* @throws Exception is thrown if an exception occurs initializing the analyzer.
|
||||
* @throws InitializationException is thrown if an exception occurs
|
||||
* initializing the analyzer.
|
||||
*/
|
||||
void initialize() throws Exception;
|
||||
void initialize() throws InitializationException;
|
||||
|
||||
/**
|
||||
* The close method is called after all of the dependencies have been analyzed.
|
||||
* The close method is called after all of the dependencies have been
|
||||
* analyzed.
|
||||
*
|
||||
* @throws Exception is thrown if an exception occurs closing the analyzer.
|
||||
*/
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.analyzer.exception.ArchiveExtractionException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
@@ -58,8 +59,8 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An analyzer that extracts files from archives and ensures any supported files contained within the archive are added to the
|
||||
* dependency list.</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
|
||||
*/
|
||||
@@ -70,7 +71,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ArchiveAnalyzer.class);
|
||||
/**
|
||||
* The count of directories created during analysis. This is used for creating temporary directories.
|
||||
* The count of directories created during analysis. This is used for
|
||||
* creating temporary directories.
|
||||
*/
|
||||
private static int dirCount = 0;
|
||||
/**
|
||||
@@ -78,7 +80,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private File tempFileLocation = null;
|
||||
/**
|
||||
* The max scan depth that the analyzer will recursively extract nested archives.
|
||||
* The max scan depth that the analyzer will recursively extract nested
|
||||
* archives.
|
||||
*/
|
||||
private static final int MAX_SCAN_DEPTH = Settings.getInt("archive.scan.depth", 3);
|
||||
/**
|
||||
@@ -100,13 +103,15 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private static final Set<String> ZIPPABLES = newHashSet("zip", "ear", "war", "jar", "sar", "apk", "nupkg");
|
||||
/**
|
||||
* The set of file extensions supported by this analyzer. Note for developers, any additions to this list will need to be
|
||||
* explicitly handled in {@link #extractFiles(File, File, Engine)}.
|
||||
* The set of file extensions supported by this analyzer. Note for
|
||||
* developers, any additions to this list will need to be explicitly handled
|
||||
* in {@link #extractFiles(File, File, Engine)}.
|
||||
*/
|
||||
private static final Set<String> EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2");
|
||||
|
||||
/**
|
||||
* Detects files with extensions to remove from the engine's collection of dependencies.
|
||||
* Detects files with extensions to remove from the engine's collection of
|
||||
* dependencies.
|
||||
*/
|
||||
private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2")
|
||||
.build();
|
||||
@@ -157,7 +162,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@@ -169,26 +175,36 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* The initialize method does nothing for this Analyzer.
|
||||
*
|
||||
* @throws Exception is thrown if there is an exception deleting or creating temporary files
|
||||
* @throws InitializationException is thrown if there is an exception
|
||||
* deleting or creating temporary files
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
setEnabled(false);
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new InitializationException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
setEnabled(false);
|
||||
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new InitializationException(msg);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create a temporary file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The close method deletes any temporary files and directories created during analysis.
|
||||
* The close method deletes any temporary files and directories created
|
||||
* during analysis.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception deleting temporary files
|
||||
* @throws Exception thrown if there is an exception deleting temporary
|
||||
* files
|
||||
*/
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
@@ -205,8 +221,9 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned,
|
||||
* and added to the list of dependencies within the engine.
|
||||
* Analyzes a given dependency. If the dependency is an archive, such as a
|
||||
* WAR or EAR, the contents are extracted, scanned, and added to the list of
|
||||
* dependencies within the engine.
|
||||
*
|
||||
* @param dependency the dependency to analyze
|
||||
* @param engine the engine scanning
|
||||
@@ -249,7 +266,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* If a zip file was identified as a possible JAR, this method will add the zip to the list of dependencies.
|
||||
* If a zip file was identified as a possible JAR, this method will add the
|
||||
* zip to the list of dependencies.
|
||||
*
|
||||
* @param dependency the zip file
|
||||
* @param engine the engine
|
||||
@@ -339,6 +357,12 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private void extractFiles(File archive, File destination, Engine engine) throws AnalysisException {
|
||||
if (archive != null && destination != null) {
|
||||
String archiveExt = FileUtils.getFileExtension(archive.getName());
|
||||
if (archiveExt == null) {
|
||||
return;
|
||||
}
|
||||
archiveExt = archiveExt.toLowerCase();
|
||||
|
||||
FileInputStream fis;
|
||||
try {
|
||||
fis = new FileInputStream(archive);
|
||||
@@ -346,10 +370,11 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.debug("", ex);
|
||||
throw new AnalysisException("Archive file was not found.", ex);
|
||||
}
|
||||
final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
|
||||
try {
|
||||
if (ZIPPABLES.contains(archiveExt)) {
|
||||
extractArchive(new ZipArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
||||
final BufferedInputStream in = new BufferedInputStream(fis);
|
||||
ensureReadableJar(archiveExt, in);
|
||||
extractArchive(new ZipArchiveInputStream(in), destination, engine);
|
||||
} else if ("tar".equals(archiveExt)) {
|
||||
extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
||||
} else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
|
||||
@@ -377,13 +402,65 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the file being scanned is a JAR that begins with '#!/bin' which
|
||||
* indicates it is a fully executable jar. If a fully executable JAR is
|
||||
* identified the input stream will be advanced to the start of the actual
|
||||
* JAR file ( skipping the script).
|
||||
*
|
||||
* @see
|
||||
* <a href="http://docs.spring.io/spring-boot/docs/1.3.0.BUILD-SNAPSHOT/reference/htmlsingle/#deployment-install">Installing
|
||||
* Spring Boot Applications</a>
|
||||
* @param archiveExt the file extension
|
||||
* @param in the input stream
|
||||
* @throws IOException thrown if there is an error reading the stream
|
||||
*/
|
||||
private void ensureReadableJar(final String archiveExt, BufferedInputStream in) throws IOException {
|
||||
if ("jar".equals(archiveExt) && in.markSupported()) {
|
||||
in.mark(7);
|
||||
final byte[] b = new byte[7];
|
||||
final int read = in.read(b);
|
||||
if (read == 7
|
||||
&& b[0] == '#'
|
||||
&& b[1] == '!'
|
||||
&& b[2] == '/'
|
||||
&& b[3] == 'b'
|
||||
&& b[4] == 'i'
|
||||
&& b[5] == 'n'
|
||||
&& b[6] == '/') {
|
||||
boolean stillLooking = true;
|
||||
int chr, nxtChr;
|
||||
while (stillLooking && (chr = in.read()) != -1) {
|
||||
if (chr == '\n' || chr == '\r') {
|
||||
in.mark(4);
|
||||
if ((chr = in.read()) != -1) {
|
||||
if (chr == 'P' && (chr = in.read()) != -1) {
|
||||
if (chr == 'K' && (chr = in.read()) != -1) {
|
||||
if ((chr == 3 || chr == 5 || chr == 7) && (nxtChr = in.read()) != -1) {
|
||||
if (nxtChr == chr + 1) {
|
||||
stillLooking = false;
|
||||
in.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
in.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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;
|
||||
@@ -442,7 +519,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @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
|
||||
* @throws ArchiveExtractionException thrown if there is an exception
|
||||
* decompressing the file
|
||||
*/
|
||||
private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException {
|
||||
LOGGER.debug("Decompressing '{}'", outputFile.getPath());
|
||||
@@ -462,7 +540,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given {@link Closeable} instance, ignoring nulls, and logging any thrown {@link IOException}.
|
||||
* Close the given {@link Closeable} instance, ignoring nulls, and logging
|
||||
* any thrown {@link IOException}.
|
||||
*
|
||||
* @param closeable to be closed
|
||||
*/
|
||||
|
||||
@@ -43,9 +43,13 @@ import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
/**
|
||||
* Analyzer for getting company, product, and version information from a .NET assembly.
|
||||
* Analyzer for getting company, product, and version information from a .NET
|
||||
* assembly.
|
||||
*
|
||||
* @author colezlaw
|
||||
*
|
||||
@@ -82,18 +86,19 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @return the list of arguments to begin populating the ProcessBuilder
|
||||
*/
|
||||
private List<String> buildArgumentList() {
|
||||
protected List<String> buildArgumentList() {
|
||||
// Use file.separator as a wild guess as to whether this is Windows
|
||||
final List<String> args = new ArrayList<String>();
|
||||
if (!"\\".equals(System.getProperty("file.separator"))) {
|
||||
if (!SystemUtils.IS_OS_WINDOWS) {
|
||||
if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) {
|
||||
args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH));
|
||||
} else {
|
||||
} else if (isInPath("mono")) {
|
||||
args.add("mono");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
args.add(grokAssemblyExe.getPath());
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -113,6 +118,10 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
final List<String> args = buildArgumentList();
|
||||
if (args == null) {
|
||||
LOGGER.warn("Assembly Analyzer was unable to execute");
|
||||
return;
|
||||
}
|
||||
args.add(dependency.getActualFilePath());
|
||||
final ProcessBuilder pb = new ProcessBuilder(args);
|
||||
Document doc = null;
|
||||
@@ -178,13 +187,20 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a temporary location.
|
||||
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a
|
||||
* temporary location.
|
||||
*
|
||||
* @throws Exception if anything goes wrong
|
||||
* @throws InitializationException thrown if anything goes wrong
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
final File tempFile;
|
||||
try {
|
||||
tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory());
|
||||
} catch (IOException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create temporary file for the assembly analyzerr", ex);
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
@@ -193,13 +209,11 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
IOUtils.copy(is, fos);
|
||||
|
||||
grokAssemblyExe = tempFile;
|
||||
// Set the temp file to get deleted when we're done
|
||||
grokAssemblyExe.deleteOnExit();
|
||||
LOGGER.debug("Extracted GrokAssembly.exe to {}", grokAssemblyExe.getPath());
|
||||
} catch (IOException ioe) {
|
||||
this.setEnabled(false);
|
||||
LOGGER.warn("Could not extract GrokAssembly.exe: {}", ioe.getMessage());
|
||||
throw new AnalysisException("Could not extract GrokAssembly.exe", ioe);
|
||||
throw new InitializationException("Could not extract GrokAssembly.exe", ioe);
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
@@ -219,6 +233,22 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
// Now, need to see if GrokAssembly actually runs from this location.
|
||||
final List<String> args = buildArgumentList();
|
||||
//TODO this creaes an "unreported" error - if someone doesn't look
|
||||
// at the command output this could easily be missed (especially in an
|
||||
// Ant or Mmaven build.
|
||||
//
|
||||
// We need to create a non-fatal warning error type that will
|
||||
// get added to the report.
|
||||
//TOOD this idea needs to get replicated to the bundle audit analyzer.
|
||||
if (args == null) {
|
||||
setEnabled(false);
|
||||
LOGGER.error("----------------------------------------------------");
|
||||
LOGGER.error(".NET Assembly Analyzer could not be initialized and at least one "
|
||||
+ "'exe' or 'dll' was scanned. The 'mono' executale could not be found on "
|
||||
+ "the path; either disable the Assembly Analyzer or configure the path mono.");
|
||||
LOGGER.error("----------------------------------------------------");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final ProcessBuilder pb = new ProcessBuilder(args);
|
||||
final Process p = pb.start();
|
||||
@@ -232,19 +262,25 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer, please see the log for more details.");
|
||||
LOGGER.debug("GrokAssembly.exe is not working properly");
|
||||
grokAssemblyExe = null;
|
||||
this.setEnabled(false);
|
||||
throw new AnalysisException("Could not execute .NET AssemblyAnalyzer");
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Could not execute .NET AssemblyAnalyzer");
|
||||
}
|
||||
} catch (AnalysisException e) {
|
||||
} catch (InitializationException e) {
|
||||
setEnabled(false);
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
LOGGER.warn("An error occurred with the .NET AssemblyAnalyzer;\n"
|
||||
+ "this can be ignored unless you are scanning .NET DLLs. Please see the log for more details.");
|
||||
LOGGER.debug("Could not execute GrokAssembly {}", e.getMessage());
|
||||
this.setEnabled(false);
|
||||
throw new AnalysisException("An error occurred with the .NET AssemblyAnalyzer", e);
|
||||
setEnabled(false);
|
||||
throw new InitializationException("An error occurred with the .NET AssemblyAnalyzer", e);
|
||||
}
|
||||
try {
|
||||
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
} catch (ParserConfigurationException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Error initializing the assembly analyzer", ex);
|
||||
}
|
||||
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,10 +293,12 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
super.close();
|
||||
try {
|
||||
if (grokAssemblyExe != null && !grokAssemblyExe.delete()) {
|
||||
LOGGER.debug("Unable to delete temporary GrokAssembly.exe; attempting delete on exit");
|
||||
grokAssemblyExe.deleteOnExit();
|
||||
}
|
||||
} catch (SecurityException se) {
|
||||
LOGGER.debug("Can't delete temporary GrokAssembly.exe");
|
||||
grokAssemblyExe.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +334,8 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@@ -304,4 +343,29 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
protected String getAnalyzerEnabledSettingKey() {
|
||||
return Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests to see if a file is in the system path. <b>Note</b> - the current
|
||||
* implementation only works on non-windows platforms. For purposes of the
|
||||
* AssemblyAnalyzer this is okay as this is only needed on Mac/*nix.
|
||||
*
|
||||
* @param file the executable to look for
|
||||
* @return <code>true</code> if the file exists; otherwise
|
||||
* <code>false</code>
|
||||
*/
|
||||
private boolean isInPath(String file) {
|
||||
final ProcessBuilder pb = new ProcessBuilder("which", file);
|
||||
try {
|
||||
final Process proc = pb.start();
|
||||
final int retCode = proc.waitFor();
|
||||
if (retCode == 0) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Path seach failed for " + file);
|
||||
} catch (InterruptedException ex) {
|
||||
LOGGER.debug("Path seach failed for " + file);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,16 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* Used to analyze Autoconf input files named configure.ac or configure.in. Files simply named "configure" are also analyzed,
|
||||
* assuming they are generated by Autoconf, and contain certain special package descriptor variables.
|
||||
* Used to analyze Autoconf input files named configure.ac or configure.in.
|
||||
* Files simply named "configure" are also analyzed, assuming they are generated
|
||||
* by Autoconf, and contain certain special package descriptor variables.
|
||||
*
|
||||
* @author Dale Visser
|
||||
* @see <a href="https://www.gnu.org/software/autoconf/">Autoconf - GNU Project - Free Software Foundation (FSF)</a>
|
||||
* @see <a href="https://www.gnu.org/software/autoconf/">Autoconf - GNU Project
|
||||
* - Free Software Foundation (FSF)</a>
|
||||
*/
|
||||
@Experimental
|
||||
public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
@@ -142,7 +145,8 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@@ -270,10 +274,11 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Initializes the file type analyzer.
|
||||
*
|
||||
* @throws Exception thrown if there is an exception during initialization
|
||||
* @throws InitializationException thrown if there is an exception during
|
||||
* initialization
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// No initialization needed.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,14 +38,18 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Used to analyze CMake build files, and collect information that can be used to determine the associated CPE.</p>
|
||||
* Used to analyze CMake build files, and collect information that can be used
|
||||
* to determine the associated CPE.</p>
|
||||
* <p>
|
||||
* Note: This analyzer catches straightforward invocations of the project command, plus some other observed patterns of version
|
||||
* inclusion in real CMake projects. Many projects make use of older versions of CMake and/or use custom "homebrew" ways to insert
|
||||
* version information. Hopefully as the newer CMake call pattern grows in usage, this analyzer allow more CPEs to be
|
||||
* Note: This analyzer catches straightforward invocations of the project
|
||||
* command, plus some other observed patterns of version inclusion in real CMake
|
||||
* projects. Many projects make use of older versions of CMake and/or use custom
|
||||
* "homebrew" ways to insert version information. Hopefully as the newer CMake
|
||||
* call pattern grows in usage, this analyzer allow more CPEs to be
|
||||
* identified.</p>
|
||||
*
|
||||
* @author Dale Visser
|
||||
@@ -135,10 +139,10 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* No-op initializer implementation.
|
||||
*
|
||||
* @throws Exception never thrown
|
||||
* @throws InitializationException never thrown
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
@@ -147,7 +151,8 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @param engine the engine being used to perform the scan
|
||||
* @throws AnalysisException thrown if there is an unrecoverable error analyzing the dependency
|
||||
* @throws AnalysisException thrown if there is an unrecoverable error
|
||||
* analyzing the dependency
|
||||
*/
|
||||
@Override
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
@@ -183,13 +188,17 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the version information from the contents. If more then one version is found additional dependencies are added to
|
||||
* the dependency list.
|
||||
* Extracts the version information from the contents. If more then one
|
||||
* version is found additional dependencies are added to the dependency
|
||||
* list.
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @param engine the dependency-check engine
|
||||
* @param contents the version information
|
||||
*/
|
||||
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
|
||||
value = "DM_DEFAULT_ENCODING",
|
||||
justification = "Default encoding is only used if UTF-8 is not available")
|
||||
private void analyzeSetVersionCommand(Dependency dependency, Engine engine, String contents) {
|
||||
Dependency currentDep = dependency;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
@@ -45,6 +46,7 @@ import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersion;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
import org.slf4j.Logger;
|
||||
@@ -123,11 +125,20 @@ public class CPEAnalyzer implements Analyzer {
|
||||
/**
|
||||
* Creates the CPE Lucene Index.
|
||||
*
|
||||
* @throws Exception is thrown if there is an issue opening the index.
|
||||
* @throws InitializationException is thrown if there is an issue opening
|
||||
* the index.
|
||||
*/
|
||||
@Override
|
||||
public void initialize() throws Exception {
|
||||
this.open();
|
||||
public void initialize() throws InitializationException {
|
||||
try {
|
||||
this.open();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Exception initializing the Lucene Index", ex);
|
||||
throw new InitializationException("An exception occurred initializing the Lucene Index", ex);
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("Exception accessing the database", ex);
|
||||
throw new InitializationException("An exception occurred accessing the database", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -540,7 +551,7 @@ public class CPEAnalyzer implements Analyzer {
|
||||
final List<IdentifierMatch> collected = new ArrayList<IdentifierMatch>();
|
||||
|
||||
//TODO the following algorithm incorrectly identifies things as a lower version
|
||||
// if there lower confidence evidence when the current (highest) version number
|
||||
// if there lower confidence evidence when the current (highest) version number
|
||||
// is newer then anything in the NVD.
|
||||
for (Confidence conf : Confidence.values()) {
|
||||
for (Evidence evidence : dependency.getVersionEvidence().iterator(conf)) {
|
||||
@@ -564,8 +575,9 @@ public class CPEAnalyzer implements Analyzer {
|
||||
final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getName(), "UTF-8"));
|
||||
final IdentifierMatch match = new IdentifierMatch("cpe", vs.getName(), url, IdentifierConfidence.EXACT_MATCH, conf);
|
||||
collected.add(match);
|
||||
} else //TODO the following isn't quite right is it? need to think about this guessing game a bit more.
|
||||
if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size()
|
||||
|
||||
//TODO the following isn't quite right is it? need to think about this guessing game a bit more.
|
||||
} else if (evVer.getVersionParts().size() <= dbVer.getVersionParts().size()
|
||||
&& evVer.matchesAtLeastThreeLevels(dbVer)) {
|
||||
if (bestGuessConf == null || bestGuessConf.compareTo(conf) > 0) {
|
||||
if (bestGuess.getVersionParts().size() < dbVer.getVersionParts().size()) {
|
||||
@@ -790,6 +802,12 @@ public class CPEAnalyzer implements Analyzer {
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(IdentifierMatch o) {
|
||||
return new CompareToBuilder()
|
||||
.append(confidence, o.confidence)
|
||||
.append(evidenceConfidence, o.evidenceConfidence)
|
||||
.append(identifier, o.identifier)
|
||||
.toComparison();
|
||||
/*
|
||||
int conf = this.confidence.compareTo(o.confidence);
|
||||
if (conf == 0) {
|
||||
conf = this.evidenceConfidence.compareTo(o.evidenceConfidence);
|
||||
@@ -798,6 +816,7 @@ public class CPEAnalyzer implements Analyzer {
|
||||
}
|
||||
}
|
||||
return conf;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,10 @@ import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
@@ -42,8 +44,8 @@ import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* Analyzer which will attempt to locate a dependency, and the GAV information, by querying Central for the dependency's SHA-1
|
||||
* digest.
|
||||
* Analyzer which will attempt to locate a dependency, and the GAV information,
|
||||
* by querying Central for the dependency's SHA-1 digest.
|
||||
*
|
||||
* @author colezlaw
|
||||
*/
|
||||
@@ -70,7 +72,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
private static final String SUPPORTED_EXTENSIONS = "jar";
|
||||
|
||||
/**
|
||||
* The analyzer should be disabled if there are errors, so this is a flag to determine if such an error has occurred.
|
||||
* The analyzer should be disabled if there are errors, so this is a flag to
|
||||
* determine if such an error has occurred.
|
||||
*/
|
||||
private boolean errorFlag = false;
|
||||
|
||||
@@ -96,7 +99,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Determines if this analyzer is enabled.
|
||||
*
|
||||
* @return <code>true</code> if the analyzer is enabled; otherwise <code>false</code>
|
||||
* @return <code>true</code> if the analyzer is enabled; otherwise
|
||||
* <code>false</code>
|
||||
*/
|
||||
private boolean checkEnabled() {
|
||||
boolean retval = false;
|
||||
@@ -122,16 +126,21 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Initializes the analyzer once before any analysis is performed.
|
||||
*
|
||||
* @throws Exception if there's an error during initialization
|
||||
* @throws InitializationException if there's an error during initialization
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
LOGGER.debug("Initializing Central analyzer");
|
||||
LOGGER.debug("Central analyzer enabled: {}", isEnabled());
|
||||
if (isEnabled()) {
|
||||
final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL);
|
||||
LOGGER.debug("Central Analyzer URL: {}", searchUrl);
|
||||
searcher = new CentralSearch(new URL(searchUrl));
|
||||
try {
|
||||
searcher = new CentralSearch(new URL(searchUrl));
|
||||
} catch (MalformedURLException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("The configured URL to Maven Central is malformed: " + searchUrl, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +155,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to to reference the
|
||||
* analyzer's enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key.
|
||||
*/
|
||||
@@ -219,7 +229,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.warn("Unable to download pom.xml for {} from Central; "
|
||||
+ "this could result in undetected CPE/CVEs.", dependency.getFileName());
|
||||
} finally {
|
||||
if (pomFile != null && !FileUtils.deleteQuietly(pomFile)) {
|
||||
if (pomFile != null && pomFile.exists() && !FileUtils.deleteQuietly(pomFile)) {
|
||||
LOGGER.debug("Failed to delete temporary pom file {}", pomFile.toString());
|
||||
pomFile.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 IBM Corporation. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* This analyzer is used to analyze SWIFT and Objective-C packages by collecting
|
||||
* information from .podspec files. CocoaPods dependency manager see
|
||||
* https://cocoapods.org/.
|
||||
*
|
||||
* @author Bianca Jiang (https://twitter.com/biancajiang)
|
||||
*/
|
||||
@Experimental
|
||||
public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
// private static final Logger LOGGER = LoggerFactory.getLogger(CocoaPodsAnalyzer.class);
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "CocoaPods Package Analyzer";
|
||||
|
||||
/**
|
||||
* The phase that this analyzer is intended to run in.
|
||||
*/
|
||||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
|
||||
|
||||
/**
|
||||
* The file name to scan.
|
||||
*/
|
||||
public static final String PODSPEC = "podspec";
|
||||
/**
|
||||
* Filter that detects files named "*.podspec".
|
||||
*/
|
||||
private static final FileFilter PODSPEC_FILTER = FileFilterBuilder.newInstance().addExtensions(PODSPEC).build();
|
||||
|
||||
/**
|
||||
* The capture group #1 is the block variable. e.g. "Pod::Spec.new do
|
||||
* |spec|"
|
||||
*/
|
||||
private static final Pattern PODSPEC_BLOCK_PATTERN = Pattern.compile("Pod::Spec\\.new\\s+?do\\s+?\\|(.+?)\\|");
|
||||
|
||||
/**
|
||||
* Returns the FileFilter
|
||||
*
|
||||
* @return the FileFilter
|
||||
*/
|
||||
@Override
|
||||
protected FileFilter getFileFilter() {
|
||||
return PODSPEC_FILTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@Override
|
||||
protected String getAnalyzerEnabledSettingKey() {
|
||||
return Settings.KEYS.ANALYZER_COCOAPODS_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
throws AnalysisException {
|
||||
|
||||
String contents;
|
||||
try {
|
||||
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
|
||||
} catch (IOException e) {
|
||||
throw new AnalysisException(
|
||||
"Problem occurred while reading dependency file.", e);
|
||||
}
|
||||
final Matcher matcher = PODSPEC_BLOCK_PATTERN.matcher(contents);
|
||||
if (matcher.find()) {
|
||||
contents = contents.substring(matcher.end());
|
||||
final String blockVariable = matcher.group(1);
|
||||
|
||||
final EvidenceCollection vendor = dependency.getVendorEvidence();
|
||||
final EvidenceCollection product = dependency.getProductEvidence();
|
||||
final EvidenceCollection version = dependency.getVersionEvidence();
|
||||
|
||||
final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST);
|
||||
if (!name.isEmpty()) {
|
||||
vendor.addEvidence(PODSPEC, "name_project", name, Confidence.HIGHEST);
|
||||
}
|
||||
addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.HIGHEST);
|
||||
|
||||
addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST);
|
||||
addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST);
|
||||
addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST);
|
||||
|
||||
addStringEvidence(version, contents, blockVariable, "version", "version", Confidence.HIGHEST);
|
||||
}
|
||||
|
||||
setPackagePath(dependency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts evidence from the contents and adds it to the given evidence
|
||||
* collection.
|
||||
*
|
||||
* @param evidences the evidence collection to update
|
||||
* @param contents the text to extract evidence from
|
||||
* @param blockVariable the block variable within the content to search for
|
||||
* @param field the name of the field being searched for
|
||||
* @param fieldPattern the field pattern within the contents to search for
|
||||
* @param confidence the confidence level of the evidence if found
|
||||
* @return the string that was added as evidence
|
||||
*/
|
||||
private String addStringEvidence(EvidenceCollection evidences, String contents,
|
||||
String blockVariable, String field, String fieldPattern, Confidence confidence) {
|
||||
String value = "";
|
||||
|
||||
//capture array value between [ ]
|
||||
final Matcher arrayMatcher = Pattern.compile(
|
||||
String.format("\\s*?%s\\.%s\\s*?=\\s*?\\{\\s*?(.*?)\\s*?\\}", blockVariable, fieldPattern),
|
||||
Pattern.CASE_INSENSITIVE).matcher(contents);
|
||||
if (arrayMatcher.find()) {
|
||||
value = arrayMatcher.group(1);
|
||||
} else { //capture single value between quotes
|
||||
final Matcher matcher = Pattern.compile(
|
||||
String.format("\\s*?%s\\.%s\\s*?=\\s*?(['\"])(.*?)\\1", blockVariable, fieldPattern),
|
||||
Pattern.CASE_INSENSITIVE).matcher(contents);
|
||||
if (matcher.find()) {
|
||||
value = matcher.group(2);
|
||||
}
|
||||
}
|
||||
if (value.length() > 0) {
|
||||
evidences.addEvidence(PODSPEC, field, value, confidence);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the package path on the given dependency.
|
||||
*
|
||||
* @param dep the dependency to update
|
||||
*/
|
||||
private void setPackagePath(Dependency dep) {
|
||||
final File file = new File(dep.getFilePath());
|
||||
final String parent = file.getParent();
|
||||
if (parent != null) {
|
||||
dep.setPackagePath(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* Used to analyze a composer.lock file for a composer PHP app.
|
||||
@@ -77,15 +79,22 @@ public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Initializes the analyzer.
|
||||
*
|
||||
* @throws Exception thrown if an exception occurs getting an instance of SHA1
|
||||
* @throws InitializationException thrown if an exception occurs getting an
|
||||
* instance of SHA1
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
sha1 = MessageDigest.getInstance("SHA1");
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
sha1 = MessageDigest.getInstance("SHA1");
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create SHA1 MmessageDigest", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The MessageDigest for calculating a new digest for the new dependencies added.
|
||||
* The MessageDigest for calculating a new digest for the new dependencies
|
||||
* added.
|
||||
*/
|
||||
private MessageDigest sha1 = null;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
|
||||
|
||||
/**
|
||||
* The suppression analyzer processes an externally defined XML document that complies with the suppressions.xsd schema.
|
||||
|
||||
@@ -117,6 +117,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
final ListIterator<Dependency> subIterator = engine.getDependencies().listIterator(mainIterator.nextIndex());
|
||||
while (subIterator.hasNext()) {
|
||||
final Dependency nextDependency = subIterator.next();
|
||||
Dependency main = null;
|
||||
if (hashesMatch(dependency, nextDependency) && !containedInWar(dependency.getFilePath())
|
||||
&& !containedInWar(nextDependency.getFilePath())) {
|
||||
if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) {
|
||||
@@ -143,8 +144,14 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||
break; //since we merged into the next dependency - skip forward to the next in mainIterator
|
||||
}
|
||||
} else if (isSameRubyGem(dependency, nextDependency)) {
|
||||
final Dependency main = getMainGemspecDependency(dependency, nextDependency);
|
||||
} else if ((main = getMainGemspecDependency(dependency, nextDependency)) != null) {
|
||||
if (main == dependency) {
|
||||
mergeDependencies(dependency, nextDependency, dependenciesToRemove);
|
||||
} else {
|
||||
mergeDependencies(nextDependency, dependency, dependenciesToRemove);
|
||||
break; //since we merged into the next dependency - skip forward to the next in mainIterator
|
||||
}
|
||||
} else if ((main = getMainSwiftDependency(dependency, nextDependency)) != null) {
|
||||
if (main == dependency) {
|
||||
mergeDependencies(dependency, nextDependency, dependenciesToRemove);
|
||||
} else {
|
||||
@@ -302,10 +309,13 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
String right = rFile.getParent();
|
||||
if (left == null) {
|
||||
return right == null;
|
||||
} else if (right == null) {
|
||||
return false;
|
||||
}
|
||||
if (left.equalsIgnoreCase(right)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (left.matches(".*[/\\\\]repository[/\\\\].*") && right.matches(".*[/\\\\]repository[/\\\\].*")) {
|
||||
left = getBaseRepoPath(left);
|
||||
right = getBaseRepoPath(right);
|
||||
@@ -376,6 +386,49 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer implements Anal
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bundling same swift dependencies with the same packagePath but identified
|
||||
* by different analyzers.
|
||||
*
|
||||
* @param dependency1 dependency to test
|
||||
* @param dependency2 dependency to test
|
||||
* @return <code>true</code> if the dependencies appear to be the same;
|
||||
* otherwise <code>false</code>
|
||||
*/
|
||||
private boolean isSameSwiftPackage(Dependency dependency1, Dependency dependency2) {
|
||||
if (dependency1 == null || dependency2 == null
|
||||
|| (!dependency1.getFileName().endsWith(".podspec")
|
||||
&& !dependency1.getFileName().equals("Package.swift"))
|
||||
|| (!dependency2.getFileName().endsWith(".podspec")
|
||||
&& !dependency2.getFileName().equals("Package.swift"))
|
||||
|| dependency1.getPackagePath() == null
|
||||
|| dependency2.getPackagePath() == null) {
|
||||
return false;
|
||||
}
|
||||
if (dependency1.getPackagePath().equalsIgnoreCase(dependency2.getPackagePath())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which of the swift dependencies should be considered the
|
||||
* primary.
|
||||
*
|
||||
* @param dependency1 the first swift dependency to compare
|
||||
* @param dependency2 the second swift dependency to compare
|
||||
* @return the primary swift dependency
|
||||
*/
|
||||
private Dependency getMainSwiftDependency(Dependency dependency1, Dependency dependency2) {
|
||||
if (isSameSwiftPackage(dependency1, dependency2)) {
|
||||
if (dependency1.getFileName().endsWith(".podspec")) {
|
||||
return dependency1;
|
||||
}
|
||||
return dependency2;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is likely a very broken attempt at determining if the 'left'
|
||||
* dependency is the 'core' library in comparison to the 'right' library.
|
||||
|
||||
@@ -70,11 +70,12 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
/**
|
||||
* Python init files
|
||||
*/
|
||||
//CSOFF: WhitespaceAfter
|
||||
private static final NameFileFilter IGNORED_FILES = new NameFileFilter(new String[]{
|
||||
"__init__.py",
|
||||
"__init__.pyc",
|
||||
"__init__.pyo",
|
||||
});
|
||||
"__init__.pyo",});
|
||||
//CSON: WhitespaceAfter
|
||||
|
||||
/**
|
||||
* Collects information about the file name.
|
||||
@@ -93,26 +94,27 @@ public class FileNameAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
|
||||
//add version evidence
|
||||
final DependencyVersion version = DependencyVersionUtil.parseVersion(fileName);
|
||||
final String packageName = DependencyVersionUtil.parsePreVersion(fileName);
|
||||
if (version != null) {
|
||||
// If the version number is just a number like 2 or 23, reduce the confidence
|
||||
// a shade. This should hopefully correct for cases like log4j.jar or
|
||||
// struts2-core.jar
|
||||
if (version.getVersionParts() == null || version.getVersionParts().size() < 2) {
|
||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
||||
dependency.getVersionEvidence().addEvidence("file", "version",
|
||||
version.toString(), Confidence.MEDIUM);
|
||||
} else {
|
||||
dependency.getVersionEvidence().addEvidence("file", "version",
|
||||
version.toString(), Confidence.HIGHEST);
|
||||
}
|
||||
dependency.getVersionEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.MEDIUM);
|
||||
packageName, Confidence.MEDIUM);
|
||||
}
|
||||
|
||||
if (!IGNORED_FILES.accept(f)) {
|
||||
dependency.getProductEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.HIGH);
|
||||
packageName, Confidence.HIGH);
|
||||
dependency.getVendorEvidence().addEvidence("file", "name",
|
||||
fileName, Confidence.HIGH);
|
||||
packageName, Confidence.HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,37 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.xml.suppression.PropertyType;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileUtils;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.xml.hints.VendorDuplicatingHintRule;
|
||||
import org.owasp.dependencycheck.xml.hints.HintParseException;
|
||||
import org.owasp.dependencycheck.xml.hints.HintParser;
|
||||
import org.owasp.dependencycheck.xml.hints.HintRule;
|
||||
import org.owasp.dependencycheck.xml.hints.Hints;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* This analyzer adds evidence to dependencies to enhance the accuracy of
|
||||
* library identification.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -62,18 +82,101 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
public AnalysisPhase getAnalysisPhase() {
|
||||
return ANALYSIS_PHASE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The initialize method does nothing for this Analyzer.
|
||||
*
|
||||
* @throws InitializationException thrown if there is an exception
|
||||
*/
|
||||
@Override
|
||||
public void initialize() throws InitializationException {
|
||||
try {
|
||||
super.initialize();
|
||||
loadHintRules();
|
||||
} catch (HintParseException ex) {
|
||||
LOGGER.debug("Unable to parse hint file", ex);
|
||||
throw new InitializationException("Unable to parse the hint file", ex);
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* The HintAnalyzer uses knowledge about a dependency to add additional information to help in identification of identifiers
|
||||
* or vulnerabilities.
|
||||
* The Logger for use throughout the class
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HintAnalyzer.class);
|
||||
/**
|
||||
* The name of the hint rule file
|
||||
*/
|
||||
private static final String HINT_RULE_FILE_NAME = "dependencycheck-base-hint.xml";
|
||||
/**
|
||||
* The collection of hints.
|
||||
*/
|
||||
private Hints hints;
|
||||
|
||||
/**
|
||||
* The HintAnalyzer uses knowledge about a dependency to add additional
|
||||
* information to help in identification of identifiers or vulnerabilities.
|
||||
*
|
||||
* @param dependency The dependency being analyzed
|
||||
* @param engine The scanning engine
|
||||
* @throws AnalysisException is thrown if there is an exception analyzing the dependency.
|
||||
* @throws AnalysisException is thrown if there is an exception analyzing
|
||||
* the dependency.
|
||||
*/
|
||||
@Override
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
for (HintRule hint : hints.getHintRules()) {
|
||||
boolean shouldAdd = false;
|
||||
for (Evidence given : hint.getGivenVendor()) {
|
||||
if (dependency.getVendorEvidence().getEvidence().contains(given)) {
|
||||
shouldAdd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!shouldAdd) {
|
||||
for (Evidence given : hint.getGivenProduct()) {
|
||||
if (dependency.getProductEvidence().getEvidence().contains(given)) {
|
||||
shouldAdd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shouldAdd) {
|
||||
for (PropertyType pt : hint.getFilenames()) {
|
||||
if (pt.matches(dependency.getFileName())) {
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldAdd) {
|
||||
for (Evidence e : hint.getAddVendor()) {
|
||||
dependency.getVendorEvidence().addEvidence(e);
|
||||
}
|
||||
for (Evidence e : hint.getAddProduct()) {
|
||||
dependency.getProductEvidence().addEvidence(e);
|
||||
}
|
||||
for (Evidence e : hint.getAddVersion()) {
|
||||
dependency.getVersionEvidence().addEvidence(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Iterator<Evidence> itr = dependency.getVendorEvidence().iterator();
|
||||
final List<Evidence> newEntries = new ArrayList<Evidence>();
|
||||
while (itr.hasNext()) {
|
||||
final Evidence e = itr.next();
|
||||
for (VendorDuplicatingHintRule dhr : hints.getVendorDuplicatingHintRules()) {
|
||||
if (dhr.getValue().equalsIgnoreCase(e.getValue(false))) {
|
||||
newEntries.add(new Evidence(e.getSource() + " (hint)",
|
||||
e.getName(), dhr.getDuplicate(), e.getConfidence()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Evidence e : newEntries) {
|
||||
dependency.getVendorEvidence().addEvidence(e);
|
||||
}
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="Old implementation">
|
||||
/*
|
||||
final Evidence springTest1 = new Evidence("Manifest",
|
||||
"Implementation-Title",
|
||||
"Spring Framework",
|
||||
@@ -171,6 +274,90 @@ public class HintAnalyzer extends AbstractAnalyzer implements Analyzer {
|
||||
for (Evidence e : newEntries) {
|
||||
dependency.getVendorEvidence().addEvidence(e);
|
||||
}
|
||||
*/
|
||||
//</editor-fold>
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the hint rules file.
|
||||
*
|
||||
* @throws HintParseException thrown if the XML cannot be parsed.
|
||||
*/
|
||||
private void loadHintRules() throws HintParseException {
|
||||
final HintParser parser = new HintParser();
|
||||
File file = null;
|
||||
try {
|
||||
hints = parser.parseHints(this.getClass().getClassLoader().getResourceAsStream(HINT_RULE_FILE_NAME));
|
||||
} catch (HintParseException ex) {
|
||||
LOGGER.error("Unable to parse the base hint data file");
|
||||
LOGGER.debug("Unable to parse the base hint data file", ex);
|
||||
} catch (SAXException ex) {
|
||||
LOGGER.error("Unable to parse the base hint data file");
|
||||
LOGGER.debug("Unable to parse the base hint data file", ex);
|
||||
}
|
||||
final String filePath = Settings.getString(Settings.KEYS.HINTS_FILE);
|
||||
if (filePath == null) {
|
||||
return;
|
||||
}
|
||||
boolean deleteTempFile = false;
|
||||
try {
|
||||
final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE);
|
||||
if (uriRx.matcher(filePath).matches()) {
|
||||
deleteTempFile = true;
|
||||
file = FileUtils.getTempFile("hint", "xml");
|
||||
final URL url = new URL(filePath);
|
||||
try {
|
||||
Downloader.fetchFile(url, file, false);
|
||||
} catch (DownloadFailedException ex) {
|
||||
Downloader.fetchFile(url, file, true);
|
||||
}
|
||||
} else {
|
||||
file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
InputStream fromClasspath = null;
|
||||
try {
|
||||
fromClasspath = this.getClass().getClassLoader().getResourceAsStream(filePath);
|
||||
if (fromClasspath != null) {
|
||||
deleteTempFile = true;
|
||||
file = FileUtils.getTempFile("hint", "xml");
|
||||
try {
|
||||
org.apache.commons.io.FileUtils.copyInputStreamToFile(fromClasspath, file);
|
||||
} catch (IOException ex) {
|
||||
throw new HintParseException("Unable to locate suppressions file in classpath", ex);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (fromClasspath != null) {
|
||||
fromClasspath.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
try {
|
||||
final Hints newHints = parser.parseHints(file);
|
||||
hints.getHintRules().addAll(newHints.getHintRules());
|
||||
hints.getVendorDuplicatingHintRules().addAll(newHints.getVendorDuplicatingHintRules());
|
||||
LOGGER.debug("{} hint rules were loaded.", hints.getHintRules().size());
|
||||
LOGGER.debug("{} duplicating hint rules were loaded.", hints.getVendorDuplicatingHintRules().size());
|
||||
} catch (HintParseException ex) {
|
||||
LOGGER.warn("Unable to parse hint rule xml file '{}'", file.getPath());
|
||||
LOGGER.warn(ex.getMessage());
|
||||
LOGGER.debug("", ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} catch (DownloadFailedException ex) {
|
||||
throw new HintParseException("Unable to fetch the configured hint file", ex);
|
||||
} catch (MalformedURLException ex) {
|
||||
throw new HintParseException("Configured hint file has an invalid URL", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new HintParseException("Unable to create temp file for hints", ex);
|
||||
} finally {
|
||||
if (deleteTempFile && file != null) {
|
||||
FileUtils.delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.xml.pom.License;
|
||||
import org.owasp.dependencycheck.xml.pom.PomUtils;
|
||||
@@ -324,8 +325,10 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
} else {
|
||||
pom = PomUtils.readPom(externalPom);
|
||||
}
|
||||
pom.processProperties(pomProperties);
|
||||
foundSomething |= setPomEvidence(dependency, pom, classes);
|
||||
if (pom != null) {
|
||||
pom.processProperties(pomProperties);
|
||||
foundSomething |= setPomEvidence(dependency, pom, classes);
|
||||
}
|
||||
}
|
||||
} catch (AnalysisException ex) {
|
||||
LOGGER.warn("An error occurred while analyzing '{}'.", dependency.getActualFilePath());
|
||||
@@ -408,6 +411,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
final File file = new File(tmpDir, "pom.xml");
|
||||
try {
|
||||
final ZipEntry entry = jar.getEntry(path);
|
||||
if (entry == null) {
|
||||
throw new AnalysisException(String.format("Pom (%s)does not exist in %s", path, jar.getName()));
|
||||
}
|
||||
input = jar.getInputStream(entry);
|
||||
fos = new FileOutputStream(file);
|
||||
IOUtils.copy(input, fos);
|
||||
@@ -486,7 +492,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
final String originalGroupID = groupid;
|
||||
if (groupid.startsWith("org.") || groupid.startsWith("com.")) {
|
||||
if (groupid != null && (groupid.startsWith("org.") || groupid.startsWith("com."))) {
|
||||
groupid = groupid.substring(4);
|
||||
}
|
||||
|
||||
@@ -495,7 +501,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
final String originalArtifactID = artifactid;
|
||||
if (artifactid.startsWith("org.") || artifactid.startsWith("com.")) {
|
||||
if (artifactid != null && (artifactid.startsWith("org.") || artifactid.startsWith("com."))) {
|
||||
artifactid = artifactid.substring(4);
|
||||
}
|
||||
|
||||
@@ -644,9 +650,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @return whether evidence was identified parsing the manifest
|
||||
* @throws IOException if there is an issue reading the JAR file
|
||||
*/
|
||||
protected boolean parseManifest(Dependency dependency,
|
||||
List<ClassNameInformation> classInformation)
|
||||
throws IOException {
|
||||
protected boolean parseManifest(Dependency dependency, List<ClassNameInformation> classInformation) throws IOException {
|
||||
boolean foundSomething = false;
|
||||
JarFile jar = null;
|
||||
try {
|
||||
@@ -665,7 +669,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
final EvidenceCollection vendorEvidence = dependency.getVendorEvidence();
|
||||
final EvidenceCollection productEvidence = dependency.getProductEvidence();
|
||||
final EvidenceCollection versionEvidence = dependency.getVersionEvidence();
|
||||
|
||||
String source = "Manifest";
|
||||
String specificationVersion = null;
|
||||
boolean hasImplementationVersion = false;
|
||||
@@ -687,7 +690,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
foundSomething = true;
|
||||
versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
|
||||
} else if ("specification-version".equalsIgnoreCase(key)) {
|
||||
specificationVersion = key;
|
||||
specificationVersion = value;
|
||||
} else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) {
|
||||
foundSomething = true;
|
||||
vendorEvidence.addEvidence(source, key, value, Confidence.HIGH);
|
||||
@@ -706,17 +709,12 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
addMatchingValues(classInformation, value, productEvidence);
|
||||
// //the following caused false positives.
|
||||
// } else if (key.equalsIgnoreCase(BUNDLE_VENDOR)) {
|
||||
// foundSomething = true;
|
||||
// vendorEvidence.addEvidence(source, key, value, Confidence.HIGH);
|
||||
// addMatchingValues(classInformation, value, vendorEvidence);
|
||||
} else if (key.equalsIgnoreCase(BUNDLE_VERSION)) {
|
||||
foundSomething = true;
|
||||
versionEvidence.addEvidence(source, key, value, Confidence.HIGH);
|
||||
} else if (key.equalsIgnoreCase(Attributes.Name.MAIN_CLASS.toString())) {
|
||||
continue;
|
||||
//skipping main class as if this has important information to add
|
||||
// it will be added during class name analysis... if other fields
|
||||
// have the information from the class name then they will get added...
|
||||
//skipping main class as if this has important information to add it will be added during class name analysis...
|
||||
} else {
|
||||
key = key.toLowerCase();
|
||||
if (!IGNORE_KEYS.contains(key)
|
||||
@@ -782,7 +780,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Attributes> item : manifest.getEntries().entrySet()) {
|
||||
final String name = item.getKey();
|
||||
source = "manifest: " + name;
|
||||
@@ -903,20 +900,27 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Initializes the JarAnalyzer.
|
||||
*
|
||||
* @throws Exception is thrown if there is an exception creating a temporary
|
||||
* directory
|
||||
* @throws InitializationException is thrown if there is an exception
|
||||
* creating a temporary directory
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath());
|
||||
setEnabled(false);
|
||||
throw new InitializationException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
final String msg = String.format("Unable to create directory '%s'.", tempFileLocation.getAbsolutePath());
|
||||
setEnabled(false);
|
||||
throw new InitializationException(msg);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create a temporary file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
@@ -42,15 +43,18 @@ import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
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.
|
||||
* 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>
|
||||
* <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
|
||||
@@ -58,7 +62,8 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The default URL - this will be used by the CentralAnalyzer to determine whether to enable this.
|
||||
* The default URL - this will be used by the CentralAnalyzer to determine
|
||||
* whether to enable this.
|
||||
*/
|
||||
public static final String DEFAULT_URL = "https://repository.sonatype.org/service/local/";
|
||||
|
||||
@@ -95,7 +100,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Determines if this analyzer is enabled
|
||||
*
|
||||
* @return <code>true</code> if the analyzer is enabled; otherwise <code>false</code>
|
||||
* @return <code>true</code> if the analyzer is enabled; otherwise
|
||||
* <code>false</code>
|
||||
*/
|
||||
private boolean checkEnabled() {
|
||||
/* Enable this analyzer ONLY if the Nexus URL has been set to something
|
||||
@@ -131,10 +137,10 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Initializes the analyzer once before any analysis is performed.
|
||||
*
|
||||
* @throws Exception if there's an error during initialization
|
||||
* @throws InitializationException if there's an error during initialization
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
LOGGER.debug("Initializing Nexus Analyzer");
|
||||
LOGGER.debug("Nexus Analyzer enabled: {}", isEnabled());
|
||||
if (isEnabled()) {
|
||||
@@ -143,14 +149,12 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
try {
|
||||
searcher = new NexusSearch(new URL(searchUrl));
|
||||
if (!searcher.preflightRequest()) {
|
||||
LOGGER.warn("There was an issue getting Nexus status. Disabling analyzer.");
|
||||
setEnabled(false);
|
||||
throw new InitializationException("There was an issue getting Nexus status. Disabling analyzer.");
|
||||
}
|
||||
} 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.warn("Property {} not a valid URL. Nexus Analyzer disabled", searchUrl);
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Malformed URL to Nexus: " + searchUrl, mue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +170,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@@ -240,7 +245,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.warn("Unable to download pom.xml for {} from Nexus repository; "
|
||||
+ "this could result in undetected CPE/CVEs.", dependency.getFileName());
|
||||
} finally {
|
||||
if (pomFile != null && !FileUtils.deleteQuietly(pomFile)) {
|
||||
if (pomFile != null && pomFile.exists() && !FileUtils.deleteQuietly(pomFile)) {
|
||||
LOGGER.debug("Failed to delete temporary pom file {}", pomFile.toString());
|
||||
pomFile.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,11 @@ import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import javax.json.JsonString;
|
||||
import javax.json.JsonValue;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* Used to analyze Node Package Manager (npm) package.json files, and collect information that can be used to determine the
|
||||
* associated CPE.
|
||||
* Used to analyze Node Package Manager (npm) package.json files, and collect
|
||||
* information that can be used to determine the associated CPE.
|
||||
*
|
||||
* @author Dale Visser
|
||||
*/
|
||||
@@ -84,7 +85,7 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@@ -109,7 +110,8 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@@ -155,7 +157,8 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to an evidence collection from the node json configuration.
|
||||
* Adds information to an evidence collection from the node json
|
||||
* configuration.
|
||||
*
|
||||
* @param json information from node.js
|
||||
* @param collection a set of evidence about a dependency
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* Analyzer which will parse a Nuspec file to gather module information.
|
||||
@@ -65,10 +66,10 @@ public class NuspecAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* Initializes the analyzer once before any analysis is performed.
|
||||
*
|
||||
* @throws Exception if there's an error during initialization
|
||||
* @throws InitializationException if there's an error during initialization
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +83,8 @@ public class NuspecAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* NvdCveAnalyzer is a utility class that takes a project dependency and attempts to discern if there is an associated
|
||||
@@ -35,7 +37,10 @@ import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class NvdCveAnalyzer implements Analyzer {
|
||||
|
||||
/**
|
||||
* The Logger for use throughout the class
|
||||
*/
|
||||
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(NvdCveAnalyzer.class);
|
||||
/**
|
||||
* The maximum number of query results to return.
|
||||
*/
|
||||
@@ -79,7 +84,7 @@ public class NvdCveAnalyzer implements Analyzer {
|
||||
/**
|
||||
* Ensures that the CVE Database is closed.
|
||||
*
|
||||
* @throws Throwable when a throwable is thrown.
|
||||
* @throws Throwable an exception raised by this method
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
@@ -94,7 +99,7 @@ public class NvdCveAnalyzer implements Analyzer {
|
||||
*
|
||||
* @param dependency The Dependency to analyze
|
||||
* @param engine The analysis engine
|
||||
* @throws AnalysisException is thrown if there is an issue analyzing the dependency
|
||||
* @throws AnalysisException thrown if there is an issue analyzing the dependency
|
||||
*/
|
||||
@Override
|
||||
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
|
||||
@@ -145,10 +150,24 @@ public class NvdCveAnalyzer implements Analyzer {
|
||||
/**
|
||||
* Opens the database used to gather NVD CVE data.
|
||||
*
|
||||
* @throws Exception is thrown if there is an issue opening the index.
|
||||
* @throws InitializationException is thrown if there is an issue opening the index.
|
||||
*/
|
||||
@Override
|
||||
public void initialize() throws Exception {
|
||||
this.open();
|
||||
public void initialize() throws InitializationException {
|
||||
try {
|
||||
this.open();
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.debug("SQL Exception initializing NvdCveAnalyzer", ex);
|
||||
throw new InitializationException(ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("IO Exception initializing NvdCveAnalyzer", ex);
|
||||
throw new InitializationException(ex);
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("Database Exception initializing NvdCveAnalyzer", ex);
|
||||
throw new InitializationException(ex);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
LOGGER.debug("Exception initializing NvdCveAnalyzer", ex);
|
||||
throw new InitializationException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* Used to analyze OpenSSL source code present in the file system.
|
||||
@@ -145,10 +146,10 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* No-op initializer implementation.
|
||||
*
|
||||
* @throws Exception never thrown
|
||||
* @throws InitializationException never thrown
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.commons.io.filefilter.NameFileFilter;
|
||||
import org.apache.commons.io.filefilter.SuffixFileFilter;
|
||||
import org.apache.commons.io.input.AutoCloseInputStream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
@@ -37,6 +38,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.InternetHeaders;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.ExtractionException;
|
||||
import org.owasp.dependencycheck.utils.ExtractionUtil;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
@@ -45,8 +47,9 @@ import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.owasp.dependencycheck.utils.UrlStringUtils;
|
||||
|
||||
/**
|
||||
* Used to analyze a Wheel or egg distribution files, or their contents in unzipped form, and collect information that can be used
|
||||
* to determine the associated CPE.
|
||||
* Used to analyze a Wheel or egg distribution files, or their contents in
|
||||
* unzipped form, and collect information that can be used to determine the
|
||||
* associated CPE.
|
||||
*
|
||||
* @author Dale Visser
|
||||
*/
|
||||
@@ -70,7 +73,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
.getLogger(PythonDistributionAnalyzer.class);
|
||||
|
||||
/**
|
||||
* The count of directories created during analysis. This is used for creating temporary directories.
|
||||
* The count of directories created during analysis. This is used for
|
||||
* creating temporary directories.
|
||||
*/
|
||||
private static int dirCount = 0;
|
||||
|
||||
@@ -104,7 +108,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
private File tempFileLocation;
|
||||
|
||||
/**
|
||||
* Filter that detects *.dist-info files (but doesn't verify they are directories.
|
||||
* Filter that detects *.dist-info files (but doesn't verify they are
|
||||
* directories.
|
||||
*/
|
||||
private static final FilenameFilter DIST_INFO_FILTER = new SuffixFileFilter(
|
||||
".dist-info");
|
||||
@@ -164,7 +169,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@@ -206,7 +212,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* @param dependency the archive being scanned
|
||||
* @param folderFilter the filter to apply to the folder
|
||||
* @param metadataFilter the filter to apply to the meta data
|
||||
* @throws AnalysisException thrown when there is a problem analyzing the dependency
|
||||
* @throws AnalysisException thrown when there is a problem analyzing the
|
||||
* dependency
|
||||
*/
|
||||
private void collectMetadataFromArchiveFormat(Dependency dependency,
|
||||
FilenameFilter folderFilter, FilenameFilter metadataFilter)
|
||||
@@ -221,32 +228,43 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
throw new AnalysisException(ex);
|
||||
}
|
||||
|
||||
collectWheelMetadata(
|
||||
dependency,
|
||||
getMatchingFile(getMatchingFile(temp, folderFilter),
|
||||
metadataFilter));
|
||||
File matchingFile = getMatchingFile(temp, folderFilter);
|
||||
if (matchingFile != null) {
|
||||
matchingFile = getMatchingFile(matchingFile, metadataFilter);
|
||||
if (matchingFile != null) {
|
||||
collectWheelMetadata(dependency, matchingFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure a usable temporary directory is available.
|
||||
*
|
||||
* @throws Exception an AnalyzeException is thrown when the temp directory cannot be created
|
||||
* @throws InitializationException an AnalyzeException is thrown when the
|
||||
* temp directory cannot be created
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
final String msg = String.format(
|
||||
"Unable to delete temporary file '%s'.",
|
||||
tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
final String msg = String.format(
|
||||
"Unable to create directory '%s'.",
|
||||
tempFileLocation.getAbsolutePath());
|
||||
throw new AnalysisException(msg);
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
final File baseDir = Settings.getTempDirectory();
|
||||
tempFileLocation = File.createTempFile("check", "tmp", baseDir);
|
||||
if (!tempFileLocation.delete()) {
|
||||
setEnabled(false);
|
||||
final String msg = String.format(
|
||||
"Unable to delete temporary file '%s'.",
|
||||
tempFileLocation.getAbsolutePath());
|
||||
throw new InitializationException(msg);
|
||||
}
|
||||
if (!tempFileLocation.mkdirs()) {
|
||||
setEnabled(false);
|
||||
final String msg = String.format(
|
||||
"Unable to create directory '%s'.",
|
||||
tempFileLocation.getAbsolutePath());
|
||||
throw new InitializationException(msg);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create a temporary file", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +330,8 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of files that match the given filter, this does not recursively scan the directory.
|
||||
* Returns a list of files that match the given filter, this does not
|
||||
* recursively scan the directory.
|
||||
*
|
||||
* @param folder the folder to filter
|
||||
* @param filter the filter to apply to the files in the directory
|
||||
@@ -338,20 +357,30 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
if (null == manifest) {
|
||||
LOGGER.debug("Manifest file not found.");
|
||||
} else {
|
||||
InputStream in = null;
|
||||
try {
|
||||
result.load(new AutoCloseInputStream(new BufferedInputStream(
|
||||
new FileInputStream(manifest))));
|
||||
in = new BufferedInputStream(new FileInputStream(manifest));
|
||||
result.load(in);
|
||||
} catch (MessagingException e) {
|
||||
LOGGER.warn(e.getMessage(), e);
|
||||
} catch (FileNotFoundException e) {
|
||||
LOGGER.warn(e.getMessage(), e);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("failed to close input stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the next temporary destination directory for extracting an archive.
|
||||
* Retrieves the next temporary destination directory for extracting an
|
||||
* archive.
|
||||
*
|
||||
* @return a directory
|
||||
* @throws AnalysisException thrown if unable to create temporary directory
|
||||
|
||||
@@ -37,6 +37,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* Used to analyze a Python package, and collect information that can be used to
|
||||
@@ -144,10 +145,10 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
/**
|
||||
* No-op initializer implementation.
|
||||
*
|
||||
* @throws Exception never thrown
|
||||
* @throws InitializationException never thrown
|
||||
*/
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
|
||||
@@ -22,24 +22,27 @@ import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
|
||||
/**
|
||||
* Used to analyze Ruby Bundler Gemspec.lock files utilizing the 3rd party
|
||||
@@ -50,6 +53,9 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
@Experimental
|
||||
public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzer.class);
|
||||
|
||||
/**
|
||||
@@ -126,10 +132,10 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
* Initialize the analyzer. In this case, extract GrokAssembly.exe to a
|
||||
* temporary location.
|
||||
*
|
||||
* @throws Exception if anything goes wrong
|
||||
* @throws InitializationException if anything goes wrong
|
||||
*/
|
||||
@Override
|
||||
public void initializeFileTypeAnalyzer() throws Exception {
|
||||
public void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
try {
|
||||
cvedb = new CveDB();
|
||||
cvedb.open();
|
||||
@@ -137,25 +143,36 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
LOGGER.warn("Exception opening the database");
|
||||
LOGGER.debug("error", ex);
|
||||
setEnabled(false);
|
||||
throw ex;
|
||||
throw new InitializationException("Error connecting to the database", ex);
|
||||
}
|
||||
// Now, need to see if bundle-audit actually runs from this location.
|
||||
Process process = null;
|
||||
try {
|
||||
process = launchBundleAudit(Settings.getTempDirectory());
|
||||
} catch (AnalysisException ae) {
|
||||
LOGGER.warn("Exception from bundle-audit process: {}. Disabling {}", ae.getCause(), ANALYZER_NAME);
|
||||
|
||||
setEnabled(false);
|
||||
cvedb.close();
|
||||
cvedb = null;
|
||||
throw ae;
|
||||
final String msg = String.format("Exception from bundle-audit process: %s. Disabling %s", ae.getCause(), ANALYZER_NAME);
|
||||
throw new InitializationException(msg, ae);
|
||||
} catch (IOException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to create temporary file, the Ruby Bundle Audit Analyzer will be disabled", ex);
|
||||
}
|
||||
|
||||
final int exitValue = process.waitFor();
|
||||
if (0 == exitValue) {
|
||||
LOGGER.warn("Unexpected exit code from bundle-audit process. Disabling {}: {}", ANALYZER_NAME, exitValue);
|
||||
final int exitValue;
|
||||
try {
|
||||
exitValue = process.waitFor();
|
||||
} catch (InterruptedException ex) {
|
||||
setEnabled(false);
|
||||
throw new AnalysisException("Unexpected exit code from bundle-audit process.");
|
||||
final String msg = String.format("Bundle-audit process was interupted. Disabling %s", ANALYZER_NAME);
|
||||
throw new InitializationException(msg);
|
||||
}
|
||||
if (0 == exitValue) {
|
||||
setEnabled(false);
|
||||
final String msg = String.format("Unexpected exit code from bundle-audit process. Disabling %s: %s", ANALYZER_NAME, exitValue);
|
||||
throw new InitializationException(msg);
|
||||
} else {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
@@ -163,18 +180,28 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
if (!reader.ready()) {
|
||||
LOGGER.warn("Bundle-audit error stream unexpectedly not ready. Disabling " + ANALYZER_NAME);
|
||||
setEnabled(false);
|
||||
throw new AnalysisException("Bundle-audit error stream unexpectedly not ready.");
|
||||
throw new InitializationException("Bundle-audit error stream unexpectedly not ready.");
|
||||
} else {
|
||||
final String line = reader.readLine();
|
||||
if (line == null || !line.contains("Errno::ENOENT")) {
|
||||
LOGGER.warn("Unexpected bundle-audit output. Disabling {}: {}", ANALYZER_NAME, line);
|
||||
setEnabled(false);
|
||||
throw new AnalysisException("Unexpected bundle-audit output.");
|
||||
throw new InitializationException("Unexpected bundle-audit output.");
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unexpected bundle-audit encoding.", ex);
|
||||
} catch (IOException ex) {
|
||||
setEnabled(false);
|
||||
throw new InitializationException("Unable to read bundle-audit output.", ex);
|
||||
} finally {
|
||||
if (null != reader) {
|
||||
reader.close();
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Error closing reader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,11 +280,16 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
final File parentFile = dependency.getActualFile().getParentFile();
|
||||
final Process process = launchBundleAudit(parentFile);
|
||||
final int exitValue;
|
||||
try {
|
||||
process.waitFor();
|
||||
exitValue = process.waitFor();
|
||||
} catch (InterruptedException ie) {
|
||||
throw new AnalysisException("bundle-audit process interrupted", ie);
|
||||
}
|
||||
if (exitValue != 0) {
|
||||
final String msg = String.format("Unexpected exit code from bundle-audit process; exit code: %s", exitValue);
|
||||
throw new AnalysisException(msg);
|
||||
}
|
||||
BufferedReader rdr = null;
|
||||
BufferedReader errReader = null;
|
||||
try {
|
||||
@@ -456,7 +488,9 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
*/
|
||||
private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String filePath, String gem) throws IOException {
|
||||
final File gemFile = new File(Settings.getTempDirectory(), gem + "_Gemfile.lock");
|
||||
gemFile.createNewFile();
|
||||
if (!gemFile.createNewFile()) {
|
||||
throw new IOException("Unable to create temporary gem file");
|
||||
}
|
||||
final String displayFileName = String.format("%s%c%s:%s", parentName, File.separatorChar, fileName, gem);
|
||||
|
||||
FileUtils.write(gemFile, displayFileName, Charset.defaultCharset()); // unique contents to avoid dependency bundling
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Bianca Jiang. All Rights Reserved.
|
||||
* Copyright (c) 2016 IBM Corporation. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.owasp.dependencycheck.dependency.Dependency;
|
||||
* {@link RubyGemspecAnalyzer}, so it will enabled/disabled with
|
||||
* {@link RubyGemspecAnalyzer}.
|
||||
*
|
||||
* @author Bianca Jiang (biancajiang@gmail.com)
|
||||
* @author Bianca Jiang (https://twitter.com/biancajiang)
|
||||
*/
|
||||
@Experimental
|
||||
public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
|
||||
@@ -108,6 +108,7 @@ public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer {
|
||||
final File gemsDir = new File(parentDir, GEMS);
|
||||
if (gemsDir.exists()) {
|
||||
final File[] matchingFiles = gemsDir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.equals(gemName);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
@@ -88,7 +89,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() throws Exception {
|
||||
protected void initializeFileTypeAnalyzer() throws InitializationException {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@@ -211,10 +212,14 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
final File parentDir = dependencyFile.getParentFile();
|
||||
if (parentDir != null) {
|
||||
final File[] matchingFiles = parentDir.listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.contains(VERSION_FILE_NAME);
|
||||
}
|
||||
});
|
||||
if (matchingFiles == null) {
|
||||
return;
|
||||
}
|
||||
for (File f : matchingFiles) {
|
||||
try {
|
||||
final List<String> lines = FileUtils.readLines(f, Charset.defaultCharset());
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 IBM Corporation. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.EvidenceCollection;
|
||||
import org.owasp.dependencycheck.utils.FileFilterBuilder;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
* This analyzer is used to analyze the SWIFT Package Manager
|
||||
* (https://swift.org/package-manager/). It collects information about a package
|
||||
* from Package.swift files.
|
||||
*
|
||||
* @author Bianca Jiang (https://twitter.com/biancajiang)
|
||||
*/
|
||||
@Experimental
|
||||
public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer {
|
||||
|
||||
/**
|
||||
* The name of the analyzer.
|
||||
*/
|
||||
private static final String ANALYZER_NAME = "SWIFT Package Manager Analyzer";
|
||||
|
||||
/**
|
||||
* The phase that this analyzer is intended to run in.
|
||||
*/
|
||||
private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION;
|
||||
|
||||
/**
|
||||
* The file name to scan.
|
||||
*/
|
||||
public static final String SPM_FILE_NAME = "Package.swift";
|
||||
|
||||
/**
|
||||
* Filter that detects files named "package.json".
|
||||
*/
|
||||
private static final FileFilter SPM_FILE_FILTER = FileFilterBuilder.newInstance().addFilenames(SPM_FILE_NAME).build();
|
||||
|
||||
/**
|
||||
* The capture group #1 is the block variable. e.g. "import
|
||||
* PackageDescription let package = Package( name: "Gloss" )"
|
||||
*/
|
||||
private static final Pattern SPM_BLOCK_PATTERN = Pattern.compile("let[^=]+=\\s*Package\\s*\\(\\s*([^)]*)\\s*\\)", Pattern.DOTALL);
|
||||
|
||||
/**
|
||||
* Returns the FileFilter
|
||||
*
|
||||
* @return the FileFilter
|
||||
*/
|
||||
@Override
|
||||
protected FileFilter getFileFilter() {
|
||||
return SPM_FILE_FILTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeFileTypeAnalyzer() {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used in the properties file to reference the analyzer's
|
||||
* enabled property.
|
||||
*
|
||||
* @return the analyzer's enabled property setting key
|
||||
*/
|
||||
@Override
|
||||
protected String getAnalyzerEnabledSettingKey() {
|
||||
return Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void analyzeFileType(Dependency dependency, Engine engine)
|
||||
throws AnalysisException {
|
||||
|
||||
String contents;
|
||||
try {
|
||||
contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset());
|
||||
} catch (IOException e) {
|
||||
throw new AnalysisException(
|
||||
"Problem occurred while reading dependency file.", e);
|
||||
}
|
||||
final Matcher matcher = SPM_BLOCK_PATTERN.matcher(contents);
|
||||
if (matcher.find()) {
|
||||
final String packageDescription = matcher.group(1);
|
||||
if (packageDescription.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final EvidenceCollection product = dependency.getProductEvidence();
|
||||
final EvidenceCollection vendor = dependency.getVendorEvidence();
|
||||
|
||||
//SPM is currently under development for SWIFT 3. Its current metadata includes package name and dependencies.
|
||||
//Future interesting metadata: version, license, homepage, author, summary, etc.
|
||||
final String name = addStringEvidence(product, packageDescription, "name", "name", Confidence.HIGHEST);
|
||||
if (name != null && !name.isEmpty()) {
|
||||
vendor.addEvidence(SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST);
|
||||
}
|
||||
}
|
||||
setPackagePath(dependency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts evidence from the package description and adds it to the given
|
||||
* evidence collection.
|
||||
*
|
||||
* @param evidences the evidence collection to update
|
||||
* @param packageDescription the text to extract evidence from
|
||||
* @param field the name of the field being searched for
|
||||
* @param fieldPattern the field pattern within the contents to search for
|
||||
* @param confidence the confidence level of the evidence if found
|
||||
* @return the string that was added as evidence
|
||||
*/
|
||||
private String addStringEvidence(EvidenceCollection evidences,
|
||||
String packageDescription, String field, String fieldPattern, Confidence confidence) {
|
||||
String value = "";
|
||||
|
||||
final Matcher matcher = Pattern.compile(
|
||||
String.format("%s *:\\s*\"([^\"]*)", fieldPattern), Pattern.DOTALL).matcher(packageDescription);
|
||||
if (matcher.find()) {
|
||||
value = matcher.group(1);
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
value = value.trim();
|
||||
if (value.length() > 0) {
|
||||
evidences.addEvidence(SPM_FILE_NAME, field, value, confidence);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the package path on the given dependency.
|
||||
*
|
||||
* @param dep the dependency to update
|
||||
*/
|
||||
private void setPackagePath(Dependency dep) {
|
||||
final File file = new File(dep.getFilePath());
|
||||
final String parent = file.getParent();
|
||||
if (parent != null) {
|
||||
dep.setPackagePath(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.analyzer;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
|
||||
|
||||
/**
|
||||
* The suppression analyzer processes an externally defined XML document that complies with the suppressions.xsd schema.
|
||||
|
||||
@@ -61,8 +61,8 @@ public class CentralSearch {
|
||||
/**
|
||||
* Creates a NexusSearch for the given repository URL.
|
||||
*
|
||||
* @param rootURL the URL of the repository on which searches should execute. Only parameters are added to this (so it should
|
||||
* end in /select)
|
||||
* @param rootURL the URL of the repository on which searches should
|
||||
* execute. Only parameters are added to this (so it should end in /select)
|
||||
*/
|
||||
public CentralSearch(URL rootURL) {
|
||||
this.rootURL = rootURL;
|
||||
@@ -76,18 +76,20 @@ public class CentralSearch {
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the configured Central URL for the given sha1 hash. If the artifact is found, a <code>MavenArtifact</code> is
|
||||
* populated with the GAV.
|
||||
* Searches the configured Central URL for the given sha1 hash. If the
|
||||
* artifact is found, a <code>MavenArtifact</code> is populated with the
|
||||
* GAV.
|
||||
*
|
||||
* @param sha1 the SHA-1 hash string for which to search
|
||||
* @return the populated Maven GAV.
|
||||
* @throws IOException if it's unable to connect to the specified repository or if the specified artifact is not found.
|
||||
* @throws IOException if it's unable to connect to the specified repository
|
||||
* or if the specified artifact is not found.
|
||||
*/
|
||||
public List<MavenArtifact> searchSha1(String sha1) throws IOException {
|
||||
if (null == sha1 || !sha1.matches("^[0-9A-Fa-f]{40}$")) {
|
||||
throw new IllegalArgumentException("Invalid SHA1 format");
|
||||
}
|
||||
|
||||
List<MavenArtifact> result = null;
|
||||
final URL url = new URL(rootURL + String.format("?q=1:\"%s\"&wt=xml", sha1));
|
||||
|
||||
LOGGER.debug("Searching Central url {}", url);
|
||||
@@ -116,7 +118,7 @@ public class CentralSearch {
|
||||
if ("0".equals(numFound)) {
|
||||
missing = true;
|
||||
} else {
|
||||
final List<MavenArtifact> result = new ArrayList<MavenArtifact>();
|
||||
result = new ArrayList<MavenArtifact>();
|
||||
final NodeList docs = (NodeList) xpath.evaluate("/response/result/doc", doc, XPathConstants.NODESET);
|
||||
for (int i = 0; i < docs.getLength(); i++) {
|
||||
final String g = xpath.evaluate("./str[@name='g']", docs.item(i));
|
||||
@@ -144,16 +146,12 @@ public class CentralSearch {
|
||||
useHTTPS = true;
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.trace("Version: {}", v);
|
||||
result.add(new MavenArtifact(g, a, v, jarAvailable, pomAvailable, useHTTPS));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Anything else is jacked up XML stuff that we really can't recover
|
||||
// from well
|
||||
// Anything else is jacked up XML stuff that we really can't recover from well
|
||||
throw new IOException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@@ -162,10 +160,9 @@ public class CentralSearch {
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug("Could not connect to Central received response code: {} {}",
|
||||
conn.getResponseCode(), conn.getResponseMessage());
|
||||
conn.getResponseCode(), conn.getResponseMessage());
|
||||
throw new IOException("Could not connect to Central");
|
||||
}
|
||||
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.owasp.dependencycheck.data.lucene.FieldAnalyzer;
|
||||
import org.owasp.dependencycheck.data.lucene.LuceneUtils;
|
||||
import org.owasp.dependencycheck.data.lucene.SearchFieldAnalyzer;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
@@ -48,8 +47,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An in memory lucene index that contains the vendor/product combinations from the CPE (application) identifiers within the NVD
|
||||
* CVE data.
|
||||
* An in memory lucene index that contains the vendor/product combinations from
|
||||
* the CPE (application) identifiers within the NVD CVE data.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -101,11 +100,11 @@ public final class CpeMemoryIndex {
|
||||
/**
|
||||
* The search field analyzer for the product field.
|
||||
*/
|
||||
private SearchFieldAnalyzer productSearchFieldAnalyzer;
|
||||
private SearchFieldAnalyzer productFieldAnalyzer;
|
||||
/**
|
||||
* The search field analyzer for the vendor field.
|
||||
*/
|
||||
private SearchFieldAnalyzer vendorSearchFieldAnalyzer;
|
||||
private SearchFieldAnalyzer vendorFieldAnalyzer;
|
||||
|
||||
/**
|
||||
* Creates and loads data into an in memory index.
|
||||
@@ -144,17 +143,6 @@ public final class CpeMemoryIndex {
|
||||
return openState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the indexing analyzer for the CPE Index.
|
||||
*
|
||||
* @return the CPE Analyzer.
|
||||
*/
|
||||
private Analyzer createIndexingAnalyzer() {
|
||||
final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
|
||||
fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer());
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Analyzer for searching the CPE Index.
|
||||
*
|
||||
@@ -163,12 +151,12 @@ public final class CpeMemoryIndex {
|
||||
private Analyzer createSearchingAnalyzer() {
|
||||
final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
|
||||
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);
|
||||
productFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
vendorFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION);
|
||||
fieldAnalyzers.put(Fields.PRODUCT, productFieldAnalyzer);
|
||||
fieldAnalyzers.put(Fields.VENDOR, vendorFieldAnalyzer);
|
||||
|
||||
return new PerFieldAnalyzerWrapper(new FieldAnalyzer(LuceneUtils.CURRENT_VERSION), fieldAnalyzers);
|
||||
return new PerFieldAnalyzerWrapper(new KeywordAnalyzer(), fieldAnalyzers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,7 +194,7 @@ public final class CpeMemoryIndex {
|
||||
Analyzer analyzer = null;
|
||||
IndexWriter indexWriter = null;
|
||||
try {
|
||||
analyzer = createIndexingAnalyzer();
|
||||
analyzer = createSearchingAnalyzer();
|
||||
final IndexWriterConfig conf = new IndexWriterConfig(LuceneUtils.CURRENT_VERSION, analyzer);
|
||||
indexWriter = new IndexWriter(index, conf);
|
||||
try {
|
||||
@@ -221,9 +209,13 @@ public final class CpeMemoryIndex {
|
||||
|
||||
final Set<Pair<String, String>> data = cve.getVendorProductList();
|
||||
for (Pair<String, String> pair : data) {
|
||||
v.setStringValue(pair.getLeft());
|
||||
p.setStringValue(pair.getRight());
|
||||
indexWriter.addDocument(doc);
|
||||
//todo figure out why there are null products
|
||||
if (pair.getLeft() != null && pair.getRight() != null) {
|
||||
v.setStringValue(pair.getLeft());
|
||||
p.setStringValue(pair.getRight());
|
||||
indexWriter.addDocument(doc);
|
||||
resetFieldAnalyzer();
|
||||
}
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
@@ -254,14 +246,14 @@ public final class CpeMemoryIndex {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the searching analyzers
|
||||
* Resets the product and vendor field analyzers.
|
||||
*/
|
||||
private void resetSearchingAnalyzer() {
|
||||
if (productSearchFieldAnalyzer != null) {
|
||||
productSearchFieldAnalyzer.clear();
|
||||
private void resetFieldAnalyzer() {
|
||||
if (productFieldAnalyzer != null) {
|
||||
productFieldAnalyzer.clear();
|
||||
}
|
||||
if (vendorSearchFieldAnalyzer != null) {
|
||||
vendorSearchFieldAnalyzer.clear();
|
||||
if (vendorFieldAnalyzer != null) {
|
||||
vendorFieldAnalyzer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +264,8 @@ public final class CpeMemoryIndex {
|
||||
* @param maxQueryResults the maximum number of documents to return
|
||||
* @return the TopDocs found by the search
|
||||
* @throws ParseException thrown when the searchString is invalid
|
||||
* @throws IOException is thrown if there is an issue with the underlying Index
|
||||
* @throws IOException is thrown if there is an issue with the underlying
|
||||
* Index
|
||||
*/
|
||||
public TopDocs search(String searchString, int maxQueryResults) throws ParseException, IOException {
|
||||
if (searchString == null || searchString.trim().isEmpty()) {
|
||||
@@ -293,7 +286,7 @@ public final class CpeMemoryIndex {
|
||||
* @throws IOException thrown if there is an IOException
|
||||
*/
|
||||
public TopDocs search(Query query, int maxQueryResults) throws CorruptIndexException, IOException {
|
||||
resetSearchingAnalyzer();
|
||||
resetFieldAnalyzer();
|
||||
return indexSearcher.search(query, maxQueryResults);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,15 @@ import org.apache.lucene.util.Version;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A Lucene Analyzer that utilizes the WhitespaceTokenizer, WordDelimiterFilter, LowerCaseFilter, and StopFilter. The intended
|
||||
* purpose of this Analyzer is to index the CPE fields vendor and product.</p>
|
||||
* A Lucene Analyzer that utilizes the WhitespaceTokenizer, WordDelimiterFilter,
|
||||
* LowerCaseFilter, and StopFilter. The intended purpose of this Analyzer is to
|
||||
* index the CPE fields vendor and product.</p>
|
||||
*
|
||||
* @author Jeremy Long
|
||||
* @deprecated the field analyzer should not be used, instead use the
|
||||
* SearchFieldAnalyzer so that the token analyzing filter is used.
|
||||
*/
|
||||
@Deprecated
|
||||
public class FieldAnalyzer extends Analyzer {
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.owasp.dependencycheck.data.nvdcve;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.DriverManager;
|
||||
@@ -36,8 +36,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
*/
|
||||
@@ -87,12 +89,13 @@ public final class ConnectionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the connection factory. Ensuring that the appropriate drivers are loaded and that a connection can be made
|
||||
* successfully.
|
||||
* Initializes the connection factory. Ensuring that the appropriate drivers
|
||||
* are loaded and that a connection can be made successfully.
|
||||
*
|
||||
* @throws DatabaseException thrown if we are unable to connect to the database
|
||||
* @throws DatabaseException thrown if we are unable to connect to the
|
||||
* database
|
||||
*/
|
||||
public static synchronized void initialize() throws DatabaseException {
|
||||
public static void initialize() throws DatabaseException {
|
||||
//this only needs to be called once.
|
||||
if (connectionString != null) {
|
||||
return;
|
||||
@@ -188,11 +191,12 @@ public final class ConnectionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up resources and unloads any registered database drivers. This needs to be called to ensure the driver is
|
||||
* unregistered prior to the finalize method being called as during shutdown the class loader used to load the driver may be
|
||||
* unloaded prior to the driver being de-registered.
|
||||
* Cleans up resources and unloads any registered database drivers. This
|
||||
* needs to be called to ensure the driver is unregistered prior to the
|
||||
* finalize method being called as during shutdown the class loader used to
|
||||
* load the driver may be unloaded prior to the driver being de-registered.
|
||||
*/
|
||||
public static synchronized void cleanup() {
|
||||
public static void cleanup() {
|
||||
if (driver != null) {
|
||||
try {
|
||||
DriverManager.deregisterDriver(driver);
|
||||
@@ -210,10 +214,12 @@ public final class ConnectionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new database connection object per the database configuration.
|
||||
* Constructs a new database connection object per the database
|
||||
* configuration.
|
||||
*
|
||||
* @return a database connection object
|
||||
* @throws DatabaseException thrown if there is an exception loading the database connection
|
||||
* @throws DatabaseException thrown if there is an exception loading the
|
||||
* database connection
|
||||
*/
|
||||
public static Connection getConnection() throws DatabaseException {
|
||||
initialize();
|
||||
@@ -228,10 +234,12 @@ public final class ConnectionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the H2 database file exists. If it does not exist then the data structure will need to be created.
|
||||
* 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
|
||||
* @throws IOException thrown if the data directory does not exist and
|
||||
* cannot be created
|
||||
*/
|
||||
private static boolean h2DataFileExists() throws IOException {
|
||||
final File dir = Settings.getDataDirectory();
|
||||
@@ -241,7 +249,8 @@ public final class ConnectionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the database structure (tables and indexes) to store the CVE data.
|
||||
* 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
|
||||
@@ -271,14 +280,17 @@ public final class ConnectionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the database schema by loading the upgrade script for the version specified. The intended use is that if the
|
||||
* current schema version is 2.9 then we would call updateSchema(conn, "2.9"). This would load the upgrade_2.9.sql file and
|
||||
* execute it against the database. The upgrade script must update the 'version' in the properties table.
|
||||
* Updates the database schema by loading the upgrade script for the version
|
||||
* specified. The intended use is that if the current schema version is 2.9
|
||||
* then we would call updateSchema(conn, "2.9"). This would load the
|
||||
* upgrade_2.9.sql file and execute it against the database. The upgrade
|
||||
* script must update the 'version' in the properties table.
|
||||
*
|
||||
* @param conn the database connection object
|
||||
* @param appExpectedVersion the schema version that the application expects
|
||||
* @param currentDbVersion the current schema version of the database
|
||||
* @throws DatabaseException thrown if there is an exception upgrading the database schema
|
||||
* @throws DatabaseException thrown if there is an exception upgrading the
|
||||
* database schema
|
||||
*/
|
||||
private static void updateSchema(Connection conn, DependencyVersion appExpectedVersion, DependencyVersion currentDbVersion)
|
||||
throws DatabaseException {
|
||||
@@ -340,26 +352,35 @@ public final class ConnectionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Counter to ensure that calls to ensureSchemaVersion does not end up in an endless loop.
|
||||
* Counter to ensure that calls to ensureSchemaVersion does not end up in an
|
||||
* endless loop.
|
||||
*/
|
||||
private static int callDepth = 0;
|
||||
|
||||
/**
|
||||
* Uses the provided connection to check the specified schema version within the database.
|
||||
* 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
|
||||
* @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;
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
//TODO convert this to use DatabaseProperties
|
||||
cs = conn.prepareCall("SELECT value FROM properties WHERE id = 'version'");
|
||||
rs = cs.executeQuery();
|
||||
ps = conn.prepareStatement("SELECT value FROM properties WHERE id = 'version'");
|
||||
rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
final DependencyVersion appDbVersion = DependencyVersionUtil.parseVersion(DB_SCHEMA_VERSION);
|
||||
if (appDbVersion == null) {
|
||||
throw new DatabaseException("Invalid application database schema");
|
||||
}
|
||||
final DependencyVersion db = DependencyVersionUtil.parseVersion(rs.getString(1));
|
||||
if (db == null) {
|
||||
throw new DatabaseException("Invalid database schema");
|
||||
}
|
||||
if (appDbVersion.compareTo(db) > 0) {
|
||||
LOGGER.debug("Current Schema: {}", DB_SCHEMA_VERSION);
|
||||
LOGGER.debug("DB Schema: {}", rs.getString(1));
|
||||
@@ -376,7 +397,7 @@ public final class ConnectionFactory {
|
||||
throw new DatabaseException("Unable to check the database schema version");
|
||||
} finally {
|
||||
DBUtils.closeResultSet(rs);
|
||||
DBUtils.closeStatement(cs);
|
||||
DBUtils.closeStatement(ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
@@ -69,17 +68,16 @@ public class CveDB {
|
||||
private ResourceBundle statementBundle = null;
|
||||
|
||||
/**
|
||||
* Creates a new CveDB object and opens the database
|
||||
* connection. Note, the connection must be closed by the caller by calling
|
||||
* the close method. ======= Does the underlying connection support batch
|
||||
* operations?
|
||||
* Creates a new CveDB object and opens the database connection. Note, the
|
||||
* connection must be closed by the caller by calling the close method.
|
||||
* ======= Does the underlying connection support batch operations?
|
||||
*/
|
||||
private boolean batchSupported;
|
||||
|
||||
/**
|
||||
* Creates a new CveDB object and opens the database connection. Note, the
|
||||
* connection must be closed by the caller by calling the close method.
|
||||
*
|
||||
*
|
||||
* @throws DatabaseException thrown if there is an exception opening the
|
||||
* database.
|
||||
*/
|
||||
@@ -89,10 +87,12 @@ public class CveDB {
|
||||
open();
|
||||
try {
|
||||
final String databaseProductName = conn.getMetaData().getDatabaseProductName();
|
||||
batchSupported = conn.getMetaData().supportsBatchUpdates();
|
||||
LOGGER.debug("Database dialect: {}", databaseProductName);
|
||||
final Locale dbDialect = new Locale(databaseProductName);
|
||||
statementBundle = ResourceBundle.getBundle("data/dbStatements", dbDialect);
|
||||
if ("mysql".equalsIgnoreCase(databaseProductName)) {
|
||||
batchSupported = false;
|
||||
}
|
||||
} catch (SQLException se) {
|
||||
LOGGER.warn("Problem loading database specific dialect!", se);
|
||||
statementBundle = ResourceBundle.getBundle("data/dbStatements");
|
||||
@@ -660,7 +660,7 @@ public class CveDB {
|
||||
+ "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please "
|
||||
+ "create a log file (see documentation at http://jeremylong.github.io/DependencyCheck/) and open a ticket at "
|
||||
+ "https://github.com/jeremylong/DependencyCheck/issues and include the log file.\n\n",
|
||||
dd, dd, Settings.getString(Settings.KEYS.APPLICATION_VAME));
|
||||
dd, dd, Settings.getString(Settings.KEYS.APPLICATION_NAME));
|
||||
LOGGER.debug("", ex);
|
||||
} finally {
|
||||
DBUtils.closeResultSet(rs);
|
||||
@@ -813,14 +813,14 @@ public class CveDB {
|
||||
* Deletes unused dictionary entries from the database.
|
||||
*/
|
||||
public void deleteUnusedCpe() {
|
||||
CallableStatement cs = null;
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
cs = getConnection().prepareCall(statementBundle.getString("DELETE_UNUSED_DICT_CPE"));
|
||||
cs.executeUpdate();
|
||||
ps = getConnection().prepareStatement(statementBundle.getString("DELETE_UNUSED_DICT_CPE"));
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException ex) {
|
||||
LOGGER.error("Unable to delete CPE dictionary entries", ex);
|
||||
} finally {
|
||||
DBUtils.closeStatement(cs);
|
||||
DBUtils.closeStatement(ps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -837,7 +837,7 @@ public class CveDB {
|
||||
public void addCpe(String cpe, String vendor, String product) {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
ps = getConnection().prepareCall(statementBundle.getString("ADD_DICT_CPE"));
|
||||
ps = getConnection().prepareStatement(statementBundle.getString("ADD_DICT_CPE"));
|
||||
ps.setString(1, cpe);
|
||||
ps.setString(2, vendor);
|
||||
ps.setString(3, product);
|
||||
|
||||
@@ -158,6 +158,7 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
final String originalPath = file.getPath();
|
||||
final File gzip = new File(originalPath + ".gz");
|
||||
if (gzip.isFile() && !gzip.delete()) {
|
||||
LOGGER.debug("Failed to delete intial temporary file {}", gzip.toString());
|
||||
gzip.deleteOnExit();
|
||||
}
|
||||
if (!file.renameTo(gzip)) {
|
||||
@@ -192,8 +193,9 @@ public class CpeUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
LOGGER.trace("ignore", ex);
|
||||
}
|
||||
}
|
||||
if (gzip.isFile()) {
|
||||
FileUtils.deleteQuietly(gzip);
|
||||
if (gzip.isFile() && !FileUtils.deleteQuietly(gzip)) {
|
||||
LOGGER.debug("Failed to delete temporary file {}", gzip.toString());
|
||||
gzip.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Checks the gh-pages dependency-check site to determine the current released
|
||||
* version number. If the released version number is greater then the running
|
||||
* version number a warning is printed recommending that an upgrade be
|
||||
* performed.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -59,12 +63,14 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
private CveDB cveDB = null;
|
||||
|
||||
/**
|
||||
* The version retrieved from the database properties or web to check against.
|
||||
* The version retrieved from the database properties or web to check
|
||||
* against.
|
||||
*/
|
||||
private String updateToVersion;
|
||||
|
||||
/**
|
||||
* Getter for updateToVersion - only used for testing. Represents the version retrieved from the database.
|
||||
* Getter for updateToVersion - only used for testing. Represents the
|
||||
* version retrieved from the database.
|
||||
*
|
||||
* @return the version to test
|
||||
*/
|
||||
@@ -73,7 +79,8 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for updateToVersion - only used for testing. Represents the version retrieved from the database.
|
||||
* Setter for updateToVersion - only used for testing. Represents the
|
||||
* version retrieved from the database.
|
||||
*
|
||||
* @param version the version to test
|
||||
*/
|
||||
@@ -81,9 +88,16 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
updateToVersion = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the current released version number and compares it to the
|
||||
* running engine's version number. If the released version number is newer
|
||||
* a warning is printed recommending an upgrade.
|
||||
*
|
||||
* @throws UpdateException thrown if the local database properties could not
|
||||
* be updated
|
||||
*/
|
||||
@Override
|
||||
public void update() throws UpdateException {
|
||||
|
||||
try {
|
||||
if (Settings.getBoolean(Settings.KEYS.AUTO_UPDATE)) {
|
||||
openDatabase();
|
||||
@@ -109,20 +123,21 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
LOGGER.debug("Unable to determine if autoupdate is enabled", ex);
|
||||
} finally {
|
||||
closeDatabase();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a new version of the dependency-check engine has been released.
|
||||
* Determines if a new version of the dependency-check engine has been
|
||||
* released.
|
||||
*
|
||||
* @param lastChecked the epoch time of the last version check
|
||||
* @param now the current epoch time
|
||||
* @param properties the database properties object
|
||||
* @param currentVersion the current version of dependency-check
|
||||
* @return <code>true</code> if a newer version of the database has been released; otherwise <code>false</code>
|
||||
* @throws UpdateException thrown if there is an error connecting to the github documentation site or accessing the local
|
||||
* database.
|
||||
* @return <code>true</code> if a newer version of the database has been
|
||||
* released; otherwise <code>false</code>
|
||||
* @throws UpdateException thrown if there is an error connecting to the
|
||||
* github documentation site or accessing the local database.
|
||||
*/
|
||||
protected boolean shouldUpdate(final long lastChecked, final long now, final DatabaseProperties properties,
|
||||
String currentVersion) throws UpdateException {
|
||||
@@ -185,7 +200,8 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current released version number from the github documentation site.
|
||||
* Retrieves the current released version number from the github
|
||||
* documentation site.
|
||||
*
|
||||
* @return the current released version number
|
||||
*/
|
||||
@@ -204,11 +220,11 @@ public class EngineVersionCheck implements CachedWebDataSource {
|
||||
return releaseVersion.trim();
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
LOGGER.debug("unable to retrieve current release version of dependency-check", ex);
|
||||
LOGGER.debug("Unable to retrieve current release version of dependency-check - malformed url?");
|
||||
} catch (URLConnectionFailureException ex) {
|
||||
LOGGER.debug("unable to retrieve current release version of dependency-check", ex);
|
||||
LOGGER.debug("Unable to retrieve current release version of dependency-check - connection failed");
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("unable to retrieve current release version of dependency-check", ex);
|
||||
LOGGER.debug("Unable to retrieve current release version of dependency-check - i/o exception");
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.slf4j.LoggerFactory;
|
||||
public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
|
||||
/**
|
||||
* The logger
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NvdCveUpdater.class);
|
||||
/**
|
||||
@@ -59,9 +59,8 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
public static final int MAX_THREAD_POOL_SIZE = Settings.getInt(Settings.KEYS.MAX_DOWNLOAD_THREAD_POOL_SIZE, 3);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Downloads the latest NVD CVE XML file from the web and imports it into
|
||||
* the current CVE Database.</p>
|
||||
* the current CVE Database.
|
||||
*
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
@@ -78,15 +77,13 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
}
|
||||
if (autoUpdate && checkUpdate()) {
|
||||
final UpdateableNvdCve updateable = getUpdatesNeeded();
|
||||
getProperties().save(DatabaseProperties.LAST_CHECKED, Long.toString(System.currentTimeMillis()));
|
||||
if (updateable.isUpdateNeeded()) {
|
||||
performUpdate(updateable);
|
||||
}
|
||||
getProperties().save(DatabaseProperties.LAST_CHECKED, Long.toString(System.currentTimeMillis()));
|
||||
}
|
||||
} catch (MalformedURLException ex) {
|
||||
LOGGER.warn(
|
||||
"NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.");
|
||||
LOGGER.debug("", ex);
|
||||
throw new UpdateException("NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.", ex);
|
||||
} catch (DownloadFailedException ex) {
|
||||
LOGGER.warn(
|
||||
"Unable to download the NVD CVE data; the results may not include the most recent CPE/CVEs from the NVD.");
|
||||
@@ -94,7 +91,7 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
LOGGER.info(
|
||||
"If you are behind a proxy you may need to configure dependency-check to use the proxy.");
|
||||
}
|
||||
LOGGER.debug("", ex);
|
||||
throw new UpdateException("Unable to download the NVD CVE data.", ex);
|
||||
} finally {
|
||||
closeDataStores();
|
||||
}
|
||||
@@ -107,9 +104,9 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
* checking again. A database property stores the timestamp of the last
|
||||
* check.
|
||||
*
|
||||
* @return true to proceed with the check, or false to skip.
|
||||
* @return true to proceed with the check, or false to skip
|
||||
* @throws UpdateException thrown when there is an issue checking for
|
||||
* updates.
|
||||
* updates
|
||||
*/
|
||||
private boolean checkUpdate() throws UpdateException {
|
||||
boolean proceed = true;
|
||||
@@ -159,94 +156,86 @@ public class NvdCveUpdater extends BaseUpdater implements CachedWebDataSource {
|
||||
* @throws UpdateException is thrown if there is an error updating the
|
||||
* database
|
||||
*/
|
||||
public void performUpdate(UpdateableNvdCve updateable) throws UpdateException {
|
||||
private void performUpdate(UpdateableNvdCve updateable) throws UpdateException {
|
||||
int maxUpdates = 0;
|
||||
try {
|
||||
for (NvdCveInfo cve : updateable) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
maxUpdates += 1;
|
||||
for (NvdCveInfo cve : updateable) {
|
||||
if (cve.getNeedsUpdate()) {
|
||||
maxUpdates += 1;
|
||||
}
|
||||
}
|
||||
if (maxUpdates <= 0) {
|
||||
return;
|
||||
}
|
||||
if (maxUpdates > 3) {
|
||||
LOGGER.info("NVD CVE requires several updates; this could take a couple of minutes.");
|
||||
}
|
||||
|
||||
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 DownloadTask call = new DownloadTask(cve, processExecutor, getCveDB(), Settings.getInstance());
|
||||
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.debug("Thread was interrupted during download", ex);
|
||||
throw new UpdateException("The download was interrupted", ex);
|
||||
} catch (ExecutionException ex) {
|
||||
downloadExecutors.shutdownNow();
|
||||
processExecutor.shutdownNow();
|
||||
|
||||
LOGGER.debug("Thread was interrupted during download execution", ex);
|
||||
throw new UpdateException("The execution of the download was interrupted", ex);
|
||||
}
|
||||
if (task == null) {
|
||||
downloadExecutors.shutdownNow();
|
||||
processExecutor.shutdownNow();
|
||||
LOGGER.debug("Thread was interrupted during download");
|
||||
throw new UpdateException("The download was interrupted; 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.debug("Thread was interrupted during processing", ex);
|
||||
throw new UpdateException(ex);
|
||||
} catch (ExecutionException ex) {
|
||||
processExecutor.shutdownNow();
|
||||
LOGGER.debug("Execution Exception during process", ex);
|
||||
throw new UpdateException(ex);
|
||||
} finally {
|
||||
processExecutor.shutdown();
|
||||
}
|
||||
if (maxUpdates <= 0) {
|
||||
return;
|
||||
}
|
||||
if (maxUpdates > 3) {
|
||||
LOGGER.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 DownloadTask call = new DownloadTask(cve, processExecutor, getCveDB(), Settings.getInstance());
|
||||
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.debug("Thread was interrupted during download", ex);
|
||||
throw new UpdateException("The download was interrupted", ex);
|
||||
} catch (ExecutionException ex) {
|
||||
downloadExecutors.shutdownNow();
|
||||
processExecutor.shutdownNow();
|
||||
|
||||
LOGGER.debug("Thread was interrupted during download execution", ex);
|
||||
throw new UpdateException("The execution of the download was interrupted", ex);
|
||||
}
|
||||
if (task == null) {
|
||||
downloadExecutors.shutdownNow();
|
||||
processExecutor.shutdownNow();
|
||||
LOGGER.debug("Thread was interrupted during download");
|
||||
throw new UpdateException("The download was interrupted; 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.debug("Thread was interrupted during processing", ex);
|
||||
throw new UpdateException(ex);
|
||||
} catch (ExecutionException ex) {
|
||||
processExecutor.shutdownNow();
|
||||
LOGGER.debug("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)
|
||||
getProperties().save(updateable.get(MODIFIED));
|
||||
LOGGER.info("Begin database maintenance.");
|
||||
getCveDB().cleanupDatabase();
|
||||
LOGGER.info("End database maintenance.");
|
||||
}
|
||||
} finally {
|
||||
closeDataStores();
|
||||
if (maxUpdates >= 1) { //ensure the modified file date gets written (we may not have actually updated it)
|
||||
getProperties().save(updateable.get(MODIFIED));
|
||||
LOGGER.info("Begin database maintenance.");
|
||||
getCveDB().cleanupDatabase();
|
||||
LOGGER.info("End database maintenance.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.owasp.dependencycheck.data.update.NvdCveUpdater;
|
||||
import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.Attributes;
|
||||
@@ -40,7 +41,12 @@ public class CPEHandler extends DefaultHandler {
|
||||
*/
|
||||
private static final String CURRENT_SCHEMA_VERSION = "2.3";
|
||||
/**
|
||||
* The text content of the node being processed. This can be used during the end element event.
|
||||
* The Starts with expression to filter CVE entries by CPE.
|
||||
*/
|
||||
private static final String CPE_STARTS_WITH = Settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:/a:");
|
||||
/**
|
||||
* The text content of the node being processed. This can be used during the
|
||||
* end element event.
|
||||
*/
|
||||
private StringBuilder nodeText = null;
|
||||
/**
|
||||
@@ -72,7 +78,8 @@ public class CPEHandler extends DefaultHandler {
|
||||
* @param localName the local name
|
||||
* @param qName the qualified name
|
||||
* @param attributes the attributes
|
||||
* @throws SAXException thrown if there is an exception processing the element
|
||||
* @throws SAXException thrown if there is an exception processing the
|
||||
* element
|
||||
*/
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
@@ -82,7 +89,7 @@ public class CPEHandler extends DefaultHandler {
|
||||
final String temp = attributes.getValue("deprecated");
|
||||
final String value = attributes.getValue("name");
|
||||
final boolean delete = "true".equalsIgnoreCase(temp);
|
||||
if (!delete && value.startsWith("cpe:/a:") && value.length() > 7) {
|
||||
if (!delete && value.startsWith(CPE_STARTS_WITH) && value.length() > 7) {
|
||||
try {
|
||||
final Cpe cpe = new Cpe(value);
|
||||
data.add(cpe);
|
||||
@@ -123,7 +130,8 @@ public class CPEHandler extends DefaultHandler {
|
||||
* @param ch the char array
|
||||
* @param start the start position of the data read
|
||||
* @param length the length of the data read
|
||||
* @throws SAXException thrown if there is an exception processing the characters
|
||||
* @throws SAXException thrown if there is an exception processing the
|
||||
* characters
|
||||
*/
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
@@ -133,12 +141,14 @@ public class CPEHandler extends DefaultHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the end element event. Stores the CPE data in the Cve Database if the cpe item node is ending.
|
||||
* Handles the end element event. Stores the CPE data in the Cve Database if
|
||||
* the cpe item node is ending.
|
||||
*
|
||||
* @param uri the element's uri
|
||||
* @param localName the local name
|
||||
* @param qName the qualified name
|
||||
* @throws SAXException thrown if there is an exception processing the element
|
||||
* @throws SAXException thrown if there is an exception processing the
|
||||
* element
|
||||
*/
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
@@ -177,7 +187,8 @@ public class CPEHandler extends DefaultHandler {
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="The Element Class that maintains state information about the current node">
|
||||
/**
|
||||
* A simple class to maintain information about the current element while parsing the CPE XML.
|
||||
* A simple class to maintain information about the current element while
|
||||
* parsing the CPE XML.
|
||||
*/
|
||||
protected static final class Element {
|
||||
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.update.exception;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception used when an error occurs reading a setting.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class UpdateException extends IOException {
|
||||
public class UpdateException extends Exception {
|
||||
|
||||
/**
|
||||
* The serial version uid.
|
||||
|
||||
@@ -55,8 +55,9 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
|
||||
* @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
|
||||
* @param settings a reference to the global settings object; this is necessary so that when the thread is started the
|
||||
* dependencies have a correct reference to the global settings.
|
||||
* @param settings a reference to the global settings object; this is
|
||||
* necessary so that when the thread is started the dependencies have a
|
||||
* correct reference to the global settings.
|
||||
* @throws UpdateException thrown if temporary files could not be created
|
||||
*/
|
||||
public DownloadTask(NvdCveInfo nvdCveInfo, ExecutorService processor, CveDB cveDB, Settings settings) throws UpdateException {
|
||||
@@ -205,25 +206,13 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
|
||||
* 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();
|
||||
}
|
||||
if (first != null && first.exists() && first.delete()) {
|
||||
LOGGER.debug("Failed to delete first temporary file {}", second.toString());
|
||||
first.deleteOnExit();
|
||||
}
|
||||
try {
|
||||
deleted = false;
|
||||
if (second != null && second.exists()) {
|
||||
deleted = second.delete();
|
||||
}
|
||||
} finally {
|
||||
if (second != null && (second.exists() || !deleted)) {
|
||||
second.deleteOnExit();
|
||||
}
|
||||
if (second != null && second.exists() && !second.delete()) {
|
||||
LOGGER.debug("Failed to delete second temporary file {}", second.toString());
|
||||
second.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +257,8 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the file contained in a gzip archive. The extracted file is placed in the exact same path as the file specified.
|
||||
* Extracts the file contained in a gzip archive. The extracted file is
|
||||
* placed in the exact same path as the file specified.
|
||||
*
|
||||
* @param file the archive file
|
||||
* @throws FileNotFoundException thrown if the file does not exist
|
||||
@@ -278,6 +268,7 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
|
||||
final String originalPath = file.getPath();
|
||||
final File gzip = new File(originalPath + ".gz");
|
||||
if (gzip.isFile() && !gzip.delete()) {
|
||||
LOGGER.debug("Failed to delete initial temporary file when extracting 'gz' {}", gzip.toString());
|
||||
gzip.deleteOnExit();
|
||||
}
|
||||
if (!file.renameTo(gzip)) {
|
||||
@@ -312,8 +303,9 @@ public class DownloadTask implements Callable<Future<ProcessTask>> {
|
||||
LOGGER.trace("ignore", ex);
|
||||
}
|
||||
}
|
||||
if (gzip.isFile()) {
|
||||
FileUtils.deleteQuietly(gzip);
|
||||
if (gzip.isFile() && !FileUtils.deleteQuietly(gzip)) {
|
||||
LOGGER.debug("Failed to delete temporary file when extracting 'gz' {}", gzip.toString());
|
||||
gzip.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import org.owasp.dependencycheck.utils.DownloadFailedException;
|
||||
import org.owasp.dependencycheck.utils.Downloader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Contains a collection of updateable NvdCveInfo objects. This is used to determine which files need to be downloaded and
|
||||
@@ -34,6 +36,10 @@ import org.owasp.dependencycheck.utils.Downloader;
|
||||
*/
|
||||
public class UpdateableNvdCve implements Iterable<NvdCveInfo>, Iterator<NvdCveInfo> {
|
||||
|
||||
/**
|
||||
* A reference to the logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(UpdateableNvdCve.class);
|
||||
/**
|
||||
* A collection of sources of data.
|
||||
*/
|
||||
@@ -91,6 +97,7 @@ public class UpdateableNvdCve implements Iterable<NvdCveInfo>, Iterator<NvdCveIn
|
||||
item.setId(id);
|
||||
item.setUrl(url);
|
||||
item.setOldSchemaVersionUrl(oldUrl);
|
||||
LOGGER.debug("Checking for updates from: {}", url);
|
||||
item.setTimestamp(Downloader.getLastModified(new URL(url)));
|
||||
collection.put(id, item);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.owasp.dependencycheck.dependency;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
|
||||
/**
|
||||
* An external reference for a vulnerability. This contains a name, URL, and a
|
||||
@@ -141,18 +142,10 @@ public class Reference implements Serializable, Comparable<Reference> {
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Reference o) {
|
||||
if (source.equals(o.source)) {
|
||||
if (name.equals(o.name)) {
|
||||
if (url.equals(o.url)) {
|
||||
return 0; //they are equal
|
||||
} else {
|
||||
return url.compareTo(o.url);
|
||||
}
|
||||
} else {
|
||||
return name.compareTo(o.name);
|
||||
}
|
||||
} else {
|
||||
return source.compareTo(o.source);
|
||||
}
|
||||
return new CompareToBuilder()
|
||||
.append(source, o.source)
|
||||
.append(name, o.name)
|
||||
.append(url, o.url)
|
||||
.toComparison();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Iterator;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
|
||||
/**
|
||||
* Contains the information about a vulnerability.
|
||||
@@ -162,7 +162,8 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
|
||||
* Adds an entry for vulnerable software.
|
||||
*
|
||||
* @param cpe string representation of a cpe
|
||||
* @param previousVersion the previous version (previousVersion - cpe would be considered vulnerable)
|
||||
* @param previousVersion the previous version (previousVersion - cpe would
|
||||
* be considered vulnerable)
|
||||
* @return if the add succeeded
|
||||
*/
|
||||
public boolean addVulnerableSoftware(String cpe, String previousVersion) {
|
||||
@@ -390,29 +391,33 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
|
||||
final StringBuilder sb = new StringBuilder("Vulnerability ");
|
||||
sb.append(this.name);
|
||||
sb.append("\nReferences:\n");
|
||||
for (Iterator i = this.references.iterator(); i.hasNext();) {
|
||||
sb.append("=> ");
|
||||
sb.append(i.next());
|
||||
sb.append("\n");
|
||||
for (Reference reference : this.references) {
|
||||
sb.append("=> ");
|
||||
sb.append(reference);
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append("\nSoftware:\n");
|
||||
for (Iterator i = this.vulnerableSoftware.iterator(); i.hasNext();) {
|
||||
sb.append("=> ");
|
||||
sb.append(i.next());
|
||||
sb.append("\n");
|
||||
for (VulnerableSoftware software : this.vulnerableSoftware) {
|
||||
sb.append("=> ");
|
||||
sb.append(software);
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two vulnerabilities.
|
||||
*
|
||||
* @param v a vulnerability to be compared
|
||||
* @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
|
||||
* the specified vulnerability
|
||||
* @return a negative integer, zero, or a positive integer as this object is
|
||||
* less than, equal to, or greater than the specified vulnerability
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Vulnerability v) {
|
||||
return v.getName().compareTo(this.getName());
|
||||
return new CompareToBuilder()
|
||||
.append(this.name, v.name)
|
||||
.toComparison();
|
||||
//return v.getName().compareTo(this.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -428,8 +433,8 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
|
||||
* Sets the CPE that caused this vulnerability to be flagged.
|
||||
*
|
||||
* @param cpeId a CPE identifier
|
||||
* @param previous a flag indicating whether or not all previous versions were affected (any non-null value is
|
||||
* considered true)
|
||||
* @param previous a flag indicating whether or not all previous versions
|
||||
* were affected (any non-null value is considered true)
|
||||
*/
|
||||
public void setMatchedCPE(String cpeId, String previous) {
|
||||
matchedCPE = cpeId;
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.exception;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A collection of several exceptions.
|
||||
*
|
||||
* @author Jeremy Lomg
|
||||
*/
|
||||
public class ExceptionCollection extends Exception {
|
||||
|
||||
/**
|
||||
* Instantiates a new exception collection.
|
||||
*
|
||||
* @param exceptions a list of exceptions
|
||||
*/
|
||||
public ExceptionCollection(List<Throwable> exceptions) {
|
||||
super();
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new exception collection.
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param exceptions a list of exceptions
|
||||
*/
|
||||
public ExceptionCollection(String msg, List<Throwable> exceptions) {
|
||||
super(msg);
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new exception collection.
|
||||
*
|
||||
* @param exceptions a list of exceptions
|
||||
* @param fatal indicates if the exception that occurred is fatal - meaning
|
||||
* that no analysis was performed.
|
||||
*/
|
||||
public ExceptionCollection(List<Throwable> exceptions, boolean fatal) {
|
||||
super();
|
||||
this.exceptions = exceptions;
|
||||
this.fatal = fatal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new exception collection.
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param exceptions a list of exceptions
|
||||
* @param fatal indicates if the exception that occurred is fatal - meaning
|
||||
* that no analysis was performed.
|
||||
*/
|
||||
public ExceptionCollection(String msg, List<Throwable> exceptions, boolean fatal) {
|
||||
super(msg);
|
||||
this.exceptions = exceptions;
|
||||
this.fatal = fatal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new exception collection.
|
||||
*
|
||||
* @param exceptions a list of exceptions
|
||||
* @param fatal indicates if the exception that occurred is fatal - meaning
|
||||
* that no analysis was performed.
|
||||
*/
|
||||
public ExceptionCollection(Throwable exceptions, boolean fatal) {
|
||||
super();
|
||||
this.exceptions = new ArrayList<Throwable>();
|
||||
this.exceptions.add(exceptions);
|
||||
this.fatal = fatal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new exception collection.
|
||||
*
|
||||
* @param msg the exception message
|
||||
* @param exception a list of exceptions
|
||||
*/
|
||||
public ExceptionCollection(String msg, Throwable exception) {
|
||||
super(msg);
|
||||
this.exceptions = new ArrayList<Throwable>();
|
||||
this.exceptions.add(exception);
|
||||
this.fatal = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new exception collection.
|
||||
*/
|
||||
public ExceptionCollection() {
|
||||
super();
|
||||
this.exceptions = new ArrayList<Throwable>();
|
||||
}
|
||||
/**
|
||||
* The serial version uid.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* A collection of exceptions.
|
||||
*/
|
||||
private List<Throwable> exceptions;
|
||||
|
||||
/**
|
||||
* Get the value of exceptions.
|
||||
*
|
||||
* @return the value of exceptions
|
||||
*/
|
||||
public List<Throwable> getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an exception to the collection.
|
||||
*
|
||||
* @param ex the exception to add
|
||||
*/
|
||||
public void addException(Throwable ex) {
|
||||
this.exceptions.add(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an exception to the collection.
|
||||
*
|
||||
* @param ex the exception to add
|
||||
* @param fatal flag indicating if this is a fatal error
|
||||
*/
|
||||
public void addException(Throwable ex, boolean fatal) {
|
||||
addException(ex);
|
||||
this.fatal = fatal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag indicating if a fatal exception occurred that would prevent the
|
||||
* attempt at completing the analysis even if exceptions occurred.
|
||||
*/
|
||||
private boolean fatal = false;
|
||||
|
||||
/**
|
||||
* Get the value of fatal.
|
||||
*
|
||||
* @return the value of fatal
|
||||
*/
|
||||
public boolean isFatal() {
|
||||
return fatal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of fatal.
|
||||
*
|
||||
* @param fatal new value of fatal
|
||||
*/
|
||||
public void setFatal(boolean fatal) {
|
||||
this.fatal = fatal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the stack trace.
|
||||
*
|
||||
* @param s the writer to print to
|
||||
*/
|
||||
@Override
|
||||
public void printStackTrace(PrintWriter s) {
|
||||
s.println("Multiple Exceptions Occured");
|
||||
super.printStackTrace(s);
|
||||
for (Throwable t : this.exceptions) {
|
||||
s.println("Next Exception:");
|
||||
t.printStackTrace(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the stack trace.
|
||||
*
|
||||
* @param s the stream to write the stack trace to
|
||||
*/
|
||||
@Override
|
||||
public void printStackTrace(PrintStream s) {
|
||||
s.println("Multiple Exceptions Occurred");
|
||||
super.printStackTrace(s);
|
||||
for (Throwable t : this.exceptions) {
|
||||
s.println("Next Exception:");
|
||||
t.printStackTrace(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error message, including the message from all contained
|
||||
* exceptions.
|
||||
*
|
||||
* @return the error message
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final String msg = super.getMessage();
|
||||
if (msg == null || msg.isEmpty()) {
|
||||
sb.append("One or more exceptions occured during analysis:");
|
||||
} else {
|
||||
sb.append(msg);
|
||||
}
|
||||
for (Throwable t : this.exceptions) {
|
||||
sb.append("\n\t").append(t.getMessage());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.exception;
|
||||
|
||||
/**
|
||||
* An exception used when initializing analyzers.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class InitializationException extends Exception {
|
||||
|
||||
/**
|
||||
* The serial version uid.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new InitializationException.
|
||||
*/
|
||||
public InitializationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new InitializationException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
public InitializationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new InitializationException.
|
||||
*
|
||||
* @param ex the cause of the exception.
|
||||
*/
|
||||
public InitializationException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new InitializationException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the exception.
|
||||
*/
|
||||
public InitializationException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.exception;
|
||||
|
||||
/**
|
||||
* An exception used when generating reports.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class ReportException extends Exception {
|
||||
|
||||
/**
|
||||
* The serial version uid.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new ReportException.
|
||||
*/
|
||||
public ReportException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ReportException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
public ReportException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ReportException.
|
||||
*
|
||||
* @param ex the cause of the exception.
|
||||
*/
|
||||
public ReportException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ReportException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the exception.
|
||||
*/
|
||||
public ReportException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
@@ -37,13 +38,16 @@ import org.apache.velocity.runtime.RuntimeConstants;
|
||||
import org.owasp.dependencycheck.analyzer.Analyzer;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.exception.ReportException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The ReportGenerator is used to, as the name implies, generate reports. Internally the generator uses the Velocity
|
||||
* Templating Engine. The ReportGenerator exposes a list of Dependencies to the template when generating the report.
|
||||
* The ReportGenerator is used to, as the name implies, generate reports.
|
||||
* Internally the generator uses the Velocity Templating Engine. The
|
||||
* ReportGenerator exposes a list of Dependencies to the template when
|
||||
* generating the report.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -79,7 +83,7 @@ public class ReportGenerator {
|
||||
/**
|
||||
* The Velocity Engine.
|
||||
*/
|
||||
private final VelocityEngine engine;
|
||||
private final VelocityEngine velocityEngine;
|
||||
/**
|
||||
* The Velocity Engine Context.
|
||||
*/
|
||||
@@ -91,13 +95,14 @@ public class ReportGenerator {
|
||||
* @param applicationName the application name being analyzed
|
||||
* @param dependencies the list of dependencies
|
||||
* @param analyzers the list of analyzers used
|
||||
* @param properties the database properties (containing timestamps of the NVD CVE data)
|
||||
* @param properties the database properties (containing timestamps of the
|
||||
* NVD CVE data)
|
||||
*/
|
||||
public ReportGenerator(String applicationName, List<Dependency> dependencies, List<Analyzer> analyzers, DatabaseProperties properties) {
|
||||
engine = createVelocityEngine();
|
||||
velocityEngine = createVelocityEngine();
|
||||
context = createContext();
|
||||
|
||||
engine.init();
|
||||
velocityEngine.init();
|
||||
|
||||
final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy 'at' HH:mm:ss z");
|
||||
final DateFormat dateFormatXML = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
||||
@@ -119,19 +124,19 @@ public class ReportGenerator {
|
||||
/**
|
||||
* Creates a new Velocity Engine.
|
||||
*
|
||||
* @return a velocity engine.
|
||||
* @return a velocity engine
|
||||
*/
|
||||
private VelocityEngine createVelocityEngine() {
|
||||
final VelocityEngine engine = new VelocityEngine();
|
||||
final VelocityEngine velocity = new VelocityEngine();
|
||||
// Logging redirection for Velocity - Required by Jenkins and other server applications
|
||||
engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName());
|
||||
return engine;
|
||||
velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, VelocityLoggerRedirect.class.getName());
|
||||
return velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Velocity Context.
|
||||
*
|
||||
* @return a Velocity Context.
|
||||
* @return a Velocity Context
|
||||
*/
|
||||
private Context createContext() {
|
||||
return new VelocityContext();
|
||||
@@ -143,7 +148,7 @@ public class ReportGenerator {
|
||||
* @param outputStream the OutputStream to send the generated report to
|
||||
* @param format the format the report should be written in
|
||||
* @throws IOException is thrown when the template file does not exist
|
||||
* @throws Exception is thrown if there is an error writing out the reports.
|
||||
* @throws Exception is thrown if there is an error writing out the reports
|
||||
*/
|
||||
public void generateReports(OutputStream outputStream, Format format) throws IOException, Exception {
|
||||
if (format == Format.XML || format == Format.ALL) {
|
||||
@@ -162,10 +167,9 @@ public class ReportGenerator {
|
||||
*
|
||||
* @param outputDir the path where the reports should be written
|
||||
* @param format the format the report should be written in
|
||||
* @throws IOException is thrown when the template file does not exist
|
||||
* @throws Exception is thrown if there is an error writing out the reports.
|
||||
* @throws ReportException is thrown if there is an error writing out the reports
|
||||
*/
|
||||
public void generateReports(String outputDir, Format format) throws IOException, Exception {
|
||||
public void generateReports(String outputDir, Format format) throws ReportException {
|
||||
if (format == Format.XML || format == Format.ALL) {
|
||||
generateReport("XmlReport", outputDir + File.separator + "dependency-check-report.xml");
|
||||
}
|
||||
@@ -181,11 +185,12 @@ public class ReportGenerator {
|
||||
* Generates the Dependency Reports for the identified dependencies.
|
||||
*
|
||||
* @param outputDir the path where the reports should be written
|
||||
* @param outputFormat the format the report should be written in (XML, HTML, ALL)
|
||||
* @throws IOException is thrown when the template file does not exist
|
||||
* @throws Exception is thrown if there is an error writing out the reports.
|
||||
* @param outputFormat the format the report should be written in (XML,
|
||||
* HTML, ALL)
|
||||
* @throws ReportException is thrown if there is an error creating out the
|
||||
* reports
|
||||
*/
|
||||
public void generateReports(String outputDir, String outputFormat) throws IOException, Exception {
|
||||
public void generateReports(String outputDir, String outputFormat) throws ReportException {
|
||||
final String format = outputFormat.toUpperCase();
|
||||
final String pathToCheck = outputDir.toLowerCase();
|
||||
if (format.matches("^(XML|HTML|VULN|ALL)$")) {
|
||||
@@ -217,16 +222,16 @@ public class ReportGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a report from a given Velocity Template. The template name provided can be the name of a template
|
||||
* contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a
|
||||
* Generates a report from a given Velocity Template. The template name
|
||||
* provided can be the name of a template contained in the jar file, such as
|
||||
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
|
||||
* template file.
|
||||
*
|
||||
* @param templateName the name of the template to load.
|
||||
* @param outputStream the OutputStream to write the report to.
|
||||
* @throws IOException is thrown when the template file does not exist.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
* @param templateName the name of the template to load
|
||||
* @param outputStream the OutputStream to write the report to
|
||||
* @throws ReportException is thrown when an exception occurs
|
||||
*/
|
||||
protected void generateReport(String templateName, OutputStream outputStream) throws IOException, Exception {
|
||||
protected void generateReport(String templateName, OutputStream outputStream) throws ReportException {
|
||||
InputStream input = null;
|
||||
String templatePath = null;
|
||||
final File f = new File(templateName);
|
||||
@@ -235,27 +240,30 @@ public class ReportGenerator {
|
||||
templatePath = templateName;
|
||||
input = new FileInputStream(f);
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.error("Unable to generate the report, the report template file could not be found.");
|
||||
LOGGER.debug("", ex);
|
||||
throw new ReportException("Unable to locate template file: " + templateName, ex);
|
||||
}
|
||||
} else {
|
||||
templatePath = "templates/" + templateName + ".vsl";
|
||||
input = this.getClass().getClassLoader().getResourceAsStream(templatePath);
|
||||
}
|
||||
if (input == null) {
|
||||
throw new IOException("Template file doesn't exist");
|
||||
throw new ReportException("Template file doesn't exist: " + templatePath);
|
||||
}
|
||||
|
||||
final InputStreamReader reader = new InputStreamReader(input, "UTF-8");
|
||||
InputStreamReader reader = null;
|
||||
OutputStreamWriter writer = null;
|
||||
|
||||
try {
|
||||
reader = new InputStreamReader(input, "UTF-8");
|
||||
writer = new OutputStreamWriter(outputStream, "UTF-8");
|
||||
|
||||
if (!engine.evaluate(context, writer, templatePath, reader)) {
|
||||
throw new Exception("Failed to convert the template into html.");
|
||||
if (!velocityEngine.evaluate(context, writer, templatePath, reader)) {
|
||||
throw new ReportException("Failed to convert the template into html.");
|
||||
}
|
||||
writer.flush();
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new ReportException("Unable to generate the report using UTF-8", ex);
|
||||
} catch (IOException ex) {
|
||||
throw new ReportException("Unable to write the report", ex);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
@@ -271,25 +279,27 @@ public class ReportGenerator {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
}
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.trace("", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a report from a given Velocity Template. The template name provided can be the name of a template
|
||||
* contained in the jar file, such as 'XmlReport' or 'HtmlReport', or the template name can be the path to a
|
||||
* Generates a report from a given Velocity Template. The template name
|
||||
* provided can be the name of a template contained in the jar file, such as
|
||||
* 'XmlReport' or 'HtmlReport', or the template name can be the path to a
|
||||
* template file.
|
||||
*
|
||||
* @param templateName the name of the template to load.
|
||||
* @param outFileName the filename and path to write the report to.
|
||||
* @throws IOException is thrown when the template file does not exist.
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
* @param templateName the name of the template to load
|
||||
* @param outFileName the filename and path to write the report to
|
||||
* @throws ReportException is thrown when the report cannot be generated
|
||||
*/
|
||||
protected void generateReport(String templateName, String outFileName) throws Exception {
|
||||
protected void generateReport(String templateName, String outFileName) throws ReportException {
|
||||
File outFile = new File(outFileName);
|
||||
if (outFile.getParentFile() == null) {
|
||||
outFile = new File(".", outFileName);
|
||||
@@ -297,7 +307,7 @@ public class ReportGenerator {
|
||||
if (!outFile.getParentFile().exists()) {
|
||||
final boolean created = outFile.getParentFile().mkdirs();
|
||||
if (!created) {
|
||||
throw new Exception("Unable to create directory '" + outFile.getParentFile().getAbsolutePath() + "'.");
|
||||
throw new ReportException("Unable to create directory '" + outFile.getParentFile().getAbsolutePath() + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +315,8 @@ public class ReportGenerator {
|
||||
try {
|
||||
outputSteam = new FileOutputStream(outFile);
|
||||
generateReport(templateName, outputSteam);
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new ReportException("Unable to write to file: " + outFile, ex);
|
||||
} finally {
|
||||
if (outputSteam != null) {
|
||||
try {
|
||||
|
||||
@@ -24,7 +24,8 @@ import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A utility class to extract version numbers from file names (or other strings containing version numbers.</p>
|
||||
* A utility class to extract version numbers from file names (or other strings
|
||||
* containing version numbers.</p>
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
@@ -35,11 +36,19 @@ public final class DependencyVersionUtil {
|
||||
*/
|
||||
private static final Pattern RX_VERSION = Pattern.compile("\\d+(\\.\\d{1,6})+(\\.?([_-](release|beta|alpha|\\d+)|[a-zA-Z_-]{1,3}\\d{0,8}))?");
|
||||
/**
|
||||
* Regular expression to extract a single version number without periods. This is a last ditch effort just to check in case we
|
||||
* are missing a version number using the previous regex.
|
||||
* Regular expression to extract a single version number without periods.
|
||||
* This is a last ditch effort just to check in case we are missing a
|
||||
* version number using the previous regex.
|
||||
*/
|
||||
private static final Pattern RX_SINGLE_VERSION = Pattern.compile("\\d+(\\.?([_-](release|beta|alpha)|[a-zA-Z_-]{1,3}\\d{1,8}))?");
|
||||
|
||||
/**
|
||||
* Regular expression to extract the part before the version numbers if
|
||||
* there are any based on RX_VERSION. In most cases, this part represents a
|
||||
* more accurate name.
|
||||
*/
|
||||
private static final Pattern RX_PRE_VERSION = Pattern.compile("^(.+)[_-](\\d+\\.\\d{1,6})+");
|
||||
|
||||
/**
|
||||
* Private constructor for utility class.
|
||||
*/
|
||||
@@ -48,7 +57,8 @@ public final class DependencyVersionUtil {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A utility class to extract version numbers from file names (or other strings containing version numbers.</p>
|
||||
* A utility class to extract version numbers from file names (or other
|
||||
* strings containing version numbers.</p>
|
||||
* <pre>
|
||||
* Example:
|
||||
* Give the file name: library-name-1.4.1r2-release.jar
|
||||
@@ -95,4 +105,30 @@ public final class DependencyVersionUtil {
|
||||
}
|
||||
return new DependencyVersion(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A utility class to extract the part before version numbers from file
|
||||
* names (or other strings containing version numbers. In most cases, this
|
||||
* part represents a more accurate name than the full file name.</p>
|
||||
* <pre>
|
||||
* Example:
|
||||
* Give the file name: library-name-1.4.1r2-release.jar
|
||||
* This function would return: library-name</pre>
|
||||
*
|
||||
* @param text the text being analyzed
|
||||
* @return the part before the version numbers if any, otherwise return the
|
||||
* text itself.
|
||||
*/
|
||||
public static String parsePreVersion(String text) {
|
||||
if (parseVersion(text) == null) {
|
||||
return text;
|
||||
}
|
||||
|
||||
final Matcher matcher = RX_PRE_VERSION.matcher(text);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
/**
|
||||
* An XML parsing error handler.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class HintErrorHandler implements ErrorHandler {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HintErrorHandler.class);
|
||||
|
||||
/**
|
||||
* Builds a prettier exception message.
|
||||
*
|
||||
* @param ex the SAXParseException
|
||||
* @return an easier to read exception message
|
||||
*/
|
||||
private String getPrettyParseExceptionInfo(SAXParseException ex) {
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (ex.getSystemId() != null) {
|
||||
sb.append("systemId=").append(ex.getSystemId()).append(", ");
|
||||
}
|
||||
if (ex.getPublicId() != null) {
|
||||
sb.append("publicId=").append(ex.getPublicId()).append(", ");
|
||||
}
|
||||
if (ex.getLineNumber() > 0) {
|
||||
sb.append("Line=").append(ex.getLineNumber());
|
||||
}
|
||||
if (ex.getColumnNumber() > 0) {
|
||||
sb.append(", Column=").append(ex.getColumnNumber());
|
||||
}
|
||||
sb.append(": ").append(ex.getMessage());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs warnings.
|
||||
*
|
||||
* @param ex the warning to log
|
||||
* @throws SAXException is never thrown
|
||||
*/
|
||||
@Override
|
||||
public void warning(SAXParseException ex) throws SAXException {
|
||||
LOGGER.debug("", ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors.
|
||||
*
|
||||
* @param ex the error to handle
|
||||
* @throws SAXException is always thrown
|
||||
*/
|
||||
@Override
|
||||
public void error(SAXParseException ex) throws SAXException {
|
||||
throw new SAXException(getPrettyParseExceptionInfo(ex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fatal exceptions.
|
||||
*
|
||||
* @param ex a fatal exception
|
||||
* @throws SAXException is always
|
||||
*/
|
||||
@Override
|
||||
public void fatalError(SAXParseException ex) throws SAXException {
|
||||
throw new SAXException(getPrettyParseExceptionInfo(ex));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.xml.suppression.PropertyType;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
/**
|
||||
* A handler to load hint rules.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class HintHandler extends DefaultHandler {
|
||||
|
||||
//<editor-fold defaultstate="collapsed" desc="Element and attribute names">
|
||||
/**
|
||||
* Element name.
|
||||
*/
|
||||
private static final String HINT = "hint";
|
||||
/**
|
||||
* Element name.
|
||||
*/
|
||||
private static final String GIVEN = "given";
|
||||
/**
|
||||
* Element name.
|
||||
*/
|
||||
private static final String ADD = "add";
|
||||
/**
|
||||
* Element name.
|
||||
*/
|
||||
private static final String EVIDENCE = "evidence";
|
||||
/**
|
||||
* Element name.
|
||||
*/
|
||||
private static final String FILE_NAME = "fileName";
|
||||
/**
|
||||
* Element name.
|
||||
*/
|
||||
private static final String VENDOR_DUPLICATING_RULE = "vendorDuplicatingHint";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String DUPLICATE = "duplicate";
|
||||
/**
|
||||
* Attribute value.
|
||||
*/
|
||||
private static final String VENDOR = "vendor";
|
||||
/**
|
||||
* Attribute value.
|
||||
*/
|
||||
private static final String PRODUCT = "product";
|
||||
/**
|
||||
* Attribute value.
|
||||
*/
|
||||
private static final String VERSION = "version";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String CONFIDENCE = "confidence";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String VALUE = "value";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String NAME = "name";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String SOURCE = "source";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String TYPE = "type";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String CASE_SENSITIVE = "caseSensitive";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String REGEX = "regex";
|
||||
/**
|
||||
* Attribute name.
|
||||
*/
|
||||
private static final String CONTAINS = "contains";
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* The list of hint rules.
|
||||
*/
|
||||
private final List<HintRule> hintRules = new ArrayList<HintRule>();
|
||||
|
||||
/**
|
||||
* Returns the list of hint rules.
|
||||
*
|
||||
* @return the value of hintRules
|
||||
*/
|
||||
public List<HintRule> getHintRules() {
|
||||
return hintRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of vendor duplicating hint rules.
|
||||
*/
|
||||
private final List<VendorDuplicatingHintRule> vendorDuplicatingHintRules = new ArrayList<VendorDuplicatingHintRule>();
|
||||
|
||||
/**
|
||||
* Returns the list of vendor duplicating hint rules.
|
||||
*
|
||||
* @return the list of vendor duplicating hint rules
|
||||
*/
|
||||
public List<VendorDuplicatingHintRule> getVendorDuplicatingHintRules() {
|
||||
return vendorDuplicatingHintRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current rule being read.
|
||||
*/
|
||||
private HintRule rule;
|
||||
/**
|
||||
* The current state of the parent node (to differentiate between 'add' and
|
||||
* 'given').
|
||||
*/
|
||||
private boolean inAddNode = false;
|
||||
|
||||
/**
|
||||
* Handles the start element event.
|
||||
*
|
||||
* @param uri the uri of the element being processed
|
||||
* @param localName the local name of the element being processed
|
||||
* @param qName the qName of the element being processed
|
||||
* @param attr the attributes of the element being processed
|
||||
* @throws SAXException thrown if there is an exception processing
|
||||
*/
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attr) throws SAXException {
|
||||
if (HINT.equals(qName)) {
|
||||
rule = new HintRule();
|
||||
} else if (ADD.equals(qName)) {
|
||||
inAddNode = true;
|
||||
} else if (GIVEN.equals(qName)) {
|
||||
inAddNode = false;
|
||||
} else if (EVIDENCE.equals(qName)) {
|
||||
final String hintType = attr.getValue(TYPE);
|
||||
if (VENDOR.equals(hintType)) {
|
||||
if (inAddNode) {
|
||||
rule.addAddVendor(attr.getValue(SOURCE),
|
||||
attr.getValue(NAME),
|
||||
attr.getValue(VALUE),
|
||||
Confidence.valueOf(attr.getValue(CONFIDENCE)));
|
||||
} else {
|
||||
rule.addGivenVendor(attr.getValue(SOURCE),
|
||||
attr.getValue(NAME),
|
||||
attr.getValue(VALUE),
|
||||
Confidence.valueOf(attr.getValue(CONFIDENCE)));
|
||||
}
|
||||
} else if (PRODUCT.equals(hintType)) {
|
||||
if (inAddNode) {
|
||||
rule.addAddProduct(attr.getValue(SOURCE),
|
||||
attr.getValue(NAME),
|
||||
attr.getValue(VALUE),
|
||||
Confidence.valueOf(attr.getValue(CONFIDENCE)));
|
||||
} else {
|
||||
rule.addGivenProduct(attr.getValue(SOURCE),
|
||||
attr.getValue(NAME),
|
||||
attr.getValue(VALUE),
|
||||
Confidence.valueOf(attr.getValue(CONFIDENCE)));
|
||||
}
|
||||
} else if (VERSION.equals(hintType)) {
|
||||
if (inAddNode) {
|
||||
rule.addAddVersion(attr.getValue(SOURCE),
|
||||
attr.getValue(NAME),
|
||||
attr.getValue(VALUE),
|
||||
Confidence.valueOf(attr.getValue(CONFIDENCE)));
|
||||
}
|
||||
}
|
||||
} else if (FILE_NAME.equals(qName)) {
|
||||
final PropertyType pt = new PropertyType();
|
||||
pt.setValue(attr.getValue(CONTAINS));
|
||||
if (attr.getLength() > 0) {
|
||||
final String regex = attr.getValue(REGEX);
|
||||
if (regex != null) {
|
||||
pt.setRegex(Boolean.parseBoolean(regex));
|
||||
}
|
||||
final String caseSensitive = attr.getValue(CASE_SENSITIVE);
|
||||
if (caseSensitive != null) {
|
||||
pt.setCaseSensitive(Boolean.parseBoolean(caseSensitive));
|
||||
}
|
||||
}
|
||||
rule.addFilename(pt);
|
||||
} else if (VENDOR_DUPLICATING_RULE.equals(qName)) {
|
||||
vendorDuplicatingHintRules.add(new VendorDuplicatingHintRule(attr.getValue(VALUE), attr.getValue(DUPLICATE)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the end element event.
|
||||
*
|
||||
* @param uri the element's URI
|
||||
* @param localName the local name
|
||||
* @param qName the qualified name
|
||||
* @throws SAXException thrown if there is an exception processing the
|
||||
* element
|
||||
*/
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
if (HINT.equals(qName) && rule != null) {
|
||||
hintRules.add(rule);
|
||||
rule = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An exception used when parsing a suppression rule file fails.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class HintParseException extends IOException {
|
||||
|
||||
/**
|
||||
* The serial version UID for serialization.
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*/
|
||||
public HintParseException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
*/
|
||||
public HintParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*
|
||||
* @param ex the cause of the parse exception
|
||||
*/
|
||||
public HintParseException(Throwable ex) {
|
||||
super(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SuppressionParseException.
|
||||
*
|
||||
* @param msg a message for the exception.
|
||||
* @param ex the cause of the parse exception
|
||||
*/
|
||||
public HintParseException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
/**
|
||||
* A simple validating parser for XML Hint Rules.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class HintParser {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HintParser.class);
|
||||
/**
|
||||
* JAXP Schema Language. Source:
|
||||
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
|
||||
*/
|
||||
public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
|
||||
/**
|
||||
* W3C XML Schema. Source:
|
||||
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
|
||||
*/
|
||||
public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
|
||||
/**
|
||||
* JAXP Schema Source. Source:
|
||||
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
|
||||
*/
|
||||
public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
|
||||
|
||||
/**
|
||||
* The schema for the hint XML files.
|
||||
*/
|
||||
private static final String HINT_SCHEMA = "schema/dependency-hint.1.1.xsd";
|
||||
|
||||
/**
|
||||
* Parses the given XML file and returns a list of the hints contained.
|
||||
*
|
||||
* @param file an XML file containing hints
|
||||
* @return a list of hint rules
|
||||
* @throws HintParseException thrown if the XML file cannot be parsed
|
||||
*/
|
||||
public Hints parseHints(File file) throws HintParseException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
return parseHints(fis);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new HintParseException(ex);
|
||||
} catch (SAXException ex) {
|
||||
throw new HintParseException(ex);
|
||||
} finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Unable to close stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given XML stream and returns a list of the hint rules
|
||||
* contained.
|
||||
*
|
||||
* @param inputStream an InputStream containing hint rules
|
||||
* @return a list of hint rules
|
||||
* @throws HintParseException thrown if the XML cannot be parsed
|
||||
* @throws SAXException thrown if the XML cannot be parsed
|
||||
*/
|
||||
public Hints parseHints(InputStream inputStream) throws HintParseException, SAXException {
|
||||
InputStream schemaStream = null;
|
||||
try {
|
||||
schemaStream = this.getClass().getClassLoader().getResourceAsStream(HINT_SCHEMA);
|
||||
final HintHandler handler = new HintHandler();
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
factory.setValidating(true);
|
||||
final SAXParser saxParser = factory.newSAXParser();
|
||||
saxParser.setProperty(HintParser.JAXP_SCHEMA_LANGUAGE, HintParser.W3C_XML_SCHEMA);
|
||||
saxParser.setProperty(HintParser.JAXP_SCHEMA_SOURCE, new InputSource(schemaStream));
|
||||
final XMLReader xmlReader = saxParser.getXMLReader();
|
||||
xmlReader.setErrorHandler(new HintErrorHandler());
|
||||
xmlReader.setContentHandler(handler);
|
||||
|
||||
final Reader reader = new InputStreamReader(inputStream, "UTF-8");
|
||||
final InputSource in = new InputSource(reader);
|
||||
|
||||
xmlReader.parse(in);
|
||||
final Hints hints = new Hints();
|
||||
hints.setHintRules(handler.getHintRules());
|
||||
hints.setVendorDuplicatingHintRules(handler.getVendorDuplicatingHintRules());
|
||||
return hints;
|
||||
} catch (ParserConfigurationException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new HintParseException(ex);
|
||||
} catch (SAXException ex) {
|
||||
if (ex.getMessage().contains("Cannot find the declaration of element 'hints'.")) {
|
||||
throw ex;
|
||||
} else {
|
||||
LOGGER.debug("", ex);
|
||||
throw new HintParseException(ex);
|
||||
}
|
||||
} catch (FileNotFoundException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new HintParseException(ex);
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new HintParseException(ex);
|
||||
} finally {
|
||||
if (schemaStream != null) {
|
||||
try {
|
||||
schemaStream.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Error closing hint file stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.xml.suppression.PropertyType;
|
||||
|
||||
/**
|
||||
* A collection of product and vendor evidence to match; if any evidence is
|
||||
* matched the addVendor and addProduct evidence should be added to the
|
||||
* dependency.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class HintRule {
|
||||
|
||||
/**
|
||||
* The list of file names to match.
|
||||
*/
|
||||
private final List<PropertyType> filenames = new ArrayList<PropertyType>();
|
||||
|
||||
/**
|
||||
* Adds the filename evidence to the collection.
|
||||
*
|
||||
* @param filename the filename to add
|
||||
*/
|
||||
public void addFilename(PropertyType filename) {
|
||||
this.filenames.add(filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of filename evidence to match against.
|
||||
*
|
||||
* @return the list of filename evidence to match against
|
||||
*/
|
||||
public List<PropertyType> getFilenames() {
|
||||
return filenames;
|
||||
}
|
||||
/**
|
||||
* The list of product evidence that is being matched.
|
||||
*/
|
||||
private final List<Evidence> givenProduct = new ArrayList<Evidence>();
|
||||
|
||||
/**
|
||||
* Adds a given product to the list of evidence to matched.
|
||||
*
|
||||
* @param source the source of the evidence
|
||||
* @param name the name of the evidence
|
||||
* @param value the value of the evidence
|
||||
* @param confidence the confidence of the evidence
|
||||
*/
|
||||
public void addGivenProduct(String source, String name, String value, Confidence confidence) {
|
||||
givenProduct.add(new Evidence(source, name, value, confidence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of givenProduct.
|
||||
*
|
||||
* @return the value of givenProduct
|
||||
*/
|
||||
public List<Evidence> getGivenProduct() {
|
||||
return givenProduct;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of vendor evidence that is being matched.
|
||||
*/
|
||||
private final List<Evidence> givenVendor = new ArrayList<Evidence>();
|
||||
|
||||
/**
|
||||
* The list of product evidence to add.
|
||||
*/
|
||||
private final List<Evidence> addProduct = new ArrayList<Evidence>();
|
||||
/**
|
||||
* The list of version evidence to add.
|
||||
*/
|
||||
private final List<Evidence> addVersion = new ArrayList<Evidence>();
|
||||
|
||||
/**
|
||||
* Adds a given vendors to the list of evidence to matched.
|
||||
*
|
||||
* @param source the source of the evidence
|
||||
* @param name the name of the evidence
|
||||
* @param value the value of the evidence
|
||||
* @param confidence the confidence of the evidence
|
||||
*/
|
||||
public void addGivenVendor(String source, String name, String value, Confidence confidence) {
|
||||
givenVendor.add(new Evidence(source, name, value, confidence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of givenVendor.
|
||||
*
|
||||
* @return the value of givenVendor
|
||||
*/
|
||||
public List<Evidence> getGivenVendor() {
|
||||
return givenVendor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a given product to the list of evidence to add when matched.
|
||||
*
|
||||
* @param source the source of the evidence
|
||||
* @param name the name of the evidence
|
||||
* @param value the value of the evidence
|
||||
* @param confidence the confidence of the evidence
|
||||
*/
|
||||
public void addAddProduct(String source, String name, String value, Confidence confidence) {
|
||||
addProduct.add(new Evidence(source, name, value, confidence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of addProduct.
|
||||
*
|
||||
* @return the value of addProduct
|
||||
*/
|
||||
public List<Evidence> getAddProduct() {
|
||||
return addProduct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a given version to the list of evidence to add when matched.
|
||||
*
|
||||
* @param source the source of the evidence
|
||||
* @param name the name of the evidence
|
||||
* @param value the value of the evidence
|
||||
* @param confidence the confidence of the evidence
|
||||
*/
|
||||
public void addAddVersion(String source, String name, String value, Confidence confidence) {
|
||||
addVersion.add(new Evidence(source, name, value, confidence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of addVersion.
|
||||
*
|
||||
* @return the value of addVersion
|
||||
*/
|
||||
public List<Evidence> getAddVersion() {
|
||||
return addVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of vendor hints to add.
|
||||
*/
|
||||
private final List<Evidence> addVendor = new ArrayList<Evidence>();
|
||||
|
||||
/**
|
||||
* Adds a given vendor to the list of evidence to add when matched.
|
||||
*
|
||||
* @param source the source of the evidence
|
||||
* @param name the name of the evidence
|
||||
* @param value the value of the evidence
|
||||
* @param confidence the confidence of the evidence
|
||||
*/
|
||||
public void addAddVendor(String source, String name, String value, Confidence confidence) {
|
||||
addVendor.add(new Evidence(source, name, value, confidence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of addVendor.
|
||||
*
|
||||
* @return the value of addVendor
|
||||
*/
|
||||
public List<Evidence> getAddVendor() {
|
||||
return addVendor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A collection of hint rules.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class Hints {
|
||||
|
||||
/**
|
||||
* The list of hint rules.
|
||||
*/
|
||||
private List<HintRule> hintRules;
|
||||
|
||||
/**
|
||||
* Get the value of hintRules.
|
||||
*
|
||||
* @return the value of hintRules
|
||||
*/
|
||||
public List<HintRule> getHintRules() {
|
||||
return hintRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of hintRules.
|
||||
*
|
||||
* @param hintRules new value of hintRules
|
||||
*/
|
||||
public void setHintRules(List<HintRule> hintRules) {
|
||||
this.hintRules = hintRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* The duplicating hint rules.
|
||||
*/
|
||||
private List<VendorDuplicatingHintRule> vendorDuplicatingHintRules;
|
||||
|
||||
/**
|
||||
* Get the value of vendorDuplicatingHintRules.
|
||||
*
|
||||
* @return the value of vendorDuplicatingHintRules
|
||||
*/
|
||||
public List<VendorDuplicatingHintRule> getVendorDuplicatingHintRules() {
|
||||
return vendorDuplicatingHintRules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of vendorDuplicatingHintRules.
|
||||
*
|
||||
* @param vendorDuplicatingHintRules new value of vendorDuplicatingHintRules
|
||||
*/
|
||||
public void setVendorDuplicatingHintRules(List<VendorDuplicatingHintRule> vendorDuplicatingHintRules) {
|
||||
this.vendorDuplicatingHintRules = vendorDuplicatingHintRules;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of dependency-check-core.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright (c) 2016 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
|
||||
/**
|
||||
* Used to duplicate vendor evidence within a collection. The intent is if any evidence
|
||||
* is found in a collection that matches the value given the evidence will be
|
||||
* duplicated and the value replaced with the value indicated.
|
||||
*
|
||||
* @author Jeremy Long
|
||||
*/
|
||||
public class VendorDuplicatingHintRule {
|
||||
|
||||
/**
|
||||
* Constructs a new duplicating rule.
|
||||
*
|
||||
* @param value the value to duplicate the evidence if found
|
||||
* @param duplicate the value to replace within the duplicated evidence
|
||||
*/
|
||||
public VendorDuplicatingHintRule(String value, String duplicate) {
|
||||
this.value = value;
|
||||
this.duplicate = duplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* The evidence value to duplicate if found.
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Get the value of value.
|
||||
*
|
||||
* @return the value of value
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of value.
|
||||
*
|
||||
* @param value new value of value
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value to replace when duplicating the evidence.
|
||||
*/
|
||||
private String duplicate;
|
||||
|
||||
/**
|
||||
* Get the value of duplicate.
|
||||
*
|
||||
* @return the value of duplicate
|
||||
*/
|
||||
public String getDuplicate() {
|
||||
return duplicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of duplicate.
|
||||
*
|
||||
* @param duplicate new value of duplicate
|
||||
*/
|
||||
public void setDuplicate(String duplicate) {
|
||||
this.duplicate = duplicate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains classes used to parse the hints file to add evidence to dependencies.
|
||||
*/
|
||||
package org.owasp.dependencycheck.xml.hints;
|
||||
@@ -48,13 +48,17 @@ public final class PomUtils {
|
||||
*
|
||||
* @param file the pom.xml file
|
||||
* @return returns a
|
||||
* @throws AnalysisException is thrown if there is an exception extracting or parsing the POM {@link Model} object
|
||||
* @throws AnalysisException is thrown if there is an exception extracting
|
||||
* or parsing the POM {@link Model} object
|
||||
*/
|
||||
public static Model readPom(File file) throws AnalysisException {
|
||||
Model model = null;
|
||||
try {
|
||||
final PomParser parser = new PomParser();
|
||||
model = parser.parse(file);
|
||||
final Model model = parser.parse(file);
|
||||
if (model == null) {
|
||||
throw new AnalysisException(String.format("Unable to parse pom '%s'", file.getPath()));
|
||||
}
|
||||
return model;
|
||||
} catch (PomParseException ex) {
|
||||
LOGGER.warn("Unable to parse pom '{}'", file.getPath());
|
||||
LOGGER.debug("", ex);
|
||||
@@ -68,7 +72,6 @@ public final class PomUtils {
|
||||
LOGGER.debug("", ex);
|
||||
throw new AnalysisException(ex);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +80,8 @@ public final class PomUtils {
|
||||
* @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
|
||||
* @throws AnalysisException is thrown if there is an exception extracting or parsing the POM {@link Model} object
|
||||
* @throws AnalysisException is thrown if there is an exception extracting
|
||||
* or parsing the POM {@link Model} object
|
||||
*/
|
||||
public static Model readPom(String path, JarFile jar) throws AnalysisException {
|
||||
final ZipEntry entry = jar.getEntry(path);
|
||||
@@ -86,7 +90,9 @@ public final class PomUtils {
|
||||
try {
|
||||
final PomParser parser = new PomParser();
|
||||
model = parser.parse(jar.getInputStream(entry));
|
||||
LOGGER.debug("Read POM {}", path);
|
||||
if (model == null) {
|
||||
throw new AnalysisException(String.format("Unable to parse pom '%s/%s'", jar.getName(), path));
|
||||
}
|
||||
} catch (SecurityException ex) {
|
||||
LOGGER.warn("Unable to parse pom '{}' in jar '{}'; invalid signature", path, jar.getName());
|
||||
LOGGER.debug("", ex);
|
||||
@@ -105,11 +111,13 @@ public final class PomUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in the pom file and adds elements as evidence to the given dependency.
|
||||
* Reads in the pom file and adds elements as evidence to the given
|
||||
* dependency.
|
||||
*
|
||||
* @param dependency the dependency being analyzed
|
||||
* @param pomFile the pom file to read
|
||||
* @throws AnalysisException is thrown if there is an exception parsing the pom
|
||||
* @throws AnalysisException is thrown if there is an exception parsing the
|
||||
* pom
|
||||
*/
|
||||
public static void analyzePOM(Dependency dependency, File pomFile) throws AnalysisException {
|
||||
final Model pom = PomUtils.readPom(pomFile);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
package org.owasp.dependencycheck.xml.suppression;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
package org.owasp.dependencycheck.xml.suppression;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
@@ -33,7 +31,7 @@ public class SuppressionErrorHandler implements ErrorHandler {
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionErrorHandler.class);
|
||||
//private static final Logger LOGGER = LoggerFactory.getLogger(SuppressionErrorHandler.class);
|
||||
|
||||
/**
|
||||
* Builds a prettier exception message.
|
||||
@@ -70,7 +68,7 @@ public class SuppressionErrorHandler implements ErrorHandler {
|
||||
*/
|
||||
@Override
|
||||
public void warning(SAXParseException ex) throws SAXException {
|
||||
LOGGER.debug("", ex);
|
||||
//LOGGER.debug("", ex);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
package org.owasp.dependencycheck.xml.suppression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
package org.owasp.dependencycheck.xml.suppression;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
package org.owasp.dependencycheck.xml.suppression;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -61,14 +61,22 @@ public class SuppressionParser {
|
||||
* http://docs.oracle.com/javase/tutorial/jaxp/sax/validation.html
|
||||
*/
|
||||
public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
|
||||
/**
|
||||
* The suppression schema file location.
|
||||
*/
|
||||
private static final String SUPPRESSION_SCHEMA = "schema/dependency-suppression.1.1.xsd";
|
||||
/**
|
||||
* The old suppression schema file location.
|
||||
*/
|
||||
private static final String OLD_SUPPRESSION_SCHEMA = "schema/suppression.xsd";
|
||||
|
||||
/**
|
||||
* Parses the given xml file and returns a list of the suppression rules
|
||||
* Parses the given XML file and returns a list of the suppression rules
|
||||
* contained.
|
||||
*
|
||||
* @param file an xml file containing suppression rules
|
||||
* @param file an XML file containing suppression rules
|
||||
* @return a list of suppression rules
|
||||
* @throws SuppressionParseException thrown if the xml file cannot be parsed
|
||||
* @throws SuppressionParseException thrown if the XML file cannot be parsed
|
||||
*/
|
||||
public List<SuppressionRule> parseSuppressionRules(File file) throws SuppressionParseException {
|
||||
FileInputStream fis = null;
|
||||
@@ -104,17 +112,18 @@ public class SuppressionParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given xml stream and returns a list of the suppression rules
|
||||
* Parses the given XML stream and returns a list of the suppression rules
|
||||
* contained.
|
||||
*
|
||||
* @param inputStream an InputStream containing suppression rues
|
||||
* @param inputStream an InputStream containing suppression rules
|
||||
* @return a list of suppression rules
|
||||
* @throws SuppressionParseException thrown if the xml cannot be parsed
|
||||
* @throws SAXException thrown if the xml cannot be parsed
|
||||
* @throws SuppressionParseException thrown if the XML cannot be parsed
|
||||
* @throws SAXException thrown if the XML cannot be parsed
|
||||
*/
|
||||
public List<SuppressionRule> parseSuppressionRules(InputStream inputStream) throws SuppressionParseException, SAXException {
|
||||
InputStream schemaStream = null;
|
||||
try {
|
||||
final InputStream schemaStream = this.getClass().getClassLoader().getResourceAsStream("schema/dependency-suppression.1.1.xsd");
|
||||
schemaStream = this.getClass().getClassLoader().getResourceAsStream(SUPPRESSION_SCHEMA);
|
||||
final SuppressionHandler handler = new SuppressionHandler();
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
@@ -149,20 +158,29 @@ public class SuppressionParser {
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new SuppressionParseException(ex);
|
||||
} finally {
|
||||
if (schemaStream != null) {
|
||||
try {
|
||||
schemaStream.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Error closing suppression file stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given xml stream and returns a list of the suppression rules
|
||||
* Parses the given XML stream and returns a list of the suppression rules
|
||||
* contained.
|
||||
*
|
||||
* @param inputStream an InputStream containing suppression rues
|
||||
* @return a list of suppression rules
|
||||
* @throws SuppressionParseException if the xml cannot be parsed
|
||||
* @throws SuppressionParseException if the XML cannot be parsed
|
||||
*/
|
||||
private List<SuppressionRule> parseOldSuppressionRules(InputStream inputStream) throws SuppressionParseException {
|
||||
InputStream schemaStream = null;
|
||||
try {
|
||||
final InputStream schemaStream = this.getClass().getClassLoader().getResourceAsStream("schema/suppression.xsd");
|
||||
schemaStream = this.getClass().getClassLoader().getResourceAsStream(OLD_SUPPRESSION_SCHEMA);
|
||||
final SuppressionHandler handler = new SuppressionHandler();
|
||||
final SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
@@ -176,7 +194,6 @@ public class SuppressionParser {
|
||||
|
||||
final Reader reader = new InputStreamReader(inputStream, "UTF-8");
|
||||
final InputSource in = new InputSource(reader);
|
||||
//in.setEncoding("UTF-8");
|
||||
|
||||
xmlReader.parse(in);
|
||||
|
||||
@@ -193,6 +210,14 @@ public class SuppressionParser {
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("", ex);
|
||||
throw new SuppressionParseException(ex);
|
||||
} finally {
|
||||
if (schemaStream != null) {
|
||||
try {
|
||||
schemaStream.close();
|
||||
} catch (IOException ex) {
|
||||
LOGGER.debug("Error closing old suppression file stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* Copyright (c) 2013 Jeremy Long. All Rights Reserved.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
package org.owasp.dependencycheck.xml.suppression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* Contains classes used to suppress findings.
|
||||
*/
|
||||
package org.owasp.dependencycheck.suppression;
|
||||
package org.owasp.dependencycheck.xml.suppression;
|
||||
Binary file not shown.
@@ -22,3 +22,5 @@ org.owasp.dependencycheck.analyzer.RubyGemspecAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.RubyBundlerAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.RubyBundleAuditAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.ComposerLockAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.CocoaPodsAnalyzer
|
||||
org.owasp.dependencycheck.analyzer.SwiftPackageManagerAnalyzer
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<hints xmlns="https://jeremylong.github.io/DependencyCheck/dependency-hint.1.1.xsd">
|
||||
<hint>
|
||||
<given>
|
||||
<evidence type="product" source="Manifest" name="Implementation-Title" value="Spring Framework" confidence="HIGH"/>
|
||||
<evidence type="product" source="Manifest" name="Implementation-Title" value="org.springframework.core" confidence="HIGH"/>
|
||||
<evidence type="product" source="Manifest" name="Implementation-Title" value="spring-core" confidence="HIGH"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="product" source="hint analyzer" name="product" value="springsource_spring_framework" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="SpringSource" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="vmware" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="pivotal" confidence="HIGH"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<evidence type="product" source="jar" name="package name" value="springframework" confidence="LOW"/>
|
||||
<fileName contains="spring"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="product" source="hint analyzer" name="product" value="springsource_spring_framework" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="SpringSource" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="vmware" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="pivotal" confidence="HIGH"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<evidence type="product" source="jar" name="package name" value="springframework" confidence="LOW"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="product" source="hint analyzer" name="product" value="springsource_spring_framework" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="vmware" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="pivotal" confidence="HIGH"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<evidence type="product" source="Manifest" name="Bundle-Name" value="Spring Security Core" confidence="MEDIUM"/>
|
||||
<evidence type="product" source="pom" name="artifactid" value="spring-security-core" confidence="HIGH"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="product" source="hint analyzer" name="product" value="springsource_spring_framework" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="SpringSource" confidence="HIGH"/>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="vmware" confidence="HIGH"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<evidence type="vendor" source="composer.lock" name="vendor" value="symfony" confidence="HIGHEST"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="sensiolabs" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<evidence type="vendor" source="composer.lock" name="vendor" value="zendframework" confidence="HIGHEST"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="zend" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<evidence type="product" source="composer.lock" name="product" value="zendframework" confidence="HIGHEST"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="vendor" source="hint analyzer" name="vendor" value="zend_framework" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
|
||||
<!-- begin hack for temporary patch of issue #534-->
|
||||
<hint>
|
||||
<given>
|
||||
<fileName regex="true" contains=".*hibernate-validator-5\.0\..*"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="version" source="hint" name="version" value="5.0" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<fileName regex="true" contains=".*hibernate-validator-5\.1\.[01].*"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="version" source="hint" name="version" value="5.1" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<fileName regex="true" contains=".*hibernate-validator-4\.1\..*"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="version" source="hint" name="version" value="4.1.0" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<fileName regex="true" contains=".*hibernate-validator-4\.2\.0.*"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="version" source="hint" name="version" value="4.2.0" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
<hint>
|
||||
<given>
|
||||
<fileName regex="true" contains=".*hibernate-validator-4\.3\.[01]\..*"/>
|
||||
</given>
|
||||
<add>
|
||||
<evidence type="version" source="hint" name="version" value="4.3.0" confidence="HIGHEST"/>
|
||||
</add>
|
||||
</hint>
|
||||
<!-- end hack for temporary patch of issue #534-->
|
||||
|
||||
|
||||
<vendorDuplicatingHint value="sun" duplicate="oracle"/>
|
||||
<vendorDuplicatingHint value="oracle" duplicate="sun"/>
|
||||
</hints>
|
||||
@@ -56,6 +56,13 @@
|
||||
<cpe>cpe:/a:oracle:glassfish</cpe>
|
||||
<cpe>cpe:/a:oracle:oracle_client</cpe>
|
||||
</suppress>
|
||||
<suppress base="true">
|
||||
<notes><![CDATA[
|
||||
Supresses false positives on jersey-apache-client4
|
||||
]]></notes>
|
||||
<gav regex="true">com\.sun\.jersey\.contribs:jersey-apache-client.*</gav>
|
||||
<cpe>cpe:/a:apache:httpclient</cpe>
|
||||
</suppress>
|
||||
<suppress base="true">
|
||||
<notes><![CDATA[
|
||||
Suppresses false positives on glassfish
|
||||
@@ -403,7 +410,7 @@
|
||||
<notes><![CDATA[
|
||||
Aether false positive.
|
||||
]]></notes>
|
||||
<gav regex="true">org.eclipse.aether:aether.*</gav>
|
||||
<gav regex="true">org\.eclipse\.aether:aether.*</gav>
|
||||
<cpe>cpe:/a:eclipse:eclipse_ide</cpe>
|
||||
</suppress>
|
||||
<suppress base="true">
|
||||
@@ -413,4 +420,11 @@
|
||||
<filePath regex="true">.*\.(jar|ear|war|pom)</filePath>
|
||||
<cpe>cpe:/a:services_project:services</cpe>
|
||||
</suppress>
|
||||
<suppress base="true">
|
||||
<notes><![CDATA[
|
||||
jenkins-client false positives
|
||||
]]></notes>
|
||||
<gav regex="true">com\.offbytwo\.jenkins:jenkins-client:.*</gav>
|
||||
<cpe>cpe:/a:jenkins:jenkins</cpe>
|
||||
</suppress>
|
||||
</suppressions>
|
||||
|
||||
@@ -59,6 +59,7 @@ cve.url-1.2.base=https://nvd.nist.gov/download/nvdcve-%d.xml.gz
|
||||
#cve.url-1.2.base=http://nvd.nist.gov/download/nvdcve-%d.xml
|
||||
cve.url-2.0.base=https://nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml.gz
|
||||
#cve.url-2.0.base=http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml
|
||||
cve.cpe.startswith.filter=cpe:/a:
|
||||
|
||||
cpe.validfordays=30
|
||||
cpe.url=http://static.nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.3.xml.gz
|
||||
@@ -95,6 +96,8 @@ analyzer.nuspec.enabled=true
|
||||
analyzer.openssl.enabled=true
|
||||
analyzer.central.enabled=true
|
||||
analyzer.nexus.enabled=false
|
||||
analyzer.cocoapods.enabled=true
|
||||
analyzer.swift.package.manager.enabled=true
|
||||
#whether the nexus analyzer uses the proxy
|
||||
analyzer.nexus.proxy=true
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema id="hints"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
targetNamespace="https://jeremylong.github.io/DependencyCheck/dependency-hint.1.0.xsd"
|
||||
xmlns:dc="https://jeremylong.github.io/DependencyCheck/dependency-hint.1.0.xsd">
|
||||
|
||||
<xs:simpleType name="type">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="vendor"/>
|
||||
<xs:enumeration value="product"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="confidence">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="HIGHEST"/>
|
||||
<xs:enumeration value="HIGH"/>
|
||||
<xs:enumeration value="MEDIUM"/>
|
||||
<xs:enumeration value="LOW"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="evidence">
|
||||
<xs:attribute name="type" use="required" type="dc:type"/>
|
||||
<xs:attribute name="source" use="required" type="xs:string"/>
|
||||
<xs:attribute name="name" use="required" type="xs:string"/>
|
||||
<xs:attribute name="value" use="required" type="xs:string"/>
|
||||
<xs:attribute name="confidence" use="required" type="dc:confidence"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="fileName">
|
||||
<xs:attribute name="contains" use="required" type="xs:string"/>
|
||||
<xs:attribute name="regex" use="optional" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="caseSensitive" use="optional" type="xs:boolean" default="false"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="given">
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="evidence" type="dc:evidence"/>
|
||||
<xs:element name="fileName" type="dc:fileName"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="add">
|
||||
<xs:sequence minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="evidence" type="dc:evidence"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="hint">
|
||||
<xs:sequence minOccurs="1" maxOccurs="1">
|
||||
<xs:element name="given" type="dc:given"/>
|
||||
<xs:element name="add" type="dc:add"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="duplicatingHint">
|
||||
<xs:attribute name="value" use="required" type="xs:string"/>
|
||||
<xs:attribute name="duplicate" use="required" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:element name="hints">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="hint" type="dc:hint"/>
|
||||
</xs:sequence>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="vendorDuplicatingHint" type="dc:duplicatingHint"/>
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema id="hints"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
targetNamespace="https://jeremylong.github.io/DependencyCheck/dependency-hint.1.1.xsd"
|
||||
xmlns:dc="https://jeremylong.github.io/DependencyCheck/dependency-hint.1.1.xsd">
|
||||
|
||||
<xs:simpleType name="givenType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="vendor"/>
|
||||
<xs:enumeration value="product"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="addType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="vendor"/>
|
||||
<xs:enumeration value="product"/>
|
||||
<xs:enumeration value="version"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="confidence">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="HIGHEST"/>
|
||||
<xs:enumeration value="HIGH"/>
|
||||
<xs:enumeration value="MEDIUM"/>
|
||||
<xs:enumeration value="LOW"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:complexType name="givenEvidence">
|
||||
<xs:attribute name="type" use="required" type="dc:givenType"/>
|
||||
<xs:attribute name="source" use="required" type="xs:string"/>
|
||||
<xs:attribute name="name" use="required" type="xs:string"/>
|
||||
<xs:attribute name="value" use="required" type="xs:string"/>
|
||||
<xs:attribute name="confidence" use="required" type="dc:confidence"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="addEvidence">
|
||||
<xs:attribute name="type" use="required" type="dc:addType"/>
|
||||
<xs:attribute name="source" use="required" type="xs:string"/>
|
||||
<xs:attribute name="name" use="required" type="xs:string"/>
|
||||
<xs:attribute name="value" use="required" type="xs:string"/>
|
||||
<xs:attribute name="confidence" use="required" type="dc:confidence"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="fileName">
|
||||
<xs:attribute name="contains" use="required" type="xs:string"/>
|
||||
<xs:attribute name="regex" use="optional" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="caseSensitive" use="optional" type="xs:boolean" default="false"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="given">
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="evidence" type="dc:givenEvidence"/>
|
||||
<xs:element name="fileName" type="dc:fileName"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="add">
|
||||
<xs:sequence minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="evidence" type="dc:addEvidence"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="hint">
|
||||
<xs:sequence minOccurs="1" maxOccurs="1">
|
||||
<xs:element name="given" type="dc:given"/>
|
||||
<xs:element name="add" type="dc:add"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="duplicatingHint">
|
||||
<xs:attribute name="value" use="required" type="xs:string"/>
|
||||
<xs:attribute name="duplicate" use="required" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:element name="hints">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="hint" type="dc:hint"/>
|
||||
</xs:sequence>
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="vendorDuplicatingHint" type="dc:duplicatingHint"/>
|
||||
</xs:sequence>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -541,7 +541,7 @@ arising out of or in connection with the use of this tool, the analysis performe
|
||||
<ul class="indent">
|
||||
<li><i>dependency-check version</i>: $version</li>
|
||||
<li><i>Report Generated On</i>: $scanDate</li>
|
||||
<li><i>Dependencies Scanned</i>: $depCount</li>
|
||||
<li><i>Dependencies Scanned</i>: $depCount ($dependencies.size() unique)</li>
|
||||
<li><i>Vulnerable Dependencies</i>: $vulnDepCount</li>
|
||||
<li><i>Vulnerabilities Found</i>: $vulnCount</li>
|
||||
<li><i>Vulnerabilities Suppressed</i>: $vulnSuppressedCount</li>
|
||||
@@ -660,13 +660,13 @@ arising out of or in connection with the use of this tool, the analysis performe
|
||||
<b>MD5:</b> $enc.html($dependency.Md5sum)<br/>
|
||||
<b>SHA1:</b> $enc.html($dependency.Sha1sum)
|
||||
#if ($dependency.projectReferences.size()==1)
|
||||
<br/><b>Referenced In Project:</b>
|
||||
<br/><b>Referenced In Project/Scope:</b>
|
||||
#foreach($ref in $dependency.projectReferences)
|
||||
$enc.html($ref)
|
||||
#end
|
||||
#end
|
||||
#if ($dependency.projectReferences.size()>1)
|
||||
<br/><b>Referenced In Projects:</b><ul>
|
||||
<br/><b>Referenced In Projects/Scopes:</b><ul>
|
||||
#foreach($ref in $dependency.projectReferences)
|
||||
<li>$enc.html($ref)</li>
|
||||
#end
|
||||
|
||||
@@ -17,13 +17,19 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck;
|
||||
|
||||
import org.junit.After;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Before;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.owasp.dependencycheck.data.nvdcve.CveDB;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
|
||||
import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.exception.ReportException;
|
||||
import org.owasp.dependencycheck.reporting.ReportGenerator;
|
||||
import org.owasp.dependencycheck.utils.InvalidSettingException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
@@ -35,10 +41,14 @@ public class EngineIntegrationTest extends BaseDBTestCase {
|
||||
/**
|
||||
* Test running the entire engine.
|
||||
*
|
||||
* @throws Exception is thrown when an exception occurs.
|
||||
* @throws java.io.IOException
|
||||
* @throws org.owasp.dependencycheck.utils.InvalidSettingException
|
||||
* @throws org.owasp.dependencycheck.data.nvdcve.DatabaseException
|
||||
* @throws org.owasp.dependencycheck.exception.ReportException
|
||||
* @throws org.owasp.dependencycheck.exception.ExceptionCollection
|
||||
*/
|
||||
@Test
|
||||
public void testEngine() throws Exception {
|
||||
public void testEngine() throws IOException, InvalidSettingException, DatabaseException, ReportException, ExceptionCollection {
|
||||
String testClasses = "target/test-classes";
|
||||
boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
|
||||
@@ -46,7 +56,23 @@ public class EngineIntegrationTest extends BaseDBTestCase {
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
|
||||
instance.scan(testClasses);
|
||||
assertTrue(instance.getDependencies().size() > 0);
|
||||
instance.analyzeDependencies();
|
||||
try {
|
||||
instance.analyzeDependencies();
|
||||
} catch (ExceptionCollection ex) {
|
||||
if (ex.getExceptions().size()==1 &&
|
||||
(ex.getExceptions().get(0).getMessage().contains("bundle-audit") ||
|
||||
ex.getExceptions().get(0).getMessage().contains("AssemblyAnalyzer"))) {
|
||||
//this is fine to ignore
|
||||
} else if (ex.getExceptions().size()==2 &&
|
||||
((ex.getExceptions().get(0).getMessage().contains("bundle-audit") &&
|
||||
ex.getExceptions().get(1).getMessage().contains("AssemblyAnalyzer")) ||
|
||||
(ex.getExceptions().get(1).getMessage().contains("bundle-audit") &&
|
||||
ex.getExceptions().get(0).getMessage().contains("AssemblyAnalyzer")))) {
|
||||
//this is fine to ignore
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
CveDB cveDB = new CveDB();
|
||||
cveDB.open();
|
||||
DatabaseProperties dbProp = cveDB.getDatabaseProperties();
|
||||
|
||||
@@ -23,8 +23,7 @@ import org.owasp.dependencycheck.BaseTest;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionParseException;
|
||||
import org.owasp.dependencycheck.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.xml.suppression.SuppressionRule;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -35,6 +34,7 @@ import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
|
||||
/**
|
||||
* @author Jeremy Long
|
||||
@@ -49,7 +49,8 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getSupportedExtensions method, of class AbstractSuppressionAnalyzer.
|
||||
* Test of getSupportedExtensions method, of class
|
||||
* AbstractSuppressionAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testGetSupportedExtensions() {
|
||||
@@ -58,7 +59,8 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getRules method, of class AbstractSuppressionAnalyzer for suppression file declared as URL.
|
||||
* Test of getRules method, of class AbstractSuppressionAnalyzer for
|
||||
* suppression file declared as URL.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRulesFromSuppressionFileFromURL() throws Exception {
|
||||
@@ -70,7 +72,8 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getRules method, of class AbstractSuppressionAnalyzer for suppression file declared as URL.
|
||||
* Test of getRules method, of class AbstractSuppressionAnalyzer for
|
||||
* suppression file declared as URL.
|
||||
*/
|
||||
@Test
|
||||
public void testGetRulesFromSuppressionFileInClasspath() throws Exception {
|
||||
@@ -81,7 +84,7 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest {
|
||||
assertTrue(expCount <= currentSize);
|
||||
}
|
||||
|
||||
@Test(expected = SuppressionParseException.class)
|
||||
@Test(expected = InitializationException.class)
|
||||
public void testFailureToLocateSuppressionFileAnywhere() throws Exception {
|
||||
Settings.setString(Settings.KEYS.SUPPRESSION_FILE, "doesnotexist.xml");
|
||||
instance.initialize();
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -116,7 +116,6 @@ public class ArchiveAnalyzerIntegrationTest extends BaseDBTestCase {
|
||||
try {
|
||||
instance.initialize();
|
||||
File file = BaseTest.getResourceAsFile(this, "daytrader-ear-2.1.7.ear");
|
||||
//File file = new File(this.getClass().getClassLoader().getResource("daytrader-ear-2.1.7.ear").getPath());
|
||||
Dependency dependency = new Dependency(file);
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false);
|
||||
@@ -136,6 +135,36 @@ public class ArchiveAnalyzerIntegrationTest extends BaseDBTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class ArchiveAnalyzer, with an executable jar.
|
||||
*/
|
||||
@Test
|
||||
public void testAnalyzeExecutableJar() throws Exception {
|
||||
ArchiveAnalyzer instance = new ArchiveAnalyzer();
|
||||
//trick the analyzer into thinking it is active.
|
||||
instance.accept(new File("test.ear"));
|
||||
try {
|
||||
instance.initialize();
|
||||
File file = BaseTest.getResourceAsFile(this, "bootable-0.1.0.jar");
|
||||
Dependency dependency = new Dependency(file);
|
||||
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false);
|
||||
Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false);
|
||||
Engine engine = new Engine();
|
||||
|
||||
int initial_size = engine.getDependencies().size();
|
||||
instance.analyze(dependency, engine);
|
||||
int ending_size = engine.getDependencies().size();
|
||||
|
||||
engine.cleanup();
|
||||
|
||||
assertTrue(initial_size < ending_size);
|
||||
|
||||
} finally {
|
||||
instance.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class ArchiveAnalyzer.
|
||||
*/
|
||||
|
||||
@@ -16,22 +16,17 @@
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
|
||||
import org.owasp.dependencycheck.BaseTest;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import org.junit.Assume;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.owasp.dependencycheck.BaseTest;
|
||||
@@ -31,6 +32,7 @@ import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.exception.InitializationException;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -60,11 +62,12 @@ public class AssemblyAnalyzerTest extends BaseTest {
|
||||
analyzer = new AssemblyAnalyzer();
|
||||
analyzer.accept(new File("test.dll")); // trick into "thinking it is active"
|
||||
analyzer.initialize();
|
||||
Assume.assumeTrue("Mono is not installed, skipping tests.", analyzer.buildArgumentList() == null);
|
||||
} catch (Exception e) {
|
||||
if (e.getMessage().contains("Could not execute .NET AssemblyAnalyzer")) {
|
||||
LOGGER.warn("Exception setting up AssemblyAnalyzer. Tests will be incomplete");
|
||||
} else {
|
||||
LOGGER.warn("Exception setting up AssemblyAnalyzer. Tests will be incomplete", e);
|
||||
LOGGER.warn("Exception setting up AssemblyAnalyzer. Tests will be incomplete");
|
||||
}
|
||||
Assume.assumeNoException("Is mono installed? TESTS WILL BE INCOMPLETE", e);
|
||||
}
|
||||
@@ -80,7 +83,7 @@ public class AssemblyAnalyzerTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void testAnalysis() throws Exception {
|
||||
//File f = new File(AssemblyAnalyzerTest.class.getClassLoader().getResource("GrokAssembly.exe").getPath());
|
||||
assumeNotNull(analyzer.buildArgumentList());
|
||||
File f = BaseTest.getResourceAsFile(this, "GrokAssembly.exe");
|
||||
Dependency d = new Dependency(f);
|
||||
analyzer.analyze(d, null);
|
||||
@@ -103,7 +106,7 @@ public class AssemblyAnalyzerTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void testLog4Net() throws Exception {
|
||||
//File f = new File(AssemblyAnalyzerTest.class.getClassLoader().getResource("log4net.dll").getPath());
|
||||
assumeNotNull(analyzer.buildArgumentList());
|
||||
File f = BaseTest.getResourceAsFile(this, "log4net.dll");
|
||||
|
||||
Dependency d = new Dependency(f);
|
||||
@@ -115,9 +118,10 @@ public class AssemblyAnalyzerTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void testNonexistent() {
|
||||
assumeNotNull(analyzer.buildArgumentList());
|
||||
|
||||
// Tweak the log level so the warning doesn't show in the console
|
||||
String oldProp = System.getProperty(LOG_KEY, "info");
|
||||
//File f = new File(AssemblyAnalyzerTest.class.getClassLoader().getResource("log4net.dll").getPath());
|
||||
File f = BaseTest.getResourceAsFile(this, "log4net.dll");
|
||||
File test = new File(f.getParent(), "nonexistent.dll");
|
||||
Dependency d = new Dependency(test);
|
||||
@@ -157,8 +161,8 @@ public class AssemblyAnalyzerTest extends BaseTest {
|
||||
AssemblyAnalyzer aanalyzer = new AssemblyAnalyzer();
|
||||
aanalyzer.accept(new File("test.dll")); // trick into "thinking it is active"
|
||||
aanalyzer.initialize();
|
||||
fail("Expected an AnalysisException");
|
||||
} catch (AnalysisException ae) {
|
||||
fail("Expected an InitializationException");
|
||||
} catch (InitializationException ae) {
|
||||
assertEquals("An error occurred with the .NET AssemblyAnalyzer", ae.getMessage());
|
||||
} finally {
|
||||
System.setProperty(LOG_KEY, oldProp);
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
import org.owasp.dependencycheck.BaseTest;
|
||||
import org.owasp.dependencycheck.BaseDBTestCase;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.data.cpe.IndexEntry;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
@@ -82,23 +83,33 @@ public class CPEAnalyzerIntegrationTest extends BaseDBTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void testDetermineCPE_full() throws Exception {
|
||||
CPEAnalyzer instance = new CPEAnalyzer();
|
||||
instance.open();
|
||||
FileNameAnalyzer fnAnalyzer = new FileNameAnalyzer();
|
||||
JarAnalyzer jarAnalyzer = new JarAnalyzer();
|
||||
HintAnalyzer hAnalyzer = new HintAnalyzer();
|
||||
FalsePositiveAnalyzer fp = new FalsePositiveAnalyzer();
|
||||
//update needs to be performed so that xtream can be tested
|
||||
Engine e = new Engine();
|
||||
e.doUpdates();
|
||||
|
||||
CPEAnalyzer cpeAnalyzer = new CPEAnalyzer();
|
||||
try {
|
||||
//callDetermineCPE_full("struts2-core-2.3.16.3.jar", "cpe:/a:apache:struts:2.3.16.3", instance, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("hazelcast-2.5.jar", null, instance, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("spring-context-support-2.5.5.jar", "cpe:/a:springsource:spring_framework:2.5.5", instance, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("spring-core-3.0.0.RELEASE.jar", "cpe:/a:vmware:springsource_spring_framework:3.0.0", instance, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("org.mortbay.jetty.jar", "cpe:/a:mortbay_jetty:jetty:4.2.27", instance, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("jaxb-xercesImpl-1.5.jar", null, instance, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("ehcache-core-2.2.0.jar", null, instance, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
cpeAnalyzer.initialize();
|
||||
FileNameAnalyzer fnAnalyzer = new FileNameAnalyzer();
|
||||
fnAnalyzer.initialize();
|
||||
JarAnalyzer jarAnalyzer = new JarAnalyzer();
|
||||
jarAnalyzer.accept(new File("test.jar"));//trick analyzer into "thinking it is active"
|
||||
jarAnalyzer.initialize();
|
||||
HintAnalyzer hAnalyzer = new HintAnalyzer();
|
||||
hAnalyzer.initialize();
|
||||
FalsePositiveAnalyzer fp = new FalsePositiveAnalyzer();
|
||||
fp.initialize();
|
||||
|
||||
callDetermineCPE_full("hazelcast-2.5.jar", null, cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("spring-context-support-2.5.5.jar", "cpe:/a:springsource:spring_framework:2.5.5", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("spring-core-3.0.0.RELEASE.jar", "cpe:/a:vmware:springsource_spring_framework:3.0.0", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("org.mortbay.jetty.jar", "cpe:/a:mortbay_jetty:jetty:4.2.27", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("jaxb-xercesImpl-1.5.jar", null, cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("ehcache-core-2.2.0.jar", null, cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
callDetermineCPE_full("xstream-1.4.8.jar", "cpe:/a:x-stream:xstream:1.4.8", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp);
|
||||
|
||||
} finally {
|
||||
instance.close();
|
||||
cpeAnalyzer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +118,7 @@ public class CPEAnalyzerIntegrationTest extends BaseDBTestCase {
|
||||
*
|
||||
* @throws Exception is thrown when an exception occurs
|
||||
*/
|
||||
public void callDetermineCPE_full(String depName, String expResult, CPEAnalyzer instance, FileNameAnalyzer fnAnalyzer, JarAnalyzer jarAnalyzer, HintAnalyzer hAnalyzer, FalsePositiveAnalyzer fp) throws Exception {
|
||||
public void callDetermineCPE_full(String depName, String expResult, CPEAnalyzer cpeAnalyzer, FileNameAnalyzer fnAnalyzer, JarAnalyzer jarAnalyzer, HintAnalyzer hAnalyzer, FalsePositiveAnalyzer fp) throws Exception {
|
||||
|
||||
//File file = new File(this.getClass().getClassLoader().getResource(depName).getPath());
|
||||
File file = BaseTest.getResourceAsFile(this, depName);
|
||||
@@ -117,7 +128,7 @@ public class CPEAnalyzerIntegrationTest extends BaseDBTestCase {
|
||||
fnAnalyzer.analyze(dep, null);
|
||||
jarAnalyzer.analyze(dep, null);
|
||||
hAnalyzer.analyze(dep, null);
|
||||
instance.analyze(dep, null);
|
||||
cpeAnalyzer.analyze(dep, null);
|
||||
fp.analyze(dep, null);
|
||||
|
||||
if (expResult != null) {
|
||||
@@ -146,8 +157,10 @@ public class CPEAnalyzerIntegrationTest extends BaseDBTestCase {
|
||||
fnAnalyzer.analyze(struts, null);
|
||||
|
||||
HintAnalyzer hintAnalyzer = new HintAnalyzer();
|
||||
hintAnalyzer.initialize();
|
||||
JarAnalyzer jarAnalyzer = new JarAnalyzer();
|
||||
jarAnalyzer.accept(new File("test.jar"));//trick analyzer into "thinking it is active"
|
||||
jarAnalyzer.initialize();
|
||||
|
||||
jarAnalyzer.analyze(struts, null);
|
||||
hintAnalyzer.analyze(struts, null);
|
||||
@@ -176,6 +189,7 @@ public class CPEAnalyzerIntegrationTest extends BaseDBTestCase {
|
||||
instance.determineCPE(spring);
|
||||
instance.determineCPE(spring3);
|
||||
instance.close();
|
||||
|
||||
|
||||
String expResult = "cpe:/a:apache:struts:2.1.2";
|
||||
Identifier expIdentifier = new Identifier("cpe", expResult, expResult);
|
||||
|
||||
@@ -23,16 +23,11 @@ import org.junit.Test;
|
||||
import org.owasp.dependencycheck.BaseTest;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Confidence;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.owasp.dependencycheck.BaseDBTestCase;
|
||||
|
||||
@@ -53,6 +48,7 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase {
|
||||
*
|
||||
* @throws Exception thrown if there is a problem
|
||||
*/
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.Set;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Before;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.owasp.dependencycheck.BaseTest;
|
||||
import org.owasp.dependencycheck.Engine;
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.io.File;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
@@ -40,6 +41,7 @@ import org.owasp.dependencycheck.dependency.Dependency;
|
||||
import org.owasp.dependencycheck.dependency.Evidence;
|
||||
import org.owasp.dependencycheck.dependency.Identifier;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.exception.ExceptionCollection;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -63,6 +65,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
*
|
||||
* @throws Exception thrown if there is a problem
|
||||
*/
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -114,7 +117,6 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
final Engine engine = new Engine();
|
||||
analyzer.analyze(result, engine);
|
||||
int size = engine.getDependencies().size();
|
||||
|
||||
assertTrue(size >= 1);
|
||||
|
||||
Dependency dependency = engine.getDependencies().get(0);
|
||||
@@ -146,7 +148,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
assertEquals(vulnerability.getCvssScore(), 5.0f, 0.0);
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Make sure Ruby gem bundle-audit is installed. You may also need to set property \"analyzer.bundle.audit.path\".", e);
|
||||
LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Make sure Ruby gem bundle-audit is installed. You may also need to set property \"analyzer.bundle.audit.path\".");
|
||||
Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", e);
|
||||
}
|
||||
}
|
||||
@@ -175,6 +177,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
* Test Ruby dependencies and their paths.
|
||||
*
|
||||
* @throws AnalysisException is thrown when an exception occurs.
|
||||
* @throws DatabaseException thrown when an exception occurs
|
||||
*/
|
||||
@Test
|
||||
public void testDependenciesPath() throws AnalysisException, DatabaseException {
|
||||
@@ -186,6 +189,8 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase {
|
||||
} catch (NullPointerException ex) {
|
||||
LOGGER.error("NPE", ex);
|
||||
throw ex;
|
||||
} catch (ExceptionCollection ex) {
|
||||
Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", ex);
|
||||
}
|
||||
List<Dependency> dependencies = engine.getDependencies();
|
||||
LOGGER.info(dependencies.size() + " dependencies found.");
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package org.owasp.dependencycheck.analyzer;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.owasp.dependencycheck.BaseTest;
|
||||
import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
|
||||
import org.owasp.dependencycheck.dependency.Dependency;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Unit tests for CocoaPodsAnalyzer.
|
||||
*
|
||||
* @author Bianca Jiang
|
||||
*/
|
||||
public class SwiftAnalyzersTest extends BaseTest {
|
||||
|
||||
/**
|
||||
* The analyzer to test.
|
||||
*/
|
||||
CocoaPodsAnalyzer podsAnalyzer;
|
||||
SwiftPackageManagerAnalyzer spmAnalyzer;
|
||||
|
||||
/**
|
||||
* Correctly setup the analyzer for testing.
|
||||
*
|
||||
* @throws Exception thrown if there is a problem
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
podsAnalyzer = new CocoaPodsAnalyzer();
|
||||
podsAnalyzer.setFilesMatched(true);
|
||||
podsAnalyzer.initialize();
|
||||
|
||||
spmAnalyzer = new SwiftPackageManagerAnalyzer();
|
||||
spmAnalyzer.setFilesMatched(true);
|
||||
spmAnalyzer.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the analyzer's temp files, etc.
|
||||
*
|
||||
* @throws Exception thrown if there is a problem
|
||||
*/
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
podsAnalyzer.close();
|
||||
podsAnalyzer = null;
|
||||
|
||||
spmAnalyzer.close();
|
||||
spmAnalyzer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getName method, of class CocoaPodsAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testPodsGetName() {
|
||||
assertThat(podsAnalyzer.getName(), is("CocoaPods Package Analyzer"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of getName method, of class SwiftPackageManagerAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testSPMGetName() {
|
||||
assertThat(spmAnalyzer.getName(), is("SWIFT Package Manager Analyzer"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of supportsFiles method, of class CocoaPodsAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testPodsSupportsFiles() {
|
||||
assertThat(podsAnalyzer.accept(new File("test.podspec")), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of supportsFiles method, of class SwiftPackageManagerAnalyzer.
|
||||
*/
|
||||
@Test
|
||||
public void testSPMSupportsFiles() {
|
||||
assertThat(spmAnalyzer.accept(new File("Package.swift")), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class CocoaPodsAnalyzer.
|
||||
*
|
||||
* @throws AnalysisException is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testCocoaPodsAnalyzer() throws AnalysisException {
|
||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
|
||||
"swift/cocoapods/EasyPeasy.podspec"));
|
||||
podsAnalyzer.analyze(result, null);
|
||||
final String vendorString = result.getVendorEvidence().toString();
|
||||
|
||||
assertThat(vendorString, containsString("Carlos Vidal"));
|
||||
assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy"));
|
||||
assertThat(vendorString, containsString("MIT"));
|
||||
assertThat(result.getProductEvidence().toString(), containsString("EasyPeasy"));
|
||||
assertThat(result.getVersionEvidence().toString(), containsString("0.2.3"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test of analyze method, of class SwiftPackageManagerAnalyzer.
|
||||
*
|
||||
* @throws AnalysisException is thrown when an exception occurs.
|
||||
*/
|
||||
@Test
|
||||
public void testSPMAnalyzer() throws AnalysisException {
|
||||
final Dependency result = new Dependency(BaseTest.getResourceAsFile(this,
|
||||
"swift/Gloss/Package.swift"));
|
||||
spmAnalyzer.analyze(result, null);
|
||||
|
||||
assertThat(result.getProductEvidence().toString(), containsString("Gloss"));
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,8 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.cwe;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.owasp.dependencycheck.BaseTest;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user