From f6a42cba966c8a6e85eab6aa59fad125131d338a Mon Sep 17 00:00:00 2001 From: Zicklag Date: Fri, 10 Jan 2025 16:58:34 -0600 Subject: [PATCH] fix: add timeout and data limit to link verification fetches. --- src/lib/limited-fetch.ts | 41 +++++++++++++++++++++++++++ src/lib/link_verifier/LinkVerifier.ts | 3 +- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/lib/limited-fetch.ts diff --git a/src/lib/limited-fetch.ts b/src/lib/limited-fetch.ts new file mode 100644 index 00000000..1e2315af --- /dev/null +++ b/src/lib/limited-fetch.ts @@ -0,0 +1,41 @@ +export async function limitedFetch( + options: { timeout: number; maxSize: number }, + ...fetchArgs: Parameters +): Promise { + const signal = AbortSignal.timeout(options.timeout); + const resp = await fetch(fetchArgs[0], { ...fetchArgs[1], signal }); + + const reader = resp.body?.getReader(); + if (!reader) return resp; + + let bytesSoFar = 0; + const stream = new ReadableStream({ + start(controller) { + return pump(); + async function pump(): Promise { + const { done, value } = await reader!.read(); + // When no more data needs to be consumed, close the stream + if (done) { + controller.close(); + return; + } + bytesSoFar += value?.length; + if (bytesSoFar > options.maxSize) { + throw new Error( + `Could not fetch URL ( ${fetchArgs[0]} ) because returned number of \ +bytes exceeded limit of ${options.maxSize} bytes.` + ); + } + // Enqueue the next data chunk into our target stream + controller.enqueue(value); + return pump(); + } + } + }); + + return new Response(stream, { + headers: resp.headers, + status: resp.status, + statusText: resp.statusText + }); +} diff --git a/src/lib/link_verifier/LinkVerifier.ts b/src/lib/link_verifier/LinkVerifier.ts index 7282285d..79a08ddb 100644 --- a/src/lib/link_verifier/LinkVerifier.ts +++ b/src/lib/link_verifier/LinkVerifier.ts @@ -2,6 +2,7 @@ import { parseHTML } from 'linkedom'; import type { LinkVerificationStrategyFactory } from './strategy/LinkVerificationStrategy'; import { DefaultLinkVerificationStrategy } from './strategy/DefaultLinkVerificationStrategy'; +import { limitedFetch } from '$lib/limited-fetch'; export const VERIFIABLE_ORIGIN_STRATEGY: Record = { // Put custom link verifiers for specific domains here once we have them. @@ -20,7 +21,7 @@ export class LinkVerifier { } private static async fetchHtml(webLink: string): Promise { - const res = await fetch(webLink); + const res = await limitedFetch({ timeout: 3 * 1000, maxSize: 1 * 1024 * 1024 }, webLink); if (res.status === 200) { const resText = await res.text();