Add schema validation to all external trigger types
This commit is contained in:
parent
bf578420f0
commit
272326ae18
3 changed files with 297 additions and 6 deletions
|
@ -1,8 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from jsonschema import validate
|
||||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||||
TriggerDeactivationException, TriggerStartException,
|
TriggerDeactivationException, TriggerStartException,
|
||||||
|
InvalidPayloadException,
|
||||||
determine_build_ref, raise_if_skipped_build,
|
determine_build_ref, raise_if_skipped_build,
|
||||||
find_matching_branches)
|
find_matching_branches)
|
||||||
|
|
||||||
|
@ -18,11 +20,172 @@ logger = logging.getLogger(__name__)
|
||||||
_BITBUCKET_COMMIT_URL = 'https://bitbucket.org/%s/commits/%s'
|
_BITBUCKET_COMMIT_URL = 'https://bitbucket.org/%s/commits/%s'
|
||||||
_RAW_AUTHOR_REGEX = re.compile(r'.*<(.+)>')
|
_RAW_AUTHOR_REGEX = re.compile(r'.*<(.+)>')
|
||||||
|
|
||||||
|
BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'repository': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'full_name': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['full_name'],
|
||||||
|
},
|
||||||
|
'push': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'changes': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'new': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'target': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'hash': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'message': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'date': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'author': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'user': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'username': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'links': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'html': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['href'],
|
||||||
|
},
|
||||||
|
'avatar': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['href'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['html', 'avatar'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['username'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['user'],
|
||||||
|
},
|
||||||
|
'links': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'html': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['href'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['html'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['hash', 'message', 'date'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['target'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['changes'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'actor': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'username': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'links': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'html': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['href'],
|
||||||
|
},
|
||||||
|
'avatar': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'href': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['href'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['html', 'avatar'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['username'],
|
||||||
|
},
|
||||||
|
'required': ['push', 'repository'],
|
||||||
|
}
|
||||||
|
|
||||||
|
BITBUCKET_COMMIT_INFO_SCHEMA = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'node': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'message': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'timestamp': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'raw_author': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['node', 'message', 'timestamp']
|
||||||
|
}
|
||||||
|
|
||||||
def get_transformed_commit_info(bb_commit, ref, default_branch, repository_name, lookup_author):
|
def get_transformed_commit_info(bb_commit, ref, default_branch, repository_name, lookup_author):
|
||||||
""" Returns the BitBucket commit information transformed into our own
|
""" Returns the BitBucket commit information transformed into our own
|
||||||
payload format.
|
payload format.
|
||||||
"""
|
"""
|
||||||
# TODO(jschorr): Validate commit JSON
|
try:
|
||||||
|
validate(bb_commit, BITBUCKET_COMMIT_INFO_SCHEMA)
|
||||||
|
except Exception as exc:
|
||||||
|
raise InvalidPayloadException(exc.message)
|
||||||
|
|
||||||
commit = JSONPathDict(bb_commit)
|
commit = JSONPathDict(bb_commit)
|
||||||
|
|
||||||
config = SafeDictSetter()
|
config = SafeDictSetter()
|
||||||
|
@ -51,7 +214,10 @@ def get_transformed_webhook_payload(bb_payload, default_branch=None):
|
||||||
""" Returns the BitBucket webhook JSON payload transformed into our own payload
|
""" Returns the BitBucket webhook JSON payload transformed into our own payload
|
||||||
format. If the bb_payload is not valid, returns None.
|
format. If the bb_payload is not valid, returns None.
|
||||||
"""
|
"""
|
||||||
# TODO(jschorr): Validate payload JSON
|
try:
|
||||||
|
validate(bb_payload, BITBUCKET_WEBHOOK_PAYLOAD_SCHEMA)
|
||||||
|
except Exception as exc:
|
||||||
|
raise InvalidPayloadException(exc.message)
|
||||||
|
|
||||||
payload = JSONPathDict(bb_payload)
|
payload = JSONPathDict(bb_payload)
|
||||||
change = payload['push.changes[-1].new']
|
change = payload['push.changes[-1].new']
|
||||||
|
|
|
@ -3,11 +3,12 @@ import os.path
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from app import app, github_trigger
|
from app import app, github_trigger
|
||||||
|
from jsonschema import validate
|
||||||
|
|
||||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||||
TriggerDeactivationException, TriggerStartException,
|
TriggerDeactivationException, TriggerStartException,
|
||||||
EmptyRepositoryException, ValidationRequestException,
|
EmptyRepositoryException, ValidationRequestException,
|
||||||
SkipRequestException,
|
SkipRequestException, InvalidPayloadException,
|
||||||
determine_build_ref, raise_if_skipped_build,
|
determine_build_ref, raise_if_skipped_build,
|
||||||
find_matching_branches)
|
find_matching_branches)
|
||||||
|
|
||||||
|
@ -21,12 +22,82 @@ from github import (Github, UnknownObjectException, GithubException,
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
GITHUB_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'ref': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'head_commit': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'id': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'url': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'message': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'timestamp': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'author': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'username': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'html_url': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'avatar_url': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['username'],
|
||||||
|
},
|
||||||
|
'committer': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'username': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'html_url': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'avatar_url': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['username'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['id', 'url', 'message', 'timestamp'],
|
||||||
|
},
|
||||||
|
'repository': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'ssh_url': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['ssh_url'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['ref', 'head_commit', 'repository'],
|
||||||
|
}
|
||||||
|
|
||||||
def get_transformed_webhook_payload(gh_payload, default_branch=None, lookup_user=None):
|
def get_transformed_webhook_payload(gh_payload, default_branch=None, lookup_user=None):
|
||||||
""" Returns the GitHub webhook JSON payload transformed into our own payload
|
""" Returns the GitHub webhook JSON payload transformed into our own payload
|
||||||
format. If the gh_payload is not valid, returns None.
|
format. If the gh_payload is not valid, returns None.
|
||||||
"""
|
"""
|
||||||
# TODO(jschorr): Validate payload JSON
|
try:
|
||||||
|
validate(gh_payload, GITHUB_WEBHOOK_PAYLOAD_SCHEMA)
|
||||||
|
except Exception as exc:
|
||||||
|
raise InvalidPayloadException(exc.message)
|
||||||
|
|
||||||
payload = JSONPathDict(gh_payload)
|
payload = JSONPathDict(gh_payload)
|
||||||
|
|
||||||
config = SafeDictSetter()
|
config = SafeDictSetter()
|
||||||
|
|
|
@ -2,10 +2,11 @@ import logging
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
|
|
||||||
|
from jsonschema import validate
|
||||||
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException,
|
||||||
TriggerDeactivationException, TriggerStartException,
|
TriggerDeactivationException, TriggerStartException,
|
||||||
EmptyRepositoryException, ValidationRequestException,
|
EmptyRepositoryException, ValidationRequestException,
|
||||||
SkipRequestException,
|
SkipRequestException, InvalidPayloadException,
|
||||||
determine_build_ref, raise_if_skipped_build,
|
determine_build_ref, raise_if_skipped_build,
|
||||||
find_matching_branches)
|
find_matching_branches)
|
||||||
|
|
||||||
|
@ -18,12 +19,65 @@ import gitlab
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
GITLAB_WEBHOOK_PAYLOAD_SCHEMA = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'ref': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'checkout_sha': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'repository': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'git_ssh_url': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['git_ssh_url'],
|
||||||
|
},
|
||||||
|
'commits': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'url': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'message': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'timestamp': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
'author': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'email': {
|
||||||
|
'type': 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['email'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['url', 'message', 'timestamp'],
|
||||||
|
},
|
||||||
|
'minItems': 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'required': ['ref', 'checkout_sha', 'repository'],
|
||||||
|
}
|
||||||
|
|
||||||
def get_transformed_webhook_payload(gl_payload, default_branch=None, lookup_user=None):
|
def get_transformed_webhook_payload(gl_payload, default_branch=None, lookup_user=None):
|
||||||
""" Returns the Gitlab webhook JSON payload transformed into our own payload
|
""" Returns the Gitlab webhook JSON payload transformed into our own payload
|
||||||
format. If the gl_payload is not valid, returns None.
|
format. If the gl_payload is not valid, returns None.
|
||||||
"""
|
"""
|
||||||
# TODO(jschorr): Validate payload JSON
|
try:
|
||||||
|
validate(gl_payload, GITLAB_WEBHOOK_PAYLOAD_SCHEMA)
|
||||||
|
except Exception as exc:
|
||||||
|
raise InvalidPayloadException(exc.message)
|
||||||
|
|
||||||
payload = JSONPathDict(gl_payload)
|
payload = JSONPathDict(gl_payload)
|
||||||
|
|
||||||
config = SafeDictSetter()
|
config = SafeDictSetter()
|
||||||
|
|
Reference in a new issue