Update security scan col in repo view to use donut chart and better language
This commit is contained in:
parent
af743b156b
commit
4b51fa5e5b
9 changed files with 113 additions and 20 deletions
|
@ -110,6 +110,19 @@
|
||||||
.repo-panel-tags-element .security-scan-col .has-vulns.Defcon1 .highest-vuln {
|
.repo-panel-tags-element .security-scan-col .has-vulns.Defcon1 .highest-vuln {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.repo-panel-tags-element .security-scan-col .has-vulns {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .security-scan-col .has-vulns .donut-chart {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-panel-tags-element .security-scan-col .has-vulns a {
|
||||||
|
color: ablack;
|
||||||
|
}
|
||||||
|
|
||||||
.repo-panel-tags-element .other-vulns {
|
.repo-panel-tags-element .other-vulns {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
5
static/css/directives/ui/donut-chart.css
Normal file
5
static/css/directives/ui/donut-chart.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.donut-chart-element svg {
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
|
@ -21,6 +21,7 @@
|
||||||
width: 40px;
|
width: 40px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: black !important;
|
color: black !important;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-link .id-label.cas {
|
.image-link .id-label.cas {
|
||||||
|
|
1
static/directives/donut-chart.html
Normal file
1
static/directives/donut-chart.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<span class="donut-chart-element" ng-style="{'line-height': size + 'px'}"></span>
|
|
@ -7,7 +7,7 @@
|
||||||
bs-tooltip>V1ID</span>
|
bs-tooltip>V1ID</span>
|
||||||
|
|
||||||
<span class="id-label cas" ng-if="hasSHA256(manifestDigest)"
|
<span class="id-label cas" ng-if="hasSHA256(manifestDigest)"
|
||||||
data-title="The content-addressable SHA256 hash of this layer."
|
data-title="The content-addressable SHA256 hash of this tag."
|
||||||
data-container="body"
|
data-container="body"
|
||||||
bs-tooltip>SHA256</span>
|
bs-tooltip>SHA256</span>
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden-xs"
|
<td class="hidden-xs"
|
||||||
ng-class="tablePredicateClass('security_scanned', options.predicate, options.reverse)"
|
ng-class="tablePredicateClass('security_scanned', options.predicate, options.reverse)"
|
||||||
style="width: 270px;"
|
style="width: 180px;"
|
||||||
quay-require="['SECURITY_SCANNER']">
|
quay-require="['SECURITY_SCANNER']">
|
||||||
Security Scan
|
Security Scan
|
||||||
</td>
|
</td>
|
||||||
|
@ -141,7 +141,7 @@
|
||||||
data-title="The image for this tag is queued to be scanned for vulnerabilities"
|
data-title="The image for this tag is queued to be scanned for vulnerabilities"
|
||||||
bs-tooltip>
|
bs-tooltip>
|
||||||
<i class="fa fa-ellipsis-h"></i>
|
<i class="fa fa-ellipsis-h"></i>
|
||||||
Queued for scan
|
Queued
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Scan Failed -->
|
<!-- Scan Failed -->
|
||||||
|
@ -149,7 +149,7 @@
|
||||||
data-title="The image for this tag could not be scanned for vulnerabilities"
|
data-title="The image for this tag could not be scanned for vulnerabilities"
|
||||||
bs-tooltip>
|
bs-tooltip>
|
||||||
<i class="fa fa-question-circle"></i>
|
<i class="fa fa-question-circle"></i>
|
||||||
Unable to scan image
|
Unable to scan
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- No Features -->
|
<!-- No Features -->
|
||||||
|
@ -178,22 +178,20 @@
|
||||||
<span ng-if="getTagVulnerabilities(tag).status == 'scanned' && getTagVulnerabilities(tag).hasFeatures && getTagVulnerabilities(tag).hasVulnerabilities"
|
<span ng-if="getTagVulnerabilities(tag).status == 'scanned' && getTagVulnerabilities(tag).hasFeatures && getTagVulnerabilities(tag).hasVulnerabilities"
|
||||||
ng-class="getTagVulnerabilities(tag).highestVulnerability.Priority"
|
ng-class="getTagVulnerabilities(tag).highestVulnerability.Priority"
|
||||||
class="has-vulns" bindonce>
|
class="has-vulns" bindonce>
|
||||||
<a class="vuln-link" bo-href-i="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}?tab=vulnerabilities"
|
|
||||||
data-title="The image for this tag has {{ getTagVulnerabilities(tag).highestVulnerability.Count }} {{ getTagVulnerabilities(tag).highestVulnerability.Priority }} level vulnerabilities"
|
|
||||||
bs-tooltip>
|
|
||||||
<span class="highest-vuln">
|
|
||||||
<span class="vulnerability-priority-view" priority="getTagVulnerabilities(tag).highestVulnerability.Priority">
|
|
||||||
{{ getTagVulnerabilities(tag).highestVulnerability.Count }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span ng-if="getTagVulnerabilities(tag).vulnerabilities.length - getTagVulnerabilities(tag).highestVulnerability.Count > 0"
|
<a class="vuln-link" bo-href-i="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}?tab=vulnerabilities"
|
||||||
class="other-vulns">
|
data-title="This tag has {{ getTagVulnerabilities(tag).vulnerabilities.length }} vulnerabilities across {{ getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount }} packages"
|
||||||
+ {{ getTagVulnerabilities(tag).vulnerabilities.length - getTagVulnerabilities(tag).highestVulnerability.Count }} others
|
bs-tooltip>
|
||||||
|
<!-- Donut -->
|
||||||
|
<span class="donut-chart" width="24" data="getTagVulnerabilities(tag).featuresInfo.severityBreakdown"></span>
|
||||||
|
|
||||||
|
<!-- Messaging -->
|
||||||
|
<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount == 0">
|
||||||
|
{{ getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount }} vulnerable package<span ng-if="getTagVulnerabilities(tag).featuresInfo.brokenFeaturesCount != 1">s</span>
|
||||||
|
</span>
|
||||||
|
<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount > 0">
|
||||||
|
{{ getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount }} fixable package<span ng-if="getTagVulnerabilities(tag).featuresInfo.fixableFeatureCount != 1">s</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
|
||||||
<a bo-href-i="/repository/{{ repository.namespace }}/{{ repository.name }}/image/{{ tag.image_id }}?tab=vulnerabilities" style="display: inline-block; margin-left: 6px;">
|
|
||||||
More Info
|
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -213,7 +213,8 @@ angular.module('quay').directive('repoPanelTags', function () {
|
||||||
highest = {
|
highest = {
|
||||||
'Priority': vuln.Severity,
|
'Priority': vuln.Severity,
|
||||||
'Count': 1,
|
'Count': 1,
|
||||||
'index': VulnerabilityService.LEVELS[vuln.Severity].index
|
'index': VulnerabilityService.LEVELS[vuln.Severity].index,
|
||||||
|
'Color': VulnerabilityService.LEVELS[vuln.Severity].color
|
||||||
}
|
}
|
||||||
} else if (VulnerabilityService.LEVELS[vuln.Severity].index == highest.index) {
|
} else if (VulnerabilityService.LEVELS[vuln.Severity].index == highest.index) {
|
||||||
highest['Count']++;
|
highest['Count']++;
|
||||||
|
@ -226,6 +227,7 @@ angular.module('quay').directive('repoPanelTags', function () {
|
||||||
imageData.hasVulnerabilities = !!vulnerabilities.length;
|
imageData.hasVulnerabilities = !!vulnerabilities.length;
|
||||||
imageData.vulnerabilities = vulnerabilities;
|
imageData.vulnerabilities = vulnerabilities;
|
||||||
imageData.highestVulnerability = highest;
|
imageData.highestVulnerability = highest;
|
||||||
|
imageData.featuresInfo = VulnerabilityService.buildFeaturesInfo(null, resp);
|
||||||
}
|
}
|
||||||
}, function() {
|
}, function() {
|
||||||
imageData.loading = false;
|
imageData.loading = false;
|
||||||
|
|
71
static/js/directives/ui/donut-chart.js
Normal file
71
static/js/directives/ui/donut-chart.js
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
* An element which displays a donut chart of data.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('donutChart', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/donut-chart.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: false,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'width': '@width',
|
||||||
|
'data': '=data',
|
||||||
|
},
|
||||||
|
controller: function($scope, $element) {
|
||||||
|
$scope.created = false;
|
||||||
|
|
||||||
|
// Based on: http://bl.ocks.org/erichoco/6694616
|
||||||
|
var chart = d3.select($element.find('.donut-chart-element')[0]);
|
||||||
|
|
||||||
|
var renderChart = function() {
|
||||||
|
if (!$scope.data || !$scope.data.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $chart = $element.find('.donut-chart-element');
|
||||||
|
$chart.empty();
|
||||||
|
|
||||||
|
var width = $scope.width * 1;
|
||||||
|
var chart_m = width / 2 * 0.14;
|
||||||
|
var chart_r = width / 2 * 0.85;
|
||||||
|
|
||||||
|
var topG = chart.append('svg:svg')
|
||||||
|
.attr('width', (chart_r + chart_m) * 2)
|
||||||
|
.attr('height', (chart_r + chart_m) * 2)
|
||||||
|
.append('svg:g')
|
||||||
|
.attr('class', 'donut')
|
||||||
|
.attr('transform', 'translate(' + (chart_r + chart_m) + ',' +
|
||||||
|
(chart_r + chart_m) + ')');
|
||||||
|
|
||||||
|
|
||||||
|
var arc = d3.svg.arc()
|
||||||
|
.innerRadius(chart_r * 0.6)
|
||||||
|
.outerRadius(function(d, i) {
|
||||||
|
return i == $scope.data.length - 1 ? chart_r * 1.2 : chart_r * 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
var pie = d3.layout.pie()
|
||||||
|
.sort(null)
|
||||||
|
.value(function(d) {
|
||||||
|
return d.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
var reversed = $scope.data.slice(0).reverse();
|
||||||
|
var g = topG.selectAll(".arc")
|
||||||
|
.data(pie(reversed))
|
||||||
|
.enter().append("g")
|
||||||
|
.attr("class", "arc");
|
||||||
|
|
||||||
|
g.append("path")
|
||||||
|
.attr("d", arc)
|
||||||
|
.style('stroke', '#fff')
|
||||||
|
.style("fill", function(d) { return d.data.color; });
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('data', renderChart);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
|
@ -279,6 +279,8 @@ angular.module('quay').factory('VulnerabilityService', ['Config', 'ApiService',
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'features': features,
|
'features': features,
|
||||||
|
'brokenFeaturesCount': features.length - totalCount,
|
||||||
|
'fixableFeatureCount': features.filter(function(f) { return f.fixableScore > 0 }).length,
|
||||||
'severityBreakdown': severityBreakdown,
|
'severityBreakdown': severityBreakdown,
|
||||||
'highestFixableScore': highestFixableScore
|
'highestFixableScore': highestFixableScore
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue