From 37b88e47b98359e99720e35b63628c2049ac829f Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sun, 25 Jan 2026 15:24:37 +0100 Subject: [PATCH] feat(portal): Add Threat Monitor to security cards and stats - Add threat-monitor app to security section in portal.js - Add security stats RPC call (get_security_stats) - Display packets blocked and alerts on dashboard - Add Threat Monitor to featured quick access apps - Show WAN dropped + firewall rejects in events section - Link to Threat Monitor dashboard from events Co-Authored-By: Claude Opus 4.5 --- .../resources/secubox-portal/portal.js | 12 +++++ .../resources/view/secubox-portal/index.js | 53 ++++++++++++++----- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/secubox-portal/portal.js b/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/secubox-portal/portal.js index 97fca8fe..f0121c80 100644 --- a/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/secubox-portal/portal.js +++ b/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/secubox-portal/portal.js @@ -61,6 +61,18 @@ return baseclass.extend({ service: 'mitmproxy', version: '8.1.1' }, + 'threat-monitor': { + id: 'threat-monitor', + name: 'Threat Monitor', + desc: 'Real-time threat detection combining netifyd DPI with CrowdSec intelligence', + icon: '\ud83d\udc41\ufe0f', + iconBg: 'rgba(239, 68, 68, 0.15)', + iconColor: '#ef4444', + section: 'security', + path: 'admin/secubox/security/threats/dashboard', + service: null, + version: '1.0.0' + }, // Network Apps 'bandwidth-manager': { diff --git a/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/index.js b/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/index.js index 4de93749..c059a759 100644 --- a/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/index.js +++ b/package/secubox/luci-app-secubox-portal/htdocs/luci-static/resources/view/secubox-portal/index.js @@ -23,6 +23,11 @@ var callCrowdSecStats = rpc.declare({ method: 'nftables_stats' }); +var callSecurityStats = rpc.declare({ + object: 'luci.secubox-security-threats', + method: 'get_security_stats' +}); + var callGetServices = rpc.declare({ object: 'luci.secubox', method: 'get_services', @@ -42,13 +47,16 @@ return view.extend({ this.loadAppStatuses(), callCrowdSecStats().catch(function() { return null; }), portal.checkInstalledApps(), - callGetServices().catch(function() { return []; }) + callGetServices().catch(function() { return []; }), + callSecurityStats().catch(function() { return null; }) ]).then(function(results) { // Store installed apps info from the last promise self.installedApps = results[4] || {}; // RPC expect unwraps the services array directly var svcResult = results[5] || []; self.detectedServices = Array.isArray(svcResult) ? svcResult : (svcResult.services || []); + // Security stats + self.securityStats = results[6] || {}; return results; }); }, @@ -104,6 +112,7 @@ return view.extend({ var boardInfo = data[0] || {}; var sysInfo = data[1] || {}; var crowdSecStats = data[3] || {}; + var securityStats = this.securityStats || {}; var self = this; // Set portal app context and hide LuCI navigation @@ -142,7 +151,7 @@ return view.extend({ this.renderHeader(), // Content E('div', { 'class': 'sb-portal-content' }, [ - this.renderDashboardSection(boardInfo, sysInfo, crowdSecStats), + this.renderDashboardSection(boardInfo, sysInfo, crowdSecStats, securityStats), this.renderSecuritySection(), this.renderNetworkSection(), this.renderMonitoringSection(), @@ -228,7 +237,7 @@ return view.extend({ }); }, - renderDashboardSection: function(boardInfo, sysInfo, crowdSecStats) { + renderDashboardSection: function(boardInfo, sysInfo, crowdSecStats, securityStats) { var self = this; var securityApps = portal.getAppsBySection('security'); var networkApps = portal.getAppsBySection('network'); @@ -245,6 +254,12 @@ return view.extend({ var crowdSecHealth = crowdSecStats.firewall_health || {}; var crowdSecActive = crowdSecHealth.bouncer_running && crowdSecHealth.decisions_synced; + // Security stats + var wanDropped = securityStats.wan_dropped || 0; + var fwRejects = securityStats.firewall_rejects || 0; + var csBans = securityStats.crowdsec_bans || 0; + var csAlerts = securityStats.crowdsec_alerts_24h || 0; + return E('div', { 'class': 'sb-portal-section active', 'data-section': 'dashboard' }, [ E('div', { 'class': 'sb-section-header' }, [ E('h2', { 'class': 'sb-section-title' }, 'SecuBox Dashboard'), @@ -275,32 +290,33 @@ return view.extend({ E('div', { 'class': 'sb-quick-stat-label' }, 'Services Running') ]), - // CrowdSec Blocked IPs + // Firewall Blocked E('div', { 'class': 'sb-quick-stat' }, [ E('div', { 'class': 'sb-quick-stat-header' }, [ E('div', { 'class': 'sb-quick-stat-icon security' }, '\ud83d\udeab'), E('span', { 'class': 'sb-quick-stat-status ' + (crowdSecActive ? 'running' : 'warning') }, - crowdSecActive ? 'Active' : 'Inactive') + crowdSecActive ? 'Protected' : 'Monitoring') ]), - E('div', { 'class': 'sb-quick-stat-value' }, totalBlocked.toLocaleString()), - E('div', { 'class': 'sb-quick-stat-label' }, 'IPs Blocked') + E('div', { 'class': 'sb-quick-stat-value' }, (wanDropped + fwRejects).toLocaleString()), + E('div', { 'class': 'sb-quick-stat-label' }, 'Packets Blocked') ]), - // Network Apps + // Threat Alerts E('div', { 'class': 'sb-quick-stat' }, [ E('div', { 'class': 'sb-quick-stat-header' }, [ - E('div', { 'class': 'sb-quick-stat-icon network' }, '\ud83c\udf10'), - E('span', { 'class': 'sb-quick-stat-status running' }, 'Configured') + E('div', { 'class': 'sb-quick-stat-icon security' }, '\ud83d\udc41\ufe0f'), + E('span', { 'class': 'sb-quick-stat-status ' + (csAlerts > 0 ? 'warning' : 'running') }, + csAlerts > 0 ? 'Alerts' : 'Clear') ]), - E('div', { 'class': 'sb-quick-stat-value' }, networkApps.length), - E('div', { 'class': 'sb-quick-stat-label' }, 'Network Tools') + E('div', { 'class': 'sb-quick-stat-value' }, csBans + '/' + csAlerts), + E('div', { 'class': 'sb-quick-stat-label' }, 'Bans / Alerts 24h') ]) ]), // Featured Apps E('h3', { 'style': 'margin: 1.5rem 0 1rem; color: var(--cyber-text-primary);' }, 'Quick Access'), E('div', { 'class': 'sb-app-grid' }, - this.renderFeaturedApps(['crowdsec', 'bandwidth-manager', 'media-flow', 'ndpid']) + this.renderFeaturedApps(['crowdsec', 'threat-monitor', 'bandwidth-manager', 'media-flow']) ), // Recent Events placeholder @@ -342,6 +358,17 @@ return view.extend({ (crowdSecStats.ipv4_cscli_count || 0) + ' local) | IPv6: ' + blockedIPv6.toLocaleString()), E('span', { 'class': 'sb-events-meta' }, 'CrowdSec Firewall Protection') ]) + ]) : null, + wanDropped > 0 ? E('div', { 'class': 'sb-events-item' }, [ + E('div', { 'class': 'sb-events-icon warning' }, '\ud83d\udc41\ufe0f'), + E('div', { 'class': 'sb-events-content' }, [ + E('p', { 'class': 'sb-events-message' }, + 'WAN Dropped: ' + wanDropped.toLocaleString() + ' | Firewall Rejects: ' + fwRejects), + E('span', { 'class': 'sb-events-meta' }, [ + 'Threat Monitor - ', + E('a', { 'href': L.url('admin/secubox/security/threats/dashboard') }, 'View Details') + ]) + ]) ]) : null ].filter(Boolean)) ]);