diff --git a/src/app/out/page.tsx b/src/app/out/page.tsx new file mode 100644 index 0000000..d72c91b --- /dev/null +++ b/src/app/out/page.tsx @@ -0,0 +1,72 @@ +import { Metadata } from "next"; +import Link from "next/link"; +import { redirect } from "next/navigation"; +import { Icon } from "@iconify/react"; + +export const metadata: Metadata = { + title: "Leaving TomodachiShare", + description: "Warning: You are leaving TomodachiShare, proceed with caution", +}; + +interface Props { + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +} + +export default async function LinkOutPage({ searchParams }: Props) { + const url = (await searchParams).url; + if (!url || Array.isArray(url)) redirect("/"); + + let parsed: URL; + try { + parsed = new URL(url); + } catch { + redirect("/"); // redirect if URL is invalid + } + + // Next.js doesn't allow attacks like these but you can never be too safe + if (!["http:", "https:"].includes(parsed.protocol)) redirect("/"); + + const isSafe = Array.from(SAFE_LINKS).some((domain) => parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`)); + if (isSafe) redirect(url); + + return ( +
You're attempting to leave TomodachiShare island! The destination website is potentially dangerous.
+ +{url}
+
- {/* Adds fancy formatting when linking to other pages on the site */}
- {(() => {
- const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "https://tomodachishare.com";
+ {parts.map(async (part, index) => {
+ try {
+ // Check if it's a URL
+ if (!urlRegex.test(part)) throw new Error("Not a URL");
+ const url = new URL(part);
- // Match both mii and profile links
- const regex = new RegExp(`(${baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")}/(?:mii|profile)/\\d+)`, "g");
- const parts = text.split(regex);
-
- return parts.map(async (part, index) => {
- const miiMatch = part.match(new RegExp(`^${baseUrl}/mii/(\\d+)$`));
- const profileMatch = part.match(new RegExp(`^${baseUrl}/profile/(\\d+)$`));
-
- if (miiMatch) {
- const id = Number(miiMatch[1]);
- const linkedMii = await prisma.mii.findUnique({
- where: {
- id,
- },
- });
-
- if (!linkedMii) return;
-
- return (
-
-