Release v1.7.15: security hardening, async auth, CSP tightening, model validation, SSRF guard, rate limiting improvements, frontend extraction, Docker compose security
Release / build-and-push (push) Successful in 3m12s
Release / build-and-push (push) Successful in 3m12s
This commit is contained in:
@@ -4,7 +4,9 @@ Supported channels:
|
||||
- webhook: POST JSON to any URL (Slack, Teams, generic)
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
from datetime import UTC, datetime
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
import structlog
|
||||
@@ -15,6 +17,26 @@ logger = structlog.get_logger("aoc.notifications")
|
||||
WEBHOOK_TIMEOUT = 15
|
||||
|
||||
|
||||
def _validate_webhook_url(url: str):
|
||||
"""Prevent SSRF by rejecting internal/reserved addresses."""
|
||||
parsed = urlparse(url)
|
||||
if parsed.scheme not in ("http", "https"):
|
||||
raise ValueError(f"Webhook URL scheme '{parsed.scheme}' is not allowed")
|
||||
hostname = (parsed.hostname or "").lower()
|
||||
if not hostname:
|
||||
raise ValueError("Webhook URL must have a valid hostname")
|
||||
blocked = {"localhost", "127.0.0.1", "0.0.0.0", "::1", "169.254.169.254"}
|
||||
if hostname in blocked:
|
||||
raise ValueError(f"Webhook URL hostname '{hostname}' is not allowed")
|
||||
try:
|
||||
ip = ipaddress.ip_address(hostname)
|
||||
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
|
||||
raise ValueError(f"Webhook URL IP '{hostname}' is not allowed")
|
||||
except ValueError as exc:
|
||||
if "not allowed" in str(exc):
|
||||
raise
|
||||
|
||||
|
||||
@retry(
|
||||
stop=stop_after_attempt(3),
|
||||
wait=wait_exponential(multiplier=1, min=2, max=10),
|
||||
@@ -142,6 +164,12 @@ def send_notification(
|
||||
if not webhook_url:
|
||||
return False
|
||||
|
||||
try:
|
||||
_validate_webhook_url(webhook_url)
|
||||
except ValueError as exc:
|
||||
logger.warning("Notification blocked: invalid webhook URL", error=str(exc))
|
||||
return False
|
||||
|
||||
builders = {
|
||||
"slack": _build_slack_payload,
|
||||
"teams": _build_teams_payload,
|
||||
|
||||
Reference in New Issue
Block a user