diff --git a/LICENSE b/LICENSE
index fe5e893..d0f6848 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright (c) 2017-2023 Splunk Inc.
+ Copyright (c) 2017-2024 Splunk Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index e954575..f97b6b8 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,16 @@
# Zscaler
Publisher: Splunk
-Connector Version: 2.4.0
+Connector Version: 3.0.0
Product Vendor: Zscaler
Product Name: Zscaler
Product Version Supported (regex): ".\*"
-Minimum Product Version: 6.0.0
+Minimum Product Version: 6.2.2
This app implements containment and investigative actions on Zscaler
[comment]: # " File: README.md"
-[comment]: # " Copyright (c) 2017-2023 Splunk Inc."
+[comment]: # " Copyright (c) 2017-2024 Splunk Inc."
[comment]: # ""
[comment]: # "Licensed under the Apache License, Version 2.0 (the 'License');"
[comment]: # "you may not use this file except in compliance with the License."
@@ -120,6 +120,19 @@ VARIABLE | REQUIRED | TYPE | DESCRIPTION
[get groups](#action-get-groups) - Gets a list of groups
[add group user](#action-add-group-user) - Add user to group
[remove group user](#action-remove-group-user) - Remove user from group
+[get allowlist](#action-get-allowlist) - Get urls on the allow list
+[get denylist](#action-get-denylist) - Get urls on the deny list
+[update user](#action-update-user) - Update user with given id
+[add category url](#action-add-category-url) - Add urls to a cetgory
+[add category ip](#action-add-category-ip) - Add IPs to a cetgory
+[remove category url](#action-remove-category-url) - Add urls to a cetgory
+[remove category ip](#action-remove-category-ip) - Remove IPs to a cetgory
+[create destination group](#action-create-destination-group) - Create destination group
+[list destination group](#action-list-destination-group) - List destination group
+[edit destination group](#action-edit-destination-group) - Edit destination group
+[delete destination group](#action-delete-destination-group) - Delete destination group
+[get departments](#action-get-departments) - Get a list of departments
+[get category details](#action-get-category-details) - Get the urls and keywords of a category
## action: 'test connectivity'
Validate the asset configuration for connectivity using supplied configuration
@@ -185,12 +198,15 @@ Type: **investigate**
Read only: **True**
#### Action Parameters
-No parameters are required for this action
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**get_ids_and_names_only** | optional | Whether to retrieve only a list containing URL category IDs and names. Even if displayURL is set to true, URLs will not be returned | boolean |
#### Action Output
DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
--------- | ---- | -------- | --------------
action_result.status | string | | test success test failed
+action_result.parameter.get_ids_and_names_only | string | | RADIO_STATIONS
action_result.data.\*.configuredName | string | | test Test-Caution
action_result.data.\*.customCategory | boolean | | True False
action_result.data.\*.customIpRangesCount | numeric | | 0
@@ -745,4 +761,465 @@ action_result.summary.message | string | | test User removed from group
action_result.message | string | | test User removed from group
summary.message | string | |
summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'get allowlist'
+Get urls on the allow list
+
+Type: **investigate**
+Read only: **True**
+
+#### Action Parameters
+No parameters are required for this action
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.data.\*.url | string | |
+action_result.summary.total_allowlist_items | numeric | | 10
+action_result.summary.message | string | | Allowlist retrieved
+action_result.message | string | | Allowlist retrieved
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'get denylist'
+Get urls on the deny list
+
+Type: **investigate**
+Read only: **True**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**filter** | optional | Filter results be url or ip | string |
+**query** | optional | Regular expression to match url or ip against | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.query | string | | 8...8
+action_result.parameter.filter | string | |
+action_result.data.\*.url | string | |
+action_result.summary.message | string | | Blacklist retrieved
+action_result.message | string | | Denylist retrieved
+action_result.summary.total_denylist_items | numeric | | 10
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'update user'
+Update user with given id
+
+Type: **correct**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**user_id** | required | ZScaler User Id | numeric | `zscaler user id`
+**user** | optional | JSON object containing the user details (see https://help.zscaler.com/zia/user-management#/users/{userId}-put) | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.user | string | |
+action_result.parameter.user_id | numeric | `zscaler user id` | 889814
+action_result.data.\*.adminUser | boolean | | True False
+action_result.data.\*.comments | string | | test This is test user
+action_result.data.\*.deleted | boolean | | True False
+action_result.data.\*.department.id | numeric | | 81896690
+action_result.data.\*.department.name | string | | test IT
+action_result.data.\*.email | string | `email` | test first.last@domain.com
+action_result.data.\*.groups.\*.id | numeric | `zscaler group id` | 8894813
+action_result.data.\*.groups.\*.name | string | | test Super Admin
+action_result.data.\*.id | numeric | `zscaler user id` | 889814
+action_result.data.\*.name | string | | test First Last
+action_result.summary | string | |
+action_result.summary.message | string | | test User removed from group
+action_result.message | string | | test User removed from group
+summary.message | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'add category url'
+Add urls to a cetgory
+
+Type: **generic**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**category_id** | required | The ID of the category to add the specified URLs to | string |
+**urls** | optional | A comma-separated list of URLs to add to the specified category | string |
+**retaining-parent-category-url** | optional | A comma-separated list of URLs to add to the retaining parent category section inside the specified category | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | success failed
+action_result.parameter.category_id | string | | RADIO_STATIONS
+action_result.parameter.urls | string | |
+action_result.parameter.retaining-parent-category-url | string | |
+action_result.data.\*.id | string | |
+action_result.data.\*.val | numeric | |
+action_result.data.\*.type | string | |
+action_result.data.\*.urls | string | |
+action_result.data.\*.scopes.\*.Type | string | |
+action_result.data.\*.editable | boolean | |
+action_result.data.\*.keywords | string | |
+action_result.data.\*.description | string | |
+action_result.data.\*.configuredName | string | |
+action_result.data.\*.customCategory | boolean | |
+action_result.data.\*.customUrlsCount | numeric | |
+action_result.data.\*.dbCategorizedUrls | string | |
+action_result.data.\*.customIpRangesCount | numeric | |
+action_result.data.\*.keywordsRetainingParentCategory | string | |
+action_result.data.\*.urlsRetainingParentCategoryCount | numeric | |
+action_result.data.\*.ipRangesRetainingParentCategoryCount | numeric | |
+action_result.message | string | | Message: Category urs updated
+action_result.summary | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'add category ip'
+Add IPs to a cetgory
+
+Type: **generic**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**category_id** | required | The ID of the category to add the specified URLs to | string |
+**ips** | optional | A comma-separated list of IP addresses to add to the specified category | string |
+**retaining-parent-category-ip** | optional | A comma-separated list of IPs to add to the retaining parent category section inside the specified category | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | success failed
+action_result.parameter.category_id | string | | RADIO_STATIONS
+action_result.parameter.ips | string | |
+action_result.parameter.retaining-parent-category-ip | string | |
+action_result.data.\*.id | string | |
+action_result.data.\*.val | numeric | |
+action_result.data.\*.type | string | |
+action_result.data.\*.urls | string | |
+action_result.data.\*.scopes.\*.Type | string | |
+action_result.data.\*.editable | boolean | |
+action_result.data.\*.keywords | string | |
+action_result.data.\*.description | string | |
+action_result.data.\*.configuredName | string | |
+action_result.data.\*.customCategory | boolean | |
+action_result.data.\*.customUrlsCount | numeric | |
+action_result.data.\*.dbCategorizedUrls | string | |
+action_result.data.\*.customIpRangesCount | numeric | |
+action_result.data.\*.keywordsRetainingParentCategory | string | |
+action_result.data.\*.urlsRetainingParentCategoryCount | numeric | |
+action_result.data.\*.ipRangesRetainingParentCategoryCount | numeric | |
+action_result.message | string | | Message: Category ips updated
+action_result.summary | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'remove category url'
+Add urls to a cetgory
+
+Type: **generic**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**category_id** | required | The ID of the category to add the specified URLs to | string |
+**urls** | optional | A comma-separated list of URLs to remove from the specified category | string |
+**retaining-parent-category-url** | optional | A comma-separated list of URLs to remove from the retaining parent category section inside the specified category | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | success failed
+action_result.parameter.category_id | string | | RADIO_STATIONS
+action_result.parameter.urls | string | |
+action_result.parameter.retaining-parent-category-url | string | |
+action_result.data.\*.id | string | |
+action_result.data.\*.val | numeric | |
+action_result.data.\*.type | string | |
+action_result.data.\*.urls | string | |
+action_result.data.\*.scopes.\*.Type | string | |
+action_result.data.\*.editable | boolean | |
+action_result.data.\*.keywords | string | |
+action_result.data.\*.description | string | |
+action_result.data.\*.configuredName | string | |
+action_result.data.\*.customCategory | boolean | |
+action_result.data.\*.customUrlsCount | numeric | |
+action_result.data.\*.dbCategorizedUrls | string | |
+action_result.data.\*.customIpRangesCount | numeric | |
+action_result.data.\*.keywordsRetainingParentCategory | string | |
+action_result.data.\*.urlsRetainingParentCategoryCount | numeric | |
+action_result.data.\*.ipRangesRetainingParentCategoryCount | numeric | |
+action_result.message | string | | Message: Category urls removed
+action_result.summary | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'remove category ip'
+Remove IPs to a cetgory
+
+Type: **generic**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**category_id** | required | The ID of the category to add the specified URLs to | string |
+**ips** | optional | A comma-separated list of IP addresses to add to the specified category | string |
+**retaining-parent-category-ip** | optional | A comma-separated list of IPs to add to the retaining parent category section inside the specified category | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | success failed
+action_result.parameter.category_id | string | | RADIO_STATIONS
+action_result.parameter.ips | string | |
+action_result.parameter.retaining-parent-category-ip | string | |
+action_result.data.\*.id | string | |
+action_result.data.\*.val | numeric | |
+action_result.data.\*.type | string | |
+action_result.data.\*.urls | string | |
+action_result.data.\*.scopes.\*.Type | string | |
+action_result.data.\*.editable | boolean | |
+action_result.data.\*.keywords | string | |
+action_result.data.\*.description | string | |
+action_result.data.\*.configuredName | string | |
+action_result.data.\*.customCategory | boolean | |
+action_result.data.\*.customUrlsCount | numeric | |
+action_result.data.\*.dbCategorizedUrls | string | |
+action_result.data.\*.customIpRangesCount | numeric | |
+action_result.data.\*.keywordsRetainingParentCategory | string | |
+action_result.data.\*.urlsRetainingParentCategoryCount | numeric | |
+action_result.data.\*.ipRangesRetainingParentCategoryCount | numeric | |
+action_result.message | string | | Message: Category ips removed
+action_result.summary | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'create destination group'
+Create destination group
+
+Type: **generic**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**name** | required | Destination IP group name | string |
+**type** | required | Destination IP group type (i.e., the group can contain destination IP addresses, countries, URL categories or FQDNs) | string |
+**addresses** | optional | Comma seperated string of destination IP addresses, FQDNs, or wildcard FQDNs added to the group | string |
+**description** | optional | Additional information about the destination IP group. | string |
+**ip_categories** | optional | Destination IP address URL categories | string |
+**countries** | optional | Destination IP address countries. You can identify destinations based on the location of a server. | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.countries | string | |
+action_result.parameter.ip_categories | string | |
+action_result.parameter.description | string | |
+action_result.parameter.addresses | string | |
+action_result.parameter.type | string | |
+action_result.parameter.name | string | |
+action_result.data.\*.id | numeric | |
+action_result.data.\*.name | string | |
+action_result.data.\*.type | string | | DSTN_IP DSTN_FQDN DSTN_DOMAIN DSTN_OTHER
+action_result.data.\*.addresses | string | | 192.168.1.1
+action_result.data.\*.countries | string | |
+action_result.data.\*.description | string | |
+action_result.data.\*.ipCategories | string | | TRADING_BROKARAGE_INSURANCE
+action_result.data.\*.isNonEditable | boolean | | True False
+action_result.data.\*.creatorContext | string | |
+action_result.summary | string | |
+action_result.summary.message | string | | test User removed from group
+action_result.message | string | | test User removed from group
+summary.message | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'list destination group'
+List destination group
+
+Type: **investigate**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**ip_group_ids** | optional | A comma-separated list of unique identifiers for the IP destination groups | string |
+**exclude_type** | optional | The IP group type to be excluded from the results | string |
+**category_type** | optional | Comma seperated list of IP group types to be filtered from results. This argument is only supported when the 'lite' argument is set to True | string |
+**limit** | optional | Limit of the results to be retrieved | numeric |
+**lite** | optional | Whether to retrieve only limited information of IP destination groups. Includes ID, name and type of the IP destination groups | boolean |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.lite | boolean | |
+action_result.parameter.limit | numeric | |
+action_result.parameter.category_type | string | |
+action_result.parameter.exclude_type | string | |
+action_result.parameter.ip_group_ids | string | |
+action_result.data.\*.id | numeric | |
+action_result.data.\*.name | string | |
+action_result.data.\*.type | string | | DSTN_IP DSTN_FQDN DSTN_DOMAIN DSTN_OTHER
+action_result.data.\*.addresses | string | | 192.168.1.1
+action_result.data.\*.countries | string | |
+action_result.data.\*.description | string | |
+action_result.data.\*.ipCategories | string | | TRADING_BROKARAGE_INSURANCE
+action_result.data.\*.isNonEditable | boolean | | True False
+action_result.data.\*.creatorContext | string | |
+action_result.summary | string | |
+action_result.summary.message | string | | Retreived Destination Groups
+action_result.message | string | | Retreived Destination Groups
+summary.message | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'edit destination group'
+Edit destination group
+
+Type: **generic**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**ip_group_id** | required | The unique identifier for the IP destination group | numeric |
+**name** | optional | Destination IP group name | string |
+**addresses** | optional | Comma seperated string of destination IP addresses, FQDNs, or wildcard FQDNs added to the group | string |
+**description** | optional | Additional information about the destination IP group. | string |
+**ip_categories** | optional | Destination IP address URL categories | string |
+**countries** | optional | Destination IP address countries. You can identify destinations based on the location of a server. | string |
+**is_non_editable** | optional | If set to true, the destination IP address group is non-editable. This field is applicable only to predefined IP address groups, which cannot be modified | boolean |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.is_non_editable | boolean | |
+action_result.parameter.countries | string | |
+action_result.parameter.ip_categories | string | |
+action_result.parameter.description | string | |
+action_result.parameter.addresses | string | |
+action_result.parameter.name | string | |
+action_result.parameter.ip_group_id | numeric | |
+action_result.data.\*.id | numeric | |
+action_result.data.\*.name | string | |
+action_result.data.\*.type | string | | DSTN_IP DSTN_FQDN DSTN_DOMAIN DSTN_OTHER
+action_result.data.\*.addresses | string | | 192.168.1.1
+action_result.data.\*.countries | string | |
+action_result.data.\*.description | string | |
+action_result.data.\*.ipCategories | string | | TRADING_BROKARAGE_INSURANCE
+action_result.data.\*.isNonEditable | boolean | | True False
+action_result.data.\*.creatorContext | string | |
+action_result.summary | string | |
+action_result.summary.message | string | | Destination group edited
+action_result.message | string | | Destination group edited
+summary.message | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'delete destination group'
+Delete destination group
+
+Type: **generic**
+Read only: **False**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**ip_group_ids** | optional | A comma-separated list of unique identifiers for the IP destination groups | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.ip_group_ids | string | |
+action_result.data.\*.ip_group_ids | string | |
+action_result.summary | string | |
+action_result.summary.message | string | | Destination group deleted
+action_result.message | string | | Destination group deleted
+summary.message | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'get departments'
+Get a list of departments
+
+Type: **investigate**
+Read only: **True**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**name** | optional | Filter by department name | string |
+**page** | optional | Specifies the page offset | numeric |
+**pageSize** | optional | Specifies the page size | numeric |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.pageSize | string | |
+action_result.parameter.page | string | |
+action_result.parameter.name | string | |
+action_result.data.\*.id | numeric | |
+action_result.data.\*.name | string | |
+action_result.data.\*.isNonEditable | boolean | |
+action_result.summary | string | |
+action_result.summary.message | string | | Departments Retrieved
+action_result.summary.total_deparments | numeric | | 97
+action_result.message | string | | Departments Retrieved
+summary.message | string | |
+summary.total_objects | numeric | | 1
+summary.total_objects_successful | numeric | | 1
+
+## action: 'get category details'
+Get the urls and keywords of a category
+
+Type: **investigate**
+Read only: **True**
+
+#### Action Parameters
+PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
+--------- | -------- | ----------- | ---- | --------
+**category_ids** | optional | Comma seperated string of category id's to query | string |
+
+#### Action Output
+DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
+--------- | ---- | -------- | --------------
+action_result.status | string | | test success test failed
+action_result.parameter.category_ids | string | | CUSTOM_001, CUSTOM_002
+action_result.data.\*.configuredName | string | | test Test-Caution
+action_result.data.\*.customCategory | boolean | | True False
+action_result.data.\*.keywords | string | |
+action_result.data.\*.urls | string | |
+action_result.data.\*.customIpRangesCount | numeric | | 0
+action_result.data.\*.customUrlsCount | numeric | | 0
+action_result.data.\*.dbCategorizedUrls | string | | test 6.5.3.2.4
+action_result.data.\*.description | string | | test OTHER_RESTRICTED_WEBSITE_DESC
+action_result.data.\*.editable | boolean | | True False
+action_result.data.\*.id | string | `zscaler url category` | test OTHER_RESTRICTED_WEBSITE
+action_result.data.\*.ipRangesRetainingParentCategoryCount | numeric | | 0
+action_result.data.\*.scopes.\*.Type | string | | test ORGANIZATION
+action_result.data.\*.type | string | | test URL_CATEGORY
+action_result.data.\*.urlsRetainingParentCategoryCount | numeric | | 0
+action_result.data.\*.val | numeric | | 1
+action_result.summary.total_categories | numeric | | 97
+action_result.message | string | | Category details recieved
+summary.total_objects | numeric | | 1
summary.total_objects_successful | numeric | | 1
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
index ed269c4..04fd24e 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,6 +1,6 @@
# File: __init__.py
#
-# Copyright (c) 2017-2023 Splunk Inc.
+# Copyright (c) 2017-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/manual_readme_content.md b/manual_readme_content.md
new file mode 100644
index 0000000..26220a8
--- /dev/null
+++ b/manual_readme_content.md
@@ -0,0 +1,76 @@
+[comment]: # " File: README.md"
+[comment]: # " Copyright (c) 2017-2024 Splunk Inc."
+[comment]: # ""
+[comment]: # "Licensed under the Apache License, Version 2.0 (the 'License');"
+[comment]: # "you may not use this file except in compliance with the License."
+[comment]: # "You may obtain a copy of the License at"
+[comment]: # ""
+[comment]: # " http://www.apache.org/licenses/LICENSE-2.0"
+[comment]: # ""
+[comment]: # "Unless required by applicable law or agreed to in writing, software distributed under"
+[comment]: # "the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,"
+[comment]: # "either express or implied. See the License for the specific language governing permissions"
+[comment]: # "and limitations under the License."
+[comment]: # ""
+Below points are considered for providing the **URL Category** parameter value.
+
+- Entire URL category string has to be mentioned in block letters
+
+- The most child category on UI has to be passed as the URL category parameter value to the action
+
+- From the URL category value on UI, every space has to be replaced by an underscore '\_' before
+ passing it in the action's parameter value
+
+
+
+ - For example, **Alternate Lifestyle** on UI becomes **ALTERNATE_LIFESTYLE**
+
+- When you specify a **url_category** , you can give it either the name you created or the ID
+ which is assigned to it from Zscaler. The search will first search for the name, as opposed to
+ the ID. So if you create a category **phantom-block** , you could use either **phantom-block**
+ or **CUSTOM\_\*\*** . The name for these is case sensitive.
+
+The following are considered for providing the **URL** parameter value.
+
+- The comma-separated values of **URL** should correctly be given e.g. test.com,test1.com else the
+ Phantom framework's parameter validator will return the error mentioning **Exception occurred:
+ string index out of range** .
+
+Configure and set up permissions for the **lookup_url** action
+
+- Login to Zscaler UI using the Administrator credentials.
+- Once logged in, go to **Administration -> Role Management** section.
+- Click on the **Edit** icon beside the role that your account uses to configure the test
+ connectivity.
+- Go to the **Functional Scope** section, enable **Security** if disabled, and save it.
+
+The above steps would help run the Lookup URL action as expected.
+
+The Sandbox Submission API requires a separate API key and uses a different host
+(csbapi.\[zscaler-cloud-name\]). For the **submit_file** action, the **sandbox_base_url** and
+**sandbox_api_token** asset configuration parameters should be configured. These two asset
+parameters won't affect test_connectivity. Follow the below steps to fetch these credentials for the
+**submit_file** action
+
+- Log in to the ZIA Admin Portal using your **admin** credentials.
+- Once logged in, go to **Administration -> Cloud Service API Key Management** section. In order
+ to view the Cloud Service API Key Management page, the admin must be assigned an admin role.
+- For the Cloud Sandbox Submission API used in this action, the base URL and token are displayed
+ on the **Sandbox Submission API Token** tab.
+- The base URL and token displayed here can be configured in the asset parameters in
+ **sandbox_base_url** and **sandbox_api_token** parameters respectively and will be used for the
+ submit_file action.
+
+The above steps would help run the Submit File action as expected.
+
+**NOTE:** This action would work according to the API behavior
+
+Port Information
+
+The app uses HTTP/ HTTPS protocol for communicating with the Zscaler server. Below are the default
+ports used by Splunk SOAR.
+
+| Service Name | Transport Protocol | Port |
+|----------------------|--------------------|------|
+| http | tcp | 80 |
+| https | tcp | 443 |
diff --git a/readme.html b/readme.html
deleted file mode 100644
index b00f74c..0000000
--- a/readme.html
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
Below points are considered for providing the URL Category parameter value.
-
- - Entire URL category string has to be mentioned in block letters
- - The most child category on UI has to be passed as the URL category parameter value to the action
- - From the URL category value on UI, every space has to be replaced by an underscore '_' before passing it in the action's parameter value
-
- - For example, Alternate Lifestyle on UI becomes ALTERNATE_LIFESTYLE
-
- - When you specify a url_category, you can give it either the name you created or the ID which is assigned to it from Zscaler.
- The search will first search for the name, as opposed to the ID. So if you create a category phantom-block, you could
- use either phantom-block or CUSTOM_**. The name for these is case sensitive.
-
-
-
-The following are considered for providing the URL parameter value.
-
- - The comma-separated values of URL should correctly be given e.g. test.com,test1.com else the Phantom framework's parameter validator will return the error mentioning Exception occurred: string index out of range.
-
-
-
-Configure and set up permissions for the lookup_url action
-
-
- - Login to Zscaler UI using the Administrator credentials.
- - Once logged in, go to Administration -> Role Management section.
- - Click on the Edit icon beside the role that your account uses to configure the test connectivity.
- - Go to the Functional Scope section, enable Security if disabled, and save it.
-
- The above steps would help run the Lookup URL action as expected.
-
-
-The Sandbox Submission API requires a separate API key and uses a different host (csbapi.[zscaler-cloud-name]). For the submit_file action, the sandbox_base_url and sandbox_api_token asset configuration parameters should be configured. These two asset parameters won't affect test_connectivity. Follow the below steps to fetch these credentials for the submit_file action
-
-
- - Log in to the ZIA Admin Portal using your admin credentials.
- - Once logged in, go to Administration -> Cloud Service API Key Management section. In order to view the Cloud Service API Key Management page, the admin must be assigned an admin role.
- - For the Cloud Sandbox Submission API used in this action, the base URL and token are displayed on the Sandbox Submission API Token tab.
- - The base URL and token displayed here can be configured in the asset parameters in sandbox_base_url and sandbox_api_token parameters respectively and will be used for the submit_file action.
-
- The above steps would help run the Submit File action as expected.
- NOTE: This action would work according to the API behavior
-
-
-Port Information
-
- The app uses HTTP/ HTTPS protocol for communicating with the Zscaler server. Below are the default ports used by Splunk SOAR.
-
-
- Service Name |
- Transport Protocol |
- Port |
-
-
- http |
- tcp |
- 80 |
-
-
- https |
- tcp |
- 443 |
-
-
-
diff --git a/release_notes/3.0.0.md b/release_notes/3.0.0.md
new file mode 100644
index 0000000..1dcf0a8
--- /dev/null
+++ b/release_notes/3.0.0.md
@@ -0,0 +1,14 @@
+* [PAPP-34457]
+ * New `get blacklist` action that retrieves the Zscaler default block list.
+ * New `get whitelist` action that retrieves the Zscaler default allow list.
+ * New `update user` action that updates the user information for the specified ID.
+ * New `add category url:` action that adds URLs to the specified category.
+ * New `add category IP:` action that adds IPs to the specified category.
+ * New `remove category url:` action that removes URLs from the specified category.
+ * New `remove category IP:` action that removes IPs from the specified category.
+ * New `get categories:` action that retrieves a list of all categories.
+ * New `create destination groups:` action that adds a new IP destination group.
+ * New `edit destination groups:` action that updates the IP destination group information for the specified group ID.
+ * New `list destination groups:` action that gets a list of all IP destination groups or the IP destination group information for the specified group ID.
+ * New `delete destination groups:` action that deletes the IP destination groups for the specified group IDs.
+ * New `get departments:` action that gets a list of departments. It can be searched by name.
\ No newline at end of file
diff --git a/zscaler.json b/zscaler.json
index 5ed9741..27f6e64 100644
--- a/zscaler.json
+++ b/zscaler.json
@@ -9,13 +9,13 @@
"product_name": "Zscaler",
"product_version_regex": ".*",
"publisher": "Splunk",
- "license": "Copyright (c) 2017-2023 Splunk Inc.",
- "app_version": "2.4.0",
+ "license": "Copyright (c) 2017-2024 Splunk Inc.",
+ "app_version": "3.0.0",
"utctime_updated": "2022-01-24T06:29:48.000000Z",
"package_name": "phantom_zscaler",
"main_module": "zscaler_connector.py",
"python_version": "3",
- "min_phantom_version": "6.0.0",
+ "min_phantom_version": "6.2.2",
"fips_compliant": true,
"latest_tested_versions": [
"Cloud v6.2, 29th June 2023"
@@ -316,7 +316,19 @@
"description": "List all URL categories",
"type": "investigate",
"read_only": true,
- "parameters": {},
+ "parameters": {
+ "get_ids_and_names_only": {
+ "description": "Whether to retrieve only a list containing URL category IDs and names. Even if displayURL is set to true, URLs will not be returned",
+ "data_type": "boolean",
+ "order": 0,
+ "default": false,
+ "example_values": [
+ true,
+ false
+ ],
+ "primary": true
+ }
+ },
"output": [
{
"data_path": "action_result.status",
@@ -326,6 +338,13 @@
"test failed"
]
},
+ {
+ "data_path": "action_result.parameter.get_ids_and_names_only",
+ "data_type": "string",
+ "example_values": [
+ "RADIO_STATIONS"
+ ]
+ },
{
"data_path": "action_result.data.*.configuredName",
"data_type": "string",
@@ -3000,6 +3019,2025 @@
"type": "table"
},
"versions": "EQ(*)"
+ },
+ {
+ "action": "get allowlist",
+ "identifier": "get_allowlist",
+ "description": "Get urls on the allow list",
+ "type": "investigate",
+ "read_only": true,
+ "parameters": {},
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.url",
+ "data_type": "string",
+ "column_name": "allowlist url",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.summary.total_allowlist_items",
+ "data_type": "numeric",
+ "example_values": [
+ 10
+ ]
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "Allowlist retrieved"
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Allowlist retrieved"
+ ]
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "title": "Allowlist",
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "get denylist",
+ "identifier": "get_denylist",
+ "description": "Get urls on the deny list",
+ "type": "investigate",
+ "read_only": true,
+ "parameters": {
+ "filter": {
+ "description": "Filter results be url or ip",
+ "data_type": "string",
+ "primary": true,
+ "value_list": [
+ "url",
+ "ip"
+ ],
+ "order": 0
+ },
+ "query": {
+ "description": "Regular expression to match url or ip against",
+ "data_type": "string",
+ "primary": true,
+ "example_values": [
+ "8...8"
+ ],
+ "order": 1
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.query",
+ "data_type": "string",
+ "column_name": "Query",
+ "example_values": [
+ "8...8"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.filter",
+ "data_type": "string",
+ "column_name": "Filter",
+ "value_list": [
+ "url",
+ "ip"
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.url",
+ "data_type": "string",
+ "column_name": "denylist url",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "Blacklist retrieved"
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Denylist retrieved"
+ ]
+ },
+ {
+ "data_path": "action_result.summary.total_denylist_items",
+ "data_type": "numeric",
+ "example_values": [
+ 10
+ ]
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "title": "Denylist",
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "update user",
+ "identifier": "update_user",
+ "description": "Update user with given id",
+ "type": "correct",
+ "read_only": false,
+ "parameters": {
+ "user_id": {
+ "description": "ZScaler User Id",
+ "data_type": "numeric",
+ "required": true,
+ "primary": true,
+ "contains": [
+ "zscaler user id"
+ ],
+ "example_values": [
+ 889814
+ ],
+ "order": 0
+ },
+ "user": {
+ "description": "JSON object containing the user details (see https://help.zscaler.com/zia/user-management#/users/{userId}-put)",
+ "data_type": "string",
+ "primary": true,
+ "order": 1
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "column_name": "Status",
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.user",
+ "data_type": "string",
+ "column_name": "User"
+ },
+ {
+ "data_path": "action_result.parameter.user_id",
+ "data_type": "numeric",
+ "contains": [
+ "zscaler user id"
+ ],
+ "column_name": "User ID",
+ "example_values": [
+ 889814
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.adminUser",
+ "data_type": "boolean",
+ "example_values": [
+ true,
+ false
+ ],
+ "column_name": "Is Admin User",
+ "column_order": 9
+ },
+ {
+ "data_path": "action_result.data.*.comments",
+ "data_type": "string",
+ "example_values": [
+ "test This is test user"
+ ],
+ "column_name": "Comments",
+ "column_order": 8
+ },
+ {
+ "data_path": "action_result.data.*.deleted",
+ "data_type": "boolean",
+ "example_values": [
+ true,
+ false
+ ],
+ "column_name": "Is Deleted",
+ "column_order": 7
+ },
+ {
+ "data_path": "action_result.data.*.department.id",
+ "data_type": "numeric",
+ "example_values": [
+ 81896690
+ ],
+ "column_name": "Department ID",
+ "column_order": 6
+ },
+ {
+ "data_path": "action_result.data.*.department.name",
+ "data_type": "string",
+ "example_values": [
+ "test IT"
+ ],
+ "column_name": "Department name",
+ "column_order": 5
+ },
+ {
+ "data_path": "action_result.data.*.email",
+ "data_type": "string",
+ "contains": [
+ "email"
+ ],
+ "example_values": [
+ "test first.last@domain.com"
+ ],
+ "column_name": "User Email",
+ "column_order": 4
+ },
+ {
+ "data_path": "action_result.data.*.groups.*.id",
+ "data_type": "numeric",
+ "contains": [
+ "zscaler group id"
+ ],
+ "example_values": [
+ 8894813
+ ],
+ "column_name": "Group ID",
+ "column_order": 3
+ },
+ {
+ "data_path": "action_result.data.*.groups.*.name",
+ "data_type": "string",
+ "example_values": [
+ "test Super Admin"
+ ],
+ "column_name": "Group Name",
+ "column_order": 2
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "numeric",
+ "contains": [
+ "zscaler user id"
+ ],
+ "example_values": [
+ 889814
+ ],
+ "column_name": "User ID",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.data.*.name",
+ "data_type": "string",
+ "example_values": [
+ "test First Last"
+ ],
+ "column_name": "User Name",
+ "column_order": 1
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "test User removed from group"
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "test User removed from group"
+ ]
+ },
+ {
+ "data_path": "summary.message",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "add category url",
+ "description": "Add urls to a cetgory",
+ "type": "generic",
+ "identifier": "add_category_url",
+ "read_only": false,
+ "parameters": {
+ "category_id": {
+ "description": "The ID of the category to add the specified URLs to",
+ "data_type": "string",
+ "order": 0,
+ "example_values": [
+ "RADIO_STATIONS"
+ ],
+ "primary": true,
+ "required": true
+ },
+ "urls": {
+ "description": "A comma-separated list of URLs to add to the specified category",
+ "data_type": "string",
+ "order": 1,
+ "primary": true
+ },
+ "retaining-parent-category-url": {
+ "description": "A comma-separated list of URLs to add to the retaining parent category section inside the specified category",
+ "data_type": "string",
+ "primary": true,
+ "order": 2
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "example_values": [
+ "success",
+ "failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.category_id",
+ "data_type": "string",
+ "example_values": [
+ "RADIO_STATIONS"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.urls",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.parameter.retaining-parent-category-url",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "string",
+ "column_name": "URL category",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.data.*.val",
+ "data_type": "numeric",
+ "column_name": "Value",
+ "column_order": 1
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string",
+ "column_name": "Category type",
+ "column_order": 2
+ },
+ {
+ "data_path": "action_result.data.*.urls",
+ "data_type": "string",
+ "column_name": "URLs added",
+ "column_order": 3
+ },
+ {
+ "data_path": "action_result.data.*.scopes.*.Type",
+ "data_type": "string",
+ "column_name": "Category scope",
+ "column_order": 4
+ },
+ {
+ "data_path": "action_result.data.*.editable",
+ "data_type": "boolean",
+ "column_name": "Is editable",
+ "column_order": 5
+ },
+ {
+ "data_path": "action_result.data.*.keywords",
+ "data_type": "string",
+ "column_name": "Category keywords",
+ "column_order": 6
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string",
+ "column_name": "Category description",
+ "column_order": 7
+ },
+ {
+ "data_path": "action_result.data.*.configuredName",
+ "data_type": "string",
+ "column_name": "Configured Name",
+ "column_order": 8
+ },
+ {
+ "data_path": "action_result.data.*.customCategory",
+ "data_type": "boolean",
+ "column_name": "Is Custom Category",
+ "column_order": 9
+ },
+ {
+ "data_path": "action_result.data.*.customUrlsCount",
+ "data_type": "numeric",
+ "column_name": "Custom url count",
+ "column_order": 10
+ },
+ {
+ "data_path": "action_result.data.*.dbCategorizedUrls",
+ "data_type": "string",
+ "column_name": "Parent urls",
+ "column_order": 11
+ },
+ {
+ "data_path": "action_result.data.*.customIpRangesCount",
+ "data_type": "numeric",
+ "column_name": "Custom ip ranges count",
+ "column_order": 12
+ },
+ {
+ "data_path": "action_result.data.*.keywordsRetainingParentCategory",
+ "data_type": "string",
+ "column_name": "Parent url keywords",
+ "column_order": 13
+ },
+ {
+ "data_path": "action_result.data.*.urlsRetainingParentCategoryCount",
+ "data_type": "numeric",
+ "column_name": "Number of parent urls",
+ "column_order": 14
+ },
+ {
+ "data_path": "action_result.data.*.ipRangesRetainingParentCategoryCount",
+ "data_type": "numeric",
+ "column_name": "Number of parent ips",
+ "column_order": 15
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Message: Category urs updated"
+ ]
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "add category ip",
+ "description": "Add IPs to a cetgory",
+ "type": "generic",
+ "identifier": "add_category_ip",
+ "read_only": false,
+ "parameters": {
+ "category_id": {
+ "description": "The ID of the category to add the specified URLs to",
+ "data_type": "string",
+ "order": 0,
+ "example_values": [
+ "RADIO_STATIONS"
+ ],
+ "primary": true,
+ "required": true
+ },
+ "ips": {
+ "description": "A comma-separated list of IP addresses to add to the specified category",
+ "data_type": "string",
+ "order": 1,
+ "primary": true
+ },
+ "retaining-parent-category-ip": {
+ "description": "A comma-separated list of IPs to add to the retaining parent category section inside the specified category",
+ "data_type": "string",
+ "primary": true,
+ "order": 2
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "example_values": [
+ "success",
+ "failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.category_id",
+ "data_type": "string",
+ "example_values": [
+ "RADIO_STATIONS"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.ips",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.parameter.retaining-parent-category-ip",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "string",
+ "column_name": "URL category",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.data.*.val",
+ "data_type": "numeric",
+ "column_name": "Value",
+ "column_order": 1
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string",
+ "column_name": "Category type",
+ "column_order": 2
+ },
+ {
+ "data_path": "action_result.data.*.urls",
+ "data_type": "string",
+ "column_name": "URLs added",
+ "column_order": 3
+ },
+ {
+ "data_path": "action_result.data.*.scopes.*.Type",
+ "data_type": "string",
+ "column_name": "Category scope",
+ "column_order": 4
+ },
+ {
+ "data_path": "action_result.data.*.editable",
+ "data_type": "boolean",
+ "column_name": "Is editable",
+ "column_order": 5
+ },
+ {
+ "data_path": "action_result.data.*.keywords",
+ "data_type": "string",
+ "column_name": "Category keywords",
+ "column_order": 6
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string",
+ "column_name": "Category description",
+ "column_order": 7
+ },
+ {
+ "data_path": "action_result.data.*.configuredName",
+ "data_type": "string",
+ "column_name": "Configured Name",
+ "column_order": 8
+ },
+ {
+ "data_path": "action_result.data.*.customCategory",
+ "data_type": "boolean",
+ "column_name": "Is Custom Category",
+ "column_order": 9
+ },
+ {
+ "data_path": "action_result.data.*.customUrlsCount",
+ "data_type": "numeric",
+ "column_name": "Custom url count",
+ "column_order": 10
+ },
+ {
+ "data_path": "action_result.data.*.dbCategorizedUrls",
+ "data_type": "string",
+ "column_name": "Parent urls",
+ "column_order": 11
+ },
+ {
+ "data_path": "action_result.data.*.customIpRangesCount",
+ "data_type": "numeric",
+ "column_name": "Custom ip ranges count",
+ "column_order": 12
+ },
+ {
+ "data_path": "action_result.data.*.keywordsRetainingParentCategory",
+ "data_type": "string",
+ "column_name": "Parent url keywords",
+ "column_order": 13
+ },
+ {
+ "data_path": "action_result.data.*.urlsRetainingParentCategoryCount",
+ "data_type": "numeric",
+ "column_name": "Number of parent urls",
+ "column_order": 14
+ },
+ {
+ "data_path": "action_result.data.*.ipRangesRetainingParentCategoryCount",
+ "data_type": "numeric",
+ "column_name": "Number of parent ips",
+ "column_order": 15
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Message: Category ips updated"
+ ]
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "remove category url",
+ "description": "Add urls to a cetgory",
+ "type": "generic",
+ "identifier": "remove_category_url",
+ "read_only": false,
+ "parameters": {
+ "category_id": {
+ "description": "The ID of the category to add the specified URLs to",
+ "data_type": "string",
+ "order": 0,
+ "example_values": [
+ "RADIO_STATIONS"
+ ],
+ "primary": true,
+ "required": true
+ },
+ "urls": {
+ "description": "A comma-separated list of URLs to remove from the specified category",
+ "data_type": "string",
+ "order": 1,
+ "primary": true
+ },
+ "retaining-parent-category-url": {
+ "description": "A comma-separated list of URLs to remove from the retaining parent category section inside the specified category",
+ "data_type": "string",
+ "primary": true,
+ "order": 2
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "example_values": [
+ "success",
+ "failed"
+ ],
+ "column_order": 0,
+ "column_name": "Status"
+ },
+ {
+ "data_path": "action_result.parameter.category_id",
+ "data_type": "string",
+ "example_values": [
+ "RADIO_STATIONS"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.urls",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.parameter.retaining-parent-category-url",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.val",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.urls",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.scopes.*.Type",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.editable",
+ "data_type": "boolean"
+ },
+ {
+ "data_path": "action_result.data.*.keywords",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.configuredName",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.customCategory",
+ "data_type": "boolean"
+ },
+ {
+ "data_path": "action_result.data.*.customUrlsCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.dbCategorizedUrls",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.customIpRangesCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.keywordsRetainingParentCategory",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.urlsRetainingParentCategoryCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.ipRangesRetainingParentCategoryCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Message: Category urls removed"
+ ]
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "remove category ip",
+ "description": "Remove IPs to a cetgory",
+ "type": "generic",
+ "identifier": "remove_category_ip",
+ "read_only": false,
+ "parameters": {
+ "category_id": {
+ "description": "The ID of the category to add the specified URLs to",
+ "data_type": "string",
+ "order": 0,
+ "example_values": [
+ "RADIO_STATIONS"
+ ],
+ "primary": true,
+ "required": true
+ },
+ "ips": {
+ "description": "A comma-separated list of IP addresses to add to the specified category",
+ "data_type": "string",
+ "order": 1,
+ "primary": true
+ },
+ "retaining-parent-category-ip": {
+ "description": "A comma-separated list of IPs to add to the retaining parent category section inside the specified category",
+ "data_type": "string",
+ "primary": true,
+ "order": 2
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "example_values": [
+ "success",
+ "failed"
+ ],
+ "column_order": 0,
+ "column_name": "Status"
+ },
+ {
+ "data_path": "action_result.parameter.category_id",
+ "data_type": "string",
+ "example_values": [
+ "RADIO_STATIONS"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.ips",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.parameter.retaining-parent-category-ip",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.val",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.urls",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.scopes.*.Type",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.editable",
+ "data_type": "boolean"
+ },
+ {
+ "data_path": "action_result.data.*.keywords",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.configuredName",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.customCategory",
+ "data_type": "boolean"
+ },
+ {
+ "data_path": "action_result.data.*.customUrlsCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.dbCategorizedUrls",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.customIpRangesCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.keywordsRetainingParentCategory",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.data.*.urlsRetainingParentCategoryCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.data.*.ipRangesRetainingParentCategoryCount",
+ "data_type": "numeric"
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Message: Category ips removed"
+ ]
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "create destination group",
+ "identifier": "create_destination_group",
+ "description": "Create destination group",
+ "type": "generic",
+ "read_only": false,
+ "parameters": {
+ "name": {
+ "description": "Destination IP group name",
+ "data_type": "string",
+ "required": true,
+ "primary": true,
+ "order": 0
+ },
+ "type": {
+ "description": "Destination IP group type (i.e., the group can contain destination IP addresses, countries, URL categories or FQDNs)",
+ "data_type": "string",
+ "required": true,
+ "primary": true,
+ "example_values": [
+ "DSTN_IP",
+ "DSTN_FQDN",
+ "DSTN_DOMAIN",
+ "DSTN_OTHER"
+ ],
+ "order": 1
+ },
+ "addresses": {
+ "description": "Comma seperated string of destination IP addresses, FQDNs, or wildcard FQDNs added to the group",
+ "data_type": "string",
+ "order": 2
+ },
+ "description": {
+ "description": "Additional information about the destination IP group.",
+ "data_type": "string",
+ "order": 3
+ },
+ "ip_categories": {
+ "description": "Destination IP address URL categories",
+ "data_type": "string",
+ "order": 4
+ },
+ "countries": {
+ "description": "Destination IP address countries. You can identify destinations based on the location of a server.",
+ "data_type": "string",
+ "order": 5
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "column_name": "Status",
+ "column_order": 2,
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.countries",
+ "data_type": "string",
+ "column_name": "Countries"
+ },
+ {
+ "data_path": "action_result.parameter.ip_categories",
+ "data_type": "string",
+ "column_name": "Ip Categories"
+ },
+ {
+ "data_path": "action_result.parameter.description",
+ "data_type": "string",
+ "column_name": "Description"
+ },
+ {
+ "data_path": "action_result.parameter.addresses",
+ "data_type": "string",
+ "column_name": "Addresses"
+ },
+ {
+ "data_path": "action_result.parameter.type",
+ "data_type": "string",
+ "column_name": "Type"
+ },
+ {
+ "data_path": "action_result.parameter.name",
+ "data_type": "string",
+ "column_name": "Name"
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "numeric",
+ "column_name": "Group ID",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.data.*.name",
+ "data_type": "string",
+ "column_name": "Group name",
+ "column_order": 1
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string",
+ "example_values": [
+ "DSTN_IP",
+ "DSTN_FQDN",
+ "DSTN_DOMAIN",
+ "DSTN_OTHER"
+ ],
+ "column_name": "Group type",
+ "column_order": 3
+ },
+ {
+ "data_path": "action_result.data.*.addresses",
+ "data_type": "string",
+ "example_values": [
+ "192.168.1.1"
+ ],
+ "column_name": "Destination addresses",
+ "column_order": 4
+ },
+ {
+ "data_path": "action_result.data.*.countries",
+ "data_type": "string",
+ "column_name": "Destination countries",
+ "column_order": 5
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string",
+ "column_name": "Description",
+ "column_order": 6
+ },
+ {
+ "data_path": "action_result.data.*.ipCategories",
+ "data_type": "string",
+ "example_values": [
+ "TRADING_BROKARAGE_INSURANCE"
+ ],
+ "column_name": "Destination categories",
+ "column_order": 7
+ },
+ {
+ "data_path": "action_result.data.*.isNonEditable",
+ "data_type": "boolean",
+ "example_values": [
+ true,
+ false
+ ],
+ "column_name": "Is editable",
+ "column_order": 8
+ },
+ {
+ "data_path": "action_result.data.*.creatorContext",
+ "data_type": "string",
+ "column_name": "Creator Context",
+ "column_order": 9
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "test User removed from group"
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "test User removed from group"
+ ]
+ },
+ {
+ "data_path": "summary.message",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "list destination group",
+ "identifier": "list_destination_group",
+ "description": "List destination group",
+ "type": "investigate",
+ "read_only": false,
+ "parameters": {
+ "ip_group_ids": {
+ "description": "A comma-separated list of unique identifiers for the IP destination groups",
+ "data_type": "string",
+ "primary": true,
+ "order": 0
+ },
+ "exclude_type": {
+ "description": "The IP group type to be excluded from the results",
+ "data_type": "string",
+ "primary": true,
+ "example_values": [
+ "DSTN_IP",
+ "DSTN_FQDN",
+ "DSTN_DOMAIN",
+ "DSTN_OTHER"
+ ],
+ "order": 1
+ },
+ "category_type": {
+ "description": "Comma seperated list of IP group types to be filtered from results. This argument is only supported when the 'lite' argument is set to True",
+ "data_type": "string",
+ "example_values": [
+ "DSTN_IP",
+ "DSTN_FQDN",
+ "DSTN_DOMAIN",
+ "DSTN_OTHER"
+ ],
+ "order": 2
+ },
+ "limit": {
+ "description": "Limit of the results to be retrieved",
+ "data_type": "numeric",
+ "default": 50,
+ "order": 3
+ },
+ "lite": {
+ "description": "Whether to retrieve only limited information of IP destination groups. Includes ID, name and type of the IP destination groups",
+ "data_type": "boolean",
+ "example_values": [
+ true,
+ false
+ ],
+ "default": false,
+ "order": 4
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "column_name": "Status",
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.lite",
+ "data_type": "boolean",
+ "column_name": "Lite"
+ },
+ {
+ "data_path": "action_result.parameter.limit",
+ "data_type": "numeric",
+ "column_name": "Limit"
+ },
+ {
+ "data_path": "action_result.parameter.category_type",
+ "data_type": "string",
+ "column_name": "Category Type"
+ },
+ {
+ "data_path": "action_result.parameter.exclude_type",
+ "data_type": "string",
+ "column_name": "Exclude Type"
+ },
+ {
+ "data_path": "action_result.parameter.ip_group_ids",
+ "data_type": "string",
+ "column_name": "Ip Group ID"
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "numeric",
+ "column_name": "Group ID",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.data.*.name",
+ "data_type": "string",
+ "column_name": "Group name",
+ "column_order": 1
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string",
+ "example_values": [
+ "DSTN_IP",
+ "DSTN_FQDN",
+ "DSTN_DOMAIN",
+ "DSTN_OTHER"
+ ],
+ "column_name": "Group type",
+ "column_order": 2
+ },
+ {
+ "data_path": "action_result.data.*.addresses",
+ "data_type": "string",
+ "example_values": [
+ "192.168.1.1"
+ ],
+ "column_name": "Destination addresses",
+ "column_order": 3
+ },
+ {
+ "data_path": "action_result.data.*.countries",
+ "data_type": "string",
+ "column_name": "Destination countries",
+ "column_order": 4
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string",
+ "column_name": "Description",
+ "column_order": 5
+ },
+ {
+ "data_path": "action_result.data.*.ipCategories",
+ "data_type": "string",
+ "example_values": [
+ "TRADING_BROKARAGE_INSURANCE"
+ ],
+ "column_name": "Destination categories",
+ "column_order": 6
+ },
+ {
+ "data_path": "action_result.data.*.isNonEditable",
+ "data_type": "boolean",
+ "example_values": [
+ true,
+ false
+ ],
+ "column_name": "Is editable",
+ "column_order": 7
+ },
+ {
+ "data_path": "action_result.data.*.creatorContext",
+ "data_type": "string",
+ "column_name": "Creator Context",
+ "column_order": 8
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "Retreived Destination Groups"
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Retreived Destination Groups"
+ ]
+ },
+ {
+ "data_path": "summary.message",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "edit destination group",
+ "identifier": "edit_destination_group",
+ "description": "Edit destination group",
+ "type": "generic",
+ "read_only": false,
+ "parameters": {
+ "ip_group_id": {
+ "description": "The unique identifier for the IP destination group",
+ "data_type": "numeric",
+ "required": true,
+ "primary": true,
+ "order": 0
+ },
+ "name": {
+ "description": "Destination IP group name",
+ "data_type": "string",
+ "primary": true,
+ "order": 1
+ },
+ "addresses": {
+ "description": "Comma seperated string of destination IP addresses, FQDNs, or wildcard FQDNs added to the group",
+ "data_type": "string",
+ "order": 2
+ },
+ "description": {
+ "description": "Additional information about the destination IP group.",
+ "data_type": "string",
+ "order": 3
+ },
+ "ip_categories": {
+ "description": "Destination IP address URL categories",
+ "data_type": "string",
+ "order": 4
+ },
+ "countries": {
+ "description": "Destination IP address countries. You can identify destinations based on the location of a server.",
+ "data_type": "string",
+ "order": 5
+ },
+ "is_non_editable": {
+ "description": "If set to true, the destination IP address group is non-editable. This field is applicable only to predefined IP address groups, which cannot be modified",
+ "data_type": "boolean",
+ "default": false,
+ "example_values": [
+ true,
+ false
+ ],
+ "order": 6
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "column_name": "Status",
+ "column_order": 2,
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.is_non_editable",
+ "data_type": "boolean",
+ "column_name": "Is Non Editable"
+ },
+ {
+ "data_path": "action_result.parameter.countries",
+ "data_type": "string",
+ "column_name": "Countries"
+ },
+ {
+ "data_path": "action_result.parameter.ip_categories",
+ "data_type": "string",
+ "column_name": "Ip Categories"
+ },
+ {
+ "data_path": "action_result.parameter.description",
+ "data_type": "string",
+ "column_name": "Description"
+ },
+ {
+ "data_path": "action_result.parameter.addresses",
+ "data_type": "string",
+ "column_name": "Addresses"
+ },
+ {
+ "data_path": "action_result.parameter.name",
+ "data_type": "string",
+ "column_name": "Name"
+ },
+ {
+ "data_path": "action_result.parameter.ip_group_id",
+ "data_type": "numeric",
+ "column_name": "IP Group Id"
+
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "numeric",
+ "column_name": "Group ID",
+ "column_order": 0
+ },
+ {
+ "data_path": "action_result.data.*.name",
+ "data_type": "string",
+ "column_name": "Group name",
+ "column_order": 1
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string",
+ "example_values": [
+ "DSTN_IP",
+ "DSTN_FQDN",
+ "DSTN_DOMAIN",
+ "DSTN_OTHER"
+ ],
+ "column_name": "Group type",
+ "column_order": 3
+ },
+ {
+ "data_path": "action_result.data.*.addresses",
+ "data_type": "string",
+ "example_values": [
+ "192.168.1.1"
+ ],
+ "column_name": "Destination addresses",
+ "column_order": 4
+ },
+ {
+ "data_path": "action_result.data.*.countries",
+ "data_type": "string",
+ "column_name": "Destination countries",
+ "column_order": 5
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string",
+ "column_name": "Description",
+ "column_order": 6
+ },
+ {
+ "data_path": "action_result.data.*.ipCategories",
+ "data_type": "string",
+ "example_values": [
+ "TRADING_BROKARAGE_INSURANCE"
+ ],
+ "column_name": "Destination categories",
+ "column_order": 7
+ },
+ {
+ "data_path": "action_result.data.*.isNonEditable",
+ "data_type": "boolean",
+ "example_values": [
+ true,
+ false
+ ],
+ "column_name": "Is editable",
+ "column_order": 8
+ },
+ {
+ "data_path": "action_result.data.*.creatorContext",
+ "data_type": "string",
+ "column_name": "Creator Context",
+ "column_order": 9
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "Destination group edited"
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Destination group edited"
+ ]
+ },
+ {
+ "data_path": "summary.message",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "delete destination group",
+ "identifier": "delete_destination_group",
+ "description": "Delete destination group",
+ "type": "generic",
+ "read_only": false,
+ "parameters": {
+ "ip_group_ids": {
+ "description": "A comma-separated list of unique identifiers for the IP destination groups",
+ "data_type": "string",
+ "primary": true,
+ "order": 0
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "column_name": "Status",
+ "column_order": 0,
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.ip_group_ids",
+ "data_type": "string",
+ "column_name": "IP Group Ids"
+ },
+ {
+ "data_path": "action_result.data.*.ip_group_ids",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "Destination group deleted"
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Destination group deleted"
+ ]
+ },
+ {
+ "data_path": "summary.message",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "get departments",
+ "identifier": "get_departments",
+ "description": "Get a list of departments",
+ "type": "investigate",
+ "read_only": true,
+ "parameters": {
+ "name": {
+ "description": "Filter by department name",
+ "data_type": "string",
+ "primary": true,
+ "order": 0
+ },
+ "page": {
+ "description": "Specifies the page offset",
+ "data_type": "numeric",
+ "primary": true,
+ "order": 1
+ },
+ "pageSize": {
+ "description": "Specifies the page size",
+ "default": 100,
+ "data_type": "numeric",
+ "primary": true,
+ "order": 2
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "column_name": "Status",
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.pageSize",
+ "data_type": "string",
+ "column_name": "Page Size"
+ },
+ {
+ "data_path": "action_result.parameter.page",
+ "data_type": "string",
+ "column_name": "Page"
+ },
+ {
+ "data_path": "action_result.parameter.name",
+ "data_type": "string",
+ "column_name": "Name"
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "numeric",
+ "column_name": "Department Id",
+ "column_order": 0
+
+ },
+ {
+ "data_path": "action_result.data.*.name",
+ "data_type": "string",
+ "column_name": "Department Name",
+ "column_order": 1
+ },
+ {
+ "data_path": "action_result.data.*.isNonEditable",
+ "data_type": "boolean",
+ "column_name": "Is editable",
+ "column_order": 2
+ },
+ {
+ "data_path": "action_result.summary",
+ "data_type": "string"
+ },
+ {
+ "data_path": "action_result.summary.message",
+ "data_type": "string",
+ "example_values": [
+ "Departments Retrieved"
+ ]
+ },
+ {
+ "data_path": "action_result.summary.total_deparments",
+ "data_type": "numeric",
+ "example_values": [
+ 97
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Departments Retrieved"
+ ]
+ },
+ {
+ "data_path": "summary.message",
+ "data_type": "string"
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
+ },
+ {
+ "action": "get category details",
+ "identifier": "get_category_details",
+ "description": "Get the urls and keywords of a category",
+ "type": "investigate",
+ "read_only": true,
+ "parameters": {
+ "category_ids": {
+ "description": "Comma seperated string of category id's to query",
+ "data_type": "string",
+ "order": 0,
+ "example_values": [
+ "CUSTOM_001, CUSTOM_002"
+ ],
+ "primary": true
+ }
+ },
+ "output": [
+ {
+ "data_path": "action_result.status",
+ "data_type": "string",
+ "example_values": [
+ "test success",
+ "test failed"
+ ]
+ },
+ {
+ "data_path": "action_result.parameter.category_ids",
+ "data_type": "string",
+ "example_values": [
+ "CUSTOM_001, CUSTOM_002"
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.configuredName",
+ "data_type": "string",
+ "example_values": [
+ "test Test-Caution"
+ ],
+ "column_order": 2,
+ "column_name": "Configured Name"
+ },
+ {
+ "data_path": "action_result.data.*.customCategory",
+ "data_type": "boolean",
+ "column_name": "Is Custom Category",
+ "column_order": 4,
+ "example_values": [
+ true,
+ false
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.keywords",
+ "data_type": "string",
+ "column_order": 5,
+ "column_name": "Category Keywords"
+ },
+ {
+ "data_path": "action_result.data.*.urls",
+ "data_type": "string",
+ "column_order": 6,
+ "column_name": "Category Urls"
+ },
+ {
+ "data_path": "action_result.data.*.customIpRangesCount",
+ "data_type": "numeric",
+ "example_values": [
+ 0
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.customUrlsCount",
+ "data_type": "numeric",
+ "example_values": [
+ 0
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.dbCategorizedUrls",
+ "data_type": "string",
+ "example_values": [
+ "test 6.5.3.2.4"
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.description",
+ "data_type": "string",
+ "column_name": "Description",
+ "column_order": 3,
+ "example_values": [
+ "test OTHER_RESTRICTED_WEBSITE_DESC"
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.editable",
+ "data_type": "boolean",
+ "column_name": "Editable",
+ "column_order": 7,
+ "example_values": [
+ true,
+ false
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.id",
+ "data_type": "string",
+ "column_name": "Category ID",
+ "column_order": 0,
+ "contains": [
+ "zscaler url category"
+ ],
+ "example_values": [
+ "test OTHER_RESTRICTED_WEBSITE"
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.ipRangesRetainingParentCategoryCount",
+ "data_type": "numeric",
+ "example_values": [
+ 0
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.scopes.*.Type",
+ "data_type": "string",
+ "example_values": [
+ "test ORGANIZATION"
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.type",
+ "data_type": "string",
+ "example_values": [
+ "test URL_CATEGORY"
+ ],
+ "column_order": 1,
+ "column_name": "Type"
+ },
+ {
+ "data_path": "action_result.data.*.urlsRetainingParentCategoryCount",
+ "data_type": "numeric",
+ "example_values": [
+ 0
+ ]
+ },
+ {
+ "data_path": "action_result.data.*.val",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "action_result.summary.total_categories",
+ "data_type": "numeric",
+ "example_values": [
+ 97
+ ]
+ },
+ {
+ "data_path": "action_result.message",
+ "data_type": "string",
+ "example_values": [
+ "Category details recieved"
+ ]
+ },
+ {
+ "data_path": "summary.total_objects",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ },
+ {
+ "data_path": "summary.total_objects_successful",
+ "data_type": "numeric",
+ "example_values": [
+ 1
+ ]
+ }
+ ],
+ "render": {
+ "type": "table"
+ },
+ "versions": "EQ(*)"
}
],
"pip_dependencies": {
diff --git a/zscaler_connector.py b/zscaler_connector.py
index dd0686c..038e8db 100644
--- a/zscaler_connector.py
+++ b/zscaler_connector.py
@@ -1,6 +1,6 @@
# File: zscaler_connector.py
#
-# Copyright (c) 2017-2023 Splunk Inc.
+# Copyright (c) 2017-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
#
#
# Phantom App imports
+import ipaddress
import json
import re
import time
@@ -104,10 +105,11 @@ def _validate_integer(self, action_result, parameter, key, allow_zero=False):
def _process_empty_response(self, response, action_result):
if response.status_code == 200 or response.status_code == 204:
return RetVal(phantom.APP_SUCCESS, {})
- return RetVal(action_result.set_status(
- phantom.APP_ERROR,
- "Status code : {}. Empty response and no information in the header".format(response.status_code)),
- None
+ return RetVal(
+ action_result.set_status(
+ phantom.APP_ERROR, "Status code : {}. Empty response and no information in the header".format(response.status_code)
+ ),
+ None,
)
def _process_html_response(self, response, action_result):
@@ -121,22 +123,24 @@ def _process_html_response(self, response, action_result):
for element in soup(["script", "style", "footer", "nav"]):
element.extract()
err_text = soup.text
- split_lines = err_text.split('\n')
+ split_lines = err_text.split("\n")
split_lines = [x.strip() for x in split_lines if x.strip()]
- err_text = '\n'.join(split_lines)
+ err_text = "\n".join(split_lines)
except Exception as e:
err_text = "Cannot parse err details"
self.debug_print("{}. Error: {}".format(err_text, e))
err_text = err_text
- msg = "Please check the asset configuration parameters (the base_url should not end with "\
+ msg = (
+ "Please check the asset configuration parameters (the base_url should not end with "
"/api/v1 e.g. https://admin.zscaler_instance.net)."
+ )
if len(err_text) <= 500:
msg += "Status Code: {0}. Data from server:\n{1}\n".format(status_code, err_text)
- msg = msg.replace('{', '{{').replace('}', '}}')
+ msg = msg.replace("{", "{{").replace("}", "}}")
return RetVal(action_result.set_status(phantom.APP_ERROR, msg), None)
def _process_json_response(self, r, action_result):
@@ -145,8 +149,12 @@ def _process_json_response(self, r, action_result):
try:
resp_json = r.json()
except Exception as e:
- return RetVal(action_result.set_status(phantom.APP_ERROR, "Unable to parse JSON response. Error: {0}"
- .format(self._get_err_msg_from_exception(e))), None)
+ return RetVal(
+ action_result.set_status(
+ phantom.APP_ERROR, "Unable to parse JSON response. Error: {0}".format(self._get_err_msg_from_exception(e))
+ ),
+ None,
+ )
# Please specify the status codes here
if 200 <= r.status_code < 399:
@@ -154,32 +162,30 @@ def _process_json_response(self, r, action_result):
# You should process the error returned in the json
try:
- msg = resp_json['message']
+ msg = resp_json["message"]
except Exception:
- msg = "Error from server. Status Code: {0} Data from server: {1}".format(
- r.status_code, r.text.replace('{', '{{').replace('}', '}}')
- )
+ msg = "Error from server. Status Code: {0} Data from server: {1}".format(r.status_code, r.text.replace("{", "{{").replace("}", "}}"))
return RetVal(action_result.set_status(phantom.APP_ERROR, msg), None)
def _process_response(self, r, action_result):
# store the r_text in debug data, it will get dumped in the logs if the action fails
- if hasattr(action_result, 'add_debug_data'):
- action_result.add_debug_data({'r_status_code': r.status_code})
- action_result.add_debug_data({'r_text': r.text})
- action_result.add_debug_data({'r_headers': r.headers})
+ if hasattr(action_result, "add_debug_data"):
+ action_result.add_debug_data({"r_status_code": r.status_code})
+ action_result.add_debug_data({"r_text": r.text})
+ action_result.add_debug_data({"r_headers": r.headers})
# Process each 'Content-Type' of response separately
# Process a json response
- if 'json' in r.headers.get('Content-Type', ''):
+ if "json" in r.headers.get("Content-Type", ""):
return self._process_json_response(r, action_result)
# Process an HTML response, Do this no matter what the api talks.
# There is a high chance of a PROXY in between phantom and the rest of
# world, in case of errors, PROXY's return HTML, this function parses
# the error and adds it to the action_result.
- if 'html' in r.headers.get('Content-Type', ''):
+ if "html" in r.headers.get("Content-Type", ""):
return self._process_html_response(r, action_result)
# it's not content-type that is to be parsed, handle an empty response
@@ -188,13 +194,13 @@ def _process_response(self, r, action_result):
# everything else is actually an error at this point
msg = "Can't process response from server. Status Code: {0} Data from server: {1}".format(
- r.status_code, r.text.replace('{', '{{').replace('}', '}}')
+ r.status_code, r.text.replace("{", "{{").replace("}", "}}")
)
return RetVal(action_result.set_status(phantom.APP_ERROR, msg), None)
def _is_ip(self, input_ip_address):
- """ Function that checks given address and return True if address is valid IPv4 or IPV6 address.
+ """Function that checks given address and return True if address is valid IPv4 or IPV6 address.
:param input_ip_address: IP address
:return: status (success/failure)
@@ -212,15 +218,16 @@ def _is_ip(self, input_ip_address):
return True
- def _make_rest_call(self, endpoint, action_result, headers=None, params=None,
- data=None, method="get", use_json=True, timeout=ZSCALER_DEFAULT_TIMEOUT):
+ def _make_rest_call(
+ self, endpoint, action_result, headers=None, params=None, data=None, method="get", use_json=True, timeout=ZSCALER_DEFAULT_TIMEOUT
+ ):
resp_json = None
if headers is None:
headers = {}
- if self.get_action_identifier() != 'submit_file':
+ if self.get_action_identifier() != "submit_file":
headers.update(self._headers)
try:
@@ -229,29 +236,16 @@ def _make_rest_call(self, endpoint, action_result, headers=None, params=None,
return RetVal(action_result.set_status(phantom.APP_ERROR, "Invalid method: {0}".format(method)), resp_json)
# Create a URL to connect to
- url = '{}{}'.format(self._base_url, endpoint)
+ url = "{}{}".format(self._base_url, endpoint)
try:
if use_json:
- r = request_func(
- url,
- json=data,
- headers=headers,
- params=params,
- timeout=timeout
- )
+ r = request_func(url, json=data, headers=headers, params=params, timeout=timeout)
else:
- r = request_func(
- url,
- data=data,
- headers=headers,
- params=params,
- timeout=timeout
- )
+ r = request_func(url, data=data, headers=headers, params=params, timeout=timeout)
except Exception as e:
error_message = self._get_err_msg_from_exception(e)
error_message = re.sub(ZSCALER_MATCH_REGEX, ZSCALER_REPLACE_REGEX, error_message)
- return RetVal(action_result.set_status(phantom.APP_ERROR, "Error Connecting to Zscaler server. {}"
- .format(error_message)), resp_json)
+ return RetVal(action_result.set_status(phantom.APP_ERROR, "Error Connecting to Zscaler server. {}".format(error_message)), resp_json)
self._response = r
@@ -277,16 +271,16 @@ def _make_rest_call_helper(self, *args, **kwargs):
if phantom.is_fail(ret_val):
if self._response is None:
return ret_val, response
- if self._response.status_code == 409 and self._retry_rest_call: # Lock not available
+ if self._response.status_code == 409 and self._retry_rest_call != 0: # Lock not available
# This basically just means we need to try again
self.debug_print("Error 409: Lock not available")
self.send_progress("Error 409: Lock not available: Retrying in 1 second")
time.sleep(1)
- self._retry_rest_call = False # make it to false to avoid extra rest call
+ self._retry_rest_call -= 1 # reduce the number of retries
return self._make_rest_call_helper(*args, **kwargs)
- if self._response.status_code == 429 and self._retry_rest_call: # Rate limit exceeded
+ if self._response.status_code == 429 and self._retry_rest_call != 0: # Rate limit exceeded
try:
- retry_time = self._response.json()['Retry-After']
+ retry_time = self._response.json()["Retry-After"]
except KeyError:
self.debug_print("KeyError")
return ret_val, response
@@ -296,7 +290,7 @@ def _make_rest_call_helper(self, *args, **kwargs):
return retry_time, response
self.send_progress("Exceeded rate limit: Retrying after {}".format(retry_time))
time.sleep(seconds_to_wait)
- self._retry_rest_call = False # make it to false to avoid extra rest call
+ self._retry_rest_call -= 1 # reduce the number of retries
return self._make_rest_call_helper(*args, **kwargs)
return ret_val, response
@@ -319,42 +313,25 @@ def _init_session(self):
try:
timestamp, obf_api_key = self._obfuscate_api_key(api_key)
except Exception:
- return self.set_status(
- phantom.APP_ERROR,
- "Error obfuscating API key"
- )
+ return self.set_status(phantom.APP_ERROR, "Error obfuscating API key")
- body = {
- 'apiKey': obf_api_key,
- 'username': username,
- 'password': password,
- 'timestamp': timestamp
- }
+ body = {"apiKey": obf_api_key, "username": username, "password": password, "timestamp": timestamp}
action_result = ActionResult()
- ret_val, _ = self._make_rest_call_helper(
- '/api/v1/authenticatedSession',
- action_result, data=body,
- method='post'
- )
+ ret_val, _ = self._make_rest_call_helper("/api/v1/authenticatedSession", action_result, data=body, method="post")
if phantom.is_fail(ret_val):
- self.debug_print('Error starting Zscaler session: {}'.format(action_result.get_message()))
- return self.set_status(
- phantom.APP_ERROR,
- 'Error starting Zscaler session: {}'.format(action_result.get_message())
- )
+ self.debug_print("Error starting Zscaler session: {}".format(action_result.get_message()))
+ return self.set_status(phantom.APP_ERROR, "Error starting Zscaler session: {}".format(action_result.get_message()))
else:
- self.save_progress('Successfully started Zscaler session')
- self._headers = {
- 'cookie': self._response.headers['Set-Cookie'].split(';')[0].strip()
- }
+ self.save_progress("Successfully started Zscaler session")
+ self._headers = {"cookie": self._response.headers["Set-Cookie"].split(";")[0].strip()}
return phantom.APP_SUCCESS
def _deinit_session(self):
action_result = ActionResult()
config = self.get_config()
- self._base_url = config['base_url'].rstrip('/')
- ret_val, response = self._make_rest_call_helper('/api/v1/authenticatedSession', action_result, method='delete')
+ self._base_url = config["base_url"].rstrip("/")
+ ret_val, response = self._make_rest_call_helper("/api/v1/authenticatedSession", action_result, method="delete")
if phantom.is_fail(ret_val):
self.debug_print("Deleting the authenticated session failed on the ZScaler server.")
@@ -378,58 +355,55 @@ def _filter_endpoints(self, action_result, to_add, existing, action, name):
if not endpoints:
summary = action_result.set_summary({})
- summary['updated'] = []
- summary['ignored'] = to_add
+ summary["updated"] = []
+ summary["ignored"] = to_add
return RetVal(action_result.set_status(phantom.APP_SUCCESS, msg), None)
return RetVal(phantom.APP_SUCCESS, endpoints)
def _get_blocklist(self, action_result):
- return self._make_rest_call_helper('/api/v1/security/advanced', action_result)
+ return self._make_rest_call_helper("/api/v1/security/advanced", action_result)
def _check_blocklist(self, action_result, endpoints, action):
ret_val, response = self._get_blocklist(action_result)
if phantom.is_fail(ret_val):
return RetVal(ret_val, None)
- blocklist = response.get('blacklistUrls', [])
+ blocklist = response.get("blacklistUrls", [])
- return self._filter_endpoints(action_result, endpoints, blocklist, action, 'Blocklist')
+ return self._filter_endpoints(action_result, endpoints, blocklist, action, "Blocklist")
def _amend_blocklist(self, action_result, endpoints, action):
ret_val, filtered_endpoints = self._check_blocklist(action_result, endpoints, action)
if phantom.is_fail(ret_val) or filtered_endpoints is None:
return ret_val
- params = {'action': action}
- data = {
- "blacklistUrls": filtered_endpoints
- }
+ params = {"action": action}
+ data = {"blacklistUrls": filtered_endpoints}
ret_val, response = self._make_rest_call_helper(
- '/api/v1/security/advanced/blacklistUrls', action_result, params=params,
- data=data, method="post"
+ "/api/v1/security/advanced/blacklistUrls", action_result, params=params, data=data, method="post"
)
if phantom.is_fail(ret_val) and self._response.status_code != 204:
return ret_val
summary = action_result.set_summary({})
- summary['updated'] = filtered_endpoints
- summary['ignored'] = list(set(endpoints) - set(filtered_endpoints))
+ summary["updated"] = filtered_endpoints
+ summary["ignored"] = list(set(endpoints) - set(filtered_endpoints))
# Encode the unicode IP or URL strings
- summary['updated'] = [element for element in summary['updated']]
- summary['ignored'] = [element for element in summary['ignored']]
+ summary["updated"] = [element for element in summary["updated"]]
+ summary["ignored"] = [element for element in summary["ignored"]]
return action_result.set_status(phantom.APP_SUCCESS)
def _get_allowlist(self, action_result):
- return self._make_rest_call_helper('/api/v1/security', action_result)
+ return self._make_rest_call_helper("/api/v1/security", action_result)
def _check_allowlist(self, action_result, endpoints, action):
ret_val, response = self._get_allowlist(action_result)
if phantom.is_fail(ret_val):
return RetVal(ret_val, None)
- allowlist = response.get('whitelistUrls', [])
+ allowlist = response.get("whitelistUrls", [])
self._allowlist = allowlist
- return self._filter_endpoints(action_result, endpoints, allowlist, action, 'Allowlist')
+ return self._filter_endpoints(action_result, endpoints, allowlist, action, "Allowlist")
def _amend_allowlist(self, action_result, endpoints, action):
ret_val, filtered_endpoints = self._check_allowlist(action_result, endpoints, action)
@@ -441,44 +415,34 @@ def _amend_allowlist(self, action_result, endpoints, action):
else:
to_add_endpoints = list(set(self._allowlist) - set(filtered_endpoints))
- data = {
- "whitelistUrls": to_add_endpoints
- }
- ret_val, response = self._make_rest_call_helper(
- '/api/v1/security', action_result,
- data=data, method='put'
- )
+ data = {"whitelistUrls": to_add_endpoints}
+ ret_val, response = self._make_rest_call_helper("/api/v1/security", action_result, data=data, method="put")
if phantom.is_fail(ret_val):
return ret_val
action_result.add_data(response)
summary = action_result.set_summary({})
- summary['updated'] = filtered_endpoints
- summary['ignored'] = list(set(endpoints) - set(filtered_endpoints))
+ summary["updated"] = filtered_endpoints
+ summary["ignored"] = list(set(endpoints) - set(filtered_endpoints))
# Encode the unicode IP or URL strings
- summary['updated'] = [element for element in summary['updated']]
- summary['ignored'] = [element for element in summary['ignored']]
+ summary["updated"] = [element for element in summary["updated"]]
+ summary["ignored"] = [element for element in summary["ignored"]]
return action_result.set_status(phantom.APP_SUCCESS)
def _get_category(self, action_result, category):
- ret_val, response = self._make_rest_call_helper('/api/v1/urlCategories', action_result)
+ ret_val, response = self._make_rest_call_helper("/api/v1/urlCategories", action_result)
if phantom.is_fail(ret_val):
return ret_val, response
for cat in response:
- if cat.get('configuredName', None) == category:
+ if cat.get("configuredName", None) == category:
return RetVal(phantom.APP_SUCCESS, cat)
for cat in response:
- if cat['id'] == category:
+ if cat["id"] == category:
return RetVal(phantom.APP_SUCCESS, cat)
- return RetVal(
- action_result.set_status(
- phantom.APP_ERROR, "Unable to find category"
- ),
- None
- )
+ return RetVal(action_result.set_status(phantom.APP_ERROR, "Unable to find category"), None)
def _check_category(self, action_result, endpoints, category, action):
ret_val, response = self._get_category(action_result, category)
@@ -486,159 +450,153 @@ def _check_category(self, action_result, endpoints, category, action):
return ret_val, response
self._category = response
- urls = response.get('dbCategorizedUrls', [])
+ urls = response.get("dbCategorizedUrls", [])
- return self._filter_endpoints(action_result, endpoints, urls, action, 'Category')
+ return self._filter_endpoints(action_result, endpoints, urls, action, "Category")
def _amend_category(self, action_result, endpoints, category, action):
ret_val, filtered_endpoints = self._check_category(action_result, endpoints, category, action)
if phantom.is_fail(ret_val) or filtered_endpoints is None:
return ret_val
- params = {'action': action }
+ params = {"action": action}
data = {
- "configuredName": self._category.get('configuredName'),
+ "configuredName": self._category.get("configuredName"),
"keywordsRetainingParentCategory": self._category.get("keywordsRetainingParentCategory", []),
"urls": [],
- "dbCategorizedUrls": filtered_endpoints
+ "dbCategorizedUrls": filtered_endpoints,
}
ret_val, response = self._make_rest_call_helper(
- '/api/v1/urlCategories/{}'.format(self._category['id']),
- action_result, data=data, method='put', params=params, timeout=None
+ "/api/v1/urlCategories/{}".format(self._category["id"]), action_result, data=data, method="put", params=params, timeout=None
)
if phantom.is_fail(ret_val):
return ret_val
action_result.add_data(response)
summary = action_result.set_summary({})
- summary['updated'] = filtered_endpoints
- summary['ignored'] = list(set(endpoints) - set(filtered_endpoints))
+ summary["updated"] = filtered_endpoints
+ summary["ignored"] = list(set(endpoints) - set(filtered_endpoints))
# Encode the unicode IP or URL strings
- summary['updated'] = [element for element in summary['updated']]
- summary['ignored'] = [element for element in summary['ignored']]
+ summary["updated"] = [element for element in summary["updated"]]
+ summary["ignored"] = [element for element in summary["ignored"]]
return action_result.set_status(phantom.APP_SUCCESS)
def _block_endpoint(self, action_result, endpoints, category):
list_endpoints = list()
- list_endpoints = [x.strip() for x in endpoints.split(',')]
+ list_endpoints = [x.strip() for x in endpoints.split(",") if x.strip()]
endpoints = list(filter(None, list_endpoints))
endpoints = self._truncate_protocol(endpoints)
- if self.get_action_identifier() in ['block_url']:
+ if self.get_action_identifier() in ["block_url"]:
ret_val = self._check_for_overlength(action_result, endpoints)
if phantom.is_fail(ret_val):
return ret_val
if category is None:
- return self._amend_blocklist(action_result, endpoints, 'ADD_TO_LIST')
+ return self._amend_blocklist(action_result, endpoints, "ADD_TO_LIST")
else:
- return self._amend_category(action_result, endpoints, category, 'ADD_TO_LIST')
+ return self._amend_category(action_result, endpoints, category, "ADD_TO_LIST")
def _unblock_endpoint(self, action_result, endpoints, category):
list_endpoints = list()
- list_endpoints = [x.strip() for x in endpoints.split(',')]
+ list_endpoints = [x.strip() for x in endpoints.split(",") if x.strip()]
endpoints = list(filter(None, list_endpoints))
endpoints = self._truncate_protocol(endpoints)
- if self.get_action_identifier() in ['unblock_url']:
+ if self.get_action_identifier() in ["unblock_url"]:
ret_val = self._check_for_overlength(action_result, endpoints)
if phantom.is_fail(ret_val):
return ret_val
if category is None:
- return self._amend_blocklist(action_result, endpoints, 'REMOVE_FROM_LIST')
+ return self._amend_blocklist(action_result, endpoints, "REMOVE_FROM_LIST")
else:
- return self._amend_category(action_result, endpoints, category, 'REMOVE_FROM_LIST')
+ return self._amend_category(action_result, endpoints, category, "REMOVE_FROM_LIST")
def _handle_block_ip(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._block_endpoint(action_result, param['ip'], param.get('url_category'))
+ return self._block_endpoint(action_result, param["ip"], param.get("url_category"))
def _handle_block_url(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._block_endpoint(action_result, param['url'], param.get('url_category'))
+ return self._block_endpoint(action_result, param["url"], param.get("url_category"))
def _handle_unblock_ip(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._unblock_endpoint(action_result, param['ip'], param.get('url_category'))
+ return self._unblock_endpoint(action_result, param["ip"], param.get("url_category"))
def _handle_unblock_url(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._unblock_endpoint(action_result, param['url'], param.get('url_category'))
+ return self._unblock_endpoint(action_result, param["url"], param.get("url_category"))
def _allowlist_endpoint(self, action_result, endpoints, category):
list_endpoints = list()
- list_endpoints = [x.strip() for x in endpoints.split(',')]
+ list_endpoints = [x.strip() for x in endpoints.split(",")]
endpoints = list(filter(None, list_endpoints))
endpoints = self._truncate_protocol(endpoints)
- if self.get_action_identifier() in ['allow_url']:
+ if self.get_action_identifier() in ["allow_url"]:
ret_val = self._check_for_overlength(action_result, endpoints)
if phantom.is_fail(ret_val):
return ret_val
if category is None:
- return self._amend_allowlist(action_result, endpoints, 'ADD_TO_LIST')
+ return self._amend_allowlist(action_result, endpoints, "ADD_TO_LIST")
else:
- return self._amend_category(action_result, endpoints, category, 'ADD_TO_LIST')
+ return self._amend_category(action_result, endpoints, category, "ADD_TO_LIST")
def _unallow_endpoint(self, action_result, endpoints, category):
list_endpoints = list()
- list_endpoints = [x.strip() for x in endpoints.split(',')]
+ list_endpoints = [x.strip() for x in endpoints.split(",")]
endpoints = list(filter(None, list_endpoints))
endpoints = self._truncate_protocol(endpoints)
- if self.get_action_identifier() in ['unallow_url']:
+ if self.get_action_identifier() in ["unallow_url"]:
ret_val = self._check_for_overlength(action_result, endpoints)
if phantom.is_fail(ret_val):
return ret_val
if category is None:
- return self._amend_allowlist(action_result, endpoints, 'REMOVE_FROM_LIST')
+ return self._amend_allowlist(action_result, endpoints, "REMOVE_FROM_LIST")
else:
- return self._amend_category(action_result, endpoints, category, 'REMOVE_FROM_LIST')
+ return self._amend_category(action_result, endpoints, category, "REMOVE_FROM_LIST")
def _handle_allow_ip(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._allowlist_endpoint(action_result, param['ip'], param.get('url_category'))
+ return self._allowlist_endpoint(action_result, param["ip"], param.get("url_category"))
def _handle_allow_url(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._allowlist_endpoint(action_result, param['url'], param.get('url_category'))
+ return self._allowlist_endpoint(action_result, param["url"], param.get("url_category"))
def _handle_unallow_ip(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._unallow_endpoint(action_result, param['ip'], param.get('url_category'))
+ return self._unallow_endpoint(action_result, param["ip"], param.get("url_category"))
def _handle_unallow_url(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- return self._unallow_endpoint(action_result, param['url'], param.get('url_category'))
+ return self._unallow_endpoint(action_result, param["url"], param.get("url_category"))
def _lookup_endpoint(self, action_result, endpoints):
if not endpoints:
action_result.set_status(phantom.APP_ERROR, "Please provide valid list of URL(s)")
- ret_val, response = self._make_rest_call_helper(
- '/api/v1/urlLookup', action_result,
- data=endpoints, method='post'
- )
+ ret_val, response = self._make_rest_call_helper("/api/v1/urlLookup", action_result, data=endpoints, method="post")
if phantom.is_fail(ret_val):
return ret_val
- ret_val, blocklist_response = self._make_rest_call_helper(
- '/api/v1/security/advanced', action_result, method='get'
- )
+ ret_val, blocklist_response = self._make_rest_call_helper("/api/v1/security/advanced", action_result, method="get")
if phantom.is_fail(ret_val):
return ret_val
for e in endpoints:
- if e in blocklist_response.get('blacklistUrls', []):
- [response[i].update({"blocklisted": True}) for i, item in enumerate(response) if item['url'] == e]
+ if e in blocklist_response.get("blacklistUrls", []):
+ [response[i].update({"blocklisted": True}) for i, item in enumerate(response) if item["url"] == e]
else:
- [response[i].update({"blocklisted": False}) for i, item in enumerate(response) if item['url'] == e]
+ [response[i].update({"blocklisted": False}) for i, item in enumerate(response) if item["url"] == e]
action_result.update_data(response)
@@ -653,16 +611,14 @@ def _handle_get_report(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- file_hash = param['file_hash']
+ file_hash = param["file_hash"]
- ret_val, sandbox_report = self._make_rest_call_helper('/api/v1/sandbox/report/{0}?details=full'
- .format(file_hash), action_result)
+ ret_val, sandbox_report = self._make_rest_call_helper("/api/v1/sandbox/report/{0}?details=full".format(file_hash), action_result)
if phantom.is_fail(ret_val):
return action_result.get_status()
- if sandbox_report.get(ZSCALER_JSON_FULL_DETAILS) and ZSCLAER_ERR_MD5_UNKNOWN_MSG in sandbox_report.get(
- ZSCALER_JSON_FULL_DETAILS):
+ if sandbox_report.get(ZSCALER_JSON_FULL_DETAILS) and ZSCLAER_ERR_MD5_UNKNOWN_MSG in sandbox_report.get(ZSCALER_JSON_FULL_DETAILS):
return action_result.set_status(phantom.APP_ERROR, sandbox_report.get(ZSCALER_JSON_FULL_DETAILS))
action_result.add_data(sandbox_report)
@@ -680,49 +636,50 @@ def _handle_submit_file(self, param):
if not (self._sandbox_api_token and self._sandbox_base_url):
return action_result.set_status(
- phantom.APP_ERROR, "Please provide ZScaler Sandbox Base URL and API token to submit the file to Sandbox")
+ phantom.APP_ERROR, "Please provide ZScaler Sandbox Base URL and API token to submit the file to Sandbox"
+ )
self._base_url = self._sandbox_base_url
try:
- file_id = param['vault_id']
+ file_id = param["vault_id"]
success, msg, file_info = phantom_rules.vault_info(vault_id=file_id)
file_info = list(file_info)[0]
except IndexError:
- return action_result.set_status(phantom.APP_ERROR, 'Vault file could not be found with supplied Vault ID')
+ return action_result.set_status(phantom.APP_ERROR, "Vault file could not be found with supplied Vault ID")
except Exception as e:
err_msg = self._get_err_msg_from_exception(e)
self.debug_print("Vault ID not valid. Error: {}".format(err_msg))
- return action_result.set_status(phantom.APP_ERROR, 'Vault ID not valid')
+ return action_result.set_status(phantom.APP_ERROR, "Vault ID not valid")
- params = {
- 'force': 1 if param.get('force', False) else 0,
- 'api_token': self._sandbox_api_token
- }
+ params = {"force": 1 if param.get("force", False) else 0, "api_token": self._sandbox_api_token}
- with open(file_info.get('path'), 'rb') as f:
+ with open(file_info.get("path"), "rb") as f:
data = f.read()
- ret_val, resp_json = self._make_rest_call_helper('/zscsb/submit',
- action_result, params=params, data=data, method='post', use_json=False)
+ ret_val, resp_json = self._make_rest_call_helper("/zscsb/submit", action_result, params=params, data=data, method="post", use_json=False)
if phantom.is_fail(ret_val):
return action_result.get_status()
- if resp_json.get('code') != 200:
- return action_result.set_status(phantom.APP_ERROR,
- "Status code: {} Details: {}. Please make sure ZScaler Sandbox Base URL and API token are configured correctly"
- .format(resp_json.get('code'), resp_json.get('message')))
+ if resp_json.get("code") != 200:
+ return action_result.set_status(
+ phantom.APP_ERROR,
+ "Status code: {} Details: {}. Please make sure ZScaler Sandbox Base URL and API token are configured correctly".format(
+ resp_json.get("code"), resp_json.get("message")
+ ),
+ )
action_result.add_data(resp_json)
- if resp_json.get('message') == '/submit response OK':
+ if resp_json.get("message") == "/submit response OK":
msg = ZSCALER_SANDBOX_SUBMIT_FILE_MSG
else:
- if resp_json.get('message').lower() != resp_json.get('sandboxSubmission').lower():
- msg = 'Status Code: {}. Data from server: {}. {}.'.format(resp_json.get('code'), resp_json.get('sandboxSubmission'),
- resp_json.get('message'))
+ if resp_json.get("message").lower() != resp_json.get("sandboxSubmission").lower():
+ msg = "Status Code: {}. Data from server: {}. {}.".format(
+ resp_json.get("code"), resp_json.get("sandboxSubmission"), resp_json.get("message")
+ )
else:
- msg = 'Status Code: {}. Data from server: {}'.format(resp_json.get('code'), resp_json.get('message'))
+ msg = "Status Code: {}. Data from server: {}".format(resp_json.get("code"), resp_json.get("message"))
return action_result.set_status(phantom.APP_SUCCESS, msg)
@@ -734,16 +691,25 @@ def _handle_list_url_categories(self, param):
"""
action_result = self.add_action_result(ActionResult(dict(param)))
- ret_val, list_url_categories = self._make_rest_call_helper('/api/v1/urlCategories', action_result)
+ ret_val, list_url_categories = self._make_rest_call_helper("/api/v1/urlCategories", action_result)
if phantom.is_fail(ret_val):
return action_result.get_status()
+ get_ids_and_names_only = param.get("get_ids_and_names_only", False)
+
for url_category in list_url_categories:
- action_result.add_data(url_category)
+ if get_ids_and_names_only:
+ category_lite = {}
+ category_lite["id"] = url_category["id"]
+ if "configuredName" in url_category:
+ category_lite["configuredName"] = url_category["configuredName"]
+ action_result.add_data(category_lite)
+ else:
+ action_result.add_data(url_category)
summary = action_result.update_summary({})
- summary['total_url_categories'] = action_result.get_data_size()
+ summary["total_url_categories"] = action_result.get_data_size()
return action_result.set_status(phantom.APP_SUCCESS)
@@ -751,7 +717,7 @@ def _handle_lookup_ip(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
list_endpoints = list()
- list_endpoints = [x.strip() for x in param['ip'].split(',')]
+ list_endpoints = [x.strip() for x in param["ip"].split(",")]
endpoints = list(filter(None, list_endpoints))
return self._lookup_endpoint(action_result, endpoints)
@@ -761,7 +727,7 @@ def _handle_lookup_url(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
list_endpoints = list()
- list_endpoints = [x.strip() for x in param['url'].split(',')]
+ list_endpoints = [x.strip() for x in param["url"].split(",")]
endpoints = list(filter(None, list_endpoints))
endpoints = self._truncate_protocol(endpoints)
@@ -780,9 +746,9 @@ def _truncate_protocol(self, endpoints):
"""
for i in range(len(endpoints)):
if endpoints[i].startswith("http://"):
- endpoints[i] = endpoints[i][(len("http://")):]
+ endpoints[i] = endpoints[i][(len("http://")) :] # noqa
elif endpoints[i].startswith("https://"):
- endpoints[i] = endpoints[i][(len("https://")):]
+ endpoints[i] = endpoints[i][(len("https://")) :] # noqa
return endpoints
@@ -793,8 +759,10 @@ def _check_for_overlength(self, action_result, endpoints):
"""
for url in endpoints:
if len(url) > 1024:
- return action_result.set_status(phantom.APP_ERROR,
- "Please provide valid comma-separated values in the action parameter. Max allowed length for each value is 1024.")
+ return action_result.set_status(
+ phantom.APP_ERROR,
+ "Please provide valid comma-separated values in the action parameter. Max allowed length for each value is 1024.",
+ )
return phantom.APP_SUCCESS
def _handle_get_admin_users(self, param):
@@ -805,31 +773,31 @@ def _handle_get_admin_users(self, param):
"""
action_result = self.add_action_result(ActionResult(dict(param)))
- ret_val, limit = self._validate_integer(action_result, param.get('limit', ZSCALER_MAX_PAGESIZE), ZSCALER_LIMIT_KEY)
+ ret_val, limit = self._validate_integer(action_result, param.get("limit", ZSCALER_MAX_PAGESIZE), ZSCALER_LIMIT_KEY)
if phantom.is_fail(ret_val):
return action_result.get_status()
params = {}
admin_users = []
- params['page'] = 1
+ params["page"] = 1
while True:
if limit < ZSCALER_MAX_PAGESIZE:
- params['pageSize'] = limit
+ params["pageSize"] = limit
else:
- params['pageSize'] = ZSCALER_MAX_PAGESIZE
- ret_val, get_admin_users = self._make_rest_call_helper('/api/v1/adminUsers', action_result, params=params)
+ params["pageSize"] = ZSCALER_MAX_PAGESIZE
+ ret_val, get_admin_users = self._make_rest_call_helper("/api/v1/adminUsers", action_result, params=params)
if phantom.is_fail(ret_val):
return action_result.get_status()
for admin_user in get_admin_users:
admin_users.append(admin_user)
- limit = limit - params['pageSize']
+ limit = limit - params["pageSize"]
if limit <= 0 or len(get_admin_users) == 0:
break
- params['page'] += 1
+ params["page"] += 1
for user in admin_users:
action_result.add_data(user)
summary = action_result.update_summary({})
- summary['total_admin_users'] = action_result.get_data_size()
+ summary["total_admin_users"] = action_result.get_data_size()
return action_result.set_status(phantom.APP_SUCCESS)
@@ -850,27 +818,22 @@ def _handle_get_users(self, param):
if not param:
return action_result.set_status(phantom.APP_ERROR, "No filters provided")
- ret_val, limit = self._validate_integer(action_result, param.get('limit', ZSCALER_MAX_PAGESIZE), ZSCALER_LIMIT_KEY)
+ ret_val, limit = self._validate_integer(action_result, param.get("limit", ZSCALER_MAX_PAGESIZE), ZSCALER_LIMIT_KEY)
if phantom.is_fail(ret_val):
return action_result.get_status()
- params = {
- "name": param.get('name'),
- "dept": param.get('dept'),
- "group": param.get('group'),
- 'page': 1
- }
+ params = {"name": param.get("name"), "dept": param.get("dept"), "group": param.get("group"), "page": 1}
users = []
while True:
- params['pageSize'] = min(limit, ZSCALER_MAX_PAGESIZE)
- ret_val, get_users = self._make_rest_call_helper('/api/v1/users', action_result, params=params, timeout=None)
+ params["pageSize"] = min(limit, ZSCALER_MAX_PAGESIZE)
+ ret_val, get_users = self._make_rest_call_helper("/api/v1/users", action_result, params=params, timeout=None)
if phantom.is_fail(ret_val):
return action_result.get_status()
for user in get_users:
users.append(user)
- limit = limit - params['pageSize']
+ limit = limit - params["pageSize"]
if limit <= 0 or len(get_users) == 0:
break
- params['page'] += 1
+ params["page"] += 1
# Add the response into the data section
for user in users:
@@ -878,7 +841,7 @@ def _handle_get_users(self, param):
# Add a dictionary that is made up of the most important values from data into the summary
summary = action_result.update_summary({})
- summary['total_users'] = action_result.get_data_size()
+ summary["total_users"] = action_result.get_data_size()
return action_result.set_status(phantom.APP_SUCCESS)
@@ -891,29 +854,29 @@ def _handle_get_groups(self, param):
self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
action_result = self.add_action_result(ActionResult(dict(param)))
- ret_val, limit = self._validate_integer(action_result, param.get('limit', ZSCALER_MAX_PAGESIZE), ZSCALER_LIMIT_KEY)
+ ret_val, limit = self._validate_integer(action_result, param.get("limit", ZSCALER_MAX_PAGESIZE), ZSCALER_LIMIT_KEY)
if phantom.is_fail(ret_val):
return action_result.get_status()
- params = {"search": param.get('search')}
+ params = {"search": param.get("search")}
groups = []
- params['page'] = 1
+ params["page"] = 1
while True:
- params['pageSize'] = min(limit, ZSCALER_MAX_PAGESIZE)
- ret_val, get_groups = self._make_rest_call_helper('/api/v1/groups', action_result, params=params)
+ params["pageSize"] = min(limit, ZSCALER_MAX_PAGESIZE)
+ ret_val, get_groups = self._make_rest_call_helper("/api/v1/groups", action_result, params=params)
if phantom.is_fail(ret_val):
return action_result.get_status()
for group in get_groups:
groups.append(group)
- limit = limit - params['pageSize']
+ limit = limit - params["pageSize"]
if limit <= 0 or len(get_groups) == 0:
break
- params['page'] += 1
+ params["page"] += 1
for group in groups:
action_result.add_data(group)
summary = action_result.update_summary({})
- summary['total_groups'] = action_result.get_data_size()
+ summary["total_groups"] = action_result.get_data_size()
return action_result.set_status(phantom.APP_SUCCESS)
@@ -928,29 +891,29 @@ def _handle_add_group_user(self, param):
action_result = self.add_action_result(ActionResult(dict(param)))
- user_id = param['user_id']
- group_id = param['group_id']
- ret_val, user_response = self._make_rest_call_helper(f'/api/v1/users/{user_id}', action_result)
+ user_id = param["user_id"]
+ group_id = param["group_id"]
+ ret_val, user_response = self._make_rest_call_helper(f"/api/v1/users/{user_id}", action_result)
if phantom.is_fail(ret_val):
return action_result.get_status()
- ret_val, group_response = self._make_rest_call_helper(f'/api/v1/groups/{group_id}', action_result)
+ ret_val, group_response = self._make_rest_call_helper(f"/api/v1/groups/{group_id}", action_result)
if phantom.is_fail(ret_val):
return action_result.get_status()
summary = action_result.update_summary({})
- if group_response in user_response['groups']:
- summary['message'] = "User already in group"
+ if group_response in user_response["groups"]:
+ summary["message"] = "User already in group"
action_result.add_data(group_response)
return action_result.set_status(phantom.APP_SUCCESS, "User already in group")
- user_response['groups'].append(group_response)
+ user_response["groups"].append(group_response)
data = user_response
- ret_val, response = self._make_rest_call_helper(f'/api/v1/users/{user_id}', action_result, data=data, method='put')
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/users/{user_id}", action_result, data=data, method="put")
if phantom.is_fail(ret_val):
return action_result.get_status()
action_result.add_data(response)
- summary['message'] = "User successfully added to group"
+ summary["message"] = "User successfully added to group"
return action_result.set_status(phantom.APP_SUCCESS)
def _handle_remove_group_user(self, param):
@@ -963,33 +926,533 @@ def _handle_remove_group_user(self, param):
self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
action_result = self.add_action_result(ActionResult(dict(param)))
- user_id = param['user_id']
- group_id = param['group_id']
- ret_val, user_response = self._make_rest_call_helper(f'/api/v1/users/{user_id}', action_result)
+ user_id = param["user_id"]
+ group_id = param["group_id"]
+ ret_val, user_response = self._make_rest_call_helper(f"/api/v1/users/{user_id}", action_result)
if phantom.is_fail(ret_val):
return action_result.get_status()
summary = action_result.update_summary({})
- if group_id not in [item['id'] for item in user_response['groups']]:
- summary['message'] = "User already removed from group"
+ if group_id not in [item["id"] for item in user_response["groups"]]:
+ summary["message"] = "User already removed from group"
action_result.add_data(user_response)
return action_result.set_status(phantom.APP_SUCCESS, "User already removed from group")
- for index, group in enumerate(user_response['groups']):
- if group_id == group['id']:
- user_response['groups'].pop(index)
+ for index, group in enumerate(user_response["groups"]):
+ if group_id == group["id"]:
+ user_response["groups"].pop(index)
data = user_response
- ret_val, response = self._make_rest_call_helper(f'/api/v1/users/{user_id}', action_result, data=data, method='put')
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/users/{user_id}", action_result, data=data, method="put")
+
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ action_result.add_data(response)
+ summary["message"] = "User removed from group"
+
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_get_allowlist(self, param):
+ """
+ This action is used to get the default allowlist in zscaler
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ ret_val, response = self._get_allowlist(action_result)
+ if phantom.is_fail(ret_val):
+ return RetVal(ret_val, None)
+
+ allowlist = response.get("whitelistUrls", [])
+ for allowed in allowlist:
+ action_result.add_data({"url": allowed})
+ summary = action_result.update_summary({})
+ summary["total_allowlist_items"] = action_result.get_data_size()
+ summary["message"] = "allowlist retrieved"
+
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _is_ip_address(self, address):
+ try:
+ ipaddress.ip_address(address)
+ return True
+ except ValueError:
+ return False
+
+ def _handle_get_denylist(self, param):
+ """
+ This action is used to get the denylist in zscaler
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ ret_val, response = self._get_blocklist(action_result)
+ if phantom.is_fail(ret_val):
+ return RetVal(ret_val, None)
+
+ filter = param.get("filter")
+ query = param.get("query")
+
+ summary = action_result.update_summary({})
+ summary["message"] = "Denylist retrieved"
+
+ blocklist = response.get("blacklistUrls", [])
+ for blocked in blocklist:
+ is_ip = self._is_ip_address(blocked)
+ if filter == "ip" and not is_ip:
+ continue
+ if filter == "url" and is_ip:
+ continue
+ if query and not re.fullmatch(query, blocked):
+ continue
+ action_result.add_data({"url": blocked})
+
+ summary["total_denylist_items"] = action_result.get_data_size()
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_update_user(self, param):
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+ user_id = param["user_id"]
+
+ try:
+ data = json.loads(param.get("user", "{}"))
+ except Exception as e:
+ return action_result.set_status(phantom.APP_ERROR, "User object needs to be valid json: {}".format(e))
+
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/users/{user_id}", action_result, data=data, method="put")
+
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ self.debug_print(response)
+ action_result.add_data(response)
+ summary = action_result.update_summary({})
+ summary["message"] = "User updated"
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _get_category_details(self, id, action_result):
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/urlCategories/{id}", action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status(), None
+ return phantom.APP_SUCCESS, response
+
+ def _add_to_category(self, data, parent_data, cat_details, category_id, action_result):
+ new_data = cat_details.get("urls", [])
+ new_data.extend(data)
+ if new_data:
+ cat_details["urls"] = new_data
+
+ new_parent_data = cat_details.get("dbCategorizedUrls", [])
+ new_parent_data.extend(parent_data)
+ if new_parent_data:
+ cat_details["dbCategorizedUrls"] = new_parent_data
+
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/urlCategories/{category_id}", action_result, data=cat_details, method="put")
+ return ret_val, response
+
+ def _handle_add_category_url(self, param):
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+ category_id = param["category_id"]
+
+ ret_val, category_details = self._get_category_details(category_id, action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ is_custom_category = category_details.get("customCategory", False)
+
+ if not is_custom_category:
+ action_result.set_status(phantom.APP_ERROR, "Category with {0} is a default category, which cannot be modified".format(category_id))
+ return action_result.get_status()
+
+ urls = param.get("urls", "")
+ urls_list = [item.strip() for item in urls.split(",") if item.strip()]
+ retaining_parent_category_url = param.get("retaining-parent-category-url", "")
+ parent_urls = [item.strip() for item in retaining_parent_category_url.split(",") if item.strip()]
+ ret_val, response = self._add_to_category(urls_list, parent_urls, category_details, category_id, action_result)
if phantom.is_fail(ret_val):
return action_result.get_status()
action_result.add_data(response)
- summary['message'] = "User removed from group"
+ summary = action_result.update_summary({})
+ summary["message"] = "Category urls updated"
return action_result.set_status(phantom.APP_SUCCESS)
+ def _handle_add_category_ip(self, param):
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+ category_id = param["category_id"]
+
+ ret_val, category_details = self._get_category_details(category_id, action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ ips = param.get("ips", "")
+ ips_list = [item.strip() for item in ips.split(",") if item.strip()]
+ retaining_parent_category_ip = param.get("retaining-parent-category-ip", "")
+ parent_ips = [item.strip() for item in retaining_parent_category_ip.split(",") if item.strip()]
+ ret_val, response = self._add_to_category(ips_list, parent_ips, category_details, category_id, action_result)
+
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ action_result.add_data(response)
+ summary = action_result.update_summary({})
+ summary["message"] = "Category ips updated"
+
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_remove_category_ip(self, param):
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+ category_id = param["category_id"]
+
+ ret_val, category_details = self._get_category_details(category_id, action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ ips = param.get("ips", "")
+ ips_to_remove = [item.strip() for item in ips.split(",") if item.strip()]
+ retaining_parent_category_ips = param.get("retaining-parent-category-ip", "")
+ parent_ips_to_remove = [item.strip() for item in retaining_parent_category_ips.split(",") if item.strip()]
+
+ ret_val, response = self._remove_from_category(ips_to_remove, parent_ips_to_remove, category_details, category_id, action_result)
+
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ action_result.add_data(response)
+ summary = action_result.update_summary({})
+ summary["message"] = "Category ips removed"
+
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _remove_from_category(self, data, parent_data, cat_details, category_id, action_result):
+ data_set = set(data)
+ new_data = []
+ for point in cat_details.get("urls", []):
+ if point not in data_set:
+ new_data.append(point)
+
+ parent_data_set = set(parent_data)
+ new_parent_data = []
+ for point in cat_details.get("dbCategorizedUrls", []):
+ if point not in parent_data_set:
+ new_parent_data.append(point)
+
+ cat_details["urls"] = new_data
+ cat_details["dbCategorizedUrls"] = new_parent_data
+
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/urlCategories/{category_id}", action_result, data=cat_details, method="put")
+ return ret_val, response
+
+ def _handle_remove_category_url(self, param):
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+ category_id = param["category_id"]
+
+ ret_val, category_details = self._get_category_details(category_id, action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ urls = param.get("urls", "")
+ urls_to_remove = [item.strip() for item in urls.split(",") if item.strip()]
+ retaining_parent_category_url = param.get("retaining-parent-category-url", "")
+ parent_urls_to_remove = [item.strip() for item in retaining_parent_category_url.split(",") if item.strip()]
+
+ ret_val, response = self._remove_from_category(urls_to_remove, parent_urls_to_remove, category_details, category_id, action_result)
+
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ action_result.add_data(response)
+ summary = action_result.update_summary({})
+ summary["message"] = "Category urls removed"
+
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_create_destination_group(self, param):
+ """
+ This action is used to create an IP Destination Group
+ :param name: IP destination group name
+ :param type: IP destination group type
+ :param addresses: Destination IP addresses, FQDNs, or wildcard FQDNs
+ :param description: Additional information about the destination IP group
+ :param ip_categories: Destination IP address URL categories
+ :param countries: Destination IP address countries
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ addresses = param.get("addresses", "")
+ ip_categories = param.get("ip_categories", "")
+ countries = param.get("countries", "")
+
+ data = {}
+ data["name"] = param["name"]
+ data["type"] = param["type"]
+ if addresses:
+ data["addresses"] = [item.strip() for item in addresses.split(",")]
+ data["description"] = param.get("description", "")
+ if ip_categories:
+ data["ipCategories"] = [item.strip() for item in ip_categories.split(",")]
+ if countries:
+ data["countries"] = [item.strip() for item in countries.split(",")]
+
+ ret_val, response = self._make_rest_call_helper("/api/v1/ipDestinationGroups", action_result, data=data, method="post")
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ action_result.add_data(response)
+ summary = action_result.update_summary({})
+ summary["message"] = "Destination Group Created"
+
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _get_destination_group(self, id, action_result, exclude_type=None, category_type=None, lite=False):
+
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/ipDestinationGroups/{id}", action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status(), None
+
+ group_type = response["type"]
+
+ if group_type == exclude_type:
+ return phantom.APP_SUCCESS, None
+
+ if lite:
+ if category_type and group_type not in category_type:
+ return phantom.APP_SUCCESS, None
+
+ lite_resp = {"id": response["id"], "name": response["name"], "type": group_type}
+ return phantom.APP_SUCCESS, lite_resp
+
+ return phantom.APP_SUCCESS, response
+
+ def _get_batched_groups(self, endpoint, params, action_result):
+ limit = params["pageSize"]
+
+ while True:
+ params["pageSize"] = min(limit, ZSCALER_MAX_PAGESIZE)
+ ret_val, get_groups = self._make_rest_call_helper("/api/v1" + endpoint, action_result, params=params)
+ self.debug_print("get groups is {0}".format(get_groups))
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+ for group in get_groups:
+ if "extensions" in group:
+ extensions = group.pop("extensions")
+ for key in extensions:
+ group[key] = extensions[key]
+ action_result.add_data(group)
+ limit = limit - params["pageSize"]
+ if limit <= 0 or len(get_groups) == 0:
+ break
+ params["page"] += 1
+
+ return phantom.APP_SUCCESS
+
+ def _handle_list_destination_group(self, param):
+ """
+ This action is used to list IP Destination Groups
+ :param ip_group_ids: Destination groups to retrieve
+ :param exclude_type: Group types to exclude from search
+ :param category_type: Destination types to filter by
+ :param limit: Number of groups to retrieve
+ :param lite: Retrieve limited information for each group
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ ip_group_ids = param.get("ip_group_ids", "")
+ ip_ids_lst = [item.strip() for item in ip_group_ids.split(",") if item.strip()]
+ exclude_type = param.get("exclude_type", "")
+ category_type = param.get("category_type", "")
+ category_type_list = [item.strip() for item in category_type.split(",") if item.strip()]
+ limit = param.get("limit", 50)
+ lite = param.get("lite", False)
+
+ params = {}
+ endpoint = "/ipDestinationGroups"
+ params["excludeType"] = exclude_type
+ self.debug_print("ip id list {0}".format(ip_ids_lst))
+ if ip_ids_lst:
+ for ip in ip_ids_lst:
+ ret_val, response = self._get_destination_group(ip, action_result, exclude_type, category_type, lite)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+ action_result.add_data(response)
+
+ summary = action_result.update_summary({})
+ summary["message"] = "Destination groups retrieved"
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ elif lite:
+ endpoint = "/ipDestinationGroups/lite"
+ params["type"] = category_type_list
+
+ params["page"] = 1
+ params["pageSize"] = limit
+ ret_val = self._get_batched_groups(endpoint, params, action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ # action_result.add_data(destination_groups)
+ summary = action_result.update_summary({})
+ summary["message"] = "Destination groups retrieved"
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_edit_destination_group(self, param):
+ """
+ This action is used to edit an IP Destination Group
+ :param ip_group_id: Id of destination group to edit
+ :param name: IP destination group name
+ :param addresses: Destination IP addresses, FQDNs, or wildcard FQDNs
+ :param description: Additional information about the destination IP group
+ :param ip_categories: Destination IP address URL categories
+ :param countries: Destination IP address countries
+ :param is_non_editable: If set to true, the destination IP address group is non-editable
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ group_id = param["ip_group_id"]
+
+ ret_val, group_resp = self._get_destination_group(group_id, action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+ group_resp["name"] = param.get("name", group_resp["name"])
+ if param.get("addresses"):
+ new_addresses = [item.strip() for item in param.get("addresses", "").split(",") if item.strip()]
+ group_resp["addresses"] = new_addresses
+ group_resp["description"] = param.get("description", group_resp["description"])
+ if param.get("ip_categories"):
+ new_ip_categories = [item.strip() for item in param.get("ip_categories", "").split(",") if item.strip()]
+ group_resp["ipCategories"] = new_ip_categories
+ if param.get("countries"):
+ new_countries = [item.strip() for item in param.get("countries", "").split(",") if item.strip()]
+ group_resp["countries"] = new_countries
+ group_resp["isNonEditable"] = param.get("is_non_editable", False)
+
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/ipDestinationGroups/{group_id}", action_result, data=group_resp, method="put")
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ action_result.add_data(response)
+ summary = action_result.update_summary({})
+ summary["message"] = "Destination Group Edited"
+
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_delete_destination_group(self, param):
+ """
+ This action is used to delete IP Destination Groups
+ :param ip_group_ids: Ids of destination group to delete
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ group_ids = param.get("ip_group_ids", "")
+ list_group_ids = [item.strip() for item in group_ids.split(",") if item.strip()]
+
+ for group_id in list_group_ids:
+ ret_val, response = self._make_rest_call_helper(f"/api/v1/ipDestinationGroups/{group_id}", action_result, method="delete")
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+ action_result.add_data({"ip_group_id": group_id})
+
+ summary = action_result.update_summary({})
+ summary["message"] = "Destination groups deleted"
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_get_category_details(self, param):
+ """
+ This action is used to get category details of specfic categories
+ :param category_ids: Ids of category's to query
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ group_ids = param.get("category_ids", "")
+ list_category_ids = [item.strip() for item in group_ids.split(",") if item.strip()]
+
+ for category_id in list_category_ids:
+ ret_val, category_details = self._get_category_details(category_id, action_result)
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+ action_result.add_data(category_details)
+
+ summary = action_result.update_summary({})
+ summary["message"] = "Category details recieved"
+ summary["total_categories"] = action_result.get_data_size()
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_get_departments(self, param):
+ """
+ This action is used to get departments
+ :param name: Filter by department name
+ :param page: Specifies the page offset
+ :param pageSize: Specifies the page size. Defaul is 100
+ :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message)
+ """
+ self.save_progress("In action handler for: {0}".format(self.get_action_identifier()))
+ action_result = self.add_action_result(ActionResult(dict(param)))
+
+ name = param.get("name")
+ page_size = param.get("pageSize")
+ page_num = param.get("page", 1)
+
+ endpoint = f"/api/v1/departments?page={page_num}&pageSize={page_size}"
+
+ if name:
+ endpoint = f"/api/v1/departments?page={page_num}&pageSize={page_size}&search={name}&limitSearch=true"
+
+ ret_val, response = self._make_rest_call_helper(endpoint, action_result)
+
+ if phantom.is_fail(ret_val):
+ return action_result.get_status()
+
+ for department in response:
+ action_result.add_data(department)
+
+ summary = action_result.update_summary({})
+ summary["message"] = "Departments retrieved"
+ summary["total_deparments"] = action_result.get_data_size()
+ return action_result.set_status(phantom.APP_SUCCESS)
+
+ def _handle_additional_actions(self, action_id, param):
+ ret_val = phantom.APP_SUCCESS
+
+ if action_id == "create_destination_group":
+ ret_val = self._handle_create_destination_group(param)
+
+ elif action_id == "list_destination_group":
+ ret_val = self._handle_list_destination_group(param)
+
+ elif action_id == "edit_destination_group":
+ ret_val = self._handle_edit_destination_group(param)
+
+ elif action_id == "delete_destination_group":
+ ret_val = self._handle_delete_destination_group(param)
+
+ elif action_id == "get_departments":
+ ret_val = self._handle_get_departments(param)
+
+ elif action_id == "get_category_details":
+ ret_val = self._handle_get_category_details(param)
+
+ return ret_val
+
def handle_action(self, param):
ret_val = phantom.APP_SUCCESS
@@ -999,63 +1462,88 @@ def handle_action(self, param):
self.debug_print("action_id", self.get_action_identifier())
- if action_id == 'test_connectivity':
+ if action_id == "test_connectivity":
ret_val = self._handle_test_connectivity(param)
- elif action_id == 'list_url_categories':
+ elif action_id == "list_url_categories":
ret_val = self._handle_list_url_categories(param)
- elif action_id == 'get_report':
+ elif action_id == "get_report":
ret_val = self._handle_get_report(param)
- elif action_id == 'block_ip':
+ elif action_id == "block_ip":
ret_val = self._handle_block_ip(param)
- elif action_id == 'block_url':
+ elif action_id == "block_url":
ret_val = self._handle_block_url(param)
- elif action_id == 'unblock_ip':
+ elif action_id == "unblock_ip":
ret_val = self._handle_unblock_ip(param)
- elif action_id == 'unblock_url':
+ elif action_id == "unblock_url":
ret_val = self._handle_unblock_url(param)
- elif action_id == 'allow_ip':
+ elif action_id == "allow_ip":
ret_val = self._handle_allow_ip(param)
- elif action_id == 'allow_url':
+ elif action_id == "allow_url":
ret_val = self._handle_allow_url(param)
- elif action_id == 'unallow_ip':
+ elif action_id == "unallow_ip":
ret_val = self._handle_unallow_ip(param)
- elif action_id == 'unallow_url':
+ elif action_id == "unallow_url":
ret_val = self._handle_unallow_url(param)
- elif action_id == 'lookup_ip':
+ elif action_id == "lookup_ip":
ret_val = self._handle_lookup_ip(param)
- elif action_id == 'lookup_url':
+ elif action_id == "lookup_url":
ret_val = self._handle_lookup_url(param)
- elif action_id == 'submit_file':
+ elif action_id == "submit_file":
ret_val = self._handle_submit_file(param)
- elif action_id == 'get_admin_users':
+ elif action_id == "get_admin_users":
ret_val = self._handle_get_admin_users(param)
- elif action_id == 'get_users':
+ elif action_id == "get_users":
ret_val = self._handle_get_users(param)
- elif action_id == 'get_groups':
+ elif action_id == "get_groups":
ret_val = self._handle_get_groups(param)
- elif action_id == 'add_group_user':
+ elif action_id == "add_group_user":
ret_val = self._handle_add_group_user(param)
- elif action_id == 'remove_group_user':
+ elif action_id == "remove_group_user":
ret_val = self._handle_remove_group_user(param)
+ elif action_id == "get_allowlist":
+ ret_val = self._handle_get_allowlist(param)
+
+ elif action_id == "get_denylist":
+ ret_val = self._handle_get_denylist(param)
+
+ elif action_id == "update_user":
+ ret_val = self._handle_update_user(param)
+
+ elif action_id == "add_category_url":
+ ret_val = self._handle_add_category_url(param)
+
+ elif action_id == "add_category_ip":
+ ret_val = self._handle_add_category_ip(param)
+
+ elif action_id == "remove_category_url":
+ ret_val = self._handle_remove_category_url(param)
+
+ elif action_id == "remove_category_ip":
+ ret_val = self._handle_remove_category_ip(param)
+
+ else:
+ # passing the action handling to another function to decrease FLAKE_8 complexity
+ ret_val = self._handle_additional_actions(action_id, param)
+
return ret_val
def initialize(self):
@@ -1068,17 +1556,17 @@ def initialize(self):
self._state = {"app_version": self.get_app_json().get("app_version")}
config = self.get_config()
- self._base_url = config['base_url'].rstrip('/')
- self._username = config['username']
- self._password = config['password']
- self._api_key = config['api_key']
- self._sandbox_base_url = config.get('sandbox_base_url', None)
+ self._base_url = config["base_url"].rstrip("/")
+ self._username = config["username"]
+ self._password = config["password"]
+ self._api_key = config["api_key"]
+ self._sandbox_base_url = config.get("sandbox_base_url", None)
if self._sandbox_base_url:
- self._sandbox_base_url = self._sandbox_base_url.rstrip('/')
- self._sandbox_api_token = config.get('sandbox_api_token', None)
+ self._sandbox_base_url = self._sandbox_base_url.rstrip("/")
+ self._sandbox_api_token = config.get("sandbox_api_token", None)
self._headers = {}
- self._retry_rest_call = True
- self.set_validator('ipv6', self._is_ip)
+ self._retry_rest_call = 5
+ self.set_validator("ipv6", self._is_ip)
return self._init_session()
@@ -1088,7 +1576,7 @@ def finalize(self):
return self._deinit_session()
-if __name__ == '__main__':
+if __name__ == "__main__":
import argparse
import sys
@@ -1099,35 +1587,33 @@ def finalize(self):
argparser = argparse.ArgumentParser()
- argparser.add_argument('input_test_json', help='Input Test JSON file')
- argparser.add_argument('-u', '--username', help='username', required=False)
- argparser.add_argument('-p', '--password', help='password', required=False)
- argparser.add_argument('-v', '--verify', action='store_true', help='verify', required=False, default=False)
+ argparser.add_argument("input_test_json", help="Input Test JSON file")
+ argparser.add_argument("-u", "--username", help="username", required=False)
+ argparser.add_argument("-p", "--password", help="password", required=False)
+ argparser.add_argument("-v", "--verify", action="store_true", help="verify", required=False, default=False)
args = argparser.parse_args()
verify = args.verify
session_id = None
- if (args.username and args.password):
+ if args.username and args.password:
login_url = BaseConnector._get_phantom_base_url() + "login"
try:
print("Accessing the Login page")
- r = requests.get(
- login_url, verify=verify, timeout=ZSCALER_DEFAULT_TIMEOUT)
- csrftoken = r.cookies['csrftoken']
- data = {'username': args.username, 'password': args.password, 'csrfmiddlewaretoken': csrftoken}
- headers = {'Cookie': 'csrftoken={0}'.format(csrftoken), 'Referer': login_url}
+ r = requests.get(login_url, verify=verify, timeout=ZSCALER_DEFAULT_TIMEOUT)
+ csrftoken = r.cookies["csrftoken"]
+ data = {"username": args.username, "password": args.password, "csrfmiddlewaretoken": csrftoken}
+ headers = {"Cookie": "csrftoken={0}".format(csrftoken), "Referer": login_url}
print("Logging into Platform to get the session id")
- r2 = requests.post(
- login_url, verify=verify, data=data, headers=headers, timeout=ZSCALER_DEFAULT_TIMEOUT)
- session_id = r2.cookies['sessionid']
+ r2 = requests.post(login_url, verify=verify, data=data, headers=headers, timeout=ZSCALER_DEFAULT_TIMEOUT)
+ session_id = r2.cookies["sessionid"]
except Exception as e:
print(("Unable to get session id from the platform. Error: {0}".format(str(e))))
sys.exit(1)
- if (len(sys.argv) < 2):
+ if len(sys.argv) < 2:
print("No test json specified as input")
sys.exit(0)
@@ -1139,8 +1625,8 @@ def finalize(self):
connector = ZscalerConnector()
connector.print_progress_message = True
- if (session_id is not None):
- in_json['user_session_token'] = session_id
+ if session_id is not None:
+ in_json["user_session_token"] = session_id
ret_val = connector._handle_action(json.dumps(in_json), None)
print(json.dumps(json.loads(ret_val), indent=4))
diff --git a/zscaler_consts.py b/zscaler_consts.py
index d34a5c4..ae4caac 100644
--- a/zscaler_consts.py
+++ b/zscaler_consts.py
@@ -1,6 +1,6 @@
# File: zscaler_consts.py
#
-# Copyright (c) 2017-2023 Splunk Inc.
+# Copyright (c) 2017-2024 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,9 +12,9 @@
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions
# and limitations under the License.
-ZSCALER_JSON_FULL_DETAILS = 'Full Details'
-ZSCLAER_ERR_MD5_UNKNOWN_MSG = 'md5 is unknown or analysis has yet not been completed'
-ZSCALER_SANDBOX_GET_REPORT_MSG = 'Sandbox report successfully fetched for the provided md5 hash'
+ZSCALER_JSON_FULL_DETAILS = "Full Details"
+ZSCLAER_ERR_MD5_UNKNOWN_MSG = "md5 is unknown or analysis has yet not been completed"
+ZSCALER_SANDBOX_GET_REPORT_MSG = "Sandbox report successfully fetched for the provided md5 hash"
ZSCALER_SANDBOX_SUBMIT_FILE_MSG = "Successfully submitted the file to Sandbox"
ZSCALER_ERR_MSG_UNAVAILABLE = "Error message unavailable. Please check the asset configuration and|or action parameters"
ZSCALER_STATE_FILE_CORRUPT_ERR = (
@@ -24,7 +24,7 @@
ZSCALER_MAX_PAGESIZE = 1000
ZSCALER_DEFAULT_TIMEOUT = 30
-# Constants relating to '_validate_integer'
+# Constants relating to "_validate_integer"
ZSCALER_VALID_INTEGER_MSG = "Please provide a valid integer value in the {param}"
ZSCALER_NON_NEGATIVE_INTEGER_MSG = "Please provide a valid non-negative integer value in the {param}"
ZSCALER_POSITIVE_INTEGER_MSG = "Please provide a valid non-zero positive integer value in the {param}"
diff --git a/zscaler_get_admin_users.html b/zscaler_get_admin_users.html
index 0bad440..ac3779d 100644
--- a/zscaler_get_admin_users.html
+++ b/zscaler_get_admin_users.html
@@ -10,7 +10,7 @@
{% block widget_content %}