Adjusted sorting

This commit is contained in:
Šesták Vít
2017-05-23 15:45:31 +02:00
parent ef1d434871
commit 70f263baaa
5 changed files with 18 additions and 27 deletions

View File

@@ -188,12 +188,12 @@ h3.library-identification{
white-space:nowrap; white-space:nowrap;
max-width:none; max-width:none;
} }
.dependencies-table .severity .score{ .dependencies-table .severity .score-vulnerability{
font-weight: bolder; font-weight: bolder;
font-size: larger; font-size: larger;
color: red; color: red;
} }
.dependencies-table .severity .score:hover{ .dependencies-table .severity .score-vulnerability:hover{
color: rgb(192, 0, 0); color: rgb(192, 0, 0);
} }
.dependencies-table .identifiers .tooltip-inner { .dependencies-table .identifiers .tooltip-inner {

View File

@@ -73,7 +73,6 @@ final case class GroupedDependency(dependencies: Map[Dependency, Set[ReportInfo]
def parsedDescriptions: Seq[Seq[Seq[String]]] = descriptions.toSeq.sorted.map(_.trim.split("\n\n").filterNot(_=="").toSeq.map(_.split("\n").toSeq)) def parsedDescriptions: Seq[Seq[Seq[String]]] = descriptions.toSeq.sorted.map(_.trim.split("\n\n").filterNot(_=="").toSeq.map(_.split("\n").toSeq))
def isVulnerable: Boolean = vulnerabilities.nonEmpty def isVulnerable: Boolean = vulnerabilities.nonEmpty
def maxCvssScore = (Seq(None) ++ vulnerabilities.map(_.cvssScore)).max def maxCvssScore = (Seq(None) ++ vulnerabilities.map(_.cvssScore)).max
def ysdssScore = maxCvssScore.map(_ * projects.size)
def descriptions = dependencies.keySet.map(_.description) def descriptions = dependencies.keySet.map(_.description)
def projects = dependencies.values.flatten.toSet def projects = dependencies.values.flatten.toSet
def fileNames = dependencies.keySet.map(_.fileName) def fileNames = dependencies.keySet.map(_.fileName)
@@ -153,7 +152,6 @@ object RichBoolean{
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 ysvssScore(affectedDeps: Set[GroupedDependency]) = cvssScore.map(_ * affectedDeps.flatMap(_.projects).toSet.size)
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(":"))

View File

@@ -16,9 +16,9 @@
<tr> <tr>
<td class="severity"> <td class="severity">
@for(s <- dep.maxCvssScore) { @for(s <- dep.maxCvssScore) {
<span class="score">@dep.ysdssScore.map("%.2f".format(_))</span> <span class="score-vulnerability">@s</span>
<span class="computation-details"> <span class="computation-details">
= <span class="score-vulnerability">@s</span> × <span class="score-projects">@dep.projects.size</span> <span class="score-projects">affects @dep.projects.size @if(dep.projects.size>1){projects}else{project}</span>
</span> </span>
} }
</td> </td>
@@ -39,18 +39,10 @@
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
$(".severity .score")
.attr("title", "total score = score of highest-rated vulnerability × number of affected projects")
.addClass("explained")
.tooltip({ placement: "top" });
$(".severity .score-vulnerability") $(".severity .score-vulnerability")
.attr("title", "score of highest-rated vulnerability") .attr("title", "score of highest-rated vulnerability")
.addClass("explained") .addClass("explained")
.tooltip({ placement: "bottom" }); .tooltip({ placement: "top" });
$(".severity .score-projects")
.attr("title", "number of affected projects")
.addClass("explained")
.tooltip({ placement: "bottom" });
var identifierTypes = { var identifierTypes = {
"cpe": "Common Platform Enumeration (CPE)", "cpe": "Common Platform Enumeration (CPE)",
"maven": "Maven", "maven": "Maven",

View File

@@ -15,24 +15,25 @@
They are affecting @statistics.vulnerabilitiesToDependencies.flatMap(_._2.flatMap(_.projects)).toSet.size projects. They are affecting @statistics.vulnerabilitiesToDependencies.flatMap(_._2.flatMap(_.projects)).toSet.size projects.
}else{ }else{
Showing only project matching the selected filter. Showing only project matching the selected filter.
<div class="alert alert-warning">When a filter is applied, YSVSS might differ, as it is computed over a subset of subprojects. As a result, order of vulnerabilities might differ from their order at all-projects view.</div> <div class="alert alert-warning">When a filter is applied, number of affected project might differ, as it is computed over a subset of subprojects. As a result, order of vulnerabilities might slightly differ from their order at all-projects view.</div>
} }
<div class="help"> <div class="help">
Vulnerabilities are sorted by number of affected projects multiplied by their severity. If the score is the same, then they are sorted by severity. If even this matches, they are sorted by name (which is related to vulnerability age). Vulnerabilities are sorted by severity. If the severity is the same, they are sorted by number of affected projects. If even this matches, they are sorted by name (which is related to vulnerability age).
</div> </div>
@for((vulnerability, dependencies) <- statistics.vulnerabilitiesToDependencies.toSeq.sortBy{case (vuln, deps) => @for((vulnerability, dependencies) <- statistics.vulnerabilitiesToDependencies.toSeq.sortBy{case (vuln, deps) =>
( (
vuln.ysvssScore(deps).map(-_), // total score vuln.cvssScore.map(-_), // CVSS score
vuln.cvssScore.map(-_), // CVSS score -deps.flatMap(_.projects).toSet.size, // number of affected projects
vuln.name // make it deterministic vuln.name // make it deterministic
) )
}){ }){
<h2><a href="@routes.Statistics.vulnerability(vulnerability.name, projectsWithSelection.selectorString)">@vulnerability.name</a> <h2><a href="@routes.Statistics.vulnerability(vulnerability.name, projectsWithSelection.selectorString)">@vulnerability.name</a>
<span class="severity"> <span class="severity">
(<span class="explained" title="vulnerability CVSS score">@(vulnerability.cvss.score.getOrElse{"?"})</span> × CVSS <span class="explained" title="vulnerability score based on Common Vulnerability Scoring System 2.0">@(vulnerability.cvss.score.getOrElse{"?"})</span>,
<span class="explained" title="number of affected projects">@dependencies.flatMap(_.projects).toSet.size</span> = @defining(dependencies.flatMap(_.projects).toSet.size){ numProjects =>
<span class="explained score" title="total score">@(vulnerability.ysvssScore(dependencies).fold{"?"}{d => f"$d%2.2f"})</span> affecting @numProjects @if(numProjects>1){projects}else{project}
)</span> }
</span>
</h2> </h2>
<p>@vulnerability.description</p> <p>@vulnerability.description</p>
@* <p>@dependencies.map(_.identifiers)</p> *@ @* <p>@dependencies.map(_.identifiers)</p> *@

View File

@@ -53,7 +53,7 @@ $(document).ready(function(){
<div class="help"> <div class="help">
<p>Libraries are sorted:</p> <p>Libraries are sorted:</p>
<ol> <ol>
<li>by total score (max vulnerability score × number of affected dependencies) if vulnerability score is defined for at least one vulnerability</li> <li>by max vulnerability score if defined for at least one vulnerability</li>
<li>by affected dependency count if the score above is not defined</li> <li>by affected dependency count if the score above is not defined</li>
<li>by number of vulnerabilities</li> <li>by number of vulnerabilities</li>
<li>by affected project count</li> <li>by affected project count</li>
@@ -63,8 +63,8 @@ $(document).ready(function(){
@dependencyList( @dependencyList(
"vulnerable", "vulnerable",
vulnerableDependencies.sortBy(d => ( vulnerableDependencies.sortBy(d => (
d.ysdssScore.map(-_), // total score is the king d.maxCvssScore.map(-_), // maximum CVSS score is the king
if(d.ysdssScore.isEmpty) Some(-d.dependencies.size) else None, // more affected dependencies if no vulnerability has defined severity if(d.maxCvssScore.isEmpty) Some(-d.dependencies.size) else None, // more affected dependencies if no vulnerability has defined severity
-d.vulnerabilities.size, // more vulnerabilities -d.vulnerabilities.size, // more vulnerabilities
-d.projects.size, // more affected projects -d.projects.size, // more affected projects
d.cpeIdentifiers.map(_.toCpeIdentifierOption.get).toSeq.sorted.mkString(" ")) // at least make the order deterministic d.cpeIdentifiers.map(_.toCpeIdentifierOption.get).toSeq.sorted.mkString(" ")) // at least make the order deterministic