From 6929b0b98b594b697dc38b7918c402ac7a44f1bb Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Thu, 27 Jun 2024 12:24:46 +0200 Subject: [PATCH] Prevent SVG images to be resized and rendered on the /~gitbook/image endpoint (#2362) --- src/app/(global)/~gitbook/image/route.ts | 8 +++++++- src/lib/images.ts | 13 +++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/app/(global)/~gitbook/image/route.ts b/src/app/(global)/~gitbook/image/route.ts index f21cf7503..429274564 100644 --- a/src/app/(global)/~gitbook/image/route.ts +++ b/src/app/(global)/~gitbook/image/route.ts @@ -1,6 +1,6 @@ import { NextRequest } from 'next/server'; -import { verifyImageSignature, resizeImage, CloudflareImageOptions } from '@/lib/images'; +import { verifyImageSignature, resizeImage, CloudflareImageOptions, checkIsSizableImageURL } from '@/lib/images'; import { parseImageAPIURL } from '@/lib/urls'; export const runtime = 'edge'; @@ -27,6 +27,12 @@ export async function GET(request: NextRequest) { return new Response('Invalid url parameter', { status: 400 }); } + // Check again if the image can be sized, even though we checked when rendering the Image component + // Otherwise, it's possible to pass just any link to this endpoint and trigger HTML injection on the domain + if (!checkIsSizableImageURL(url)) { + return new Response('Invalid url parameter', { status: 400 }); + } + // Verify the signature const verified = await verifyImageSignature(url, { signature, version: signatureVersion }); if (!verified) { diff --git a/src/lib/images.ts b/src/lib/images.ts index bb20bac16..044110f29 100644 --- a/src/lib/images.ts +++ b/src/lib/images.ts @@ -41,7 +41,7 @@ export function isImageResizingEnabled(): boolean { /** * Check if a URL is an HTTP URL. */ -export function checkIsHttpURL(input: string): boolean { +export function checkIsHttpURL(input: string | URL): boolean { if (!URL.canParse(input)) { return false; } @@ -54,11 +54,16 @@ export function checkIsHttpURL(input: string): boolean { * Skip it for non-http(s) URLs (data, etc). * Skip it for SVGs. */ -function checkIsSizableImageURL(input: string): boolean { - if (input.endsWith('.svg')) { +export function checkIsSizableImageURL(input: string): boolean { + if (!URL.canParse(input)) { return false; } - return checkIsHttpURL(input); + + const parsed = new URL(input); + if (parsed.pathname.endsWith('.svg')) { + return false; + } + return checkIsHttpURL(parsed); } /**