🛡️ Sentinel: [HIGH] Fix Server-Side Request Forgery in webhooks
Adds validation to webhook URLs before attempting to deliver them to prevent SSRF attacks targeting private IP ranges, local host, and cloud metadata endpoints. Validates URL schema, hostname, and applies `is_private_ip()`. Also resolved ruff linting errors. Tests have been expanded to ensure validation covers all cases. Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import smtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -128,6 +129,31 @@ def _send_webhook_notification(target_config: dict[str, Any], event_type: str, t
|
||||
logger.warning("Webhook notification target missing url")
|
||||
return False
|
||||
|
||||
from app.utils.network import is_private_ip
|
||||
|
||||
parsed_url = urlparse(url)
|
||||
if parsed_url.scheme not in ("http", "https"):
|
||||
logger.warning("Webhook notification to %s blocked: Invalid scheme %s", url, parsed_url.scheme)
|
||||
return False
|
||||
|
||||
hostname = parsed_url.hostname
|
||||
if not hostname:
|
||||
logger.warning("Webhook notification to %s blocked: No hostname", url)
|
||||
return False
|
||||
|
||||
if is_private_ip(hostname):
|
||||
logger.warning("Webhook notification to %s blocked: Private IP", url)
|
||||
return False
|
||||
|
||||
metadata_endpoints = [
|
||||
"169.254.169.254", # AWS, Azure, GCP metadata
|
||||
"metadata.google.internal", # GCP
|
||||
"169.254.169.253", # AWS link-local
|
||||
]
|
||||
if hostname in metadata_endpoints:
|
||||
logger.warning("Webhook notification to %s blocked: Metadata endpoint", url)
|
||||
return False
|
||||
|
||||
payload = {
|
||||
"event": event_type,
|
||||
"title": title,
|
||||
|
||||
Reference in New Issue
Block a user