Skip to content
86 changes: 86 additions & 0 deletions Packs/SentinelOne/Integrations/SentinelOne-V2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,92 @@ Returns threat notes.
| 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 |
| id | 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. |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| SentinelOne.InstalledApps.applicationCatalogName | string | The application Catalog name. |
| 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).
Expand Down
164 changes: 164 additions & 0 deletions Packs/SentinelOne/Integrations/SentinelOne-V2/SentinelOne-V2.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,14 @@ def get_remote_script_results_request(self, computer_names: list, task_ids: list
response = self._http_request(method='POST', url_suffix=endpoint_url, json_data=payload)
return response.get("data", {}).get("download_links", [])

def list_installed_applications_request(self, params: dict):
response = self._http_request(method='GET', url_suffix='singularity-marketplace/applications', params=params)
return response.get('data', []), response.get('pagination', {})

def get_service_users_request(self, params: dict):
response = self._http_request(method='GET', url_suffix='service-users', params=params)
return response.get('data', []), response.get('pagination', {})

def remove_empty_fields(self, json_payload):
"""
Removes empty fields from a JSON payload and returns a new JSON object with non-empty fields.
Expand Down Expand Up @@ -3433,6 +3441,160 @@ def get_power_query_results(client: Client, args: dict):
return poll_power_query_results(client=client, cmd="sentinelone-get-power-query-results", args=args)


def list_installed_singu_mark_apps_command(client: Client, args: dict) -> CommandResults:
"""
List all installed applications matching the input filter
"""
installed_applications = []
# Get arguments
query_params = assign_params(
accountIds=args.get('account_ids'),
applicationCatalogId=args.get('application_catalog_id'),
creator__contains=args.get('creator_contains'),
id=args.get('id'),
limit=1000,
name__contains=args.get('name_contains'),
siteIds=args.get('site_ids')
)

# Make request and get raw response
installed_applications_page, pagination = client.list_installed_applications_request(query_params)
installed_applications.extend(installed_applications_page)

while pagination and pagination.get("nextCursor"):
demisto.debug("Got the next page for installed applications \n {}".format(pagination['nextCursor']))
query_params['cursor'] = pagination['nextCursor']
# The SentinelOne API does not accept other pagination parameters if the cursor is provided in the query.
# Including additional pagination parameters alongside the cursor will result in a 400 error.
# So removing the limit from the query params
if query_params.get('limit'):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just add a comment when we need to remove the limit

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets remove limit and cursor because we will get them everything

del query_params['limit']
installed_applications_page, pagination = client.list_installed_applications_request(query_params)
installed_applications.extend(installed_applications_page)

all_scopes = []
if installed_applications:
for each_app in installed_applications:
scopes = each_app.get("scopes")
if scopes is not None and len(scopes) > 0:
for scope in scopes:
scope["applicationCatalogId"] = each_app["applicationCatalogId"]
scope["applicationCatalogName"] = each_app["name"]
all_scopes.append(scope)
meta = "Provides summary information and details for all the installed applications that matched specified filter values"
else:
meta = "The search filters provided are returning no results. Please review and adjust them accordingly."

context_entries = []
for each_scope in all_scopes:
entry = {
'ID': each_scope.get('id'),
'Account': each_scope.get('account'),
'AccountId': each_scope.get('accountId'),
'ApplicationCatalogId': each_scope.get('applicationCatalogId'),
'ApplicationCatalogName': each_scope.get('applicationCatalogName'),
'AlertMessage': each_scope.get('alertMessage'),
'CreatedAt': each_scope.get('createdAt'),
'Creator': each_scope.get('creator'),
'CreatorId': each_scope.get('creatorId'),
'DesiredStatus': each_scope.get('desiredStatus'),
'HasAlert': each_scope.get('hasAlert'),
'LastEntityCreatedAt': each_scope.get('lastEntityCreatedAt'),
'Modifier': each_scope.get('modifier'),
'ModifierId': each_scope.get('modifierId'),
'ScopeId': each_scope.get('scopeId'),
'ScopeLevel': each_scope.get('scopeLevel'),
'Status': each_scope.get('status'),
'UpdatedAt': each_scope.get('updatedAt'),
'ApplicationInstanceName': each_scope.get('applicationInstanceName'),
}
context_entries.append(entry)

return CommandResults(
readable_output=tableToMarkdown(
'SentinelOne - List of Installed Applications',
context_entries,
headerTransform=pascalToSpace,
removeNull=True,
metadata=meta
),
outputs_prefix='SentinelOne.InstalledApps',
outputs_key_field='ID',
outputs=context_entries,
raw_response=installed_applications)


def get_service_users_command(client: Client, args: dict) -> CommandResults:
"""
Get all service users matching the input filter
"""
service_users = []
# Get arguments
query_params = assign_params(
accountIds=args.get('account_ids'),
roleIds=args.get('role_ids'),
ids=args.get('ids'),
limit=1000,
siteIds=args.get('site_ids')
)
# Make request and get raw response
service_users_page, pagination = client.get_service_users_request(query_params)
service_users.extend(service_users_page)
while pagination and pagination.get("nextCursor"):
demisto.debug("Got the next page for service users \n {}".format(pagination['nextCursor']))
query_params['cursor'] = pagination['nextCursor']
# The SentinelOne API does not accept other pagination parameters if the cursor is provided in the query.
# Including additional pagination parameters alongside the cursor will result in a 400 error.
# So removing the limit from the query params
if query_params.get('limit'):
del query_params['limit']
service_users_page, pagination = client.get_service_users_request(query_params)
service_users.extend(service_users_page)

context_entries = []
if service_users:
for each_service_user in service_users:
entry = {
'ID': each_service_user.get('id'),
'ApiTokenCreatedAt': each_service_user.get('apiToken', {}).get('createdAt'),
'ApiTokenExpiresAt': each_service_user.get('apiToken', {}).get('expiresAt'),
'CreatedAt': each_service_user.get('createdAt'),
'CreatedById': each_service_user.get('createdBy', {}).get('id'),
'CreatedByName': each_service_user.get('createdBy', {}).get('name'),
'Description': each_service_user.get('description'),
'LastActivation': each_service_user.get('lastActivation'),
'Name': each_service_user.get('name'),
'Scope': each_service_user.get('scope'),
'UpdatedAt': each_service_user.get('updatedAt'),
'UpdatedById': each_service_user.get('updatedBy', {}).get("id"),
'UpdatedByName': each_service_user.get('updatedBy', {}).get("name"),
}
if each_service_user.get('scopeRoles') and len(each_service_user.get('scopeRoles')) > 0:
scope_role_items = each_service_user['scopeRoles'][0]
if scope_role_items:
entry['ScopeRolesRoleId'] = scope_role_items.get('roleId')
entry['ScopeRolesRoleName'] = scope_role_items.get('roleName')
entry['ScopeRolesAccountName'] = scope_role_items.get('accountName')
entry['ScopeRolesId'] = scope_role_items.get('id')
context_entries.append(entry)
meta = "Provides summary information and details for all the service users that matched specified filter values"
else:
meta = "The search filters provided are returning no results. Please review and adjust them accordingly."

return CommandResults(
readable_output=tableToMarkdown(
'SentinelOne - Get Service Users',
context_entries,
headerTransform=pascalToSpace,
removeNull=True,
metadata=meta
),
outputs_prefix='SentinelOne.ServiceUsers',
outputs_key_field='ID',
outputs=context_entries,
raw_response=service_users)


def get_mapping_fields_command():
"""
Returns the list of fields to map in outgoing mirroring, for incidents.
Expand Down Expand Up @@ -3872,6 +4034,8 @@ def main():
'sentinelone-get-remote-script-task-results': get_remote_script_results,
'sentinelone-remote-script-automate-results': remote_script_automate_results,
'sentinelone-get-power-query-results': get_power_query_results,
'sentinelone-list-installed-singularity-marketplace-applications': list_installed_singu_mark_apps_command,
'sentinelone-get-service-users': get_service_users_command,
},
'commands_with_params': {
'get-remote-data': get_remote_data_command,
Expand Down
Loading
Loading