Skip to content

Commit

Permalink
fix(travis): fix travis signature verification
Browse files Browse the repository at this point in the history
  • Loading branch information
geraintwhite committed Nov 30, 2017
1 parent b4b5b4d commit 4c89aff
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 33 deletions.
14 changes: 10 additions & 4 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down
13 changes: 13 additions & 0 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var http = require('http'),
https = require('https'),
socketio = require('socket.io'),
url = require('url'),
jade = require('jade'),
Expand Down Expand Up @@ -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;
});
});
};

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/build-manager-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
8 changes: 5 additions & 3 deletions tests/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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 = {
Expand Down
37 changes: 13 additions & 24 deletions tests/travis.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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';

Expand All @@ -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';

Expand Down

0 comments on commit 4c89aff

Please sign in to comment.