--- Begin Message ---
- To: Debian Bug Tracking System <submit@bugs.debian.org>
- Subject: bullseye-pu: package node-eventsource/1.0.7-1+deb11u1
- From: Yadd <yadd@debian.org>
- Date: Fri, 13 May 2022 11:30:03 +0200
- Message-id: <165243420336.2070143.13199748506466021443.reportbug@debian007.xnr.fr>
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian.org@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 <yadd@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 <espen@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
--- End Message ---