mirror of
https://github.com/ysoftdevs/odc-analyzer.git
synced 2026-01-15 00:03:59 +01:00
Added false positive heuristics.
This commit is contained in:
@@ -235,3 +235,9 @@ h3.library-identification{
|
||||
.vulnerability-expandable .more{
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.warning-expandable:before{
|
||||
padding-left: 1em;
|
||||
font-family: 'Glyphicons Halflings';
|
||||
content: "\e209";
|
||||
}
|
||||
@@ -121,7 +121,10 @@ object Confidence extends Enumeration {
|
||||
|
||||
final case class Reference(source: String, url: String, name: String)
|
||||
|
||||
final case class VulnerableSoftware(allPreviousVersion: Boolean, name: String)
|
||||
final case class VulnerableSoftware(allPreviousVersion: Boolean, name: String){
|
||||
def containsVersion = name.count(_==':') >= 4
|
||||
def isVersionless = !containsVersion
|
||||
}
|
||||
|
||||
final case class CvssRating(score: Option[Double], authenticationr: Option[String], availabilityImpact: Option[String], accessVector: Option[String], integrityImpact: Option[String], accessComplexity: Option[String], confidentialImpact: Option[String])
|
||||
|
||||
@@ -140,9 +143,24 @@ object CWE{
|
||||
def forIdentifierWithDescription(name: String) = cwePool(new CWE(name))
|
||||
}
|
||||
|
||||
final class RichBoolean(val value: Boolean) extends AnyVal{
|
||||
@inline def ==> (right: => Boolean): Boolean = !value || right
|
||||
}
|
||||
object RichBoolean{
|
||||
@inline implicit def toRichBoolean(value: Boolean) = new RichBoolean(value)
|
||||
}
|
||||
|
||||
final case class Vulnerability(name: String, cweOption: Option[CWE], cvss: CvssRating, description: String, vulnerableSoftware: Seq[VulnerableSoftware], references: Seq[Reference]){
|
||||
import RichBoolean.toRichBoolean
|
||||
def cvssScore = cvss.score
|
||||
def ysvssScore(affectedDeps: Set[GroupedDependency]) = cvssScore.map(_ * affectedDeps.flatMap(_.projects).toSet.size)
|
||||
def likelyMatchesOnlyWithoutVersion(dependencyIdentifiers: Set[Identifier]) = dependencyIdentifiers.forall { id =>
|
||||
// Rather a quick hack. Maybe it would be better to do this check in ODC.
|
||||
val versionlessCpeIdentifierOption = id.toCpeIdentifierOption.map(_.split(':').take(4).mkString(":"))
|
||||
versionlessCpeIdentifierOption.fold(true){ versionlessCpeIdentifier =>
|
||||
vulnerableSoftware.forall(vs => vs.name.startsWith(versionlessCpeIdentifier) ==> vs.isVersionless)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final case class Identifier(name: String, confidence: Confidence.Confidence, url: String, identifierType: String) {
|
||||
|
||||
@@ -68,7 +68,11 @@
|
||||
<ul id="@depPrefix-vulnerabilities-details" class="collapse in vulnerabilities-details">
|
||||
@for(vuln <- dep.vulnerabilities.toSeq.sortBy(_.cvssScore.map(-_)); vulnPrefix = s"$depPrefix-vulnerabilities-details-${vuln.name}"){
|
||||
<li>
|
||||
<h5 data-toggle="collapse" class="expandable collapsed" data-target="#@vulnPrefix-details">@vuln.name <a href="@routes.Statistics.vulnerability(vuln.name, selectorOption)" target="_blank" onclick="event.stopPropagation();"><span class="glyphicon glyphicon-new-window"></span></a></h5>
|
||||
<h5 data-toggle="collapse" class="expandable collapsed" data-target="#@vulnPrefix-details">
|
||||
@vuln.name
|
||||
<a href="@routes.Statistics.vulnerability(vuln.name, selectorOption)" target="_blank" onclick="event.stopPropagation();"><span class="glyphicon glyphicon-new-window"></span></a>
|
||||
@if(vuln.likelyMatchesOnlyWithoutVersion(dep.identifiers)){<span class="warning-expandable" title="Heuristics suspect false positive. Double check <b>what version</b> does this vulnerability apply to, please. It seems that the vulnerability database does not provide enough information to check it automatically." onmouseover="$(this).tooltip({placement: 'right', html:true}).tooltip('show');"></span>}
|
||||
</h5>
|
||||
<div id="@vulnPrefix-details" class="collapse vulnerability-expandable">
|
||||
@vulnerability("h6", depPrefix+"-"+vuln.name, vuln)
|
||||
<p><a class="btn btn-primary more" target="_blank" href="@routes.Statistics.vulnerability(vuln.name, selectorOption)">Full details about this vulnerability</a></p>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
@section("vuln-sw", "Vulnerable software"){
|
||||
<ul id="@idPrefix-details">
|
||||
@for(sw <- vuln.vulnerableSoftware){
|
||||
<li>@sw.name@if(sw.allPreviousVersion){ and all previous versions}</li>
|
||||
<li>@sw.name@if(sw.allPreviousVersion){ and all previous versions}@if(sw.isVersionless){<span class="warning-expandable" title="This identifier does not contain version. It will match <b>any</b> version, which might cause false positives." onmouseover="$(this).tooltip({placement: 'right', html:true}).tooltip('show');"></span>}</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
39
test/VulnerabilitySpec.scala
Normal file
39
test/VulnerabilitySpec.scala
Normal file
@@ -0,0 +1,39 @@
|
||||
import com.ysoft.odc._
|
||||
import org.specs2.mutable.Specification
|
||||
|
||||
//noinspection ScalaUnnecessaryParentheses
|
||||
class VulnerabilitySpec extends Specification {
|
||||
|
||||
val vuln = Vulnerability("some-vuln", None, CvssRating(None, None, None, None, None, None, None), "descr", Seq(
|
||||
VulnerableSoftware(allPreviousVersion = false, "cpe:/a:ftp:ftp"),
|
||||
VulnerableSoftware(allPreviousVersion = false, "cpe:/a:ssh:ssh:1.0"),
|
||||
VulnerableSoftware(allPreviousVersion = false, "cpe:/a:asd:asd:1.0")
|
||||
), Seq())
|
||||
|
||||
|
||||
def id(name: String) = Identifier(name = name, confidence = Confidence.Highest, url = "", identifierType = "cpe")
|
||||
|
||||
"matchesOnlyWithoutVersion should" >> {
|
||||
"return true" >> {
|
||||
"when it contains just one match and it is without version" >> {
|
||||
vuln.likelyMatchesOnlyWithoutVersion(Set(id("cpe:/a:ftp:ftp:1.0"))) should beTrue
|
||||
}
|
||||
}
|
||||
"return false" >> {
|
||||
"when it contains just one match and it contains version" >> {
|
||||
vuln.likelyMatchesOnlyWithoutVersion(Set(id("cpe:/a:ssh:ssh:1.0"))) should beFalse
|
||||
}
|
||||
"when it contains just one match by older version" >> {
|
||||
vuln.likelyMatchesOnlyWithoutVersion(Set(id("cpe:/a:ssh:ssh:0.9"))) should beFalse
|
||||
}
|
||||
"when it matches without version, but it also matches with version" >> {
|
||||
vuln.likelyMatchesOnlyWithoutVersion(Set(id("cpe:/a:ftp:ftp:1.0"), id("cpe:/a:ssh:ssh:1.0"))) should beFalse
|
||||
}
|
||||
"when it matches without version, but it also matches with version and everything matches" >> {
|
||||
vuln.likelyMatchesOnlyWithoutVersion(Set(id("cpe:/a:ftp:ftp:1.0"), id("cpe:/a:ssh:ssh:1.0"), id("cpe:/a:asd:asd:1.0"))) should beFalse
|
||||
}
|
||||
}
|
||||
// TODO: Add tests for version matching; They would not pass now, though.
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user