Files
Log-Analayzer/analyzer.js

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);
}