diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c1b324f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +*.pdf +*.csv +*.report.html \ No newline at end of file diff --git a/analyzer.js b/analyzer.js new file mode 100644 index 0000000..0bb6f51 --- /dev/null +++ b/analyzer.js @@ -0,0 +1,250 @@ +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 = ` + + + + Log Analysis Report + + + + +

Log Analysis Report

+
+

Summary

+

File: ${inputFile}

+

Period: ${stats.startTime} - ${stats.endTime}

+

Total Events: ${stats.totalEvents.toLocaleString()}

+

Firewall Drops: ${stats.dropEvents.toLocaleString()}

+

Threats: ${stats.possibleAttacks}

+
+ +
+

Event Timeline

+
+ +
+
+ +
+

Recent Threat Details (Sample)

+ + + + + ${stats.attackDetails.map(d => ``).join('')} + +
DateTimeLevelMessage
${d.date}${d.time}${d.level}${d.msg}
+
+ + + +`; + + fs.writeFileSync(filename, htmlContent); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4aeeb6d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,198 @@ +{ + "name": "networklog", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "networklog", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "csv-parser": "^3.2.0", + "pdfkit": "^0.17.2" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/csv-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.2.0.tgz", + "integrity": "sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==", + "license": "MIT", + "bin": { + "csv-parser": "bin/csv-parser" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/jpeg-exif": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", + "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==", + "license": "MIT" + }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "license": "MIT", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/pdfkit": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.2.tgz", + "integrity": "sha512-UnwF5fXy08f0dnp4jchFYAROKMNTaPqb/xgR8GtCzIcqoTnbOqtp3bwKvO4688oHI6vzEEs8Q6vqqEnC5IUELw==", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "fontkit": "^2.0.4", + "jpeg-exif": "^1.1.4", + "linebreak": "^1.1.0", + "png-js": "^1.0.0" + } + }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..89e94d6 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "networklog", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "csv-parser": "^3.2.0", + "pdfkit": "^0.17.2" + } +}