Skip to content

Knowledge Map

zach115th edited this page Jul 29, 2026 · 1 revision

Knowledge Map

The Graph tab on a case renders that case as a knowledge map: assets, IOCs, notes and evidence as nodes, with edges for the relationships between them. A layer filter in the toolbar turns each object type on and off.

Upstream, this tab drew only assets and IOCs, and connected two objects only when they appeared on the same timeline event. IRIS-NG adds note and evidence layers plus the direct object-to-object links held in the M2M link tables.

Layers

Layer Node style Source
Assets Icon (compromise-aware) case_assets
IOCs Virus icon ioc
Notes Violet box ioc_note_link
Evidence Amber box evidence_asset_link

Toggling a layer hides its nodes and every edge touching them. Filtering runs client-side against the payload already in memory, so toggles are instant and never re-hit the API. Selections persist per browser in localStorage (irisGraphLayers), and all layers default to on.

The toolbar also shows a live N nodes / N links readout for the current selection. The panel is hidden below 1200px viewport width, where the canvas needs the space more than the controls do.

Edge kinds

Every edge carries a kind:

kind Meaning Style
event The two objects appear on the same timeline event Solid (dashed if either end is an IOC)
direct A row in an M2M link table joins them Dashed, layer-coloured

direct edges come from three tables:

Table Edge
ioc_note_link Note → IOC (provenance: which note produced this IOC)
evidence_asset_link Evidence → Asset
ioc_asset_link IOC → Asset

Direct links matter because event co-occurrence alone is incomplete: two objects that are genuinely linked but never share a timeline event previously appeared unconnected on the graph.

Behaviour worth knowing

Only linked notes and evidence become nodes. A note that sources no IOC, or an evidence item linked to no asset, is not drawn — isolated nodes are noise on a force-directed layout.

Redundant direct edges are suppressed. When a timeline event already connects two nodes, a direct link between the same pair is dropped rather than drawn as a second parallel line. No connection is lost: kind is not a filter dimension, so a suppressed edge can never be the only thing joining two visible nodes.

The Notes layer is IOC-centric by construction. ioc_note_link is the only note link table, so notes attach to IOCs and nothing else. A note that discusses an asset or an evidence item has no edge to it.

Populating the layers

Notes and evidence only appear once the underlying links exist:

  • IOC ↔ Note — created automatically when the AI IOC extractor's + add flow promotes a suggestion from a note. For cases predating that feature, run backfill_ioc_note_links.py, which refangs defanged values before matching.
  • Evidence ↔ Asset — set via the Related Evidence picker on the asset modal, or the inverse chips on the edit-evidence modal.
  • IOC ↔ Asset — created by "push IOCs to assets" and by working-timeline promotion.

API

GET /case/graph/getdata?cid=<case_id>

Returns the legacy envelope ({status, message, data}) with data.nodes, data.edges and data.dates. The IRIS-NG additions are purely additive, so existing consumers are unaffected:

{
  "nodes": [
    { "id": "a14", "layer": "asset",    "label": "WE-WKS-4471", "shape": "image", ... },
    { "id": "b25", "layer": "ioc",      "label": "203.0.113.47", ... },
    { "id": "n7",  "layer": "note",     "label": "Initial summary", "shape": "box", ... },
    { "id": "e3",  "layer": "evidence", "label": "MS Sentinel logs", "shape": "box", ... }
  ],
  "edges": [
    { "from": "a14", "to": "b26", "kind": "event"  },
    { "from": "n7",  "to": "b25", "kind": "direct" }
  ]
}

Node id prefixes: a asset, b IOC, n note, e evidence.

Implementation

Concern Location
Endpoint source/app/blueprints/rest/case/case_graphs_routes.py
Link queries source/app/datamgmt/case/case_graph_db.py
Renderer + filter ui/public/assets/js/iris/case.graph.js
Toolbar + styles source/app/blueprints/pages/case/templates/case_graph.html

case.graph.js has no import statements, so it lives in ui/public/ and is copied verbatim by Vite — see Development Guide → Import-free JS belongs in ui/public/. Deploy it with docker cp plus a browser hard-reload; template changes need docker restart iriswebapp_app because Jinja auto-reload is off.

Clone this wiki locally