Skip to content

Add threat-report correlation#62

Draft
seth-goodwin wants to merge 9 commits into
elastic:mainfrom
seth-goodwin:threat-correlation
Draft

Add threat-report correlation#62
seth-goodwin wants to merge 9 commits into
elastic:mainfrom
seth-goodwin:threat-correlation

Conversation

@seth-goodwin

@seth-goodwin seth-goodwin commented Jul 13, 2026

Copy link
Copy Markdown

Summary

Adds an end-to-end threat-report correlation capability to the example security MCP app: correlate a case — a stored corpus report or pasted raw text — against the report corpus and return structured Diamond-model findings.

The authoritative path is a server-side ti-correlation Kibana Workflow: retrieval (exact anchor + Diamond-vertex kNN + BM25) → Sonnet triage → Opus synthesis with a rank-leak firewall. The app triggers the workflow and polls ti-correlations for the authoritative findings. The retrieval tools are kept as analyst exploration aids only.

What's added

  • Tools: correlate, get_correlation_run, diamond_search, diamond_search_analyst, get_report, correlation_input_check, render_correlation.
  • Service: correlationService.ts — retrieval, triage-pool shaping, workflow trigger/poll.
  • Views: correlation-input, correlation (scored triage), correlation-report (Diamond render with colored vertices, smoking-gun vs supporting evidence, BLUF, gaps, source links).
  • Canvas: correlation-report.canvas.tmpl + gen-correlation-canvas.ts — paint-by-numbers Cursor canvas generated from a run record.
  • Skill: skills/threat-correlation/SKILL.md.
  • Tests: correlation.test.ts + integration updates (full suite: 399 passing).

Configuration

  • TI_REPORTS_INDEX_PATTERN (default ti-reports*) — the report corpus the tools search.

Notes

  • Draft — opening for review/iteration.
  • Expects a threat-report corpus carrying extracted.diamond.* fields (produced by a compatible ingest/enrichment pipeline).
  • Cursor's canvas view is elegant.

TODO

  • Work through Claude Desktop visualizations.

Test plan

  • npm test green (399 tests)
  • correlate with a stored report_id returns findings from the workflow
  • correlate with raw_text runs the raw-text path
  • render_correlation produces the report view / canvas from a run record
  • retrieval aids (diamond_search, diamond_search_analyst, get_report) work against a populated corpus
image (37) image (38)

Made with Cursor

seth-goodwin and others added 8 commits July 13, 2026 14:48
ES-retrieval-only addon for threat correlation; the host LLM reasons, guided by
a vendored tradecraft rubric. diamond_search returns blind candidate stubs
(no scores) from semantic_text kNN + IOC anchors + BM25; get_report batch-fetches
full text by id. No LLM calls, no creds beyond ES. Retrieval vendored from the
Kibana IntelligenceHub engine. NOTE: requires a custom threat-report corpus index
(not stock Elastic) — see src/correlation/README.md. Self-contained: new files +
2 additive lines in server.ts.
The transparent A/B sibling to the blind correlation path. diamond_search_analyst
returns scored candidates (per-vertex match scores, overlap, thin-coverage signal)
for analyst-supervised triage. correlation_input_check is a display-only gate: the
host LLM passes its per-vertex case summaries + self-rated signal (HIGH/PARTIAL/NONE),
the analyst reviews a stoplight checkpoint and decides whether to search — "Search
this case" injects a proceed message (app.sendMessage, updateModelContext fallback)
carrying the vertex queries so the LLM runs the search immediately; "I'll revise
first" returns to chat. Two inline views: the stoplight input gate + a ranked
collapsed-diamond triage list with a thin-coverage backfill nudge. No LLM calls in
the tools (host reasons). The blind diamond_search/get_report path is unchanged.
Self-contained: new view dirs + scored types/method + gate tool; no server.ts change.
From the triage view, the analyst selects report(s) or a cluster and hits
Synthesize → app.sendMessage tells the host to get_report the full text,
synthesize per the vendored rubric, then call render_correlation with the
result. render_correlation is a pure pass-through (no ES, no synthesis — the
host does all reasoning) that hands the structured CorrelationFindings to a
new deep-dive view: BLUF + correlation-signal stoplight, lead cards with
collapsed/expanded diamonds + weighted evidence (supporting/counter), no-match
list, synthesis + next steps. Completes the gate → search → triage → deep-dive
flow. Additive: new view + render tool + triage-view selection; blind path and
prior tools unchanged.
manifest.json: register the 5 correlation tools (diamond_search, get_report,
diamond_search_analyst, correlation_input_check, render_correlation).
skills/threat-correlation/SKILL.md: host-LLM steering — analyst-led path
(input gate → scored triage → get_report → render) primary, blind path alternate.
src/tools/correlation.test.ts: cover all 5 tools (pass-through tools assert no
service calls; blind vs. scored stubs; input-gate branches; UI resources).
correlation-input view: the "Search this case" button drove a new turn via
app.sendMessage(), unsupported on Claude Desktop — removed it for a passive
prompt; the conversation is the control surface.
SKILL.md: gate now offers Full run (autonomous, ~10-candidate cap) vs
Analyst-led (interactive) — both use the same 5 tools.
analytics: correlation views reported viewId "threat-hunt"; extend VIEW_IDS
and label each view correctly.
Route authoritative correlation through the ti-correlation Kibana workflow
(correlate -> get_correlation_run -> render_correlation); keep diamond_search,
diamond_search_analyst, get_report, and correlation_input_check as analyst
exploration aids and mark host-driven synthesis deprecated.

Roll the canvas generator into src/canvas (emitCorrelationCanvas): stamp a
run's render-shape findings into correlation-report.canvas.tmpl, opt-in via
CORRELATION_CANVAS_DIR. Add dev CLIs (gen-correlation-canvas, dump-run-findings,
smoke-correlate).

Hygiene: fix stale server integration tests (correlation tools + views),
drop a hardcoded personal canvas path from gen-correlation-canvas, and
document correlation/canvas env vars in .env.example.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop internal codenames from comments (no behavior change): remove the
"Mustard" sort-key reference and generalize "threat-intel-ingest" corpus
wording in correlationService and .env.example. Index pattern stays
env-configurable via TI_REPORTS_INDEX_PATTERN (default ti-reports*).

Co-authored-by: Cursor <cursoragent@cursor.com>
@seth-goodwin seth-goodwin changed the title Add threat-report correlation (workflow-backed, with canvas render) Add threat-report correlation Jul 13, 2026
ClusterCredentials now requires sslVerify; the correlation dev/smoke scripts
built creds without it, breaking `tsc --noEmit`. Default to true (verify),
overridable via ELASTIC_SSL_VERIFY=false for self-signed dev clusters.

Co-authored-by: Cursor <cursoragent@cursor.com>
@davethegut

Copy link
Copy Markdown
Collaborator

@niros1 This aligns with a Feature we are looking for AD to support in the future; threat actor correlation. Would be a really good add for MCP as well.

CC: @jamesspi @maxcold

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants