limit the size of response we will accept (#2726)

limit the size of response we will accept
This commit is contained in:
chris48s
2019-01-11 21:50:49 +00:00
committed by GitHub
parent e22934eab0
commit 697ff80dad
9 changed files with 135 additions and 38 deletions

View File

@@ -10,7 +10,11 @@ const makeBadge = require('../gh-badges/lib/make-badge')
const analytics = require('./analytics')
const { makeSend } = require('./result-sender')
const queryString = require('query-string')
const { Inaccessible } = require('../services/errors')
const {
Inaccessible,
InvalidResponse,
ShieldsRuntimeError,
} = require('../services/errors')
const { setCacheHeaders } = require('../services/cache-headers')
// We avoid calling the vendor's server for computation of the information in a
@@ -64,6 +68,7 @@ function flattenQueryParams(queryParams) {
// the service uses
// - cacheLength: An optional badge or category-specific cache length
// (in number of seconds) to be used in preference to the default
// - fetchLimitBytes: A limit on the response size we're willing to parse
//
// For safety, the service must declare the query parameters it wants to use.
// Only the declared parameters (and the global parameters) are provided to
@@ -83,7 +88,10 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
}
const allowedKeys = flattenQueryParams(handlerOptions.queryParams)
const { cacheLength: serviceCacheLengthSeconds } = handlerOptions
const {
cacheLength: serviceCacheLengthSeconds,
fetchLimitBytes,
} = handlerOptions
return (queryParams, match, end, ask) => {
const reqTime = new Date()
@@ -167,7 +175,8 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
options.headers['User-Agent'] =
options.headers['User-Agent'] || 'Shields.io'
request(options, (err, res, body) => {
let bufferLength = 0
const r = request(options, (err, res, body) => {
if (res != null && res.headers != null) {
const cacheControl = res.headers['cache-control']
if (cacheControl != null) {
@@ -181,6 +190,18 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
}
callback(err, res, body)
})
r.on('data', chunk => {
bufferLength += chunk.length
if (bufferLength > fetchLimitBytes) {
r.abort()
r.emit(
'error',
new InvalidResponse({
prettyMessage: 'Maximum response size exceeded',
})
)
}
})
}
// Wrapper around `cachingRequest` that returns a promise rather than
@@ -189,9 +210,13 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
new Promise((resolve, reject) => {
cachingRequest(uri, options, (err, res, buffer) => {
if (err) {
// Wrap the error in an Inaccessible so it can be identified
// by the BaseService handler.
reject(new Inaccessible({ underlyingError: err }))
if (err instanceof ShieldsRuntimeError) {
reject(err)
} else {
// Wrap the error in an Inaccessible so it can be identified
// by the BaseService handler.
reject(new Inaccessible({ underlyingError: err }))
}
} else {
resolve({ res, buffer })
}