msgpack5 6.0.2 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +15 -4
- package/AGENTS.md +26 -0
- package/README.md +10 -2
- package/SECURITY.md +16 -0
- package/dist/msgpack5.js +1760 -1111
- package/dist/msgpack5.min.js +1 -1
- package/example.js +6 -1
- package/index.js +16 -2
- package/lib/decoder.js +152 -28
- package/lib/streams.js +18 -17
- package/package.json +1 -1
- package/test/4-bytes-length-maps.js +31 -0
- package/test/64-bits-signed-integers.js +21 -0
- package/test/collection-length-limits.js +64 -0
- package/test/max-depth.js +95 -0
- package/test/object-prototype-poisoning.js +50 -0
- package/test/reserved-byte.js +53 -0
- package/test/streams.js +78 -0
|
@@ -18,6 +18,52 @@ test('decode throws when object has forbidden __proto__ property', function (t)
|
|
|
18
18
|
t.end()
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
+
test('decode defaults protoAction with partial options', function (t) {
|
|
22
|
+
const payload = { hello: 'world' }
|
|
23
|
+
Object.defineProperty(payload, '__proto__', {
|
|
24
|
+
value: { polluted: true },
|
|
25
|
+
enumerable: true
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const encoded = msgpack().encode(payload)
|
|
29
|
+
const options = [
|
|
30
|
+
{},
|
|
31
|
+
{ forceFloat64: true },
|
|
32
|
+
{ compatibilityMode: true },
|
|
33
|
+
{ disableTimestampEncoding: true },
|
|
34
|
+
{ preferMap: false },
|
|
35
|
+
{ sortKeys: true },
|
|
36
|
+
{ protoAction: undefined }
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
options.forEach(function (opts) {
|
|
40
|
+
t.throws(function () {
|
|
41
|
+
msgpack(opts).decode(encoded)
|
|
42
|
+
}, /Object contains forbidden prototype property/)
|
|
43
|
+
})
|
|
44
|
+
t.equal({}.polluted, undefined, 'does not affect Object.prototype')
|
|
45
|
+
t.end()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('does not mutate the caller options object', function (t) {
|
|
49
|
+
const options = { forceFloat64: true }
|
|
50
|
+
msgpack(options)
|
|
51
|
+
|
|
52
|
+
t.equal(options.protoAction, undefined)
|
|
53
|
+
t.end()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('validates protoAction', function (t) {
|
|
57
|
+
const invalid = [null, false, '', 'typo']
|
|
58
|
+
|
|
59
|
+
invalid.forEach(function (protoAction) {
|
|
60
|
+
t.throws(function () {
|
|
61
|
+
msgpack({ protoAction })
|
|
62
|
+
}, /protoAction must be "error", "remove", or "ignore"/)
|
|
63
|
+
})
|
|
64
|
+
t.end()
|
|
65
|
+
})
|
|
66
|
+
|
|
21
67
|
test('decode ignores forbidden __proto__ property if protoAction is "ignore"', function (t) {
|
|
22
68
|
const encoder = msgpack({ protoAction: 'ignore' })
|
|
23
69
|
|
|
@@ -30,6 +76,9 @@ test('decode ignores forbidden __proto__ property if protoAction is "ignore"', f
|
|
|
30
76
|
const decoded = encoder.decode(encoder.encode(payload))
|
|
31
77
|
|
|
32
78
|
t.equal(decoded.polluted, true)
|
|
79
|
+
t.notEqual(Object.getPrototypeOf(decoded), Object.prototype)
|
|
80
|
+
t.notOk(Object.prototype.hasOwnProperty.call(decoded, 'polluted'))
|
|
81
|
+
t.equal({}.polluted, undefined, 'does not affect Object.prototype')
|
|
33
82
|
t.end()
|
|
34
83
|
})
|
|
35
84
|
|
|
@@ -45,5 +94,6 @@ test('decode removes forbidden __proto__ property if protoAction is "remove"', f
|
|
|
45
94
|
const decoded = encoder.decode(encoder.encode(payload))
|
|
46
95
|
|
|
47
96
|
t.equal(decoded.polluted, undefined)
|
|
97
|
+
t.equal(Object.getPrototypeOf(decoded), Object.prototype)
|
|
48
98
|
t.end()
|
|
49
99
|
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const Buffer = require('safe-buffer').Buffer
|
|
4
|
+
const test = require('tape').test
|
|
5
|
+
const msgpack = require('../')
|
|
6
|
+
|
|
7
|
+
const reservedByteError = '0xc1 is a reserved MessagePack byte'
|
|
8
|
+
|
|
9
|
+
test('reserved byte is invalid rather than incomplete', function (t) {
|
|
10
|
+
t.plan(3)
|
|
11
|
+
|
|
12
|
+
const pack = msgpack()
|
|
13
|
+
let error
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
pack.decode(Buffer.from([0xc1]))
|
|
17
|
+
} catch (err) {
|
|
18
|
+
error = err
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
t.ok(error, 'must throw an error')
|
|
22
|
+
t.notOk(error instanceof pack.IncompleteBufferError, 'must not report incomplete input')
|
|
23
|
+
t.equal(error.message, reservedByteError, 'must identify the reserved byte')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('stream decoder rejects reserved byte without retaining input', function (t) {
|
|
27
|
+
t.plan(5)
|
|
28
|
+
|
|
29
|
+
const decoder = msgpack().decoder()
|
|
30
|
+
let decoded = 0
|
|
31
|
+
let errors = 0
|
|
32
|
+
|
|
33
|
+
decoder.on('data', function () {
|
|
34
|
+
decoded++
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
decoder.on('error', function (err) {
|
|
38
|
+
errors++
|
|
39
|
+
t.equal(err.message, reservedByteError, 'must emit the decoding error')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
decoder.on('close', function () {
|
|
43
|
+
t.equal(decoded, 0, 'must not emit decoded values')
|
|
44
|
+
t.equal(errors, 1, 'must emit one error')
|
|
45
|
+
t.equal(decoder._chunks.length, 0, 'must release buffered input')
|
|
46
|
+
t.ok(decoder.destroyed, 'must stop accepting input')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
decoder.write(Buffer.concat([
|
|
50
|
+
Buffer.from([0xc1]),
|
|
51
|
+
Buffer.alloc(200 * 1024, 0x01)
|
|
52
|
+
]))
|
|
53
|
+
})
|
package/test/streams.js
CHANGED
|
@@ -183,6 +183,84 @@ test('concatenated buffers work', function (t) {
|
|
|
183
183
|
encoder.end()
|
|
184
184
|
})
|
|
185
185
|
|
|
186
|
+
test('map32 header split across chunks', function (t) {
|
|
187
|
+
t.plan(2)
|
|
188
|
+
|
|
189
|
+
const decoder = msgpack().decoder()
|
|
190
|
+
|
|
191
|
+
decoder.on('data', function (value) {
|
|
192
|
+
t.deepEqual(value, {}, 'must decode the map')
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
decoder.on('error', function (err) {
|
|
196
|
+
t.fail(err.message)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
decoder.on('end', function () {
|
|
200
|
+
t.pass('must end normally')
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
const encoded = [0xdf, 0x00, 0x00, 0x00, 0x00]
|
|
204
|
+
encoded.forEach(function (byte) {
|
|
205
|
+
decoder.write(Buffer.from([byte]))
|
|
206
|
+
})
|
|
207
|
+
decoder.end()
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test('many concatenated values do not overflow the stack', function (t) {
|
|
211
|
+
t.plan(2)
|
|
212
|
+
|
|
213
|
+
const total = 50000
|
|
214
|
+
const decoder = msgpack().decoder()
|
|
215
|
+
let decoded = 0
|
|
216
|
+
|
|
217
|
+
decoder.on('data', function () {
|
|
218
|
+
decoded++
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
decoder.write(Buffer.alloc(total, 0x01), function (err) {
|
|
222
|
+
t.error(err, 'must decode without an error')
|
|
223
|
+
t.equal(decoded, total, 'must decode every value')
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('incomplete containers resume without decoding elements again', function (t) {
|
|
228
|
+
t.plan(3)
|
|
229
|
+
|
|
230
|
+
const pack = msgpack()
|
|
231
|
+
const decoder = pack.decoder()
|
|
232
|
+
const values = []
|
|
233
|
+
let decodeCalls = 0
|
|
234
|
+
let maxBuffered = 0
|
|
235
|
+
|
|
236
|
+
function MyType (value) {
|
|
237
|
+
this.value = value
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
pack.register(0x42, MyType, function (obj) {
|
|
241
|
+
return Buffer.from([obj.value])
|
|
242
|
+
}, function (buf) {
|
|
243
|
+
decodeCalls++
|
|
244
|
+
return buf.readUInt8(0)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
for (let i = 0; i < 100; i++) {
|
|
248
|
+
values.push(new MyType(i))
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
decoder.on('data', function (result) {
|
|
252
|
+
t.deepEqual(result, { values: values.map(function (value) { return value.value }) })
|
|
253
|
+
t.equal(decodeCalls, values.length, 'each completed element is decoded once')
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
const encoded = pack.encode({ values })
|
|
257
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
258
|
+
decoder.write(encoded.slice(i, i + 1))
|
|
259
|
+
maxBuffered = Math.max(maxBuffered, decoder._chunks.length)
|
|
260
|
+
}
|
|
261
|
+
t.ok(maxBuffered <= 6, 'only the current incomplete value remains buffered')
|
|
262
|
+
decoder.end()
|
|
263
|
+
})
|
|
186
264
|
test('nil processing works', function (t) {
|
|
187
265
|
t.plan(3)
|
|
188
266
|
|