Skip to content

Commit

Permalink
Merge pull request from GHSA-gmjw-49p4-pcfm
Browse files Browse the repository at this point in the history
Co-authored-by: Simone Busoli <simone.busoli@gmail.com>
  • Loading branch information
mcollina and simoneb committed Mar 5, 2021
1 parent c279c34 commit d4e6cb9
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -111,6 +111,7 @@ options:
- `compatibilityMode`, a boolean that enables "compatibility mode" which doesn't use str 8 format. Defaults to false.
- `disableTimestampEncoding`, a boolean that when set disables the encoding of Dates into the [timestamp extension type](https://github.com/msgpack/msgpack/blob/master/spec.md#timestamp-extension-type). Defaults to false.
- `preferMap`, a boolean that forces all maps to be decoded to `Map`s rather than plain objects. This ensures that `decode(encode(new Map())) instanceof Map` and that iteration order is preserved. Defaults to false.
- `protoAction`, a string which can be `error|ignore|remove` that determines what happens when decoding a plain object with a `__proto__` property which would cause prototype poisoning. `error` (default) throws an error, `remove` removes the property, `ignore` (not recommended) allows the property, thereby causing prototype poisoning on the decoded object.

-------------------------------------------------------
<a name="encode"></a>
Expand Down
4 changes: 3 additions & 1 deletion index.js
Expand Up @@ -19,7 +19,9 @@ function msgpack (options) {
// if true, skips encoding Dates using the msgpack
// timestamp ext format (-1)
disableTimestampEncoding: false,
preferMap: false
preferMap: false,
// options.protoAction: 'error' (default) / 'remove' / 'ignore'
protoAction: 'error'
}

decodingTypes.set(DateCodec.type, DateCodec.decode)
Expand Down
11 changes: 11 additions & 0 deletions lib/decoder.js
Expand Up @@ -187,6 +187,17 @@ module.exports = function buildDecode (decodingTypes, options) {
for (let i = 0; i < 2 * length; i += 2) {
const key = result[i]
const val = result[i + 1]

if (key === '__proto__') {
if (options.protoAction === 'error') {
throw new SyntaxError('Object contains forbidden prototype property')
}

if (options.protoAction === 'remove') {
continue
}
}

object[key] = val
}
return [object, consumedBytes]
Expand Down
49 changes: 49 additions & 0 deletions test/object-prototype-poisoning.js
@@ -0,0 +1,49 @@
'use strict'

var test = require('tape').test
var msgpack = require('../')

test('decode throws when object has forbidden __proto__ property', function (t) {
const encoder = msgpack()

const payload = { hello: 'world' }
Object.defineProperty(payload, '__proto__', {
value: { polluted: true },
enumerable: true
})

const encoded = encoder.encode(payload)

t.throws(() => encoder.decode(encoded), /Object contains forbidden prototype property/)
t.end()
})

test('decode ignores forbidden __proto__ property if protoAction is "ignore"', function (t) {
const encoder = msgpack({ protoAction: 'ignore' })

const payload = { hello: 'world' }
Object.defineProperty(payload, '__proto__', {
value: { polluted: true },
enumerable: true
})

const decoded = encoder.decode(encoder.encode(payload))

t.equal(decoded.polluted, true)
t.end()
})

test('decode removes forbidden __proto__ property if protoAction is "remove"', function (t) {
const encoder = msgpack({ protoAction: 'remove' })

const payload = { hello: 'world' }
Object.defineProperty(payload, '__proto__', {
value: { polluted: true },
enumerable: true
})

const decoded = encoder.decode(encoder.encode(payload))

t.equal(decoded.polluted, undefined)
t.end()
})

0 comments on commit d4e6cb9

Please sign in to comment.