const esc = (value) => String(value ?? '') .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, ''') .replace(/[^\x20-\x7e]/g, (character) => `&#${character.codePointAt(0)};`); const fmt = (value) => new Date(value).toLocaleString('de-DE', { timeZone: 'Europe/Berlin', dateStyle: 'medium', timeStyle: 'short' }); 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 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 date = new Date(event.Timestamp); return !Number.isNaN(date) && date >= start && date < end; }); totalEvents += events.length; if (!events.length) { rows.push(`${esc(device.name)}Keine sicherheitsrelevanten Ereignisse im Berichtszeitraum.Sauber`); 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()) rows.push(`${esc(device.name)}${esc(event.type)}${esc(event.account)}${esc(fmt(event.latest))}${event.count}${esc(event.ip)}${device.state === 'critical' ? 'Kritisch' : 'Pruefen'}`); } const risk = criticals ? ['Kritisch', 'critical'] : warnings ? ['Beobachten', 'warning'] : ['Unauffaellig', 'ok']; const html = `
${risk[0]}

OfficeCom Sentinel Sicherheitsbericht

${esc(group.name)} | ${esc(start.toLocaleDateString('de-DE'))} bis ${esc(end.toLocaleDateString('de-DE'))}
${group.devices.length}Geräte${criticals}Kritisch${warnings}Warnungen${totalEvents}Ereignisse${ips.size}Quell-IP-Adressen${cveCritical}Kritische CVEs
${rows.join('')}
SystemVorfallKontoLetzter ZeitpunktAnzahlQuell-IPBewertung
Automatisch erstellt am ${esc(fmt(now))} durch OfficeCom Sentinel.
`; 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;