admin / Synapse-Sonar
publicAttack Surface Simulation
Synapse-Sonar / synapse-sonar / app / api / integrations / ingest-key / route.ts
2653 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 | import { NextResponse } from "next/server"; import crypto from "crypto"; import { requireTenantContext, HttpError } from "@/lib/auth"; import { assertCan } from "@/lib/rbac"; import { prisma } from "@/lib/prisma"; import { encryptJson } from "@/lib/crypto"; import { IntegrationProvider } from "@prisma/client"; export const runtime = "nodejs"; const sha256 = (v: string) => crypto.createHash("sha256").update(v).digest("hex"); /** * POST /api/integrations/ingest-key body: { kind?: "flow" | "vuln" } * * (Re)generate a NetscanXi key for this tenant. There are TWO independent keys, * so regenerating one never invalidates the other: * - kind "flow" (default) -> ingestKeyHash : used by the collectors/agents * - kind "vuln" -> vulnKeyHash : used by the NetscanXi vuln feed * Only the SHA-256 hash is persisted; the plaintext is returned exactly once. */ export async function POST(req: Request) { try { const ctx = await requireTenantContext(); assertCan(ctx.role, "integration:write"); let kind: "flow" | "vuln" = "flow"; try { const body = await req.json(); if (body?.kind === "vuln") kind = "vuln"; } catch { /* no body -> default "flow" (the Agents tab posts nothing) */ } const existing = await prisma.integrationSetting.findUnique({ where: { tenantId_provider: { tenantId: ctx.tenantId, provider: IntegrationProvider.NETSCAN_XI, }, }, }); if (!existing) { return NextResponse.json( { error: "Enable the NetscanXi integration before issuing a key." }, { status: 400 } ); } const field = kind === "vuln" ? "vulnKeyHash" : "ingestKeyHash"; const prefix = kind === "vuln" ? "nsx_api" : "nsx_sensor"; const newKey = `${prefix}_${crypto.randomBytes(24).toString("hex")}`; const cfg = (existing.config as Record<string, unknown> | null) ?? {}; // Keep the existing encrypted blob AND the other key hash; only swap this one. const base = cfg.ciphertext ? cfg : (encryptJson({}) as unknown as Record<string, unknown>); const configToStore = { ...base, [field]: sha256(newKey) }; await prisma.integrationSetting.update({ where: { id: existing.id }, data: { config: configToStore as never }, }); return NextResponse.json({ ingestKey: newKey }); } 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 }); } } |