From 4c89aff8534d493d1c0e136812268ad09ac52f39 Mon Sep 17 00:00:00 2001 From: Geraint White Date: Thu, 30 Nov 2017 21:46:54 +0000 Subject: [PATCH] fix(travis): fix travis signature verification --- src/parser.js | 14 ++++++++++---- src/server.js | 13 +++++++++++++ tests/build-manager-tests.js | 4 ++-- tests/common.js | 8 +++++--- tests/travis.js | 37 +++++++++++++----------------------- 5 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/parser.js b/src/parser.js index 14d0064..02052ef 100644 --- a/src/parser.js +++ b/src/parser.js @@ -100,10 +100,16 @@ var Travis = (function () { }; travis_parser.prototype.verify_signature = function () { - var signature = crypto.createHash('sha256') - .update(this.headers['travis-repo-slug'] + this.config.travis_token) - .digest('hex'); - return this.headers['authorization'] === signature && this.check_url_secret(); + if (!(this.headers['signature'] && this.config.travis_public_key)) { + return false; + } + + var signature = Buffer.from(this.headers['signature'], 'base64'); + var verified = crypto.createVerify('sha1') + .update(this.body) + .verify(this.config.travis_public_key, signature); + + return verified && this.check_url_secret(); }; travis_parser.prototype.extract = function () { diff --git a/src/server.js b/src/server.js index e468900..6c6f01f 100644 --- a/src/server.js +++ b/src/server.js @@ -1,4 +1,5 @@ var http = require('http'), + https = require('https'), socketio = require('socket.io'), url = require('url'), jade = require('jade'), @@ -67,6 +68,18 @@ var Server = function (options, ready) { self.ready(self); }); + + // Load Travis public key + https.get('https://api.travis-ci.org/config', function (res) { + var body = ''; + res.on('data', function(data) { + body += data; + }); + res.on('end', function() { + var json = JSON.parse(body); + self.config.travis_public_key = json.config.notifications.webhook.public_key; + }); + }); }; /** diff --git a/tests/build-manager-tests.js b/tests/build-manager-tests.js index f43e6b1..d8f2fd0 100644 --- a/tests/build-manager-tests.js +++ b/tests/build-manager-tests.js @@ -247,7 +247,7 @@ test('build_manager.data', function (t) { branch: 'master' }) }); - options.headers['authorization'] = travis_sig(config.travis_token, 'repo'); + options.headers['signature'] = travis_sig(payload); options.headers['travis-repo-slug'] = 'repo'; var build_manager = request(payload, function (res, data) { @@ -271,7 +271,7 @@ test('build_manager.data', function (t) { message: 'some commit' }) }); - options.headers['authorization'] = travis_sig(config.travis_token, 'repo'); + options.headers['signature'] = travis_sig(payload); options.headers['travis-repo-slug'] = 'repo'; var build_manager = request(payload, function (res, data) { diff --git a/tests/common.js b/tests/common.js index 0ade664..2ab3826 100644 --- a/tests/common.js +++ b/tests/common.js @@ -33,8 +33,9 @@ function data () { return 'sha1=' + crypto.createHmac('sha1', secret).update(payload).digest('hex'); }; - this.travis_sig = function (secret, slug) { - return crypto.createHash('sha256').update(slug + secret).digest('hex'); + this.travis_sig = function (payload) { + var private_key = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBAI5g7QAWN86efJNTdsmIdd5ry0OwvJ5j28JqoOJfwaXwpvW/1IgK\nu1C4AfXHm0Ci3mn4Q1ta0RX0hhmQEoGyH98CAwEAAQJAGQd/AnHlc6Q24CtfCYS8\nu9IVVJwAPJPvcRkPmVweDc6fi02y2MA8XWpZc5I4tE5eS/DrqgOhnIeymcClVoMj\n2QIhAP7B3jw8wHC5mQKhW58GUw2aIslvilXq45Dv3q6LeeILAiEAjxK5IN9MONJj\nknltBgPkhc+9fLM8JZJzTUGeY96REf0CIBms1kYB5W829VnTg1VioMo1J55flHSW\nSLsZwbqbqfwDAiBenOgWB/S04tR8CZaCUtKtdqp9K14MDqP3I/ylWIqg1QIgQnRQ\nRQ8yUzhFxVkOp3benrEpxY6E1TYBiwDXkoWS9lY=\n-----END RSA PRIVATE KEY-----'; + return crypto.createSign('sha1').update(payload).sign(private_key, 'base64'); }; this.config = { @@ -43,7 +44,8 @@ function data () { getter: '/home/git/deploy/github-getter/get.sh {repo_dir} {output} {repo} {branch}', post_receive: 'python3 /home/git/deploy/post-receive/main.py {dir}', github_secret: '1337', - travis_token: 'topsecret' + travis_token: 'topsecret', + travis_public_key: '-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI5g7QAWN86efJNTdsmIdd5ry0OwvJ5j\n28JqoOJfwaXwpvW/1IgKu1C4AfXHm0Ci3mn4Q1ta0RX0hhmQEoGyH98CAwEAAQ==\n-----END PUBLIC KEY-----' }; this.options = { diff --git a/tests/travis.js b/tests/travis.js index 7aec7ee..aeee0df 100644 --- a/tests/travis.js +++ b/tests/travis.js @@ -4,7 +4,6 @@ var test = require('tape'), var options = common.options; -var config = common.config; var request = common.request; var gen_sig = common.travis_sig; @@ -14,7 +13,7 @@ test('BEGIN TRAVIS PAYLOAD TESTS', function (t) { t.end(); }); test('pass string as payload', function (t) { var payload = 'asdf'; - options.headers['authorization'] = gen_sig(config.travis_token, 'repo'); + options.headers['signature'] = gen_sig(payload); options.headers['travis-repo-slug'] = 'repo'; request(payload, function (res, data) { @@ -28,7 +27,7 @@ test('pass string as payload', function (t) { test('pass invalid data in payload', function (t) { var payload = qs.stringify({ payload: JSON.stringify({}) }); - options.headers['authorization'] = gen_sig(config.travis_token, 'repo'); + options.headers['signature'] = gen_sig(payload); options.headers['travis-repo-slug'] = 'repo'; request(payload, function (res, data) { @@ -41,21 +40,9 @@ test('pass invalid data in payload', function (t) { test('pass valid payload but invalid signature', function (t) { - t.test('valid secret but invalid slug', function (st) { - var payload = qs.stringify({ payload: JSON.stringify({ branch: 'master' }) }); - options.headers['authorization'] = gen_sig(config.travis_token, 'invalid'); - options.headers['travis-repo-slug'] = 'repo'; - - request(payload, function (res, data) { - st.equal(data.err, 'Error: Cannot verify payload signature', 'correct server response'); - st.equal(res.statusCode, 403, 'correct status code'); - st.end(); - }); - }); - t.test('valid payload but invalid secret', function (st) { var payload = qs.stringify({ payload: JSON.stringify({ branch: 'master' }) }); - options.headers['authorization'] = gen_sig('notasecret', 'repo'); + options.headers['signature'] = gen_sig('bogus'); options.headers['travis-repo-slug'] = 'repo'; request(payload, function (res, data) { @@ -67,17 +54,19 @@ test('pass valid payload but invalid signature', function (t) { t.test('no travis-repo-slug header provided', function (st) { var payload = qs.stringify({ payload: JSON.stringify({ branch: 'master' }) }); - options.headers['travis-repo-slug'] = 'repo'; + options.headers['signature'] = gen_sig(payload); + options.headers['travis-repo-slug'] = null; request(payload, function (res, data) { - st.equal(data.err, 'Error: Cannot verify payload signature', 'correct server response'); - st.equal(res.statusCode, 403, 'correct status code'); + st.equal(data.err, 'Error: Invalid payload', 'correct server response'); + st.equal(res.statusCode, 400, 'correct status code'); st.end(); }); }); - t.test('no authorization header provided', function (st) { + t.test('no signature header provided', function (st) { var payload = qs.stringify({ payload: JSON.stringify({ branch: 'master' }) }); + options.headers['signature'] = null; options.headers['travis-repo-slug'] = 'repo'; request(payload, function (res, data) { @@ -93,7 +82,7 @@ test('pass valid payload and valid signature', function (t) { t.test('valid data but mismatching branch', function (st) { var payload = qs.stringify({ payload: JSON.stringify({ branch: 'branch' }) }); - options.headers['authorization'] = gen_sig(config.travis_token, 'repo'); + options.headers['signature'] = gen_sig(payload); options.headers['travis-repo-slug'] = 'repo'; request(payload, function (res, data) { @@ -105,7 +94,7 @@ test('pass valid payload and valid signature', function (t) { t.test('valid data and matching branch', function (st) { var payload = qs.stringify({ payload: JSON.stringify({ branch: 'master' }) }); - options.headers['authorization'] = gen_sig(config.travis_token, 'repo'); + options.headers['signature'] = gen_sig(payload); options.headers['travis-repo-slug'] = 'repo'; request(payload, function (res, data) { @@ -121,7 +110,7 @@ test('pass custom branch name', function (t) { t.test('mismatching branch in path and branch in payload', function (st) { var payload = qs.stringify({ payload: JSON.stringify({ branch: 'master' }) }); - options.headers['authorization'] = gen_sig(config.travis_token, 'repo'); + options.headers['signature'] = gen_sig(payload); options.headers['travis-repo-slug'] = 'repo'; options.query.branch = 'dev'; @@ -134,7 +123,7 @@ test('pass custom branch name', function (t) { t.test('matching branch in path and branch in payload', function (st) { var payload = qs.stringify({ payload: JSON.stringify({ branch: 'dev' }) }); - options.headers['authorization'] = gen_sig(config.travis_token, 'repo'); + options.headers['signature'] = gen_sig(payload); options.headers['travis-repo-slug'] = 'repo'; options.query.branch = 'dev';