"feat: Refactor CSP violation handler for improved security"
This commit is contained in:
parent
43fa61dced
commit
92d0fe2190
@ -1 +1,283 @@
|
||||
|
||||
/**
|
||||
* 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;">×</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');
|
Loading…
x
Reference in New Issue
Block a user