Org-context-aware threat prioritization with SSVC v2.1 decision-tree triage and ATT&CK tagging.
Single Go binary. One runtime dependency: modernc.org/sqlite (pure Go SQLite driver). Deploy in 60 seconds with Go 1.25+.
Threat Intel Arbiter transforms raw threat intelligence from MISP and CISA KEV into organisation-specific, scored, and explained actions. It answers one question:
Should this organisation care about this threat right now?
Every alert includes: Severity + Confidence + Action + Explanation with full risk score breakdown.
👥 Who it's for: SOC teams that already run MISP but don't have a dedicated TIP or vulnerability prioritization layer. If you're drowning in MISP feeds and need CVE-to-techstack matching without standing up OpenCTI or Nucleus, this is for you. If you don't run MISP, this tool isn't useful yet — KEV-only mode is on the roadmap.
- ✅ Threat prioritization engine — scores threats against your tech stack, sector, and exposure
- ✅ Multi-source — MISP + CISA KEV today, NVD + GitHub Advisory on roadmap
- ✅ SSVC v2.1 triage — CMU/CISA decision tree (Act / Attend / Track* / Track) with traceable branch paths
- ✅ ATT&CK tagging — automatically maps threats to MITRE ATT&CK techniques
- ✅ EDR integration — pushes IOCs to CrowdStrike Falcon in real-time
- ✅ Multi-user dashboard — admin/reader roles, inline editing, CSV import from CMDB
- ✅ Pull-all, filter-local — fetches everything from MISP, matches against your context internally
- ❌ A threat intelligence platform — MISP does that
- ❌ A vulnerability scanner — Nessus, Qualys, etc. do that
- ❌ A SIEM or SOAR
- ❌ A CMDB — it imports from one
- ❌ An IOC-based threat intel tool — prioritization is CVE-driven. IOCs (IPs, domains, hashes) are extracted for EDR push but are NOT used in matching or scoring. This is a CVE prioritization engine with MISP as transport.
Those platforms are heavyweight — they require infrastructure, dedicated admins, and weeks of onboarding. Threat Intel Arbiter is a single 16MB binary that deploys in 60 seconds on any machine. If you already run MISP and need prioritised alerts without standing up another platform, this is the lightweight alternative. It doesn't replace your SIEM or ticketing — it feeds them.
Click the diagram → interactive architecture page with detail drawers for every component.
Six-band pipeline: Ingest → Normalize → Buffer → Correlate & Score → Persist → Respond & Present — all in a single Go binary.
| Stage | What it does |
|---|---|
| Ingestion | Pulls full MISP API firehose (no pre-filters) + CISA KEV catalog |
| Normalization | Deduplication, attribute parsing, IOC extraction, metadata enrichment |
| Match & Score | Runs matchers locally — tech stack, sector alignment, KEV factor, severity model, confidence model |
| Explainer | Generates human-readable justification from the same scoring struct |
| Routing | Routes by severity, confidence, and TLP to Slack, Teams, Email, CrowdStrike, SIEM |
Matchers run locally against your context: tech stack (exact/fuzzy CVE match), sector alignment (NIS sector threats), KEV boost (actively exploited), CVSS/EPSS severity model, and confidence scoring based on source reliability + match quality + recency.
# Prerequisites: Go 1.25+, a running MISP instance
go build -o arbiter ./cmd/arbiter/
# Set up your tech stack
cp config/techstack.csv.example config/techstack.csv
# Run
export MISP_API_KEY="your-misp-api-key"
export ARBITER_ADMIN_KEY="your-admin-key"
./arbiter --config ./config/Open http://localhost:8080 — login with username admin and the password printed to stdout on first start.
Single-binary pipeline — pull-all, filter-local, match to org context, score, explain, route.
- Pull — fetches ALL events from MISP (no galaxy/tag/CVE pre-filter). MISP acts as an aggregation channel for peers, ISACs, OSINT feeds, commercial, and government sources. Filtering happens locally against your context.
- Normalize — extracts CVEs, CVSS, tags, threat actors, references, AND IOCs (IPs, domains, hashes) for EDR integration. Canonical ThreatEvent model is source-agnostic.
- Filter — drops TLP:RED, disputed CVEs, known false positives via MISP warning lists and noticelists. (Analyst FP marking / feedback loop on roadmap.)
- Match — pluggable matchers: CVEMatcher (version-aware), SectorMatcher (taxonomy tags), KEVMatcher (active exploitation).
- Score — 4 dimensions: Likelihood × Impact × Exposure ÷ max, with Confidence as a separate dimension.
- Explain — human-readable breakdown from the same struct that computed the score. No separate code path.
- Route — by severity + confidence. Critical+high → #sec-alerts. Medium → weekly digest. Low → log only. IOCs → CrowdStrike Falcon EDR.
The arbiter does not query MISP by galaxy, tag, or CVE. It calls:
GET /events/restSearch?returnFormat=json&limit=100
Pre-filtering at the MISP API level misses threats: an untracked actor exploiting a CVE in your stack, an event without galaxy tags but with sector-relevant taxonomies, or a feed you don't subscribe to that still contains relevant CVEs. MISP is a threat intel aggregation channel, not a filtering gate.
Leading with MISP means v1 ships with the full product experience — all four risk dimensions have data to work with. SectorMatcher uses MISP taxonomies. Confidence scoring uses sightings and community trust. The explainability engine has something to explain beyond "CVSS is high," which every tool already does.
NVD-only would produce a thinner product indistinguishable from a CVSS filter. MISP data gives us the differentiation. v2 adds NVD and GitHub Advisory to reach orgs without MISP.
Built-in single-page application — no framework, no build step, pure HTML/CSS/JS served from the binary.
| Screen | Description |
|---|---|
| Alerts | Searchable/filterable table. Click any row for full risk breakdown with explanation, CVSS, matched apps, and action labels. |
| Tech Stack | Inline-editable. Version (click to edit), criticality (dropdown), internet-facing (dropdown). Add/delete apps with custom confirmation dialog. |
| Import CSV | Drag-and-drop bulk upload from CMDB (ServiceNow, Ivanti, Snipe-IT, etc.). Delta detection shows what was added/removed. |
| Users | Admin-only. Create/edit/delete user accounts with admin/reader roles. |
| Settings | Configure admin API key, Slack/Teams/Email webhook URLs, CrowdStrike credentials. |
⚠️ Inventory staleness is the single biggest risk to prioritization accuracy. A CVE-to-app match is only as good as your techstack.csv. If your inventory says you run Apache 2.4.41 but you patched to 2.4.62, every 2.4.x CVE alert becomes a false positive — confidently wrong priorities are worse than no tool. Apps not verified in 30+ days show a staleness warning in the dashboard. Automated inventory sync (scanner export, cloud API, agent) is on the roadmap.
| Role | Permissions |
|---|---|
| admin | Full access: alerts, tech stack CRUD, CSV import, user management, settings |
| reader | View-only: alerts list, alert details, tech stack view. No write access. |
- Session cookie auth (HttpOnly, SameSite=Strict), 12-hour expiry
- Password hashing: Argon2id with legacy SHA-256 upgrade on login
- Session tokens: SHA-256 hashed at rest in SQLite
- Default admin account seeded on first start with a random one-time password (printed to stdout). You should change it immediately via the Users panel — no standing default credentials exist.
- Programmatic access via
X-Arbiter-Keyheader (API key always has admin privileges)
go build -o arbiter ./cmd/arbiter/
# → ~16MB static binary
# → Copy to any Linux/macOS/Windows machine
# → Set 4 env vars. Run. Done.- Zero infrastructure: no Docker, Postgres, Redis, Python, Node
- SQLite is a single file — backup =
cp data/arbiter.db data/arbiter.db.bak - Cross-compile:
GOOS=linux GOARCH=amd64 go build
| Document | Covers |
|---|---|
| System Design | Full architecture, scoring formula, database schema, positioning strategy |
| EDR — CrowdStrike Falcon | IOC extraction, OAuth2, batching, dedup, mock mode |
| Architecture Diagram | Interactive 6-band pipeline — click any block for details |
| API Reference | Complete endpoint reference (below) |
| Security Policy | Threat model, mitigations, vulnerability reporting |
- MISP ★ primary — REST API, HMAC-SHA256 auth. Pulls every 15 minutes. Tracks NEW/MODIFIED/DELETED events. All galaxy, taxonomy, and sighting data extracted.
- CISA KEV ★ secondary — public JSON, no auth. Pulls daily. Every entry is a confirmed actively-exploited vulnerability.
v2 roadmap: NVD API, GitHub Advisory, vendor feeds, RSS connectors.
| Decision | Why |
|---|---|
| Pull-all, filter-local | Pre-filtering at MISP would miss threats. Match engine has full org context. |
| Canonical ThreatEvent from day 1 | Adding a source = 1 normalizer. Without this = rewrite engine. |
| Multi-user auth with admin/reader roles | SOC teams need separate logins. Self-contained in SQLite, no external IdP. |
| Argon2id over bcrypt | golang.org/x/crypto is the closest thing to stdlib for a slow KDF. Self-describing hash format with legacy SHA-256 transparent upgrade. |
| Single binary, one per org | Deploy in minutes. Multi-tenancy is v2. |
| EDR integration via IOCs, not just alerts | Close the loop from detection to prevention. Feed IOCs to CrowdStrike Falcon in real-time. |
| Component | Technology | Why |
|---|---|---|
| Language | Go 1.25+ | Single binary, stdlib covers ~95% |
| HTTP | net/http | Standard library |
| Database | SQLite (modernc.org/sqlite) | Pure Go, zero-config, file-based |
| Auth | golang.org/x/crypto/argon2 + crypto/sha256 | Argon2id password hashing + session tokens |
| Dependencies | 1 | modernc.org/sqlite (and its transitive deps) |
threat-intel-arbiter/
├── cmd/arbiter/main.go # Entry point
├── internal/
│ ├── source/ # MISP + KEV connectors + normalizers
│ ├── model/ # Canonical ThreatEvent, Match, Alert, OrgContext
│ ├── filter/ # Warning list filter
│ ├── match/ # CVEMatcher, SectorMatcher, KEVMatcher + version subsystem
│ ├── risk/ # 4-dim scoring + explainability + dedup
│ ├── notify/ # Slack, Teams, Email, Webhook, CrowdStrike routers
│ ├── api/ # HTTP server, auth, dashboard
│ ├── store/ # SQLite layer
│ └── config/ # JSON config loading + CSV parsing
├── config/ # Example config files
├── docs/ # Design document, architecture diagrams, EDR docs
└── data/ # SQLite database (created at runtime)
All endpoints require authentication (session cookie or X-Arbiter-Key). /health is the only public endpoint.
| Endpoint | Method | Description |
|---|---|---|
/login |
GET | Login page (HTML) |
/auth/login |
POST | {username, password} → session cookie + {role, username} |
/auth/logout |
POST | Clear session |
/auth/session |
GET | Current {username, role} |
| Endpoint | Method | Description |
|---|---|---|
/api/alerts |
GET | List alerts. Query: ?severity=, ?status=, ?q=, ?app= |
/api/alerts/:id |
GET | Single alert with explanation, action, routed_to |
/api/techstack |
GET | Full tech stack with all fields |
/api/stats |
GET | Alert counts by severity + apps tracked |
/health |
GET | Public. MISP status, KEV entries, alert counts |
| Endpoint | Method | Description |
|---|---|---|
/admin/ack/:id |
POST | Update alert status: acked, false_pos, resolved |
/admin/import |
POST | Upload techstack.csv, delta detection |
/admin/pull |
POST | Trigger immediate pull from all sources |
/admin/techstack |
POST | Add single app |
/admin/techstack |
PUT | Update single app |
/admin/techstack |
DELETE | Remove single app {name} |
/admin/users |
GET | List all users |
/admin/users |
POST | Create user {username, password, role} |
/admin/users |
PUT | Update user role/password |
/admin/users |
DELETE | Remove user (cannot delete last admin) |
MIT — see LICENSE for full text.
Built by @jayelbotvibe
⭐ Star this repo if it's useful. Open an issue for bugs or feature requests.
Roadmap: NVD API + GitHub Advisory sources, Microsoft Defender / SentinelOne EDR connectors, multi-tenancy.


