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 / README.md 3909 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
# Synapse Sonar

**Dynamic Internal Attack Surface & Dependency Mapper** — agentless visibility into internal network topology, with vulnerability overlay, blast-radius simulation, and Zero-Trust drift detection. Multi-tenant, RBAC-secured, self-hosted, fully containerized.

Built in the **Synapse IronNode** design language: deep-space dark command center, iron-grey structure, neon cyan/violet/blue synapse accents.

---

## Quick start

```bash
cp .env.example .env
# generate real secrets:
#   openssl rand -base64 32   -> NEXTAUTH_SECRET
#   openssl rand -hex 32      -> INTEGRATION_ENCRYPTION_KEY

docker compose up -d --build      # builds the app, starts Postgres, runs migrations
```

The initial migration ships in [`prisma/migrations/`](prisma/migrations); the entrypoint runs `prisma migrate deploy` automatically on boot.

### First run — guided setup
On a fresh database there are no users, so any visit redirects to **`/setup`** — a two-step wizard that creates your first **organization (tenant)** and **Tenant Admin** account. The bootstrap endpoint (`/api/setup`) is one-shot: it refuses once any user exists. After that, sign in at **`/login`** (organization slug + email + password, plus a TOTP code when MFA is on).

### Optional: demo data instead of the wizard
```bash
docker compose exec app npm run db:seed
```
Loads two tenants (isolation demo) and a sample topology. Login: `[email protected]` / `ChangeMe123!` (tenant slug `acme`).

App: <http://localhost:3000>

---

## Architecture

| Module | Where |
| --- | --- |
| **1 · Infra & schema** | [`Dockerfile`](Dockerfile), [`docker-compose.yml`](docker-compose.yml), [`prisma/schema.prisma`](prisma/schema.prisma) |
| **2 · Identity / RBAC / tenancy** | [`lib/auth.ts`](lib/auth.ts), [`lib/rbac.ts`](lib/rbac.ts), [`lib/authOptions.ts`](lib/authOptions.ts), [`app/api/users/route.ts`](app/api/users/route.ts), [`components/UserAdministration.tsx`](components/UserAdministration.tsx), MFA: [`app/api/mfa/enroll/route.ts`](app/api/mfa/enroll/route.ts) |
| **3 · Integration center** | [`components/IntegrationsPage.tsx`](components/IntegrationsPage.tsx), [`app/api/integrations/route.ts`](app/api/integrations/route.ts) |
| **4 · Ingestion API** | [`app/api/ingest/flow-logs/route.ts`](app/api/ingest/flow-logs/route.ts) |
| **5 · Topology map** | [`components/NetworkGraph.tsx`](components/NetworkGraph.tsx), data: [`app/api/graph/route.ts`](app/api/graph/route.ts) |
| **6 · Blast radius** | [`lib/blastRadius.ts`](lib/blastRadius.ts) (recursive CTE), [`app/api/assets/[id]/blast-radius/route.ts`](app/api/assets/[id]/blast-radius/route.ts) |
| **7 · Governance / drift** | [`lib/governance.ts`](lib/governance.ts) (Zero-Trust eval + IronNode SOAR hand-off) |

### Multi-tenant isolation
Every model carries `tenantId`. Every API route opens with `requireTenantContext()` and scopes all queries by `ctx.tenantId`; cross-tenant writes use `updateMany`/`upsert` guarded by `tenantId`. There is no cross-tenant read path.

### Security posture
- Integration secrets are **AES-256-GCM encrypted at rest** ([`lib/crypto.ts`](lib/crypto.ts)); the UI only ever receives masked values.
- TOTP MFA secrets are encrypted the same way; MFA is enforced in `authOptions` when enabled.
- RBAC: `TENANT_ADMIN``SECURITY_ANALYST``READ_ONLY_VIEWER`, enforced via `assertCan()`.
- IronNode webhook payloads are HMAC-SHA256 signed (`X-Sonar-Signature`).

---

## Try the ingestion API

```bash
curl -X POST http://localhost:3000/api/ingest/flow-logs \
  -H "Authorization: Bearer sensor_acme_demo_key" \
  -H "Content-Type: application/json" \
  -d '{"flows":[{"sourceIp":"10.0.1.10","destIp":"10.0.3.30","port":5432,"protocol":"tcp","bytes":4096,"knownInventory":["10.0.1.10"]}]}'
```

`10.0.3.30` isn't in `knownInventory` → flagged **Rogue / Shadow IT**, and (with IronNode enabled) a signed violation fires to the webhook.