mirror of
https://github.com/ysoftdevs/odc-analyzer.git
synced 2026-01-15 00:03:59 +01:00
Major vulnerable libraries UI redesign
This commit is contained in:
@@ -75,15 +75,24 @@ h3.library-identification{
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
.dependencies-table .identifiers ul, .dependencies-table .identifiers ul li{
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.severity{
|
||||
font-size: smaller;
|
||||
margin-bottom: 5px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.severity .score{
|
||||
font-weight: bolder;
|
||||
}
|
||||
.severity .computation-details{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.explained{
|
||||
border-bottom: 1px dashed black;
|
||||
@@ -101,7 +110,7 @@ h3.library-identification{
|
||||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
margin-left: 0.5em;
|
||||
font-size: 50%;
|
||||
font-size: 70%;
|
||||
}
|
||||
.explained:hover{
|
||||
border-bottom-color: blue;
|
||||
@@ -155,4 +164,36 @@ h3.library-identification{
|
||||
}
|
||||
.nav > li.collapse.in {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
.dependencies-table .vulns{
|
||||
text-align: right;
|
||||
}
|
||||
.dependencies-table .actions{
|
||||
vertical-align: bottom;
|
||||
width: 10px;
|
||||
}
|
||||
.dependencies-table > tbody > tr:nth-of-type(4n+1), .dependencies-table > tbody > tr:nth-of-type(4n+2) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.dependencies-table > tbody > tr.details > td {
|
||||
border-top-style: none;
|
||||
}
|
||||
.dependencies-table .severity .explained:hover {
|
||||
color:blue;
|
||||
}
|
||||
.dependencies-table .severity .explained:after {
|
||||
content: "";
|
||||
display: none;
|
||||
}
|
||||
.dependencies-table .severity .tooltip-inner {
|
||||
white-space:nowrap;
|
||||
max-width:none;
|
||||
}
|
||||
.dependencies-table .identifiers .tooltip-inner {
|
||||
max-width: 350px;
|
||||
width: 350px;
|
||||
}
|
||||
.dependencies-table .identifiers .explained{
|
||||
border-bottom: none;
|
||||
}
|
||||
@@ -64,19 +64,24 @@ function updatePosition(){
|
||||
}
|
||||
function lazyLoad(el){
|
||||
var $el = $(el);
|
||||
var wrapExpr = $el.attr('data-wrapper');
|
||||
var targetEl = wrapExpr ? $(wrapExpr) : $el;
|
||||
var url = $el.attr("data-lazyload-url");
|
||||
function setUrl(newUrl){
|
||||
$el.attr("data-lazyload-url", newUrl);
|
||||
$el.attr("data-lazyload-url", newUrl);
|
||||
}
|
||||
if(url){
|
||||
$el.html($('<div class="progress">')
|
||||
if($el != targetEl){
|
||||
$el.html(targetEl);
|
||||
}
|
||||
targetEl.html($('<div class="progress">')
|
||||
.append(
|
||||
$('<div class="progress-bar progress-bar-striped active" role="progressbar" style="width: 100%;">Loading…</div>')
|
||||
)
|
||||
);
|
||||
$el.load(url, function( response, status, xhr ) {
|
||||
targetEl.load(url, function( response, status, xhr ) {
|
||||
if ( status == "error" ) {
|
||||
$el.html("Error when loading data");
|
||||
targetEl.html("Error when loading data");
|
||||
setUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
@(depPrefix: String, dep: GroupedDependency, selectorOption: Option[String])
|
||||
|
||||
@dep.cpeIdentifiers.toSeq match {
|
||||
case Seq() => {}
|
||||
case cpeIds => {
|
||||
<p>
|
||||
<a href="@routes.Statistics.searchVulnerableSoftware(
|
||||
cpeIds.map(_.name.split(':').take(4).mkString(":")).toSeq, None
|
||||
)" title="Search for known vulnerabilities" class="btn btn-default">Look for vulnerabilities in other versions</a>
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
||||
@if(dep.descriptions.size > 1){
|
||||
<div class="alert alert-warning">Multiple descriptions for this dependency!</div>
|
||||
}
|
||||
|
||||
@@ -3,32 +3,74 @@
|
||||
cpe.getBytes("utf-8").mkString("-")
|
||||
}
|
||||
|
||||
@for(dep <- list; depPrefix = s"$idPrefix-${dep.sha1}"){
|
||||
<h3 class="library-identification" id="@depPrefix-head" data-toggle="collapse" data-target="#@depPrefix-details">
|
||||
@libraryIdentification(dep, Some(cpe => s"$idPrefix-${dep.sha1}-suppression-cpe-${cpeHtmlId(cpe)}"), addLink = false, addButtons = addButtons)
|
||||
@for(s <- dep.maxCvssScore) {
|
||||
<span class="severity">
|
||||
(<span title="highest vulnerability score" class="explained">@s</span>
|
||||
× <span class="explained" title="affected project count">@dep.projects.size</span>
|
||||
= <span class="explained score" title="total score">@dep.ysdssScore</span>)
|
||||
(vulns: @dep.vulnerabilities.size)
|
||||
</span>
|
||||
}
|
||||
@dep.cpeIdentifiers.toSeq match {
|
||||
case Seq() => {}
|
||||
case cpeIds => {
|
||||
<a href="@routes.Statistics.searchVulnerableSoftware(cpeIds.map(_.name.split(':').take(4).mkString(":")).toSeq, None)" title="Search for known vulnerabilities"><span class="glyphicon glyphicon-flash"></span></a>
|
||||
}
|
||||
}
|
||||
</h3>
|
||||
@for(identifier <- dep.identifiers; cpe <- identifier.toCpeIdentifierOption ) {
|
||||
<div id="@(s"$idPrefix-${dep.sha1}-suppression-cpe-${cpeHtmlId(cpe)}")" class="collapse">@SuppressionXml.forCpe(dep, cpe)</div>
|
||||
<table class="table table-condensed dependencies-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Severity</th>
|
||||
<th>Identifiers</th>
|
||||
<th class="vulns">Vulns</th>
|
||||
<th class="actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@for(dep <- list; depPrefix = s"$idPrefix-${dep.sha1}"){
|
||||
<tr>
|
||||
<td class="severity">
|
||||
@for(s <- dep.maxCvssScore) {
|
||||
<span class="score">@dep.ysdssScore.map("%.2f".format(_))</span>
|
||||
<span class="computation-details">
|
||||
= <span class="score-vulnerability">@s</span> × <span class="score-projects">@dep.projects.size</span>
|
||||
</span>
|
||||
}
|
||||
</td>
|
||||
<td class="identifiers">
|
||||
@libraryIdentificationList(dep, Some(cpe => s"$idPrefix-${dep.sha1}-suppression-cpe-${cpeHtmlId(cpe)}"), addLink = false, addButtons = addButtons)
|
||||
</td>
|
||||
<td class="vulns">@for(s <- dep.maxCvssScore) {@dep.vulnerabilities.size}</td>
|
||||
<td class="actions">
|
||||
<button data-toggle="collapse" data-target="#@depPrefix-details" class="btn btn-info"><span class="glyphicon glyphicon-chevron-down"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-wrapper="<td colspan='4' xxxstyle='width: 100%;'></td>" id="@depPrefix-details" class="details collapse" data-lazyload-url="@routes.Statistics.dependencyDetails(
|
||||
depPrefix = depPrefix,
|
||||
depId = dep.hashes,
|
||||
selectorOption = selectorOption
|
||||
)"></tr>
|
||||
}
|
||||
@dependencyDetails(
|
||||
depPrefix = depPrefix,
|
||||
expandByDefault = expandByDefault,
|
||||
selectorOption = selectorOption,
|
||||
dep = dep
|
||||
)
|
||||
|
||||
}
|
||||
</table>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$(".severity .score")
|
||||
.attr("title", "total score = score of highest-rated vulnerability × number of affected projects")
|
||||
.addClass("explained")
|
||||
.tooltip({ placement: "top" });
|
||||
$(".severity .score-vulnerability")
|
||||
.attr("title", "score of highest-rated vulnerability")
|
||||
.addClass("explained")
|
||||
.tooltip({ placement: "bottom" });
|
||||
$(".severity .score-projects")
|
||||
.attr("title", "number of affected projects")
|
||||
.addClass("explained")
|
||||
.tooltip({ placement: "bottom" });
|
||||
var identifierTypes = {
|
||||
"cpe": "Common Platform Enumeration (CPE)",
|
||||
"maven": "Maven",
|
||||
"file": "File name"
|
||||
};
|
||||
var identifierTypesDetails = {
|
||||
"cpe": "This identifier is used in National Vulnerability Database, so it is important for proper matching of vulnerabilities. A mismatched CPE identifier can directly cause wrongly matched vulnerabilities.",
|
||||
"file": "This identifier is shown only if there is no identifier with high or highest confidence."
|
||||
};
|
||||
function escapeHtml(s) {
|
||||
return document.createElement("div").appendChild(document.createTextNode(s)).parentNode.innerHTML;
|
||||
}
|
||||
$(".identifiers .identifier").each(function(i, obj){
|
||||
var $obj = $(obj);
|
||||
var identifierType = $obj.attr("data-type");
|
||||
var confidence = $obj.attr("data-confidence");
|
||||
var identifierTypeFriendlyName = identifierTypes[identifierType] || identifierType;
|
||||
var identifiedDetails = identifierTypesDetails[identifierType];
|
||||
var title = "<b>"+escapeHtml(identifierTypeFriendlyName)+ "</b> identifier matched with <b>"+escapeHtml(confidence)+"</b> confidence. "+(identifiedDetails ? ("<hr>"+identifiedDetails) :"");
|
||||
$obj.addClass("explained").attr("title", title).tooltip({placement: "right", html: true});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
@(identifier: Identifier, addLink: Boolean = true)
|
||||
@identifier.confidence.toString.toLowerCase:
|
||||
@secureLink(if(addLink) identifier.url else ""){@identifier.name}
|
||||
@secureLink(if(addLink) identifier.url else ""){<span class="identifier identifier-@identifier.confidence.toString.toLowerCase" data-confidence="@identifier.confidence.toString.toLowerCase" data-type="@identifier.identifierType">@identifier.name</span>}
|
||||
29
app/views/libraryIdentificationList.scala.html
Normal file
29
app/views/libraryIdentificationList.scala.html
Normal file
@@ -0,0 +1,29 @@
|
||||
@(dep: GroupedDependency, suppressionXmlIdOption: Option[String => String] = None, addLink: Boolean = true, addButtons: Boolean = true)
|
||||
@import com.ysoft.odc.Confidence
|
||||
@implicitOrdering = @{
|
||||
// This implicit should not be theoretically needed, but missing this used to cause somehow non-deterministic scalac behavior:
|
||||
// The first ….sortBy expression used to pass, while the second one used to fail sometimes. Even though both expressions are essentially the same.
|
||||
// When these expressions are swapped, still the first (after swapping) one passes and the second one fails, no matter which one is the first and
|
||||
// which is the second. So, it looks like some compiler bug related to mutable state.
|
||||
// It also seems to be related to some compiler cache. It is somehow possible to compile it by incremental compilation and some code changes, but
|
||||
// clean build deterministically fails at the second expression.
|
||||
// So, making the implicit explicit is a workaround for this issue.
|
||||
Ordering.Tuple4[Confidence.Value, String, String, String]
|
||||
}
|
||||
<ul>
|
||||
@if(!dep.identifiers.exists(_.confidence >= Confidence.High)){
|
||||
<li><span class="identifier identifier-file" data-type="file" data-confidence="highest">@dep.fileNames.toSeq.sorted.mkString(", ")@if(addButtons){<span class="btn-xs library-identification-badge-hack"> </span>}</span></li>
|
||||
}
|
||||
@for(id <- dep.identifiers.toSeq.sortBy(i => (i.confidence, i.identifierType, i.name, i.url)).reverse){
|
||||
<li>
|
||||
@identifier(id, addLink)
|
||||
@for(cpe <- id.toCpeIdentifierOption; suppressionXmlId <- suppressionXmlIdOption; if addButtons){
|
||||
<button class="btn btn-default btn-xs" data-toggle="collapse" data-target="#@suppressionXmlId(cpe)">×</button>
|
||||
}
|
||||
@if(addButtons && suppressionXmlIdOption.isDefined){<span class="btn-xs library-identification-badge-hack"> </span>}
|
||||
</li>
|
||||
}
|
||||
@for(id <- dep.suppressedIdentifiers.toSeq.sortBy(i => (i.confidence, i.identifierType, i.name, i.url)).reverse){
|
||||
<li><del>@identifier(id, addLink)</del></li>
|
||||
}
|
||||
</ul>
|
||||
Reference in New Issue
Block a user