From 3bc3ddca287f989f66b628de9827c9ef834ed625 Mon Sep 17 00:00:00 2001 From: Kyle Hornberg Date: Tue, 6 Feb 2018 14:40:24 -0600 Subject: [PATCH] Enhancement: Check delivery guid --- src/octokit/webhook/__init__.py | 20 ++++++++++++++--- tests/test_webhook.py | 39 ++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/octokit/webhook/__init__.py b/src/octokit/webhook/__init__.py index db3c07e..99fe344 100644 --- a/src/octokit/webhook/__init__.py +++ b/src/octokit/webhook/__init__.py @@ -1,15 +1,15 @@ import hashlib import hmac import json +from uuid import UUID from octokit import utils def verify(headers, payload, secret, events=[]): - event = headers.get('X-GitHub-Event') - if event not in utils.get_json_data('events.json'): + if invalid_guid(headers.get('X-GitHub-Delivery')): return False - if event not in events and '*' not in events: + if invalid_event(headers.get('X-GitHub-Event'), events): return False return _compare(headers, payload, secret) @@ -19,3 +19,17 @@ def _compare(headers, payload, secret): algo, sig = headers.get('X-Hub-Signature').split('=') digest = hmac.new(secret.encode(encoding), json.dumps(payload).encode(encoding), getattr(hashlib, algo)).hexdigest() return hmac.compare_digest(sig.encode(encoding), digest.encode(encoding)) + + +def invalid_guid(guid): + try: + return str(UUID(guid)) != guid + except ValueError: + return True + + +def invalid_event(event, events): + if event not in utils.get_json_data('events.json'): + return True + if event not in events and '*' not in events: + return True diff --git a/tests/test_webhook.py b/tests/test_webhook.py index 797c4c1..59b6400 100644 --- a/tests/test_webhook.py +++ b/tests/test_webhook.py @@ -4,35 +4,64 @@ from octokit import webhook class TestWebhook(object): def test_can_verify_webhook(self): - headers = {'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', 'X-GitHub-Event': 'push'} + headers = { + 'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', + 'X-GitHub-Event': 'push', + 'X-GitHub-Delivery': '72d3162f-cc78-11e3-81ab-4c9367dc0958' + } payload = {} secret = 'secret' events = ['push'] assert webhook.verify(headers, payload, secret, events=events) def test_can_filter_webhook_events(self): - headers = {'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f'} + headers = { + 'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', + 'X-GitHub-Delivery': '72d3162f-cc78-11e3-81ab-4c9367dc0958' + } payload = {} secret = 'secret' events = ['push'] assert webhook.verify(headers, payload, secret, events=events) is False def test_must_specify_events_to_allow(self): - headers = {'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f'} + headers = { + 'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', + 'X-GitHub-Delivery': '72d3162f-cc78-11e3-81ab-4c9367dc0958' + } payload = {} secret = 'secret' assert webhook.verify(headers, payload, secret) is False def test_can_specify_all_events(self): - headers = {'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', 'X-GitHub-Event': 'push'} + headers = { + 'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', + 'X-GitHub-Event': 'push', + 'X-GitHub-Delivery': '72d3162f-cc78-11e3-81ab-4c9367dc0958' + } payload = {} secret = 'secret' events = ['*'] assert webhook.verify(headers, payload, secret, events=events) def test_only_known_events_are_valid(self): - headers = {'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', 'X-GitHub-Event': 'pushy'} + headers = { + 'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', + 'X-GitHub-Event': 'pushy', + 'X-GitHub-Delivery': '72d3162f-cc78-11e3-81ab-4c9367dc0958' + } payload = {} secret = 'secret' events = ['pushy'] assert webhook.verify(headers, payload, secret, events=events) is False + + def test_delivery_guids_must_be_valid_guids(self): + headers = { + 'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', + 'X-GitHub-Event': 'push', + 'X-GitHub-Delivery': 'not-a-guid' + } + payload = {} + secret = 'secret' + events = ['push'] + assert webhook.verify(headers, payload, secret, events=events) is False