Package: release.debian.org Severity: normal Tags: bullseye User: release.debian....@packages.debian.org Usertags: pu
[ Reason ] node-eventsource is vulnerable to sensible headers exposure (CVE-2022-1650) [ Impact ] Medium vulnerability [ Tests ] Sadly tests are not launched in Bullseye. Tested locally, patch works [ Risks ] Low risk: patch is trivial [ Checklist ] [X] *all* changes are documented in the d/changelog [X] I reviewed all changes and I approve them [X] attach debdiff against the package in (old)stable [X] the issue is verified as fixed in unstable [ Changes ] Drop Cookie/Authorization headers when following redirections Cheers, Yadd
diff --git a/debian/changelog b/debian/changelog index 3404ca5..fd29de9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +node-eventsource (1.0.7-1+deb11u1) bullseye; urgency=medium + + * Team upload + * Strip sensitive headers on redirect to different origin + (Closes: CVE-2022-1650) + + -- Yadd <y...@debian.org> Fri, 13 May 2022 11:13:54 +0200 + node-eventsource (1.0.7-1) unstable; urgency=medium * Team upload diff --git a/debian/patches/CVE-2022-1650.patch b/debian/patches/CVE-2022-1650.patch new file mode 100644 index 0000000..218586e --- /dev/null +++ b/debian/patches/CVE-2022-1650.patch @@ -0,0 +1,159 @@ +Description: Strip sensitive headers on redirect to different origin +Author: Espen Hovlandsdal <es...@hovlandsdal.com> +Origin: upstream, https://github.com/EventSource/eventsource/commit/f9f64165 +Bug: https://huntr.dev/bounties/dc9e467f-be5d-4945-867d-1044d27e9b8e/ +Forwarded: not-needed +Reviewed-By: <name and email of someone who approved the patch> +Last-Update: 2022-05-13 + +--- a/lib/eventsource.js ++++ b/lib/eventsource.js +@@ -31,6 +31,8 @@ + **/ + function EventSource (url, eventSourceInitDict) { + var readyState = EventSource.CONNECTING ++ var headers = eventSourceInitDict && eventSourceInitDict.headers ++ var hasNewOrigin = false + Object.defineProperty(this, 'readyState', { + get: function () { + return readyState +@@ -51,11 +53,12 @@ + readyState = EventSource.CONNECTING + _emit('error', new Event('error', {message: message})) + +- // The url may have been changed by a temporary +- // redirect. If that's the case, revert it now. ++ // The url may have been changed by a temporary redirect. If that's the case, ++ // revert it now, and flag that we are no longer pointing to a new origin + if (reconnectUrl) { + url = reconnectUrl + reconnectUrl = null ++ hasNewOrigin = false + } + setTimeout(function () { + if (readyState !== EventSource.CONNECTING) { +@@ -67,9 +70,9 @@ + + var req + var lastEventId = '' +- if (eventSourceInitDict && eventSourceInitDict.headers && eventSourceInitDict.headers['Last-Event-ID']) { +- lastEventId = eventSourceInitDict.headers['Last-Event-ID'] +- delete eventSourceInitDict.headers['Last-Event-ID'] ++ if (headers && headers['Last-Event-ID']) { ++ lastEventId = headers['Last-Event-ID'] ++ delete headers['Last-Event-ID'] + } + + var discardTrailingNewline = false +@@ -83,9 +86,10 @@ + var isSecure = options.protocol === 'https:' + options.headers = { 'Cache-Control': 'no-cache', 'Accept': 'text/event-stream' } + if (lastEventId) options.headers['Last-Event-ID'] = lastEventId +- if (eventSourceInitDict && eventSourceInitDict.headers) { +- for (var i in eventSourceInitDict.headers) { +- var header = eventSourceInitDict.headers[i] ++ if (headers) { ++ var reqHeaders = hasNewOrigin ? removeUnsafeHeaders(headers) : headers ++ for (var i in reqHeaders) { ++ var header = reqHeaders[i] + if (header) { + options.headers[i] = header + } +@@ -139,14 +143,18 @@ + } + + // Handle HTTP redirects +- if (res.statusCode === 301 || res.statusCode === 307) { +- if (!res.headers.location) { ++ if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) { ++ var location = res.headers.location ++ if (!location) { + // Server sent redirect response without Location header. + _emit('error', new Event('error', {status: res.statusCode, message: res.statusMessage})) + return + } ++ var prevOrigin = original(url) ++ var nextOrigin = original(location) ++ hasNewOrigin = prevOrigin !== nextOrigin + if (res.statusCode === 307) reconnectUrl = url +- url = res.headers.location ++ url = location + process.nextTick(connect) + return + } +@@ -428,3 +436,23 @@ + } + } + } ++ ++/** ++ * Returns a new object of headers that does not include any authorization and cookie headers ++ * ++ * @param {Object} headers An object of headers ({[headerName]: headerValue}) ++ * @return {Object} a new object of headers ++ * @api private ++ */ ++function removeUnsafeHeaders (headers) { ++ var safe = {} ++ for (var key in headers) { ++ if (/^(cookie|authorization)$/i.test(key)) { ++ continue ++ } ++ ++ safe[key] = headers[key] ++ } ++ ++ return safe ++} +--- a/test/eventsource_test.js ++++ b/test/eventsource_test.js +@@ -559,6 +559,49 @@ + }) + }) + ++ it('follows http ' + status + ' redirects, drops sensitive headers on origin change', function (done) { ++ var redirectSuffix = '/foobar' ++ var clientRequestedRedirectUrl = false ++ var receivedHeaders = {} ++ createServer(function (err, server) { ++ if (err) return done(err) ++ ++ var newServerUrl = server.url.replace('http://localhost', 'http://127.0.0.1') ++ ++ server.on('request', function (req, res) { ++ if (req.url === '/') { ++ res.writeHead(status, { ++ 'Connection': 'Close', ++ 'Location': newServerUrl + redirectSuffix ++ }) ++ res.end() ++ } else if (req.url === redirectSuffix) { ++ clientRequestedRedirectUrl = true ++ receivedHeaders = req.headers ++ res.writeHead(200, {'Content-Type': 'text/event-stream'}) ++ res.end() ++ } ++ }) ++ ++ var es = new EventSource(server.url, { ++ headers: { ++ keep: 'me', ++ authorization: 'Bearer someToken', ++ cookie: 'some-cookie=yep' ++ } ++ }) ++ ++ es.onopen = function () { ++ assert.ok(clientRequestedRedirectUrl) ++ assert.equal(newServerUrl + redirectSuffix, es.url) ++ assert.equal(receivedHeaders.keep, 'me', 'safe header no longer present') ++ assert.equal(typeof receivedHeaders.authorization, 'undefined', 'authorization header still present') ++ assert.equal(typeof receivedHeaders.cookie, 'undefined', 'cookie header still present') ++ server.close(done) ++ } ++ }) ++ }) ++ + it('causes error event when response is ' + status + ' with missing location', function (done) { + createServer(function (err, server) { + if (err) return done(err) diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..eb5b4ee --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +CVE-2022-1650.patch