From ce1944d437951f9e894a4566def5a5c52266d951 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Wed, 7 Oct 2026 16:49:27 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20coller=20du=20menu=20contextuel=20tableu?= =?UTF-8?q?r=20muet=20+=20planchers=20s=C3=A9curit=C3=A9=20CI=20(BUG-108)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG-108 — closeContextMenu() retirait le nœud du menu AVANT de déférencer _menu : le retrait émet focusout en synchrone (le menu tient le focus), qui rappelle closeContextMenu() ; le second remove() sur un nœud démonté lève NotFoundError et avorte le handler de l'action « Coller » du viewer — le coller par menu contextuel était silencieusement mort (régression de la fermeture au focus #179, reproduite par l'E2E « coller une plage », CI #850→#854). Déférencement avant retrait. Sécurité CI (job security, run #853) : - plancher multidict>=6.9.1 (CVE-2026-104874 ; 6.7.x préinstallée dans la toolcache de l'image du runner, même piège « already satisfied » que pypdf BUG-093) + entrée dans le garde-fou TestDependencySecurityFloors ; - exception documentée CVE-2026-85394 (python-jose, AUCUN correctif upstream) : non atteignable ici, jwt.decode passe toujours algorithms=["HS256"] avec un secret symétrique serveur — jamais de clé publique comme clé HMAC. Vérifié : E2E -g « coller une plage » 1/1 ; xlsx-menus 11/11, xlsx-formula 14/14, ai 100/100, ai-quick-actions 13/13 ; pytest 1586 passés ; ruff/mypy 0 erreur ; ISSUES_TODOLIST + CHANGELOG à jour. --- .gitea/workflows/ci.yml | 11 ++++++++++- CHANGELOG.md | 17 ++++++++++++++++- README.fr.md | 6 +++--- README.md | 6 +++--- VERSION | 2 +- backend/requirements.txt | 5 +++++ desktop/Cargo.lock | 2 +- desktop/Cargo.toml | 2 +- desktop/tauri.conf.json | 2 +- docs/ISSUES_TODOLIST.md | 4 +++- docs/ROADMAP.md | 2 +- frontend/js/xlsx/context-menu.js | 12 ++++++++---- package.json | 2 +- tests/test_ci_workflow.py | 1 + 14 files changed, 55 insertions(+), 19 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9e4144c..49d6ddd 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -181,13 +181,22 @@ jobs: # pypdf>=6.16.1 (BUG-093). # CVE-2026-97687 / CVE-2026-97688 / CVE-2026-97689 (urllib3 2.7.0) # corrigés par le plancher urllib3>=2.8.0. + # CVE-2026-104874 (multidict 6.7.x) corrigé par le plancher + # multidict>=6.9.1 (transitive aiohttp/yarl ; 6.7.x est dans la + # toolcache de l'image du runner — même piège « already satisfied »). + # CVE-2026-85394 (python-jose ≤3.5.0, forgery HS256 par clé publique + # DER passée comme secret HMAC) : AUCUN correctif upstream (projet + # sans release depuis 2025). Non atteignable dans ObsiGate : + # jwt.decode passe toujours algorithms=["HS256"] et un secret + # symétrique serveur (backend/auth/jwt_handler.py, + # backend/mcp/confirmations.py) — jamais une clé publique comme clé. # Ces planchers doivent rester *au-dessus* des versions préinstallées # dans la toolcache de l'image du runner : en dessous, pip répond # « already satisfied » et n'aligne jamais (c'est exactement ce qui a # fait échouer ce job). Le garde-fou tests/test_ci_workflow.py:: # TestDependencySecurityFloors verrouille ces planchers. # NOTE runner Gitea Act (BUG-083) : aucun `#` dans le `run:`. - run: pip-audit --ignore-vuln PYSEC-2026-1325 + run: pip-audit --ignore-vuln PYSEC-2026-1325 --ignore-vuln CVE-2026-85394 # ── Docker build ────────────────────────────────────────────────── build: diff --git a/CHANGELOG.md b/CHANGELOG.md index e88f571..cffccb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ 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 à venir sont listés dans la section -> [Unreleased](#unreleased). La dernière version livrée est **2.53.2**. +> [Unreleased](#unreleased). La dernière version livrée est **2.53.3**. --- @@ -14,6 +14,21 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). --- +## [2.53.3] — 2026-10-07 + +### Corrigé + +- **BUG-108 - Tableur : « Coller » du menu contextuel muet** + - `closeContextMenu()` retirait le nœud du menu **avant** de déférencer + `_menu` : retirer le menu portant le focus émet un `focusout` synchrone + qui rappelle la fonction, et le second `remove()` sur un nœud déjà + démonté lève `NotFoundError` — le handler de l'action « Coller » du + viewer est avorté avant son exécution (régression de la fermeture au + focus #179 ; reproduite par l'E2E « coller une plage », CI #850-#854). + Déférencement avant retrait ; l'E2E passe après correctif. + +--- + ## [2.53.2] — 2026-10-07 ### Modifié diff --git a/README.fr.md b/README.fr.md index e841495..a02e013 100644 --- a/README.fr.md +++ b/README.fr.md @@ -4,7 +4,7 @@ **Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive. -[![Version](https://img.shields.io/badge/Version-2.53.2-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.53.3-blue.svg)]() [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/) [![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/) @@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l ## 📝 Changelog -Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.53.2). +Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.53.3). --- -*Projet : ObsiGate | Version : 2.53.2 | Dernière mise à jour : Septembre 2026* +*Projet : ObsiGate | Version : 2.53.3 | Dernière mise à jour : Septembre 2026* diff --git a/README.md b/README.md index e9f495c..a6b8540 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface. -[![Version](https://img.shields.io/badge/Version-2.53.2-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.53.3-blue.svg)]() [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/) [![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/) @@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) ## 📝 Changelog -See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.53.2). +See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.53.3). --- -*Project: ObsiGate | Version: 2.53.2 | Last updated: September 2026* +*Project: ObsiGate | Version: 2.53.3 | Last updated: September 2026* diff --git a/VERSION b/VERSION index f3a3607..27b2061 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.53.2 +2.53.3 diff --git a/backend/requirements.txt b/backend/requirements.txt index 645ee81..e3361ef 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -39,3 +39,8 @@ reportlab>=4.0 pillow>=10.0 # Plancher urllib3 >= 2.8.0 (CVE-2026-97687, CVE-2026-97688, CVE-2026-97689) urllib3>=2.8.0 +# Plancher de sécurité (CVE-2026-104874, fix 6.9.1) : multidict est transitive +# (aiohttp/yarl). 6.7.x est la version pré-installée dans la toolcache de +# l'image du runner — sans plancher, pip répond « already satisfied » et +# n'aligne jamais (même piège que pypdf, BUG-093). +multidict>=6.9.1 diff --git a/desktop/Cargo.lock b/desktop/Cargo.lock index c0bcc3d..3fae40a 100644 --- a/desktop/Cargo.lock +++ b/desktop/Cargo.lock @@ -2626,7 +2626,7 @@ dependencies = [ [[package]] name = "obsigate-desktop" -version = "2.53.2" +version = "2.53.3" dependencies = [ "chrono", "env_logger", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index fcbf485..32e7267 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "obsigate-desktop" -version = "2.53.2" +version = "2.53.3" description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian" authors = ["Bruno Charest"] edition = "2021" diff --git a/desktop/tauri.conf.json b/desktop/tauri.conf.json index c7e224f..adf284d 100644 --- a/desktop/tauri.conf.json +++ b/desktop/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json", "productName": "ObsiGate", - "version": "2.53.2", + "version": "2.53.3", "identifier": "com.obsigate.desktop", "build": { "frontendDist": "../frontend", diff --git a/docs/ISSUES_TODOLIST.md b/docs/ISSUES_TODOLIST.md index 793f217..68eb21c 100644 --- a/docs/ISSUES_TODOLIST.md +++ b/docs/ISSUES_TODOLIST.md @@ -214,7 +214,8 @@ Avant de corriger quoi que ce soit, un agent IA doit : | *BUG-104* | Desktop : toutes les commandes Tauri invoquées depuis la page backend (`http://127.0.0.1`) rejetées « not allowed by ACL » — le bouton « Choisir mon dossier » du wizard ne fait rien | 🟢 corrigé | P1 | 🖥️ desktop | IA | `desktop/permissions/commands.toml` (nouveau), `desktop/capabilities/default.json`, `desktop/build.rs` | Installeur 2.49.2 : clic sur « Choisir mon dossier » → aucune fenêtre ; console `[desktop] invoke(pick_vault_folder) failed: Command … not allowed by ACL` (capturée via CDP, WebView2 `--remote-debugging-port`) | Manifeste de permissions applicatives créé (`allow-app-commands`, 19 commandes) et référencé par la capability : Tauri v2 ACL gate toute commande invoquée depuis une origine *remote*, y compris celles de l'`invoke_handler` | `cargo test` 25 passed (nouveau garde-fou `test_frontend_invokes_are_acl_allowed`) + vérif live CDP : `get_version`→2.49.2, clic → fenêtre native « Select Folder », parcours complet ajout de vault terminé | | *BUG-105* | Desktop : crash 2-15 s après l'ouverture de la fenêtre (`APPCRASH c0000374`, heap corruption détectée dans ntdll) — absent de v2.0.0, apparu en 2.49.2 | 🟢 corrigé | P1 | 🖥️ desktop | IA | `desktop/src/jumplist.rs` (`set_link_title`) | Installeur 2.49.2 : fenêtre visible quelques secondes puis fermeture (3/3), Event Log Windows `APPCRASH obsigate-desktop.exe … c0000374`, rien dans stderr | `set_link_title` (#77) construisait un `PROPVARIANT VT_LPWSTR` pointant un buffer heap Rust et le confiait au property store du shell, qui le libère avec l'allocateur Windows → corruption du tas ; fonction supprimée (`SetDescription` fournit déjà le libellé) | Bisect 3 états (jumplist off = stable, titre off = stable, d'origine = crash) ; run 9 min sans crash + sortie propre `EXIT=0`, zéro événement WER (avant : 3 crashes ≤20 s) | | *BUG-106* | Page blanche sur `/docs` (Swagger UI) : la CSP `script-src` sans `unsafe-inline` (#87 T5c) refuse le script inline d'init de FastAPI, jamais noncé | 🟢 corrigé | P1 | ⚙️ backend | IA | `backend/main.py` (`SecurityHeadersMiddleware`) | Ouvrir `https://og.dracodev.net/docs` : HTML servi (200) mais aucune UI, console `Refused to execute inline script … Content Security Policy` | Injection du nonce dans le HTML de `/docs` et `/redoc` depuis le middleware, via le helper existant `inject_csp_nonce` ; corps décompressé/recompressé (GZipMiddleware est plus proche de la route) | `pytest tests/test_csp_nonce.py` (3 tests ajoutés : nonce sur `/docs`, `/redoc` sans injection, regex sur bundle externe) + 45 passed sur les 3 suites sécurité ; ruff/mypy 0 | -| *BUG-107* | Drag & drop de fichiers depuis l'Explorateur inutilisable sur desktop : Tauri/wry pose son propre `IDropTarget` par-dessus de celui du WebView2 et aucun événement natif n'est écouté — #89 ne fonctionnait que sur le web | 🟢 corrigé | P1 | 🖥️ desktop | IA | `desktop/tauri.conf.json` (`create: false`), `desktop/src/main.rs` (`WebviewWindowBuilder::…disable_drag_drop_handler`) | App desktop : glisser un fichier de l'Explorateur sur la fenêtre → aucun survol, aucun dépôt — les gestionnaires HTML5 de `dragdrop.js` ne sont jamais déclenchés | Création manuelle de la fenêtre avec `disable_drag_drop_handler()` (doc Tauri : « required to use HTML5 drag and drop APIs on the frontend on Windows ») ; `create: false` évite la double création par la boucle Tauri | `cargo test` 26 passed (nouveau garde-fou `test_window_created_without_tauri_drag_drop_handler`) + lancement OK (fenêtre unique, boot normal) ; dépôt réel à valider par l'utilisateur | +| *BUG-107* | Drag & drop de fichiers depuis l'Explorateur inutilisable sur desktop : Tauri/wry pose son propre `IDropTarget` par-dessus de celui du WebView2 et aucun événement natif n'est écouté — #89 ne fonctionnait que sur le web | 🟢 corrigé | P1 | 🖥️ desktop | IA | `desktop/tauri.conf.json` (`create: false`), `desktop/src/main.rs` (`WebviewWindowBuilder::…disable_drag_drop_handler`) | App desktop : glisser un fichier de l'Explorateur sur la fenêtre → aucun survol, aucun dépôt — les gestionnaires HTML5 de `dragdrop.js` ne sont jamais déclenchés | Création manuelle de la fenêtre avec `disable_drag_drop_handler()` (doc Tauri : « required to use HTML5 drag and drop APIs on the frontend on Windows ») ; `create: false` évite la double création par la boucle Tauri | `cargo test` 26 passed (nouveau garde-fou `test_window_created_without_tauri_drag_drop_handler` : les deux marqueurs obligatoires) + lancement OK (fenêtre unique, boot normal) ; dépôt réel à valider par l'utilisateur | +| *BUG-108* | Tableur : le « Coller » du menu contextuel ne fait rien (copier-coller par menu muet) | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/xlsx/context-menu.js` (`closeContextMenu`) | Ouvrir un `.xlsx`, clic droit « Copier » sur une plage, clic droit cible puis « Coller » → rien n'est collé (le menu se ferme sans action) | `closeContextMenu()` retirait le nœud du DOM **avant** de remettre `_menu` à `null` : retirer le menu qui tient le focus émet `focusout` en synchrone, qui rappelle `closeContextMenu()` → second `remove()` sur un nœud déjà démonté → `NotFoundError` qui avorte le handler de l'action « Coller » du viewer. Introduit par #179 (fermeture au focus). Correctif : `_menu = null` avant `menu.remove()` | Reproduit en local sur HEAD (e2e « coller une plage » : attendu `Date`, obtenu la valeur datée) puis vert après fix : E2E `-g "coller une plage"` 1/1, `xlsx-menus` 11/11, `ai-quick-actions` 13/13 | | | | | | | | | | | | ### TODOs techniques (améliorations / nouvelles tâches) @@ -326,6 +327,7 @@ Avant de corriger quoi que ce soit, un agent IA doit : | 2026-10-02 | BUG-105 | Correction | `desktop/src/jumplist.rs` | **BUG-105 — crash heap (c0000374) à l'ouverture du desktop** : `set_link_title` (#77) passait un `PROPVARIANT VT_LPWSTR` pointant un buffer heap Rust au property store du shell, qui le libère avec l'allocateur Windows → `STATUS_HEAP_CORRUPTION` détectée dans ntdll ≤20 s après le lancement (3/3, Event Log WER, même empreinte de pile). Fonction supprimée : le libellé des entrées vient de `SetDescription`. Vérifié : bisect sur 3 états de build, run 9 min `EXIT=0`, zéro événement WER. | 🟢 corrigé | | 2026-10-02 | BUG-106 | Correction | `backend/main.py`, `tests/test_csp_nonce.py` | **BUG-106 — page blanche sur /docs** : la CSP `#87 T5c` (`script-src` sans `unsafe-inline`, nonce frais par réponse) s'applique aussi aux pages générées par FastAPI (`/docs`, `/redoc`) dont le balisage n'est jamais passé par `inject_csp_nonce` → le navigateur refusait le script inline `SwaggerUIBundle(…)`, la div restait vide. `SecurityHeadersMiddleware` injecte désormais le nonce dans ce HTML avec le helper existant ; le corps est décompressé/recompressé car `SSESafeGZipMiddleware` est plus proche de la route (les réponses arrivent gunzippées). `/redoc` n'a aucun script inline (bundle jsdelivr seul, couvert par `script-src`) — vérifié tel quel. Vérifié : `test_docs_inline_script_nonced`, `test_redoc_served_with_csp`, `test_docs_external_bundle_not_nonced` + 45 passed sur les 3 suites sécurité, ruff/mypy 0. | 🟢 corrigé | | 2026-10-02 | BUG-107 | Correction | `desktop/tauri.conf.json`, `desktop/src/main.rs` | **BUG-107 — drag & drop de fichiers inutilisable sur desktop** : Tauri/wry installe son propre `IDropTarget` par-dessus de celui du WebView2 (source wry : « Enumerate child windows to find the WebView2 window and override! ») et aucun événement natif n'était écouté → les glisser-déposer depuis l'Explorateur n'atteignaient jamais les gestionnaires HTML5 de `dragdrop.js` (#89 marchait donc uniquement sur le web). Correctif : la doc Tauri est explicite — `disable_drag_drop_handler()` « is required to use HTML5 drag and drop APIs on the frontend on Windows » ; la fenêtre est créée en code (`create: false` dans `tauri.conf.json`, boucle Tauri qui saute les fenêtres non auto-créées) via `WebviewWindowBuilder::from_config(...).disable_drag_drop_handler()`. Vérifié : `cargo test` 26 passed (nouveau garde-fou `test_window_created_without_tauri_drag_drop_handler` : les deux marqueurs obligatoires), lancement de l'app OK (fenêtre unique, boot log normal) ; glisser-déposer réel à valider par l'utilisateur. | 🟢 corrigé | +| 2026-10-07 | BUG-108 | Correction | `frontend/js/xlsx/context-menu.js` | **BUG-108 — « Coller » du menu contextuel du tableur muet** : `closeContextMenu()` faisait `menu.remove()` **avant** `_menu = null` ; retirer le menu portant le focus émet `focusout` synchrone qui rappelle `closeContextMenu()`, le second `remove()` sur un nœud démonté lève `NotFoundError` et avorte le handler de l'action avant son exécution (introduit par la fermeture au focus #179). Reproduit par l'E2E `xlsx-viewer.spec.js` « coller une plage » (CI #850→#854). Correctif : déférencer avant de retirer. | 🟢 corrigé | --- diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index c0c0674..9ecd61b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,6 +1,6 @@ # ObsiGate — Roadmap -> **Version :** 2.53.2 | **Dernière mise à jour :** 2026-10-07 +> **Version :** 2.53.3 | **Dernière mise à jour :** 2026-10-07 > **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact > vers les fonctionnalités livrées. > - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)** diff --git a/frontend/js/xlsx/context-menu.js b/frontend/js/xlsx/context-menu.js index 91693ec..bd5b7de 100644 --- a/frontend/js/xlsx/context-menu.js +++ b/frontend/js/xlsx/context-menu.js @@ -17,10 +17,14 @@ let _openedAt = 0; let _dismissBound = false; export function closeContextMenu() { - if (_menu) { - _menu.remove(); - _menu = null; - } + // BUG-108: drop the reference BEFORE touching the DOM — removing a menu + // that holds the focus fires blur/focusout synchronously, which re-enters + // this very function; the second remove() on the mid-flight node throws + // NotFoundError and aborts the caller's click handler before its action + // runs (contextual « Coller » silently dead, e2e xlsx-viewer A5). + const menu = _menu; + _menu = null; + if (menu) menu.remove(); } export function isContextMenuOpen() { diff --git a/package.json b/package.json index 4d4f72a..b33a76e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsigate", - "version": "2.53.2", + "version": "2.53.3", "description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.", "main": "patch.js", "directories": { diff --git a/tests/test_ci_workflow.py b/tests/test_ci_workflow.py index 7895472..282f333 100644 --- a/tests/test_ci_workflow.py +++ b/tests/test_ci_workflow.py @@ -255,6 +255,7 @@ class TestDependencySecurityFloors: # BUG-095 : 2.13.0 a son propre advisory (CVE-2026-102274, fix 2.14.0). "pyjwt": (2, 14, 0), # PYSEC-2026-178 (2.13.0) puis CVE-2026-102274 (2.14.0) "urllib3": (2, 8, 0), # CVE-2026-97687, CVE-2026-97688, CVE-2026-97689 (fix 2.8.0) + "multidict": (6, 9, 1), # CVE-2026-104874 (fix 6.9.1 ; 6.7.x toolcache runner) } def test_security_floors_are_declared(self):