Add detection patterns for latest actively exploited vulnerabilities: - CVE-2025-55182 (React2Shell, CVSS 10.0) - CVE-2025-8110 (Gogs RCE), CVE-2025-53770 (SharePoint) - CVE-2025-52691 (SmarterMail), CVE-2025-40551 (SolarWinds) - CVE-2024-47575 (FortiManager), CVE-2024-21887 (Ivanti) - CVE-2024-3400, CVE-2024-0012, CVE-2024-9474 (PAN-OS) New attack categories based on OWASP Top 10 2025: - HTTP Request Smuggling (TE.CL/CL.TE conflicts) - AI/LLM Prompt Injection (ChatML, instruction markers) - WAF Bypass techniques (Unicode normalization, double encoding) - Supply Chain attacks (CI/CD poisoning, dependency confusion) - Extended SSTI (Jinja2, Freemarker, Velocity, Thymeleaf) - API Abuse (BOLA/IDOR, mass assignment) CrowdSec scenarios split into 11 separate files for reliability. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
408 lines
16 KiB
JavaScript
408 lines
16 KiB
JavaScript
'use strict';
|
|
'require view';
|
|
'require ui';
|
|
'require dom';
|
|
'require poll';
|
|
'require request';
|
|
'require secubox/kiss-theme';
|
|
|
|
var API_BASE = window.location.protocol + '//' + window.location.hostname + ':7331/api/threat-intel';
|
|
|
|
function fetchJSON(endpoint) {
|
|
return request.get(API_BASE + '/' + endpoint, { timeout: 10000 })
|
|
.then(function(res) {
|
|
try { return res.json(); }
|
|
catch(e) { return null; }
|
|
})
|
|
.catch(function() { return null; });
|
|
}
|
|
|
|
function postJSON(endpoint) {
|
|
return request.post(API_BASE + '/' + endpoint, null, { timeout: 15000 })
|
|
.then(function(res) {
|
|
try { return res.json(); }
|
|
catch(e) { return null; }
|
|
})
|
|
.catch(function() { return null; });
|
|
}
|
|
|
|
function timeAgo(ts) {
|
|
if (!ts || ts === 0) return 'Never';
|
|
var now = Math.floor(Date.now() / 1000);
|
|
var diff = now - ts;
|
|
if (diff < 60) return diff + 's ago';
|
|
if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
|
|
if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
|
|
return Math.floor(diff / 86400) + 'd ago';
|
|
}
|
|
|
|
function severityBadge(severity) {
|
|
var colors = {
|
|
critical: '#e74c3c',
|
|
high: '#e67e22',
|
|
medium: '#f1c40f',
|
|
low: '#3498db'
|
|
};
|
|
var color = colors[severity] || '#95a5a6';
|
|
return E('span', {
|
|
'style': 'display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:bold;color:#fff;background:' + color
|
|
}, severity || 'unknown');
|
|
}
|
|
|
|
function trustBadge(trust) {
|
|
var colors = {
|
|
direct: '#27ae60',
|
|
transitive: '#f39c12',
|
|
unknown: '#95a5a6',
|
|
self: '#3498db'
|
|
};
|
|
var icons = {
|
|
direct: '\u2714',
|
|
transitive: '\u2194',
|
|
unknown: '?',
|
|
self: '\u2605'
|
|
};
|
|
var color = colors[trust] || '#95a5a6';
|
|
return E('span', {
|
|
'style': 'display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:bold;color:#fff;background:' + color
|
|
}, (icons[trust] || '') + ' ' + (trust || 'unknown'));
|
|
}
|
|
|
|
return view.extend({
|
|
status: null,
|
|
iocs: [],
|
|
peers: [],
|
|
|
|
load: function() {
|
|
return Promise.all([
|
|
fetchJSON('status'),
|
|
fetchJSON('iocs'),
|
|
fetchJSON('peers')
|
|
]);
|
|
},
|
|
|
|
render: function(data) {
|
|
this.status = data[0] || {};
|
|
this.iocs = data[1] || [];
|
|
this.peers = data[2] || [];
|
|
|
|
var self = this;
|
|
|
|
var content = E('div', { 'class': 'cbi-map', 'style': 'padding:20px;' }, [
|
|
this.renderHeader(),
|
|
this.renderSummaryCards(),
|
|
this.renderActions(),
|
|
this.renderPeerTable(),
|
|
this.renderIOCTable()
|
|
]);
|
|
|
|
poll.add(function() {
|
|
return Promise.all([
|
|
fetchJSON('status'),
|
|
fetchJSON('iocs'),
|
|
fetchJSON('peers')
|
|
]).then(function(fresh) {
|
|
self.status = fresh[0] || self.status;
|
|
self.iocs = fresh[1] || self.iocs;
|
|
self.peers = fresh[2] || self.peers;
|
|
self.updateCards();
|
|
self.updatePeerTable();
|
|
self.updateIOCTable();
|
|
});
|
|
}, 30);
|
|
|
|
return KissTheme.wrap(content, 'admin/secubox/p2p/threat-hub');
|
|
},
|
|
|
|
renderHeader: function() {
|
|
var enabled = this.status.enabled !== false;
|
|
return E('div', { 'style': 'margin-bottom:24px;' }, [
|
|
E('h2', { 'style': 'margin:0 0 8px;color:#ecf0f1;font-size:24px;' },
|
|
'Threat Intelligence Hub'),
|
|
E('p', { 'style': 'margin:0;color:#95a5a6;font-size:14px;' },
|
|
'Decentralized IOC sharing across mesh nodes via CrowdSec + mitmproxy'),
|
|
E('div', { 'style': 'margin-top:8px;' }, [
|
|
E('span', {
|
|
'style': 'display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:bold;color:#fff;background:' + (enabled ? '#27ae60' : '#e74c3c')
|
|
}, enabled ? 'ACTIVE' : 'DISABLED'),
|
|
this.status.auto_apply ?
|
|
E('span', { 'style': 'display:inline-block;margin-left:8px;padding:4px 12px;border-radius:12px;font-size:12px;color:#fff;background:#2980b9;' }, 'Auto-Apply ON') : null
|
|
])
|
|
]);
|
|
},
|
|
|
|
renderSummaryCards: function() {
|
|
var s = this.status;
|
|
var cards = [
|
|
{ id: 'card-local', label: 'Local IOCs Shared', value: s.local_iocs || 0, icon: '\uD83D\uDCE4', color: '#3498db' },
|
|
{ id: 'card-received', label: 'Received from Mesh', value: s.received_iocs || 0, icon: '\uD83D\uDCE5', color: '#e67e22' },
|
|
{ id: 'card-applied', label: 'Applied to Firewall', value: s.applied_iocs || 0, icon: '\uD83D\uDEE1', color: '#27ae60' },
|
|
{ id: 'card-peers', label: 'Peer Contributors', value: s.peer_contributors || 0, icon: '\uD83D\uDC65', color: '#9b59b6' },
|
|
{ id: 'card-chain', label: 'Chain Blocks', value: s.chain_threat_blocks || 0, icon: '\u26D3', color: '#1abc9c' }
|
|
];
|
|
|
|
return E('div', {
|
|
'id': 'summary-cards',
|
|
'style': 'display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:16px;margin-bottom:24px;'
|
|
}, cards.map(function(c) {
|
|
return E('div', {
|
|
'id': c.id,
|
|
'style': 'background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:12px;padding:16px;text-align:center;border-left:4px solid ' + c.color + ';'
|
|
}, [
|
|
E('div', { 'style': 'font-size:28px;margin-bottom:4px;' }, c.icon),
|
|
E('div', {
|
|
'class': 'card-value',
|
|
'style': 'font-size:32px;font-weight:bold;color:' + c.color + ';'
|
|
}, String(c.value)),
|
|
E('div', { 'style': 'font-size:12px;color:#95a5a6;margin-top:4px;' }, c.label)
|
|
]);
|
|
}));
|
|
},
|
|
|
|
renderActions: function() {
|
|
var self = this;
|
|
|
|
var publishBtn = E('button', {
|
|
'class': 'cbi-button cbi-button-action',
|
|
'style': 'margin-right:12px;padding:8px 20px;',
|
|
'click': function() {
|
|
this.disabled = true;
|
|
this.textContent = 'Publishing...';
|
|
var btn = this;
|
|
postJSON('publish').then(function(res) {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Publish Now';
|
|
if (res && res.success)
|
|
ui.addNotification(null, E('p', 'Published ' + (res.published || 0) + ' IOCs to chain'), 'info');
|
|
else
|
|
ui.addNotification(null, E('p', 'Publish failed'), 'error');
|
|
});
|
|
}
|
|
}, 'Publish Now');
|
|
|
|
var applyBtn = E('button', {
|
|
'class': 'cbi-button cbi-button-apply',
|
|
'style': 'padding:8px 20px;',
|
|
'click': function() {
|
|
this.disabled = true;
|
|
this.textContent = 'Applying...';
|
|
var btn = this;
|
|
postJSON('apply').then(function(res) {
|
|
btn.disabled = false;
|
|
btn.textContent = 'Apply Pending';
|
|
if (res && res.success)
|
|
ui.addNotification(null, E('p', 'Applied ' + (res.applied || 0) + ' IOCs, skipped ' + (res.skipped || 0)), 'info');
|
|
else
|
|
ui.addNotification(null, E('p', 'Apply failed'), 'error');
|
|
});
|
|
}
|
|
}, 'Apply Pending');
|
|
|
|
return E('div', { 'style': 'margin-bottom:24px;padding:16px;background:rgba(255,255,255,0.03);border-radius:8px;border:1px solid rgba(255,255,255,0.08);' }, [
|
|
E('h3', { 'style': 'margin:0 0 12px;color:#ecf0f1;font-size:16px;' }, 'Actions'),
|
|
E('div', {}, [publishBtn, applyBtn])
|
|
]);
|
|
},
|
|
|
|
renderPeerTable: function() {
|
|
var peers = this.peers || [];
|
|
|
|
var rows = peers.map(function(p) {
|
|
return E('tr', {}, [
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);font-family:monospace;font-size:13px;color:#ecf0f1;' },
|
|
(p.node || '').substring(0, 12) + '...'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);' },
|
|
trustBadge(p.trust)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#ecf0f1;text-align:center;' },
|
|
String(p.ioc_count || 0)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#ecf0f1;text-align:center;' },
|
|
String(p.applied_count || 0)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
timeAgo(p.last_seen))
|
|
]);
|
|
});
|
|
|
|
if (rows.length === 0) {
|
|
rows = [E('tr', {}, [
|
|
E('td', { 'colspan': '5', 'style': 'padding:24px;text-align:center;color:#95a5a6;' },
|
|
'No peer contributions yet. IOCs from mesh nodes will appear here after sync.')
|
|
])];
|
|
}
|
|
|
|
return E('div', { 'style': 'margin-bottom:24px;' }, [
|
|
E('h3', { 'style': 'margin:0 0 12px;color:#ecf0f1;font-size:16px;' }, 'Peer Contributions'),
|
|
E('div', { 'style': 'overflow-x:auto;' }, [
|
|
E('table', {
|
|
'id': 'peer-table',
|
|
'class': 'table',
|
|
'style': 'width:100%;border-collapse:collapse;background:rgba(255,255,255,0.03);border-radius:8px;border:1px solid rgba(255,255,255,0.08);'
|
|
}, [
|
|
E('thead', {}, [
|
|
E('tr', { 'style': 'background:rgba(255,255,255,0.05);' }, [
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Node'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Trust'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:center;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'IOCs'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:center;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Applied'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Last Seen')
|
|
])
|
|
]),
|
|
E('tbody', {}, rows)
|
|
])
|
|
])
|
|
]);
|
|
},
|
|
|
|
renderIOCTable: function() {
|
|
var iocs = this.iocs || [];
|
|
|
|
var rows = iocs.slice(0, 50).map(function(ioc) {
|
|
return E('tr', {}, [
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);font-family:monospace;font-size:13px;color:#ecf0f1;' },
|
|
ioc.ip || '-'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);' },
|
|
severityBadge(ioc.severity)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
ioc.source || '-'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
ioc.scenario || '-'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);font-family:monospace;font-size:11px;color:#7f8c8d;' },
|
|
(ioc.node || '').substring(0, 12)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);' },
|
|
trustBadge(ioc.trust)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);text-align:center;' },
|
|
ioc.applied ?
|
|
E('span', { 'style': 'color:#27ae60;font-weight:bold;' }, '\u2714') :
|
|
E('span', { 'style': 'color:#95a5a6;' }, '\u2013')),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
timeAgo(ioc.ts))
|
|
]);
|
|
});
|
|
|
|
if (rows.length === 0) {
|
|
rows = [E('tr', {}, [
|
|
E('td', { 'colspan': '8', 'style': 'padding:24px;text-align:center;color:#95a5a6;' },
|
|
'No IOCs received from mesh yet.')
|
|
])];
|
|
}
|
|
|
|
return E('div', {}, [
|
|
E('h3', { 'style': 'margin:0 0 4px;color:#ecf0f1;font-size:16px;' }, 'Received IOCs'),
|
|
E('p', { 'style': 'margin:0 0 12px;color:#7f8c8d;font-size:12px;' },
|
|
'Showing up to 50 most recent. Total: ' + (this.iocs || []).length),
|
|
E('div', { 'style': 'overflow-x:auto;' }, [
|
|
E('table', {
|
|
'id': 'ioc-table',
|
|
'class': 'table',
|
|
'style': 'width:100%;border-collapse:collapse;background:rgba(255,255,255,0.03);border-radius:8px;border:1px solid rgba(255,255,255,0.08);'
|
|
}, [
|
|
E('thead', {}, [
|
|
E('tr', { 'style': 'background:rgba(255,255,255,0.05);' }, [
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'IP'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Severity'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Source'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Scenario'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Origin'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Trust'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:center;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Applied'),
|
|
E('th', { 'style': 'padding:10px 12px;text-align:left;color:#95a5a6;font-size:12px;text-transform:uppercase;' }, 'Age')
|
|
])
|
|
]),
|
|
E('tbody', {}, rows)
|
|
])
|
|
])
|
|
]);
|
|
},
|
|
|
|
updateCards: function() {
|
|
var s = this.status || {};
|
|
var mapping = {
|
|
'card-local': s.local_iocs || 0,
|
|
'card-received': s.received_iocs || 0,
|
|
'card-applied': s.applied_iocs || 0,
|
|
'card-peers': s.peer_contributors || 0,
|
|
'card-chain': s.chain_threat_blocks || 0
|
|
};
|
|
|
|
Object.keys(mapping).forEach(function(id) {
|
|
var card = document.getElementById(id);
|
|
if (card) {
|
|
var valEl = card.querySelector('.card-value');
|
|
if (valEl) valEl.textContent = String(mapping[id]);
|
|
}
|
|
});
|
|
},
|
|
|
|
updatePeerTable: function() {
|
|
var table = document.getElementById('peer-table');
|
|
if (!table) return;
|
|
var tbody = table.querySelector('tbody');
|
|
if (!tbody) return;
|
|
|
|
var peers = this.peers || [];
|
|
dom.content(tbody, peers.length === 0 ?
|
|
E('tr', {}, [
|
|
E('td', { 'colspan': '5', 'style': 'padding:24px;text-align:center;color:#95a5a6;' },
|
|
'No peer contributions yet.')
|
|
]) :
|
|
peers.map(function(p) {
|
|
return E('tr', {}, [
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);font-family:monospace;font-size:13px;color:#ecf0f1;' },
|
|
(p.node || '').substring(0, 12) + '...'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);' },
|
|
trustBadge(p.trust)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#ecf0f1;text-align:center;' },
|
|
String(p.ioc_count || 0)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#ecf0f1;text-align:center;' },
|
|
String(p.applied_count || 0)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
timeAgo(p.last_seen))
|
|
]);
|
|
})
|
|
);
|
|
},
|
|
|
|
updateIOCTable: function() {
|
|
var table = document.getElementById('ioc-table');
|
|
if (!table) return;
|
|
var tbody = table.querySelector('tbody');
|
|
if (!tbody) return;
|
|
|
|
var iocs = (this.iocs || []).slice(0, 50);
|
|
var countEl = table.parentNode.parentNode.querySelector('p');
|
|
if (countEl) countEl.textContent = 'Showing up to 50 most recent. Total: ' + (this.iocs || []).length;
|
|
|
|
dom.content(tbody, iocs.length === 0 ?
|
|
E('tr', {}, [
|
|
E('td', { 'colspan': '8', 'style': 'padding:24px;text-align:center;color:#95a5a6;' },
|
|
'No IOCs received from mesh yet.')
|
|
]) :
|
|
iocs.map(function(ioc) {
|
|
return E('tr', {}, [
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);font-family:monospace;font-size:13px;color:#ecf0f1;' },
|
|
ioc.ip || '-'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);' },
|
|
severityBadge(ioc.severity)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
ioc.source || '-'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
ioc.scenario || '-'),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);font-family:monospace;font-size:11px;color:#7f8c8d;' },
|
|
(ioc.node || '').substring(0, 12)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);' },
|
|
trustBadge(ioc.trust)),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);text-align:center;' },
|
|
ioc.applied ?
|
|
E('span', { 'style': 'color:#27ae60;font-weight:bold;' }, '\u2714') :
|
|
E('span', { 'style': 'color:#95a5a6;' }, '\u2013')),
|
|
E('td', { 'style': 'padding:8px 12px;border-bottom:1px solid rgba(255,255,255,0.05);color:#95a5a6;font-size:12px;' },
|
|
timeAgo(ioc.ts))
|
|
]);
|
|
})
|
|
);
|
|
},
|
|
|
|
handleSaveApply: null,
|
|
handleSave: null,
|
|
handleReset: null
|
|
});
|