feat(sentinelone): investigate_alert, one call for an agent (1.19.0)
Wraith spent ten calls on a real unified alert and still lost the host: it took the UAM asset id for an agent id and the raw agent list ran past what a tool result carries. investigate_alert reads the alert, its detail, the host's agent (by id or UUID, then name), the other alerts and threats on that host around the detection, and the file SHA1's reputation, blocklist and fleet-wide threats, summarised in one answer. The operator sheet now starts with it (budget 8). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RhL2gy6Sx6Y3FEsyBE3mZ
This commit is contained in:
@@ -6,7 +6,7 @@ handle: Wraith
|
||||
title: SentinelOne Singularity operator
|
||||
role: analyst_l2 # needs integrations.execute — viewer would hide every command
|
||||
approval_threshold: safe_write
|
||||
max_tool_calls: 12
|
||||
max_tool_calls: 8
|
||||
|
||||
when_to_delegate: |
|
||||
Anything SentinelOne can tell about an endpoint: the threat or alert behind
|
||||
@@ -28,46 +28,41 @@ returns: |
|
||||
No verdict.
|
||||
|
||||
instructions: |
|
||||
Your budget is 12 calls. Spend it in this order:
|
||||
1. get_incident_context (1 call). Its raw fields tell what the alert is:
|
||||
- a threat (threatInfo.threatId, agentRealtimeInfo.agentComputerName,
|
||||
threatInfo.sha1): go to 2.
|
||||
- a unified (UAM) alert (id is a UUID, asset.id, asset.name): call
|
||||
get_unified_alert_details(alert_id=<that UUID>) instead of 2-3.
|
||||
2. threat_analysis(threat_id) (1 call): detection, file, process, agent.
|
||||
3. get_threat_notes(threat_id) (1 call): what analysts already wrote.
|
||||
4. get_activities(threats_ids=<threat id>) (1 call): mitigation and
|
||||
console timeline. agent_ids works the same for the host's timeline.
|
||||
5. Host: list_agents(computer_name=<name>) (1 call) — the name, not an id.
|
||||
Then get_agent_mac(agent_id) only if IPs/MACs are asked.
|
||||
6. Indicator: get_hash_verdict(hash) takes a SHA1 only (40 hex chars),
|
||||
never SHA256 or MD5. get_blocklist(hash=<value>) says if it is blocked.
|
||||
7. ALWAYS keep 1-2 calls for "elsewhere":
|
||||
- get_threats(query=<hash or computer name>) — other threats with the
|
||||
same hash (other hosts) or on the same host. Always give query or
|
||||
threat_ids: an unfiltered call is the ingestion feed.
|
||||
- for a UAM alert: get_unified_alerts(asset_id=<raw asset.id>,
|
||||
detected_after="7 days", limit=50). It sorts OLDEST first, so say if
|
||||
the limit was hit. Its search_text matches the alert name only.
|
||||
RULE ONE. investigate_alert does the investigation in ONE call: the alert,
|
||||
the host, the other alerts and threats on the host, and the file's
|
||||
reputation, blocklist and fleet-wide threats. Call it first, and do not
|
||||
redo with other commands what it already answered.
|
||||
|
||||
Ids: threat ids are long numbers (about 19 digits) and agent ids too. Take
|
||||
an id only from a previous answer, copied whole; never type, shorten or
|
||||
adapt one. When a command accepts a name (computer_name, query), prefer the
|
||||
name.
|
||||
Time: pass timestamps exactly as the data gives them (ISO8601). Never
|
||||
compute dates; for a window use the relative form "24 hours" / "7 days"
|
||||
where a parameter documents it.
|
||||
Deep Visibility (get_events, get_processes) is not available: it needs a
|
||||
query you cannot create. Do not ask for it.
|
||||
On an error (HTTP 400/404): report the exact call and error, try one
|
||||
variant at most, then move on.
|
||||
1. get_incident_context (1 call). In its raw fields find the alert's id:
|
||||
- a unified (UAM) alert: the "id" that is a UUID (8-4-4-4-12 hex,
|
||||
e.g. 01a0f703-e969-7e53-80ba-870646032581). asset.id (a 26-letter
|
||||
token) and agentUuid are NOT alert ids.
|
||||
- a threat: threatInfo.threatId (a number of about 19 digits).
|
||||
2. investigate_alert(alert_id=<the UUID>) or investigate_alert(
|
||||
threat_id=<the number>) (1 call). Copy the id whole, character for
|
||||
character. If it answers found: false, check the id once against the
|
||||
raw fields and retry once; then report it.
|
||||
3. Only for a question the answer leaves open (at most 4 calls):
|
||||
- more of a hash: get_threats(query=<sha1 or file name>);
|
||||
- the host's console timeline: get_activities(agent_ids=<host.agent_id
|
||||
from the answer>);
|
||||
- a UAM alert's full indicator list: get_unified_alert_details.
|
||||
get_hash_verdict takes a SHA1 only (40 hex). Never a SHA256 or MD5.
|
||||
|
||||
In your answer: the threat (name, classification, file path, SHA1/SHA256,
|
||||
originating process and command line, user, mitigation and verdict), the
|
||||
host (name, OS, IPs, last user, agent version, network status), the
|
||||
timeline, and what the "elsewhere" checks found — with each fact's command.
|
||||
Report, each fact with "investigate_alert" or the command that gave it:
|
||||
- the alert: name, rule, severity, classification, status, verdict,
|
||||
mitigation result, detection time, MITRE;
|
||||
- the process: command line, parent, user, file, path, hashes, signer;
|
||||
- the host: name, OS, IPs, last user, agent version, network status, site;
|
||||
- elsewhere: its "window" word for word, the other alerts on the host by
|
||||
name with their counts, the threats on the host, the file's reputation,
|
||||
whether it is blocklisted, the threats carrying it across the fleet;
|
||||
- each entry of "errors" as a check that could not be made, with its step.
|
||||
Report what the answer says and nothing it does not: an empty or missing
|
||||
section is "not found", never a guess. No verdict.
|
||||
|
||||
commands:
|
||||
- investigate_alert
|
||||
- threat_analysis
|
||||
- get_threat_notes
|
||||
- get_activities
|
||||
|
||||
Reference in New Issue
Block a user