From 272326ae18fd2fdb88f6f0a55e88ab4af1e0178a Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 21 Sep 2015 17:46:50 -0400 Subject: [PATCH] Add schema validation to all external trigger types --- buildtrigger/bitbuckethandler.py | 170 ++++++++++++++++++++++++++++++- buildtrigger/githubhandler.py | 75 +++++++++++++- buildtrigger/gitlabhandler.py | 58 ++++++++++- 3 files changed, 297 insertions(+), 6 deletions(-) diff --git a/buildtrigger/bitbuckethandler.py b/buildtrigger/bitbuckethandler.py index 3c7ba810a..7d89a2035 100644 --- a/buildtrigger/bitbuckethandler.py +++ b/buildtrigger/bitbuckethandler.py @@ -1,8 +1,10 @@ import logging import re +from jsonschema import validate from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException, TriggerDeactivationException, TriggerStartException, + InvalidPayloadException, determine_build_ref, raise_if_skipped_build, find_matching_branches) @@ -18,11 +20,172 @@ logger = logging.getLogger(__name__) _BITBUCKET_COMMIT_URL = 'https://bitbucket.org/%s/commits/%s' _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): """ Returns the BitBucket commit information transformed into our own 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) 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 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) change = payload['push.changes[-1].new'] diff --git a/buildtrigger/githubhandler.py b/buildtrigger/githubhandler.py index 76131f2bf..aba0e0c1a 100644 --- a/buildtrigger/githubhandler.py +++ b/buildtrigger/githubhandler.py @@ -3,11 +3,12 @@ import os.path import base64 from app import app, github_trigger +from jsonschema import validate from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException, TriggerDeactivationException, TriggerStartException, EmptyRepositoryException, ValidationRequestException, - SkipRequestException, + SkipRequestException, InvalidPayloadException, determine_build_ref, raise_if_skipped_build, find_matching_branches) @@ -21,12 +22,82 @@ from github import (Github, UnknownObjectException, GithubException, 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): """ Returns the GitHub webhook JSON payload transformed into our own payload 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) config = SafeDictSetter() diff --git a/buildtrigger/gitlabhandler.py b/buildtrigger/gitlabhandler.py index d4269337f..5734dbc75 100644 --- a/buildtrigger/gitlabhandler.py +++ b/buildtrigger/gitlabhandler.py @@ -2,10 +2,11 @@ import logging from app import app +from jsonschema import validate from buildtrigger.triggerutil import (RepositoryReadException, TriggerActivationException, TriggerDeactivationException, TriggerStartException, EmptyRepositoryException, ValidationRequestException, - SkipRequestException, + SkipRequestException, InvalidPayloadException, determine_build_ref, raise_if_skipped_build, find_matching_branches) @@ -18,12 +19,65 @@ import gitlab 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): """ Returns the Gitlab webhook JSON payload transformed into our own payload 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) config = SafeDictSetter()