Merge remote-tracking branch 'origin/master'

Former-commit-id: 16fb5a44d47fb699282b6babd112848276cb6bab
This commit is contained in:
Steve Springett
2014-09-12 10:50:08 -05:00
30 changed files with 1558 additions and 745 deletions

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.2.4-SNAPSHOT</version>
<version>1.2.5-SNAPSHOT</version>
</parent>
<artifactId>dependency-check-core</artifactId>
@@ -621,6 +621,13 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.0.RELEASE</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<profiles>
<profile>

View File

@@ -18,6 +18,7 @@
package org.owasp.dependencycheck;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
@@ -52,7 +53,7 @@ import org.owasp.dependencycheck.utils.Settings;
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
public class Engine {
public class Engine implements Serializable {
/**
* The list of dependencies.
@@ -61,19 +62,19 @@ public class Engine {
/**
* A Map of analyzers grouped by Analysis phase.
*/
private final EnumMap<AnalysisPhase, List<Analyzer>> analyzers;
private transient final EnumMap<AnalysisPhase, List<Analyzer>> analyzers;
/**
* A Map of analyzers grouped by Analysis phase.
*/
private final Set<FileTypeAnalyzer> fileTypeAnalyzers;
private transient final Set<FileTypeAnalyzer> fileTypeAnalyzers;
/**
* The ClassLoader to use when dynamically loading Analyzer and Update services.
*/
private ClassLoader serviceClassLoader;
private transient ClassLoader serviceClassLoader;
/**
* The Logger for use throughout the class.
*/
private static final Logger LOGGER = Logger.getLogger(Engine.class.getName());
private transient static final Logger LOGGER = Logger.getLogger(Engine.class.getName());
/**
* Creates a new Engine.

View File

@@ -170,29 +170,10 @@ public class CPEAnalyzer implements Analyzer {
* @throws ParseException is thrown when the Lucene query cannot be parsed.
*/
protected void determineCPE(Dependency dependency) throws CorruptIndexException, IOException, ParseException {
Confidence confidence = Confidence.HIGHEST;
String vendors = addEvidenceWithoutDuplicateTerms("", dependency.getVendorEvidence(), confidence);
String products = addEvidenceWithoutDuplicateTerms("", dependency.getProductEvidence(), confidence);
/* bug fix for #40 - version evidence is not showing up as "used" in the reports if there is no
* CPE identified. As such, we are "using" the evidence and ignoring the results. */
addEvidenceWithoutDuplicateTerms("", dependency.getVersionEvidence(), confidence);
int ctr = 0;
do {
if (!vendors.isEmpty() && !products.isEmpty()) {
final List<IndexEntry> entries = searchCPE(vendors, products, dependency.getProductEvidence().getWeighting(),
dependency.getVendorEvidence().getWeighting());
for (IndexEntry e : entries) {
if (verifyEntry(e, dependency)) {
final String vendor = e.getVendor();
final String product = e.getProduct();
determineIdentifiers(dependency, vendor, product);
}
}
}
confidence = reduceConfidence(confidence);
//TODO test dojo-war against this. we shold get dojo-toolkit:dojo-toolkit AND dojo-toolkit:toolkit
String vendors = "";
String products = "";
for (Confidence confidence : Confidence.values()) {
if (dependency.getVendorEvidence().contains(confidence)) {
vendors = addEvidenceWithoutDuplicateTerms(vendors, dependency.getVendorEvidence(), confidence);
}
@@ -201,10 +182,26 @@ public class CPEAnalyzer implements Analyzer {
}
/* bug fix for #40 - version evidence is not showing up as "used" in the reports if there is no
* CPE identified. As such, we are "using" the evidence and ignoring the results. */
if (dependency.getVersionEvidence().contains(confidence)) {
addEvidenceWithoutDuplicateTerms("", dependency.getVersionEvidence(), confidence);
// if (dependency.getVersionEvidence().contains(confidence)) {
// addEvidenceWithoutDuplicateTerms("", dependency.getVersionEvidence(), confidence);
// }
if (!vendors.isEmpty() && !products.isEmpty()) {
final List<IndexEntry> entries = searchCPE(vendors, products, dependency.getProductEvidence().getWeighting(),
dependency.getVendorEvidence().getWeighting());
boolean identifierAdded = false;
for (IndexEntry e : entries) {
if (verifyEntry(e, dependency)) {
final String vendor = e.getVendor();
final String product = e.getProduct();
identifierAdded |= determineIdentifiers(dependency, vendor, product, confidence);
}
}
if (identifierAdded) {
break;
}
}
} while ((++ctr) < 4);
}
}
/**
@@ -239,22 +236,6 @@ public class CPEAnalyzer implements Analyzer {
return sb.toString().trim();
}
/**
* Reduces the given confidence by one level. This returns LOW if the confidence passed in is not HIGH.
*
* @param c the confidence to reduce.
* @return One less then the confidence passed in.
*/
private Confidence reduceConfidence(final Confidence c) {
if (c == Confidence.HIGHEST) {
return Confidence.HIGH;
} else if (c == Confidence.HIGH) {
return Confidence.MEDIUM;
} else {
return Confidence.LOW;
}
}
/**
* <p>
* Searches the Lucene CPE index to identify possible CPE entries associated with the supplied vendor, product, and
@@ -508,14 +489,19 @@ public class CPEAnalyzer implements Analyzer {
* @param dependency the Dependency being analyzed
* @param vendor the vendor for the CPE being analyzed
* @param product the product for the CPE being analyzed
* @return <code>true</code> if an identifier was added to the dependency; otherwise <code>false</code>
* @throws UnsupportedEncodingException is thrown if UTF-8 is not supported
*/
private void determineIdentifiers(Dependency dependency, String vendor, String product) throws UnsupportedEncodingException {
private boolean determineIdentifiers(Dependency dependency, String vendor, String product, Confidence currentConfidence) throws UnsupportedEncodingException {
final Set<VulnerableSoftware> cpes = cve.getCPEs(vendor, product);
DependencyVersion bestGuess = new DependencyVersion("-");
Confidence bestGuessConf = null;
boolean hasBroadMatch = false;
final List<IdentifierMatch> collected = new ArrayList<IdentifierMatch>();
for (Confidence conf : Confidence.values()) {
// if (conf.compareTo(currentConfidence) > 0) {
// break;
// }
for (Evidence evidence : dependency.getVersionEvidence().iterator(conf)) {
final DependencyVersion evVer = DependencyVersionUtil.parseVersion(evidence.getValue());
if (evVer == null) {
@@ -528,9 +514,12 @@ public class CPEAnalyzer implements Analyzer {
} else {
dbVer = DependencyVersionUtil.parseVersion(vs.getVersion());
}
if (dbVer == null //special case, no version specified - everything is vulnerable
|| evVer.equals(dbVer)) { //yeah! exact match
if (dbVer == null) { //special case, no version specified - everything is vulnerable
hasBroadMatch = true;
final String url = String.format(NVD_SEARCH_URL, URLEncoder.encode(vs.getName(), "UTF-8"));
final IdentifierMatch match = new IdentifierMatch("cpe", vs.getName(), url, IdentifierConfidence.BROAD_MATCH, conf);
collected.add(match);
} else if (evVer.equals(dbVer)) { //yeah! exact match
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);
@@ -556,7 +545,11 @@ public class CPEAnalyzer implements Analyzer {
}
}
final String cpeName = String.format("cpe:/a:%s:%s:%s", vendor, product, bestGuess.toString());
final String url = null;
String url = null;
if (hasBroadMatch) { //if we have a broad match we can add the URL to the best guess.
final String cpeUrlName = String.format("cpe:/a:%s:%s", vendor, product);
url = String.format(NVD_SEARCH_URL, URLEncoder.encode(cpeUrlName, "UTF-8"));
}
if (bestGuessConf == null) {
bestGuessConf = Confidence.LOW;
}
@@ -566,6 +559,7 @@ public class CPEAnalyzer implements Analyzer {
Collections.sort(collected);
final IdentifierConfidence bestIdentifierQuality = collected.get(0).getConfidence();
final Confidence bestEvidenceQuality = collected.get(0).getEvidenceConfidence();
boolean identifierAdded = false;
for (IdentifierMatch m : collected) {
if (bestIdentifierQuality.equals(m.getConfidence())
&& bestEvidenceQuality.equals(m.getEvidenceConfidence())) {
@@ -576,8 +570,10 @@ public class CPEAnalyzer implements Analyzer {
i.setConfidence(bestEvidenceQuality);
}
dependency.addIdentifier(i);
identifierAdded = true;
}
}
return identifierAdded;
}
/**
@@ -592,7 +588,12 @@ public class CPEAnalyzer implements Analyzer {
/**
* A best guess for the CPE.
*/
BEST_GUESS
BEST_GUESS,
/**
* The entire vendor/product group must be added (without a guess at version) because there is a CVE with a VS
* that only specifies vendor/product.
*/
BROAD_MATCH
}
/**

View File

@@ -86,12 +86,41 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer {
public void analyze(Dependency dependency, Engine engine) throws AnalysisException {
removeJreEntries(dependency);
removeBadMatches(dependency);
removeBadSpringMatches(dependency);
removeWrongVersionMatches(dependency);
removeSpuriousCPE(dependency);
removeDuplicativeEntriesFromJar(dependency, engine);
addFalseNegativeCPEs(dependency);
}
private void removeBadSpringMatches(Dependency dependency) {
String mustContain = null;
for (Identifier i : dependency.getIdentifiers()) {
if ("maven".contains(i.getType())) {
if (i.getValue() != null && i.getValue().startsWith("org.springframework.")) {
int endPoint = i.getValue().indexOf(":", 19);
if (endPoint >= 0) {
mustContain = i.getValue().substring(19, endPoint).toLowerCase();
break;
}
}
}
}
if (mustContain != null) {
Iterator<Identifier> itr = dependency.getIdentifiers().iterator();
while (itr.hasNext()) {
Identifier i = itr.next();
if ("cpe".contains(i.getType())
&& i.getValue() != null
&& i.getValue().startsWith("cpe:/a:springsource:")
&& !i.getValue().toLowerCase().contains(mustContain)) {
dependency.getIdentifiers().remove(i);
}
}
}
}
/**
* <p>
* Intended to remove spurious CPE entries. By spurious we mean duplicate, less specific CPE entries.</p>

View File

@@ -587,7 +587,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
groupid = groupid.substring(4);
}
foundSomething = true;
dependency.getVendorEvidence().addEvidence("pom", "groupid", groupid, Confidence.HIGH);
dependency.getVendorEvidence().addEvidence("pom", "groupid", groupid, Confidence.HIGHEST);
dependency.getProductEvidence().addEvidence("pom", "groupid", groupid, Confidence.LOW);
addMatchingValues(classes, groupid, dependency.getVendorEvidence());
addMatchingValues(classes, groupid, dependency.getProductEvidence());
@@ -616,7 +616,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
artifactid = artifactid.substring(4);
}
foundSomething = true;
dependency.getProductEvidence().addEvidence("pom", "artifactid", artifactid, Confidence.HIGH);
dependency.getProductEvidence().addEvidence("pom", "artifactid", artifactid, Confidence.HIGHEST);
dependency.getVendorEvidence().addEvidence("pom", "artifactid", artifactid, Confidence.LOW);
addMatchingValues(classes, artifactid, dependency.getVendorEvidence());
addMatchingValues(classes, artifactid, dependency.getProductEvidence());

View File

@@ -19,6 +19,7 @@ package org.owasp.dependencycheck.dependency;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.security.NoSuchAlgorithmException;
import java.util.Set;
import java.util.SortedSet;
@@ -35,7 +36,7 @@ import org.owasp.dependencycheck.utils.FileUtils;
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
public class Dependency implements Comparable<Dependency> {
public class Dependency implements Serializable, Comparable<Dependency> {
/**
* The logger.

View File

@@ -17,12 +17,14 @@
*/
package org.owasp.dependencycheck.dependency;
import java.io.Serializable;
/**
* Evidence is a piece of information about a Dependency.
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
public class Evidence implements Comparable<Evidence> {
public class Evidence implements Serializable, Comparable<Evidence> {
/**
* Creates a new Evidence object.

View File

@@ -17,6 +17,7 @@
*/
package org.owasp.dependencycheck.dependency;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.HashSet;
import java.util.Iterator;
@@ -36,7 +37,7 @@ import org.owasp.dependencycheck.utils.UrlStringUtils;
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
public class EvidenceCollection implements Iterable<Evidence> {
public class EvidenceCollection implements Serializable, Iterable<Evidence> {
/**
* The logger.

View File

@@ -17,11 +17,13 @@
*/
package org.owasp.dependencycheck.dependency;
import java.io.Serializable;
/**
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
public class Identifier implements Comparable<Identifier> {
public class Identifier implements Serializable, Comparable<Identifier> {
/**
* Constructs a new Identifier with the specified data.

View File

@@ -189,17 +189,23 @@ public class DependencyVersion implements Iterable, Comparable<DependencyVersion
if (version == null) {
return false;
}
boolean ret = true;
int max = (this.versionParts.size() < version.versionParts.size())
? this.versionParts.size() : version.versionParts.size();
if (max > 3) {
max = 3;
if (Math.abs(this.versionParts.size() - version.versionParts.size()) >= 3) {
return false;
}
final int max = (this.versionParts.size() < version.versionParts.size())
? this.versionParts.size() : version.versionParts.size();
boolean ret = true;
for (int i = 0; i < max; i++) {
if (this.versionParts.get(i) == null || !this.versionParts.get(i).equals(version.versionParts.get(i))) {
String thisVersion = this.versionParts.get(i);
String otherVersion = version.getVersionParts().get(i);
if (i >= 3) {
if (thisVersion.compareToIgnoreCase(otherVersion) >= 0) {
ret = false;
break;
}
} else if (!thisVersion.equals(otherVersion)) {
ret = false;
break;
}

View File

@@ -17,4 +17,18 @@
<gav regex="true">com\.thoughtworks\.xstream:xstream:.*</gav>
<cpe>cpe:/a:springsource:spring_framework</cpe>
</suppress>
<suppress>
<notes><![CDATA[
Suppresses false positives on velocity tools.
]]></notes>
<gav regex="true">org.apache.velocity:velocity-tools:.*</gav>
<cpe>cpe:/a:apache:struts</cpe>
</suppress>
<suppress>
<notes><![CDATA[
Sandbox is a php blog platform and should not be flagged as a CPE for java or .net dependencies.
]]></notes>
<filePath regex="true">.*\.(jar|dll|exe|ear|war|pom)</filePath>
<cpe>cpe:/a:sandbox:sandbox</cpe>
</suppress>
</suppressions>

View File

@@ -33,8 +33,8 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
</script>
<script type="text/javascript">
$(document).ready(function() {
$(".expandable").click(function (e) {
e = e || window.event;
$(".expandable").click(function (event) {
e = event || window.event;
var h = e.target || e.srcElement;
var content = "#content" + h.id.substr(6);
var header = "#" + h.id;
@@ -56,6 +56,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved.
$(header).addClass("expandablesubsection");
$(header).removeClass("collaspablesubsection");
}
return false;
});
});
@@ -533,24 +534,22 @@ arising out of or in connection with the use of this tool, the analysis performe
<li class="scaninfo hidden"><i>$enc.html($prop.key)</i>: $enc.html($prop.value)</li>
#end
</ul><br/>
Display:&nbsp;<a href="#" title="Click to toggle display" onclick="return toggleDisplay(this,'.notvulnerable', 'Showing Vulnerable Dependencies', 'Showing All Dependencies'); return false;">Showing Vulnerable Dependencies</a><br/><br/>
Display:&nbsp;<a href="#" title="Click to toggle display" onclick="return toggleDisplay(this, '.notvulnerable', 'Showing Vulnerable Dependencies', 'Showing All Dependencies'); return false;">Showing Vulnerable Dependencies</a><br/><br/>
#set($lnkcnt=0)
<table class="lined">
<tr style="text-align:left">
<th title="The name of the dependency">Dependency</th>
<th title="The number of related dependencies">#&nbsp;Related</th>
<th title="The Common Platform Enumeration">CPE</th>
<th title="The Maven GAV Coordinates">GAV</th>
<th title="The highest CVE Impact">CVE&nbsp;Impact</th>
<th title="The number of Common Vulnerability and Exposure (CVE) entries">CVE&nbsp;Count</th>
<th title="The confidence rating dependency-check has for the identified CPE">CPE&nbsp;Confidence</th>
<th title="The count of evidence used to identify the CPE">Evidence&nbsp;Count</th>
<th title="The highest CVE Severity">Highest Severity</th>
<th title="The number of Common Vulnerability and Exposure (CVE) entries">CVE Count</th>
<th title="The confidence rating dependency-check has for the identified CPE">CPE Confidence</th>
<th title="The count of evidence used to identify the CPE">Evidence Count</th>
</tr>
#foreach($dependency in $dependencies)
#set($lnkcnt=$lnkcnt+1)
<tr class="#if($dependency.getVulnerabilities().size()==0)notvulnerable#else vulnerable#end">
<td><a href="#l${lnkcnt}_$enc.html($enc.url($dependency.Sha1sum))">$enc.html($dependency.DisplayFileName)</a></td>
<td>$dependency.getRelatedDependencies().size()</td>
#set($mavenlink="")
#set($cpeIdCount=0)
#set($cpeIdConf="")
@@ -754,7 +753,7 @@ arising out of or in connection with the use of this tool, the analysis performe
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li>
</ul></p>
#else
<p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" onclick="toggleDisplay(this,'.vs$vsctr'); return false;">show all</a>)<ul>
<p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" onclick="return toggleDisplay(this,'.vs$vsctr', 'show all', 'show less');">show all</a>)<ul>
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li>
<li class="vs$vsctr">...</li>
#foreach($vs in $vuln.getVulnerableSoftware())
@@ -888,7 +887,7 @@ arising out of or in connection with the use of this tool, the analysis performe
git st<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li>
</ul></p>
#else
<p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" onclick="toggleDisplay(this,'.vs$vsctr'); return false;">show all</a>)<ul>
<p>Vulnerable Software &amp; Versions:&nbsp;(<a href="#" onclick="return toggleDisplay(this,'.vs$vsctr', 'show all', 'show less');">show all</a>)<ul>
<li class="vs$vsctr"><a target="_blank" href="https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=$enc.url($vuln.matchedCPE)">$enc.html($vuln.matchedCPE)</a> #if($vuln.hasMatchedAllPreviousCPE()) and all previous versions#end</li>
<li class="vs$vsctr">...</li>
#foreach($vs in $vuln.getVulnerableSoftware())

View File

@@ -70,10 +70,10 @@ public class EngineIntegrationTest extends BaseTest {
@Test
public void testEngine() throws Exception {
String testClasses = "target/test-classes";
// boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
// Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
Engine instance = new Engine();
// Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
instance.scan(testClasses);
assertTrue(instance.getDependencies().size() > 0);
instance.analyzeDependencies();

View File

@@ -81,12 +81,24 @@ public class CPEAnalyzerIntegrationTest extends AbstractDatabaseTestCase {
*/
@Test
public void testDetermineCPE_full() throws Exception {
callDetermineCPE_full("hazelcast-2.5.jar", null);
callDetermineCPE_full("spring-context-support-2.5.5.jar", "cpe:/a:vmware:springsource_spring_framework:2.5.5");
callDetermineCPE_full("spring-core-3.0.0.RELEASE.jar", "cpe:/a:vmware:springsource_spring_framework:3.0.0");
callDetermineCPE_full("org.mortbay.jetty.jar", "cpe:/a:mortbay_jetty:jetty:4.2");
callDetermineCPE_full("jaxb-xercesImpl-1.5.jar", null);
callDetermineCPE_full("ehcache-core-2.2.0.jar", null);
CPEAnalyzer instance = new CPEAnalyzer();
instance.open();
FileNameAnalyzer fnAnalyzer = new FileNameAnalyzer();
JarAnalyzer jarAnalyzer = new JarAnalyzer();
HintAnalyzer hAnalyzer = new HintAnalyzer();
FalsePositiveAnalyzer fp = new FalsePositiveAnalyzer();
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:vmware: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", 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);
} finally {
instance.close();
}
}
/**
@@ -94,25 +106,16 @@ public class CPEAnalyzerIntegrationTest extends AbstractDatabaseTestCase {
*
* @throws Exception is thrown when an exception occurs
*/
public void callDetermineCPE_full(String depName, String expResult) throws Exception {
public void callDetermineCPE_full(String depName, String expResult, CPEAnalyzer instance, FileNameAnalyzer fnAnalyzer, JarAnalyzer jarAnalyzer, HintAnalyzer hAnalyzer, FalsePositiveAnalyzer fp) throws Exception {
File file = new File(this.getClass().getClassLoader().getResource(depName).getPath());
Dependency dep = new Dependency(file);
FileNameAnalyzer fnAnalyzer = new FileNameAnalyzer();
fnAnalyzer.analyze(dep, null);
JarAnalyzer jarAnalyzer = new JarAnalyzer();
jarAnalyzer.analyze(dep, null);
HintAnalyzer hAnalyzer = new HintAnalyzer();
hAnalyzer.analyze(dep, null);
CPEAnalyzer instance = new CPEAnalyzer();
instance.open();
instance.analyze(dep, null);
instance.close();
FalsePositiveAnalyzer fp = new FalsePositiveAnalyzer();
fp.analyze(dep, null);
if (expResult != null) {

View File

@@ -134,14 +134,14 @@ public class DependencyVersionTest {
@Test
public void testMatchesAtLeastThreeLevels() {
DependencyVersion instance = new DependencyVersion("1.2.3.4");
DependencyVersion version = new DependencyVersion("1.2.3.5");
DependencyVersion instance = new DependencyVersion("2.3.16.3");
DependencyVersion version = new DependencyVersion("2.3.16.4");
//true tests
assertEquals(true, instance.matchesAtLeastThreeLevels(version));
version = new DependencyVersion("1.2");
version = new DependencyVersion("2.3");
assertEquals(true, instance.matchesAtLeastThreeLevels(version));
//false tests
version = new DependencyVersion("1.2.2.5");
version = new DependencyVersion("2.3.16.1");
assertEquals(false, instance.matchesAtLeastThreeLevels(version));
version = new DependencyVersion("2");
assertEquals(false, instance.matchesAtLeastThreeLevels(version));

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.2.4-SNAPSHOT</version>
<version>1.2.5-SNAPSHOT</version>
</parent>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-jenkins</artifactId>

View File

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

View File

@@ -0,0 +1,462 @@
/*
* This file is part of dependency-check-maven.
*
* 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) 2014 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.maven;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.MavenReport;
import org.apache.maven.reporting.MavenReportException;
/**
* <p>
* This is an abstract reporting mojo that enables report aggregation. Some of the code in the this class was copied
* from the CoberturaReportMojo (http://mojo.codehaus.org/cobertura-maven-plugin/, version 2.6). The authors of the
* CoberturaReportMojo were <a href="will.gwaltney@sas.com">Will Gwaltney</a> and
* <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>. There working example of how to do report aggregation was
* invaluable.</p>
* <p>
* An important point about using this abstract class is that it is intended for one to write some form of serialized
* data (via the {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#writeDataFile() }; note that the
* <code>writeDataFile()</code> function is called automatically after either {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#executeNonAggregateReport(org.apache.maven.doxia.sink.Sink,
* org.apache.maven.doxia.sink.SinkFactory, java.util.Locale)
* } or {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#executeAggregateReport(org.apache.maven.doxia.sink.Sink,
* org.apache.maven.doxia.sink.SinkFactory, java.util.Locale)
* } are called. When <code>executeAggregateReport()</code> is implemented, one can call {@link org.owasp.dependencycheck.maven.ReportAggregationMojo#getChildDataFiles()
* } to obtain a list of the data files to aggregate.</p>
*
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
public abstract class ReportAggregationMojo extends AbstractMojo implements MavenReport {
/**
* The Maven Project Object.
*/
@Component
private MavenProject project;
/**
* Logger field reference.
*/
private static final Logger LOGGER = Logger.getLogger(ReportAggregationMojo.class.getName());
/**
* List of Maven project of the current build
*/
@Parameter(readonly = true, required = true, property = "reactorProjects")
private List<MavenProject> reactorProjects;
/**
* Generate aggregate reports in multi-module projects.
*/
@Parameter(property = "aggregate", defaultValue = "false")
private boolean aggregate;
/**
* Sets whether or not the external report format should be used.
*/
@Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true)
private String dataFileName;
/**
* Specifies the destination directory for the generated Dependency-Check report. This generally maps to
* "target/site".
*/
@Parameter(property = "reportOutputDirectory", defaultValue = "${project.reporting.outputDirectory}", required = true)
private File reportOutputDirectory;
/**
* Sets the Reporting output directory.
*
* @param directory the output directory
*/
@Override
public void setReportOutputDirectory(File directory) {
reportOutputDirectory = directory;
}
/**
* Returns the output directory.
*
* @return the output directory
*/
@Override
public File getReportOutputDirectory() {
return reportOutputDirectory;
}
/**
* Returns the output directory for the given project.
*
* @param project the Maven project to get the output directory for
* @return the output directory for the given project
*/
public File getReportOutputDirectory(MavenProject project) {
final Object o = project.getContextValue(getOutputDirectoryContextKey());
if (o != null && o instanceof File) {
return (File) o;
}
return null;
}
/**
* Returns whether this is an external report. This method always returns true.
*
* @return <code>true</code>
*/
@Override
public final boolean isExternalReport() {
return true;
}
/**
* The collection of child projects.
*/
private final Map<MavenProject, Set<MavenProject>> projectChildren = new HashMap<MavenProject, Set<MavenProject>>();
/**
* Called before execute; allows for any setup that is needed. If this is overridden you must call
* </code>super.preExecute()</code>.
*
* @throws MojoExecutionException thrown if there is an issue executing the mojo
* @throws MojoFailureException thrown if there is an issue executing the mojo
*/
protected void preExecute() throws MojoExecutionException, MojoFailureException {
buildAggregateInfo();
}
/**
* Called when the mojo is being executed.
*
* @throws MojoExecutionException thrown if there is an issue executing the mojo
* @throws MojoFailureException thrown if there is an issue executing the mojo
*/
protected abstract void performExecute() throws MojoExecutionException, MojoFailureException;
/**
* Runs after the mojo has executed. This implementation will call <code>writeDataFile()</code>. As such, it is
* important that if this method is overriden that <code>super.postExecute()</code> is called.
*
* @throws MojoExecutionException thrown if there is an issue executing the mojo
* @throws MojoFailureException thrown if there is an issue executing the mojo
*/
protected void postExecute() throws MojoExecutionException, MojoFailureException {
final File written = writeDataFile();
if (written != null) {
project.setContextValue(getDataFileContextKey(), written.getAbsolutePath());
}
}
/**
* Returns the key used to store the path to the data file that is saved by <code>writeDataFile()</code>. This key
* is used in the <code>MavenProject.(set|get)ContextValue</code>.
*
* @return the key used to store the path to the data file
*/
protected String getDataFileContextKey() {
return "dependency-check-path-" + this.getDataFileName();
}
/**
* Returns the key used to store the path to the output directory. When generating the report in the
* <code>executeAggregateReport()</code> the output directory should be obtained by using this key.
*
* @return the key used to store the path to the output directory
*/
protected String getOutputDirectoryContextKey() {
return "dependency-output-dir-" + this.getDataFileName();
}
/**
* Is called by Maven to execute the mojo.
*
* @throws MojoExecutionException thrown if there is an issue executing the mojo
* @throws MojoFailureException thrown if there is an issue executing the mojo
*/
public final void execute() throws MojoExecutionException, MojoFailureException {
try {
preExecute();
performExecute();
} finally {
postExecute();
}
}
/**
* Runs prior to the site report generation.
*
* @throws MavenReportException if a maven report exception occurs
*/
protected void preGenerate() throws MavenReportException {
buildAggregateInfo();
project.setContextValue(getOutputDirectoryContextKey(), getReportOutputDirectory());
}
/**
* Executes after the site report has been generated.
*
* @throws MavenReportException if a maven report exception occurs
*/
protected void postGenerate() throws MavenReportException {
final File written = writeDataFile();
if (written != null) {
project.setContextValue(getDataFileContextKey(), written.getAbsolutePath());
}
}
/**
* Generates the non aggregate report.
*
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
*/
protected abstract void executeNonAggregateReport(Locale locale) throws MavenReportException;
/**
* Generates the aggregate Site Report.
*
* @param project the maven project used to generate the aggregate report
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
*/
protected abstract void executeAggregateReport(MavenProject project, Locale locale) throws MavenReportException;
/**
* Generates the Dependency-Check Site Report.
*
* @param sink the sink to write the report to
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
* @deprecated use {@link #generate(org.apache.maven.doxia.sink.Sink, java.util.Locale) instead.
*/
@Deprecated
public final void generate(@SuppressWarnings("deprecation") org.codehaus.doxia.sink.Sink sink, Locale locale) throws MavenReportException {
generate((Sink) sink, locale);
}
/**
* Generates the Dependency-Check Site Report.
*
* @param sink the sink to write the report to
* @param locale the locale to use when generating the report
* @throws MavenReportException if a maven report exception occurs
*/
public final void generate(Sink sink, Locale locale) throws MavenReportException {
try {
preGenerate();
if (canGenerateNonAggregateReport()) {
executeNonAggregateReport(locale);
}
if (canGenerateAggregateReport()) {
for (MavenProject proj : reactorProjects) {
if (!isMultiModule(proj)) {
continue;
}
executeAggregateReport(proj, locale);
}
}
} finally {
postGenerate();
}
}
/**
* Returns whether or not the mojo can generate a non-aggregate report for this project.
*
* @return <code>true</code> if a non-aggregate report can be generated, otherwise <code>false</code>
*/
protected abstract boolean canGenerateNonAggregateReport();
/**
* Returns whether or not we can generate any aggregate reports at this time.
*
* @return <code>true</code> if an aggregate report can be generated, otherwise <code>false</code>
*/
protected abstract boolean canGenerateAggregateReport();
/**
* Returns the name of the data file that contains the serialized data.
*
* @return the name of the data file that contains the serialized data
*/
protected String getDataFileName() {
return dataFileName;
}
/**
* Writes the data file to disk in the target directory.
*
* @return the File object referencing the data file that was written
*/
protected abstract File writeDataFile();
/**
* Collects the information needed for building aggregate reports.
*/
private void buildAggregateInfo() {
// build parent-child map
for (MavenProject proj : reactorProjects) {
Set<MavenProject> depList = projectChildren.get(proj.getParent());
if (depList == null) {
depList = new HashSet<MavenProject>();
projectChildren.put(proj.getParent(), depList);
}
depList.add(proj);
}
}
/**
* Returns a list containing all the recursive, non-pom children of the given project, never <code>null</code>.
*
* @return a list of child projects
*/
protected List<MavenProject> getAllChildren() {
return getAllChildren(project);
}
/**
* Returns a list containing all the recursive, non-pom children of the given project, never <code>null</code>.
*
* @param parentProject the parent project to collect the child project references
* @return a list of child projects
*/
protected List<MavenProject> getAllChildren(MavenProject parentProject) {
final Set<MavenProject> children = projectChildren.get(parentProject);
if (children == null) {
return Collections.emptyList();
}
final List<MavenProject> result = new ArrayList<MavenProject>();
for (MavenProject child : children) {
if (isMultiModule(child)) {
result.addAll(getAllChildren(child));
} else {
result.add(child);
}
}
return result;
}
/**
* Returns a list of data files that were produced by the direct children of the given MavenProject.
*
* @param project the Maven project to obtain the child data files from
* @return a list of the data files
*/
protected List<File> getAllChildDataFiles(MavenProject project) {
final List<MavenProject> children = getAllChildren(project);
return getDataFiles(children);
}
/**
* Returns any existing output files from the given list of projects.
*
* @param projects the list of projects to obtain the output files from
* @return a list of output files
*/
protected List<File> getDataFiles(List<MavenProject> projects) {
final List<File> files = new ArrayList<File>();
for (MavenProject proj : projects) {
final Object path = project.getContextValue(getDataFileContextKey());
if (path == null) {
final String msg = String.format("Unable to aggregate data for '%s' - aggregate data file was not generated",
proj.getName());
LOGGER.warning(msg);
} else {
final File outputFile = new File((String) path);
if (outputFile.exists()) {
files.add(outputFile);
} else {
if (!isMultiModule(project)) {
final String msg = String.format("Unable to aggregate data for '%s' - missing data file '%s'",
proj.getName(), outputFile.getPath());
LOGGER.warning(msg);
}
}
}
}
return files;
}
/**
* Test if the project has pom packaging
*
* @param mavenProject Project to test
* @return <code>true</code> if it has a pom packaging; otherwise <code>false</code>
*/
protected boolean isMultiModule(MavenProject mavenProject) {
return "pom".equals(mavenProject.getPackaging());
}
/**
* Test if the current project has pom packaging
*
* @return <code>true</code> if it has a pom packaging; otherwise <code>false</code>
*/
protected boolean isMultiModule() {
return isMultiModule(project);
}
/**
* Check whether the current project is the last project in a multi-module build. If the maven build is not a
* multi-module project then this will always return true.
*
* @return <code>true</code> if the current project is the last project in a multi-module build; otherwise
* <code>false</code>
*/
protected boolean isLastProject() {
return project.equals(reactorProjects.get(reactorProjects.size() - 1));
}
/**
* Returns whether or not the mojo is configured to perform report aggregation.
*
* @return <code>true</code> if report aggregation is enabled; otherwise <code>false</code>
*/
public boolean isAggregate() {
return aggregate;
}
/**
* Returns a reference to the current project. This method is used instead of auto-binding the project via component
* annotation in concrete implementations of this. If the child has a <code>@Component MavenProject project;</code>
* defined then the abstract class (i.e. this class) will not have access to the current project (just the way Maven
* works with the binding).
*
* @return returns a reference to the current project
*/
protected MavenProject getProject() {
return project;
}
}

View File

@@ -0,0 +1,456 @@
/*
* This file is part of dependency-check-maven.
*
* 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) 2014 Jeremy Long. All Rights Reserved.
*/
package org.owasp.dependencycheck.maven;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.maven.doxia.sink.Sink;
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.dependency.Dependency;
import org.owasp.dependencycheck.dependency.Evidence;
import org.owasp.dependencycheck.dependency.Identifier;
import org.owasp.dependencycheck.dependency.Reference;
import org.owasp.dependencycheck.dependency.Vulnerability;
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
import org.owasp.dependencycheck.reporting.ReportGenerator;
/**
* A utility class that encapsulates the report generation for dependency-check-maven.
*
* @author Jeremy Long <jeremy.long@owasp.org>
*/
final class ReportingUtil {
/**
* Logger field reference.
*/
private static final Logger LOGGER = Logger.getLogger(ReportingUtil.class.getName());
/**
* Empty private constructor for this utility class.
*/
private ReportingUtil() {
}
/**
* Generates the reports for a given dependency-check engine.
*
* @param engine a dependency-check engine
* @param outDirectory the directory to write the reports to
* @param projectName the name of the project that a report is being generated for
* @param format the format of the report to generate
*/
static void generateExternalReports(Engine engine, File outDirectory, String projectName, String format) {
DatabaseProperties prop = null;
CveDB cve = null;
try {
cve = new CveDB();
cve.open();
prop = cve.getDatabaseProperties();
} catch (DatabaseException ex) {
LOGGER.log(Level.FINE, "Unable to retrieve DB Properties", ex);
} finally {
if (cve != null) {
cve.close();
}
}
final ReportGenerator r = new ReportGenerator(projectName, engine.getDependencies(), engine.getAnalyzers(), prop);
try {
r.generateReports(outDirectory.getCanonicalPath(), format);
} catch (IOException ex) {
LOGGER.log(Level.SEVERE,
"Unexpected exception occurred during analysis; please see the verbose error log for more details.");
LOGGER.log(Level.FINE, null, ex);
} catch (Throwable ex) {
LOGGER.log(Level.SEVERE,
"Unexpected exception occurred during analysis; please see the verbose error log for more details.");
LOGGER.log(Level.FINE, null, ex);
}
}
/**
* Generates a dependency-check report using the Maven Site format.
*
* @param engine the engine used to scan the dependencies
* @param sink the sink to write the data to
* @param projectName the name of the project
*/
static void generateMavenSiteReport(final Engine engine, Sink sink, String projectName) {
final List<Dependency> dependencies = engine.getDependencies();
writeSiteReportHeader(sink, projectName);
writeSiteReportTOC(sink, dependencies);
int cnt = 0;
for (Dependency d : dependencies) {
writeSiteReportDependencyHeader(sink, d);
cnt = writeSiteReportDependencyEvidenceUsed(d, cnt, sink);
cnt = writeSiteReportDependencyRelatedDependencies(d, cnt, sink);
writeSiteReportDependencyIdentifiers(d, sink);
writeSiteReportDependencyVulnerabilities(d, sink, cnt);
}
sink.body_();
}
// <editor-fold defaultstate="collapsed" desc="various writeXXXXX methods to generate the Site Report">
/**
* Writes the vulnerabilities to the site report.
*
* @param d the dependency
* @param sink the sink to write the data to
* @param collapsibleHeaderCount the collapsible header count
*/
private static void writeSiteReportDependencyVulnerabilities(Dependency d, Sink sink, int collapsibleHeaderCount) {
int cnt = collapsibleHeaderCount;
if (d.getVulnerabilities() != null && !d.getVulnerabilities().isEmpty()) {
for (Vulnerability v : d.getVulnerabilities()) {
sink.paragraph();
sink.bold();
try {
sink.link("http://web.nvd.nist.gov/view/vuln/detail?vulnId=" + URLEncoder.encode(v.getName(), "US-ASCII"));
sink.text(v.getName());
sink.link_();
sink.bold_();
} catch (UnsupportedEncodingException ex) {
sink.text(v.getName());
sink.bold_();
sink.lineBreak();
sink.text("http://web.nvd.nist.gov/view/vuln/detail?vulnId=" + v.getName());
}
sink.paragraph_();
sink.paragraph();
sink.text("Severity: ");
if (v.getCvssScore() < 4.0) {
sink.text("Low");
} else {
if (v.getCvssScore() >= 7.0) {
sink.text("High");
} else {
sink.text("Medium");
}
}
sink.lineBreak();
sink.text("CVSS Score: " + v.getCvssScore());
if (v.getCwe() != null && !v.getCwe().isEmpty()) {
sink.lineBreak();
sink.text("CWE: ");
sink.text(v.getCwe());
}
sink.paragraph_();
sink.paragraph();
sink.text(v.getDescription());
if (v.getReferences() != null && !v.getReferences().isEmpty()) {
sink.list();
for (Reference ref : v.getReferences()) {
sink.listItem();
sink.text(ref.getSource());
sink.text(" - ");
sink.link(ref.getUrl());
sink.text(ref.getName());
sink.link_();
sink.listItem_();
}
sink.list_();
}
sink.paragraph_();
if (v.getVulnerableSoftware() != null && !v.getVulnerableSoftware().isEmpty()) {
sink.paragraph();
cnt += 1;
sink.rawText("Vulnerable Software <a href=\"javascript:toggleElement(this, 'vulnSoft" + cnt + "')\">[-]</a>");
sink.rawText("<div id=\"vulnSoft" + cnt + "\" style=\"display:block\">");
sink.list();
for (VulnerableSoftware vs : v.getVulnerableSoftware()) {
sink.listItem();
try {
sink.link("http://web.nvd.nist.gov/view/vuln/search-results?cpe=" + URLEncoder.encode(vs.getName(), "US-ASCII"));
sink.text(vs.getName());
sink.link_();
if (vs.hasPreviousVersion()) {
sink.text(" and all previous versions.");
}
} catch (UnsupportedEncodingException ex) {
sink.text(vs.getName());
if (vs.hasPreviousVersion()) {
sink.text(" and all previous versions.");
}
sink.text(" (http://web.nvd.nist.gov/view/vuln/search-results?cpe=" + vs.getName() + ")");
}
sink.listItem_();
}
sink.list_();
sink.rawText("</div>");
sink.paragraph_();
}
}
}
}
/**
* Writes the identifiers to the site report.
*
* @param d the dependency
* @param sink the sink to write the data to
*/
private static void writeSiteReportDependencyIdentifiers(Dependency d, Sink sink) {
if (d.getIdentifiers() != null && !d.getIdentifiers().isEmpty()) {
sink.sectionTitle4();
sink.text("Identifiers");
sink.sectionTitle4_();
sink.list();
for (Identifier i : d.getIdentifiers()) {
sink.listItem();
sink.text(i.getType());
sink.text(": ");
if (i.getUrl() != null && i.getUrl().length() > 0) {
sink.link(i.getUrl());
sink.text(i.getValue());
sink.link_();
} else {
sink.text(i.getValue());
}
if (i.getDescription() != null && i.getDescription().length() > 0) {
sink.lineBreak();
sink.text(i.getDescription());
}
sink.listItem_();
}
sink.list_();
}
}
/**
* Writes the related dependencies to the site report.
*
* @param d the dependency
* @param sink the sink to write the data to
* @param collapsibleHeaderCount the collapsible header count
* @return the collapsible header count
*/
private static int writeSiteReportDependencyRelatedDependencies(Dependency d, int collapsibleHeaderCount, Sink sink) {
int cnt = collapsibleHeaderCount;
if (d.getRelatedDependencies() != null && !d.getRelatedDependencies().isEmpty()) {
cnt += 1;
sink.sectionTitle4();
sink.rawText("Related Dependencies <a href=\"javascript:toggleElement(this, 'related" + cnt + "')\">[+]</a>");
sink.sectionTitle4_();
sink.rawText("<div id=\"related" + cnt + "\" style=\"display:none\">");
sink.list();
for (Dependency r : d.getRelatedDependencies()) {
sink.listItem();
sink.text(r.getFileName());
sink.list();
writeListItem(sink, "File Path: " + r.getFilePath());
writeListItem(sink, "SHA1: " + r.getSha1sum());
writeListItem(sink, "MD5: " + r.getMd5sum());
sink.list_();
sink.listItem_();
}
sink.list_();
sink.rawText("</div>");
}
return cnt;
}
/**
* Writes the evidence used to the site report.
*
* @param d the dependency
* @param sink the sink to write the data to
* @param collapsibleHeaderCount the collapsible header count
* @return the collapsible header count
*/
private static int writeSiteReportDependencyEvidenceUsed(Dependency d, int collapsibleHeaderCount, Sink sink) {
int cnt = collapsibleHeaderCount;
final Set<Evidence> evidence = d.getEvidenceForDisplay();
if (evidence != null && evidence.size() > 0) {
cnt += 1;
sink.sectionTitle4();
sink.rawText("Evidence Collected <a href=\"javascript:toggleElement(this, 'evidence" + cnt + "')\">[+]</a>");
sink.sectionTitle4_();
sink.rawText("<div id=\"evidence" + cnt + "\" style=\"display:none\">");
sink.table();
sink.tableRow();
writeTableHeaderCell(sink, "Source");
writeTableHeaderCell(sink, "Name");
writeTableHeaderCell(sink, "Value");
sink.tableRow_();
for (Evidence e : evidence) {
sink.tableRow();
writeTableCell(sink, e.getSource());
writeTableCell(sink, e.getName());
writeTableCell(sink, e.getValue());
sink.tableRow_();
}
sink.table_();
sink.rawText("</div>");
}
return cnt;
}
/**
* Writes the dependency header to the site report.
*
* @param d the dependency
* @param sink the sink to write the data to
*/
private static void writeSiteReportDependencyHeader(Sink sink, Dependency d) {
sink.sectionTitle2();
sink.anchor("sha1" + d.getSha1sum());
sink.text(d.getFileName());
sink.anchor_();
sink.sectionTitle2_();
if (d.getDescription() != null && d.getDescription().length() > 0) {
sink.paragraph();
sink.bold();
sink.text("Description: ");
sink.bold_();
sink.text(d.getDescription());
sink.paragraph_();
}
if (d.getLicense() != null && d.getLicense().length() > 0) {
sink.paragraph();
sink.bold();
sink.text("License: ");
sink.bold_();
if (d.getLicense().startsWith("http://") && !d.getLicense().contains(" ")) {
sink.link(d.getLicense());
sink.text(d.getLicense());
sink.link_();
} else {
sink.text(d.getLicense());
}
sink.paragraph_();
}
}
/**
* Adds a list item to the site report.
*
* @param sink the sink to write the data to
* @param text the text to write
*/
private static void writeListItem(Sink sink, String text) {
sink.listItem();
sink.text(text);
sink.listItem_();
}
/**
* Adds a table cell to the site report.
*
* @param sink the sink to write the data to
* @param text the text to write
*/
private static void writeTableCell(Sink sink, String text) {
sink.tableCell();
sink.text(text);
sink.tableCell_();
}
/**
* Adds a table header cell to the site report.
*
* @param sink the sink to write the data to
* @param text the text to write
*/
private static void writeTableHeaderCell(Sink sink, String text) {
sink.tableHeaderCell();
sink.text(text);
sink.tableHeaderCell_();
}
/**
* Writes the TOC for the site report.
*
* @param sink the sink to write the data to
* @param dependencies the dependencies that are being reported on
*/
private static void writeSiteReportTOC(Sink sink, final List<Dependency> dependencies) {
sink.list();
for (Dependency d : dependencies) {
sink.listItem();
sink.link("#sha1" + d.getSha1sum());
sink.text(d.getFileName());
sink.link_();
if (!d.getVulnerabilities().isEmpty()) {
sink.rawText(" <font style=\"color:red\">•</font>");
}
if (!d.getRelatedDependencies().isEmpty()) {
sink.list();
for (Dependency r : d.getRelatedDependencies()) {
writeListItem(sink, r.getFileName());
}
sink.list_();
}
sink.listItem_();
}
sink.list_();
}
/**
* Writes the site report header.
*
* @param sink the sink to write the data to
* @param projectName the name of the project
*/
private static void writeSiteReportHeader(Sink sink, String projectName) {
sink.head();
sink.title();
sink.text("Dependency-Check Report: " + projectName);
sink.title_();
sink.head_();
sink.body();
sink.rawText("<script type=\"text/javascript\">");
sink.rawText("function toggleElement(el, targetId) {");
sink.rawText("if (el.innerText == '[+]') {");
sink.rawText(" el.innerText = '[-]';");
sink.rawText(" document.getElementById(targetId).style.display='block';");
sink.rawText("} else {");
sink.rawText(" el.innerText = '[+]';");
sink.rawText(" document.getElementById(targetId).style.display='none';");
sink.rawText("}");
sink.rawText("}");
sink.rawText("</script>");
sink.section1();
sink.sectionTitle1();
sink.text("Project: " + projectName);
sink.sectionTitle1_();
sink.date();
final Date now = new Date();
sink.text(DateFormat.getDateTimeInstance().format(now));
sink.date_();
sink.section1_();
}
// </editor-fold>
}

View File

@@ -4,8 +4,8 @@ The following properties can be set on the dependency-check-maven plugin.
Property | Description | Default Value
---------------------|------------------------------------|------------------
aggregate | Sets whether report aggregation will be performed for multi-module site reports. This option only affects the report generation when configured within the reporting section. | false
autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true
externalReport | When using as a Site plugin this parameter sets whether or not the external report format should be used. | false
outputDirectory | The location to write the report(s). Note, this is not used if generating the report as part of a `mvn site` build | 'target'
failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11 which means since the CVSS scores are 0-10, by default the build will never fail. | 11
format | The report format to be generated (HTML, XML, VULN, ALL). This configuration option has no affect if using this within the Site plugin unless the externalReport is set to true. | HTML
@@ -31,7 +31,7 @@ jarAnalyzer | Sets whether Jar Analyzer will be used.
nexusAnalyzerEnabled | Sets whether Nexus Analyzer will be used. | true
nexusUrl | Defines the Nexus URL. | https://repository.sonatype.org/service/local/
nexusUsesProxy | Whether or not the defined proxy should be used when connecting to Nexus. | true
nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true
nuspecAnalyzerEnabled | Sets whether or not the .NET Nuget Nuspec Analyzer will be used. | true
assemblyAnalyzerEnabled | Sets whether or not the .NET Assembly Analyzer should be used. | true
pathToMono | The path to Mono for .NET assembly analysis on non-windows systems | &nbsp;
@@ -40,21 +40,27 @@ Advanced Configuration
The following properties can be configured in the plugin. However, they are less frequently changed. One exception
may be the cvedUrl properties, which can be used to host a mirror of the NVD within an enterprise environment.
Property | Description | Default Value
---------------------|-------------------------------------------------------------------------|------------------
cveUrl12Modified | URL for the modified CVE 1.2 | http://nvd.nist.gov/download/nvdcve-modified.xml
cveUrl20Modified | URL for the modified CVE 2.0 | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-modified.xml
cveUrl12Base | Base URL for each year's CVE 1.2, the %d will be replaced with the year | http://nvd.nist.gov/download/nvdcve-%d.xml
cveUrl20Base | Base URL for each year's CVE 2.0, the %d will be replaced with the year | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml
connectionTimeout | The URL Connection Timeout. | &nbsp;
dataDirectory | Data directory to hold SQL CVEs contents. This should generally not be changed. | &nbsp;
Property | Description | Default Value
---------------------|--------------------------------------------------------------------------|------------------
cveUrl12Modified | URL for the modified CVE 1.2. | http://nvd.nist.gov/download/nvdcve-modified.xml
cveUrl20Modified | URL for the modified CVE 2.0. | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-modified.xml
cveUrl12Base | Base URL for each year's CVE 1.2, the %d will be replaced with the year. | http://nvd.nist.gov/download/nvdcve-%d.xml
cveUrl20Base | Base URL for each year's CVE 2.0, the %d will be replaced with the year. | http://static.nvd.nist.gov/feeds/xml/cve/nvdcve-2.0-%d.xml
connectionTimeout | Sets the URL Connection Timeout used when downloading external data. | &nbsp;
dataDirectory | Sets the data directory to hold SQL CVEs contents. This should generally not be changed. | &nbsp;
databaseDriverName | The name of the database driver. Example: org.h2.Driver. | &nbsp;
databaseDriverPath | The path to the database driver JAR file; only used if the driver is not in the class path. | &nbsp;
connectionString | The connection string used to connect to the database. | &nbsp;
databaseUser | The username used when connecting to the database. | &nbsp;
databasePassword | The password used when connecting to the database. | &nbsp;
metaFileName | Sets the name of the file to use for storing the metadata about the project. | dependency-check.ser
Proxy Configuration
====================
Use [Maven's settings](https://maven.apache.org/settings.html#Proxies) to configure a proxy server.
Use [Maven's settings](https://maven.apache.org/settings.html#Proxies) to configure a proxy server. If multiple proxies
are configured in the Maven settings file you must tell dependency-check which proxy to use with the following property:
Property | Description | Default Value
---------------------|--------------------------------------------------------------------------------------|------------------
mavenSettingsProxyId | The id for the proxy, configured via settings.xml, that dependency-check should use. | &nbsp;

View File

@@ -17,7 +17,9 @@ Create the DependencyCheck-report.html in the target directory
```xml
<project>
...
<build>
...
<plugins>
...
<plugin>
@@ -41,11 +43,48 @@ Create the DependencyCheck-report.html in the target directory
```
$H$H$H Example 2:
Create an aggregated dependency-check report within the site
```xml
<project>
...
<reporting>
...
<plugins>
...
<plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>${project.version}</version>
<configuration>
<aggregate>true</aggregate>
</configuration>
<reportSets>
<reportSet>
<reports>
<report>check</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugin>
...
</plugins>
...
</reporting>
...
</project>
```
$H$H$H Example 3:
Create the DependencyCheck-report.html and fail the build for CVSS greater then 8
```xml
<project>
...
<build>
...
<plugins>
...
<plugin>
@@ -71,44 +110,14 @@ Create the DependencyCheck-report.html and fail the build for CVSS greater then
</project>
```
$H$H$H Example 3:
Create the dependency-check report within the site
```xml
<project>
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<configuration>
<reportPlugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>${project.version}</version>
<configuration>
<externalReport>false</externalReport>
</configuration>
</plugin>
</reportPlugins>
</configuration>
</plugin>
...
</plugins>
...
</build>
...
</project>
```
$H$H$H Example 4:
Create the DependencyCheck-report.html and skip artifacts no bundled in distribution (Provided and Runtime scope)
Create the DependencyCheck-report.html and skip artifacts not bundled in distribution (Provided and Runtime scope)
```xml
<project>
...
<build>
...
<plugins>
...
<plugin>
@@ -140,7 +149,9 @@ Create the DependencyCheck-report.html and use internal mirroring of CVE content
```xml
<project>
...
<build>
...
<plugins>
...
<plugin>

View File

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

View File

@@ -63,8 +63,22 @@ public final class Checksum {
try {
fis = new FileInputStream(file);
FileChannel ch = fis.getChannel();
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
digest.update(byteBuffer);
long remainingToRead = file.length();
long start = 0;
while (remainingToRead > 0) {
long amountToRead;
if (remainingToRead > Integer.MAX_VALUE) {
remainingToRead -= Integer.MAX_VALUE;
amountToRead = Integer.MAX_VALUE;
} else {
amountToRead = remainingToRead;
remainingToRead = 0;
}
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, start, amountToRead);
digest.update(byteBuffer);
start += amountToRead;
}
// BufferedInputStream bis = new BufferedInputStream(fis);
// DigestInputStream dis = new DigestInputStream(bis, digest);
// //yes, we are reading in a buffer for performance reasons - 1 byte at a time is SLOW

View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This file is part of Dependency-Check.
@@ -20,7 +19,7 @@ Copyright (c) 2012 - Jeremy Long
<groupId>org.owasp</groupId>
<artifactId>dependency-check-parent</artifactId>
<version>1.2.4-SNAPSHOT</version>
<version>1.2.5-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>

View File

@@ -14,8 +14,8 @@ libraries in our applications that contain well known published vulnerabilities
More information about dependency-check can be found here:
* (How does dependency-check work)[internals.html]
* (How to read the report)[thereport.html]
* [How does dependency-check work](./internals.html)
* [How to read the report](./thereport.html)
**IMPORTANT NOTE**: Dependency-check automatically updates itself using the NVD Data Feeds hosted by
NIST. **The initial download of the data may take fifteen minutes

View File

@@ -5,7 +5,9 @@ is called Evidence; there are three types of evidence collected: vendor, product
JarAnalyzer will collect information from the Manifest, pom.xml, and the package names within the JAR files scanned and
it has heuristics to place the information from the various sources into one or more buckets of evidence.
Within the NVD CVE Data (schema can be found [here](http://nvd.nist.gov/schema/nvd-cve-feed_2.0.xsd)) each CVE Entry has a list of vulnerable software:
Within the NVD CVE Data (schema can be found [here](http://nvd.nist.gov/schema/nvd-cve-feed_2.0.xsd)) each CVE Entry has
a list of vulnerable software:
```xml
<entry id="CVE-2012-5055">
...
@@ -26,7 +28,7 @@ level that is equal to the lowest level confidence level of evidence used during
evidence was used in determining the CPE then the CPE would have a highest confidence level.
Because of the way dependency-check works both false positives and false negatives may exist. Please read
(How to read the report)[thereport.html] to get a better understanding of sorting through the false positives and false
[How to read the report](thereport.html) to get a better understanding of sorting through the false positives and false
negatives.
Dependency-check does not currently use file hashes for identification. If the dependency was built from source the hash

View File

@@ -3,8 +3,8 @@ How To Read The Report
There is a lot of information contained in the HTML version of the report. When analyzing the results, the first thing one should do is determine if the CPE looks
appropriate. Due to the way dependency-check works (see above) the report may contain false positives; these false positives are primarily on the CPE values. If the CPE value
is wrong, this is usually obvious and one should use the suppression feature in the report to generate a suppression XML file that can be used on future scans. In addition
to just looking at the CPE values in comparison to the name of the dependency - one may also consider the confidence of the CPE (as discussed in (How does dependency-check
work)[internals.html]). See the (Suppression False Positives)[suppression.html] page for more information on how to generate and use the suppression file.
to just looking at the CPE values in comparison to the name of the dependency - one may also consider the confidence of the CPE (as discussed in [How does dependency-check
work](./internals.html)). See the [Suppressing False Positives](./suppression.html) page for more information on how to generate and use the suppression file.
Once you have weeded out any obvious false positives one can then look at the remaining entries and determine if any of the identified CVE entries are actually
exploitable in your environment. Determining if a CVE is exploitable in your environment can be tricky - for this I do not currently have any tips other then