secubox-openwrt/package/secubox/luci-app-ai-insights/htdocs/luci-static/resources/view/ai-insights/dashboard.js
CyberMind-FR 040b69ad1d feat(ai-insights): Add CVE feed panel to dashboard
- Add get_cve_feed RPCD method fetching from NVD API
- Add CVE feed panel showing recent vulnerabilities with CVSS scores
- Cache CVE feed for 30 minutes to reduce API calls
- Link CVE IDs to NVD detail pages
- Color-code severity (critical/high/medium/low)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-12 09:43:41 +01:00

301 lines
10 KiB
JavaScript

'use strict';
'require view';
'require dom';
'require poll';
'require ui';
'require ai-insights.api as api';
'require secubox/kiss-theme';
/**
* AI Insights Dashboard - v1.0.0
* Unified view across all SecuBox AI agents
*/
return view.extend({
cveCache: [],
load: function() {
var self = this;
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = L.resource('ai-insights/dashboard.css');
document.head.appendChild(link);
return Promise.all([
api.getOverview().catch(function() { return {}; }),
api.getCVEFeed(10).catch(function() { return { cves: [] }; })
]).then(function(results) {
self.cveCache = (results[1] || {}).cves || [];
return results[0];
});
},
render: function(data) {
var self = this;
var s = data.status || {};
var p = data.posture || {};
var alerts = data.alerts || [];
var agents = s.agents || {};
var view = E('div', { 'class': 'ai-view' }, [
// Header
E('div', { 'class': 'ai-header' }, [
E('div', { 'class': 'ai-title' }, 'AI Security Insights'),
E('div', { 'class': 'ai-status' }, [
E('span', { 'class': 'ai-dot ' + (s.localai === 'online' ? 'online' : 'offline') }),
'LocalAI: ' + (s.localai || 'offline')
])
]),
// Posture Score (hero)
E('div', { 'class': 'ai-posture-card ' + api.getPostureColor(p.score || 0), 'id': 'ai-posture' }, [
E('div', { 'class': 'ai-posture-score' }, [
E('div', { 'class': 'ai-score-value' }, String(p.score || 0)),
E('div', { 'class': 'ai-score-label' }, 'Security Score')
]),
E('div', { 'class': 'ai-posture-info' }, [
E('div', { 'class': 'ai-posture-label' }, api.getPostureLabel(p.score || 0)),
E('div', { 'class': 'ai-posture-factors' }, p.factors || 'Calculating...')
])
]),
// Stats row
E('div', { 'class': 'ai-stats', 'id': 'ai-stats' }, this.renderStats(s)),
// Agents grid
E('div', { 'class': 'ai-agents-grid', 'id': 'ai-agents' }, this.renderAgents(agents)),
// Actions card
E('div', { 'class': 'ai-card' }, [
E('div', { 'class': 'ai-card-header' }, 'Actions'),
E('div', { 'class': 'ai-card-body' }, this.renderActions())
]),
// Alerts card
E('div', { 'class': 'ai-card' }, [
E('div', { 'class': 'ai-card-header' }, [
'Recent Activity',
E('span', { 'class': 'ai-badge' }, String(alerts.length))
]),
E('div', { 'class': 'ai-card-body', 'id': 'ai-alerts' }, this.renderAlerts(alerts))
]),
// CVE Feed card
E('div', { 'class': 'ai-card' }, [
E('div', { 'class': 'ai-card-header' }, [
'⚠️ CVE Feed (Recent)',
E('span', { 'class': 'ai-badge warning' }, String(this.cveCache.length))
]),
E('div', { 'class': 'ai-card-body', 'id': 'ai-cves' }, this.renderCVEs(this.cveCache))
])
]);
poll.add(L.bind(this.pollData, this), 15);
return KissTheme.wrap([view], 'admin/secubox/ai/insights');
},
renderStats: function(s) {
var agents = s.agents || {};
var online = Object.values(agents).filter(function(a) { return a.status === 'online'; }).length;
var totalAlerts = Object.values(agents).reduce(function(sum, a) { return sum + (a.alerts || 0); }, 0);
var statItems = [
{ label: 'Agents Online', value: online + '/4', type: online === 4 ? 'success' : online > 0 ? 'warning' : 'danger' },
{ label: 'LocalAI', value: s.localai === 'online' ? 'OK' : 'OFF', type: s.localai === 'online' ? 'success' : 'danger' },
{ label: 'Pending', value: totalAlerts, type: totalAlerts > 10 ? 'danger' : totalAlerts > 0 ? 'warning' : 'success' },
{ label: 'Memories', value: s.memories || 0, type: '' }
];
return statItems.map(function(st) {
return E('div', { 'class': 'ai-stat ' + st.type }, [
E('div', { 'class': 'ai-stat-value' }, String(st.value)),
E('div', { 'class': 'ai-stat-label' }, st.label)
]);
});
},
renderAgents: function(agents) {
var agentList = ['threat_analyst', 'dns_guard', 'network_anomaly', 'cve_triage'];
return agentList.map(function(id) {
var agent = agents[id] || {};
var isOnline = agent.status === 'online';
return E('div', { 'class': 'ai-agent-card ' + (isOnline ? 'online' : 'offline') }, [
E('div', { 'class': 'ai-agent-icon' }, api.getAgentIcon(id)),
E('div', { 'class': 'ai-agent-info' }, [
E('div', { 'class': 'ai-agent-name' }, api.getAgentName(id)),
E('div', { 'class': 'ai-agent-status' }, [
E('span', { 'class': 'ai-dot ' + (isOnline ? 'online' : 'offline') }),
isOnline ? 'Running' : 'Stopped'
])
]),
E('div', { 'class': 'ai-agent-alerts' }, [
E('span', { 'class': 'ai-badge ' + (agent.alerts > 0 ? 'warning' : '') }, String(agent.alerts || 0)),
E('span', {}, ' alerts')
])
]);
});
},
renderActions: function() {
var self = this;
return E('div', { 'class': 'ai-actions' }, [
E('button', {
'class': 'ai-btn ai-btn-primary',
'click': function() { self.runAllAgents(); }
}, 'Run All Agents'),
E('button', {
'class': 'ai-btn ai-btn-secondary',
'click': function() { self.getAIAnalysis(); }
}, 'AI Analysis'),
E('button', {
'class': 'ai-btn ai-btn-info',
'click': function() { self.showTimeline(); }
}, 'View Timeline'),
E('a', {
'class': 'ai-btn ai-btn-outline',
'href': L.url('admin/secubox/ai/localrecall')
}, 'LocalRecall')
]);
},
renderAlerts: function(alerts) {
if (!alerts || !alerts.length) {
return E('div', { 'class': 'ai-empty' }, 'No recent activity from AI agents');
}
return E('div', { 'class': 'ai-alerts-list' }, alerts.slice(0, 15).map(function(alert) {
var data = alert.data || {};
var source = alert.source || 'unknown';
var type = alert.type || 'alert';
return E('div', { 'class': 'ai-alert-item ' + type }, [
E('div', { 'class': 'ai-alert-icon' }, api.getAgentIcon(source)),
E('div', { 'class': 'ai-alert-content' }, [
E('div', { 'class': 'ai-alert-source' }, api.getAgentName(source)),
E('div', { 'class': 'ai-alert-message' }, data.message || data.type || type)
]),
E('div', { 'class': 'ai-alert-time' }, api.formatRelativeTime(data.timestamp))
]);
}));
},
renderCVEs: function(cves) {
if (!cves || !cves.length) {
return E('div', { 'class': 'ai-empty' }, 'Loading CVE feed...');
}
return E('div', { 'class': 'ai-cve-list' }, cves.slice(0, 10).map(function(cve) {
var score = cve.score || 0;
var severity = score >= 9.0 ? 'critical' : score >= 7.0 ? 'high' : score >= 4.0 ? 'medium' : 'low';
var severityClass = score >= 9.0 ? 'danger' : score >= 7.0 ? 'warning' : score >= 4.0 ? 'caution' : 'success';
return E('div', { 'class': 'ai-cve-item' }, [
E('div', { 'class': 'ai-cve-score ' + severityClass }, [
E('span', { 'class': 'score-value' }, score.toFixed(1)),
E('span', { 'class': 'score-label' }, severity.toUpperCase())
]),
E('div', { 'class': 'ai-cve-content' }, [
E('div', { 'class': 'ai-cve-id' }, [
E('a', { 'href': 'https://nvd.nist.gov/vuln/detail/' + cve.id, 'target': '_blank' }, cve.id)
]),
E('div', { 'class': 'ai-cve-desc' }, (cve.description || '').substring(0, 120) + '...')
]),
E('div', { 'class': 'ai-cve-date' }, cve.published || '')
]);
}));
},
runAllAgents: function() {
ui.showModal('Running Agents', [
E('p', { 'class': 'spinning' }, 'Starting all AI agents...')
]);
api.runAll().then(function(result) {
ui.hideModal();
var started = Object.values(result).filter(function(s) { return s === 'started'; }).length;
ui.addNotification(null, E('p', {}, 'Started ' + started + ' agents'), 'success');
}).catch(function() {
ui.hideModal();
ui.addNotification(null, E('p', {}, 'Failed to start agents'), 'error');
});
},
getAIAnalysis: function() {
ui.showModal('AI Analysis', [
E('p', { 'class': 'spinning' }, 'Generating security analysis (may take up to 60s)...')
]);
api.analyze().then(function(result) {
ui.hideModal();
if (result.analysis) {
ui.showModal('Security Analysis', [
E('div', { 'style': 'white-space: pre-wrap; max-height: 400px; overflow-y: auto;' }, result.analysis),
E('div', { 'class': 'right' }, E('button', {
'class': 'btn',
'click': ui.hideModal
}, 'Close'))
]);
} else {
ui.addNotification(null, E('p', {}, 'Error: ' + (result.error || 'Analysis failed')), 'error');
}
}).catch(function() {
ui.hideModal();
ui.addNotification(null, E('p', {}, 'Analysis failed'), 'error');
});
},
showTimeline: function() {
api.getTimeline(24).then(function(result) {
var timeline = result.timeline || [];
ui.showModal('Security Timeline (24h)', [
E('div', { 'style': 'max-height: 400px; overflow-y: auto;' },
timeline.length ? E('div', { 'class': 'ai-timeline' }, timeline.map(function(e) {
return E('div', { 'class': 'ai-timeline-item' }, [
E('div', { 'class': 'ai-timeline-time' }, e.time),
E('div', { 'class': 'ai-timeline-source' }, e.source),
E('div', { 'class': 'ai-timeline-msg' }, e.message)
]);
})) : E('p', {}, 'No events in the last 24 hours')
),
E('div', { 'class': 'right' }, E('button', {
'class': 'btn',
'click': ui.hideModal
}, 'Close'))
]);
});
},
pollData: function() {
var self = this;
return api.getOverview().then(function(data) {
var s = data.status || {};
var p = data.posture || {};
var alerts = data.alerts || [];
var agents = s.agents || {};
var el = document.getElementById('ai-stats');
if (el) dom.content(el, self.renderStats(s));
el = document.getElementById('ai-agents');
if (el) dom.content(el, self.renderAgents(agents));
el = document.getElementById('ai-alerts');
if (el) dom.content(el, self.renderAlerts(alerts));
// Update posture card
el = document.getElementById('ai-posture');
if (el) {
el.className = 'ai-posture-card ' + api.getPostureColor(p.score || 0);
var scoreEl = el.querySelector('.ai-score-value');
var labelEl = el.querySelector('.ai-posture-label');
var factorsEl = el.querySelector('.ai-posture-factors');
if (scoreEl) scoreEl.textContent = String(p.score || 0);
if (labelEl) labelEl.textContent = api.getPostureLabel(p.score || 0);
if (factorsEl) factorsEl.textContent = p.factors || '';
}
});
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});