Security audit: fix IDOR, add rate limiting, HMAC auth, token hashing, XSS sanitization
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m22s

Critical: Add ownership checks to all order routes (IDOR fix).
High: Redis rate limiting on auth endpoints, HMAC-signed internal
service calls replacing header-presence-only checks, nh3 HTML
sanitization on ghost_sync and product import, internal auth on
market API endpoints, SHA-256 hashed OAuth grant/code tokens.
Medium: SECRET_KEY production guard, AP signature enforcement,
is_admin param removal, cart_sid validation, SSRF protection on
remote actor fetch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 13:30:27 +00:00
parent 404449fcab
commit c015f3f02f
27 changed files with 607 additions and 33 deletions

View File

@@ -29,8 +29,43 @@ AP_CONTENT_TYPE = "application/activity+json"
# Helpers
# ---------------------------------------------------------------------------
def _is_safe_url(url: str) -> bool:
"""Reject URLs pointing to private/internal IPs to prevent SSRF."""
from urllib.parse import urlparse
import ipaddress
parsed = urlparse(url)
# Require HTTPS
if parsed.scheme != "https":
return False
hostname = parsed.hostname
if not hostname:
return False
# Block obvious internal hostnames
if hostname in ("localhost", "127.0.0.1", "::1", "0.0.0.0"):
return False
try:
addr = ipaddress.ip_address(hostname)
if addr.is_private or addr.is_loopback or addr.is_reserved or addr.is_link_local:
return False
except ValueError:
# Not an IP literal — hostname is fine (DNS resolution handled by httpx)
# Block common internal DNS patterns
if hostname.endswith(".internal") or hostname.endswith(".local"):
return False
return True
async def fetch_remote_actor(actor_url: str) -> dict | None:
"""Fetch a remote actor's JSON-LD profile."""
if not _is_safe_url(actor_url):
log.warning("Blocked SSRF attempt: %s", actor_url)
return None
try:
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.get(