From fda65458c5bdbe0e94dbbf146ffc0708320651ea Mon Sep 17 00:00:00 2001 From: Steven Tey Date: Wed, 2 Sep 2026 18:46:33 -0700 Subject: [PATCH 1/3] Address security advisory (#4445) --- .../groups/[groupIdOrSlug]/route.ts | 34 ++- .../profile/postbacks/[id]/page.tsx | 9 - .../page-client.tsx => [postbackId]/page.tsx} | 11 +- .../profile/postbacks/add-postback-button.tsx | 6 +- apps/web/app/api/analytics/dashboard/route.ts | 39 ++- apps/web/app/api/links/bulk/route.ts | 82 ++++++ .../webhooks/create-webhook-button.tsx | 5 +- .../(share)/share/[dashboardId]/action.ts | 2 +- apps/web/lib/api/links/process-link.ts | 35 ++- .../check-if-links-have-program-partners.ts | 3 + .../playwright/api/links/links-bulk.spec.ts | 268 ++++++++++++++++++ .../ui/partners/program-application-sheet.tsx | 8 +- .../ui/postbacks/postback-secret-modal.tsx | 19 +- 13 files changed, 462 insertions(+), 59 deletions(-) delete mode 100644 apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[id]/page.tsx rename apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/{[id]/page-client.tsx => [postbackId]/page.tsx} (96%) create mode 100644 apps/web/lib/api/links/utils/check-if-links-have-program-partners.ts create mode 100644 apps/web/playwright/api/links/links-bulk.spec.ts diff --git a/apps/web/app/(ee)/api/partner-profile/programs/[programId]/groups/[groupIdOrSlug]/route.ts b/apps/web/app/(ee)/api/partner-profile/programs/[programId]/groups/[groupIdOrSlug]/route.ts index 941b4c130eb..49f516ffc4a 100644 --- a/apps/web/app/(ee)/api/partner-profile/programs/[programId]/groups/[groupIdOrSlug]/route.ts +++ b/apps/web/app/(ee)/api/partner-profile/programs/[programId]/groups/[groupIdOrSlug]/route.ts @@ -1,16 +1,40 @@ -import { getGroupOrThrow } from "@/lib/api/groups/get-group-or-throw"; +import { DubApiError } from "@/lib/api/errors"; import { withPartnerProfile } from "@/lib/auth/partner"; +import { prisma } from "@/lib/prisma"; import { PartnerProgramGroupSchema } from "@/lib/zod/schemas/groups"; import { NextResponse } from "next/server"; // GET /api/partner-profile/programs/[programId]/groups/[groupIdOrSlug] - get information about a program's group -export const GET = withPartnerProfile(async ({ params }) => { +export const GET = withPartnerProfile(async ({ params, partner }) => { const { programId, groupIdOrSlug } = params; - const group = await getGroupOrThrow({ - programId, - groupId: groupIdOrSlug, + const group = await prisma.partnerGroup.findUnique({ + where: { + ...(groupIdOrSlug.startsWith("grp_") + ? { + id: groupIdOrSlug, + } + : { + programId_slug: { + programId, + slug: groupIdOrSlug, + }, + }), + programId, + partners: { + some: { + partnerId: partner.id, + }, + }, + }, }); + if (!group) { + throw new DubApiError({ + code: "not_found", + message: `Group "${groupIdOrSlug}" not found.`, + }); + } + return NextResponse.json(PartnerProgramGroupSchema.parse(group)); }); diff --git a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[id]/page.tsx b/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[id]/page.tsx deleted file mode 100644 index 1058c9b72b1..00000000000 --- a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[id]/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { PostbackDetailPageClient } from "./page-client"; - -export default async function PostbackDetailPage(props: { - params: Promise<{ id: string }>; -}) { - const { id } = await props.params; - - return ; -} diff --git a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[id]/page-client.tsx b/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[postbackId]/page.tsx similarity index 96% rename from apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[id]/page-client.tsx rename to apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[postbackId]/page.tsx index e6f00ec22ff..7f74640d397 100644 --- a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[id]/page-client.tsx +++ b/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/[postbackId]/page.tsx @@ -15,18 +15,13 @@ import { BackLink } from "@/ui/shared/back-link"; import { TokenAvatar } from "@/ui/token-avatar"; import { EmptyState, Webhook } from "@dub/ui"; import { fetcher } from "@dub/utils"; -import { redirect, useRouter } from "next/navigation"; +import { redirect, useParams, useRouter } from "next/navigation"; import { useState } from "react"; import useSWR from "swr"; -interface PostbackDetailPageClientProps { - postbackId: string; -} - -export function PostbackDetailPageClient({ - postbackId, -}: PostbackDetailPageClientProps) { +export default function PostbackDetailPage() { const router = useRouter(); + const { postbackId } = useParams(); const [openPopover, setOpenPopover] = useState(false); const { diff --git a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/add-postback-button.tsx b/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/add-postback-button.tsx index 8e3f5962b41..86ff7b148dc 100644 --- a/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/add-postback-button.tsx +++ b/apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/postbacks/add-postback-button.tsx @@ -5,10 +5,10 @@ import { Button } from "@dub/ui"; export function AddPostbackButton({ onClick }: { onClick: () => void }) { return (