index.html•10.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bug Bounty MCP Dashboard</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
min-height: 100vh;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
header {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px 30px;
margin-bottom: 30px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
h1 {
color: #667eea;
font-size: 2rem;
margin-bottom: 10px;
}
.subtitle {
color: #666;
font-size: 0.9rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 25px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
}
.stat-label {
color: #666;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 10px;
}
.stat-value {
color: #333;
font-size: 2.5rem;
font-weight: bold;
}
.stat-value.critical { color: #e74c3c; }
.stat-value.high { color: #f39c12; }
.stat-value.medium { color: #f1c40f; }
.stat-value.low { color: #3498db; }
.stat-value.info { color: #95a5a6; }
.main-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
.panel {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 30px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.panel.full-width {
grid-column: 1 / -1;
}
.panel h2 {
color: #667eea;
margin-bottom: 20px;
font-size: 1.5rem;
}
.findings-list {
max-height: 600px;
overflow-y: auto;
}
.finding-item {
border-left: 4px solid #ddd;
padding: 15px;
margin-bottom: 15px;
background: #f8f9fa;
border-radius: 8px;
transition: all 0.3s ease;
}
.finding-item:hover {
background: #e9ecef;
transform: translateX(5px);
}
.finding-item.critical { border-left-color: #e74c3c; }
.finding-item.high { border-left-color: #f39c12; }
.finding-item.medium { border-left-color: #f1c40f; }
.finding-item.low { border-left-color: #3498db; }
.finding-item.info { border-left-color: #95a5a6; }
.finding-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.finding-type {
font-weight: bold;
color: #333;
}
.finding-severity {
padding: 5px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: bold;
text-transform: uppercase;
}
.finding-severity.critical { background: #e74c3c; color: white; }
.finding-severity.high { background: #f39c12; color: white; }
.finding-severity.medium { background: #f1c40f; color: white; }
.finding-severity.low { background: #3498db; color: white; }
.finding-severity.info { background: #95a5a6; color: white; }
.finding-url {
color: #667eea;
font-size: 0.9rem;
word-break: break-all;
margin-bottom: 5px;
}
.finding-description {
color: #666;
font-size: 0.85rem;
margin-top: 8px;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.error {
background: #fee;
color: #c33;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
.refresh-btn {
background: #667eea;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
margin-bottom: 20px;
transition: background 0.3s ease;
}
.refresh-btn:hover {
background: #5568d3;
}
.refresh-btn:active {
transform: scale(0.98);
}
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🐛 Bug Bounty MCP Dashboard</h1>
<p class="subtitle">Real-time security findings and test results</p>
</header>
<div class="stats-grid" id="statsGrid">
<div class="stat-card">
<div class="stat-label">Total Findings</div>
<div class="stat-value" id="totalFindings">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Critical</div>
<div class="stat-value critical" id="criticalCount">-</div>
</div>
<div class="stat-card">
<div class="stat-label">High</div>
<div class="stat-value high" id="highCount">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Medium</div>
<div class="stat-value medium" id="mediumCount">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Low</div>
<div class="stat-value low" id="lowCount">-</div>
</div>
</div>
<div class="main-content">
<div class="panel full-width">
<button class="refresh-btn" onclick="loadData()">🔄 Refresh Data</button>
<h2>Recent Findings</h2>
<div class="findings-list" id="findingsList">
<div class="loading">Loading findings...</div>
</div>
</div>
</div>
</div>
<script>
const API_BASE = '/api';
async function loadData() {
try {
// Load statistics
const statsRes = await fetch(`${API_BASE}/statistics`);
if (statsRes.ok) {
const stats = await statsRes.json();
updateStats(stats);
}
// Load findings
const findingsRes = await fetch(`${API_BASE}/findings`);
if (findingsRes.ok) {
const findings = await findingsRes.json();
displayFindings(findings.findings || []);
} else {
document.getElementById('findingsList').innerHTML =
'<div class="error">Failed to load findings. Is the server running?</div>';
}
} catch (error) {
console.error('Error loading data:', error);
document.getElementById('findingsList').innerHTML =
'<div class="error">Error connecting to server. Please check if the dashboard server is running.</div>';
}
}
function updateStats(stats) {
document.getElementById('totalFindings').textContent = stats.totalFindings || 0;
document.getElementById('criticalCount').textContent = stats.bySeverity?.critical || 0;
document.getElementById('highCount').textContent = stats.bySeverity?.high || 0;
document.getElementById('mediumCount').textContent = stats.bySeverity?.medium || 0;
document.getElementById('lowCount').textContent = stats.bySeverity?.low || 0;
}
function displayFindings(findings) {
const container = document.getElementById('findingsList');
if (findings.length === 0) {
container.innerHTML = '<div class="loading">No findings yet. Start testing to see results here!</div>';
return;
}
container.innerHTML = findings.map(finding => `
<div class="finding-item ${finding.severity}">
<div class="finding-header">
<span class="finding-type">${escapeHtml(finding.type || 'Unknown')}</span>
<span class="finding-severity ${finding.severity}">${finding.severity || 'info'}</span>
</div>
<div class="finding-url">${escapeHtml(finding.target || 'N/A')}</div>
<div class="finding-description">${escapeHtml(finding.description || 'No description')}</div>
${finding.score ? `<div style="margin-top: 5px; font-size: 0.8rem; color: #999;">Score: ${finding.score}/10</div>` : ''}
</div>
`).join('');
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Load data on page load
loadData();
// Auto-refresh every 30 seconds
setInterval(loadData, 30000);
</script>
</body>
</html>