42 lines
6.9 KiB
JavaScript
42 lines
6.9 KiB
JavaScript
const esc = (value) => String(value ?? '')
|
|
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
.replace(/"/g, '"').replace(/'/g, ''')
|
|
.replace(/[^\x20-\x7e]/g, (character) => `&#${character.codePointAt(0)};`);
|
|
const now = new Date();
|
|
const end = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
end.setUTCDate(end.getUTCDate() - ((end.getUTCDay() + 6) % 7));
|
|
const start = new Date(end.getTime() - 7 * 86400000);
|
|
const formatDate = (value) => new Date(value).toLocaleString('de-DE', { timeZone: 'Europe/Berlin', dateStyle: 'medium', timeStyle: 'short' });
|
|
const groups = new Map();
|
|
for (const item of items) {
|
|
const payload = item.json.payload || {}, ninja = payload.NinjaOne || payload.ninjaOne || {};
|
|
const id = String(ninja.OrganizationId || ninja.organizationId || 'unknown');
|
|
if (!groups.has(id)) groups.set(id, { id, name: String(ninja.OrganizationName || ninja.organizationName || `Organisation ${id}`), devices: [] });
|
|
groups.get(id).devices.push({ name: item.json.machine_name || payload.MachineName || 'Unbekannt', state: String(item.json.alert_state || payload.AlertState || 'unknown').toLowerCase(), payload });
|
|
}
|
|
const output = [];
|
|
for (const group of groups.values()) {
|
|
let warnings = 0, criticals = 0, totalEvents = 0, cveCritical = 0;
|
|
const ips = new Set(), rows = [];
|
|
for (const device of group.devices.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
if (device.state === 'warning') warnings++;
|
|
if (device.state === 'critical') criticals++;
|
|
cveCritical += Number((device.payload.VulnerabilityCorrelation || {}).CriticalCount || 0);
|
|
const events = (device.payload.Events || []).filter((event) => { const timestamp = new Date(event.Timestamp); return !Number.isNaN(timestamp) && timestamp >= start && timestamp < end; });
|
|
totalEvents += events.length;
|
|
if (!events.length) { rows.push(`<tr bgcolor="#f0fdf4"><td style="padding:8px;border-bottom:1px solid #dbe5ef;font-family:Arial,sans-serif;font-size:12px">${esc(device.name)}</td><td colspan="5" style="padding:8px;border-bottom:1px solid #dbe5ef;font-family:Arial,sans-serif;font-size:12px;color:#166534">Keine sicherheitsrelevanten Ereignisse im Berichtszeitraum.</td><td style="padding:8px;border-bottom:1px solid #dbe5ef;font-family:Arial,sans-serif;font-size:12px;color:#166534"><b>Sauber</b></td></tr>`); continue; }
|
|
const grouped = new Map();
|
|
for (const event of events) {
|
|
const ip = event.SourceIp || '-', account = event.Username || '-', type = event.Target || 'Sicherheitsereignis', key = [type, account, ip].join('|');
|
|
const entry = grouped.get(key) || { ip, account, type, count: 0, latest: event.Timestamp };
|
|
entry.count++; if (new Date(event.Timestamp) > new Date(entry.latest)) entry.latest = event.Timestamp; grouped.set(key, entry); if (ip !== '-') ips.add(ip);
|
|
}
|
|
for (const event of grouped.values()) { const critical = device.state === 'critical'; rows.push(`<tr bgcolor="#fff7ed"><td style="padding:8px;border-bottom:1px solid #fed7aa;font-family:Arial,sans-serif;font-size:12px">${esc(device.name)}</td><td style="padding:8px;border-bottom:1px solid #fed7aa;font-family:Arial,sans-serif;font-size:12px">${esc(event.type)}</td><td style="padding:8px;border-bottom:1px solid #fed7aa;font-family:Arial,sans-serif;font-size:12px">${esc(event.account)}</td><td style="padding:8px;border-bottom:1px solid #fed7aa;font-family:Arial,sans-serif;font-size:12px">${esc(formatDate(event.latest))}</td><td style="padding:8px;border-bottom:1px solid #fed7aa;font-family:Arial,sans-serif;font-size:12px">${event.count}</td><td style="padding:8px;border-bottom:1px solid #fed7aa;font-family:Arial,sans-serif;font-size:12px">${esc(event.ip)}</td><td style="padding:8px;border-bottom:1px solid #fed7aa;font-family:Arial,sans-serif;font-size:12px;color:${critical ? '#991b1b' : '#92400e'}"><b>${critical ? 'Kritisch' : 'Pruefen'}</b></td></tr>`); }
|
|
}
|
|
const risk = criticals ? ['Kritisch', '#991b1b', '#fee2e2'] : warnings ? ['Beobachten', '#92400e', '#fef3c7'] : ['Unauffaellig', '#166534', '#dcfce7'];
|
|
const metric = (value, label) => `<td width="16.66%" style="padding:10px;background:#f8fafc;border:1px solid #dbe5ef;font-family:Arial,sans-serif"><b style="display:block;font-size:20px;color:#102a43">${value}</b><span style="font-size:10px;color:#526577">${label}</span></td>`;
|
|
const html = `<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="max-width:1100px;border-collapse:collapse"><tr><td style="padding:18px 20px;background:#102a43;font-family:Arial,sans-serif;color:#ffffff"><span style="float:right;padding:5px 9px;background:${risk[2]};color:${risk[1]};font-size:11px"><b>${risk[0]}</b></span><h2 style="margin:0;font-size:20px;color:#ffffff">OfficeCom Sentinel Sicherheitsbericht</h2><p style="margin:6px 0 0;color:#d5e2ee;font-size:12px">${esc(group.name)} | ${esc(start.toLocaleDateString('de-DE'))} bis ${esc(end.toLocaleDateString('de-DE'))}</p></td></tr><tr><td style="padding:16px 20px;border:1px solid #dbe5ef"><table role="presentation" width="100%" cellspacing="6" cellpadding="0" border="0"><tr>${metric(group.devices.length, 'Geräte')}${metric(criticals, 'Kritisch')}${metric(warnings, 'Warnungen')}${metric(totalEvents, 'Ereignisse')}${metric(ips.size, 'Quell-IP-Adressen')}${metric(cveCritical, 'Kritische CVEs')}</tr></table><table width="100%" cellspacing="0" cellpadding="0" border="0" style="border-collapse:collapse"><thead><tr bgcolor="#1d4e89"><th align="left" style="padding:8px;color:#fff;font-family:Arial,sans-serif;font-size:11px">System</th><th align="left" style="padding:8px;color:#fff;font-family:Arial,sans-serif;font-size:11px">Vorfall</th><th align="left" style="padding:8px;color:#fff;font-family:Arial,sans-serif;font-size:11px">Konto</th><th align="left" style="padding:8px;color:#fff;font-family:Arial,sans-serif;font-size:11px">Letzter Zeitpunkt</th><th align="left" style="padding:8px;color:#fff;font-family:Arial,sans-serif;font-size:11px">Anzahl</th><th align="left" style="padding:8px;color:#fff;font-family:Arial,sans-serif;font-size:11px">Quell-IP</th><th align="left" style="padding:8px;color:#fff;font-family:Arial,sans-serif;font-size:11px">Bewertung</th></tr></thead><tbody>${rows.join('')}</tbody></table><p style="margin:14px 0 0;text-align:right;color:#64748b;font-family:Arial,sans-serif;font-size:10px">Automatisch erstellt am ${esc(formatDate(now))} durch OfficeCom Sentinel.</p></td></tr></table>`;
|
|
output.push({ json: { organizationId: group.id, organizationName: group.name, periodStartUtc: start.toISOString(), periodEndUtc: end.toISOString(), deviceCount: group.devices.length, warningCount: warnings, criticalCount: criticals, totalEvents, uniqueIps: ips.size, cveTotal: 0, cveCritical, reportHtml: html, summaryJson: JSON.stringify({ deviceCount: group.devices.length, warningCount: warnings, criticalCount: criticals, totalEvents, uniqueIps: ips.size, cveCritical }) } });
|
|
}
|
|
return output;
|