'use strict'; 'require view'; 'require poll'; 'require dom'; 'require ui'; 'require netifyd-dashboard.api as api'; return view.extend({ title: _('Network Risks'), load: function() { return Promise.all([ api.getStats(), api.getFlows(), api.getApplications() ]).then(function(results) { return { stats: results[0] || {}, flows: results[1] || {}, applications: results[2] || {} }; }); }, analyzeRisks: function(data) { var risks = []; var flows = (data.flows || {}).flows || []; var apps = (data.applications || {}).applications || []; // Check for risky ports var riskyPorts = { 23: { name: 'Telnet', severity: 'high', desc: 'Unencrypted remote access' }, 21: { name: 'FTP', severity: 'medium', desc: 'Unencrypted file transfer' }, 445: { name: 'SMB', severity: 'medium', desc: 'Windows file sharing' }, 3389: { name: 'RDP', severity: 'medium', desc: 'Remote Desktop Protocol' }, 5900: { name: 'VNC', severity: 'medium', desc: 'Virtual Network Computing' }, 6379: { name: 'Redis', severity: 'high', desc: 'Database exposed' }, 27017: { name: 'MongoDB', severity: 'high', desc: 'Database exposed' }, 1433: { name: 'MSSQL', severity: 'high', desc: 'Database exposed' }, 3306: { name: 'MySQL', severity: 'high', desc: 'Database exposed' }, 5432: { name: 'PostgreSQL', severity: 'high', desc: 'Database exposed' } }; flows.forEach(function(flow) { var port = parseInt(flow.dst_port); if (riskyPorts[port]) { risks.push({ type: 'risky_port', severity: riskyPorts[port].severity, title: riskyPorts[port].name + ' Traffic Detected', description: riskyPorts[port].desc + ' on port ' + port, source: flow.src_ip, destination: flow.dst_ip + ':' + port, recommendation: 'Consider using encrypted alternatives or restricting access' }); } }); // Check for P2P traffic apps.forEach(function(app) { if (app.category === 'P2P' && app.flows > 5) { risks.push({ type: 'p2p', severity: 'low', title: 'P2P Traffic: ' + app.name, description: app.flows + ' active connections', recommendation: 'Monitor bandwidth usage and ensure compliance with policies' }); } }); // Check for high connection count from single source var sourceCounts = {}; flows.forEach(function(flow) { sourceCounts[flow.src_ip] = (sourceCounts[flow.src_ip] || 0) + 1; }); Object.keys(sourceCounts).forEach(function(ip) { if (sourceCounts[ip] > 50) { risks.push({ type: 'high_connections', severity: 'medium', title: 'High Connection Count', description: ip + ' has ' + sourceCounts[ip] + ' active connections', recommendation: 'Investigate for potential scanning or malware activity' }); } }); // Sort by severity var severityOrder = { high: 0, medium: 1, low: 2 }; risks.sort(function(a, b) { return severityOrder[a.severity] - severityOrder[b.severity]; }); return risks; }, render: function(data) { var risks = this.analyzeRisks(data); var highCount = risks.filter(function(r) { return r.severity === 'high'; }).length; var mediumCount = risks.filter(function(r) { return r.severity === 'medium'; }).length; var lowCount = risks.filter(function(r) { return r.severity === 'low'; }).length; var view = E('div', { 'class': 'netifyd-dashboard' }, [ // Header E('div', { 'class': 'nf-header' }, [ E('div', { 'class': 'nf-logo' }, [ E('div', { 'class': 'nf-logo-icon' }, '⚠️'), E('div', { 'class': 'nf-logo-text' }, ['Risk ', E('span', {}, 'Assessment')]) ]) ]), // Summary Stats E('div', { 'class': 'nf-quick-stats' }, [ E('div', { 'class': 'nf-quick-stat', 'style': '--stat-color: #ef4444' }, [ E('div', { 'class': 'nf-quick-stat-header' }, [ E('span', { 'class': 'nf-quick-stat-icon' }, '🔴'), E('span', { 'class': 'nf-quick-stat-label' }, 'High Risk') ]), E('div', { 'class': 'nf-quick-stat-value', 'style': 'color: #ef4444' }, highCount), E('div', { 'class': 'nf-quick-stat-sub' }, 'Immediate attention') ]), E('div', { 'class': 'nf-quick-stat', 'style': '--stat-color: #f59e0b' }, [ E('div', { 'class': 'nf-quick-stat-header' }, [ E('span', { 'class': 'nf-quick-stat-icon' }, '🟠'), E('span', { 'class': 'nf-quick-stat-label' }, 'Medium Risk') ]), E('div', { 'class': 'nf-quick-stat-value', 'style': 'color: #f59e0b' }, mediumCount), E('div', { 'class': 'nf-quick-stat-sub' }, 'Review recommended') ]), E('div', { 'class': 'nf-quick-stat', 'style': '--stat-color: #10b981' }, [ E('div', { 'class': 'nf-quick-stat-header' }, [ E('span', { 'class': 'nf-quick-stat-icon' }, '🟢'), E('span', { 'class': 'nf-quick-stat-label' }, 'Low Risk') ]), E('div', { 'class': 'nf-quick-stat-value', 'style': 'color: #10b981' }, lowCount), E('div', { 'class': 'nf-quick-stat-sub' }, 'Informational') ]), E('div', { 'class': 'nf-quick-stat' }, [ E('div', { 'class': 'nf-quick-stat-header' }, [ E('span', { 'class': 'nf-quick-stat-icon' }, '📊'), E('span', { 'class': 'nf-quick-stat-label' }, 'Total Issues') ]), E('div', { 'class': 'nf-quick-stat-value' }, risks.length), E('div', { 'class': 'nf-quick-stat-sub' }, 'Detected') ]) ]), // Risk List E('div', { 'class': 'nf-card' }, [ E('div', { 'class': 'nf-card-header' }, [ E('div', { 'class': 'nf-card-title' }, [ E('span', { 'class': 'nf-card-title-icon' }, '🛡️'), 'Security Findings' ]), E('div', { 'class': 'nf-card-badge' }, risks.length + ' issues') ]), E('div', { 'class': 'nf-card-body' }, risks.length > 0 ? risks.map(function(risk) { var severityClass = risk.severity === 'high' ? 'nf-risk-high' : risk.severity === 'medium' ? 'nf-risk-medium' : 'nf-risk-low'; var severityIcon = risk.severity === 'high' ? '🔴' : risk.severity === 'medium' ? '🟠' : '🟢'; return E('div', { 'class': 'nf-risk-item ' + severityClass }, [ E('div', { 'class': 'nf-risk-header' }, [ E('span', { 'class': 'nf-risk-icon' }, severityIcon), E('div', { 'class': 'nf-risk-title' }, [ E('h4', {}, risk.title), E('span', { 'class': 'nf-risk-severity' }, risk.severity.toUpperCase()) ]) ]), E('div', { 'class': 'nf-risk-body' }, [ E('p', { 'class': 'nf-risk-desc' }, risk.description), risk.source ? E('div', { 'class': 'nf-risk-detail' }, [ E('span', { 'class': 'nf-risk-label' }, 'Source: '), E('code', {}, risk.source) ]) : '', risk.destination ? E('div', { 'class': 'nf-risk-detail' }, [ E('span', { 'class': 'nf-risk-label' }, 'Destination: '), E('code', {}, risk.destination) ]) : '', E('div', { 'class': 'nf-risk-recommendation' }, [ E('strong', {}, '💡 Recommendation: '), risk.recommendation ]) ]) ]); }) : E('div', { 'class': 'nf-empty' }, [ E('div', { 'class': 'nf-empty-icon' }, '✅'), E('div', { 'class': 'nf-empty-text' }, 'No security issues detected'), E('p', {}, 'Your network looks healthy!') ]) ) ]) ]); // Include CSS var css = ` .nf-risk-item { background: var(--nf-bg-tertiary); border: 1px solid var(--nf-border); border-radius: 10px; padding: 16px; margin-bottom: 12px; border-left: 4px solid; } .nf-risk-high { border-left-color: #ef4444; } .nf-risk-medium { border-left-color: #f59e0b; } .nf-risk-low { border-left-color: #10b981; } .nf-risk-header { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 12px; } .nf-risk-icon { font-size: 24px; } .nf-risk-title { flex: 1; } .nf-risk-title h4 { margin: 0 0 4px 0; font-size: 15px; } .nf-risk-severity { font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 10px; background: rgba(255,255,255,0.1); } .nf-risk-desc { color: var(--nf-text-secondary); font-size: 13px; margin: 0 0 10px 0; } .nf-risk-detail { font-size: 12px; margin: 4px 0; } .nf-risk-detail code { background: var(--nf-bg-primary); padding: 2px 6px; border-radius: 4px; font-family: var(--nf-font-mono); } .nf-risk-recommendation { margin-top: 10px; padding: 10px; background: rgba(139, 92, 246, 0.1); border-radius: 6px; font-size: 12px; color: var(--nf-text-secondary); } `; var style = E('style', {}, css); document.head.appendChild(style); var cssLink = E('link', { 'rel': 'stylesheet', 'href': L.resource('netifyd-dashboard/dashboard.css') }); document.head.appendChild(cssLink); return view; }, handleSaveApply: null, handleSave: null, handleReset: null });