• # Puisque tu aimes bien l'IA...

    Posté par (Mastodon) . En réponse au journal À la recherche d'une alternative libre à Notion ou Obsidian : j'ai créé MindZJ (OSS) via Vibe coding. Évalué à 6 (+4/-1). Dernière modification le 23 avril 2026 à 08:29.

    Sommaire

    Rapport d'audit de sécurité — MindZJ

    Date : 2026年04月23日

    Analyste : Audit automatisé (Claude Code)

    Périmètre : Codebase complet git clone git@github.com:zjok/mindzj.git

    Application : MindZJ — application Tauri de prise de notes


    Résumé exécutif

    L'audit a identifié 5 vulnérabilités critiques et 3 vulnérabilités hautes dans le codebase. Les problèmes les plus graves concernent l'absence de Content Security Policy, un accès non restreint au système de fichiers, et l'exécution de code plugin sans sandbox. Combinées, ces failles permettent à un attaquant d'exécuter du code arbitraire et de lire l'intégralité du système de fichiers de l'utilisateur.


    Vulnérabilités critiques

    C1 — Content Security Policy désactivée

    Fichier : src-tauri/tauri.conf.json:37

    Sévérité : Critique

    "csp": null

    La CSP est complètement absente. Aucune protection contre l'injection de scripts, de styles ou de ressources externes. Toute attaque XSS réussit sans friction — le navigateur embarqué n'a aucune barrière à opposer.

    Remédiation : Définir une CSP stricte.

    "csp": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: asset:; font-src 'self'; connect-src 'self';"

    C2 — Asset protocol scope "**" : accès à tout le système de fichiers

    Fichier : src-tauri/tauri.conf.json:32-34

    Sévérité : Critique

    "scope": {
     "allow": ["**", "**/.mindzj/**"],
     "requireLiteralLeadingDot": false
    }

    Le wildcard ** autorise l'accès à tous les fichiers du système via le protocole asset://. En combinaison avec une XSS (C3 ou C4), un attaquant peut lire /etc/passwd, les clés SSH (~/.ssh/), les tokens d'environnement, ou tout autre fichier accessible à l'utilisateur courant.

    Remédiation : Restreindre le scope au répertoire du vault uniquement.

    "scope": {
     "allow": ["$APPDATA/**", "$HOME/.mindzj/**"],
     "requireLiteralLeadingDot": true
    }

    C3 — Exécution de code plugin sans sandbox (new Function)

    Fichier : src/stores/plugins.ts:1188

    Sévérité : Critique

    const factory = new Function("module", "exports", "require", code);

    Le code source des plugins est exécuté directement dans le contexte de la fenêtre principale, sans aucun isolement. Un plugin malveillant ou compromis (supply chain) dispose d'un accès complet au DOM, aux données du vault, aux APIs Tauri et au réseau.

    Remédiation : Exécuter les plugins dans un <iframe> sandboxé ou un Worker, avec une API de communication contrôlée (postMessage). Implémenter la vérification de signature cryptographique des plugins avant chargement.


    C4 — KaTeX configuré avec trust: true

    Fichier : src/components/editor/ReadingView.tsx:296,477

    Sévérité : Critique

    katex.renderToString(formula, { trust: true, ... })

    L'option trust: true autorise KaTeX à exécuter des macros arbitraires, notamment \href, \url et d'autres commandes pouvant générer du HTML non échappé. Vecteur XSS direct via le contenu mathématique des notes.

    Remédiation : Passer trust: false (valeur par défaut). Si certaines macros sont nécessaires, utiliser la forme fonction pour n'autoriser qu'une liste blanche explicite.

    katex.renderToString(formula, {
     trust: (context) => context.command === '\\url',
     ...
    })

    C5 — innerHTML utilisé sans validation suffisante

    Fichiers : src/components/editor/ReadingView.tsx:758,1379src/stores/plugins.ts (13+ occurrences)

    Sévérité : Critique

    // ReadingView.tsx:1379
    containerRef.innerHTML = html;
    // ReadingView.tsx:758
    block.innerHTML = svg; // SVG Mermaid non validé

    Le HTML généré par le parseur Markdown et les rendus SVG Mermaid sont injectés directement dans le DOM. Le SVG Mermaid en particulier n'est pas assaini avant injection, ce qui permet d'y embarquer des gestionnaires d'événements ou des éléments <script>.

    Remédiation : Passer tout HTML externe par DOMPurify avant insertion. Pour Mermaid, utiliser le mode securityLevel: 'strict'.

    import DOMPurify from 'dompurify';
    containerRef.innerHTML = DOMPurify.sanitize(html);

    Vulnérabilités hautes

    H1 — URLs javascript: non filtrées dans les liens

    Fichier : src/components/editor/ReadingView.tsx:523

    Sévérité : Haute

    <a href="${escapeAttr(url)}" class="mz-rv-link" ...>

    escapeAttr() échappe les caractères HTML mais ne filtre pas les protocoles d'URL. Un lien Markdown de la forme [clic](javascript:alert(1)) génère une balise <a href="javascript:alert(1)"> fonctionnelle.

    Remédiation : Valider le protocole de toute URL avant insertion.

    function isSafeUrl(url: string): boolean {
     try {
     const parsed = new URL(url);
     return ['http:', 'https:', 'obsidian:'].includes(parsed.protocol);
     } catch {
     return url.startsWith('/') || url.startsWith('./');
     }
    }

    H2 — DOMPurify en version vulnérable (≤ 3.3.3)

    Fichier : package.json

    Sévérité : Haute

    La version installée de DOMPurify est affectée par 4 CVEs actifs :

    CVE Impact
    GHSA-39q2-94rc-95cp Bypass via ADD_TAGS
    GHSA-h7mw-gpvr-xq4m Bypass via FORBID_TAGS
    GHSA-crv5-9vww-q3g8 Bypass SAFE_FOR_TEMPLATES
    GHSA-v9jr-rg53-9pgp Prototype pollution → XSS

    Remédiation : Mettre à jour vers dompurify >= 3.4.0.

    npm update dompurify

    H3 — Clé API stockée en clair (hash) sur le disque

    Fichier : cli/src/commands.rs

    Sévérité : Haute

    Le hash de la clé API est écrit dans .mindzj/api_key_hash, un fichier texte lisible par tout processus s'exécutant sous le même utilisateur. Le crate keyring est déjà listé en dépendance dans Cargo.toml mais n'est pas utilisé pour ce secret.

    Remédiation : Stocker la clé API dans le keyring système via le crate déjà disponible.

    usekeyring::Entry;
    letentry=Entry::new("mindzj","api_key")?;
    entry.set_password(&api_key)?;

    Problèmes additionnels (sévérité moyenne)

    # Localisation Description
    M1 src/stores/plugins.ts:20-265 Monkey-patching de HTMLElement.prototype — surface d'attaque pour les plugins
    M2 src-tauri/src/api/screenshot_api.rs:69-74 Race condition TOCTOU sur le nom de fichier temporaire (timestamp non unique)
    M3 src-tauri/src/api/vault_api.rs:534-539 Décodage base64 sans limite de taille — vecteur DoS par expansion mémoire

    Plan de remédiation

    Priorité Action Fichier cible
    Immédiat Activer la CSP tauri.conf.json
    Immédiat Restreindre l'asset scope au vault tauri.conf.json
    Immédiat Passer trust: false sur KaTeX ReadingView.tsx
    Immédiat Sandboxer l'exécution des plugins plugins.ts
    Urgent Filtrer les URLs javascript: ReadingView.tsx
    Urgent Assainir les SVG Mermaid avec DOMPurify ReadingView.tsx
    Urgent Upgrade dompurify ≥ 3.4.0 package.json
    Court terme Migrer la clé API vers le keyring système cli/src/commands.rs
    Court terme Ajouter une limite de taille sur le décodage base64 vault_api.rs
    Court terme Utiliser tempfile crate pour les screenshots screenshot_api.rs

    Fichiers les plus critiques à auditer en priorité

    src-tauri/tauri.conf.json ← configuration Tauri (C1, C2)
    src/components/editor/ReadingView.tsx ← rendu Markdown/KaTeX/Mermaid (C4, C5, H1)
    src/stores/plugins.ts ← exécution et API plugins (C3, C5)
    cli/src/commands.rs ← gestion clé API (H3)
    

    En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.