diff --git a/README.md b/README.md index 8556f52..ee5bec0 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,18 @@ robot.emit "github-repo-event", eventBody For details on these fields, see the [Github Webhook documentation](https://developer.github.com/webhooks/). -**SECURITY WARNING**: This script does not currently validate the Github Secret to verify that the webhook came from Github. So, if someone knows the URL to your Hubot, they can spoof webhooks and issue your Hubot commands. So, for now be careful about exposing commands like `destroy company`, etc. I plan to validate these webhooks soon. In the meantime, patches are welcome. :) +### Securing Your Webhooks +To ensure non-github sources cannot send messages to your hubot, set an +environment variable named `GITHUB_WEBHOOK_SECRET` to your [Github hooks +secret](https://developer.github.com/v3/repos/hooks/#create-a-hook). +This will emit an additional event `github-verified-repo-event` when a webhook +requests signature was generated with your shared secret. + +It will also raise an exceptin in the event the secret is not valid, but this +should not block listeners to `github-repo-event`. + +### Consuming the event You can consume it like so from one of your scripts: ```coffeescript @robot.on "github-repo-event", (repo_event) => diff --git a/package.json b/package.json index 39d02b8..1cd5969 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "querystring": "^0.2.0", - "url": "^0.10.3" + "url": "^0.10.3", + "verify-github-webhook": "^1.0.1" } } diff --git a/src/hubot-github-webhook-listener.coffee b/src/hubot-github-webhook-listener.coffee index 0e29962..7320a17 100644 --- a/src/hubot-github-webhook-listener.coffee +++ b/src/hubot-github-webhook-listener.coffee @@ -52,8 +52,8 @@ module.exports = (robot) -> robot.logger.info("Github post received: ", req) eventBody = eventType : req.headers["x-github-event"] - signature : req.headers["X-Hub-Signature"] - deliveryId : req.headers["X-Github-Delivery"] + signature : req.headers["x-hub-signature"] + deliveryId : req.headers["x-github-delivery"] payload : req.body query : querystring.parse(url.parse(req.url).query) diff --git a/src/hubot-verify-github-webhook-listener.coffee b/src/hubot-verify-github-webhook-listener.coffee new file mode 100644 index 0000000..d92f338 --- /dev/null +++ b/src/hubot-verify-github-webhook-listener.coffee @@ -0,0 +1,15 @@ +verifyGithubWebhook = require('verify-github-webhook').default + +verifySignature = (signature, payload, secret) -> + payload = new Buffer(JSON.stringify(payload)) + return true unless secret? + verifyGithubWebhook(signature, payload, secret) + +module.exports = (robot) -> + secret = process.env['GITHUB_WEBHOOK_SECRET'] + robot.on 'github-repo-event', (repoEvent) => + unless verifySignature(repoEvent.signature, repoEvent.payload, secret) + throw new Error('Invalid Webhook Signature') + + robot.logger.info('received valid webhook, notifying consumers') + robot.emit "github-verified-repo-event", repoEvent