- Add .dockerignore to exclude .git, __pycache__, docs, etc. from Docker context - Create .env.example template with documented env vars - Move OBSIGATE_ADMIN_PASSWORD from docker-compose.yml to env_file: .env - Add .env.* to .gitignore (excluding .env.example) - Enable GZipMiddleware for ~70% bandwidth reduction on text responses - Add Cache-Control: immutable for /static/ assets - Update ROADMAP: mark all 4 quick wins as done, add audit findings - Add comprehensive technical audit report (AUDIT_TECHNIQUE_2026-05-27.md)
17 KiB
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
- 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é)
-
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)
- Tests unitaires sur
-
Ajouter CI/CD (GitHub Actions / Gitea Actions) :
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 . -
Ajouter Playwright pour des tests E2E sur les flows critiques (login, recherche, navigation, ouverture de fichier)
4.2 Performance (Impact : Élevé)
-
Activer la compression HTTP :
from fastapi.middleware.gzip import GZipMiddleware app.add_middleware(GZipMiddleware, minimum_size=1000) -
Ajouter des en-têtes de cache pour les assets statiques :
# Dans SecurityHeadersMiddleware ou via StaticFiles # Cache-Control: public, max-age=31536000, immutable pour /static/ -
Remplacer
bisectparsortedcontainers:from sortedcontainers import SortedList self._sorted_tokens = SortedList() # O(log n) insert/remove au lieu de O(n) -
Pagination de l'API browse pour les très grands dossiers (>1000 entrées)
4.3 Architecture (Impact : Élevé)
-
Splitter
app.jsen 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:<script type="module" src="/static/js/app.js"></script> -
Extraire la config sensible vers
.env:# .env (gitignored) OBSIGATE_ADMIN_PASSWORD=*** # .env.example (commité, sans secrets) OBSIGATE_ADMIN_PASSWORD= -
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é)
-
Full-text search avec stemming français :
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.
-
Mode hors-ligne PWA complet : Mettre en cache l'index via IndexedDB pour une recherche offline avec synchronisation lors du retour en ligne.
-
Plugin système : Permettre des extensions utilisateur (comme Obsidian plugins) — custom renderers, custom search operators, custom dashboard widgets.
-
Graph view interactive (code backend déjà présent !) : Les modèles
GraphNode,GraphEdge,GraphResponseexistent. Finaliser l'endpoint et l'UI pour une vue graphique des wikilinks. -
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)
-
Rotation des logs Python avec
RotatingFileHandler— actuellement pas de limite de taille sur les logs. -
OAuth2/OIDC en plus du JWT local (Google, GitHub, authentification SSO).
-
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)
-
Palette de commandes (
Ctrl+Pstyle VS Code) pour navigation rapide. -
Drag & drop de fichiers dans l'arborescence pour déplacer/réorganiser.
-
Diff viewer pour comparer les versions backupées (
.obsigate-backup/). -
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 :
- Tests — c'est le maillon faible critique, zéro test aujourd'hui
- CI/CD — automatiser la qualité pour chaque commit
- 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).