Securonix
Use the Securonix integration to manage incidents, threats, lookup tables, whitelists and watchlists.
Analytics & SIEM · Securonix
Details
| ID | Securonix |
|---|---|
| Provider | Securonix |
| Category | Analytics & SIEM |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Overview
Use the Securonix integration to manage incidents, threats, lookup tables, whitelists and watchlists. Integration was built and tested with SNYPR Versions: 6.4 (Nov 2024 R2 release).
This integration supports both cloud and on-prem instances of Securonix.
To configure a cloud base instance use the Tenant parameter only.
To configure an on-prem instance, use both the Host and Tenant parameters.
Use cases
- Fetch newly created incidents from Securonix.
- Fetch newly created threats from Securonix.
- Get, update and create Securonix incidents, add comments and perform actions on the Securonix incidents.
- Get, update, create and delete records from the whitelist.
- Get, update, create and delete records from the lookup table.
- Get, update, and create records from the watchlist.
Pre-Requisites
On Securonix
Ensure the integration user satisfies below mentioned pre-requisites:
- A role “ROLE_CASE_ADMIN” must be assigned
- For the target Securonix platform, enable the “Show Incidents To All Users” option under “INCIDENT MANAGEMENT SETTINGS” as shown below:

Configure Securonix on Cortex XSOAR
- Navigate to Settings > Integrations > Instances.
- Search for Securonix.
- Click Add instance to create and configure a new integration instance.
- To fetch Securonix Incidents refer to the section “Configuration for fetching Securonix Incident as an XSOAR Incident”.
- To fetch Securonix Threats refer to the section “Configuration for fetching Securonix Threat as an XSOAR Incident”.
Configuration Parameters
| Parameter Name | Help Text | Required (Yes / No) |
|---|---|---|
Host (Overrides the default hostname: https://{tenant}.net/Snypr) |
No | |
| Tenant | Yes | |
| Username | Yes | |
| Password | Yes | |
| Type of entity to fetch | The type of Securonix entity to fetch. Supported entities are “Incident” and “Threat”. | No |
| Tenant Name | Name of the tenant to fetch threats from. This parameter is optional for Non MSSP users. | No |
| Incidents to fetch | Selecting “all” will fetch incidents updated in the given time range. Selecting “opened” will fetch incidents opened in the given time range. Selecting “closed” will fetch incidents closed in the given time range. |
No |
| Set the default incident severity | No | |
| First fetch time range | The date or relative timestamp from where to start fetching incidents. Supported formats: <number> <time unit>, e.g., 1 hour, 30 minutes, 7 days, 3 months, 1 year. Default is 1 hour. |
No |
| The maximum number of incidents to fetch each time. | If the value is greater than 200, it will be considered as 200. The maximum is 200. | No |
| Incident Mirroring Direction | The mirroring direction in which to mirror the incidents. You can mirror “Incoming” (from Securonix to XSOAR), “Outgoing” (from XSOAR to Securonix), or in both directions. | No |
| Close respective Securonix incident after fetching | If enabled, the integration will close the respective Securonix incident after fetching it in XSOAR. Following fields will be required for this functionality: 1. Securonix action name to map with XSOAR’s active state for Outgoing mirroring 2. Securonix status to map with XSOAR’s active state for Outgoing mirroring 3. Securonix action name to map with XSOAR’s closed state for Outgoing mirroring 4. Securonix status to map with XSOAR’s closed state for Outgoing mirroring |
No |
| Securonix workflow state(s) that can be considered as Close state in XSOAR for Incoming mirroring | If the Securonix incident is in any one of the state mentioned here, then the incident will be Closed on XSOAR. Supports comma-separated values. | No |
| Securonix action name to map with XSOAR’s active state for Outgoing mirroring | Provide an action name to map with XSOAR’s active state. E.g. IN PROGRESS. | No |
| Securonix status to map with XSOAR’s active state for Outgoing mirroring | Provide a workflow status to map with XSOAR’s active state. E.g. In Progress. | No |
| Securonix action name to map with XSOAR’s closed state for Outgoing mirroring | Provide an action name to map with XSOAR’s Closed state. E.g. CLOSED | No |
| Securonix status to map with XSOAR’s closed state for Outgoing mirroring | Provide a workflow status to map with XSOAR’s closed state. E.g. Completed | No |
| Comment Entry Tag | Choose the tag to add to an entry to mirror it as a comment in Securonix. | No |
| Securonix Retry Count | Numbers of retries to be performed. (Recommended is 3) | No |
| Securonix Retry Delay(In Seconds) | The delay between two retries. Range in 30 to 300 Seconds (5 minutes). Anything less than 30 seconds is considered 30 seconds, and anything more than 300 seconds is considered 300 seconds. (Recommended is 30 seconds) | No |
| Securonix Retry Delay Type | Delay type of retry mechanism. (Recommended is Exponential) | No |
| Trust any certificate (not secure) | Indicates whether to allow connections without verifying SSL certificate’s validity. | No |
| Use system proxy settings | Indicates whether to use XSOAR’s system proxy settings to connect to the API. | No |
- Click Test to validate the URLs, token, and connection.
Configuration for fetching Securonix Incident as an XSOAR Incident
To fetch Securonix Incident follow the next steps:
- Select Fetches incidents.
- Under Classifier, select “N/A”.
- Under Incident type, select “Securonix Incident”.
- Under Mapper (incoming), select “Securonix Incident - Incoming Mapper” for default mapping.
- Enter the connection parameters. (Host, Tenant, Username & Password)
- Under the Type of entity to fetch, select “Incident”.
- Select the “Incidents to fetch”:
- all - This will fetch incidents updated in the given time range.
- opened - This will fetch incidents created in the given time range.
- closed - This will fetch incidents closed in the given time range.
- Update “Set default incident severity”, “First Fetch time range” & “Max Fetch Count” based on your requirement.
- Select the Incident Mirroring Direction:
- Incoming - Mirrors changes from the Securonix incident into the Cortex XSOAR incident.
- Outgoing - Mirrors changes from the Cortex XSOAR incident to the Securonix incident.
- Incoming And Outgoing - Mirrors changes both Incoming and Outgoing directions on incidents.
- None - Turns off incident mirroring.
- Enter the relevant values for “State” & “Action” values for mirroring.
- Below table indicates which fields are required for the respective mirroring type.
| Mirroring Type | Securonix workflow States for Incoming mirroring | Securonix State for XSOAR Active State | Securonix Action for XSOAR Active Action | Securonix State for XSOAR Closed State | Securonix Action for XSOAR Closed Action |
|---|---|---|---|---|---|
| Incoming | Yes | No | No | No | No |
| Outgoing | No | Yes | Yes | Yes | Yes |
| Incoming and Outgoing | Yes | Yes | Yes | Yes | Yes |
- Enter the relevant Comment Entry Tag.
Note: This value is mapped to the dbotMirrorTags incident field in Cortex XSOAR, which defines how Cortex XSOAR handles comments when you tag them in the War Room. This is required for mirroring comments from Cortex XSOAR to Securonix. - Optional: Check the “Close respective Securonix incident after fetching” parameter, if you want to close the Securonix Incident once it is fetched in the XSOAR.
Below Parameters are required if this option is checked:- Securonix action name for XSOAR’s active state for Outgoing
- Securonix status for XSOAR’s active state for Outgoing
- Securonix action name for XSOAR’s close state for Outgoing
- Securonix status for XSOAR’s close state for Outgoing
- Enter the relevant values for Securonix Retry parameters “Count”, “Delay” & “Delay Type”.
Notes for mirroring:
- This feature is compliant with XSOAR version 6.0 and above.
- When mirroring incidents, you can make changes in Securonix that will be reflected in Cortex XSOAR, or vice versa. You can also attach files in Securonix Incident which will then be available in the XSOAR incident.
- The mirroring settings apply only for incidents that are fetched after applying the settings. Pre-existing comments are not fetched/mirrored at the time of incident creation.
- For mirroring to work flawlessly, a three-state workflow(similar to XSOAR) must be configured on the Securonix Incident side.
- The mirroring is strictly tied to Incident type “Securonix Incident” & Incoming mapper “Securonix Incident - Incoming Mapper” if you want to change or use your custom incident type/mapper then make sure changes related to these are present.
- If you want to use the mirror mechanism and you’re using custom mappers, then the incoming mapper must contain the following fields: dbotMirrorDirection, dbotMirrorId, dbotMirrorInstance, dbotMirrorTags and securonixcloseincident.
- To use a custom mapper, you must first duplicate the mapper and update the fields in the copy of the mapper. (Refer to the “Create a custom mapper consisting of the default Securonix mapper” section for more information.)
- Following new fields are introduced in the response of the incident to enable the mirroring:
- mirror_direction: This field determines the mirroring direction for the incident. It is a required field for XSOAR to enable mirroring support.
- mirror_tags: This field determines what would be the tag needed to mirror the XSOAR entry out to Securonix. It is a required field for XSOAR to enable mirroring support.
- mirror_instance: This field determines from which instance the XSOAR incident was created. It is a required field for XSOAR to enable mirroring support.
- close_sx_incident: This field determines whether to close the respective Securonix incident once fetched in the XSOAR based on the instance configuration. It is required for closing the respective incident on Securonix. This will be used in the playbook to close the securonix incident.
Configuration for fetching Securonix Threat as an XSOAR Incident
To fetch Securonix Threat follow the next steps:
- Select Fetches incidents.
- Under Classifier, select “N/A”.
- Under Incident type, select Securonix Threat.
- Under Mapper (incoming), select Securonix Threat - Incoming Mapper for default mapping.
- Under Type of entity to fetch, select Threat.
- Enter the Tenant Name in case of MSSP user.
- Enter the connection parameters. (Host, Tenant, Username & Password)
- Enter the “The maximum number of incidents to fetch each time”. The recommended number of threats to fetch is 100 considering the API implications, although 200 is allowed.
- Enter the relevant values for Securonix Retry parameters “Count”, “Delay” & “Delay Type”.
Create a custom mapper consisting of the default Securonix mapper
- Go to the settings -> Object setup -> Incidents.
- Navigate to the “Classification and Mapping” tab.
- Select the Mapper “Securonix incident - Incoming Mapper”.
- Create a copy of that mapper and click on it. (You can rename the mapper.)
- Under the Incident Type dropdown, verify that the type of Mapper is “Securonix Incident”.
- Click on “Choose data path” and map it to the custom field:
- Find the context field you want to map to this incident field on the right side and click on its value.
- Then you will see the path you’ve selected under your newly added field.
- Note: You can also type the path manually.
- Click “Save Version”.
- Created mapper will appear in the drop-down for the “Mapper (incoming)” integration instance settings fields.
- Select the newly added mapper at the time of instance configuration.
Create a custom layout consisting of the default Securonix layout
- Go to the settings -> Object setup -> Incidents
- Navigate to the “Layouts” tab.
- Select the layout “Securonix Incident Information”.
- Create a copy of that layout and click on it. (You can rename the layout.)
- Select the newly created layout and click on edit.
- To create a new section, drag and drop the “New Section” widget into the layout.
- To add a new field to the layout, navigate to the “Fields and Buttons” section and search for the field. Drag and drop the field widget in the layout.
- Once done, select “Save Version”.
- Navigate to the “Incident Type” tab and select “Securonix Incident” type and detach it.
- Attach the newly created layout.
- Reattach the same “Incident Type” again else this incident type will not receive any new updates.
Note: It is recommended to use out-of-the-box mappers, layout & incident types for better visualization and meaningful mappings. If you are changing any out-of-the-box mappers/layout then it might not render all the fields as per the expectation.
Troubleshooting
Receive Notification on an Incident Fetch Error
The administrator and Cortex XSOAR users on the recipient’s list receive a notification when an integration experiences an incident fetch error. Cortex XSOAR users can select their notification method, such as email, from their user preferences. Refer to Cortex XSOAR 6.13 documentation or Cortex XSOAR 8 Cloud documentation or Cortex XSOAR 8.7 On-prem documentation for more information.
Input and output entries of the playbook are not visible in the war room
Follow the below steps and add a new server configuration:
- Go to Settings > About > Troubleshooting.
- Add new server configuration as “task.auto.quiet.mode.enabled” and enter the value as “false” and save. Refer to this XSOAR documentation
- After this configuration, all the output entries for the execution of the playbook gets displayed in the war room.
The following are tips for handling issues with mirroring incidents between Securonix and Cortex XSOAR.
| Issue | Recommendation |
|---|---|
| Mirroring is not working. | Open Context Data and search for dbot. Confirm the dbot fields are configured correctly either through the mapper for that specific incident type or using setIncident. Specifically, make sure the integration instance is configured correctly for the mirroring direction (Incoming, Outgoing, Both) - dbotMirrorId, dbotMirrorDirection, dbotMirrorInstance, dbotMirrorTags |
| Comments from before incident creation are not fetched/mirrored. | Mirroring settings apply only for incidents that are fetched after applying the settings. For example, if a user creates a Securonix incident with added comments and then defines a Securonix integration instance with mirroring in Cortex XSOAR, those comments are not fetched with the incident in Cortex XSOAR. |
| Incident is not closing. | Verify the integration instance is configured correctly for the Mirrored Securonix action and state fields. |
| Required fields are not getting sent or not visible in UI. | This may be a mapping issue, specifically if you have used a custom mapper make sure you’ve covered all the out of box mapper fields. |
| Comments from XSOAR have not been mirrored in Securonix | Tag is required for mirroring comments from Cortex XSOAR to Securonix. There might be a reason the comment is not tagged as tag needs to be added manually. Click Actions > Tags and add the “comments” tag (OR the specific tag name which was set up on Instance Configuration). |
| Viewing masked data on the XSOAR application | If you observe masked data, it is highly likely that ‘Masking’ is enabled on your Securonix tenant. Please check with your Securonix Administrator for further details. |
The securonix-list-violation-data command not returning the violations
- Check the
fromandtoarguments provided to the command. It should be ranging in the violations’ generation time.
Note: The following steps are compatible with the Securonix Build version 6.4_Apr2024_R1.
- If the
queryarguments contain the special characters, then check and follow the below steps:- Use the XSOAR automation browser to run the command.
- Don’t add the extra backslashes for the
*,?and\characters. This is handled by the command itself. Before upgrading, revert any temporary backslash solutions that may have been provided.
- If the
Securonix Policy Typeincident field is one of these (Land Speed, DIRECTIVE, BEACONING, TIER2) then try to execute command again with the additional ‘policy_type’ argument. The value for thepolicy_typeargument should be the same as the value for theSecuronix Policy Typeincident field.
The securonix-list-activity-data command not returning the activity data
- Check the
fromandtoarguments provided to the command. It should be ranging in the activity data generation time.
Note: The following steps are compatible with the Securonix Build version 6.4_Apr2024_R1.
- If the
queryarguments contain the special characters, then check and follow the below steps:- Use the XSOAR automation browser to run the command.
- Don’t add the extra backslashes for the
*,?and\characters. This is handled by the command itself. Before upgrading, revert any temporary backslash solutions that may have been provided.
Command HR or War room entries are breaking in the table view
- There might be some special characters (restricted by Markdown viewer i.e, \*_{}[]()#+-!`|) in the value of field which is breaking the view.
- Impact is in only on the HR output, however user can get exact values from the command context for such fields if any.
Commands
You can execute these commands from the Cortex XSOAR 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.
securonix-list-workflows
Gets a list of all available workflows.
Base Command
securonix-list-workflows
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Workflows.Workflow | String | Workflow name. |
| Securonix.Workflows.Type | String | Workflow type. |
| Securonix.Workflows.Value | String | Workflow value. |
Command Example
##### Context Example
```json
{
"Securonix": {
"Workflows": [
{
"Type": "USER",
"Value": "admin",
"Workflow": "SOCTeamReview"
},
{
"Type": "USER",
"Value": "admin",
"Workflow": "ActivityOutlierWorkflow"
},
{
"Type": "USER",
"Value": "admin",
"Workflow": "AccessCertificationWorkflow"
},
{
"Type": "USER",
"Value": "admin",
"Workflow": "test"
}
]
}
}
Human Readable Output
Available workflows
Workflow Type Value SOCTeamReview USER admin ActivityOutlierWorkflow USER admin AccessCertificationWorkflow USER admin test USER admin
securonix-get-default-assignee-for-workflow
Gets the default assignee for the specified workflow.
Base Command
securonix-get-default-assignee-for-workflow
Input
| Argument Name | Description | Required |
|---|---|---|
| workflow | Workflow name. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Workflows.Workflow | String | Workflow name. |
| Securonix.Workflows.Type | String | Workflow type. |
| Securonix.Workflows.Value | String | Workflow value. |
Command Example
!securonix-get-default-assignee-for-workflow workflow=SOCTeamReview
Context Example
{
"Securonix": {
"Workflows": {
"Type": "USER",
"Value": "admin",
"Workflow": "SOCTeamReview"
}
}
}
Human Readable Output
Default assignee for the workflow SOCTeamReview is: admin.
securonix-list-possible-threat-actions
Gets a list available threat actions.
Base Command
securonix-list-possible-threat-actions
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.ThreatActions | String | A list of threat actions. |
Command Example
##### Context Example
```json
{
"Securonix": {
"ThreatActions": [
"Mark as concern and create incident",
"Non-Concern",
"Mark in progress (still investigating)"
]
}
}
Human Readable Output
Possible threat actions are: Mark as concern and create incident, Non-Concern, Mark in progress (still investigating).
securonix-list-policies
Gets a list of all policies.
Base Command
securonix-list-policies
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Policies.CreatedBy | String | Creator of the policy. |
| Securonix.Policies.CreatedOn | Date | Policy created date. |
| Securonix.Policies.Criticality | String | Policy criticality. |
| Securonix.Policies.Description | String | Policy description. |
| Securonix.Policies.Hql | String | Policy Hibernate Query Language. |
| Securonix.Policies.ID | String | Policy ID. |
| Securonix.Policies.Name | String | Policy name. |
Command Example
##### Context Example
```json
{
"Securonix": {
"Policies": [
{
"CreatedBy": "admin",
"CreatedOn": "2013-11-09T16:13:23Z",
"Criticality": "Low",
"Description": null,
"Hql": "FROM AccessAccount AS accessaccount, Resources AS resources, AccessAccountUser AS accessaccountuser WHERE ((accessaccount.resourceid = resources.id AND accessaccountuser.id.accountid = accessaccount.id )) AND ((accessaccountuser.id.userid = '-1'))",
"ID": "1",
"Name": "Accounts that dont have Users"
},
{
"CreatedBy": "admin",
"CreatedOn": "2013-11-09T16:31:09Z",
"Criticality": "Medium",
"Description": null,
"Hql": "FROM Users AS users, AccessAccountUser AS accessaccountuser, AccessAccount AS accessaccount, Resources AS resources WHERE ((users.id = accessaccountuser.id.userid AND accessaccountuser.id.accountid = accessaccount.id AND accessaccount.resourceid = resources.id )) AND ((users.status = '0'))",
"ID": "2",
"Name": "Accounts that belong to terminated user"
},
]
}
}
Human Readable Output
Policies
ID Name Criticality Created On Created By Description 1 Accounts that dont have Users Low 2013-11-09T16:13:23Z admin 2 Accounts that belong to terminated user Medium 2013-11-09T16:31:09Z admin
securonix-list-resource-groups
Gets a list of resource groups.
Base Command
securonix-list-resource-groups
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.ResourceGroups.Name | String | Resource group name. |
| Securonix.ResourceGroups.Type | String | Resource group type. |
Command Example
##### Context Example
```json
{
"Securonix": {
"ResourceGroups": [
{
"Name": "Windows-CST1",
"Type": "Microsoft Windows SNARE"
},
{
"Name": "Websense Proxy",
"Type": "Websense Proxy Server"
},
{
"Name": "Palo Alto",
"Type": "Palo Alto Next-Generation Firewall"
},
{
"Name": "CDS1",
"Type": "ControlsDS1"
},
{
"Name": "Bluecoat",
"Type": "Bluecoat Proxy"
},
{
"Name": "Symantec-Email",
"Type": "Symantec Message Security Gateway"
},
{
"Name": "Proofpoint Email Gateway",
"Type": "Proofpoint Email Gateway"
},
{
"Name": "CiscoASA",
"Type": "Cisco ASA"
},
{
"Name": "CiscoAMP",
"Type": "Cisco FireAMP"
},
{
"Name": "PA800-adam",
"Type": "Palo Alto Next-Generation Firewall"
},
{
"Name": "CrowdStrike-PartnerAPI",
"Type": "Crowdstrike Alerts Streaming"
},
{
"Name": "squid-partners",
"Type": "Squid Proxy"
},
{
"Name": "Bluecoat_OP",
"Type": "Bluecat_DHCP"
},
{
"Name": "Bluecoat - Test",
"Type": "Bluecoat Proxy"
},
{
"Name": "Bluecoat_New",
"Type": "Bluecoat Proxy"
}
]
}
}
Human Readable Output
Resource groups
Name Type Windows-CST1 Microsoft Windows SNARE Websense Proxy Websense Proxy Server Palo Alto Palo Alto Next-Generation Firewall CDS1 ControlsDS1 Bluecoat Bluecoat Proxy Symantec-Email Symantec Message Security Gateway Proofpoint Email Gateway Proofpoint Email Gateway CiscoASA Cisco ASA CiscoAMP Cisco FireAMP PA800-adam Palo Alto Next-Generation Firewall CrowdStrike-PartnerAPI Crowdstrike Alerts Streaming squid-partners Squid Proxy Bluecoat_OP Bluecat_DHCP Bluecoat - Test Bluecoat Proxy Bluecoat_New Bluecoat Proxy
securonix-list-users
Gets a list of users.
Base Command
securonix-list-users
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Users.LastName | String | User last name. |
| Securonix.Users.SkipEncryption | String | Whether user encryption was skipped. |
| Securonix.Users.Riskscore | String | User risk score. |
| Securonix.Users.EmployeeID | String | User Employee ID. |
| Securonix.Users.Masked | String | Whether the user is masked. |
| Securonix.Users.Division | String | User division. |
| Securonix.Users.Criticality | String | User criticality. |
| Securonix.Users.Status | String | User status. |
| Securonix.Users.Department | String | User department. |
| Securonix.Users.Title | String | User title. |
| Securonix.Users.FirstName | String | User first name. |
| Securonix.Users.Email | String | User email address. |
Command Example
##### Context Example
```json
{
"Securonix": {
"Users": [
{
"ContractEndDate": "2020-01-14T00:40:44Z",
"Criticality": "Low",
"Department": "Data Services",
"Division": "Global Technology",
"Email": "jon.doe@test.com",
"EmployeeID": "1001",
"FirstName": "jon",
"LastName": "doe",
"Masked": "false",
"Riskscore": "0.0",
"SkipEncryption": "false",
"Status": "1",
"Title": "Associate-Data Services"
}
]
}
}
Human Readable Output
Resource groups
First Name Last Name Criticality Title jon doe Low Associate-Data Services jon.doe@test.com
securonix-list-activity-data
Gets a list of activity data for the specified resource group.
Base Command
securonix-list-activity-data
Input
| Argument Name | Description | Required |
|---|---|---|
| from | Start date/time for which to retrieve activity data (in the format MM/dd/yyyy HH:mm:ss). | Required |
| to | End date/time for which to retrieve activity data (in the format MM/dd/yyyy HH:mm:ss). | Required |
| query | Free-text query. For example, query=”resourcegroupname=WindowsSnare and policyname=Possible Privilege Escalation - Self Escalation”. Note: Use the XSOAR automation browser to run the command if the “query” argument contains the special characters. |
Optional |
| max | Maximum number of activity records to retrieve. Default is 1000. Maximum allowed value is 10000. | Optional |
Context Output
| Path | Type | Description |
|—————————————————-| — | — |
| Securonix.ActivityData.Accountname | String | Account name. |
| Securonix.ActivityData.Accountresourcekey | String | Account source key. |
| Securonix.ActivityData.Agentfilename | String | Agent file name. |
| Securonix.ActivityData.Baseeventid | String | Base event ID. |
| Securonix.ActivityData.Categorizedtime | String | Categorized time. |
| Securonix.ActivityData.Categorybehavior | String | Category behavior. |
| Securonix.ActivityData.Categoryobject | String | Category object. |
| Securonix.ActivityData.Categoryseverity | String | Category severity. |
| Securonix.ActivityData.Collectionmethod | String | Collection method. |
| Securonix.ActivityData.Collectiontimestamp | String | Collection timestamp. |
| Securonix.ActivityData.Customnumber1 | Number | Custom number. |
| Securonix.ActivityData.Customstring13 | String | Custom string. |
| Securonix.ActivityData.Customstring17 | String | Custom string. |
| Securonix.ActivityData.Customtime1 | String | Custom time. |
| Securonix.ActivityData.Customtime2 | String | Custom time. |
| Securonix.ActivityData.Datetime | String | Date time. |
| Securonix.ActivityData.Dayofmonth | String | Day of month. |
| Securonix.ActivityData.Dayofweek | String | Day of week. |
| Securonix.ActivityData.Dayofyear | String | Day of year. |
| Securonix.ActivityData.Destinationntdomain | String | Destination NT domain. |
| Securonix.ActivityData.Destinationprocessname | String | Destination process name. |
| Securonix.ActivityData.Destinationservicename | String | Destination service name. |
| Securonix.ActivityData.Destinationuserid | String | Destination user ID. |
| Securonix.ActivityData.Destinationusername | String | Destination username. |
| Securonix.ActivityData.Deviceaddress | String | Device address. |
| Securonix.ActivityData.Destinationuserprivileges | String | Destination user privileges. |
| Securonix.ActivityData.Devicecustomstring4 | String | Device custom string. |
| Securonix.ActivityData.Deviceeventcategory | String | Device event category. |
| Securonix.ActivityData.Deviceexternalid | String | Device external ID. |
| Securonix.ActivityData.Devicehostname | String | Device hostname. |
| Securonix.ActivityData.Ehash | String | Event hash. |
| Securonix.ActivityData.EventID | String | Event ID. |
| Securonix.ActivityData.Eventoutcome | String | Event outcome. |
| Securonix.ActivityData.Eventtime | String | Time the event occurred. |
| Securonix.ActivityData.Filepath | String | File path. |
| Securonix.ActivityData.Filepermission | String | File permission. |
| Securonix.ActivityData.Hour | String | Date time hour. |
| Securonix.ActivityData.ID | String | Activity ID. |
| Securonix.ActivityData.Ingestionnodeid | String | Ingestion node ID. |
| Securonix.ActivityData.Ipaddress | String | IP address. |
| Securonix.ActivityData.Ipaddress_Long | String | IP address long. |
| Securonix.ActivityData.JobID | String | Deprecated. Use the Securonix.ActivityData.Jobid field. |
| Securonix.ActivityData.Jobid | String | Job ID. |
| Securonix.ActivityData.Jobstarttime | String | Job start time. |
| Securonix.ActivityData.Message | String | Message. |
| Securonix.ActivityData.Minute | String | Date time minute. |
| Securonix.ActivityData.Month | String | Month. |
| Securonix.ActivityData.Oldfileid | String | Old file ID. |
| Securonix.ActivityData.Oldfilepath | String | Old file path. |
| Securonix.ActivityData.Others | String | Others. |
| Securonix.ActivityData.Poprocessedtime | String | PO processed time. |
| Securonix.ActivityData.Publishedtime | String | Published time. |
| Securonix.ActivityData.Rawevent | String | Raw event. |
| Securonix.ActivityData.Raweventsize | String | Raw event size. |
| Securonix.ActivityData.Receivedtime | String | Received time. |
| Securonix.ActivityData.Resourcename | String | Resource name. |
| Securonix.ActivityData.ResourceGroupCategory | String | Resource group category. |
| Securonix.ActivityData.ResourceGroupFunctionality | String | Resource group functionality. |
| Securonix.ActivityData.ResourceGroupID | String | Resource group ID. |
| Securonix.ActivityData.ResourceGroupName | String | Resource group name. |
| Securonix.ActivityData.ResourceGroupTimezoneoffset | String | Resource Group Timezone offset. |
| Securonix.ActivityData.ResourceGroupTypeID | String | Resource group resource type ID. |
| Securonix.ActivityData.ResourceGroupVendor | String | Resource group vendor. |
| Securonix.ActivityData.Resourcegroupid | String | Resource Group ID. |
| Securonix.ActivityData.Resourcegroupname | String | Resource Group Name. |
| Securonix.ActivityData.Resourcehostname | String | Resource host name. |
| Securonix.ActivityData.Resourcehostname_Long | String | Resource host name long. |
| Securonix.ActivityData.Resourcename | String | Resource name. |
| Securonix.ActivityData.Resourcetype | String | Resource type. |
| Securonix.ActivityData.Sessionid | String | Session ID. |
| Securonix.ActivityData.Sourceaddress | String | Source address. |
| Securonix.ActivityData.Sourceaddress_Long | String | Source address long. |
| Securonix.ActivityData.Sourcehostname | String | Source hostname. |
| Securonix.ActivityData.Sourcentdomain | String | Source domain. |
| Securonix.ActivityData.Sourceport | String | Source port. |
| Securonix.ActivityData.Sourceprocessname | String | Source process name. |
| Securonix.ActivityData.Sourceuserid | String | Source user ID. |
| Securonix.ActivityData.Sourceusername | String | Source username. |
| Securonix.ActivityData.TenantID | String | Tenant ID. |
| Securonix.ActivityData.Tenantname | String | Tenant name. |
| Securonix.ActivityData.Timeline | String | Time when the activity occurred, in Epoch time. |
| Securonix.ActivityData.Timeline_By_Hour | String | Timeline by hour. |
| Securonix.ActivityData.Timeline_By_Minute | String | Timeline by minute. |
| Securonix.ActivityData.Timeline_By_Month | String | Timeline by month. |
| Securonix.ActivityData.Timeline_By_Week | String | Timeline by week. |
| Securonix.ActivityData.Timestamp | String | Timestamp. |
| Securonix.ActivityData.Transactionstring1 | String | Transaction string 1. |
| Securonix.ActivityData.Userid | String | User ID. |
| Securonix.ActivityData.Week | String | Week. |
| Securonix.ActivityData.Year | String | Year. |
| Securonix.ActivityData.Indexed_At_Tdt | String | Indexed at TDT. |
| Securonix.ActivityData._Version | String | Activity version. |
| Securonix.Activity.totalDocuments | Number | Total number of events. |
| Securonix.Activity.message | String | Message from the API. |
| Securonix.Activity.queryId | String | Query Id for the pagination. |
| Securonix.Activity.command_name | String | The command name. |
Command example
!securonix-list-activity-data from="01/12/2024 10:00:00" to="01/15/2024 12:01:00"
Context Example
{
"Securonix": {
"Activity": {
"queryId": "spotter_web_service_00000000-0000-0000-0000-000000000001",
"totalDocuments": 1182,
"command_name": "securonix-list-activity-data"
},
"ActivityData": [
{
"Accountname": "ACCOUNT_001",
"Accountresourcekey": "00000000000~000000000.0000.com~pipe_line_test~0000~-1",
"Agentfilename": "test.txt",
"Categorybehavior": "Account Create",
"Categoryobject": "Account Management",
"Categoryseverity": "0",
"Collectionmethod": "file",
"Collectiontimestamp": "1690803374000",
"Destinationusername": "TEST134044",
"Devicehostname": "HOST.com",
"EventID": "00000000-0000-0000-0000-000000000001",
"Eventoutcome": "Success",
"Filepath": "N/A",
"Ingestionnodeid": "CONSOLE",
"Jobstarttime": "1690803374000",
"Message": "A user account was created.",
"Publishedtime": "1690803374572",
"Receivedtime": "1690803420706",
"Resourcename": "HOST.com",
"Sourceusername": "USER",
"TenantID": "2",
"Tenantname": "Response-Automation",
"Timeline": "1670911200000"
},
{
"Accountname": "ACCOUNT_002",
"Accountresourcekey": "00000000000~000000000.0000.com~pipe_line_test~0000~-2",
"Agentfilename": "test.txt",
"Categorybehavior": "Account Create",
"Categoryobject": "Account Management",
"Categoryseverity": "0",
"Collectionmethod": "file",
"Collectiontimestamp": "1690803374000",
"Destinationusername": "TEST134044",
"Devicehostname": "HOST.com",
"EventID": "00000000-0000-0000-0000-000000000002",
"Eventoutcome": "Success",
"Filepath": "N/A",
"Ingestionnodeid": "CONSOLE",
"Jobstarttime": "1690803374000",
"Message": "A user account was created.",
"Publishedtime": "1690803374572",
"Receivedtime": "1690803420500",
"Resourcename": "HOST.com",
"Sourceusername": "USER",
"TenantID": "2",
"Tenantname": "Response-Automation",
"Timeline": "1670911200000"
}
]
}
}
Human Readable Output
Activity data
EventID Message Accountname Timeline Devicehostname Accountresourcekey 00000000-0000-0000-0000-000000000001 A user account was created. ACCOUNT_001 2024-01-13T06:00:00.000Z HOST.com 00000000000~000000000.0000.com~pipe_line_test~0000~-1 00000000-0000-0000-0000-000000000002 A user account was created. ACCOUNT_002 2024-01-13T06:00:00.000Z HOST.com 00000000000~000000000.0000.com~pipe_line_test~0000~-2
securonix-list-violation-data
Gets a list activity data for an account name.
Base Command
securonix-list-violation-data
Input
| Argument Name | Description | Required |
|---|---|---|
| from | Start date/time for which to retrieve activity data (in the format MM/dd/yyyy HH:mm:ss). | Required |
| to | End date/time for which to retrieve activity data (in the format MM/dd/yyyy HH:mm:ss). | Required |
| query | Free-text query. For example, query=”resourcegroupname=WindowsSnare and policyname=Possible Privilege Escalation - Self Escalation”. Note: Use the XSOAR automation browser to run the command if the “query” argument contains the special characters. |
Optional |
| query_id | Paginate next set of results. | Optional |
| policy_type | Provide the policy type for retrying if the violations are not found in the initial attempt. The types of policies that can be retried are: “Land Speed”, “TIER2”, “DIRECTIVE”, “BEACONING”. | Optional |
| max | Maximum number of violations to retrieve. Default is 1000. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.ViolationData.Accountname | String | Account name. |
| Securonix.ViolationData.Agentfilename | String | Agent file name. |
| Securonix.ViolationData.Baseeventid | String | Base event ID. |
| Securonix.ViolationData.Categorybehavior | String | Category behavior. |
| Securonix.ViolationData.Category | String | Violation category. |
| Securonix.ViolationData.Categoryobject | String | Category object. |
| Securonix.ViolationData.Categoryseverity | String | Category severity. |
| Securonix.ViolationData.Destinationaddress | String | Destination address. |
| Securonix.ViolationData.Destinationntdomain | String | Destination nt domain. |
| Securonix.ViolationData.Destinationuserid | String | Destination user ID. |
| Securonix.ViolationData.Gestinationusername | String | Destination username. |
| Securonix.ViolationData.Deviceaddress | String | Device address. |
| Securonix.ViolationData.Deviceeventcategory | String | Device event category. |
| Securonix.ViolationData.Deviceexternalid | String | Device external ID. |
| Securonix.ViolationData.Devicehostname | String | Device hostname. |
| Securonix.ViolationData.EventID | String | Event ID. |
| Securonix.ViolationData.Eventoutcome | String | Event outcome. |
| Securonix.ViolationData.Eventtime | String | Time the event occurred. |
| Securonix.ViolationData.Generationtime | String | Time that the violation was generated in Securonix. |
| Securonix.ViolationData.Invalid | String | Whether the violation is valid. |
| Securonix.ViolationData.JobID | String | Job ID. |
| Securonix.ViolationData.Jobstarttime | String | Job start time. |
| Securonix.ViolationData.Policyname | String | Policy name. |
| Securonix.ViolationData.Resourcename | String | Resource name. |
| Securonix.ViolationData.ResourceGroupID | String | Resource group ID. |
| Securonix.ViolationData.ResourceGroupName | String | Resource group name. |
| Securonix.ViolationData.Riskscore | String | Risk score. |
| Securonix.ViolationData.Riskthreatname | String | Risk threat name. |
| Securonix.ViolationData.Sessionid | String | Session ID. |
| Securonix.ViolationData.Sourcehostname | String | Source hostname. |
| Securonix.ViolationData.Sourcentdomain | String | Source nt domain. |
| Securonix.ViolationData.Sourceuserid | String | Source user ID. |
| Securonix.ViolationData.Sourceusername | String | Source username. |
| Securonix.ViolationData.Sourceuserprivileges | String | Source user privileges. |
| Securonix.ViolationData.TenantID | String | Tenant ID. |
| Securonix.ViolationData.Tenantname | String | Tenant name. |
| Securonix.ViolationData.Timeline | String | Time when the activity occurred, in Epoch time. |
| Securonix.ViolationData.Createdate | String | Create date. |
| Securonix.ViolationData.Criticality | String | Violation criticality. |
| Securonix.ViolationData.DataSourceID | String | Data source ID. |
| Securonix.ViolationData.Department | String | Department affected by the violation. |
| Securonix.ViolationData.EmployeeID | String | Employee ID. |
| Securonix.ViolationData.Encrypted | String | Whether the violation is encrypted. |
| Securonix.ViolationData.Firstname | String | First name of the user that violated the policy. |
| Securonix.ViolationData.Fullname | String | Full name of the user that violated the policy. |
| Securonix.ViolationData.ID | String | ID of the user that violated the policy. |
| Securonix.ViolationData.LanID | String | LAN ID associated with the policy violation. |
| Securonix.ViolationData.Lastname | String | Last name of the user that violated the policy. |
| Securonix.ViolationData.Lastsynctime | String | Last sync time, in Epoch time. |
| Securonix.ViolationData.Masked | String | Whether the violation is masked. |
| Securonix.ViolationData.Mergeuniquecode | String | Merge unique code. |
| Securonix.ViolationData.Riskscore | String | Risk score. |
| Securonix.ViolationData.Skipencryption | String | Skip encryption. |
| Securonix.ViolationData.Status | String | Status of the policy violation. |
| Securonix.ViolationData.Timezoneoffset | String | Timezone offset. |
| Securonix.ViolationData.Title | String | Title. |
| Securonix.ViolationData.Uniquecode | String | Unique code. |
| Securonix.ViolationData.UserID | String | Last sync time, in Epoch time. |
| Securonix.ViolationData.Workemail | String | Work email address of the user that violated the policy. |
| Securonix.ViolationData.Violator | String | Violator. |
| Securonix.Violation.totalDocuments | Number | Total number of events. |
| Securonix.Violation.message | String | Message from the API. |
| Securonix.Violation.queryId | String | Query Id for the pagination. |
Command Example
!securonix-list-violation-data from="01/01/2023 00:00:00" to="03/10/2023 00:00:00"
Context Example
{
"Securonix": {
"ViolationData": [
{
"Accountname": "TESTDPB30",
"Categorizedtime": "Morning",
"Category": "Account Compromise",
"Categoryseverity": "0",
"Datetime": "1676960205747",
"Dayofmonth": "21",
"Dayofweek": "3",
"Dayofyear": "52",
"Eventid": "test_event",
"GenerationTime": "02/21/2023 00:17:19",
"Hour": "0",
"ID": "-1",
"Invalid": "false",
"Ipaddress": "IP Address",
"Jobid": "140",
"Jobstarttime": "1676960199000",
"Month": "1",
"Policyname": "Policy1",
"Resourcecomments": "ingestion_2.0",
"Resourcegroupid": "6",
"Resourcegroupname": "test",
"Resourcename": "mac30",
"Resourcetype": "mvk",
"Riskthreatname": "Abnormal amount of data egressed compared to peer",
"TenantID": "2",
"Tenantname": "test_tenant",
"Timeline": "1676959200000",
"Timeline_By_Hour": "1676980800000",
"Timeline_By_Minute": "1676960100000",
"Timeline_By_Month": "1675231200000",
"Timeline_By_Week": "1676786400000",
"Transactionstring1": "Logon failure 30",
"Userid": "-1",
"Violator": "RTActivityAccount",
"Week": "8",
"Year": "2023"
},
{
"Accountname": "TESTDPB30",
"Categorizedtime": "Morning",
"Category": "Account Compromise",
"Categoryseverity": "0",
"Datetime": "1676960205747",
"Dayofmonth": "21",
"Dayofweek": "3",
"Dayofyear": "52",
"Eventid": "Event ID",
"GenerationTime": "02/21/2023 00:17:19",
"Hour": "0",
"ID": "-1",
"Invalid": "false",
"Ipaddress": "IP Address",
"Jobid": "140",
"Jobstarttime": "1676960199000",
"Month": "1",
"Policyname": "Policy2",
"Resourcecomments": "ingestion_2.0",
"Resourcegroupid": "6",
"Resourcegroupname": "tets",
"Resourcename": "mac30",
"Resourcetype": "mvk",
"Riskthreatname": "Abnormal attempts to reset domain admin password",
"TenantID": "2",
"Tenantname": "test_tenant",
"Timeline": "1676959200000",
"Timeline_By_Hour": "1676980800000",
"Timeline_By_Minute": "1676960100000",
"Timeline_By_Month": "1675231200000",
"Timeline_By_Week": "1676786400000",
"Transactionstring1": "Logon failure 30",
"Userid": "-1",
"Violator": "RTActivityAccount",
"Week": "8",
"Year": "2023"
}
],
"Violation": {
"totalDocuments": 2,
"message": "",
"queryId": "abcd1234"
}
}
}
Human Readable Output
Activity data
Policyname Accountname Policy1 TESTDPB30 Policy2 TESTDPB30 Next page query id: abcd1234
securonix-list-incidents
Gets a list of incidents.
Base Command
securonix-list-incidents
Input
| Argument Name | Description | Required |
|---|---|---|
| from | Start time range for which to return incidents (<number> <time unit>, e.g., 1 hour, 30 minutes) |
Required |
| to | End date/time for which to retrieve incidents (in the format MM/dd/yyyy HH:mm:ss) Default is current time. | Optional |
| incident_types | The incident type. Can be “updated”, “opened”, or “closed”. Supports multiple selections. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Incidents.ViolatorID | String | Incident Violator ID. |
| Securonix.Incidents.Entity | String | Incident entity. |
| Securonix.Incidents.Riskscore | Number | Incident risk score. |
| Securonix.Incidents.Priority | String | Incident priority. |
| Securonix.Incidents.Reason | String | Reason for the incident. Usually includes policy name and/or possible threat name. |
| Securonix.Incidents.IncidentStatus | String | Incident status. |
| Securonix.Incidents.WorkflowName | String | Incident workflow name. |
| Securonix.Incidents.Watchlisted | Boolean | Whether the incident is in a watchlist. |
| Securonix.Incidents.IncidentType | String | Incident type. |
| Securonix.Incidents.IncidentID | String | Incident ID. |
| Securonix.Incidents.LastUpdateDate | Number | Last update date of the incident in Epoch time. |
| Securonix.Incidents.Url | String | URL that links to the incident on Securonix. |
| Securonix.Incidents.ViolatorText | String | Incident violator text. |
| Securonix.Incidents.AssignedUser | String | User assigned to the incident. |
| Securonix.Incidents.IsWhitelisted | Boolean | Whether the incident is added to allow list. |
| Securonix.Incidents.Policystarttime | Number | Epoch time when the policy is first violated. |
| Securonix.Incidents.Policyendtime | Number | Epoch time when the policy is last violated. |
| Securonix.Incidents.Solrquery | String | Spotter query to fetch the related violations. |
Command Example
!securonix-list-incidents from="5 days" incident_types=opened
Context Example
{
"Securonix": {
"Incidents": [
{
"AssignedUser": "Admin Admin",
"Bulkactionallowed": true,
"CaseEventEndTime": 1675849673983,
"CaseEventStartTime": 1675845486324,
"Casecreatetime": 1675849649900,
"Entity": "RTActivityAccount",
"IncidentID": "30107",
"IncidentStatus": "COMPLETED",
"IncidentType": "HighRiskRTActivityAccount",
"IsWhitelisted": false,
"LastUpdateDate": 1675849674032,
"ParentCaseId": "",
"Priority": "None",
"Reason": [
"ResourceType: mvk",
"Policy: SandboxFeb8",
"Threat: Abnormal attempts to reset domain admin password"
],
"Riskscore": 0,
"SandBoxPolicy": true,
"StatusCompleted": true,
"TenantInfo": {
"tenantid": 2,
"tenantname": "test_tenant"
},
"Type": "HighRiskRTActivityAccount",
"Url": "url",
"ViolatorID": "TESTING2",
"ViolatorText": "TESTING2",
"Watchlisted": false,
"WorkflowName": "SOCTeamReview",
"Policystarttime": 1692950376801,
"Policyendtime": 1695613655539,
"Solrquery": "index = violation and ( ( @policyname = \"Response-PB-Resources-AutoPlay\" and @resourcename=\"Activityres17-Resource-549829\" ) ) AND @tenantname=\"Response-Automation\" AND datetime between \"02/07/2023 15:52:12\" \"02/07/2023 15:52:13\""
},
{
"AssignedUser": "Admin Admin",
"Bulkactionallowed": true,
"CaseEventEndTime": 1675851019318,
"CaseEventStartTime": 1675850440699,
"Casecreatetime": 1675850942351,
"Entity": "RTActivityAccount",
"IncidentID": "30108",
"IncidentStatus": "COMPLETED",
"IncidentType": "HighRiskRTActivityAccount",
"IsWhitelisted": true,
"LastUpdateDate": 1675851019367,
"ParentCaseId": "",
"Priority": "None",
"Reason": [
"ResourceType: mvk",
"Policy: SandboxFeb8",
"Threat: Abnormal attempts to reset domain admin password"
],
"Riskscore": 0,
"SandBoxPolicy": true,
"StatusCompleted": true,
"TenantInfo": {
"tenantid": 2,
"tenantname": "test_tenant"
},
"Type": "HighRiskRTActivityAccount",
"Url": "url",
"ViolatorID": "TESTING4",
"ViolatorText": "TESTING4",
"Watchlisted": false,
"WorkflowName": "SOCTeamReview",
"Policystarttime": 1692950376801,
"Policyendtime": 1695613655539,
"Solrquery": "index = violation and ( ( @policyname = \"Response-PB-Resources-AutoPlay\" and @resourcename=\"Activityres17-Resource-549829\" ) ) AND @tenantname=\"Response-Automation\" AND datetime between \"02/07/2023 15:52:12\" \"02/07/2023 15:52:13\""
}
]
}
}
Human Readable Output
Incidents
IncidentID Incident Status Incident Type Priority Reason 30107 COMPLETED HighRiskRTActivityAccount None ResourceType: mvk,
Policy: SandboxFeb8,
Threat: Abnormal attempts to reset domain admin password30108 COMPLETED HighRiskRTActivityAccount None ResourceType: mvk,
Policy: SandboxFeb8,
Threat: Abnormal attempts to reset domain admin password
securonix-get-incident
Gets details of the specified incident.
Base Command
securonix-get-incident
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Incidents.ViolatorID | String | Incident violator ID. |
| Securonix.Incidents.Entity | String | Incident entity. |
| Securonix.Incidents.Riskscore | Number | Incident risk score. |
| Securonix.Incidents.Priority | String | Incident priority. |
| Securonix.Incidents.Reason | String | Reason for the incident. Usually includes policy name and/or possible threat name. |
| Securonix.Incidents.IncidentStatus | String | Incident status. |
| Securonix.Incidents.WorkflowName | String | Incident workflow name. |
| Securonix.Incidents.Watchlisted | Boolean | Whether the incident is in a watchlist. |
| Securonix.Incidents.IncidentType | String | Incident type. |
| Securonix.Incidents.IncidentID | String | Incident ID. |
| Securonix.Incidents.LastUpdateDate | Number | The time when the incident was last updated, in Epoch time. |
| Securonix.Incidents.Url | String | URL that links to the incident on Securonix. |
| Securonix.Incidents.ViolatorText | String | Incident violator text. |
| Securonix.Incidents.AssignedUser | String | User assigned to the incident. |
| Securonix.Incidents.IsWhitelisted | Boolean | Whether the incident is added to allow list. |
| Securonix.Incidents.Policystarttime | Number | Epoch time when the policy is first violated. |
| Securonix.Incidents.Policyendtime | Number | Epoch time when the policy is last violated. |
| Securonix.Incidents.Solrquery | String | Spotter query to fetch the related violations. |
Command Example
!securonix-get-incident incident_id=30107
Context Example
{
"Securonix": {
"Incidents": {
"AssignedUser": "Admin Admin",
"Casecreatetime": 1579687173702,
"Entity": "Users",
"IncidentID": "30107",
"IncidentStatus": "Open",
"IncidentType": "Policy",
"IsWhitelisted": false,
"LastUpdateDate": 1579687173702,
"ParentCaseId": "",
"Priority": "Critical",
"Reason": [
"Resource: BLUECOAT",
"Policy: Uploads to personal websites",
"Threat: Data egress via network uploads"
],
"Riskscore": 0,
"SandBoxPolicy": false,
"StatusCompleted": false,
"TenantInfo": {
"tenantcolor": "#000000",
"tenantid": 1,
"tenantname": "Securonix",
"tenantshortcode": "SE"
},
"Url": "url",
"ViolatorID": "9",
"ViolatorSubText": "1009",
"ViolatorText": "Judi Mcabee",
"Watchlisted": false,
"WorkflowName": "SOCTeamReview",
"Policystarttime": 1692950376801,
"Policyendtime": 1695613655539,
"Solrquery": "index = violation and ( ( @policyname = \"Response-PB-Resources-AutoPlay\" and @resourcename=\"Activityres17-Resource-549829\" ) ) AND @tenantname=\"Response-Automation\" AND datetime between \"02/07/2023 15:52:12\" \"02/07/2023 15:52:13\""
}
}
}
Human Readable Output
Incident
Assigned User Casecreatetime Entity Incident Status Incident Type IncidentID Is Whitelisted Last Update Date Priority Reason Riskscore Sand Box Policy Status Completed Tenant Info Url Violator Sub Text Violator Text ViolatorID Watchlisted Workflow Name Policystarttime Policyendtime Solrquery Admin Admin 1579687173702 Users Open Policy 30107 false 1579687173702 Critical Resource: BLUECOAT,Policy: Uploads to personal websites,Threat: Data egress via network uploads 0.0 false false tenantid: 1 tenantname: {name} url 1009 john smith 9 false SOCTeamReview 1692950376801 1695613655539 index = violation and ( ( @policyname = "Response-PB-Resources-AutoPlay" and @resourcename="Activityres17-Resource-549829" ) ) AND @tenantname="Response-Automation" AND datetime between "02/07/2023 15:52:12" "02/07/2023 15:52:13"
securonix-get-incident-status
Gets the status of the specified incident.
Base Command
securonix-get-incident-status
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Incidents.IncidentStatus | String | Incident status. |
| Securonix.Incidents.IncidentID | String | Incident ID. |
Command Example
!securonix-get-incident-status incident_id=30107
Context Example
{
"Securonix": {
"Incidents": {
"IncidentID": "30107",
"IncidentStatus": "Open"
}
}
}
Human Readable Output
Incident 30107 status is Open.
securonix-get-incident-workflow
Gets the workflow of the specified incident.
Base Command
securonix-get-incident-workflow
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Incidents.Workflow | String | Incident workflow. |
| Securonix.Incidents.IncidentID | String | Incident ID. |
Command Example
!securonix-get-incident-workflow incident_id=30107
Context Example
{
"Securonix": {
"Incidents": {
"IncidentID": "30107",
"WorkflowName": "SOCTeamReview"
}
}
}
Human Readable Output
Incident 30107 workflow is SOCTeamReview.
securonix-get-incident-available-actions
Gets a list of available actions for the specified incident.
Base Command
securonix-get-incident-available-actions
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID. | Required |
Context Output
There is no context output for this command.
Command Example
!securonix-get-incident-available-actions incident_id=30107
Context Example
{
"Securonix": {
"Incidents": {
"AvailableActions": [
"CLAIM",
"ASSIGN TO ANALYST",
"ASSIGN TO SECOPS"
],
"IncidentID": "30107"
}
}
}
Human Readable Output
Incident 30107 available actions: [‘CLAIM’, ‘ASSIGN TO ANALYST’, ‘ASSIGN TO SECOPS’].
securonix-perform-action-on-incident
Performs an action on the specified incident.
Base Command
securonix-perform-action-on-incident
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID. | Required |
| action | Action to perform on the incident. You can see them using securonix-get-incident-available-actions. e.g: “CLAIM”, “ASSIGN TO SECOPS”, “ASSIGN TO ANALYST”, “RELEASE”, or “COMMENT”. | Required |
| action_parameters | The parameters, if needed, to perform the action. e.g, For the ASSIGN TO ANALYST action: assigntouserid={user_id},assignedTo=USER. | Optional |
Context Output
There is no context output for this command.
Command Example
!securonix-perform-action-on-incident action="Close Incident" incident_id=330365
Context Example
{}
Human Readable Output
Action Close Incident was performed on incident 330365.
securonix-add-comment-to-incident
Adds a comment to the specified incident.
Base Command
securonix-add-comment-to-incident
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID. | Required |
| comment | Comment to add to the incident. | Required |
Context Output
There is no context output for this command.
Command Example
!securonix-add-comment-to-incident incident_id=30107 comment="Just a comment"
Context Example
{}
Human Readable Output
Comment was added to the incident 30107 successfully.
securonix-list-watchlists
Gets a list of watchlists.
Base Command
securonix-list-watchlists
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.WatchlistsNames | String | Watchlist names. |
Command Example
##### Context Example
```json
{
"Securonix": {
"WatchlistsNames": {
"Bad_Performance_Review": "0",
"Contractors-UpComing_Termination": "0",
"Domain_Admin": "0",
"Employees-UpComing_Terminations": "0",
"Exiting_Behavior_Watchlist": "0",
"Flight_Risk_Users_Watchlist": "0",
"Privileged_Accounts": "0",
"Privileged_Users": "0",
"Recent_Hires": "0",
"Recent_Transfers": "0",
"Terminated_Contractors": "0",
"Terminated_Employees": "0",
"Test_watchlist": "0",
"Test_watchlist2": "0"
}
}
}
Human Readable Output
Watchlists: Domain_Admin, Flight_Risk_Users_Watchlist, Recent_Transfers, Exiting_Behavior_Watchlist, Test_watchlist2, Bad_Performance_Review, Terminated_Contractors, Contractors-UpComing_Termination, Privileged_Accounts, Terminated_Employees, Test_watchlist, Privileged_Users, Recent_Hires, Employees-UpComing_Terminations.
securonix-get-watchlist
Gets information for the specified watchlist.
Base Command
securonix-get-watchlist
Input
| Argument Name | Description | Required |
|---|---|---|
| watchlist_name | Watchlist name. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Watchlists.TenantID | String | Watchlist tenant ID. |
| Securonix.Watchlists.Tenantname | String | Watchlist tenant name. |
| Securonix.Watchlists.Type | String | Watchlist type. |
| Securonix.Watchlists.Watchlistname | String | Watchlist name. |
| Securonix.Watchlists.Events.ExpiryDate | String | Expiration date of the entity in the watchlist, in Epoch time. |
| Securonix.Watchlists.Events.Workemail | String | Work email address of the entity in the watchlist. |
| Securonix.Watchlists.Events.Fullname | String | Full name of the entity in the watchlist. |
| Securonix.Watchlists.Events.Reason | String | Reason that the entity is in the watchlist. |
| Securonix.Watchlists.Events.LanID | String | Lan ID of the entity in the watchlist. |
| Securonix.Watchlists.Events.Lastname | String | Last name of the entity in the watchlist. |
| Securonix.Watchlists.Events.EntityName | String | Entity name of the entity in the watchlist. |
| Securonix.Watchlists.Events.Title | String | Title of the entity in the watchlist. |
| Securonix.Watchlists.Events.Firstname | String | First name of the entity in the watchlist. |
| Securonix.Watchlists.Events.EmployeeID | String | Employee ID of the entity in the watchlist. |
| Securonix.Watchlists.Events.Masked | String | Whether the entity in the watchlist is masked. |
| Securonix.Watchlists.Events.Division | String | Division of the entity in the watchlist. |
| Securonix.Watchlists.Events.Departmant | String | Department of the entity in the watchlist. |
| Securonix.Watchlists.Events.Status | String | Status of the entity in the watchlist. |
Command Example
!securonix-get-watchlist watchlist_name="test_watchlist"
Context Example
{
"Securonix": {
"Watchlists": {
"Events": [{
"Createdate": "1678438310720",
"Entityname": "123",
"Expired": "false",
"Expirydate": "1681084799000",
"Reason": "Added from web service..!",
"Resourcegroupid": "-1",
"Resourcename": "123",
"Uniqueid": "2^~R^~-1|123",
"Updatedate": "1678438310720"
}],
"TenantID": "2",
"TenantName": "test_tenant",
"Type": "Resources",
"Watchlistname": "test_watchlist"
}
}
}
Human Readable Output
Watchlist test_watchlist of type Resources
Entityname Expired 123 false
securonix-create-watchlist
Creates a watchlist in Securonix.
Base Command
securonix-create-watchlist
Input
| Argument Name | Description | Required |
|---|---|---|
| watchlist_name | The name of the watchlist. | Required |
| tenant_name | Name of the tenant the watchlist belongs to. The tenant name parameter is required for MSSP users. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Watchlists.Watchlistname | String | Name of the Watchlist. |
| Securonix.Watchlists.TenantName | String | Tenant Name. |
Command Example
!securonix-create-watchlist watchlist_name=test_watchlist
Context Example
{
"Securonix": {
"Watchlists": "test_watchlist"
}
}
Human Readable Output
Watchlist test_watchlist was created successfully.
securonix-check-entity-in-watchlist
Checks if the specified entity is in a watchlist.
Base Command
securonix-check-entity-in-watchlist
Input
| Argument Name | Description | Required |
|---|---|---|
| entity_name | The name of the entity to check. For example: 1002. | Required |
| watchlist_name | The name of the watchlist in which to check the entity. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.EntityInWatchlist.Watchlistnames | String | The names of the watchlists in which the entity appears. |
| Securonix.EntityInWatchlist.EntityID | String | The entity ID. |
Command Example
!securonix-check-entity-in-watchlist entity_name=1002 watchlist_name=test_watchlist
Context Example
{
"Securonix": {
"EntityInWatchlist": {
"Entityname": "1002"
}
}
}
Human Readable Output
Entity unique identifier 1002 provided is not in the watchlist: test_watchlist.
securonix-add-entity-to-watchlist
Adds an entity to a watchlist.
Base Command
securonix-add-entity-to-watchlist
Input
| Argument Name | Description | Required |
|---|---|---|
| watchlist_name | The name of the watchlist to which to add the entity. | Required |
| entity_type | The entity type. Can be “Users”, “Activityaccount”, “RGActivityaccount”, “Resources”, or “Activityip”. | Required |
| entity_name | The name of the entity to add to the watchlist. For example: 1022. | Required |
| expiry_days | The number of days after which the entity will be removed from the watchlist. The default value is “30”. | Optional |
Context Output
There is no context output for this command.
Command Example
!securonix-add-entity-to-watchlist entity_type=Resources entity_name=123 watchlist_name=test_watchlist
Context Example
{}
Human Readable Output
Added successfully the entity 123 to the watchlist test_watchlist.
securonix-create-incident
Creates an incident. For more information about the required arguments, see the Securonix documentation.
Base Command
securonix-create-incident
Input
| Argument Name | Description | Required |
|---|---|---|
| violation_name | The violation name or policy name. For example: “Uploads to personal Websites”. | Required |
| resource_group | The resource group name. For example: “BLUECOAT”, “Palo Alto Firewall”. | Required |
| entity_type | The entity type. Can be “Users”, “Activityaccount”, “RGActivityaccount”, “Resources”, or “Activityip”. | Required |
| entity_name | The entity name associated with the violation. Can be “LanID” or “Workemail”. For more information, see the Securonix documentation. | Required |
| action_name | The action name. Can be “Mark as concern and create incident”, “Non-Concern”, or “Mark in progress (still investigating)”. | Required |
| resource_name | The resource name. For example: “BLUECOAT”, “Palo Alto Firewall”. | Required |
| criticality | The incident severity (criticality) for the new incident. Can be “Low”, “High”, or “Critical”. | Optional |
| comment | A comment for the new incident. | Optional |
| workflow | The workflow name. This argument is optional, but required when the action_name argument is set to “Mark as concern and create incident”. Can be “SOCTeamReview”, “ActivityOutlierWorkflow”, or “AccessCertificationWorkflow”. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Incidents.ViolatorID | String | The ID of the incident violator. |
| Securonix.Incidents.Entity | String | The incident entity. |
| Securonix.Incidents.Riskscore | Number | The incident risk score. |
| Securonix.Incidents.Priority | String | The incident priority. |
| Securonix.Incidents.Reason | String | The reason that the incident was created. Usually includes the policy name and/or possible threat name. |
| Securonix.Incidents.IncidentStatus | String | The incident status. |
| Securonix.Incidents.WorkflowName | String | The incident workflow name. |
| Securonix.Incidents.Watchlisted | Boolean | Whether the incident is in a watchlist. |
| Securonix.Incidents.IncidentType | String | The incident type. |
| Securonix.Incidents.IncidentID | String | The incident ID. |
| Securonix.Incidents.LastUpdateDate | Number | The time when the incident was last updated, in Epoch time. |
| Securonix.Incidents.Url | String | The URL that links to the incident on Securonix. |
| Securonix.Incidents.ViolatorText | String | Text of the incident violator. |
| Securonix.Incidents.AssignedUser | String | The user assigned to the incident. |
| Securonix.Incidents.IsWhitelisted | Boolean | Whether the incident is added to allow list. |
Command Example
!securonix-create-incident action_name="Mark as concern and create incident" entity_name=MH1014 entity_type=Users resource_group="BLUECOAT" resource_name="BLUECOAT" violation_name="Uploads to personal Websites" workflow=SOCTeamReview comment=bgdfs criticality=Critical
Context Example
{
"Securonix": {
"Incidents": {
"AssignedUser": "Admin Admin",
"Casecreatetime": 1579687771677,
"Entity": "Users",
"IncidentID": "30134",
"IncidentStatus": "Open",
"IncidentType": "Policy",
"IsWhitelisted": false,
"LastUpdateDate": 1579687771677,
"ParentCaseId": "",
"Priority": "Critical",
"Reason": [
"Resource: BLUECOAT",
"Policy: Uploads to personal websites",
"Threat: Data egress via network uploads"
],
"Riskscore": 0,
"SandBoxPolicy": false,
"StatusCompleted": false,
"TenantInfo": {
"tenantcolor": "#000000",
"tenantid": 1,
"tenantname": "Securonix",
"tenantshortcode": "SE"
},
"Url": "{url}",
"ViolatorID": "14",
"ViolatorSubText": "1014",
"ViolatorText": "john doe",
"Watchlisted": false,
"WorkflowName": "SOCTeamReview"
}
}
}
Human Readable Output
Incident was created successfully
Entity Incident Status Incident Type IncidentID Priority Reason Url Users Open Policy 30134 Critical Resource: BLUECOAT,Policy: Uploads to personal websites,Threat: Data egress via network uploads url
securonix-threats-list
Retrieve a list of threats violated within a specified time range and get details about
the threat models and policies violated.
Base Command
securonix-threats-list
Input
| Argument Name | Description | Required |
|---|---|---|
| date_from | Start time range for which to return threats (Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ For example: 01 Jan 2023, 01 Feb 2023 04:45:33, 2023-01-26T14:05:44Z) | Required |
| date_to | End date/time for which to retrieve threats (Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ For example: 01 Jan 2023, 01 Feb 2023 04:45:33, 2023-01-26T14:05:44Z) Default is current time. | Optional |
| page_size | The number of results to retrieve. Default is 10. | Optional |
| tenant_name | Name of the tenant to fetch threats from. This parameter is optional for Non MSSP users. | Optional |
| offset | Sets the starting index for the returned results. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Threat.TenantID | Number | Tenant ID. |
| Securonix.Threat.Tenantname | String | Tenant Name. |
| Securonix.Threat.Violator | String | Violator of threat. |
| Securonix.Threat.EntityID | String | Threat entity ID. |
| Securonix.Threat.Resourcegroupname | String | Name of the resource group. |
| Securonix.Threat.ThreatName | String | Threat Name. |
| Securonix.Threat.Category | String | Category of threat. |
| Securonix.Threat.Resourcename | String | Name of the resource. |
| Securonix.Threat.Resourcetype | String | Type of the resource. |
| Securonix.Threat.GenerationTime | Date | Date and Time when the threat is generated. |
| Securonix.Threat.GenerationTime_Epoch | Number | Epoch time when the threat is generated. |
| Securonix.Threat.Policies | Unknown | List of policies violated. |
| Securonix.Threat.Policystarttime | Number | Epoch time when the policy is first violated. |
| Securonix.Threat.Policyendtime | Number | Epoch time when the policy is last violated. |
| Securonix.Threat.Solrquery | String | Spotter query to fetch the related violations. |
Command Example
!securonix-threats-list date_from="1 day"
Context Example
{
"Securonix": {
"Threats": {
"TenantID": 2,
"Tenantname": "Response-Automation",
"Violator": "Activityaccount",
"EntityID": "VIOLATOR5-1673852881421",
"Resourcegroupname": "RES-PLAYBOOK-DS-AUTOMATION",
"ThreatName": "TM_Response-PB-ActivityAccount-Manual",
"Category": "NONE",
"Resourcename": "RES10-RESOURCE-302184",
"Resourcetype": "Res-Playbook",
"GenerationTime": "Mon, 16 Jan 2023 @ 01:53:31 AM",
"GenerationTime_Epoch": 1673855611090,
"Policies": [
"Response-PB-ActivityAccount-Manual"
],
"policystarttime": 1661161072000,
"policyendtime": 1661161072000,
"solrquery": "index = violation and ( ( @policyname = \"Response-PB-ActivityAccount-Manual\" and @ipaddress=\"127.0.0.1\" ) ) AND @tenantname=\"Response-Automation\" AND datetime between \"08/22/2022 04:37:52\" \"08/22/2022 04:37:53\""
}
}
}
Human Readable Output
Threats
ThreatName EntityID Violator Category Resourcegroupname Resourcename Resourcetype GenerationTime Policies TenantID Tenantname TM_Response-PB-ActivityAccount-Manual VIOLATOR5-1673852881421 Activityaccount NONE RES-PLAYBOOK-DS-AUTOMATION RES10-RESOURCE-302184 Res-Playbook Mon, 16 Jan 2023 @ 01:53:31 AM Response-PB-ActivityAccount-Manual 2 Response-Automation
securonix-incident-activity-history-get
Retrieves incident activity history for a specified incident.
Base Command
securonix-incident-activity-history-get
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID for which to retrieve the activity history. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.IncidentHistory.caseid | Number | Incident ID. |
| Securonix.IncidentHistory.actiontaken | String | The type of action taken. |
| Securonix.IncidentHistory.status | String | The status of the incident. |
| Securonix.IncidentHistory.comment.Comments | String | Comment text. |
| Securonix.IncidentHistory.eventTime | Number | Timestamp in epoch when the action is taken. |
| Securonix.IncidentHistory.username | String | Username of the person who carried out the action. |
| Securonix.IncidentHistory.currentassignee | String | The current assignee of the incident. |
| Securonix.IncidentHistory.commentType | String | The type of the comment. |
| Securonix.IncidentHistory.currWorkflow | String | The current workflow of the incident. |
| Securonix.IncidentHistory.isPlayBookOutAvailable | Boolean | Whether or not the playbook is available. |
| Securonix.IncidentHistory.creator | String | The creator of the activity. |
| Securonix.IncidentHistory.lastStatus | String | The previous status of the incident. |
| Securonix.IncidentHistory.pastassignee | String | The previous assignee of the incident. |
| Securonix.IncidentHistory.prevWorkflow | String | The previous workflow of the incident. |
| Securonix.IncidentHistory.attachment | String | The name of the attached file. |
| Securonix.IncidentHistory.attachmentType | String | The type of the attachment. |
| Securonix.IncidentHistory.playBookOutput.playBookId | Number | The ID of the playbook. |
| Securonix.IncidentHistory.playBookOutput.playBookName | String | The name of the playbook. |
| Securonix.IncidentHistory.playBookOutput.playRunId | String | The playbook run ID. |
| Securonix.IncidentHistory.playBookOutput.executorId | Number | The ID of the executor. |
| Securonix.IncidentHistory.playBookOutput.executor | String | The name of the executor. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.taskName | String | The name of the playbook task. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.description | String | The description of the playbook task. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.icon | String | Playbook icon. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.taskId | Number | The ID of the playbook task. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.lastExecutedTime | Date | The last execution time in epoch. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.lastStatus | String | The last status of the playbook. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.executedTask.executionId | String | The execution ID of the playbook. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.executedTask.taskStartTime | Date | The start time of the task. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.executedTask.taskEndTime | Date | The end time of the task. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.executedTask.status | String | The status of the task. |
| Securonix.IncidentHistory.playBookOutput.tasksForParticularRun.connectionMetadata | String | Connection metadata. |
Command example
!securonix-incident-activity-history-get incident_id=3235505
Context Example
{
"Securonix": {
"IncidentHistory": [
{
"caseid": "3235505",
"eventTime": "Jan 18, 2023 2:34:21 AM",
"isPlayBookOutAvailable": true,
"playBookOutput": {
"executor": "Admin Admin",
"executorId": 41,
"playBookId": 104,
"playBookName": "Create Security Incident",
"playRunId": "Playbook Run ID",
"tasksForParticularRun": [
{
"connectionMetadata": "{\"source\":\"test\"}",
"description": "Create Security incidents",
"executedTask": {
"executionId": "104-c08be5e4-480d-4105-9ed7-232cf902fa83",
"status": "FINISHED",
"taskEndTime": 1674009264968,
"taskInfo": {
"Incident created": [
{
"highlight": false,
"isurl": false,
"key": "INCIDENT NUMBER",
"showonui": true,
"value": "0724324"
},
{
"highlight": false,
"isurl": false,
"key": "sys_id",
"showonui": false,
"value": "61a6b780dbeca910dabd266e13961933"
},
{
"highlight": false,
"isurl": false,
"key": "lastViolationTime",
"showonui": false,
"value": "1674009261355"
},
{
"highlight": false,
"isurl": true,
"key": "INCIDENT URL",
"showonui": true,
"value": "<URL of Securonix platform>"
},
{
"highlight": false,
"isurl": false,
"key": "VIOLATION_COUNT",
"showonui": true,
"value": "15"
}
]
},
"taskStartTime": 1674009261396
},
"icon": "test",
"lastExecutedTime": 1674009261396,
"lastStatus": "FINISHED",
"taskId": 104,
"taskName": "Create Security Incident"
}
]
}
},
{
"actiontaken": "CREATED",
"caseid": "3235505",
"comment": [
{
"Comments": "Incident created while executing playbook - Create Security Incident"
}
],
"commentType": [
"text"
],
"creator": "admin",
"currWorkflow": "SOCTeamReview",
"currentassignee": "API_TEST_SS",
"eventTime": "Jan 18, 2023 2:34:22 AM",
"isPlayBookOutAvailable": false,
"status": "Open",
"username": "Admin Admin"
},
{
"actiontaken": "CLOSE AS FIXED",
"caseid": "3235505",
"comment": [
{
"Comments": "Incident closed as part of AutoClosure"
}
],
"commentType": [
"text"
],
"creator": "admin",
"currWorkflow": "DEFAULTWORKFLOW",
"currentassignee": "API_TEST_SS",
"eventTime": "Jan 20, 2023 5:08:42 AM",
"isPlayBookOutAvailable": false,
"lastStatus": "Open",
"pastassignee": "API_TEST_SS",
"prevWorkflow": "SOCTeamReview",
"status": "COMPLETED",
"username": "Admin Admin"
}
]
}
}
Human Readable Output
Incident activity history for ID: 3235505
Action Taken Username Event Time Status Last Status Comment Playbook ID Playbook Name Playbook Executor CLOSE AS FIXED Admin Admin Jan 20, 2023 5:08:42 AM COMPLETED Open Incident closed as part of AutoClosure CREATED Admin Admin Jan 18, 2023 2:34:22 AM Open Incident created while executing playbook - Create Security Incident Jan 18, 2023 2:34:21 AM 104 Create Security Incident Admin Admin
securonix-incident-attachment-get
Retrieves the attachments available on the Securonix platform.
Base Command
securonix-incident-attachment-get
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_id | Incident ID for which to retrieve the attachments. | Required |
| attachment_type | The type of attachment to retrieve. Supported options are csv, pdf, and txt. Comma-separated values are supported. | Optional |
| from | Start time for which to retrieve attachments.(Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ, yyyy-MM-ddTHH:mm:ss.SSSZ. For example: 01 Jan 2023, 01 Feb 2023 04:45:33, 2023-01-26T14:05:44Z, 2023-01-26T14:05:44.000Z) | Optional |
| to | End time for which to retrieve attachments.(Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ, yyyy-MM-ddTHH:mm:ss.SSSZ. For example: 01 Jan 2023, 01 Feb 2023 04:45:33, 2023-01-26T14:05:44Z, 2023-01-26T14:05:44.000Z) | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| File.Size | Number | The size of the file in bytes. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.SHA512 | String | The SHA512 hash of the file. |
| File.Name | String | The name of the file. |
| File.SSDeep | String | The SSDeep hash of the file. |
| File.EntryID | String | The entry ID of the file. |
| File.Info | String | File information. |
| File.Type | String | The file type. |
| File.MD5 | String | The MD5 hash of the file. |
| File.Extension | String | The file extension. |
| Securonix.Incidents.Attachments.IncidentID | String | Attachment Incident ID. |
| Securonix.Incidents.Attachments.Files | Unknown | Attachment File names. |
Command example
!securonix-incident-attachment-get incident_id=12345678 from=2023-01-27T07:25:00Z to=2023-01-27T08:25:00Z
Context Example
{
"Securonix": {
"Incidents": {
"Attachments": [
{
"Files": "REST_API_Categories___SNYPR_6.4.pdf",
"IncidentID": 3422464053
}
]
}
},
"File": {
"Type": "application/pdf",
"Size": "6,157,973 bytes",
"Info": "PDF document, version 1.4",
"MD5": "ee0e57a311beb1c9a326b921625d31e4",
"SHA1": "e60bb6364981039bca21285a5c35a41afcbcdbb",
"SHA256": "b1c383bb218218b5a816841a7a91f1dcab08c1034d434fcefab70b4d804b7cc",
"SHA512": "0c2208f9dd5c65b18fb88dc8dec81c412e2b22b6122f837827079c7dad9b27c5d691a4d09edf3583a8313fae2a7a620c86ff4a186e46273970e542d42ca4bb0",
"SSDeep": "98304:EIkHaH04jKTu8dEp/i6fVm+RG9de2VaqhEIrST6k2WLcJVHvLx4jO1mzPyX:BYgjfyE46fbRGze2gTILk+VHvLx719",
}
}
Human Readable Output
Incident ID: 3235505
Uploaded file: REST_API_Categories___SNYPR_6.4.pdfDownload
Property Type Size Info MD5 SHA1 SHA256 SHA512 SSDeep Value application/pdf 6,157,973 bytes PDF document, version 1.4 ee0e57a311beb1c9a326b921625d31e4 ae60bb6364981039bca21285a5c35a41afcbcdbb 8b1c383bb218218b5a816841a7a91f1dcab08c1034d434fcefab70b4d804b7cc 60c2208f9dd5c65b18fb88dc8dec81c412e2b22b6122f837827079c7dad9b27c5d691a4d09edf3583a8313fae2a7a620c86ff4a186e46273970e542d42ca4bb0 98304:EIkHaH04jKTu8dEp/i6fVm+RG9de2VaqhEIrST6k2WLcJVHvLx4jO1mzPyX:BYgjfyE46fbRGze2gTILk+VHvLx719
securonix-whitelists-get
Gets a list of whitelists.
Base Command
securonix-whitelists-get
Input
| Argument Name | Description | Required |
|---|---|---|
| tenant_name | Name of the tenant the whitelist belongs to. The tenant name parameter is required for MSSP users. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Whitelist.WhitelistName | String | Name of the Whitelist. |
| Securonix.Whitelist.TenantName | String | Tenant Name. |
| Securonix.Whitelist.WhitelistType | String | Type of the Whitelist. |
Command example
#### Context Example
```json
{
"Securonix": {
"Whitelist": [{
"WhitelistName": "Dummy Whitelist 1",
"WhitelistType": "Automated",
"TenantName": "test_tenant"
},
{
"WhitelistName": "Dummy Whitelist 2",
"WhitelistType": "Automated",
"TenantName": "test_tenant"
}]
}
}
Human Readable Output
Whitelists
WhitelistName WhitelistType TenantName Dummy Whitelist 1 Automated test_tenant Dummy Whitelist 2 Automated test_tenant
securonix-whitelist-entry-list
Gets information for the specified whitelist.
Base Command
securonix-whitelist-entry-list
Input
| Argument Name | Description | Required |
|---|---|---|
| tenant_name | Name of the tenant the whitelist belongs to. The tenant name parameter is required for MSSP users. |
Optional |
| whitelist_name | Name of the whitelist that the user wants to list. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.Whitelist.WhitelistName | String | Name of the Whitelist. |
| Securonix.Whitelist.TenantName | String | Tenant Name. |
| Securonix.Whitelist.Entries.Entity/Attribute | String | Entity/Attribute which is whitelisted. |
| Securonix.Whitelist.Entries.ExpiryDate | Date | The date when the entity will be removed from the whitelist. |
Command example
!securonix-whitelist-entry-list whitelist_name="test_whitelist"
Context Example
{
"Securonix": {
"Whitelist": {
"Entries": [
{
"Entity/Attribute": "TEST123",
"ExpiryDate": "09/28/2035 21:21:19"
}
],
"TenantName": "TenantAug02",
"Whitelistname": "test_whitelist"
}
}
}
Human Readable Output
Whitelist: Dummy Threat Model MM
Entity/Attribute ExpiryDate TEST123 09/28/2035 21:21:19
securonix-xsoar-state-mapping-get
Returns the state mapping of XSOAR with Securonix.
Base Command
securonix-xsoar-state-mapping-get
Input
This command does not have any arguments.
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.StateMapping.ACTIVE.action | String | Securonix action name to map with XSOAR’s active state. |
| Securonix.StateMapping.ACTIVE.status | String | Securonix status to map with XSOAR’s active state. |
| Securonix.StateMapping.DONE.action | String | Securonix action name to map with XSOAR’s closed state. |
| Securonix.StateMapping.DONE.status | String | Securonix status to map with XSOAR’s closed state. |
Command example
#### Context Example
```json
{
"Securonix": {
"StateMapping": {
"ACTIVE": {
"action": "Start Investigation",
"status": "in progress"
},
"DONE": {
"action": "Close Incident",
"status": "completed"
}
}
}
}
Human Readable Output
State Mapping
XSOAR Status Securonix Status Securonix Action Name Active in progress Start Investigation Closed completed Close Incident
securonix-whitelist-create
Creates a whitelist in Securonix.
Base Command
securonix-whitelist-create
Input
| Argument Name | Description | Required |
|---|---|---|
| tenant_name | Name of the tenant the whitelist belongs to. The tenant name parameter is required for MSSP users. |
Optional |
| whitelist_name | Name of the whitelist that the user wants to create. | Required |
| entity_type | Type of entity that the whitelist is intended to hold. | Optional |
Context Output
There is no context output for this command.
Command Example
!securonix-whitelist-create whitelistname="test_whitelist" entity_type="Users"
Context Example
{}
Human Readable Output
Whitelist test_whitelist was created successfully.
securonix-whitelist-entry-add
Add entity or attribute to the specified whitelist entry.
Base Command
securonix-whitelist-entry-add
Input
| Argument Name | Description | Required |
|---|---|---|
| tenant_name | Name of the tenant the whitelist belongs to. The tenant name parameter is required for MSSP users. |
Optional |
| whitelist_name | The name of the whitelist to which data is being added. If whitelist_type is Attribute, then whitelist_name and violation_name should be the same. |
Required |
| whitelist_type | The type of the whitelist to which data is being added. (Supported Values are Global and Attribute.) | Required |
| entity_type | The type of entity being added. (Supported values are Users, Activityaccount, Activityip, Resources). This parameter is required if whitelist_type is Global. |
Optional |
| entity_id | ID of the entity being added. This parameter is required if whitelist_type is Global. |
Optional |
| expiry_date | The date when the entity will be removed from the whitelist.(In MM/DD/YYYY format) | Optional |
| resource_name | The resource name to which the account belongs. This parameter is required if whitelist_type is Global and entity_type is Activityaccount. |
Optional |
| resource_group_id | The resource group id to which the account belongs. This parameter is required if whitelist_type is Global and entity_type is Activityaccount. |
Optional |
| attribute_name | Name of the attribute being added. (Supported values are source ip, resourcetype,transactionstring) This parameter is required if whitelist_type is Attribute. |
Optional |
| attribute_value | The attribute value being added. This parameter is required if whitelist_type is Attribute. |
Optional |
| violation_type | Type of the violation. (Supported Values are Policy,ThreatModel,Functionality.) This parameter is required if whitelist_type is Attribute. |
Optional |
| violation_name | Name of the violations. (Supported values are Policy names, ThreatModel names, Functionality names) This parameter is required if whitelist_type is set to Attribute, and is the same as the whitelist name parameter. |
Optional |
Context Output
There is no context output for this command.
Command example
!securonix-whitelist-entry-add whitelist_name=whitelistdemo_Activityip whitelist_type=Global tenant_name=test_tenant entity_type=Activityip entity_id=0.0.0.1 expiry_date=04/02/2023
Context example
{}
Human Readable Output
Entity added to global whitelist Successfully.
securonix-lookup-table-create
Creates a lookup table.
Base Command
securonix-lookup-table-create
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Name of the lookup table to create. | Required |
| scope | Scope of the lookup table. This argument is mandatory for MSSP users. | Optional |
| tenant_name | Name of the tenant in which to create a lookup table. This argument is mandatory for MSSP users. | Optional |
| field_names | A comma-separated string of column names. | Required |
| encrypt | A comma-separated string of column names for which data needs to be encrypted. | Optional |
| key | A comma-separated string of column names to be used as key | Required |
Context Output
There is no context output for this command.
Command Example
!securonix-lookup-table-create name=test_lookup_table field_names="samplefield,samplefield2" key="samplefield" tenant_name=test_tenant scope=Global
Context Example
{}
Human Readable Output
Lookup Table test_lookup_table created successfully
securonix-lookup-table-config-and-data-delete
Deletes the data and configuration of the provided lookup table.
Base Command
securonix-lookup-table-config-and-data-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Name of the lookup table to delete. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.LookupTable.lookupTableName | String | Name of the lookup table. |
| Securonix.LookupTable.isDeleted | Boolean | True, if the lookup table data and configuration deleted successfully. |
Command Example
!securonix-lookup-table-config-and-data-delete name="test"
Context Example
{
"Securonix": {
"LookupTable": {
"lookupTableName": "test",
"isDeleted": true
}
}
}
Human Readable Output
test and data deleted successfully
securonix-lookup-tables-list
Retrieves a list of lookup tables available within the Securonix platform.
Base Command
securonix-lookup-tables-list
Input
| Argument Name | Description | Required |
|---|---|---|
| max | Number of records to return. | Optional |
| offset | Specify from which record the data should be returned. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.LookupTable.tenantName | String | Name of the tenant. |
| Securonix.LookupTable.lookupTableName | String | Name of the lookup table. |
| Securonix.LookupTable.totalRecords | Number | Number of records in the lookup table. |
| Securonix.LookupTable.scope | String | Scope of the lookup table. |
| Securonix.LookupTable.type | String | Type of the lookup table. |
Command example
!securonix-lookup-tables-list max=2 offset=0
Context Example
{
"Securonix": {
"LookupTable": [
{
"lookupTableName": "NonBusinessDomains",
"scope": "global",
"tenantName": "All Tenants",
"totalRecords": 2213,
"type": "system"
},
{
"lookupTableName": "CompressedFileExtensions",
"scope": "meta",
"tenantName": "All Tenants",
"totalRecords": 240,
"type": "system"
}
]
}
}
Human Readable Output
Lookup Tables
Tenant Name Lookup Table Name Total Records Scope Type of Lookup Table All Tenants NonBusinessDomains 2213 global system All Tenants CompressedFileExtensions 240 meta system
securonix-whitelist-entry-delete
Remove entity or attribute from the specified whitelist entry.
Base Command
securonix-whitelist-entry-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| tenant_name | Name of the tenant the whitelist belongs to. | Required |
| whitelist_name | Name of the whitelist the user wants to delete the value from. | Required |
| whitelist_type | Type of whitelist that user wants to delete from. Possible values are: Global, Attribute. | Optional |
| entity_id | Entity ID value that needs to be removed from the whitelist. This parameter is required if whitelist_type is set to “Global”. Example: - employeeId for type User - accountname for type ActivityAccount - resourcename for type Resources - ipadress for type IpAddress. |
Optional |
| attribute_name | Name of the attribute being removed. This parameter is required if whitelist_type is set to “Attribute”. Example: - accountname - transactionstring - sourcetype. |
Optional |
| attribute_value | The value of the attribute being removed. This parameter is required if whitelist_type is “Attribute”. |
Optional |
Context Output
There is no context output for this command.
Command Example
!securonix-whitelist-entry-delete whitelistname="test_whitelist" entity_id="test_user"
Context Example
{}
Human Readable Output
test_user Item removed from whitelist Successfully.
securonix-lookup-table-entries-list
Retrieves the entries stored in a specified lookup table.
Base Command
securonix-lookup-table-entries-list
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Lookup Table name. | Required |
| query | Use to filter the records. By default it will filter the records on key. To filter on other column use attribute argument. | Optional |
| attribute | Column name on which to filter the data. Default is key. | Optional |
| max | Number of records to retrieve. Default is 15. | Optional |
| offset | Specify from which record the data should be returned. Default is 0. | Optional |
| page_num | Specify a value to retrieve records from a specific page. Default is 1. | Optional |
| sort | Name of the column on which to sort the data. By default the data will be sorted on the key. | Optional |
| order | The order in which to sort the data. By default the data will be sorted in ascending order. Possible values are: asc, desc. Default is asc. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Securonix.LookupTableEntries.lookupname | String | Name of the lookup table. |
| Securonix.LookupTableEntries.tenantid | Number | ID of the tenant. |
| Securonix.LookupTableEntries.lookupuniquekey | String | Unique key of the entry. |
| Securonix.LookupTableEntries.timestamp | String | The UTC timestamp indicates when the entry was added. |
| Securonix.LookupTableEntries.key | String | The value of the key field. |
| Securonix.LookupTableEntries.defaultenrichedevent | Unknown | Entry data. |
| Securonix.LookupTableEntries.tenantname | String | Name of the tenant. |
| Securonix.LookupTableEntries.entry.key | String | Key of the entry. |
| Securonix.LookupTableEntries.entry.value | String | Value of the entry. |
Command example
!securonix-lookup-table-entries-list name="TEST_M" max=2 sort="ip" order=desc
Context Example
{
"Securonix": {
"LookupTableEntries": [
{
"defaultenrichedevent": [
"127.0.0.9",
"B5E3195FB4E1EB3BE797077CEE398C04",
"bad"
],
"entry": [
{
"key": "reputation",
"value": "bad"
},
{
"key": "ip",
"value": "127.0.0.9"
},
{
"key": "id",
"value": "B5E3195FB4E1EB3BE797077CEE398C04"
}
],
"key": "B5E3195FB4E1EB3BE797077CEE398C04",
"lookupname": "TEST_M",
"lookupuniquekey": "2^~TEST_M|B5E3195FB4E1EB3BE797077CEE398C04",
"tenantid": 2,
"tenantname": "novr3nonmssp",
"timestamp": "Feb 18, 2023 5:50:16 AM"
},
{
"defaultenrichedevent": [
"127.0.0.8",
"497B9EC67354A28D62A98E82299871CC",
"good"
],
"entry": [
{
"key": "reputation",
"value": "good"
},
{
"key": "ip",
"value": "127.0.0.8"
},
{
"key": "id",
"value": "497B9EC67354A28D62A98E82299871CC"
}
],
"key": "497B9EC67354A28D62A98E82299871CC",
"lookupname": "TEST_M",
"lookupuniquekey": "2^~TEST_M|497B9EC67354A28D62A98E82299871CC",
"tenantid": 2,
"tenantname": "novr3nonmssp",
"timestamp": "Feb 18, 2023 5:50:16 AM"
}
]
}
}
Human Readable Output
Entries
Key Lookup Unique Key Tenant Name Timestamp id ip reputation B5E3195FB4E1EB3BE797077CEE398C04 2^~TEST_M|B5E3195FB4E1EB3BE797077CEE398C04 novr3nonmssp Feb 18, 2023 5:50:16 AM B5E3195FB4E1EB3BE797077CEE398C04 127.0.0.9 bad 497B9EC67354A28D62A98E82299871CC 2^~TEST_M|497B9EC67354A28D62A98E82299871CC novr3nonmssp Feb 18, 2023 5:50:16 AM 497B9EC67354A28D62A98E82299871CC 127.0.0.8 good
securonix-lookup-table-entries-delete
Deletes the entries from the lookup table.
Base Command
securonix-lookup-table-entries-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| lookup_unique_keys | Comma-separated list of lookup unique keys to delete. | Required |
| name | Name of the lookup table from which to delete the entries. | Required |
Context Output
There is no context output for this command.
Command example
!securonix-lookup-table-entries-delete name=TEST_M lookup_unique_keys=2^~TEST_M|158267F0BC6E7484E3C0F5964ABE9D2B,2^~TEST_M|175A9FFD55480ED376C992AC86ABE3D7
Context Example
{}
Human Readable Output
Successfully deleted following entries from TEST_M: 2^~TEST_M 158267F0BC6E7484E3C0F5964ABE9D2B, 2^~TEST_M 175A9FFD55480ED376C992AC86ABE3D7.
securonix-lookup-table-entry-add
Add entries to the provided lookup table.
Base Command
securonix-lookup-table-entry-add
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Lookup Table name to which the data needs to be added. | Required |
| tenant_name | Name of the tenant to which the lookup table belongs. This argument is required for MSSP users and if the scope of the lookup table is “Meta”. | Optional |
| json_data | JSON formatted string containing the field names and values in the below format. To specify an expiration date for an entry, add “expiryDate” key (in the format of “MM/DD/YYYY”) in the respective JSON object. E.g. [{“field1”: “Value1”, “field2”: “Value2”}, {“field1”: “Value3”, “field2”: “Value4”}]. |
Optional |
| file_entry_id | War room entry of the file. To specify an expiration date for an entry, add “expiryDate” key (in the format of “MM/DD/YYYY”) in the respective JSON object. | Optional |
Context Output
There is no context output for this command.
Command example
!securonix-lookup-table-entry-add name="TEST_TABLE" json_data="[{\"id\": \"1\",\"ip\": \"127.0.0.1\",\"reputation\": \"bad\",\"expiryDate\":\"02/13/2023\"},{\"id\": \"2\",\"ip\": \"127.0.0.2\",\"reputation\": \"good\"}]"
Human Readable Output
Entries added to TEST_TABLE successfully
Configuration parameters
host— Host (Overrides the default hostname, https://{tenant}.net/Snypr)tenant— Tenant (required)username— Username (required)password— Password (required)entity_type_to_fetch— Type of entity to fetchtenant_name— Tenant NameisFetch— Fetch incidentsincident_status— Incidents to fetchdefault_severity— Set default incident severityincidentType— Incident typeincidentFetchInterval— Incidents Fetch Intervalfetch_time— First fetch time rangemax_fetch— The maximum number of incidents to fetch each time.mirror_direction— Incident Mirroring Directionclose_incident— Close respective Securonix incident after fetchingclose_states_of_securonix— Securonix workflow state(s) that can be considered as Close state in XSOAR for Incoming mirroringactive_state_action_mapping— Securonix action name to map with XSOAR's active state for Outgoing mirroringactive_state_status_mapping— Securonix status to map with XSOAR's active state for Outgoing mirroringclosed_state_action_mapping— Securonix action name to map with XSOAR's closed state for Outgoing mirroringclosed_state_status_mapping— Securonix status to map with XSOAR's closed state for Outgoing mirroringcomment_tag— Comment Entry Tagsecuronix_retry_count— Securonix Retry Countsecuronix_retry_delay— Securonix Retry Delaysecuronix_retry_delay_type— Securonix Retry Delay Typeunsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (36)
-
securonix-add-comment-to-incidentAdds a comment to the specified incident.
-
securonix-add-entity-to-watchlistAdds an entity to a watchlist.
-
securonix-check-entity-in-watchlistChecks if the specified entity is in a watchlist.
-
securonix-create-incidentCreates an incident. For more information about the required arguments, see the Securonix documentation.
-
securonix-create-watchlistCreates a watchlist in Securonix.
-
securonix-get-default-assignee-for-workflowGets the default assignee for the specified workflow.
-
securonix-get-incidentGets details of the specified incident.
-
securonix-get-incident-available-actionsGets a list of available actions for the specified incident.
-
securonix-get-incident-statusGets the status of the specified incident.
-
securonix-get-incident-workflowGets the workflow of the specified incident.
-
securonix-get-watchlistGets information for the specified watchlist.
-
securonix-incident-activity-history-getRetrieves incident activity history for a specified incident.
-
securonix-incident-attachment-getRetrieves the attachments available on the Securonix platform.
-
securonix-list-activity-dataGets a list of activity data for the specified resource group.
-
securonix-list-incidentsGets a list of incidents.
-
securonix-list-policiesGets a list of all policies.
-
securonix-list-possible-threat-actionsGets a list available threat actions.
-
securonix-list-resource-groupsGets a list of resource groups.
-
securonix-list-usersGets a list of users.
-
securonix-list-violation-dataGets a list activity data for an account name.
-
securonix-list-watchlistsGets a list of watchlists.
-
securonix-list-workflowsGets a list of all available workflows.
-
securonix-lookup-table-config-and-data-deleteDeletes a lookup table with its data and configuration.
-
securonix-lookup-table-createCreates a lookup table.
-
securonix-lookup-table-entries-deleteDeletes the entries from the lookup table.
-
securonix-lookup-table-entries-listRetrieves the entries stored in a specified lookup table.
-
securonix-lookup-table-entry-addAdd entries to the provided lookup table.
-
securonix-lookup-tables-listRetrieves a list of lookup tables available within the Securonix platform.
-
securonix-perform-action-on-incidentPerforms an action on the specified incident.
-
securonix-threats-listRetrieve a list of threats violated within a specified time range and get details about the threat models and policies violated.
-
securonix-whitelist-createCreates a whitelist in Securonix.
-
securonix-whitelist-entry-addAdd entity or attribute to the specified whitelist entry.
-
securonix-whitelist-entry-deleteRemove entity or attribute from the specified whitelist entry.
-
securonix-whitelist-entry-listGets information for the specified whitelist.
-
securonix-whitelists-getGets a list of whitelists.
-
securonix-xsoar-state-mapping-getReturns the state mapping of XSOAR with Securonix.
import io import json from collections.abc import Callable from datetime import datetime from itertools import takewhile from typing import Any from zipfile import ZipFile import dateparser import demistomock as demisto # noqa: F401 import urllib3 from CommonServerPython import * # noqa: F401 from dateutil.parser import parse # Disable insecure warnings urllib3.disable_warnings() # These parameters will be used for retry mechanism logging TOTAL_RETRY_COUNT = 0 FULL_URL = None # Valid Entity Type for Whitelists VALID_ENTITY_TYPE = ["Users", "Activityaccount", "Resources", "Activityip"] # Valid Whitelist Types VALID_WHITELIST_TYPE = ["Global", "Attribute"] # Special characters for spotter query SPOTTER_SPECIAL_CHARACTERS = ["\\", "*", "?"] # Markdown characters. MARKDOWN_CHARS = r"\*_{}[]()#+-!" # Mapping of user input of mirroring direction to XSOAR. MIRROR_DIRECTION = {"None": None, "Incoming": "In", "Outgoing": "Out", "Incoming And Outgoing": "Both"} # If any comment is added to the incident, then this will be the action we'll get through incident activity history # command. COMMENT_ACTION = "COMMENTS_ADDED" # If any file is attached to the incident, then this will be the action we'll get through incident activity history # command. ATTACHMENT_ACTION = "ATTACHED_FILE" # This will store the state mapping of XSOAR states with Securonix states. XSOAR_TO_SECURONIX_STATE_MAPPING: dict = {} # Policy types for which retry should have end time to the current time. POLICY_TYPES_TO_RETRY = ["DIRECTIVE", "LAND SPEED", "TIER2", "BEACONING"] MESSAGE = { "INVALID_MAX_VALUE": "Please provide a value for 'max' between 1 and 10,000.", } def reformat_resource_groups_outputs(text: str) -> str: """rg_*text -> ResourceGroupText Args: text: the text to transform Returns: A Camel Cased string. """ suffix = text[3:] if suffix == "id": suffix = "ID" elif suffix == "resourcetypeid": suffix = "TypeID" else: suffix = suffix.title() return f"ResourceGroup{suffix}" def reformat_outputs(text: str) -> str: """camelCase -> Camel Case, id -> ID Args: text: the text to transform Returns: A Demisto output standard string """ if text.startswith("rg_"): return reformat_resource_groups_outputs(text) if text == "id": return "ID" if text in ["lanid", "u_lanid"]: return "LanID" if text == "jobId": return "JobID" if text == "eventId": return "EventID" if text in ["entityId", "entityid"]: return "EntityID" if text in ["tenantId", "tenantid"]: return "TenantID" if text == "incidentId": return "IncidentID" if text == "Datasourceid": return "DataSourceID" if text in ["employeeId", "employeeid", "u_employeeid"]: return "EmployeeID" if text == "violatorId": return "ViolatorID" if text == "threatname": return "ThreatName" if text == "generationtime": return "GenerationTime" if text == "generationtime_epoch": return "GenerationTime_Epoch" if text.startswith(("U_", "u_")): text = text[2:] return "".join(" " + char if char.isupper() else char.strip() for char in text).strip().title() def parse_data_arr(data_arr: Any, fields_to_drop: list = [], fields_to_include: list = []): """Parse data as received from Securonix into Demisto's conventions Args: data_arr: a dictionary containing the data fields_to_drop: Fields to drop from the array of the data fields_to_include: Fields to include from the array of the data Returns: A Camel Cased dictionary with the relevant fields. readable: for the human readable outputs: for the entry context """ if isinstance(data_arr, list): readable_arr, outputs_arr = [], [] for data in data_arr: readable = {reformat_outputs(i): j for i, j in data.items() if i not in fields_to_drop} if fields_to_include: readable = {i: j for i, j in readable.items() if i in fields_to_include} readable_arr.append(readable) outputs_arr.append({k.replace(" ", ""): v for k, v in readable.copy().items()}) return readable_arr, outputs_arr readable = {reformat_outputs(i): j for i, j in data_arr.items() if i not in fields_to_drop} if fields_to_include: readable = {i: j for i, j in readable.items() if i in fields_to_include} outputs = {k.replace(" ", ""): v for k, v in readable.copy().items()} return readable, outputs def string_escape_MD(data: Any): """ Escape any chars that might break a markdown string. :type data: ``Any`` :param data: The data to be modified (required). :return: A modified data. :rtype: ``str`` """ if isinstance(data, str): data = "".join(["\\" + str(c) if c in MARKDOWN_CHARS else str(c) for c in data]) elif isinstance(data, list): new_data = [] for sub_data in data: if isinstance(sub_data, str): sub_data = "".join(["\\" + str(c) if c in MARKDOWN_CHARS else str(c) for c in sub_data]) new_data.append(sub_data) data = new_data return data def incident_priority_to_dbot_score(priority_str: str, default_severity: str): """Converts an priority string to DBot score representation alert severity. Can be one of: Low -> 1 Medium -> 2 High -> 3 Args: priority_str: String representation of priority. default_severity: Default incoming incident severity Returns: Dbot representation of severity """ if default_severity: priority = default_severity.lower() else: priority = priority_str.lower() if priority == "low": return 1 if priority == "medium": return 2 if priority == "high": return 3 demisto.info(f"Securonix incident priority: {priority} is not known. Setting as unknown(DBotScore of 0).") return 0 def validate_configuration_parameters(params: dict[str, Any]): """ Check whether entered configuration parameters are valid or not. :type: params: dict :param: Dictionary of demisto configuration parameter :return: raise ValueError if any configuration parameter is not in valid format else returns None :rtype: None """ fetch_time = params.get("fetch_time") max_fetch = params.get("max_fetch") # Validate empty values if fetch_time is None: raise ValueError("Please provide First fetch time") if max_fetch is None: raise ValueError("Please provide max_fetch") # validate max_fetch arg_to_number(max_fetch, arg_name="max_fetch") # validate first_fetch parameter arg_to_datetime(fetch_time, "First fetch time") class RetryExponential(Retry): """ Create wrapper of urllib3.util.retry for Add extra logs before making a retry request with exponential delay """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff = TOTAL_RETRY_COUNT - self.total # type: ignore if 0 < diff <= TOTAL_RETRY_COUNT: demisto.debug(f"Performing retry {diff} with {self.get_backoff_time()} seconds delay for URL {FULL_URL}") class RetryFixed(Retry): """ Create wrapper of urllib3.util.retry for Add extra logs before making a retry request with fixed delay """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff = TOTAL_RETRY_COUNT - self.total # type: ignore if 0 < diff <= TOTAL_RETRY_COUNT: demisto.debug(f"Performing retry {diff} with {self.get_backoff_time()} seconds delay for URL {FULL_URL}.") # Overriding get_backoff_time logic for fixed backoff_factor def get_backoff_time(self) -> float: """ Formula for computing the fixed backoff :rtype: float """ consecutive_errors_len = len(list(takewhile(lambda x: x.redirect_location is None, reversed(self.history)))) if consecutive_errors_len <= 1: return 0 return min(self.DEFAULT_BACKOFF_MAX, int(self.backoff_factor)) def validate_mirroring_parameters(params: dict[str, Any]) -> None: """Validate mirroring specific configuration parameters. Args: params: The integration configuration parameters got from demisto.params() """ mirror_direction = params.get("mirror_direction", "None").strip() close_states_of_securonix = params.get("close_states_of_securonix", "").strip().lower() active_state_action = params.get("active_state_action_mapping", "").strip() active_state_status = params.get("active_state_status_mapping", "").strip().lower() close_state_action = params.get("closed_state_action_mapping", "").strip() close_state_status = params.get("closed_state_status_mapping", "").strip().lower() close_incident = argToBoolean(params.get("close_incident", False)) comment_entry_tag = params.get("comment_tag", "").strip() if mirror_direction == "None": return if mirror_direction == "Incoming" and (not close_states_of_securonix or not argToList(close_states_of_securonix)): raise ValueError( 'Following field is required for Incoming Mirroring: "Securonix workflow state(s) that ' 'can be considered as Close state in XSOAR for Incoming mirroring".' ) if mirror_direction == "Outgoing" and ( not active_state_action or not active_state_status or not close_state_action or not close_state_status or not comment_entry_tag ): raise ValueError( 'Following fields are required for Outgoing Mirroring: "Securonix action name to map ' 'with XSOAR\'s active state for Outgoing mirroring", "Securonix status to map with ' 'XSOAR\'s active state for Outgoing mirroring", "Securonix action name to map with ' "XSOAR's closed state for Outgoing mirroring\", \"Securonix status to map with XSOAR's " 'closed state for Outgoing mirroring", "Comment Entry Tag".' ) if mirror_direction == "Incoming And Outgoing" and ( not active_state_action or not active_state_status or not close_state_action or not close_state_status or not close_states_of_securonix or not argToList(close_states_of_securonix) or not comment_entry_tag ): raise ValueError( 'Following fields are required for Incoming And Outgoing Mirroring: "Securonix workflow ' 'state(s) that can be considered as Close state in XSOAR for Incoming mirroring", ' '"Securonix action name to map with XSOAR\'s active state for Outgoing mirroring", ' '"Securonix status to map with XSOAR\'s active state for Outgoing mirroring", "Securonix' ' action name to map with XSOAR\'s closed state for Outgoing mirroring", "Securonix status' ' to map with XSOAR\'s closed state for Outgoing mirroring", "Comment Entry Tag".' ) if close_incident and not active_state_action or not active_state_status or not close_state_action or not close_state_status: raise ValueError( 'Following fields are required for closing incident on Securonix: "Securonix action name ' 'to map with XSOAR\'s active state for Outgoing mirroring", "Securonix status to map ' 'with XSOAR\'s active state for Outgoing mirroring", "Securonix action name to map with ' "XSOAR's closed state for Outgoing mirroring\", \"Securonix status to map with XSOAR's " 'closed state for Outgoing mirroring".' ) def validate_delete_whitelist_parameters( whitelist_type: str, entity_id: str, attribute_name: str, attribute_value: str, tenant_name: str ) -> None: """Validate parameters for delete whitelist entry command. Args: whitelist_type: Type of whitelist that user wants to delete from. entity_id: Entity ID value that needs to be removed from the whitelist. attribute_name: Name of the attribute being removed. attribute_value: The value of the attribute being removed. tenant_name: The name of the tenant the whitelist belongs to. Raises: ValueError: Raises ValueError if parameters are invalid. """ # Validate whitelist_type parameter. if whitelist_type and whitelist_type not in VALID_WHITELIST_TYPE: raise ValueError(f"{whitelist_type} is an invalid value for whitelist_type.Valid whitelist types are {VALID_ENTITY_TYPE}") # Validate entity_id parameter. if whitelist_type == "Global" and not entity_id: raise ValueError("entity_id is required parameter for Global whitelist type.") # Validate attribute_name parameter. if whitelist_type == "Attribute" and not attribute_name: raise ValueError("attribute_name is required parameter for Attribute whitelist type.") # Validate attribute_value parameter. if whitelist_type == "Attribute" and not attribute_value: raise ValueError("attribute_value is required parameter for Attribute whitelist type.") # Validate tenant_name parameter. if not tenant_name: raise ValueError("tenant_name is a required parameter.") def get_mirroring() -> dict: """Add mirroring related keys in an incident. Returns: Dict: A dictionary containing required key-value pairs for mirroring. """ # Fetch the integration configuration parameters to determine the flow of the mirroring and mirror tags. params = demisto.params() mirror_direction = params.get("mirror_direction", "None").strip() mirror_tags = params.get("comment_tag", "").strip() return { "mirror_direction": MIRROR_DIRECTION.get(mirror_direction), "mirror_instance": demisto.integrationInstance(), "mirror_tags": mirror_tags, } def filter_activity_entries_by_time(activity_data: list[dict[str, Any]], timestamp: int) -> list[dict[str, Any]]: """Filter the incident activity entries by the given timestamp. Args: activity_data (List[Dict[str, Any]]): A list of incident activity data. timestamp (int): The timestamp to filter the activity data. Returns: List[Dict[str, Any]]: Filtered incident activity entries. """ filtered_activities = [] for activity in activity_data: activity_timestamp = activity.get("eventTime", "") # If no event timestamp found in an entry, then skip that entry. if not activity_timestamp: demisto.debug(f"Skipping entry as no event timestamp found: {json.dumps(activity)}") continue if date_to_timestamp(parse(activity_timestamp)) >= timestamp: filtered_activities.append(activity) return filtered_activities def filter_comment_activity_entries(activity_data: list[dict[str, Any]]) -> list[dict[str, Any]]: """Filter the comment entries from the given incident activity entries. Args: activity_data (List[Dict[str, Any]]): A list of incident activity data. Returns: List[Dict[str, Any]]: Filtered comment entries from incident activity entries. """ comment_entries = [] for activity in activity_data: activity_action = activity.get("actiontaken", "") if activity_action == COMMENT_ACTION: comment_entries.append(activity) return comment_entries def filter_attachment_activity_entries(activity_data: list[dict[str, Any]]) -> list[dict[str, Any]]: """Filter the attachment entries from the given incident activity entries. Args: activity_data (List[Dict[str, Any]]): A list of incident activity data. Returns: List[Dict[str, Any]]: Filtered attachment entries from incident activity entries. """ attachment_entries = [] for activity in activity_data: activity_action = activity.get("actiontaken", "") if activity_action == ATTACHMENT_ACTION: attachment_entries.append(activity) return attachment_entries def extract_names_of_attachments_from_entries(attachment_entries: list[dict[str, Any]]) -> list[str]: """Return names of the attachments for a list of attachment entries. Args: attachment_entries (List[Dict[str, Any]]): A list of attachment entries. Returns: List[str]: A list of attachment names. """ attachment_names = [attachment.get("attachment") for attachment in attachment_entries] # Remove any None values from the list and return it. return list(filter(None, attachment_names)) def is_incident_closed_on_securonix(activity_data: list[dict[str, Any]], close_states_of_securonix: list[str]) -> bool: """Check whether the incident is closed on the Securonix. Args: activity_data: A list of activity data from which to determine whether the incident is closed or not. close_states_of_securonix: A list of Securonix states which defines the close state for XSOAR. Returns: bool: Indicating whether the incident is closed on Securonix or not. """ incident_closed = [] for activity in activity_data: current_status = activity.get("status", "").strip().lower() last_status = activity.get("lastStatus", "").strip().lower() if current_status != last_status and current_status in close_states_of_securonix: incident_closed.append(True) else: incident_closed.append(False) return any(incident_closed) def extract_closing_comments(activity_data: list[dict[str, Any]], close_states_of_securonix: list[str]) -> str: """Extract the contents of the closing comments from activity data provided from Securonix. Args: activity_data: A list of activity data from which to extract the closing comments. close_states_of_securonix: A list of Securonix states which defines the close state for XSOAR. Returns: str: A string representing closing comments. """ closing_comments = [] for activity in activity_data: current_status = activity.get("status", "").strip().lower() last_status = activity.get("lastStatus", "").strip().lower() if current_status != last_status and current_status in close_states_of_securonix: comments_list = activity.get("comment", []) for _comment in comments_list: closing_comments.append(_comment.get("Comments", "")) if not closing_comments: closing_comments.append("Closing the XSOAR incident as Securonix incident is closed.") return " | ".join(closing_comments) def escape_spotter_query(original_query: str) -> str: """Escape the special characters of the spotter query provided from Securonix Incident. Args: original_query: The original spotter query provided from Securonix Incident Returns: str: The spotter query escaped for special characters. """ escaped_query = original_query for special_char in SPOTTER_SPECIAL_CHARACTERS: escaped_query = escaped_query.replace(special_char, f"\\{special_char}") return escaped_query class Client(BaseClient): """ Client to use in the Securonix integration. Overrides BaseClient """ def __init__( self, tenant: str, server_url: str, username: str, password: str, verify: bool, proxy: bool, securonix_retry_count: int, securonix_retry_delay: int, securonix_retry_delay_type: str, ): super().__init__(base_url=server_url, verify=verify, proxy=proxy) self._username = username self._password = password self._tenant = tenant self._securonix_retry_count = securonix_retry_count self._securonix_retry_delay = securonix_retry_delay self._securonix_retry_delay_type = securonix_retry_delay_type self.session = requests.Session() # Fetch cached integration context. integration_context = get_integration_context() self._token = integration_context.get("token") or self._generate_token() # the following condition was added to overcome the security hardening happened in Python 3.10. # https://github.com/python/cpython/pull/25778 # https://bugs.python.org/issue43998 if IS_PY3 and PY_VER_MINOR >= 10 and not verify: self.session.mount("https://", SSLAdapter(verify=verify)) def get_securonix_retry_count(self): return self._securonix_retry_count def get_securonix_retry_delay(self): return self._securonix_retry_delay def get_securonix_retry_delay_type(self): return self._securonix_retry_delay_type def implement_retry( self, retries: int = 0, status_list_to_retry: list = None, backoff_factor: int = 30, raise_on_redirect: bool = False, raise_on_status: bool = False, ): """ Implements the retry mechanism. In the default case where retries = 0 the request will fail on the first time :type retries: ``int`` :param retries: How many retries should be made in case of a failure. when set to '0'- will fail on the first time :type status_list_to_retry: ``iterable`` :param status_list_to_retry: A set of integer HTTP status codes that we should force a retry on. A retry is initiated if the request method is in ['GET', 'POST', 'PUT'] and the response status code is in ``status_list_to_retry``. :type backoff_factor ``float`` :param backoff_factor: A backoff factor to apply between attempts after the second try (most errors are resolved immediately by a second try without a delay). urllib3 will sleep for:: {backoff factor} * (2 ** ({number of total retries} - 1)) seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer than :attr:`Retry.BACKOFF_MAX`. By default, backoff_factor set to 5 :type raise_on_redirect ``bool`` :param raise_on_redirect: Whether, if the number of redirects is exhausted, to raise a MaxRetryError, or to return a response with a response code in the 3xx range. :type raise_on_status ``bool`` :param raise_on_status: Similar meaning to ``raise_on_redirect``: whether we should raise an exception, or return a response, if status falls in ``status_forcelist`` range and retries have been exhausted. """ try: method_whitelist = ( "allowed_methods" if hasattr( Retry.DEFAULT, # type: ignore[attr-defined] "allowed_methods", ) else "method_whitelist" ) whitelist_kawargs = {method_whitelist: frozenset(["GET", "POST", "PUT"])} retry = None if self._securonix_retry_delay_type == "Fixed": demisto.debug("Securonix Retry delay type is Fixed") # Set DEFAULT_BACKOFF_MAX to 2hour(in seconds) RetryFixed.DEFAULT_BACKOFF_MAX = 7200 retry = RetryFixed( total=retries, connect=0, read=0, backoff_factor=backoff_factor, status=retries, status_forcelist=status_list_to_retry, raise_on_status=raise_on_status, raise_on_redirect=raise_on_redirect, **whitelist_kawargs, # type: ignore[arg-type] ) else: demisto.debug("Securonix Retry delay type is Exponential") # Set DEFAULT_BACKOFF_MAX to 2hour(in seconds) RetryExponential.DEFAULT_BACKOFF_MAX = 7200 retry = RetryExponential( # type: ignore total=retries, backoff_factor=backoff_factor, connect=0, read=0, status=retries, status_forcelist=status_list_to_retry, raise_on_status=raise_on_status, raise_on_redirect=raise_on_redirect, **whitelist_kawargs, # type: ignore[arg-type] ) http_adapter = HTTPAdapter(max_retries=retry) # the following condition was added to overcome the security hardening happened in Python 3.10. # https://github.com/python/cpython/pull/25778 # https://bugs.python.org/issue43998 if self._verify: https_adapter = http_adapter elif IS_PY3 and PY_VER_MINOR >= 10: https_adapter = SSLAdapter(max_retries=retry, verify=self._verify) # type: ignore[arg-type] else: https_adapter = http_adapter self.session.mount("https://", https_adapter) except NameError: pass def http_request( self, method, url_suffix, headers=None, params=None, response_type: str = "json", json=None, data=None, regenerate_access_token=True, ): """ Generic request to Securonix """ global FULL_URL FULL_URL = urljoin(self._base_url, url_suffix) status_list_to_retry = [429] + list(range(500, 600)) if self._securonix_retry_count > 0: self.implement_retry( retries=self._securonix_retry_count, status_list_to_retry=status_list_to_retry, backoff_factor=self._securonix_retry_delay, raise_on_redirect=False, raise_on_status=True, ) try: demisto.debug(f"Making HTTP request with URL {FULL_URL}") result = self.session.request( method, FULL_URL, params=params, headers=headers, verify=self._verify, json=json, data=data, ) if result.status_code == 403 and regenerate_access_token: self._token = self._generate_token() headers["token"] = self._token return self.http_request(method, url_suffix, headers, params, response_type, json, data, False) if not result.ok: raise ValueError(f"Error in API call to Securonix {result.status_code}. Reason: {result.text}") try: if url_suffix == "/incident/attachments": return result if response_type != "json": return result.text return result.json() except Exception: raise ValueError(f"Failed to parse http response to JSON format. Original response body: \n{result.text}") except requests.exceptions.ConnectTimeout as exception: err_msg = ( "Connection Timeout Error - potential reasons might be that the Server URL parameter" " is incorrect or that the Server is not accessible from your host." ) raise Exception(f"{err_msg}\n{exception}") except requests.exceptions.SSLError as exception: err_msg = ( "SSL Certificate Verification Failed - try selecting 'Trust any certificate' checkbox in" " the integration configuration." ) raise Exception(f"{err_msg}\n{exception}") except requests.exceptions.ProxyError as exception: err_msg = ( "Proxy Error - if the 'Use system proxy' checkbox in the integration configuration is" " selected, try clearing the checkbox." ) raise Exception(f"{err_msg}\n{exception}") except requests.exceptions.ConnectionError as exception: error_class = str(exception.__class__) err_type = "<" + error_class[error_class.find("'") + 1 : error_class.rfind("'")] + ">" # noqa: E203 err_msg = ( f"Error Type: {err_type}\n" f"Error Number: [{exception.errno}]\n" f"Message: {exception.strerror}\n" f"Verify that the tenant parameter is correct " f"and that you have access to the server from your host." ) raise Exception(f"{err_msg}\n{exception}") except requests.exceptions.RetryError as exception: try: reason = f"Reason: {exception.args[0].reason.args[0]}" # pylint: disable=no-member except Exception: # noqa: disable=broad-except reason = "" err_msg = ( f"Max Retries Error: Request attempts with {self._securonix_retry_count} retries and with " f"{self._securonix_retry_delay} seconds {self._securonix_retry_delay_type} delay " f"failed.\n{reason}" ) if self._securonix_retry_delay_type == "Exponential": # For Exponential delay we are dividing it by 2 so for error message make it to original value err_msg = ( f"Max Retries Error: Request attempts with {self._securonix_retry_count} retries and with" f" {self._securonix_retry_delay * 2} seconds {self._securonix_retry_delay_type} delay " f"failed.\n{reason}" ) demisto.error(err_msg) raise Exception(f"{err_msg}\n{exception}") except requests.exceptions.InvalidHeader as exception: set_integration_context({}) raise Exception(f"Invalid token generated from the API.\n{exception}") except Exception as exception: raise Exception(str(exception)) def _generate_token(self) -> str: """Generate a token Returns: token valid for 1 day """ demisto.info("Generating new access token.") headers = { "username": self._username, "password": self._password, "validity": "1", } token = self.http_request("GET", "/token/generate", headers=headers, response_type="text") set_integration_context({"token": token}) return token def list_workflows_request(self) -> dict: """List workflows. Returns: Response from API. """ workflows = self.http_request("GET", "/incident/get", headers={"token": self._token}, params={"type": "workflows"}) return workflows.get("result").get("workflows") def get_default_assignee_for_workflow_request(self, workflow: str) -> dict: """Get default assignee for a workflow.. Args: workflow: workflow name Returns: Response from API. """ params = {"type": "defaultAssignee", "workflow": workflow} default_assignee = self.http_request("GET", "/incident/get", headers={"token": self._token}, params=params) return default_assignee.get("result") def list_possible_threat_actions_request(self) -> dict: """List possible threat actions. Returns: Response from API. """ threat_actions = self.http_request( "GET", "/incident/get", headers={"token": self._token}, params={"type": "threatActions"} ) return threat_actions.get("result") def list_policies_request(self) -> dict: """List policies. Returns: Response from API. """ policies = self.http_request("GET", "/policy/getAllPolicies", headers={"token": self._token}, response_type="xml") return policies def list_resource_groups_request(self) -> dict: """List resource groups. Returns: Response from API. """ resource_groups = self.http_request("GET", "/list/resourceGroups", headers={"token": self._token}, response_type="xml") return resource_groups def list_users_request(self) -> dict: """List users. Returns: Response from API. """ users = self.http_request("GET", "/list/allUsers", headers={"token": self._token}, response_type="xml") return users def list_activity_data_request(self, from_: str, to_: str, query: str = None, max_records: int = None) -> dict: """List activity data. Args: from_: eventtime start range in format MM/dd/yyyy HH:mm:ss. to_: eventtime end range in format MM/dd/yyyy HH:mm:ss. query: open query. max_records: maximum number of activity records to retrieve. Returns: Response from API. """ params = {"query": "index=activity", "eventtime_from": from_, "eventtime_to": to_, "prettyJson": True} if max_records is not None: params["max"] = max_records remove_nulls_from_dictionary(params) if query: if re.findall(r"index\s*=\s*\w+", query): params["query"] = query else: params["query"] = f"{params['query']} AND {query}" activity_data = self.http_request("GET", "/spotter/index/search", headers={"token": self._token}, params=params) return activity_data def list_violation_data_request( self, from_: str, to_: str, query: str = None, query_id: str = None, max_violations: int | None = 1000 ) -> dict: """List violation data. Args: from_: eventtime start range in format MM/dd/yyyy HH:mm:ss. to_: eventtime end range in format MM/dd/yyyy HH:mm:ss. query: open query. query_id: query_id to paginate violations. max_violations: max number of violations to return. Returns: Response from API. """ params = { "query": "index=violation", "generationtime_from": from_, "generationtime_to": to_, "queryId": query_id, "prettyJson": True, "max": max_violations, } if query: if re.findall(r"index\s*=\s*\w+", query): params["query"] = query else: params["query"] = f"{params['query']} AND {query}" remove_nulls_from_dictionary(params) violation_data = self.http_request("GET", "/spotter/index/search", headers={"token": self._token}, params=params) return violation_data def list_incidents_request( self, from_epoch: str, to_epoch: str, incident_status: str, max_incidents: str = "200", offset: str = "0" ) -> dict: """List all incidents by sending a GET request. Args: from_epoch: from time in epoch to_epoch: to time in epoch incident_status: incident status e.g:closed, opened max_incidents: max incidents to get offset: offset to be used Returns: Response from API. """ headers = {"token": self._token, "Accept": "application/vnd.snypr.app-v6.0+json"} params = { "type": "list", "from": from_epoch, "to": to_epoch, "rangeType": incident_status, "max": max_incidents, "order": "asc", "offset": offset, } incidents = self.http_request("GET", "/incident/get", headers=headers, params=params) return incidents.get("result").get("data") def get_incident_request(self, incident_id: str) -> dict: """get incident meta data by sending a GET request. Args: incident_id: incident ID. Returns: Response from API. """ headers = {"token": self._token, "Accept": "application/vnd.snypr.app-v6.0+json"} params = { "type": "metaInfo", "incidentId": incident_id, } incident = self.http_request("GET", "/incident/get", headers=headers, params=params) return incident.get("result").get("data") def get_incident_status_request(self, incident_id: str) -> dict: """get incident meta data by sending a GET request. Args: incident_id: incident ID. Returns: Response from API. """ params = { "type": "status", "incidentId": incident_id, } incident = self.http_request("GET", "/incident/get", headers={"token": self._token}, params=params) return incident.get("result") def get_incident_workflow_request(self, incident_id: str) -> dict: """get incident workflow by sending a GET request. Args: incident_id: incident ID. Returns: Response from API. """ params = { "type": "workflow", "incidentId": incident_id, } incident = self.http_request("GET", "/incident/get", headers={"token": self._token}, params=params) return incident.get("result") def get_incident_available_actions_request(self, incident_id: str) -> dict: """get incident available actions by sending a GET request. Args: incident_id: incident ID. Returns: Response from API. """ params = { "type": "actions", "incidentId": incident_id, } incident = self.http_request("GET", "/incident/get", headers={"token": self._token}, params=params) return incident.get("result") def get_incident_attachments_request( self, incident_id, attachment_type: str = None, attachment_from: int = None, attachment_to: int = None ): """Get incident attachments by sending a GET request. Args: incident_id: Incident ID. attachment_type: The type of attachment to retrieve. Supported options are csv, pdf, and txt. Comma-separated values are supported. attachment_from: Start time for which to retrieve attachments. (in the format YYYY-MM-DDTHH:MM:SS format) attachment_to: End time for which to retrieve attachments. (in the in the format YYYY-MM-DDTHH:MM:SS format) format) Returns: Response from API. """ params = { "incidentId": incident_id, "attachmenttype": attachment_type, "datefrom": attachment_from, "dateto": attachment_to, } remove_nulls_from_dictionary(params) attachment_res = self.http_request("GET", "/incident/attachments", headers={"token": self._token}, params=params) return attachment_res def perform_action_on_incident_request(self, incident_id, action: str, action_parameters: str) -> dict: """get incident available actions by sending a GET request. Args: incident_id: incident ID. action: action to perform on the incident. action_parameters: parameters needed in order to perform the action. Returns: Response from API. """ params = {"type": "actionInfo", "incidentId": incident_id, "actionName": action} if action_parameters: action_parameters_dict = {k: v.strip('"') for k, v in [i.split("=", 1) for i in action_parameters.split(",")]} params.update(action_parameters_dict) possible_action = self.http_request("GET", "/incident/get", headers={"token": self._token}, params=params) if "error" in possible_action: err_msg = possible_action.get("error") raise Exception( f"Failed to perform the action {action} on incident {incident_id}.\nError from Securonix is: {err_msg}" ) incident = self.http_request("POST", "/incident/actions", headers={"token": self._token}, params=params) return incident.get("result") def add_comment_to_incident_request(self, incident_id: str, comment: str) -> dict: """add comment to an incident by sending a POST request. Args: incident_id: incident ID. comment: action to perform on the incident Returns: Response from API. """ params = {"incidentId": incident_id, "comment": comment, "actionName": "comment"} incident = self.http_request("POST", "/incident/actions", headers={"token": self._token}, params=params) return incident.get("result") def create_incident_request( self, violation_name: str, resource_group: str, resource_name: str, entity_type: str, entity_name: str, action_name: str, workflow: str = None, comment: str = None, criticality: str = None, ) -> dict: """create an incident by sending a POST request. Args: violation_name: violation or policy name. resource_group: resource group name. resource_name: resource name. entity_type: entity type. entity_name: entity name. action_name: action name. workflow: workflow name. comment: comment on the incident. criticality: criticality for the incident. Returns: Response from API. """ params = { "violationName": violation_name, "datasourceName": resource_group, "resourceName": resource_name, "entityType": entity_type, "entityName": entity_name, "actionName": action_name, } if workflow: params["workflow"] = workflow if comment: params["comment"] = comment if criticality: params["criticality"] = criticality response = self.http_request("POST", "/incident/actions", headers={"token": self._token}, params=params) return response def list_watchlist_request(self): """list watchlists by sending a GET request. Returns: Response from API. """ watchlists = self.http_request("GET", "/incident/listWatchlist", headers={"token": self._token}) return watchlists.get("result") def get_watchlist_request(self, watchlist_name: str) -> dict: """Get a watchlist by sending a GET request. Args: watchlist_name: watchlist name. Returns: Response from API. """ params = { "query": f'index=watchlist AND watchlistname="{watchlist_name}"', } watchlist = self.http_request("GET", "/spotter/index/search", headers={"token": self._token}, params=params) return watchlist def create_watchlist_request(self, watchlist_name: str, tenant_name: str) -> dict: """Create a watchlist by sending a POST request. Args: watchlist_name: watchlist name. tenant_name: Name of the tenant the watchlist belongs to. Returns: Response from API. """ params = {"watchlistname": watchlist_name, "tenantname": tenant_name} remove_nulls_from_dictionary(params) watchlist = self.http_request( "POST", "/incident/createWatchlist", headers={"token": self._token}, params=params, response_type="text" ) return watchlist def check_entity_in_watchlist_request(self, entity_name: str, watchlist_name: str) -> dict: """Check if an entity is whitelisted by sending a GET request. Args: entity_name: Entity name. watchlist_name: Watchlist name. Returns: Response from API. """ params = {"entityId": entity_name, "watchlistname": watchlist_name} response = self.http_request("GET", "/incident/checkIfWatchlisted", headers={"token": self._token}, params=params) return response def add_entity_to_watchlist_request(self, watchlist_name: str, entity_type: str, entity_name: str, expiry_days: str) -> dict: """Check if an entity is whitelisted by sending a GET request. Args: watchlist_name: Watchlist name. entity_type: Entity type. entity_name: Entity name. expiry_days: Expiry in days. Returns: Response from API. """ params = { "watchlistname": watchlist_name, "entityType": entity_type, "entityId": entity_name, "expirydays": expiry_days, "resourcegroupid": "-1", } watchlist = self.http_request( "POST", "/incident/addToWatchlist", headers={"token": self._token}, params=params, response_type="txt" ) return watchlist def list_threats_request( self, from_epoch: int, to_epoch: int, tenant_name: str, offset: int = 0, max_incidents: int = 10 ) -> dict: """List all threats by sending a GET request. Args: from_epoch: from time in epoch to_epoch: to time in epoch tenant_name: tenant name offset: A page number to fetch from max_incidents: max incidents to get Returns: Response from API. """ params = { "datefrom": from_epoch, "dateto": to_epoch, "tenantname": tenant_name, "max": max_incidents, "offset": offset, } headers = {"token": self._token, "Accept": "application/vnd.snypr.app-v1.0+json"} remove_nulls_from_dictionary(params) response = self.http_request("GET", "/sccWidget/getThreats", headers=headers, params=params) return response.get("Response", {}).get("threats", {}) def get_incident_activity_history_request(self, incident_id: str) -> list: """Get incident activity history by sending a GET request. Args: incident_id (str): Incident ID for which to retrieve the activity history. Returns: Response from API. """ params = { "type": "activityStreamInfo", "incidentId": incident_id, } incident = self.http_request("GET", "/incident/get", headers={"token": self._token}, params=params) return incident.get("result", {}).get("activityStreamData", []) def list_whitelists_request(self, tenant_name: str) -> list: """Get a whitelist information by sending a GET request. Args: tenant_name: Name of the tenant the whitelist belongs to. Returns: Response from API. """ params = {"tenantname": tenant_name} remove_nulls_from_dictionary(params) whitelist = self.http_request("GET", "/incident/getlistofWhitelist", headers={"token": self._token}, params=params) return whitelist.get("result", []) def get_whitelist_entry_request(self, tenant_name: str, whitelist_name: str) -> dict: """Get a whitelist information by sending a GET request. Args: tenant_name: Name of the tenant the whitelist belongs to. whitelist_name: Name of the whitelist. Returns: Response from API. """ params = {"tenantname": tenant_name, "whitelistname": whitelist_name} remove_nulls_from_dictionary(params) whitelist = self.http_request("GET", "/incident/listWhitelistEntities", headers={"token": self._token}, params=params) return whitelist.get("result", {}) def add_whitelist_entry_request( self, tenant_name: str, whitelist_name: str, whitelist_type: str, entity_type: str, entity_id: str, expiry_date: str, resource_name: str, resource_group_id: str, attribute_name: str, attribute_value: str, violation_type: str, violation_name: str, ): """Add entry in whitelist by sending a POST request. Args: tenant_name: Name of the tenant the whitelist belongs to. whitelist_name: Name of the whitelist. whitelist_type: Type of the whitelist. entity_type: Entity Type is required if whitelist is global. entity_id: Entity ID is required if whitelist is global. expiry_date: Expiry Date in format(MM/DD/YYYY). resource_name: Resource name which the account belongs to. resource_group_id: Resource Group ID which the account belongs to. attribute_name: Attribute name. attribute_value: Attribute Value. violation_type: Violation Type. violation_name: Violation Name. Returns: Response from API. """ params = { "tenantname": tenant_name, "whitelistname": whitelist_name, "whitelisttype": whitelist_type, "entitytype": entity_type, "entityid": entity_id, "expirydate": expiry_date, "resourcename": resource_name, "resourcegroupid": resource_group_id, "attributename": attribute_name, "attributevalue": attribute_value, "violationtype": violation_type, "violationname": violation_name, } remove_nulls_from_dictionary(params) response = self.http_request("POST", "/incident/addToWhitelist", headers={"token": self._token}, params=params) return response def create_whitelist_request(self, tenant_name: str, whitelist_name: str, entity_type: str) -> dict: """Create a whitelist by sending a POST request. Args: tenant_name: Name of the tenant the whitelist belongs to. whitelist_name: Name of the whitelist. entity_type: Type of entity that the whitelist is intended to hold. Returns: Response from API. """ params = {"tenantname": tenant_name, "whitelistname": whitelist_name, "entitytype": entity_type} remove_nulls_from_dictionary(params) whitelist = self.http_request("POST", "/incident/createGlobalWhitelist", headers={"token": self._token}, params=params) return whitelist def delete_whitelist_entry_request( self, tenant_name: str, whitelist_name: str, whitelist_type: str, entity_id: str, attribute_name: str, attribute_value: str, ) -> dict: """Delete a whitelist entry by sending POST request. Args: tenant_name: Name of the tenant the whitelist belongs to. whitelist_name: Name of the whitelist. whitelist_type: Type of whitelist that user wants to delete from. entity_id: Entity ID value that needs to be removed from the whitelist. attribute_name: Name of the attribute being removed. attribute_value: The value of the attribute being removed. Returns: Response from API. """ params = { "tenantname": tenant_name, "whitelistname": whitelist_name, "whitelisttype": whitelist_type, "entityid": entity_id, "attributename": attribute_name, "attributevalue": attribute_value, } remove_nulls_from_dictionary(params) return self.http_request("GET", "/incident/removeFromWhitelist", headers={"token": self._token}, params=params) def delete_lookup_table_config_and_data_request(self, name: str) -> str: """Delete a lookup table and its configuration data from Securonix. Args: name (str): Name of the lookup table. Returns: str: Response from API. """ params = {"lookupTableName": name} return self.http_request( "DELETE", "/lookupTable/deleteLookupConfigAndData", headers={"token": self._token}, params=params, response_type="text", ) def get_lookup_tables_request(self, max_records: int | None = 50, offset: int | None = 0) -> list: """Get the list of lookup tables stored on the Securonix platform. Args: max_records (Optional[int]): Number of records to return. Default value is 50. offset (Optional[int]): Specify from which record the data should be returned. Returns: Response from API. """ params = {"max": max_records, "offset": offset} return self.http_request("GET", "/lookupTable/listLookupTables", headers={"token": self._token}, params=params) def add_entry_to_lookup_table_request(self, name: str, entries: list[dict], tenant_name: str | None = None) -> str: """Adds the provided entries to the specified lookup table. Args: name (str): Name of the lookup table in which to add the data. entries (List[Dict]): List of entries to add to the table. tenant_name (Optional[str]): Tenant name to which the lookup table belongs to. """ body = {"lookupTableName": name, "tenantName": tenant_name, "lookupTableData": entries} remove_nulls_from_dictionary(body) return self.http_request( "POST", "/lookupTable/addLookupTableData", headers={"token": self._token}, json=body, response_type="text" ) def list_lookup_table_entries_request( self, name: str, query: str | None = None, attribute: str | None = "key", max_records: int | None = 15, offset: int | None = 0, page_num: int | None = 1, sort: str | None = None, order: str | None = "asc", ) -> list: """List the entries of the lookup table. Args: name (str): Name of the lookup table. query (Optional[str], optional): Query to filter the entries of the lookup table. Defaults to None. attribute (Optional[str], optional): Column name on which to filter the data. Defaults to 'key'. max_records (Optional[int], optional): Number of records to retrieve. Defaults to 15. offset (Optional[int], optional): Specify from which record the data should be returned. Defaults to 0. page_num (Optional[int], optional): Specify a value to retrieve the records from a specified page. Defaults to 1. sort (Optional[str]): Name of the column on which to sort the data. order (Optional[str]): The order in which to sort the data. Returns: List: List of lookup table entries. """ headers = {"token": self._token, "Content-Type": "application/json"} body = { "lookupTableName": name, "query": query, "attribute": attribute, "max": max_records, "offset": offset, "pagenum": page_num, "sort": sort, "order": order, } remove_nulls_from_dictionary(body) payload = json.dumps(body) return self.http_request("GET", "/lookupTable/getLookupTableData", headers=headers, data=payload) def create_lookup_table_request( self, tenant_name: str, name: str, scope: str, field_names: list, encrypt: list, key: list ) -> dict: """Create a lookup table by sending a POST request. Args: tenant_name: Name of the tenant the whitelist belongs to. name: Lookup table name. scope: Scope of lookup table. field_names: Field names for lookup table. encrypt: Field name which data needs to be encrypted. key: Field name to be used as key. Returns: Response from API. """ data: dict[str, Any] = {"lookupTableName": name, "lookupTableScope": scope, "tenantName": tenant_name} field_list: list = [] for field in field_names: field_dic = {"fieldName": field, "encrypt": field in encrypt, "key": field in key} field_list.append(field_dic) data.update({"lookupFieldList": field_list}) remove_nulls_from_dictionary(data) response = self.http_request( "POST", "/lookupTable/createLookupTable", headers={"token": self._token}, json=data, response_type="text" ) return response def delete_lookup_table_entries(self, name: str, lookup_unique_keys: list[str]) -> str: """Delete entries from the lookup table. Args: name (str): Name of the lookup table. lookup_unique_keys (List[str]): List of keys to delete from the lookup table. Returns: str: Response from API. """ data: dict[str, Any] = {"lookupTableName": name, "keyList": lookup_unique_keys} response = self.http_request( "DELETE", "/lookupTable/deleteLookupKeys", headers={"token": self._token}, json=data, response_type="text" ) return response def test_module(client: Client) -> str: """ Performs basic get request to get incident samples """ params = demisto.params() client.list_workflows_request() if params.get("isFetch"): validate_configuration_parameters(params) validate_mirroring_parameters(params=params) timestamp_format = "%Y-%m-%dT%H:%M:%S.%fZ" from_epoch = date_to_timestamp(parse_date_range("1 day", utc=True)[0], date_format=timestamp_format) to_epoch = date_to_timestamp(datetime.now(), date_format=timestamp_format) client.list_incidents_request(from_epoch, to_epoch, incident_status="opened") return "ok" def list_workflows(client: Client, *_) -> tuple[str, dict, dict]: """List all workflows. Args: client: Client object with request. *_: Returns: Outputs. """ workflows = client.list_workflows_request() workflows_readable, workflows_outputs = parse_data_arr(workflows) human_readable = tableToMarkdown( name="Available workflows:", t=workflows_readable, headers=["Workflow", "Type", "Value"], removeNull=True ) entry_context = {"Securonix.Workflows(val.Workflow == obj.Workflow)": workflows_outputs} return human_readable, entry_context, workflows def get_default_assignee_for_workflow(client: Client, args: dict) -> tuple[str, dict, dict]: """Perform action on an incident. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ workflow = str(args.get("workflow")) default_assignee = client.get_default_assignee_for_workflow_request(workflow) workflow_output = { "Workflow": workflow, "Type": default_assignee.get("type"), "Value": default_assignee.get("value"), } entry_context = {"Securonix.Workflows(val.Workflow === obj.Workflow)": workflow_output} human_readable = f"Default assignee for the workflow {workflow} is: {default_assignee.get('value')}." return human_readable, entry_context, default_assignee def list_possible_threat_actions(client: Client, *_) -> tuple[str, dict, dict]: """List all workflows. Args: client: Client object with request. *_: Returns: Outputs. """ threat_actions = client.list_possible_threat_actions_request() human_readable = f"Possible threat actions are: {', '.join(threat_actions)}." entry_context = {"Securonix.ThreatActions": threat_actions} return human_readable, entry_context, threat_actions def list_policies(client: Client, *_) -> tuple[str, dict, dict]: """List all policies. Args: client: Client object with request. *_: Returns: Outputs. """ policies_xml = client.list_policies_request() policies_json = xml2json(policies_xml) policies = json.loads(policies_json) policies_arr = policies.get("policies").get("policy") policies_readable, policies_outputs = parse_data_arr(policies_arr) headers = ["ID", "Name", "Criticality", "Created On", "Created By", "Description"] human_readable = tableToMarkdown(name="Policies:", t=policies_readable, headers=headers, removeNull=True) entry_context = {"Securonix.Policies(val.ID === obj.ID)": policies_outputs} return human_readable, entry_context, policies def list_resource_groups(client: Client, *_) -> tuple[str, dict, dict]: """List all resource groups. Args: client: Client object with request. *_: Returns: Outputs. """ resource_groups_xml = client.list_resource_groups_request() resource_groups_json = xml2json(resource_groups_xml) resource_groups = json.loads(resource_groups_json) resource_groups_arr = resource_groups.get("resourceGroups").get("resourceGroup") resource_groups_readable, resource_groups_outputs = parse_data_arr(resource_groups_arr) headers = ["Name", "Type"] human_readable = tableToMarkdown(name="Resource groups:", t=resource_groups_readable, headers=headers, removeNull=True) entry_context = {"Securonix.ResourceGroups(val.Name === obj.Name)": resource_groups_outputs} return human_readable, entry_context, resource_groups def list_users(client: Client, *_) -> tuple[str, dict, dict]: """List all users. Args: client: Client object with request. *_: Returns: Outputs. """ users_xml = client.list_users_request() users_json = xml2json(users_xml) users = json.loads(users_json) users_arr = users.get("users").get("user") users_readable, users_outputs = parse_data_arr(users_arr) headers = ["Employee Id", "First Name", "Last Name", "Criticality", "Title", "Email"] human_readable = tableToMarkdown(name="Resource groups:", t=users_readable, headers=headers, removeNull=True) entry_context = {"Securonix.Users(val.EmployeeID === obj.EmployeeID)": users_outputs} return human_readable, entry_context, users def list_activity_data(client: Client, args) -> tuple[str, dict, dict]: """List activity data. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ from_ = args.get("from", "").strip() to_ = args.get("to", "").strip() query = escape_spotter_query(args.get("query", "").strip()) max_records = arg_to_number(args.get("max", "1000"), arg_name="max") if max_records is not None and (max_records < 1 or max_records > 10000): raise ValueError(MESSAGE["INVALID_MAX_VALUE"]) activity_data = client.list_activity_data_request(from_, to_, query, max_records) # type: ignore if activity_data.get("error"): raise Exception( f"Failed to get activity data in the given time frame.\nError from Securonix is: {activity_data.get('errorMessage')}" ) activity_events = activity_data.get("events") activity_readables, activity_outputs = parse_data_arr(activity_events) for index, activity in enumerate(activity_readables): if activity.get("Eventid"): activity["EventID"] = activity.get("Eventid") del activity["Eventid"] if index < len(activity_outputs) and activity_outputs[index].get("Eventid"): activity_outputs[index]["EventID"] = activity_outputs[index].get("Eventid") del activity_outputs[index]["Eventid"] if "Timeline" in activity: activity["Timeline"] = timestamp_to_datestring(activity.get("Timeline", 0), is_utc=True) headers = ["EventID", "Eventtime", "Message", "Accountname", "Timeline", "Devicehostname", "Accountresourcekey"] human_readable = tableToMarkdown( name="Activity data:", t=[ {key: string_escape_MD(value) for key, value in activity_readable.items()} for activity_readable in activity_readables ], headers=headers, removeNull=True, ) pagination_data = { "totalDocuments": activity_data.get("totalDocuments"), "message": activity_data.get("message"), "queryId": activity_data.get("queryId"), "command_name": "securonix-list-activity-data", } entry_context = {"Securonix.Activity(val.command_name === obj.command_name)": remove_empty_elements(pagination_data)} activity_outputs = remove_empty_elements(activity_outputs) if activity_outputs: entry_context["Securonix.ActivityData(val.EventID === obj.EventID)"] = activity_outputs return human_readable, entry_context, activity_data def list_violation_data(client: Client, args) -> list[CommandResults]: """List violation data. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ from_ = args.get("from", "").strip() to_ = args.get("to", "").strip() query = escape_spotter_query(args.get("query", "").strip()) query_id = args.get("query_id", "").strip() max_violations = arg_to_number(args.get("max", "1000")) if max_violations is not None and max_violations <= 0: raise ValueError(MESSAGE["INVALID_MAX_VALUE"]) violation_data = client.list_violation_data_request(from_, to_, query, query_id, max_violations) if violation_data.get("error"): raise Exception( f"Failed to get violation data in the given time frame.\n" f"Error from Securonix is: {violation_data.get('errorMessage')}" ) violation_events = violation_data.get("events") if len(violation_events) > 0: # type: ignore[arg-type] violation_readables, violation_outputs = parse_data_arr(violation_events) headers = ["EventID", "Eventtime", "Message", "Policyname", "Accountname"] human_readable = tableToMarkdown( name="Activity data:", t=[ {key: string_escape_MD(value) for key, value in violation_readable.items()} for violation_readable in violation_readables ], headers=headers, removeNull=True, ) data = { "totalDocuments": violation_data.get("totalDocuments"), "message": violation_data.get("message"), "queryId": violation_data.get("queryId"), } return [ CommandResults( outputs_prefix="Securonix.ViolationData", readable_output=human_readable, outputs=remove_empty_elements(violation_outputs), raw_response=violation_data, outputs_key_field=[ "Policyname", "Violator", "Resourcegroupid", "Tenantname", "Resourcename", "EmployeeID", "Accountname", "Ipaddress", ], ), CommandResults( outputs_prefix="Securonix.Violation", outputs=remove_empty_elements(data), readable_output=f"#### Next page query id: {data.get('queryId')}", ), ] else: return [CommandResults(readable_output="There are no violation events.", outputs={}, raw_response=violation_data)] def run_polling_command(client, args: dict, command_name: str, search_function: Callable): """ For Scheduling command. Args: client: Client object with request. args: Command arguments. command_name: Name of the command. search_function: Callable object of command. Returns: Outputs. """ command_results = [] result = search_function(client, args) command_results.append(result) outputs = result[0].raw_response.get("events") delay_type = client.get_securonix_retry_delay_type() retry_count: int = client.get_securonix_retry_count() retry_delay: int = client.get_securonix_retry_delay() if len(outputs) == 0 and retry_count > 0: if delay_type == "Exponential": retry_delay = client.get_securonix_retry_delay() * 2 retry_timeout: int = retry_delay * retry_count + retry_count * 1 policy_type = args.get("policy_type", "").strip().upper() if policy_type in POLICY_TYPES_TO_RETRY: args["to"] = datetime.now().astimezone(timezone.utc).strftime(r"%m/%d/%Y %H:%M:%S") polling_args = {"polling": True, **args} scheduled_command = ScheduledCommand( command=command_name, next_run_in_seconds=retry_delay, args=polling_args, timeout_in_seconds=retry_timeout ) command_results.append(CommandResults(scheduled_command=scheduled_command)) return command_results return result def list_incidents(client: Client, args: dict) -> tuple[str, dict, dict]: """List incidents. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ timestamp_format = "%Y-%m-%dT%H:%M:%S.%fZ" from_, _ = parse_date_range(args.get("from"), utc=True) from_epoch = date_to_timestamp(from_, date_format=timestamp_format) to_ = args.get("to") if "to_" in args else datetime.now() to_epoch = date_to_timestamp(to_, date_format=timestamp_format) incident_types = str(args.get("incident_types")) if "incident_types" in args else "opened" max_incidents = str(args.get("max", "50")) incidents = client.list_incidents_request(from_epoch, to_epoch, incident_types, max_incidents) total_incidents = incidents.get("totalIncidents") if not total_incidents or float(total_incidents) <= 0.0: return "No incidents where found in this time frame.", {}, incidents incidents_items: list = incidents.get("incidentItems", []) incidents_readables, incidents_outputs = parse_data_arr(incidents_items) headers = ["IncidentID", "Incident Status", "Incident Type", "Priority", "Reason"] human_readable = tableToMarkdown( name="Incidents:", t=[ {key: string_escape_MD(value) for key, value in incidents_readable.items()} for incidents_readable in incidents_readables ], headers=headers, removeNull=True, ) entry_context = {"Securonix.Incidents(val.IncidentID === obj.IncidentID)": incidents_outputs} return human_readable, entry_context, incidents def get_incident(client: Client, args: dict) -> tuple[str, dict, dict]: """Get incident. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ incident_id = str(args.get("incident_id")) incident = client.get_incident_request(incident_id) incident_items = incident.get("incidentItems") if not incident_items: raise Exception("Incident ID is not in Securonix.") incident_readables, incident_outputs = parse_data_arr(incident_items) human_readable = tableToMarkdown( name="Incident:", t=[ {key: string_escape_MD(value) for key, value in incident_readable.items()} for incident_readable in incident_readables ], removeNull=True, ) entry_context = {"Securonix.Incidents(val.IncidentID === obj.IncidentID)": incident_outputs} return human_readable, entry_context, incident def get_incident_status(client: Client, args: dict) -> tuple[str, dict, dict]: """Get incident. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ incident_id = str(args.get("incident_id")) incident = client.get_incident_status_request(incident_id) incident_status = incident.get("status") incident_outputs = {"IncidentID": incident_id, "IncidentStatus": incident_status} entry_context = {"Securonix.Incidents(val.IncidentID === obj.IncidentID)": incident_outputs} return f"Incident {incident_id} status is {incident_status}.", entry_context, incident def get_incident_workflow(client: Client, args: dict) -> tuple[str, dict, dict]: """Get incident workflow. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ incident_id = str(args.get("incident_id")) incident = client.get_incident_workflow_request(incident_id) incident_workflow = incident.get("workflow") incident_outputs = {"IncidentID": incident_id, "WorkflowName": incident_workflow} entry_context = {"Securonix.Incidents(val.IncidentId === obj.IncidentId)": incident_outputs} return f"Incident {incident_id} workflow is {incident_workflow}.", entry_context, incident def get_incident_available_actions(client: Client, args: dict) -> tuple[str, dict, dict]: """Get incident available actions. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ incident_id = str(args.get("incident_id")) incident_actions = client.get_incident_available_actions_request(incident_id) if not incident_actions: return f"Incident {incident_id} does not have any available actions.", {}, incident_actions actions = [] for action_details in incident_actions: actions.append(action_details.get("actionName")) incident_outputs = {"IncidentID": incident_id, "AvailableActions": actions} entry_context = {"Securonix.Incidents(val.IncidentID === obj.IncidentID)": incident_outputs} return f"Incident {incident_id} available actions: {actions}.", entry_context, incident_actions def get_incident_attachments(client: Client, args: dict, incident_id: str = None): """Get incident attachments. Args: client: Client object with request. args: Usually demisto.args() incident_id: Incident ID Returns: Outputs. """ incident_id_ = args.get("incident_id", "").strip() attachment_type = ",".join(argToList(args.get("attachment_type"))) attachment_from = args.get("from") attachment_to = args.get("to") if attachment_from: attachment_from = attachment_from.strip() attachment_from = date_to_timestamp(arg_to_datetime(attachment_from, arg_name="attachment_from")) if attachment_to: attachment_to = attachment_to.strip() attachment_to = date_to_timestamp(arg_to_datetime(attachment_to, arg_name="attachment_to")) if incident_id: attachments_res = client.get_incident_attachments_request(incident_id) else: attachments_res = client.get_incident_attachments_request( incident_id_, attachment_type, attachment_from, # type: ignore attachment_to, ) # type: ignore try: # So if there is no attachments then in response status code will be 200 and in content there is json with # error field if "Content-Disposition" not in attachments_res.headers: return CommandResults(readable_output=f"#### No Attachments found for Incident ID: {incident_id_}") except requests.exceptions.JSONDecodeError: # type: ignore # Here if API have attachments then it will return byte data so then res.json() raise decode error. Means we # received attachments that's in below code there is debug log demisto.debug("Retrieved attachment for incident.") content_disposition = attachments_res.headers.get("Content-Disposition") filename = content_disposition.split(";")[1].replace("filename=", "") file_list = [] if filename.startswith(incident_id or incident_id_): zip_obj = ZipFile(io.BytesIO(attachments_res.content)) zip_filenames = zip_obj.namelist() zip_obj.extractall(path=os.path.abspath(os.getcwd())) zip_obj.close() file_list.append( CommandResults( outputs_prefix="Securonix.Incidents.Attachments", outputs=[{"IncidentID": incident_id_, "Files": zip_filenames}], readable_output=f"### Incident ID: {incident_id_}", ) ) for name in zip_filenames: with open(name, "br") as file: file_list.append(fileResult(filename=name, data=file.read())) return file_list else: file_list.extend( [ CommandResults( outputs_prefix="Securonix.Incidents.Attachments", outputs=[{"IncidentID": incident_id_, "Files": filename}], readable_output=f"### Incident ID: {incident_id_}", ), fileResult(filename=filename, data=attachments_res.content), ] ) return file_list def perform_action_on_incident(client: Client, args: dict) -> tuple[str, dict, dict]: """Perform action on an incident. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ incident_id = str(args.get("incident_id")) action = str(args.get("action")) action_parameters = str(args.get("action_parameters", "")) incident_result = client.perform_action_on_incident_request(incident_id, action, action_parameters) if incident_result != "submitted": raise Exception(f"Failed to perform the action {action} on incident {incident_id}.") return f"Action {action} was performed on incident {incident_id}.", {}, incident_result def add_comment_to_incident(client: Client, args: dict) -> tuple[str, dict, dict]: """Add comment to an incident. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ incident_id = str(args.get("incident_id")) comment = str(args.get("comment")) incident = client.add_comment_to_incident_request(incident_id, comment) if not incident: raise Exception(f"Failed to add comment to the incident {incident_id}.") return f"Comment was added to the incident {incident_id} successfully.", {}, incident def create_incident(client: Client, args: dict) -> tuple[str, dict, dict]: """Create an incident. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ violation_name = str(args.get("violation_name")) resource_group = str(args.get("resource_group")) resource_name = str(args.get("resource_name")) entity_type = str(args.get("entity_type")) entity_name = str(args.get("entity_name")) action_name = str(args.get("action_name")) workflow = str(args.get("workflow")) if "workflow" in args else None comment = str(args.get("comment")) if "comment" in args else None criticality = str(args.get("criticality")) if "criticality" in args else None if "create incident" in action_name and not workflow: raise Exception(f"Creating an incident with the action: {action_name}, Supply a workflow.") response = client.create_incident_request( violation_name, resource_group, resource_name, entity_type, entity_name, action_name, workflow, comment, criticality ) result = response.get("result") if not result: raise Exception(f"Failed to create the incident.\nResponse from Securonix is: {response!s}") message = response.get("messages") if message: if isinstance(message, list) and "Invalid" in message[0]: message = message[0] raise Exception(f"Failed to create the incident with message: \n{message}") if "Invalid" in message: raise Exception(f"Failed to create the incident with message: \n{message}") incident_data = result.get("data") incident_items = incident_data.get("incidentItems") incident_readable, incident_outputs = parse_data_arr(incident_items) headers = ["Entity", "Incident Status", "Incident Type", "IncidentID", "Priority", "Reason", "Url"] human_readable = tableToMarkdown( name="Incident was created successfully", t=incident_readable, headers=headers, removeNull=True ) entry_context = {"Securonix.Incidents(val.IncidentID === obj.IncidentID)": incident_outputs} return human_readable, entry_context, response def list_watchlists(client: Client, *_) -> tuple[str, dict, dict]: """List all watchlists. Args: client: Client object with request. Returns: Outputs. """ watchlists = client.list_watchlist_request() if not watchlists: raise Exception("Failed to list watchlists.") human_readable = f"Watchlists: {', '.join(watchlists)}." entry_context = {"Securonix.WatchlistsNames": watchlists} return human_readable, entry_context, watchlists def get_watchlist(client: Client, args) -> tuple[str, dict, dict]: """Get watchlist data. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ watchlist_name = args.get("watchlist_name", "").strip() watchlist = client.get_watchlist_request(watchlist_name) watchlist_events = watchlist.get("events") if not watchlist_events: raise Exception( "Watchlist does not contain items.\nMake sure the watchlist is not empty and that the watchlist name is correct." ) fields_to_drop = ["decayflag", "tenantid", "tenantname", "watchlistname", "type"] watchlist_readable, watchlist_events_outputs = parse_data_arr(watchlist_events, fields_to_drop=fields_to_drop) watchlist_outputs = { "Watchlistname": watchlist_name, "Type": watchlist_events[0].get("type"), "TenantID": watchlist_events[0].get("tenantid"), "TenantName": watchlist_events[0].get("tenantname"), "Events": watchlist_events_outputs, } headers = ["Entityname", "Fullname", "Workemail", "Expired"] human_readable = tableToMarkdown( name=f"Watchlist {watchlist_name} of type {watchlist_outputs.get('Type')}: ", t=watchlist_readable, headers=headers, removeNull=True, ) entry_context = {"Securonix.Watchlists(val.Watchlistname === obj.Watchlistname)": watchlist_outputs} return human_readable, entry_context, watchlist def create_watchlist(client: Client, args) -> tuple[str, dict, dict]: """Create a watchlist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ watchlist_name = args.get("watchlist_name", "").strip() tenant_name = args.get("tenant_name", "").strip() response = client.create_watchlist_request(watchlist_name, tenant_name) if "successfully" not in response: raise Exception(f"Failed to list watchlists.\nResponse from Securonix is: {response!s}") human_readable = f"Watchlist {watchlist_name} was created successfully." watchlist = {"Watchlistname": watchlist_name, "TenantName": tenant_name} remove_nulls_from_dictionary(watchlist) entry_context = { "Securonix.Watchlists(val.Watchlistname === obj.Watchlistname && val.TenantName === obj.TenantName)": watchlist } return human_readable, entry_context, response def check_entity_in_watchlist(client: Client, args) -> tuple[str, dict, dict]: """Check if entity is in a watchlist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ entity_name = args.get("entity_name") watchlist_name = args.get("watchlist_name") watchlist = client.check_entity_in_watchlist_request(entity_name, watchlist_name) result = watchlist.get("result") if result == "NO" or (isinstance(result, list) and result[0] == "NO"): human_readable = f"Entity unique identifier {entity_name} provided is not on the watchlist: {watchlist_name}." output = {"Entityname": entity_name} else: # YES human_readable = f"The Entity unique identifier {entity_name} provided is on the watchlist: {watchlist_name}." output = {"Entityname": entity_name, "Watchlistname": watchlist_name} entry_context = {"Securonix.EntityInWatchlist(val.Entityname === obj.Entityname)": output} return human_readable, entry_context, watchlist def add_entity_to_watchlist(client: Client, args) -> tuple[str, dict, dict]: """Adds an entity to a watchlist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ watchlist_name = args.get("watchlist_name") entity_type = args.get("entity_type") entity_name = args.get("entity_name") expiry_days = args.get("expiry_days") if "expiry_days" in args else "30" response = client.add_entity_to_watchlist_request(watchlist_name, entity_type, entity_name, expiry_days) if "successfull" not in response: raise Exception( f"Failed to add entity {entity_name} to the watchlist {watchlist_name}.\nError from Securonix is: {response}." ) human_readable = f"Added successfully the entity {entity_name} to the watchlist {watchlist_name}." return human_readable, {}, response def list_threats(client: Client, args: dict[str, Any]) -> tuple[str, dict, dict]: """List threats violated within a specified time range and get details about the threat models and policies violated. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ date_from = date_to_timestamp(arg_to_datetime(args.get("date_from"), arg_name="date_from")) date_to = date_to_timestamp( arg_to_datetime(args.get("date_to", datetime.now().strftime("'%Y-%m-%dT%H:%M:%S'")), arg_name="date_to") ) page_size = arg_to_number(args.get("page_size", 10), arg_name="page_size") tenant_name = args.get("tenant_name") offset = arg_to_number(args.get("offset", 0), arg_name="offset") threat_response = client.list_threats_request(date_from, date_to, tenant_name, offset, page_size) # type: ignore threat_response = remove_empty_elements(threat_response) threat_readable, threats_outputs = parse_data_arr(threat_response) headers = [ "ThreatName", "EntityID", "Violator", "Category", "Resourcegroupname", "Resourcename", "Resourcetype", "GenerationTime", "Policies", "TenantID", "Tenantname", ] human_readable = tableToMarkdown(name="Threats:", t=threat_readable, headers=headers, removeNull=True) entry_context = { "Securonix.Threat(val.EntityID === obj.EntityID && val.Resourcename === obj.Resourcename && val.Resourcetype " "=== obj.Resourcetype && val.Resourcegroupname === obj.Resourcegroupname && val.Policies.toString() === " "obj.Policies.toString())": threats_outputs } return human_readable, entry_context, threat_response def get_incident_activity_history(client: Client, args: dict[str, Any]) -> tuple[str, dict, list]: """Get the incident activity history for the specified incident ID. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ incident_id = args.get("incident_id", "").strip() # Raises error when user has provided ' ' in input. if not incident_id: raise ValueError("Incident ID is a required parameter.") # Retrieve activity history for the specified incident ID. activity_history = client.get_incident_activity_history_request(incident_id) # Prepare entry context for the command. # As the response is in such a format, we can not determine a primary key for the context data. entry_context = {"Securonix.IncidentHistory": activity_history} # Prepare human-readable output for the command. activity_history_readable = [ { "Action Taken": h.get("actiontaken"), "Username": h.get("username"), "Event Time": h.get("eventTime"), "Status": h.get("status"), "Last Status": h.get("lastStatus"), "Comment": "\n".join([c.get("Comments", "") for c in h.get("comment", [])]), "Playbook ID": h.get("playBookOutput", {}).get("playBookId"), "Playbook Name": h.get("playBookOutput", {}).get("playBookName"), "Playbook Executor": h.get("playBookOutput", {}).get("executor"), "Attachment Name": h.get("attachment"), } for h in activity_history ] # Reversing the human-readable list, as we want to show the latest activity first, rather than the old. activity_history_readable.reverse() headers = [ "Action Taken", "Username", "Event Time", "Status", "Last Status", "Comment", "Playbook ID", "Playbook Name", "Playbook Executor", "Attachment Name", ] human_readable = tableToMarkdown( f"Incident activity history for ID: {incident_id}", t=activity_history_readable, headers=headers, removeNull=True ) return human_readable, entry_context, activity_history def list_whitelists(client: Client, args: dict[str, Any]) -> tuple[str, dict, list]: """List all whitelist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ tenant_name = args.get("tenant_name", "").strip() whitelists = client.list_whitelists_request(tenant_name) whitelists_entries = [] for whitelist in whitelists: whitelist_details = whitelist.split("|", 3) if len(whitelist_details) < 3: empty_details = 3 - len(whitelist_details) whitelist_details += "null" * empty_details whitelists_entries.append( { "WhitelistName": whitelist_details[0].strip().replace("null", ""), "WhitelistType": whitelist_details[1].strip().replace("null", ""), "TenantName": whitelist_details[2].strip().replace("null", ""), } ) whitelists_entries = remove_empty_elements(whitelists_entries) headers = ["WhitelistName", "WhitelistType", "TenantName"] human_readable = tableToMarkdown(name="Whitelists:", t=whitelists_entries, headers=headers, removeNull=True) entry_context = { "Securonix.Whitelist(val.WhitelistName === obj.WhitelistName && val.TenantName === obj.TenantName)": whitelists_entries } return human_readable, entry_context, whitelists def get_whitelist_entry(client: Client, args: dict[str, Any]) -> tuple[str, dict, dict]: """Get information for the specified whitelist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ tenant_name = args.get("tenant_name", "").strip() whitelist_name = args.get("whitelist_name", "").strip() whitelist = client.get_whitelist_entry_request(tenant_name, whitelist_name) if not whitelist: raise Exception("Whitelist does not contain items.\nMake sure the whitelist_name is not empty and it is correct.") whitelist_entries = [] for key, val in whitelist.items(): whitelist_entries.append({"Entity/Attribute": key, "ExpiryDate": val}) watchlist_outputs = {"WhitelistName": whitelist_name, "TenantName": tenant_name, "Entries": whitelist_entries} remove_nulls_from_dictionary(watchlist_outputs) headers = ["Entity/Attribute", "ExpiryDate"] human_readable = tableToMarkdown( name=f"Whitelist: {whitelist_name}", t=remove_empty_elements(whitelist_entries), headers=headers, removeNull=True ) entry_context = { "Securonix.Whitelist(val.WhitelistName === obj.WhitelistName && val.TenantName === obj.TenantName)": watchlist_outputs } return human_readable, entry_context, whitelist def add_whitelist_entry(client: Client, args) -> tuple[str, dict, dict]: """Adds an entry to a whitelist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ tenant_name = args.get("tenant_name", "").strip() whitelist_name = args.get("whitelist_name", "").strip() whitelist_type = args.get("whitelist_type", "").strip() entity_type = args.get("entity_type", "").strip() entity_id = args.get("entity_id", "").strip() expiry_date = args.get("expiry_date", "").strip() resource_name = args.get("resource_name", "").strip() resource_group_id = args.get("resource_group_id", "").strip() attribute_name = args.get("attribute_name", "").strip() attribute_value = args.get("attribute_value", "").strip() violation_type = args.get("violation_type", "").strip() violation_name = args.get("violation_name", "").strip() if whitelist_type not in ["Global", "Attribute"]: raise Exception("Provide valid whitelist_type") if whitelist_type == "Global" and entity_type not in ["Users", "Activityaccount", "Resources", "Activityip"]: raise Exception("Provide valid entity_type") if whitelist_type == "Attribute": if attribute_name not in ["source ip", "resourcetype", "transactionstring"]: raise Exception("Provide valid attribute_name") if violation_type not in ["Policy", "ThreatModel", "Functionality"]: raise Exception("Provide valid violation_type") try: if expiry_date: datetime.strptime(expiry_date, "%m/%d/%Y") except ValueError: raise Exception("exipry_date is not in MM/DD/YYYY format") response = client.add_whitelist_entry_request( tenant_name, whitelist_name, whitelist_type, entity_type, entity_id, expiry_date, resource_name, resource_group_id, attribute_name, attribute_value, violation_type, violation_name, ) if response.get("status_code") == 400: raise Exception(f"Failed to add entity to the whitelist.\nError from Securonix is: {response}.") human_readable = "Entity added to global whitelist Successfully." return human_readable, {}, response def create_whitelist(client: Client, args) -> tuple[str, dict, dict]: """Create a whitelist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ tenant_name = args.get("tenant_name", "").strip() whitelist_name = args.get("whitelist_name", "").strip() entity_type = args.get("entity_type", "").strip() if entity_type not in VALID_ENTITY_TYPE: raise Exception(f"{entity_type} is invalid entity_type. Valid entity types are {VALID_ENTITY_TYPE}") response = client.create_whitelist_request(tenant_name, whitelist_name, entity_type) if "successfully" not in str(response.get("messages")).lower(): raise Exception(f"Failed to create whitelist.\nResponse from Securonix is: {response!s}") human_readable = f"Whitelist {whitelist_name} was created successfully." return human_readable, {}, response def delete_lookup_table_config_and_data(client: Client, args: dict[str, Any]) -> tuple: """Delete a lookup table and its configuration data from Securonix. Args: client (Client): Client object with request. args: (Dict[str, Any]): Usually demisto.args(). Returns: Outputs. """ name = args.get("name", "").strip() if not name: raise ValueError("Lookup table name is a required argument.") response = client.delete_lookup_table_config_and_data_request(name=name) if "successfully" not in response.lower(): raise Exception(f"Failed to delete lookup table and its data.\nResponse from Securonix is: {response!s}") human_readable = f"The table {name} has been deleted successfully on Securonix." entry_context = { "Securonix.LookupTable(val.lookupTableName === obj.lookupTableName)": {"lookupTableName": name, "isDeleted": True} } return human_readable, entry_context, response def delete_whitelist_entry(client: Client, args) -> tuple[str, dict, dict]: """Delete an entry from the whitelist. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ tenant_name = args.get("tenant_name", "").strip() whitelist_name = args.get("whitelist_name", "").strip() whitelist_type = args.get("whitelist_type", "").strip() entity_id = args.get("entity_id", "").strip() attribute_name = args.get("attribute_name", "").strip() attribute_value = args.get("attribute_value", "").strip() validate_delete_whitelist_parameters(whitelist_type, entity_id, attribute_name, attribute_value, tenant_name) response = client.delete_whitelist_entry_request( tenant_name, whitelist_name, whitelist_type, entity_id, attribute_name, attribute_value ) result = response.get("result", []) if "successfully" not in str(result).lower(): raise Exception(f"Failed to remove entry from whitelist.\nResponse from Securonix is: {result!s}") human_readable = "".join(result).replace(" ..! ", ".") return human_readable, {}, response def list_lookup_tables(client: Client, args: dict[str, Any]) -> tuple[str, dict, list]: """Retrieves a list of lookup tables available within the Securonix platform. Args: client (Client): Client object with request. args: (Dict[str, Any]): Usually demisto.args(). Returns: Outputs. """ max_records = arg_to_number(args.get("max", "50").strip() or "50") offset = arg_to_number(args.get("offset", "0").strip() or "0") lookup_tables = client.get_lookup_tables_request(max_records=max_records, offset=offset) lookup_table_readable = [ { "Tenant Name": table.get("tenantName", ""), "Lookup Table Name": table.get("lookupTableName", ""), "Total Records": table.get("totalRecords", ""), "Scope": table.get("scope", ""), "Type of Lookup Table": table.get("type", ""), } for table in lookup_tables ] headers = ["Tenant Name", "Lookup Table Name", "Total Records", "Scope", "Type of Lookup Table"] human_readable = tableToMarkdown("Lookup Tables:", t=lookup_table_readable, headers=headers, removeNull=True) entry_context = {"Securonix.LookupTable(val.lookupTableName === obj.lookupTableName)": remove_empty_elements(lookup_tables)} return human_readable, entry_context, lookup_tables def validate_expiry_time_of_lookup_table_entries(table_entries: Union[dict, list[dict]]) -> None: """Check whether the expiration time of the lookup table entries is valid. Args: table_entries (Union[Dict, List[Dict]]): Lookup table entries to add to the lookup table. """ def is_expiration_time_in_valid_format(expiration_time: str) -> None: try: datetime.strptime(expiration_time, "%m/%d/%Y") except ValueError as exception: raise ValueError("The value of expiryDate field is not in MM/DD/YYYY format.") from exception if isinstance(table_entries, dict): expiration_time = table_entries.get("expiryDate") if expiration_time: is_expiration_time_in_valid_format(expiration_time) if isinstance(table_entries, list): for entry in table_entries: expiration_time = entry.get("expiryDate") if expiration_time: is_expiration_time_in_valid_format(expiration_time) def add_entry_to_lookup_table(client: Client, args: dict[str, Any]) -> tuple: """Add entries to the lookup table. Args: client (Client): Client object with request. args: (Dict[str, Any]): Usually demisto.args(). Returns: Outputs. """ table_name = args.get("name", "").strip() tanant_name = args.get("tenant_name", "").strip() json_data = args.get("json_data", "").strip() entry_id = args.get("file_entry_id", "").strip() # Validate the command arguments. if not table_name: raise ValueError("Lookup table name is a required parameter.") if not json_data and not entry_id: raise ValueError("Either JSON data or file entry ID is required to add data to lookup table.") # File will take precedence over JSON data. if entry_id: file_obj = demisto.getFilePath(entry_id) file_path = file_obj.get("path") try: with open(file_path) as file: json_entries = json.loads(file.read()) except json.JSONDecodeError as exception: raise Exception(f"Could not able to parse the provided JSON data. Error: {exception!s}") from exception else: try: json_entries = json.loads(json_data) except json.JSONDecodeError as exception: raise Exception(f"Could not able to parse the provided JSON data. Error: {exception!s}") from exception validate_expiry_time_of_lookup_table_entries(table_entries=json_entries) if isinstance(json_entries, dict): json_entries = [json_entries] response = client.add_entry_to_lookup_table_request(name=table_name, entries=json_entries, tenant_name=tanant_name) if "successfully" not in response.lower(): raise Exception(f"Failed adding entries to the lookup table. Error from Securonix: {response!s}") return response, {}, response def prepare_entry_contex_lookup_table_entries_list(entries: list[dict]) -> list[dict]: """Prepare entry context for list-lookup-table-entries command. Args: entries (List[Dict]): Response received from API. Returns: List[Dict]: Entry context list. """ new_entries = [] for entry in entries: new_entry: dict[str, Any] = {"entry": []} for key, value in entry.items(): if key.startswith("value_"): new_entry["entry"].append( { "key": key[6:], # Remove "value_" from the key. "value": value, } ) else: new_entry[key] = value new_entries.append(new_entry) return new_entries def prepare_human_readable_for_lookup_table_entries_list(entries: list[dict]) -> str: """Prepare human-readable string for lookup-table-entries-list command. Args: entries (List[Dict]): List of entries. Returns: str: Markdown string. """ table = [] for entry in entries: new_entry = { "Key": entry.get("key"), "Timestamp": entry.get("timestamp"), "Lookup Unique Key": entry.get("lookupuniquekey"), "Tenant Name": entry.get("tenantname"), } for e in entry.get("entry", []): new_entry[e["key"]] = e["value"] table.append(new_entry) return tableToMarkdown(name="Entries:", t=table, removeNull=True) def list_lookup_table_entries(client: Client, args: dict[str, Any]) -> tuple: """List the entries of the provided lookup table. Args: client (Client): Client object with request. args (Dict[str, Any]): Usually demisto.args(). Returns: Outputs. """ name = args.get("name", "").strip() query = args.get("query", "").strip() attribute = args.get("attribute", "key").strip() or "key" max_records = arg_to_number(args.get("max", "15").strip() or "15") offset = arg_to_number(args.get("offset", "0").strip() or "0") page_num = arg_to_number(args.get("page_num", "1").strip() or "1") sort = args.get("sort", "").strip() order = args.get("order", "asc").strip().lower() or "asc" # Validate required parameters. if not name: raise ValueError("Lookup table name is a required argument.") # Validate order argument. if order and order not in ["asc", "desc"]: raise ValueError('Order argument must be "asc" or "desc".') response = client.list_lookup_table_entries_request( name=name, query=query, attribute=attribute, max_records=max_records, offset=offset, page_num=page_num, sort=sort, order=order, ) entry_context_list: list[dict] = prepare_entry_contex_lookup_table_entries_list(response) human_readable = prepare_human_readable_for_lookup_table_entries_list(entry_context_list) entry_context = { "Securonix.LookupTableEntries(val.lookupuniquekey === obj.lookupuniquekey)": remove_empty_elements(entry_context_list) } return human_readable, entry_context, response def create_lookup_table(client: Client, args) -> tuple[str, dict, dict]: """Create a lookup table. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ name = args.get("name", "").strip() scope = args.get("scope") tenant_name = args.get("tenant_name", "").strip() field_names = argToList(args.get("field_names", "").strip()) encrypt = argToList(args.get("encrypt", "").strip()) key = argToList(args.get("key", "").strip()) response = client.create_lookup_table_request(tenant_name, name, scope, field_names, encrypt, key) if "successfully" not in response.lower(): # type: ignore[attr-defined] raise Exception(f"Failed to create lookup table.\nResponse from Securonix is: {response}") human_readable = f"Lookup Table {name} created successfully." return human_readable, {}, response def delete_lookup_table_entries(client: Client, args: dict[str, Any]): """Delete entries from the lookup table. Args: client: Client object with request. args: Usually demisto.args() Returns: Outputs. """ name = args.get("name", "").strip() lookup_unique_keys = argToList(args.get("lookup_unique_keys", "").strip()) if not name: raise ValueError("Lookup table name is a required parameter.") if not lookup_unique_keys: raise ValueError("At least one lookup table key is required to execute the command.") response = client.delete_lookup_table_entries(name=name, lookup_unique_keys=lookup_unique_keys) human_readable = f"Successfully deleted following entries from {name}: {', '.join(lookup_unique_keys)}." return human_readable, {}, response def fetch_securonix_incident( client: Client, fetch_time: str | None, incident_status: str, default_severity: str, max_fetch: str, last_run: dict, close_incident: bool, ) -> list: """Uses to fetch incidents into Demisto Documentation: https://github.com/demisto/content/tree/master/docs/fetching_incidents Args: client: Client object with request fetch_time: From when to fetch if first time, e.g. `3 days` incident_status: Incident statuses to fetch, can be: all, opened, closed, updated default_severity: Default incoming incident severity last_run: Last fetch object. max_fetch: maximum amount of incidents to fetch close_incident: Close respective Securonix incident. Returns: incidents, new last_run """ timestamp_format = "%Y-%m-%dT%H:%M:%S.%fZ" if not last_run: # if first time running new_last_run = { "from": int( arg_to_datetime(fetch_time, arg_name="First fetch time range").timestamp() * 1000 # type: ignore ), "to": int(datetime.now(tz=timezone.utc).timestamp() * 1000), "offset": 0, } demisto.debug(f"No last run object found, creating new last run object with value: {json.dumps(new_last_run)}") elif "time" in last_run: demisto.debug("Upgrading the last run object.") new_last_run = last_run new_last_run["from"] = date_to_timestamp(last_run.get("time"), date_format=timestamp_format) new_last_run["to"] = int(datetime.now(tz=timezone.utc).timestamp() * 1000) new_last_run["offset"] = 0 del new_last_run["time"] else: new_last_run = last_run demisto.debug("Using the last run object got from the previous run.") demisto_incidents: list = [] from_epoch = new_last_run.get("from") to_epoch = new_last_run.get("to") offset = new_last_run.get("offset") demisto.info(f"Fetching Securonix incidents. From: {from_epoch}. To: {to_epoch}. Offset: {offset}") if incident_status.lower() == "all": incident_status = "updated" securonix_incidents = client.list_incidents_request( from_epoch=str(from_epoch), to_epoch=str(to_epoch), incident_status=incident_status, max_incidents=max_fetch, offset=str(offset), ) if securonix_incidents: already_fetched: list[str] = new_last_run.get("already_fetched", []) # type: ignore incident_items = securonix_incidents.get("incidentItems", []) for incident in incident_items: incident_id = str(incident.get("incidentId", 0)) violator_id = str(incident.get("violatorId", 0)) reasons = incident.get("reason", []) policy_list: list[str] = [] policy_stages_json = {} policy_stages_table = [] if isinstance(reasons, list): for reason in reasons: if isinstance(reason, str) and "PolicyType" in reason: policy_type = reason.split(":")[-1].strip() incident["policy_type"] = policy_type if isinstance(reason, dict) and "Policies" in reason: # Parse the policies. policies = reason.get("Policies") if not isinstance(policies, dict): continue policy_keys = list(policies.keys()) policy_keys.sort() for stage_key in policy_keys: stage_dict = policies.get(stage_key) if not stage_dict or not isinstance(stage_dict, dict): continue stage_name = list(stage_dict.keys())[0] stage_policies: list[str] = stage_dict.get(stage_name) # type: ignore if not stage_policies or not isinstance(stage_policies, list): continue stage_policies_str = ", ".join(str(policy) for policy in stage_policies) # type: ignore policy_list.extend(stage_policies) # type: ignore policy_stages_json[f"{stage_key}:{stage_name}"] = stage_policies # noqa: E231 policy_stages_table.append( {"Stage Name": f"{stage_key}:{stage_name}", "Policies": stage_policies_str} # noqa: E231 ) if policy_list: # Add the parsed policies to the incident. incident["policy_list"] = list(dict.fromkeys(policy_list)) incident["policy_stages_json"] = policy_stages_json incident["policy_stages_table"] = policy_stages_table if incident_id not in already_fetched: incident.update(get_mirroring()) if close_incident: incident["close_sx_incident"] = True else: incident["close_sx_incident"] = False incident_name = get_incident_name(incident, incident_id, violator_id) demisto_incidents.append( { "name": incident_name, "occurred": timestamp_to_datestring(incident.get("lastUpdateDate")), "severity": incident_priority_to_dbot_score(incident.get("priority"), default_severity), "rawJSON": json.dumps(incident), } ) already_fetched.append(str(incident_id)) # If incidents returned from API, then only update the offset value. if incident_items: new_offset = offset + len(incident_items) # type: ignore new_from = from_epoch new_to = to_epoch demisto.debug(f"Updating the offset to {new_offset}.") # Else, reset the value of offset. From value would be the to_epoch of previous run. # And, To value would be current timestamp. else: new_offset = 0 new_from = to_epoch new_to = int(datetime.now(tz=timezone.utc).timestamp() * 1000) demisto.debug(f"Resetting the offset to 0. New From is {new_from}. New To is {new_to}.") new_last_run.update( { "from": new_from, # type: ignore "to": new_to, # type: ignore "offset": new_offset, "already_fetched": already_fetched, # type: ignore } ) demisto.setLastRun({"value": json.dumps(new_last_run)}) demisto.info(f"Creating {len(demisto_incidents)} new incidents.") return demisto_incidents def fetch_securonix_threat(client: Client, fetch_time: str | None, tenant_name: str, max_fetch: str, last_run: dict) -> list: """Uses to fetch threats into Demisto. Args: client: Client object with request fetch_time: From when to fetch if first time, e.g. `3 days` tenant_name: Name of the tenant from which threat belongs to last_run: Last fetch object. max_fetch: maximum amount of incidents to fetch Returns: incidents, new last_run """ timestamp_format = "%Y-%m-%dT%H:%M:%S.%fZ" if not last_run: # if first time running new_last_run = { "time": arg_to_datetime(fetch_time, arg_name="First fetch time range").strftime( # type: ignore timestamp_format ) } else: new_last_run = last_run demisto_incidents: list = [] from_epoch = date_to_timestamp(new_last_run.get("time"), date_format=timestamp_format) to_epoch = date_to_timestamp(datetime.now(), date_format=timestamp_format) # Get threats from Securonix demisto.info(f"Fetching Securonix Threats. From: {from_epoch}. To: {to_epoch}") offset = last_run.get("offset", 0) securonix_threats = client.list_threats_request(from_epoch, to_epoch, tenant_name, offset, max_fetch) # type: ignore already_fetched = last_run.get("already_fetched", []) if securonix_threats: for threat in securonix_threats: threat_name = threat.get("threatname", "Securonix Threat") entity_id = threat.get("entityid", "") resource_name = threat.get("resourcename", "") resource_type = threat.get("resourcetype", "") resource_group_name = threat.get("resourcegroupname", "") policies = ", ".join(sorted(threat.get("policies", []))) if (entity_id, resource_name, resource_type, resource_group_name, policies) not in already_fetched: incident_name = f"{threat_name}, Entity ID: {entity_id}" demisto_incidents.append( { "name": incident_name, "occurred": timestamp_to_datestring(threat.get("generationtime_epoch", datetime.now())), "rawJSON": json.dumps(threat), } ) already_fetched.append((entity_id, resource_name, resource_type, resource_group_name, policies)) new_last_run.update({"offset": offset + int(max_fetch), "already_fetched": already_fetched}) else: now = datetime.now().strftime(timestamp_format) new_last_run.update({"offset": 0, "time": now}) demisto.setLastRun({"value": json.dumps(new_last_run)}) return demisto_incidents def get_incident_name(incident: dict, incident_id: str, violator_id: str) -> str: """Get the incident name by concatenating the incident reasons if possible Args: incident: incident details incident_id: the incident id violator_id: the violator id Returns: incident name. """ incident_reasons = incident.get("reason", []) try: incident_reason = "" for reason in incident_reasons: if isinstance(reason, str): if reason.startswith("Threat Model: "): incident_reason += f"{reason[14:]}, " if reason.startswith("Policy: "): incident_reason += f"{reason[8:]}, " if incident_reason: # Remove ", " last chars and concatenate with the incident ID incident_name = f"{incident_reason[:-2]}: {incident_id}" else: incident_name = f"Securonix Incident {incident_id}, Violator ID: {violator_id}" except ValueError: incident_name = f"Securonix Incident: {incident_id}." return incident_name def get_modified_remote_data_command(client: Client, args: dict[str, Any]) -> GetModifiedRemoteDataResponse: """Retrieve the IDs of the incidents which are updated since the last updated. Args: client: XSOAR client to use. args: lastUpdate: When was the last time we retrieved data. Returns: GetModifiedRemoteDataResponse: List of incidents IDs which are modified since the last update. """ # Retrieve the arguments passed with the command. command_args = GetModifiedRemoteDataArgs(args) # Parse the last update date got from the command arguments. command_last_run_date = dateparser.parse(command_args.last_update, settings={"TIMEZONE": "UTC"}) demisto.debug(f"Last update date of get-modified-remote-data command is {command_last_run_date}.") # Convert the datetime object to epoch as the API requires the time in epoch format. from_epoch_time = date_to_timestamp(command_last_run_date) # End time for the API call will be current time. to_epoch_time = date_to_timestamp(datetime.now(tz=timezone.utc)) offset = 0 len_of_incidents = 0 updated_incident_ids = [] while True: offset += len_of_incidents list_incidents_resp = client.list_incidents_request( from_epoch=str(from_epoch_time), to_epoch=str(to_epoch_time), incident_status="updated", max_incidents="500", offset=str(offset), ) len_of_incidents = len(list_incidents_resp.get("incidentItems", [])) if len_of_incidents == 0: break # Extract the IDs of the incidents. updated_incident_ids.extend([inc.get("incidentId") for inc in list_incidents_resp.get("incidentItems", [])]) if len(updated_incident_ids) >= 10000: break # Filter out None values if there are any. updated_incident_ids: list[str] = list(filter(None, updated_incident_ids)) # Filter out any duplicate incident IDs. updated_incident_ids = list(set(updated_incident_ids)) # At max 10,000 incidents should be updated. updated_incident_ids = updated_incident_ids[:10000] demisto.debug(f"Number of incidents modified between {from_epoch_time} to {to_epoch_time} are {len(updated_incident_ids)}.") demisto.debug(f"List of modified incident ids between {from_epoch_time} to {to_epoch_time} is {updated_incident_ids}.") return GetModifiedRemoteDataResponse(updated_incident_ids) def get_remote_data_command( client: Client, args: dict[str, Any], close_states_of_securonix: list[str] ) -> Union[str, GetRemoteDataResponse]: """Return the updated incident and updated entries. Args: client: XSOAR client to use. args: id: Incident ID to retrieve. lastUpdate: When was the last time we retrieved data. close_states_of_securonix: List of Securonix incident states that can be considered as closed. Returns: Union[str, GetRemoteDataResponse]: first entry is the incident (which can be completely empty) and the new entries. """ new_entries_to_return = [] timestamp_format = "%Y-%m-%dT%H:%M:%S.%fZ" sx_incident_id: str = args.get("id") # type: ignore demisto.debug(f"Getting update for remote {sx_incident_id}.") command_last_run_dt = arg_to_datetime(arg=args.get("lastUpdate"), arg_name="lastUpdate", required=True) command_last_run_epoch = date_to_timestamp(command_last_run_dt, date_format=timestamp_format) demisto.debug(f"The time when the last time get-remote-data command is called for current incident is {command_last_run_dt}.") # Retrieve the latest incident data from the Securonix platform. remote_incident_data = client.get_incident_request(incident_id=sx_incident_id) remote_incident_data = remote_incident_data.get("incidentItems", []) remote_incident_data = remote_incident_data[0] if not remote_incident_data: return "Incident was not found." # Check the last modified date of the incident fetched. incident_last_update_dt = arg_to_datetime( arg=remote_incident_data.get("lastUpdateDate"), arg_name="lastUpdateDate", required=True ) incident_last_update_epoch = date_to_timestamp(incident_last_update_dt, date_format=timestamp_format) if command_last_run_epoch > incident_last_update_epoch: demisto.debug(f"Nothing new in the Securonix incident {sx_incident_id}.") else: demisto.debug(f"The Securonix incident {sx_incident_id} is updated.") activity_history = client.get_incident_activity_history_request(incident_id=sx_incident_id) filtered_history_entries = filter_activity_entries_by_time(activity_history, timestamp=command_last_run_epoch) # Update the status of XSOAR incident. close_incident = argToBoolean(demisto.params().get("close_incident", False)) # Skip closing of XSOAR if the close Securonix incident checkbox is checked. if not close_incident: if is_incident_closed_on_securonix(filtered_history_entries, close_states_of_securonix): demisto.debug(f"Closing the XSOAR incident as its respective Securonix incident {sx_incident_id} is closed.") close_comments = extract_closing_comments(filtered_history_entries, close_states_of_securonix) new_entries_to_return.append( { "Type": EntryType.NOTE, "Contents": {"dbotIncidentClose": True, "closeNotes": close_comments, "closeReason": "Resolved"}, "ContentsFormat": EntryFormat.JSON, "Note": True, } ) else: demisto.debug(f"Not closing the XSOAR incident as its respective Securonix incident {sx_incident_id} is still open.") # Update the comments. comment_entries = filter_comment_activity_entries(filtered_history_entries) for entry in comment_entries: comments_text = [] comments_list = entry.get("comment", {}) for _comment in comments_list: comments_text.append(_comment.get("Comments")) comments_text: list[str] = list(filter(None, comments_text)) if "Mirrored From XSOAR" in ", ".join(comments_text): demisto.debug("Skipping the comment as it is mirrored from XSOAR.") continue new_entries_to_return.append( { "Type": EntryType.NOTE, "Contents": f"[Mirrored From Securonix]\n" f"Added By: {entry.get('username')}\n" f"Added At: {entry.get('eventTime')} UTC\n" f"Comment Content: {', '.join(comments_text)}", "ContentsFormat": EntryFormat.TEXT, "Note": True, } ) # Update the attachments. attachment_entries = filter_attachment_activity_entries(filtered_history_entries) attachment_names = extract_names_of_attachments_from_entries(attachment_entries) if attachment_entries: attachment_file_entries = get_incident_attachments(client, {}, incident_id=sx_incident_id) # Removing the first entry from the response, as it is CommandResults. for xsoar_file_entry in attachment_file_entries: if isinstance(xsoar_file_entry, CommandResults): continue if xsoar_file_entry.get("File", "") in attachment_names: new_entries_to_return.append(xsoar_file_entry) return GetRemoteDataResponse(remote_incident_data, new_entries_to_return) def create_xsoar_to_securonix_state_mapping(params: dict[str, Any]) -> CommandResults: """Create a mapping of Securonix status and action with XSOAR's states. Args: params: The configuration parameters got from demisto.params() Returns: CommandResults: Standard CommandResults object. """ global XSOAR_TO_SECURONIX_STATE_MAPPING active_state_action = params.get("active_state_action_mapping", "").strip() active_state_status = params.get("active_state_status_mapping", "").strip().lower() close_state_action = params.get("closed_state_action_mapping", "").strip() close_state_status = params.get("closed_state_status_mapping", "").strip().lower() XSOAR_TO_SECURONIX_STATE_MAPPING["ACTIVE"] = {"action": active_state_action, "status": active_state_status} XSOAR_TO_SECURONIX_STATE_MAPPING["DONE"] = {"action": close_state_action, "status": close_state_status} hr_table = [ { "XSOAR Status": "Active", "Securonix Status": XSOAR_TO_SECURONIX_STATE_MAPPING["ACTIVE"]["status"], "Securonix Action Name": XSOAR_TO_SECURONIX_STATE_MAPPING["ACTIVE"]["action"], }, { "XSOAR Status": "Closed", "Securonix Status": XSOAR_TO_SECURONIX_STATE_MAPPING["DONE"]["status"], "Securonix Action Name": XSOAR_TO_SECURONIX_STATE_MAPPING["DONE"]["action"], }, ] human_readable = tableToMarkdown( "State Mapping:", t=hr_table, headers=["XSOAR Status", "Securonix Status", "Securonix Action Name"], removeNull=True ) return CommandResults( outputs_prefix="Securonix.StateMapping", outputs=XSOAR_TO_SECURONIX_STATE_MAPPING, readable_output=human_readable, raw_response=XSOAR_TO_SECURONIX_STATE_MAPPING, ) def update_remote_system(client: Client, args: dict[str, Any]) -> str: """This command pushes local changes to the remote incident. Args: client (Client): XSOAR Client to use. args (Dict[str, Any]): args['data']: The data to send to the remote system. args['entries']: The entries to send to the remote system. args['incident_changed']: Boolean telling us if the local incident indeed changed or not. args['remote_incident_id']: The remote incident id. Returns: str: The remote incident ID. """ parsed_args = UpdateRemoteSystemArgs(args) remote_incident_id = parsed_args.remote_incident_id xsoar_incident_id = parsed_args.data.get("id", "") new_entries = parsed_args.entries if new_entries: for entry in new_entries: demisto.debug(f"Sending the entry with ID: {entry.get('id')} and Type: {entry.get('type')}") entry_content = entry.get("contents", "") entry_user = entry.get("user", "dbot") or "dbot" comment_str = ( f"[Mirrored From XSOAR] XSOAR Incident ID: {xsoar_incident_id}\nAdded By: {entry_user}\nComment: {entry_content}" ) client.add_comment_to_incident_request(remote_incident_id, comment_str) close_incident = parsed_args.data.get("securonixcloseincident", False) if not close_incident and parsed_args.incident_changed and parsed_args.inc_status == IncidentStatus.DONE: delta_keys = parsed_args.delta.keys() if "closingUserId" not in delta_keys and "closeReason" not in delta_keys: return remote_incident_id close_notes = parsed_args.delta.get("closeNotes", "") close_reason = parsed_args.delta.get("closeReason", "") close_user_id = parsed_args.delta.get("closingUserId", "") closing_comment = ( f"[Mirrored From XSOAR] XSOAR Incident ID: {xsoar_incident_id}\n" f"Closed By: {close_user_id}\nClose Reason: {close_reason}\nClose Notes: {close_notes}" ) demisto.debug(f"Closing Comment: {closing_comment}") client.perform_action_on_incident_request( incident_id=remote_incident_id, action=XSOAR_TO_SECURONIX_STATE_MAPPING["DONE"]["action"], action_parameters="" ) client.add_comment_to_incident_request(incident_id=remote_incident_id, comment=closing_comment) return remote_incident_id def main(): """ PARSE AND VALIDATE INTEGRATION PARAMS """ params = demisto.params() remove_nulls_from_dictionary(params) host = params.get("host", None) tenant = params.get("tenant") if not host: server_url = tenant if not tenant.startswith("http://") and not tenant.startswith("https://"): server_url = f"https://{tenant}" # noqa: E231 if not tenant.endswith(".securonix.net/Snypr/ws/"): server_url += ".securonix.net/Snypr/ws/" else: host = host.rstrip("/") if not host.endswith("/ws"): host += "/ws/" server_url = host username = params.get("username") password = params.get("password") verify = not params.get("unsecure", False) proxy = demisto.params().get("proxy") is True # Updating TOTAL_RETRY_COUNT to get user provided value global TOTAL_RETRY_COUNT TOTAL_RETRY_COUNT = arg_to_number( params.get("securonix_retry_count", "0"), # type: ignore arg_name="securonix_retry_count", ) TOTAL_RETRY_COUNT = min(TOTAL_RETRY_COUNT, 5) securonix_retry_delay_type = params.get("securonix_retry_delay_type", "Exponential") securonix_retry_delay = arg_to_number(params.get("securonix_retry_delay", "30"), arg_name="securonix_retry_delay") if securonix_retry_delay <= 30: # type: ignore securonix_retry_delay = 30 elif securonix_retry_delay >= 300: # type: ignore securonix_retry_delay = 300 if securonix_retry_delay_type == "Exponential": securonix_retry_delay = int(securonix_retry_delay / 2) # type: ignore # Create a state mapping from XSOAR to Securonix. create_xsoar_to_securonix_state_mapping(params) command = demisto.command() LOG(f"Command being called in Securonix is: {command}") try: client = Client( tenant=tenant, server_url=server_url, username=username, password=password, verify=verify, proxy=proxy, securonix_retry_count=TOTAL_RETRY_COUNT, # type: ignore securonix_retry_delay=securonix_retry_delay, # type: ignore[arg-type] securonix_retry_delay_type=securonix_retry_delay_type, ) commands: dict[str, Callable[[Client, dict[str, str]], tuple[str, dict[Any, Any], dict[Any, Any]]]] = { "securonix-list-workflows": list_workflows, "securonix-get-default-assignee-for-workflow": get_default_assignee_for_workflow, "securonix-list-possible-threat-actions": list_possible_threat_actions, "securonix-list-policies": list_policies, "securonix-list-resource-groups": list_resource_groups, "securonix-list-users": list_users, "securonix-list-activity-data": list_activity_data, "securonix-list-incidents": list_incidents, "securonix-get-incident": get_incident, "securonix-get-incident-status": get_incident_status, "securonix-get-incident-workflow": get_incident_workflow, "securonix-get-incident-available-actions": get_incident_available_actions, "securonix-perform-action-on-incident": perform_action_on_incident, "securonix-add-comment-to-incident": add_comment_to_incident, "securonix-create-incident": create_incident, "securonix-list-watchlists": list_watchlists, "securonix-get-watchlist": get_watchlist, "securonix-create-watchlist": create_watchlist, "securonix-check-entity-in-watchlist": check_entity_in_watchlist, "securonix-add-entity-to-watchlist": add_entity_to_watchlist, "securonix-threats-list": list_threats, "securonix-incident-activity-history-get": get_incident_activity_history, # type: ignore[dict-item] "securonix-whitelists-get": list_whitelists, # type: ignore[dict-item] "securonix-whitelist-entry-list": get_whitelist_entry, "securonix-whitelist-entry-add": add_whitelist_entry, "securonix-whitelist-create": create_whitelist, "securonix-lookup-table-config-and-data-delete": delete_lookup_table_config_and_data, # type: ignore "securonix-whitelist-entry-delete": delete_whitelist_entry, "securonix-lookup-tables-list": list_lookup_tables, # type: ignore[dict-item] "securonix-lookup-table-entry-add": add_entry_to_lookup_table, # type: ignore[dict-item] "securonix-lookup-table-entries-list": list_lookup_table_entries, # type: ignore[dict-item] "securonix-lookup-table-create": create_lookup_table, "securonix-lookup-table-entries-delete": delete_lookup_table_entries, } if command == "fetch-incidents": validate_mirroring_parameters(params=params) fetch_time = params.get("fetch_time", "1 hour") tenant_name = params.get("tenant_name") incident_status = params.get("incident_status") if "incident_status" in params else "opened" default_severity = params.get("default_severity", "") max_fetch_ = arg_to_number(params.get("max_fetch", "200"), arg_name="max_fetch") max_fetch = str(min(200, max_fetch_)) # type: ignore last_run = json.loads(demisto.getLastRun().get("value", "{}")) close_incident = argToBoolean(params.get("close_incident", False)) if params.get("entity_type_to_fetch") == "Threat": incidents = fetch_securonix_threat(client, fetch_time, tenant_name, max_fetch, last_run=last_run) else: incidents = fetch_securonix_incident( client, fetch_time, incident_status, default_severity, max_fetch, last_run=last_run, close_incident=close_incident, ) demisto.incidents(incidents) elif command == "securonix-list-violation-data": return_results( run_polling_command( client=client, args=demisto.args(), search_function=list_violation_data, command_name="securonix-list-violation-data", ) ) elif command == "test-module": demisto.results(test_module(client)) elif command == "securonix-incident-attachment-get": return_results(get_incident_attachments(client=client, args=demisto.args())) elif command == "get-remote-data": close_states_of_securonix = params.get("close_states_of_securonix", "").strip().lower() close_states_of_securonix = argToList(close_states_of_securonix) return_results(get_remote_data_command(client, demisto.args(), close_states_of_securonix)) elif command == "get-modified-remote-data": return_results(get_modified_remote_data_command(client, demisto.args())) elif command == "securonix-xsoar-state-mapping-get": return_results(create_xsoar_to_securonix_state_mapping(params=params)) elif command == "update-remote-system": return_results(update_remote_system(client, demisto.args())) elif command in commands: return_outputs(*commands[command](client, demisto.args())) else: raise NotImplementedError(f'Command "{command}" is not implemented.') except Exception as err: return_error(str(err)) if __name__ in ["__main__", "builtin", "builtins"]: main()