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:
parent
0f5f1c4c30
commit
15f854d96c
15 changed files with 284 additions and 53 deletions
|
|
@ -4,10 +4,12 @@ import os
|
|||
import shutil
|
||||
import time
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile, File
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, UploadFile, File
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
from database import db
|
||||
from auth import get_current_user, get_current_user_optional
|
||||
from ratelimit import check as rl_check, block_ip
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
|
||||
|
|
@ -17,6 +19,17 @@ SUBMIT_DIR = os.path.join(BREEDS_DIR, "submissions")
|
|||
router = APIRouter()
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Honeypot — URL die kein echter Browser aufruft
|
||||
# GET /api/wiki/trap → IP 24h sperren
|
||||
# ------------------------------------------------------------------
|
||||
@router.get("/trap", include_in_schema=False)
|
||||
async def honeypot(request: Request):
|
||||
block_ip(request, hours=24)
|
||||
logger.warning("Honeypot ausgelöst von %s", request.client.host if request.client else "?")
|
||||
raise HTTPException(404, "Not found")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schemas
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -82,11 +95,13 @@ async def get_stats():
|
|||
# ------------------------------------------------------------------
|
||||
@router.get("/rassen")
|
||||
async def get_rassen(
|
||||
request: Request,
|
||||
search: str = Query(""),
|
||||
gruppe: str = Query(""),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
rl_check(request, max_requests=60, window_seconds=60, key="wiki_list")
|
||||
conditions = []
|
||||
args = []
|
||||
|
||||
|
|
@ -131,7 +146,8 @@ async def get_rassen(
|
|||
# GET /api/wiki/rassen/{slug} — Rasse-Detail + Community-Berichte
|
||||
# ------------------------------------------------------------------
|
||||
@router.get("/rassen/{rasse_slug}")
|
||||
async def get_rasse(rasse_slug: str):
|
||||
async def get_rasse(rasse_slug: str, request: Request):
|
||||
rl_check(request, max_requests=30, window_seconds=60, key="wiki_detail")
|
||||
with db() as conn:
|
||||
rasse = conn.execute(
|
||||
"SELECT * FROM wiki_rassen WHERE slug = ?", (rasse_slug,)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue