rapid7-insightidr 1.14.6: narrow a log result by field, no query syntax
query_log_set takes only="field=value[, …]", applied to the events' fields after the search (case ignored), and says how many matched before it. Static runs a second summary of the same query restricted to what the alert is about — for a "new assets" alert, the new authentications — and names their destination assets: the first real run summarized 978 events but left the 64 new ones mixed in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RhL2gy6Sx6Y3FEsyBE3mZ
This commit is contained in:
@@ -62,6 +62,11 @@ instructions: |
|
||||
hours_before 24, minutes_after 30 (the defaults). Other alerts:
|
||||
hours_before 0.5. Report the "window" field of the answer word for
|
||||
word, never your own reading of it.
|
||||
- Then isolate what the alert is about with a second summary of the SAME
|
||||
query and window, adding only "<field>=<value>" with a field name and
|
||||
value exactly as the first summary printed them. A "new assets" alert:
|
||||
only the new authentications (e.g. authentication_type=
|
||||
new_authentication) — its destination assets are the answer. Name them.
|
||||
- "Invalid Query Syntax": report the exact query and error, no variants.
|
||||
- Keep at most 6 calls for logs.
|
||||
5. ALWAYS call search_investigation(actor_user_name=<user>) once, whatever
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -172,6 +172,26 @@ def _fields(e):
|
||||
return flat
|
||||
|
||||
|
||||
def _parse_only(v):
|
||||
""""field=value, field2=value2" -> {field: value}. Applied to the events'
|
||||
fields after the search, so narrowing a result needs no query syntax."""
|
||||
out = {}
|
||||
for part in str(v or "").split(","):
|
||||
if "=" in part:
|
||||
k, val = part.split("=", 1)
|
||||
if k.strip():
|
||||
out[k.strip()] = val.strip().strip("\"'")
|
||||
return out
|
||||
|
||||
|
||||
def _matches(fields, only):
|
||||
"""Every condition holds, comparing as text and ignoring case."""
|
||||
for k, want in only.items():
|
||||
if str(fields.get(k, "")).lower() != want.lower():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _summarize(events, limit):
|
||||
"""Counts instead of lines: per field, its most frequent values.
|
||||
|
||||
@@ -283,9 +303,17 @@ def run():
|
||||
# No fallback log id: a log set spans several logs and every event names its own.
|
||||
events = _identify(_collect(first, limit), "", inp.get("detection_name"))
|
||||
window = "%s to %s" % (_iso(start_time), _iso(end_time))
|
||||
only = _parse_only(inp.get("only"))
|
||||
matched_before_filter = len(events)
|
||||
if only:
|
||||
events = [e for e in events if _matches(_fields(e), only)]
|
||||
if str(inp.get("summarize") or "").strip().lower() in ("1", "true", "yes"):
|
||||
out = _summarize(events, limit)
|
||||
out["truncated"] = matched_before_filter >= limit
|
||||
out.update({"from": start_time, "to": end_time, "window": window})
|
||||
if only:
|
||||
out["only"] = only
|
||||
out["matched_before_only"] = matched_before_filter
|
||||
print(json.dumps(out))
|
||||
return
|
||||
print(json.dumps({"events": events, "count": len(events), "from": start_time, "to": end_time,
|
||||
|
||||
Reference in New Issue
Block a user