Feature: verify webhooks

This commit is contained in:
Kyle Hornberg
2018-02-06 14:18:47 -06:00
parent 81db4b1f18
commit f8515ab5be
5 changed files with 192 additions and 0 deletions
+5
View File
@@ -85,6 +85,11 @@ Default values::
TODO Show them
Webhooks::
from octokit import webhook
webhook.verify(headers, payload, secret, events=['push'])
Authentication
--------------
+121
View File
@@ -0,0 +1,121 @@
[
"*",
"error",
"commit_comment",
"commit_comment.created",
"create",
"delete",
"deployment",
"deployment_status",
"fork",
"gollum",
"installation",
"installation_repositories",
"installation_repositories.added",
"installation_repositories.removed",
"installation.created",
"installation.deleted",
"issue_comment",
"issue_comment.created",
"issue_comment.deleted",
"issue_comment.edited",
"issues",
"issues.assigned",
"issues.closed",
"issues.demilestoned",
"issues.edited",
"issues.labeled",
"issues.milestoned",
"issues.opened",
"issues.reopened",
"issues.unassigned",
"issues.unlabeled",
"label",
"label.created",
"label.deleted",
"label.edited",
"marketplace_purchase",
"marketplace_purchase.cancelled",
"marketplace_purchase.changed",
"marketplace_purchase.purchased",
"member",
"member.added",
"member.deleted",
"member.edited",
"membership",
"membership.added",
"membership.removed",
"milestone",
"milestone.closed",
"milestone.created",
"milestone.deleted",
"milestone.edited",
"milestone.opened",
"org_block",
"org_block.blocked",
"org_block.unblocked",
"organization",
"organization.member_added",
"organization.member_invited",
"organization.member_removed",
"page_build",
"ping",
"project",
"project_card",
"project_card.closed",
"project_card.created",
"project_card.deleted",
"project_card.edited",
"project_card.reopened",
"project_column",
"project_column.created",
"project_column.deleted",
"project_column.edited",
"project_column.moved",
"project.converted",
"project.created",
"project.deleted",
"project.edited",
"project.moved",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"pull_request_review_comment.created",
"pull_request_review_comment.deleted",
"pull_request_review_comment.edited",
"pull_request_review.dismissed",
"pull_request_review.edited",
"pull_request_review.submitted",
"pull_request.assigned",
"pull_request.closed",
"pull_request.edited",
"pull_request.labeled",
"pull_request.opened",
"pull_request.reopened",
"pull_request.review_request_removed",
"pull_request.review_requested",
"pull_request.synchronize",
"pull_request.unassigned",
"pull_request.unlabeled",
"push",
"release",
"release.published",
"repository",
"repository.archived",
"repository.created",
"repository.deleted",
"repository.privatized",
"repository.publicized",
"repository.unarchived",
"status",
"team",
"team_add",
"team.added_to_repository",
"team.created",
"team.deleted",
"team.edited",
"team.removed_from_repository",
"watch",
"watch.started"
]
+7
View File
@@ -1,3 +1,5 @@
import json
import os
import re
@@ -6,3 +8,8 @@ def snake_case(string):
From https://gist.github.com/jaytaylor/3660565#gistcomment-2271689
"""
return re.compile(r'(?!^)(?<!_)([A-Z])').sub(r'_\1', string).lower()
def get_json_data(filename):
with open(os.path.join(os.path.dirname(__file__), 'data', 'rest.json'), 'r') as f:
return json.load(f)
+21
View File
@@ -0,0 +1,21 @@
import hashlib
import hmac
import json
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'):
return False
if event not in events and '*' not in events:
return False
return _compare(headers, payload, secret)
def _compare(headers, payload, secret):
encoding = 'utf-8'
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))
+38
View File
@@ -0,0 +1,38 @@
from octokit import webhook
class TestWebhook(object):
def test_can_verify_webhook(self):
headers = {'X-Hub-Signature': 'sha1=5d61605c3feea9799210ddcb71307d4ba264225f', 'X-GitHub-Event': 'push'}
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'}
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'}
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'}
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'}
payload = {}
secret = 'secret'
events = ['pushy']
assert webhook.verify(headers, payload, secret, events=events) is False