From 41b03bb1420b75391d5e533c163a6ea1bd007b7d Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Mon, 25 May 2026 13:59:04 +0530 Subject: [PATCH] Merge commit from fork The webhook dispatcher validated webhook.url before posting but called requests.post() without allow_redirects=False, so a webhook destination could return a 3xx redirect to an internal address (cloud metadata, internal services) and have the worker fetch it and persist the response body to webhook_logs, readable back via the webhook-logs API. Pass allow_redirects=False so the original validate_url() guard is authoritative. Matches the pattern already used by safe_get() in work_item_link_task.py and the behavior of GitHub/Stripe/Slack webhooks. --- apps/api/plane/bgtasks/webhook_task.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/api/plane/bgtasks/webhook_task.py b/apps/api/plane/bgtasks/webhook_task.py index dd41575056..ddb5299fb5 100644 --- a/apps/api/plane/bgtasks/webhook_task.py +++ b/apps/api/plane/bgtasks/webhook_task.py @@ -334,7 +334,15 @@ def webhook_send_task( ) # Send the webhook event - response = requests.post(webhook.url, headers=headers, json=payload, timeout=30) + # allow_redirects=False prevents SSRF via 3xx hops to internal addresses + # bypassing the validate_url() check above (GHSA-mq87-52pf-hm3h). + response = requests.post( + webhook.url, + headers=headers, + json=payload, + timeout=30, + allow_redirects=False, + ) # Log the webhook request save_webhook_log(