admin / Synapse-Sonar
publicAttack Surface Simulation
Synapse-Sonar / synapse-sonar / app / api / graph / route.ts
2515 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | import { NextResponse } from "next/server"; import { requireTenantContext, HttpError } from "@/lib/auth"; import { assertCan, can } from "@/lib/rbac"; import { prisma } from "@/lib/prisma"; import { getEnabledIntegration, NetscanXiConfig } from "@/lib/integrations"; import { IntegrationProvider } from "@prisma/client"; export const runtime = "nodejs"; /** * GET /api/graph * Tenant-scoped topology payload for NetworkGraph.tsx. * Vulnerability indicators are only included when the NetscanXi toggle is On. */ export async function GET() { try { const ctx = await requireTenantContext(); assertCan(ctx.role, "asset:read"); const netscanEnabled = Boolean( await getEnabledIntegration<NetscanXiConfig>( ctx.tenantId, IntegrationProvider.NETSCAN_XI ) ); const [assets, connections] = await Promise.all([ prisma.asset.findMany({ where: { tenantId: ctx.tenantId }, include: { vulnerabilities: netscanEnabled ? { select: { cveId: true, cvssScore: true, severity: true, title: true } } : false, }, }), prisma.connection.findMany({ where: { tenantId: ctx.tenantId } }), ]); const nodes = assets.map((a) => { const vulns = netscanEnabled ? a.vulnerabilities ?? [] : []; const criticalCount = vulns.filter((v) => v.severity === "CRITICAL").length; return { id: a.id, hostname: a.hostname ?? a.ipAddress, ipAddress: a.ipAddress, relatedIps: a.relatedIps, assetType: a.assetType, criticality: a.criticality, isRogue: a.isRogue, offline: a.manualOffline, openPorts: a.openPorts, vulnerabilities: vulns, criticalCount, }; }); const links = connections.map((c) => ({ id: c.id, source: c.sourceAssetId, target: c.targetAssetId, protocol: c.protocol, port: c.port, // Normalize cumulative bytes into a 1-10 weight for edge thickness/speed. volume: Number(c.bytes), lastSeenAt: c.lastSeenAt, })); return NextResponse.json({ nodes, links, netscanEnabled, canEdit: can(ctx.role, "asset:write"), }); } catch (err) { if (err instanceof HttpError) { return NextResponse.json({ error: err.message }, { status: err.status }); } const status = (err as { status?: number }).status ?? 500; return NextResponse.json({ error: (err as Error).message }, { status }); } } |