- Escape all innerHTML assignments with user/external data across 12 JS files - Upgrade credential encryption: per-value IV, key moved to sessionStorage - Fix open redirect in TOTP auth via proper URL hostname validation - Remove sensitive DNS topology data from localStorage cache - Add security regression test suite (51 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
73 lines
3.4 KiB
JavaScript
73 lines
3.4 KiB
JavaScript
// ========== ERROR LOG VIEWER ==========
|
|
(function() {
|
|
// Inject modal HTML
|
|
injectModal('error-log-modal', '<div id="error-log-modal" class="logs-modal"><div class="logs-modal-content"><div class="logs-header"><h3>📋 Error Logs</h3><div class="logs-controls"><button id="error-log-refresh" style="padding:4px 12px!important;font-size:.85rem!important">🔄 Refresh</button><button id="error-log-clear" style="padding:4px 12px!important;font-size:.85rem!important;background:color-mix(in srgb,var(--bad-fg) 15%,transparent)!important;border-color:var(--bad-fg)!important;color:var(--bad-fg)!important">🗑️ Clear</button><button id="error-log-close" class="close-btn">✕</button></div></div><div class="logs-container"><div id="error-log-content" class="logs-content"><div class="logs-loading">Loading error logs...</div></div></div></div></div>');
|
|
|
|
const modal = document.getElementById('error-log-modal');
|
|
const content = document.getElementById('error-log-content');
|
|
const viewBtn = document.getElementById('view-error-logs');
|
|
const refreshBtn = document.getElementById('error-log-refresh');
|
|
const clearBtn = document.getElementById('error-log-clear');
|
|
const closeBtn = document.getElementById('error-log-close');
|
|
|
|
async function loadErrorLogs() {
|
|
content.innerHTML = '<div class="logs-loading">Loading error logs...</div>';
|
|
|
|
try {
|
|
const response = await fetch('/api/v1/error-logs');
|
|
const data = await response.json();
|
|
|
|
if (data.success && data.logs) {
|
|
if (data.logs.length === 0) {
|
|
content.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--muted);">✅ No errors logged! Everything is working smoothly.</div>';
|
|
} else {
|
|
content.innerHTML = data.logs.map(log => {
|
|
const date = new Date(log.timestamp).toLocaleString();
|
|
return `
|
|
<div class="log-entry error">
|
|
<span class="log-timestamp">${date}</span>
|
|
<span class="log-level">ERROR</span>
|
|
<div class="log-message">
|
|
<strong>${escapeHtml(log.context)}</strong>: ${escapeHtml(log.error)}
|
|
${log.details ? `<br><small style="opacity: 0.7;">${escapeHtml(log.details)}</small>` : ''}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
}
|
|
} else {
|
|
content.innerHTML = '<div style="padding: 20px; color: var(--bad-fg);">❌ Failed to load error logs</div>';
|
|
}
|
|
} catch (error) {
|
|
content.innerHTML = `<div style="padding: 20px; color: var(--bad-fg);">❌ Error loading logs: ${escapeHtml(error.message)}</div>`;
|
|
}
|
|
}
|
|
|
|
async function clearErrorLogs() {
|
|
if (!confirm('Clear all error logs?')) return;
|
|
|
|
try {
|
|
const response = await secureFetch('/api/v1/error-logs', { method: 'DELETE' });
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showNotification('✅ Error logs cleared', 'success', 3000);
|
|
loadErrorLogs();
|
|
} else {
|
|
showNotification('❌ Failed to clear logs', 'error', 3000);
|
|
}
|
|
} catch (error) {
|
|
showNotification(`❌ Error: ${error.message}`, 'error', 3000);
|
|
}
|
|
}
|
|
|
|
viewBtn?.addEventListener('click', () => {
|
|
modal.classList.add('show');
|
|
loadErrorLogs();
|
|
});
|
|
|
|
refreshBtn?.addEventListener('click', loadErrorLogs);
|
|
clearBtn?.addEventListener('click', clearErrorLogs);
|
|
wireModal(modal, closeBtn);
|
|
})();
|