# RAPPORT D'AUDIT TECHNIQUE — OBSIGATE v1.4.0/1.5.0 **Projet :** Portail web ultra-léger pour vaults Obsidian **Auteur :** Bruno Beloeil **Date d'audit :** 27 mai 2026 **Auditeur :** Hermes Agent (DeepSeek V4 Pro) **Méthodologie :** Revue de code exhaustive — 20,457 lignes analysées --- ## 1. VUE D'ENSEMBLE ### 1.1 Statistiques du codebase | Composant | Fichier(s) | Lignes | Langage | |-----------|-----------|--------|---------| | Backend API | `backend/main.py` | 3,105 | Python/FastAPI | | Moteur de recherche | `backend/search.py` | 1,211 | Python | | Indexeur | `backend/indexer.py` | 987 | Python | | Auth (5 modules) | `backend/auth/*.py` | ~600 | Python | | Modules backend | 11 fichiers | ~1,200 | Python | | Frontend SPA | `frontend/app.js` | 8,869 | Vanilla JS | | Styles | `frontend/style.css` | 6,134 | CSS3 (variables) | | Template HTML | `frontend/index.html` | 1,138 | HTML5 | | **Total** | | **~20,457** | | ### 1.2 Architecture ``` ┌─────────────────────────────────────────────────────────┐ │ Navigateur (SPA vanilla JS — 8,869 lignes) │ │ • CodeMirror 6, Lucide Icons, Highlight.js │ │ • PWA (Service Worker, manifest.json) │ │ • dark/light theme via CSS variables │ └──────────────────┬──────────────────────────────────────┘ │ REST API + SSE ┌──────────────────▼──────────────────────────────────────┐ │ FastAPI (backend/main.py — 3,105 lignes) │ │ • 50+ endpoints REST │ │ • SSE Manager (Server-Sent Events) │ │ • Security middleware (CSP, X-Frame, etc.) │ │ • Auth JWT + Argon2id + rate limiting │ └──────┬───────────┬──────────┬──────────┬────────────────┘ │ │ │ │ ┌────▼────┐ ┌───▼────┐ ┌───▼───┐ ┌───▼──────────┐ │ Indexer │ │ Search │ │ Auth │ │ Modules │ │(987 loc)│ │(1211) │ │(~600) │ │watcher,share │ │InMemory │ │TF-IDF │ │JWT │ │pdf,audit, │ │+ lookup │ │+ facets│ │users │ │webhooks,etc │ └─────────┘ └────────┘ └───────┘ └──────────────┘ ``` --- ## 2. FORCES DU PROJET ### 2.1 Architecture (Excellent) - **Séparation propre** backend/frontend sans framework lourd - **In-memory indexing** performant — pas de base de données externe - **Inverted Index TF-IDF** avec mise à jour incrémentale via hooks (plan.md implémenté) - **Watchdog hot-reload** avec debounce (2s) et SSE broadcast - **Multi-stage Docker** (~180MB) avec utilisateur non-root (UID 1000) - **CodeMirror 6** en tant qu'éditeur intégré — choix moderne - **PWA complète** — manifest, service worker, icônes multi-tailles - **Zero dépendance npm** côté frontend — vanilla JS pur ### 2.2 Sécurité (Très bon) - **Path traversal protection** (`_resolve_safe_path`) avec résolution symlink-aware - **JWT + Argon2id** avec refresh tokens sur cookies httpOnly - **Rate limiting** IP-based (10 tentatives/15min) + lockout par compte - **Secret redaction** automatique (JWT, API keys, tokens, connection strings) - **Audit logging** (JSON lines dans `data/audit.log`) avec rotation 10MB - **Backup automatique** avant écriture/suppression (`.obsigate-backup/`) - **Security headers** (CSP, X-Frame-Options, XSS-Protection, Referrer-Policy) - **Safe atomic writes** (tmp + replace) pour shares.json, webhooks.json ### 2.3 Fonctionnalités (Riche) - **Multi-vault** avec contrôle d'accès granulaire par utilisateur - **Recherche avancée** : opérateurs `tag:`, `vault:`, `title:`, `path:`, `ext:`, regex - **Autocomplétion intelligente** : historique + titres + tags avec debounce 150ms - **Wikilinks** résolus avec backlink index bidirectionnel - **Rendu d'images** 7 stratégies de résolution (Obsidian-compatible) - **Mode onglets** (preview/persistant) avec drag-and-drop - **Éditeur intégré** CodeMirror 6 avec syntax highlighting (10+ langages) - **Dashboard** avec 4 panneaux (stats, bookmarks, récents, partagés) - **Partage public** avec tokens 64-char hex, expiration configurable - **Export PDF** via WeasyPrint - **Webhooks** avec signature HMAC-SHA256 - **Find-in-page** avec TreeWalker (regex, case, whole word) - **TOC/Outline** avec scroll spy et barre de progression - **Vault settings** persistés (hideHiddenFiles) - **Saved searches** avec CRUD API ### 2.4 Qualité du code (Bon) - Docstrings Google-style sur toutes les fonctions publiques backend - Annotations de type sur tous les paramètres et retours - Modèles Pydantic documentés avec `Field(description=...)` - Logging structuré par module (`obsigate.indexer`, `obsigate.search`, etc.) - Gestion d'erreurs systématique (try/catch avec toast user-friendly) - Frontend vanilla JS — zéro framework, zéro npm - `"use strict"` + IIFE wrapper - Conventions de nommage cohérentes --- ## 3. PROBLÈMES ET VULNÉRABILITÉS ### 3.1 Critique (P0) **Aucun problème critique détecté.** Les failles P0 identifiées précédemment (rate limiting, secret redaction) ont été corrigées. ### 3.2 Élevé (P1) #### H1 — Pas de tests automatisés Le projet n'a **aucun test** — pas de `pytest`, pas de `tests/`, pas de CI/CD. Avec 20k+ lignes de code, c'est le risque technique numéro 1. Chaque modification est un saut dans l'inconnu. **Impact :** Régressions silencieuses, bugs non détectés, refactoring risqué, onboarding développeur difficile. #### H2 — Pas de CI/CD pipeline Aucun fichier `.github/workflows/` ou équivalent, pas de linting automatisé, pas de vérification de build Docker. Le `build.sh` vérifie les prérequis mais ne run aucun test. #### H3 — Mot de passe admin dans `docker-compose.yml` ```yaml - OBSIGATE_ADMIN_PASSWORD=chab30 ``` C'est le fichier de configuration local — le risque est limité si le dépôt est privé, mais c'est une mauvaise pratique à corriger. **Recommandation :** utiliser un fichier `.env` + `env_file` dans docker-compose. #### H4 — Pas de `.dockerignore` Tout le dossier `.git` et les fichiers de documentation sont copiés dans le contexte Docker, augmentant inutilement la taille du contexte de build et risquant des fuites accidentelles. ### 3.3 Modéré (P2) #### M1 — Frontend monolithique (8,869 lignes dans un seul fichier) `app.js` est très gros pour un fichier unique. La navigation et la maintenance deviennent difficiles. **Suggestion :** Split en modules ES (même sans build step, les `import`/`export` natifs fonctionnent dans tous les navigateurs modernes). #### M2 — Content Security Policy permissive La CSP actuelle autorise `'unsafe-inline'` et plusieurs CDN : ``` script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://unpkg.com https://esm.sh ``` Le risque est théorique (pas d'input utilisateur qui génère du HTML), mais `'unsafe-inline'` pourrait être retiré avec des nonces/hashes. #### M3 — Pas de compression/streaming des réponses Les fichiers statiques sont servis bruts. Activer la compression gzip/brotli dans Uvicorn ou via middleware FastAPI réduirait la bande passante de ~70%. #### M4 — Index chargé entièrement en mémoire Avec 40k+ fichiers, l'index mémoire peut devenir conséquent. Un fichier de 100KB de contenu = 100KB en RAM. 40,000 × 100KB = 4GB théoriques. La limite `SEARCH_CONTENT_LIMIT = 100_000` (100KB par fichier) atténue le problème mais un lazy-loading pourrait être nécessaire à très grande échelle. ### 3.4 Faible (P3) #### L1 — Pas de rate limiting sur les endpoints non-auth Les endpoints comme `/api/health` ne sont pas limités. Faible risque mais un scraper pourrait saturer le serveur. #### L2 — Pas de validation d'extension pour les uploads La création de fichiers via API (`POST /api/file/{vault}`) ne valide pas l'extension. Un utilisateur pourrait créer un `.php` ou `.exe` dans un vault. #### L3 — `handle_file_move` non atomique `remove_single_file` puis `update_single_file` — si le processus crashe entre les deux, l'index est incohérent (fichier supprimé mais pas recréé). #### L4 — Pas d'i18n structurée Tout le texte UI est en dur en français dans le HTML et le JS. Pour une audience internationale, l'internationalisation serait bénéfique. #### L5 — Duplication de `IGNORED_DIRS` Les dossiers ignorés sont définis à deux endroits : `backend/indexer.py` (ligne 69) et `backend/watcher.py` (ligne 17). Une source unique serait préférable. --- ## 4. RECOMMANDATIONS NEXT LEVEL ### 4.1 Tests et CI/CD (Impact : Très Élevé) 1. **Ajouter pytest** avec une couverture minimale de 70% sur le backend : - Tests unitaires sur `search.py` (tokenizer, TF-IDF scoring, snippet extraction, regex search) - Tests unitaires sur `indexer.py` (parsing frontmatter, extraction tags inline/frontmatter, wikilinks) - Tests d'intégration sur l'API (FastAPI `TestClient`) - Tests du rate limiter et de l'auth (JWT flow, lockout, permissions) 2. **Ajouter CI/CD** (GitHub Actions / Gitea Actions) : ```yaml name: CI on: [push, pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: astral-sh/ruff-action@v1 - run: mypy backend/ test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: pip install -r backend/requirements.txt - run: pytest --cov=backend --cov-report=xml security: runs-on: ubuntu-latest steps: - run: pip-audit - run: bandit -r backend/ build: runs-on: ubuntu-latest steps: - run: docker build -t obsigate:ci . ``` 3. **Ajouter Playwright** pour des tests E2E sur les flows critiques (login, recherche, navigation, ouverture de fichier) ### 4.2 Performance (Impact : Élevé) 4. **Activer la compression HTTP :** ```python from fastapi.middleware.gzip import GZipMiddleware app.add_middleware(GZipMiddleware, minimum_size=1000) ``` 5. **Ajouter des en-têtes de cache pour les assets statiques :** ```python # Dans SecurityHeadersMiddleware ou via StaticFiles # Cache-Control: public, max-age=31536000, immutable pour /static/ ``` 6. **Remplacer `bisect` par `sortedcontainers` :** ```python from sortedcontainers import SortedList self._sorted_tokens = SortedList() # O(log n) insert/remove au lieu de O(n) ``` 7. **Pagination de l'API browse** pour les très grands dossiers (>1000 entrées) ### 4.3 Architecture (Impact : Élevé) 8. **Splitter `app.js` en modules ES :** ``` frontend/ js/ core.js (init, state global, api helper) search.js (QueryParser, AutocompleteDropdown, SearchHistory) tabs.js (TabManager) dashboard.js (showWelcome, widgets) editor.js (CodeMirror wrapper) auth.js (AuthManager, login screen) find.js (FindInPageManager) ui.js (helpers: el(), icon(), toast, etc.) ``` Puis dans `index.html` : ```html ``` 9. **Extraire la config sensible vers `.env` :** ```bash # .env (gitignored) OBSIGATE_ADMIN_PASSWORD=*** # .env.example (commité, sans secrets) OBSIGATE_ADMIN_PASSWORD= ``` 10. **Ajouter `.dockerignore` :** ``` .git .gitignore __pycache__ *.pyc .venv test_vault docs *.md !README.md docker-compose.yml .env ``` ### 4.4 Fonctionnalités différenciantes (Impact : Moyen-Élevé) 11. **Full-text search avec stemming français :** ```python from snowballstemmer import FrenchStemmer stemmer = FrenchStemmer() # "mangé" → "mang", "chevaux" → "cheval" ``` Actuellement TF-IDF tokenise mais ne stemme pas, donc "manger" et "mangé" sont des tokens différents. 12. **Mode hors-ligne PWA complet :** Mettre en cache l'index via IndexedDB pour une recherche offline avec synchronisation lors du retour en ligne. 13. **Plugin système :** Permettre des extensions utilisateur (comme Obsidian plugins) — custom renderers, custom search operators, custom dashboard widgets. 14. **Graph view interactive** (code backend déjà présent !) : Les modèles `GraphNode`, `GraphEdge`, `GraphResponse` existent. Finaliser l'endpoint et l'UI pour une vue graphique des wikilinks. 15. **Collaboration temps réel :** Via WebSocket — édition simultanée avec CRDT (Yjs/Automerge), présence utilisateur, commentaires. ### 4.5 Sécurité & Opérations (Impact : Moyen) 16. **Rotation des logs Python** avec `RotatingFileHandler` — actuellement pas de limite de taille sur les logs. 17. **OAuth2/OIDC** en plus du JWT local (Google, GitHub, authentification SSO). 18. **Health check enrichi :** Ajouter l'état de l'index (size, readiness), mémoire utilisée, uptime, nombre de clients SSE. ### 4.6 UX/UI (Impact : Moyen) 19. **Palette de commandes** (`Ctrl+P` style VS Code) pour navigation rapide. 20. **Drag & drop de fichiers** dans l'arborescence pour déplacer/réorganiser. 21. **Diff viewer** pour comparer les versions backupées (`.obsigate-backup/`). 22. **Fichiers récents par vault** dans le dashboard (filtrer par vault actif). --- ## 5. MATRICE DE PRIORITÉ | Rang | Action | Effort | Impact | Risque si ignoré | |------|--------|--------|--------|------------------| | 1 | Tests unitaires backend (pytest) | 3-5 jours | 🔴 Critique | Régressions silencieuses | | 2 | CI/CD pipeline | 1-2 jours | 🔴 Critique | Bugs en production | | 3 | `.dockerignore` | 15 min | 🟠 Élevé | Fuite de code | | 4 | `.env` pour secrets | 10 min | 🟠 Élevé | Secret dans le repo | | 5 | Compression HTTP (GZip) | 5 min | 🟠 Élevé | Bande passante gaspillée | | 6 | Cache-Control headers | 30 min | 🟠 Élevé | Requêtes inutiles | | 7 | Splitter `app.js` en modules | 2-3 jours | 🟡 Moyen | Dette technique | | 8 | `sortedcontainers` pour index | 2 heures | 🟡 Moyen | Performance à l'échelle | | 9 | Stemming français | 2-4 heures | 🟡 Moyen | Qualité recherche | | 10 | Mode hors-ligne PWA | 3-5 jours | 🟡 Moyen | Feature gap | | 11 | OAuth2/OIDC | 2-3 jours | 🟢 Faible | Adoption entreprise | | 12 | Plugin système | 5-10 jours | 🟢 Faible | Extensibilité | --- ## 6. VERDICT GLOBAL ObsiGate est un projet **impressionnant par sa complétude et sa qualité** pour une codebase de cette taille développée en solo. Les fondamentaux sont solides : - ✅ Sécurité bien traitée (path traversal, JWT, Argon2id, rate limiting, audit, backup) - ✅ Architecture propre et cohérente (séparation backend/frontend, modules bien découpés côté backend) - ✅ Fonctionnalités riches (50+ endpoints, recherche TF-IDF, PWA, éditeur intégré) - ✅ Attention aux détails (docstrings, type hints, Pydantic models documentés, logging structuré) - ✅ Choix techniques judicieux (FastAPI, CodeMirror 6, vanilla JS, multi-stage Docker) Les axes d'amélioration sont clairs et actionnables. Le passage de "bon projet" à "projet professionnel de niveau production" passe par **trois piliers :** 1. **Tests** — c'est le maillon faible critique, zéro test aujourd'hui 2. **CI/CD** — automatiser la qualité pour chaque commit 3. **Modularisation du frontend** — préparer la scalabilité du code Le reste (OAuth, stemming, mode hors-ligne) sont des fonctionnalités qui différencieront ObsiGate des alternatives comme Obsidian Publish. ### Note technique | Dimension | Note | Commentaire | |-----------|------|-------------| | Architecture | 8/10 | Propre, bien pensée, scalable | | Sécurité | 8/10 | Excellentes bases, quelques améliorations possibles | | Qualité du code | 7/10 | Bon backend, frontend monolithique | | Performance | 7/10 | Index mémoire efficace, compression manquante | | Tests | 0/10 | **Aucun test** — priorité absolue | | Documentation | 7/10 | README excellent, docs/ fourni, manque CHANGELOG | | DevOps | 6/10 | Docker OK, pas de CI/CD, pas de .dockerignore | | **Global** | **7.5/10** | Excellent pour un projet solo, potentiel 9/10 | --- *Rapport généré le 27 mai 2026 par audit automatisé Hermes Agent.* *Ce document complète `ANALYSE_REVIEW.md` (bugs dashboard/search) et `ROADMAP.md` (fonctionnalités planifiées).*