Adapt for new version of ODC database

This commit is contained in:
Šesták Vít
2020-01-31 02:07:08 +01:00
parent 52c3228ac3
commit c537a5c5c5
11 changed files with 108 additions and 107 deletions

View File

@@ -177,7 +177,7 @@ object Confidence extends Enumeration {
final case class Reference(source: String, url: String, name: String) 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: Boolean = name.count(_==':') >= 4 def containsVersion: Boolean = name.count(_==':') >= 4
def isCpe: Boolean = name.startsWith("cpe:") def isCpe: Boolean = name.startsWith("cpe:")
def isVersionless: Boolean = isCpe && !containsVersion def isVersionless: Boolean = isCpe && !containsVersion
@@ -207,16 +207,16 @@ object RichBoolean{
@inline implicit def toRichBoolean(value: Boolean) = new RichBoolean(value) @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]){ final case class Vulnerability(name: String, /*cweOption: Option[CWE],*/ cvss: CvssRating, description: String, /*vulnerableSoftware: Seq[VulnerableSoftware],*/ references: Seq[Reference]){
import RichBoolean.toRichBoolean import RichBoolean.toRichBoolean
def cvssScore = cvss.score def cvssScore = cvss.score
def likelyMatchesOnlyWithoutVersion(dependencyIdentifiers: Set[Identifier]) = dependencyIdentifiers.forall { id => // def likelyMatchesOnlyWithoutVersion(dependencyIdentifiers: Set[Identifier]) = dependencyIdentifiers.forall { id =>
// Rather a quick hack. Maybe it would be better to do this check in ODC. // // Rather a quick hack. Maybe it would be better to do this check in ODC.
val versionlessCpeIdentifierOption = id.toCpeIdentifierOption.map(_.split(':').take(4).mkString(":")) // val versionlessCpeIdentifierOption = id.toCpeIdentifierOption.map(_.split(':').take(4).mkString(":"))
versionlessCpeIdentifierOption.fold(true){ versionlessCpeIdentifier => // versionlessCpeIdentifierOption.fold(true){ versionlessCpeIdentifier =>
vulnerableSoftware.forall(vs => vs.name.startsWith(versionlessCpeIdentifier) ==> vs.isVersionless) // vulnerableSoftware.forall(vs => vs.name.startsWith(versionlessCpeIdentifier) ==> vs.isVersionless)
} // }
} // }
} }
final case class Identifier(name: String, confidence: Confidence.Confidence, url: String, identifierType: String) { final case class Identifier(name: String, confidence: Confidence.Confidence, url: String, identifierType: String) {
@@ -284,8 +284,8 @@ object OdcParser {
sys.error(s"Unexpected element for vulnerableSoftware: ${node.label}") sys.error(s"Unexpected element for vulnerableSoftware: ${node.label}")
} }
vulnerableSoftwarePool(VulnerableSoftware( vulnerableSoftwarePool(VulnerableSoftware(
name = node.text, name = node.text
allPreviousVersion = node.boolAttribute("allPreviousVersion").getOrElse(false) //allPreviousVersion = node.boolAttribute("allPreviousVersion").getOrElse(false)
)) ))
} }
@@ -356,8 +356,8 @@ object OdcParser {
//cweOption = (node \ "cwe").headOption.map(_.text).map(CWE.forIdentifierWithDescription), //cweOption = (node \ "cwe").headOption.map(_.text).map(CWE.forIdentifierWithDescription),
description = (node \ "description").text, description = (node \ "description").text,
cvss = cvssScore, cvss = cvssScore,
references = (node \ "references").flatMap(filterWhitespace).map(parseReference(_)), references = (node \ "references").flatMap(filterWhitespace).map(parseReference(_))
vulnerableSoftware = (node \ "vulnerableSoftware").flatMap(filterWhitespace).map(parseVulnerableSoftware) //vulnerableSoftware = (node \ "vulnerableSoftware").flatMap(filterWhitespace).map(parseVulnerableSoftware)
)) ))
} }

View File

@@ -129,35 +129,35 @@ class Statistics @Inject()(
private def select(allResults: (Map[String, (Build, ArtifactItem, ArtifactFile)], Map[String, Throwable]), selectorOption: Option[String]): Option[ResultWithSelection] = select(allResults._1, allResults._2, selectorOption) private def select(allResults: (Map[String, (Build, ArtifactItem, ArtifactFile)], Map[String, Throwable]), selectorOption: Option[String]): Option[ResultWithSelection] = select(allResults._1, allResults._2, selectorOption)
private def select(successfulResults: Map[String, (Build, ArtifactItem, ArtifactFile)], failedResults: Map[String, Throwable], selectorOption: Option[String]): Option[ResultWithSelection] = dependencyCheckReportsParser.parseReports(successfulResults, failedResults).selection(selectorOption) private def select(successfulResults: Map[String, (Build, ArtifactItem, ArtifactFile)], failedResults: Map[String, Throwable], selectorOption: Option[String]): Option[ResultWithSelection] = dependencyCheckReportsParser.parseReports(successfulResults, failedResults).selection(selectorOption)
def searchVulnerableSoftware(versionlessCpes: Seq[String], versionOption: Option[String]) = ReadAction.async{ implicit req => // def searchVulnerableSoftware(versionlessCpes: Seq[String], versionOption: Option[String]) = ReadAction.async{ implicit req =>
if(versionlessCpes.isEmpty){ // if(versionlessCpes.isEmpty){
Future.successful(notFound()) // Future.successful(notFound())
}else{ // }else{
val now = DateTime.now() // val now = DateTime.now()
val oldDataThreshold = 2.days // val oldDataThreshold = 2.days
val lastDbUpdateFuture = odcDbService.loadLastDbUpdate() // val lastDbUpdateFuture = odcDbService.loadLastDbUpdate()
val isOldFuture = lastDbUpdateFuture.map{ lastUpdate => now - oldDataThreshold > lastUpdate} // val isOldFuture = lastDbUpdateFuture.map{ lastUpdate => now - oldDataThreshold > lastUpdate}
versionOption match { // versionOption match {
case Some(version) => // case Some(version) =>
for { // for {
res1 <- Future.traverse(versionlessCpes) { versionlessCpe => odcDbService.findRelevantCpes(versionlessCpe, version) } // res1 <- Future.traverse(versionlessCpes) { versionlessCpe => odcDbService.findRelevantCpes(versionlessCpe, version) }
vulnIds = res1.flatten.map(_.vulnerabilityId).toSet // vulnIds = res1.flatten.map(_.vulnerabilityId).toSet
vulns <- Future.traverse(vulnIds)(id => odcDbService.getVulnerabilityDetails(id).map(_.get)) // vulns <- Future.traverse(vulnIds)(id => odcDbService.getVulnerabilityDetails(id).map(_.get))
isOld <- isOldFuture // isOld <- isOldFuture
} yield Ok(views.html.statistics.vulnerabilitiesForLibrary( // } yield Ok(views.html.statistics.vulnerabilitiesForLibrary(
vulnsAndVersionOption = Some((vulns, version)), // vulnsAndVersionOption = Some((vulns, version)),
cpes = versionlessCpes, // cpes = versionlessCpes,
isDbOld = isOld // isDbOld = isOld
)) // ))
case None => // case None =>
for(isOld <- isOldFuture) yield Ok(views.html.statistics.vulnerabilitiesForLibrary( // for(isOld <- isOldFuture) yield Ok(views.html.statistics.vulnerabilitiesForLibrary(
vulnsAndVersionOption = None, // vulnsAndVersionOption = None,
cpes = versionlessCpes, // cpes = versionlessCpes,
isDbOld = isOld // isDbOld = isOld
)) // ))
} // }
} // }
} // }
def basic(selectorOption: Option[String]) = ReadAction.async{ implicit req => def basic(selectorOption: Option[String]) = ReadAction.async{ implicit req =>
val (lastRefreshTime, resultsFuture) = projectReportsProvider.resultsForVersions(versions) val (lastRefreshTime, resultsFuture) = projectReportsProvider.resultsForVersions(versions)

View File

@@ -47,11 +47,12 @@ package object controllers {
def vulnerableSoftwareSearches(groupedDependency: GroupedDependency): Seq[(Call, String)] = { def vulnerableSoftwareSearches(groupedDependency: GroupedDependency): Seq[(Call, String)] = {
val legacySearchOption = groupedDependency.cpeIdentifiers match { val legacySearchOption = groupedDependency.cpeIdentifiers match {
case Seq() => None case Seq() => None
case cpeIds => Some( case cpeIds => None
routes.Statistics.searchVulnerableSoftware( // Some(
cpeIds.map(_.name.split(':').take(4).mkString(":")).toSeq, None // routes.Statistics.searchVulnerableSoftware(
) -> "Search by CPE (legacy option)" // cpeIds.map(_.name.split(':').take(4).mkString(":")).toSeq, None
) // ) -> "Search by CPE (legacy option)"
// )
} }
val mavenSearches = groupedDependency.mavenIdentifiers.map(_.name).toSeq.sorted.map{mavenIdentifier => val mavenSearches = groupedDependency.mavenIdentifiers.map(_.name).toSeq.sorted.map{mavenIdentifier =>
val Array(groupId, artifactId, version) = mavenIdentifier.split(":", 3) val Array(groupId, artifactId, version) = mavenIdentifier.split(":", 3)

View File

@@ -9,12 +9,12 @@ import slick.jdbc.JdbcType
import scala.reflect.ClassTag import scala.reflect.ClassTag
// TODO: consider renaming to CpeEntryVulnerability or something like that // TODO: consider renaming to CpeEntryVulnerability or something like that
final case class SoftwareVulnerability (vulnerabilityId: Int, cpeEntryId: Int, includesAllPreviousVersionsRaw: Option[String]){ final case class SoftwareVulnerability (vulnerabilityId: Int, cpeEntryId: Int/*, includesAllPreviousVersionsRaw: Option[String]*/){
def includesAllPreviousVersions: Boolean = includesAllPreviousVersionsRaw match { // def includesAllPreviousVersions: Boolean = includesAllPreviousVersionsRaw match {
case Some("1") => true // case Some("1") => true
case None => false // case None => false
case _ => sys.error("Unexpected value from ODC database") // case _ => sys.error("Unexpected value from ODC database")
} // }
} }
/*private class OdcBooleanType(implicit t: JdbcType[Option[String]]) extends MappedJdbcType[Boolean, Option[String]] { /*private class OdcBooleanType(implicit t: JdbcType[Option[String]]) extends MappedJdbcType[Boolean, Option[String]] {
@@ -35,6 +35,6 @@ class SoftwareVulnerabilities(tag: Tag) extends Table[SoftwareVulnerability](tag
def cpeEntryId = column[Int]("cpeentryid") def cpeEntryId = column[Int]("cpeentryid")
//private val bt = new OdcBooleanType()(jdbcTypeFor(implicitly[BaseColumnType[String]].optionType).asInstanceOf[JdbcType[Option[String]]]) //private val bt = new OdcBooleanType()(jdbcTypeFor(implicitly[BaseColumnType[String]].optionType).asInstanceOf[JdbcType[Option[String]]])
//MappedJdbcType.base[Boolean, Option[String]](???, ???)(implicitly[ClassTag[Boolean]], ) //MappedJdbcType.base[Boolean, Option[String]](???, ???)(implicitly[ClassTag[Boolean]], )
def includesAllPreviousVersionsRaw = column[String]("previousversion").? //def includesAllPreviousVersionsRaw = column[String]("previousversion").?
def * = (vulnerabilityId, cpeEntryId, includesAllPreviousVersionsRaw) <> (SoftwareVulnerability.tupled, SoftwareVulnerability.unapply) def * = (vulnerabilityId, cpeEntryId/*, includesAllPreviousVersionsRaw*/) <> (SoftwareVulnerability.tupled, SoftwareVulnerability.unapply)
} }

View File

@@ -4,23 +4,23 @@ import com.ysoft.odc.{CvssRating, CWE}
import models.odc.profile.api._ import models.odc.profile.api._
import slick.lifted.Tag import slick.lifted.Tag
case class Vulnerability (cve: String, description: String, cweOption: Option[CWE], cvss: CvssRating) case class Vulnerability (cve: String, description: String, /*cweOption: Option[CWE],*/ cvss: CvssRating)
class Vulnerabilities(tag: Tag) extends Table[(Int, Vulnerability)](tag, "vulnerability") { class Vulnerabilities(tag: Tag) extends Table[(Int, Vulnerability)](tag, "vulnerability") {
def id = column[Int]("id") def id = column[Int]("id")
def cve = column[String]("cve") def cve = column[String]("cve")
def description = column[String]("description") def description = column[String]("description")
def cweOption = column[String]("cwe").? //def cweOption = column[String]("cwe").?
def cvssScore = column[Double]("cvssscore").? def cvssScore = column[Double]("cvssv2score").?
def authentication = column[String]("cvssauthentication").? def authentication = column[String]("cvssv2authentication").?
def availabilityImpact = column[String]("cvssavailabilityimpact").? def availabilityImpact = column[String]("cvssv2availabilityimpact").?
def accessVector = column[String]("cvssaccessvector").? def accessVector = column[String]("cvssv2accessvector").?
def integrityImpact = column[String]("cvssintegrityimpact").? def integrityImpact = column[String]("cvssv2integrityimpact").?
def cvssAccessComplexity = column[String]("cvssaccesscomplexity").? def cvssAccessComplexity = column[String]("cvssv2accesscomplexity").?
def cvssConfidentialityImpact = column[String]("cvssconfidentialityimpact").? def cvssConfidentialityImpact = column[String]("cvssv2confidentialityimpact").?
def cvssRating = (cvssScore, authentication, availabilityImpact, accessVector, integrityImpact, cvssAccessComplexity, cvssConfidentialityImpact) <> (CvssRating.tupled, CvssRating.unapply) def cvssRating = (cvssScore, authentication, availabilityImpact, accessVector, integrityImpact, cvssAccessComplexity, cvssConfidentialityImpact) <> (CvssRating.tupled, CvssRating.unapply)
def cweOptionMapped = cweOption <> ((_: Option[String]).map(CWE.forIdentifierWithDescription), (_: Option[CWE]).map(CWE.unapply)) //def cweOptionMapped = cweOption <> ((_: Option[String]).map(CWE.forIdentifierWithDescription), (_: Option[CWE]).map(CWE.unapply))
def base = (cve, description, cweOptionMapped, cvssRating) <> (Vulnerability.tupled, Vulnerability.unapply) def base = (cve, description, cvssRating) <> (Vulnerability.tupled, Vulnerability.unapply)
def * = (id, base) def * = (id, base)
} }

View File

@@ -23,13 +23,13 @@ class OdcDbService @Inject()(@NamedDatabase("odc") protected val dbConfigProvide
import dbConfig.driver.api._ import dbConfig.driver.api._
private def getVulnerableSoftware(id: Int): Future[Seq[com.ysoft.odc.VulnerableSoftware]] = { // private def getVulnerableSoftware(id: Int): Future[Seq[com.ysoft.odc.VulnerableSoftware]] = {
db.run(softwareVulnerabilities.joinLeft(cpeEntries).on((sv, ce) => sv.cpeEntryId === ce.id).filter{case (sv, ceo) => sv.vulnerabilityId === id}.result).map{rawRefs => // db.run(softwareVulnerabilities.joinLeft(cpeEntries).on((sv, ce) => sv.cpeEntryId === ce.id).filter{case (sv, ceo) => sv.vulnerabilityId === id}.result).map{rawRefs =>
rawRefs.map{ // rawRefs.map{
case (softVuln, Some((_, cpeEntry))) => com.ysoft.odc.VulnerableSoftware(allPreviousVersion = softVuln.includesAllPreviousVersions, name=cpeEntry.cpe) // case (softVuln, Some((_, cpeEntry))) => com.ysoft.odc.VulnerableSoftware(/*allPreviousVersion = softVuln.includesAllPreviousVersions, */name=cpeEntry.cpe)
} // }
} // }
} // }
private def getReferences(id: Int): Future[Seq[com.ysoft.odc.Reference]] = db.run(references.filter(_.cveId === id).map(_.base).result) private def getReferences(id: Int): Future[Seq[com.ysoft.odc.Reference]] = db.run(references.filter(_.cveId === id).map(_.base).result)
@@ -43,7 +43,7 @@ class OdcDbService @Inject()(@NamedDatabase("odc") protected val dbConfigProvide
db.run(vulnerabilities.filter(cond).result).map(_.headOption) flatMap { bareVulnOption => db.run(vulnerabilities.filter(cond).result).map(_.headOption) flatMap { bareVulnOption =>
bareVulnOption.fold[Future[Option[com.ysoft.odc.Vulnerability]]](Future.successful(None)) { case (id, bareVuln) => bareVulnOption.fold[Future[Option[com.ysoft.odc.Vulnerability]]](Future.successful(None)) { case (id, bareVuln) =>
for { for {
vulnerableSoftware <- getVulnerableSoftware(id) // vulnerableSoftware <- getVulnerableSoftware(id)
references <- getReferences(id) references <- getReferences(id)
} yield Some( } yield Some(
com.ysoft.odc.Vulnerability( com.ysoft.odc.Vulnerability(
@@ -51,7 +51,7 @@ class OdcDbService @Inject()(@NamedDatabase("odc") protected val dbConfigProvide
//cweOption = bareVuln.cweOption, //cweOption = bareVuln.cweOption,
cvss = bareVuln.cvss, cvss = bareVuln.cvss,
description = bareVuln.description, description = bareVuln.description,
vulnerableSoftware = vulnerableSoftware, //vulnerableSoftware = vulnerableSoftware,
references = references references = references
) )
) )
@@ -69,34 +69,34 @@ class OdcDbService @Inject()(@NamedDatabase("odc") protected val dbConfigProvide
DependencyVersionUtil.parseVersion(version) DependencyVersionUtil.parseVersion(version)
} }
def findRelevantCpes(versionlessCpe: String, version: String) = { // def findRelevantCpes(versionlessCpe: String, version: String) = {
println(s"versionlessCpe: $versionlessCpe") // println(s"versionlessCpe: $versionlessCpe")
val Seq("cpe", "/a", vendor, product, rest @ _*) = versionlessCpe.split(':').toSeq // val Seq("cpe", "/a", vendor, product, rest @ _*) = versionlessCpe.split(':').toSeq
val cpesFuture = db.run( // val cpesFuture = db.run(
cpeEntries.filter(c => // cpeEntries.filter(c =>
c.vendor === vendor && c.product === product // c.vendor === vendor && c.product === product
).result // ).result
) // )
for(cpes <- cpesFuture){println(s"cpes: $cpes")} // for(cpes <- cpesFuture){println(s"cpes: $cpes")}
val cpesMapFuture = cpesFuture.map(_.toMap) // val cpesMapFuture = cpesFuture.map(_.toMap)
val cpeIdsFuture = cpesFuture.map(_.map(_._1)) // val cpeIdsFuture = cpesFuture.map(_.map(_._1))
val parsedVersion = parseVersion(version) // val parsedVersion = parseVersion(version)
val res = for{ // val res = for{
cpeIds <- cpeIdsFuture // cpeIds <- cpeIdsFuture
relevantVulnerabilities <- db.run( // relevantVulnerabilities <- db.run(
softwareVulnerabilities.join(vulnerabilities).on( (sv, v) => sv.vulnerabilityId === v.id) // softwareVulnerabilities.join(vulnerabilities).on( (sv, v) => sv.vulnerabilityId === v.id)
.filter{case (sv, v) => sv.cpeEntryId inSet cpeIds}.map{case (sv, v) sv}.result // .filter{case (sv, v) => sv.cpeEntryId inSet cpeIds}.map{case (sv, v) ⇒ sv}.result
).map(_.groupBy(_.vulnerabilityId).mapValues(_.toSet)) // ).map(_.groupBy(_.vulnerabilityId).mapValues(_.toSet))
cpesMap <- cpesMapFuture // cpesMap <- cpesMapFuture
//relevantVulnerabilities <- db.run(vulnerabilities.filter(_.id inSet relevantVulnerabilityIds).result) // //relevantVulnerabilities <- db.run(vulnerabilities.filter(_.id inSet relevantVulnerabilityIds).result)
} yield relevantVulnerabilities.filter{case (vulnId, sv) => Option(CveDbHelper.matchSofware( // } yield relevantVulnerabilities.filter{case (vulnId, sv) => Option(CveDbHelper.matchSofware(
vulnerableSoftware = sv.map(sv => cpesMap(sv.cpeEntryId).cpe -> sv.includesAllPreviousVersions).toMap, // vulnerableSoftware = sv.map(sv => cpesMap(sv.cpeEntryId).cpe -> sv.includesAllPreviousVersions).toMap,
vendor = vendor, // vendor = vendor,
product = product, // product = product,
identifiedVersion = parsedVersion // identifiedVersion = parsedVersion
)).isDefined} // )).isDefined}
res.map(_.values.toSet.flatten) // res.map(_.values.toSet.flatten)
} // }
private def loadUpdateProperties(): Future[Map[String, Long]] = db.run(properties.filter(_.id like "NVD CVE%").result).map(_.map{case OdcProperty(id, value) => (id, value.toLong)}.toMap) private def loadUpdateProperties(): Future[Map[String, Long]] = db.run(properties.filter(_.id like "NVD CVE%").result).map(_.map{case OdcProperty(id, value) => (id, value.toLong)}.toMap)

View File

@@ -97,7 +97,7 @@
<h5 data-toggle="collapse" class="expandable@if(!expandVulnerabilities){ collapsed}" data-target="#@vulnPrefix-details"> <h5 data-toggle="collapse" class="expandable@if(!expandVulnerabilities){ collapsed}" data-target="#@vulnPrefix-details">
@vuln.name @vuln.name
<a href="@routes.Statistics.vulnerability(vuln.name, selectorOption)" target="_blank" onclick="event.stopPropagation();"><span class="glyphicon glyphicon-new-window"></span></a> <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>} @*@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> </h5>
<div id="@vulnPrefix-details" class="collapse vulnerability-expandable@if(expandVulnerabilities){ in}"> <div id="@vulnPrefix-details" class="collapse vulnerability-expandable@if(expandVulnerabilities){ in}">
@vulnerability("h6", depPrefix+"-"+vuln.name, vuln) @vulnerability("h6", depPrefix+"-"+vuln.name, vuln)

View File

@@ -28,13 +28,13 @@
</span></p> </span></p>
@*@vuln.cweOption.map{cwe =><p><label>Category:</label> <span class="explained" title="Vulnerability category according to Common Weakness Enumeration" onmouseover="$(this).tooltip({placement: 'right'}).tooltip('show')"><b>@cwe</b></span></p>}*@ @*@vuln.cweOption.map{cwe =><p><label>Category:</label> <span class="explained" title="Vulnerability category according to Common Weakness Enumeration" onmouseover="$(this).tooltip({placement: 'right'}).tooltip('show')"><b>@cwe</b></span></p>}*@
<label>Description:</label> @vuln.description <label>Description:</label> @vuln.description
@section("vuln-sw", "Vulnerable software"){ @*@section("vuln-sw", "Vulnerable software"){
<ul id="@idPrefix-details"> <ul id="@idPrefix-details">
@for(sw <- vuln.vulnerableSoftware){ @for(sw <- vuln.vulnerableSoftware){
<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> <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> </ul>
} }*@
@section("references", "References"){ @section("references", "References"){
<ul> <ul>
@for(reference <- vuln.references){ @for(reference <- vuln.references){

View File

@@ -45,7 +45,7 @@ POST /notifications/watch controllers.Notifications.watch(pr
POST /notifications/unwatch controllers.Notifications.unwatch(project: String, filter: Option[String]) POST /notifications/unwatch controllers.Notifications.unwatch(project: String, filter: Option[String])
GET /notifications/cron/:key controllers.Notifications.cron(key: String, purgeCache: Boolean ?= true) GET /notifications/cron/:key controllers.Notifications.cron(key: String, purgeCache: Boolean ?= true)
GET /libraries/vulnerabilities controllers.Statistics.searchVulnerableSoftware(versionlessCpes: Seq[String], versionOption: Option[String]) #GET /libraries/vulnerabilities controllers.Statistics.searchVulnerableSoftware(versionlessCpes: Seq[String], versionOption: Option[String])
GET /vulnerability/:name controllers.Statistics.vulnerability(name, selector: Option[String]) GET /vulnerability/:name controllers.Statistics.vulnerability(name, selector: Option[String])