Surface d'exposition
Surface d'exposition
Dernière mise à jour : 2026-05-10
Endpoints publics vs internes
Public (via NPM)
- Tout
/api/*est exposé surhttps://rules.thymon.fr - Frontend SPA servi à
https://rules.thymon.fr/ - Mais : auth requise sur quasi tous les endpoints (sauf
/api/health,/api/auth/login,/api/auth/register,/api/confirm-user/:token)
Interne (LAN seulement)
- Container app
:3000: pas exposé sur l'hôte (docker-compose.ymln'a pas deports:) - Qdrant
:6333: exposé sur LAN (192.168.10.100:6333) pour que d'autres apps puissent le consommer (pas idéal — voir ci-dessous) - TEI
:8099, Reranker:8990: LAN seulement
CORS
config.CORS_ORIGIN whitelist :
- Origines exactes :
https://rules.thymon.fr - CIDR IPv4 LAN :
192.168.10.0/24(matche n'importe quelle originehttp://192.168.10.X:Y)
Parsing : splitAndTrimArray() + matching CIDR via ipaddr.js. Parsé une fois au boot, comparé à chaque requête.
# Exemple .env prod
CORS_ORIGIN=https://rules.thymon.fr,192.168.10.0/24
⚠️ Ne pas laisser CORS_ORIGIN=* en prod — annule la protection.
Headers sécurité
Gérés par NPM en amont (Advanced → Custom Nginx Configuration) :
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; ... (à durcir si nécessaire)
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: ...
Vérifier : curl -I https://rules.thymon.fr.
Ports ouverts (Unraid host)
À auditer périodiquement avec nmap -p- localhost depuis Unraid CLI :
| Port | Service | Exposition |
|---|---|---|
| 80, 443 | NPM (entrée publique) | Internet |
| 22 | SSH Unraid | LAN seulement (firewall) |
| 6333 | Qdrant | LAN |
| 8099 | TEI | LAN |
| 8990 | TEI Reranker | LAN |
| 3000 | App | LAN seulement |
⚠️ Qdrant exposé sur LAN sans auth : si quelqu'un a accès au LAN, il peut lire / modifier toutes les collections vectorielles. Acceptable pour un home network privé, mais à durcir si le LAN devient moins de confiance (genre invités, IoT) :
- Activer auth Qdrant :
QDRANT__SERVICE__API_KEY=<random>côté config Qdrant - Mettre cette clé dans
QDRANT_API_KEYenv var côté backend - Le client Qdrant la passe en header
api-key
Injection / validation
- Inputs API : tous validés via Zod (
src/lib/schemas.ts). UUID v4 partout, longueurs bornées, enums stricts. - SQL injection : impossible — Drizzle ORM utilise des prepared statements. Pas de SQL string-concat.
- Shell injection (Claude SSH) :
validateModel()valide toutmodeluser-provided avant interpolation. Le wrapper oracle valide le préfixe strict. Le system prompt est passé via stdin JSON, pas en argument. - Path traversal (page-image) :
GET /api/games/:id/page-image/:pagevalide que:pageest un nombre, et résout viaresolvePageImageFile()qui contraint au dossier/app/pdfs/images/<slug>/. Pas d'accès../../etc/passwdpossible.
Rate limiting
- Login : 5 tentatives / IP / 15 min
- Ask : 20 questions / user / min
- Deck parse : 10 / user / min
Rate-limiters in-memory (Map). Restart container = compteurs reset.
Couverture log
Les actions sensibles sont logées :
- Login (succès et échec) avec IP + username
- Création / suppression de user
- Action admin (sync cards, export feedback, send password reset)
- Erreurs SSH / quota / wrapper rejet
Filtrer dans /app/data/logs/server.log :
grep -E "auth|admin|ssh|quota" /app/data/logs/server.log
CVE / dépendances vulnérables
npm audit régulièrement. Niveau acceptable : --audit-level=high. Critical → fix immédiat.
Pas de scanner CI auto actuellement. À ajouter : un job audit dans .gitea/workflows/build.yml qui fail sur high+.
Pas de WAF
NPM ne fait pas de WAF (rules custom Nginx limitées). Si un jour tu veux du WAF :
- Cloudflare en frontal (mais SSL passthrough OK avec NPM)
- ModSecurity dans NPM
Pour l'usage actuel (single user, LAN-friendly), c'est overkill.
Données collectées / RGPD
Ce que l'app stocke :
- Users : username, email, password hash, role
- Questions : contenu de la question, réponse Claude, vote, comment, diagnostics RAG
- PDFs : règles de jeux uploadés (potentiellement copyrightées — usage personnel acceptable)
- Sessions : in-memory, perdues au restart
Pas d'analytics tiers (Google Analytics, Hotjar, etc.). Logs serveur en local.
Pour un usage perso, RGPD n'est pas un sujet. Si tu ouvres à des tiers :
- Politique de confidentialité claire
- Droit à l'effacement (DELETE user supprime cascade les questions)
- Droit d'accès (export CSV des questions de l'utilisateur — pas implémenté actuellement)
No comments to display
No comments to display