From 859a8bb317bb3ecdb5fb31129fa11d033917fa46 Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Sun, 16 Apr 2023 18:33:25 -0400 Subject: [PATCH] cleanup for readability --- lib/jambonz/webhook-response.js | 38 +++++++++------------------------ lib/jambonz/ws-router.js | 36 +++++++++++++------------------ 2 files changed, 25 insertions(+), 49 deletions(-) diff --git a/lib/jambonz/webhook-response.js b/lib/jambonz/webhook-response.js index 3f4df5b..dd15fa5 100644 --- a/lib/jambonz/webhook-response.js +++ b/lib/jambonz/webhook-response.js @@ -18,19 +18,7 @@ function secureCompare(a, b) { return false; } - // use crypto.timingSafeEqual if available (since Node.js v6.6.0), - // otherwise use our own scmp-internal function. - if (crypto.timingSafeEqual) { - return crypto.timingSafeEqual(a, b); - } - - const len = a.length; - let result = 0; - - for (let i = 0; i < len; ++i) { - result |= a[i] ^ b[i]; - } - return result === 0; + return crypto.timingSafeEqual(a, b); } function parseHeader(header, scheme) { @@ -38,19 +26,15 @@ function parseHeader(header, scheme) { return null; } + const keyActions = { + t: (accum, value) => (accum.timestamp = value, accum), + [scheme]: (accum, value) => (accum.signatures.push(value), accum) + }; + return header.split(',').reduce( (accum, item) => { - const kv = item.split('='); - - if (kv[0] === 't') { - accum.timestamp = kv[1]; - } - - if (kv[0] === scheme) { - accum.signatures.push(kv[1]); - } - - return accum; + const [key, value] = item.split('='); + return keyActions[key] ? keyActions[key](accum, value) : accum; }, { timestamp: -1, @@ -93,7 +77,7 @@ class WebhookResponse { static verifyJambonzSignature(secret) { return (req, res, next) => { const header = req.get('Jambonz-Signature'); - if (!header) throw new Error(`missing Jambonz-Signature in ${req.url}`); + if (!header) throw new Error(`Missing Jambonz-Signature in ${req.url}`); const details = parseHeader(header, EXPECTED_SCHEME); if (!details || details.timestamp === -1) { throw new Error('unable to extract timestamp and signatures from header'); @@ -105,9 +89,7 @@ class WebhookResponse { let signatureFound = false; for (const secret of secrets) { const expectedSignature = computeSignature(req.body, details.timestamp, secret); - signatureFound = details.signatures.filter( - secureCompare.bind(null, expectedSignature) - ).length > 0; + signatureFound = details.signatures.some((sig) => secureCompare(expectedSignature, sig)); if (signatureFound) break; } if (!signatureFound) { diff --git a/lib/jambonz/ws-router.js b/lib/jambonz/ws-router.js index 0497648..1b3fa12 100644 --- a/lib/jambonz/ws-router.js +++ b/lib/jambonz/ws-router.js @@ -1,6 +1,19 @@ const assert = require('assert'); const parseurl = require('parseurl'); +function matchesPath(match, path) { + if ('*' === match) return true; + + const urlChunks = path.split('/').filter((c) => c.length); + const matchChunks = match.split('/').filter((c) => c.length); + + if (urlChunks.length >= matchChunks.length) { + return matchChunks.every((chunk, idx) => chunk === urlChunks[idx]); + } + + return false; +} + class WsRouter { constructor() { this.routes = []; @@ -13,6 +26,7 @@ class WsRouter { } assert.ok(typeof callback === 'function' || callback instanceof WsRouter, 'WsRouter.use - callback must be a function or a WsRouter instance'); + assert.ok(typeof match === 'string', 'WsRouter.use - match must be a string'); this.routes.push({match, callback}); } @@ -22,27 +36,7 @@ class WsRouter { const path = parsed.pathname; const route = this.routes.find(({match}) => { - /* wildcard */ - if ('*' === match) return true; - - /* try matching by path */ - const urlChunks = path.split('/').filter((c) => c.length); - const matchChunks = match.split('/').filter((c) => c.length); - if (urlChunks.length >= matchChunks.length) { - let idx = 0; - do { - if (urlChunks[idx] !== matchChunks[idx]) break; - idx++; - } while (idx < matchChunks.length); - if (idx > 0) { - req.url = urlChunks.slice(idx).join('/') + '/' + (parsed.search || ''); - return true; - } - } - - /* TODO: try matching by param */ - - /* TODO : try matching by query args */ + return matchesPath(match, path); }); if (!route) return false;