# Changelog — ObsiGate Toutes les modifications notables d'ObsiGate sont documentées dans ce fichier. Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/), et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). > **En cours de développement** : les changements non publiés sont dans la section > [Unreleased](#unreleased). La dernière version publiée est **2.2.1**. --- ## [Unreleased] ### Sécurité - **#84 Consolidation & sécurité — phase 1 (BUG-021 → BUG-034)** — traitement des vulnérabilités de la revue statique du 2026-09-13 : - **BUG-021/022 — XSS stocké** : nouveau sanitizer serveur en liste blanche (`backend/services/sanitizer.py`, stdlib) appliqué au rendu markdown ; échappement systématique du `title`, du frontmatter et du JSON de la page publique `/s/{token}` (`` neutralisé). Tests : `tests/test_security_hardening.py` (9). - **BUG-023 — brute-force MFA** : rate-limit IP + compte et verrouillage de compte sur `mfa/totp/verify`, `mfa/recovery` et `mfa/webauthn/verify` (`_enforce_mfa_rate_limit`). - **BUG-024 — traversal inter-vaults** : `resolve_safe_path` compare désormais les chemins par **segment** (`Path.relative_to` + repli casse-insensible), plus par préfixe de chaîne : `vault` ne peut plus lire `vault-evil`. - **BUG-025 — ReDoS** : validation des regex utilisateur (longueur max, rejet des quantificateurs imbriqués/backrefs), contenu tronqué et nombre de matchs plafonné (`backend/services/regex_safety.py`), appliqué à la recherche avancée et au find/replace. - **BUG-026 — SSRF webhooks** : validation d'URL (HTTPS par défaut, IP privées/boucle interdites, résolution DNS vérifiée au dispatch, redirections non suivies) et **externalisation du secret** dans `data/webhook_secrets.json` (0600) ou variable `OBSIGATE_WEBHOOK_SECRET_` (plus de secret en clair dans `webhooks.json`). - **BUG-027 — sessions** : rotation du refresh token à chaque usage, révocation du JTI de l'access token au logout, et vérification de la révocation dans le middleware. - **BUG-028 — politique de mot de passe** : validation centralisée (8–128 caractères) à la création, à la modification admin et au changement ; `password_changed_at` invalide tous les jetons émis avant un changement de mot de passe. - **BUG-029 — race `users.json`** : verrou `threading.RLock` autour des cycles lecture-modification-écriture. - **BUG-030 — audits** : l'adresse IP réelle du client (`X-Forwarded-For` si `OBSIGATE_TRUST_PROXY=true`) est injectée dans `current_user` et consignée dans les audits. - **BUG-031 — rate-limiter** : budget **par compte** en plus du budget par IP (rotation d'IP neutralisée) ; limite mono-process documentée. - **BUG-032 — indexation** : `_scan_vault` utilise `os.walk(followlinks=False)` et refuse tout symlink sortant de la racine du vault. - **BUG-033 — recherche O(N)** : la recherche simple et l'outil IA `search_fulltext` utilisent l'inverted index (repli sur le scan pendant la construction). - **BUG-034 — CSP & jetons** : le token d'accès n'est plus persisté dans `sessionStorage` (mémoire + cookie `HttpOnly`) ; directives CSP durcies (`object-src`, `base-uri`, `form-action`, `frame-ancestors`). *Reste : migration CSP par nonce (exige la conversion des gestionnaires d'événements inline).* ### Ajouté - **#82 Assistant IA — menu `@` instantané** — nouvel endpoint `GET /api/vault/{vault}/paths` (liste plate et plafonnée de l'index des chemins). Le frontend la récupère **une fois par vault** (préchargée à l'ouverture du panneau) puis filtre **côté client** pendant la frappe : l'affichage des fichiers/répertoires est immédiat, au lieu d'une requête réseau par caractère. Tests : `tests/test_api_main.py` (+2), `tests/frontend/ai.test.mjs` (+1). - **#82 Assistant IA — Section « Fournisseur & modèle » compacte** — la barre de sélection du modèle devient **discrète** : la liste complète des types d'endpoints (Chat, Embeddings, Vision…) est remplacée par un **bouton d'information ⓘ** qui affiche les capacités dans une **bulle** au survol, au clic ou par **appui long** (mobile). Le choix du modèle passe par une **liste déroulante avec recherche** (filtrage insensible aux accents) au lieu d'un `