WIP: UI for QuaySec
This commit is contained in:
parent
75dfec7875
commit
8c144397e9
5 changed files with 177 additions and 1 deletions
|
@ -85,6 +85,42 @@
|
|||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .fa-flag {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .vuln-name {
|
||||
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .vuln-description {
|
||||
color: #ccc;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .fa-flag.None {
|
||||
color: #00CA00;
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .fa-flag.Medium {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .fa-flag.High {
|
||||
color: red;
|
||||
}
|
||||
|
||||
@keyframes flickerAnimation { /* flame pulses */
|
||||
0% { opacity:1; }
|
||||
50% { opacity:0; }
|
||||
100% { opacity:1; }
|
||||
}
|
||||
|
||||
.repo-panel-tags-element .fa-flag.Critical {
|
||||
color: red;
|
||||
opacity:1;
|
||||
animation: flickerAnimation 1s infinite;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.repo-panel-tags-element .tag-span {
|
||||
|
|
|
@ -86,6 +86,12 @@
|
|||
style="min-width: 62px;">
|
||||
<a href="javascript:void(0)" ng-click="orderBy('size')">Size</a>
|
||||
</td>
|
||||
<td ng-class="tablePredicateClass('vuln_level', options.predicate, options.reverse)"
|
||||
style="width: 60px;">
|
||||
<a href="javascript:void(0)" ng-click="orderBy('vuln_level')">
|
||||
<i class="fa fa-flag"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td class="hidden-xs"
|
||||
ng-class="tablePredicateClass('image_id', options.predicate, options.reverse)"
|
||||
colspan="{{ imageTracks.length + 1 }}"
|
||||
|
@ -108,6 +114,42 @@
|
|||
<span bo-if="!tag.last_modified">Unknown</span>
|
||||
</td>
|
||||
<td class="hidden-xs" bo-text="tag.size | bytes"></td>
|
||||
<td>
|
||||
<span class="cor-loader-inline" ng-if="getTagVulnerabilities(tag).loading"></span>
|
||||
<span ng-if="!getTagVulnerabilities(tag).loading">
|
||||
<i class="fa fa-flag-o" ng-if="!getTagVulnerabilities(tag).indexed"
|
||||
data-title="Image is currently being checked for vulnerabilities"
|
||||
bs-tooltip>
|
||||
</i>
|
||||
<i class="fa fa-flag None"
|
||||
ng-if="getTagVulnerabilities(tag).indexed && !getTagVulnerabilities(tag).hasVulnerabilities"
|
||||
data-title="Image has no vulnerabilities"
|
||||
bs-tooltip>
|
||||
</i>
|
||||
<div class="dropdown" style="text-align: left;"
|
||||
ng-if="getTagVulnerabilities(tag).indexed && getTagVulnerabilities(tag).hasVulnerabilities">
|
||||
<i class="fa fa-flag"
|
||||
data-title="Image has vulnerabilities"
|
||||
data-toggle="dropdown"
|
||||
ng-class="getTagVulnerabilities(tag).highestVulnerability.Priority"
|
||||
bs-tooltip>
|
||||
</i>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li ng-repeat="vuln in getTagVulnerabilities(tag).vulnerabilities">
|
||||
<a href="{{ vuln.Link }}" target="_new">
|
||||
<div class="vuln-name">
|
||||
<i class="fa fa-flag" bo-class="vuln.Priority"></i>
|
||||
{{ vuln.ID }}
|
||||
</div>
|
||||
<div class="vuln-description">
|
||||
{{ vuln.Description }}
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td class="hidden-xs image-id-col">
|
||||
<span class="image-link" repository="repository" image-id="tag.image_id"></span>
|
||||
</td>
|
||||
|
|
|
@ -18,7 +18,7 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
|
||||
'getImages': '&getImages'
|
||||
},
|
||||
controller: function($scope, $element, $filter, $location, ApiService, UIService) {
|
||||
controller: function($scope, $element, $filter, $location, ApiService, UIService, VulnerabilityService) {
|
||||
var orderBy = $filter('orderBy');
|
||||
|
||||
$scope.checkedTags = UIService.createCheckStateController([], 'name');
|
||||
|
@ -35,6 +35,7 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
$scope.tagActionHandler = null;
|
||||
$scope.showingHistory = false;
|
||||
$scope.tagsPerPage = 50;
|
||||
$scope.tagVulnerabilities = {};
|
||||
|
||||
var setTagState = function() {
|
||||
if (!$scope.repository || !$scope.selectedTags) { return; }
|
||||
|
@ -149,6 +150,53 @@ angular.module('quay').directive('repoPanelTags', function () {
|
|||
setTagState();
|
||||
});
|
||||
|
||||
$scope.loadTagVulnerabilities = function(tag, tagData) {
|
||||
var params = {
|
||||
'tag': tag.name,
|
||||
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
|
||||
};
|
||||
|
||||
ApiService.getRepoTagVulnerabilities(null, params).then(function(resp) {
|
||||
tagData.indexed = resp.security_indexed;
|
||||
tagData.loading = false;
|
||||
|
||||
if (resp.security_indexed) {
|
||||
tagData.hasVulnerabilities = !!resp.data.Vulnerabilities.length;
|
||||
tagData.vulnerabilities = resp.data.Vulnerabilities;
|
||||
|
||||
var highest = null;
|
||||
resp.data.Vulnerabilities.forEach(function(v) {
|
||||
if (highest == null ||
|
||||
VulnerabilityService.LEVELS[v.Priority].index < VulnerabilityService.LEVELS[highest.Priority].index) {
|
||||
highest = v;
|
||||
}
|
||||
});
|
||||
|
||||
tagData.highestVulnerability = highest;
|
||||
}
|
||||
}, function() {
|
||||
tagData.loading = false;
|
||||
tagData.hasError = true;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getTagVulnerabilities = function(tag) {
|
||||
if (!$scope.repository) {
|
||||
return
|
||||
}
|
||||
|
||||
var tagName = tag.name;
|
||||
if (!$scope.tagVulnerabilities[tagName]) {
|
||||
$scope.tagVulnerabilities[tagName] = {
|
||||
'loading': true
|
||||
};
|
||||
|
||||
$scope.loadTagVulnerabilities(tag, $scope.tagVulnerabilities[tagName]);
|
||||
}
|
||||
|
||||
return $scope.tagVulnerabilities[tagName];
|
||||
};
|
||||
|
||||
$scope.clearSelectedTags = function() {
|
||||
$scope.checkedTags.setChecked([]);
|
||||
};
|
||||
|
|
|
@ -40,6 +40,19 @@
|
|||
loadImage();
|
||||
loadRepository();
|
||||
|
||||
$scope.downloadPackages = function() {
|
||||
if ($scope.packagesResource) { return; }
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'imageid': imageid
|
||||
};
|
||||
|
||||
$scope.packagesResource = ApiService.getRepoImagePackagesAsResource(params).get(function(packages) {
|
||||
$scope.packages = packages;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.downloadChanges = function() {
|
||||
if ($scope.changesResource) { return; }
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
tab-init="downloadChanges()">
|
||||
<i class="fa fa-code-fork"></i>
|
||||
</span>
|
||||
<span class="cor-tab" tab-title="Packages" tab-target="#packages"
|
||||
tab-init="downloadPackages()">
|
||||
<i class="fa ci-package"></i>
|
||||
</span>
|
||||
</div> <!-- /cor-tabs -->
|
||||
|
||||
<div class="cor-tab-content">
|
||||
|
@ -53,6 +57,39 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Packages -->
|
||||
<div id="packages" class="tab-pane">
|
||||
<div class="resource-view" resource="packagesResource" error-message="'Could not load image packages'">
|
||||
<h3>Image Packages</h3>
|
||||
<div class="empty" ng-if="!packages.security_indexed">
|
||||
<div class="empty-primary-msg">This image has not been indexed yet</div>
|
||||
<div class="empty-secondary-msg">
|
||||
Please try again in a few minutes.
|
||||
</div>
|
||||
</div>
|
||||
<div class="empty" ng-if="packages.security_indexed && !packages.data.InstalledPackages.length">
|
||||
<div class="empty-primary-msg">This image contains no recognized packages</div>
|
||||
<div class="empty-secondary-msg">
|
||||
Quay currently indexes Debian, Red Hat and Ubuntu packages.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table" ng-if="packages.security_indexed && packages.data.InstalledPackages.length">
|
||||
<thead>
|
||||
<th>Package Name</th>
|
||||
<th>Package Version</th>
|
||||
<th>OS</th>
|
||||
</thead>
|
||||
|
||||
<tr ng-repeat="package in packages.data.InstalledPackages | orderBy:'Name'">
|
||||
<td>{{ package.Name }}</td>
|
||||
<td>{{ package.Version }}</td>
|
||||
<td>{{ package.OS }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Reference in a new issue