From 00f0d807b8e85523a99cabe80072d30bfc1d8e4c Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Thu, 21 Apr 2022 18:33:27 +0200 Subject: [PATCH 1/2] feat: add gateway forbidden content types --- packages/edge-gateway/src/constants.js | 1 + packages/edge-gateway/src/errors.js | 15 +++++++++++++++ packages/edge-gateway/src/gateway.js | 18 +++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/edge-gateway/src/constants.js b/packages/edge-gateway/src/constants.js index be704e4..3c1d60b 100644 --- a/packages/edge-gateway/src/constants.js +++ b/packages/edge-gateway/src/constants.js @@ -9,3 +9,4 @@ export const HTTP_STATUS_RATE_LIMITED = 429 export const HTTP_STATUS_SUCCESS = 200 export const REQUEST_PREVENTED_RATE_LIMIT_CODE = 'RATE_LIMIT' export const TIMEOUT_CODE = 'TIMEOUT' +export const FORBIDDEN_CONTENT_TYPES = ['application/octet-stream'] diff --git a/packages/edge-gateway/src/errors.js b/packages/edge-gateway/src/errors.js index c439eb0..83d8a3c 100644 --- a/packages/edge-gateway/src/errors.js +++ b/packages/edge-gateway/src/errors.js @@ -13,6 +13,21 @@ export class InvalidUrlError extends Error { } InvalidUrlError.CODE = 'ERROR_INVALID_URL' +export class ForbiddenContentError extends Error { + /** + * @param {string} message + */ + constructor(message = 'Forbidden content') { + const status = 403 + super(createErrorHtmlContent(status, message)) + this.name = 'ForbiddenContentError' + this.status = status + this.code = ForbiddenContentError.CODE + this.contentType = 'text/html' + } +} +ForbiddenContentError.CODE = 'ERROR_INVALID_URL' + export class TimeoutError extends Error { /** * @param {string} message diff --git a/packages/edge-gateway/src/gateway.js b/packages/edge-gateway/src/gateway.js index 33a78d8..aa3d259 100644 --- a/packages/edge-gateway/src/gateway.js +++ b/packages/edge-gateway/src/gateway.js @@ -5,7 +5,7 @@ import pAny, { AggregateError } from 'p-any' import { FilterError } from 'p-some' import pSettle from 'p-settle' -import { TimeoutError } from './errors.js' +import { TimeoutError, ForbiddenContentError } from './errors.js' import { getCidFromSubdomainUrl } from './utils/cid.js' import { toDenyListAnchor } from './utils/deny-list.js' import { @@ -17,6 +17,7 @@ import { HTTP_STATUS_RATE_LIMITED, REQUEST_PREVENTED_RATE_LIMIT_CODE, TIMEOUT_CODE, + FORBIDDEN_CONTENT_TYPES, } from './constants.js' /** @@ -109,7 +110,6 @@ export async function gatewayGet(request, env, ctx) { const contentLengthMb = Number( winnerGwResponse.response.headers.get('content-length') ) - await Promise.all([ storeWinnerGwResponse(request, env, winnerGwResponse), settleGatewayRequests(), @@ -120,6 +120,19 @@ export async function gatewayGet(request, env, ctx) { })() ) + // Block content types + if ( + FORBIDDEN_CONTENT_TYPES.includes( + winnerGwResponse.response.headers.get('content-type') + ) + ) { + throw new ForbiddenContentError( + `Forbidden content type: ${winnerGwResponse.response.headers.get( + 'content-type' + )}` + ) + } + // forward winner gateway response return winnerGwResponse.response } catch (err) { @@ -166,7 +179,6 @@ export async function gatewayGet(request, env, ctx) { throw new TimeoutError() } } - throw err } } From 21b9408d2fc3a3880faba126cfabce88e5a0849f Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Wed, 27 Apr 2022 10:18:05 +0200 Subject: [PATCH 2/2] chore: apply suggestions from code review Co-authored-by: Alan Shaw --- packages/edge-gateway/src/errors.js | 2 +- packages/edge-gateway/src/gateway.js | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/edge-gateway/src/errors.js b/packages/edge-gateway/src/errors.js index 83d8a3c..ec3a50e 100644 --- a/packages/edge-gateway/src/errors.js +++ b/packages/edge-gateway/src/errors.js @@ -26,7 +26,7 @@ export class ForbiddenContentError extends Error { this.contentType = 'text/html' } } -ForbiddenContentError.CODE = 'ERROR_INVALID_URL' +ForbiddenContentError.CODE = 'ERROR_FORBIDDEN_CONTENT' export class TimeoutError extends Error { /** diff --git a/packages/edge-gateway/src/gateway.js b/packages/edge-gateway/src/gateway.js index aa3d259..ee280cd 100644 --- a/packages/edge-gateway/src/gateway.js +++ b/packages/edge-gateway/src/gateway.js @@ -105,6 +105,15 @@ export async function gatewayGet(request, env, ctx) { ]) } + const winnerContentType = + winnerGwResponse.response.headers.get('content-type') + // Block content types + if (FORBIDDEN_CONTENT_TYPES.includes(winnerContentType)) { + throw new ForbiddenContentError( + `Forbidden content type: ${winnerContentType}` + ) + } + ctx.waitUntil( (async () => { const contentLengthMb = Number( @@ -120,19 +129,6 @@ export async function gatewayGet(request, env, ctx) { })() ) - // Block content types - if ( - FORBIDDEN_CONTENT_TYPES.includes( - winnerGwResponse.response.headers.get('content-type') - ) - ) { - throw new ForbiddenContentError( - `Forbidden content type: ${winnerGwResponse.response.headers.get( - 'content-type' - )}` - ) - } - // forward winner gateway response return winnerGwResponse.response } catch (err) {