diff --git a/cmdb b/cmdb
index 805f0fe..63caa76 160000
--- a/cmdb
+++ b/cmdb
@@ -1 +1 @@
-Subproject commit 805f0fe00b1314970a95c717c2945102130a0fd4
+Subproject commit 63caa769a0646db22b4a8381742a456739f95ddb
diff --git a/password-generator/password_generator.html b/password-generator/password_generator.html
index fd36986..aee520e 100644
--- a/password-generator/password_generator.html
+++ b/password-generator/password_generator.html
@@ -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 @@
- chargement…
@@ -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 + ')
' + ad + '';
- }
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, '$1')
.replace(/\*\*([^*]+)\*\*/g, '$1')
- .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '$1');
+ .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (mm, txt, url) => '' + txt + '');
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 += '' + inline(buf.join('
')) + '
';
+ html += '' + inline(buf.join('
')) + '
';
continue;
}
if(/^```/.test(line)){
const buf = [];
for(i++; i < lines.length && !/^```/.test(lines[i]); i++) buf.push(lines[i]);
i++;
- html += '' + buf.join('\n') + '
';
+ html += '';
continue;
}
if(m = line.match(/^!\[([^\]]*)\]\(([^)]+)\)\s*$/)){
- html += '';
+ html += '';
i++; continue;
}
if(/^---+\s*$/.test(line)){ html += '
'; i++; continue; }