Add a vulnerability_found event for notice when we detect a vuln
Fixes #637 Note: This PR does *not* actually raise the event; it merely adds support for it
This commit is contained in:
parent
3677947521
commit
0f3db709ea
19 changed files with 476 additions and 159 deletions
17
static/js/directives/object-order-by.js
Normal file
17
static/js/directives/object-order-by.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
// From: http://justinklemm.com/angularjs-filter-ordering-objects-ngrepeat/ under MIT License
|
||||
quayApp.filter('orderObjectBy', function() {
|
||||
return function(items, field, reverse) {
|
||||
var filtered = [];
|
||||
angular.forEach(items, function(item) {
|
||||
filtered.push(item);
|
||||
});
|
||||
|
||||
filtered.sort(function (a, b) {
|
||||
return (a[field] > b[field] ? 1 : -1);
|
||||
});
|
||||
|
||||
if(reverse) filtered.reverse();
|
||||
|
||||
return filtered;
|
||||
};
|
||||
});
|
|
@ -18,6 +18,7 @@ angular.module('quay').directive('createExternalNotificationDialog', function ()
|
|||
$scope.currentMethod = null;
|
||||
$scope.status = '';
|
||||
$scope.currentConfig = {};
|
||||
$scope.currentEventConfig = {};
|
||||
$scope.clearCounter = 0;
|
||||
$scope.unauthorizedEmail = false;
|
||||
|
||||
|
@ -30,6 +31,7 @@ angular.module('quay').directive('createExternalNotificationDialog', function ()
|
|||
|
||||
$scope.setEvent = function(event) {
|
||||
$scope.currentEvent = event;
|
||||
$scope.currentEventConfig = {};
|
||||
};
|
||||
|
||||
$scope.setMethod = function(method) {
|
||||
|
@ -89,6 +91,7 @@ angular.module('quay').directive('createExternalNotificationDialog', function ()
|
|||
'event': $scope.currentEvent.id,
|
||||
'method': $scope.currentMethod.id,
|
||||
'config': $scope.currentConfig,
|
||||
'eventConfig': $scope.currentEventConfig,
|
||||
'title': $scope.currentTitle
|
||||
};
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
* Service which defines the various kinds of external notification and provides methods for
|
||||
* easily looking up information about those kinds.
|
||||
*/
|
||||
angular.module('quay').factory('ExternalNotificationData', ['Config', 'Features',
|
||||
angular.module('quay').factory('ExternalNotificationData', ['Config', 'Features','VulnerabilityService',
|
||||
|
||||
function(Config, Features) {
|
||||
function(Config, Features, VulnerabilityService) {
|
||||
var externalNotificationData = {};
|
||||
|
||||
var events = [
|
||||
|
@ -43,6 +43,22 @@ function(Config, Features) {
|
|||
}
|
||||
}
|
||||
|
||||
if (Features.SECURITY_SCANNER) {
|
||||
events.push({
|
||||
'id': 'vulnerability_found',
|
||||
'title': 'Package Vulnerability Found',
|
||||
'icon': 'fa-flag',
|
||||
'fields': [
|
||||
{
|
||||
'name': 'level',
|
||||
'type': 'enum',
|
||||
'title': 'Minimum Severity Level',
|
||||
'values': VulnerabilityService.LEVELS,
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
var methods = [
|
||||
{
|
||||
'id': 'quay_notification',
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
* in the sidebar) and provides helper methods for working with them.
|
||||
*/
|
||||
angular.module('quay').factory('NotificationService',
|
||||
['$rootScope', '$interval', 'UserService', 'ApiService', 'StringBuilderService', 'PlanService', 'UserService', 'Config', '$location',
|
||||
['$rootScope', '$interval', 'UserService', 'ApiService', 'StringBuilderService', 'PlanService', 'UserService', 'Config', '$location', 'VulnerabilityService',
|
||||
|
||||
function($rootScope, $interval, UserService, ApiService, StringBuilderService, PlanService, UserService, Config, $location) {
|
||||
function($rootScope, $interval, UserService, ApiService, StringBuilderService, PlanService, UserService, Config, $location, VulnerabilityService) {
|
||||
var notificationService = {
|
||||
'user': null,
|
||||
'notifications': [],
|
||||
|
@ -120,6 +120,16 @@ function($rootScope, $interval, UserService, ApiService, StringBuilderService, P
|
|||
return '/repository/' + metadata.repository + '/build?current=' + metadata.build_id;
|
||||
},
|
||||
'dismissable': true
|
||||
},
|
||||
'vulnerability_found': {
|
||||
'level': function(metadata) {
|
||||
var priority = metadata['vulnerability']['priority'];
|
||||
return VulnerabilityService.LEVELS[priority].level;
|
||||
},
|
||||
'message': 'A {vulnerability.priority} vulnerability was detected in repository {repository}',
|
||||
'page': function(metadata) {
|
||||
return '/repository/' + metadata.repository + '?tab=tags';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -182,7 +192,13 @@ function($rootScope, $interval, UserService, ApiService, StringBuilderService, P
|
|||
if (!kindInfo) {
|
||||
return 'notification-info';
|
||||
}
|
||||
return 'notification-' + kindInfo['level'];
|
||||
|
||||
var level = kindInfo['level'];
|
||||
if (level != null && typeof level != 'string') {
|
||||
level = level(notification['metadata']);
|
||||
}
|
||||
|
||||
return 'notification-' + level;
|
||||
};
|
||||
|
||||
notificationService.getClasses = function(notifications) {
|
||||
|
|
|
@ -4,6 +4,27 @@
|
|||
angular.module('quay').factory('StringBuilderService', ['$sce', 'UtilService', function($sce, UtilService) {
|
||||
var stringBuilderService = {};
|
||||
|
||||
var fieldIcons = {
|
||||
'inviter': 'user',
|
||||
'username': 'user',
|
||||
'user': 'user',
|
||||
'email': 'envelope',
|
||||
'activating_username': 'user',
|
||||
'delegate_user': 'user',
|
||||
'delegate_team': 'group',
|
||||
'team': 'group',
|
||||
'token': 'key',
|
||||
'repo': 'hdd-o',
|
||||
'robot': 'ci-robot',
|
||||
'tag': 'tag',
|
||||
'role': 'th-large',
|
||||
'original_role': 'th-large',
|
||||
'application_name': 'cloud',
|
||||
'image': 'archive',
|
||||
'original_image': 'archive',
|
||||
'client_id': 'chain'
|
||||
};
|
||||
|
||||
stringBuilderService.buildUrl = function(value_or_func, metadata) {
|
||||
var url = value_or_func;
|
||||
if (typeof url != 'string') {
|
||||
|
@ -43,28 +64,47 @@ angular.module('quay').factory('StringBuilderService', ['$sce', 'UtilService', f
|
|||
return $sce.trustAsHtml(stringBuilderService.buildString(value_or_func, metadata, opt_codetag));
|
||||
};
|
||||
|
||||
stringBuilderService.buildString = function(value_or_func, metadata, opt_codetag) {
|
||||
var fieldIcons = {
|
||||
'inviter': 'user',
|
||||
'username': 'user',
|
||||
'user': 'user',
|
||||
'email': 'envelope',
|
||||
'activating_username': 'user',
|
||||
'delegate_user': 'user',
|
||||
'delegate_team': 'group',
|
||||
'team': 'group',
|
||||
'token': 'key',
|
||||
'repo': 'hdd-o',
|
||||
'robot': 'ci-robot',
|
||||
'tag': 'tag',
|
||||
'role': 'th-large',
|
||||
'original_role': 'th-large',
|
||||
'application_name': 'cloud',
|
||||
'image': 'archive',
|
||||
'original_image': 'archive',
|
||||
'client_id': 'chain'
|
||||
};
|
||||
stringBuilderService.replaceField = function(description, prefix, key, value, opt_codetag) {
|
||||
if (Array.isArray(value)) {
|
||||
value = value.join(', ');
|
||||
} else if (typeof value == 'object') {
|
||||
for (var subkey in value) {
|
||||
if (value.hasOwnProperty(subkey)) {
|
||||
description = stringBuilderService.replaceField(description, prefix + key + '.',
|
||||
subkey, value[subkey], opt_codetag)
|
||||
}
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
||||
|
||||
value = value.toString();
|
||||
|
||||
if (key.indexOf('image') >= 0) {
|
||||
value = value.substr(0, 12);
|
||||
}
|
||||
|
||||
var safe = UtilService.escapeHtmlString(value);
|
||||
var markedDown = UtilService.getMarkedDown(value);
|
||||
markedDown = markedDown.substr('<p>'.length, markedDown.length - '<p></p>'.length);
|
||||
|
||||
var icon = fieldIcons[key];
|
||||
if (icon) {
|
||||
if (icon.indexOf('ci-') < 0) {
|
||||
icon = 'fa-' + icon;
|
||||
}
|
||||
|
||||
markedDown = '<i class="fa ' + icon + '"></i>' + markedDown;
|
||||
}
|
||||
|
||||
var codeTag = opt_codetag || 'code';
|
||||
description = description.replace('{' + prefix + key + '}',
|
||||
'<' + codeTag + ' title="' + safe + '">' + markedDown + '</' + codeTag + '>');
|
||||
|
||||
return description
|
||||
}
|
||||
|
||||
stringBuilderService.buildString = function(value_or_func, metadata, opt_codetag) {
|
||||
var filters = {
|
||||
'obj': function(value) {
|
||||
if (!value) { return []; }
|
||||
|
@ -89,32 +129,7 @@ angular.module('quay').factory('StringBuilderService', ['$sce', 'UtilService', f
|
|||
value = filters[key](value);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value = value.join(', ');
|
||||
}
|
||||
|
||||
value = value.toString();
|
||||
|
||||
if (key.indexOf('image') >= 0) {
|
||||
value = value.substr(0, 12);
|
||||
}
|
||||
|
||||
var safe = UtilService.escapeHtmlString(value);
|
||||
var markedDown = UtilService.getMarkedDown(value);
|
||||
markedDown = markedDown.substr('<p>'.length, markedDown.length - '<p></p>'.length);
|
||||
|
||||
var icon = fieldIcons[key];
|
||||
if (icon) {
|
||||
if (icon.indexOf('ci-') < 0) {
|
||||
icon = 'fa-' + icon;
|
||||
}
|
||||
|
||||
markedDown = '<i class="fa ' + icon + '"></i>' + markedDown;
|
||||
}
|
||||
|
||||
var codeTag = opt_codetag || 'code';
|
||||
description = description.replace('{' + key + '}',
|
||||
'<' + codeTag + ' title="' + safe + '">' + markedDown + '</' + codeTag + '>');
|
||||
description = stringBuilderService.replaceField(description, '', key, value, opt_codetag);
|
||||
}
|
||||
}
|
||||
return description.replace('\n', '<br>');
|
||||
|
|
98
static/js/services/vulnerability-service.js
Normal file
98
static/js/services/vulnerability-service.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* Service which provides helper methods for working with the vulnerability system.
|
||||
*/
|
||||
angular.module('quay').factory('VulnerabilityService', ['Config', function(Config) {
|
||||
var vulnService = {};
|
||||
|
||||
// NOTE: This objects are used directly in the external-notification-data service, so make sure
|
||||
// to update that code if the format here is changed.
|
||||
vulnService.LEVELS = {
|
||||
'Unknown': {
|
||||
'title': 'Unknown',
|
||||
'index': '6',
|
||||
'level': 'info',
|
||||
|
||||
'description': 'Unknown is either a security problem that has not been assigned ' +
|
||||
'to a priority yet or a priority that our system did not recognize',
|
||||
'banner_required': false
|
||||
},
|
||||
|
||||
'Negligible': {
|
||||
'title': 'Negligible',
|
||||
'index': '5',
|
||||
'level': 'info',
|
||||
|
||||
'description': 'Negligible is technically a security problem, but is only theoretical ' +
|
||||
'in nature, requires a very special situation, has almost no install base, ' +
|
||||
'or does no real damage.',
|
||||
'banner_required': false
|
||||
},
|
||||
|
||||
'Low': {
|
||||
'title': 'Low',
|
||||
'index': '4',
|
||||
'level': 'warning',
|
||||
|
||||
'description': 'Low is a security problem, but is hard to exploit due to environment, ' +
|
||||
'requires a user-assisted attack, a small install base, or does very ' +
|
||||
'little damage.',
|
||||
'banner_required': false
|
||||
},
|
||||
|
||||
'Medium': {
|
||||
'title': 'Medium',
|
||||
'value': 'Medium',
|
||||
'index': '3',
|
||||
'level': 'warning',
|
||||
|
||||
'description': 'Medium is a real security problem, and is exploitable for many people. ' +
|
||||
'Includes network daemon denial of service attacks, cross-site scripting, ' +
|
||||
'and gaining user privileges.',
|
||||
'banner_required': false
|
||||
},
|
||||
|
||||
'High': {
|
||||
'title': 'High',
|
||||
'value': 'High',
|
||||
'index': '2',
|
||||
'level': 'warning',
|
||||
|
||||
'description': 'High is a real problem, exploitable for many people in a default installation. ' +
|
||||
'Includes serious remote denial of services, local root privilege escalations, ' +
|
||||
'or data loss.',
|
||||
'banner_required': false
|
||||
},
|
||||
|
||||
'Critical': {
|
||||
'title': 'Critical',
|
||||
'value': 'Critical',
|
||||
'index': '1',
|
||||
'level': 'error',
|
||||
|
||||
'description': 'Critical is a world-burning problem, exploitable for nearly all people in ' +
|
||||
'a installation of the package. Includes remote root privilege escalations, ' +
|
||||
'or massive data loss.',
|
||||
'banner_required': true
|
||||
},
|
||||
|
||||
'Defcon1': {
|
||||
'title': 'Defcon 1',
|
||||
'value': 'Defcon1',
|
||||
'index': '0',
|
||||
'level': 'error',
|
||||
|
||||
'description': 'Defcon1 is a Critical problem which has been manually highlighted ' +
|
||||
'by the Quay team. It requires immediate attention.',
|
||||
'banner_required': true
|
||||
}
|
||||
};
|
||||
|
||||
vulnService.getLevels = function() {
|
||||
return Object.keys(vulnService.LEVELS).map(function(key) {
|
||||
return vulnService.LEVELS[key];
|
||||
});
|
||||
};
|
||||
|
||||
return vulnService;
|
||||
}]);
|
||||
|
Reference in a new issue