From 662631a27948d431e6bd37fed15077b1bcccc7f8 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Mon, 1 Jun 2026 15:37:21 +0000 Subject: fix(security): allow-list web-push endpoints to stop SSRF Stored push subscriptions carry a client-supplied `endpoint`, and the notifications job POSTed to it with no host check, so a subscription with an internal/metadata URL turned the Trigger.dev worker into a blind SSRF primitive. Add isAllowedPushEndpoint (https + known vendor hosts: FCM, Mozilla, Apple, WNS), skip non-conforming endpoints in the job, and reject them at subscribe time. Browser-minted subscriptions always match a vendor host, so real delivery is unchanged; a behaviour-gate test asserts vendor endpoints pass and internal/non-https/look-alikes fail. --- src/trigger/notifications.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/trigger') diff --git a/src/trigger/notifications.ts b/src/trigger/notifications.ts index e36f913f..3daca47e 100644 --- a/src/trigger/notifications.ts +++ b/src/trigger/notifications.ts @@ -1,6 +1,7 @@ import { createClient } from "@supabase/supabase-js"; import { envvars, schedules } from "@trigger.dev/sdk"; import * as webpush from "web-push"; +import { isAllowedPushEndpoint } from "../lib/Utility/pushEndpoint"; const isExpiredSubscriptionError = (error: unknown) => { const statusCode = @@ -94,11 +95,11 @@ export const notificationsTask = schedules.task({ for (const subscription of await getUserSubscriptions()) { const endpoint = subscriptionEndpoint(subscription.subscription); - if (endpoint) { - if (seenEndpoints.has(endpoint)) continue; + if (!endpoint || !isAllowedPushEndpoint(endpoint)) continue; - seenEndpoints.add(endpoint); - } + if (seenEndpoints.has(endpoint)) continue; + + seenEndpoints.add(endpoint); try { await webpush.sendNotification(subscription.subscription, "."); -- cgit v1.2.3