fix(sentinelone): accept a skipped UAM update when the alert is already set (1.17.3)
The console skips an alert already in the requested state, and 1.17.2 failed every skip: re-assigning an alert to its current holder, as a second run of the same playbook does, broke the block. A skipped alert is now read back. Already assigned to that user, or already carrying that status or verdict, the block succeeds with already_assigned / already_set; otherwise it fails naming the state the console holds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013u8PFdkYuuPiC6JdZBV5x3
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -79,11 +79,15 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
result = resp.get("data") or {}
|
result = resp.get("data") or {}
|
||||||
|
|
||||||
# A refused or skipped alert comes back in the payload, not in errors.
|
# A refused alert comes back in the payload, not in errors. A skipped one is
|
||||||
|
# what the console answers for an alert already assigned to that user — a
|
||||||
|
# second run of the same playbook — so it is settled by the read-back below
|
||||||
|
# rather than failed outright.
|
||||||
triggered = (result.get("alertTriggerActions") or {}).get("actions") or []
|
triggered = (result.get("alertTriggerActions") or {}).get("actions") or []
|
||||||
failures = [f for a in triggered for f in (a.get("failure") or [])]
|
failures = [f for a in triggered for f in (a.get("failure") or [])]
|
||||||
skipped = [s for a in triggered for s in (a.get("skip") or [])]
|
skipped = [s for a in triggered for s in (a.get("skip") or [])]
|
||||||
if failures or skipped or not any(a.get("success") for a in triggered):
|
succeeded = any(a.get("success") for a in triggered)
|
||||||
|
if failures or not (succeeded or skipped):
|
||||||
print(json.dumps({"error": "The console did not assign the alert",
|
print(json.dumps({"error": "The console did not assign the alert",
|
||||||
"failure": failures, "skip": skipped, "result": result}))
|
"failure": failures, "skip": skipped, "result": result}))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -101,13 +105,15 @@ def main():
|
|||||||
if str((assignee or {}).get("userId") or "") == user_id:
|
if str((assignee or {}).get("userId") or "") == user_id:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
said = "reported success" if succeeded else "skipped the alert"
|
||||||
print(json.dumps({
|
print(json.dumps({
|
||||||
"error": "The console reported success but the alert is not assigned to "
|
"error": "The console " + said + " but the alert is not assigned to "
|
||||||
+ user_id + " — check that this id is the console user's (assignee.userId)",
|
+ user_id + " — check that this id is the console user's (assignee.userId)",
|
||||||
"assignee_now": assignee, "result": result}))
|
"assignee_now": assignee, "result": result}))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print(json.dumps({"alert_id": alert_id, "assignee_user_id": user_id,
|
print(json.dumps({"alert_id": alert_id, "assignee_user_id": user_id,
|
||||||
|
"already_assigned": not succeeded,
|
||||||
"assignee": assignee, "result": result}))
|
"assignee": assignee, "result": result}))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,22 @@ def main():
|
|||||||
triggered = ((resp.get("data") or {}).get("alertTriggerActions") or {}).get("actions") or []
|
triggered = ((resp.get("data") or {}).get("alertTriggerActions") or {}).get("actions") or []
|
||||||
failures = [f for a in triggered for f in (a.get("failure") or [])]
|
failures = [f for a in triggered for f in (a.get("failure") or [])]
|
||||||
skipped = [s for a in triggered for s in (a.get("skip") or [])]
|
skipped = [s for a in triggered for s in (a.get("skip") or [])]
|
||||||
if failures or skipped or not any(a.get("success") for a in triggered):
|
succeeded = any(a.get("success") for a in triggered)
|
||||||
|
if skipped and not failures and not succeeded:
|
||||||
|
# The console skips an alert already in the requested state. That is a
|
||||||
|
# done update, not a failed one — but only once the alert says so.
|
||||||
|
back = request("POST", base + "/unifiedalerts/graphql", headers, {
|
||||||
|
"query": "query AlertState($id: ID!) { alert(id: $id) { status } }",
|
||||||
|
"variables": {"id": variables["id"]}})
|
||||||
|
now = ((back.get("data") or {}).get("alert") or {}).get("status")
|
||||||
|
if str(now or "") == str(variables["status"] or ""):
|
||||||
|
resp["already_set"] = True
|
||||||
|
print(json.dumps(resp))
|
||||||
|
return
|
||||||
|
print(json.dumps({"error": "The console skipped the alert, which is status " + str(now),
|
||||||
|
"skip": skipped, "result": resp.get("data")}))
|
||||||
|
sys.exit(1)
|
||||||
|
if failures or not succeeded:
|
||||||
print(json.dumps({"error": "The console did not update the alert",
|
print(json.dumps({"error": "The console did not update the alert",
|
||||||
"failure": failures, "skip": skipped, "result": resp.get("data")}))
|
"failure": failures, "skip": skipped, "result": resp.get("data")}))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
@@ -40,7 +40,22 @@ def main():
|
|||||||
triggered = ((resp.get("data") or {}).get("alertTriggerActions") or {}).get("actions") or []
|
triggered = ((resp.get("data") or {}).get("alertTriggerActions") or {}).get("actions") or []
|
||||||
failures = [f for a in triggered for f in (a.get("failure") or [])]
|
failures = [f for a in triggered for f in (a.get("failure") or [])]
|
||||||
skipped = [s for a in triggered for s in (a.get("skip") or [])]
|
skipped = [s for a in triggered for s in (a.get("skip") or [])]
|
||||||
if failures or skipped or not any(a.get("success") for a in triggered):
|
succeeded = any(a.get("success") for a in triggered)
|
||||||
|
if skipped and not failures and not succeeded:
|
||||||
|
# The console skips an alert already in the requested state. That is a
|
||||||
|
# done update, not a failed one — but only once the alert says so.
|
||||||
|
back = request("POST", base + "/unifiedalerts/graphql", headers, {
|
||||||
|
"query": "query AlertState($id: ID!) { alert(id: $id) { analystVerdict } }",
|
||||||
|
"variables": {"id": variables["id"]}})
|
||||||
|
now = ((back.get("data") or {}).get("alert") or {}).get("analystVerdict")
|
||||||
|
if str(now or "") == str(variables["verdict"] or ""):
|
||||||
|
resp["already_set"] = True
|
||||||
|
print(json.dumps(resp))
|
||||||
|
return
|
||||||
|
print(json.dumps({"error": "The console skipped the alert, which is analystVerdict " + str(now),
|
||||||
|
"skip": skipped, "result": resp.get("data")}))
|
||||||
|
sys.exit(1)
|
||||||
|
if failures or not succeeded:
|
||||||
print(json.dumps({"error": "The console did not update the alert",
|
print(json.dumps({"error": "The console did not update the alert",
|
||||||
"failure": failures, "skip": skipped, "result": resp.get("data")}))
|
"failure": failures, "skip": skipped, "result": resp.get("data")}))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user