Catalyst / admin/SAynapse-Horizon 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / SAynapse-Horizon

public

Self Hosted Cyber Threat Intelligence Hub

Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
SAynapse-Horizon / Synapse-Horizon / README.md 6424 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
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Synapse-Horizon

A modern, dark-mode-first **Cyber Threat Intelligence (CTI) dashboard** for Tier-3 SOC
analysts, threat hunters, and security architects. Synapse-Horizon ingests **live** telemetry
from multiple CTI sources with divergent schemas, **normalizes** each indicator into a single
scannable headline, and surfaces the most critical events on an executive triage radar.

Built with **React 18 + Vite + Tailwind CSS + Lucide** on the frontend and a **Node/Express
ingestion backend**, packaged for a **Docker Compose** environment.

## Live data architecture

Browsers cannot call these CTI APIs directly (CORS blocks them, and several require secret
API keys). So the stack runs two services:

```
┌────────────────────┐     /api      ┌────────────────────────┐     HTTPS      ┌─────────────┐
│  horizon (nginx)   │ ───proxy───▶  │  api (Node ingestion)  │ ───fetch───▶   │ Live CTI    │
│  React SPA :8080   │               │  normalize + cache     │                │ APIs        │
└────────────────────┘  ◀── JSON ──  └────────────────────────┘                └─────────────┘
```

The backend holds the API keys (never the browser), fetches each source on demand, normalizes
every record into one uniform IOC schema, and exposes `GET /api/feeds` + `POST /api/sync`.

---

## Feature overview

| Area | What it does |
|------|--------------|
| **Global key metrics** | Active feeds (`7 / 12`-style), latest feed-pull timestamp (`DD-MMM-YYYY HH:mm:ss UTC`), and total aggregated IOCs held in state. |
| **Feed Control Panel** (left) | Per-source toggles for URLhaus, ThreatFox, Feodo Tracker, PhishTank, CISA KEV, Blocklist.de, AlienVault OTX, and an RSS parser — toggling instantly filters metrics, radar and feed. |
| **Lifecycle controls** | Independent **Auto-Refresh Interval** (display polling: Manual/30s/1m/5m/15m) and **Feed Pull Cadence** (ingestion: On-Demand/1h/6h/24h), plus a prominent **Manual Force Sync**. |
| **Critical Priority Triage** | Max 5 highest-severity events, auto-resorting when a lifecycle completes. |
| **Normalized CTI Feed** | Full-width uniform rows: UTC time · source tag · normalized headline · **View Details**. |
| **Drill-down drawer** | STIX/TAXII attributes, MITRE ATT&CK mappings, raw JSON payload, and a confidence meter. |
| **Loading states** | Skeletons + spinners during force sync / cadence changes. |

## SOC alerting palette

Restrained by design: **Emerald** = stable/active, **Amber** = suspicious/high,
**Rose/Red** = critical / active exploitation, **Sky/Slate** = informational structure.

---

## Live sources & API keys

| Source | Key required | Notes |
|--------|:---:|-------|
| **CISA KEV** | — | Public JSON. Live out of the box. |
| **Blocklist.de** | — | Public IP lists. Live out of the box. |
| **RSS Parser** | — | Public advisory feeds. Live out of the box. |
| **URLhaus** | `ABUSE_CH_AUTH_KEY` | Free [abuse.ch](https://auth.abuse.ch/) account. |
| **ThreatFox** | `ABUSE_CH_AUTH_KEY` | Same abuse.ch key. |
| **Feodo Tracker** | `ABUSE_CH_AUTH_KEY` | Same abuse.ch key. |
| **AlienVault OTX** | `OTX_API_KEY` | From your OTX account. |
| **PhishTank** | `PHISHTANK_APP_KEY` | Registered application key. |

Copy `.env.example``.env` and fill in the keys you have. Sources without a key report as
**"needs API key"** in the sidebar instead of failing. Set `HORIZON_DEMO=1` to serve synthetic
data for any un-keyed source so the dashboard is populated out of the box.

## Running it

### Production (nginx SPA + ingestion backend)

```bash
cp .env.example .env      # add any API keys you have (optional)
docker compose up -d --build
# open http://localhost:8080   (API on http://localhost:4000/api/feeds)
```

### Hot-reloading dev stack

```bash
docker compose --profile dev up
# frontend http://localhost:5190  ·  backend http://localhost:4000
```

### Local (no Docker)

```bash
# terminal 1 — ingestion backend
cd backend && npm install && npm start        # http://localhost:4000

# terminal 2 — frontend (Vite proxies /api → :4000)
npm install && npm run dev                     # http://localhost:5190
npm run build                                  # emits ./dist
```

---

## Architecture

```
backend/                         # Node/Express ingestion service
├── src/server.js                # routes, in-memory cache, background warm-up sync
├── src/normalize.js             # IOC contract + fetch/timeout helpers + severity model
└── src/sources/
    ├── index.js                 # registry + concurrent, failure-isolated orchestration
    ├── cisakev.js  blocklistde.js  rss.js        # no-key live sources
    ├── urlhaus.js  threatfox.js  feodo.js         # abuse.ch (Auth-Key)
    ├── otx.js  phishtank.js                       # OTX / PhishTank (keys)
    └── demo.js                  # synthetic fallback (HORIZON_DEMO=1)

src/                             # React frontend
├── App.jsx                      # layout shell + error banner
├── api/client.js                # GET /api/feeds · POST /api/sync
├── hooks/useFeeds.js            # single source of truth: toggles, store, cadences, lifecycle
├── data/sources.js              # source registry + refresh/cadence option sets
├── utils/{format,theme}.js      # UTC formatting · severity/accent/confidence styling
└── components/
    ├── Header.jsx  Sidebar.jsx  MetricsRow.jsx
    ├── CriticalTriagePanel.jsx  CTIFeed.jsx  DetailsDrawer.jsx
    └── ToggleSwitch.jsx
```

### Data normalization

Each source fetcher pulls its native schema live and compresses every record into one uniform
IOC object (`backend/src/normalize.js → makeIoc`):

```js
{ id, source, detectedAt, ingestedAt, severity, category,
  indicator, indicatorType, target, confidence, mitre[], headline, raw, stix }
```

The UI only ever consumes this normalized shape, so URLhaus URLs, ThreatFox C2 IPs, and CISA
KEV CVEs all render through the same components. Fetch failures are isolated per source — one
API being down never sinks the rest of the pull.