diff --git a/.gitignore b/.gitignore index fa324a2..deac8f3 100644 --- a/.gitignore +++ b/.gitignore @@ -181,4 +181,5 @@ __fixtures__ /.data /.dump /.vscode -/source.md \ No newline at end of file +/source.md +SECURITY.md \ No newline at end of file diff --git a/src/functions/server/handle-routes.ts b/src/functions/server/handle-routes.ts index 69186bb..56a66bd 100644 --- a/src/functions/server/handle-routes.ts +++ b/src/functions/server/handle-routes.ts @@ -68,16 +68,16 @@ export default async function ({ req }: Params): Promise { const config = module.config as BunextServerRouteConfig | undefined; + const maxBodyBytes = config?.max_request_body_mb + ? config.max_request_body_mb * MBInBytes + : ServerDefaultRequestBodyLimitBytes; + const contentLength = req.headers.get("content-length"); if (contentLength) { const size = parseInt(contentLength, 10); - if ( - (config?.max_request_body_mb && - size > config.max_request_body_mb * MBInBytes) || - size > ServerDefaultRequestBodyLimitBytes - ) { + if (size > maxBodyBytes) { return Response.json( { success: false, @@ -91,6 +91,26 @@ export default async function ({ req }: Params): Promise { }, ); } + } else if (req.method !== "GET" && req.method !== "HEAD") { + const body = await req.arrayBuffer(); + if (body.byteLength > maxBodyBytes) { + return Response.json( + { + success: false, + msg: "Request Body Too Large!", + }, + { + status: 413, + headers: { + "Content-Type": "application/json", + }, + }, + ); + } + + routeParams.body = JSON.parse( + new TextDecoder().decode(body) || "{}", + ); } const target_module = (module["default"] || diff --git a/src/utils/deserialize-query.ts b/src/utils/deserialize-query.ts index 3e4e4e8..aa761c2 100644 --- a/src/utils/deserialize-query.ts +++ b/src/utils/deserialize-query.ts @@ -1,8 +1,18 @@ import EJSON from "./ejson"; -/** - * # Convert Serialized Query back to object - */ +const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]); + +function sanitize(value: T): T { + if (value === null || typeof value !== "object") return value; + if (Array.isArray(value)) return value.map(sanitize) as T; + const clean: Record = Object.create(null); + for (const key of Object.keys(value as Record)) { + if (DANGEROUS_KEYS.has(key)) continue; + clean[key] = sanitize((value as Record)[key]); + } + return clean as T; +} + export default function deserializeQuery( query: string | { [s: string]: any } ): { @@ -17,12 +27,17 @@ export default function deserializeQuery( const key = keys[i]; const value = queryObject[key]; + if (DANGEROUS_KEYS.has(key)) { + delete queryObject[key]; + continue; + } + if (typeof value == "string") { if (value.match(/^\{|^\[/)) { - queryObject[key] = EJSON.parse(value); + queryObject[key] = sanitize(EJSON.parse(value)); } } } - return queryObject; + return sanitize(queryObject); }