Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mqtt-packet

Package Overview
Dependencies
Maintainers
2
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mqtt-packet - npm Package Compare versions

Comparing version 6.5.0 to 6.6.0

4

constants.js

@@ -40,4 +40,4 @@ /* Protocol - protocol constants */

/* Length */
protocol.LENGTH_MASK = 0x7F
protocol.LENGTH_FIN_MASK = 0x80
protocol.VARBYTEINT_MASK = 0x7F
protocol.VARBYTEINT_FIN_MASK = 0x80

@@ -44,0 +44,0 @@ /* Connack */

const max = 65536
const cache = {}
// in node 6 Buffer.subarray returns a Uint8Array instead of a Buffer
// later versions return a Buffer
// alternative is Buffer.slice but that creates a new buffer
// creating new buffers takes time
// SubOk is only false on node < 8
const SubOk = Buffer.isBuffer(Buffer.from([1, 2]).subarray(0, 1))
function generateBuffer (i) {

@@ -18,21 +25,7 @@ const buffer = Buffer.allocUnsafe(2)

/**
* calcVariableByteIntLength - calculate the variable byte integer
* length field
*
* @api private
*/
function calcVariableByteIntLength (length) {
if (length >= 0 && length < 128) return 1
else if (length >= 128 && length < 16384) return 2
else if (length >= 16384 && length < 2097152) return 3
else if (length >= 2097152 && length < 268435456) return 4
else return 0
}
function genBufVariableByteInt (num) {
const maxLength = 4 // max 4 bytes
let digit = 0
let pos = 0
const length = calcVariableByteIntLength(num)
const buffer = Buffer.allocUnsafe(length)
const buffer = Buffer.allocUnsafe(maxLength)

@@ -45,8 +38,9 @@ do {

buffer.writeUInt8(digit, pos++)
} while (num > 0)
} while (num > 0 && pos < maxLength)
return {
data: buffer,
length
if (num > 0) {
pos = 0
}
return SubOk ? buffer.subarray(0, pos) : buffer.slice(0, pos)
}

@@ -53,0 +47,0 @@

{
"name": "mqtt-packet",
"version": "6.5.0",
"version": "6.6.0",
"description": "Parse and generate MQTT packets like a breeze",

@@ -5,0 +5,0 @@ "main": "mqtt.js",

@@ -75,2 +75,4 @@ const bl = require('bl')

this._list.consume(result.bytes)
} else {
this._emitError(new Error('Invalid length'))
}

@@ -546,4 +548,4 @@ debug('_parseLength %d', result.value)

let mul = 1
let length = 0
let result = true
let value = 0
let result = false
let current

@@ -554,8 +556,10 @@ const padding = this._pos ? this._pos : 0

current = this._list.readUInt8(padding + bytes++)
length += mul * (current & constants.LENGTH_MASK)
value += mul * (current & constants.VARBYTEINT_MASK)
mul *= 0x80
if ((current & constants.LENGTH_FIN_MASK) === 0) break
if ((current & constants.VARBYTEINT_FIN_MASK) === 0) {
result = true
break
}
if (this._list.length <= bytes) {
result = false
break

@@ -572,4 +576,4 @@ }

bytes,
value: length
} : length
value
} : value
: false

@@ -576,0 +580,0 @@

@@ -72,2 +72,11 @@ const test = require('tape')

// the API allows to pass strings as buffers to writeToStream and generate
// parsing them back will result in a string so only generate and compare to buffer
function testGenerateOnly (name, object, buffer, opts) {
test(name, t => {
t.equal(mqtt.generate(object, opts).toString('hex'), buffer.toString('hex'))
t.end()
})
}
function testParseError (expected, fixture, opts) {

@@ -88,7 +97,8 @@ test(expected, t => {

parser.parse(fixture)
t.end()
})
}
function testGenerateError (expected, fixture, opts) {
test(expected, t => {
function testGenerateError (expected, fixture, opts, name) {
test(name || expected, t => {
t.plan(1)

@@ -101,5 +111,15 @@

}
t.end()
})
}
function testGenerateErrorMultipleCmds (cmds, expected, fixture, opts) {
cmds.forEach(cmd => {
const obj = Object.assign({}, fixture)
obj.cmd = cmd
testGenerateError(expected, obj, opts, `${expected} on ${cmd}`)
}
)
}
function testParseGenerateDefaults (name, object, buffer, opts) {

@@ -120,2 +140,3 @@ test(`${name} parse`, t => {

t.equal(parser.parse(fixture), 0, 'remaining bytes')
t.end()
})

@@ -141,5 +162,15 @@

t.false(result, 'result should be false')
t.end()
})
}
test('cacheNumbers get/set/unset', t => {
t.true(mqtt.writeToStream.cacheNumbers, 'initial state of cacheNumbers is enabled')
mqtt.writeToStream.cacheNumbers = false
t.false(mqtt.writeToStream.cacheNumbers, 'cacheNumbers can be disabled')
mqtt.writeToStream.cacheNumbers = true
t.true(mqtt.writeToStream.cacheNumbers, 'cacheNumbers can be enabled')
t.end()
})
test('disabled numbers cache', t => {

@@ -179,2 +210,9 @@ const stream = WS()

testGenerateError('Unknown command', {})
testParseError('Not supported', Buffer.from([0, 1, 0]), {})
testParseError('Invalid length', Buffer.from(
[16, 255, 255, 255, 255]
), {})
testParseGenerate('minimal connect', {

@@ -202,3 +240,3 @@ cmd: 'connect',

testParseGenerate('connect MQTT 5.0', {
testGenerateOnly('minimal connect with clientId as Buffer', {
cmd: 'connect',

@@ -208,2 +246,24 @@ retain: false,

dup: false,
length: 18,
protocolId: 'MQIsdp',
protocolVersion: 3,
clean: false,
keepalive: 30,
clientId: Buffer.from('test')
}, Buffer.from([
16, 18, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
0, // Connect flags
0, 30, // Keepalive
0, 4, // Client ID length
116, 101, 115, 116 // Client ID
]))
testParseGenerate('connect MQTT 5', {
cmd: 'connect',
retain: false,
qos: 0,
dup: false,
length: 125,

@@ -278,3 +338,3 @@ protocolId: 'MQTT',

testParseGenerate('connect MQTT 5.0 with will properties but w/o will payload', {
testParseGenerate('connect MQTT 5 with will properties but with empty will payload', {
cmd: 'connect',

@@ -352,3 +412,3 @@ retain: false,

testParseGenerate('connect MQTT 5.0 w/o will properties', {
testParseGenerate('connect MQTT 5 w/o will properties', {
cmd: 'connect',

@@ -687,46 +747,40 @@ retain: false,

test('connect all strings generate', t => {
const message = {
cmd: 'connect',
retain: false,
qos: 0,
dup: false,
length: 54,
protocolId: 'MQIsdp',
protocolVersion: 3,
will: {
retain: true,
qos: 2,
topic: 'topic',
payload: 'payload'
},
clean: true,
keepalive: 30,
clientId: 'test',
username: 'username',
password: 'password'
}
const expected = Buffer.from([
16, 54, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246, // Connect flags
0, 30, // Keepalive
0, 4, // Client ID length
116, 101, 115, 116, // Client ID
0, 5, // Will topic length
116, 111, 112, 105, 99, // Will topic
0, 7, // Will payload length
112, 97, 121, 108, 111, 97, 100, // Will payload
0, 8, // Username length
117, 115, 101, 114, 110, 97, 109, 101, // Username
0, 8, // Password length
112, 97, 115, 115, 119, 111, 114, 100 // Password
])
testGenerateOnly('connect all strings generate', {
cmd: 'connect',
retain: false,
qos: 0,
dup: false,
length: 54,
protocolId: 'MQIsdp',
protocolVersion: 3,
will: {
retain: true,
qos: 2,
topic: 'topic',
payload: 'payload'
},
clean: true,
keepalive: 30,
clientId: 'test',
username: 'username',
password: 'password'
}, Buffer.from([
16, 54, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246, // Connect flags
0, 30, // Keepalive
0, 4, // Client ID length
116, 101, 115, 116, // Client ID
0, 5, // Will topic length
116, 111, 112, 105, 99, // Will topic
0, 7, // Will payload length
112, 97, 121, 108, 111, 97, 100, // Will payload
0, 8, // Username length
117, 115, 101, 114, 110, 97, 109, 101, // Username
0, 8, // Password length
112, 97, 115, 115, 119, 111, 114, 100 // Password
]))
t.equal(mqtt.generate(message).toString('hex'), expected.toString('hex'))
t.end()
})
testParseError('Cannot parse protocolId', Buffer.from([

@@ -738,2 +792,88 @@ 16, 4,

// missing protocol version on connect
testParseError('Packet too short', Buffer.from([
16, 8, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112 // Protocol ID
]))
// missing keepalive on connect
testParseError('Packet too short', Buffer.from([
16, 10, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246 // Connect flags
]))
// missing clientid on connect
testParseError('Packet too short', Buffer.from([
16, 10, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246, // Connect flags
0, 30 // Keepalive
]))
// missing will topic on connect
testParseError('Cannot parse will topic', Buffer.from([
16, 16, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246, // Connect flags
0, 30, // Keepalive
0, 2, // Will topic length
0, 0 // Will topic
]))
// missing will payload on connect
testParseError('Cannot parse will payload', Buffer.from([
16, 23, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246, // Connect flags
0, 30, // Keepalive
0, 5, // Will topic length
116, 111, 112, 105, 99, // Will topic
0, 2, // Will payload length
0, 0 // Will payload
]))
// missing username on connect
testParseError('Cannot parse username', Buffer.from([
16, 32, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246, // Connect flags
0, 30, // Keepalive
0, 5, // Will topic length
116, 111, 112, 105, 99, // Will topic
0, 7, // Will payload length
112, 97, 121, 108, 111, 97, 100, // Will payload
0, 2, // Username length
0, 0 // Username
]))
// missing password on connect
testParseError('Cannot parse password', Buffer.from([
16, 42, // Header
0, 6, // Protocol ID length
77, 81, 73, 115, 100, 112, // Protocol ID
3, // Protocol version
246, // Connect flags
0, 30, // Keepalive
0, 5, // Will topic length
116, 111, 112, 105, 99, // Will topic
0, 7, // Will payload length
112, 97, 121, 108, 111, 97, 100, // Will payload
0, 8, // Username length
117, 115, 101, 114, 110, 97, 109, 101, // Username
0, 2, // Password length
0, 0 // Password
]))
testParseGenerate('connack with return code 0', {

@@ -751,3 +891,3 @@ cmd: 'connack',

testParseGenerate('connack MQTT5 with properties', {
testParseGenerate('connack MQTT 5 with properties', {
cmd: 'connack',

@@ -803,3 +943,3 @@ retain: false,

testParseGenerate('connack MQTT5 with properties and doubled user properties', {
testParseGenerate('connack MQTT 5 with properties and doubled user properties', {
cmd: 'connack',

@@ -880,2 +1020,12 @@ retain: false,

testGenerateError('Invalid return code', {
cmd: 'connack',
retain: false,
qos: 0,
dup: false,
length: 2,
sessionPresent: false,
returnCode: '5' // returncode must be a number
})
testParseGenerate('minimal publish', {

@@ -896,3 +1046,3 @@ cmd: 'publish',

testParseGenerate('publish MQTT5 properties', {
testParseGenerate('publish MQTT 5 properties', {
cmd: 'publish',

@@ -902,3 +1052,3 @@ retain: true,

dup: true,
length: 60,
length: 86,
topic: 'test',

@@ -914,3 +1064,3 @@ payload: Buffer.from('test'),

userProperties: {
test: 'test'
test: ['test', 'test', 'test']
},

@@ -921,7 +1071,7 @@ subscriptionIdentifier: 120,

}, Buffer.from([
61, 60, // Header
61, 86, // Header
0, 4, // Topic length
116, 101, 115, 116, // Topic (test)
0, 10, // Message ID
47, // properties length
73, // properties length
1, 1, // payloadFormatIndicator

@@ -933,2 +1083,4 @@ 2, 0, 0, 16, 225, // message expiry interval

38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
11, 120, // subscriptionIdentifier

@@ -939,3 +1091,3 @@ 3, 0, 4, 116, 101, 115, 116, // content type

testParseGenerate('publish MQTT5 with multiple same properties', {
testParseGenerate('publish MQTT 5 with multiple same properties', {
cmd: 'publish',

@@ -945,3 +1097,3 @@ retain: true,

dup: true,
length: 62,
length: 64,
topic: 'test',

@@ -959,11 +1111,11 @@ payload: Buffer.from('test'),

},
subscriptionIdentifier: [120, 121],
subscriptionIdentifier: [120, 121, 122],
contentType: 'test'
}
}, Buffer.from([
61, 62, // Header
61, 64, // Header
0, 4, // Topic length
116, 101, 115, 116, // Topic (test)
0, 10, // Message ID
49, // properties length
51, // properties length
1, 1, // payloadFormatIndicator

@@ -977,2 +1129,3 @@ 2, 0, 0, 16, 225, // message expiry interval

11, 121, // subscriptionIdentifier
11, 122, // subscriptionIdentifier
3, 0, 4, 116, 101, 115, 116, // content type

@@ -982,3 +1135,29 @@ 116, 101, 115, 116 // Payload (test)

;(() => {
testParseGenerate('publish MQTT 5 properties with 0-4 byte varbyte', {
cmd: 'publish',
retain: true,
qos: 2,
dup: true,
length: 27,
topic: 'test',
payload: Buffer.from('test'),
messageId: 10,
properties: {
payloadFormatIndicator: false,
subscriptionIdentifier: [128, 16384, 2097152] // this tests the varbyte handling
}
}, Buffer.from([
61, 27, // Header
0, 4, // Topic length
116, 101, 115, 116, // Topic (test)
0, 10, // Message ID
14, // properties length
1, 0, // payloadFormatIndicator
11, 128, 1, // subscriptionIdentifier
11, 128, 128, 1, // subscriptionIdentifier
11, 128, 128, 128, 1, // subscriptionIdentifier
116, 101, 115, 116 // Payload (test)
]), { protocolVersion: 5 })
; (() => {
const buffer = Buffer.alloc(2048)

@@ -1099,2 +1278,17 @@ testParseGenerate('2KB publish packet', {

testGenerateError('Invalid subscriptionIdentifier: 268435456', {
cmd: 'publish',
retain: true,
qos: 2,
dup: true,
length: 27,
topic: 'test',
payload: Buffer.from('test'),
messageId: 10,
properties: {
payloadFormatIndicator: false,
subscriptionIdentifier: 268435456
}
}, { protocolVersion: 5 }, 'MQTT 5.0 var byte integer >24 bits throws error')
testParseGenerate('puback', {

@@ -1112,3 +1306,3 @@ cmd: 'puback',

testParseGenerate('puback with reason and no MQTT5 properties', {
testParseGenerate('puback with reason and no MQTT 5 properties', {
cmd: 'puback',

@@ -1127,3 +1321,3 @@ retain: false,

testParseGenerate('puback MQTT5 properties', {
testParseGenerate('puback MQTT 5 properties', {
cmd: 'puback',

@@ -1163,3 +1357,3 @@ retain: false,

testParseGenerate('pubrec MQTT5 properties', {
testParseGenerate('pubrec MQTT 5 properties', {
cmd: 'pubrec',

@@ -1234,3 +1428,3 @@ retain: false,

testParseGenerate('pubcomp MQTT5 properties', {
testParseGenerate('pubcomp MQTT 5 properties', {
cmd: 'pubcomp',

@@ -1419,3 +1613,3 @@ retain: false,

testParseGenerate('suback MQTT5', {
testParseGenerate('suback MQTT 5', {
cmd: 'suback',

@@ -1463,2 +1657,22 @@ retain: false,

testGenerateError('Invalid unsubscriptions', {
cmd: 'unsubscribe',
retain: false,
qos: 1,
dup: true,
length: 5,
unsubscriptions: 5,
messageId: 7
}, {}, 'unsubscribe with unsubscriptions not an array')
testGenerateError('Invalid unsubscriptions', {
cmd: 'unsubscribe',
retain: false,
qos: 1,
dup: true,
length: 5,
unsubscriptions: [1, 2],
messageId: 7
}, {}, 'unsubscribe with unsubscriptions as an object')
testParseGenerate('unsubscribe MQTT 5', {

@@ -1606,4 +1820,2 @@ cmd: 'unsubscribe',

testGenerateError('Unknown command', {})
testGenerateError('Invalid protocolId', {

@@ -1630,2 +1842,23 @@ cmd: 'connect',

testGenerateError('Invalid protocol version', {
cmd: 'connect',
retain: false,
qos: 0,
dup: false,
length: 54,
protocolId: 'MQIsdp',
protocolVersion: 1,
will: {
retain: true,
qos: 2,
topic: 'topic',
payload: 'payload'
},
clean: true,
keepalive: 30,
clientId: 'test',
username: 'username',
password: 'password'
})
testGenerateError('clientId must be supplied before 3.1.1', {

@@ -1944,3 +2177,3 @@ cmd: 'connect',

// CONNECT Packets that contain an unsupported protocol version
// Flag (i.e. not `3` or `4`) should cause an error
// Flag (i.e. not `3` or `4` or '5') should cause an error
testParseError('Invalid protocol version', Buffer.from([

@@ -2015,2 +2248,68 @@ 16, 18,

testWriteToStreamError('Invalid command', {
cmd: 'invalid'
})
testWriteToStreamError('Invalid protocolId', {
cmd: 'connect',
protocolId: {}
})
test('userProperties null prototype', t => {
t.plan(3)
const packet = mqtt.generate({
cmd: 'connect',
retain: false,
qos: 0,
dup: false,
length: 125,
protocolId: 'MQTT',
protocolVersion: 5,
will: {
retain: true,
qos: 2,
properties: {
willDelayInterval: 1234,
payloadFormatIndicator: false,
messageExpiryInterval: 4321,
contentType: 'test',
responseTopic: 'topic',
correlationData: Buffer.from([1, 2, 3, 4]),
userProperties: {
test: 'test'
}
},
topic: 'topic',
payload: Buffer.from([4, 3, 2, 1])
},
clean: true,
keepalive: 30,
properties: {
sessionExpiryInterval: 1234,
receiveMaximum: 432,
maximumPacketSize: 100,
topicAliasMaximum: 456,
requestResponseInformation: true,
requestProblemInformation: true,
userProperties: {
test: 'test'
},
authenticationMethod: 'test',
authenticationData: Buffer.from([1, 2, 3, 4])
},
clientId: 'test'
})
const parser = mqtt.parser()
parser.on('packet', packet => {
t.equal(packet.cmd, 'connect')
t.equal(Object.getPrototypeOf(packet.properties.userProperties), null)
t.equal(Object.getPrototypeOf(packet.will.properties.userProperties), null)
})
parser.parse(packet)
})
test('stops parsing after first error', t => {

@@ -2089,72 +2388,15 @@ t.plan(4)

testWriteToStreamError('Invalid protocolId', {
cmd: 'connect',
protocolId: {}
})
testWriteToStreamError('Invalid topic', {
cmd: 'publish',
topic: {}
})
testWriteToStreamError('Invalid messageId', {
cmd: 'subscribe',
mid: {}
})
test('userProperties null prototype', t => {
t.plan(3)
const packet = mqtt.generate({
cmd: 'connect',
retain: false,
qos: 0,
dup: false,
length: 125,
protocolId: 'MQTT',
protocolVersion: 5,
will: {
retain: true,
qos: 2,
properties: {
willDelayInterval: 1234,
payloadFormatIndicator: false,
messageExpiryInterval: 4321,
contentType: 'test',
responseTopic: 'topic',
correlationData: Buffer.from([1, 2, 3, 4]),
userProperties: {
test: 'test'
}
},
topic: 'topic',
payload: Buffer.from([4, 3, 2, 1])
},
clean: true,
keepalive: 30,
properties: {
sessionExpiryInterval: 1234,
receiveMaximum: 432,
maximumPacketSize: 100,
topicAliasMaximum: 456,
requestResponseInformation: true,
requestProblemInformation: true,
userProperties: {
test: 'test'
},
authenticationMethod: 'test',
authenticationData: Buffer.from([1, 2, 3, 4])
},
clientId: 'test'
})
const parser = mqtt.parser()
parser.on('packet', packet => {
t.equal(packet.cmd, 'connect')
t.equal(Object.getPrototypeOf(packet.properties.userProperties), null)
t.equal(Object.getPrototypeOf(packet.will.properties.userProperties), null)
})
parser.parse(packet)
})
testGenerateErrorMultipleCmds([
'publish',
'puback',
'pubrec',
'pubrel',
'subscribe',
'suback',
'unsubscribe',
'unsuback'
], 'Invalid messageId', {
qos: 1, // required for publish
topic: 'test', // required for publish
messageId: 'a'
}, {})

@@ -786,3 +786,3 @@ const protocol = require('./constants')

if (!buffer) {
buffer = genBufVariableByteInt(num).data
buffer = genBufVariableByteInt(num)
if (num < 16384) varByteIntCache[num] = buffer

@@ -926,7 +926,8 @@ }

case 'var': {
if (typeof value !== 'number' || value < 0 || value > 0xffffffff) {
// var byte integer is max 24 bits packed in 32 bits
if (typeof value !== 'number' || value < 0 || value > 0x0fffffff) {
stream.emit('error', new Error(`Invalid ${name}: ${value}`))
return false
}
length += 1 + genBufVariableByteInt(value).length
length += 1 + Buffer.byteLength(genBufVariableByteInt(value))
break

@@ -988,3 +989,3 @@ }

}
const propertiesLengthLength = genBufVariableByteInt(propertiesLength).length
const propertiesLengthLength = Buffer.byteLength(genBufVariableByteInt(propertiesLength))

@@ -991,0 +992,0 @@ return {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc