Skip to content

Commit baf85dc

Browse files
committed
Merge branch 'feature/events-option' of https://github.com/michaelrhodes/github-webhook-handler into michaelrhodes-feature/events-option
2 parents c59c4ec + a0d47e6 commit baf85dc

File tree

4 files changed

+73
-0
lines changed

4 files changed

+73
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ github-webhook-handler exports a single function, use this function to *create*
4646

4747
* `"path"`: the complete case sensitive path/route to match when looking at `req.url` for incoming requests. Any request not matching this path will cause the callback function to the handler to be called (sometimes called the `next` handler).
4848
* `"secret"`: this is a hash key used for creating the SHA-1 HMAC signature of the JSON blob sent by GitHub. You should register the same secret key with GitHub. Any request not delivering a `X-Hub-Signature` that matches the signature generated using this key against the blob will be rejected and cause an `'error'` event (also the callback will be called with an `Error` object).
49+
* `"events"`: an optional array of whitelisted event types (see: *events.json*). If defined, any incoming request whose `X-Github-Event` can't be found in the whitelist will be rejected. If only a single event type is acceptable, this option can also be a string.
4950

5051
The resulting **handler** function acts like a common "middleware" handler that you can insert into a processing chain. It takes `request`, `response`, and `callback` arguments. The `callback` is not called if the request is successfully handled, otherwise it is called either with an `Error` or no arguments.
5152

github-webhook-handler.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ function create (options) {
2020
if (typeof options.secret != 'string')
2121
throw new TypeError('must provide a \'secret\' option')
2222

23+
var events
24+
25+
if (typeof options.events == 'string' && options.events != '*')
26+
events = [ options.events ]
27+
28+
else if (Array.isArray(options.events) && options.events.indexOf('*') == -1)
29+
events = options.events
30+
2331
// make it an EventEmitter, sort of
2432
handler.__proto__ = EventEmitter.prototype
2533
EventEmitter.call(handler)
@@ -54,6 +62,9 @@ function create (options) {
5462
if (!id)
5563
return hasError('No X-Github-Delivery found on request')
5664

65+
if (events && events.indexOf(event) == -1)
66+
return hasError('X-Github-Event is not acceptable')
67+
5768
req.pipe(bl(function (err, data) {
5869
if (err) {
5970
return hasError(err.message)

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"buffer-equal-constant-time": "~1.0.1"
2222
},
2323
"devDependencies": {
24+
"run-series": "~1.0.2",
2425
"tape": "~4.6.0",
2526
"through2": "~2.0.1"
2627
}

test.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const test = require('tape')
22
, crypto = require('crypto')
33
, handler = require('./')
44
, through2 = require('through2')
5+
, series = require('run-series')
56

67

78
function signBlob (key, blob) {
@@ -96,6 +97,65 @@ test('handler accepts valid urls', function (t) {
9697
})
9798

9899

100+
test('handler can reject events', function (t) {
101+
var acceptableEvents = {
102+
'undefined' : undefined
103+
, 'a string equal to the event' : 'bogus'
104+
, 'a string equal to *' : '*'
105+
, 'an array containing the event' : ['bogus']
106+
, 'an array containing *' : ['not-bogus', '*']
107+
}
108+
, unacceptableEvents = {
109+
'a string not equal to the event or *' : 'not-bogus'
110+
, 'an array not containing the event or *' : ['not-bogus']
111+
}
112+
, acceptable = Object.keys(acceptableEvents)
113+
, unacceptable = Object.keys(unacceptableEvents)
114+
, acceptableTests = acceptable.map(function (events) {
115+
return acceptableReq.bind(null, events)
116+
})
117+
, unacceptableTests = unacceptable.map(function (events) {
118+
return unacceptableReq.bind(null, events)
119+
})
120+
121+
t.plan(acceptable.length + unacceptable.length)
122+
series(acceptableTests.concat(unacceptableTests))
123+
124+
function acceptableReq (events, callback) {
125+
var h = handler({
126+
path : '/some/url'
127+
, secret : 'bogus'
128+
, events : acceptableEvents[events]
129+
})
130+
131+
h(mkReq('/some/url'), mkRes(), function (err) {
132+
t.error(err)
133+
t.fail(false, 'should not call')
134+
})
135+
136+
setTimeout(function () {
137+
t.ok(true, 'accepted because options.events was ' + events)
138+
callback()
139+
})
140+
}
141+
142+
function unacceptableReq (events, callback) {
143+
var h = handler({
144+
path : '/some/url'
145+
, secret : 'bogus'
146+
, events : unacceptableEvents[events]
147+
})
148+
149+
h.on('error', function () {})
150+
151+
h(mkReq('/some/url'), mkRes(), function (err) {
152+
t.ok(err, 'rejected because options.events was ' + events)
153+
callback()
154+
})
155+
}
156+
})
157+
158+
99159
// because we don't inherit in a traditional way
100160
test('handler is an EventEmitter', function (t) {
101161
t.plan(5)

0 commit comments

Comments
 (0)