From 23d851aff066a39e3ceb01f85c30539e467a70ea Mon Sep 17 00:00:00 2001 From: kongr45gpen Date: Tue, 15 Mar 2016 16:41:58 +0200 Subject: Add support for github secrets --- local/globals.py | 2 ++ local/handler/GithubHandler.py | 37 ++++++++++++++++++++++++++++--------- local/utility.py | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) (limited to 'local') diff --git a/local/globals.py b/local/globals.py index 6cc8c7b..b389cab 100644 --- a/local/globals.py +++ b/local/globals.py @@ -2,7 +2,9 @@ def init(): global messageList global configOverrides global travisStatuses + global secretDB messageList = [] configOverrides = {} travisStatuses = {} + secretDB = None diff --git a/local/handler/GithubHandler.py b/local/handler/GithubHandler.py index 4d420e3..53ea740 100644 --- a/local/handler/GithubHandler.py +++ b/local/handler/GithubHandler.py @@ -1,10 +1,12 @@ import os import re +import hmac import json import time import random import urllib import urllib2 +import hashlib import urlparse import threading import BaseHTTPServer @@ -37,17 +39,12 @@ class GithubHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_POST(s): """Respond to a POST request.""" length = int(s.headers['Content-Length']) + payload = s.rfile.read(length).decode('utf-8') if 'content-type' not in s.headers or s.headers['content-type'] == 'application/x-www-form-urlencoded': - post_data = urlparse.parse_qs(s.rfile.read(length).decode('utf-8')) + post_data = urlparse.parse_qs(payload) data = json.loads(post_data['payload'][0]) else: - data = json.loads(s.rfile.read(length).decode('utf-8')) - - s.send_response(200) - s.send_header('Content-type', 'text/html') - s.end_headers() - s.wfile.write("Thanks, you're awesome.\n") - s.wfile.write(s.path.split('/')) + data = json.loads(payload) if 'X-GitHub-Event' in s.headers: eventType = s.headers['X-GitHub-Event'] @@ -58,7 +55,7 @@ class GithubHandler(BaseHTTPServer.BaseHTTPRequestHandler): if not os.path.exists('requests/'): os.makedirs('requests') - f = open('requests/' + eventType + strftime("%Y-%m-%d %H:%M:%S") + '.json', 'w') + f = open('requests/' + eventType.replace('/','_') + strftime("%Y-%m-%d %H:%M:%S") + '.json', 'w') f.write(json.dumps(data, sort_keys=True, indent=4, separators=(',', ': '))) f.close() @@ -92,11 +89,33 @@ class GithubHandler(BaseHTTPServer.BaseHTTPRequestHandler): i+=1 + s.send_response(200) + s.send_header('Content-type', 'text/html') + s.end_headers() + s.wfile.write("Thanks, you're awesome.\n") + s.wfile.write(s.path.split('/')) + if requireCode and receivedcode != configValue('passcode'): # The password is wrong s.wfile.write("The password is wrong") return + secret = getChannelSecret(channel) + if secret is not None: + if not 'X-Hub-Signature' in s.headers: + s.wfile.write("This channel requires a secret") + return + + digest = "sha1=%s" % (hmac.new(secret, payload, hashlib.sha1).hexdigest(),) + log.debug("expected digest: %s", digest) + + provided = s.headers['X-Hub-Signature'] + log.debug("provided digest: %s", provided) + + if not secureCompare(digest, provided): + s.wfile.write("Invalid secret key") + return + brackets = parseBrackets(configValue('brackets')) themeName = configValue('theme') diff --git a/local/utility.py b/local/utility.py index de540a6..2594b61 100644 --- a/local/utility.py +++ b/local/utility.py @@ -1,5 +1,7 @@ import re import math +import random +import string import urllib2 import supybot.conf as conf @@ -134,6 +136,26 @@ def isStatusVisible(repo, status): globals.travisStatuses[repo] = status return changed +def randomString(length): + """Returns a securely generated random string of a specific length""" + return ''.join(random.SystemRandom().choice( + string.ascii_uppercase + string.ascii_lowercase + string.digits + ) for _ in range(length)) + +def secureCompare(s1, s2): + """Securely compare two strings""" + return sum(i != j for i, j in zip(s1, s2)) is 0 + +def getChannelSecret(channel): + """Returns a secret for a channel, or None if that channel has no secret""" + if globals.secretDB is None: + return None + try: + record = globals.secretDB.get(channel, 1) + return record.secret + except KeyError: + return None + def hexToMirc(hash): colors = { 'white': (255, 255, 255), -- cgit v1.2.3