Sicherheitscheck: Schwachstellen behoben

- Pfad-Traversal: safeFilename/containedPath-Helper, storeAnhang sanitizes
  filename, alle Download/Delete/Mail-Send-Routen pruefen Containment
- Stored XSS: serveInline entscheidet Viewable-Typ nur nach Extension,
  nicht nach client/seitigem MIME; nicht viewbare Typen werden als Download
  erzwungen. Upload fileFilter (Basis/Interne) + Extension-Validierung
  (Signatur/Foto leiten Ext aus MIME, blockieren .html)
- URL-Scheme-Allowlist (safeUrl) fuer quelle_url-hrefs gegen javascript:-XSS
- E-Mail-Iframe: Sandbox auf allow-same-only (kein allow-popups-to-escape)
- Sicherheits-Header: CSP, X-Content-Type-Options, X-Frame-Options,
  Referrer-Policy, COOP; x-powered-by aus; jsPDF self-hosted unter /vendor
- Session: Secure-Flag bei TLS, serverseitige absoluteexpiry, Scrypt async
  + Dummy-Verify gegen Timing/Enumerate + Login-Rate-Limit
- Open Redirect: /email/fetch nur same-origin Redirects
- SSRF: Validierung von OLLAMA_HOST/CALDAV_URL/MAIL_HOST gegen
  Metadata/Link-Local-BLock (localhost/LAN bleibt erlaubt)
- Globaler Error-Handler ohne Interna-Leak, env=production

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-14 02:09:17 +02:00
co-authored by Claude
parent da0b497972
commit fd1db3970f
10 changed files with 751 additions and 76 deletions
+10 -2
View File
@@ -24,13 +24,21 @@
}
// Minimal markdown → HTML. HTML is escaped first, so model output can never
// inject markup; only the markdown tokens below are turned into tags.
// inject markup; only the markdown tokens below are turned into tags. Link
// targets are restricted to http(s)/mailto/relative so a `javascript:` URL
// from the model cannot become a clickable XSS vector, and the URL is stripped
// of characters that could break out of the href attribute.
function inlineFmt(t) {
return t
.replace(/`([^`]+)`/g, '<code>$1</code>')
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
.replace(/(^|[^*])\*([^*]+)\*/g, '$1<em>$2</em>')
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, label, url) => {
const u = String(url).trim();
if (u && !/^(https?:|mailto:|\/|#)/i.test(u)) return m;
const safe = u.replace(/["'<>`\\]/g, '');
return `<a href="${safe}" target="_blank" rel="noopener noreferrer">${label}</a>`;
});
}
function renderMarkdown(src) {