diff --git a/packages/chronicle/src/server/api/apis-proxy.ts b/packages/chronicle/src/server/api/apis-proxy.ts index eb4a3ba..0f31c73 100644 --- a/packages/chronicle/src/server/api/apis-proxy.ts +++ b/packages/chronicle/src/server/api/apis-proxy.ts @@ -10,11 +10,19 @@ interface ProxyRequest { body?: unknown; } +const MAX_BODY_BYTES = 50 * 1024 * 1024; +const UPSTREAM_TIMEOUT_MS = 120_000; + export default defineHandler(async event => { if (event.req.method !== 'POST') { throw new HTTPError({ status: 405, message: 'Method not allowed' }); } + const contentLength = Number(event.req.headers.get('content-length') ?? '0'); + if (contentLength > MAX_BODY_BYTES) { + throw new HTTPError({ status: 413, message: 'Request body too large' }); + } + const { specName, method, path, headers, body } = (await event.req.json()) as ProxyRequest; @@ -33,7 +41,8 @@ export default defineHandler(async event => { throw new HTTPError({ status: 404, message: `Unknown spec: ${specName}` }); } - if (/^[a-z]+:\/\//i.test(path) || path.includes('..')) { + const decoded = decodeURIComponent(path); + if (/^[a-z]+:\/\//i.test(decoded) || decoded.includes('..')) { throw new HTTPError({ status: 400, message: 'Invalid path' }); } @@ -43,7 +52,8 @@ export default defineHandler(async event => { const response = await fetch(url, { method, headers, - body: body ? JSON.stringify(body) : undefined + body: body ? JSON.stringify(body) : undefined, + signal: AbortSignal.timeout(UPSTREAM_TIMEOUT_MS), }); const contentType = response.headers.get('content-type') ?? '';