feat: url normalization and http(s) allowlist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-05-08 17:44:44 -04:00
parent 1e4bd8082b
commit 45adba1643
5 changed files with 483 additions and 3 deletions

33
src/lib/url.ts Normal file
View File

@@ -0,0 +1,33 @@
export type UrlValidation =
| { ok: true; url: string }
| { ok: false; error: string };
const ALLOWED_PROTOCOLS = new Set(["http:", "https:"]);
export function normalizeUrl(raw: string): string {
const trimmed = raw.trim();
if (trimmed === "") return trimmed;
if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed)) return trimmed;
return `https://${trimmed}`;
}
export function validateUrl(raw: string): UrlValidation {
const trimmed = raw.trim();
if (trimmed === "") return { ok: false, error: "Enter a URL" };
const candidate = normalizeUrl(trimmed);
let parsed: URL;
try {
parsed = new URL(candidate);
} catch {
return { ok: false, error: "That doesn't look like a URL" };
}
if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) {
return { ok: false, error: "Only http and https URLs are allowed" };
}
if (!parsed.hostname) {
return { ok: false, error: "That doesn't look like a URL" };
}
return { ok: true, url: parsed.toString() };
}