Comparing version 0.0.9 to 1.0.0
45
index.js
@@ -12,3 +12,6 @@ // Description: | ||
const crypto = require('crypto'); | ||
const defaultRoom = process.env.HUBOT_NPM_ROOM; | ||
const secret = process.env.HUBOT_NPM_SECRET; | ||
@@ -45,3 +48,3 @@ const getMessage = (hook) => { | ||
return `${hook.name}@${hook.change.deprecated} deprecated – ${url}`; | ||
case 'package:undeprecated': | ||
@@ -52,15 +55,45 @@ return `${hook.name}@${hook.change.deprecated} undeprecated – ${url}`; | ||
const sign = (hook) => { | ||
return crypto.createHmac('sha256', secret).update(JSON.stringify(hook)).digest('hex'); | ||
}; | ||
const prefix = (room) => { | ||
if (room && room.indexOf('#') !== 0) { | ||
return `#${room}`; | ||
} else { | ||
return room; | ||
} | ||
}; | ||
module.exports = (robot) => { | ||
if (!secret) { | ||
robot.logger.error('npm: NPM_HUBOT_SECRET is not set https://github.com/robinjmurphy/hubot-npm#installation'); | ||
} | ||
robot.router.post('/hubot/npm', (req, res) => { | ||
const hook = req.body; | ||
let room = req.query.room || defaultRoom; | ||
const signature = req.headers['x-npm-signature']; | ||
if (!room) { | ||
return res.status(400).end('Missing ?room parameter'); | ||
if (!signature) { | ||
robot.logger.error('npm: missing x-npm-signature header'); | ||
res.status(400).end('missing x-npm-signature header'); | ||
return; | ||
} | ||
if (room && !/^#/.test(room)) { | ||
room = `#${room}`; | ||
const expected = sign(hook); | ||
if (signature !== `sha256=${expected}`) { | ||
robot.logger.error('npm: invalid payload signature in x-npm-signature header'); | ||
res.status(400).end('invalid payload signature in x-npm-signature header'); | ||
return; | ||
} | ||
let room = prefix(req.query.room || defaultRoom); | ||
if (!room) { | ||
robot.logger.error('npm: ?room parameter missing from hook https://github.com/robinjmurphy/hubot-npm#usage'); | ||
res.status(400).end('missing ?room parameter'); | ||
return; | ||
} | ||
const message = getMessage(hook); | ||
@@ -67,0 +100,0 @@ |
{ | ||
"name": "hubot-npm", | ||
"version": "0.0.9", | ||
"version": "1.0.0", | ||
"description": "Hubot script for npm notifications", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
'use strict'; | ||
process.env.HUBOT_NPM_SECRET = 'secret'; | ||
const script = require('..'); | ||
@@ -9,5 +11,6 @@ const request = require('supertest'); | ||
const assert = require('assert'); | ||
const crypto = require('crypto'); | ||
const sandbox = sinon.sandbox.create(); | ||
const hooks = [ | ||
const tests = [ | ||
{ | ||
@@ -55,2 +58,6 @@ event: 'package:publish', | ||
const sign = (hook) => { | ||
return crypto.createHmac('sha256', 'secret').update(JSON.stringify(hook)).digest('hex'); | ||
}; | ||
describe('hubot-npm', () => { | ||
@@ -66,3 +73,6 @@ let robot; | ||
router, | ||
send: sandbox.stub() | ||
send: sandbox.stub(), | ||
logger: { | ||
error: sandbox.stub() | ||
} | ||
}; | ||
@@ -76,8 +86,11 @@ | ||
}); | ||
hooks.forEach((hook) => { | ||
it(`supports the ${hook.event} hook`, (done) => { | ||
tests.forEach((test) => { | ||
it(`supports the ${test.event} hook`, (done) => { | ||
const hook = require(`./hooks/${test.event}`); | ||
request(robot.router) | ||
.post('/hubot/npm?room=test-room') | ||
.send(require(`./hooks/${hook.event}`)) | ||
.send(hook) | ||
.set('x-npm-signature', `sha256=${sign(hook)}`) | ||
.expect(200) | ||
@@ -91,3 +104,3 @@ .end((err) => { | ||
}), | ||
hook.expected | ||
test.expected | ||
); | ||
@@ -100,7 +113,47 @@ done(); | ||
it('returns a 400 when the room is missing', (done) => { | ||
const hook = require('./hooks/package:publish'); | ||
request(robot.router) | ||
.post('/hubot/npm') | ||
.send(hooks.publish) | ||
.expect(400, done); | ||
.set('x-npm-signature', `sha256=${sign(hook)}`) | ||
.send(hook) | ||
.expect(400) | ||
.expect('missing ?room parameter', done); | ||
}); | ||
it('logs an error when the room is missing', (done) => { | ||
const hook = require('./hooks/package:publish'); | ||
request(robot.router) | ||
.post('/hubot/npm') | ||
.set('x-npm-signature', `sha256=${sign(hook)}`) | ||
.send(hook) | ||
.expect(400) | ||
.end((err) => { | ||
assert.ifError(err); | ||
sinon.assert.called(robot.logger.error); | ||
done(); | ||
}); | ||
}); | ||
it('returns a 400 when the x-npm-signature header is missing', (done) => { | ||
const hook = require('./hooks/package:publish'); | ||
request(robot.router) | ||
.post('/hubot/npm?room=test-room') | ||
.send(hook) | ||
.expect(400) | ||
.expect('missing x-npm-signature header', done); | ||
}); | ||
it('returns a 400 when the secret does not match', (done) => { | ||
const hook = require('./hooks/package:publish'); | ||
request(robot.router) | ||
.post('/hubot/npm?room=test-room') | ||
.set('x-npm-signature', 'sha256=not-valid') | ||
.send(hook) | ||
.expect(400) | ||
.expect('invalid payload signature in x-npm-signature header', done); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
11818
356
0
4