CVE-2018-3778 (JavaScript)

Improper authorization in aedes version <0.35.0 will publish a LWT in a channel when a client is not authorized.

Weakness:
CWE-285: name: Improper Authorization
description: The product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
CWE-250: name: Execution with Unnecessary Privileges
description: The product performs an operation at a privilege level that is higher than the minimum level required, which creates new weaknesses or amplifies the consequences of other weaknesses.
CWE-269: name: Improper Privilege Management
description: The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.

Repository: https://github.com/moscajs/aedes

Vulnerable code:
file_path: lib/client.js
snippet:   function finish () {
    if (!that.disconnected && that.will) {
      that.broker.publish(that.will, that, function (err) {
        if (!err) {
          that.broker.persistence.delWill({
            id: that.id,
            brokerId: that.broker.id
          }, nop)
        }
      })
      that.will = null // this function might be called twice
    }

    conn.removeAllListeners('error')
    conn.on('error', nop)

    if (that.broker.clients[that.id]) {
      that.broker.unregisterClient(that)
    }

    // hack to clean up the write callbacks
    // supports streams2 & streams3, so node 0.10, 0.11, and iojs
    var state = that.conn._writableState
    var list = (state.getBuffer && state.getBuffer()) || state.buffer
    list.forEach(drainRequest)

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    if (conn.destroySoon) {
      conn.destroySoon()
    } if (conn.destroy) {
      conn.destroy()
    } else {
      conn.end()
    }

    if (typeof done === 'function') {
      done()
    }
  }
}

Fixed code:
file_path: lib/client.js
snippet:   function finish () {
    if (!that.disconnected && that.will) {
      that.broker.authorizePublish(that, that.will, function (err) {
        if (!err) {
          that.broker.publish(that.will, that, done)
        } else {
          done()
        }

        function done (err) {
          if (!err) {
            that.broker.persistence.delWill({
              id: that.id,
              brokerId: that.broker.id
            }, nop)
          }
        }
      })
      that.will = null // this function might be called twice
    }

    conn.removeAllListeners('error')
    conn.on('error', nop)

    if (that.broker.clients[that.id]) {
      that.broker.unregisterClient(that)
    }

    // hack to clean up the write callbacks
    // supports streams2 & streams3, so node 0.10, 0.11, and iojs
    var state = that.conn._writableState
    var list = (state.getBuffer && state.getBuffer()) || state.buffer
    list.forEach(drainRequest)

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    if (conn.destroySoon) {
      conn.destroySoon()
    } if (conn.destroy) {
      conn.destroy()
    } else {
      conn.end()
    }

    if (typeof done === 'function') {
      done()
    }
  }
}