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:
Joseph Schorr 2015-10-13 18:14:52 -04:00 committed by Jimmy Zelinskie
parent 3677947521
commit 0f3db709ea
19 changed files with 476 additions and 159 deletions

View file

@ -255,5 +255,5 @@ class DefaultConfig(object):
FEATURE_SECURITY_SCANNER = True
SECURITY_SCANNER = {
'ENDPOINT': 'http://192.168.99.100:6060',
'ENGINE_VERSION_TARGET': 1
'ENGINE_VERSION_TARGET': 1,
}

View file

@ -751,6 +751,7 @@ class RepositoryNotification(BaseModel):
method = ForeignKeyField(ExternalNotificationMethod)
title = CharField(null=True)
config_json = TextField()
event_config_json = TextField(default='{}')
class RepositoryAuthorizedEmail(BaseModel):

View file

@ -0,0 +1,27 @@
"""Add event-specific config
Revision ID: 50925110da8c
Revises: 2fb9492c20cc
Create Date: 2015-10-13 18:03:14.859839
"""
# revision identifiers, used by Alembic.
revision = '50925110da8c'
down_revision = '2fb9492c20cc'
from alembic import op
import sqlalchemy as sa
from util.migrate import UTF8LongText
def upgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.add_column('repositorynotification', sa.Column('event_config_json', UTF8LongText, nullable=False))
### end Alembic commands ###
def downgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.drop_column('repositorynotification', 'event_config_json')
### end Alembic commands ###

View file

@ -6,7 +6,7 @@ Create Date: 2015-07-13 16:51:41.669249
# revision identifiers, used by Alembic.
revision = '57dad559ff2d'
down_revision = '3ff4fbc94644'
down_revision = '35f538da62'
from alembic import op
import sqlalchemy as sa

View file

@ -0,0 +1,41 @@
"""Add vulnerability_found event
Revision ID: 5cdc2d819c5
Revises: 50925110da8c
Create Date: 2015-10-13 18:05:32.157858
"""
# revision identifiers, used by Alembic.
revision = '5cdc2d819c5'
down_revision = '50925110da8c'
from alembic import op
import sqlalchemy as sa
def upgrade(tables):
op.bulk_insert(tables.externalnotificationevent,
[
{'id':6, 'name':'vulnerability_found'},
])
op.bulk_insert(tables.notificationkind,
[
{'id':11, 'name':'vulnerability_found'},
])
def downgrade(tables):
op.execute(
(tables.externalnotificationevent.delete()
.where(tables.externalnotificationevent.c.name == op.inline_literal('vulnerability_found')))
)
op.execute(
(tables.notificationkind.delete()
.where(tables.notificationkind.c.name == op.inline_literal('vulnerability_found')))
)

View file

@ -113,12 +113,13 @@ def delete_matching_notifications(target, kind_name, **kwargs):
notification.delete_instance()
def create_repo_notification(repo, event_name, method_name, config, title=None):
def create_repo_notification(repo, event_name, method_name, method_config, event_config, title=None):
event = ExternalNotificationEvent.get(ExternalNotificationEvent.name == event_name)
method = ExternalNotificationMethod.get(ExternalNotificationMethod.name == method_name)
return RepositoryNotification.create(repository=repo, event=event, method=method,
config_json=json.dumps(config), title=title)
config_json=json.dumps(method_config), title=title,
event_config_json=json.dumps(event_config))
def get_repo_notification(uuid):

View file

@ -57,6 +57,10 @@ class RepositoryNotificationList(RepositoryParamResource):
'type': 'object',
'description': 'JSON config information for the specific method of notification'
},
'eventConfig': {
'type': 'object',
'description': 'JSON config information for the specific event of notification',
},
'title': {
'type': 'string',
'description': 'The human-readable title of the notification',
@ -84,6 +88,7 @@ class RepositoryNotificationList(RepositoryParamResource):
new_notification = model.notification.create_repo_notification(repo, parsed['event'],
parsed['method'], parsed['config'],
parsed['eventConfig'],
parsed.get('title', None))
resp = notification_view(new_notification)

View file

@ -84,6 +84,40 @@ def _build_summary(event_data):
return summary
class VulnerabilityFoundEvent(NotificationEvent):
@classmethod
def event_name(cls):
return 'vulnerability_found'
def get_level(self, event_data, notification_data):
priority = event_data['vulnerability']['priority']
if priority == 'Defcon1' or priority == 'Critical':
return 'error'
if priority == 'Medium' or priority == 'High':
return 'warning'
return 'info'
def get_sample_data(self, repository):
return build_event_data(repository, {
'tags': ['latest', 'prod'],
'image': 'some-image-id',
'vulnerability': {
'id': 'CVE-FAKE-CVE',
'description': 'A futurist vulnerability',
'link': 'https://security-tracker.debian.org/tracker/CVE-FAKE-CVE',
'priority': 'Critical',
},
})
def get_summary(self, event_data, notification_data):
msg = '%s vulnerability detected in repository %s in tags %s'
return msg % (event_data['vulnerability']['priority'],
event_data['repository'],
', '.join(event_data['tags']))
class BuildQueueEvent(NotificationEvent):
@classmethod
def event_name(cls):

View file

@ -0,0 +1,4 @@
A <a href="{{ event_data.vulnerability.link }}">{{ event_data.vulnerability.priority }} vulnerability</a> ({{ event_data.vulnerability.id }}) was detected in tags
{{ 'tags' | icon_image }}
{% for tag in event_data.tags %}{%if loop.index > 1 %}, {% endif %}{{ (event_data.repository, tag) | repository_tag_reference }}{% endfor %} in
repository {{ event_data.repository | repository_reference }}

View file

@ -95,7 +95,7 @@ def __create_subtree(repo, structure, creator_username, parent, tag_map):
for path_builder in paths:
path = path_builder(new_image.storage.uuid)
store.put_content('local_us', path, checksum)
new_image.security_indexed = False
new_image.security_indexed_engine = maxsize
new_image.save()
@ -314,6 +314,7 @@ def initialize_database():
ExternalNotificationEvent.create(name='build_start')
ExternalNotificationEvent.create(name='build_success')
ExternalNotificationEvent.create(name='build_failure')
ExternalNotificationEvent.create(name='vulnerability_found')
ExternalNotificationMethod.create(name='quay_notification')
ExternalNotificationMethod.create(name='email')
@ -328,6 +329,7 @@ def initialize_database():
NotificationKind.create(name='build_start')
NotificationKind.create(name='build_success')
NotificationKind.create(name='build_failure')
NotificationKind.create(name='vulnerability_found')
NotificationKind.create(name='password_required')
NotificationKind.create(name='over_private_usage')

View file

@ -0,0 +1,20 @@
#createNotificationModal .dropdown-select {
margin: 0px;
}
#createNotificationModal .options-table {
width: 100%;
margin-bottom: 10px;
}
#createNotificationModal .options-table td {
padding-bottom: 6px;
}
#createNotificationModal .options-table td.name {
width: 160px;
}
#createNotificationModal .options-table-wrapper {
padding: 10px;
}

View file

@ -1,12 +1,12 @@
<!-- Modal message dialog -->
<div class="modal fade" id="createNotificationModal">
<div class="co-dialog modal fade" id="createNotificationModal">
<div class="modal-dialog">
<div class="modal-content">
<form id="createForm" name="createForm" ng-submit="createNotification()">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-disabled="creating">&times;</button>
<h4 class="modal-title">
Create Repository Notification
<i class="fa fa-bell"></i> Create Repository Notification
</h4>
</div>
<div class="modal-body">
@ -29,109 +29,127 @@
</div>
<!-- Create View -->
<table style="width: 100%" ng-show="status == ''">
<tr>
<td style="width: 120px">Notification title:</td>
<td style="padding-right: 21px;">
<input class="form-control" type="text" placeholder="(Optional Title)" ng-model="currentTitle"
style="margin: 10px;">
</td>
</tr>
<div class="options-table-wrapper">
<table class="options-table" ng-show="status == ''">
<tr>
<td class="name">Notification title:</td>
<td>
<input class="form-control" type="text" placeholder="(Optional Title)" ng-model="currentTitle">
</td>
</tr>
</table>
<tr>
<td style="width: 120px">When this occurs:</td>
<td>
<div class="dropdown-select" placeholder="'(Notification Event)'" selected-item="currentEvent.title"
handle-item-selected="handleEventSelected(datum)" clear-value="clearCounter">
<!-- Icons -->
<i class="dropdown-select-icon fa fa-lg" ng-class="currentEvent.icon"></i>
<table class="options-table" ng-show="status == ''">
<tr>
<td class="name">When this occurs:</td>
<td>
<div class="dropdown-select" placeholder="'(Notification Event)'" selected-item="currentEvent.title"
handle-item-selected="handleEventSelected(datum)" clear-value="clearCounter">
<!-- Icons -->
<i class="dropdown-select-icon fa fa-lg" ng-class="currentEvent.icon"></i>
<!-- Dropdown menu -->
<ul class="dropdown-select-menu pull-right" role="menu">
<li ng-repeat="event in events">
<a href="javascript:void(0)" ng-click="setEvent(event)">
<i class="fa fa-lg" ng-class="event.icon"></i> {{ event.title }}
</a>
</li>
</ul>
</div>
</td>
</tr>
<!-- Dropdown menu -->
<ul class="dropdown-select-menu pull-right" role="menu">
<li ng-repeat="event in events">
<a href="javascript:void(0)" ng-click="setEvent(event)">
<i class="fa fa-lg" ng-class="event.icon"></i> {{ event.title }}
</a>
</li>
</ul>
</div>
</td>
</tr>
<tr>
<td>Then issue a:</td>
<td>
<div class="dropdown-select" placeholder="'(Notification Action)'" selected-item="currentMethod.title"
handle-item-selected="handleMethodSelected(datum)" clear-value="clearCounter">
<!-- Icons -->
<i class="dropdown-select-icon fa fa-lg" ng-class="currentMethod.icon"></i>
<tr ng-repeat="field in currentEvent.fields">
<td class="name" valign="top">With {{ field.title }} of:</td>
<td>
<div ng-switch on="field.type">
<select class="form-control" ng-if="field.type == 'enum'"
ng-model="currentEventConfig[field.name]" required>
<option ng-repeat="(key, info) in field.values | orderObjectBy: 'index'" value="{{key}}">{{ info.title }}</option>
</select>
<!-- Dropdown menu -->
<ul class="dropdown-select-menu pull-right" role="menu">
<li ng-repeat="method in methods">
<a href="javascript:void(0)" ng-click="setMethod(method)">
<i class="fa fa-lg" ng-class="method.icon"></i> {{ method.title }}
</a>
</li>
</ul>
</div>
</td>
</tr>
<tr ng-if="currentMethod.fields.length"><td colspan="2"><hr></td></tr>
<tr ng-repeat="field in currentMethod.fields">
<td valign="top" style="padding-top: 10px">{{ field.title }}:</td>
<td>
<div ng-switch on="field.type">
<span ng-switch-when="email">
<input type="email" class="form-control" ng-model="currentConfig[field.name]" required>
</span>
<input type="url" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="url" required>
<input type="text" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="string" required>
<!-- TODO(jschorr): unify the ability to create an input box with all the usual features -->
<div ng-switch-when="regex">
<input type="text" class="form-control" ng-model="currentConfig[field.name]"
ng-pattern="getPattern(field)"
placeholder="{{ field.placeholder }}"
ng-name="field.name"
id="{{ field.name }}"
required>
<div class="alert alert-warning" style="margin-top: 10px; margin-bottom: 10px"
ng-if="field.regex_fail_message && hasRegexMismatch(createForm.$error, field.name)">
<span ng-bind-html="field.regex_fail_message"></span>
<div class="co-alert co-alert-info"
style="margin-top: 6px; margin-bottom: 10px;"
ng-if="field.values[currentEventConfig[field.name]].description">
{{ field.values[currentEventConfig[field.name]].description }}
</div>
</div>
<div class="entity-search" namespace="repository.namespace"
placeholder="''"
current-entity="currentConfig[field.name]"
ng-model="currentConfig[field.name]"
allowed-entities="['user', 'team', 'org']"
ng-switch-when="entity"></div>
</td>
</tr>
</table>
<div ng-if="getHelpUrl(field, currentConfig)"
style="margin-top: 10px; margin-bottom: 10px">
See: <a href="{{ getHelpUrl(field, currentConfig) }}" target="_blank">{{ getHelpUrl(field, currentConfig) }}</a>
<table class="options-table" ng-show="status == ''">
<tr>
<td class="name">Then issue a:</td>
<td>
<div class="dropdown-select" placeholder="'(Notification Action)'" selected-item="currentMethod.title"
handle-item-selected="handleMethodSelected(datum)" clear-value="clearCounter">
<!-- Icons -->
<i class="dropdown-select-icon fa fa-lg" ng-class="currentMethod.icon"></i>
<!-- Dropdown menu -->
<ul class="dropdown-select-menu pull-right" role="menu">
<li ng-repeat="method in methods">
<a href="javascript:void(0)" ng-click="setMethod(method)">
<i class="fa fa-lg" ng-class="method.icon"></i> {{ method.title }}
</a>
</li>
</ul>
</div>
</div>
</td>
</tr>
</td>
</tr>
<tr ng-if="currentMethod.id == 'webhook'">
<td colspan="2">
<div class="alert alert-info" style="margin-top: 20px; margin-bottom: 0px">
JSON metadata representing the event will be <b>POST</b>ed to the URL.
<br><br>
The contents for each event can be found in the user guide:
<a href="http://docs.quay.io/guides/notifications.html#webhook{{ currentEvent.id ? '_' + currentEvent.id : '' }}"
target="_blank">
http://docs.quay.io/guides/notifications.html
</a>
</div>
</td>
</tr>
</table>
<tr ng-repeat="field in currentMethod.fields">
<td valign="top" class="name">{{ field.title }}:</td>
<td>
<div ng-switch on="field.type">
<span ng-switch-when="email">
<input type="email" class="form-control" ng-model="currentConfig[field.name]" required>
</span>
<input type="url" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="url" required>
<input type="text" class="form-control" ng-model="currentConfig[field.name]" ng-switch-when="string" required>
<!-- TODO(jschorr): unify the ability to create an input box with all the usual features -->
<div ng-switch-when="regex">
<input type="text" class="form-control" ng-model="currentConfig[field.name]"
ng-pattern="getPattern(field)"
placeholder="{{ field.placeholder }}"
ng-name="field.name"
id="{{ field.name }}"
required>
<div class="alert alert-warning" style="margin-top: 10px; margin-bottom: 10px"
ng-if="field.regex_fail_message && hasRegexMismatch(createForm.$error, field.name)">
<span ng-bind-html="field.regex_fail_message"></span>
</div>
</div>
<div class="entity-search" namespace="repository.namespace"
placeholder="''"
current-entity="currentConfig[field.name]"
ng-model="currentConfig[field.name]"
allowed-entities="['user', 'team', 'org']"
ng-switch-when="entity"></div>
<div ng-if="getHelpUrl(field, currentConfig)"
style="margin-top: 10px; margin-bottom: 10px">
See: <a href="{{ getHelpUrl(field, currentConfig) }}" target="_blank">{{ getHelpUrl(field, currentConfig) }}</a>
</div>
<div class="co-alert co-alert-info" ng-if="currentMethod.id == 'webhook'"
style="margin-top: 6px; margin-bottom: 0px">
JSON metadata representing the event will be <b>POST</b>ed to the URL.
<br><br>
The contents for each event can be found in the user guide:
<a href="http://docs.quay.io/guides/notifications.html#webhook{{ currentEvent.id ? '_' + currentEvent.id : '' }}"
target="_blank">
http://docs.quay.io/guides/notifications.html
</a>
</div>
</div>
</td>
</tr>
</table>
</div>
</div>
<!-- Auth e-mail button bar -->

View 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;
};
});

View file

@ -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
};

View file

@ -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',

View file

@ -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) {

View file

@ -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>');

View 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;
}]);

View file

@ -5,6 +5,9 @@ from digest import checksums
logger = logging.getLogger(__name__)
# TODO: Fix this to use random
# TODO: Copy in all referenced peewee models, as a later migration changes these
def _get_imagestorages_with_locations(query_modifier):
query = (ImageStoragePlacement
.select(ImageStoragePlacement, ImageStorage, ImageStorageLocation)
@ -35,16 +38,12 @@ def backfill_checksum(imagestorage_with_locations):
with store.stream_read_file(imagestorage_with_locations.locations, store.image_layer_path(imagestorage_with_locations.uuid)) as fp:
imagestorage_with_locations.checksum = 'sha256:{0}'.format(checksums.sha256_file(fp, json_data + '\n'))
imagestorage_with_locations.save()
except IOError as e:
if str(e).startswith("No such key"):
imagestorage_with_locations.checksum = 'unknown:{0}'.format(imagestorage_with_locations.uuid)
imagestorage_with_locations.save()
except:
logger.exception('exception when backfilling checksum of %s', imagestorage_with_locations.uuid)
imagestorage_with_locations.checksum = 'unknown:{0}'.format(imagestorage_with_locations.uuid)
imagestorage_with_locations.save()
def backfill_checksums():
logger.setLevel(logging.DEBUG)
logger.debug('backfill_checksums: Starting')
logger.debug('backfill_checksums: This can be a LONG RUNNING OPERATION. Please wait!')