feat(password-generator): enrichit et sécurise les mails, épure la sidebar
- rendu HTML des mails: banner et bloc de code arrondis + padding, bloc de code en <table> (largeur au contenu, rendu robuste sous Outlook) - sécurité: emailUrl() (whitelist de scheme + échappement) sur les URLs de liens et d'images, échappement des guillemets dans alt — corrige une injection XSS dans l'aperçu du mail - sidebar: suppression de l'indicateur d'état AD et du CSS associé - bump du sous-module cmdb Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8c20793236
commit
7e8cf8a053
2 changed files with 14 additions and 14 deletions
2
cmdb
2
cmdb
|
|
@ -1 +1 @@
|
|||
Subproject commit 805f0fe00b1314970a95c717c2945102130a0fd4
|
||||
Subproject commit 63caa769a0646db22b4a8381742a456739f95ddb
|
||||
|
|
@ -73,7 +73,6 @@
|
|||
.psw-txt b{display:block;font-size:13px;color:var(--ink);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.psw-txt small{display:block;font-size:10.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.sidebar-footer{margin-top:auto;display:flex;flex-direction:column;gap:7px;padding-top:14px;border-top:1px solid var(--border)}
|
||||
.db-status{font-size:11px;color:var(--muted);padding:4px 8px;word-break:break-word;line-height:1.5}
|
||||
|
||||
/* ---------- main ---------- */
|
||||
main{flex:1;padding:30px 36px;max-width:1100px;min-width:0}
|
||||
|
|
@ -244,7 +243,6 @@
|
|||
<div class="psw">
|
||||
<a href="../cmdb/preferences.html#pwgen"><div class="psw-ico">⚙</div><div class="psw-txt"><b>Préférences</b><small>Réglages du générateur</small></div></a>
|
||||
</div>
|
||||
<div class="db-status" id="adStatus">chargement…</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
|
@ -768,13 +766,6 @@ function applyAdFromMap(map){
|
|||
}else AD_CFG = null;
|
||||
}
|
||||
function renderAdStatus(){
|
||||
const status = document.getElementById('adStatus');
|
||||
if(!SERVER_MODE){
|
||||
status.innerHTML = '● Base locale (localStorage)';
|
||||
}else{
|
||||
const ad = AD_CFG ? 'AD : ' + (AD_CFG.label || AD_CFG.host) : 'AD non configuré';
|
||||
status.innerHTML = '● Base partagée (v' + dbVersion + ')<br><span style="opacity:.75">' + ad + '</span>';
|
||||
}
|
||||
const hint = document.getElementById('adHint');
|
||||
if(hint) hint.textContent = AD_CFG ? '' :
|
||||
'Recherche AD indisponible : configurez l’AD dans Préférences CMDB (base partagée).';
|
||||
|
|
@ -1054,6 +1045,15 @@ function fillTplHtml(text, ctx){
|
|||
réinterprétées) — un mot de passe contenant * ou ` ne casse donc rien.
|
||||
========================================================================== */
|
||||
function emailEsc(s){ return String(s == null ? '' : s).replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c])); }
|
||||
/* URL sûre en attribut : whitelist de scheme + échappe les guillemets.
|
||||
Neutralise javascript:, data:text/html, vbscript:… → « # » si scheme non autorisé.
|
||||
(emailEsc a déjà neutralisé & < > ; il reste les " qui casseraient l'attribut.) */
|
||||
function emailUrl(u){
|
||||
const s = String(u == null ? '' : u).trim();
|
||||
const sch = /^([a-z][a-z0-9+.\-]*):/i.exec(s);
|
||||
const ok = !sch || /^(https?|mailto|tel)$/i.test(sch[1]) || /^data:image\//i.test(s);
|
||||
return (ok ? s : '#').replace(/"/g, '"');
|
||||
}
|
||||
function emailFillVars(html, ctx){
|
||||
return html.replace(/\{\{\s*(\w+)\s*\}\}/g, (m, k) => {
|
||||
if(!(k in ctx)) return m;
|
||||
|
|
@ -1068,7 +1068,7 @@ function renderEmailHtml(src, ctx){
|
|||
const inline = t => t
|
||||
.replace(/`([^`]+)`/g, '<code style="font-family:ui-monospace,Consolas,monospace;background:#f2f2f2;color:#222;padding:1px 5px;border-radius:4px">$1</code>')
|
||||
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '<a href="$2" style="color:#2f6fd8;text-decoration:underline">$1</a>');
|
||||
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (mm, txt, url) => '<a href="' + emailUrl(url) + '" style="color:#2f6fd8;text-decoration:underline">' + txt + '</a>');
|
||||
let html = '', i = 0, m;
|
||||
while(i < lines.length){
|
||||
const line = lines[i];
|
||||
|
|
@ -1076,18 +1076,18 @@ function renderEmailHtml(src, ctx){
|
|||
const color = m[1] || '#5b9cf5', buf = [];
|
||||
for(i++; i < lines.length && !/^:::\s*$/.test(lines[i]); i++) buf.push(lines[i]);
|
||||
i++;
|
||||
html += '<div style="background:' + color + ';color:#fff;padding:22px 24px;border-radius:8px;font-size:20px;font-weight:700;line-height:1.35;margin:0 0 18px">' + inline(buf.join('<br>')) + '</div>';
|
||||
html += '<div style="background:' + color + ';color:#fff;padding:24px 28px;border-radius:14px;font-size:20px;font-weight:700;line-height:1.35;margin:0 0 18px">' + inline(buf.join('<br>')) + '</div>';
|
||||
continue;
|
||||
}
|
||||
if(/^```/.test(line)){
|
||||
const buf = [];
|
||||
for(i++; i < lines.length && !/^```/.test(lines[i]); i++) buf.push(lines[i]);
|
||||
i++;
|
||||
html += '<pre style="background:#1d2026;color:#e6e6e6;padding:14px 16px;border-radius:8px;overflow:auto;font-family:ui-monospace,Consolas,monospace;font-size:13px;line-height:1.55;margin:0 0 16px"><code>' + buf.join('\n') + '</code></pre>';
|
||||
html += '<table role="presentation" cellpadding="0" cellspacing="0" style="margin:0 0 16px;border-collapse:separate"><tr><td style="background:#1d2026;color:#e6e6e6;padding:16px 18px;border-radius:12px;font-family:ui-monospace,Consolas,monospace;font-size:13px;line-height:1.55"><pre style="margin:0;overflow-x:auto;font-family:inherit"><code>' + buf.join('\n') + '</code></pre></td></tr></table>';
|
||||
continue;
|
||||
}
|
||||
if(m = line.match(/^!\[([^\]]*)\]\(([^)]+)\)\s*$/)){
|
||||
html += '<div style="margin:0 0 16px"><img src="' + m[2] + '" alt="' + m[1] + '" style="max-width:100%;height:auto;border-radius:8px;display:block"></div>';
|
||||
html += '<div style="margin:0 0 16px"><img src="' + emailUrl(m[2]) + '" alt="' + m[1].replace(/"/g, '"') + '" style="max-width:100%;height:auto;border-radius:8px;display:block"></div>';
|
||||
i++; continue;
|
||||
}
|
||||
if(/^---+\s*$/.test(line)){ html += '<hr style="border:0;border-top:1px solid #e2e2e2;margin:20px 0">'; i++; continue; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue