cbor-x 1.6.6 → 1.6.8
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/README.md +4 -0
- package/decode.js +81 -49
- package/dist/decode-no-eval.cjs +83 -49
- package/dist/decode-no-eval.cjs.map +1 -1
- package/dist/index-no-eval.cjs +88 -51
- package/dist/index-no-eval.cjs.map +1 -1
- package/dist/index-no-eval.min.js +1 -1
- package/dist/index-no-eval.min.js.map +1 -1
- package/dist/index.js +88 -51
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/node.cjs +101 -67
- package/dist/node.cjs.map +1 -1
- package/dist/test.js +82 -49
- package/dist/test.js.map +1 -1
- package/encode.js +1 -1
- package/iterators.js +6 -4
- package/package.json +1 -1
- package/stream.js +11 -13
package/README.md
CHANGED
|
@@ -241,8 +241,12 @@ setSizeLimits({
|
|
|
241
241
|
});
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
+
`maxMapSize` limits the entries of a decoded `Map`. A map decoded as an object (with `mapsAsObjects`) is limited to the smaller of `maxObjectSize` and `maxMapSize`.
|
|
245
|
+
|
|
244
246
|
Declared array and map lengths are also always validated against the number of bytes remaining in the source, so truncated or malformed input is rejected immediately instead of allocating for the length it claims. These limits are for bounding legitimately encoded (but unwanted) large data.
|
|
245
247
|
|
|
248
|
+
Nesting depth is bounded by the JavaScript call stack: input nested deeper than the stack allows (typically several thousand levels) throws the engine's stack overflow error (a `RangeError` in V8 and JavaScriptCore). Like other malformed input, this is not marked as `incomplete`, so a `DecoderStream` emits it as an error rather than waiting for more data. (An error from the last few bytes of a chunk may only be reported once the next chunk arrives.)
|
|
249
|
+
|
|
246
250
|
## Performance
|
|
247
251
|
Cbor-x is fast. Really fast. Here is comparison with the next fastest JS projects using the benchmark tool from `msgpack-lite` (and the sample data is from some clinical research data we use that has a good mix of different value types and structures). It also includes comparison to V8 native JSON functionality, and JavaScript Avro (`avsc`, a very optimized Avro implementation):
|
|
248
252
|
|
package/decode.js
CHANGED
|
@@ -19,8 +19,9 @@ let maxArraySize = 112810000 // This is the maximum array size in V8. We would p
|
|
|
19
19
|
let maxMapSize = 16810000 // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
|
|
20
20
|
// so we don't need to
|
|
21
21
|
|
|
22
|
-
let maxObjectSize = 16710000; //
|
|
23
|
-
//
|
|
22
|
+
let maxObjectSize = 16710000; // It takes over a minute to create this many keys in an object, so this is probably a
|
|
23
|
+
// reasonable choice.
|
|
24
|
+
let objectSizeLimit = maxObjectSize // objects are limited by both maxObjectSize and maxMapSize
|
|
24
25
|
let strings = EMPTY_ARRAY
|
|
25
26
|
let stringPosition = 0
|
|
26
27
|
let currentDecoder = {}
|
|
@@ -124,7 +125,13 @@ export class Decoder {
|
|
|
124
125
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
125
126
|
return saveState(() => {
|
|
126
127
|
clearSource()
|
|
127
|
-
|
|
128
|
+
try {
|
|
129
|
+
return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (error && typeof error == 'object')
|
|
132
|
+
nestedDecodeErrors.add(error)
|
|
133
|
+
throw error
|
|
134
|
+
}
|
|
128
135
|
})
|
|
129
136
|
}
|
|
130
137
|
srcEnd = end > -1 ? end : source.length
|
|
@@ -204,15 +211,15 @@ export class Decoder {
|
|
|
204
211
|
export function getPosition() {
|
|
205
212
|
return position
|
|
206
213
|
}
|
|
214
|
+
const nestedDecodeErrors = new WeakSet()
|
|
215
|
+
let arrayAllocationBudget
|
|
207
216
|
export function checkedRead() {
|
|
208
217
|
try {
|
|
218
|
+
arrayAllocationBudget = srcEnd - position
|
|
209
219
|
let result = read()
|
|
210
220
|
if (bundledStrings) {
|
|
211
|
-
if (position >= bundledStrings.postBundlePosition)
|
|
212
|
-
|
|
213
|
-
error.incomplete = true;
|
|
214
|
-
throw error
|
|
215
|
-
}
|
|
221
|
+
if (position >= bundledStrings.postBundlePosition)
|
|
222
|
+
throw new Error('Unexpected bundle position')
|
|
216
223
|
// bundled strings to skip past
|
|
217
224
|
position = bundledStrings.postBundlePosition;
|
|
218
225
|
bundledStrings = null;
|
|
@@ -236,7 +243,14 @@ export function checkedRead() {
|
|
|
236
243
|
return result
|
|
237
244
|
} catch(error) {
|
|
238
245
|
clearSource()
|
|
239
|
-
if (
|
|
246
|
+
if (nestedDecodeErrors.has(error)) {
|
|
247
|
+
// from a nested decode of embedded data (by an extension), which was complete in this source
|
|
248
|
+
if (error.incomplete)
|
|
249
|
+
error.incomplete = false
|
|
250
|
+
} else if (error instanceof RangeError && position + 8 > srcEnd ||
|
|
251
|
+
error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
|
|
252
|
+
// a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
|
|
253
|
+
// Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
|
|
240
254
|
error.incomplete = true
|
|
241
255
|
}
|
|
242
256
|
throw error
|
|
@@ -321,13 +335,13 @@ export function read() {
|
|
|
321
335
|
let i = 0;
|
|
322
336
|
if (currentDecoder.keyMap) {
|
|
323
337
|
while((key = read()) != STOP_CODE) {
|
|
324
|
-
if (i++ >=
|
|
338
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
325
339
|
object[safeKey(currentDecoder.decodeKey(key))] = read()
|
|
326
340
|
}
|
|
327
341
|
}
|
|
328
342
|
else {
|
|
329
343
|
while ((key = read()) != STOP_CODE) {
|
|
330
|
-
if (i++ >=
|
|
344
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
331
345
|
object[safeKey(key)] = read()
|
|
332
346
|
}
|
|
333
347
|
}
|
|
@@ -373,11 +387,13 @@ export function read() {
|
|
|
373
387
|
case 1: // negative int
|
|
374
388
|
return ~token
|
|
375
389
|
case 2: // buffer
|
|
390
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
376
391
|
return readBin(token)
|
|
377
392
|
case 3: // string
|
|
378
|
-
if (srcStringEnd >= position) {
|
|
393
|
+
if (srcStringEnd >= position) { // already extracted, which only covers complete strings
|
|
379
394
|
return srcString.slice(position - srcStringStart, (position += token) - srcStringStart)
|
|
380
395
|
}
|
|
396
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
381
397
|
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
|
|
382
398
|
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
383
399
|
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token)
|
|
@@ -386,19 +402,21 @@ export function read() {
|
|
|
386
402
|
}
|
|
387
403
|
return readFixedString(token)
|
|
388
404
|
case 4: // array
|
|
389
|
-
if (token
|
|
405
|
+
if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
390
406
|
// every element occupies at least one byte, so a length beyond what remains in the source can
|
|
391
407
|
// never be satisfied; check before allocating so a tiny header can not force a huge allocation
|
|
392
|
-
|
|
408
|
+
// nested arrays each pass the check above yet still preallocate, so also spend from a
|
|
409
|
+
// per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
|
|
410
|
+
if (token > srcEnd - position || (arrayAllocationBudget -= token) < 0) throw endOfCBORError()
|
|
393
411
|
let array = new Array(token)
|
|
394
412
|
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
395
413
|
//else
|
|
396
414
|
for (let i = 0; i < token; i++) array[i] = read()
|
|
397
415
|
return array
|
|
398
416
|
case 5: // map
|
|
399
|
-
if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
|
|
400
|
-
if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
401
417
|
if (currentDecoder.mapsAsObjects) {
|
|
418
|
+
if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
419
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
402
420
|
let object = {}
|
|
403
421
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read()
|
|
404
422
|
else for (let i = 0; i < token; i++) object[safeKey(read())] = read()
|
|
@@ -408,6 +426,8 @@ export function read() {
|
|
|
408
426
|
currentDecoder.mapsAsObjects = true
|
|
409
427
|
restoreMapsAsObject = false
|
|
410
428
|
}
|
|
429
|
+
if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
430
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError()
|
|
411
431
|
let map = new Map()
|
|
412
432
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read())
|
|
413
433
|
else for (let i = 0; i < token; i++) map.set(read(), read())
|
|
@@ -879,6 +899,7 @@ function readKey() {
|
|
|
879
899
|
if (length >= 0x60 && length < 0x78) {
|
|
880
900
|
// fixstr, potentially use key cache
|
|
881
901
|
length = length - 0x60
|
|
902
|
+
if (length > srcEnd - position) throw endOfCBORError()
|
|
882
903
|
if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway)
|
|
883
904
|
return srcString.slice(position - srcStringStart, (position += length) - srcStringStart)
|
|
884
905
|
else if (!(srcStringEnd == 0 && srcEnd < 180))
|
|
@@ -1014,18 +1035,12 @@ currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
|
|
|
1014
1035
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
1015
1036
|
}
|
|
1016
1037
|
const packedTable = (read) => {
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
error.incomplete = true
|
|
1021
|
-
throw error
|
|
1022
|
-
}
|
|
1038
|
+
if (!(position < srcEnd)) throw endOfCBORError()
|
|
1039
|
+
if (src[position++] != 0x84)
|
|
1040
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
1023
1041
|
let newPackedValues = read() // packed values
|
|
1024
|
-
if (!newPackedValues || !newPackedValues.length)
|
|
1025
|
-
|
|
1026
|
-
error.incomplete = true
|
|
1027
|
-
throw error
|
|
1028
|
-
}
|
|
1042
|
+
if (!Array.isArray(newPackedValues) || !newPackedValues.length)
|
|
1043
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
1029
1044
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues
|
|
1030
1045
|
packedValues.prefixes = read()
|
|
1031
1046
|
packedValues.suffixes = read()
|
|
@@ -1043,10 +1058,7 @@ currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
|
|
|
1043
1058
|
}
|
|
1044
1059
|
if (typeof data == 'number')
|
|
1045
1060
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
1046
|
-
|
|
1047
|
-
if (data === undefined)
|
|
1048
|
-
error.incomplete = true
|
|
1049
|
-
throw error
|
|
1061
|
+
throw new Error('No support for non-integer packed references yet')
|
|
1050
1062
|
}
|
|
1051
1063
|
|
|
1052
1064
|
// The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
|
|
@@ -1116,9 +1128,19 @@ currentExtensions[258] = (array) => new Set(array); // https://github.com/input-
|
|
|
1116
1128
|
(currentExtensions[259] = (read) => {
|
|
1117
1129
|
// https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
|
|
1118
1130
|
// for decoding as a standard Map
|
|
1119
|
-
|
|
1120
|
-
|
|
1131
|
+
let decoder = currentDecoder
|
|
1132
|
+
if (decoder.mapsAsObjects) {
|
|
1133
|
+
decoder.mapsAsObjects = false
|
|
1121
1134
|
restoreMapsAsObject = true
|
|
1135
|
+
try {
|
|
1136
|
+
return read()
|
|
1137
|
+
} finally {
|
|
1138
|
+
// normally restored by the map header, but not if the tagged value is not a map or the read fails
|
|
1139
|
+
if (restoreMapsAsObject) {
|
|
1140
|
+
decoder.mapsAsObjects = true
|
|
1141
|
+
restoreMapsAsObject = false
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1122
1144
|
}
|
|
1123
1145
|
return read()
|
|
1124
1146
|
}).handlesRead = true
|
|
@@ -1238,10 +1260,12 @@ function readJustLength() {
|
|
|
1238
1260
|
token = src[position++]
|
|
1239
1261
|
break
|
|
1240
1262
|
case 0x19:
|
|
1263
|
+
if (position + 2 > srcEnd) throw endOfCBORError()
|
|
1241
1264
|
token = dataView.getUint16(position)
|
|
1242
1265
|
position += 2
|
|
1243
1266
|
break
|
|
1244
1267
|
case 0x1a:
|
|
1268
|
+
if (position + 4 > srcEnd) throw endOfCBORError()
|
|
1245
1269
|
token = dataView.getUint32(position)
|
|
1246
1270
|
position += 4
|
|
1247
1271
|
break
|
|
@@ -1283,22 +1307,29 @@ function saveState(callback) {
|
|
|
1283
1307
|
let savedStructures = currentStructures
|
|
1284
1308
|
let savedDecoder = currentDecoder
|
|
1285
1309
|
let savedSequentialMode = sequentialMode
|
|
1286
|
-
let
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1310
|
+
let savedRestoreMapsAsObject = restoreMapsAsObject
|
|
1311
|
+
let savedArrayAllocationBudget = arrayAllocationBudget
|
|
1312
|
+
restoreMapsAsObject = false
|
|
1313
|
+
try {
|
|
1314
|
+
return callback()
|
|
1315
|
+
} finally {
|
|
1316
|
+
srcEnd = savedSrcEnd
|
|
1317
|
+
position = savedPosition
|
|
1318
|
+
stringPosition = savedStringPosition
|
|
1319
|
+
srcStringStart = savedSrcStringStart
|
|
1320
|
+
srcStringEnd = savedSrcStringEnd
|
|
1321
|
+
srcString = savedSrcString
|
|
1322
|
+
strings = savedStrings
|
|
1323
|
+
referenceMap = savedReferenceMap
|
|
1324
|
+
bundledStrings = savedBundledStrings
|
|
1325
|
+
src = savedSrc
|
|
1326
|
+
sequentialMode = savedSequentialMode
|
|
1327
|
+
currentStructures = savedStructures
|
|
1328
|
+
currentDecoder = savedDecoder
|
|
1329
|
+
restoreMapsAsObject = savedRestoreMapsAsObject
|
|
1330
|
+
arrayAllocationBudget = savedArrayAllocationBudget
|
|
1331
|
+
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength)
|
|
1332
|
+
}
|
|
1302
1333
|
}
|
|
1303
1334
|
export function clearSource() {
|
|
1304
1335
|
src = null
|
|
@@ -1314,6 +1345,7 @@ export function setSizeLimits(limits) {
|
|
|
1314
1345
|
if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
|
|
1315
1346
|
if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
|
|
1316
1347
|
if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
|
|
1348
|
+
objectSizeLimit = Math.min(maxObjectSize, maxMapSize)
|
|
1317
1349
|
}
|
|
1318
1350
|
|
|
1319
1351
|
export const mult10 = new Array(147) // this is a table matching binary exponents to the multiplier to determine significant digit rounding
|
package/dist/decode-no-eval.cjs
CHANGED
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
|
|
22
22
|
// for JSC, but this is pretty large and should be sufficient for most use cases
|
|
23
23
|
let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
|
|
24
|
-
//
|
|
24
|
+
// so we don't need to
|
|
25
|
+
|
|
26
|
+
let maxObjectSize = 16710000; // It takes over a minute to create this many keys in an object, so this is probably a
|
|
27
|
+
// reasonable choice.
|
|
28
|
+
let objectSizeLimit = maxObjectSize; // objects are limited by both maxObjectSize and maxMapSize
|
|
25
29
|
let strings = EMPTY_ARRAY;
|
|
26
30
|
let stringPosition = 0;
|
|
27
31
|
let currentDecoder = {};
|
|
@@ -125,7 +129,13 @@
|
|
|
125
129
|
// re-entrant execution, save the state and restore it after we do this decode
|
|
126
130
|
return saveState(() => {
|
|
127
131
|
clearSource();
|
|
128
|
-
|
|
132
|
+
try {
|
|
133
|
+
return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error && typeof error == 'object')
|
|
136
|
+
nestedDecodeErrors.add(error);
|
|
137
|
+
throw error
|
|
138
|
+
}
|
|
129
139
|
})
|
|
130
140
|
}
|
|
131
141
|
srcEnd = end > -1 ? end : source.length;
|
|
@@ -205,15 +215,15 @@
|
|
|
205
215
|
function getPosition() {
|
|
206
216
|
return position
|
|
207
217
|
}
|
|
218
|
+
const nestedDecodeErrors = new WeakSet();
|
|
219
|
+
let arrayAllocationBudget;
|
|
208
220
|
function checkedRead() {
|
|
209
221
|
try {
|
|
222
|
+
arrayAllocationBudget = srcEnd - position;
|
|
210
223
|
let result = read();
|
|
211
224
|
if (bundledStrings) {
|
|
212
|
-
if (position >= bundledStrings.postBundlePosition)
|
|
213
|
-
|
|
214
|
-
error.incomplete = true;
|
|
215
|
-
throw error
|
|
216
|
-
}
|
|
225
|
+
if (position >= bundledStrings.postBundlePosition)
|
|
226
|
+
throw new Error('Unexpected bundle position')
|
|
217
227
|
// bundled strings to skip past
|
|
218
228
|
position = bundledStrings.postBundlePosition;
|
|
219
229
|
bundledStrings = null;
|
|
@@ -237,7 +247,14 @@
|
|
|
237
247
|
return result
|
|
238
248
|
} catch(error) {
|
|
239
249
|
clearSource();
|
|
240
|
-
if (
|
|
250
|
+
if (nestedDecodeErrors.has(error)) {
|
|
251
|
+
// from a nested decode of embedded data (by an extension), which was complete in this source
|
|
252
|
+
if (error.incomplete)
|
|
253
|
+
error.incomplete = false;
|
|
254
|
+
} else if (error instanceof RangeError && position + 8 > srcEnd ||
|
|
255
|
+
error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
|
|
256
|
+
// a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
|
|
257
|
+
// Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
|
|
241
258
|
error.incomplete = true;
|
|
242
259
|
}
|
|
243
260
|
throw error
|
|
@@ -322,13 +339,13 @@
|
|
|
322
339
|
let i = 0;
|
|
323
340
|
if (currentDecoder.keyMap) {
|
|
324
341
|
while((key = read()) != STOP_CODE) {
|
|
325
|
-
if (i++ >=
|
|
342
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
326
343
|
object[safeKey(currentDecoder.decodeKey(key))] = read();
|
|
327
344
|
}
|
|
328
345
|
}
|
|
329
346
|
else {
|
|
330
347
|
while ((key = read()) != STOP_CODE) {
|
|
331
|
-
if (i++ >=
|
|
348
|
+
if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
332
349
|
object[safeKey(key)] = read();
|
|
333
350
|
}
|
|
334
351
|
}
|
|
@@ -374,11 +391,13 @@
|
|
|
374
391
|
case 1: // negative int
|
|
375
392
|
return ~token
|
|
376
393
|
case 2: // buffer
|
|
394
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
377
395
|
return readBin(token)
|
|
378
396
|
case 3: // string
|
|
379
|
-
if (srcStringEnd >= position) {
|
|
397
|
+
if (srcStringEnd >= position) { // already extracted, which only covers complete strings
|
|
380
398
|
return srcString.slice(position - srcStringStart, (position += token) - srcStringStart)
|
|
381
399
|
}
|
|
400
|
+
if (token > srcEnd - position) throw endOfCBORError()
|
|
382
401
|
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
|
|
383
402
|
// for small blocks, avoiding the overhead of the extract call is helpful
|
|
384
403
|
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
|
|
@@ -387,19 +406,21 @@
|
|
|
387
406
|
}
|
|
388
407
|
return readFixedString(token)
|
|
389
408
|
case 4: // array
|
|
390
|
-
if (token
|
|
409
|
+
if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
|
|
391
410
|
// every element occupies at least one byte, so a length beyond what remains in the source can
|
|
392
411
|
// never be satisfied; check before allocating so a tiny header can not force a huge allocation
|
|
393
|
-
|
|
412
|
+
// nested arrays each pass the check above yet still preallocate, so also spend from a
|
|
413
|
+
// per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
|
|
414
|
+
if (token > srcEnd - position || (arrayAllocationBudget -= token) < 0) throw endOfCBORError()
|
|
394
415
|
let array = new Array(token);
|
|
395
416
|
//if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
|
|
396
417
|
//else
|
|
397
418
|
for (let i = 0; i < token; i++) array[i] = read();
|
|
398
419
|
return array
|
|
399
420
|
case 5: // map
|
|
400
|
-
if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
|
|
401
|
-
if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
402
421
|
if (currentDecoder.mapsAsObjects) {
|
|
422
|
+
if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
|
|
423
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError() // each entry needs at least two bytes
|
|
403
424
|
let object = {};
|
|
404
425
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
|
|
405
426
|
else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
|
|
@@ -409,6 +430,8 @@
|
|
|
409
430
|
currentDecoder.mapsAsObjects = true;
|
|
410
431
|
restoreMapsAsObject = false;
|
|
411
432
|
}
|
|
433
|
+
if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
|
|
434
|
+
if (token > (srcEnd - position) / 2) throw endOfCBORError()
|
|
412
435
|
let map = new Map();
|
|
413
436
|
if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
|
|
414
437
|
else for (let i = 0; i < token; i++) map.set(read(), read());
|
|
@@ -939,18 +962,12 @@
|
|
|
939
962
|
return (glbl[data[0]] || Error)(data[1], data[2])
|
|
940
963
|
};
|
|
941
964
|
const packedTable = (read) => {
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
error.incomplete = true;
|
|
946
|
-
throw error
|
|
947
|
-
}
|
|
965
|
+
if (!(position < srcEnd)) throw endOfCBORError()
|
|
966
|
+
if (src[position++] != 0x84)
|
|
967
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
948
968
|
let newPackedValues = read(); // packed values
|
|
949
|
-
if (!newPackedValues || !newPackedValues.length)
|
|
950
|
-
|
|
951
|
-
error.incomplete = true;
|
|
952
|
-
throw error
|
|
953
|
-
}
|
|
969
|
+
if (!Array.isArray(newPackedValues) || !newPackedValues.length)
|
|
970
|
+
throw new Error('Packed values structure must be followed by a 4 element array')
|
|
954
971
|
packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
|
|
955
972
|
packedValues.prefixes = read();
|
|
956
973
|
packedValues.suffixes = read();
|
|
@@ -968,10 +985,7 @@
|
|
|
968
985
|
}
|
|
969
986
|
if (typeof data == 'number')
|
|
970
987
|
return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
|
|
971
|
-
|
|
972
|
-
if (data === undefined)
|
|
973
|
-
error.incomplete = true;
|
|
974
|
-
throw error
|
|
988
|
+
throw new Error('No support for non-integer packed references yet')
|
|
975
989
|
};
|
|
976
990
|
|
|
977
991
|
// The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
|
|
@@ -1041,9 +1055,19 @@
|
|
|
1041
1055
|
(currentExtensions[259] = (read) => {
|
|
1042
1056
|
// https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
|
|
1043
1057
|
// for decoding as a standard Map
|
|
1044
|
-
|
|
1045
|
-
|
|
1058
|
+
let decoder = currentDecoder;
|
|
1059
|
+
if (decoder.mapsAsObjects) {
|
|
1060
|
+
decoder.mapsAsObjects = false;
|
|
1046
1061
|
restoreMapsAsObject = true;
|
|
1062
|
+
try {
|
|
1063
|
+
return read()
|
|
1064
|
+
} finally {
|
|
1065
|
+
// normally restored by the map header, but not if the tagged value is not a map or the read fails
|
|
1066
|
+
if (restoreMapsAsObject) {
|
|
1067
|
+
decoder.mapsAsObjects = true;
|
|
1068
|
+
restoreMapsAsObject = false;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1047
1071
|
}
|
|
1048
1072
|
return read()
|
|
1049
1073
|
}).handlesRead = true;
|
|
@@ -1163,10 +1187,12 @@
|
|
|
1163
1187
|
token = src[position++];
|
|
1164
1188
|
break
|
|
1165
1189
|
case 0x19:
|
|
1190
|
+
if (position + 2 > srcEnd) throw endOfCBORError()
|
|
1166
1191
|
token = dataView.getUint16(position);
|
|
1167
1192
|
position += 2;
|
|
1168
1193
|
break
|
|
1169
1194
|
case 0x1a:
|
|
1195
|
+
if (position + 4 > srcEnd) throw endOfCBORError()
|
|
1170
1196
|
token = dataView.getUint32(position);
|
|
1171
1197
|
position += 4;
|
|
1172
1198
|
break
|
|
@@ -1208,22 +1234,29 @@
|
|
|
1208
1234
|
let savedStructures = currentStructures;
|
|
1209
1235
|
let savedDecoder = currentDecoder;
|
|
1210
1236
|
let savedSequentialMode = sequentialMode;
|
|
1211
|
-
let
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1237
|
+
let savedRestoreMapsAsObject = restoreMapsAsObject;
|
|
1238
|
+
let savedArrayAllocationBudget = arrayAllocationBudget;
|
|
1239
|
+
restoreMapsAsObject = false;
|
|
1240
|
+
try {
|
|
1241
|
+
return callback()
|
|
1242
|
+
} finally {
|
|
1243
|
+
srcEnd = savedSrcEnd;
|
|
1244
|
+
position = savedPosition;
|
|
1245
|
+
stringPosition = savedStringPosition;
|
|
1246
|
+
srcStringStart = savedSrcStringStart;
|
|
1247
|
+
srcStringEnd = savedSrcStringEnd;
|
|
1248
|
+
srcString = savedSrcString;
|
|
1249
|
+
strings = savedStrings;
|
|
1250
|
+
referenceMap = savedReferenceMap;
|
|
1251
|
+
bundledStrings = savedBundledStrings;
|
|
1252
|
+
src = savedSrc;
|
|
1253
|
+
sequentialMode = savedSequentialMode;
|
|
1254
|
+
currentStructures = savedStructures;
|
|
1255
|
+
currentDecoder = savedDecoder;
|
|
1256
|
+
restoreMapsAsObject = savedRestoreMapsAsObject;
|
|
1257
|
+
arrayAllocationBudget = savedArrayAllocationBudget;
|
|
1258
|
+
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
|
|
1259
|
+
}
|
|
1227
1260
|
}
|
|
1228
1261
|
function clearSource() {
|
|
1229
1262
|
src = null;
|
|
@@ -1238,7 +1271,8 @@
|
|
|
1238
1271
|
function setSizeLimits(limits) {
|
|
1239
1272
|
if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
|
|
1240
1273
|
if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
|
|
1241
|
-
if (limits.maxObjectSize) limits.maxObjectSize;
|
|
1274
|
+
if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
|
|
1275
|
+
objectSizeLimit = Math.min(maxObjectSize, maxMapSize);
|
|
1242
1276
|
}
|
|
1243
1277
|
|
|
1244
1278
|
const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
|