parent
0dce935c40
commit
03d4445a02
3 changed files with 206 additions and 9 deletions
|
@ -1,6 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from notificationhelper import build_event_data
|
from notificationhelper import build_event_data
|
||||||
|
@ -138,7 +139,28 @@ class VulnerabilityFoundEvent(NotificationEvent):
|
||||||
', '.join(event_data['tags']))
|
', '.join(event_data['tags']))
|
||||||
|
|
||||||
|
|
||||||
class BuildQueueEvent(NotificationEvent):
|
class BaseBuildEvent(NotificationEvent):
|
||||||
|
def should_perform(self, event_data, notification_data):
|
||||||
|
event_config = json.loads(notification_data.event_config_json)
|
||||||
|
ref_regex = event_config.get('ref-regex') or None
|
||||||
|
if ref_regex is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Lookup the ref. If none, this is a non-git build and we should not fire the event.
|
||||||
|
ref = event_data.get('trigger_metadata', {}).get('ref', None)
|
||||||
|
if ref is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Try parsing the regex string as a regular expression. If we fail, we fail to fire
|
||||||
|
# the event.
|
||||||
|
try:
|
||||||
|
return bool(re.compile(str(ref_regex)).match(ref))
|
||||||
|
except Exception:
|
||||||
|
logger.warning('Regular expression error for build event filter: %s', ref_regex)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class BuildQueueEvent(BaseBuildEvent):
|
||||||
@classmethod
|
@classmethod
|
||||||
def event_name(cls):
|
def event_name(cls):
|
||||||
return 'build_queued'
|
return 'build_queued'
|
||||||
|
@ -177,7 +199,7 @@ class BuildQueueEvent(NotificationEvent):
|
||||||
return 'Build queued ' + _build_summary(event_data)
|
return 'Build queued ' + _build_summary(event_data)
|
||||||
|
|
||||||
|
|
||||||
class BuildStartEvent(NotificationEvent):
|
class BuildStartEvent(BaseBuildEvent):
|
||||||
@classmethod
|
@classmethod
|
||||||
def event_name(cls):
|
def event_name(cls):
|
||||||
return 'build_start'
|
return 'build_start'
|
||||||
|
@ -205,7 +227,7 @@ class BuildStartEvent(NotificationEvent):
|
||||||
return 'Build started ' + _build_summary(event_data)
|
return 'Build started ' + _build_summary(event_data)
|
||||||
|
|
||||||
|
|
||||||
class BuildSuccessEvent(NotificationEvent):
|
class BuildSuccessEvent(BaseBuildEvent):
|
||||||
@classmethod
|
@classmethod
|
||||||
def event_name(cls):
|
def event_name(cls):
|
||||||
return 'build_success'
|
return 'build_success'
|
||||||
|
@ -234,7 +256,7 @@ class BuildSuccessEvent(NotificationEvent):
|
||||||
return 'Build succeeded ' + _build_summary(event_data)
|
return 'Build succeeded ' + _build_summary(event_data)
|
||||||
|
|
||||||
|
|
||||||
class BuildFailureEvent(NotificationEvent):
|
class BuildFailureEvent(BaseBuildEvent):
|
||||||
@classmethod
|
@classmethod
|
||||||
def event_name(cls):
|
def event_name(cls):
|
||||||
return 'build_failure'
|
return 'build_failure'
|
||||||
|
|
|
@ -20,22 +20,62 @@ function(Config, Features, VulnerabilityService) {
|
||||||
{
|
{
|
||||||
'id': 'build_queued',
|
'id': 'build_queued',
|
||||||
'title': 'Dockerfile Build Queued',
|
'title': 'Dockerfile Build Queued',
|
||||||
'icon': 'fa-tasks'
|
'icon': 'fa-tasks',
|
||||||
|
'fields': [
|
||||||
|
{
|
||||||
|
'name': 'ref-regex',
|
||||||
|
'type': 'regex',
|
||||||
|
'title': 'matching ref(s)',
|
||||||
|
'help_text': 'An optional regular expression for matching the git branch or tag ' +
|
||||||
|
'git ref. If left blank, the notification will fire for all builds.',
|
||||||
|
'optional': true,
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 'build_start',
|
'id': 'build_start',
|
||||||
'title': 'Dockerfile Build Started',
|
'title': 'Dockerfile Build Started',
|
||||||
'icon': 'fa-circle-o-notch'
|
'icon': 'fa-circle-o-notch',
|
||||||
|
'fields': [
|
||||||
|
{
|
||||||
|
'name': 'ref-regex',
|
||||||
|
'type': 'regex',
|
||||||
|
'title': 'matching ref(s)',
|
||||||
|
'help_text': 'An optional regular expression for matching the git branch or tag ' +
|
||||||
|
'git ref. If left blank, the notification will fire for all builds.',
|
||||||
|
'optional': true,
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 'build_success',
|
'id': 'build_success',
|
||||||
'title': 'Dockerfile Build Successfully Completed',
|
'title': 'Dockerfile Build Successfully Completed',
|
||||||
'icon': 'fa-check-circle-o'
|
'icon': 'fa-check-circle-o',
|
||||||
|
'fields': [
|
||||||
|
{
|
||||||
|
'name': 'ref-regex',
|
||||||
|
'type': 'regex',
|
||||||
|
'title': 'matching ref(s)',
|
||||||
|
'help_text': 'An optional regular expression for matching the git branch or tag ' +
|
||||||
|
'git ref. If left blank, the notification will fire for all builds.',
|
||||||
|
'optional': true,
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 'build_failure',
|
'id': 'build_failure',
|
||||||
'title': 'Dockerfile Build Failed',
|
'title': 'Dockerfile Build Failed',
|
||||||
'icon': 'fa-times-circle-o'
|
'icon': 'fa-times-circle-o',
|
||||||
|
'fields': [
|
||||||
|
{
|
||||||
|
'name': 'ref-regex',
|
||||||
|
'type': 'regex',
|
||||||
|
'title': 'matching ref(s)',
|
||||||
|
'help_text': 'An optional regular expression for matching the git branch or tag ' +
|
||||||
|
'git ref. If left blank, the notification will fire for all builds.',
|
||||||
|
'optional': true,
|
||||||
|
}
|
||||||
|
]
|
||||||
}];
|
}];
|
||||||
|
|
||||||
for (var i = 0; i < buildEvents.length; ++i) {
|
for (var i = 0; i < buildEvents.length; ++i) {
|
||||||
|
@ -52,7 +92,7 @@ function(Config, Features, VulnerabilityService) {
|
||||||
{
|
{
|
||||||
'name': 'level',
|
'name': 'level',
|
||||||
'type': 'enum',
|
'type': 'enum',
|
||||||
'title': 'Minimum Severity Level',
|
'title': 'minimum severity level',
|
||||||
'values': VulnerabilityService.LEVELS,
|
'values': VulnerabilityService.LEVELS,
|
||||||
'help_text': 'A vulnerability must have a severity of the chosen level (or higher) ' +
|
'help_text': 'A vulnerability must have a severity of the chosen level (or higher) ' +
|
||||||
'for this notification to fire. Defcon 1 is a special severity level ' +
|
'for this notification to fire. Defcon 1 is a special severity level ' +
|
||||||
|
|
135
test/test_notifications.py
Normal file
135
test/test_notifications.py
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from endpoints.notificationevent import BuildSuccessEvent
|
||||||
|
from util.morecollections import AttrDict
|
||||||
|
|
||||||
|
class TestShouldPerform(unittest.TestCase):
|
||||||
|
def test_build_nofilter(self):
|
||||||
|
notification_data = AttrDict({
|
||||||
|
'event_config_json': '{}',
|
||||||
|
})
|
||||||
|
|
||||||
|
# No build data at all.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata but no ref.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and a ref.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/heads/somebranch',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_emptyfilter(self):
|
||||||
|
notification_data = AttrDict({
|
||||||
|
'event_config_json': '{"ref-regex": ""}',
|
||||||
|
})
|
||||||
|
|
||||||
|
# No build data at all.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata but no ref.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and a ref.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/heads/somebranch',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_invalidfilter(self):
|
||||||
|
notification_data = AttrDict({
|
||||||
|
'event_config_json': '{"ref-regex": "]["}',
|
||||||
|
})
|
||||||
|
|
||||||
|
# No build data at all.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata but no ref.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and a ref.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/heads/somebranch',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_withfilter(self):
|
||||||
|
notification_data = AttrDict({
|
||||||
|
'event_config_json': '{"ref-regex": "refs/heads/master"}',
|
||||||
|
})
|
||||||
|
|
||||||
|
# No build data at all.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata but no ref.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and a not-matching ref.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/heads/somebranch',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and a matching ref.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/heads/master',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_withwildcardfilter(self):
|
||||||
|
notification_data = AttrDict({
|
||||||
|
'event_config_json': '{"ref-regex": "refs/heads/.+"}',
|
||||||
|
})
|
||||||
|
|
||||||
|
# No build data at all.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata but no ref.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and a not-matching ref.
|
||||||
|
self.assertFalse(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/tags/sometag',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and a matching ref.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/heads/master',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
# With trigger metadata and another matching ref.
|
||||||
|
self.assertTrue(BuildSuccessEvent().should_perform({
|
||||||
|
'trigger_metadata': {
|
||||||
|
'ref': 'refs/heads/somebranch',
|
||||||
|
},
|
||||||
|
}, notification_data))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
|
Reference in a new issue