manage-your-printer/static/js/csp-violation-handler.js
2025-06-04 10:03:22 +02:00

283 lines
10 KiB
JavaScript

/**
* Mercedes-Benz MYP Platform - CSP Violation Handler
* Protokolliert und behandelt Content Security Policy Verletzungen
*/
class CSPViolationHandler {
constructor() {
this.violations = [];
this.init();
}
init() {
// CSP Violation Event Listener
document.addEventListener('securitypolicyviolation', this.handleViolation.bind(this));
// Report-To API fallback
if ('ReportingObserver' in window) {
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type === 'csp-violation') {
this.handleViolation(report.body);
}
}
});
observer.observe();
}
console.log('🛡️ CSP Violation Handler initialisiert');
}
/**
* CSP-Verletzung behandeln
*/
handleViolation(violationEvent) {
const violation = {
timestamp: new Date().toISOString(),
blockedURI: violationEvent.blockedURI || 'unknown',
violatedDirective: violationEvent.violatedDirective || 'unknown',
originalPolicy: violationEvent.originalPolicy || 'unknown',
documentURI: violationEvent.documentURI || window.location.href,
sourceFile: violationEvent.sourceFile || 'unknown',
lineNumber: violationEvent.lineNumber || 0,
columnNumber: violationEvent.columnNumber || 0,
sample: violationEvent.sample || '',
disposition: violationEvent.disposition || 'enforce'
};
this.violations.push(violation);
this.logViolation(violation);
this.suggestFix(violation);
// Violation an Server senden (falls API verfügbar)
this.reportViolation(violation);
}
/**
* Verletzung protokollieren
*/
logViolation(violation) {
console.group('🚨 CSP Violation detected');
console.error('Blocked URI:', violation.blockedURI);
console.error('Violated Directive:', violation.violatedDirective);
console.error('Source:', `${violation.sourceFile}:${violation.lineNumber}:${violation.columnNumber}`);
console.error('Sample:', violation.sample);
console.error('Full Policy:', violation.originalPolicy);
console.groupEnd();
}
/**
* Lösungsvorschlag basierend auf Verletzungstyp
*/
suggestFix(violation) {
const directive = violation.violatedDirective;
const blockedURI = violation.blockedURI;
console.group('💡 Lösungsvorschlag');
if (directive.includes('script-src')) {
if (blockedURI === 'inline') {
console.log('Problem: Inline-Script blockiert');
console.log('Lösung 1: Script in externe .js-Datei auslagern');
console.log('Lösung 2: data-action Attribute für Event-Handler verwenden');
console.log('Lösung 3: Nonce verwenden (nicht empfohlen für Entwicklung)');
console.log('Beispiel: <button data-action="refresh-dashboard">Aktualisieren</button>');
} else if (blockedURI.includes('eval')) {
console.log('Problem: eval() oder ähnliche Funktionen blockiert');
console.log('Lösung: Verwende sichere Alternativen zu eval()');
} else {
console.log(`Problem: Externes Script von ${blockedURI} blockiert`);
console.log('Lösung: URL zur CSP script-src Richtlinie hinzufügen');
}
} else if (directive.includes('style-src')) {
console.log('Problem: Style blockiert');
console.log('Lösung: CSS in externe .css-Datei auslagern oder CSP erweitern');
} else if (directive.includes('connect-src')) {
console.log(`Problem: Verbindung zu ${blockedURI} blockiert`);
console.log('Lösung: URL zur CSP connect-src Richtlinie hinzufügen');
console.log('Tipp: Für API-Calls relative URLs verwenden');
} else if (directive.includes('img-src')) {
console.log(`Problem: Bild von ${blockedURI} blockiert`);
console.log('Lösung: URL zur CSP img-src Richtlinie hinzufügen');
}
console.groupEnd();
}
/**
* Verletzung an Server senden
*/
async reportViolation(violation) {
try {
// Nur in Produktion an Server senden
if (window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
await fetch('/api/security/csp-violation', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(violation)
});
}
} catch (error) {
console.warn('Fehler beim Senden der CSP-Verletzung:', error);
}
}
/**
* Alle Verletzungen abrufen
*/
getViolations() {
return this.violations;
}
/**
* Verletzungsstatistiken
*/
getStats() {
const stats = {
total: this.violations.length,
byDirective: {},
byURI: {},
recent: this.violations.slice(-10)
};
this.violations.forEach(violation => {
// Nach Direktive gruppieren
const directive = violation.violatedDirective;
stats.byDirective[directive] = (stats.byDirective[directive] || 0) + 1;
// Nach URI gruppieren
const uri = violation.blockedURI;
stats.byURI[uri] = (stats.byURI[uri] || 0) + 1;
});
return stats;
}
/**
* Entwickler-Debugging-Tools
*/
enableDebugMode() {
// Debug-Panel erstellen
this.createDebugPanel();
// Konsolen-Hilfe ausgeben
console.log('🔧 CSP Debug Mode aktiviert');
console.log('Verfügbare Befehle:');
console.log('- cspHandler.getViolations() - Alle Verletzungen anzeigen');
console.log('- cspHandler.getStats() - Statistiken anzeigen');
console.log('- cspHandler.clearViolations() - Verletzungen löschen');
console.log('- cspHandler.exportViolations() - Als JSON exportieren');
}
/**
* Debug-Panel erstellen
*/
createDebugPanel() {
const panel = document.createElement('div');
panel.id = 'csp-debug-panel';
panel.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
width: 300px;
max-height: 400px;
background: rgba(0, 0, 0, 0.9);
color: white;
font-family: monospace;
font-size: 12px;
padding: 10px;
border-radius: 5px;
z-index: 10000;
overflow-y: auto;
display: none;
`;
panel.innerHTML = `
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<strong>CSP Violations</strong>
<button onclick="this.parentElement.parentElement.style.display='none'"
style="background: none; border: none; color: white; cursor: pointer;">&times;</button>
</div>
<div id="csp-violations-list"></div>
<div style="margin-top: 10px;">
<button onclick="cspHandler.clearViolations()"
style="background: #333; color: white; border: none; padding: 5px; margin-right: 5px; cursor: pointer;">Clear</button>
<button onclick="cspHandler.exportViolations()"
style="background: #333; color: white; border: none; padding: 5px; cursor: pointer;">Export</button>
</div>
`;
document.body.appendChild(panel);
// Shortcut zum Anzeigen/Verstecken
document.addEventListener('keydown', (event) => {
if (event.ctrlKey && event.shiftKey && event.key === 'C') {
panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
this.updateDebugPanel();
}
});
}
/**
* Debug-Panel aktualisieren
*/
updateDebugPanel() {
const list = document.getElementById('csp-violations-list');
if (!list) return;
const recent = this.violations.slice(-5);
list.innerHTML = recent.map(v => `
<div style="margin-bottom: 5px; padding: 5px; background: rgba(255, 255, 255, 0.1);">
<div><strong>${v.violatedDirective}</strong></div>
<div style="color: #ff6b6b;">${v.blockedURI}</div>
<div style="color: #ffd93d; font-size: 10px;">${v.timestamp}</div>
</div>
`).join('');
}
/**
* Verletzungen löschen
*/
clearViolations() {
this.violations = [];
this.updateDebugPanel();
console.log('🗑️ CSP Violations gelöscht');
}
/**
* Verletzungen exportieren
*/
exportViolations() {
const data = {
timestamp: new Date().toISOString(),
stats: this.getStats(),
violations: this.violations
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `csp-violations-${new Date().toISOString().split('T')[0]}.json`;
a.click();
URL.revokeObjectURL(url);
console.log('📄 CSP Violations exportiert');
}
}
// Globale Instanz erstellen
const cspHandler = new CSPViolationHandler();
// In Entwicklungsumgebung Debug-Mode aktivieren
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
cspHandler.enableDebugMode();
console.log('🔍 CSP Debug Mode aktiv - Drücken Sie Ctrl+Shift+C für Debug-Panel');
}
// Global verfügbar machen
window.cspHandler = cspHandler;
console.log('🛡️ CSP Violation Handler geladen');