Skip to content

MISP Cluster Publishing

zach115th edited this page Jul 31, 2026 · 1 revision

MISP Cluster Publishing

IrisMISPCluster publishes a cross-case IOC correlation cluster to MISP as a single campaign event, carrying the cluster's shared indicators, their analyst descriptions, the notes linked to them, and the AI campaign narrative.

One-directional by design (iris-ng → MISP). Nothing is read back, so a MISP-side edit is never overwritten by iris-ng and vice versa.

Distinct from IrisMISPSync, which mirrors individual cases and IOCs continuously. This module publishes a correlation result on demand.

Trigger

A Push to MISP button on each cluster card in the Correlation tab. The module registers no hooks.

That is deliberate. Applying a campaign tag is local bookkeeping an analyst may do while still triaging; wiring publication to it would make an ordinary tagging action push to a shared MISP instance as a side effect, which is surprising and hard to undo.

What gets published, and where

Each kind of content goes to the MISP structure that actually models it:

iris-ng MISP
AI campaign narrative Event Report (first-class Markdown document)
Cluster's shared IOCs Attributes
Ioc.ioc_description that attribute's comment
Notes linked via ioc_note_link Analyst Notes on that attribute
IOC tags attribute tags
Campaign name misp-galaxy:campaign="<name>" on the event
TLP + campaign tag event tags

Indicators

The full typed pair list for the cluster is published — not cluster['shared_iocs'], which is a display list capped at 20 values and carries no type ID. The STIX exporter has the same requirement and resolves it the same way.

MISP attribute types come from IocType.type_taxonomy (Goal #2). An IOC type with no MISP mapping is skipped rather than published with a guessed type.

Fixed-length hex hashes (md5, sha1, sha256, sha512) are shape-checked before the API call, so a malformed value reports "not a valid sha256: 65 characters, expected 64" instead of an opaque MISP rejection.

Descriptions

Ioc.ioc_description is the analyst's comment on that indicator — the text iris-ng shows as "Analyst note on this IOC" — and it becomes the MISP attribute comment.

The same IOC value carries a different description in each case it appears in ("sending infrastructure" in one, "beacon C2 endpoint" in another). Every distinct description is published rather than one being chosen, so the MISP comment shows how the indicator was seen from each case. A generic provenance line appears only when no analyst wrote a description.

Notes

Notes linked through ioc_note_link attach to the indicator they document as MISP analyst Notes, each with its own bolded title.

Case notes at large are never published — only notes an analyst deliberately linked as the source of a shared IOC.

Entity-name redaction

The v2 cluster-narrative prompt forbids the model from writing organisation names, which is what makes the narrative and the STIX export safe to share. Analyst notes and IOC descriptions were never prompt-constrained — they routinely name the client, its divisions, dollar amounts and job titles.

All free text is therefore redacted before it leaves: notes, IOC descriptions, the event title, the galaxy tag, and the narrative itself as defence in depth (an analyst-edited narrative carries no prompt guarantee).

Terms are derived on every push

Nothing is a fixed list. Customers and cases created later are covered with no configuration:

  • Every client name in the instance — not just the cluster's cases, because a note in one case routinely references another customer.
  • The case names of the cluster's cases — divisions, subsidiaries and project names live in case titles (… Applied Sciences Credential Harvest, … WayneTech Prototype Schematics) rather than in customer records.

Each contributes its full string plus its significant tokens. Wayne Enterprises therefore also catches WayneTech and Wayne Foundation.

The stoplist keeps notes readable

Tokens are filtered through a vocabulary stoplist with two groups:

  • Incident vocabularyphishing, beacon, credential, exfiltration, cobalt, strike, compromise … Redacting these would strip what the note is describing without protecting anyone.
  • Sector vocabularymanufacturing, energy, water, healthcare, financialDeliberately preserved. The narrative prompt describes victims by sector role ("a water utility") precisely because that is the shareable context.

A case titled #10 - Cobalt Strike Beacon - Manufacturing VLAN therefore yields no redaction terms at all.

IOC values are never redacted

Indicator values are masked before redaction and restored after, including inside note and description prose. A lookalike domain built from the victim's name is the intelligence — masking wayne-sso-verify.example would destroy the reason for sharing.

What redaction cannot do

This is term matching, not named-entity recognition. A name appearing only in note prose — never in a customer record or a case title — cannot be derived.

Rather than fail silently, surviving proper-noun phrases are returned as possible_unredacted_names on the push response and logged as a warning naming the setting to fix. Add them to Additional terms to redact; admin-supplied terms are never stoplist-filtered, since typing one is explicit intent.

For a hard guarantee, disable Push linked analyst notes. Indicators, tags and the prompt-constrained narrative still publish.

Duplicate protection

misp_cluster_link (Alembic e7c1a94d2f38) records which clusters have been published, UNIQUE on cluster_id.

A repeat push returns HTTP 409 with the existing event ID rather than silently creating a duplicate MISP event. force: true republishes deliberately, creating a new event. The UI renders the 409 as a confirm dialog.

Configuration

/manage/modulesIrisMISPCluster. Separate from IrisMISPSync, so the two can point at different instances or organisations.

Section Parameters
Connection misp_cluster_url, misp_cluster_api_key, misp_cluster_verify_tls, HTTP/HTTPS proxy
Event defaults org_id, distribution, sharing_group_id, threat_level_id, analysis, attribute_to_ids
Content galaxy_enabled, tag_sync_enabled, notes_enabled, note_max_chars, tlp_tag
Redaction redact_enabled, redact_terms

Set your own org_id — the default is a placeholder. Uncheck Verify TLS for a self-signed or private-CA certificate, or every push fails with SSLCertVerificationError.

Endpoint

POST /api/v2/correlation/clusters/<cluster_id>/misp-push

Body (all optional): min_shared, start_date, end_date — the same filter params as /report, so the cluster is reproducible — plus force to republish.

Returns counts of attributes, notes, event reports, redactions applied, any possible_unredacted_names, and per-item failures.

MISP API behaviours worth knowing

Both established against a live MISP 2.5.34 instance, and both return a 403 that reads like a permissions failure but is not:

Analyst notes require a wrapped body. POST analyst_data/add/Note must receive {"Note": {...}}. A flat body is rejected.

Analyst data accepts distribution 0–3 only. Unlike attributes and event reports it rejects 5 ("inherit event"), so notes cannot mirror the event and the level must be resolved explicitly. Notes follow the configured event distribution, falling back to org-only for sharing groups (4), which analyst data cannot express.

Implementation notes

The MISP REST client is shared with IrisMISPSync, not duplicated. It carries non-obvious fixes — notably search_tags() using POST /tags/index, because GET /tags/search/<term> silently returns [] for any name containing a colon, which is every taxonomy tag. Forking the client would fork that bug back in. If this module is ever packaged independently, vendor the client rather than re-implementing it.

Files: source/iris_misp_cluster_module/, source/app/blueprints/rest/v2/correlation/__init__.py.

Clone this wiki locally