Palo Alto Networks - Prisma SASE
Provides commands for interaction with Prisma SASE API.
Network Security · Palo Alto Networks - Strata Cloud Manager
Details
| ID | Palo Alto Networks - Prisma SASE |
|---|---|
| Provider | Palo Alto Networks |
| Category | Network Security |
| From Version | 6.5.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud |
README
Provides commands for interaction with Prisma SASE API.
This integration was integrated and tested with version v1 of Palo Alto Networks - Prisma SASE
Configure Palo Alto Networks - Strata Cloud Manager in Cortex
| Parameter | Description | Required |
|---|---|---|
| Server URL | True | |
| API Client ID | True | |
| API Client Secret | True | |
| Tenant Services Group ID | Default Tenant Services Group ID to use for API calls. Example: 1234567890. | True |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
prisma-sase-security-rule-create
Create a new security rule.
Base Command
prisma-sase-security-rule-create
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| position | Rule position. Possible values are: pre, post. Default is pre. | Optional |
| name | The name of the security rule. | Required |
| action | Rule action. Possible values are: deny, allow, drop, reset-client, reset-server, reset-both. | Required |
| description | The security rule’s description. | Optional |
| log_setting | Rule log setting. | Optional |
| application | A comma-separated list of applications. Default is any. | Optional |
| category | A comma-separated list of categories. You can get category values by running the prisma-sase-custom-url-category-list command. Default is any. | Optional |
| destination | A comma-separated list of destination networks. Default is any. | Optional |
| destination_hip | A comma-separated list of destination HIPs. | Optional |
| profile_setting | Security profiles to apply to the traffic. | Optional |
| service | Services the rule applies to. Default is any. | Optional |
| source | A comma-separated list of source networks. Default is any. | Optional |
| source_hip | A comma-separated list of source HIPs. | Optional |
| source_user | A semi-colon (;) separated list of source users or groups. Default is any. | Optional |
| tag | A comma-separated list of rule tags. | Optional |
| from | A comma-separated list of source zones. Default is any. | Optional |
| to | A comma-separated list of destination zones. Default is any. | Optional |
| disabled | Whether the rule is disabled. | Optional |
| negate_source | Negate the source. | Optional |
| negate_destination | Negate the destination. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.SecurityRule | String | Created security rule. |
| PrismaSase.SecurityRule.action | String | Security rule action. |
| PrismaSase.SecurityRule.application | String | Security rule application. |
| PrismaSase.SecurityRule.category | String | Security rule category. |
| PrismaSase.SecurityRule.description | String | Security rule description. |
| PrismaSase.SecurityRule.destination | String | Security rule destination. |
| PrismaSase.SecurityRule.folder | String | Security rule folder. |
| PrismaSase.SecurityRule.from | String | Security rule from field (source zone(s)). |
| PrismaSase.SecurityRule.id | String | Security rule ID. |
| PrismaSase.SecurityRule.name | String | Security rule name. |
| PrismaSase.SecurityRule.position | String | Security rule position. |
| PrismaSase.SecurityRule.profile_setting.group | String | Security rule group. |
| PrismaSase.SecurityRule.service | String | Security rule service. |
| PrismaSase.SecurityRule.source | String | Security rule source. |
| PrismaSase.SecurityRule.source_user | String | Security rule source user. |
| PrismaSase.SecurityRule.to | String | Security rule to field (destination zone(s)). |
Command example
!prisma-sase-security-rule-create name="somename11" action="allow"
Context Example
{
"PrismaSase": {
"SecurityRule": {
"action": "allow",
"application": [
"any"
],
"category": [
"any"
],
"destination": [
"any"
],
"folder": "Shared",
"from": [
"any"
],
"id": "Id",
"name": "somename11",
"position": "pre",
"service": [
"any"
],
"source": [
"any"
],
"source_user": [
"any"
],
"to": [
"any"
]
}
}
}
Human Readable Output
Security Rule Created
Action Application Category Destination Folder From Id Name Position Service Source Source User To allow any any any Shared any Id somename11 pre any any any any
prisma-sase-security-rule-list
Lists all security rules.
Base Command
prisma-sase-security-rule-list
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| position | Security rule position. Possible values are: pre, post. Default is pre. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| page | Page number you would like to view. Each page contains page_size values. Must be used along with page_size. | Optional |
| page_size | Number of results per page to display. | Optional |
| rule_id | A specific security rule to return. If not specified, all security rules will be returned. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.SecurityRule | String | Found security rule. |
| PrismaSase.SecurityRule.action | String | Security rule action. |
| PrismaSase.SecurityRule.application | String | Security rule application. |
| PrismaSase.SecurityRule.category | String | Security rule category. |
| PrismaSase.SecurityRule.description | String | Security rule description. |
| PrismaSase.SecurityRule.destination | String | Security rule destination. |
| PrismaSase.SecurityRule.folder | String | Security rule folder. |
| PrismaSase.SecurityRule.from | String | Security rule from field (source zone(s)). |
| PrismaSase.SecurityRule.id | String | Security rule ID. |
| PrismaSase.SecurityRule.log_setting | String | Security rule log setting. |
| PrismaSase.SecurityRule.name | String | Security rule name. |
| PrismaSase.SecurityRule.position | String | Security rule position. |
| PrismaSase.SecurityRule.service | String | Security rule service. |
| PrismaSase.SecurityRule.source | String | Security rule source. |
| PrismaSase.SecurityRule.source_user | String | Security rule source user. |
| PrismaSase.SecurityRule.tag | String | Security rule tag. |
| PrismaSase.SecurityRule.to | String | Security rule to field (destination zone(s)). |
| PrismaSase.SecurityRule.negate_destination | Boolean | Security rule negate destination. |
Command example
!prisma-sase-security-rule-list limit=1
Context Example
{
"PrismaSase": {
"SecurityRule": {
"action": "drop",
"application": [
"any"
],
"category": [
"any"
],
"description": "Rule to block",
"destination": [
"panw-highrisk-ip-list"
],
"folder": "Shared",
"from": [
"any"
],
"id": "Id",
"log_setting": "Cortex Data Lake",
"name": "name",
"negate_destination": false,
"position": "pre",
"service": [
"any"
],
"source": [
"any"
],
"source_user": [
"any"
],
"tag": [
"best-practice"
],
"to": [
"any"
]
}
}
}
Human Readable Output
Security Rules
Id Name Description Action Destination Folder Id Name Rule to block drop panw-highrisk-ip-list Shared
prisma-sase-candidate-config-push
Push the candidate configuration.
Base Command
prisma-sase-candidate-config-push
Input
| Argument Name | Description | Required |
|---|---|---|
| folders | Comma-separated list of targets (Remote Networks, Mobile Users, Service Connections). | Required |
| description | Configuration push job description. | Optional |
| interval_in_seconds | interval for polling command. Default is 30. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.CandidateConfig.job_id | String | Configuration job ID. |
| PrismaSase.CandidateConfig.result | String | The configuration push result, e.g. OK, FAIL. |
| PrismaSase.CandidateConfig.details | String | The configuration push details. |
Command example
!prisma-sase-candidate-config-push folders="Mobile Users"
Human Readable Output
Waiting for all data to push for job id 201
prisma-sase-security-rule-update
Update an existing security rule.
Base Command
prisma-sase-security-rule-update
Input
| Argument Name | Description | Required |
|---|---|---|
| rule_id | ID of the rule to be changed. | Required |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| position | Security rule position. Possible values are: pre, post. Default is pre. | Optional |
| action | Rule action. Possible values are: deny, allow, drop, reset-client, reset-server, reset-both. | Optional |
| description | The security rule’s description. | Optional |
| log_setting | Rule log setting. | Optional |
| application | A comma-separated list of applications. | Optional |
| category | A comma-separated list of categories. | Optional |
| destination | A comma-separated list of destination networks. | Optional |
| destination_hip | A comma-separated list of destination HIPs. | Optional |
| profile_setting | Security profiles to apply to the traffic. | Optional |
| service | Services the rule applies to. | Optional |
| source | A comma-separated list of source networks. | Optional |
| source_hip | A comma-separated list of source HIPs. | Optional |
| source_user | A semi-colon (;) separated list of source user(s). | Optional |
| tag | A comma-separated list of rule tags. | Optional |
| from | A comma-separated list of source zones. | Optional |
| to | A comma-separated list of destination zones. | Optional |
| disabled | Whether the rule is disabled. | Optional |
| negate_source | Negate source. | Optional |
| negate_destination | Negate the destination. | Optional |
| overwrite | Whether to overwrite the original rule values. Possible values are: true, false. Default is false. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.SecurityRule | String | Edited security rule. |
| PrismaSase.SecurityRule.action | String | Security rule action. |
| PrismaSase.SecurityRule.application | String | Security rule application. |
| PrismaSase.SecurityRule.category | String | Security rule category. |
| PrismaSase.SecurityRule.description | String | Security rule description. |
| PrismaSase.SecurityRule.destination | String | Security rule destination. |
| PrismaSase.SecurityRule.folder | String | Security rule folder. |
| PrismaSase.SecurityRule.from | String | Security rule from field (source zone(s)). |
| PrismaSase.SecurityRule.id | String | Security rule ID. |
| PrismaSase.SecurityRule.name | String | Security rule name. |
| PrismaSase.SecurityRule.profile_setting.group | String | Security rule profile setting. |
| PrismaSase.SecurityRule.service | String | Security rule service. |
| PrismaSase.SecurityRule.source | String | Security rule source. |
| PrismaSase.SecurityRule.source_user | String | Security rule source user. |
| PrismaSase.SecurityRule.to | String | Security rule to field (destination zone(s)). |
Command example
!prisma-sase-security-rule-update rule_id="Id" position="pre" action="deny"
Context Example
{
"PrismaSase": {
"SecurityRule": {
"action": "deny",
"application": [
"any"
],
"category": [
"test"
],
"description": "test",
"destination": [
"any"
],
"folder": "Shared",
"from": [
"any"
],
"id": "Id",
"name": "somename",
"position": "pre",
"service": [
"any"
],
"source": [
"any"
],
"source_user": [
"any"
],
"to": [
"any"
]
}
}
}
Human Readable Output
Security Rule Updated
Action Application Category Description Destination Folder From Id Name Position Service Source Source User To deny any test test any Shared any Id name pre any any any any
prisma-sase-address-object-update
Update an existing address object.
Base Command
prisma-sase-address-object-update
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the address object to edit. | Required |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| description | The address object’s description. | Optional |
| type | The type of the address. Possible values are: ip_netmask, ip_range, ip_wildcard, fqdn. | Optional |
| address_value | The address value (should match the type). | Required |
| tag | A comma-separated list of address object tags. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.Address.description | String | Address description. |
| PrismaSase.Address.folder | String | Address folder. |
| PrismaSase.Address.id | String | Address ID. |
| PrismaSase.Address.address_value | String | Address value. |
| PrismaSase.Address.type | String | Address type. |
| PrismaSase.Address.name | String | Address name. |
Command example
!prisma-sase-address-object-update object_id="Id" address_value="8.8.8.4/32"
Context Example
{
"PrismaSase": {
"Address": {
"address_value": "8.8.8.4/32",
"folder": "Shared",
"id": "Id",
"name": "name",
"type": "ip_netmask"
}
}
}
Human Readable Output
Address Object updated
Address Value Folder Id Name Type 8.8.8.4/32 Shared Id name ip_netmask
prisma-sase-config-job-list
Lists all configuration jobs.
Base Command
prisma-sase-config-job-list
Input
| Argument Name | Description | Required |
|---|---|---|
| job_id | A specific config job to return. If not specified, all config jobs will be returned. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| page | Page number you would like to view. Each page contains page_size values. Must be used along with page_size. | Optional |
| page_size | Number of results per page to display. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.ConfigJob.description | String | Configuration job description. |
| PrismaSase.ConfigJob.end_ts | Date | Configuration job end timestamp. |
| PrismaSase.ConfigJob.id | String | Configuration job ID. |
| PrismaSase.ConfigJob.job_result | String | Configuration job result. |
| PrismaSase.ConfigJob.job_status | String | Configuration job status. |
| PrismaSase.ConfigJob.job_type | String | Configuration job type. |
| PrismaSase.ConfigJob.parent_id | String | Configuration job parent ID. |
| PrismaSase.ConfigJob.percent | String | Configuration job percent. |
| PrismaSase.ConfigJob.result_str | String | Configuration job result string. |
| PrismaSase.ConfigJob.start_ts | Date | Configuration job start timestamp. |
| PrismaSase.ConfigJob.status_str | String | Configuration job status string. |
| PrismaSase.ConfigJob.summary | String | Configuration job summary. |
| PrismaSase.ConfigJob.type_str | String | Configuration job type string. |
| PrismaSase.ConfigJob.uname | String | Configuration job uname. |
prisma-sase-security-rule-delete
Delete a specific security rule.
Base Command
prisma-sase-security-rule-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| rule_id | Rule ID of the rule to be deleted. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
There is no context output for this command.
Command example
!prisma-sase-security-rule-delete rule_id="Id"
Human Readable Output
Security Rule object with id Id and name name was deleted successfully
prisma-sase-address-object-create
Create a new address object.
Base Command
prisma-sase-address-object-create
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| name | The name of the address object. | Required |
| description | The address object’s description. | Optional |
| type | The type of the address. Possible values are: ip_netmask, ip_range, ip_wildcard, fqdn. | Required |
| tag | A comma-separated list of address object tags. | Optional |
| address_value | The address value (should match the type). | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.Address | String | Created address object. |
| PrismaSase.Address.description | String | Address description. |
| PrismaSase.Address.folder | String | Address folder. |
| PrismaSase.Address.id | String | Address ID. |
| PrismaSase.Address.type | String | Address type. |
| PrismaSase.Address.address_value | String | Address value. |
| PrismaSase.Address.name | String | Address name. |
Command example
!prisma-sase-address-object-create name="somename11" type="ip_netmask" address_value="8.8.8.8/32"
Context Example
{
"PrismaSase": {
"Address": {
"address_value": "8.8.8.8/32",
"folder": "Shared",
"id": "id",
"name": "somename11",
"type": "ip_netmask"
}
}
}
Human Readable Output
Address Object Created
Address Value Folder Id Name Type 8.8.8.8/32 Shared Id somename11 ip_netmask
prisma-sase-address-object-delete
Delete a specific address object.
Base Command
prisma-sase-address-object-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the address object to delete. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
There is no context output for this command.
Command example
!prisma-sase-address-object-delete object_id="Id"
Human Readable Output
Address object with id Id and name name was deleted successfully
prisma-sase-address-object-list
Lists all addresses objects.
Base Command
prisma-sase-address-object-list
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | The specific address object to return. If not specified, all addresses will be returned. | Optional |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| page | Page number you would like to view. Each page contains page_size values. Must be used along with page_size. | Optional |
| page_size | Number of results per page to display. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.Address.description | String | Address description. |
| PrismaSase.Address.folder | String | Address folder. |
| PrismaSase.Address.id | String | Address ID. |
| PrismaSase.Address.name | String | Address name. |
| PrismaSase.Address.address_value | String | Address value. |
| PrismaSase.Address.type | String | Address type. |
Command example
!prisma-sase-address-object-list limit=1
Context Example
{
"PrismaSase": {
"Address": {
"address_value": "test.com",
"description": "description",
"folder": "Shared",
"id": "Id",
"name": "name",
"type": "fqdn"
}
}
}
Human Readable Output
Address Objects
Id Name Description Type Address Value Tag Id Palo Alto Networks Sinkhole Palo Alto Networks fqdn test.com
prisma-sase-tag-list
Lists all tags.
Base Command
prisma-sase-tag-list
Input
| Argument Name | Description | Required |
|---|---|---|
| tag_id | A specific tag to return. If not specified, all tags will be returned. | Optional |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| page | Page number you would like to view. Each page contains page_size values. Must be used along with page_size. | Optional |
| page_size | Number of results per page to display. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.Tag.id | String | Tag ID. |
| PrismaSase.Tag.name | String | Tag name. |
| PrismaSase.Tag.folder | String | Tag folder. |
| PrismaSase.Tag.comments | String | Tag comments. |
| PrismaSase.Tag.color | String | The tag color. |
Command example
!prisma-sase-tag-list limit=1
Context Example
{
"PrismaSase": {
"Tag": {
"color": "Olive",
"folder": "predefined",
"name": "Sanctioned"
}
}
}
Human Readable Output
Tags
Id Name Folder Color Comments Id test predefined Olive
prisma-sase-tag-create
Create a new tag.
Base Command
prisma-sase-tag-create
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| name | The tag unique name. | Required |
| color | Tag color. Possible values are: Red, Green, Blue, Yellow, Copper, Orange, Purple, Gray, Light Green, Cyan, Light Gray, Blue Gray, Lime, Black, Gold, Brown, Olive, Maroon, Red-Orange, Yellow-Orange, Forest Green, Turquoise Blue, Azure Blue, Cerulean Blue, Midnight Blue, Medium Blue, Cobalt Blue, Violet Blue, Blue Violet, Medium Violet, Medium Rose, Lavender, Orchid, Thistle, Peach, Salmon, Magenta, Red Violet, Mahogany, Burnt Sienna, Chestnut. | Optional |
| comments | Tag comments. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.Tag.id | String | The tag ID. |
| PrismaSase.Tag.name | String | The tag name. |
| PrismaSase.Tag.folder | String | The tag folder. |
| PrismaSase.Tag.color | String | The tag color. |
| PrismaSase.Tag.comments | String | The tag comments. |
Command example
!prisma-sase-tag-create name="somename11" color="Azure Blue"
Context Example
{
"PrismaSase": {
"Tag": {
"color": "Azure Blue",
"folder": "Shared",
"id": "Id",
"name": "somename11"
}
}
}
Human Readable Output
Address Object Created
Color Folder Id Name Azure Blue Shared Id somename11
prisma-sase-tag-update
Update an existing tag.
Base Command
prisma-sase-tag-update
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| tag_id | The tag ID. | Required |
| color | Tag color. Possible values are: Red, Green, Blue, Yellow, Copper, Orange, Purple, Gray, Light Green, Cyan, Light Gray, Blue Gray, Lime, Black, Gold, Brown, Olive, Maroon, Red-Orange, Yellow-Orange, Forest Green, Turquoise Blue, Azure Blue, Cerulean Blue, Midnight Blue, Medium Blue, Cobalt Blue, Violet Blue, Blue Violet, Medium Violet, Medium Rose, Lavender, Orchid, Thistle, Peach, Salmon, Magenta, Red Violet, Mahogany, Burnt Sienna, Chestnut. | Optional |
| comments | Tag comments. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.Tag.id | String | The tag ID. |
| PrismaSase.Tag.name | String | The tag name. |
| PrismaSase.Tag.folder | String | The tag folder. |
| PrismaSase.Tag.color | String | The tag color. |
| PrismaSase.Tag.comments | String | The tag comments. |
Command example
!prisma-sase-tag-update tag_id="Id" color="Black"
Context Example
{
"PrismaSase": {
"Tag": {
"color": "Black",
"folder": "Shared",
"id": "Id",
"name": "somename"
}
}
}
Human Readable Output
Tag Edited
Color Folder Id Name Black Shared Id somename
prisma-sase-tag-delete
Delete a specific tag.
Base Command
prisma-sase-tag-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| tag_id | The specific tag to delete. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
There is no context output for this command.
Command example
!prisma-sase-tag-delete tag_id="Id"
Human Readable Output
Tag with id Id and name somename was deleted successfully
prisma-sase-address-group-list
Lists all address groups.
Base Command
prisma-sase-address-group-list
Input
| Argument Name | Description | Required |
|---|---|---|
| group_id | A specific address group to return. If not specified, all address groups will be returned. | Optional |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| page | Page number you would like to view. Each page contains page_size values. Must be used along with page_size. | Optional |
| page_size | Number of results per page to display. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.AddressGroup.id | String | The address group ID. |
| PrismaSase.AddressGroup.name | String | The address group name. |
| PrismaSase.AddressGroup.description | String | The address group description. |
| PrismaSase.AddressGroup.addresses | String | The address group addresses. |
| PrismaSase.AddressGroup.dynamic_filter | String | The address group filter. |
Command example
!prisma-sase-address-group-list limit=1
Context Example
{
"PrismaSase": {
"AddressGroup": {
"description": "test",
"dynamic_filter": "'test' or 'test1' and 'best-practice'",
"folder": "Shared",
"id": "Id",
"name": "Test"
}
}
}
Human Readable Output
Address Groups
Id Name Description Addresses Dynamic Filter Id Moishy_Test test ‘test’ or ‘test1’ and ‘best-practice’
prisma-sase-address-group-create
Create a new address group.
Base Command
prisma-sase-address-group-create
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| type | The address group type. Possible values are: static, dynamic. | Required |
| static_addresses | Static addresses for the address group. If the type is static, a value must be provided. | Optional |
| dynamic_filter | Dynamic filter for the address group. If the type is dynamic, a value must be provided. | Optional |
| description | The address group’s description. | Optional |
| name | The name of the address group. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.AddressGroup.id | String | The address group ID. |
| PrismaSase.AddressGroup.name | String | The address group name. |
| PrismaSase.AddressGroup.folder | String | The address group folder. |
| PrismaSase.AddressGroup.description | String | The address group description. |
| PrismaSase.AddressGroup.addresses | String | The address group addresses. |
| PrismaSase.AddressGroup.dynamic_filter | String | The address group filter. |
Command example
!prisma-sase-address-group-create name="somename" dynamic_filter="test" overwrite="false"
Context Example
{
"PrismaSase": {
"AddressGroup": {
"dynamic_filter": "test",
"folder": "Shared",
"id": "Id",
"name": "somename"
}
}
}
Human Readable Output
Address Group created
Dynamic Filter Folder Id Name test Shared Id somename
prisma-sase-address-group-update
Update an existing address group.
Base Command
prisma-sase-address-group-update
Input
| Argument Name | Description | Required |
|---|---|---|
| group_id | The id of the address group. | Required |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| static_addresses | Static addresses for the address group. If the type is static, a value must be provided. | Optional |
| dynamic_filter | Dynamic filter for the address group. If the type is dynamic, a value must be provided. | Optional |
| overwrite | Whether to overwrite the original address group values. Possible values are: true, false. Default is false. | Optional |
| description | The address group’s description. | Optional |
| type | The address group type. Possible values are: dynamic, static. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.AddressGroup.id | String | The address group ID. |
| PrismaSase.AddressGroup.name | String | The address group name. |
| PrismaSase.AddressGroup.folder | String | The address group folder. |
| PrismaSase.AddressGroup.description | String | The address group description. |
| PrismaSase.AddressGroup.addresses | String | The address group addresses. |
| PrismaSase.AddressGroup.dynamic_filter | String | The address group filter. |
Command example
!prisma-sase-address-group-update group_id="Id" dynamic_filter="and 'test2'" overwrite="false"
Context Example
{
"PrismaSase": {
"AddressGroup": {
"dynamic_filter": "test and test2",
"folder": "Shared",
"id": "Id",
"name": "somename"
}
}
}
Human Readable Output
Address Group updated
Dynamic Filter Folder Id Name test and test2 Shared Id somename
prisma-sase-address-group-delete
Delete a specific address group.
Base Command
prisma-sase-address-group-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| group_id | The name of the address group. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
There is no context output for this command.
Command example
!prisma-sase-address-group-delete group_id="Id"
Human Readable Output
Address group with id Id and name somename was deleted successfully
prisma-sase-custom-url-category-list
Lists all custom URL categories.
Base Command
prisma-sase-custom-url-category-list
Input
| Argument Name | Description | Required |
|---|---|---|
| id | A specific url category to return. If not specified, all url categories will be returned. | Optional |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| page | Page number you would like to view. Each page contains page_size values. Must be used along with page_size. | Optional |
| page_size | Number of results per page to display. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.CustomURLCategory.id | String | The URL category ID. |
| PrismaSase.CustomURLCategory.name | String | The URL category name. |
| PrismaSase.CustomURLCategory.folder | String | The URL category folder. |
| PrismaSase.CustomURLCategory.type | String | The URL category type. |
| PrismaSase.CustomURLCategory.list | String | The URL category match list. |
| PrismaSase.CustomURLCategory.description | String | The URL category description. |
Command example
!prisma-sase-custom-url-category-list limit=1
Context Example
{
"PrismaSase": {
"CustomURLCategory": {
"folder": "Shared",
"id": "Id",
"list": [
"www.test.com",
"www.test2.com"
],
"name": "name",
"type": "URL List"
}
}
}
Human Readable Output
Custom Url Categories
Id Name Folder Type List Id name Shared URL List www.test2.com,
www.test.com
prisma-sase-custom-url-category-create
Create a new URL category.
Base Command
prisma-sase-custom-url-category-create
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| type | The custom URL category’s type. Possible values are: URL List, Category Match. | Required |
| value | If the type is URL List, the value will be a comma-separated array of URL addresses. If the type is Category Match, the value will be a comma-separated array of category names. You can get the names by running the prisma-sase-url-access-profile-list command. | Required |
| description | The custom URL category’s description. | Optional |
| name | The name of the custom URL category. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.CustomURLCategory.id | String | The URL category ID. |
| PrismaSase.CustomURLCategory.name | String | The URL category name. |
| PrismaSase.CustomURLCategory.folder | String | The URL category folder. |
| PrismaSase.CustomURLCategory.type | String | The URL category type. |
| PrismaSase.CustomURLCategory.list | String | The URL category match list. |
| PrismaSase.CustomURLCategory.description | String | The URL category description. |
Command example
!prisma-sase-custom-url-category-create type="Category Match" value="low-risk" name="somename11"
Context Example
{
"PrismaSase": {
"CustomURLCategory": {
"folder": "Shared",
"id": "id",
"list": [
"low-risk"
],
"name": "somename11",
"type": "Category Match"
}
}
}
Human Readable Output
Custom URrl Category Created
Folder Id List Name Type Shared Id low-risk somename11 Category Match
prisma-sase-custom-url-category-update
Update an existing url category.
Base Command
prisma-sase-custom-url-category-update
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The custom URL category id. | Required |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| value | The custom URL category’s value. | Optional |
| overwrite | Whether to overwrite the original custom URL category values. Possible values are: true, false. Default is false. | Optional |
| description | The custom URL category’s description. | Optional |
| type | The custom URL category’s type. Possible values are: URL List, Category Match. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.CustomURLCategory.id | String | The URL category ID. |
| PrismaSase.CustomURLCategory.name | String | The URL category name. |
| PrismaSase.CustomURLCategory.folder | String | The URL category folder. |
| PrismaSase.CustomURLCategory.type | String | The URL category type. |
| PrismaSase.CustomURLCategory.list | String | The URL category match list. |
| PrismaSase.CustomURLCategory.description | String | The URL category description. |
Command example
!prisma-sase-custom-url-category-update id="Id" value="high-risk" overwrite="false"
Context Example
{
"PrismaSase": {
"CustomURLCategory": {
"folder": "Shared",
"id": "Id",
"list": [
"low-risk",
"high-risk"
],
"name": "somename",
"type": "Category Match"
}
}
}
Human Readable Output
Custom Url Category updated
Folder Id List Name Type Shared Id low-risk,
high-risksomename Category Match
prisma-sase-custom-url-category-delete
Delete a specific url category.
Base Command
prisma-sase-custom-url-category-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The custom URL category id. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
There is no context output for this command.
Command example
!prisma-sase-custom-url-category-delete id="Id"
Human Readable Output
Custom Url Category with id Id and name somename was deleted successfully
prisma-sase-external-dynamic-list-list
Lists all external dynamic lists.
Base Command
prisma-sase-external-dynamic-list-list
Input
| Argument Name | Description | Required |
|---|---|---|
| id | A specific external dynamic list to return. If not specified, all external dynamic lists will be returned. | Optional |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| limit | The maximum number of results to return. Default is 50. | Optional |
| page | Page number you would like to view. Each page contains page_size values. Must be used along with page_size. | Optional |
| page_size | Number of results per page to display. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.ExternalDynamicList.id | String | The external dynamic list ID. |
| PrismaSase.ExternalDynamicList.name | String | The external dynamic list name. |
| PrismaSase.ExternalDynamicList.folder | String | The external dynamic list folder. |
| PrismaSase.ExternalDynamicList.description | String | The external dynamic list description. |
| PrismaSase.ExternalDynamicList.type | String | The external dynamic list type. |
| PrismaSase.ExternalDynamicList.source | String | The external dynamic list source. |
| PrismaSase.ExternalDynamicList.frequency | String | The external dynamic list frequency. |
Command example
!prisma-sase-external-dynamic-list-list limit=1
Context Example
{
"PrismaSase": {
"ExternalDynamicList": {
"description": "description",
"display_name": "display name",
"folder": "predefined",
"name": "panw-known-ip-list",
"source": "predefined",
"type": "predefined"
}
}
}
Human Readable Output
External Dynamic Lists
Id Name Type Folder Description Source Frequency panw-known-ip-list predefined predefined description predefined
prisma-sase-external-dynamic-list-create
Create a new dynamic list.
Base Command
prisma-sase-external-dynamic-list-create
Input
| Argument Name | Description | Required |
|---|---|---|
| name | The external dynamic list name. | Required |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| description | The dynamic list’s description. | Optional |
| type | The dynamic list’s type. Possible values are: predefined_ip, predefined_url, ip, domain, url. | Required |
| predefined_ip_list | The predefined IP list. If the type is predefined_ip, a value must be provided. Possible values are: panw-torexit-ip-list, panw-bulletproof-ip-list, panw-highrisk-ip-list, panw-known-ip-list. | Optional |
| predefined_url_list | The predefined URL list. If the type is predefined_url, a value must be provided. Possible values are: panw–auth-portal-exclude-list. | Optional |
| source_url | The source URL. If the type is ip, url or domain, a value must be provided. | Optional |
| frequency | Frequency to check for updates. Possible values are: five_minute, hourly, daily, weekly, monthly. | Optional |
| frequency_hour | The frequency hour. If the frequency argument is daily, weekly or monthly, value must be provided. Possible values are 00-23. | Optional |
| day_of_week | The day of the week. If the frequency argument is weekly or monthly, a value must be provided. Possible values are: monday, tuesday, wednesday, thursday, friday, saturday, sunday. | Optional |
| day_of_month | The day of the month. If the frequency argument is monthly, a value must be provided. Possible values are between 1 and 31. | Optional |
| exception_list | The user can exclude certain addresses from the list depending on the type of list. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.ExternalDynamicList.id | String | The external dynamic list ID. |
| PrismaSase.ExternalDynamicList.name | String | The external dynamic list name. |
| PrismaSase.ExternalDynamicList.folder | String | The external dynamic list folder. |
| PrismaSase.ExternalDynamicList.description | String | The external dynamic list description. |
| PrismaSase.ExternalDynamicList.type | String | The external dynamic list type. |
| PrismaSase.ExternalDynamicList.source | String | The external dynamic list source. |
| PrismaSase.ExternalDynamicList.frequency | String | The external dynamic list frequency. |
Command example
!prisma-sase-external-dynamic-list-create name="somename11" folder="Shared" type="predefined_ip" predefined_ip_list="panw-highrisk-ip-list"
Context Example
{
"PrismaSase": {
"ExternalDynamicList": {
"description": null,
"exception_list": null,
"folder": "Shared",
"frequency": null,
"id": "Id",
"name": "somename11",
"source": "panw-highrisk-ip-list",
"type": "predefined_ip"
}
}
}
Human Readable Output
External Dynamic List Created
Id Name Type Folder Description Source Frequency Id somename11 predefined_ip Shared panw-highrisk-ip-list
Command example
!prisma-sase-external-dynamic-list-create name="somename111" folder="Shared" type="domain" source_url="test.com" frequency="monthly" frequency_hour="09" day_of_month="1"
Context Example
{
"PrismaSase": {
"ExternalDynamicList": {
"description": null,
"exception_list": null,
"folder": "Shared",
"frequency": {
"monthly": {
"at": "09",
"day_of_month": 1
}
},
"id": "Id",
"name": "somename111",
"source": "test.com",
"type": "domain"
}
}
}
Human Readable Output
External Dynamic List Created
Id Name Type Folder Description Source Frequency Id somename111 domain Shared test.com monthly:
at: 09
day_of_month: 1
prisma-sase-external-dynamic-list-update
Update an existing dynamic list.
Base Command
prisma-sase-external-dynamic-list-update
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The external dynamic list id. | Required |
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| overwrite | Whether to overwrite the external dynamic list values. Possible values are: true, false. Default is false. | Optional |
| description | The dynamic list’s description. | Optional |
| type | The dynamic list’s type. Possible values are: predefined_ip, predefined_url, ip, domain, url. | Optional |
| predefined_ip_list | The predefined ip list. If the type is predefined_ip, a value must be provided. Possible values are: panw-torexit-ip-list, panw-bulletproof-ip-list, panw-highrisk-ip-list, panw-known-ip-list. | Optional |
| predefined_url_list | The predefined URL list. If the type is predefined_url, a value must be provided. Possible values are: panw–auth-portal-exclude-list. | Optional |
| source_url | The source URL. If the type is ip, url or domain, a value must be provided. | Optional |
| frequency | Frequency to check for updates. Possible values are: five_minute, hourly, daily, weekly, monthly. | Optional |
| frequency_hour | The frequency hour. | Optional |
| day_of_week | The day of the week. Possible values are: monday, tuesday, wednesday, thursday, friday, saturday, sunday. | Optional |
| day_of_month | The day of the month. Possible values are between 1 and 31. | Optional |
| exception_list | The user can exclude certain addresses from the list depending on the type of list. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.ExternalDynamicList.id | String | The external dynamic list ID. |
| PrismaSase.ExternalDynamicList.name | String | The external dynamic list name. |
| PrismaSase.ExternalDynamicList.folder | String | The external dynamic list folder. |
| PrismaSase.ExternalDynamicList.description | String | The external dynamic list description. |
| PrismaSase.ExternalDynamicList.type | String | The external dynamic list type. |
| PrismaSase.ExternalDynamicList.source | String | The external dynamic list source. |
| PrismaSase.ExternalDynamicList.frequency | String | The external dynamic list frequency. |
Command example
!prisma-sase-external-dynamic-list-update id'"Id"" predefined_ip_list="panw-lowrisk-ip-list"
Context Example
{
"PrismaSase": {
"ExternalDynamicList": {
"description": null,
"exception_list": null,
"folder": "Shared",
"frequency": null,
"id": "Id",
"name": "somename11",
"source": "panw-lowrisk-ip-list",
"type": "predefined_ip"
}
}
}
Human Readable Output
External Dynamic List Updated
Id Name Type Folder Description Source Frequency Id somename11 predefined_ip Shared panw-lowrisk-ip-list
prisma-sase-external-dynamic-list-delete
Delete a specific dynamic list.
Base Command
prisma-sase-external-dynamic-list-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The external dynamic list id. | Required |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
There is no context output for this command.
Command example
!prisma-sase-external-dynamic-list-delete id="Id"
Human Readable Output
External Dynamic List with id Id and name name was deleted successfully
prisma-sase-url-category-list
Get all predefined URL categories.
Base Command
prisma-sase-url-category-list
Input
| Argument Name | Description | Required |
|---|---|---|
| folder | The configuration folder group setting. Possible values are: Shared, Mobile Users, Remote Networks, Service Connections, Mobile Users Container, Mobile Users Explicit Proxy. Default is Shared. | Optional |
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
Context Output
There is no context output for this command.
Command example
!prisma-sase-url-category-list limit=1
Human Readable Output
URL categories
alert allow block continue override recreation-and-hobbies,
educational-institutions,
real-estate,
web-advertisements,
health-and-medicine,
stock-advice-and-tools,
travel,
computer-and-internet-info,
personal-sites-and-blogs,
swimsuits-and-intimate-apparel,
social-networking,
religion,
medium-risk,
business-and-economy,
private-ip-addresses,
web-hosting,
entertainment-and-arts,
streaming-media,
abortion,
translation,
internet-portals,
online-storage-and-backup,
job-search,
motor-vehicles,
web-based-email,
nudity,
sports,
training-and-tools,
government,
shareware-and-freeware,
legal,
shopping,
alcohol-and-tobacco,
low-risk,
auctions,
high-risk,
search-engines,
cryptocurrency,
not-resolved,
society,
financial-services,
military,
news,
philosophy-and-political-advocacy,
content-delivery-networks,
internet-communications-and-telephony,
music,
home-and-garden,
hunting-and-fishing,
reference-and-research,
dating,
sex-education,
gameshacking,
extremism,
weapons,
command-and-control,
ransomware,
copyright-infringement,
dynamic-dns,
parked,
phishing,
medium-risk,
unknown,
abused-drugs,
insufficient-content,
adult,
newly-registered-domain,
grayware,
high-risk,
gambling,
malware,
peer-to-peer,
proxy-avoidance-and-anonymizers,
questionable
prisma-sase-quarantine-host
Quarantine a host.
Base Command
prisma-sase-quarantine-host
Input
| Argument Name | Description | Required |
|---|---|---|
| tsg_id | Tenant services group ID. If not provided, the tsg_id integration parameter will be used as the default. | Optional |
| host_id | The host ID. | Required |
Context Output
There is no context output for this command.
Command example
!prisma-sase-quarantine-host host_id="host_id"
Human Readable Output
Host Quarantined
host_id test_host
prisma-sase-cie-user-get
Get the CIE user.
Base Command
prisma-sase-cie-user-get
Input
| Argument Name | Description | Required |
|---|---|---|
| domain | The domain of the directory-sync connected to the CIE. | Required |
| value_for_filter | The attribute value for the filter. | Required |
| attributes_to_return | A comma-separated list of attributes to return. Default is Common-Name, Unique Identifier, Manager, User Principal Name, Name, Distinguished Name. | Optional |
| attributes_to_filter_by | A comma-separated list of attributes to filter by. Default is Distinguished Name, Unique Identifier, Common-Name, Name, User Principal Name. | Optional |
| operator | The operator. Possible values are: Equal, Starts With, Ends With, Contain, Text Search. Default is Equal. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| PrismaSase.CIE.User.common_name | String | The common name. |
| PrismaSase.CIE.User.distinguished_name | String | The distinguished name. |
| PrismaSase.CIE.User.manager | String | The manager. |
| PrismaSase.CIE.User.name | String | The name. |
| PrismaSase.CIE.User.unique_identifier | String | The unique identifier. |
Configuration parameters
url— Server URL (required)credentials— API Client ID (required)tsg_id— Tenant Services Group ID (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (29)
-
prisma-sase-address-group-createCreate a new address group.
-
prisma-sase-address-group-deleteDelete a specific address group.
-
prisma-sase-address-group-listLists all address groups.
-
prisma-sase-address-group-updateUpdate an existing address group.
-
prisma-sase-address-object-createCreate a new address object.
-
prisma-sase-address-object-deleteDelete a specific address object.
-
prisma-sase-address-object-listLists all addresses objects.
-
prisma-sase-address-object-updateUpdate an existing address object.
-
prisma-sase-candidate-config-pushPush the candidate configuration.
-
prisma-sase-cie-user-getGet the CIE user.
-
prisma-sase-config-job-listLists all configuration jobs.
-
prisma-sase-custom-url-category-createCreate a new URL category.
-
prisma-sase-custom-url-category-deleteDelete a specific URL category.
-
prisma-sase-custom-url-category-listLists all custom URL categories.
-
prisma-sase-custom-url-category-updateUpdate an existing url category.
-
prisma-sase-external-dynamic-list-createCreate a new dynamic list.
-
prisma-sase-external-dynamic-list-deleteDelete a specific dynamic list.
-
prisma-sase-external-dynamic-list-listLists all external dynamic lists.
-
prisma-sase-external-dynamic-list-updateUpdate an existing dynamic list.
-
prisma-sase-quarantine-hostQuarantine a host.
-
prisma-sase-security-rule-createCreate a new security rule.
-
prisma-sase-security-rule-deleteDelete a specific security rule.
-
prisma-sase-security-rule-listLists all security rules.
-
prisma-sase-security-rule-updateUpdate an existing security rule.
-
prisma-sase-tag-createCreate a new tag.
-
prisma-sase-tag-deleteDelete a specific tag.
-
prisma-sase-tag-listLists all tags.
-
prisma-sase-tag-updateUpdate an existing tag.
-
prisma-sase-url-category-listGet all predefined URL categories.
import demistomock as demisto import urllib3 from CommonServerPython import * # noqa # pylint: disable=unused-wildcard-import from CommonServerUserPython import * # noqa # Disable insecure warnings urllib3.disable_warnings() # CONSTANTS DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" # ISO8601 format with UTC, default in XSOAR SEARCH_LIMIT = 200 DEFAULT_LIMIT = 50 PA_OUTPUT_PREFIX = "PrismaSase." CONFIG_URI_PREFIX = "/sse/config/v1/" DEFAULT_POLLING_INTERVAL = 30 DEFAULT_POLLING_TIMEOUT = 600 DEFAULT_POSITION = "pre" DEFAULT_FOLDER = "Shared" FREQUENCY_HOUR_REGEX = "[01][0-9]|2[0-3]" SECURITYRULE_FIELDS = { "action": "", "application": [], "category": [], "description": "", "destination": [], "destination_hip": [], "disabled": "", "from": [], "log_setting": "", "name": "", "negate_destination": "", "negate_source": "", "profile_setting": {}, "service": [], "source": [], "source_hip": [], "source_user": [], "tag": [], "to": [], } ADDRESS_TYPES = ("ip_netmask", "ip_range", "ip_wildcard", "fqdn") class Client(BaseClient): """Client class to interact with the service API This Client implements API calls to the Saas Security platform, and does not contain any XSOAR logic. Handles the token retrieval. Args: base_url (str): Saas Security server url. client_id (str): client ID. client_secret (str): client secret. verify (bool): specifies whether to verify the SSL certificate or not. proxy (bool): specifies if to use XSOAR proxy settings. tsg_id: The default target Prisma SASE tenant ID """ def __init__( self, base_url: str, client_id: str, client_secret: str, tsg_id: str | None, verify: bool, proxy: bool, headers: dict, **kwargs, ): super().__init__(base_url=base_url, verify=verify, proxy=proxy, headers=headers, **kwargs) self.client_id = client_id self.client_secret = client_secret self.tsg_id = tsg_id def http_request( self, method: str, url_suffix: str = "", params: dict = None, json_data: dict = None, tsg_id: str | None = None ) -> dict: # pragma: no cover headers = self.access_token_to_headers(tsg_id) return self._http_request(method=method, url_suffix=url_suffix, params=params, json_data=json_data, headers=headers) @staticmethod def build_security_rule(args: dict) -> dict: """Build a dictionary of security rule parameters to be used to create or update a rule Args: args: demisto.args() Returns: Security rule dictionary """ rule = {} keys = args.keys() for key in SECURITYRULE_FIELDS: if key in keys: field_value = args.get(key) if field_value: if key == "profile_setting": val = argToList(field_value) rule[key] = {"group": val} elif key == "source_user": val = argToList(field_value, ";") rule[key] = val elif isinstance(SECURITYRULE_FIELDS.get(key), str): rule[key] = field_value # type: ignore elif isinstance(SECURITYRULE_FIELDS.get(key), list): val = argToList(field_value) rule[key] = val # type: ignore return rule def create_security_rule(self, rule: dict, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """Creates new security rule Args: rule: Security rule dictionary query_params: folder and position params tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}security-rules" return self.http_request(method="POST", url_suffix=uri, params=query_params, json_data=rule, tsg_id=tsg_id) def edit_security_rule(self, rule: dict, rule_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Update an existing security rule Args: rule: Security rule dictionary rule_id: identifier of rule to be updated tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}security-rules/{rule_id}" return self.http_request(method="PUT", url_suffix=uri, json_data=rule, tsg_id=tsg_id) def delete_security_rule(self, rule_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Delete en existing security rule Args: rule_id: Identifier of the rule to be deleted tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}security-rules/{rule_id}" return self.http_request(method="DELETE", url_suffix=uri, tsg_id=tsg_id) def create_address_object(self, address: dict, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """Create new address object Args: address: address object dictionary query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}addresses" return self.http_request(method="POST", url_suffix=uri, params=query_params, json_data=address, tsg_id=tsg_id) def edit_address_object(self, address: dict, address_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Update an existing address object Args: address: Address object dictionary address_id: Identifier of existing address to be updated tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}addresses/{address_id}" return self.http_request(method="PUT", url_suffix=uri, json_data=address, tsg_id=tsg_id) def delete_address_object(self, address_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Delete an existing address object Args: address_id: Identifier of existing address to be deleted tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}addresses/{address_id}" return self.http_request(method="DELETE", url_suffix=uri, tsg_id=tsg_id) def list_address_objects(self, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """List of address objects Args: query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}addresses" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def list_security_rules(self, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """List of security rules Args: query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}security-rules" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def push_candidate_config( self, folders: list, tsg_id: str | None, description: str | None = None ) -> dict: # pragma: no cover """Push candidate configuration Args: folders: Target Prisma SASE Folders for the configuration commit description: Description for the job tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}config-versions/candidate:push" body: Dict[str, Any] = {"folders": folders} if description: body["description"] = description return self.http_request(method="POST", url_suffix=uri, json_data=body, tsg_id=tsg_id) def get_config_job_by_id(self, job_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Get a specific config job Args: job_id: ID of the config job tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}jobs/{job_id}" return self.http_request(method="GET", url_suffix=uri, tsg_id=tsg_id) def list_config_jobs(self, tsg_id: str | None, query_params: dict | None = None) -> dict: # pragma: no cover """List config jobs Args: query_params: limit and offset param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}jobs" return self.http_request(method="GET", url_suffix=uri, tsg_id=tsg_id, params=query_params) def get_address_by_id(self, query_params: dict, address_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Get an existing address object Args: query_params: folder param address_id: Identifier of existing address tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}addresses/{address_id}" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def get_security_rule_by_id(self, query_params: dict, rule_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Get a specific security rule Args: query_params: folder and position params rule_id: Identifier of existing security rule tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}security-rules/{rule_id}" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def get_tag_by_id(self, query_params: dict, tag_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Get a specific tag Args: query_params: folder param tag_id: Identifier of existing tag tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}tags/{tag_id}" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def list_tags(self, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """List all tags Args: query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}tags" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def update_tag(self, tag_id: str, tag: dict, tsg_id: str | None) -> dict: # pragma: no cover """Update an existing Tag Args: tag: Tag dictionary tag_id: Identifier of existing tag to be updated tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}tags/{tag_id}" return self.http_request(method="PUT", url_suffix=uri, json_data=tag, tsg_id=tsg_id) def delete_tag(self, tag_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Delete a tag Args: tag_id: Identifier of the existing tag to be deleted tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}tags/{tag_id}" return self.http_request(method="DELETE", url_suffix=uri, tsg_id=tsg_id) def create_tag(self, query_params: dict, tag: dict, tsg_id: str | None) -> dict: # pragma: no cover """Create new tagr Args: tag: tag dictionary query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}tags" return self.http_request(method="POST", url_suffix=uri, params=query_params, json_data=tag, tsg_id=tsg_id) def get_address_group_by_id(self, query_params: dict, group_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Get a specific address group Args: query_params: folder param group_id: Identifier of existing address group tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}address-groups/{group_id}" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def list_address_group(self, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """List all address groups Args: query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}address-groups" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def update_address_group(self, address_group: dict, group_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Update an existing address group Args: address_group: Address object dictionary group_id: Identifier of existing address group to be updated tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}address-groups/{group_id}" return self.http_request(method="PUT", url_suffix=uri, json_data=address_group, tsg_id=tsg_id) def create_address_group(self, query_params: dict, address_group: dict, tsg_id: str | None) -> dict: # pragma: no cover """Create new address group Args: address_group: address group dictionary query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}address-groups" return self.http_request(method="POST", url_suffix=uri, params=query_params, json_data=address_group, tsg_id=tsg_id) def delete_address_group(self, group_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Delete an existing address group Args: group_id: Identifier of the existing address group to be deleted tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}address-groups/{group_id}" return self.http_request(method="DELETE", url_suffix=uri, tsg_id=tsg_id) def get_custom_url_category_by_id( self, query_params: dict, url_category_id: str, tsg_id: str | None ) -> dict: # pragma: no cover """Get a specific custom URL category Args: query_params: folder param url_category_id: Identifier of existing url category tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}url-categories/{url_category_id}" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def list_custom_url_category(self, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """List all custom url category Args: query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}url-categories" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def update_custom_url_category( self, custom_url_category: dict, url_category_id: str, tsg_id: str | None ) -> dict: # pragma: no cover """Update an existing custom url category Args: custom_url_category: custom url category dictionary url_category_id: Identifier of existing address group to be updated tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}url-categories/{url_category_id}" return self.http_request(method="PUT", url_suffix=uri, json_data=custom_url_category, tsg_id=tsg_id) def create_custom_url_category( self, query_params: dict, custom_url_category: dict, tsg_id: str | None ) -> dict: # pragma: no cover """Create new custom url category Args: custom_url_category: custom url category dictionary query_params: Prisma SASE Folder tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}url-categories" return self.http_request(method="POST", url_suffix=uri, params=query_params, json_data=custom_url_category, tsg_id=tsg_id) def delete_custom_url_category(self, url_category_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Delete an existing custom url category Args: url_category_id: Identifier of the existing custom url category to be deleted tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}url-categories/{url_category_id}" return self.http_request(method="DELETE", url_suffix=uri, tsg_id=tsg_id) def get_external_dynamic_list_by_id( self, query_params: dict, external_dynamic_list_id: str, tsg_id: str | None ) -> dict: # pragma: no cover """Get a specific external dynamic list Args: query_params: folder param external_dynamic_list_id: Identifier of existing external dynamic list tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}external-dynamic-lists/{external_dynamic_list_id}" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def list_external_dynamic_list(self, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """List all external dynamic list Args: query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}external-dynamic-lists" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def update_external_dynamic_list( self, external_dynamic_list: dict, dynamic_list_id: str, tsg_id: str | None ) -> dict: # pragma: no cover """Update an existing external dynamic list Args: external_dynamic_list: external dynamic list dictionary dynamic_list_id: Identifier of existing external dynamic list to be updated tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}external-dynamic-lists/{dynamic_list_id}" return self.http_request(method="PUT", url_suffix=uri, json_data=external_dynamic_list, tsg_id=tsg_id) def create_external_dynamic_list( self, query_params: dict, external_dynamic_list: dict, tsg_id: str | None ) -> dict: # pragma: no cover """Create new external dynamic list Args: external_dynamic_list: external dynamic list dictionary query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}external-dynamic-lists" return self.http_request( method="POST", url_suffix=uri, params=query_params, json_data=external_dynamic_list, tsg_id=tsg_id ) def delete_external_dynamic_list(self, dynamic_list_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Delete external dynamic list Args: dynamic_list_id: Identifier of the existing external dynamic list to be deleted tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}external-dynamic-lists/{dynamic_list_id}" return self.http_request(method="DELETE", url_suffix=uri, tsg_id=tsg_id) def list_url_access_profile(self, query_params: dict, tsg_id: str | None) -> dict: # pragma: no cover """Get all url access profiles Args: query_params: folder param tsg_id: Target Prisma SASE tenant ID Returns: Outputs. """ uri = f"{CONFIG_URI_PREFIX}url-access-profiles" return self.http_request(method="GET", url_suffix=uri, params=query_params, tsg_id=tsg_id) def get_access_token(self, tsg_id: str | None) -> str: # pragma: no cover """Get access token to use for API call. The SASE API is multi-tenant capable and the tenant structure is hierarchical. The TSG (tenant services group) is an identifier for a particular tenant. A single API service account can have access to the root tenant and any number of sub-tenants underneath. The scope / target of the API call is determined via the requested authorization token. If there is an existing access token, and it has not expired, set it as the access token for this request Else request a new access token for the provided TSG and store it in the integration context and add the TSG ID as a prefix. Returns: The access token """ integration_context = get_integration_context() tsg_access_token = f"{tsg_id}.access_token" tsg_expiry_time = f"{tsg_id}.expiry_time" previous_token = integration_context.get(tsg_access_token) previous_token_expiry_time = integration_context.get(tsg_expiry_time) if previous_token and previous_token_expiry_time > date_to_timestamp(datetime.now()): return previous_token else: tsg = f"tsg_id:{tsg_id}" data = {"grant_type": "client_credentials", "scope": tsg} try: headers = { "Content-Type": "application/x-www-form-urlencoded", } res = self._http_request( method="POST", full_url="https://auth.apps.paloaltonetworks.com/am/oauth2/access_token", auth=(self.client_id, self.client_secret), resp_type="response", headers=headers, data=data, ) try: res = res.json() except ValueError as exception: raise DemistoException(f"Failed to parse json object from response: {res.text}.\nError: {exception}") if access_token := res.get("access_token"): expiry_time = date_to_timestamp(datetime.now(), date_format=DATE_FORMAT) expiry_time += res.get("expires_in", 0) - 10 new_token = {tsg_access_token: access_token, tsg_expiry_time: expiry_time} # store received token and expiration time in the integration context set_integration_context(new_token) return access_token else: raise DemistoException( "Error occurred while creating an access token. Access token field has not" " found in the response data. Please check the instance configuration.\n" ) except Exception as e: raise DemistoException( f"Error occurred while creating an access token. Please check the instance configuration.\n\n{e}" ) def access_token_to_headers(self, tsg_id: str | None = None) -> dict: """Updates the headers with the access token Args: tsg_id: Target Prisma SASE tenant ID Returns: Headers """ tsg_id = tsg_id if tsg_id else self.tsg_id access_token = self.get_access_token(tsg_id) headers = self._headers headers["Authorization"] = f"Bearer {access_token}" return headers def quarantine_host(self, host_id: str, tsg_id: str | None) -> dict: # pragma: no cover """Quarantine a given host Args: host_id: Host ID that needs to be added to quarantine List tsg_id: Target Prisma SASE tenant ID """ uri = f"{CONFIG_URI_PREFIX}quarantined-devices" return self.http_request(method="POST", url_suffix=uri, json_data={"host_id": host_id}, tsg_id=tsg_id) def get_cie_user(self, json_data: Dict[str, Any]) -> dict: # pragma: no cover """ Get CIE user Args: json_data: The JSON data to send in the request body. Returns: The response from the API. """ url_suffix = "cie/directory-sync/v1/cache-users" return self.http_request( method="POST", url_suffix=url_suffix, json_data=json_data, ) """HELPER FUNCTIONS""" def address_to_xsoar_format(outputs): """Modify an address object or list of address objects to XSOAR format Args: outputs: address objects """ if isinstance(outputs, dict): outputs = [outputs] for output in outputs: for address_type in ADDRESS_TYPES: if address_type in output: output["type"] = address_type output["address_value"] = output[address_type] output.pop(address_type) def address_group_to_xsoar_format(outputs): """Modify an address group or list of address groups to XSOAR format Args: outputs: address groups """ if isinstance(outputs, dict): outputs = [outputs] for output in outputs: if "static" in output: output["addresses"] = output["static"] output.pop("static") elif "dynamic" in output: output["dynamic_filter"] = output["dynamic"].get("filter", "") output.pop("dynamic") def external_dynamic_list_to_xsoar_format(outputs): """Modify an external dynamic list or list of external dynamic lists to XSOAR format Args: outputs: external dynamic list Returns: Modified external dynamic list """ if isinstance(outputs, dict): outputs = [outputs] for output in outputs: # For pre-defined list, also predefined values are returned, and their structure is different if output.get("snippet") == "predefined": output["type"] = "predefined" output["source"] = "predefined" continue dynamic_list_type_object = output.get("type", {}) try: # The object should contain exactly one key, and the key indicates the type of the dynamic list. dynamic_list_type = list(dynamic_list_type_object.keys())[0] except IndexError: raise DemistoException( f"Could not parse the type of the Dynamic list. " f"Type is missing. Dynamic list as returned by the API: {output}" ) output["description"] = dynamic_list_type_object.get(dynamic_list_type, {}).get("description") output["source"] = dynamic_list_type_object.get(dynamic_list_type, {}).get("url") output["frequency"] = next(iter(dynamic_list_type_object.get(dynamic_list_type, {}).get("recurring", {})), None) output["exception_list"] = dynamic_list_type_object.get(dynamic_list_type, {}).get("exception_list") output["type"] = dynamic_list_type def get_address_group_type(original_address_group: dict) -> str: return "static" if "static" in original_address_group else "dynamic" def update_new_rule(new_rule: dict, original_rule: dict, overwrite: bool) -> dict: """Updates a security rule with the required data Args: new_rule: The new rule original_rule: The original rule overwrite: Rather to overwrite or append the updated values Returns: """ if overwrite: # simply update the relevant keys with the new data original_rule.update(new_rule) return original_rule for key, value in new_rule.items(): if isinstance(SECURITYRULE_FIELDS.get(key), list): # the 'any' value should be overwritten in all cases if "any" in value or "any" in original_rule.get(key, []): original_rule[key] = argToList(new_rule.get(key)) else: original_rule.setdefault(key, []).extend(argToList(new_rule.get(key, []))) if isinstance(SECURITYRULE_FIELDS.get(key), str): original_rule[key] = new_rule.get(key) return original_rule def get_url_according_to_type(args: dict) -> str: """Returns the url parameter according to the external dynamic list type. Args: args: Command args Returns: Url """ dynamic_list_type = args.get("type") if dynamic_list_type in ("ip", "domain", "url"): url = args.get("source_url") if not url: raise DemistoException("Please provide the source_url argument when using IP, URL or Domain types") elif dynamic_list_type == "predefined_url": url = args.get("predefined_url_list") if not url: raise DemistoException("Please provide the predefined_url_list argument when using predefined_url type") else: # dynamic_list_type == 'predefined_ip': url = args.get("predefined_ip_list") if not url: raise DemistoException("Please provide the predefined_ip_list argument when using predefined_ip") return url def validate_url_is_type_compatible( args: dict, type_changed: bool, original_dynamic_list_type: dict, original_dynamic_list_url: dict ) -> str: """Validates that the update is valid and returns the correct URL Args: args: Command arguments original_dynamic_list_url: The original dynamic list url original_dynamic_list_type: The original dynamic list type type_changed: Rather the type has changed Returns: Url """ dynamic_list_type = args.get("type") or original_dynamic_list_type if dynamic_list_type in ("ip", "domain", "url"): url = args.get("source_url", "") if not url and type_changed: raise DemistoException("Please provide the source_url argument when using IP, URL or Domain types") elif dynamic_list_type == "predefined_url": url = args.get("predefined_url_list", "") if not url and type_changed: raise DemistoException("Please provide the predefined_url_list argument when using predefined_url type") else: # dynamic_list_type == 'predefined_ip': url = args.get("predefined_ip_list", "") if not url and type_changed: raise DemistoException("Please provide the predefined_ip_list argument when using predefined_ip") url = url if url else original_dynamic_list_url return url def build_recurring_according_to_params(args: dict) -> dict: """Returns a frequency object for the API according to the command arguments Args: args: Command arguments Returns: Frequency object """ frequency = args.get("frequency") or "five_minute" frequency_object: dict = {frequency: {}} if frequency in ("daily", "weekly", "monthly"): frequency_hour = args.get("frequency_hour") if not frequency_hour: raise DemistoException("Please provide the frequency_hour argument when using daily, weekly or monthly frequency") if not re.match(FREQUENCY_HOUR_REGEX, frequency_hour): raise DemistoException("frequency_hour argument should be 00,01,02...-23 only") frequency_object[frequency]["at"] = frequency_hour if frequency == "weekly": day_of_week = args.get("day_of_week") if not day_of_week: raise DemistoException("Please provide the day_of_week argument when using weekly frequency") frequency_object[frequency]["day_of_week"] = day_of_week elif frequency == "monthly": day_of_month = args.get("day_of_month") if not day_of_month: raise DemistoException("Please provide the day_of_month argument when using monthly frequency") day_of_month = arg_to_number(day_of_month) or 0 if day_of_month < 1 or day_of_month > 31: raise DemistoException("day_of_month argument must be between 1 and 31") frequency_object[frequency]["day_of_month"] = str(day_of_month) return frequency_object def validate_recurring_is_type_compatible(args: dict, original_frequency_obj: dict) -> dict: """Validates that the update is valid and returns the correct frequency object Args: args: Command arguments original_frequency_obj: The original frequency object Returns: Frequency object for the API """ frequency = args.get("frequency") if len(list(original_frequency_obj.keys())) == 0 and not frequency: raise DemistoException("Could not find frequency for dynamic list type. Please check your configuration") original_frequency = list(original_frequency_obj.keys())[0] frequency = frequency if frequency else original_frequency frequency_object: dict = {frequency: {}} if frequency in ("daily", "weekly", "monthly"): frequency_hour = args.get("frequency_hour") or original_frequency_obj[original_frequency].get("frequency_hour") if not frequency_hour: raise DemistoException("Please provide the frequency_hour argument when using daily, weekly or monthly frequency") if not re.match(FREQUENCY_HOUR_REGEX, frequency_hour): raise DemistoException("frequency_hour argument should be 00,01,02...-23 only") frequency_object[frequency]["at"] = frequency_hour or original_frequency_obj[original_frequency].get("at") if frequency == "weekly": day_of_week = args.get("day_of_week") or original_frequency_obj[original_frequency].get("day_of_week") if not day_of_week: raise DemistoException("Please provide the day_of_week argument when using weekly frequency") frequency_object[frequency]["day_of_week"] = day_of_week elif frequency == "monthly": day_of_month = args.get("day_of_month") or original_frequency_obj[original_frequency].get("day_of_month") if not day_of_month: raise DemistoException("Please provide the day_of_month argument when using monthly frequency") day_of_month = arg_to_number(day_of_month) or 0 if day_of_month < 1 or day_of_month > 31: raise DemistoException("day_of_month argument must be between 1 and 31") frequency_object[frequency]["day_of_month"] = str(day_of_month) return frequency_object if frequency_object else original_frequency_obj def get_pagination_params(args: dict) -> dict: """Returns the pagination parameters Args: args: Command arguments Returns: Pagination params """ pagination_params = {} page = arg_to_number(args.get("page")) or 1 page_size = arg_to_number(args.get("page_size")) if page and page_size: pagination_params["offset"] = (page - 1) * page_size pagination_params["limit"] = page_size elif limit := arg_to_number(args.get("limit", DEFAULT_LIMIT)): pagination_params["limit"] = limit return pagination_params """COMMANDS""" def test_module(client: Client) -> CommandResults: """Test command to determine if integration is working correctly. Args: client: Client object with request Returns: Outputs. Returning 'ok' indicates that the integration works like it is supposed to. Connection to the service is successful. """ uri = f"{CONFIG_URI_PREFIX}config-versions?limit=1" client.http_request(method="GET", url_suffix=uri) return CommandResults(raw_response="ok") def create_security_rule_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to create new security rule """ rule = client.build_security_rule(args) query_params = { "folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "position": encode_string_results(args.get("position")) or DEFAULT_POSITION, } tsg_id = args.get("tsg_id") demisto.debug(f"sending security rule to the API. Rule: {rule}") raw_response = client.create_security_rule(rule=rule, query_params=query_params, tsg_id=tsg_id) # type: ignore outputs = raw_response outputs["position"] = query_params["position"] return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}SecurityRule", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Security Rule Created", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def create_address_object_command(client: Client, args: Dict[str, Any]) -> CommandResults: """Command to create new address object Args: client: Client object with request args: demisto.args() Returns: Outputs. """ address_object = {args.get("type"): args.get("address_value"), "name": args.get("name")} query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} if args.get("description"): address_object["description"] = args.get("description") if args.get("tag"): address_object["tag"] = args.get("tag") demisto.debug(f"sending address_object to the API. address_object: {address_object}") raw_response = client.create_address_object(address=address_object, query_params=query_params, tsg_id=args.get("tsg_id")) # type: ignore outputs = raw_response.copy() address_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}Address", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Address Object Created", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def edit_address_object_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to update address object """ query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} object_id = args.get("object_id", "") tsg_id = args.get("tsg_id") # first get the original address, so user won't need to send all data original_address = client.get_address_by_id(query_params=query_params, address_id=object_id, tsg_id=tsg_id) original_address_type = None if not args.get("type"): for address_type in ADDRESS_TYPES: if address_type in original_address: original_address_type = address_type else: original_address_type = args.get("type") original_address[original_address_type] = args.get("address_value") # in case the type has changed, we want to remove other types from the address object for address_type in ADDRESS_TYPES: if address_type in original_address and address_type != original_address_type: original_address.pop(address_type) if description := args.get("description"): original_address["description"] = description if tag := args.get("tag"): original_address["tag"] = tag demisto.debug(f"sending address_object to the API. address_object: {original_address}") raw_response = client.edit_address_object(address=original_address, address_id=object_id, tsg_id=tsg_id) # type: ignore outputs = raw_response.copy() address_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}Address", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Address Object updated", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def delete_address_object_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to delete address object """ tsg_id = args.get("tsg_id") address_id = args.get("object_id") demisto.debug(f"deleting address_object with id {address_id}") raw_response = client.delete_address_object(address_id=address_id, tsg_id=tsg_id) # type: ignore return CommandResults( readable_output=f'Address object with id {raw_response.get("id", "")} ' f'and name {raw_response.get("name", "")} was deleted successfully', raw_response=raw_response, ) def list_address_objects_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to get all address objects """ query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "name": args.get("name")} tsg_id = args.get("tsg_id") if object_id := args.get("object_id"): raw_response = client.get_address_by_id(query_params=query_params, address_id=object_id, tsg_id=tsg_id) outputs = raw_response.copy() else: query_params.update(get_pagination_params(args)) raw_response = client.list_address_objects(query_params=query_params, tsg_id=tsg_id) # type: ignore outputs = raw_response.copy() # A dict containing a list of results (data) is returned from the API. # When filtering by name the key 'data' does not exist in the response, therefore we return the entire response. outputs = outputs.get("data", outputs) address_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}Address", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "Address Objects", outputs, headers=["id", "name", "description", "type", "address_value", "tag"], headerTransform=string_to_table_header, ), raw_response=raw_response, ) def delete_security_rule_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to delete the specified security rule """ rule_id = args.get("rule_id") tsg_id = args.get("tsg_id") demisto.debug(f"deleting security_rule with id {rule_id}") raw_response = client.delete_security_rule(rule_id=rule_id, tsg_id=tsg_id) # type: ignore return CommandResults( readable_output=f'Security Rule object with id {raw_response.get("id", "")} ' f'and name {raw_response.get("name", "")} was deleted successfully', raw_response=raw_response, ) def edit_security_rule_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to update an existing security rule """ rule = client.build_security_rule(args) rule_id = args.get("rule_id", "") tsg_id = args.get("tsg_id") overwrite = argToBoolean(args.get("overwrite")) query_params = { "folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "position": encode_string_results(args.get("position")) or DEFAULT_POSITION, } original_rule = client.get_security_rule_by_id(query_params=query_params, rule_id=rule_id, tsg_id=tsg_id) updated_rule = update_new_rule(rule, original_rule, overwrite=overwrite) demisto.debug(f"Sending security_rule to the API. Rule {updated_rule}") raw_response = client.edit_security_rule(rule=updated_rule, rule_id=rule_id, tsg_id=tsg_id) # type: ignore outputs = raw_response return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}SecurityRule", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Security Rule Updated", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def push_candidate_config_command(client: Client, args: Dict[str, Any]) -> CommandResults: """Command to Trigger a configuration push for the identified Folder/Devices""" folders = argToList(args.get("folders")) # type: ignore tsg_id = args.get("tsg_id") raw_response = client.push_candidate_config(folders, args.get("description", ""), tsg_id=tsg_id) # type: ignore outputs = raw_response return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}CandidateConfig", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Configuration Push Requested", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def list_security_rules_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to Get all security rules """ query_params = { "folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "position": encode_string_results(args.get("position")) or DEFAULT_POSITION, "name": args.get("name"), } tsg_id = args.get("tsg_id") if rule_id := args.get("rule_id"): raw_response = client.get_security_rule_by_id(query_params=query_params, rule_id=rule_id, tsg_id=tsg_id) outputs = raw_response else: query_params.update(get_pagination_params(args)) raw_response = client.list_security_rules(query_params=query_params, tsg_id=tsg_id) # type: ignore # A dict containing a list of results is returned by the API. # A single dict is returned when filtering the request by name. outputs = raw_response.get("data", raw_response) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}SecurityRule", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "Security Rules", outputs, headers=["id", "name", "description", "action", "destination", "folder", "disabled"], headerTransform=string_to_table_header, removeNull=True, ), raw_response=raw_response, ) def list_config_jobs_command(client: Client, args: Dict[str, Any]) -> CommandResults: """Command to list config jobs from Prisma Access""" query_params = {} tsg_id = args.get("tsg_id") if job_id := args.get("job_id"): raw_response = client.get_config_job_by_id(job_id=job_id, tsg_id=tsg_id) else: query_params.update(get_pagination_params(args)) raw_response = client.list_config_jobs(tsg_id=tsg_id, query_params=query_params) # type: ignore outputs = raw_response.get("data") return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}ConfigJob", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "Config Job", outputs, headers=["id", "type_str", "status_str", "result_str", "start_ts", "end_ts"], headerTransform=string_to_table_header, ), raw_response=raw_response, ) def list_tags_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to list all tags """ query_params = { "folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "name": args.get("name"), } tsg_id = args.get("tsg_id") if tag_id := args.get("tag_id"): raw_response = client.get_tag_by_id(query_params=query_params, tag_id=tag_id, tsg_id=tsg_id) outputs = raw_response else: query_params.update(get_pagination_params(args)) raw_response = client.list_tags(query_params=query_params, tsg_id=tsg_id) # type: ignore # A dict containing a list of results is returned by the API. # A single dict is returned when filtering the request by name. outputs = raw_response.get("data", raw_response) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}Tag", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "Tags", outputs, headers=["id", "name", "folder", "color", "comments"], headerTransform=string_to_table_header ), raw_response=raw_response, ) def create_tag_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to create new tag """ tag = {"name": args.get("name")} if color := args.get("color"): tag["color"] = color if comments := args.get("comments"): tag["comments"] = comments query_params = {"folder": args.get("folder") or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") demisto.debug(f"Sending tag to the API. Tag: {tag}") raw_response = client.create_tag(query_params=query_params, tag=tag, tsg_id=tsg_id) # type: ignore return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}Tag", outputs_key_field="id", outputs=raw_response, readable_output=tableToMarkdown("Address Object Created", raw_response, headerTransform=string_to_table_header), raw_response=raw_response, ) def update_tag_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to update an existing tag """ query_params = {"folder": encode_string_results(args.get("folder") or DEFAULT_FOLDER)} # first get the original tag, so user won't need to send all data tag_id = args.get("tag_id", "") tsg_id = args.get("tsg_id") original_tag = client.get_tag_by_id(query_params=query_params, tag_id=tag_id, tsg_id=tsg_id) if color := args.get("color"): original_tag["color"] = color if comments := args.get("comments"): original_tag["comments"] = comments demisto.debug(f"Sending tag to the API. Tag: {original_tag}") raw_response = client.update_tag(tag_id=tag_id, tag=original_tag, tsg_id=tsg_id) # type: ignore outputs = raw_response return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}Tag", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Tag Edited", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def delete_tag_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to delete the specified tagG """ tag_id = args.get("tag_id") tsg_id = args.get("tsg_id") demisto.debug(f"Deleting tag twith id {tag_id}") raw_response = client.delete_tag(tag_id=tag_id, tsg_id=tsg_id) # type: ignore return CommandResults( readable_output=f'Tag with id {raw_response.get("id", "")} ' f'and name {raw_response.get("name", "")} was deleted successfully', raw_response=raw_response, ) def list_address_group_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to get address groups """ query_params = { "folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "name": args.get("name"), } tsg_id = args.get("tsg_id") if group_id := args.get("group_id"): raw_response = client.get_address_group_by_id(query_params=query_params, group_id=group_id, tsg_id=tsg_id) outputs = raw_response.copy() else: query_params.update(get_pagination_params(args)) raw_response = client.list_address_group(query_params=query_params, tsg_id=tsg_id) # type: ignore outputs = raw_response.copy() # A dict containing a list of results is returned by the API. # A single dict is returned when filtering the request by name. outputs = outputs.get("data", outputs) address_group_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}AddressGroup", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "Address Groups", outputs, headers=["id", "name", "description", "addresses", "dynamic_filter"], headerTransform=string_to_table_header, ), raw_response=raw_response, ) def create_address_group_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to create new address group """ address_group = {"name": args.get("name")} query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") if description := args.get("description"): address_group["description"] = description if group_type := args.get("type"): if group_type == "static": if static_addresses := argToList(args.get("static_addresses")): address_group["static"] = static_addresses else: # type == 'dynamic' if dynamic_filter := args.get("dynamic_filter"): address_group["dynamic"] = {"filter": dynamic_filter} demisto.debug(f"Sending address_group to the API. address_group: {address_group}") raw_response = client.create_address_group(query_params=query_params, address_group=address_group, tsg_id=tsg_id) # type: ignore outputs = raw_response.copy() address_group_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}AddressGroup", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Address Group Created", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def update_address_group_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to update an existing address group """ query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") group_id = args.get("group_id", "") # first get the original address, so user won't need to send all data original_address_group = client.get_address_group_by_id(query_params=query_params, group_id=group_id, tsg_id=tsg_id) if description := args.get("description"): original_address_group["description"] = description overwrite = argToBoolean(args.get("overwrite")) group_type = args.get("type", "") if group_type and group_type != get_address_group_type(original_address_group): # we can not concatenate static value to dynamic demisto.info( f"setting overwrite parameter to True as the type of the address group has changed." f"overwrite original value: {overwrite}" ) overwrite = True if not group_type: group_type = get_address_group_type(original_address_group) static_addresses = argToList(args.get("static_addresses")) dynamic_filter = args.get("dynamic_filter") if group_type == "static" and (dynamic_filter and not static_addresses): raise DemistoException("Please provide the static_addresses argument with type static") if group_type == "dynamic" and (not dynamic_filter and static_addresses): raise DemistoException("Please provide the dynamic_filter argument with type dynamic") if group_type == "static": if overwrite: original_address_group["static"] = static_addresses else: original_address_group.setdefault("static", []).extend(static_addresses) original_address_group.pop("dynamic") if "dynamic" in original_address_group else None else: # type == 'dynamic' if not overwrite: dynamic_filter = original_address_group.get("dynamic", {}).get("filter", "") + " " + dynamic_filter original_address_group["dynamic"] = {"filter": dynamic_filter} original_address_group.pop("static") if "static" in original_address_group else None demisto.debug(f"Sending address_group to the API. address_group: {original_address_group}") raw_response = client.update_address_group(address_group=original_address_group, group_id=group_id, tsg_id=tsg_id) # type: ignore outputs = raw_response.copy() address_group_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}AddressGroup", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Address Group updated", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def delete_address_group_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to delete address group """ group_id = args.get("group_id") tsg_id = args.get("tsg_id") demisto.debug(f"Deleting address group with id {group_id}") raw_response = client.delete_address_group(group_id=group_id, tsg_id=tsg_id) # type: ignore return CommandResults( readable_output=f'Address group with id {raw_response.get("id", "")} ' f'and name {raw_response.get("name", "")} was deleted successfully', raw_response=raw_response, ) def list_custom_url_category_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to get all custom url categories """ query_params = { "folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "name": args.get("name"), } tsg_id = args.get("tsg_id") if url_category_id := args.get("id"): raw_response = client.get_custom_url_category_by_id( query_params=query_params, url_category_id=url_category_id, tsg_id=tsg_id ) outputs = [raw_response] else: query_params.update(get_pagination_params(args)) raw_response = client.list_custom_url_category(query_params=query_params, tsg_id=tsg_id) # type: ignore # A dict containing a list of results is returned by the API. # A single dict is returned when filtering the request by name. outputs = raw_response.get("data", raw_response) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}CustomURLCategory", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "Custom Url Categories", outputs, headers=["id", "name", "folder", "type", "list"], headerTransform=string_to_table_header, ), raw_response=raw_response, ) def create_custom_url_category_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to create new custom url category """ custom_url_category = {"name": args.get("name"), "type": args.get("type")} query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") if description := args.get("description"): custom_url_category["description"] = description if value := argToList(args.get("value")): custom_url_category["list"] = value demisto.debug(f"Sending custom_url_category to the API. custom_url_category: {custom_url_category}") raw_response = client.create_custom_url_category( query_params=query_params, custom_url_category=custom_url_category, tsg_id=tsg_id ) # type: ignore return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}CustomURLCategory", outputs_key_field="id", outputs=raw_response, readable_output=tableToMarkdown("Custom URrl Category Created", raw_response, headerTransform=string_to_table_header), raw_response=raw_response, ) def update_custom_url_category_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to update an existing custom url category """ query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") url_category_id = args.get("id", "") # first get the original, so user won't need to send all data original_custom_url_category = client.get_custom_url_category_by_id( query_params=query_params, url_category_id=url_category_id, tsg_id=tsg_id ) if description := args.get("description"): original_custom_url_category["description"] = description overwrite = argToBoolean(args.get("overwrite")) if category_type := args.get("type"): if category_type != original_custom_url_category["type"]: demisto.info( f"setting overwrite parameter to True as the type of the URL category has changed." f"overwrite original value: {overwrite}" ) overwrite = True original_custom_url_category["type"] = category_type if value := argToList(args.get("value")): if overwrite: original_custom_url_category["list"] = value else: original_custom_url_category.setdefault("list", []).extend(value) demisto.debug(f"Sending custom_url_category to the API. custom_url_category: {original_custom_url_category}") raw_response = client.update_custom_url_category( custom_url_category=original_custom_url_category, url_category_id=url_category_id, tsg_id=tsg_id ) # type: ignore outputs = raw_response return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}CustomURLCategory", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown("Custom Url Category updated", outputs, headerTransform=string_to_table_header), raw_response=raw_response, ) def delete_custom_url_category_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to delete custom url category """ url_category_id = args.get("id") tsg_id = args.get("tsg_id") demisto.debug(f"Deleting custom_url_category with id {url_category_id}") raw_response = client.delete_custom_url_category(url_category_id=url_category_id, tsg_id=tsg_id) # type: ignore return CommandResults( readable_output=f'Custom Url Category with id {raw_response.get("id", "")} ' f'and name {raw_response.get("name", "")} was deleted successfully', raw_response=raw_response, ) def list_external_dynamic_list_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to get all external dynamic lists """ query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER, "name": args.get("name")} tsg_id = args.get("tsg_id") if external_dynamic_list_id := args.get("id"): raw_response = client.get_external_dynamic_list_by_id( query_params=query_params, external_dynamic_list_id=external_dynamic_list_id, tsg_id=tsg_id ) outputs = raw_response.copy() else: query_params.update(get_pagination_params(args)) raw_response = client.list_external_dynamic_list(query_params=query_params, tsg_id=tsg_id) # type: ignore outputs = raw_response.copy() # A dict containing a list of results is returned by the API. # A single dict is returned when filtering the request by name. outputs = outputs.get("data", outputs) external_dynamic_list_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}ExternalDynamicList", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "External Dynamic Lists", outputs, headers=["id", "name", "type", "folder", "description", "source", "frequency"], headerTransform=string_to_table_header, is_auto_json_transform=True, ), raw_response=raw_response, ) def create_external_dynamic_list_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to create new external dynamic list """ dynamic_list_type = args.get("type", "") external_dynamic_list: dict = {"name": args.get("name"), "type": {dynamic_list_type: {}}} query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") url = get_url_according_to_type(args) external_dynamic_list["type"][dynamic_list_type]["url"] = url if exception_list := argToList(args.get("exception_list")): external_dynamic_list["type"][dynamic_list_type]["exception_list"] = exception_list if description := args.get("description"): external_dynamic_list["type"][dynamic_list_type]["description"] = description if dynamic_list_type in ("ip", "domain", "url"): external_dynamic_list["type"][dynamic_list_type]["recurring"] = build_recurring_according_to_params(args) demisto.debug(f"Sending external_dynamic_list to the API. external_dynamic_list: {external_dynamic_list}") raw_response = client.create_external_dynamic_list( query_params=query_params, external_dynamic_list=external_dynamic_list, tsg_id=tsg_id ) # type: ignore outputs = raw_response.copy() external_dynamic_list_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}ExternalDynamicList", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "External Dynamic List Created", outputs, headers=["id", "name", "type", "folder", "description", "source", "frequency"], headerTransform=string_to_table_header, is_auto_json_transform=True, ), raw_response=raw_response, ) def update_external_dynamic_list_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command to update an existing external dynamic list """ query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") dynamic_list_id = args.get("id", "") # first get the original, so user won't need to send all data original_dynamic_list = client.get_external_dynamic_list_by_id( query_params=query_params, external_dynamic_list_id=dynamic_list_id, tsg_id=tsg_id ) overwrite = argToBoolean(args.get("overwrite")) original_dynamic_list_type_object = original_dynamic_list["type"] try: original_dynamic_list_type = list(original_dynamic_list_type_object.keys())[0] except IndexError: raise DemistoException( f"Could not parse the type of the Dynamic list. " f"Type is missing. Dynamic list as returned by the API: {original_dynamic_list}" ) original_dynamic_list_url = original_dynamic_list_type_object[original_dynamic_list_type]["url"] original_frequency_object = original_dynamic_list_type_object[original_dynamic_list_type].get("recurring", {"recurring": {}}) type_changed = False if (dynamic_list_type := args.get("type")) and original_dynamic_list_type != dynamic_list_type: # changing the key that indicates the type original_dynamic_list["type"][dynamic_list_type] = original_dynamic_list_type_object[original_dynamic_list_type] demisto.info( f"setting overwrite parameter to True as the type of the dynamic list has changed." f"overwrite original value: {overwrite}" ) type_changed = True overwrite = True dynamic_list_type = dynamic_list_type if dynamic_list_type else original_dynamic_list_type if exception_list := argToList(args.get("exception_list")): if overwrite: original_dynamic_list["type"][dynamic_list_type]["exception_list"] = exception_list else: original_dynamic_list["type"][dynamic_list_type].setdefault("exception_list", []).extend(exception_list) if description := args.get("description"): original_dynamic_list["type"][dynamic_list_type]["description"] = description url = validate_url_is_type_compatible(args, type_changed, original_dynamic_list_type, original_dynamic_list_url) original_dynamic_list["type"][dynamic_list_type]["url"] = url if dynamic_list_type in ("ip", "domain", "url"): original_dynamic_list["type"][dynamic_list_type]["recurring"] = validate_recurring_is_type_compatible( args, original_frequency_object ) if type_changed: original_dynamic_list["type"].pop(original_dynamic_list_type) if not original_dynamic_list["type"][dynamic_list_type].get("recurring"): original_dynamic_list["type"][dynamic_list_type].pop("recurring") demisto.debug(f"Sending external_dynamic_list to the API. external_dynamic_list: {original_dynamic_list}") raw_response = client.update_external_dynamic_list( external_dynamic_list=original_dynamic_list, dynamic_list_id=dynamic_list_id, tsg_id=tsg_id ) # type: ignore outputs = raw_response.copy() external_dynamic_list_to_xsoar_format(outputs) return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}ExternalDynamicList", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown( "External Dynamic List updated", outputs, headers=["id", "name", "type", "folder", "description", "source", "frequency"], headerTransform=string_to_table_header, is_auto_json_transform=True, ), raw_response=raw_response, ) def delete_external_dynamic_list_command(client: Client, args: Dict[str, Any]) -> CommandResults: """Command to delete external dynamic list""" dynamic_list_id = args.get("id") tsg_id = args.get("tsg_id") demisto.debug(f"Deleting external_dynamic_list with id {dynamic_list_id}") raw_response = client.delete_external_dynamic_list(dynamic_list_id=dynamic_list_id, tsg_id=tsg_id) # type: ignore return CommandResults( readable_output=f'External Dynamic List with id {raw_response.get("id", "")} ' f'and name {raw_response.get("name", "")} was deleted successfully', raw_response=raw_response, ) def list_url_category_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command get all built-in url categories """ query_params = {"folder": encode_string_results(args.get("folder")) or DEFAULT_FOLDER} tsg_id = args.get("tsg_id") raw_response = client.list_url_access_profile(query_params=query_params, tsg_id=tsg_id) # type: ignore profiles = raw_response.get("data", []) categories: dict = {"alert": [], "allow": [], "block": [], "continue": [], "override": []} for profile in profiles: # we only want predefined profiles if profile.get("folder", "") == "predefined": for category in categories: categories[category].extend(profile.get(category, [])) categories[category].extend(profile.get("credential_enforcement", {}).get(category, [])) # remove duplicates categories[category] = list(set(categories[category])) return CommandResults(readable_output=tableToMarkdown("URL categories", categories), raw_response=raw_response) def quarantine_host_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command quarantine a given host """ host_id = args.get("host_id", "") tsg_id = args.get("tsg_id") raw_response = client.quarantine_host(host_id=host_id, tsg_id=tsg_id) outputs = raw_response return CommandResults(readable_output=tableToMarkdown("Host Quarantined", outputs), raw_response=raw_response) def cie_user_prepare_args(args: Dict[str, Any]) -> Dict[str, Any]: """ Prepare args for get_cie_user_command. Args: args: Dict[str, Any] - args to prepare Returns: Dict[str, Any] - The JSON data for the API call """ operator_mapping = { "Equal": "equal", "Starts With": "startsWith", "Ends With": "endsWith", "Contain": "contain", "Text Search": "textSearch", } missing_args = { "attributes_to_return": args.get("attributes_to_return"), "operator": args.get("operator"), "attributes_to_filter_by": args.get("attributes_to_filter_by"), } errors = [name for name, value in missing_args.items() if not value] if errors: raise ValueError(f"The following arguments are empty: {', '.join(errors)}") # Ensure "Unique Identifier" is always in the list for the deduplication process attributes_to_return = list(set(argToList(args.get("attributes_to_return", ""))) | {"Unique Identifier"}) args["attributes_to_return"] = attributes_to_return return { "domain": args.get("domain"), "attrs": attributes_to_return, "name": { "attrNameOR": argToList(args.get("attributes_to_filter_by", "")), "attrValue": args.get("value_for_filter"), "match": operator_mapping.get(args.get("operator", "Equal")), }, "useNormalizedAttrs": "True", } def parse_cie_user_response(args: Dict[str, Any], raw_response: Dict[str, Any]) -> Dict[str, Any]: """ Parse the raw response from the API call. Args: args: dict - The arguments passed to the command raw_response: dict - The raw response from the API call Returns: Dict[str, Any] - The parsed response """ default_error_msg = "The get_cie_user_command failed. Please verify the arguments and try again." result_response = raw_response.get("result", {}) if error := result_response.get("error"): raise ValueError(f"Error: {error.get('error-message', default_error_msg)}") parsed_raw_response = result_response.get("data", {}).get("domains", []) if parsed_raw_response and (objects := parsed_raw_response[0].get("objects", [])): return {key: objects[0].get(key) for key in args.get("attributes_to_return", [])} return {} def get_cie_user_command(client: Client, args: Dict[str, Any]) -> CommandResults: """ Command get cie user Args: client: Client args: Dict[str, Any] Returns: CommandResults """ payload = cie_user_prepare_args(args) raw_response = client.get_cie_user(payload) outputs = parse_cie_user_response(args, raw_response) if outputs: return CommandResults( outputs_prefix=f"{PA_OUTPUT_PREFIX}CIE.User", outputs_key_field="unique_identifier", outputs={key.lower().replace(" ", "_").replace("-", "_"): value for key, value in outputs.items()}, readable_output=tableToMarkdown("CIE User", outputs), raw_response=raw_response, ) return CommandResults( readable_output="No user found with the given arguments. Please verify the arguments and try again.", raw_response=raw_response, ) def run_push_jobs_polling_command(client: Client, args: dict): """ This function is generically handling the polling flow. In the polling flow, there is always an initial call that starts the uploading to the API (referred here as the 'upload' function) and another call that retrieves the status of that upload (referred here as the 'results' function). The run_polling_command function runs the 'upload' function and returns a ScheduledCommand object that schedules the next 'results' function, until the polling is complete. """ polling_interval = args.get("interval_in_seconds") or DEFAULT_POLLING_INTERVAL polling_timeout = arg_to_number(args.get("polling_timeout_in_seconds")) or DEFAULT_POLLING_TIMEOUT tsg_id = args.get("tsg_id") if (folders := argToList(args.get("folders"))) and folders[0] != "done": # first call, folder in args. We make the first push res = client.push_candidate_config(folders=folders, tsg_id=tsg_id) # remove folders, not needed for the rest args["folders"] = ["done"] # The result from the push returns a job id job_id = res.get("job_id", "") args["job_id"] = job_id # The push job creates sub processes once done. at this point, the parent job hasn't finished. args["parent_finished"] = False return CommandResults( scheduled_command=ScheduledCommand( command="prisma-sase-candidate-config-push", args=args, next_run_in_seconds=polling_interval, timeout_in_seconds=polling_timeout, ), readable_output=f"Waiting for all data to push for job id {job_id}", ) job_id = args.get("job_id", "") outputs: dict = {"job_id": job_id, "result": "OK"} if not argToBoolean(args.get("parent_finished")): res = client.get_config_job_by_id(job_id=job_id, tsg_id=tsg_id).get("data", [{}])[0] if res.get("result_str") == "PEND": demisto.debug(f"waiting for parent processes to finish, parent job_id {job_id}") return CommandResults( scheduled_command=ScheduledCommand( command="prisma-sase-candidate-config-push", args=args, next_run_in_seconds=polling_interval, timeout_in_seconds=polling_timeout, ) ) # From testing (as this is not documented) the status returns only as OK if the job succeeded job_result = res.get("result_str") if job_result != "OK": outputs["result"] = job_result outputs["details"] = res.get("details", "") return CommandResults( entry_type=EntryType.ERROR, outputs=outputs, outputs_prefix=f"{PA_OUTPUT_PREFIX}CandidateConfig", readable_output=f"Something went wrong while trying to push job id {job_id}. Result: {job_result}", ) # Parent is the first push. After finishing, sub processes created for each folder. args["parent_finished"] = True res = client.list_config_jobs(tsg_id=tsg_id).get("data", {}) for job in res: # looking for all sub processes with parent id as the job id if job.get("parent_id") == job_id: demisto.debug(f"looking for child processes with parent_id {job}") if job.get("result_str") == "PEND": return CommandResults( scheduled_command=ScheduledCommand( command="prisma-sase-candidate-config-push", args=args, next_run_in_seconds=polling_interval, timeout_in_seconds=polling_timeout, ) ) job_result = job.get("result_str") if job_result != "OK": outputs["result"] = job_result outputs["details"] = job.get("summary") return CommandResults( entry_type=EntryType.ERROR, outputs=outputs, outputs_prefix=f"{PA_OUTPUT_PREFIX}CandidateConfig", readable_output=f'Something went wrong while trying to push sub process ' f'with id {job.get("id", "")}job id {job_id}. ' f'Result: {job_result}', ) return CommandResults( readable_output=f"Finished pushing job {job_id}", outputs_prefix=f"{PA_OUTPUT_PREFIX}CandidateConfig", outputs=outputs ) def main(): # pragma: no cover """ PARSE AND VALIDATE INTEGRATION PARAMS """ # get the service API url params = demisto.params() base_url = params.get("url").strip("/") client_id = params.get("credentials", {}).get("identifier") client_secret = params.get("credentials", {}).get("password") tsg_id = params.get("tsg_id") verify_certificate = not argToBoolean(params.get("insecure", False)) proxy = argToBoolean(params.get("proxy", False)) handle_proxy() command = demisto.command() demisto.debug(f"Command being called is {command}") commands = { "prisma-sase-security-rule-create": create_security_rule_command, "prisma-sase-security-rule-list": list_security_rules_command, "prisma-sase-security-rule-delete": delete_security_rule_command, "prisma-sase-security-rule-update": edit_security_rule_command, "prisma-sase-candidate-config-push": run_push_jobs_polling_command, "prisma-sase-config-job-list": list_config_jobs_command, "prisma-sase-address-object-create": create_address_object_command, "prisma-sase-address-object-update": edit_address_object_command, "prisma-sase-address-object-delete": delete_address_object_command, "prisma-sase-address-object-list": list_address_objects_command, "prisma-sase-tag-list": list_tags_command, "prisma-sase-tag-create": create_tag_command, "prisma-sase-tag-update": update_tag_command, "prisma-sase-tag-delete": delete_tag_command, "prisma-sase-address-group-list": list_address_group_command, "prisma-sase-address-group-create": create_address_group_command, "prisma-sase-address-group-update": update_address_group_command, "prisma-sase-address-group-delete": delete_address_group_command, "prisma-sase-custom-url-category-list": list_custom_url_category_command, "prisma-sase-custom-url-category-create": create_custom_url_category_command, "prisma-sase-custom-url-category-update": update_custom_url_category_command, "prisma-sase-custom-url-category-delete": delete_custom_url_category_command, "prisma-sase-url-category-list": list_url_category_command, "prisma-sase-external-dynamic-list-list": list_external_dynamic_list_command, "prisma-sase-external-dynamic-list-create": create_external_dynamic_list_command, "prisma-sase-external-dynamic-list-update": update_external_dynamic_list_command, "prisma-sase-external-dynamic-list-delete": delete_custom_url_category_command, "prisma-sase-quarantine-host": quarantine_host_command, "prisma-sase-cie-user-get": get_cie_user_command, } client = Client( base_url=base_url, client_id=client_id, client_secret=client_secret, tsg_id=tsg_id, verify=verify_certificate, headers={"Accept": "application/json", "Content-Type": "application/json"}, proxy=proxy, ok_codes=(200, 201, 204), ) try: if command == "test-module": return_results(test_module(client)) elif command in commands: return_results(commands[command](client, demisto.args())) # type: ignore else: raise NotImplementedError(f'Command "{command}" is not implemented.') # Log exceptions except DemistoException as e: # special handling for 404 error, which is returned when the item is not found if e.res is not None and e.res.status_code == 404 and "Object Not Present" in e.message: return_results("The item you're searching for does not exist within the Prisma SASE API.") else: return_error(f"Failed to execute {command} command. Error: {e!s}") if __name__ in ("__main__", "__builtin__", "builtins"): main()