# Review — API-Eligible Cyber Threat Reporting & Escalation Platforms (Draft v1)

**Reviewer:** Claude (Opus 4.7, 1M context)
**Review date:** 2026-05-13
**Document reviewed:** `waypoints.md` (first draft)
**Verdict:** Strong bones. Tone-perfect for white-hat defensive work — machine-to-machine, no vigilante framing. Publishable as an internal whitepaper after the critical fixes below.

---

## 1. What's Already Solid

Don't change these — they're load-bearing and correct.

- **Section 1.1 vs 1.2 split** (normal vs imminent harm) — exactly the right hinge for routing decisions.
- **Section 8 (never-submit list)** — covers GDPR / exploitation amplification / credential leakage failure modes well.
- **Section 9 normalized object** — the right abstraction. Transform-to-target instead of N bespoke pipelines.
- **Section 10 architecture sentence** — the whole project on one line: *Sensors → OpenCTI → TheHive/IRIS → routing engine → MISP + abuse APIs + CERT/AIS → sanitized public.*

---

## 2. Critical Fixes (do these before this leaves draft)

### 2.1 Geography mismatch — CISA AIS at #1 is US-only

For European-focused work, **MISP via CIRCL.lu** (Luxembourg) or the **ENISA CSIRTs Network** is the workhorse. CISA AIS does not cover EU institutions.

**Action:** Swap priorities #1 ↔ #2 (MISP first, AIS second). Add a row for **CERT-EU** specifically for European institutions.

### 2.2 National CERTs are referenced generically but never named

The doc says "National CERT/CSIRT" everywhere but never resolves it to an actionable receiver.

**Action:** Add a small table after Section 1:

| Country | Receiver                       | Channel                                |
|---------|--------------------------------|----------------------------------------|
| DE      | BSI / CERT-Bund                | reports@cert-bund.de, MISP community   |
| FR      | ANSSI / CERT-FR                | TAXII feed                             |
| UK      | NCSC-UK                        | structured email + early-warning service |
| NL      | NCSC-NL                        | MISP                                   |
| ES      | CCN-CERT, INCIBE-CERT          | MISP                                   |
| EU      | CERT-EU, Europol EC3           | TLP-tagged MISP                        |

The routing engine should pick the right one based on victim country.

> **Note on Europol EC3:** they handle *criminal cases*, not first-call technical sharing. Route through your national CERT first; EC3 receives via national channels for cross-border coordination.

### 2.3 Domain registrar abuse is missing from Section 1.3

Cloudflare is covered, but registrars (Namecheap, Tucows, GoDaddy, EURid for `.eu`, DENIC for `.de`) are often the faster takedown path.

**Action:** Add to the malicious-infrastructure flow:
*registrar abuse contact from WHOIS → registrar abuse API/email → registry as escalation.*

### 2.4 Severity scale `A|B|C|D|E` is unusual and undefined

Either define it inline or replace with the standard `low|medium|high|critical` (CVSS-style) or NIS2 severity categories for EU consistency. Receivers will normalize anyway — but defining it lets the routing engine make automatic decisions.

### 2.5 Normalized object missing an `actor` block

You have `victim` but no `actor`. Add:

```json
"actor": {
  "name": "Adira",
  "aliases": [],
  "campaign": "",
  "confidence": "A1|A2|B1|B2|C2|C3|D|E|F"
}
```

This field connects the doc to the project mission and lets the routing matrix differentiate actor-specific sightings from generic abuse reports.

(`A1`–`F` is the Admiralty Code, the de-facto CTI standard. If that's too much, fall back to `low|medium|high`.)

### 2.6 PII at submission time is a GDPR landmine

Section 9 has `observables.emails: []`. Submitting victim email addresses to AbuseIPDB or VirusTotal is a personal-data transfer under GDPR.

**Action:** Add a pre-submission sanitizer step that:

- Hashes / redacts emails to `local-part-hash@domain` when destination is public
- Strips PII from URLs (tokens, query params containing identifiers)
- Keeps raw originals only in `evidence.raw_evidence_location` (internal-only storage)

This belongs in the doc *before* the normalized-object section, not as an afterthought.

---

## 3. High-Value Additions

### 3.1 TLP enforcement at the routing layer

Nothing in the current schema *prevents* TLP:RED data being routed to a TLP:CLEAR destination.

**Action:** Add a routing precondition: `submission.tlp <= destination.max_tlp_allowed`.

- CISA AIS rejects TLP:RED
- Cloudflare doesn't care
- Spamhaus has its own rules
- MISP communities each have their own ceiling

Encode the ceiling per destination in the routing matrix.

### 3.2 STIX 2.1 as the serialization

Right now the doc implies *internal object → bespoke transform per API*. Cheaper and more standard:

**internal object → STIX 2.1 bundle → minor adapter per destination**

MISP, OpenCTI, CISA AIS, and most CTI tools are STIX-native. One serializer beats thirteen, and you get free interop with anything that already speaks STIX.

### 3.3 Rate-limit budgets

Many of these APIs have strict limits:

- AbuseIPDB free tier: 1000 reports/day
- VirusTotal public API: 4 req/min
- Spamhaus: per-submitter quotas
- Cloudflare: per-account rate limits

Without a token-bucket per destination, high-confidence submissions get silently dropped during bursts.

**Action:** Add a `destination_quota` field to the routing matrix and an enforcement layer.

### 3.4 Feedback loop is missing

When you submit to URLhaus, you can poll for status. When you submit to MISP, you get sightings. When you submit to Cloudflare, you get a case number. These should flow back into your OpenCTI graph as evidence-of-effectiveness.

Without this, you're operating open-loop — you don't know which destinations actually act on your reports.

**Action:** Add a Section 11 "Receipt and Effectiveness Tracking" that defines:

- Per-destination receipt schema (case ID, ack timestamp, outcome status)
- Polling cadence per destination
- A success metric per destination type (takedowns confirmed, sightings count, classification adopted)

### 3.5 NoMoreRansom (NMR)

Ransomware.live is listed under monitoring, but if a decryptor research effort produces anything, NMR is the destination.

**Action:** Add to the routing matrix:

| Evidence type                  | First API destination          | Second destination   | Internal system        |
|-------------------------------|--------------------------------|----------------------|------------------------|
| Ransomware decryptor evidence | NoMoreRansom (private channel) | Victim CERT chain    | OpenCTI internal only  |

NMR coordinates so victims can decrypt before the adversary sees the fix — *never* publish a working decryptor publicly first.

---

## 4. Nice-to-Have

### 4.1 Submitter identity & signing

- Register a stable submitter handle with MISP / MalwareBazaar / AbuseIPDB — not a personal account.
- Sign internal objects with a project PGP key before they leave the system.
- CIRCL and other major MISP communities weight trust by submitter history.

### 4.2 Audit log requirement

Every external submission writes an immutable row:

```
(timestamp, destination, payload_hash, submitter_identity, tlp, response_id, outcome)
```

Legal cover, debugging, and the feedback loop in 3.4 all need this.

### 4.3 NIS2 callout for critical-infra reporting

EU NIS2 mandates incident reporting from regulated entities within 24h of awareness. If detections involve essential/important entity sectors, the routing engine should flag NIS2 obligation regardless of receiver choice.

### 4.4 Section ordering

Sections 8 (data handling) and 9 (normalized object) are foundations, not appendices. Move them up to Sections 3–4. Currently a reader hits the platform list before knowing what *not* to send.

### 4.5 Confidence convention

`low|medium|high` is fine, but production CTI commonly uses the **Admiralty Code** (`A1`, `B2`, etc., describing source reliability × information credibility) or estimative language. Mention the convention even if you don't fully adopt it.

---

## 5. Implementation Notes (Blue48 Hookup)

This doc is the spec for two components in the agent stack:

1. **`report_writer` agent** outputs Section 9's normalized object as its canonical format.
2. **A routing engine** (extension of `report_writer`, or a 7th agent) consumes that object, applies the matrix in Section 6, and fans out via API adapters.

Agents stop at *"produce the normalized object."* Human review reads it, decides "yes, ship this to MISP and Cloudflare," and clicks. The routing engine then runs the API calls, captures receipts, and feeds them back to OpenCTI.

### 5.1 Suggested initial adapters (Block G priority)

1. MISP (PyMISP)
2. AbuseIPDB
3. URLhaus
4. Cloudflare Abuse Reports
5. urlscan.io

These five cover ~80% of common evidence types in the routing matrix.

### 5.2 Secrets handling

Every adapter needs API credentials. They must:

- Live in `.env` (already excluded from image via `.dockerignore`)
- Be passed at container runtime via `env_file`, never baked into the image
- Be rotatable on a schedule (the audit log in 4.2 helps prove non-overlap)

---

## 6. Summary

| Category   | Count | Notes                                       |
|------------|------:|---------------------------------------------|
| Critical   |     6 | Geography, CERT mapping, registrar abuse, severity scale, actor block, PII sanitizer |
| High-value |     5 | TLP enforcement, STIX 2.1, rate limits, feedback loop, NoMoreRansom |
| Nice-to-have |   5 | Signing, audit log, NIS2, ordering, Admiralty Code |

After the critical fixes, this is a publishable internal whitepaper and a clear spec for the routing engine. Good draft.
