Skip to content

Commit 502509c

Browse files
authored
Throw if the content length is too big (#1355)
1 parent 959aee5 commit 502509c

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

lib/Transport.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
const debug = require('debug')('elasticsearch')
2323
const os = require('os')
2424
const { gzip, unzip, createGzip } = require('zlib')
25+
const buffer = require('buffer')
2526
const ms = require('ms')
2627
const {
2728
ConnectionError,
@@ -35,6 +36,8 @@ const noop = () => {}
3536

3637
const clientVersion = require('../package.json').version
3738
const userAgent = `elasticsearch-js/${clientVersion} (${os.platform()} ${os.release()}-${os.arch()}; Node.js ${process.version})`
39+
const MAX_BUFFER_LENGTH = buffer.constants.MAX_LENGTH
40+
const MAX_STRING_LENGTH = buffer.constants.MAX_STRING_LENGTH
3841

3942
class Transport {
4043
constructor (opts) {
@@ -218,6 +221,22 @@ class Transport {
218221

219222
const contentEncoding = (result.headers['content-encoding'] || '').toLowerCase()
220223
const isCompressed = contentEncoding.indexOf('gzip') > -1 || contentEncoding.indexOf('deflate') > -1
224+
225+
/* istanbul ignore else */
226+
if (result.headers['content-length'] !== undefined) {
227+
const contentLength = Number(result.headers['content-length'])
228+
if (isCompressed && contentLength > MAX_BUFFER_LENGTH) {
229+
response.destroy()
230+
return onConnectionError(
231+
new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed buffer (${MAX_BUFFER_LENGTH})`, result)
232+
)
233+
} else if (contentLength > MAX_STRING_LENGTH) {
234+
response.destroy()
235+
return onConnectionError(
236+
new RequestAbortedError(`The content length (${contentLength}) is bigger than the maximum allowed string (${MAX_STRING_LENGTH})`, result)
237+
)
238+
}
239+
}
221240
// if the response is compressed, we must handle it
222241
// as buffer for allowing decompression later
223242
let payload = isCompressed ? [] : ''

test/unit/client.test.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121

2222
const { test } = require('tap')
2323
const { URL } = require('url')
24-
const { Client, ConnectionPool, Transport, errors } = require('../../index')
24+
const buffer = require('buffer')
25+
const intoStream = require('into-stream')
26+
const { Client, ConnectionPool, Transport, Connection, errors } = require('../../index')
2527
const { CloudConnectionPool } = require('../../lib/pool')
2628
const { buildServer } = require('../utils')
2729

@@ -1243,3 +1245,58 @@ test('Socket destryed while reading the body', t => {
12431245
})
12441246
})
12451247
})
1248+
1249+
test('Content length too big (buffer)', t => {
1250+
t.plan(4)
1251+
1252+
class MockConnection extends Connection {
1253+
request (params, callback) {
1254+
const stream = intoStream(JSON.stringify({ hello: 'world' }))
1255+
stream.statusCode = 200
1256+
stream.headers = {
1257+
'content-type': 'application/json;utf=8',
1258+
'content-encoding': 'gzip',
1259+
'content-length': buffer.constants.MAX_LENGTH + 10,
1260+
connection: 'keep-alive',
1261+
date: new Date().toISOString()
1262+
}
1263+
stream.on('close', () => t.pass('Stream destroyed'))
1264+
process.nextTick(callback, null, stream)
1265+
return { abort () {} }
1266+
}
1267+
}
1268+
1269+
const client = new Client({ node: 'http://localhost:9200', Connection: MockConnection })
1270+
client.info((err, result) => {
1271+
t.ok(err instanceof errors.RequestAbortedError)
1272+
t.is(err.message, `The content length (${buffer.constants.MAX_LENGTH + 10}) is bigger than the maximum allowed buffer (${buffer.constants.MAX_LENGTH})`)
1273+
t.strictEqual(result.meta.attempts, 0)
1274+
})
1275+
})
1276+
1277+
test('Content length too big (string)', t => {
1278+
t.plan(4)
1279+
1280+
class MockConnection extends Connection {
1281+
request (params, callback) {
1282+
const stream = intoStream(JSON.stringify({ hello: 'world' }))
1283+
stream.statusCode = 200
1284+
stream.headers = {
1285+
'content-type': 'application/json;utf=8',
1286+
'content-length': buffer.constants.MAX_STRING_LENGTH + 10,
1287+
connection: 'keep-alive',
1288+
date: new Date().toISOString()
1289+
}
1290+
stream.on('close', () => t.pass('Stream destroyed'))
1291+
process.nextTick(callback, null, stream)
1292+
return { abort () {} }
1293+
}
1294+
}
1295+
1296+
const client = new Client({ node: 'http://localhost:9200', Connection: MockConnection })
1297+
client.info((err, result) => {
1298+
t.ok(err instanceof errors.RequestAbortedError)
1299+
t.is(err.message, `The content length (${buffer.constants.MAX_STRING_LENGTH + 10}) is bigger than the maximum allowed string (${buffer.constants.MAX_STRING_LENGTH})`)
1300+
t.strictEqual(result.meta.attempts, 0)
1301+
})
1302+
})

0 commit comments

Comments
 (0)