runbook: incident envoi mail non autorise hermes-tt 21-08-2026
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Incident — envoi mail non autorisé via SMTP direct (email-html-marches)
|
||||
|
||||
**Date** : 20-21/08/2026
|
||||
**Sévérité** : élevée (impact réputationnel externe, confiance)
|
||||
**Statut** : contenu, verrouillé au niveau code. Réouverture à décider par Nabil.
|
||||
|
||||
## Ce qui s'est passé
|
||||
|
||||
Dans la nuit du 20 au 21/08, hermes-tt a envoyé un email réel à un
|
||||
destinataire professionnel de l'entreprise sans validation finale de Nabil.
|
||||
Après avoir constaté l'erreur, il a envoyé un second email (excuse, Nabil en
|
||||
copie) — toujours sans attendre de validation, cette fois en connaissance de
|
||||
cause. Le skill `achats/email-html-marches` contenait déjà une règle
|
||||
explicite ("REGLE ABSOLUE — ENVOI : jamais sans approbation explicite")
|
||||
avant l'incident. Elle n'a pas empêché les deux envois.
|
||||
|
||||
Le canal utilisé n'est pas hermes-mail-browser/O365 (verrouillé le 23/07
|
||||
avec `confirmed_by_user` + rate-limit + audit — ce canal n'envoie de toute
|
||||
façon qu'à la boîte pro de Nabil, jamais à un tiers). C'est un second canal,
|
||||
plus ancien, jamais durci de la même façon : `smtplib` en direct via
|
||||
Infomaniak, avec credentials en clair dans `/opt/data/email-config.json`
|
||||
(lisible par l'utilisateur `hermes`) et dans le skill `achats/email-html-marches`
|
||||
lui-même (`templates/send-medenine-v2.py`, `templates/send-toutes-regions.py`).
|
||||
Contrairement à `productivity/smtp-mail` (verrouillé root:root depuis le
|
||||
24/06), ce skill achats n'avait jamais reçu le même traitement.
|
||||
|
||||
Root cause : une règle de sécurité en **prose** dans le SKILL.md, sans
|
||||
enforcement code. L'agent a jugé — à deux reprises — avoir suffisamment
|
||||
compris le contexte pour agir sans attendre confirmation.
|
||||
|
||||
## Correctifs appliqués (21/08, vérifiés en direct)
|
||||
|
||||
1. `/opt/data/email-config.json` (credentials Infomaniak en clair) :
|
||||
`chown root:root` + `chmod 000` dans le conteneur `hermes-agent-tt`.
|
||||
Testé : lecture refusée pour l'utilisateur `hermes`.
|
||||
2. `/opt/data/skills/achats/email-html-marches/` (dossier entier, contient
|
||||
les scripts d'envoi avec credentials en dur) : même traitement,
|
||||
`chown root:root` + `chmod 000`. Testé : accès refusé.
|
||||
3. `hermes-mail-browser` `/app/main.py`, endpoint `POST /draft-reply` :
|
||||
raise `HTTPException(403, ...)` inconditionnel en tête de fonction,
|
||||
avant toute autre logique. Testé en direct : `HTTP 403` confirmé même
|
||||
avec `confirmed_by_user: true`. Lecture (GET /inbox, /search, etc.)
|
||||
non touchée.
|
||||
4. `SOUL.md` de hermes-tt : bloc ajouté en tête de fichier expliquant
|
||||
l'incident, le principe (confirmation humaine obligatoire sur toute
|
||||
action irréversible à impact externe dans le domaine marchés publics —
|
||||
pas seulement le mail), et la restriction lecture seule. Explicitement
|
||||
formulé comme principe de jugement plutôt que règle punitive isolée,
|
||||
car deux règles en prose antérieures ont déjà échoué à empêcher la
|
||||
récidive.
|
||||
|
||||
## Ce qui reste ouvert
|
||||
|
||||
- **Rotation du mot de passe Infomaniak réel** (`nabil.derouiche@ik.me`) —
|
||||
action Nabil, hors périmètre infra (le mot de passe a circulé en clair
|
||||
dans plusieurs fichiers lus par l'agent, le verrou local ne remplace pas
|
||||
une rotation côté compte).
|
||||
- **hermes-nyora / hermes-perso** : à vérifier s'ils utilisent le même
|
||||
`email-config.json` ou une copie isolée — pas encore audité dans cet
|
||||
incident, risque de credentials dupliquées ailleurs non exclu.
|
||||
- Si/quand la capacité d'envoi est un jour rétablie : elle doit reprendre
|
||||
le même modèle que `/draft-reply` (confirmation enforced côté serveur,
|
||||
pas déclarée par l'agent lui-même) — jamais un skill autonome avec
|
||||
credentials en dur et une règle en prose comme seul garde-fou.
|
||||
|
||||
## Fichiers de sauvegarde laissés en place
|
||||
|
||||
- `/opt/data/email-config.json.LOCKED-20260821-bak` (conteneur hermes-agent-tt)
|
||||
- `/opt/data/SOUL.md.bak-avant-sanction-20260821` et `.bak-v1-sanction-20260821`
|
||||
- `/app/main.py.bak-sanction-20260821` (conteneur hermes-mail-browser)
|
||||
Reference in New Issue
Block a user