# Surface d'exposition

# Surface d'exposition

> _Dernière mise à jour : 2026-05-10_

## Endpoints publics vs internes

### Public (via NPM)
- **Tout `/api/*`** est exposé sur `https://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.yml` n'a pas de `ports:`)
- **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 origine `http://192.168.10.X:Y`)

Parsing : `splitAndTrimArray()` + matching CIDR via `ipaddr.js`. Parsé une fois au boot, comparé à chaque requête.

```env
# 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_KEY` env 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 tout `model` user-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/:page` valide que `:page` est un nombre, et résout via `resolvePageImageFile()` qui contraint au dossier `/app/pdfs/images/<slug>/`. Pas d'accès `../../etc/passwd` possible.

## 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` :

```bash
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)