diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index cb20046..4076c3a 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -44,6 +44,11 @@ jobs: node tests/frontend/config-mobile.test.mjs node tests/frontend/settings-order-avatar.test.mjs node tests/frontend/mobile-toolbar.test.mjs + node tests/frontend/upload.test.mjs + node tests/frontend/pretty.test.mjs + node tests/frontend/media-viewer.test.mjs + node tests/frontend/mfa-settings.test.mjs + node tests/frontend/config-ai-keys.test.mjs - name: Frontend JSDOM tests (PaneManager + Excalidraw + Plugins + AI + SW + Collab + Mobile + Semantic + Desktop + Inline edition) run: | @@ -126,11 +131,17 @@ jobs: pip install bandit pip-audit pip install -r backend/requirements.txt - - name: Bandit (SAST) - run: bandit -r backend/ --skip B101,B110,B310 || echo "bandit found issues (non-blocking)" + - name: Bandit (SAST, bloquant — #87) + # B105 est exclu (aligné avec [tool.bandit] de pyproject.toml : + # faux positifs systématiques sur les noms de variables) ; les rares + # vrais positifs restants portent un `# nosec` justifié inline. + run: bandit -r backend/ --skip B101,B105,B110,B310 - - name: Pip-audit (dependency vulnerabilities) - run: pip-audit || echo "pip-audit found vulnerabilities (non-blocking)" + - name: Pip-audit (consultatif — #87) + # Reste non bloquant tant que les montées de version requises + # (starlette via fastapi, weasyprint) ne sont pas qualifiées : + # upgrade FastAPI = chantier de régression dédié, hors périmètre. + run: pip-audit || echo "pip-audit found vulnerabilities (non-blocking, see #87)" # ── Docker build ────────────────────────────────────────────────── build: @@ -192,6 +203,9 @@ jobs: npm ci npx playwright install --with-deps chromium + - name: Npm audit (bloquant — #87, 0 dépendance prod hors Playwright) + run: npm audit --omit=dev + - name: Start ObsiGate run: | docker rm -f obsigate-e2e 2>/dev/null || true diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cb833c..f2c198c 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.28.0**. +> [Unreleased](#unreleased). La dernière version livrée est **2.28.1**. --- @@ -14,6 +14,21 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html). --- +## [2.28.1] — 2026-09-26 + +### Modifié + +- **#87 (T1) — CI sécurité durcie.** + `bandit` passe en bloquant (`# nosec` justifiés : SHA1 non-crypto, + subprocess git à argv fixe, `saxutils.escape` sans parsing ; B105 exclu + comme `pyproject.toml`) ; `npm audit --omit=dev` bloquant (0 faille) ; + les 5 suites frontend hors CI (`upload`, `pretty`, `media-viewer`, + `mfa-settings`, `config-ai-keys`, vertes en local) rejoignent le job + `lint`. `pip-audit` reste consultatif (upgrades starlette/weasyprint à + qualifier, chantier dédié). + +--- + ## [2.28.0] — 2026-09-26 --- diff --git a/README.fr.md b/README.fr.md index 3290d3c..f8192d8 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.28.0-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.28.1-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.28.0). +Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.28.1). --- -*Projet : ObsiGate | Version : 2.28.0 | Dernière mise à jour : Septembre 2026* +*Projet : ObsiGate | Version : 2.28.1 | Dernière mise à jour : Septembre 2026* diff --git a/README.md b/README.md index 72ffafc..bc9323d 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.28.0-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-2.28.1-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.28.0). +See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.28.1). --- -*Project: ObsiGate | Version: 2.28.0 | Last updated: September 2026* +*Project: ObsiGate | Version: 2.28.1 | Last updated: September 2026* diff --git a/VERSION b/VERSION index 90efbd4..9738a24 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.28.0 +2.28.1 diff --git a/backend/auth/router.py b/backend/auth/router.py index e9dfef9..dcebd49 100644 --- a/backend/auth/router.py +++ b/backend/auth/router.py @@ -253,7 +253,8 @@ def _issue_tokens(user: dict, username: str, remember_me: bool, response: Respon ) return { "access_token": access_token, - "token_type": "bearer", # nosec B105 — OAuth2 token_type, pas un mot de passe + # OAuth2 token_type, pas un mot de passe (B105) : + "token_type": "bearer", # nosec B105 "expires_in": ACCESS_TOKEN_EXPIRE_SECONDS, "user": { "username": user["username"], @@ -332,7 +333,8 @@ async def refresh_token_endpoint(request: Request, response: Response): return { "access_token": new_access_token, - "token_type": "bearer", # nosec B105 — OAuth2 token_type, pas un mot de passe + # OAuth2 token_type, pas un mot de passe (B105) : + "token_type": "bearer", # nosec B105 "expires_in": ACCESS_TOKEN_EXPIRE_SECONDS, } diff --git a/backend/guide_export.py b/backend/guide_export.py index cd66d74..484f13a 100644 --- a/backend/guide_export.py +++ b/backend/guide_export.py @@ -35,7 +35,8 @@ def diagram_png_for(code: str) -> Path | None: Mermaid, ou None. Le hash doit rester synchrone avec le script de build : sha1(unescape(code).strip())[:16].""" normalized = html.unescape(code).strip() - sha = hashlib.sha1(normalized.encode("utf-8")).hexdigest()[:16] + # Identifiant de cache déterministe (pas un usage sécurité). + sha = hashlib.sha1(normalized.encode("utf-8")).hexdigest()[:16] # nosec B324 png = DIAGRAMS_DIR / (sha + ".png") return png if png.exists() else None diff --git a/backend/media_thumbs.py b/backend/media_thumbs.py index 1e6808e..c1e793d 100644 --- a/backend/media_thumbs.py +++ b/backend/media_thumbs.py @@ -32,7 +32,8 @@ def thumb_cache_path(file_path: Path, size: int) -> Path: stamp = f"{st.st_mtime_ns}:{st.st_size}" except OSError: stamp = "0:0" - key = hashlib.sha1(f"{file_path}:{stamp}:{size}".encode()).hexdigest() + # Clé de cache miniature (pas un usage sécurité). + key = hashlib.sha1(f"{file_path}:{stamp}:{size}".encode()).hexdigest() # nosec B324 return thumbs_cache_dir() / f"{key}.webp" diff --git a/backend/tools/documents.py b/backend/tools/documents.py index 8d50f59..7d85da6 100644 --- a/backend/tools/documents.py +++ b/backend/tools/documents.py @@ -19,7 +19,9 @@ import io import logging import re from typing import Any -from xml.sax import saxutils + +# saxutils.escape uniquement (échappement de chaînes, aucun parsing XML). +from xml.sax import saxutils # nosec B406 from backend.services.errors import ServiceError from backend.services.mutations import save_raw_file diff --git a/backend/version.py b/backend/version.py index ebe9835..bd55947 100644 --- a/backend/version.py +++ b/backend/version.py @@ -22,7 +22,7 @@ Exemples : from __future__ import annotations import os -import subprocess +import subprocess # nosec B404 from pathlib import Path _ROOT = Path(__file__).resolve().parent.parent # racine du dépôt ObsiGate @@ -34,7 +34,8 @@ _ENV_VAR = "OBSIGATE_VERSION" def _run_git(args: list[str]) -> str: """Run a git command in the repo root; return stdout (stripped) or ''.""" try: - result = subprocess.run( + # argv fixe (git + args internes), sans shell : pas d'injection. + result = subprocess.run( # nosec B404 B603 B607 ["git", *args], cwd=str(_ROOT), capture_output=True, diff --git a/backend/watcher.py b/backend/watcher.py index a6a55ee..0e997fc 100644 --- a/backend/watcher.py +++ b/backend/watcher.py @@ -280,7 +280,8 @@ class VaultWatcher: for observer in self.observers.values(): try: observer.join(timeout=5) - except Exception: # nosec B110 — best-effort shutdown, ignore failures + # best-effort shutdown, ignore failures (B110) : + except Exception: # nosec B110 pass self.observers.clear() logger.info("VaultWatcher stopped") diff --git a/desktop/Cargo.lock b/desktop/Cargo.lock index e20f048..bf5fcf1 100644 --- a/desktop/Cargo.lock +++ b/desktop/Cargo.lock @@ -2626,7 +2626,7 @@ dependencies = [ [[package]] name = "obsigate-desktop" -version = "2.28.0" +version = "2.28.1" dependencies = [ "chrono", "env_logger", diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 9b652da..5f9280a 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "obsigate-desktop" -version = "2.28.0" +version = "2.28.1" 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 88381e9..d376dbf 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.28.0", + "version": "2.28.1", "identifier": "com.obsigate.desktop", "build": { "frontendDist": "../frontend", diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index c0bcc35..0f17059 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,6 +1,6 @@ # ObsiGate — Roadmap -> **Version :** 2.28.0 | **Dernière mise à jour :** 2026-09-26 +> **Version :** 2.28.1 | **Dernière mise à jour :** 2026-09-26 > **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)** @@ -67,6 +67,7 @@ - **Effort :** 3-5 jours | **Impact :** 🟡 | **Zone :** `.gitea/workflows/`, `tests/` - **Décision 2026-09-26 : prioritaire (axe Dette & sécurité).** +- **Statut :** 🔵 en cours depuis 2026-09-26 — par tranches. **T1 livrée (v2.28.1) :** bandit bloquant (`nosec` justifiés B324/B404/B603/B607/B406, B105 exclu comme `pyproject`), `npm audit` bloquant (0 vulnérabilité), 5 suites frontend intégrées au CI (`upload`, `pretty`, `media-viewer`, `mfa-settings`, `config-ai-keys`). pip-audit reste consultatif (montées starlette/weasyprint à qualifier). - **Description :** renforcer le pipeline (`.gitea/workflows/ci.yml`, `desktop-build.yml`) pour le rendre bloquant par défaut et accompagner les phases 1 → 3. Constat 2026-09-26 : job `security` non bloquant (`bandit`/`pip-audit` en `|| echo`, ni semgrep ni trivy), E2E limité à `chromium-desktop`, 5 suites frontend hors CI. - **Sous-tâches :** - [ ] Jobs CI sécurité **bloquants** (bandit/semgrep/trivy, audits pip/npm) + tests E2E XSS (page de partage + lecteur markdown) diff --git a/package.json b/package.json index 6a9b84e..f6bb9fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsigate", - "version": "2.28.0", + "version": "2.28.1", "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": {