Merge pull request '[fix] Mypy Coffre Secrets (3 erreurs) + version 2.80.2' (#27) from fix/mypy-coffre-2.80.2 into main
This commit was merged in pull request #27.
This commit is contained in:
+12
-1
@@ -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).
|
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
|
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||||
> [Unreleased](#unreleased). La dernière version livrée est **2.80.1**.
|
> [Unreleased](#unreleased). La dernière version livrée est **2.80.2**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -14,6 +14,17 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2.80.2] — 2026-10-27
|
||||||
|
|
||||||
|
### Corrigé
|
||||||
|
|
||||||
|
- **CI lint (mypy)** — 3 erreurs dans le Coffre Secrets : annotation explicite `type[Any] | None` pour `AESGCM` dans `coffre_crypto.py` (le repli sans `cryptography` reste `None`, et `_require_aesgcm()` retourne désormais la classe pour un narrowing propre) ; annotation `dict[str, Any]` du coffre dans `create_coffre` (`coffre_store.py`), qui inférait ses valeurs en `str | None` et refusait le dict de `wrap_dek` ainsi que `coffre["id"]` en argument. Aucun `type: ignore` aveugle ajouté, aucun changement de comportement.
|
||||||
|
|
||||||
|
- **Bouton Nouveau secret inoperant (console NotFoundError)** — `valeurField` appelait `wrap.insertBefore(input, eyeBtn)` alors que le bouton oeil n'avait jamais ete rattache a `wrap` : l'oeil est desormais ajoute a `wrap` avant le premier `build`, la modale de creation (et celle de rotation) s'ouvre.
|
||||||
|
- **CI security (pip-audit)** — plancher `msgpack>=1.2.3` : la version transitive 1.2.1 etait signalee pour CVE-2026-114613 (correctif 1.2.3).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [2.80.1] — 2026-10-27
|
## [2.80.1] — 2026-10-27
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|||||||
+3
-3
@@ -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.
|
**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.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -977,8 +977,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.80.1).
|
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.80.2).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Projet : ObsiGate | Version : 2.80.1 | Dernière mise à jour : Septembre 2026*
|
*Projet : ObsiGate | Version : 2.80.2 | Dernière mise à jour : Septembre 2026*
|
||||||
|
|||||||
@@ -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.
|
**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.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -1152,8 +1152,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.80.1).
|
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.80.2).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Project: ObsiGate | Version: 2.80.1 | Last updated: September 2026*
|
*Project: ObsiGate | Version: 2.80.2 | Last updated: September 2026*
|
||||||
|
|||||||
@@ -24,14 +24,19 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
logger = logging.getLogger("obsigate.coffre_crypto")
|
logger = logging.getLogger("obsigate.coffre_crypto")
|
||||||
|
|
||||||
|
# Annotation explicite : sans elle, mypy refuse l'assignation de deux types
|
||||||
|
# (la classe AESGCM importée, puis None dans le repli sans cryptography)
|
||||||
|
# et le commentaire `type: ignore[assignment]` ne couvre pas ce code [misc].
|
||||||
|
AESGCM: type[Any] | None
|
||||||
try: # cryptography est une dépendance de la Phase 1a (requirements.txt)
|
try: # cryptography est une dépendance de la Phase 1a (requirements.txt)
|
||||||
from cryptography.exceptions import InvalidTag
|
from cryptography.exceptions import InvalidTag
|
||||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||||
except Exception: # pragma: no cover - environnement sans cryptography
|
except Exception: # pragma: no cover - environnement sans cryptography
|
||||||
AESGCM = None # type: ignore[assignment]
|
AESGCM = None
|
||||||
InvalidTag = Exception # type: ignore[assignment,misc]
|
InvalidTag = Exception # type: ignore[assignment,misc]
|
||||||
|
|
||||||
|
|
||||||
@@ -55,11 +60,17 @@ class CryptoIndisponibleError(CoffreCryptoError):
|
|||||||
"""La bibliothèque ``cryptography`` n'est pas installée."""
|
"""La bibliothèque ``cryptography`` n'est pas installée."""
|
||||||
|
|
||||||
|
|
||||||
def _require_aesgcm() -> None:
|
def _require_aesgcm() -> type[Any]:
|
||||||
|
"""Retourne la classe AESGCM, ou lève si cryptography est absent.
|
||||||
|
|
||||||
|
Retourner la classe (plutôt qu'un simple garde) permet à mypy de
|
||||||
|
savoir que l'appelant détient bien un constructible, et non None.
|
||||||
|
"""
|
||||||
if AESGCM is None:
|
if AESGCM is None:
|
||||||
raise CryptoIndisponibleError(
|
raise CryptoIndisponibleError(
|
||||||
"cryptography n'est pas installé (dépendance Phase 1a)"
|
"cryptography n'est pas installé (dépendance Phase 1a)"
|
||||||
)
|
)
|
||||||
|
return AESGCM
|
||||||
|
|
||||||
|
|
||||||
def parse_kek(raw: str) -> bytes:
|
def parse_kek(raw: str) -> bytes:
|
||||||
@@ -146,17 +157,17 @@ def _unb64(value: str) -> bytes:
|
|||||||
|
|
||||||
def encrypt_with_key(key: bytes, plaintext: bytes, aad: bytes) -> dict:
|
def encrypt_with_key(key: bytes, plaintext: bytes, aad: bytes) -> dict:
|
||||||
"""Chiffre avec AES-256-GCM. Retourne {nonce, ciphertext} en base64."""
|
"""Chiffre avec AES-256-GCM. Retourne {nonce, ciphertext} en base64."""
|
||||||
_require_aesgcm()
|
aesgcm = _require_aesgcm()
|
||||||
nonce = os.urandom(12)
|
nonce = os.urandom(12)
|
||||||
ciphertext = AESGCM(key).encrypt(nonce, plaintext, aad)
|
ciphertext = aesgcm(key).encrypt(nonce, plaintext, aad)
|
||||||
return {"nonce": _b64(nonce), "ciphertext": _b64(ciphertext)}
|
return {"nonce": _b64(nonce), "ciphertext": _b64(ciphertext)}
|
||||||
|
|
||||||
|
|
||||||
def decrypt_with_key(key: bytes, nonce_b64: str, ciphertext_b64: str, aad: bytes) -> bytes:
|
def decrypt_with_key(key: bytes, nonce_b64: str, ciphertext_b64: str, aad: bytes) -> bytes:
|
||||||
"""Déchiffre avec AES-256-GCM. Tag invalide -> TagInvalideError."""
|
"""Déchiffre avec AES-256-GCM. Tag invalide -> TagInvalideError."""
|
||||||
_require_aesgcm()
|
aesgcm = _require_aesgcm()
|
||||||
try:
|
try:
|
||||||
return AESGCM(key).decrypt(_unb64(nonce_b64), _unb64(ciphertext_b64), aad)
|
return aesgcm(key).decrypt(_unb64(nonce_b64), _unb64(ciphertext_b64), aad)
|
||||||
except InvalidTag as exc:
|
except InvalidTag as exc:
|
||||||
raise TagInvalideError("Tag GCM invalide : données altérées ou mauvaise clé") from exc
|
raise TagInvalideError("Tag GCM invalide : données altérées ou mauvaise clé") from exc
|
||||||
except (binascii.Error, ValueError) as exc:
|
except (binascii.Error, ValueError) as exc:
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import threading
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import UTC, datetime, timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from backend.coffre_crypto import (
|
from backend.coffre_crypto import (
|
||||||
decrypt_secret_value,
|
decrypt_secret_value,
|
||||||
@@ -306,7 +307,7 @@ def create_coffre(
|
|||||||
data = _load_coffres(base)
|
data = _load_coffres(base)
|
||||||
if any(c["slug"] == slug for c in data["coffres"]):
|
if any(c["slug"] == slug for c in data["coffres"]):
|
||||||
raise CoffreExisteError(f"Coffre déjà existant : {slug}")
|
raise CoffreExisteError(f"Coffre déjà existant : {slug}")
|
||||||
coffre = {
|
coffre: dict[str, Any] = {
|
||||||
"id": str(uuid.uuid4()),
|
"id": str(uuid.uuid4()),
|
||||||
"slug": slug,
|
"slug": slug,
|
||||||
"nom": nom,
|
"nom": nom,
|
||||||
|
|||||||
@@ -75,3 +75,7 @@ filelock>=4.2.1
|
|||||||
opentelemetry-api>=1.45.1
|
opentelemetry-api>=1.45.1
|
||||||
# Coffre Secrets (Phase 1a) : chiffrement par enveloppe AES-256-GCM.
|
# Coffre Secrets (Phase 1a) : chiffrement par enveloppe AES-256-GCM.
|
||||||
cryptography>=44.0
|
cryptography>=44.0
|
||||||
|
|
||||||
|
# Plancher de securite : msgpack est transitif, version 1.2.1 relevee par le job
|
||||||
|
# security (pip-audit, bloquant) — CVE-2026-114613, corrigee en 1.2.3.
|
||||||
|
msgpack>=1.2.3
|
||||||
|
|||||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.80.1"
|
version = "2.80.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.80.1"
|
version = "2.80.2"
|
||||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||||
authors = ["Bruno Charest"]
|
authors = ["Bruno Charest"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||||
"productName": "ObsiGate",
|
"productName": "ObsiGate",
|
||||||
"version": "2.80.1",
|
"version": "2.80.2",
|
||||||
"identifier": "com.obsigate.desktop",
|
"identifier": "com.obsigate.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../frontend",
|
"frontendDist": "../frontend",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# ObsiGate — Roadmap
|
# ObsiGate — Roadmap
|
||||||
|
|
||||||
> **Version :** 2.80.1 | **Dernière mise à jour :** 2026-10-27
|
> **Version :** 2.80.2 | **Dernière mise à jour :** 2026-10-27
|
||||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||||
> vers les fonctionnalités livrées.
|
> vers les fonctionnalités livrées.
|
||||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||||
|
|||||||
@@ -292,6 +292,9 @@ function valeurField(initialType) {
|
|||||||
else if (input) input.type = 'password';
|
else if (input) input.type = 'password';
|
||||||
});
|
});
|
||||||
eyeBtn.title = t('coffres.show_before_save');
|
eyeBtn.title = t('coffres.show_before_save');
|
||||||
|
// L'oeil doit etre enfant de wrap AVANT le premier build : insertBefore(input, eyeBtn)
|
||||||
|
// leve sinon NotFoundError et la modale ne s'ouvre jamais (bug bouton Nouveau secret).
|
||||||
|
wrap.appendChild(eyeBtn);
|
||||||
build(initialType);
|
build(initialType);
|
||||||
return {
|
return {
|
||||||
wrap,
|
wrap,
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsigate",
|
"name": "obsigate",
|
||||||
"version": "2.80.1",
|
"version": "2.80.2",
|
||||||
"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.",
|
"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",
|
"main": "patch.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|||||||
Reference in New Issue
Block a user