diff --git a/backend/app/routers/settings.py b/backend/app/routers/settings.py index afba67d..e6b205a 100644 --- a/backend/app/routers/settings.py +++ b/backend/app/routers/settings.py @@ -62,6 +62,14 @@ async def update_settings( """Update settings.""" update_data = settings_update.model_dump(exclude_unset=True) + # PT-L02: SSRF-validate ntfy_server_url at save time, not just at dispatch + if "ntfy_server_url" in update_data and update_data["ntfy_server_url"]: + from app.services.ntfy import validate_ntfy_host + try: + validate_ntfy_host(update_data["ntfy_server_url"]) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + for key, value in update_data.items(): setattr(current_settings, key, value) diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 3a39185..bae1954 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -100,6 +100,9 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $forwarded_proto; proxy_cache_bypass $http_upgrade; + + # PT-L01: Prevent browser caching of authenticated API responses + add_header Cache-Control "no-store, no-cache, must-revalidate" always; } # SPA fallback - serve index.html for all routes @@ -124,4 +127,6 @@ server { add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com; connect-src 'self';" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + # PT-I03: Restrict unnecessary browser APIs + add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always; }