Skip to content

Commit 5d0b419

Browse files
[Backport 5.x] Secure json parsing (#1111)
* Safe json parsing * Updated test Co-authored-by: Tomas Della Vedova <delvedor@users.noreply.github.com>
1 parent 7529cd8 commit 5d0b419

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

lib/Serializer.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
const { stringify } = require('querystring')
88
const debug = require('debug')('elasticsearch')
9+
const sjson = require('secure-json-parse')
910
const { SerializationError, DeserializationError } = require('./errors')
1011

1112
class Serializer {
@@ -22,7 +23,7 @@ class Serializer {
2223
deserialize (json) {
2324
debug('Deserializing', json)
2425
try {
25-
var object = JSON.parse(json)
26+
var object = sjson.parse(json)
2627
} catch (err) {
2728
throw new DeserializationError(err.message)
2829
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@
6666
"into-stream": "^5.1.0",
6767
"ms": "^2.1.1",
6868
"once": "^1.4.0",
69-
"pump": "^3.0.0"
69+
"pump": "^3.0.0",
70+
"secure-json-parse": "^2.1.0"
7071
},
7172
"license": "Apache-2.0",
7273
"repository": {

test/unit/transport.test.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,3 +2209,71 @@ test('Should pass request params and options to generateRequestId', t => {
22092209

22102210
transport.request(params, options, t.error)
22112211
})
2212+
2213+
test('Secure json parsing', t => {
2214+
t.test('__proto__ protection', t => {
2215+
t.plan(2)
2216+
function handler (req, res) {
2217+
res.setHeader('Content-Type', 'application/json;utf=8')
2218+
res.end('{"__proto__":{"a":1}}')
2219+
}
2220+
2221+
buildServer(handler, ({ port }, server) => {
2222+
const pool = new ConnectionPool({ Connection })
2223+
pool.addConnection(`http://localhost:${port}`)
2224+
2225+
const transport = new Transport({
2226+
emit: () => {},
2227+
connectionPool: pool,
2228+
serializer: new Serializer(),
2229+
maxRetries: 3,
2230+
requestTimeout: 30000,
2231+
sniffInterval: false,
2232+
sniffOnStart: false
2233+
})
2234+
2235+
transport.request({
2236+
method: 'GET',
2237+
path: '/hello'
2238+
}, (err, { body }) => {
2239+
t.true(err instanceof DeserializationError)
2240+
t.is(err.message, 'Object contains forbidden prototype property')
2241+
server.stop()
2242+
})
2243+
})
2244+
})
2245+
2246+
t.test('constructor protection', t => {
2247+
t.plan(2)
2248+
function handler (req, res) {
2249+
res.setHeader('Content-Type', 'application/json;utf=8')
2250+
res.end('{"constructor":{"prototype":{"bar":"baz"}}}')
2251+
}
2252+
2253+
buildServer(handler, ({ port }, server) => {
2254+
const pool = new ConnectionPool({ Connection })
2255+
pool.addConnection(`http://localhost:${port}`)
2256+
2257+
const transport = new Transport({
2258+
emit: () => {},
2259+
connectionPool: pool,
2260+
serializer: new Serializer(),
2261+
maxRetries: 3,
2262+
requestTimeout: 30000,
2263+
sniffInterval: false,
2264+
sniffOnStart: false
2265+
})
2266+
2267+
transport.request({
2268+
method: 'GET',
2269+
path: '/hello'
2270+
}, (err, { body }) => {
2271+
t.true(err instanceof DeserializationError)
2272+
t.is(err.message, 'Object contains forbidden prototype property')
2273+
server.stop()
2274+
})
2275+
})
2276+
})
2277+
2278+
t.end()
2279+
})

0 commit comments

Comments
 (0)