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}
+
+
+
+
+
+
Recent Threat Details (Sample)
+
+
+ | Date | Time | Level | Message |
+
+ ${stats.attackDetails.map(d => `| ${d.date} | ${d.time} | ${d.level} | ${d.msg} |
`).join('')}
+
+
+
+
+
+
+`;
+
+ 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"
+ }
+}