58 lines
3.0 KiB
Markdown
58 lines
3.0 KiB
Markdown
# 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
|
|
|
|
1. **Détection universelle** — une alternation unique de formats
|
|
fournisseur (OpenAI/Anthropic `sk-`, Stripe `sk_live_`/`whsec_`, GitLab
|
|
`glpat-`, GitHub `ghp_`/`github_pat_`, Google `AIza…`/`ya29.`, AWS
|
|
`AKIA`/`ASIA`, Slack `xoxb-`, SendGrid `SG.`, Hugging Face `hf_`, 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.
|
|
2. **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é : `pwd` n'est reconnu que qualifié.
|
|
3. **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), puis
|
|
`restore_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).
|
|
|
|
## 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.
|