Datenbank bereinigt / Gitea-Integration gefixt
Dieser Commit ist enthalten in:
committet von
Server Deploy
Ursprung
395598c2b0
Commit
c21be47428
@ -5,24 +5,52 @@
|
||||
*/
|
||||
|
||||
const sanitizeHtml = require('sanitize-html');
|
||||
const createDOMPurify = require('dompurify');
|
||||
const { JSDOM } = require('jsdom');
|
||||
|
||||
// DOMPurify für Server-side Rendering initialisieren
|
||||
const window = new JSDOM('').window;
|
||||
const DOMPurify = createDOMPurify(window);
|
||||
|
||||
/**
|
||||
* HTML-Tags entfernen (für reine Text-Felder)
|
||||
* HTML-Entities dekodieren
|
||||
*/
|
||||
function stripHtml(input) {
|
||||
if (typeof input !== 'string') return input;
|
||||
return sanitizeHtml(input, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: {}
|
||||
}).trim();
|
||||
function decodeHtmlEntities(str) {
|
||||
if (typeof str !== 'string') return str;
|
||||
const entities = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
''': "'",
|
||||
''': "'",
|
||||
''': "'"
|
||||
};
|
||||
return str.replace(/&(amp|lt|gt|quot|#039|#x27|apos);/g, match => entities[match] || match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown-sichere Bereinigung (erlaubt bestimmte Tags)
|
||||
* HTML-Tags entfernen (für reine Text-Felder)
|
||||
* Wichtig: sanitize-html encoded &-Zeichen zu &, daher dekodieren wir danach
|
||||
*/
|
||||
function stripHtml(input) {
|
||||
if (typeof input !== 'string') return input;
|
||||
const sanitized = sanitizeHtml(input, {
|
||||
allowedTags: [],
|
||||
allowedAttributes: {}
|
||||
}).trim();
|
||||
// Entities wieder dekodieren, da sanitize-html sie encoded
|
||||
return decodeHtmlEntities(sanitized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown-sichere Bereinigung mit DOMPurify (doppelte Sicherheit)
|
||||
*/
|
||||
function sanitizeMarkdown(input) {
|
||||
if (typeof input !== 'string') return input;
|
||||
return sanitizeHtml(input, {
|
||||
|
||||
// Erste Bereinigung mit sanitize-html
|
||||
const firstPass = sanitizeHtml(input, {
|
||||
allowedTags: [
|
||||
'p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre',
|
||||
'ul', 'ol', 'li', 'blockquote', 'a', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
||||
@ -44,6 +72,16 @@ function sanitizeMarkdown(input) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Zweite Bereinigung mit DOMPurify (zusätzliche Sicherheit)
|
||||
return DOMPurify.sanitize(firstPass, {
|
||||
ALLOWED_TAGS: [
|
||||
'p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre',
|
||||
'ul', 'ol', 'li', 'blockquote', 'a', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
||||
],
|
||||
ALLOWED_ATTR: ['href', 'title', 'target', 'rel'],
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto):|[^a-z]|[a-z+.-]+(?:[^a-z+.-:]|$))/i
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,7 +103,15 @@ function sanitizeObject(obj, options = {}) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
// Bestimmte Felder dürfen Markdown enthalten
|
||||
const allowHtml = ['description', 'content'].includes(key);
|
||||
sanitized[key] = sanitizeObject(value, { allowHtml });
|
||||
|
||||
// Passwort-Felder NICHT sanitizen (Sonderzeichen erhalten)
|
||||
const skipSanitization = ['password', 'oldPassword', 'newPassword', 'confirmPassword'].includes(key);
|
||||
|
||||
if (skipSanitization) {
|
||||
sanitized[key] = value; // Passwort unverändert lassen
|
||||
} else {
|
||||
sanitized[key] = sanitizeObject(value, { allowHtml });
|
||||
}
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
@ -119,12 +165,32 @@ const validators = {
|
||||
},
|
||||
|
||||
/**
|
||||
* URL-Format prüfen
|
||||
* URL-Format prüfen (erweiterte Sicherheit)
|
||||
*/
|
||||
url: (value, fieldName) => {
|
||||
try {
|
||||
if (value) {
|
||||
new URL(value);
|
||||
const url = new URL(value);
|
||||
|
||||
// Nur HTTP/HTTPS erlauben
|
||||
if (!['http:', 'https:'].includes(url.protocol)) {
|
||||
return `${fieldName} muss HTTP oder HTTPS verwenden`;
|
||||
}
|
||||
|
||||
// Localhost und private IPs blocken (SSRF-Schutz)
|
||||
const hostname = url.hostname;
|
||||
if (hostname === 'localhost' ||
|
||||
hostname === '127.0.0.1' ||
|
||||
hostname.startsWith('192.168.') ||
|
||||
hostname.startsWith('10.') ||
|
||||
hostname.startsWith('172.')) {
|
||||
return `${fieldName} darf nicht auf lokale Adressen verweisen`;
|
||||
}
|
||||
|
||||
// JavaScript URLs blocken
|
||||
if (url.href.toLowerCase().startsWith('javascript:')) {
|
||||
return `${fieldName} enthält ungültigen JavaScript-Code`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren