Fix handling of non-features from Clair and other small UI fixes
This commit is contained in:
parent
5b7d6b0638
commit
65037ac5e1
8 changed files with 95 additions and 17 deletions
|
@ -1243,6 +1243,13 @@ a:focus {
|
||||||
border-bottom: none !important;
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
color: #ddd;
|
||||||
|
font-size: 46px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.empty-primary-msg {
|
.empty-primary-msg {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
|
@ -4136,3 +4136,13 @@ i.rocket-icon {
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nvtooltip h3 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: normal;
|
||||||
|
background-color: transparent !important;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
|
@ -15,8 +15,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Scanned and has no features -->
|
||||||
|
<div ng-if="securityStatus == 'scanned' && !securityFeatures.length">
|
||||||
|
<div class="empty" style="margin-top: 20px;">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="fa ci-package"></i>
|
||||||
|
</div>
|
||||||
|
<div class="empty-primary-msg">Image is not supported by Quay Security Scanner</div>
|
||||||
|
<div class="empty-secondary-msg">
|
||||||
|
This image has an operating system or package manager unsupported by Quay Security Scanner.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Scanned -->
|
<!-- Scanned -->
|
||||||
<div ng-if="securityStatus == 'scanned'">
|
<div ng-if="securityStatus == 'scanned' && securityFeatures.length">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="security-header row">
|
<div class="security-header row">
|
||||||
<div class="donut-col col-md-3">
|
<div class="donut-col col-md-3">
|
||||||
|
|
|
@ -11,12 +11,25 @@
|
||||||
<div class="empty" ng-if="securityStatus == 'failed'">
|
<div class="empty" ng-if="securityStatus == 'failed'">
|
||||||
<div class="empty-primary-msg">This image could not be indexed</div>
|
<div class="empty-primary-msg">This image could not be indexed</div>
|
||||||
<div class="empty-secondary-msg">
|
<div class="empty-secondary-msg">
|
||||||
Our security scanner was unable to index this image.
|
Quay security scanner was unable to index this image.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Scanned -->
|
<!-- Scanned and has no features -->
|
||||||
<div ng-if="securityStatus == 'scanned'">
|
<div ng-if="securityStatus == 'scanned' && !securityFeatures.length">
|
||||||
|
<div class="empty" style="margin-top: 20px;">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="fa fa-bug"></i>
|
||||||
|
</div>
|
||||||
|
<div class="empty-primary-msg">Image is not supported by Quay Security Scanner</div>
|
||||||
|
<div class="empty-secondary-msg">
|
||||||
|
This image has an operating system or package manager unsupported by Quay Security Scanner.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scanned and has features -->
|
||||||
|
<div ng-if="securityStatus == 'scanned' && securityFeatures.length">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="security-header row">
|
<div class="security-header row">
|
||||||
<div class="donut-col col-md-3">
|
<div class="donut-col col-md-3">
|
||||||
|
@ -37,13 +50,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div ng-if="!priorityBreakdown.length">
|
<div ng-if="!priorityBreakdown.length">
|
||||||
<li class="title-item">Quay Security Scanner has detected no vulnerabilities in this image.</li>
|
Quay Security Scanner has detected no vulnerabilities in this image.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filter -->
|
<!-- Filter -->
|
||||||
<span class="co-filter-box">
|
<span class="co-filter-box" ng-show="priorityBreakdown.length">
|
||||||
<span class="filter-message" ng-if="options.vulnFilter || options.fixableVulns">
|
<span class="filter-message" ng-if="options.vulnFilter || options.fixableVulns">
|
||||||
Showing {{ orderedVulnerabilities.entries.length }} of {{ securityVulnerabilities.length }} Vulnerabilities
|
Showing {{ orderedVulnerabilities.entries.length }} of {{ securityVulnerabilities.length }} Vulnerabilities
|
||||||
</span>
|
</span>
|
||||||
|
@ -55,7 +68,13 @@
|
||||||
<h3>Image Vulnerabilities</h3>
|
<h3>Image Vulnerabilities</h3>
|
||||||
|
|
||||||
<!-- Table -->
|
<!-- Table -->
|
||||||
<table class="co-table">
|
<div class="empty" ng-if="!securityVulnerabilities.length"
|
||||||
|
style="margin-top: 20px;">
|
||||||
|
<div class="empty-primary-msg">No vulnerabilities found.</div>
|
||||||
|
<div class="empty-secondary-msg">Quay Security Scanner has detected no vulnerabilities in this image.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="co-table" ng-show="priorityBreakdown.length">
|
||||||
<thead>
|
<thead>
|
||||||
<td class="caret-col"></td>
|
<td class="caret-col"></td>
|
||||||
<td ng-class="tablePredicateClass('name', options.predicate, options.reverse)">
|
<td ng-class="tablePredicateClass('name', options.predicate, options.reverse)">
|
||||||
|
|
|
@ -143,12 +143,22 @@
|
||||||
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>
|
||||||
Unrecognized OS
|
Unable to scan image
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- No Vulns -->
|
<!-- No Features -->
|
||||||
|
<span class="failed-scan"
|
||||||
|
ng-if="getTagVulnerabilities(tag).status == 'scanned' && !getTagVulnerabilities(tag).hasFeatures"
|
||||||
|
data-title="The image for this tag has an operating system or package manager unsupported by Quay Security Scanner"
|
||||||
|
bs-tooltip
|
||||||
|
bindonce>
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
Unsupported
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Features and No Vulns -->
|
||||||
<span class="no-vulns"
|
<span class="no-vulns"
|
||||||
ng-if="getTagVulnerabilities(tag).status == 'scanned' && !getTagVulnerabilities(tag).hasVulnerabilities"
|
ng-if="getTagVulnerabilities(tag).status == 'scanned' && getTagVulnerabilities(tag).hasFeatures && !getTagVulnerabilities(tag).hasVulnerabilities"
|
||||||
data-title="The image for this tag has no vulnerabilities as found in our database"
|
data-title="The image for this tag has no vulnerabilities as found in our database"
|
||||||
bs-tooltip
|
bs-tooltip
|
||||||
bindonce>
|
bindonce>
|
||||||
|
|
|
@ -171,8 +171,11 @@ angular.module('quay').directive('repoPanelTags', function () {
|
||||||
'index': 100000
|
'index': 100000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var hasFeatures = false;
|
||||||
if (resp.data && resp.data.Layer && resp.data.Layer.Features) {
|
if (resp.data && resp.data.Layer && resp.data.Layer.Features) {
|
||||||
resp.data.Layer.Features.forEach(function(feature) {
|
resp.data.Layer.Features.forEach(function(feature) {
|
||||||
|
hasFeatures = true;
|
||||||
|
|
||||||
if (feature.Vulnerabilities) {
|
if (feature.Vulnerabilities) {
|
||||||
feature.Vulnerabilities.forEach(function(vuln) {
|
feature.Vulnerabilities.forEach(function(vuln) {
|
||||||
if (VulnerabilityService.LEVELS[vuln.Severity].index == 0) {
|
if (VulnerabilityService.LEVELS[vuln.Severity].index == 0) {
|
||||||
|
@ -197,6 +200,7 @@ angular.module('quay').directive('repoPanelTags', function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
imageData.hasVulnerabilities = !!vulnerabilities.length;
|
imageData.hasVulnerabilities = !!vulnerabilities.length;
|
||||||
|
imageData.hasFeatures = hasFeatures;
|
||||||
imageData.vulnerabilities = vulnerabilities;
|
imageData.vulnerabilities = vulnerabilities;
|
||||||
imageData.highestVulnerability = highest;
|
imageData.highestVulnerability = highest;
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,11 @@ angular.module('quay').directive('imageFeatureView', function () {
|
||||||
|
|
||||||
if (data && data.Layer && data.Layer.Features) {
|
if (data && data.Layer && data.Layer.Features) {
|
||||||
data.Layer.Features.forEach(function(feature) {
|
data.Layer.Features.forEach(function(feature) {
|
||||||
var imageId = feature.AddedBy.split('.')[0];
|
var imageId = null;
|
||||||
|
if (feature.AddedBy) {
|
||||||
|
imageId = feature.AddedBy.split('.')[0];
|
||||||
|
}
|
||||||
|
|
||||||
feature_obj = {
|
feature_obj = {
|
||||||
'name': feature.Name,
|
'name': feature.Name,
|
||||||
'namespace': feature.Namespace,
|
'namespace': feature.Namespace,
|
||||||
|
@ -268,11 +272,13 @@ angular.module('quay').directive('imageFeatureView', function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.featureBreakdown.push({
|
if (greenCount > 0) {
|
||||||
'label': 'None',
|
$scope.featureBreakdown.push({
|
||||||
'value': greenCount,
|
'label': 'None',
|
||||||
'color': '#2FC98E'
|
'value': greenCount,
|
||||||
});
|
'color': '#2FC98E'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
buildOrderedFeatures();
|
buildOrderedFeatures();
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,6 +25,10 @@ angular.module('quay').factory('ImageMetadataService', ['UtilService', function(
|
||||||
};
|
};
|
||||||
|
|
||||||
metadataService.getImageCommand = function(image, imageId) {
|
metadataService.getImageCommand = function(image, imageId) {
|
||||||
|
if (!image) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!image.__imageMap) {
|
if (!image.__imageMap) {
|
||||||
image.__imageMap = {};
|
image.__imageMap = {};
|
||||||
for (var i = 0; i < image.history.length; ++i) {
|
for (var i = 0; i < image.history.length; ++i) {
|
||||||
|
@ -33,7 +37,12 @@ angular.module('quay').factory('ImageMetadataService', ['UtilService', function(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getDockerfileCommand(image.__imageMap[imageId].command);
|
var found = image.__imageMap[imageId];
|
||||||
|
if (!found) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDockerfileCommand(found.command);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getDockerfileCommand = function(command) {
|
var getDockerfileCommand = function(command) {
|
||||||
|
|
Reference in a new issue