rapid7-insightidr 1.14.4: keep UBA alerts in the full investigation feed
get_investigations_full dropped every alert without an RRN, so a behaviour (UBA) investigation arrived with alerts: [] and alert_count: 0 and its reading worked from a title alone. Those alerts are now kept under alerts_without_rrn as the investigation endpoint returns them, counted in alert_count, rendered in summary_markdown with where their evidence lives, and lift first_alert_type / first_alert_source / first_alert_id when they are all the investigation has. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RhL2gy6Sx6Y3FEsyBE3mZ
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -399,6 +399,37 @@ def _evidence_rows(alerts):
|
||||
return rows, max(dropped, 0)
|
||||
|
||||
|
||||
def _without_rrn(a):
|
||||
"""An investigation alert the Alert Triage API cannot read, as returned,
|
||||
with what an agent or analyst needs to know about it."""
|
||||
out = dict(a)
|
||||
out["evidence_api"] = False
|
||||
out["evidence_hint"] = ("Behaviour (UBA) alert: InsightIDR exposes no actors or evidence for it through the "
|
||||
"API. Its activity is in the authentication logs (Asset Authentication log set), "
|
||||
"around its event times.")
|
||||
return out
|
||||
|
||||
|
||||
def _without_rrn_summary(alerts):
|
||||
"""Markdown for the alerts that have no RRN, so a layout or a reading shows
|
||||
them instead of an empty investigation."""
|
||||
lines = []
|
||||
for a in alerts:
|
||||
lines.append("### " + str(a.get("title") or a.get("alert_type") or "Behaviour alert"))
|
||||
meta = []
|
||||
for k, label in (("alert_type", "type"), ("alert_source", "source"),
|
||||
("first_event_time", "first event"), ("latest_event_time", "latest event"),
|
||||
("created_time", "created")):
|
||||
if a.get(k):
|
||||
meta.append("%s %s" % (label, a[k]))
|
||||
if meta:
|
||||
lines.append(" · ".join(meta))
|
||||
lines.append("")
|
||||
lines.append("_" + a["evidence_hint"] + "_")
|
||||
lines.append("")
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
def _markdown_summary(inv, alerts):
|
||||
"""A readable rendering of what the detection actually says.
|
||||
|
||||
@@ -495,6 +526,12 @@ def run():
|
||||
# output with an alerts_error: losing the whole run over one of them, or
|
||||
# dropping it silently, would both be worse.
|
||||
rrns_by_inv, order, truncated, skipped = {}, [], False, 0
|
||||
# Alerts with no RRN — behaviour (UBA) alerts such as "attempted to remotely
|
||||
# access N new assets". The Alert Triage API cannot fetch them, and they
|
||||
# used to be dropped here: the investigation arrived with alerts: [] and
|
||||
# alert_count: 0, and every reading of it worked from a title alone. They
|
||||
# are kept as the investigation endpoint returned them.
|
||||
no_rrn_by_inv = {}
|
||||
region_failures, regions_seen = [], set()
|
||||
for inv in investigations:
|
||||
inv_rrn = inv.get("rrn") or ""
|
||||
@@ -514,6 +551,7 @@ def run():
|
||||
rrn = _alert_rrn(a)
|
||||
if rrn is None:
|
||||
skipped += 1
|
||||
no_rrn_by_inv.setdefault(inv_rrn, []).append(_without_rrn(a))
|
||||
continue
|
||||
got.append(rrn)
|
||||
r = _rrn_region(rrn)
|
||||
@@ -598,7 +636,10 @@ def run():
|
||||
rrns = rrns_by_inv.get(inv.get("rrn") or "", [])
|
||||
alerts = [by_rrn[r] for r in rrns if r in by_rrn]
|
||||
inv["alerts"] = alerts
|
||||
inv["alert_count"] = len(rrns)
|
||||
no_rrn = no_rrn_by_inv.get(inv.get("rrn") or "", [])
|
||||
inv["alerts_without_rrn"] = no_rrn
|
||||
# What the investigation holds, not what this run could fetch.
|
||||
inv["alert_count"] = len(rrns) + len(no_rrn)
|
||||
# The block reads ctx.process_tree at the top level and draws one chain
|
||||
# per alert, so every alert's chains are gathered here rather than left
|
||||
# nested where no mapper expression could reach past alerts[0].
|
||||
@@ -645,6 +686,18 @@ def run():
|
||||
if ef:
|
||||
inv["first_evidence_message"] = ef.get("msg") or ef.get("logdesc") or ef.get("message")
|
||||
inv["first_evidence_raw_line"] = a.get("evidence_raw_line")
|
||||
if no_rrn:
|
||||
# Readable beside the detection alerts, or alone when they are all
|
||||
# the investigation has — a UBA investigation usually is.
|
||||
uba_md = _without_rrn_summary(no_rrn)
|
||||
inv["summary_markdown"] = (inv["summary_markdown"] + "\n\n" + uba_md) if inv.get("summary_markdown") else uba_md
|
||||
if not alerts:
|
||||
u = no_rrn[0]
|
||||
inv.update({
|
||||
"first_alert_type": u.get("alert_type") or u.get("title"),
|
||||
"first_alert_source": u.get("alert_source"),
|
||||
"first_alert_id": u.get("id"),
|
||||
})
|
||||
|
||||
print(json.dumps({
|
||||
"data": investigations,
|
||||
|
||||
Reference in New Issue
Block a user