251 lines
8.5 KiB
JavaScript
251 lines
8.5 KiB
JavaScript
const fs = require('fs');
|
|
const csv = require('csv-parser');
|
|
const PDFDocument = require('pdfkit');
|
|
|
|
const inputFile = process.argv[2];
|
|
|
|
if (!inputFile) {
|
|
console.error('Please provide a log file path as an argument.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const stats = {
|
|
totalEvents: 0,
|
|
severityCounts: {},
|
|
dropEvents: 0,
|
|
dropBySrcIP: {},
|
|
thermalWarnings: 0,
|
|
possibleAttacks: 0,
|
|
attackDetails: [],
|
|
startTime: null,
|
|
endTime: null,
|
|
timeline: {} // Format: "YYYY-MM-DD HH:00": { Info: 0, Error: 0, ... }
|
|
};
|
|
|
|
const THREAT_KEYWORDS = ['attack', 'exploit', 'malware', 'bad password', 'auth fail', 'intrusion'];
|
|
|
|
console.log(`Starting analysis of ${inputFile}...`);
|
|
|
|
// Helper to update timeline
|
|
function updateTimeline(dateStr, timeStr, severity) {
|
|
try {
|
|
// Assuming Date format MM/DD/YYYY and Time HH:MM:SS
|
|
const hourKey = `${dateStr} ${timeStr.split(':')[0]}:00`;
|
|
if (!stats.timeline[hourKey]) stats.timeline[hourKey] = {};
|
|
stats.timeline[hourKey][severity] = (stats.timeline[hourKey][severity] || 0) + 1;
|
|
|
|
// Track start/end time
|
|
if (!stats.startTime) stats.startTime = `${dateStr} ${timeStr}`;
|
|
stats.endTime = `${dateStr} ${timeStr}`;
|
|
} catch (e) {
|
|
// Ignore parsing errors for timeline
|
|
}
|
|
}
|
|
|
|
fs.createReadStream(inputFile)
|
|
.pipe(csv({ skipLines: 1 }))
|
|
.on('data', (row) => {
|
|
stats.totalEvents++;
|
|
const level = (row['Level'] || 'Unknown').trim();
|
|
const message = (row['Messages'] || '').toString();
|
|
const date = row['Date'];
|
|
const time = row['Time'];
|
|
|
|
// Stats
|
|
stats.severityCounts[level] = (stats.severityCounts[level] || 0) + 1;
|
|
updateTimeline(date, time, level);
|
|
|
|
// Security Rules
|
|
if (message.includes('DROP')) {
|
|
stats.dropEvents++;
|
|
const srcMatch = message.match(/SRC=([\d\.]+)/);
|
|
if (srcMatch && srcMatch[1]) {
|
|
const ip = srcMatch[1];
|
|
stats.dropBySrcIP[ip] = (stats.dropBySrcIP[ip] || 0) + 1;
|
|
}
|
|
}
|
|
|
|
if (level === 'Emergency' && message.includes('THERMAL')) {
|
|
stats.thermalWarnings++;
|
|
}
|
|
|
|
const lowerMsg = message.toLowerCase();
|
|
for (const word of THREAT_KEYWORDS) {
|
|
if (lowerMsg.includes(word)) {
|
|
stats.possibleAttacks++;
|
|
// Increase capture limit for HTML report
|
|
if (stats.possibleAttacks <= 100) {
|
|
stats.attackDetails.push({ date, time, level, msg: message });
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
})
|
|
.on('end', () => {
|
|
generateReports();
|
|
});
|
|
|
|
function generateReports() {
|
|
const reportBase = inputFile.replace(/\.csv$/i, '');
|
|
generatePDFReport(`${reportBase}.report.pdf`);
|
|
generateHTMLReport(`${reportBase}.report.html`);
|
|
console.log('\nAnalysis Complete.');
|
|
console.log(`PDF Report: ${reportBase}.report.pdf`);
|
|
console.log(`HTML Report: ${reportBase}.report.html`);
|
|
}
|
|
|
|
function generatePDFReport(filename) {
|
|
const doc = new PDFDocument();
|
|
doc.pipe(fs.createWriteStream(filename));
|
|
|
|
// Title
|
|
doc.fontSize(20).text('ASUS Log Security Analysis', { align: 'center' });
|
|
doc.moveDown();
|
|
|
|
// Summary
|
|
doc.fontSize(12).text(`Analyzed File: ${inputFile}`);
|
|
doc.text(`Time Period: ${stats.startTime} to ${stats.endTime}`);
|
|
doc.text(`Total Log Lines: ${stats.totalEvents.toLocaleString()}`);
|
|
doc.moveDown();
|
|
|
|
// Key Stats
|
|
doc.fontSize(14).text('Security Summary', { underline: true });
|
|
doc.fontSize(12).text(`Total Firewall Drops: ${stats.dropEvents.toLocaleString()}`);
|
|
doc.text(`Potential Threats: ${stats.possibleAttacks.toLocaleString()}`);
|
|
doc.text(`Thermal Warnings: ${stats.thermalWarnings}`);
|
|
doc.moveDown();
|
|
|
|
// Chart: Severity Breakdown (Simple Bar Chart)
|
|
doc.addPage();
|
|
doc.fontSize(14).text('Severity Breakdown', { align: 'center' });
|
|
doc.moveDown();
|
|
|
|
const severities = Object.entries(stats.severityCounts).sort(([,a], [,b]) => b - a);
|
|
const maxVal = Math.max(...Object.values(stats.severityCounts));
|
|
const startX = 50;
|
|
let startY = 150;
|
|
const barHeight = 20;
|
|
const maxBarWidth = 400;
|
|
|
|
severities.forEach(([label, count]) => {
|
|
const width = (count / maxVal) * maxBarWidth;
|
|
doc.rect(startX + 100, startY, width, barHeight).fill('blue');
|
|
doc.fillColor('black').text(label, startX, startY + 5);
|
|
doc.text(count.toLocaleString(), startX + 100 + width + 5, startY + 5);
|
|
startY += 30;
|
|
});
|
|
|
|
// Table: Top Blocked IPs
|
|
doc.addPage();
|
|
doc.fontSize(14).text('Top 10 Blocked Source IPs', { underline: true });
|
|
doc.moveDown();
|
|
|
|
const topIPs = Object.entries(stats.dropBySrcIP).sort(([,a], [,b]) => b - a).slice(0, 10);
|
|
topIPs.forEach(([ip, count], i) => {
|
|
doc.fontSize(12).text(`${i+1}. ${ip}: ${count.toLocaleString()} drops`);
|
|
});
|
|
|
|
doc.end();
|
|
}
|
|
|
|
function generateHTMLReport(filename) {
|
|
// Aggregating timeline for Chart.js
|
|
const labels = Object.keys(stats.timeline).sort();
|
|
const datasets = [];
|
|
|
|
// Check which severities exist
|
|
const allSeverities = new Set();
|
|
labels.forEach(t => Object.keys(stats.timeline[t]).forEach(s => allSeverities.add(s)));
|
|
|
|
const colors = { 'Info': '#36a2eb', 'Warning': '#ffcd56', 'Error': '#ff6384', 'Emergency': '#c45850' };
|
|
|
|
allSeverities.forEach(sev => {
|
|
const data = labels.map(t => stats.timeline[t][sev] || 0);
|
|
datasets.push({
|
|
label: sev,
|
|
data: data,
|
|
borderColor: colors[sev] || '#999',
|
|
fill: false
|
|
});
|
|
});
|
|
|
|
const htmlContent = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Log Analysis Report</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<style>
|
|
body { font-family: sans-serif; margin: 20px; }
|
|
.card { border: 1px solid #ccc; padding: 15px; margin-bottom: 20px; border-radius: 5px; }
|
|
.chart-container { position: relative; height: 400px; width: 100%; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
th { background-color: #f2f2f2; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Log Analysis Report</h1>
|
|
<div class="card">
|
|
<h2>Summary</h2>
|
|
<p><strong>File:</strong> ${inputFile}</p>
|
|
<p><strong>Period:</strong> ${stats.startTime} - ${stats.endTime}</p>
|
|
<p><strong>Total Events:</strong> ${stats.totalEvents.toLocaleString()}</p>
|
|
<p><strong>Firewall Drops:</strong> ${stats.dropEvents.toLocaleString()}</p>
|
|
<p><strong>Threats:</strong> ${stats.possibleAttacks}</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Event Timeline</h2>
|
|
<div class="chart-container">
|
|
<canvas id="timelineChart"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Recent Threat Details (Sample)</h2>
|
|
<input type="text" id="filterInput" onkeyup="filterTable()" placeholder="Search threats...">
|
|
<table id="threatTable">
|
|
<thead><tr><th>Date</th><th>Time</th><th>Level</th><th>Message</th></tr></thead>
|
|
<tbody>
|
|
${stats.attackDetails.map(d => `<tr><td>${d.date}</td><td>${d.time}</td><td>${d.level}</td><td>${d.msg}</td></tr>`).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<script>
|
|
const ctx = document.getElementById('timelineChart').getContext('2d');
|
|
new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: ${JSON.stringify(labels)},
|
|
datasets: ${JSON.stringify(datasets)}
|
|
},
|
|
options: { responsive: true, maintainAspectRatio: false }
|
|
});
|
|
|
|
function filterTable() {
|
|
var input, filter, table, tr, td, i, txtValue;
|
|
input = document.getElementById("filterInput");
|
|
filter = input.value.toUpperCase();
|
|
table = document.getElementById("threatTable");
|
|
tr = table.getElementsByTagName("tr");
|
|
for (i = 0; i < tr.length; i++) {
|
|
td = tr[i].getElementsByTagName("td")[3]; // Message column
|
|
if (td) {
|
|
txtValue = td.textContent || td.innerText;
|
|
if (txtValue.toUpperCase().indexOf(filter) > -1) {
|
|
tr[i].style.display = "";
|
|
} else {
|
|
tr[i].style.display = "none";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>`;
|
|
|
|
fs.writeFileSync(filename, htmlContent);
|
|
}
|