diff --git a/README.md b/README.md index 8837c18..3d30a55 100644 --- a/README.md +++ b/README.md @@ -63,14 +63,14 @@ Create a webhook on your git source (On GitHub, in the `Settings/Webhooks` part Here are the steps for Github, if you use another platform adapt it your way (header format on the config) : * Create a password or random secret -* Calculate it's SHA1 * Edit your configuration to add webhook info ```json { ... "webhook": { - "secret_value": "sha1=", - "secret_header": "X-Hub-Signature" + "endpoint": "/webhook", + "secret": "sha1=", + "signature_header": "X-Hub-Signature" }, ... } diff --git a/package-lock.json b/package-lock.json index 0862e4d..4328a5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2704,6 +2704,11 @@ "which": "^1.2.9" } }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, "cssom": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", diff --git a/package.json b/package.json index e309b2a..9acd811 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "description": "A static blog using Markdown pulled from your git repository.", "main": "src/server.js", "dependencies": { + "body-parser": "^1.19.0", + "crypto": "^1.0.1", "ejs": "^2.6.2", "express": "^4.17.1", "ncp": "^2.0.0", diff --git a/src/app.js b/src/app.js index 1ce5022..f5f1ef2 100644 --- a/src/app.js +++ b/src/app.js @@ -3,6 +3,9 @@ const app = express(); const fs = require('fs'); const path = require('path'); const Rss = require('rss'); +const bodyParser = require('body-parser'); +const crypto = require('crypto'); +app.use(bodyParser.json()); /** * Terminal colors and symbols to display status messages @@ -127,12 +130,20 @@ module.exports = (config) => { //webhook endpoint app.post(config['webhook']['endpoint'], (req, res) => { if (config['modules']['webhook']) { - if (config['webhook']['secret_header'] && req.get(config['webhook']['secret_header']) !== config['webhook']['secret_value']) { - res.sendStatus(403); - } else { - res.sendStatus(200); - //TODO reload + if (config['webhook']['signature_header'] && config['webhook']['secret']) { + const payload = JSON.stringify(req.body); + if (!payload) { + return res.sendStatus(403); + } + const hmac = crypto.createHmac('sha1', config['webhook']['secret']); + const digest = 'sha1=' + hmac.update(payload).digest('hex'); + const checksum = req.headers[config['webhook']['signature_header']]; + if (!checksum || !digest || checksum !== digest) { + return res.sendStatus(403); + } } + res.sendStatus(200); + //TODO reload } else { res.sendStatus(400); } diff --git a/src/config.default.json b/src/config.default.json index 9e30297..f138c22 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -30,8 +30,8 @@ }, "webhook": { "endpoint": "/webhook", - "secret_value": "", - "secret_header": "" + "secret": "", + "signature_header": "" }, "showdown": { "parseImgDimensions": true, diff --git a/test/app.test.js b/test/app.test.js index 3174ce5..1ca5f06 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -151,18 +151,29 @@ describe('Test webhook', () => { done(); }); }); + test('403 no payload', (done) => { + config['webhook']['signature_header'] = 'testheader'; + config['webhook']['secret'] = 'testvalue'; + request(app).post('/webhooktest').then((response) => { + expect(response.statusCode).toBe(403); + done(); + }); + }); test('403 wrong secret', (done) => { - config['webhook']['secret_header'] = 'testheader'; - config['webhook']['secret_value'] = 'testvalue'; - request(app).post('/webhooktest').set('testheader','testvalue2').then((response) => { + config['webhook']['signature_header'] = 'testheader'; + config['webhook']['secret'] = 'testvalue'; + request(app).post('/webhooktest').set('testheader', 'sha1=invalid').then((response) => { expect(response.statusCode).toBe(403); done(); }); }); test('200 valid secret', (done) => { - config['webhook']['secret_header'] = 'testheader'; - config['webhook']['secret_value'] = 'testvalue'; - request(app).post('/webhooktest').set('testheader','testvalue').then((response) => { + config['webhook']['signature_header'] = 'testheader'; + config['webhook']['secret'] = 'testvalue'; + request(app).post('/webhooktest') + .send({}) + .set('testheader', 'sha1=d924d5bd4b36faf9d572844ac9c12a09ce3e7134') + .then((response) => { expect(response.statusCode).toBe(200); //TODO test reload done();