# Trigger.dev: Server-side request forgery via unvalidated webhook alert-channel URL

Low-privilege org member stores unvalidated webhook alert URL in trigger.dev, making the control plane POST signed payloads to internal/metadata endpoints.

- Payout: Research
- Severity: high
- Program: Trigger.dev
- Found by: geo-chen
- Disclosed: 2026-10-02
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-xxv7-2vv3-h682
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-xxv7-2vv3-h682
- Tags: area:web domain:web impact:data-leak impact:internal-access impact:privesc kind:research platform:ghsa sev:high tech:nodejs technique:source-review vuln:ssrf

**Target:** Webapp alert-channel webhook delivery sink (deliverAlert/deliverErrorGroupAlert)
**Root cause:** Webhook URL stored as bare z.string() with no scheme/IP/redirect validation, then fetched directly server-side by the control plane.
**Steps:**
1. Authenticate as any org member with a project-scoped token.
2. `POST /api/v1/projects/<projectRef>/alertChannels` with `channelData.url` set to `http://169.254.169.254/latest/meta-data/iam/security-credentials/`.
3. Cause a task run failure in own environment to fire DeliverAlert.
4. Webapp issues POST to internal URL with valid HMAC signature; observe status/latency via delivery logs.
**How found:** Source review of the alert delivery sink found a raw fetch to a stored URL, then traced the schema (bare z.string()) and API presenter to confirm no validation, and repo-wide search confirmed no SSRF guard anywhere.
**Exploitation:** Attacker creates a webhook alert channel pointing at an internal or metadata address, triggers an alert, and the control plane sends a signed POST to that internal endpoint; blind but request reaches target and timing/status leak via delivery state.
**Insight:** Alert/notification webhook sinks are a recurring SSRF surface: grep for raw fetch() on user-stored URLs and check whether the schema uses z.string() instead of z.string().url() plus IP filtering.

