Skip to content

Commit

Permalink
Merge pull request #161 from KubqoA/add-duplicate-map-keys-checking
Browse files Browse the repository at this point in the history
Add option to not accept maps with duplicate keys
  • Loading branch information
hildjj authored Nov 8, 2021
2 parents 815ba89 + 660d5e3 commit caca0bc
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 1 deletion.
11 changes: 11 additions & 0 deletions packages/cbor/lib/decoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class UnexpectedDataError extends Error {
* @property {boolean} [extendedResults=false] If true, emit extended
* results, which will be an object with shape {@link ExtendedResults}.
* The value will already have been null-checked.
* @property {boolean} [preventDuplicateKeys=false] If true, error is
* thrown if a map has duplicate keys.
*/
/**
* @callback decodeCallback
Expand Down Expand Up @@ -129,6 +131,7 @@ class Decoder extends BinaryParseStream {
required = false,
encoding = 'hex',
extendedResults = false,
preventDuplicateKeys = false,
...superOpts
} = options

Expand All @@ -140,6 +143,7 @@ class Decoder extends BinaryParseStream {
this.preferWeb = preferWeb
this.extendedResults = extendedResults
this.required = required
this.preventDuplicateKeys = preventDuplicateKeys

if (extendedResults) {
this.bs.on('read', this._onRead.bind(this))
Expand Down Expand Up @@ -594,11 +598,18 @@ class Decoder extends BinaryParseStream {
if (allstrings) {
val = {}
for (let i = 0, len = parent.length; i < len; i += 2) {
if (this.preventDuplicateKeys &&
Object.prototype.hasOwnProperty.call(val, parent[i])) {
throw new Error('Duplicate keys in a map')
}
val[parent[i]] = parent[i + 1]
}
} else {
val = new Map()
for (let i = 0, len = parent.length; i < len; i += 2) {
if (this.preventDuplicateKeys && val.has(parent[i])) {
throw new Error('Duplicate keys in a map')
}
val.set(parent[i], parent[i + 1])
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cbor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"Artyom Yagilev <github@scorpi.org> (http://scorpi.org/)",
"Denis Lapaev <den@lapaev.me> (http://lapaev.me/)",
"Ruben Bridgewater <ruben@bridgewater.de>",
"Burt Harris <Burt_Harris_cbor@azxs.33mail.com>"
"Burt Harris <Burt_Harris_cbor@azxs.33mail.com>",
"Jakub Arbet <hi@jakubarbet.me> (https://jakubarbet.me/)"
],
"types": "./types/lib/cbor.d.ts",
"dependencies": {
Expand Down
30 changes: 30 additions & 0 deletions packages/cbor/test/decoder.ava.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,33 @@ test('Buffers', t => {
],
})
})

test('duplicate map keys', t => {
// Default behaviour is unchanged and allows parsing maps with duplicate keys
t.deepEqual(
cbor.decode(Buffer.from('a200010002', 'hex')),
new Map([[0, 2]])
)
// When the new decoder option is set the decode will throw
t.throws(() => cbor.decode(Buffer.from('a200010002', 'hex'), {preventDuplicateKeys: true}))

// Default behaviour for maps with only string keys is also unchanged
t.deepEqual(
cbor.decode(Buffer.from('a268746f537472696e670068746f537472696e6701', 'hex')),
{
toString: 1,
}
)

// When parsing a map with only strings as keys the result is an object and
// we ignore keys from the object's prototype when checking for duplicates
t.deepEqual(
cbor.decode(Buffer.from('a268746f537472696e670063666f6f01', 'hex'), {preventDuplicateKeys: true}),
{
toString: 0,
foo: 1,
}
)

t.throws(() => cbor.decode(Buffer.from('a268746f537472696e670068746f537472696e6701', 'hex'), {preventDuplicateKeys: true}))
})
6 changes: 6 additions & 0 deletions packages/cbor/types/lib/decoder.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ declare class Decoder extends BinaryParseStream {
preferWeb: boolean;
extendedResults: boolean;
required: boolean;
preventDuplicateKeys: boolean;
valueBytes: NoFilter;
/**
* Stop processing.
Expand Down Expand Up @@ -163,6 +164,11 @@ type DecoderOptions = {
* The value will already have been null-checked.
*/
extendedResults?: boolean;
/**
* If true, error is
* thrown if a map has duplicate keys.
*/
preventDuplicateKeys?: boolean;
};
type ExtendedResults = {
/**
Expand Down

0 comments on commit caca0bc

Please # to comment.