Skip to content

Commit

Permalink
Fix logsol#21: Compare HMAC of request body with X-Hub-Signature header
Browse files Browse the repository at this point in the history
  • Loading branch information
kumy committed Apr 18, 2015
1 parent ee8ba92 commit 31405d6
Showing 1 changed file with 55 additions and 3 deletions.
58 changes: 55 additions & 3 deletions GitAutoDeploy.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env python

import json, urlparse, sys, os
import hmac, hashlib
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from subprocess import call


class GitAutoDeploy(BaseHTTPRequestHandler):

CONFIG_FILEPATH = './GitAutoDeploy.conf.json'
Expand Down Expand Up @@ -36,6 +38,18 @@ def getConfig(myClass):

def do_POST(self):
event = self.headers.getheader('X-Github-Event')
body, payload = self.getPayload()
try:
urls = self.parseRequest(payload)
except:
if not self.quiet:
print 'Cannot parse url. Invalid payload?'
self.respond(304)
return

if not self.checkHMACSignature(body, urls):
return

if event == 'ping':
if not self.quiet:
print 'Ping event received'
Expand All @@ -49,18 +63,56 @@ def do_POST(self):

self.respond(204)

urls = self.parseRequest()
for url in urls:
paths = self.getMatchingPaths(url)
for path in paths:
self.fetch(path)
self.deploy(path)

def parseRequest(self):
def getPayload(self):
length = int(self.headers.getheader('content-length'))
body = self.rfile.read(length)
payload = json.loads(body)
self.branch = payload['ref']
return (body, payload)

def _validate_signature(self, secret, data):
sha_name, signature = self.headers.getheader('X-Hub-Signature').split('=')
if sha_name != 'sha1':
return False

# HMAC requires its key to be bytes, but data is strings.
mac = hmac.new(bytes(secret), msg=data, digestmod=hashlib.sha1)
return mac.hexdigest() == signature

def checkHMACSignature(self, body, urls):
signature = self.headers.getheader('X-Hub-Signature')
if not signature:
return True

config = self.getConfig()
secret = None
for url in urls:
for repository in config['repositories']:
if (repository['url'] == url):
if 'secret' in repository:
secret = repository['secret']
if not secret:
if not self.quiet:
print 'No secret configured'
self.respond(304)
return False

if not self._validate_signature(secret, body):
if not self.quiet:
print 'Bad request signature'
self.respond(304)
return False

return True

def parseRequest(self, payload):
if 'ref' in payload:
self.branch = payload['ref']
return [payload['repository']['url']]

def getMatchingPaths(self, repoUrl):
Expand Down

0 comments on commit 31405d6

Please sign in to comment.