fix(health): invalidate the cache when HIBP results change

The dashboard reads its pwned column from state.hibpResults but its cache was computed before the scan finished, so enabling the breach check from the health view showed 0 pwned while the entry cards already carried the flag. Disabling had the mirror problem: results cleared, dashboard still counting them.
This commit is contained in:
r-zakarya
2026-08-01 19:49:07 +01:00
parent 6bee69eb1a
commit e80703d2c4
2 changed files with 14 additions and 0 deletions
+6
View File
@@ -357,6 +357,12 @@ l'éditeur slideover : créer + éditer une entrée, ajouter/retirer des tags,
- **ACTION prévue** : ouvrir *Vault health* → mettre à la corbeille une entrée listée → revenir sur health sans *Recompute* → **Fix** sur cette entrée. - **ACTION prévue** : ouvrir *Vault health* → mettre à la corbeille une entrée listée → revenir sur health sans *Recompute* → **Fix** sur cette entrée.
- **Pourquoi c'est irréalisable** : G3 a rendu ce cas impossible à mettre en scène — le cache est invalidé à **chaque** mutation, donc revenir sur le tableau de bord recalcule toujours et l'entrée supprimée n'y figure plus. Le garde reste en place dans le code (`openEntryForFix` sort si `openSlideOver` renvoie `false`), il n'est simplement plus atteignable par l'interface. - **Pourquoi c'est irréalisable** : G3 a rendu ce cas impossible à mettre en scène — le cache est invalidé à **chaque** mutation, donc revenir sur le tableau de bord recalcule toujours et l'entrée supprimée n'y figure plus. Le garde reste en place dans le code (`openEntryForFix` sort si `openSlideOver` renvoie `false`), il n'est simplement plus atteignable par l'interface.
### G5 · activer / désactiver HIBP depuis la vue santé ✅
- **ACTION** : dans *Vault health*, activer la vérification HIBP → attendre la fin du scan **sans quitter la vue**.
- **ATTENDU** : la carte *Pwned* et le score se mettent à jour **tout seuls** quand le scan finit.
- **ACTION 2** : désactiver HIBP → la catégorie *Pwned* retombe à 0 immédiatement.
- **Historique** : le cache santé était calculé **avant** l'arrivée des résultats et rien ne l'invalidait ensuite. La vue affichait `0 pwned` pendant que les cartes de la liste portaient déjà le drapeau ; il fallait sortir et revenir pour voir `2`. Symétrique à la désactivation, qui vidait `hibpResults` en laissant le tableau de bord compter des brèches disparues. `hibpCheckAllEntries` et le toggle invalident désormais `healthCache`.
### G4 · le score bouge par paliers de 10 — comportement attendu ### G4 · le score bouge par paliers de 10 — comportement attendu
- **OBSERVÉ** : après correction de deux mots de passe, `30 → 40 → 50`. - **OBSERVÉ** : après correction de deux mots de passe, `30 → 40 → 50`.
- **C'EST NORMAL** : le barème est `-5` par mot de passe **faible**, `-10` par **groupe de réutilisation**, `-2` par entrée **ancienne**, `-15` par entrée **compromise**. Corriger un mot de passe partagé par deux entrées casse le groupe → `+10`. - **C'EST NORMAL** : le barème est `-5` par mot de passe **faible**, `-10` par **groupe de réutilisation**, `-2` par entrée **ancienne**, `-15` par entrée **compromise**. Corriger un mot de passe partagé par deux entrées casse le groupe → `+10`.
+8
View File
@@ -983,6 +983,11 @@ async function hibpCheckAllEntries() {
})()); })());
} }
await Promise.all(workers); await Promise.all(workers);
// The health dashboard reads its "pwned" column from state.hibpResults, and
// its cache may well have been computed before these results landed — that
// is exactly what happens when HIBP is switched on from the health view
// itself, which then showed 0 pwned while the entry cards showed the flag.
if (typeof healthCache !== 'undefined') healthCache = null;
render(); render();
} }
@@ -7718,6 +7723,9 @@ async function init() {
hibpCheckAllEntries(); hibpCheckAllEntries();
} else { } else {
state.hibpResults.clear(); state.hibpResults.clear();
// Same reason as after a scan: the dashboard would keep counting
// breaches from results that no longer exist.
if (typeof healthCache !== 'undefined') healthCache = null;
render(); render();
toast('Breach check disabled'); toast('Breach check disabled');
} }