93 lines
10 KiB
JSON
93 lines
10 KiB
JSON
{
|
|
"id": "OCwRpt7eK3mQ2xL9",
|
|
"name": "OCSentinel - Weekly Organization Reports",
|
|
"nodes": [
|
|
{
|
|
"parameters": { "rule": { "interval": [{ "field": "weeks", "weeksInterval": 1, "triggerAtDay": [1], "triggerAtHour": 7, "triggerAtMinute": 20 }] } },
|
|
"id": "schedule-weekly-reports", "name": "Every Monday 07:20", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.3, "position": [300, 300]
|
|
},
|
|
{
|
|
"parameters": { "operation": "executeQuery", "query": "WITH latest AS (\n SELECT DISTINCT ON (d.id) d.machine_name, r.alert_state, r.payload\n FROM ocsentinel.scan_report AS r\n JOIN ocsentinel.device AS d ON d.id = r.device_id\n WHERE r.received_at >= now() - interval '8 days'\n ORDER BY d.id, r.generated_at_utc DESC, r.received_at DESC\n)\nSELECT machine_name, alert_state, payload\nFROM latest\nORDER BY payload #>> '{NinjaOne,OrganizationName}', machine_name;" },
|
|
"id": "load-weekly-data", "name": "Load Latest Device Reports", "type": "n8n-nodes-base.postgres", "typeVersion": 2.5, "position": [560, 300],
|
|
"credentials": { "postgres": { "id": "WkjY0kIF3kHvREys", "name": "OCSentinel PostgreSQL" } }
|
|
},
|
|
{
|
|
"parameters": { "jsCode": "const esc=v=>String(v??'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\\\"/g,'"').replace(/'/g,''');\nconst now=new Date(),end=new Date(Date.UTC(now.getUTCFullYear(),now.getUTCMonth(),now.getUTCDate()));\nend.setUTCDate(end.getUTCDate()-((end.getUTCDay()+6)%7)); const start=new Date(end-7*86400000);\nconst groups=new Map();\nfor(const item of items){const p=item.json.payload||{},n=p.NinjaOne||p.ninjaOne||{},id=String(n.OrganizationId||n.organizationId||'unknown'),name=String(n.OrganizationName||n.organizationName||`Organisation ${id}`);if(!groups.has(id))groups.set(id,{id,name,devices:[]});groups.get(id).devices.push({machine:item.json.machine_name||p.MachineName||'Unbekannt',state:String(item.json.alert_state||p.AlertState||'unknown').toLowerCase(),p});}\nconst result=[];\nfor(const group of groups.values()){let warnings=0,criticals=0,total=0,cveTotal=0,cveCritical=0;const ips=new Set(),alerts=[],clean=[];for(const d of group.devices){if(d.state==='warning')warnings++;if(d.state==='critical')criticals++;const vc=d.p.VulnerabilityCorrelation||{};cveTotal+=Number(vc.TotalCount||0);cveCritical+=Number(vc.CriticalCount||0);const events=(d.p.Events||[]).filter(e=>{const t=new Date(e.Timestamp);return !Number.isNaN(t)&&t>=start&&t<end;});total+=events.length;if(!events.length){clean.push(`<tr class=\"clean\"><td>${esc(d.machine)}</td><td>Keine</td><td>-</td><td>-</td><td>0</td><td>-</td><td>Log sauber / Keine Angriffe</td></tr>`);continue;}const rows=new Map();for(const e of events){const ip=e.SourceIp||'-',account=e.Username||'-',type=e.Target||'Sicherheitsereignis',key=[type,account,ip].join('|'),row=rows.get(key)||{ip,account,type,count:0,last:e.Timestamp};row.count++;if(new Date(e.Timestamp)>new Date(row.last))row.last=e.Timestamp;rows.set(key,row);if(ip!=='-')ips.add(ip);}for(const r of rows.values()){alerts.push(`<tr class=\"alert\"><td>${esc(d.machine)}</td><td>${esc(r.type)}</td><td>${esc(r.account)}</td><td>${esc(new Date(r.last).toLocaleString('de-DE',{timeZone:'Europe/Berlin'}))}</td><td>${r.count}</td><td>${esc(r.ip)}</td><td>${d.state==='critical'?'Problem entdeckt (kritisch)':'Problem entdeckt'}</td></tr>`);}}const summary={deviceCount:group.devices.length,warningCount:warnings,criticalCount:criticals,totalEvents:total,uniqueIps:ips.size,cveTotal,cveCritical};const html=`<div class=\"ocsentinel-report\"><style>.ocsentinel-report{font-family:Arial,sans-serif;font-size:12px;color:#1f2937;max-width:1200px}.ocsentinel-report h2{font-size:16px;color:#183b79;margin:0 0 5px;border-bottom:1px solid #183b79;padding-bottom:5px}.ocsentinel-report .meta{font-size:11px;color:#4b5563;margin-bottom:12px}.ocsentinel-report .summary{margin:10px 0;padding:8px;background:#eef5ff;border:1px solid #bfd4f2;color:#183b79}.ocsentinel-report table{width:100%;border-collapse:collapse}.ocsentinel-report th{background:#1f4a99;color:#fff;text-align:left;padding:6px;font-size:11px}.ocsentinel-report td{border:1px solid #e5e7eb;padding:6px;vertical-align:top}.ocsentinel-report tr.alert{background:#fff4ed;color:#8a3d16}.ocsentinel-report tr.clean{background:#effcf4;color:#17643a}.ocsentinel-report .footer{margin-top:10px;font-size:10px;color:#6b7280;text-align:right}</style><h2>OCSentinel - Konsolidierter Sicherheitsbericht</h2><div class=\"meta\"><strong>${esc(group.name)}</strong><br>Berichtszeitraum: ${start.toLocaleDateString('de-DE')} bis ${end.toLocaleDateString('de-DE')}<br>Erstellt am: ${now.toLocaleString('de-DE',{timeZone:'Europe/Berlin'})}</div><div class=\"summary\"><strong>${summary.deviceCount} Geräte</strong> | <strong>${summary.criticalCount} kritisch</strong> | <strong>${summary.warningCount} Warnungen</strong> | <strong>${summary.totalEvents} Ereignisse</strong> | <strong>${summary.uniqueIps} IPs</strong> | <strong>${summary.cveCritical} kritische CVEs</strong></div><table><thead><tr><th>Server</th><th>Vorfall-Typ</th><th>Betroffenes Konto</th><th>Letzter Zeitpunkt</th><th>Anzahl</th><th>Angreifer-IP</th><th>Status / Bemerkung</th></tr></thead><tbody>${alerts.join('')}${clean.join('')}</tbody></table><div class=\"footer\">Automatisch durch OCSentinel und n8n erzeugt.</div></div>`;result.push({json:{organizationId:group.id,organizationName:group.name,periodStartUtc:start.toISOString(),periodEndUtc:end.toISOString(),...summary,reportHtml:html,summaryJson:JSON.stringify(summary)}});}return result;" },
|
|
"id": "build-weekly-reports", "name": "Build Organization HTML Reports", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [820, 300]
|
|
},
|
|
{
|
|
"parameters": { "operation": "executeQuery", "query": "INSERT INTO ocsentinel.weekly_organization_report (organization_id, organization_name, period_start_utc, period_end_utc, device_count, warning_count, critical_count, total_events, unique_ips, cve_total, cve_critical, report_html, summary)\nVALUES ($1, $2, $3::timestamptz, $4::timestamptz, $5, $6, $7, $8, $9, $10, $11, $12, $13::jsonb)\nON CONFLICT (organization_id, period_start_utc) DO UPDATE SET organization_name=EXCLUDED.organization_name, period_end_utc=EXCLUDED.period_end_utc, generated_at=now(), device_count=EXCLUDED.device_count, warning_count=EXCLUDED.warning_count, critical_count=EXCLUDED.critical_count, total_events=EXCLUDED.total_events, unique_ips=EXCLUDED.unique_ips, cve_total=EXCLUDED.cve_total, cve_critical=EXCLUDED.cve_critical, report_html=EXCLUDED.report_html, summary=EXCLUDED.summary\nRETURNING id;", "options": { "queryReplacement": "={{ [$json.organizationId, $json.organizationName, $json.periodStartUtc, $json.periodEndUtc, $json.deviceCount, $json.warningCount, $json.criticalCount, $json.totalEvents, $json.uniqueIps, $json.cveTotal, $json.cveCritical, $json.reportHtml, $json.summaryJson] }}" } },
|
|
"id": "store-weekly-reports", "name": "Store Weekly Organization Reports", "type": "n8n-nodes-base.postgres", "typeVersion": 2.5, "position": [1080, 300],
|
|
"credentials": { "postgres": { "id": "WkjY0kIF3kHvREys", "name": "OCSentinel PostgreSQL" } }
|
|
},
|
|
{
|
|
"parameters": {
|
|
"assignments": {
|
|
"assignments": [
|
|
{ "id": "email-enabled", "name": "emailEnabled", "value": true, "type": "boolean" }
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "email-delivery-toggle",
|
|
"name": "E-Mail-Versand aktiv",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 3.4,
|
|
"position": [1330, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "SELECT coalesce(array_agg(recipient_email ORDER BY recipient_email), ARRAY[]::text[]) AS recipients\nFROM ocsentinel.organization_report_recipient\nWHERE enabled = TRUE AND (organization_id = '*' OR organization_id = $1);",
|
|
"options": { "queryReplacement": "={{ [ $('Build Organization HTML Reports').item.json.organizationId ] }}" }
|
|
},
|
|
"id": "load-report-recipients",
|
|
"name": "Empfaenger aus zentraler Zuordnung laden",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.5,
|
|
"position": [1560, 300]
|
|
,"credentials": { "postgres": { "id": "WkjY0kIF3kHvREys", "name": "OCSentinel PostgreSQL" } }
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const report = $('Build Organization HTML Reports').item.json;\nconst emailEnabled = $('E-Mail-Versand aktiv').item.json.emailEnabled === true;\nif (!emailEnabled) return [];\nconst recipients = Array.from(new Set($json.recipients || []));\nif (recipients.length === 0) throw new Error(`No weekly report recipients configured for ${report.organizationName}.`);\nreturn [{ json: { ...report, recipients } }];"
|
|
},
|
|
"id": "prepare-report-email",
|
|
"name": "E-Mail vorbereiten",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [1800, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"fromEmail": "donotreply@officecom.biz",
|
|
"toEmail": "={{ $json.recipients.join(', ') }}",
|
|
"subject": "=OCSentinel Wochenbericht - {{ $json.organizationName }}",
|
|
"html": "={{ $json.reportHtml }}",
|
|
"options": { "appendAttribution": false }
|
|
},
|
|
"id": "send-weekly-report-email",
|
|
"name": "Send Weekly Organization Report",
|
|
"type": "n8n-nodes-base.emailSend",
|
|
"typeVersion": 2.1,
|
|
"position": [2040, 300],
|
|
"credentials": { "smtp": { "id": "vafGYgYzM9SbxQbW", "name": "SMTP account" } }
|
|
}
|
|
],
|
|
"connections": {
|
|
"Every Monday 07:20": { "main": [[{ "node": "Load Latest Device Reports", "type": "main", "index": 0 }]] },
|
|
"Load Latest Device Reports": { "main": [[{ "node": "Build Organization HTML Reports", "type": "main", "index": 0 }]] },
|
|
"Build Organization HTML Reports": { "main": [[{ "node": "Store Weekly Organization Reports", "type": "main", "index": 0 }]] },
|
|
"Store Weekly Organization Reports": { "main": [[{ "node": "E-Mail-Versand aktiv", "type": "main", "index": 0 }]] },
|
|
"E-Mail-Versand aktiv": { "main": [[{ "node": "Empfaenger aus zentraler Zuordnung laden", "type": "main", "index": 0 }]] },
|
|
"Empfaenger aus zentraler Zuordnung laden": { "main": [[{ "node": "E-Mail vorbereiten", "type": "main", "index": 0 }]] },
|
|
"E-Mail vorbereiten": { "main": [[{ "node": "Send Weekly Organization Report", "type": "main", "index": 0 }]] }
|
|
},
|
|
"settings": { "executionOrder": "v1", "timezone": "Europe/Berlin" },
|
|
"active": true,
|
|
"pinData": {},
|
|
"versionId": "af98f45b-192e-49c8-9a1e-e7b1fc7e00b2",
|
|
"meta": { "templateCredsSetupCompleted": true },
|
|
"tags": []
|
|
}
|