3.0 KiB
3.0 KiB
Fiche — #188 · Secrets masqués : couverture universelle + clic pour copier
- Statut : ✅ livré le 2026-10-08 (ouvert le 2026-10-08)
- Effort : ~1 jour · Impact : 🟡
- Zones :
backend/secret_redactor.py,backend/render.py,backend/routers/files_read.py,frontend/js/viewer.js,frontend/style.css,frontend/locales/{fr,en}.json
Problème
Le redacteur (secret_redactor.py) ne masquait que les JWT, les clés
sk-/pk-/rk-, AKIA…, ghp_…, les connection strings et les
affectations api_key=/token=/secret= de 20 caractères ou plus.
Résultat : la majorité des clés API réelles (Google, Slack, Stripe, GitLab,
Hugging Face, npm…) et tous les mots de passe courts passaient en clair
dans l'aperçu markdown, et un masque affiché ([CLÉ API MASQUÉE]) n'était
pas récupérable : il fallait ouvrir la source pour relire la valeur.
Solution
- Détection universelle — une alternation unique de formats
fournisseur (OpenAI/Anthropic
sk-, Stripesk_live_/whsec_, GitLabglpat-, GitHubghp_/github_pat_, GoogleAIza…/ya29., AWSAKIA/ASIA, Slackxoxb-, SendGridSG., Hugging Facehf_, npm, Docker, Resend, Square, Atlassian, Discord, Telegram,Bearer …) en plus des motifs déjà présents ; le plancher des affectations génériques passe de 20 à 8 caractères. - Mots de passe — motif dédié (
[MOT DE PASSE MASQUÉ]) valable toute longueur et toute quote (password=,"passwd": "…",db_password=,passphrase=,mot de passe=), qualificateur inclus.PWD=(dossier de travail shell) reste épargné :pwdn'est reconnu que qualifié. - Clic → copie — le pipeline de rendu passe par des
placeholders (
\uE000n\uE001) qui survivent à la conversion markdown (un<span>littéral dans un bloc de code serait affiché tel quel), puisrestore_masks()les réécrit après le rendu :- aperçu authentifié (
GET /api/file/…,click_to_copy=True) :<span class="secret-mask" data-secret="valeur">[CLÉ API MASQUÉE]</span>— la valeur n'est jamais affichée, un clic la copie (listener délégué unique sur la zone de contenu, toast i18n FR/EN, infobulle) ; - partages publics, exports PDF, contexte IA / MCP : libellé seul,
la valeur ne quitte pas le serveur.
Les blocs
<pre><code>portant un masque ne sont pas re-colorés (highlight.js reconstruirait le markup et perdrait le badge).
- aperçu authentifié (
Hors périmètre (volontaire)
- La vue « source » (fichier brut) et les aperçus de fichiers non-markdown ne sont pas masqués : c'est le fichier lui-même qui est affiché, le redacteur ne s'y appliquait jamais.
- Le serveur MCP / l'assistant IA continuent de recevoir les libellés seuls (inchangé).
Tests
tests/test_api_main.py::TestSecretRedactor : formats fournisseurs
(paramétrés), mots de passe (paramétrés), PWD= non masqué, round-trip
placeholder → span cliquable, rendu markdown cliquable vs libellé seul.