Session 2026-04-23: Security, Content-Schutz, Wiki-Temperament-Migration

Security (9 Fixes):
- JWT_SECRET Pflicht-Check beim Start (Production)
- Rate-Limit: Login (10/5min), Register (5/h), KI-Training (10/h), Giftköder (3/h)
- KI-Training-Endpoint: Auth-Pflicht hinzugefügt
- Private Profile aus Freunde-Suche gefiltert
- OG-Tags XSS mit html.escape() gesichert
- Globales File-Upload-Limit 20 MB (Middleware)
- E-Mail-Maskierung für Moderatoren im Admin-Panel
- IP-Blocklist in ratelimit.py

Content-Schutz (4 Schichten):
- robots.txt: /api/ komplett Disallow, SSR-Seiten Allow
- Rate-Limit auf /api/wiki/rassen (60/min) + Detail (30/min)
- Honeypot /api/wiki/trap + unsichtbarer Link in index.html
- Wasserzeichen in KI-Enricher-Prompt

Wiki Temperament-Migration:
- 60-Wort Übersetzungsmap EN→DE
- Datenmüll-Filter (hunderasse, dog breed etc.)
- translate_existing_temperaments() + Admin-Button
- SW by-v318, APP_VER 306
This commit is contained in:
rene 2026-04-23 18:34:05 +02:00
parent 0f5f1c4c30
commit 15f854d96c
15 changed files with 284 additions and 53 deletions

View file

@ -2,13 +2,14 @@
import os, uuid, math
from datetime import datetime, timedelta
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile, File
from pydantic import BaseModel
from typing import Optional
from database import db
from auth import get_current_user
from routes.push import send_push_to_all
from media_utils import convert_media
from ratelimit import check as rl_check
router = APIRouter()
MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
@ -74,7 +75,9 @@ async def list_poison(lat: float, lon: float, radius: int = 5000):
# POST /api/poison — neue Meldung (Login erforderlich)
# ------------------------------------------------------------------
@router.post("", status_code=201)
async def report_poison(data: PoisonCreate, user=Depends(get_current_user)):
async def report_poison(data: PoisonCreate, request: Request,
user=Depends(get_current_user)):
rl_check(request, max_requests=3, window_seconds=3600, key="poison")
expires_at = (datetime.utcnow() + timedelta(days=7)).isoformat()
with db() as conn:
conn.execute(