Catalyst / admin/Synapse-Sonar 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / Synapse-Sonar

public

Attack Surface Simulation

Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
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 });
  }
}