SentinelOne V2

Use the SentinelOne integration to send requests to your management server and get responses with data pulled from agents or from the management database.

Endpoint · SentinelOne

Details

IDSentinelOne V2
ProviderSentinelOne
CategoryEndpoint
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Use the SentinelOne integration to send requests to your management server and get responses with data pulled from agents or from the management database.
This integration was integrated and tested with versions 2.0 and 2.1 of SentinelOne V2

Some changes have been made that might affect your existing content.
If you are upgrading from a previous version of this integration, see Breaking Changes.

Configure SentinelOne v2 in Cortex

Parameter Description Required
Server URL (e.g., https://usea1.sentinelone.net)   True
API Token   False
API Version   True
Fetch incidents   False
Incident type   False
Fetch incidents from type   False
Fetch incidents from UAM Alert type   False
First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)   False
Minimum risk score for importing incidents (0-10), where 0 is low risk and 10 is high risk. Relevant for API version 2.0.   False
Defines Alert severity to fetch. Supported values: Low, Medium, High, Critical.   False
Define which Alerts should be fetched.   False
Define which Threats should be fetched.   False
Fetch limit: The maximum number of threats or alerts to fetch   False
Site IDs Comma-separated list of site IDs to fetch incidents for. Leave blank to fetch all sites. False
Block Site IDs Comma-separated list of site IDs for where hashes should be blocked. If left blank all hashes will be blocked globally. If filled out with site ids all hashes will be no longer be blocked globally, they will now be blocked in the scope of those sites. False
Trust any certificate (not secure)   False
Use system proxy settings   False
Incidents Fetch Interval   False
Incident Mirroring Direction Choose the direction to mirror the incident: Incoming (from SentinelOne to Cortex XSOAR), Outgoing (from Cortex XSOAR to SentinelOne), or Incoming and Outgoing (from/to Cortex XSOAR and SentinelOne). Cortex XSOAR only parameter. False
Close Mirrored XSOAR Incident When selected, closing the SentinelOne ticket is mirrored in Cortex XSOAR. False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

sentinelone-list-agents


Returns all agents that match the specified criteria.

Base Command

sentinelone-list-agents

Input

Argument Name Description Required
computer_name The computer name by which to filter the results. It can match a partial computer name value (substring). Optional
scan_status A comma-separated list of scan statuses by which to filter the results, for example: “started,aborted”. Possible values are: started, none, finished, aborted. Optional
os_type Included operating system types, for example: “windows”. Possible values are: windows, windows_legacy, macos, linux. Optional
created_at Endpoint creation timestamp, for example: “2018-02-27T04:49:26.257525Z”. Optional
min_active_threats Minimum number of threats per agent. Optional
limit The maximum number of agents to return. Default is 10. Optional
params Query params field=value pairs delimited by comma (e.g., activeThreats=3,gatewayIp=1.2.3.4). Query params are OR’d. Optional
columns A comma-separated list of additionals fields to display. Optional

Context Output

Path Type Description
SentinelOne.Agents.NetworkStatus string The agent network status.
SentinelOne.Agents.ID string The agent ID.
SentinelOne.Agents.AgentVersion string The agent software version.
SentinelOne.Agents.IsDecommissioned boolean Whether the agent is decommissioned.
SentinelOne.Agents.IsActive boolean Whether the agent is active.
SentinelOne.Agents.LastActiveDate date When was the agent last active.
SentinelOne.Agents.RegisteredAt date The registration date of the agent.
SentinelOne.Agents.ExternalIP string The agent IP address.
SentinelOne.Agents.ThreatCount number Number of active threats.
SentinelOne.Agents.EncryptedApplications boolean Whether disk encryption is enabled.
SentinelOne.Agents.OSName string Name of operating system.
SentinelOne.Agents.ComputerName string Name of agent computer.
SentinelOne.Agents.MachineType string Machine type.
SentinelOne.Agents.Domain string Domain name of the agent.
SentinelOne.Agents.CreatedAt date Creation time of the agent.
SentinelOne.Agents.SiteName string Site name associated with the agent.
SentinelOne.Agents.Tags unknown Tags associated with the agent.

sentinelone-create-white-list-item


Creates an exclusion item that matches the specified input filter.

Base Command

sentinelone-create-white-list-item

Input

Argument Name Description Required
exclusion_type Exclusion item type. Possible values are: file_type, path, white_hash, certificate, browser. Required
exclusion_value Value of the exclusion item for the exclusion list. Required
os_type Operating system type. Required for hash exclusions. Possible values are: windows, windows_legacy, macos, linux. Required
description Description for adding the exclusion item. Optional
exclusion_mode Exclusion mode (path exclusion only). Possible values are: suppress, disable_in_process_monitor_deep, disable_in_process_monitor, disable_all_monitors, disable_all_monitors_deep. Optional
path_exclusion_type Excluded path for a path exclusion list. Optional
group_ids A comma-separated list of group IDs by which to filter. Optional
site_ids A comma-separated list of site IDs by which to filter. Optional

Context Output

Path Type Description
SentinelOne.Exclusions.ID string The entity ID on the allow list.
SentinelOne.Exclusions.Type string The item type on the allow list.
SentinelOne.Exclusions.CreatedAt date Time when the allow list item was created.

sentinelone-get-white-list


Lists all exclusion items that match the specified input filter.

Base Command

sentinelone-get-white-list

Input

Argument Name Description Required
item_ids List of IDs by which to filter, for example: “225494730938493804,225494730938493915”. Optional
os_types A comma-separated list of operating system types by which to filter, for example: “windows, linux”. Possible values are: windows, windows_legacy, macos, linux. Optional
exclusion_type Exclusion type. Possible values are: file_type, path, white_hash, certificate, browser. Optional
limit The maximum number of items to return. Default is 10. Optional
include_parent Whether to include parent information of each item. Default value is false. Default is false. Optional
include_children Whether to include children information of each item. Default value is false. Default is false. Optional

Context Output

Path Type Description
SentinelOne.Exclusions.ID string The exclusion item ID.
SentinelOne.Exclusions.Type string The exclusion item type.
SentinelOne.Exclusions.CreatedAt date Timestamp when the exclusion item was added.
SentinelOne.Exclusions.Value string Value of the exclusion item.
SentinelOne.Exclusions.Source string Source of the exclusion item.
SentinelOne.Exclusions.UserID string User ID of the user qho added the exclusion item.
SentinelOne.Exclusions.UpdatedAt date Timestamp when the exclusion item was updated.
SentinelOne.Exclusions.OsType string Operating system type of the exclusion item.
SentinelOne.Exclusions.UserName string User name of the user who added the exclusion item.
SentinelOne.Exclusions.Mode string A comma-separated list of modes by which to filter (path exclusions only), for example: “suppress”.

sentinelone-get-hash


Gets the file reputation verdict by a SHA1 hash.

Base Command

sentinelone-get-hash

Input

Argument Name Description Required
hash The content hash. Required

Context Output

Path Type Description
SentinelOne.Hash.Rank Number The hash reputation (1-10).
SentinelOne.Hash.Verdict String The hash reputation verdict.
SentinelOne.Hash.Hash String The content hash.

sentinelone-get-threats


Returns threats according to the specified filters.

Base Command

sentinelone-get-threats

Input

Argument Name Description Required
content_hash A comma-separated list of content hashes of the threat. Optional
mitigation_status A comma-separated list of mitigation statuses. Possible values are: mitigated, active, blocked, suspicious, pending, suspicious_resolved. Optional
created_before Searches for threats created before this timestamp, for example: “2018-02-27T04:49:26.257525Z”, “10 days”, “5 months”, “2 hours”. Optional
created_after Searches for threats created after this timestamp, for example: “2018-02-27T04:49:26.257525Z”, “10 days”, “5 months”, “2 hours”. Optional
created_until Searches for threats created on or before this timestamp, for example: “2018-02-27T04:49:26.257525Z”, “10 days”, “5 months”, “2 hours”. Optional
created_from Search for threats created on or after this timestamp, for example: “2018-02-27T04:49:26.257525Z”, “10 days”, “5 months”, “2 hours”. Optional
resolved Whether to only return resolved threats. Possible values are: false, true. Default is false. Optional
display_name Threat display name. For API version 2.0 it can be a partial display name, doesn’t have to be an exact match. Optional
limit The maximum number of threats to return. Default is 20. Optional
query Full free-text search for fields. Can be “content_hash”, “file_display_name”, “file_path”, “computer_name”, or “uuid”. Optional
threat_ids A comma-separated list of threat IDs, for example: “225494730938493804,225494730938493915”. Optional
classifications A comma-separated list of threat classifications to search, for example: “Malware”, “Network”, “Benign”. Possible values are: Engine, Static, Cloud, Behavioral. Optional
rank Risk level threshold to retrieve (1-10). Relevant for API version 2.0 only. Optional
site_ids A comma-separated list of site IDs to search for threats, for example: “225494730938493804,225494730938493915”. Optional
incident_statuses Incident status. Example: “IN_PROGRESS, UNRESOLVED”. Optional
include_resolved_param Whether to include the resolved parameter in the query. Possible values are: false, true. Default is false. Optional

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.AgentComputerName String The agent computer name.
SentinelOne.Threat.CreatedDate Date The threat creation date.
SentinelOne.Threat.SiteID String The site ID.
SentinelOne.Threat.Classification string The threat classification.
SentinelOne.Threat.ClassificationSource string Source of the threat classification.
SentinelOne.Threat.ConfidenceLevel string SentinelOne threat confidence level.
SentinelOne.Threat.FileSha256 string SHA256 hash of the file content.
SentinelOne.Threat.MitigationStatus String The agent mitigation status.
SentinelOne.Threat.AgentID String The threat agent ID.
SentinelOne.Threat.Rank Number The number representing the cloud reputation (1-10).
SentinelOne.Threat.MarkedAsBenign Boolean Whether the threat is marked as benign. Relevant for version 2.0 only.

sentinelone-threat-summary


Returns a dashboard threat summary. Can only be used with API V2.1.

Base Command

sentinelone-threat-summary

Input

Argument Name Description Required
group_ids A comma-separated list of group IDs by which to filter, for example: “225494730938493804,225494730938493915”. Optional

Context Output

Path Type Description
SentinelOne.Threat.NotResolved Number Number of unresolved threats in the system.
SentinelOne.Threat.SuspiciousNotMitigatedNotResolved Number Number of unmitigated suspicious threats in the system.
SentinelOne.Threat.SuspiciousNotResolved Number Number of unresolved suspicious threats in the system.
SentinelOne.Threat.Resolved Number Number of resolved threats in the system.
SentinelOne.Threat.InProgress Number Number of active threats in the system.
SentinelOne.Threat.Total Number Total number of threats in the system.
SentinelOne.Threat.NotMitigated Number Number of unmitigated threats in the system.
SentinelOne.Threat.MaliciousNotResolved Number Number of unresolved malicious threats in the system.
SentinelOne.Threat.NotMitigatedNotResolved Number Number of unmitigated and unresolved threats in the system.

sentinelone-mark-as-threat


Marks suspicious threats as threats. Can only be used with API V2.0.

Base Command

sentinelone-mark-as-threat

Input

Argument Name Description Required
threat_ids A comma-separated list of threat IDs. Optional
target_scope Scope to use for exclusions. Possible values are: site, tenant. Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.MarkedAsThreat Boolean Whether the suspicious threat was successfully marked as a threat.

sentinelone-mitigate-threat


Applies a mitigation action to a group of threats that match the specified input filter.

Base Command

sentinelone-mitigate-threat

Input

Argument Name Description Required
action Mitigation action. Possible values are: kill, quarantine, un-quarantine, remediate, rollback-remediation. Required
threat_ids A comma-separated list of threat IDs. Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.Mitigated Boolean Whether the threat was successfully mitigated.
SentinelOne.Threat.Mitigation.Action String The mitigation action performed.

sentinelone-resolve-threat


Resolves threats using the threat ID. Can only be used with API V2.0.

Base Command

sentinelone-resolve-threat

Input

Argument Name Description Required
threat_ids A comma-separated list of threat IDs. Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.Resolved Boolean Whether the threat was successfully resolved.

sentinelone-get-agent


Returns the details of an agent according to the agent ID.

Base Command

sentinelone-get-agent

Input

Argument Name Description Required
agent_id A comma-separated string of agent IDs. Required

Context Output

Path Type Description
SentinelOne.Agent.NetworkStatus string The agent network status.
SentinelOne.Agent.ID string The agent ID.
SentinelOne.Agent.AgentVersion string The agent software version.
SentinelOne.Agent.IsDecommissioned boolean Whether the agent is decommissioned.
SentinelOne.Agent.IsActive boolean Whether the agent is active.
SentinelOne.Agent.LastActiveDate date When was the agent last active.
SentinelOne.Agent.RegisteredAt date The registration date of the agent.
SentinelOne.Agent.ExternalIP string The agent IP address.
SentinelOne.Agent.ThreatCount number Number of active threats.
SentinelOne.Agent.EncryptedApplications boolean Whether disk encryption is enabled.
SentinelOne.Agent.OSName string Name of the operating system.
SentinelOne.Agent.ComputerName string Name of the agent computer.
SentinelOne.Agent.MachineType string Machine type.
SentinelOne.Agent.Domain string Domain name of the agent.
SentinelOne.Agent.CreatedAt date Agent creation time.
SentinelOne.Agent.SiteName string Site name associated with the agent.

sentinelone-get-sites


Returns all sites that match the specified criteria.

Base Command

sentinelone-get-sites

Input

Argument Name Description Required
updated_at Timestamp of the last update, for example: “2018-02-27T04:49:26.257525Z”. Optional
query Full-text search for fields: name, account_name. Optional
site_type Site type. Possible values are: Trial, Paid, POC, DEV, NFR. Optional
features Returns sites that support the specified features. Possible values are: firewall-control, device-control, ioc. Optional
state Site state. Possible values are: active, deleted, expired. Optional
suite The suite of product features active for this site. Possible values are: Core, Complete. Optional
admin_only Sites for which the user has admin privileges. Possible values are: true, false. Optional
account_id Account ID, for example: “225494730938493804”. Optional
site_name Site name, for example: “My Site”. Optional
created_at Timestamp of the site creation, for example: “2018-02-27T04:49:26.257525Z”. Optional
limit Maximum number of results to return. Default is 50. Optional

Context Output

Path Type Description
SentinelOne.Site.Creator string The site creator name.
SentinelOne.Site.Name string The site name.
SentinelOne.Site.Type string The site type.
SentinelOne.Site.AccountName string The site account name.
SentinelOne.Site.State string The site state.
SentinelOne.Site.HealthStatus boolean The health status of the site.
SentinelOne.Site.Suite string The suite to which the site belongs.
SentinelOne.Site.ActiveLicenses number Number of active licenses for the site.
SentinelOne.Site.ID string ID of the site.
SentinelOne.Site.TotalLicenses number Number of total licenses for the site.
SentinelOne.Site.CreatedAt date Timestamp when the site was created.
SentinelOne.Site.Expiration string Timestamp when the site will expire.
SentinelOne.Site.UnlimitedLicenses boolean Whether the site has unlimited licenses.

sentinelone-get-site


Returns information about the site, according to the site ID.

Base Command

sentinelone-get-site

Input

Argument Name Description Required
site_id ID of the site. Required

Context Output

Path Type Description
SentinelOne.Site.Creator string The site creator name.
SentinelOne.Site.Name string The site name.
SentinelOne.Site.Type string The site type.
SentinelOne.Site.AccountName string The site account name.
SentinelOne.Site.State string The site state.
SentinelOne.Site.HealthStatus boolean The health status of the site.
SentinelOne.Site.Suite string The suite to which the site belongs.
SentinelOne.Site.ActiveLicenses number Number of active licenses for the site.
SentinelOne.Site.ID string ID of the site.
SentinelOne.Site.TotalLicenses number Number of total licenses for the site.
SentinelOne.Site.CreatedAt date Timestamp when the site was created.
SentinelOne.Site.Expiration string Timestamp when the site will expire.
SentinelOne.Site.UnlimitedLicenses boolean Whether the site has unlimited licenses.
SentinelOne.Site.AccountID string Site account ID.
SentinelOne.Site.IsDefault boolean Whether the site is the default site.

sentinelone-reactivate-site


Reactivates an expired site.

Base Command

sentinelone-reactivate-site

Input

Argument Name Description Required
site_id Site ID. For example: “225494730938493804”. Required
unlimited If false, an expiration should be supplied. Optional
expiration Expiration date in case unlimited is false, for example, “2019-08-03T04:49:26.257525Z”. Optional

Context Output

Path Type Description
SentinelOne.Site.ID string Site ID.
SentinelOne.Site.Reactivated boolean Whether the site was reactivated.

sentinelone-get-activities


Returns a list of activities.

Base Command

sentinelone-get-activities

Input

Argument Name Description Required
created_after Return activities created after this timestamp, for example: “2018-02-27T04:49:26.257525Z”. Optional
user_emails Email address of the user who invoked the activity (if applicable). Optional
group_ids List of group IDs by which to filter, for example: “225494730938493804,225494730938493915”. Optional
created_until Return activities created on or before this timestamp, for example: “2018-02-27T04:49:26.257525Z”. Optional
include_hidden Include internal activities hidden from display. Possible values are: true, false. Optional
activities_ids A comma-separated list of activity IDs by which to filter, for example: “225494730938493804,225494730938493915”. Optional
created_before Return activities created before this timestamp, for example: “2018-02-27T04:49:26.257525Z”. Optional
threats_ids A comma-separated list of threat IDs for which to return activities, for example: “225494730938493804,225494730938493915”. Optional
activity_types A comma-separated list of activity codes to return, for example: “52,53,71,72”. Optional
user_ids A comma-separated list of user IDs for users that invoked the activity (if applicable), for example: “225494730938493804,225494730938493915”. Optional
created_from Return activities created on or after this timestamp, for example: “2018-02-27T04:49:26.257525Z”. Optional
created_between Return activities created within this range (inclusive), for example: “1514978764288-1514978999999”. Optional
agent_ids Return activities related to specified agents. For example: “225494730938493804,225494730938493915”. Optional
limit Maximum number of items to return (1-100). Optional
sort_by Field to sort results by. Possible values are: activityType, createdAt, id. Optional
sort_order Order to sort by. Possible values are: asc, desc. Optional

Context Output

Path Type Description
SentinelOne.Activity.AgentID String Related agent (if applicable).
SentinelOne.Activity.AgentUpdatedVersion String Agent’s new version (if applicable).
SentinelOne.Activity.SiteID String Related site (if applicable).
SentinelOne.Activity.UserID String The user who invoked the activity (if applicable).
SentinelOne.Activity.SecondaryDescription String Secondary description.
SentinelOne.Activity.OsFamily String Agent’s operating system type (if applicable). Can be “linux”, “macos”, “windows”, or “windows_legacy”.
SentinelOne.Activity.ActivityType Number Activity type.
SentinelOne.Activity.data.SiteID String The site ID.
SentinelOne.Activity.data.SiteName String The site name.
SentinelOne.Activity.data.username String The name of the site creator.
SentinelOne.Activity.Hash String Threat file hash (if applicable).
SentinelOne.Activity.UpdatedAt Date Activity last updated time (UTC).
SentinelOne.Activity.Comments String Comments for the activity.
SentinelOne.Activity.ThreatID String Related threat (if applicable).
SentinelOne.Activity.PrimaryDescription String Primary description for the activity.
SentinelOne.Activity.GroupID String Related group (if applicable).
SentinelOne.Activity.ID String Activity ID.
SentinelOne.Activity.CreatedAt Date Activity creation time (UTC).
SentinelOne.Activity.Description String Extra activity information.

sentinelone-get-groups


Returns data for the specified group.

Base Command

sentinelone-get-groups

Input

Argument Name Description Required
group_type Group type, for example: “static”. Optional
group_ids A comma-separated list of group IDs by which to filter, for example: “225494730938493804,225494730938493915”. Optional
group_id Group ID by which to filter, for example: “225494730938493804”. Optional
is_default Whether this is the default group. Possible values are: true, false. Optional
name The name of the group. Optional
query Free-text search. Optional
rank The priority of a dynamic group over others, for example, “1”, which is the highest priority. Optional
limit Maximum number of items to return (1-200). Optional

Context Output

Path Type Description
SentinelOne.Group.siteId String The ID of the site of which this group is a member.
SentinelOne.Group.filterName String If the group is dynamic, the name of the filter which is used to associate agents.
SentinelOne.Group.creatorId String The ID of the user who created the group.
SentinelOne.Group.name String The name of the group.
SentinelOne.Group.creator String The user who created the group.
SentinelOne.Group.rank Number The rank, which sets the priority of a dynamic group over others.
SentinelOne.Group.updatedAt Date Timestamp of the last update.
SentinelOne.Group.totalAgents Number Number of agents in the group.
SentinelOne.Group.filterId String If the group is dynamic, the group ID of the filter that is used to associate agents.
SentinelOne.Group.isDefault Boolean Whether the groups is the default group of the site.
SentinelOne.Group.inherits Boolean Whether the policy is inherited from a site. “False” if the group has its own edited policy.
SentinelOne.Group.type String Group type. Can be static or dynamic
SentinelOne.Group.id String The ID of the group.
SentinelOne.Group.createdAt Date Timestamp of group creation.

sentinelone-move-agent


Moves agents to a new group.

Base Command

sentinelone-move-agent

Input

Argument Name Description Required
group_id The ID of the group to move the agent to. Required
agents_ids Agents IDs. Optional

Context Output

Path Type Description
SentinelOne.Agent.AgentsMoved Number The number of agents that were moved to another group.

sentinelone-delete-group


Deletes a group, by the group ID.

Base Command

sentinelone-delete-group

Input

Argument Name Description Required
group_id The ID of the group to delete. Required

Context Output

Path Type Description
SentinelOne.DeleteGroup.Success String The status of the command.

sentinelone-connect-agent


Connects agents to the network.

Base Command

sentinelone-connect-agent

Input

Argument Name Description Required
agent_id A comma-separated list of agent IDs to connect to the network. Run the list-agents command to get a list of agent IDs. Required

Context Output

Path Type Description
SentinelOne.Agent.AgentsAffected Number The number of affected agents.
SentinelOne.Agent.NetworkStatus String Agent network status.
SentinelOne.Agent.ID String Input agents’ IDs.

sentinelone-disconnect-agent


Disconnects agents from the network.

Base Command

sentinelone-disconnect-agent

Input

Argument Name Description Required
agent_id A comma-separated list of agent IDs to disconnect from the network. Run the list-agents command to get a list of agent IDs. Required

Context Output

Path Type Description
SentinelOne.Agent.NetworkStatus String Agent network status.
SentinelOne.Agent.ID String Input agents’ IDs.

sentinelone-broadcast-message


Broadcasts a message to all agents that match the input filters.

Base Command

sentinelone-broadcast-message

Input

Argument Name Description Required
message The message to broadcast to agents. Required
active_agent Whether to only include active agents. Default is “false”. Possible values are: true, false. Optional
group_id A comma-separated list of group IDs by which to filter the results. Optional
agent_id A comma-separated list of agent IDs by which to filter the results. Optional
domain A comma-separated of included network domains. Optional

Context Output

Path Type Description
SentinelOne.BroadcastMessage.Affected String Number of affected endpoints.

sentinelone-get-events


Returns all Deep Visibility events that match the query.

Base Command

sentinelone-get-events

Input

Argument Name Description Required
limit Maximum number of items to return (1-100). Default is 50. Optional
query_id QueryId obtained when creating a query in the sentinelone-create-query command. Example: “q1xx2xx3”. Required
cursor Cursor pointer to get next page of results from query. Optional
columns A comma-separated list of additionals fields to display. Optional

Context Output

Path Type Description
SentinelOne.Event.ProcessUID String Process unique identifier.
SentinelOne.Event.SHA256 String SHA256 hash of the file.
SentinelOne.Event.AgentOS String Operating system type. Can be “windows”, “linux”, “macos”, or “windows_legac”.
SentinelOne.Event.ProcessID Number The process ID.
SentinelOne.Event.User String User assigned to the event.
SentinelOne.Event.Time Date Process start time.
SentinelOne.Event.Endpoint String The agent name.
SentinelOne.Event.SiteName String Site name.
SentinelOne.Event.EventType String Event type. Can be “events”, “file”, “ip”, “url”, “dns”, “process”, “registry”, “scheduled_task”, or “logins”.
SentinelOne.Event.ProcessName String The name of the process.
SentinelOne.Event.MD5 String MD5 hash of the file.
SentinelOne.Event.SourceIP String The source ip.
SentinelOne.Event.SourcePort String The source port.
SentinelOne.Event.DestinationIP String The destination IP.
SentinelOne.Event.DestinationPort String The destination port.
SentinelOne.Event.SourceProcessUser String The source process user.
SentinelOne.Event.SourceProcessCommandLine String The source process command line.
SentinelOne.Event.DNSRequest String The DNS Request.
SentinelOne.Event.FileFullName String The file full name.
SentinelOne.Event.EventTime String The event time.
Event.ID String Event process ID.
Event.Name String Event name.
Event.Type String Event type.
SentinelOne.Cursor.Event String cursor to recieve next page

sentinelone-create-query


Runs a Deep Visibility query and returns the queryId. You can use the queryId for all other commands, such as the sentinelone-get-events command.

Base Command

sentinelone-create-query

Input

Argument Name Description Required
query The query string for which to return events. Required
from_date Query start date, for example, “2019-08-03T04:49:26.257525Z”. Limited to 93 days ago. Required
to_date Query end date, for example, “2019-08-03T04:49:26.257525Z”. Required

Context Output

Path Type Description
SentinelOne.Query.FromDate Date Query start date.
SentinelOne.Query.Query String The search query string.
SentinelOne.Query.QueryID String The query ID.
SentinelOne.Query.ToDate Date Query end date.

sentinelone-get-processes


Returns a list of Deep Visibility events from query by event type - process.

Base Command

sentinelone-get-processes

Input

Argument Name Description Required
query_id The queryId that is returned when creating a query under Create Query. Example: “q1xx2xx3”. Get the query_id from the “get-query-id” command. Required
limit Maximum number of items to return (1-100). Default is 50. Optional

Context Output

Path Type Description
SentinelOne.Event.ParentProcessID Number Parent process ID.
SentinelOne.Event.ProcessUID String The process unique identifier.
SentinelOne.Event.SHA1 String SHA1 hash of the process image.
SentinelOne.Event.SubsystemType String Process sub-system.
SentinelOne.Event.ParentProcessStartTime Date The parent process start time.
SentinelOne.Event.ProcessID Number The process ID.
SentinelOne.Event.ParentProcessUID String Parent process unique identifier.
SentinelOne.Event.User String User assigned to the event.
SentinelOne.Event.Time Date Start time of the process.
SentinelOne.Event.ParentProcessName String Parent process name.
SentinelOne.Event.SiteName String Site name.
SentinelOne.Event.EventType String The event type.
SentinelOne.Event.Endpoint String The agent name (endpoint).
SentinelOne.Event.IntegrityLevel String Process integrity level.
SentinelOne.Event.CMD String Process CMD.
SentinelOne.Event.ProcessName String Process name.
SentinelOne.Event.ProcessDisplayName String Process display name.

sentinelone-shutdown-agent


Sends a shutdown command to all agents that match the input filter.

Base Command

sentinelone-shutdown-agent

Input

Argument Name Description Required
query A free-text search term that will match applicable attributes (sub-string match). Note: A device’s physical addresses will only be matched if they start with the search term (not if they contain the search term). Optional
agent_id A comma-separated list of agents IDs to shutdown. Optional
group_id The ID of the network group. Optional

Context Output

Path Type Description
SentinelOne.Agent.ID String The ID of the agent that was shutdown.

sentinelone-uninstall-agent


Sends an uninstall command to all agents that match the input filter.

Base Command

sentinelone-uninstall-agent

Input

Argument Name Description Required
query A free-text search term that will match applicable attributes (sub-string match). Note: A device’s physical addresses will only be matched if they start with the search term (not if they contain the search term). Optional
agent_id A comma-separated list of agents IDs to shutdown. Optional
group_id The ID of the network group. Optional

Context Output

Path Type Description
SentinelOne.uninstall.Affected String Number of affected agents.

sentinelone-update-threats-verdict


Updates the analyst verdict to a group of threats that match the specified input filter. Relevant for API version 2.1.

Base Command

sentinelone-update-threats-verdict

Input

Argument Name Description Required
verdict Analyst verdict action. Possible values are: undefined, true_positive, false_positive, suspicious. Required
threat_ids A comma-separated list of threat IDs. Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.Updated Boolean Whether the threat was successfully updated in the analyst verdict.
SentinelOne.Threat.Update.Action String Name of the analyst verdict action performed on the threats.

sentinelone-update-alerts-verdict


Updates the analyst verdict to a group of alerts that match the specified input filter. Relevant for API version 2.1.

Base Command

sentinelone-update-alerts-verdict

Input

Argument Name Description Required
verdict Analyst verdict action. Possible values are: undefined, true_positive, false_positive, suspicious. Required
alert_ids A comma-separated list of alert IDs. Required

Context Output

Path Type Description
SentinelOne.Alert.ID String The alert ID.
SentinelOne.Alert.Updated Boolean Whether the alert was successfully updated in the analyst verdict.
SentinelOne.Alert.Update.Action String Name of the analyst verdict action performed on the alerts.

sentinelone-create-star-rule


Creates a custom STAR rule. Relevant for API version 2.1.

Base Command

sentinelone-create-star-rule

Input

Argument Name Description Required
name The name of the STAR rule. Required
rule_severity The rule severity. Possible values are: Low, Medium, High, Critical. Required
expiration_mode Type of expiration mode. Possible values are: Permanent, Temporary. Required
query_type Type of the query. For now it’s “events”. Possible values are: events, processes. Required
query The query string for which to return events. Required
description The description of the STAR rule. Optional
expiration_date If expiration mode is “Temporary” then it should be supplied, for example, “2019-08-03T04:49:26.257525Z” . Optional
site_ids A comma-separated list of site IDs. Optional
group_ids A comma-separated list of Group IDs. Optional
account_ids A comma-separated list of Account IDs. Optional
network_quarantine Whether to enable the network quarantine of the STAR rule. Possible values are: true, false. Required
treatAsThreat The treatAsThreat type. Possible values are: Malicious, Suspicious, UNDEFINED. Required
query_lang The query language version. Supported values are “1.0” and “2.0”. Optional

Context Output

Path Type Description
SentinelOne.StarRule.ID String The STAR rule ID.
SentinelOne.StarRule.Name String The STAR rule name.
SentinelOne.StarRule.Status String The status of the STAR rule.
SentinelOne.StarRule.Severity String The severity of the STAR rule.
SentinelOne.StarRule.Description String The description of the STAR rule.
SentinelOne.StarRule.NetworkQuarantine Boolean The network quarantine of the STAR rule.
SentinelOne.StarRule.TreatAsThreat String The Treat As Threat of the STAR rule.
SentinelOne.StarRule.ExpirationMode String The expiration mode of the STAR rule.
SentinelOne.StarRule.ExpirationDate String The expiration date of the STAR rule.
SentinelOne.StarRule.ScopeHierarchy String The scope hierarchy of the STAR rule.
SentinelOne.StarRule.CreatedAt String The created time for the STAR rule.
SentinelOne.StarRule.UpdatedAt String The updated time for the STAR rule.
SentinelOne.StarRule.QueryLanguage String The Query language for the STAR rule.

sentinelone-get-star-rules


Get a list of custom detection rules for a given scope. Relevant for API version 2.1.

Base Command

sentinelone-get-star-rules

Input

Argument Name Description Required
status A comma-separated list of the status of the STAR rule. Available options are: “Activating, Active, Deleted, Deleting, Disabled, Disabling and Draft”.Example: “Draft,Active”. Optional
creator_contains Free-text filter by rule creator (supports multiple values). Example: “Service Pack 1”. Optional
queryType Return rules with the filtered type. Example: “events”. Possible values are: events, processes. Optional
query Free-text filter by S1 query (supports multiple values). Example: “Service Pack 1”. Optional
description_contains Free-text filter by rule description (supports multiple values). Example: “Service Pack 1”. Optional
ruleIds A comma-separated list of Rules IDs. Example: “225494730938493804,225494730938493915”. Optional
name_contains Free-text filter by rule name (supports multiple values). Example: “Service Pack 1”. Optional
accountIds A comma-separated list of Account IDs to filter by. Example: “225494730938493804,225494730938493915”. Optional
expirationMode Return rules with the filtered expiration mode. Example: “Permanent”. Possible values are: Temporary, Permanent. Optional
limit Limit number of returned items (1-1000). Example: “10”. Optional
siteIds A comma-separated list of site IDs to filter by. Example: “225494730938493804,225494730938493915”. Optional

Context Output

Path Type Description
SentinelOne.StarRule.ID Number The STAR rule ID.
SentinelOne.StarRule.Creator string The STAR rule creator.
SentinelOne.StarRule.Name string The STAR rule name.
SentinelOne.StarRule.Status string The STAR rule status.
SentinelOne.StarRule.Severity string The STAR rule severity.
SentinelOne.StarRule.GeneratedAlerts Number The number of STAR rule generated alerts.
SentinelOne.StarRule.Description string The STAR rule description.
SentinelOne.StarRule.StatusReason string The STAR rule status reason.
SentinelOne.StarRule.ExpirationMode string The STAR rule expiration mode.
SentinelOne.StarRule.ExpirationDate Date The STAR rule expiration date.
SentinelOne.StarRule.Expired Boolean Whether the STAR rule expired.

sentinelone-update-star-rule


Updates a custom STAR rule. Relevant for API version 2.1.

Base Command

sentinelone-update-star-rule

Input

Argument Name Description Required
rule_id Rule ID Example: “225494730938493804”. Required
name The name of the STAR rule. Required
rule_severity The rule severity. Possible values are: Low, Medium, High, Critical. Required
expiration_mode Type of expiration mode. Possible values are: Permanent, Temporary. Required
query_type Type of the query. For now it’s “events”. Possible values are: events, processes. Required
query The query string for which to return events. Required
description The description of the STAR rule. Optional
expiration_date If expiration mode is “Temporary” then it should be supplied, for example, “2019-08-03T04:49:26.257525Z”. Optional
site_ids A comma-separated list of site IDs. Optional
group_ids A comma-separated list of group IDs. Optional
account_ids A comma-separated list of account IDs. Optional
network_quarantine Whether to enable the network quarantine of the STAR rule. Possible values are: true, false. Required
treatAsThreat The treatAsThreat. Possible values are: Malicious, Suspicious, UNDEFINED. Required
query_lang The query language version. Supported values are “1.0” and “2.0”. Optional

Context Output

Path Type Description
SentinelOne.StarRule.ID String The STAR rule ID.
SentinelOne.StarRule.Name String The STAR rule name.
SentinelOne.StarRule.Status String The status of the STAR rule.
SentinelOne.StarRule.Severity String The severity of the STAR rule.
SentinelOne.StarRule.Description String The description of the STAR rule.
SentinelOne.StarRule.NetworkQuarantine Boolean The network quarantine of the STAR rule.
SentinelOne.StarRule.TreatAsThreat String The Treat As Threat of the STAR rule.
SentinelOne.StarRule.ExpirationMode String The expiration mode of the STAR rule.
SentinelOne.StarRule.ExpirationDate String The expiration date of the STAR rule.
SentinelOne.StarRule.ScopeHierarchy String The scope hierarchy of the STAR rule.
SentinelOne.StarRule.CreatedAt String The created time for the STAR rule.
SentinelOne.StarRule.UpdatedAt String The updated time for the STAR rule.
SentinelOne.StarRule.QueryLanguage String The Query language for the STAR rule.

sentinelone-enable-star-rules


Activate Custom Detection rules that match the specified input filter. Relevant for API version 2.1.

Base Command

sentinelone-enable-star-rules

Input

Argument Name Description Required
rule_ids A comma-separated list of STAR rule IDs. Required

Context Output

Path Type Description
SentinelOne.StarRule.ID String The Rule ID.
SentinelOne.StarRule.Enabled Boolean Whether the STAR rule was successfully enabled.

sentinelone-disable-star-rules


Disable Custom Detection rules that match the specified input filter. Relevant for API version 2.1.

Base Command

sentinelone-disable-star-rules

Input

Argument Name Description Required
rule_ids A comma-separated list of STAR rule IDs. Required

Context Output

Path Type Description
SentinelOne.StarRule.ID String The Rule ID.
SentinelOne.StarRule.Disabled Boolean Whether the STAR rule was successfully disabled.

sentinelone-delete-star-rule


Deletes Custom Detection Rules that match the specified input filter. Relevant for API version 2.1.

Base Command

sentinelone-delete-star-rule

Input

Argument Name Description Required
rule_ids A comma-separated list of STAR rule IDs. Required

Context Output

Path Type Description
SentinelOne.StarRule.ID String The Rule ID.
SentinelOne.StarRule.Deleted Boolean Whether the STAR rule was successfully deleted.

sentinelone-get-blocklist


Retrieve the blocklist (“blacklist” in SentinelOne documentation). You can filter by SHA1 or SHA256 hash. If the global flag is true, then group_ids, site_ids, and account_ids are ignored.

Base Command

sentinelone-get-blocklist

Input

Argument Name Description Required
global Whether the global list is accessible. (Same as tenant flag in API docs.). Possible values are: true, false. Default is true. Optional
group_ids Comma-separated list of group IDs to filter by. Optional
site_ids Comma-separated list of site IDs to filter by. Optional
account_ids Comma-separated list of account IDs to filter by. Optional
offset The number of records to skip (for paging). Default is 0. Optional
limit The maximum number of records to return. Default is 1000. Optional
hash Hash to search for in the blocklist. Optional
sha1 SHA1 hash to search for in the blocklist. Optional
sha256Value SHA256 hash to search for in the blocklist. Optional

Context Output

Path Type Description
SentinelOne.Blocklist.UserId String User ID.
SentinelOne.Blocklist.UpdatedAt String When entry was most recently updated.
SentinelOne.Blocklist.Value String File hash.
SentinelOne.Blocklist.ScopePath String SentinelOne list scope.
SentinelOne.Blocklist.Type String Block list type.
SentinelOne.Blocklist.Source String Source of entry.
SentinelOne.Blocklist.ID String Entry ID.
SentinelOne.Blocklist.CreatedAt String Date entry was created.
SentinelOne.Blocklist.Description String Description of the blocklist.
SentinelOne.Blocklist.OSType String Operating system type block is enforced on.
SentinelOne.Blocklist.ScopeName String Name of the blocklist scope.

sentinelone-add-hash-to-blocklist


Add a hash to the blocklist in SentinelOne.
If a scope is provided (site, account, or group), the hash will be added to that specific scope.
If no scope is provided, the hash will be added to the global blocklist.

Base Command

sentinelone-add-hash-to-blocklist

Input

Argument Name Description Required
sha1 SHA1 hash to add to the blocklist. Optional
sha256Value SHA256 hash to add to the blocklist. Optional
source String describing the source of the block. Default is XSOAR. Optional
os_type Type of operating system. Possible values are: windows, linux, macos. Required
description Note stored in SentinelOne about the block. Default is Blocked from XSOAR. Optional
site_ids Comma-separated string of site IDs to add the hash to. Optional
account_ids Comma-separated string of account IDs to add the hash to. Optional
group_ids Comma-separated string of group IDs to add the hash to. Optional

Context Output

Path Type Description
SentinelOne.AddHashToBlocklist.hash unknown Hash of the file.
SentinelOne.AddHashToBlocklist.status unknown Status of the action to add a hash to the blocklist.

sentinelone-remove-hash-from-blocklist


Remove a hash from the blocklist in SentinelOne.
If a scope is provided (site, account, or group), the hash will be removed from that specific scope.
If no scope is provided, the hash will be removed from the global blocklist.

Base Command

sentinelone-remove-hash-from-blocklist

Input

Argument Name Description Required
sha1 SHA1 hash to remove from the blocklist. Optional
sha256Value SHA256 hash to remove from the blocklist. Optional
os_type Optional operating system type. If not supplied, will remove the SHA1 hash across all platforms. Possible values are: windows, macos, linux. Optional
site_ids Comma-separated string of site IDs to remove the hash from. Optional
account_ids Comma-separated string of account IDs to remove the hash from. Optional
group_ids Comma-separated string of group IDs to remove the hash from. Optional

Context Output

Path Type Description
SentinelOne.RemoveHashFromBlocklist.hash unknown Hash of the file.
SentinelOne.RemoveHashFromBlocklist.status unknown Status of the action to remove a hash from the blocklist.

sentinelone-fetch-file


Invokes a fetch files command against an agent endpoint.

Base Command

sentinelone-fetch-file

Input

Argument Name Description Required
agent_id Agent ID to retrieve the file from. Required
file_path File path to download the file from. Required
password Password to protect the zip file with. Required

Context Output

There is no context output for this command.

sentinelone-download-fetched-file


Download a file fetched using th sentinelone-fetch-file command to submit the request and the sentinelone-get-activities command to get the download path.

Base Command

sentinelone-download-fetched-file

Input

Argument Name Description Required
agent_id SentinelOne agent ID. Default is Agent ID. Required
activity_id Activity ID in the get-activities command. Required
password Password used in the sentinelone-fetch-file command. Required

Context Output

There is no context output for this command.

sentinelone-write-threat-note


Add a threat note to one or more threats. Relevant for API version 2.1.

Base Command

sentinelone-write-threat-note

Input

Argument Name Description Required
threat_ids A comma-separated list of threat IDs. Required
note Threat Note Text. Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.Note String The threat note.
SentinelOne.Threat.Status String Whether the note was added successfully.

sentinelone-create-ioc


Add an IoC to the Threat Intelligence database. Relevant for API version 2.1.

Base Command

sentinelone-create-ioc

Input

Argument Name Description Required
name Threat Intelligence indicator name. Required
source The source of the identified Threat Intelligence indicator. Required
type The type of the Threat Intelligence indicator. Possible values are: DNS, IPV4, IPV6, MD5, SHA1, SHA256, URL. Required
method The comparison method used by SentinelOne to trigger the event. Possible values are: EQUALS. Required
validUntil Expiration date for the Threat Intelligence indicator. Required
value The value of the Threat Intelligence indicator. Required
account_ids List of account IDs to filter by. Required
externalId The unique identifier of the indicator as provided by the Threat Intelligence source. Optional
description Description of the Threat Intelligence indicator. Optional

Context Output

Path Type Description
SentinelOne.IOC.UUID String The IOC UUID.
SentinelOne.IOC.Name String Threat Intelligence indicator name.
SentinelOne.IOC.Source String The source of the identified Threat Intelligence indicator.
SentinelOne.IOC.Type String The type of the Threat Intelligence indicator.
SentinelOne.IOC.BatchId String The IOC batch ID.
SentinelOne.IOC.Creator String The IOC creator.
SentinelOne.IOC.Scope String The IOC scope.
SentinelOne.IOC.ScopeId String The IOC scope ID.
SentinelOne.IOC.ValidUntil String Expiration date for the Threat Intelligence indicator.
SentinelOne.IOC.Description String Description of the Threat Intelligence indicator.
SentinelOne.IOC.ExternalId String The unique identifier of the indicator as provided by the Threat Intelligence source.

sentinelone-delete-ioc


Delete an IOC from the Threat Intelligence database that matches a filter. Relevant for API version 2.1.

Base Command

sentinelone-delete-ioc

Input

Argument Name Description Required
account_ids List of account IDs to filter by. Required
uuids UUID of Threat Intelligence indicator. Required

Context Output

Path Type Description
SentinelOne.IOC.UUID String The IOC UUID.
SentinelOne.IOC.Deleted Boolean Whether the Threat Intelligence indicator was deleted.

sentinelone-get-iocs


Get the IOCs of a specified account that match the filter. Relevant for API version 2.1.

Base Command

sentinelone-get-iocs

Input

Argument Name Description Required
account_ids List of account IDs to filter by. Required
limit Limit number of returned items (1-1000). Default is 1000. Optional
upload_time_gte The time (greater than or equal to) at which the Threat Intelligence indicator was uploaded to the SentinelOne database. Example: “2022-07-13T20:33:29.007906Z”. Optional
upload_time_lte The time (less than or equal to) at which the Threat Intelligence indicator was uploaded to the SentinelOne database. Example: “2022-07-13T20:33:29.007906Z”. Optional
cursor Cursor position returned by the last request. Should be used for iterating over more than 1000 items. Example: “YWdlbnRfaWQ6NTgwMjkzODE=”. Optional
uuids A list of unique IDs of the parent process of the indicator of compromise. Example: “2cffae871197f20d864fe8363eee6651”. Optional
type The type of the Threat Intelligence indicator. Possible values are: DNS, IPV4, IPV6, MD5, SHA1, SHA256, URL. Optional
batch_id Unique ID of the uploaded indicators batch. Example: “atmtn000000028a881bcf939dc6d92ab55443”. Optional
source List of the sources of the identified Threat Intelligence indicator. Example: “AlienVault”. Optional
value The value of the Threat Intelligence indicator. Example: “175.0.x.x”. Optional
external_id The unique identifier of the indicator as provided by the Threat Intelligence source. Example: “e277603e-1060-5ad4-9937-c26c97f1ca68”. Optional
name_contains A comma-separated list of free-text filtered by the indicator name. Example: “foo.dll”. Optional
creator_contains A comma-separated list of free-text filtered by the user who uploaded the Threat Intelligence indicator. Example: “admin@sentinelone.com”. Optional
description_contains A comma-separated list of free-text filtered by the description of the indicator. Example: “Malicious-activity”. Optional
category_in The categories of the Threat Intelligence indicator. Example: The malware type associated with the IOC. Optional
updated_at_gte The time (greater or equal to) at which the indicator was last updated in the SentinelOne database. Example: “2021-07-13T20:33:29.007906Z”. Optional
updated_at_lte The time (less than or equal to) at which the indicator was last updated in the SentinelOne database. Example: “2021-07-13T20:33:29.007906Z”. Optional
creation_time_gte Creation time (greater than or equal to) as set by the user. Example: “2021-07-13T20:33:29.007906Z”. Optional
creation_time_lte Creation time (less than or equal to) as set by the user. Example: “2021-07-13T20:33:29.007906Z”. Optional

Context Output

Path Type Description
SentinelOne.IOC.UUID String The IOC UUID.
SentinelOne.IOC.Creator String Threat Intelligence indicator creator.
SentinelOne.IOC.Name String Threat Intelligence indicator name.
SentinelOne.IOC.Value String Threat Intelligence indicator value.
SentinelOne.IOC.Description String Threat Intelligence indicator description.
SentinelOne.IOC.Type String Threat Intelligence indicator type.
SentinelOne.IOC.ExternalId String Threat Intelligence indicator external ID.
SentinelOne.IOC.Source String Threat Intelligence indicator source.
SentinelOne.IOC.UploadTime String Threat Intelligence indicator upload time.
SentinelOne.IOC.ValidUntil String Threat Intelligence indicator expiration time.

sentinelone-create-power-query


Deprecated. Start a Deep Visibility Power query to get back status and potential results (ping afterwards using the queryId if query has not finished). Relevant for API version 2.1

Base Command

sentinelone-create-power-query

Input

Argument Name Description Required
query Events matching the query search term will be returned. Required
from_date Events created after this timestamp. Required
to_date Events created before or at this timestamp. Required
limit Limit number of returned items (1-100000). Optional

Context Output

There is no context output for this command.

sentinelone-ping-power-query


Deprecated. Ping a Deep Visibility Power query using the queryId argument if results have not returned from an initial Power query or a previous ping. Relevant for API version 2.1.

Base Command

sentinelone-ping-power-query

Input

Argument Name Description Required
queryId QueryId. Required

Context Output

There is no context output for this command.

sentinelone-update-threats-status


Updates the incident status to a group of threats that match the specified input filter. Relevant for API version 2.1.

Base Command

sentinelone-update-threats-status

Input

Argument Name Description Required
status Incident status. Possible values are: in_progress, resolved, unresolved. Required
threat_ids A comma-separated list of threat IDs. Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.Updated Boolean Whether the threat was successfully updated.
SentinelOne.Threat.Status String Name of the status performed on the threats.

sentinelone-update-alerts-status


Updates the incident status to a group of alerts that match the specified input filter. Relevant for API version 2.1.

Base Command

sentinelone-update-alerts-status

Input

Argument Name Description Required
status Incident status. Possible values are: in_progress, resolved, unresolved. Required
alert_ids A comma-separated list of alert IDs. Required

Context Output

Path Type Description
SentinelOne.Alert.ID String The alert ID.
SentinelOne.Alert.Updated Boolean Whether the alert was successfully updated.
SentinelOne.Alert.Status String The status performed on the alerts.

sentinelone-expire-site


Expire the site of the given ID

Base Command

sentinelone-expire-site

Input

Argument Name Description Required
site_id A valid site ID. Required

Context Output

Path Type Description
SentinelOne.Site.ID String The site ID.
SentinelOne.Site.Name String The site name.
SentinelOne.Site.State String The site state.
SentinelOne.Site.SKU String The SKU of product features active for this site.
SentinelOne.Site.SiteType String The site type.
SentinelOne.Site.Suite String The site suite.
SentinelOne.Site.TotalLicenses String The total licenses.
SentinelOne.Site.AccountID String The account ID.
SentinelOne.Site.Creator String Full name of the creating user.
SentinelOne.Site.CreatorID String ID of the creating user.
SentinelOne.Site.Description String Description of the site.
SentinelOne.Site.Expiration String Expiration date of the site.

sentinelone-fetch-threat-file


Fetch a file associated with the threat that matches the filter.

Base Command

sentinelone-fetch-threat-file

Input

Argument Name Description Required
threat_id Please provide the Valid Threat ID. Example: 14629133470822878. Required
password File encryption password. (At least 10 characters, three out of this list “uppercase”, “lowercase”, “digits” and “symbols” are mandatory. Maximum length is 256 characters.). Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.Downloadable Boolean Whether the file is downloadable.
SentinelOne.Threat.ZippedFile String Details of the zipped folder.

sentinelone-get-alerts


Get the list of alerts that matches the filter provided. Relevant for API version 2.1.

Base Command

sentinelone-get-alerts

Input

Argument Name Description Required
created_from Greater than or equal to the time created. Example: “2018-02-27T04:49:26.257525Z”, “10 days”, “2 hours”,”5 months”. Required
created_until Less than or equal to the time created. Example: “2018-02-27T04:49:26.257525Z”, “10 days”, “2 hours”,”5 months”. Optional
ruleName Free-text filter by rule name. Example: “rule1”. Optional
incidentStatus Incident status. Example: “IN_PROGRESS”. Optional
analystVerdict Analyst verdict. Example: “TRUE_POSITIVE”. Optional
alert_ids A comma-separated list of alert IDs. Optional
limit Limit number of returned items (1-1000). Default is 1000. Optional
site_ids A comma-separated list of site IDs to filter by. Example: “225494730938493804,225494730938493915”. Optional
cursor Cursor position returned by the last request. Should be used for iterating over more than 1000 items. Example: “YWdlbnRfaWQ6NTgwMjkzODE=”. Optional

Context Output

Path Type Description
SentinelOne.Alert.EventType String Event type.
SentinelOne.Alert.RuleName String The rule name.
SentinelOne.Alert.SrcProcUser String Source process user.
SentinelOne.Alert.SrcProcName String Source process name.
SentinelOne.Alert.SrcProcPath String Source process file path.
SentinelOne.Alert.SrcProcCommandline String The command line
SentinelOne.Alert.SrcProcSHA1 String Source process SHA1 file hash.
SentinelOne.Alert.SrcProcStartTime String PID start time.
SentinelOne.Alert.SrcProcStorylineId String Source process story line ID.
SentinelOne.Alert.SrcParentProcName String Source parent process name.
SentinelOne.Alert.SrcParentProcPath String Source parent process file path.
SentinelOne.Alert.SrcParentProcCommandline String Source parent process command line.
SentinelOne.Alert.SrcParentProcStartTime String PID start time.
SentinelOne.Alert.SrcParentProcUser String Source parent process user.
SentinelOne.Alert.SrcParentProcSHA1 String Source parent process SHA1 file hash.
SentinelOne.Alert.SrcProcSignerIdentity String Source process file signer identity.
SentinelOne.Alert.SrcParentProcSignerIdentity String Source parent process file signer identity.
SentinelOne.Alert.AlertCreatedAt String The the alert was created.
SentinelOne.Alert.AlertId String Alert ID.
SentinelOne.Alert.AnalystVerdict String Analyst verdict.
SentinelOne.Alert.IncidentStatus String Incident status
SentinelOne.Alert.EndpointName String Endpoint name
SentinelOne.Alert.AgentId String Agent ID.
SentinelOne.Alert.AgentUUID String Agent UUID.
SentinelOne.Alert.dvEventId String Deep Visibility event ID.
SentinelOne.Alert.AgentOS String Agent operating system.
SentinelOne.Alert.AgentVersion String Agent version.
SentinelOne.Alert.SiteId String Site ID.
SentinelOne.Alert.RuleId String Rule ID.

sentinelone-get-installed-applications


Get the installed applications for a specific agent.

Base Command

sentinelone-get-installed-applications

Input

Argument Name Description Required
agent_ids A comma-separated list of agent IDs. Example: 14629133470822878,14627455454652878. Required

Context Output

Path Type Description
SentinelOne.Application.Name String The application name.
SentinelOne.Application.Publisher String The publisher.
SentinelOne.Application.Size String The size of the application in bytes.
SentinelOne.Application.Version String The version of the application.
SentinelOne.Application.InstalledOn String The date the application was installed.

sentinelone-initiate-endpoint-scan


Initiate the endpoint virus scan on provided agent IDs.

Base Command

sentinelone-initiate-endpoint-scan

Input

Argument Name Description Required
agent_ids A comma-separated list of Agent IDs. Example: 14629133470822878,14627455454652878. Required

Context Output

Path Type Description
SentinelOne.Agent.AgentID String The Agent ID.
SentinelOne.Agent.Initiated Boolean Whether the scan was initiated.

sentinelone-remove-item-from-whitelist


Remove an item from the SentinelOne exclusion list

Base Command

sentinelone-remove-item-from-whitelist

Input

Argument Name Description Required
item Value of the item to be removed from the exclusion list. Required
os_type OS type. Can be “windows”, “windows_legacy”, “macos”, or “linux”. Possible values are: windows, windows_legacy, macos, linux. Optional
exclusion_type Exclusion item type. The options are: file_type, path, white_hash, certificate, or browser. Optional

Context Output

Path Type Description
SentinelOne.RemoveItemFromWhitelist.status String Status on if items were removed from whitelist or not found on whitelist.
SentinelOne.RemoveItemFromWhitelist.item String Item removed fom whitelist.

sentinelone-run-remote-script


Run a remote script that was uploaded to the SentinelOne Script Library.

Base Command

sentinelone-run-remote-script

Input

Argument Name Description Required
account_ids A comma-separated list of account IDs. Required
output_destination Output destination. Possible values: DataSetCloud/Local/None/SentinelCloud. Possible values are: DataSetCloud, Local, None, SentinelCloud. Required
task_description Task description. Required
script_id Script ID. Required
output_directory Output directory. Optional
agent_ids A comma-separated list of agent IDs on which the script should run. Required
singularity_xdr_Keyword Singularityxdr keyword. Optional
singularity_xdr_Url Singularityxdr keyword. Optional
api_key Api key. Optional
input_params Input params. Optional
password Password. Optional
script_runtime_timeout_seconds Script runtime timout in seconds for current execution. Optional
requires_approval If set to true, execution will require approval. Optional

Context Output

Path Type Description
SentinelOne.RunRemoteScript.pendingExecutionId string ID of the created pending execution. Present only if pending flag is true.
SentinelOne.RunRemoteScript.pending boolean Flag indicating if the requested script execution requires approval and is created as a pending execution.
SentinelOne.RunRemoteScript.affected number Number of entities affected by the requested operation.
SentinelOne.RunRemoteScript.parentTaskId string The parent task ID of the script execution task. Null in case of pending execution.

sentinelone-get-remote-script-task-status


Get remote script tasks using a variety of filters.

Base Command

sentinelone-get-remote-script-task-status

Input

Argument Name Description Required
account_ids A comma-separated list of account IDs. Example: ‘225494730938493804,225494730938493915’. Optional
computer_name_contains Free-text filter by agent computer name (supports multiple values). Optional
count_only If true, only total number of items will be returned, without any of the actual objects. Optional
created_at_gt Created at greater than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
created_at_gte Created at greater or equal than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
created_at_lt Created at lesser than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
created_at_lte Created at lesser or equal than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
cursor Cursor position returned by the last request. Use to iterate over more than 1000 items. Example: ‘YWdlbnRfaWQ6NTgwMjkzODE=’. Optional
description_contains Only include tasks with specific description. Optional
detailed_status_contains Only include tasks with specific detailed status. Optional
group_ids Comma-separated list of Group IDs to filter by. Example: ‘225494730938493804,225494730938493915’. Optional
ids Comma-separated list of IDs to filter by. Example: ‘225494730938493804,225494730938493915’. Optional
initiated_by_contains Only include tasks from specific initiating user. Optional
limit Limit number of returned items (1-1000). Example: ‘10’. Optional
parent_task_id Parent task ID to fetch the status by. Example: ‘225494730938493804’. Required
parent_task_id_in Comma-separated list of IDs to filter by. Optional
query A free-text search term that will match applicable attributes (sub-string match). Optional
site_ids Comma-separated list of Site IDs to filter by. Example: ‘225494730938493804,225494730938493915’. Optional
status Status of the script task. Example: ‘created’. Optional
tenant A tenant scope request. Optional
updated_at_gt Updated at greater than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
updated_at_gte Updated at greater or equal than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
updated_at_lt Updated at lesser than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
updated_at_lte Updated at lesser or equal than datetime. Example: ‘2018-02-27T04:49:26.257525Z’. Optional
uuid_contains Free-text filter by agent UUID (supports multiple values). Optional

Context Output

Path Type Description
SentinelOne.GetRemoteScript.id string ID of the task.
SentinelOne.GetRemoteScript.accountId string Account ID where this script is executed.
SentinelOne.GetRemoteScript.accountName string Account name where this script is executed.
SentinelOne.GetRemoteScript.agentId string Agent ID where this script is executed.
SentinelOne.GetRemoteScript.agentIsActive boolean The status of the agent.
SentinelOne.GetRemoteScript.agentMachineType string Agent machine type.
SentinelOne.GetRemoteScript.agentOsType string Agent operating system type.
SentinelOne.GetRemoteScript.agentUuid string Agent UUID.
SentinelOne.GetRemoteScript.createdAt string The script created at datetime.
SentinelOne.GetRemoteScript.description string The description of the remote script.
SentinelOne.GetRemoteScript.detailedStatus string The detailed status of the remote script.
SentinelOne.GetRemoteScript.groupId string Group ID where this script is executed.
SentinelOne.GetRemoteScript.groupName string Group name where this script is executed.
SentinelOne.GetRemoteScript.initiatedBy string Remote script initiate by.
SentinelOne.GetRemoteScript.initiatedById string ID of the remote script initiator.
SentinelOne.GetRemoteScript.parentTaskId string Parent task ID of the remote script.
SentinelOne.GetRemoteScript.siteId string Site ID where this script is executed.
SentinelOne.GetRemoteScript.siteName string Site name where this script is executed.
SentinelOne.GetRemoteScript.status string Status of the remote script.
SentinelOne.GetRemoteScript.statusCode string Status code of the remote script.
SentinelOne.GetRemoteScript.statusDescription string Status description of the remote script.
SentinelOne.GetRemoteScript.type string Type of remote script.
SentinelOne.GetRemoteScript.updateAt string Remote script upated at.

sentinelone-get-remote-script-task-results


Get a script’s result download URL.

Base Command

sentinelone-get-remote-script-task-results

Input

Argument Name Description Required
computer_names A comma-separated list of partial or whole computer names, which ran scripts. Optional
task_ids A comma-separated list of task IDs to get a download link for. Required

Context Output

Path Type Description
SentinelOne.RemoteScriptResults.taskId string ID of the task.
SentinelOne.RemoteScriptResults.fileName string File name.
SentinelOne.RemoteScriptResults.downloadUrl string Download URL.

sentinelone-remote-script-automate-results


Automate a remote script’s execution cycle and return the script’s results.

Base Command

sentinelone-remote-script-automate-results

Input

Argument Name Description Required
account_ids A comma-separated list of account IDs. Required
output_destination Output destination. Possible values are: DataSetCloud, Local, None, SentinelCloud. Required
task_description Task description. Required
script_id Script ID. Required
output_directory Output directory. Optional
agent_ids A comma-separated list of agent IDs on which the script should run. Required
singularity_xdr_Keyword Singularity XDR keyword. Optional
singularity_xdr_Url Singularity XDR URL. Optional
api_key API key. Optional
input_params Input parameters. Optional
password Password. Optional
script_runtime_timeout_seconds Script runtime timeout in seconds for current execution. Optional
requires_approval If set to true, execution will require approval. Optional
interval Indicates how long to wait between command execution (in seconds) when ‘polling’ argument is true. Minimum value is 10 seconds. Default is 60. Optional
timeout Indicates the time in seconds until the polling sequence timeouts. Default is 600. Optional
parent_task_id Parent task ID to fetch the status by. Example: ‘225494730938493804’. Optional

Context Output

Path Type Description
SentinelOne.RemoteScriptResults.taskId string ID of the task.
SentinelOne.RemoteScriptResults.fileName string File name.
SentinelOne.RemoteScriptResults.downloadUrl string Download URL.

sentinelone-get-power-query-results


Automate a power query and return the query results. (The maximum timeout of 300 seconds is allowed.)

Base Command

sentinelone-get-power-query-results

Input

Argument Name Description Required
account_ids A comma-separated list of account IDs. Optional
site_ids A comma-separated list of site IDs on which the query should run. Optional
query Events matching the query search term will be returned. Required
from_date Events created after this date. Example: ‘2018-02-27T04:49:26.257525Z’. Required
to_date Events created before or at this date. Example: ‘2018-02-27T04:49:26.257525Z’. Required
limit Limit number of returned items (1-100000). Optional
interval Indicates how long to wait between command execution (in seconds) when ‘polling’ argument is true. Minimum value is 10 seconds. Optional
timeout Indicates the time in seconds until the polling sequence timeouts. Optional
query_id QueryId. Example: pq3be5e2747f716cxxxxxxxxxxxxx20a0. Optional

Context Output

Path Type Description
SentinelOne.PowerQuery.ResultIndex List Result from the power query in list of objects format

get-mapping-fields


Returns the list of fields for an incident type.

Base Command

get-mapping-fields

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

There is no context output for this command.

update-remote-system


Pushes local changes to the remote system.

Base Command

update-remote-system

Input

| Argument Name | Description | Required |
| — | — | — |

Context Output

There is no context output for this command.

get-remote-data


Get remote data from a remote incident. This method does not update the current incident, and should be used for debugging purposes.

Base Command

get-remote-data

Input

Argument Name Description Required
id The ticket ID. Required
lastUpdate Retrieve entries that were created after lastUpdate. Required

Context Output

There is no context output for this command.

get-modified-remote-data


Gets the list of incidents that were modified since the last update time. Note that this method is here for debugging purposes. The get-modified-remote-data command is used as part of a Mirroring feature, which is available from version 6.1.

Base Command

get-modified-remote-data

Input

Argument Name Description Required
last_update Retrieve entries that were created after lastUpdate. Optional

Context Output

There is no context output for this command.

sentinelone-get-dv-query-status


Returns status of a Deep Visibility Query

Base Command

sentinelone-get-dv-query-status

Input

Argument Name Description Required
query_id The queryId that is returned when creating a query under Create Query. Example: “q1xx2xx3”. Get the query_id from the “get-query-id” command. Required

Context Output

Path Type Description
SentinelOne.Query.Status.progressStatus string Progress Query Status
SentinelOne.Query.Status.queryModeInfo.lastActivatedAt string Last Activated At
SentinelOne.Query.Status.queryModeInfo.mode string Query Mode
SentinelOne.Query.Status.responseState string State of the Query
SentinelOne.Query.Status.warnings string Warnings during Query
SentinelOne.Query.Status.QueryId string QueryID From Request

sentinelone-get-agent-mac


Returns network interface details for a given Agent ID. This includes MAC address details and interface description.

Base Command

sentinelone-get-agent-mac

Input

Argument Name Description Required
agent_id AgentId of the System. Required

Context Output

Path Type Description
SentinelOne.MAC string Agent network interface details.
SentinelOne.MAC.agent_id string AgentID
SentinelOne.MAC.hostname string Hostname
SentinelOne.MAC.int_name string Interface Name
SentinelOne.MAC.ip string IP Address
SentinelOne.MAC.mac string MAC Address

sentinelone-get-accounts


Returns details of accounts.

Base Command

sentinelone-get-accounts

Input

Argument Name Description Required
account_id Can filter on one account ID. Otherwise, it returns information from all accounts. Optional

Context Output

Path Type Description
SentinelOne.Accounts.AccountType string The account type.
SentinelOne.Accounts.ActiveAgents number The account number of active agents.
SentinelOne.Accounts.NumberOfSites number The account number of sites.
SentinelOne.Accounts.State string The account state.
SentinelOne.Accounts.CreatedAt string The account creation date.
SentinelOne.Accounts.Expiration string The account expiration date.
SentinelOne.Accounts.ID string The account ID.
SentinelOne.Accounts.Name string The account name.

sentinelone-get-threat-notes


Returns threat notes.

Base Command

sentinelone-get-threat-notes

Input

Argument Name Description Required
threat_id The ID of the threat. Required

Context Output

Path Type Description
SentinelOne.Notes.CreatedAt string The note creation date.
SentinelOne.Notes.Creator string The note creator.
SentinelOne.Notes.CreatorID string The note creator ID.
SentinelOne.Notes.Edited boolean Whether the note was edited or not..
SentinelOne.Notes.ID string The note ID.
SentinelOne.Notes.Text string The note text.
SentinelOne.Notes.UpdatedAt string The note updated time.

sentinelone-list-installed-singularity-marketplace-applications


Returns all installed singularity marketplace applications that match the specified filter values.

Base Command

sentinelone-list-installed-singularity-marketplace-applications

Input

Argument Name Description Required
account_ids A comma-separated list of account IDs. Optional
application_catalog_id Filter results by application catalog id. Optional
creator_contains Free-text filter by application creator. Optional
ids A comma-separated list of applications IDs. Optional
name_contains Free-text filter by application name Optional
site_ids A comma-separated list of site IDs. Optional

Context Output

Path Type Description
SentinelOne.InstalledApps.ID string The application ID.
SentinelOne.InstalledApps.Account string The account name.
SentinelOne.InstalledApps.AccountId string The account ID.
SentinelOne.InstalledApps.ApplicationCatalogId string The application Catalog ID.
SentinelOne.InstalledApps.ApplicationCatalogName string The application Catalog name.
SentinelOne.InstalledApps.AlertMessage string The alert message.
SentinelOne.InstalledApps.CreatedAt date Application created at.
SentinelOne.InstalledApps.Creator string Application creator.
SentinelOne.InstalledApps.CreatorId string Application creator ID.
SentinelOne.InstalledApps.DesiredStatus string Application desired status.
SentinelOne.InstalledApps.HasAlert boolean Application has alert.
SentinelOne.InstalledApps.LastEntityCreatedAt date Application last entity created at.
SentinelOne.InstalledApps.Modifier string Modifier.
SentinelOne.InstalledApps.ModifierId string Modifier ID.
SentinelOne.InstalledApps.ScopeId string The scope ID.
SentinelOne.InstalledApps.ScopeLevel string The scope level.
SentinelOne.InstalledApps.Status string Status of application.
SentinelOne.InstalledApps.UpdatedAt string Application updated at.
SentinelOne.InstalledApps.ApplicationInstanceName string Application instance name.

sentinelone-get-service-users


Returns all service users that match the specified filter values.

Base Command

sentinelone-get-service-users

Input

Argument Name Description Required
account_ids A comma-separated list of account IDs. Optional
role_ids A comma-separated list of rbac roles to filter by. Optional
ids A comma-separated list of service user IDs to filter by. Optional
site_ids A comma-separated list of site IDs. Optional

Context Output

Path Type Description
SentinelOne.ServiceUsers.ID string The service user ID.
SentinelOne.ServiceUsers.ApiTokenCreatedAt date Api token created at.
SentinelOne.ServiceUsers.ApiTokenExpiresAt date Api token expires at.
SentinelOne.ServiceUsers.CreatedAt date Service user created at.
SentinelOne.ServiceUsers.CreatedById string The service user created by Id.
SentinelOne.ServiceUsers.CreatedByName string The service user created by name.
SentinelOne.ServiceUsers.Description string Service user description.
SentinelOne.ServiceUsers.LastActivation date Last activation date.
SentinelOne.ServiceUsers.Name string Service user name.
SentinelOne.ServiceUsers.Scope string Service user scope.
SentinelOne.ServiceUsers.UpdatedAt date Service user updated at.
SentinelOne.ServiceUsers.UpdatedById string Service user updated by Id.
SentinelOne.ServiceUsers.UpdatedByName string Service user updated by name.
SentinelOne.ServiceUsers.ScopeRolesRoleId string Scope roles role Id.
SentinelOne.ServiceUsers.ScopeRolesRoleName string Scope roles role name.
SentinelOne.ServiceUsers.ScopeRolesAccountName string Scope roles account name.
SentinelOne.ServiceUsers.ScopeRolesId string Scope roles Id.

Incident Mirroring

You can enable incident mirroring between Cortex XSOAR incidents and SentinelOne v2 corresponding events (available from Cortex XSOAR version 6.0.0).
To set up the mirroring:

  1. Enable Fetching incidents in your instance configuration.
  2. In the Mirroring Direction integration parameter, select in which direction the incidents should be mirrored:

    Option Description
    None Turns off incident mirroring.
    Incoming Any changes in SentinelOne v2 events (mirroring incoming fields) will be reflected in Cortex XSOAR incidents.
    Outgoing Any changes in Cortex XSOAR incidents will be reflected in SentinelOne v2 events (outgoing mirrored fields).
    Incoming And Outgoing Changes in Cortex XSOAR incidents and SentinelOne v2 events will be reflected in both directions.

Newly fetched incidents will be mirrored in the chosen direction. However, this selection does not affect existing incidents.

Important Note: To ensure the mirroring works as expected, mappers are required, both for incoming and outgoing, to map the expected fields in Cortex XSOAR and SentinelOne v2.

sentinelone-threat-download-from-cloud


Download a file associated with the threat from the Cloud (BinaryVault).

Base Command

sentinelone-threat-download-from-cloud

Input

Argument Name Description Required
threat_id Please provide the Valid Threat ID. Example: 14629133470822878. Required

Context Output

Path Type Description
SentinelOne.Threat.ID String The threat ID.
SentinelOne.Threat.Downloadable Boolean Whether the file is downloadable.
SentinelOne.Threat.ZippedFile String Details of the zipped folder.

sentinelone-create-bulk-ioc


Adds a bulk list of IoCs to the Threat Intelligence database. To use this command, the user must upload a JSON file containing a list of IoC objects(each object represents a single IoC to be created) with the required attributes specified in the sentinelone-create-ioc command. Relevant for API version 2.1.

Base Command

sentinelone-create-bulk-ioc

Input

Argument Name Description Required
entry_id Entry ID of uploaded IOCs JSON file. Required
account_ids List of account IDs to filter by. Required

Context Output

Path Type Description
SentinelOne.IOC.UUID String The IOC UUID.
SentinelOne.IOC.Name String Threat Intelligence indicator name.
SentinelOne.IOC.Source String The source of the identified Threat Intelligence indicator.
SentinelOne.IOC.Type String The type of the Threat Intelligence indicator.
SentinelOne.IOC.BatchId String The IOC batch ID.
SentinelOne.IOC.Creator String The IOC creator.
SentinelOne.IOC.Scope String The IOC scope.
SentinelOne.IOC.ScopeId String The IOC scope ID.
SentinelOne.IOC.ValidUntil String Expiration date for the Threat Intelligence indicator.
SentinelOne.IOC.Description String Description of the Threat Intelligence indicator.
SentinelOne.IOC.ExternalId String The unique identifier of the indicator as provided by the Threat Intelligence source.

sentinelone-run-powerquery


Run a PowerQuery, where you can pipe one or many search expressions into a set of commands to transform, manipulate, group, and summarize your data.

Base Command

sentinelone-run-powerquery

Input

Argument Name Description Required
singularity_xdr_url Singularity Data Lake XDR URL. Required
singularity_xdr_api_key A Log Read Access API key. Required
query The query, in PowerQuery syntax. Required
start_time Start time for your query. Optional
end_time End time for your query. Optional
priority Query execution priority (defaults to “low”). Default is low. Optional
recurring Optional (defaults to false). When set to true, a materialized view of your query is created. Possible values are: true, false. Default is false. Optional
team_emails Comma-separated list of account emails to query, enabling Cross Team Search. Optional

Context Output

Path Type Description
SentinelOne.PowerQuery.Results.status String The status of the PowerQuery execution.
SentinelOne.PowerQuery.Results.matchingEvents Number Number of events that match the query’s initial filter.
SentinelOne.PowerQuery.Results.omittedEvents Number Number of events omitted from the final result due to memory limits.
SentinelOne.PowerQuery.Results.results List A list of result rows returned by the PowerQuery, where each object represents one row (column:value pairs).

sentinelone-abort-endpoint-scan


Abort the endpoint virus scan on provided agent IDs.

Base Command

sentinelone-abort-endpoint-scan

Input

Argument Name Description Required
agent_ids A comma-separated list of Agent IDs. Example: 14629133470822878,14627455454652878. Required

Context Output

Path Type Description
SentinelOne.Agent.AgentID String The Agent ID.
SentinelOne.Agent.Aborted Boolean Whether the scan was aborted.

sentinelone-threat-analysis


Returns threat analysis. Can only be used with API V2.1.

Base Command

sentinelone-threat-analysis

Input

Argument Name Description Required
threat_id Threat ID to get the analysis, for example: “2341398296147451190”. Required

Context Output

Path Type Description
SentinelOne.Threat.AgentDetectionInfo 18 Agent detection time information.
SentinelOne.Threat.AgentRealtimeInfo 18 Agent realtime information.
SentinelOne.Threat.ThreatInfo 18 Threat information.

sentinelone-endpoint-fetch-logs


Get the Agent and Endpoint logs from Agents for provided agent IDs

Base Command

sentinelone-endpoint-fetch-logs

Input

Argument Name Description Required
agent_ids A comma-separated list of Agent IDs. Example: 14629133470822878,14627455454652878. Required
agents_logs Fetch Agent logs. Possible values are: true, false. Default is true. Required
customer_facing_logs Fetch customer-facing logs. Possible values are: true, false. Default is false. Required
platform_logs Actively fetch logs from the relevant platform (Windows, macOS, or Linux). Possible values are: true, false. Default is false. Required

Context Output

Path Type Description
SentinelOne.Agent.Affected String Number of affected endpoints.

sentinelone-update-uam-alert-verdict


Updates the analyst verdict for a group of UAM alerts. Relevant for API version 2.1.

Base Command

sentinelone-update-uam-alert-verdict

Input

Argument Name Description Required
analyst_verdict UAM alert analyst verdict. Possible values are: False positive - Benign, False positive - Benign but suspicious, False positive - System error, False positive - Undefined, False positive - User error, True positive - Advanced persistent threat, True positive - Benign, True positive - Benign but suspicious, True positive - Data exfiltration, True positive - Denial of service, True positive - Exploitation tools, True positive - Insider threat, True positive - Malware, True positive - Phishing attack, True positive - Policy violation, True positive - PUA/Adware, True positive - Ransomware, True positive - Unauthorized access, True positive - Undefined, Undefined. Required
alert_ids A comma-separated list of UAM alert IDs. Required

Context Output

Path Type Description
SentinelOne.UAMAlert.ID String The UAM alert ID.
SentinelOne.UAMAlert.Updated Boolean Whether the analyst verdict was successfully updated.
SentinelOne.UAMAlert.AnalystVerdict String Name of the analyst verdict performed on the alerts.

sentinelone-update-uam-alert-status


Updates the status for a group of UAM alerts. Relevant for API version 2.1.

Base Command

sentinelone-update-uam-alert-status

Input

Argument Name Description Required
status UAM alert status. Possible values are: New, In progress, Resolved. Required
alert_ids A comma-separated list of UAM alert IDs. Required

Context Output

Path Type Description
SentinelOne.UAMAlert.ID String The UAM alert ID.
SentinelOne.UAMAlert.Updated Boolean Whether the status was successfully updated.
SentinelOne.UAMAlert.Status String Name of the status performed on the alerts.

sentinelone-export-threat-events


Exports the threat’s events as a JSON File. Relevant for API version 2.1.

Base Command

sentinelone-export-threat-events

Input

Argument Name Description Required
threat_id Please provide the Valid Threat ID. Example: 14629133470822878. Required

Context Output

Path Type Description
SentinelOne.Export.Events.ThreatId String The threat ID.
SentinelOne.Export.Events.Filename String The name of the file containing the Events.

sentinelone-export-full-threat-timeline


Exports the threat’s full timeline as a JSON File. Relevant for API version 2.1.

Base Command

sentinelone-export-full-threat-timeline

Input

Argument Name Description Required
threat_id Please provide the Valid Threat ID. Example: 14629133470822878. Required
limit The max number of timeline entries to return, default is 5000. Optional

Context Output

Path Type Description
SentinelOne.Export.Timeline.ThreatId String The threat ID.
SentinelOne.Export.Timeline.Filename String The name of the file containing the Timeline.

Configuration parameters

  • url — Server URL (e.g., https://usea1.sentinelone.net) (required)
  • credentials
  • api_version — API Version (required)
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • fetch_type — Fetch incidents from type
  • fetch_uam_alert_type — Fetch incidents from UAM Alert type
  • fetch_time — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)
  • fetch_threat_rank — Minimum risk score for importing incidents (0-10), where 0 is low risk and 10 is high risk. Relevant for API version 2.0.
  • fetch_severity — Defines Alert severity to fetch.
  • fetch_incidentStatus — Define which Alerts should be fetched.
  • fetch_threat_incident_statuses — Define which Threats should be fetched.
  • fetch_limit — Fetch limit: The maximum number of threats or alerts to fetch
  • fetch_site_ids — Site IDs
  • block_site_ids — Block Site IDs
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • token — API Token (Deprecated)
  • incidentFetchInterval — Incidents Fetch Interval
  • mirror_direction — Incident Mirroring Direction
  • close_xsoar_incident — Close Mirrored XSOAR Incident

Commands (78)

  • get-mapping-fields

    Returns the list of fields for an incident type.

  • get-modified-remote-data

    Gets the list of incidents that were modified since the last update time. Note that this method is here for debugging purposes. The get-modified-remote-data command is used as part of a Mirroring feature, which is available from version 6.1.

  • get-remote-data

    Get remote data from a remote incident. This method does not update the current incident, and should be used for debugging purposes.

  • sentinelone-abort-endpoint-scan

    Abort the endpoint virus scan on provided agent IDs.

  • sentinelone-add-hash-to-blocklist

    Add a hash to the blocklist in SentinelOne. Supports scoping by site, group, or account. If Scope not provided, the block will be global.

  • sentinelone-agent-processes Deprecated

    Deprecated. Retrieves running processes for a specific agent.

  • sentinelone-broadcast-message

    Broadcasts a message to all agents that match the input filters.

  • sentinelone-connect-agent

    Connects agents to the network.

  • sentinelone-create-bulk-ioc

    Add bulk list of IoCs to the Threat Intelligence database. Relevant for API version 2.1.

  • sentinelone-create-ioc

    Add an IoC to the Threat Intelligence database. Relevant for API version 2.1.

  • sentinelone-create-power-query Deprecated

    Deprecated. Use ***sentinelone-get-power-query-results*** instead. Start a Deep Visibility Power query to get back status and potential results (ping afterwards using the queryId if query has not finished). Relevant for API version 2.1.

  • sentinelone-create-query

    Runs a Deep Visibility query and returns the queryId. You can use the queryId for all other commands, such as the sentinelone-get-events command.

  • sentinelone-create-star-rule

    Creates a custom STAR rule. Relevant for API version 2.1.

  • sentinelone-create-white-list-item

    Creates an exclusion item that matches the specified input filter.

  • sentinelone-delete-group

    Deletes a group, by the group ID.

  • sentinelone-delete-ioc

    Delete an IOC from the Threat Intelligence database that matches a filter. Relevant for API version 2.1.

  • sentinelone-delete-star-rule

    Deletes Custom Detection Rules that match the specified input filter. Relevant for API version 2.1.

  • sentinelone-disable-star-rules

    Disable Custom Detection rules that match the specified input filter. Relevant for API version 2.1.

  • sentinelone-disconnect-agent

    Disconnects agents from the network.

  • sentinelone-download-fetched-file

    Download a file fetched using th sentinelone-fetch-file command to submit the request and the sentinelone-get-activities command to get the download path.

  • sentinelone-enable-star-rules

    Activate Custom Detection rules that match the specified input filter. Relevant for API version 2.1.

  • sentinelone-endpoint-fetch-logs

    Get the Agent and Endpoint logs from Agents for provided agent IDs.

  • sentinelone-expire-site

    Expire the site of the given ID.

  • sentinelone-export-full-threat-timeline

    Exports the threat's full timeline as a JSON File. Relevant for API version 2.1.

  • sentinelone-export-threat-events

    Exports the threat's events as a JSON File. Relevant for API version 2.1.

  • sentinelone-fetch-file

    Invokes a fetch files command against an agent endpoint.

  • sentinelone-fetch-threat-file

    Fetch a file associated with the threat that matches the filter.

  • sentinelone-get-accounts

    Returns details of accounts.

  • sentinelone-get-activities

    Returns a list of activities.

  • sentinelone-get-agent

    Returns the details of an agent according to the agent ID.

  • sentinelone-get-agent-mac

    Returns network interface details for a given Agent ID. This includes MAC address details and interface description.

  • sentinelone-get-alerts

    Get the list of alerts that matches the filter provided. Relevant for API version 2.1.

  • sentinelone-get-blocklist

    Retrieve the blocklist ("blacklist" in SentinelOne). You can filter by SHA1 or SHA256 hash. If the `global` flag is `true`, then group_ids, site_ids, and account_ids are ignored.

  • sentinelone-get-dv-query-status

    Returns status of a Deep Visibility Query.

  • sentinelone-get-events

    Returns all Deep Visibility events that match the query.

  • sentinelone-get-groups

    Returns data for the specified group.

  • sentinelone-get-hash

    Gets the file reputation by a SHA1 hash.

  • sentinelone-get-installed-applications

    Get the installed applications for a specific agent.

  • sentinelone-get-iocs

    Get the IOCs of a specified account that match the filter. Relevant for API version 2.1.

  • sentinelone-get-power-query-results

    Automate a power query and return the query results. (The maximum timeout of 300 seconds is allowed.)

  • sentinelone-get-processes

    Returns a list of Deep Visibility events from query by event type - process.

  • sentinelone-get-remote-script-task-results

    Get a script's result download URL.

  • sentinelone-get-remote-script-task-status

    Get remote scripts tasks using a variety of filters.

  • sentinelone-get-service-users

    Returns all service users that match the specified filter values.

  • sentinelone-get-site

    Returns information about the site, according to the site ID.

  • sentinelone-get-sites

    Returns all sites that match the specified criteria.

  • sentinelone-get-star-rules

    Get a list of custom detection rules for a given scope. Relevant for API version 2.1.

  • sentinelone-get-threat-notes

    Returns threat notes.

  • sentinelone-get-threats

    Returns threats according to the specified filters.

  • sentinelone-get-white-list

    Lists all exclusion items that match the specified input filter.

  • sentinelone-initiate-endpoint-scan

    Initiate the endpoint virus scan on provided agent IDs.

  • sentinelone-list-agents

    Returns all agents that match the specified criteria.

  • sentinelone-list-installed-singularity-marketplace-applications

    Returns all installed singularity marketplace applications that match the specified filter values.

  • sentinelone-mark-as-threat

    Marks suspicious threats as threats. Can only be used with API V2.0.

  • sentinelone-mitigate-threat

    Applies a mitigation action to a group of threats that match the specified input filter.

  • sentinelone-move-agent

    Moves agents to a new group.

  • sentinelone-ping-power-query Deprecated

    Deprecated. Use ***sentinelone-get-power-query-results*** instead. Ping a Deep Visibility Power query using the queryId argument if results have not returned from an initial Power query or a previous ping. Relevant for API version 2.1.

  • sentinelone-reactivate-site

    Reactivates an expired site.

  • sentinelone-remote-script-automate-results

    Automate a remote script's execution cycle and return the script's results.

  • sentinelone-remove-hash-from-blocklist

    Remove a hash from the global blocklist in SentinelOne.

  • sentinelone-remove-item-from-whitelist

    Remove an item from the SentinelOne exclusion list.

  • sentinelone-resolve-threat

    Resolves threats using the threat ID. Can only be used with API V2.0.

  • sentinelone-run-powerquery

    Run a PowerQuery, where you can pipe one or many search expressions into a set of commands to transform, manipulate, group, and summarize your data.

  • sentinelone-run-remote-script

    Run a remote script that was uploaded to the SentinelOne Script Library.

  • sentinelone-shutdown-agent

    Sends a shutdown command to all agents that match the input filter.

  • sentinelone-threat-analysis

    Returns threat analysis. Can only be used with API V2.1.

  • sentinelone-threat-download-from-cloud

    Download a file associated with the threat from the Cloud (BinaryVault).

  • sentinelone-threat-summary

    Returns a dashboard threat summary. Can only be used with API V2.1.

  • sentinelone-uninstall-agent

    Sends an uninstall command to all agents that match the input filter.

  • sentinelone-update-alerts-status

    Updates the incident status to a group of alerts that match the specified input filter. Relevant for API version 2.1.

  • sentinelone-update-alerts-verdict

    Updates the analyst verdict to a group of alerts that match the specified input filter. Relevant for API version 2.1.

  • sentinelone-update-star-rule

    Updates a custom STAR rule. Relevant for API version 2.1.

  • sentinelone-update-threats-status

    Updates the incident status to a group of threats that match the specified input filter. Relevant for API version 2.1.

  • sentinelone-update-threats-verdict

    Updates the analyst verdict to a group of threats that match the specified input filter. Relevant for API version 2.1.

  • sentinelone-update-uam-alert-status

    Updates the status for a group of UAM alerts. Relevant for API version 2.1.

  • sentinelone-update-uam-alert-verdict

    Updates the analyst verdict for a group of UAM alerts. Relevant for API version 2.1.

  • sentinelone-write-threat-note

    Add a threat note to one or more threats. Relevant for API version 2.1.

  • update-remote-system

    Pushes local changes to the remote system.

import json
import pytest
import demistomock as demisto
from importlib import import_module
import os
import sys
import builtins

sentinelone_v2 = import_module("SentinelOne-V2")
main = sentinelone_v2.main

UAM_ALERT_ID = "01tc8est-test-7a6f-beeb-eddummya986"
THREAT_ID = "2421825212122725692"
BASE_URL = "https://usea1.sentinelone.net"
BASE_PARAMS = {"token": "token", "url": BASE_URL, "api_version": "2.1"}
GRAPHQL_URL = f"{BASE_URL}/web/api/v2.1/unifiedalerts/graphql"
THREATS_URL = f"{BASE_URL}/web/api/v2.1/threats"


def _make_client() -> "sentinelone_v2.Client":
    """Return a minimal Client instance for unit tests."""
    return sentinelone_v2.Client(
        base_url=f"{BASE_URL}/web/api/v2.1",
        verify=False,
        proxy=False,
        headers={"Authorization": "ApiToken token"},
    )


def util_load_json(path):
    # Always resolve path relative to this test file's directory
    base = os.path.dirname(__file__)
    with open(os.path.join(base, path), encoding="utf-8") as f:
        return json.loads(f.read())


@pytest.fixture()
def demisto_mocker_2_1(mocker):
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
            "fetch_type": "Threats",
        },
    )
    mocker.patch.object(demisto, "getLastRun", return_value={"time": 1558541949000})
    mocker.patch.object(demisto, "incidents")


@pytest.fixture()
def demisto_mocker_2_0(mocker):
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.0",
            "fetch_threat_rank": "4",
            "fetch_type": "Threats",
        },
    )
    mocker.patch.object(demisto, "getLastRun", return_value={"time": 1558541949000})
    mocker.patch.object(demisto, "incidents")
    mocker.patch.object(demisto, "results")


def test_fetch_incidents__2_1(mocker, requests_mock, demisto_mocker_2_1):
    """
    When:
        fetch-incident and API version is 2.1
    Returns:
        All the threats received by the API as incidents regardless to the rank.
    """
    raw_threat_response = util_load_json("test_data/get_threats_2_1_raw_response.json")
    incidents_for_fetch = util_load_json("test_data/incidents_2_1.json")
    mocker.patch.object(demisto, "command", return_value="fetch-incidents")
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/threats", json=raw_threat_response)

    main()

    assert demisto.incidents.call_count == 1
    incidents = demisto.incidents.call_args[0][0]
    assert len(incidents) == 4
    assert incidents[0]["occurred"] == "2019-09-15T12:05:49.095889Z"
    assert incidents[1]["occurred"] == "2019-09-15T12:14:42.440985Z"
    assert incidents[2]["occurred"] == "2019-09-15T12:14:43.349807Z"
    assert incidents[3]["occurred"] == "2019-09-15T12:14:44.069617Z"
    assert incidents_for_fetch == incidents


def test_fetch_incidents__2_0(mocker, requests_mock, demisto_mocker_2_0):
    """
    When:
        fetch-incident and API version is 2.0
    Returns:
        List of incidents with rank threshold matches to the fetch_threat_rank.
    """
    raw_threat_response = util_load_json("test_data/get_threats_2_0_raw_response.json")
    incidents_for_fetch = util_load_json("test_data/incidents_2_0.json")
    mocker.patch.object(demisto, "command", return_value="fetch-incidents")
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.0/threats", json=raw_threat_response)

    main()

    assert demisto.incidents.call_count == 1
    incidents = demisto.incidents.call_args[0][0]
    assert len(incidents) == 2
    assert incidents[0]["occurred"] == "2019-09-15T12:05:49.095889Z"
    assert incidents[1]["occurred"] == "2019-09-15T12:14:42.440985Z"
    assert incidents_for_fetch == incidents


def test_get_threats_outputs():
    """
    When:
        parsing raw response from the API to XSOAR output
    Returns:
        List of threat outputs.
    """
    raw_threat_response = util_load_json("test_data/get_threats_2_1_raw_response.json")["data"]
    expected = util_load_json("test_data/threats_outputs.json")
    threats_output = list(sentinelone_v2.get_threats_outputs(raw_threat_response))
    assert expected == threats_output


def test_get_agents_outputs():
    """
    When:
        parsing raw response of agents from the API to XSOAR output
    Returns:
        List of agents.
    """
    raw_agent_response = util_load_json("test_data/agents_raw_response.json")
    expected = util_load_json("test_data/agent_outputs.json")
    agent_output = list(sentinelone_v2.get_agents_outputs(raw_agent_response))
    assert expected == agent_output


def test_fetch_uam_alerts(mocker, requests_mock):
    """
    When:
        fetch-incidents is called with fetch_uam_alert_type configured.
    Then:
        Ensure UAM alerts are fetched via GraphQL and converted to XSOAR incidents with correct mapping.
    """

    mock_graphql_response = util_load_json("test_data/uam_alerts_raw.json")
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/unifiedalerts/graphql", json=mock_graphql_response)

    # Mock demisto params to include UAM fetch settings
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_type": "None",  # Disable standard Threats/Alerts for this test
            "fetch_uam_alert_type": "all",
            "fetch_limit": "10",
        },
    )

    # Set the last run to a time before our mock incident
    mocker.patch.object(demisto, "getLastRun", return_value={"uam_time": 1735041600000})
    mocker.patch.object(demisto, "command", return_value="fetch-incidents")
    mock_incidents = mocker.patch.object(demisto, "incidents")

    main()

    assert mock_incidents.call_count == 1
    incidents = mock_incidents.call_args[0][0]

    assert len(incidents) == 1
    uam_incident = incidents[0]

    assert uam_incident["name"] == "Sentinel One UAM Alert: Malicious Activity Detected"
    assert uam_incident["occurred"] == "2025-12-23T12:24:03.059Z"
    assert uam_incident["severity"] == 4

    # Verify Custom Fields
    custom_fields = uam_incident["CustomFields"]
    assert custom_fields["sentineloneaccountid"] == "12345"
    assert custom_fields["sentinelonesitename"] == "Default site"


def test_fetch_file(mocker, requests_mock):
    """
    When:
        fetch file request submitted
    Returns
        "String that it was successfully initiated"
    """
    agent_id = 1
    requests_mock.post(f"https://usea1.sentinelone.net/web/api/v2.1/agents/{agent_id}/actions/fetch-files", json={})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-fetch-file")
    mocker.patch.object(
        demisto,
        "args",
        return_value={"agent_id": agent_id, "file_path": "/does/not/matter/for/test", "password": "doesnotmatterfortest"},
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    sentinelone_v2.return_results.assert_called_once_with(
        f"Intiated fetch-file action for /does/not/matter/for/test on Agent {agent_id}"
    )


def test_download_fetched_file(mocker, requests_mock, capfd):
    """
    When:
        request sent to retrieve a downloaded file
    Return:
        File entry of the file downloaded
    """
    agent_id = 1
    test_data_path = os.path.join(os.path.dirname(__file__), "test_data", "download_fetched_file.zip")
    with open(test_data_path, "rb") as f:
        dffzip_contents = f.read()

    requests_mock.get(f"https://usea1.sentinelone.net/web/api/v2.1/agents/{agent_id}/uploads/1", content=dffzip_contents)

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-download-fetched-file")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "agent_id": agent_id,
            "activity_id": "1",
            "password": "password",  # This matches the password of the `download_fetched_file.zip` file in test_data
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results, file_result = call[0].args[0]

    assert command_results.outputs["Path"] == "download_fetched_file/"


def test_get_blocklist(mocker, requests_mock):
    """
    When:
        Request is made to retrieve the blocklist
    Return:
        The blocklist
    """
    raw_blockist_response = util_load_json("test_data/get_blocklist.json")
    blocklist_results = util_load_json("test_data/get_blocklist_results.json")
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions?tenant=True&groupIds=group_id&siteIds=site_id"
        "&accountIds=account_id&skip=0&limit=1&sortBy=updatedAt&sortOrder=desc",
        json=raw_blockist_response,
    )

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-blocklist")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "offset": "0",
            "limit": "1",
            "group_ids": ["group_id"],
            "site_ids": ["site_id"],
            "account_ids": ["account_id"],
            "global": "true",
        },
    )

    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]

    assert command_results.outputs == blocklist_results


def test_remove_hash_from_blocklist_global(mocker, requests_mock):
    """
    When:
        A hash is removed from the blocklist globally (no scope)
    Return:
        Status that it has been removed from the blocklist
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = (
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        "?tenant=True&skip=0&limit=4&sortBy=updatedAt&sortOrder=asc&value__contains=" + sha1
    )
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist.json")
    requests_mock.get(url, json=raw_blockist_response)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1})

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_remove_hash_from_blocklist_global_sha256(mocker, requests_mock):
    """
    When:
        A SHA256 hash is removed from the blocklist globally (no scope)
    Return:
        Status that it has been removed from the blocklist
    """
    sha256 = "3a7bd3e2360a3d5bca2c7e6f3c4d7b1a2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8"
    url = (
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        "?tenant=True&skip=0&limit=4&sortBy=updatedAt&sortOrder=asc&value__contains=" + sha256
    )
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist_sha256.json")
    requests_mock.get(url, json=raw_blockist_response)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(demisto, "args", return_value={"sha256Value": sha256})

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha256
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_remove_hash_from_blocklist_multiple_site_scope(mocker, requests_mock):
    mocker.patch.object(sys, "exit")
    """
    When:
        A hash is removed from the blocklist for multiple sites
    Return:
        Status that it has been removed from the blocklist
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist.json")
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions",
        json=raw_blockist_response,
        request_headers={"Authorization": "ApiToken token"},
        complete_qs=False,  # ignore exact query string match
    )

    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(
        demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "site_ids": "2134673222384,2144637475766"}
    )

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_remove_hash_from_blocklist_single_site_scope(mocker, requests_mock):
    """
    When:
        A hash is removed from the blocklist for a specific site
    Return:
        Status that it has been removed from the blocklist
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = (
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        "?tenant=False&siteIds=2134673222384&skip=0&limit=20&osTypes=WINDOWS&sortBy=updatedAt&sortOrder=asc&value__contains="
        + sha1
    )
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist.json")
    requests_mock.get(url, json=raw_blockist_response)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "site_ids": "2134673222384"})

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_remove_hash_from_blocklist_wrong_site_scope(mocker, requests_mock):
    """
    When:
        An invalid site_id is provided, the response should indicate that the hash is not on the blocklist.
    Return:
        Status indicating the hash is not found on the blocklist.
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"

    # Construct the URL with the invalid site ID
    url = (
        f"https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        f"?tenant=False&siteIds=2134673222300&skip=0&limit=20&osTypes=WINDOWS&sortBy=updatedAt&sortOrder=asc&value__contains={sha1}"
    )

    # Simulate the hash not being on the blocklist (empty list)
    raw_blocklist_response = {"data": []}  # Empty response indicates hash not found

    # Mock the GET request to return the blocklist response indicating the hash is not on the blocklist
    requests_mock.get(url, json=raw_blocklist_response)

    # Mock the DELETE request (this should not be called as the hash is not found)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    # Mocking the 'demisto' environment as in the reference test
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "site_ids": "2134673222300"})

    # Patch the return_results function to capture the result of the command execution
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    # Run the main function which triggers the integration logic
    main()

    # Capture the call and assert the correct outputs
    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    # Assertions for the test
    assert outputs["hash"] == sha1
    assert outputs["status"] == "Not on blocklist"  # Status should indicate hash is not on blocklist

    # Ensure that the GET and DELETE requests were called
    assert len(requests_mock.request_history) == 1  # Only one GET request, no DELETE since hash is not found
    assert requests_mock.request_history[0].method == "GET"  # First request is GET

    # Ensure the GET request was called with the correct URL (with the invalid site_id)
    assert requests_mock.request_history[0].url == url


def test_remove_hash_from_blocklist_invalid_site_id(mocker, requests_mock):
    """
    When:
        An invalid site_id (e.g., site_id = 0) is provided, the response should indicate that the site_id is invalid.
    Return:
        Status indicating an invalid site_id error.
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"

    # Construct the URL with an invalid site ID (0)
    url = (
        f"https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        f"?tenant=False&siteIds=0&skip=0&limit=20&osTypes=WINDOWS&sortBy=updatedAt&sortOrder=asc&value__contains={sha1}"
    )

    # Mocking the error response from SentinelOne API for invalid siteId
    error_response = {
        "errors": [
            {
                "code": 4000010,
                "detail": "siteIds: 0: Must be greater than or equal to 100000000000000000.",
                "title": "Validation Error",
            }
        ]
    }

    # Mock the GET request to return the error response for invalid siteId
    requests_mock.get(url, json=error_response, status_code=400)

    # Mock the DELETE request (this should not be called due to the error)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    # Mocking the 'demisto' environment as in the reference test
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "site_ids": "0"})

    # Patch the return_results function to capture the result of the command execution
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    # Run the main function which triggers the integration logic
    main()

    # Capture the call and assert the correct outputs
    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    # Assertions for the test
    assert outputs["hash"] == sha1
    assert outputs["status"] == "Error: Invalid siteId - siteIds: 0: Must be greater than or equal to 100000000000000000."

    # Ensure that the GET request was called with the correct URL (with invalid site_id)
    assert len(requests_mock.request_history) == 1  # Only one GET request due to the error
    assert requests_mock.request_history[0].method == "GET"  # First request is GET

    # Ensure the GET request was called with the correct URL (with the invalid site_id)
    assert requests_mock.request_history[0].url == url


def test_remove_hash_from_blocklist_multiple_group_scope(mocker, requests_mock):
    """
    When:
        A hash is removed from the blocklist for multiple group
    Return:
        Status that it has been removed from the blocklist
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = (
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        "?tenant=False&groupIds=3327473684756,3365722136475&skip=0&limit=20&osTypes=WINDOWS&sortBy=updatedAt&sortOrder=asc&value__contains="
        + sha1
    )
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist.json")
    requests_mock.get(url, json=raw_blockist_response)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(
        demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "group_ids": "3327473684756,3365722136475"}
    )

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_remove_hash_from_blocklist_single_group_scope(mocker, requests_mock):
    """
    When:
        A hash is removed from the blocklist for a specific group
    Return:
        Status that it has been removed from the blocklist
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = (
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        "?tenant=False&groupIds=3327473684756&skip=0&limit=20&osTypes=WINDOWS&sortBy=updatedAt&sortOrder=asc&value__contains="
        + sha1
    )
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist.json")
    requests_mock.get(url, json=raw_blockist_response)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "group_ids": "3327473684756"})

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_remove_hash_from_blocklist_multiple_account_scope(mocker, requests_mock):
    """
    When:
        A hash is removed from the blocklist for multiple account
    Return:
        Status that it has been removed from the blocklist
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = (
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        "?tenant=False&accountIds=4437562837465,4467983212746&skip=0&limit=20&osTypes=WINDOWS&sortBy=updatedAt&sortOrder=asc&value__contains="
        + sha1
    )
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist.json")
    requests_mock.get(url, json=raw_blockist_response)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(
        demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "account_ids": "4437562837465,4467983212746"}
    )

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_remove_hash_from_blocklist_single_account_scope(mocker, requests_mock):
    """
    When:
        A hash is removed from the blocklist for a specific account
    Return:
        Status that it has been removed from the blocklist
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = (
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
        "?tenant=False&accountIds=4437562837465&skip=0&limit=20&osTypes=WINDOWS&sortBy=updatedAt&sortOrder=asc&value__contains="
        + sha1
    )
    raw_blockist_response = util_load_json("test_data/remove_hash_from_blocklist.json")
    requests_mock.get(url, json=raw_blockist_response)
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/restrictions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-hash-from-blocklist")
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "account_ids": "4437562837465"})

    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = mock_return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Removed 1 entries from blocklist"


def test_add_hash_to_blocklist_args_multiple_site_ids(mocker, requests_mock):
    """
    Test: args multiple site_ids
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
    requests_mock.post(url, json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
        },
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")
    args = {"sha1": sha1, "os_type": "WINDOWS", "site_ids": "2134673222384,2144637475766"}
    mocker.patch.object(demisto, "args", return_value=args)
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    request_body = requests_mock.last_request.json()
    request_filter = request_body.get("filter")

    assert sorted(request_filter.get("siteIds").split(",")) == sorted(["2134673222384", "2144637475766"])

    call = mock_return_results.call_args_list
    assert call, "return_results not called"

    result = call[0].args[0]
    outputs = result.outputs

    assert outputs["hash"] == sha1

    actual_sites = sorted(outputs["status"].replace("Added to site: ", "").replace(" blocklist", "").split(","))
    expected_sites = sorted(["2134673222384", "2144637475766"])
    assert actual_sites == expected_sites

    assert sorted(outputs["site_ids"].split(",")) == expected_sites


def test_add_hash_to_blocklist_args_single_site_id(mocker, requests_mock):
    """
    Test: arg single site_id
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
    requests_mock.post(url, json={"data": []})
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
        },
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")
    args = {"sha1": sha1, "os_type": "WINDOWS", "site_ids": "2134673222384"}
    mocker.patch.object(demisto, "args", return_value=args)
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    request_body = requests_mock.last_request.json()
    request_filter = request_body.get("filter")
    assert request_filter.get("siteIds") == "2134673222384"

    call = mock_return_results.call_args_list
    assert call, "return_results not called"

    result = call[0].args[0]
    outputs = result.outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Added to site: 2134673222384 blocklist"
    assert outputs.get("site_ids") == "2134673222384"


def test_add_hash_to_blocklist_invalid_site_id(mocker, requests_mock):
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"

    error_response = {
        "errors": [
            {
                "code": 4000010,
                "detail": "siteIds: 0: Must be greater than or equal to 100000000000000000.",
                "title": "Validation Error",
            }
        ]
    }

    # Mock the POST request to the SentinelOne API to return validation error
    requests_mock.post(
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions",
        json=error_response,
        status_code=400,
    )

    # Patch demisto.args() to provide inputs
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "site_ids": "0"})

    # Patch demisto.params() to provide integration params
    mocker.patch.object(
        demisto, "params", return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1"}
    )

    # Patch demisto.command() to simulate command name
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")

    # Patch return_results to capture the output
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    # Run main() which will call add_hash_to_blocklist internally
    main()

    # Check what was returned
    call_args = mock_return_results.call_args[0][0]
    outputs = call_args.outputs

    assert outputs["hash"] == sha1
    assert "Invalid siteId" in outputs["status"] or "Validation Error" in outputs["status"]

    # You can also assert the requests_mock history to confirm one POST call with the invalid site id
    assert len(requests_mock.request_history) == 1
    assert requests_mock.request_history[0].method == "POST"


def test_add_hash_to_blocklist_wrong_site_id(mocker, requests_mock):
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"

    error_response = {
        "errors": [
            {
                "code": 4000010,
                "detail": "Cannot find blocklist or exclusion for the requested scope [2163822726089822800]",
                "title": "Validation Error",
            }
        ]
    }

    # Mock the POST request to the SentinelOne API to return validation error
    requests_mock.post(
        "https://usea1.sentinelone.net/web/api/v2.1/restrictions",
        json=error_response,
        status_code=400,
    )

    # Patch demisto.args() to provide inputs
    mocker.patch.object(demisto, "args", return_value={"sha1": sha1, "os_type": "WINDOWS", "site_ids": "2163822726089822800"})

    # Patch demisto.params() to provide integration params
    mocker.patch.object(
        demisto, "params", return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1"}
    )

    # Patch demisto.command() to simulate command name
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")

    # Patch return_results to capture the output
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    # Run main() which will call add_hash_to_blocklist internally
    main()

    # Get what was returned by return_results()
    call_args = mock_return_results.call_args[0][0]

    outputs = call_args.outputs
    readable = call_args.readable_output

    assert outputs["hash"] == sha1
    assert "Invalid siteId" in outputs["status"] or "Validation Error" in outputs["status"]
    assert "Cannot find blocklist or exclusion for the requested scope" in readable

    # Confirm one POST call with the invalid site id was made
    assert len(requests_mock.request_history) == 1
    assert requests_mock.request_history[0].method == "POST"


def test_add_hash_to_blocklist_args_multiple_group_ids(mocker, requests_mock):
    """
    Test: args multiple group_ids
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
    requests_mock.post(url, json={"data": []})
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
        },
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")
    args = {"sha1": sha1, "os_type": "WINDOWS", "group_ids": "3327473684756,3365722136475"}
    mocker.patch.object(demisto, "args", return_value=args)
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    request_body = requests_mock.last_request.json()
    request_filter = request_body.get("filter")
    assert request_filter.get("groupIds") == "3327473684756,3365722136475"

    call = mock_return_results.call_args_list
    assert call, "return_results not called"

    result = call[0].args[0]
    outputs = result.outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Added to group: 3327473684756,3365722136475 blocklist"
    assert outputs.get("group_ids") == "3327473684756,3365722136475"


def test_add_hash_to_blocklist_args_single_group_id(mocker, requests_mock):
    """
    Test: args single group_id
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
    requests_mock.post(url, json={"data": []})
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
        },
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")
    args = {"sha1": sha1, "os_type": "WINDOWS", "group_ids": "3327473684756"}
    mocker.patch.object(demisto, "args", return_value=args)
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    request_body = requests_mock.last_request.json()
    request_filter = request_body.get("filter")
    assert request_filter.get("groupIds") == "3327473684756"

    call = mock_return_results.call_args_list
    assert call, "return_results not called"

    result = call[0].args[0]
    outputs = result.outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Added to group: 3327473684756 blocklist"
    assert outputs.get("group_ids") == "3327473684756"


def test_add_hash_to_blocklist_args_multiple_account_ids(mocker, requests_mock):
    """
    Test: args multiple account_ids
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
    requests_mock.post(url, json={"data": []})
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
        },
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")
    args = {"sha1": sha1, "os_type": "WINDOWS", "account_ids": "4437562837465,4467983212746"}
    mocker.patch.object(demisto, "args", return_value=args)
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    request_body = requests_mock.last_request.json()
    request_filter = request_body.get("filter")
    assert request_filter.get("accountIds") == "4437562837465,4467983212746"

    call = mock_return_results.call_args_list
    assert call, "return_results not called"

    result = call[0].args[0]
    outputs = result.outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Added to account: 4437562837465,4467983212746 blocklist"
    assert outputs.get("account_ids") == "4437562837465,4467983212746"


def test_add_hash_to_blocklist_args_single_account_id(mocker, requests_mock):
    """
    Test: args single account_id
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
    requests_mock.post(url, json={"data": []})
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
        },
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")
    args = {"sha1": sha1, "os_type": "WINDOWS", "account_ids": "4437562837465"}
    mocker.patch.object(demisto, "args", return_value=args)
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    request_body = requests_mock.last_request.json()
    request_filter = request_body.get("filter")
    assert request_filter.get("accountIds") == "4437562837465"

    call = mock_return_results.call_args_list
    assert call, "return_results not called"

    result = call[0].args[0]
    outputs = result.outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Added to account: 4437562837465 blocklist"
    assert outputs.get("account_ids") == "4437562837465"


def test_add_hash_to_blocklist_global_fallback(mocker, requests_mock):
    """
    Test: No args, global fallback
    """
    sha1 = "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    url = "https://usea1.sentinelone.net/web/api/v2.1/restrictions"
    requests_mock.post(url, json={"data": []})
    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "token": "token",
            "url": "https://usea1.sentinelone.net",
            "api_version": "2.1",
            "fetch_threat_rank": "4",
        },
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-add-hash-to-blocklist")
    args = {"sha1": sha1, "os_type": "WINDOWS"}
    mocker.patch.object(demisto, "args", return_value=args)
    mock_return_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    request_body = requests_mock.last_request.json()
    request_filter = request_body.get("filter")
    assert request_filter.get("tenant") is True

    call = mock_return_results.call_args_list
    assert call, "return_results not called"

    result = call[0].args[0]
    outputs = result.outputs

    assert outputs["hash"] == sha1
    assert outputs["status"] == "Added to global blocklist"


def test_remove_item_from_whitelist(mocker, requests_mock):
    """
    When:
        A hash is removed from the whitelist
    Return:
        Status that it has been removed from the whitelist
    """
    raw_whitelist_response = util_load_json("test_data/remove_item_from_whitelist.json")
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/exclusions?osTypes=windows&type=white_hash"
        "&value__contains=f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2&"
        "includeChildren=True&includeParents=True&limit=5",
        json=raw_whitelist_response,
    )
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/exclusions", json={"data": []})

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-remove-item-from-whitelist")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            # 'sha1': 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2'
            "item": "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2",
            "exclusion_type": "white_hash",
            "os_type": "windows",
        },
    )

    mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = sentinelone_v2.return_results.call_args_list
    outputs = call[0].args[0].outputs

    assert outputs["item"] == "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
    assert outputs["status"] == "Removed 1 entries from whitelist"


def test_update_threat_analyst_verdict(mocker, requests_mock):
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/threats/analyst-verdict", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-update-threats-verdict")
    mocker.patch.object(demisto, "args", return_value={"threat_ids": "1234567890", "verdict": "true_positive"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": "1234567890", "Updated": True, "Update": {"Action": "true_positive"}}]


def test_update_alert_analyst_verdict(mocker, requests_mock):
    requests_mock.post(
        "https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/alerts/analyst-verdict", json={"data": {"affected": 1}}
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-update-alerts-verdict")
    mocker.patch.object(demisto, "args", return_value={"alert_ids": "1234567890", "verdict": "true_positive"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": "1234567890", "Updated": True, "Update": {"Action": "true_positive"}}]


def test_update_threat_status(mocker, requests_mock):
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/threats/incident", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-update-threats-status")
    mocker.patch.object(demisto, "args", return_value={"threat_ids": "1234567890", "status": "in_progress"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": "1234567890", "Updated": True, "Status": "in_progress"}]


def test_update_alert_status(mocker, requests_mock):
    requests_mock.post(
        "https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/alerts/incident", json={"data": {"affected": 1}}
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-update-alerts-status")
    mocker.patch.object(demisto, "args", return_value={"alert_ids": "1234567890", "status": "in_progress"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": "1234567890", "Updated": True, "Status": "in_progress"}]


def test_create_star_rule(mocker, requests_mock):
    raw_star_rule_response = util_load_json("test_data/create_star_rule_response.json")
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/rules", json=raw_star_rule_response)
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-create-star-rule")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "name": "sample",
            "description": "description",
            "query": "sample query",
            "query_type": "events",
            "rule_severity": "Low",
            "account_ids": "1234567890",
            "group_ids": "1234567890",
            "site_ids": "123456789",
            "expiration_mode": "Permanent",
            "expiration_date": "",
            "network_quarantine": "true",
            "treatAsThreat": "suspicious",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == {}


def test_create_star_rule_with_query_lang(mocker, requests_mock):
    """
    Given: Valid args for sentinelone-create-star-rule with query_lang set to "2.0".
    When: Running the command.
    Then: queryLang is included in the request payload with value "2.0".
    """
    raw_star_rule_response = util_load_json("test_data/create_star_rule_response.json")
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/rules", json=raw_star_rule_response)
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-create-star-rule")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "name": "sample",
            "description": "description",
            "query": "sample query",
            "query_type": "events",
            "rule_severity": "Low",
            "account_ids": "1234567890",
            "group_ids": "1234567890",
            "site_ids": "123456789",
            "expiration_mode": "Permanent",
            "expiration_date": "",
            "network_quarantine": "true",
            "treatAsThreat": "suspicious",
            "query_lang": "2.0",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    request_body = requests_mock.last_request.json()
    assert request_body.get("data", {}).get("queryLang") == "2.0"


def test_update_star_rule_with_query_lang(mocker, requests_mock):
    """
    Given: Valid args for sentinelone-update-star-rule with query_lang set to "1.0".
    When: Running the command.
    Then: queryLang is included in the request payload with value "1.0".
    """
    raw_star_rule_response = util_load_json("test_data/create_star_rule_response.json")
    requests_mock.put(
        "https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/rules/225494730938493804",
        json=raw_star_rule_response,
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-update-star-rule")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "rule_id": "225494730938493804",
            "name": "sample",
            "description": "description",
            "query": "sample query",
            "query_type": "events",
            "rule_severity": "Low",
            "account_ids": "1234567890",
            "group_ids": "1234567890",
            "site_ids": "123456789",
            "expiration_mode": "Permanent",
            "expiration_date": "",
            "network_quarantine": "true",
            "treatAsThreat": "suspicious",
            "query_lang": "1.0",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    request_body = requests_mock.last_request.json()
    assert request_body.get("data", {}).get("queryLang") == "1.0"


def test_enable_star_rules(mocker, requests_mock):
    requests_mock.put("https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/rules/enable", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-enable-star-rules")
    mocker.patch.object(demisto, "args", return_value={"rule_ids": "1234567890"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": "1234567890", "Enabled": True}]


def test_disable_star_rules(mocker, requests_mock):
    requests_mock.put("https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/rules/disable", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-disable-star-rules")
    mocker.patch.object(demisto, "args", return_value={"rule_ids": "1234567890"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": "1234567890", "Disabled": True}]


def test_delete_star_rule(mocker, requests_mock):
    requests_mock.delete("https://usea1.sentinelone.net/web/api/v2.1/cloud-detection/rules", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-delete-star-rule")
    mocker.patch.object(demisto, "args", return_value={"rule_ids": "1234567890"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": "1234567890", "Deleted": True}]


def test_get_events(mocker, requests_mock):
    """
    Given:
    When: run get events
    Then: ensure the context output are as expected and contained the 'ProcessID' and 'EventID' as id keys
    """
    from CommonServerPython import CommandResults

    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/dv/events",
        json={
            "data": [{"ProcessID": "ProcessID_1", "EventID": "EventID_1"}, {"ProcessID": "ProcessID_2", "EventID": "EventID_2"}]
        },
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-events")
    mocker.patch.object(demisto, "args", return_value={"query_id": "1234567890"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    expected_context = (
        CommandResults(outputs_prefix="SentinelOne.Event", outputs_key_field=["ProcessID", "EventID"], outputs=[{}])
        .to_context()
        .get("EntryContext", {})
    )

    call = sentinelone_v2.return_results.call_args_list
    context_outputs = call[0].args[0].outputs
    assert all(key in context_outputs for key in expected_context)


def test_run_remote_script(mocker, requests_mock):
    """
    Given
        - required arguments i.e account_id, script_id, output_description, task_description, agent_ids and output_directory
    When
        - running sentinelone-run-remote-script command
    Then
        - returns a table of result had the affected process details
    """
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/remote-scripts/execute", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-run-remote-script")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "account_ids": "1234567890",
            "script_id": "1",
            "output_destination": "test",
            "task_description": "test",
            "output_directory": "file",
            "agent_ids": "2",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == {"affected": 1}


def test_initiate_endpoint_scan(mocker, requests_mock):
    """
    Given
        - required agent_ids argument
    When
        - running sentinelone-initiate-endpoint-scan command
    Then
        - returns a table of result had the details, like agent id and status of the scan
    """
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/agents/actions/initiate-scan", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-initiate-endpoint-scan")
    mocker.patch.object(demisto, "args", return_value={"agent_ids": "123456"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"Agent ID": "123456", "Initiated": True}]


def test_abort_endpoint_scan(mocker, requests_mock):
    """
    Given
        - required agent_ids argument
    When
        - running sentinelone-abort-endpoint-scan command
    Then
        - returns a table of result had the details, like agent id and status of the scan
    """
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/agents/actions/abort-scan", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-abort-endpoint-scan")
    mocker.patch.object(demisto, "args", return_value={"agent_ids": "123456"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"Agent ID": "123456", "Aborted": True}]


def test_endpoint_fetch_logs(mocker, requests_mock):
    """
    Given
        - required arguments i.e agent_ids, agents_logs, customer_facing_logs, platform_logs
    When
        - running sentinelone-endpoint-fetch-logs command
    Then
        - returns a table of result with the number of affected endpoints.
    """
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/agents/actions/fetch-logs", json={"data": {"affected": 1}})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-endpoint-fetch-logs")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "agent_ids": "123456",
            "agents_logs": "true",
            "customer_facing_logs": "false",
            "platform_logs": "false",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == {"Affected": 1}


def test_run_powerquery(mocker, requests_mock):
    """
    Given
        - required arguments i.e singularity_xdr_url, singularity_xdr_api_key, and query
    When
        - running sentinelone-run-powerquery command.
    Then
        - returns a table of query results with correct columns and data.
    """
    mock_response_data = {
        "cpuUsage": 49,
        "columns": [{"name": "dataSource.name"}, {"name": "dataSource.vendor"}],
        "warnings": [],
        "values": [
            ["Microsoft O365", "Microsoft"],
            ["Microsoft O365", "Microsoft"],
            ["Microsoft O365", "Microsoft"],
        ],
        "matchingEvents": 3.0,
        "status": "success",
        "omittedEvents": 0.0,
    }
    requests_mock.post(
        "https://example.com/api/powerQuery",
        json=mock_response_data,
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-run-powerquery")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "query": 'dataSource.name = "Microsoft O365" | columns dataSource.name, dataSource.vendor| limit 3',
            "singularity_xdr_url": "https://example.com",
            "singularity_xdr_api_key": "api_key",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs["status"] == "success"
    assert command_results.outputs["matchingEvents"] == 3.0
    assert command_results.outputs["results"][0]["dataSource.name"] == "Microsoft O365"
    assert command_results.outputs["results"][0]["dataSource.vendor"] == "Microsoft"


def test_create_bulk_ioc(mocker, requests_mock):
    """
    Given
        - required arguments i.e entry_id, account_ids
    When
        - running sentinelone-create-bulk-ioc command
    Then
        - returns a table of result had the list of data from the API response
    """
    mock_ioc_payload = util_load_json("test_data/iocs_payload.json")
    mock_ioc_raw_response = util_load_json("test_data/iocs_raw_response.json")
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/threat-intelligence/iocs", json=mock_ioc_raw_response)

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-create-bulk-ioc")
    mocker.patch.object(
        demisto,
        "args",
        return_value={"entry_id": "fake-entry-id", "account_ids": "123456"},
    )
    mocker.patch.object(demisto, "getFilePath", return_value={"path": "fake_path", "name": "iocs.json"})
    mocker.patch.object(builtins, "open", mocker.mock_open(read_data=json.dumps(mock_ioc_payload)))
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]

    assert command_results.outputs_prefix == "SentinelOne.IOCs"
    assert len(command_results.outputs) == len(mock_ioc_raw_response["data"])
    assert command_results.outputs[0]["Creator"] == "mark@test.com"


def test_get_threat_analysis_command(mocker, requests_mock):
    """
    Given
        - required argument i.e threat_id
    When
        - running sentinelone-threat-analysis command
    Then
        - returns CommandResults with Threat Analysis data containing AgentDetectionInfo, AgentRealtimeInfo, and ThreatInfo
    """
    threat_analysis_response = util_load_json("test_data/threat_analysis_raw.json")
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/private/threats/12345/analysis", json=threat_analysis_response)

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )

    mocker.patch.object(demisto, "command", return_value="sentinelone-threat-analysis")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "threat_id": "12345",
        },
    )

    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs_prefix == "SentinelOne.Threat"
    assert command_results.outputs["ThreatInfo"]["threatId"] == "12345"
    assert command_results.outputs["AgentDetectionInfo"]["accountName"] == "Test"


def test_get_installed_applications(mocker, requests_mock):
    """
    Given
        - required agent_ids argument
    When
        - running sentinelone-get-installed-applications command
    Then
        - returns a table of result had the list of installed applications on the provided agent
    """
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/agents/applications",
        json={"data": [{"name": "test", "publisher": "abc", "size": 50, "version": "2.1", "installedDate": "2023-02-10"}]},
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-installed-applications")
    mocker.patch.object(demisto, "args", return_value={"agent_ids": "123456"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [
        {"InstalledOn": "2023-02-10", "Name": "test", "Publisher": "abc", "Size": 50, "Version": "2.1"}
    ]  # noqa


def test_get_remote_script_status(mocker, requests_mock):
    """
    Given
        - required parentTaskId argument
    When
        - running sentinelone-get-remote-script-task-status command
    Then
        - returns a table of result had the list of taskIds which are available on ParentTaskIds
    """
    json_output = {
        "data": [
            {
                "accountId": "1234567890",
                "accountName": "Metron Team",
                "agentComputerName": "MSEDGEWIN10",
                "agentId": "0987654321",
                "agentIsActive": True,
                "agentIsDecommissioned": False,
                "agentMachineType": "desktop",
                "agentOsType": "windows",
                "agentUuid": "25682583752987932878722323",
                "createdAt": "2024-07-30T06:43:22.938877Z",
                "description": "A test get cloud services",
                "detailedStatus": "Execution completed successfully",
                "groupId": "12334654321",
                "groupName": "Default Group",
                "id": "123456",
                "initiatedBy": "user",
                "initiatedById": "099999",
                "parentTaskId": "123456789",
                "scriptResultsSignature": "34324324324324235r24fe2r2333432",
                "siteId": "99999999",
                "siteName": "Default site",
                "status": "completed",
                "statusCode": None,
                "statusDescription": "Completed",
                "type": "script_execution",
                "updatedAt": "2024-07-30T06:44:50.881432Z",
            }
        ]
    }
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/remote-scripts/status", json=json_output)
    mocker.patch.object(
        demisto, "params", return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1"}
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-remote-script-task-status")
    mocker.patch.object(demisto, "args", return_value={"parent_task_id": "123456789"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert_response = util_load_json("test_data/get_remote_script_task_status.json")
    assert command_results.outputs == assert_response


def test_remote_script_results(mocker, requests_mock):
    """
    Given
        - required taskIds argument
    When
        - running sentinelone-get-remote-script-task-results command
    Then
        - returns file details
    """
    task_ids = "1234566"
    output_json = {
        "data": {"download_links": [{"downloadUrl": "https://url/1", "fileName": "file1.zip", "taskId": task_ids}], "errors": []}
    }
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/remote-scripts/fetch-files", json=output_json)
    requests_mock.get("https://url/1", json={})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-remote-script-task-results")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "task_ids": task_ids,
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()
    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    outputs = command_results[0].outputs
    assert outputs[0].get("taskId") == task_ids
    assert outputs[0].get("fileName") == "file1.zip"
    assert outputs[0].get("downloadUrl") == "https://url/1"


def test_get_power_query_results(mocker, requests_mock):
    """
    Given
        - required query, from_date and to_date arguments
    When
        - running sentinelone-get-power-query-results command
    Then
        - returns a table of result if data present
    """
    json_output = util_load_json("test_data/get_power_query_response.json")
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/dv/events/pq-ping", json=json_output)
    mocker.patch.object(
        demisto, "params", return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1"}
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-power-query-results")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "query_id": "pq123456789",
            "from_date": "2024-08-20T04:49:26.257525Z",
            "to_date": "2024-08-21T04:49:26.257525Z",
            "query": "event.time = * | columns eventTime = event.time, agentUuid = agent.uuid, siteId = site.id",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()
    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    readable_output = (
        "### SentinelOne - Get Power Query Results for ID pqe5a4cbb0a0f0981f4125976b49fb0ebb"
        "\nRecommendation: Result set limited to 1000 rows by default. To display more rows, add"
        ' a command like "| limit 10000".\n\nSummary information and details about the power query'
        "\n|Agent Uuid|Event Time|Site Id|\n|---|---|---|\n"
        "| ed8f14f1-f35b-0eca-1c1e-e31e97aefc71 | 1724151854609 | 123456789 |\n"
        "| ed8f14f1-f35b-0eca-1c1e-e31e97aefc71 | 1724151823332 | 123456789 |\n"
    )
    assert command_results.readable_output == readable_output


def test_get_power_query_results_without_query_id(mocker, requests_mock):
    """
    Given
        - required query, from_date and to_date arguments
    When
        - running sentinelone-get-power-query-results command
    Then
        - returns a table of result if data present
    """
    json_output = util_load_json("test_data/get_power_query_response.json")
    requests_mock.post("https://usea1.sentinelone.net/web/api/v2.1/dv/events/pq", json=json_output)
    mocker.patch.object(
        demisto, "params", return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1"}
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-power-query-results")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "from_date": "2024-08-20T04:49:26.257525Z",
            "to_date": "2024-08-21T04:49:26.257525Z",
            "query": "event.time = * | columns eventTime = event.time, agentUuid = agent.uuid, siteId = site.id",
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()
    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    readable_output = (
        "### SentinelOne - Get Power Query Results for ID pqe5a4cbb0a0f0981f4125976b49fb0ebb"
        "\nRecommendation: Result set limited to 1000 rows by default. To display more rows, add"
        ' a command like "| limit 10000".\n\nSummary information and details about the power query'
        "\n|Agent Uuid|Event Time|Site Id|\n|---|---|---|\n"
        "| ed8f14f1-f35b-0eca-1c1e-e31e97aefc71 | 1724151854609 | 123456789 |\n"
        "| ed8f14f1-f35b-0eca-1c1e-e31e97aefc71 | 1724151823332 | 123456789 |\n"
    )
    assert command_results.readable_output == readable_output


def test_get_remote_data_command(mocker, requests_mock):
    """
    Given
        - an incident ID on the remote system
    When
        - running get_remote_data_command with changes to make on an incident
    Then
        - returns the relevant incident entity from the remote system with the relevant incoming mirroring fields
    """
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/threats", json={"data": [{"name": "test", "id": "123456"}]})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="get-remote-data")
    mocker.patch.object(demisto, "args", return_value={"id": "123456", "lastUpdate": "321456"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = vars(call[0].args[0])
    assert command_results == {
        "mirrored_object": {"name": "test", "id": "123456", "incident_type": "SentinelOne Incident"},
        "entries": [],
    }


def test_list_installed_singularity_mark_apps(mocker, requests_mock):
    """
    Given
        - all of these are optional arguments, but for this case, providing the id argument
    When
        - running sentinelone-list-installed-singularity-marketplace-applications command
    Then
        - returns a table of result had the list of installed singularity marketplace applications
    """
    json_page_1 = util_load_json("test_data/get_singularity_marketplace_apps_page_1_response.json")

    json_page_2 = util_load_json("test_data/get_singularity_marketplace_apps_page_2_response.json")

    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/singularity-marketplace/applications", json=json_page_1)
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/singularity-marketplace/applications?cursor=1234", json=json_page_2
    )

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-list-installed-singularity-marketplace-applications")
    mocker.patch.object(demisto, "args", return_value={"id": "123456"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]

    expected_outputs = expected_outputs = [
        {
            "ID": "123456",
            "Account": "SentinelOne",
            "AccountId": "1234567890",
            "ApplicationCatalogId": "90909090909090",
            "ApplicationCatalogName": "SentinelOne Threat Intelligence IOC Ingestion",
            "AlertMessage": "",
            "CreatedAt": "2025-01-23T13:11:23.12758",
            "Creator": "admin@user.sentinelone.net",
            "CreatorId": "212212121211212",
            "DesiredStatus": "active",
            "HasAlert": False,
            "LastEntityCreatedAt": "2025-01-23T13:11:23.127579",
            "Modifier": "admin@user.sentinelone.net",
            "ModifierId": "212212121211212",
            "ScopeId": "1230123012301230",
            "ScopeLevel": "account",
            "Status": "active",
            "UpdatedAt": "2025-01-23T13:11:25.96604",
            "ApplicationInstanceName": "SentinelOne Threat Intelligence IOC Ingestion",
        },
        {
            "ID": "123457",
            "Account": "SentinelOne",
            "AccountId": "1234567890",
            "ApplicationCatalogId": "90909090909090",
            "ApplicationCatalogName": "SentinelOne Threat Intelligence IOC Ingestion",
            "AlertMessage": "",
            "CreatedAt": "2025-01-23T13:11:23.12758",
            "Creator": "admin@user.sentinelone.net",
            "CreatorId": "212212121211212",
            "DesiredStatus": "active",
            "HasAlert": False,
            "LastEntityCreatedAt": "2025-01-23T13:11:23.127579",
            "Modifier": "admin@user.sentinelone.net",
            "ModifierId": "212212121211212",
            "ScopeId": "1230123012301230",
            "ScopeLevel": "account",
            "Status": "active",
            "UpdatedAt": "2025-01-23T13:11:25.96604",
            "ApplicationInstanceName": "SentinelOne Threat Intelligence IOC Ingestion",
        },
    ]
    assert command_results.outputs == expected_outputs
    assert requests_mock.call_count == 2, f"Expected 2 API calls, but got {requests_mock.call_count}"


def test_get_service_users(mocker, requests_mock):
    """
    Given
        - all of these are optional arguments, but for this case, providing the ids argument
    When
        - running sentinelone-get-service-users command
    Then
        - returns a table of result had the list of service users
    """
    json_page_1 = util_load_json("test_data/get_service_users_page_1_response.json")
    json_page_2 = util_load_json("test_data/get_service_users_page_2_response.json")
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/service-users", json=json_page_1)
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/service-users?cursor=1234", json=json_page_2)
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-service-users")
    mocker.patch.object(demisto, "args", return_value={"ids": "123456"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    expected_outputs = [
        {
            "ID": "123456",
            "ApiTokenCreatedAt": "2025-01-30T10:12:09.458490Z",
            "ApiTokenExpiresAt": "2025-03-01T10:12:08Z",
            "CreatedAt": "2025-01-30T10:12:09.407923Z",
            "CreatedById": "123456789099",
            "CreatedByName": "sentinelone",
            "Description": None,
            "LastActivation": "2025-02-04T10:00:07.637184Z",
            "Name": "Service user for SentinelOne Alert Ingestion app for 0101010101010 site id",
            "Scope": "site",
            "UpdatedAt": "2025-01-30T10:12:09.405748Z",
            "UpdatedById": "2323232323232323",
            "UpdatedByName": "sentinelone",
            "ScopeRolesRoleId": "999999999",
            "ScopeRolesRoleName": "Admin",
            "ScopeRolesAccountName": "SentinelOne",
            "ScopeRolesId": "0101010101010",
        },
        {
            "ID": "123457",
            "ApiTokenCreatedAt": "2025-01-30T10:12:09.458490Z",
            "ApiTokenExpiresAt": "2025-03-01T10:12:08Z",
            "CreatedAt": "2025-01-30T10:12:09.407923Z",
            "CreatedById": "123456789099",
            "CreatedByName": "sentinelone",
            "Description": None,
            "LastActivation": "2025-02-04T10:00:07.637184Z",
            "Name": "Service user for SentinelOne Alert Ingestion app for 0101010101010 site id",
            "Scope": "site",
            "UpdatedAt": "2025-01-30T10:12:09.405748Z",
            "UpdatedById": "2323232323232323",
            "UpdatedByName": "sentinelone",
            "ScopeRolesRoleId": "999999999",
            "ScopeRolesRoleName": "Admin",
            "ScopeRolesAccountName": "SentinelOne",
            "ScopeRolesId": "0101010101010",
        },
    ]

    assert command_results.outputs == expected_outputs
    assert requests_mock.call_count == 2, f"Expected 2 API calls, but got {requests_mock.call_count}"


def test_get_modified_remote_data_command(mocker, requests_mock):
    """
    Given
        - arguments - lastUpdate time
        - raw incidents (results of get_incidents_ids and get_fetch_detections)
    When
        - running get_modified_remote_data_command
    Then
        - returns a list of incidents and detections IDs that were modified since the lastUpdate time
    """
    requests_mock.get("https://usea1.sentinelone.net/web/api/v2.1/threats", json={"data": [{"name": "test", "id": "123456"}]})
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="get-modified-remote-data")
    mocker.patch.object(demisto, "args", return_value={"id": "123456", "lastUpdate": "2023-02-16 09:35:40.020660+00:00"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = vars(call[0].args[0])
    assert command_results == {"modified_incident_ids": ["123456"]}


def test_update_remote_system_command(requests_mock):
    """
    Given
        - incident changes (one of the mirroring field changed or it was closed in XSOAR)
    When
        - outgoing mirroring triggered by a change in the incident
    Then
        - the relevant incident is updated with the corresponding fields in the remote system
        - the returned result corresponds to the incident ID
    """
    args = {
        "delta": {"sentinelonethreatanalystverdict": "", "sentinelonethreatstatus": "", "closeNotes": "a test"},
        "incidentChanged": True,
        "remoteId": "123456",
    }
    command_result = sentinelone_v2.update_remote_system_command(requests_mock, args)
    assert command_result == "123456"


def test_get_mapping_fields_command():
    """
    Given
        - nothing
    When
        - running get_mapping_fields_command
    Then
        - the result fits the expected mapping scheme
    """
    result = sentinelone_v2.get_mapping_fields_command()
    assert result.scheme_types_mappings[0].type_name == "SentinelOne Incident"
    assert list(result.scheme_types_mappings[0].fields.keys()) == [
        "analystVerdict",
        "incidentStatus",
        "uamStatus",
        "uamAnalystVerdict",
    ]


def test_get_dv_query_status(mocker, requests_mock):
    """
    Given: queryId
    When: run get_status
    Then: ensure the context output are as expected and contained the Query Status
    """
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/dv/query-status",
        json={
            "data": {
                "QueryID": "1234567890",
                "progressStatus": 100,
                "queryModeInfo": {"lastActivatedAt": "2022-07-19T21:20:54+00:00", "mode": "scalyr"},
                "responseState": "FINISHED",
                "warnings": None,
            }
        },
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-dv-query-status")
    mocker.patch.object(demisto, "args", return_value={"query_id": "1234567890"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs.get("QueryID") == "1234567890"
    assert command_results.outputs.get("responseState") == "FINISHED"


def test_get_agent_request(mocker, requests_mock):
    """
    Test get_agent_request returns agent details for a valid agent_id.
    """
    # Arrange
    agent_id = "1234567890"
    api_response = {
        "data": [
            {
                "id": agent_id,
                "computerName": "test-computer",
                "networkInterfaces": [{"int_name": "eth0", "inet": "192.168.1.10", "physical": "00:11:22:33:44:55"}],
            }
        ]
    }
    # Patch the GET request to the agents endpoint
    requests_mock.get(
        f"https://usea1.sentinelone.net/web/api/v2.1/agents?ids={agent_id}",
        json=api_response,
    )

    # Patch demisto params and command context
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1"},
    )
    client = sentinelone_v2.Client(base_url="https://usea1.sentinelone.net/web/api/v2.1", verify=False, proxy=False, headers={})

    # Act
    result = client.get_agent_request(agent_id)

    # Assert
    assert isinstance(result, list)
    assert result[0]["id"] == agent_id
    assert result[0]["computerName"] == "test-computer"
    assert result[0]["networkInterfaces"][0]["inet"] == "192.168.1.10"


def test_get_agent_request_multiple_ids(mocker, requests_mock):
    """
    Test get_agent_request returns agent details for multiple agent_ids.
    """
    # Arrange
    agent_ids = "1234567890,1475812345"
    api_response = {
        "data": [
            {
                "id": "1234567890",
                "computerName": "test-computer-1",
                "networkInterfaces": [{"int_name": "eth0", "inet": "192.168.1.10", "physical": "00:11:22:33:44:55"}],
            },
            {
                "id": "1475812345",
                "computerName": "test-computer-2",
                "networkInterfaces": [{"int_name": "eth1", "inet": "10.0.0.5", "physical": "66:77:88:99:AA:BB"}],
            },
        ]
    }
    # Patch the GET request to the agents endpoint
    requests_mock.get(
        f"https://usea1.sentinelone.net/web/api/v2.1/agents?ids={agent_ids}",
        json=api_response,
    )

    # Patch demisto params and command context
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1"},
    )
    client = sentinelone_v2.Client(base_url="https://usea1.sentinelone.net/web/api/v2.1", verify=False, proxy=False, headers={})

    # Act
    result = client.get_agent_request(agent_ids)

    # Assert
    assert isinstance(result, list)
    assert len(result) == 2
    assert result[0]["id"] == "1234567890"
    assert result[1]["id"] == "1475812345"
    assert result[0]["computerName"] == "test-computer-1"
    assert result[1]["computerName"] == "test-computer-2"
    assert result[0]["networkInterfaces"][0]["inet"] == "192.168.1.10"
    assert result[1]["networkInterfaces"][0]["inet"] == "10.0.0.5"


def test_get_agent_mac(mocker, requests_mock):
    """
    Given: agentId
    When: run get_status
    Then: ensures returned context details are as expected
    """

    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/agents",
        json={
            "data": [
                {
                    "computerName": "computerName_test",
                    "id": "agentId_test",
                    "networkInterfaces": [{"int_name": "int_name_test", "inet": "ip_test", "physical": "mac_test"}],
                }
            ]
        },
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-get-agent-mac")
    mocker.patch.object(demisto, "args", return_value={"agent_id": "1234567890"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs[0].get("agent_id") == "agentId_test"
    assert command_results.outputs[0].get("ip") == "ip_test"
    assert command_results.outputs[0].get("mac") == "mac_test"


def test_is_uam_alert_returns_true_for_node_key():
    """
    When:
        A mirrored object contains a 'node' key (UAM alert GraphQL edge).
    Then:
        _is_uam_alert returns True.
    """
    assert sentinelone_v2._is_uam_alert({"node": {"id": UAM_ALERT_ID, "status": "NEW"}}) is True


def test_is_uam_alert_returns_false_for_threat():
    """
    When:
        A mirrored object has no 'node' key (THREAT REST API response).
    Then:
        _is_uam_alert returns False.
    """
    assert sentinelone_v2._is_uam_alert({"threatInfo": {"incidentStatus": "unresolved"}}) is False


def test_to_incident_uam_alert_promotes_id():
    """
    When:
        to_incident is called with type='UAM Alert' and node.id set.
    Then:
        data['id'] is promoted to top level so XSOAR sets dbotMirrorId correctly,
        name / occurred / severity are mapped correctly.
    """
    data = {
        "node": {
            "id": UAM_ALERT_ID,
            "name": "Test Alert",
            "severity": "HIGH",
            "createdAt": "2026-02-24T11:35:23.392Z",
            "realTime": {"scope": {"account": {}, "group": {}, "site": {}}},
            "detectionTime": {"cloud": None, "kubernetes": None},
            "asset": {},
        }
    }
    incident = sentinelone_v2.to_incident("UAM Alert", data)

    assert data["id"] == UAM_ALERT_ID
    assert incident["name"] == "Sentinel One UAM Alert: Test Alert"
    assert incident["occurred"] == "2026-02-24T11:35:23.392Z"
    assert incident["severity"] == 3  # HIGH → 3


def test_get_remote_data_uam_alert_normalizes_status_and_verdict(mocker, requests_mock):
    """
    When:
        get-remote-data is called with a UUID-format incident id (UAM alert).
    Then:
        The GraphQL endpoint is called, status/analystVerdict are normalized to
        XSOAR display labels, and the mirrored object contains a 'node' key.
    """
    raw = util_load_json("test_data/uam_alert_by_id_response.json")
    requests_mock.post(GRAPHQL_URL, json=raw)

    mocker.patch.object(
        demisto,
        "params",
        return_value={**BASE_PARAMS, "close_incident": False},
    )
    mocker.patch.object(demisto, "command", return_value="get-remote-data")
    mocker.patch.object(
        demisto,
        "args",
        return_value={"id": UAM_ALERT_ID, "lastUpdate": "2026-01-01T00:00:00Z"},
    )
    mock_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    mock_results.assert_called_once()
    response = mock_results.call_args[0][0]
    mirrored_object = response.mirrored_object

    assert mirrored_object["node"]["status"] == "In progress"
    assert mirrored_object["node"]["analystVerdict"] == "True positive - Ransomware"
    assert mirrored_object["incident_type"] == "SentinelOne Incident"


def test_get_remote_data_threat_routes_to_threats_api(mocker, requests_mock):
    """
    When:
        get-remote-data is called with a numeric incident id (THREAT).
    Then:
        The threats REST API is called (not GraphQL) and the mirrored object
        has no 'node' key.
    """
    threat_response = {"data": [{"id": THREAT_ID, "threatInfo": {"incidentStatus": "unresolved"}}]}
    requests_mock.get(THREATS_URL, json=threat_response)

    mocker.patch.object(
        demisto,
        "params",
        return_value={**BASE_PARAMS, "close_incident": False},
    )
    mocker.patch.object(demisto, "command", return_value="get-remote-data")
    mocker.patch.object(
        demisto,
        "args",
        return_value={"id": THREAT_ID, "lastUpdate": "2026-01-01T00:00:00Z"},
    )
    mock_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    mock_results.assert_called_once()
    response = mock_results.call_args[0][0]
    mirrored_object = response.mirrored_object

    assert mirrored_object.get("id") == THREAT_ID
    assert "node" not in mirrored_object


def test_update_remote_system_uam_status_via_delta(mocker, requests_mock):
    """
    When:
        update-remote-system is called with sentineloneuamalertstatus in delta
        and sentineloneuamalertid in data.
    Then:
        The UAM alert status mutation is called with the correct alert ID and
        the API enum value (not the display label).
    """
    mutation_response = {"data": {"alertTriggerActions": {"actions": [{"success": [UAM_ALERT_ID], "failure": []}]}}}
    requests_mock.post(GRAPHQL_URL, json=mutation_response)

    mocker.patch.object(
        demisto,
        "params",
        return_value=BASE_PARAMS,
    )
    mocker.patch.object(demisto, "command", return_value="update-remote-system")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "remoteId": UAM_ALERT_ID,
            "incidentChanged": "true",
            "delta": {"sentineloneuamalertstatus": "Resolved"},
            "data": {"sentineloneuamalertid": UAM_ALERT_ID},
            "entries": [],
            "status": 1,
        },
    )
    mock_results = mocker.patch.object(sentinelone_v2, "return_results")

    main()

    mock_results.assert_called_once()
    sent_body = requests_mock.last_request.json()
    # Verify the mutation was called with the API enum value
    assert "RESOLVED" in sent_body["query"] or sent_body.get("variables", {}).get("status") == "RESOLVED"


def test_update_remote_system_uam_no_change_skips_api_call(mocker, requests_mock):
    """
    When:
        update-remote-system is called with incidentChanged=false.
    Then:
        No API call is made to SentinelOne.
    """
    requests_mock.post(GRAPHQL_URL, json={})

    mocker.patch.object(
        demisto,
        "params",
        return_value=BASE_PARAMS,
    )
    mocker.patch.object(demisto, "command", return_value="update-remote-system")
    mocker.patch.object(
        demisto,
        "args",
        return_value={
            "remoteId": UAM_ALERT_ID,
            "incidentChanged": "false",
            "delta": {},
            "data": {"sentineloneuamalertid": UAM_ALERT_ID},
            "entries": [],
            "status": 1,
        },
    )
    mocker.patch.object(sentinelone_v2, "return_results")

    main()

    assert requests_mock.call_count == 0


def test_get_uam_alert_by_id_uses_graphql_variables(mocker, requests_mock):
    """
    When:
        get_uam_alert_by_id is called with a UAM alert UUID.
    Then:
        POSTs to the GraphQL endpoint with variables={'id': alert_id} (not f-string),
        and returns a dict with 'node' wrapping the raw alert data.
    """
    raw = util_load_json("test_data/uam_alert_by_id_response.json")
    graphql_mock = requests_mock.post(
        GRAPHQL_URL,
        json=raw,
    )
    mocker.patch.object(demisto, "params", return_value=BASE_PARAMS)
    client = _make_client()

    result = client.get_uam_alert_by_id(UAM_ALERT_ID)

    sent_body = graphql_mock.last_request.json()
    assert sent_body.get("variables") == {"id": UAM_ALERT_ID}
    assert "alert(id: $id)" in sent_body["query"]
    assert result["node"]["id"] == UAM_ALERT_ID
    assert result["node"]["status"] == "IN_PROGRESS"


def test_update_uam_alert_status(mocker, requests_mock):
    requests_mock.post(
        GRAPHQL_URL,
        json={"data": {"alertTriggerActions": {"actions": [{"success": [{"id": UAM_ALERT_ID}], "failure": []}]}}},
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value=BASE_PARAMS,
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-update-uam-alert-status")
    mocker.patch.object(demisto, "args", return_value={"alert_ids": UAM_ALERT_ID, "status": "Resolved"})
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": UAM_ALERT_ID, "Status": "Resolved", "Updated": True}]


def test_update_uam_alert_verdict(mocker, requests_mock):
    requests_mock.post(
        GRAPHQL_URL,
        json={"data": {"alertTriggerActions": {"actions": [{"success": [{"id": UAM_ALERT_ID}], "failure": []}]}}},
    )
    mocker.patch.object(
        demisto,
        "params",
        return_value=BASE_PARAMS,
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-update-uam-alert-verdict")
    mocker.patch.object(
        demisto, "args", return_value={"alert_ids": UAM_ALERT_ID, "analyst_verdict": "True positive - Ransomware"}
    )
    mocker.patch.object(sentinelone_v2, "return_results")
    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results = call[0].args[0]
    assert command_results.outputs == [{"ID": UAM_ALERT_ID, "AnalystVerdict": "True positive - Ransomware", "Updated": True}]


def test_export_full_threat_timeline(mocker, requests_mock):
    """
    Given
        - required argument i.e threat_id
    When
        - running sentinelone-export-full-threat-timeline command
    Then
        - returns CommandResults and a file entry with the exported timeline content
    """
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/threats/12345/timeline",
        [
            {"json": {"data": [{"id": "evt-1"}], "pagination": {"nextCursor": "cursor-1"}}},
            {"json": {"data": [{"id": "evt-2"}], "pagination": {"nextCursor": None}}},
        ],
    )

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-export-full-threat-timeline")
    mocker.patch.object(demisto, "args", return_value={"threat_id": "12345"})
    mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results, file_result = call[0].args[0]

    assert command_results.outputs_prefix == "SentinelOne.Export.Timeline"
    assert command_results.outputs["ThreatId"] == "12345"
    assert command_results.outputs["Filename"] == "12345_timeline.json"

    assert len(requests_mock.request_history) == 2
    assert all(req.method == "GET" for req in requests_mock.request_history)


def test_export_threat_events(mocker, requests_mock):
    """
    Given
        - required argument i.e threat_id
    When
        - running sentinelone-export-threat-events command
    Then
        - returns CommandResults and a file entry with the exported events content
    """
    requests_mock.get(
        "https://usea1.sentinelone.net/web/api/v2.1/export/threats/12345/explore/events",
        json={"data": [{"id": "evt-1", "eventName": "event-1"}]},
    )

    mocker.patch.object(
        demisto,
        "params",
        return_value={"token": "token", "url": "https://usea1.sentinelone.net", "api_version": "2.1", "fetch_threat_rank": "4"},
    )
    mocker.patch.object(demisto, "command", return_value="sentinelone-export-threat-events")
    mocker.patch.object(demisto, "args", return_value={"threat_id": "12345"})
    mocker.patch.object(sentinelone_v2, "return_results")

    main()

    call = sentinelone_v2.return_results.call_args_list
    command_results, file_result = call[0].args[0]

    assert command_results.outputs_prefix == "SentinelOne.Export.Events"
    assert command_results.outputs["ThreatId"] == "12345"
    assert command_results.outputs["Filename"] == "threats_12345.json"

    assert len(requests_mock.request_history) == 1
    assert requests_mock.request_history[0].method == "GET"
    assert requests_mock.request_history[0].qs.get("format") == ["json"]
    assert requests_mock.request_history[0].qs.get("eventtypes") == ["events"]