Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b6b73ba

Browse files
authored
Fix Buffer Out of Bounds Error (#100)
* Code fix and additional tests. Refactor test function testParseGenerateDefaults() to allow just the parsing to be performed on its own. * Modify testParseGenerateDefaults to take an partial packet object, generate the mqtt bytes, and then parse that buffer to a full packet object with defaults shown. Add an additional testParseAndGenerate function which takes a packet and an mqtt bytes and tests they Parse and Generate in both directions.
1 parent fb14930 commit b6b73ba

File tree

2 files changed

+101
-23
lines changed

2 files changed

+101
-23
lines changed

parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ class Parser extends EventEmitter {
548548
let current
549549
const padding = this._pos ? this._pos : 0
550550

551-
while (bytes < maxBytes) {
551+
while (bytes < maxBytes && (padding + bytes) < this._list.length) {
552552
current = this._list.readUInt8(padding + bytes++)
553553
value += mul * (current & constants.VARBYTEINT_MASK)
554554
mul *= 0x80

test.js

Lines changed: 100 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,26 @@ function testGenerateOnly (name, object, buffer, opts) {
9797
})
9898
}
9999

100+
function testParseOnly (name, object, buffer, opts) {
101+
test(name, t => {
102+
const parser = mqtt.parser(opts)
103+
// const expected = object
104+
// const fixture = buffer
105+
106+
t.plan(2 + Object.keys(object).length)
107+
108+
parser.on('packet', packet => {
109+
t.equal(Object.keys(object).length, Object.keys(packet).length, 'key count')
110+
Object.keys(object).forEach(key => {
111+
t.deepEqual(packet[key], object[key], `expected packet property ${key}`)
112+
})
113+
})
114+
115+
t.equal(parser.parse(buffer), 0, 'remaining bytes')
116+
t.end()
117+
})
118+
}
119+
100120
function testParseError (expected, fixture, opts) {
101121
test(expected, t => {
102122
t.plan(1)
@@ -138,28 +158,14 @@ function testGenerateErrorMultipleCmds (cmds, expected, fixture, opts) {
138158
)
139159
}
140160

141-
function testParseGenerateDefaults (name, object, buffer, opts) {
142-
test(`${name} parse`, t => {
143-
const parser = mqtt.parser(opts)
144-
const expected = object
145-
const fixture = buffer
146-
147-
t.plan(1 + Object.keys(expected).length)
148-
149-
parser.on('packet', packet => {
150-
Object.keys(expected).forEach(key => {
151-
t.deepEqual(packet[key], expected[key], `expected packet property ${key}`)
152-
})
153-
})
154-
155-
t.equal(parser.parse(fixture), 0, 'remaining bytes')
156-
t.end()
157-
})
161+
function testParseGenerateDefaults (name, object, buffer, generated, opts) {
162+
testParseOnly(`${name} parse`, generated, buffer, opts)
163+
testGenerateOnly(`${name} generate`, object, buffer, opts)
164+
}
158165

159-
test(`${name} generate`, t => {
160-
t.equal(mqtt.generate(object).toString('hex'), buffer.toString('hex'))
161-
t.end()
162-
})
166+
function testParseAndGenerate (name, object, buffer, opts) {
167+
testParseOnly(`${name} parse`, object, buffer, opts)
168+
testGenerateOnly(`${name} generate`, object, buffer, opts)
163169
}
164170

165171
function testWriteToStreamError (expected, fixture) {
@@ -520,7 +526,66 @@ testParseGenerateDefaults('default connect', {
520526
16, 16, 0, 4, 77, 81, 84,
521527
84, 4, 2, 0, 0,
522528
0, 4, 116, 101, 115, 116
523-
]))
529+
]), {
530+
cmd: 'connect',
531+
retain: false,
532+
qos: 0,
533+
dup: false,
534+
length: 16,
535+
topic: null,
536+
payload: null,
537+
protocolId: 'MQTT',
538+
protocolVersion: 4,
539+
clean: true,
540+
keepalive: 0,
541+
clientId: 'test'
542+
})
543+
544+
testParseAndGenerate('Version 4 CONACK', {
545+
cmd: 'connack',
546+
retain: false,
547+
qos: 0,
548+
dup: false,
549+
length: 2,
550+
topic: null,
551+
payload: null,
552+
sessionPresent: false,
553+
returnCode: 1
554+
}, Buffer.from([
555+
32, 2, // Fixed Header (CONNACK, Remaining Length)
556+
0, 1 // Variable Header (Session not present, Connection Refused - unacceptable protocol version)
557+
]), {}) // Default protocolVersion (4)
558+
559+
testParseAndGenerate('Version 5 CONACK', {
560+
cmd: 'connack',
561+
retain: false,
562+
qos: 0,
563+
dup: false,
564+
length: 3,
565+
topic: null,
566+
payload: null,
567+
sessionPresent: false,
568+
reasonCode: 140
569+
}, Buffer.from([
570+
32, 3, // Fixed Header (CONNACK, Remaining Length)
571+
0, 140, // Variable Header (Session not present, Bad authentication method)
572+
0 // Property Length Zero
573+
]), { protocolVersion: 5 })
574+
575+
testParseOnly('Version 4 CONACK in Version 5 mode', {
576+
cmd: 'connack',
577+
retain: false,
578+
qos: 0,
579+
dup: false,
580+
length: 2,
581+
topic: null,
582+
payload: null,
583+
sessionPresent: false,
584+
reasonCode: 1 // a version 4 return code stored in the version 5 reasonCode because this client is in version 5
585+
}, Buffer.from([
586+
32, 2, // Fixed Header (CONNACK, Remaining Length)
587+
0, 1 // Variable Header (Session not present, Connection Refused - unacceptable protocol version)
588+
]), { protocolVersion: 5 }) // message is in version 4 format, but this client is in version 5 mode
524589

525590
testParseGenerate('empty will payload', {
526591
cmd: 'connect',
@@ -1903,6 +1968,19 @@ testParseGenerate('disconnect MQTT 5', {
19031968
28, 0, 4, 116, 101, 115, 116// serverReference
19041969
]), { protocolVersion: 5 })
19051970

1971+
testParseGenerate('disconnect MQTT 5 with no properties', {
1972+
cmd: 'disconnect',
1973+
retain: false,
1974+
qos: 0,
1975+
dup: false,
1976+
length: 2,
1977+
reasonCode: 0
1978+
}, Buffer.from([
1979+
224, 2, // Fixed Header (DISCONNECT, Remaining Length)
1980+
0, // Reason Code (Normal Disconnection)
1981+
0 // Property Length (0 => No Properties)
1982+
]), { protocolVersion: 5 })
1983+
19061984
testParseGenerate('auth MQTT 5', {
19071985
cmd: 'auth',
19081986
retain: false,

0 commit comments

Comments
 (0)