Update quay sec code to fix problems identified in previous review
- Change get_repository_images_recursive to operate over a single docker image and storage uuid - Move endpoints/sec to endpoints/secscan - Change notification system to work with new Quay-sec format Fixes #768
This commit is contained in:
parent
16c364a90c
commit
a69c9e12fd
7 changed files with 146 additions and 79 deletions
88
endpoints/secscan.py
Normal file
88
endpoints/secscan.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
import logging
|
||||
import features
|
||||
|
||||
from app import secscan_endpoint
|
||||
from flask import request, make_response, Blueprint
|
||||
from data import model
|
||||
from data.database import (RepositoryNotification, Repository, ExternalNotificationEvent,
|
||||
RepositoryTag, Image, ImageStorage)
|
||||
from endpoints.common import route_show_if
|
||||
from endpoints.notificationhelper import spawn_notification
|
||||
from collections import defaultdict
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
secscan = Blueprint('secscan', __name__)
|
||||
|
||||
@route_show_if(features.SECURITY_SCANNER)
|
||||
@secscan.route('/notification', methods=['POST'])
|
||||
def secscan_notification():
|
||||
data = request.get_json()
|
||||
logger.debug('Got notification from Clair: %s', data)
|
||||
|
||||
# Find all tags that contain the layer(s) introducing the vulnerability.
|
||||
content = data['Content']
|
||||
layer_ids = content.get('NewIntroducingLayersIDs', content.get('IntroducingLayersIDs', []))
|
||||
if not layer_ids:
|
||||
return make_response('Okay')
|
||||
|
||||
# TODO(jzelinkskie): Write a queueitem for these layer ids, and do the rest of this
|
||||
# in a worker.
|
||||
cve_id = data['Name']
|
||||
vulnerability = data['Content']['Vulnerability']
|
||||
priority = vulnerability['Priority']
|
||||
|
||||
# Lookup the external event for when we have vulnerabilities.
|
||||
event = ExternalNotificationEvent.get(name='vulnerability_found')
|
||||
|
||||
# For each layer, retrieving the matching tags and join with repository to determine which
|
||||
# require new notifications.
|
||||
tag_map = defaultdict(set)
|
||||
repository_map = {}
|
||||
|
||||
for layer_id in layer_ids:
|
||||
(docker_image_id, storage_uuid) = layer_id.split('.', 2)
|
||||
tags = model.tag.get_matching_tags(docker_image_id, storage_uuid, RepositoryTag,
|
||||
Repository, Image, ImageStorage)
|
||||
|
||||
# Additionally filter to tags only in repositories that have the event setup.
|
||||
matching = (tags.switch(RepositoryTag)
|
||||
.join(Repository)
|
||||
.join(RepositoryNotification)
|
||||
.where(RepositoryNotification.event == event))
|
||||
|
||||
check_map = {}
|
||||
for tag in matching:
|
||||
# Verify that the tag's root image has the vulnerability.
|
||||
tag_layer_id = '%s.%s' % (tag.image.docker_image_id, tag.image.storage.uuid)
|
||||
logger.debug('Checking if layer %s is vulnerable to %s', tag_layer_id, cve_id)
|
||||
|
||||
if not tag_layer_id in check_map:
|
||||
is_vulerable = secscan_endpoint.check_layer_vulnerable(tag_layer_id, cve_id)
|
||||
check_map[tag_layer_id] = is_vulerable
|
||||
|
||||
logger.debug('Result of layer %s is vulnerable to %s check: %s', tag_layer_id, cve_id,
|
||||
check_map[tag_layer_id])
|
||||
|
||||
if check_map[tag_layer_id]:
|
||||
# Add the vulnerable tag to the list.
|
||||
tag_map[tag.repository_id].add(tag.name)
|
||||
repository_map[tag.repository_id] = tag.repository
|
||||
|
||||
# For each of the tags found, issue a notification.
|
||||
for repository_id in tag_map:
|
||||
tags = tag_map[repository_id]
|
||||
event_data = {
|
||||
'tags': list(tags),
|
||||
'vulnerability': {
|
||||
'id': data['Name'],
|
||||
'description': vulnerability['Description'],
|
||||
'link': vulnerability['Link'],
|
||||
'priority': priority,
|
||||
},
|
||||
}
|
||||
|
||||
# TODO: only add this notification if the repository's event(s) defined meet the priority
|
||||
# minimum.
|
||||
spawn_notification(repository_map[repository_id], 'vulnerability_found', event_data)
|
||||
|
||||
return make_response('Okay')
|
Reference in a new issue