feat: secrets masqués — couverture universelle clés API/mots de passe + clic pour copier (#188)
This commit is contained in:
+6
-2
@@ -5302,8 +5302,12 @@ curl -X POST https://votre-serveur.com/webhook \
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Secret redactor</strong><span data-i18n="help.desc_9846fc07"> : Masque
|
||||
JWT, clés API, tokens GitHub dans les
|
||||
aperçus</span></li>
|
||||
JWT, mots de passe et clés API (OpenAI,
|
||||
Anthropic, GitHub, Google, AWS, Slack,
|
||||
Stripe, GitLab, Hugging Face…) dans les
|
||||
aperçus markdown ; cliquez sur un masque
|
||||
pour copier la valeur dans le
|
||||
presse-papiers</span></li>
|
||||
<li>
|
||||
<strong>Path traversal</strong> : Validation
|
||||
des chemins contre les attaques
|
||||
|
||||
+24
-2
@@ -7276,7 +7276,10 @@ export function renderFile(data) {
|
||||
prettyBtn.classList.add("active");
|
||||
} else {
|
||||
mdDiv.innerHTML = data.html;
|
||||
mdDiv.querySelectorAll("pre code").forEach((block) => safeHighlight(block));
|
||||
mdDiv.querySelectorAll("pre code").forEach((block) => {
|
||||
if (block.querySelector(".secret-mask")) return;
|
||||
safeHighlight(block);
|
||||
});
|
||||
prettyBtn.classList.remove("active");
|
||||
}
|
||||
});
|
||||
@@ -7327,8 +7330,10 @@ export function renderFile(data) {
|
||||
renderBacklinksPanel(data.vault, data.path, area);
|
||||
}
|
||||
|
||||
// Highlight code blocks
|
||||
// Highlight code blocks — a block carrying a secret mask (#188) must be
|
||||
// left alone: highlight.js rebuilds the markup and would drop the badge.
|
||||
area.querySelectorAll("pre code").forEach((block) => {
|
||||
if (block.querySelector(".secret-mask")) return;
|
||||
safeHighlight(block);
|
||||
});
|
||||
|
||||
@@ -7370,6 +7375,23 @@ export function renderFile(data) {
|
||||
});
|
||||
});
|
||||
|
||||
// Secret masks (#188): click copies the real value to the clipboard.
|
||||
// The handler is delegated (and attached once) so it survives re-renders.
|
||||
area.querySelectorAll(".secret-mask").forEach((mask) => {
|
||||
mask.title = t("viewer.secret_copy_hint");
|
||||
});
|
||||
if (!area.dataset.secretMaskWired) {
|
||||
area.dataset.secretMaskWired = "1";
|
||||
area.addEventListener("click", (e) => {
|
||||
const mask = e.target instanceof Element ? e.target.closest(".secret-mask") : null;
|
||||
if (!mask || !area.contains(mask)) return;
|
||||
const secret = mask.getAttribute("data-secret");
|
||||
if (!secret) return;
|
||||
const ok = copyToClipboard(secret);
|
||||
showToast(ok ? t("viewer.secret_copied") : t("viewer.secret_copy_failed"), ok ? "success" : "error");
|
||||
});
|
||||
}
|
||||
|
||||
safeCreateIcons();
|
||||
area.scrollTop = 0;
|
||||
|
||||
|
||||
@@ -1004,7 +1004,7 @@
|
||||
"help.desc_8f0bffe3": "JWT tokens",
|
||||
"help.desc_9186a3a3": "Explorez l'arborescence : Naviguez dans les dossiers",
|
||||
"help.desc_93902d18": "Utilisez les tags : Filtrez par tags pour affiner",
|
||||
"help.desc_9846fc07": "In the",
|
||||
"help.desc_9846fc07": ": Masks JWTs, passwords and API keys (OpenAI, Anthropic, GitHub, Google, AWS, Slack, Stripe, GitLab, Hugging Face…) in markdown previews — click a mask to copy the value to the clipboard",
|
||||
"help.desc_985d759b": "Token unique : 64 caractères hexadécimaux, impossible à deviner",
|
||||
"help.desc_991307d6": " — Generate content from the selection:",
|
||||
"help.desc_995a711f": ": Opens the CodeMirror editor",
|
||||
@@ -2221,6 +2221,9 @@
|
||||
"viewer.export_html": "Export as HTML",
|
||||
"viewer.export_md_bundle": "Export as Markdown bundle (.zip)",
|
||||
"viewer.export_start": "Exporting...",
|
||||
"viewer.secret_copy_hint": "Click to copy the value",
|
||||
"viewer.secret_copied": "Value copied to the clipboard",
|
||||
"viewer.secret_copy_failed": "Copy failed",
|
||||
"viewer.export_title": "Export document",
|
||||
"viewer.forge_brand": "Forge",
|
||||
"viewer.forge_title": "Forge (new editor)",
|
||||
|
||||
@@ -1004,7 +1004,7 @@
|
||||
"help.desc_8f0bffe3": ": Tokens JWT\n avec mots de passe hachés Argon2id",
|
||||
"help.desc_9186a3a3": "Explorez l'arborescence :\n Naviguez dans les dossiers",
|
||||
"help.desc_93902d18": "Utilisez les tags : Filtrez\n par tags pour affiner",
|
||||
"help.desc_9846fc07": ": Masque\n JWT, clés API, tokens GitHub dans les\n aperçus",
|
||||
"help.desc_9846fc07": ": Masque JWT, mots de passe et clés API (OpenAI, Anthropic, GitHub, Google, AWS, Slack, Stripe, GitLab, Hugging Face…) dans les aperçus markdown ; cliquez sur un masque pour copier la valeur dans le presse-papiers",
|
||||
"help.desc_985d759b": "Token unique : 64\n caractères hexadécimaux, impossible à\n deviner",
|
||||
"help.desc_991307d6": "— Génère du\n contenu à partir de la sélection :",
|
||||
"help.desc_995a711f": ": Ouvre l'éditeur\n CodeMirror",
|
||||
@@ -2221,6 +2221,9 @@
|
||||
"viewer.export_html": "Exporter en HTML",
|
||||
"viewer.export_md_bundle": "Exporter en bundle Markdown (.zip)",
|
||||
"viewer.export_start": "Export en cours...",
|
||||
"viewer.secret_copy_hint": "Cliquer pour copier la valeur",
|
||||
"viewer.secret_copied": "Valeur copiée dans le presse-papiers",
|
||||
"viewer.secret_copy_failed": "Échec de la copie",
|
||||
"viewer.export_title": "Exporter le document",
|
||||
"viewer.forge_brand": "Forge",
|
||||
"viewer.forge_title": "Forge (nouvel éditeur)",
|
||||
|
||||
@@ -2324,6 +2324,18 @@ select {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Secret masks (#188) — click copies the real value to the clipboard */
|
||||
.secret-mask {
|
||||
cursor: pointer;
|
||||
padding: 0 3px;
|
||||
border-radius: 3px;
|
||||
border-bottom: 1px dashed var(--text-muted);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
.secret-mask:hover {
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
/* Image placeholders */
|
||||
.image-not-found {
|
||||
display: inline-block;
|
||||
|
||||
Reference in New Issue
Block a user