Skip to content

Commit 6839df0

Browse files
[Backport 6.x] Secure json parsing (#1112)
* Safe json parsing * Updated test Co-authored-by: Tomas Della Vedova <[email protected]>
1 parent 4c9b263 commit 6839df0

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
@@ -2157,3 +2157,71 @@ test('Should pass request params and options to generateRequestId', t => {
21572157

21582158
transport.request(params, options, t.error)
21592159
})
2160+
2161+
test('Secure json parsing', t => {
2162+
t.test('__proto__ protection', t => {
2163+
t.plan(2)
2164+
function handler (req, res) {
2165+
res.setHeader('Content-Type', 'application/json;utf=8')
2166+
res.end('{"__proto__":{"a":1}}')
2167+
}
2168+
2169+
buildServer(handler, ({ port }, server) => {
2170+
const pool = new ConnectionPool({ Connection })
2171+
pool.addConnection(`http://localhost:${port}`)
2172+
2173+
const transport = new Transport({
2174+
emit: () => {},
2175+
connectionPool: pool,
2176+
serializer: new Serializer(),
2177+
maxRetries: 3,
2178+
requestTimeout: 30000,
2179+
sniffInterval: false,
2180+
sniffOnStart: false
2181+
})
2182+
2183+
transport.request({
2184+
method: 'GET',
2185+
path: '/hello'
2186+
}, (err, { body }) => {
2187+
t.true(err instanceof DeserializationError)
2188+
t.is(err.message, 'Object contains forbidden prototype property')
2189+
server.stop()
2190+
})
2191+
})
2192+
})
2193+
2194+
t.test('constructor protection', t => {
2195+
t.plan(2)
2196+
function handler (req, res) {
2197+
res.setHeader('Content-Type', 'application/json;utf=8')
2198+
res.end('{"constructor":{"prototype":{"bar":"baz"}}}')
2199+
}
2200+
2201+
buildServer(handler, ({ port }, server) => {
2202+
const pool = new ConnectionPool({ Connection })
2203+
pool.addConnection(`http://localhost:${port}`)
2204+
2205+
const transport = new Transport({
2206+
emit: () => {},
2207+
connectionPool: pool,
2208+
serializer: new Serializer(),
2209+
maxRetries: 3,
2210+
requestTimeout: 30000,
2211+
sniffInterval: false,
2212+
sniffOnStart: false
2213+
})
2214+
2215+
transport.request({
2216+
method: 'GET',
2217+
path: '/hello'
2218+
}, (err, { body }) => {
2219+
t.true(err instanceof DeserializationError)
2220+
t.is(err.message, 'Object contains forbidden prototype property')
2221+
server.stop()
2222+
})
2223+
})
2224+
})
2225+
2226+
t.end()
2227+
})

0 commit comments

Comments
 (0)