import { ShieldAlert, Bug, Skull, Fish, Radio, Server, Zap, HelpCircle } from "lucide-react"; import { createSvgIconUrl, urlProp, type WorldPlugin, type GeoEntity, type TimeRange, type PluginContext, type LayerConfig, type CesiumEntityOptions, type SelectionBehavior, type FilterDefinition, type ServerPluginConfig, } from "@worldwideview/wwv-plugin-sdk"; const THREAT_COLORS: Record = { APT: "#dc2626", // Deep red — nation-state Ransomware: "#f97316", // Orange — financial Botnet: "#a855f7", // Purple — infrastructure Phishing: "#eab308", // Yellow — social eng DDoS: "#3b82f6", // Blue — volumetric Malware: "#ef4444", // Red — generic malicious "C2 Server": "#14b8a6",// Teal — command & control Other: "#6b7280", // Gray — unclassified }; const THREAT_ICONS: Record = { APT: Skull, Ransomware: Zap, Botnet: Radio, Phishing: Fish, DDoS: ShieldAlert, Malware: Bug, "C2 Server": Server, Other: HelpCircle, }; export class CyberAttacksPlugin implements WorldPlugin { id = "cyber-attacks"; name = "Cyber Threats (OTX)"; description = "Active threat campaigns from AlienVault Open Threat Exchange"; icon = ShieldAlert; category = "cyber" as const; version = "2.0.0"; private context: PluginContext | null = null; private iconUrls: Record = {}; async initialize(ctx: PluginContext): Promise { this.context = ctx; } destroy(): void { this.context = null; } async fetch(timeRange: TimeRange): Promise { try { let engineBase = this.context?.getEngineUrl() || 'https://dataenginev2.worldwideview.dev'; engineBase = engineBase.replace(/\/$/, ""); let res; if (timeRange) { res = await globalThis.fetch(`${engineBase}/api/cyber-attacks/history?start=${timeRange.start.getTime()}&end=${timeRange.end.getTime()}`); if (res.status === 404) { res = await globalThis.fetch(`${engineBase}/api/cyber-attacks`); } } else { res = await globalThis.fetch(`${engineBase}/api/cyber-attacks`); } if (!res.ok) throw new Error(`Cyber API returned ${res.status}`); const data = await res.json(); const items = data.items || []; return items.map((item: any): GeoEntity => ({ id: item.id, pluginId: "cyber-attacks", latitude: item.lat, longitude: item.lon, timestamp: new Date(item.pulseModified || Date.now()), label: `${item.threatType}: ${item.ip}`, properties: { ip: item.ip, country: item.country, city: item.city, threatType: item.threatType, adversary: item.adversary, pulseName: item.pulseName, pulseDescription: item.pulseDescription, malwareFamilies: (item.malwareFamilies || []).join(", "), tags: (item.tags || []).join(", "), targetedCountries: (item.targetedCountries || []).join(", "), pulseUrl: urlProp(`https://otx.alienvault.com/pulse/${item.pulseId}`), }, })); } catch (err) { console.error("[CyberAttacksPlugin] Fetch error:", err); return []; } } mapWebsocketPayload(payload: any): GeoEntity[] { const items = payload && Array.isArray(payload.items) ? payload.items : (Array.isArray(payload) ? payload : []); return items.map((item: any): GeoEntity => ({ id: item.id, pluginId: "cyber-attacks", latitude: item.lat, longitude: item.lon, timestamp: new Date(item.pulseModified || Date.now()), label: `${item.threatType}: ${item.ip}`, properties: { ip: item.ip, country: item.country, city: item.city, threatType: item.threatType, adversary: item.adversary, pulseName: item.pulseName, pulseDescription: item.pulseDescription, malwareFamilies: (item.malwareFamilies || []).join(", "), tags: (item.tags || []).join(", "), targetedCountries: (item.targetedCountries || []).join(", "), pulseUrl: urlProp(`https://otx.alienvault.com/pulse/${item.pulseId}`), }, })); } getPollingInterval(): number { return 0; } getServerConfig(): ServerPluginConfig { return { streamUrl: "wss://dataenginev2.worldwideview.dev/stream", apiBasePath: "/api/cyber-attacks", pollingIntervalMs: 0, historyEnabled: true, }; } getLayerConfig(): LayerConfig { return { color: "#ef4444", clusterEnabled: true, clusterDistance: 35, maxEntities: 5000, }; } renderEntity(entity: GeoEntity): CesiumEntityOptions { const threatType = (entity.properties.threatType as string) || "Other"; const color = THREAT_COLORS[threatType] || THREAT_COLORS.Other; const IconComponent = THREAT_ICONS[threatType] || THREAT_ICONS.Other; const cacheKey = `${threatType}-${color}`; if (!this.iconUrls[cacheKey]) { this.iconUrls[cacheKey] = createSvgIconUrl(IconComponent, { color }); } return { type: "billboard", iconUrl: this.iconUrls[cacheKey], color, iconScale: 0.7, }; } getSelectionBehavior(_entity: GeoEntity): SelectionBehavior | null { return { showTrail: false, flyToOffsetMultiplier: 2, flyToBaseDistance: 800000, }; } getFilterDefinitions(): FilterDefinition[] { return [ { id: "threatType", label: "Threat Type", type: "select", propertyKey: "threatType", options: Object.keys(THREAT_COLORS).map((t) => ({ value: t, label: t, })), }, { id: "country", label: "Country", type: "text", propertyKey: "country", }, { id: "adversary", label: "Threat Actor", type: "text", propertyKey: "adversary", }, ]; } getLegend() { return Object.entries(THREAT_COLORS).map(([label, color]) => ({ label, color, filterId: "threatType", filterValue: label, })); } }