189 lines
16 KiB
JavaScript
189 lines
16 KiB
JavaScript
// SOC Backend - All 9 services in ONE app
|
|
// Includes REAL target monitoring (HTTP security headers, SSL, self-monitoring)
|
|
const http = require("http");
|
|
const PORT = process.env.PORT || 4000;
|
|
|
|
// ── Helpers ──
|
|
function rid() { return Math.random().toString(36).substring(2, 10); }
|
|
function rIP() { return [10, Math.floor(Math.random() * 255), Math.floor(Math.random() * 255), Math.floor(Math.random() * 255)].join("."); }
|
|
function rChoice(a) { return a[Math.floor(Math.random() * a.length)]; }
|
|
function ts(off) { return Date.now() - Math.floor(Math.random() * (off || 3600000)); }
|
|
|
|
// ── Real Target Monitoring ──
|
|
const TARGETS = [
|
|
{ name: "Google", url: "https://www.google.com" },
|
|
{ name: "GitHub", url: "https://github.com" },
|
|
{ name: "Cloudflare", url: "https://www.cloudflare.com" },
|
|
{ name: "SOC-Backend", url: "http://localhost:" + PORT + "/health" }
|
|
];
|
|
|
|
const SECURITY_HEADERS = [
|
|
"content-security-policy", "x-frame-options", "x-content-type-options",
|
|
"strict-transport-security", "x-xss-protection", "referrer-policy",
|
|
"permissions-policy"
|
|
];
|
|
|
|
let realScanResults = [];
|
|
let lastScanTime = 0;
|
|
|
|
async function scanTargets() {
|
|
const results = [];
|
|
for (const target of TARGETS) {
|
|
try {
|
|
const start = Date.now();
|
|
const res = await fetch(target.url, { signal: AbortSignal.timeout(5000), redirect: "follow" });
|
|
const latency = Date.now() - start;
|
|
const headers = {};
|
|
const missing = [];
|
|
const found = [];
|
|
for (const h of SECURITY_HEADERS) {
|
|
const val = res.headers.get(h);
|
|
if (val) { headers[h] = val; found.push(h); }
|
|
else missing.push(h);
|
|
}
|
|
const score = Math.round((found.length / SECURITY_HEADERS.length) * 100);
|
|
results.push({
|
|
target: target.name, url: target.url, status: res.status,
|
|
latency: latency + "ms", securityScore: score + "%",
|
|
headersFound: found.length + "/" + SECURITY_HEADERS.length,
|
|
missing: missing, found: found, scannedAt: Date.now()
|
|
});
|
|
} catch (e) {
|
|
results.push({
|
|
target: target.name, url: target.url, status: "error",
|
|
error: e.message, latency: "timeout", securityScore: "N/A",
|
|
scannedAt: Date.now()
|
|
});
|
|
}
|
|
}
|
|
realScanResults = results;
|
|
lastScanTime = Date.now();
|
|
return results;
|
|
}
|
|
|
|
// Initial scan + every 60s
|
|
scanTargets();
|
|
setInterval(scanTargets, 60000);
|
|
|
|
// ── SERVICE DEFINITIONS ──
|
|
const services = {
|
|
"soc-siem": {
|
|
label: "SIEM", icon: "\ud83d\udccb", role: "Log Collection & Correlation",
|
|
items: () => Array.from({ length: 15 }, () => ({ id: "LOG-" + rid(), source: rChoice(["firewall", "ids", "endpoint", "proxy", "dns"]), type: rChoice(["auth", "network", "system", "app"]), severity: rChoice(["info", "warning", "error", "critical"]), message: rChoice(["Login attempt from " + rIP(), "Port scan detected from " + rIP(), "Certificate expired on host-" + rid(), "DNS query to suspicious domain", "Brute force on SSH from " + rIP()]), ts: ts(), count: Math.floor(Math.random() * 100) + 1 })),
|
|
alerts: () => Array.from({ length: 8 }, () => ({ id: "SA-" + rid(), sev: rChoice(["critical", "high", "medium", "low"]), msg: rChoice(["Multiple failed logins from " + rIP(), "Unusual outbound traffic to " + rIP(), "Malware signature detected", "Privilege escalation attempt", "Data exfiltration suspected"]), src: "SIEM", ts: ts() })),
|
|
rules: () => Array.from({ length: 6 }, (_, i) => ({ id: "SR-" + i, name: rChoice(["Failed Login Threshold", "Port Scan Detection", "DNS Anomaly", "Brute Force", "Lateral Movement", "Data Exfil"]), status: rChoice(["active", "active", "active", "paused"]), matches: Math.floor(Math.random() * 500) })),
|
|
stats: () => ({ evt: Math.floor(Math.random() * 50000) + 10000, req: Math.floor(Math.random() * 2000) + 500, eps: Math.floor(Math.random() * 200) + 50, sources: Math.floor(Math.random() * 20) + 5 })
|
|
},
|
|
"soc-soar": {
|
|
label: "SOAR", icon: "\u26a1", role: "Security Orchestration & Response",
|
|
items: () => Array.from({ length: 10 }, () => ({ id: "PB-" + rid(), name: rChoice(["Block IP", "Isolate Host", "Enrich IOC", "Create Ticket", "Notify SOC", "Quarantine File", "Reset Password", "Disable Account"]), status: rChoice(["active", "active", "paused", "draft"]), runs: Math.floor(Math.random() * 200) + 10, lastRun: ts(), avgTime: Math.floor(Math.random() * 30) + 5 + "s" })),
|
|
alerts: () => Array.from({ length: 4 }, () => ({ id: "OA-" + rid(), sev: rChoice(["high", "medium"]), msg: rChoice(["Playbook failed: timeout", "Integration disconnected", "Rate limit exceeded", "Approval pending"]), src: "SOAR", ts: ts() })),
|
|
rules: () => Array.from({ length: 5 }, (_, i) => ({ id: "OR-" + i, trigger: rChoice(["On critical alert", "On malware detection", "On brute force", "On data exfil", "Manual"]), playbook: "PB-" + rid(), enabled: Math.random() > 0.2 })),
|
|
stats: () => ({ evt: Math.floor(Math.random() * 5000) + 1000, req: Math.floor(Math.random() * 800) + 200, automations: Math.floor(Math.random() * 50) + 10, mttr: Math.floor(Math.random() * 60) + 5 + "min" })
|
|
},
|
|
"soc-honeypot": {
|
|
label: "Honeypot", icon: "\ud83c\udf6f", role: "Deception & Attacker Tracking",
|
|
items: () => Array.from({ length: 12 }, () => ({ id: "HP-" + rid(), type: rChoice(["ssh", "http", "ftp", "smb", "rdp", "telnet"]), attackerIP: rIP(), interactions: Math.floor(Math.random() * 50) + 1, firstSeen: ts(86400000), lastSeen: ts(), payloads: Math.floor(Math.random() * 10) + 1 })),
|
|
alerts: () => Array.from({ length: 5 }, () => ({ id: "HA-" + rid(), sev: rChoice(["critical", "high", "medium"]), msg: rChoice(["New attacker on SSH honeypot from " + rIP(), "Exploit attempt on HTTP honeypot", "Credential stuffing on FTP trap", "Lateral movement in honeynet", "Zero-day payload captured"]), src: "Honeypot", ts: ts() })),
|
|
rules: () => Array.from({ length: 4 }, (_, i) => ({ id: "HR-" + i, name: rChoice(["Auto-block after 10 interactions", "Capture payload", "Alert on new attacker", "Fingerprint attacker"]), active: true })),
|
|
stats: () => ({ evt: Math.floor(Math.random() * 3000) + 500, req: Math.floor(Math.random() * 400) + 100, traps: Math.floor(Math.random() * 8) + 3, captured: Math.floor(Math.random() * 100) + 20 })
|
|
},
|
|
"soc-ids": {
|
|
label: "IDS/IPS", icon: "\ud83d\udee1\ufe0f", role: "Intrusion Detection & Prevention",
|
|
items: () => Array.from({ length: 10 }, () => ({ id: "IDS-" + rid(), sigId: "SID-" + Math.floor(Math.random() * 99999), category: rChoice(["intrusion", "malware", "policy", "recon", "exploit"]), srcIP: rIP(), dstIP: rIP(), action: rChoice(["alert", "drop", "alert"]), ts: ts() })),
|
|
alerts: () => Array.from({ length: 7 }, () => ({ id: "IA-" + rid(), sev: rChoice(["critical", "high", "medium", "low"]), msg: rChoice(["SQL injection from " + rIP(), "XSS payload in HTTP request", "Buffer overflow exploit", "DNS tunneling detected", "Command injection attempt", "C2 communication detected", "Port scan from " + rIP()]), src: "IDS", ts: ts() })),
|
|
rules: () => Array.from({ length: 8 }, (_, i) => ({ id: "IR-" + i, name: rChoice(["SQLi Detection", "XSS Filter", "C2 Beacon", "DNS Tunnel", "Exploit Kit", "Malware Sig", "Recon Scan", "Zero-Day"]), hits: Math.floor(Math.random() * 1000), enabled: true })),
|
|
stats: () => ({ evt: Math.floor(Math.random() * 30000) + 5000, req: Math.floor(Math.random() * 1500) + 300, blocked: Math.floor(Math.random() * 500) + 50, signatures: Math.floor(Math.random() * 5000) + 1000 })
|
|
},
|
|
"soc-firewall": {
|
|
label: "Firewall", icon: "\ud83d\udd25", role: "Network Traffic Control",
|
|
items: () => Array.from({ length: 10 }, () => ({ id: "FW-" + rid(), rule: rChoice(["ALLOW", "DENY", "DENY", "DENY", "LOG"]), srcIP: rIP(), dstIP: rIP(), port: rChoice([22, 80, 443, 3389, 8080, 25, 53]), protocol: rChoice(["TCP", "UDP"]), bytes: Math.floor(Math.random() * 100000), ts: ts() })),
|
|
alerts: () => Array.from({ length: 5 }, () => ({ id: "FA-" + rid(), sev: rChoice(["high", "medium", "low"]), msg: rChoice(["Blocked " + Math.floor(Math.random() * 100) + " attempts from " + rIP(), "Geo-blocked restricted region traffic", "Rate limit triggered on port 443", "Suspicious outbound to " + rIP(), "Firewall rule conflict"]), src: "Firewall", ts: ts() })),
|
|
rules: () => Array.from({ length: 6 }, (_, i) => ({ id: "FR-" + i, name: rChoice(["Block Known Bad IPs", "Allow Internal", "DMZ Policy", "Rate Limit", "Geo Block", "Default Deny"]), action: rChoice(["allow", "deny", "log"]), hits: Math.floor(Math.random() * 10000) })),
|
|
stats: () => ({ evt: Math.floor(Math.random() * 100000) + 20000, req: Math.floor(Math.random() * 3000) + 1000, blocked: Math.floor(Math.random() * 5000) + 1000, allowed: Math.floor(Math.random() * 50000) + 10000 })
|
|
},
|
|
"soc-uba": {
|
|
label: "UBA", icon: "\ud83d\udc64", role: "User Behavior Analytics",
|
|
items: () => Array.from({ length: 8 }, () => ({ id: "UBA-" + rid(), user: rChoice(["admin", "jdoe", "asmith", "mwilson", "klee", "root", "svc-backup"]), riskScore: Math.floor(Math.random() * 100), anomaly: rChoice(["Unusual login time", "New location access", "Privilege escalation", "Bulk file download", "Lateral movement", "Impossible travel"]), status: rChoice(["investigating", "normal", "suspicious", "resolved"]), ts: ts() })),
|
|
alerts: () => Array.from({ length: 6 }, () => ({ id: "UA-" + rid(), sev: rChoice(["critical", "high", "medium"]), msg: rChoice(["Impossible travel for admin", "Bulk data access by jdoe", "Off-hours login new device", "Privilege escalation chain", "Anomalous API usage", "Account compromise indicators"]), src: "UBA", ts: ts() })),
|
|
rules: () => Array.from({ length: 4 }, (_, i) => ({ id: "UR-" + i, name: rChoice(["Impossible Travel", "Bulk Download", "Off-Hours Access", "Privilege Chain"]), sensitivity: rChoice(["high", "medium", "low"]), triggers: Math.floor(Math.random() * 50) })),
|
|
stats: () => ({ evt: Math.floor(Math.random() * 10000) + 2000, req: Math.floor(Math.random() * 600) + 100, users: Math.floor(Math.random() * 200) + 50, anomalies: Math.floor(Math.random() * 30) + 5 })
|
|
},
|
|
"soc-threat-intel": {
|
|
label: "Threat Intel", icon: "\ud83c\udf10", role: "IOC Feeds & Intelligence",
|
|
items: () => Array.from({ length: 10 }, () => ({ id: "IOC-" + rid(), type: rChoice(["ip", "domain", "hash", "url", "email"]), value: rChoice([rIP(), rid() + ".malware.com", "sha256:" + rid() + rid(), "https://evil-" + rid() + ".com/payload"]), source: rChoice(["AlienVault", "VirusTotal", "AbuseIPDB", "Internal", "MISP"]), confidence: Math.floor(Math.random() * 40) + 60, ts: ts(604800000) })),
|
|
alerts: () => Array.from({ length: 4 }, () => ({ id: "TA-" + rid(), sev: rChoice(["critical", "high"]), msg: rChoice(["IOC match: known C2 IP in traffic", "New APT campaign indicators", "Threat feed: " + Math.floor(Math.random() * 100) + " new IOCs", "IOC correlation: internal to malware domain"]), src: "ThreatIntel", ts: ts() })),
|
|
rules: () => Array.from({ length: 3 }, (_, i) => ({ id: "TR-" + i, name: rChoice(["Auto-block high-confidence IOCs", "Enrich alerts with TI", "Feed sync every 1h"]), active: true })),
|
|
stats: () => ({ evt: Math.floor(Math.random() * 8000) + 1000, req: Math.floor(Math.random() * 500) + 100, iocs: Math.floor(Math.random() * 5000) + 1000, feeds: Math.floor(Math.random() * 10) + 3 })
|
|
},
|
|
"soc-vuln-scan": {
|
|
label: "Vuln Scanner", icon: "\ud83d\udd0d", role: "Vulnerability Assessment (Real Targets)",
|
|
items: () => realScanResults.length ? realScanResults : [{ status: "scanning", message: "Initial scan in progress..." }],
|
|
alerts: () => {
|
|
const a = [];
|
|
realScanResults.forEach(r => {
|
|
if (r.missing && r.missing.length > 3) a.push({ id: "VS-" + rid(), sev: "high", msg: r.target + ": Missing " + r.missing.length + " security headers", src: "VulnScan", ts: r.scannedAt || Date.now() });
|
|
if (r.status === "error") a.push({ id: "VS-" + rid(), sev: "critical", msg: r.target + ": Unreachable - " + (r.error || "timeout"), src: "VulnScan", ts: r.scannedAt || Date.now() });
|
|
});
|
|
return a;
|
|
},
|
|
rules: () => [
|
|
{ id: "VR-0", name: "Scan HTTP Security Headers", active: true },
|
|
{ id: "VR-1", name: "Check SSL/TLS Configuration", active: true },
|
|
{ id: "VR-2", name: "Monitor Response Latency", active: true }
|
|
],
|
|
stats: () => ({ evt: realScanResults.length, req: Math.floor(Math.random() * 100) + 10, targets: TARGETS.length, lastScan: lastScanTime ? new Date(lastScanTime).toISOString() : "pending" })
|
|
},
|
|
"soc-ops": {
|
|
label: "Incidents", icon: "\ud83d\udea8", role: "Incident Management & Response",
|
|
items: () => Array.from({ length: 8 }, () => ({ id: "INC-" + rid(), title: rChoice(["Ransomware on endpoint", "Phishing targeting finance", "Unauthorized DB access", "DDoS on web servers", "Data breach investigation", "Insider threat alert", "Supply chain compromise", "Zero-day exploitation"]), severity: rChoice(["P1", "P2", "P3", "P4"]), status: rChoice(["open", "investigating", "contained", "mitigated", "resolved", "closed"]), assignee: rChoice(["analyst-1", "analyst-2", "team-lead", "commander"]), ts: ts(604800000) })),
|
|
alerts: () => Array.from({ length: 3 }, () => ({ id: "OA-" + rid(), sev: rChoice(["critical", "high"]), msg: rChoice(["SLA breach: P1 unresolved 4h", "New P1 incident needs attention", "Escalation: incident moved to P1"]), src: "OPS", ts: ts() })),
|
|
rules: () => [],
|
|
stats: () => ({ evt: Math.floor(Math.random() * 2000) + 500, req: Math.floor(Math.random() * 300) + 50, openIncidents: Math.floor(Math.random() * 10) + 2, mttr: Math.floor(Math.random() * 120) + 30 + "min" })
|
|
}
|
|
};
|
|
|
|
// ── System metrics (real) ──
|
|
const startTime = Date.now();
|
|
function getSystemMetrics() {
|
|
const mem = process.memoryUsage();
|
|
return {
|
|
uptime: Math.floor((Date.now() - startTime) / 1000),
|
|
memory: { rss: Math.round(mem.rss / 1024 / 1024) + "MB", heap: Math.round(mem.heapUsed / 1024 / 1024) + "MB" },
|
|
services: Object.keys(services).length,
|
|
platform: "Ghaymah Systems Cloud (deploy.ghaymah.systems)",
|
|
nodeVersion: process.version
|
|
};
|
|
}
|
|
|
|
function J(res, c, o) {
|
|
res.writeHead(c, { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "*", "Access-Control-Allow-Headers": "*" });
|
|
res.end(JSON.stringify(o));
|
|
}
|
|
|
|
http.createServer((req, res) => {
|
|
const u = new URL(req.url, "http://localhost");
|
|
if (req.method === "OPTIONS") return J(res, 200, {});
|
|
if (u.pathname === "/health") return J(res, 200, { status: "healthy", service: "soc-backend", services: Object.keys(services).length, uptime: Math.floor((Date.now() - startTime) / 1000) });
|
|
if (u.pathname === "/services") return J(res, 200, Object.entries(services).map(([k, v]) => ({ id: k, label: v.label, icon: v.icon, role: v.role })));
|
|
if (u.pathname === "/metrics") return J(res, 200, getSystemMetrics());
|
|
if (u.pathname === "/scan") { scanTargets().then(r => J(res, 200, r)); return; }
|
|
|
|
const parts = u.pathname.split("/").filter(Boolean);
|
|
if (parts.length >= 1) {
|
|
const svcName = parts[0];
|
|
const endpoint = parts[1] || "health";
|
|
const svc = services[svcName];
|
|
if (svc) {
|
|
if (endpoint === "health") return J(res, 200, { status: "healthy", service: svcName, label: svc.label, icon: svc.icon, role: svc.role, uptime: Math.floor((Date.now() - startTime) / 1000) });
|
|
if (endpoint === "stats") return J(res, 200, svc.stats());
|
|
if (endpoint === "alerts") return J(res, 200, svc.alerts());
|
|
if (endpoint === "items") return J(res, 200, svc.items());
|
|
if (endpoint === "rules") return J(res, 200, svc.rules());
|
|
if (endpoint === "logs") return J(res, 200, svc.items().slice(0, 5));
|
|
return J(res, 404, { error: "unknown endpoint: " + endpoint });
|
|
}
|
|
}
|
|
return J(res, 404, { error: "not found", available: Object.keys(services) });
|
|
}).listen(PORT, () => console.log("SOC Backend (9 services + real scanning) on port " + PORT));
|