[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1010924: bullseye-pu: package node-eventsource/1.0.7-1+deb11u1



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

Reply to: