ObsiGate/docs/AUDIT_TECHNIQUE_2026-05-27.md
Bruno Charest 58a0ffc76c feat: quick wins - dockerignore, env secrets, gzip, cache-control
- 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)
2026-05-27 20:35:08 -04:00

17 KiB
Raw Permalink Blame History

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é)

  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) :

    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é)

  1. Activer la compression HTTP :

    from fastapi.middleware.gzip import GZipMiddleware
    app.add_middleware(GZipMiddleware, minimum_size=1000)
    
  2. 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/
    
  3. Remplacer bisect par sortedcontainers :

    from sortedcontainers import SortedList
    self._sorted_tokens = SortedList()  # O(log n) insert/remove au lieu de O(n)
    
  4. Pagination de l'API browse pour les très grands dossiers (>1000 entrées)

4.3 Architecture (Impact : Élevé)

  1. 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 :

    <script type="module" src="/static/js/app.js"></script>
    
  2. Extraire la config sensible vers .env :

    # .env (gitignored)
    OBSIGATE_ADMIN_PASSWORD=***
    # .env.example (commité, sans secrets)
    OBSIGATE_ADMIN_PASSWORD=
    
  3. 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é)

  1. 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.

  2. Mode hors-ligne PWA complet : Mettre en cache l'index via IndexedDB pour une recherche offline avec synchronisation lors du retour en ligne.

  3. Plugin système : Permettre des extensions utilisateur (comme Obsidian plugins) — custom renderers, custom search operators, custom dashboard widgets.

  4. 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.

  5. 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)

  1. Rotation des logs Python avec RotatingFileHandler — actuellement pas de limite de taille sur les logs.

  2. OAuth2/OIDC en plus du JWT local (Google, GitHub, authentification SSO).

  3. 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)

  1. Palette de commandes (Ctrl+P style VS Code) pour navigation rapide.

  2. Drag & drop de fichiers dans l'arborescence pour déplacer/réorganiser.

  3. Diff viewer pour comparer les versions backupées (.obsigate-backup/).

  4. 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).