netskope_api_v2

Netskope API v2 provides a powerful interface for managing and monitoring Netskope deployments. It enables users to retrieve alerts and events, manage URL lists, and control clients. With Netskope API v2, organizations can proactively respond to security threats, enforce web access policies, and efficiently administer their Netskope environment.

Network Security · Netskope

Details

IDnetskope_api_v2
ProviderNetskope
CategoryNetwork Security
From Version6.9.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Netskope API v2 provides a powerful interface for managing and monitoring Netskope deployments. It enables users to retrieve alerts and events, manage URL lists, and control clients. With Netskope API v2, organizations can proactively respond to security threats, enforce web access policies, and efficiently administer their Netskope environment.
This integration was integrated and tested with version 2 of the Netskope API.

To successfully instantiate an instance of the Netskope API v2 integration under the RBACv3 framework, follow the steps below.
Note: Netskope has deprecated the legacy single-step API token generation process. All new and migrated tenants must use RBACv3 to create API tokens through Service Accounts.

Step 1: Create a Service Account (RBACv3)

Follow the instructions under the section Create a New Service Account.

Step 2: Assign required functional roles

Ensure the Service Account has access to the following functional areas:
In total 4 sections need to be enabled:

  1. Administration
  2. Access Control
  3. DLP
  4. Events & Analytics

Add new role

For Administration

In the Administration section, select Function > Audit Log and grant it View rights.
Add new role - Administration

Under Users & Groups, select Manage:
Users and Groups

Events & Analytics

Functions that need to be set to view: Application Events, Page Events, Network Events, and Alerts.
Events and Analytics

For Access Control

Select Access Control, for the Infrastructure function, grant View rights.
Access Control

For the URL List, set to Manage and Apply.
Manage and Apply

For DLP

For DLP Incident, set to View.
DLP Incident

Step 3

  • API Token: Copy the generated API token and store it securely.
  • Server URL: Construct the server URL using your account name and region. Example:
    If your account name is xsoar and the region is de, your Server URL is: https://xsoar.de.goskope.com/

Configue Netskope (API v2) in Cortex

Parameter Description Required
Server URL   True
Use system proxy settings   False
Trust any certificate (not secure)   False
API token Netskope API access token (make sure to generate token for the required endpoints). True
First fetch timestamp First alert created date to fetch. e.g., “1 min ago”,”2 weeks ago”,”3 months ago”. False
Maximum incidents per fetch Maximum number of incidents per fetch. Default is 50. The maximum is 100. False
Maximum Netskope events per fetch. Max value is 200.   False
Maximum Netskope DLP incidents per fetch. Max value is 200.   False
Fetch Events Fetch events as incidents, in addition to the alerts. False
Fetch DLP incidents Fetch Netskope DLP incidents as incidents, in addition to the alerts. False
Event types to fetch. The event types to fetch as incidents. False
Alerts Query Free text query to filter the fetched alerts. For more information, visit Netskope documentation (https://docs.netskope.com/en/get-alerts-data.html). False
Events Query Free text query to filter the fetched events (if configured). For more information, visit Netskope documentation (https://docs.netskope.com/en/get-alerts-data.html). False
Incident type   False
Fetch incidents   False
User Email The user email for update incident in Netskope. False
Incidents Fetch Interval   False
Incident Mirroring Direction Cortex XSOAR only parameter. False
Close Mirrored XSOAR Incident Cortex XSOAR only parameter. False
Close Mirrored Netskope Incident   False

Commands

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

netskope-alert-list


Retrieve alerts generated by Netskope. Select the desired alerts using the alert_type parameter. Mandatory inputs include start_time and end_time, or insertion_start_time and insertion_end_time (If end_time or insertion_end_time is not provided, it defaults to the current date and time). Additionally, it is not permissible to supply a combination of the aforementioned options.

Base Command

netskope-alert-list

Input

Argument Name Description Required
start_time Restrict events to those that have dates greater than the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘end_time’ argument must be provided as well. Optional
end_time Restrict events to those that have dates less than or equal to the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘start_time’ argument must also be provided. If the start_time argument is provided and this argument is not - the default value will be set for now. Optional
insertion_start_time Restrict events to those that were inserted into the system after the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘insertion_end_time’ argument must also be provided. Optional
insertion_end_time Restrict events to those that were inserted into the system before the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘insertion_start_time’ argument must also be provided. If the insertion_start_time argument is provided and this argument is not - the default value will be set for now. Optional
query Free query to filter the alerts. For example, “alert_name like test”. For more information, please visit Netskope documentation: https://docs.netskope.com/en/get-alerts-data.html. Optional
alert_type Select alerts by their type. Optional
acked Whether to retrieve acknowledged alerts. Possible values are: True, False. Optional
page Page number of paginated results. Minimum value: 1. Optional
limit The maximum number of records to retrieve. Default is 50. Optional

Context Output

Path Type Description
Netskope.Alert._appsession_start String The timestamp marking the start of an application session.
Netskope.Alert._category_id String The unique identifier for a category.
Netskope.Alert._category_name String The name or label associated with a category.
Netskope.Alert._category_tags Number Numeric tags or labels associated with the category.
Netskope.Alert._content_version Number A numeric value representing the content version.
Netskope.Alert._correlation_id String An identifier used for correlating events or data.
Netskope.Alert._creation_timestamp Number The timestamp when the data or event was created.
Netskope.Alert._ef_received_at Date The timestamp indicating when the event was received.
Netskope.Alert._event_id String A unique identifier for the event.
Netskope.Alert._forwarded_by String Information indicating the source responsible for forwarding the event.
Netskope.Alert._gef_src_dp String The source data path for the event.
Netskope.Alert._id String A unique identifier for the event or data.
Netskope.Alert._insertion_epoch_timestamp Number Insertion timestamp.
Netskope.Alert._nshostname String The hostname associated with Netskope.
Netskope.Alert._raw_event_inserted_at Date The timestamp indicating when the raw event was inserted.
Netskope.Alert._service_identifier String An identifier associated with a specific service.
Netskope.Alert._session_begin String The timestamp marking the beginning of a session.
Netskope.Alert._skip_geoip_lookup String A flag indicating whether GeoIP lookup should be skipped.
Netskope.Alert._src_epoch_now Number A numeric value representing the source epoch.
Netskope.Alert.access_method String Cloud app traffic can be steered to the Netskope cloud using different deployment methods such as Client (Netskope Client), Secure Forwarder etc. Administrators can also upload firewall and/or proxy logs for log analytics. This field shows the actual access method that triggered the event. For log uploads this shows the actual log type such as PAN, Websense, etc.
Netskope.Alert.acked String Whether the user acknowledged the alert or not.
Netskope.Alert.action String Action taken on the event for the policy.
Netskope.Alert.activity String Description of the user-performed activity.
Netskope.Alert.alert String Indicates whether the alert is generated or not. Populated as yes for all alerts.
Netskope.Alert.alert_name String Name of the alert.
Netskope.Alert.alert_type String Type of the alert.
Netskope.Alert.app String Specific cloud application used by the user (e.g., app = Dropbox).
Netskope.Alert.app_session_id Number Unique App/Site Session ID for traffic_type = CloudApp and Web. An app session starts when a user starts using a cloud app/site and ends once they have been inactive for a certain period of time (15 mins). Use app_session_id to check all the user activities in a single app session. app_session_id is unique for a user, device, browser, and domain.
Netskope.Alert.appcategory String Application category as designated by Netskope.
Netskope.Alert.appsuite String Information related to the suite of applications or software used.
Netskope.Alert.browser String Shows the actual browser from where the cloud app was accessed.
Netskope.Alert.browser_session_id Number Browser session ID. If there is an idle timeout of 15 minutes, it will time out the session.
Netskope.Alert.category String A classification or grouping label for data or events.
Netskope.Alert.cci Number Cloud Confidence Index, indicating the readiness and security of cloud applications.
Netskope.Alert.ccl String “Cloud Confidence Level. CCL measures the enterprise readiness of the cloud apps taking into consideration those apps’ security, auditability, and business continuity. Each app is assigned one of five cloud confidence levels: excellent, high, medium, low, or poor. Useful for querying if users are accessing a cloud app with a lower CCL.”
Netskope.Alert.connection_id Number Each connection has a unique ID. Shows the ID for the connection event.
Netskope.Alert.count Number Number of raw log lines/events sessionized or suppressed during the suppressed interval.
Netskope.Alert.device String Device type from where the user accessed the cloud app. It could be a Macintosh, Windows device, iPad, etc.
Netskope.Alert.device_classification String Designation of the device as determined by the Netskope Client as to whether the device is managed or not.
Netskope.Alert.domain String Domain value. This will hold the host header value or SNI or extracted from an absolute URI.
Netskope.Alert.dst_country String Application’s two-letter country code as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.dst_latitude Number Latitude of the application as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.dst_location String Application’s city as determined by maxmind or IP2Location Geolocation database.
Netskope.Alert.dst_longitude Number Longitude of the application as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.dst_region String Application’s state or region as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.dst_timezone String Destination timezone.
Netskope.Alert.dst_zipcode String Application’s zip code as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.dstip String IP address where the destination app is hosted.
Netskope.Alert.hostname String Host name.
Netskope.Alert.incident_id Number A unique identifier for an incident or event.
Netskope.Alert.ja3 String A field indicating JA3 information.
Netskope.Alert.ja3s String A field indicating JA3S information.
Netskope.Alert.managed_app String Whether or not the app in question is managed.
Netskope.Alert.managementID String Management ID.
Netskope.Alert.netskope_pop String Netskope Point of Presence, related to network infrastructure.
Netskope.Alert.notify_template String The template used for notifications or alerts.
Netskope.Alert.nsdeviceuid String Device identifiers on macOS and Windows.
Netskope.Alert.organization_unit String Organization units for which the event correlates to. This ties to user information extracted from Active Directory using the Directory Importer/AD Connector application.
Netskope.Alert.os String Operating system of the host that generated the event.
Netskope.Alert.os_version String Operating system version of the host.
Netskope.Alert.other_categories String Additional categories or labels not specified elsewhere.
Netskope.Alert.page String The URL of the originating page.
Netskope.Alert.page_site String Information about the web page or site being accessed.
Netskope.Alert.policy String Name of the policy configured by an admin.
Netskope.Alert.policy_id String The Netskope internal ID for the policy created by an admin.
Netskope.Alert.port String The network port used for communication.
Netskope.Alert.protocol String The communication protocol or method used.
Netskope.Alert.request_id Number Unique request ID for the event.
Netskope.Alert.severity String Severity used by watchlist and malware alerts.
Netskope.Alert.site String For traffic_type = CloudApp, site = app, and for traffic_type = Web, it will be the second-level domain name + top-level domain name. For example, in “www.cnn.com”, it is “cnn.com”.
Netskope.Alert.src_country String User’s country’s two-letter country code as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.src_latitude Number Latitude of the user as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.src_location String User’s city as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.src_longitude Number Longitude of the user as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.src_region String Source state or region as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.src_time Date A timestamp associated with the source or event.
Netskope.Alert.src_timezone String Source timezone. Shows the long-format timezone designation.
Netskope.Alert.src_zipcode String Source zip code as determined by Maxmind or IP2Location Geolocation database.
Netskope.Alert.srcip String IP address of the source/user.
Netskope.Alert.telemetry_app String Typically, SaaS app websites use web analytics code within the pages to gather analytic data. When a SaaS app action or page is shown, there is subsequent traffic generated to tracking apps such as doubleclick.net, Optimizely, etc. These tracking apps are listed if applicable in the Telemetry App field.
Netskope.Alert.timestamp Number Timestamp when the event/alert happened. Event timestamp in Unix epoch format.
Netskope.Alert.traffic_type String “Type of the traffic: CloudApp or Web. CloudApp indicates CASB and web indicates HTTP traffic. Web traffic is only captured for inline access method. It is currently not captured for Risk Insights.”
Netskope.Alert.transaction_id Number Unique ID for a given request/response.
Netskope.Alert.type String Shows if it is an application event or a connection event. Application events are recorded to track user events inside a cloud app. Connection events show the actual HTTP connection.
Netskope.Alert.ur_normalized String All lowercase user email.
Netskope.Alert.url String URL of the application that the user visited as provided by the log or data plane traffic.
Netskope.Alert.user String User email.
Netskope.Alert.useragent String Browser HTTP user agent header.
Netskope.Alert.userip String IP address of the user.
Netskope.Alert.userkey String User ID or email.
Netskope.Alert._client_timeout Number Information related to client timeouts.
Netskope.Alert._dlp_backup_profile String Information related to DLP (Data Loss Prevention) backup profiles.
Netskope.Alert._nsp_dur_back Number Duration information for NSP (Network Security Platform) on the back end.
Netskope.Alert._nsp_dur_front Number Duration information for NSP on the front end.
Netskope.Alert._nsp_retrans_back Number Retransmission information for NSP on the back end.
Netskope.Alert._nsp_retrans_front Number Retransmission information for NSP on the front end.
Netskope.Alert._nsp_rtt_back Number Round-trip time information for NSP on the back end.
Netskope.Alert._nsp_rtt_front Number Round-trip time information for NSP on the front end.
Netskope.Alert._resource_name String The name associated with a resource.
Netskope.Alert._scan_source String Information indicating the source of a scan.
Netskope.Alert._tenant_max_file_size Number The maximum file size allowed for a tenant.
Netskope.Alert.all_policy_matches String Information related to policy matches.
Netskope.Alert.browser_version String Browser version.
Netskope.Alert.file_size Number Size of the file in bytes.
Netskope.Alert.file_type String File type.
Netskope.Alert.md5 String MD5 of the file.
Netskope.Alert.object String Name of the object which is being acted on. It could be a filename, folder name, report name, document name, etc.
Netskope.Alert.object_type String Type of the object which is being acted on. Object type could be a file, folder, report, document, message, etc.
Netskope.Alert.web_universal_connector String Universal web connector information.

Command example

!netskope-alert-list start_time="2023-05-05 11:06" alert_type=policy limit=2

Context Example

{
    "Netskope": {
        "Alert": [
            {
                "_appsession_start": "yes",
                "_category_id": "8",
                "_category_name": "Collaboration",
                "_category_tags": [10001, 564, 8],
                "_content_version": 1687272302,
                "_correlation_id": "011c0f84-9938-460b-8cbe-dab38fa6cb31",
                "_creation_timestamp": 1687656279,
                "_ef_received_at": 1687656276462,
                "_event_id": "724d1174-d78c-4197-8243-4fbd3644b192",
                "_forwarded_by": "msg-relayer",
                "_gef_src_dp": "IL-TLV1",
                "_id": "c3c98336e9d6807dd821b8dc",
                "_insertion_epoch_timestamp": 1687656283,
                "_nshostname": "dppool1-2-egress",
                "_raw_event_inserted_at": 1687656276776,
                "_service_identifier": "service-nsproxy",
                "_session_begin": "1",
                "_skip_geoip_lookup": "yes",
                "_src_epoch_now": 1687667040,
                "access_method": "Client",
                "acked": "false",
                "action": "block",
                "activity": "Browse",
                "alert": "yes",
                "alert_id": "c3c98336e9d6807dd821b8dc",
                "alert_name": "365 block",
                "alert_type": "policy",
                "app": "Microsoft Teams",
                "app_session_id": 3379014715943843300,
                "appcategory": "Collaboration",
                "appsuite": "Office365",
                "browser": "Native",
                "browser_session_id": 2893692091617575400,
                "category": "Collaboration",
                "cci": 92,
                "ccl": "excellent",
                "connection_id": 1717056737521399300,
                "count": 1,
                "device": "Windows Device",
                "device_classification": "unmanaged",
                "domain": "config.teams.microsoft.com",
                "dst_country": "US",
                "dst_latitude": 47.682899475097656,
                "dst_location": "Redmond",
                "dst_longitude": -122.12090301513672,
                "dst_region": "Washington",
                "dst_timezone": "America/Los_Angeles",
                "dst_zipcode": "N/A",
                "dstip": "8.8.8.8",
                "hostname": "DESKTOP-TOR2VO7",
                "incident_id": 6782360912641091000,
                "ja3": "a0e9f5d64349fb13191bc781f81f42e1",
                "ja3s": "NotAvailable",
                "managed_app": "no",
                "managementID": "",
                "netskope_pop": "IL-TLV1",
                "notify_template": "block_page.html",
                "nsdeviceuid": "A633E874-D3B2-0FB7-F5CC-AF89F428B182",
                "organization_unit": "",
                "os": "Windows 10",
                "os_version": "Windows 10",
                "other_categories": ["Test web Policy Beni", "Technology", "Collaboration"],
                "page": "config.teams.microsoft.com",
                "page_site": "Microsoft Teams",
                "policy": "365 block",
                "policy_id": "84BE7DC6087E38BCA19B3788C5E02A67 2023-06-22 14:42:51.404368",
                "port": "443",
                "protocol": "HTTPS/1.1",
                "request_id": 2605870162901070000,
                "severity": "unknown",
                "site": "Microsoft Teams",
                "src_country": "IL",
                "src_latitude": 32.0803,
                "src_location": "Tel Aviv",
                "src_longitude": 34.7805,
                "src_region": "Tel Aviv",
                "src_time": "Sun Jun 25 04:24:00 2023",
                "src_timezone": "Asia/Jerusalem",
                "src_zipcode": "N/A",
                "srcip": "8.8.8.8",
                "telemetry_app": "",
                "timestamp": "2023-06-25T01:24:36.000Z",
                "traffic_type": "CloudApp",
                "transaction_id": 6782360912641091000,
                "type": "nspolicy",
                "ur_normalized": "example@qmasters.co",
                "url": "config.teams.microsoft.com/config/v1/ODSP_Sync_Client/23.119.0606.0001",
                "user": "example@qmasters.co",
                "useragent": "OneDrive-23.119.0606.0001",
                "userip": "8.8.8.8",
                "userkey": "example@qmasters.co"
            },
            {
                "_appsession_start": "yes",
                "_category_id": "8",
                "_category_name": "Collaboration",
                "_category_tags": [10001, 564, 8],
                "_content_version": 1687272302,
                "_correlation_id": "46647142-2f24-4802-b8f5-22814e80353a",
                "_creation_timestamp": 1687659879,
                "_ef_received_at": 1687659876494,
                "_event_id": "6827a5eb-de85-48af-8eae-6d3034084fd6",
                "_forwarded_by": "msg-relayer",
                "_gef_src_dp": "IL-TLV1",
                "_id": "da711d311019f02d79ebc8f4",
                "_insertion_epoch_timestamp": 1687659883,
                "_nshostname": "dppool1-2-egress",
                "_raw_event_inserted_at": 1687659876771,
                "_service_identifier": "service-nsproxy",
                "_session_begin": "1",
                "_skip_geoip_lookup": "yes",
                "_src_epoch_now": 1687670640,
                "access_method": "Client",
                "acked": "false",
                "action": "block",
                "activity": "Browse",
                "alert": "yes",
                "alert_id": "da711d311019f02d79ebc8f4",
                "alert_name": "365 block",
                "alert_type": "policy",
                "app": "Microsoft Teams",
                "app_session_id": 4359394467077842400,
                "appcategory": "Collaboration",
                "appsuite": "Office365",
                "browser": "Native",
                "browser_session_id": 2893692091617575400,
                "category": "Collaboration",
                "cci": 92,
                "ccl": "excellent",
                "connection_id": 8981978357397935000,
                "count": 1,
                "device": "Windows Device",
                "device_classification": "unmanaged",
                "domain": "config.teams.microsoft.com",
                "dst_country": "AT",
                "dst_latitude": 48.2049,
                "dst_location": "Vienna",
                "dst_longitude": 16.3662,
                "dst_region": "Vienna",
                "dst_timezone": "Europe/Vienna",
                "dst_zipcode": "1010",
                "dstip": "8.8.8.8",
                "hostname": "DESKTOP-TOR2VO7",
                "incident_id": 1478029261577663500,
                "ja3": "a0e9f5d64349fb13191bc781f81f42e1",
                "ja3s": "NotAvailable",
                "managed_app": "no",
                "managementID": "",
                "netskope_pop": "IL-TLV1",
                "notify_template": "block_page.html",
                "nsdeviceuid": "A633E874-D3B2-0FB7-F5CC-AF89F428B182",
                "organization_unit": "",
                "os": "Windows 10",
                "os_version": "Windows 10",
                "other_categories": ["Test web Policy Beni", "Technology", "Collaboration"],
                "page": "config.teams.microsoft.com",
                "page_site": "Microsoft Teams",
                "policy": "365 block",
                "policy_id": "84BE7DC6087E38BCA19B3788C5E02A67 2023-06-22 14:42:51.404368",
                "port": "443",
                "protocol": "HTTPS/1.1",
                "request_id": 2605900362175087600,
                "severity": "unknown",
                "site": "Microsoft Teams",
                "src_country": "IL",
                "src_latitude": 32.0803,
                "src_location": "Tel Aviv",
                "src_longitude": 34.7805,
                "src_region": "Tel Aviv",
                "src_time": "Sun Jun 25 05:24:00 2023",
                "src_timezone": "Asia/Jerusalem",
                "src_zipcode": "N/A",
                "srcip": "8.8.8.8",
                "telemetry_app": "",
                "timestamp": "2023-06-25T02:24:36.000Z",
                "traffic_type": "CloudApp",
                "transaction_id": 1478029261577663500,
                "type": "nspolicy",
                "ur_normalized": "example@qmasters.co",
                "url": "config.teams.microsoft.com/config/v1/ODSP_Sync_Client/23.119.0606.0001",
                "user": "example@qmasters.co",
                "useragent": "OneDrive-23.119.0606.0001",
                "userip": "8.8.8.8",
                "userkey": "example@qmasters.co"
            }
        ]
    }
}

Human Readable Output

Alert List

Showing page 1.
Current page size: 2.

Alert Id Alert Name Alert Type Severity Action Activity Type Category Name Event Id Domain Dst Country Policy Port Protocol Md5 Timestamp
c3c98336e9d6807dd821b8dc 365 block policy unknown block Browse nspolicy Collaboration 724d1174-d78c-4197-8243-4fbd3644b192 config.teams.microsoft.com US 365 block 443 HTTPS/1.1   2023-06-25T01:24:36.000Z
da711d311019f02d79ebc8f4 365 block policy unknown block Browse nspolicy Collaboration 6827a5eb-de85-48af-8eae-6d3034084fd6 config.teams.microsoft.com AT 365 block 443 HTTPS/1.1   2023-06-25T02:24:36.000Z

netskope-event-list


Get events extracted from SaaS traffic. You may choose what events to receive with the event_type parameter. You must provide start_time and end_time, or insertion_start_time and insertion_end_time (If end_time or insertion_end_time isn’t provided - it defaults to the current date and time). Also, you cannot provide a combination of the options mentioned above.

Base Command

netskope-event-list

Input

Argument Name Description Required
event_type Select events by their type. Available types: page,application,audit,infrastructure,network. Possible values are: page, application, audit, infrastructure, network. Required
query Free query to filter the events. For example, “app eq Dropbox”. For more information, please visit Netskope documentation: https://docs.netskope.com/en/get-events-data.html. Optional
start_time Restrict events to those that have dates greater than the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘end_time’ argument must also be provided. Optional
end_time Restrict events to those that have dates less than or equal to the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘start_time’ argument must also be provided. If the start_time argument is provided and this argument is not - the default value will be set for now. Optional
insertion_start_time Restrict events to those that were inserted into the system after the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘insertion_end_time’ argument must also be provided. Optional
insertion_end_time Restrict events to those that were inserted into the system before the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). When this argument is provided, the ‘insertion_start_time’ argument must also be provided. If the insertion_start_time argument is provided and this argument is not - the default value will be set for now. Optional
page Page number of paginated results. Minimum value: 1. Optional
limit The maximum number of records to retrieve. Default is 50. Optional

Context Output

Path Type Description
Netskope.Event._appsession_start String Netskope event application session start.
Netskope.Event._category_id String Netskope event category ID.
Netskope.Event._category_name String Netskope event category name.
Netskope.Event._category_tags Number Netskope event category tags.
Netskope.Event._content_version Number Netskope event content version.
Netskope.Event._correlation_id String Netskope event correlation ID.
Netskope.Event._creation_timestamp Number Netskope event creation timestamp.
Netskope.Event._ef_received_at Date The timestamp indicating when the event was received.
Netskope.Event._event_id String Netskope event event ID.
Netskope.Event._forwarded_by String Netskope event forwarded by.
Netskope.Event._gef_src_dp String The source data path for the event.
Netskope.Event._id String Netskope event ID.
Netskope.Event._insertion_epoch_timestamp Number Netskope event insertion epoch timestamp
Netskope.Event._nshostname String The hostname associated with Netskope.
Netskope.Event._raw_event_inserted_at Date The date the Netskope raw event was inserted.
Netskope.Event._service_identifier String Netskope event service identifier.
Netskope.Event._session_begin String The timestamp marking the beginning of a session.
Netskope.Event._skip_geoip_lookup String Netskope event skip GeoIP lookup.
Netskope.Event._src_epoch_now Number A numeric value representing the source epoch.
Netskope.Event.access_method String Netskope event access method.
Netskope.Event.action String Netskope event action.
Netskope.Event.activity String Netskope event activity.
Netskope.Event.alert String Netskope event alert.
Netskope.Event.app String Netskope event app.
Netskope.Event.app_session_id Number Netskope event app session ID.
Netskope.Event.appcategory String Netskope event app category.
Netskope.Event.appsuite String Netskope event app suite.
Netskope.Event.browser String Netskope event browser.
Netskope.Event.browser_session_id Number Netskope event browser session ID.
Netskope.Event.category String Netskope event category.
Netskope.Event.cci Number Netskope event Cloud Confidence Index.
Netskope.Event.ccl String Netskope event Cloud Confidence Levels.
Netskope.Event.connection_id Number Netskope event connection ID.
Netskope.Event.count Number Netskope event count.
Netskope.Event.device String Netskope event device.
Netskope.Event.device_classification String Netskope event device classification.
Netskope.Event.dom String Netskope event Document Object Model (DOM).
Netskope.Event.dst_country String Netskope event destination country.
Netskope.Event.dst_latitude Number Netskope event destination latitude.
Netskope.Event.dst_location String Netskope event destination location.
Netskope.Event.dst_longitude Number Netskope event destination longitude.
Netskope.Event.dst_region String Netskope event destination region.
Netskope.Event.dst_timezone String Netskope event destination timezone.
Netskope.Event.dst_zipcode String Netskope event destination zip code.
Netskope.Event.dstip String Netskope event destination IP.
Netskope.Event.hostname String Netskope event host name.
Netskope.Event.incident_id Number Netskope event incident ID.
Netskope.Event.ja3 String A field indicating JA3 information.
Netskope.Event.ja3s String A field indicating JA3S information.
Netskope.Event.managed_app String Netskope event managed app.
Netskope.Event.managementID String Netskope event management ID.
Netskope.Event.netskope_pop String Netskope event Netskope POP.
Netskope.Event.notify_template String Netskope event notify template.
Netskope.Event.nsdeviceuid String Netskope event Netskope device UID.
Netskope.Event.organization_unit String Netskope event organization unit.
Netskope.Event.os String Netskope event operating system.
Netskope.Event.os_version String Netskope event operating system version.
Netskope.Event.other_categories String Netskope event other categories.
Netskope.Event.page String Netskope event page.
Netskope.Event.page_site String Netskope event page site.
Netskope.Event.policy String Netskope event policy.
Netskope.Event.policy_id String Netskope event policy ID.
Netskope.Event.port Number Netskope event port.
Netskope.Event.protocol String Netskope event protocol.
Netskope.Event.request_id Number Netskope event request ID.
Netskope.Event.severity String Netskope event severity.
Netskope.Event.site String Netskope event site.
Netskope.Event.src_country String Netskope event source country.
Netskope.Event.src_latitude Number Netskope event source latitude.
Netskope.Event.src_location String Netskope event source location.
Netskope.Event.src_longitude Number Netskope event source longitude.
Netskope.Event.src_region String Netskope event source region.
Netskope.Event.src_time Date Netskope event source time.
Netskope.Event.src_timezone String Netskope event source timezone.
Netskope.Event.src_zipcode String Netskope event source zip code.
Netskope.Event.srcip String Netskope event source IP.
Netskope.Event.telemetry_app String Netskope event telemetry app.
Netskope.Event.timestamp Number Netskope event timestamp.
Netskope.Event.traffic_type String Netskope event traffic type.
Netskope.Event.transaction_id Number Netskope event transaction ID.
Netskope.Event.type String Netskope event type.
Netskope.Event.ur_normalized String All lowercase user email.
Netskope.Event.url String Netskope event URL.
Netskope.Event.user String Netskope event user.
Netskope.Event.useragent String Netskope event user agent.
Netskope.Event.userip String Netskope event user IP.
Netskope.Event.userkey String Netskope event user key.

Command example

!netskope-event-list event_type=page start_time="10 days ago" limit=2

Human Readable Output

Event List

Showing page 1.
Current page size: 2.
No entries.

netskope-url-list-update


Update the URL List with the values provided. please note that this command overrides the list.

Base Command

netskope-url-list-update

Input

Argument Name Description Required
url_list_id The URL list ID to update (use netskope-url-list-list command to get URL list ID). Required
name The updated URL list name. Required
urls The updated URL list items (For Exact - Enter URLs like .example.com, or IP addresses, separated by a new line. For Regex - Enter URLs like ^client[0-9]\.google\.com , ^app\.slack\.com/./netskope, or ^google.com, separated by a new line). Required
list_type The updated URL list type. Possible values are: exact, regex. Required
deploy Whether to deploy URL list changes or not. Default is False. Possible values are: True, False. Optional

Context Output

Path Type Description
Netskope.URLList.id Number Netskope URL list ID.
Netskope.URLList.name String Netskope URL list name.
Netskope.URLList.data.urls String Netskope URL list data URLs.
Netskope.URLList.data.type String Netskope URL list data type.
Netskope.URLList.data.json_version Number Netskope URL list data JSON version.
Netskope.URLList.modify_by String Netskope URL list modify by.
Netskope.URLList.modify_time Date Netskope URL list modify time.
Netskope.URLList.modify_type String Netskope URL list modify type.
Netskope.URLList.pending String Netskope URL list pending status.

Command example

!netskope-url-list-update url_list_id=11 name="QMASTERS list" urls="google.com" list_type=regex deploy=false

Context Example

{
    "Netskope": {
        "URLList": {
            "id": 11,
            "json_version": 2,
            "modify_by": "Tal New Token",
            "modify_time": "2023-07-18",
            "modify_type": "Edited",
            "name": "QMASTERS list",
            "pending": "pending",
            "type": "regex",
            "urls": ["google.com"]
        }
    }
}

Human Readable Output

URL List

Id Json Version Modify By Modify Time Modify Type Name Pending Type Urls
11 2 Tal New Token 2023-07-18 Edited QMASTERS list pending regex google.com

netskope-url-list-create


Create a new URL list.

Base Command

netskope-url-list-create

Input

Argument Name Description Required
name The unique name for the URL list. Required
urls The URL list items (For Exact - Enter URLs like .example.com, or IP addresses, separated by a new line. For Regex - Enter URLs like ^client[0-9]\.google\.com , ^app\.slack\.com/./netskope, or ^google.com, separated by a new line). Required
list_type The URL list type. Possible values are: exact, regex. Required
deploy Whether to deploy URL list changes or not. Default is False. Possible values are: True, False. Optional

Context Output

Path Type Description
Netskope.URLList.id Number Netskope URL list ID.
Netskope.URLList.name String Netskope URL list name.
Netskope.URLList.data.urls String Netskope URL list data URLs.
Netskope.URLList.data.type String Netskope URL list data type.
Netskope.URLList.data.json_version Number Netskope URL list data JSON version.
Netskope.URLList.modify_type String Netskope URL list modify type.
Netskope.URLList.modify_by String Netskope URL list modify by.
Netskope.URLList.modify_time Date Netskope URL list modify time.
Netskope.URLList.pending String Netskope URL list pending status.

Command example

!netskope-url-list-create name="New QMASTERS list" urls="xsoar.com,qmasters.com,google.com" list_type=regex deploy=false

Context Example

{
    "Netskope": {
        "URLList": {
            "id": 12,
            "json_version": 2,
            "modify_by": "Tal New Token",
            "modify_time": "2023-07-18",
            "modify_type": "Created",
            "name": "New QMASTERS list",
            "pending": "pending",
            "type": "regex",
            "urls": ["xsoar.com", "qmasters.com", "google.com"]
        }
    }
}

Human Readable Output

URL List

Id Json Version Modify By Modify Time Modify Type Name Pending Type Urls
12 2 Tal New Token 2023-07-18 Created New QMASTERS list pending regex xsoar.com,
qmasters.com,
google.com

netskope-url-lists-list


Get all URL Lists or a specific by specifying the list ID.

Base Command

netskope-url-lists-list

Input

Argument Name Description Required
url_list_id The URL list ID to get. Optional
pending Get a list of only applied or pending URL lists. Possible values are: applied, pending. Optional
field Comma separated data values to return in response call (for example: name, id, data, modify_by, modify_time, modify_type, pending). Defaults to all values. Optional
all_results Whether to retrieve all results or not. Defaults is false. Possible values are: True, False. Optional
limit The maximum number of records to retrieve. Default is 50. Optional

Context Output

Path Type Description
Netskope.URLList.id Number Netskope URL list ID.
Netskope.URLList.name String Netskope URL list name.
Netskope.URLList.data.urls String Netskope URL list data URLs.
Netskope.URLList.modify_by String Netskope URL list modify by.
Netskope.URLList.modify_time Date Netskope URL list modify time.
Netskope.URLList.modify_type String Netskope URL list modify type.
Netskope.URLList.pending String Netskope URL list pending status.

Command example

!netskope-url-lists-list

Context Example

{
    "Netskope": {
        "URLList": [
            {
                "data": {
                    "json_version": 2,
                    "type": "exact",
                    "urls": ["g.g"]
                },
                "id": 1,
                "modify_by": "example@qmasters.co",
                "modify_time": "2023-07-16T00:00:00.000Z",
                "modify_type": "Edited",
                "name": "myList",
                "pending": 0
            },
            {
                "data": {
                    "json_version": 2,
                    "type": "regex",
                    "urls": ["google.com"]
                },
                "id": 2,
                "modify_by": "Tal New Token",
                "modify_time": "2023-07-18T00:00:00.000Z",
                "modify_type": "Edited",
                "name": "NewURLList",
                "pending": 0
            },
            {
                "data": {
                    "json_version": 2,
                    "type": "exact",
                    "urls": ["google.com", "www.abc.com", "example.com", "lulu.com"]
                },
                "id": 4,
                "modify_by": "Netskope REST API",
                "modify_time": "2023-07-05T10:24:57.000Z",
                "modify_type": "Edited",
                "name": "Tal-newURLlist",
                "pending": 0
            },
            {
                "data": {
                    "json_version": 2,
                    "type": "exact",
                    "urls": ["google.com", "example.com", "lulu.com"]
                },
                "id": 5,
                "modify_by": "Netskope REST API",
                "modify_time": "2023-07-05T00:00:00.000Z",
                "modify_type": "Created",
                "name": "New URL list",
                "pending": 0
            },
            {
                "data": {
                    "json_version": 2,
                    "type": "exact",
                    "urls": ["google.com", "example.com", "lulu.com"]
                },
                "id": 6,
                "modify_by": "Netskope REST API",
                "modify_time": "2023-07-05T00:00:00.000Z",
                "modify_type": "Created",
                "name": "New URL list 2",
                "pending": 0
            },
            {
                "data": {
                    "json_version": 2,
                    "type": "regex",
                    "urls": ["xsoar.com", "qmasters.com"]
                },
                "id": 8,
                "modify_by": "Netskope REST API",
                "modify_time": "2023-07-17T00:00:00.000Z",
                "modify_type": "Created",
                "name": "NewNewURLlist",
                "pending": 0
            },
            {
                "data": {
                    "json_version": 2,
                    "type": "regex",
                    "urls": ["xsoar.com", "qmasters.com"]
                },
                "id": 9,
                "modify_by": "Netskope REST API",
                "modify_time": "2023-07-17T00:00:00.000Z",
                "modify_type": "Created",
                "name": "NewNewURLlist1",
                "pending": 0
            }
        ]
    }
}

Human Readable Output

URL List

Id Json Version Modify By Modify Time Modify Type Name Pending Type Urls
1 2 example@qmasters.co 2023-07-16T00:00:00.000Z Edited myList applied exact g.g
2 2 Tal New Token 2023-07-18T00:00:00.000Z Edited NewURLList applied regex google.com
4 2 Netskope REST API 2023-07-05T10:24:57.000Z Edited Tal-newURLlist applied exact google.com,
www.abc.com,
example.com,
lulu.com
5 2 Netskope REST API 2023-07-05T00:00:00.000Z Created New URL list applied exact google.com,
example.com,
lulu.com
6 2 Netskope REST API 2023-07-05T00:00:00.000Z Created New URL list 2 applied exact google.com,
example.com,
lulu.com
8 2 Netskope REST API 2023-07-17T00:00:00.000Z Created NewNewURLlist applied regex xsoar.com,
qmasters.com
9 2 Netskope REST API 2023-07-17T00:00:00.000Z Created NewNewURLlist1 applied regex xsoar.com,
qmasters.com

netskope-url-list-delete


Delete a URL list by the list ID.

Base Command

netskope-url-list-delete

Input

Argument Name Description Required
url_list_id The URL list ID to delete (use netskope-url-list-list to get the URL list ID). Required
deploy Whether to deploy URL list changes or not. Default is False. Possible values are: True, False. Optional

Context Output

Path Type Description
Netskope.URLList.id Number Netskope URL list ID.
Netskope.URLList.name String Netskope URL list name.

Command example

!netskope-url-list-delete url_list_id=10

Context Example

{
    "Netskope": {
        "URLList": {
            "data": {
                "json_version": 2,
                "type": "regex",
                "urls": ["xsoar.com", "qmasters.com"]
            },
            "id": 10,
            "modify_by": "Netskope REST API",
            "modify_time": "2023-07-17T00:00:00.000Z",
            "modify_type": "Deleted",
            "name": "TalURLlist",
            "pending": 1
        }
    }
}

Human Readable Output

The URL list 10 was deleted successfully

netskope-client-list


Get information about Netskope SCIM users. The command provides a list of users who have been imported into the Netskope tenant through SCIM integration. Users imported through other methods, such as manual CSV import or manual creation, will not be included in the returned results.

Base Command

netskope-client-list

Input

Argument Name Description Required
filter Filter the Netskope user by ‘key eq value’ template. For example: userName eq “someUserName” OR externalId eq “User-Ext_id”. Optional
page Page number of paginated results. Minimum value: 1. Optional
limit The maximum number of records to retrieve. Default is 50. Optional

Context Output

Path Type Description
Netskope.Client.id Number Netskope client ID.
Netskope.Client.name String Netskope client name.
Netskope.Client.userName String Netskope client username.
Netskope.Client.externalId String Netskope client external ID.
Netskope.Client.active Boolean Netskope client activate.
Netskope.Client.emails String Netskope client emails.

Command example

!netskope-client-list page=1 limit=2

Context Example

{
    "Netskope": {
        "Client": [
            {
                "active": true,
                "client_id": "6a4dbb07-f465-4a6c-8af2-1c84ced65010",
                "emails": ["email1@netskope.local"],
                "family_name": "last_name",
                "given_name": "first_name",
                "user_name": "upn1"
            },
            {
                "active": true,
                "client_id": "f8d26597-e4a4-400d-a24b-40318a9e80e5",
                "emails": ["email11@netskope.local"],
                "family_name": "last_name1",
                "given_name": "first_name1",
                "user_name": "upn2"
            }
        ]
    }
}

Human Readable Output

Client List

Showing page 1.
Current page size: 2.

Client Id User Name Given Name Family Name Emails Active
6a4dbb07-f465-4a6c-8af2-1c84ced65010 upn1 first_name last_name email1@netskope.local true
f8d26597-e4a4-400d-a24b-40318a9e80e5 upn2 first_name1 last_name1 email11@netskope.local true

netskope-url-list-add


Update the URL list with the values provided. Note that this command appends the list.

Base Command

netskope-url-list-add

Input

Argument Name Description Required
url_list_id The URL list ID to update (use netskope-url-list-list command to get URL list ID). Required
urls The updated URL list items (For Exact - Enter URLs like .example.com, or IP addresses, separated by a new line. For Regex - Enter URLs like ^client[0-9]\.google\.com , ^app\.slack\.com/./netskope, or ^google.com, separated by a new line). Required
list_type The updated URL list type. Possible values are: exact, regex. Required
deploy Whether to deploy URL list changes or not. Possible values are: True, False. Default is false. Optional

Context Output

Path Type Description
Netskope.URLList.id Number Netskope URL list ID.
Netskope.URLList.name String Netskope URL list name.
Netskope.URLList.data.urls String Netskope URL list data URLs.
Netskope.URLList.data.type String Netskope URL list data type.
Netskope.URLList.data.json_version Number Netskope URL list data JSON version.
Netskope.URLList.modify_by String Netskope URL list modify by.
Netskope.URLList.modify_time Date Netskope URL list modify time.
Netskope.URLList.modify_type String Netskope URL list modify type.
Netskope.URLList.pending Number Netskope URL list pending.

netskope-incident-dlp-list


Fetch DLP incidents.

Base Command

netskope-incident-dlp-list

Input

Argument Name Description Required
start_time Restrict incidents to those that have updated dates greater than the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). Default is ‘1 hour ago’. Default is 1 hour ago. Optional
end_time Restrict incidents to those that have updated dates less than or equal to the provided date string (for example “YYYY-MM-DDThh:mm”, “1 min ago”, “2 weeks ago”). Default is ‘now’. Default is now. Optional

Context Output

Path Type Description
Netskope.Incident._id String  
Netskope.Incident.access_method String  
Netskope.Incident.acting_user String  
Netskope.Incident.activity String  
Netskope.Incident.app String  
Netskope.Incident.app_session_id Number  
Netskope.Incident.assignee String  
Netskope.Incident.connection_id Number  
Netskope.Incident.dlp_incident_id Number  
Netskope.Incident.dlp_match_info Unknown  
Netskope.Incident.dlp_match_info.dlp_action String  
Netskope.Incident.dlp_match_info.dlp_forensic_id Number  
Netskope.Incident.dlp_match_info.dlp_policy String  
Netskope.Incident.dlp_match_info.dlp_profile_name String  
Netskope.Incident.dlp_match_info.dlp_rules Unknown  
Netskope.Incident.dlp_match_info.dlp_rules.dlp_incident_rule_count Number  
Netskope.Incident.dlp_match_info.dlp_rules.dlp_match_type String  
Netskope.Incident.dlp_match_info.dlp_rules.dlp_rule_name String  
Netskope.Incident.dlp_match_info.dlp_rules.dlp_rule_severity String  
Netskope.Incident.dlp_parent_id Number  
Netskope.Incident.dst_location String  
Netskope.Incident.file_lang String  
Netskope.Incident.file_size Number  
Netskope.Incident.file_type String  
Netskope.Incident.from_user String  
Netskope.Incident.instance_id String  
Netskope.Incident.md5 String  
Netskope.Incident.object String  
Netskope.Incident.object_id String  
Netskope.Incident.object_type String  
Netskope.Incident.severity String  
Netskope.Incident.site String  
Netskope.Incident.src_location String  
Netskope.Incident.status String  
Netskope.Incident.timestamp Number  
Netskope.Incident.title String  
Netskope.Incident.true_obj_category String  
Netskope.Incident.true_obj_type String  
Netskope.Incident.url String  
Netskope.Incident.user String  
Netskope.Incident.owner_pdl String  
Netskope.Incident.classification String  
Netskope.Incident.to_user String  
Netskope.Incident.cc String  
Netskope.Incident.owner String  
Netskope.Incident.destination_site String  
Netskope.Incident.user_id String  
Netskope.Incident.instance String  
Netskope.Incident.channel String  
Netskope.Incident.bcc String  
Netskope.Incident.inline_dlp_match_info Unknown  
Netskope.Incident.exposure String  
Netskope.Incident.file_path String  
Netskope.Incident.original_file_snapshot_id String  
Netskope.Incident.destination_app String  
Netskope.Incident.latest_incident_id Number  
Netskope.Incident.dlp_file String  
Netskope.Incident.zip_file_id String  
Netskope.Incident.referer String  
Netskope.Incident.destination_instance_id String  

Command example

!netskope-incident-dlp-list

Context Example

{
    "Netskope": {
        "Incident": [
            {
                "_id": "dg",
                "access_method": "Client",
                "acting_user": "d@d.co",
                "activity": "Upload",
                "app": "Microsoft Office 365 OneDrive for Business",
                "app_session_id": 6089117609268013425,
                "assignee": "d@d.co",
                "connection_id": 1888383367374049276,
                "dlp_incident_id": 7061311227778495851,
                "dlp_match_info": [
                    {
                        "dlp_action": "block",
                        "dlp_forensic_id": 7061311227778495851,
                        "dlp_policy": "DLP test policy - Beni",
                        "dlp_profile_name": "test dlp profile -beni",
                        "dlp_rules": [
                            {
                                "dlp_incident_rule_count": 1,
                                "dlp_match_type": "FileFilter",
                                "dlp_rule_name": "test dlp profile - beni",
                                "dlp_rule_severity": "Medium"
                            }
                        ]
                    }
                ],
                "dlp_parent_id": 7061311227778495851,
                "dst_location": "Redmond",
                "file_lang": "Unknown",
                "file_size": 10,
                "file_type": "text/plain",
                "from_user": "d@d.co",
                "instance_id": "d",
                "md5": "fsd",
                "object": "d.txt",
                "object_id": "01",
                "object_type": "File",
                "severity": "Critical",
                "site": "Microsoft Office 365 OneDrive for Business",
                "src_location": "Tel Aviv",
                "status": "in_progress",
                "timestamp": 1710928898,
                "title": "d.txt",
                "true_obj_category": "Text",
                "true_obj_type": "Plain Text file",
                "url": "v1.g.f",
                "user": "sdf",
                "owner_pdl": "",
                "classification": "",
                "to_user": "",
                "cc": "",
                "owner": "",
                "destination_site": "",
                "user_id": "",
                "instance": "",
                "channel": "",
                "bcc": "",
                "inline_dlp_match_info": [],
                "exposure": "",
                "file_path": "",
                "original_file_snapshot_id": "",
                "destination_app": "",
                "latest_incident_id": 0,
                "dlp_file": "",
                "zip_file_id": "",
                "referer": "",
                "destination_instance_id": ""
            }
        ]
    }
}

Human Readable Output

Client List

Showing page 1.
Current page size: 2.

Object Id Status Severity Activity Assignee Timestamp Acting User App Instance Id Object Type
12 new High FormPost None 1725903291 None Form    

get-mapping-fields


Returns the list of fields for an incident type.

Base Command

get-mapping-fields

Input

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

Context Output

There is no context output for this command.

update-remote-system


Updates the remote incident or detection with local incident or detection changes. This method is only used for debugging purposes and will not update the current incident or detection.

Base Command

update-remote-system

Input

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

Context Output

There is no context output for this command.

get-remote-data


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

Base Command

get-remote-data

Input

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

Context Output

There is no context output for this command.

get-modified-remote-data


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

Base Command

get-modified-remote-data

Input

Argument Name Description Required
lastUpdate A date string in local time representing the last time the incident was updated. The incident is only returned if it was modified after the last update time. Optional

Context Output

There is no context output for this command.

Incident Mirroring

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

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

    Option Description
    None Turns off incident mirroring.
    Incoming Any changes in Netskope (API v2) LOCAL events (mirroring incoming fields) will be reflected in Cortex XSOAR incidents.
    Outgoing Any changes in Cortex XSAIM/XSOAR incidents will be reflected in Netskope (API v2) LOCAL events (outgoing mirrored fields).
    Incoming and Outgoing  
  3. Optional: Check the Close Mirrored XSAIM/XSOAR Incident integration parameter to close the Cortex XSAIM/XSOAR incident when the corresponding event is closed in Netskope (API v2) LOCAL.

Newly fetched incidents will be mirrored in the chosen direction. However, this selection does not affect existing incidents.
Important Note: To ensure the mirroring works as expected, mappers are required, both for incoming and outgoing, to map the expected fields in Cortex XSAIM/XSOAR and Netskope (API v2) LOCAL.

Configuration parameters

  • url — Server URL (required)
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)
  • credentials — (required)
  • first_fetch — First fetch timestamp
  • max_fetch — Maximum incidents per fetch
  • max_events_fetch — Maximum Netskope events per fetch. Max value is 200.
  • max_dlp_incidents_fetch — Maximum Netskope DLP incidents per fetch. Max value is 200.
  • fetch_events — Fetch Events
  • fetch_dlp_incidents — Fetch DLP incidents
  • event_types — Event types to fetch.
  • alerts_query — Alerts Query
  • events_query — Events Query
  • incidentType — Incident type
  • isFetch — Fetch incidents
  • user_email — User Email
  • incidentFetchInterval — Incidents Fetch Interval
  • mirror_direction — Incident Mirroring Direction
  • close_incident — Close Mirrored XSOAR Incident
  • close_netskope_incident — Close Mirrored Netskope Incident

Commands (13)

  • get-mapping-fields

    Returns the list of fields for an incident type.

  • get-modified-remote-data

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

  • get-remote-data

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

  • netskope-alert-list

    Get alerts generated by Netskope. You may choose what alerts to receive with the alert_type parameter. You must provide start_time and end_time, or insertion_start_time and insertion_end_time. (Note that if end_time or insertion_end_time aren't provided. the date time would be set as now.) You cannot provide a combination of the options mentioned above.

  • netskope-client-list

    Get information about Netskope SCIM users. The command provides a list of users who have been imported into the Netskope tenant through SCIM integration. Users imported through other methods, such as manual CSV import or manual creation, will not be included in the returned results.

  • netskope-event-list

    Get events extracted from SaaS traffic. You may choose what events to receive with the event_type parameter. You must provide start_time and end_time, or insertion_start_time and insertion_end_time. (Note that if end_time or insertion_end_time aren't provided, the date time would be set as now.) You cannot provide a combination of the options mentioned above.

  • netskope-incident-dlp-list

    Fetch DLP incidents by update time. The maximum recommended time range should be no more from 3 days.

  • netskope-url-list-add

    Update the URL list with the values provided. Note that this command appends the list.

  • netskope-url-list-create

    Create a new URL list.

  • netskope-url-list-delete

    Delete a URL list by the list ID.

  • netskope-url-list-update

    Update the URL list with the values provided. Note that this command overrides the list.

  • netskope-url-lists-list

    Get all URL lists or a specific URL list by specifying the list ID.

  • update-remote-system

    Updates the remote incident or detection with local incident or detection changes. This method is only used for debugging purposes and will not update the current incident or detection.

import copy
from collections.abc import Callable
from http import HTTPStatus
from typing import Any, NamedTuple

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401

MAX_IDS_NUMBER = 289262
DEFAULT_WAIT_TIME = 5
MIN_PAGE_NUM = 1
MAX_PAGE_SIZE = 50
MIN_PAGE_SIZE = 1
MAX_LIMIT = 50
MIN_LIMIT = 1
MAX_FETCH_PER_EVENT_TYPE = 50
JS_NUMBER_LIMIT = 2**53 - 1
TIME_FORMAT = "%d-%m-%Y %H:%M"

ALERT_HEADERS = [
    "alert_id",
    "alert_name",
    "alert_type",
    "severity",
    "action",
    "activity",
    "type",
    "acked",
    "_category_name",
    "_event_id",
    "domain",
    "dst_country",
    "policy",
    "port",
    "protocol",
    "md5",
    "timestamp",
]
EVENT_HEADERS = {
    "application": [
        "event_id",
        "alert",
        "action",
        "activity",
        "category",
        "hostname",
        "policy",
        "severity",
        "type",
        "useragent",
        "timestamp",
    ],
    "page": [
        "event_id",
        "category",
        "domain",
        "page",
        "type",
        "user",
        "appcategory",
        "bypass_reason",
        "src_location",
        "srcip",
        "dst_location",
        "dstip",
        "timestamp",
    ],
    "audit": [
        "event_id",
        "audit_log_event",
        "severity_level",
        "count",
        "supporting_data",
        "timestamp",
    ],
    "infrastructure": [
        "event_id",
        "hostname",
        "timestamp",
        "serial",
        "metric_name",
        "metric_value",
    ],
    "network": [
        "event_id",
        "category",
        "domain",
        "page",
        "type",
        "user",
        "policy",
        "action",
        "protocol",
        "src_location",
        "srcip",
        "dst_location",
        "dstip",
        "timestamp",
    ],
}
URL_HEADER = [
    "id",
    "name",
    "urls",
    "type",
    "pending",
    "modify_by",
    "modify_time",
    "modify_type",
]
INCIDENT_HEADERS = [
    "object_id",
    "status",
    "severity",
    "activity",
    "assignee",
    "timestamp",
    "acting_user",
    "app",
    "instance_id",
    "object_type",
]
MIRRORING_FIELDS = ["status", "severity", "original_status", "original_severity"]

MIRROR_DIRECTION_MAPPING = {
    "Incoming": "In",
    "Outgoing": "Out",
    "Incoming and Outgoing": "Both",
}


class Pagination(NamedTuple):
    updated_page: int
    limit: int
    pagination_message: str


class TimeArgs(NamedTuple):
    start_time_number: int | None
    end_time_number: int | None
    insertion_start_time_number: int | None
    insertion_end_time_number: int | None


class Client(BaseClient):
    def __init__(
        self,
        server_url: str,
        api_token: str,
        verify: bool = False,
        proxy: bool = False,
        user_mail: str | None = None,
    ):
        self.user_mail = user_mail
        headers = {"Netskope-Api-Token": api_token}
        super().__init__(base_url=server_url, verify=verify, proxy=proxy, headers=headers)

    def list_alert(
        self,
        page: int,
        limit: int,
        alert_type: str | None = None,
        query: str | None = None,
        acked: bool | None = None,
        start_time: int | None = None,
        end_time: int | None = None,
        insertion_start_time: int | None = None,
        insertion_end_time: int | None = None,
    ) -> dict[str, Any]:
        """Get alerts generated by Netskope.

        Args:
            page (int): Page number of paginated results.
            limit (int): The maximum number of records to retrieve.
            alert_type (Optional[str], optional): Alert type. Defaults to None.
            query (Optional[str], optional): Free query to filter the alerts.
                Defaults to None.
            acked (Optional[bool], optional): Whether to retrieve acknowledged alerts or not.
                Defaults to None.
            start_time (Optional[int], optional): Restrict alerts to those that have dates
                greater than the provided date. Defaults to None.
            end_time (Optional[int], optional): Restrict alerts to those that have dates less than or equal to the
                provided date. Defaults to None.
            insertion_start_time (Optional[int], optional): Restrict alerts to those that were inserted to the system
                after the provided date. Defaults to None.
            insertion_end_time (Optional[int], optional): Restrict alerts to those that were inserted to the system
                before the provided date. Defaults to None.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        params = assign_params(
            limit=limit,
            offset=page,
            starttime=start_time,
            endtime=end_time,
            insertionstarttime=insertion_start_time,
            insertionendtime=insertion_end_time,
            type=alert_type,
            query=query,
            acked=acked,
        )

        return self._http_request("GET", "api/v2/events/data/alert", params=params)

    def list_event(
        self,
        page: int,
        limit: int,
        event_type: str,
        start_time: int | None = None,
        end_time: int | None = None,
        insertion_start_time: int | None = None,
        insertion_end_time: int | None = None,
        query: str | None = None,
    ) -> dict[str, Any]:
        """Get events from Netskope.

        Args:
            page (int): Page number of paginated results.
            limit (int): The maximum number of records to retrieve.
            event_type (str): Event type.
            start_time (Optional[int], optional): Restrict events to those that have dates greater than the provided
                date. Defaults to None.
            end_time (Optional[int], optional): Restrict events to those that have dates less than or equal to the
                provided date . Defaults to None.
            insertion_start_time (Optional[int], optional): Restrict events to those that were inserted to the system
                after the provided date. Defaults to None.
            insertion_end_time (Optional[int], optional): Restrict events to those that were inserted to the system
                before the provided date. Defaults to None.
            query (Optional[str], optional): Free query to filter the events. Defaults to None.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        params = assign_params(
            limit=limit,
            offset=page,
            starttime=start_time,
            endtime=end_time,
            insertionstarttime=insertion_start_time,
            insertionendtime=insertion_end_time,
            query=query,
        )

        return self._http_request("GET", f"api/v2/events/data/{event_type}", params=params)

    def update_url_list(
        self,
        url_list_id: str,
        name: str | None = None,
        urls: list[str] | None = None,
        list_type: str | None = None,
    ) -> dict[str, Any]:
        """Update (override) the given URL list.

        Args:
            url_list_id (str): URL list ID.
            name (str): URL list name.
            urls (List[str]): URL lists.
            list_type (str): URL list type.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        data = {
            "data": {"type": list_type, "urls": urls},
            "name": name,
        }

        return self._http_request("PUT", f"api/v2/policy/urllist/{url_list_id}", json_data=data)

    def patch_url_list(
        self,
        url_list_id: str,
        urls: list[str] | None = None,
        list_type: str | None = None,
    ) -> dict[str, Any]:
        """Update the given URL list.

        Args:
            url_list_id (str): URL list ID.
            urls (List[str]): URL lists.
            list_type (str): URL list type.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        data = {"data": {"type": list_type, "urls": urls}}

        return self._http_request("PATCH", f"api/v2/policy/urllist/{url_list_id}/append", json_data=data)

    def create_url_list(
        self,
        name: str,
        urls: list[str],
        list_type: str,
    ) -> dict[str, Any]:
        """Create URL list.

        Args:
            name (str): URL list name.
            urls (List[str]): URL lists.
            list_type (str): URL list type.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        data = {
            "data": {"type": list_type, "urls": urls},
            "name": name,
        }

        return self._http_request("POST", "api/v2/policy/urllist", json_data=data)

    def list_url_list(
        self,
        url_list_id: str | None,
        pending: int | None = None,
        field: list[str] | None = None,
    ) -> dict[str, Any]:
        """Get all URL Lists or a specific URL list by the list ID.

        Args:
            url_list_id (Optional[str]): _description_
            pending (Optional[int]): _description_
            field (List[str]): _description_

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        params = assign_params(
            pending=pending,
            field=field,
        )

        url_suffix = f"/{url_list_id}" if url_list_id else ""

        return self._http_request(
            "GET",
            f"api/v2/policy/urllist{url_suffix}",
            params=params,
        )

    def delete_url_list(self, url_list_id: str) -> dict[str, Any]:
        """Delete a URL list by the list ID.

        Args:
            url_list_id (str): URL list ID.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        return self._http_request(
            "DELETE",
            f"api/v2/policy/urllist/{url_list_id}",
        )

    def list_client(
        self,
        page: int,
        limit: int,
        client_filter: str | None = None,
    ) -> dict[str, Any]:
        """Get information about Netskope SCIM users.

        Args:
            page (int): Page number of paginated results.
            limit (int): The maximum number of records to retrieve.
            client_filter (str, optional): Filter the Netskope user by 'key eq value' template. Defaults to None.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        params = assign_params(count=limit, startIndex=page, filter=client_filter)

        return self._http_request(
            "GET",
            "api/v2/scim/Users",
            params=params,
        )

    def deploy_url_list(self) -> dict[str, Any]:
        """Deploy URL list changes.

        Returns:
            Dict[str, Any]: API response from Netskope.
        """

        return self._http_request(
            "POST",
            "api/v2/policy/urllist/deploy",
        )

    def list_dlp_incident(self, timestamp: int) -> dict[str, Any]:
        """Fetch DLP incidents.

        Args:
            timestamp (int): The timestamp to filter by.

        Returns:
            dict[str, Any]: The API response with large integers converted to strings.
        """
        response = self._http_request(
            "GET",
            url_suffix="api/v2/events/dataexport/events/incident",
            params={"operation": timestamp},
            retries=10,
            status_list_to_retry=[409, 429],
            backoff_factor=DEFAULT_WAIT_TIME,
        )

        # Convert large integers to strings to prevent JavaScript precision loss
        if incidents := response.get("result"):
            for incident in incidents:
                convert_large_integers_to_strings(incident)

        return response

    def update_dlp_incident(
        self,
        object_id: str,
        field: str,
        old_value: str,
        new_value: str,
    ) -> dict[str, Any]:
        """Update DLP incident.

        Args:
            object_id (str): The incident object ID.
            field (str): The field to update.
            old_value (str): The old value.
            new_value (str): The new value.

        Returns:
            dict[str, Any]: The API response.
        """
        return self._http_request(
            "PATCH",
            url_suffix="api/v2/incidents/update",
            json_data=remove_empty_elements(
                {
                    "payload": [
                        {
                            "object_id": object_id,
                            "field": field,
                            "old_value": old_value,
                            "new_value": new_value,
                            "user": self.user_mail,
                        }
                    ]
                }
            ),
        )


def list_alert_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Get alerts generated by Netskope.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """
    pagination_data = pagination(args)

    page = pagination_data.updated_page
    limit = pagination_data.limit
    pagination_message = pagination_data.pagination_message

    alert_type = args.get("alert_type")
    query = args.get("query")
    acked = optional_arg_to_boolean(args.get("retrieve_acknowledged"))

    time_args = convert_time_args_to_num(args)

    start_time = time_args.start_time_number
    end_time = time_args.end_time_number
    insertion_start_time = time_args.insertion_start_time_number
    insertion_end_time = time_args.insertion_end_time_number

    response = client.list_alert(
        page=page,
        limit=limit,
        start_time=start_time,
        end_time=end_time,
        insertion_start_time=insertion_start_time,
        insertion_end_time=insertion_end_time,
        alert_type=alert_type,
        query=query,
        acked=acked,
    )

    output = response["result"]

    for alert in output:
        alert["alert_id"] = alert["_id"]
        alert["timestamp"] = timestamp_to_datestring(alert["timestamp"] * 1000)
        convert_large_integers_to_strings(alert)

    readable_output = tableToMarkdown(
        name="Alert List",
        metadata=pagination_message,
        t=remove_empty_elements(output),
        headers=ALERT_HEADERS,
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Netskope.Alert",
        outputs_key_field="id",
        outputs=output,
        raw_response=response,
    )


def list_event_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Get events extracted from SaaS traffic.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """

    pagination_data = pagination(args)

    page = pagination_data.updated_page
    limit = pagination_data.limit
    pagination_message = pagination_data.pagination_message

    event_type = args["event_type"]
    query = args.get("query")

    time_args = convert_time_args_to_num(args)

    start_time = time_args.start_time_number
    end_time = time_args.end_time_number
    insertion_start_time = time_args.insertion_start_time_number
    insertion_end_time = time_args.insertion_end_time_number

    response = client.list_event(
        page=page,
        limit=limit,
        start_time=start_time,
        end_time=end_time,
        insertion_start_time=insertion_start_time,
        insertion_end_time=insertion_end_time,
        event_type=event_type,
        query=query,
    )

    output = response["result"]

    for event in output:
        event["event_id"] = event["_id"]
        event["timestamp"] = timestamp_to_datestring(event["timestamp"] * 1000)
        convert_large_integers_to_strings(event)

    readable_output = tableToMarkdown(
        name="Event List",
        metadata=pagination_message,
        t=remove_empty_elements(output),
        headers=EVENT_HEADERS[event_type],
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Netskope.Event",
        outputs_key_field="id",
        outputs=output,
        raw_response=response,
    )


def update_url_list_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Update (override) URL List.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """
    url_list_id = args["url_list_id"]
    name = args.get("name")
    urls = argToList(args.get("urls"))
    list_type = args.get("list_type", "").lower() or None
    is_overwrite = optional_arg_to_boolean(args.get("is_overwrite"))

    if not all([name, urls, list_type]) or not is_overwrite:
        list_response = client.list_url_list(url_list_id=url_list_id)

        name = name or list_response.get("name")
        list_type = list_type or dict_safe_get(list_response, ["data", "type"])
        exist_urls = dict_safe_get(list_response, ["data", "urls"])

        if not is_overwrite:
            urls += exist_urls
        else:
            urls = urls or exist_urls

    response = client.update_url_list(
        url_list_id,
        name,
        urls,
        list_type,
    )

    deploy_url_list_if_required(args, client.deploy_url_list)
    output = get_updated_url_list(response)

    readable_output = tableToMarkdown(
        name="URL list was updated successfully",
        t=remove_empty_elements(output),
        headers=URL_HEADER,
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Netskope.URLList",
        outputs_key_field="id",
        outputs=output,
        raw_response=response,
    )


def add_url_list_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Update URL List.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """
    url_list_id = args["url_list_id"]
    urls = argToList(args.get("urls"))
    list_type = args.get("list_type", "").lower() or None

    response = client.patch_url_list(url_list_id, urls, list_type)

    deploy_url_list_if_required(args, client.deploy_url_list)
    output = get_updated_url_list(response)

    readable_output = tableToMarkdown(
        name="URL list was updated successfully",
        t=remove_empty_elements(output),
        headers=URL_HEADER,
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Netskope.URLList",
        outputs_key_field="id",
        outputs=output,
        raw_response=response,
    )


def create_url_list_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Create a new URL list.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """

    name = args["name"]
    urls = argToList(args["urls"])
    list_type = args.get("list_type", "").lower()

    response = client.create_url_list(
        name,
        urls,
        list_type,
    )
    deploy_url_list_if_required(args, client.deploy_url_list)
    output = get_updated_url_list(response)

    readable_output = tableToMarkdown(
        name="URL list was created successfully",
        t=remove_empty_elements(output),
        headers=URL_HEADER,
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Netskope.URLList",
        outputs_key_field="id",
        outputs=output,
        raw_response=response,
    )


def lists_url_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Get all URL Lists or a specific URL list.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """

    url_list_id = args.get("url_list_id")
    pending = args.get("pending")
    fields = argToList(args.get("field"))
    all_results = optional_arg_to_boolean(args.get("all_results"))
    pending = int(pending == "pending")

    updated_fields = []
    updated_fields = ["data" if field in ("urls", "type") else field for field in fields]

    response = client.list_url_list(url_list_id=url_list_id, pending=pending, field=updated_fields)
    output = get_updated_url_list(response)

    if not all_results:
        limit = arg_to_number(args["limit"])
        output = output[:limit]

    readable_output = tableToMarkdown(
        name="URL Lists",
        t=remove_empty_elements(output),
        headers=fields or URL_HEADER,
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Netskope.URLList",
        outputs_key_field="id",
        outputs=response,
        raw_response=response,
    )


def delete_url_list_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Delete a URL list by the list ID.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """

    url_list_id = args["url_list_id"]

    response = client.delete_url_list(url_list_id)
    deploy_url_list_if_required(args, client.deploy_url_list)

    return CommandResults(
        readable_output=f"The URL list {url_list_id} was deleted successfully",
        outputs_prefix="Netskope.URLList",
        outputs_key_field="id",
        outputs=response,
        raw_response=response,
    )


def list_client_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Get information about Netskope SCIM users.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: outputs, readable outputs and raw response for XSOAR.
    """
    pagination_data = pagination(args, False)

    page = pagination_data.updated_page
    limit = pagination_data.limit
    pagination_message = pagination_data.pagination_message

    client_filter = args.get("filter")
    updated_client_filter = convert_client_filter(client_filter) if client_filter else None

    response = client.list_client(page, limit, updated_client_filter)
    output = response.get("Resources", [])
    output = get_updated_list_client(output)

    readable_output = tableToMarkdown(
        name="Client List",
        t=remove_empty_elements(output),
        metadata=pagination_message,
        headers=[
            "client_id",
            "external_id",
            "user_name",
            "given_name",
            "family_name",
            "emails",
            "active",
        ],
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Netskope.Client",
        outputs_key_field="id",
        outputs=output,
        raw_response=response,
    )


def list_dlp_incident_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """Fetch DLP incidents.

    Args:
        client (Client): A session to run HTTP requests to the API.
        args (dict[str, Any]): Arguments passed down by the CLI to configure the request.

    Returns:
        CommandResults: Outputs of the command that represent an entry in the warroom.
    """
    start_time_dt = arg_to_datetime(args["start_time"])
    end_time_dt = arg_to_datetime(args["end_time"])

    if not start_time_dt or not end_time_dt:
        raise DemistoException("Time argument is required.")

    start_time = int(start_time_dt.timestamp())
    end_time = int(end_time_dt.timestamp())

    incidents = []
    hourly_timestamps = get_hourly_timestamps(
        start_time=start_time,
        end_time=end_time,
    )

    for timestamp in hourly_timestamps:
        response = client.list_dlp_incident(timestamp=timestamp)

        if dlp_incidents := response["result"]:
            incidents += dlp_incidents

    incidents = remove_duplicates(incidents, "object_id")

    readable_output = tableToMarkdown(
        name="DLP Incident List:",
        t=incidents,
        headers=INCIDENT_HEADERS,
        headerTransform=string_to_table_header,
    )

    return CommandResults(
        raw_response=incidents,
        outputs=incidents,
        outputs_key_field="object_id",
        outputs_prefix="Netskope.Incident",
        readable_output=readable_output,
    )


def test_module(client: Client) -> str:
    try:
        client.list_client(1, 1)
        return "ok"

    except DemistoException as exc:
        if exc.res is not None and exc.res.status_code in [
            HTTPStatus.UNAUTHORIZED,
            HTTPStatus.NOT_FOUND,
            HTTPStatus.FORBIDDEN,
        ]:
            return "Authorization Error: Unknown API key or Netskope URL"

        return exc.message


def fetch_incidents(client: Client, params: dict[str, Any]):
    """Fetch Netskope alerts, DLP incidents, and events (if requested) as incidents in XSOAR.
        In case of DLP incidents: the incidents will fetch by timestamp and will saved in XSOAR
        and integration context to handle duplicates issue.

    Args:
        client (Client): Netskope API client.
        params (Dict[str, Any]): Integration parameters.
    """

    incidents: list[dict] = []
    last_run: dict[str, Any] = {}

    mirror_direction = MIRROR_DIRECTION_MAPPING.get(params["mirror_direction"], None)

    event_types = argToList(params.get("event_types"))
    alert_query = params.get("alerts_query")
    alert_max_fetch = arg_to_number(params.get("max_fetch") or 50) or MAX_LIMIT

    num_event_types = len(event_types) if event_types else 1
    max_fetch_per_event_type = MAX_FETCH_PER_EVENT_TYPE // num_event_types
    if event_types and (event_max_fetch := arg_to_number(params.get("max_events_fetch") or 50)) is not None:
        max_fetch_per_event_type = event_max_fetch // (len(event_types))

    if params.get("fetch_events") and event_types:
        events, events_last_run = fetch_events_as_incidents(
            list_event_func=client.list_event,
            params=params,
            max_fetch_per_event_type=max_fetch_per_event_type,
            last_run=last_run,
            mirror_direction=mirror_direction,
        )
        incidents += events
        last_run.update(events_last_run)

    if params.get("fetch_dlp_incidents"):
        dlp_incidents, dlp_incidents_last_run = fetch_dlp_incidents_as_incidents(
            list_event_func=client.list_dlp_incident,
            params=params,
            last_run=last_run,
            mirror_direction=mirror_direction,
        )
        incidents += dlp_incidents
        last_run.update(dlp_incidents_last_run)

    alerts, alerts_last_run = fetch_alerts_as_incidents(
        list_event_func=client.list_alert,
        params=params,
        max_fetch=alert_max_fetch,
        alert_query=alert_query,
        last_run=last_run,
        mirror_direction=mirror_direction,
    )

    incidents += alerts
    last_run.update(alerts_last_run)

    return last_run, incidents


def get_modified_remote_data(client: Client) -> GetModifiedRemoteDataResponse:
    """
    Queries for incidents that were modified since the last update.

    Args:
        client: Netskope API client.

    Returns:
        GetModifiedRemoteDataResponse: modified tickets from Netskope.
    """
    # get the fetch last run time to check if exist incidents was updated for sync incidents
    start_timestamp = get_demisto_integration_context("dlp_incident_last_run_timestamp", None)

    end_timestamp = date_to_seconds_timestamp(datetime.now())

    if not start_timestamp:
        start_timestamp = end_timestamp - 600

    hourly_timestamps = get_hourly_timestamps(
        start_time=start_timestamp,
        end_time=end_timestamp,
    )

    demisto.debug(f"Get modified remote data from {start_timestamp} - {end_timestamp}")

    dlp_incidents = []
    modified_tickets = []

    for timestamp in hourly_timestamps:
        response = client.list_dlp_incident(timestamp=timestamp)
        if data := response["result"]:
            dlp_incidents += data

    dlp_incidents = remove_duplicates(dlp_incidents, "object_id")

    # save the updated incidents for get_remote_data_command
    set_demisto_integration_context("dlp_incidents_to_update", dlp_incidents, "append")

    for ticket in dlp_incidents:
        modified_tickets.append(ticket["object_id"])

    demisto.debug(f"There are {len(modified_tickets)} modified incidents from Netskope")

    return GetModifiedRemoteDataResponse(modified_tickets)


def get_mapping_fields_command() -> GetMappingFieldsResponse:
    """
    Pulls the remote schema for the different incident types, and their associated incident fields, from the remote system.

    Returns:
    GetMappingFieldsResponse: Dictionary with keys as field names.
    """
    demisto.debug("Get Netskope mapping fields")
    mapping_response = GetMappingFieldsResponse()

    incident_type_scheme = SchemeTypeMapping(type_name="Netskope Incident")

    for field in MIRRORING_FIELDS:
        incident_type_scheme.add_field(field)

    mapping_response.add_scheme_type(incident_type_scheme)

    return mapping_response


def update_remote_system(
    client: Client,
    args: dict[str, Any],
    params: dict[str, Any],
) -> str:
    """
    This command pushes local changes to the remote system.
    Args:
        client: XSOAR Client to use.
        args:
            args['data']: the data to send to the remote system.
            args['entries']: the entries to send to the remote system.
            args['incident_changed']: boolean telling us if the local incident indeed changed or not.
            args['remote_incident_id']: the remote incident id.
    Returns: The remote incident id - ticket_id
    """
    parsed_args = UpdateRemoteSystemArgs(args)
    incident_id = parsed_args.remote_incident_id

    demisto.debug(
        f"Got the following delta keys {list(parsed_args.delta.keys())!s}"
        if parsed_args.delta
        else "There is no delta fields in Netskope"
    )

    try:
        if parsed_args.incident_changed and parsed_args.delta:
            demisto.debug(f"Incident changed: {parsed_args.incident_changed}, {parsed_args.delta=}, {parsed_args.inc_status=}")

            update_args: dict[str, Any] = parsed_args.delta
            # fetch old arguments data for API call
            old_args: dict[str, Any] = parsed_args.data

            updated_arguments = {}

            if parsed_args.inc_status == IncidentStatus.DONE and params.get("close_netskope_incident"):
                updated_arguments["field"] = "status"
                updated_arguments["old_value"] = old_args["original_status"]
                updated_arguments["new_value"] = "closed"
                updated_arguments["object_id"] = incident_id

            else:
                for key, value in update_args.items():
                    if key in MIRRORING_FIELDS:
                        updated_arguments["field"] = key
                        updated_arguments["old_value"] = old_args[f"original_{key}"]
                        updated_arguments["new_value"] = value
                        updated_arguments["object_id"] = incident_id

            if updated_arguments:
                demisto.debug(f"Send remote ID [{incident_id}] updates to Netskope. {updated_arguments=}|| {update_args=}")

                client.update_dlp_incident(**updated_arguments)

    except Exception as error:
        demisto.info(f"Error in Netskope outgoing mirror for incident {incident_id}. Error message: {error}")

    finally:
        return incident_id


def get_remote_data_command(
    args: dict[str, Any],
    params: dict[str, Any],
) -> GetRemoteDataResponse:
    """
    Gets new information about the incidents in the remote system
    and updates existing incidents in Cortex XSOAR.
    Args:
        args (Dict[str, Any]): command arguments.
        params (Dict[str, Any]): command parameters.
    Returns:
        List[Dict[str, Any]]: first entry is the incident (which can be completely empty) and the new entries.
    """
    parsed_args = GetRemoteDataArgs(args)
    incident_id = parsed_args.remote_incident_id

    demisto.debug(f"Check if incident {incident_id} updated")

    # fetch updated incidents (that saved in get_modified_remote_data command)
    dlp_incidents: list = get_demisto_integration_context("dlp_incidents_to_update", [])

    mirrored_ticket = {}

    for incident in dlp_incidents:
        # get the incident data by the relevant incident ID
        if incident["object_id"] == incident_id:
            mirrored_ticket = incident
            # remove the incident from integration context
            dlp_incidents.remove(mirrored_ticket)
            set_demisto_integration_context("dlp_incidents_to_update", dlp_incidents, "override")
            break

    entries = []
    mirrored_ticket["incident_type"] = "dlp_incident"

    if mirrored_ticket.get("status") == "closed" and params.get("close_incident"):
        entries.append(
            {
                "Type": EntryType.NOTE,
                "Contents": {
                    "dbotIncidentClose": True,
                    "closeReason": "Closed from Netskope.",
                },
                "ContentsFormat": EntryFormat.JSON,
            }
        )

    return GetRemoteDataResponse(mirrored_ticket, entries)


# HELPERS FUNCTIONS #


def convert_large_integers_to_strings(data: dict[str, Any]) -> None:
    """Convert large integers to strings to prevent JavaScript precision loss.

    JavaScript cannot safely represent integers larger than 2^53-1 (Number.MAX_SAFE_INTEGER).
    This function converts such integers to strings in-place to preserve their exact values.

    Args:
        data (dict[str, Any]): Dictionary containing potential large integer values.

    Reference:
        https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
    """
    for key, value in list(data.items()):
        if isinstance(value, int) and value > JS_NUMBER_LIMIT:
            demisto.debug(f"Converting large integer to string: {key}={value}")
            data[key] = str(value)


def get_hourly_timestamps(start_time: int, end_time: int) -> list[int]:
    """Get a list of timestamps with a one hour gap between the received start and end timestamps.

    Args:
        start_time (int): The start timestamp.
        end_time (int): The end timestamp.

    Returns:
        list[int]: The timestamps list.
    """
    timestamps = []
    current_timestamp = start_time

    while current_timestamp <= end_time:
        timestamps.append(current_timestamp)
        current_timestamp += 3600  # Increment by 3600 seconds (1 hour)

    return timestamps


def remove_duplicates(
    dicts_list: list[dict[str, Any]],
    key: str,
) -> list[dict[str, Any]]:
    """Remove duplicates from a list of dicts by unique key.

    Args:
        dicts_list (list[dict[str, Any]]): The list of dicts to update.
        key (str): The unique key.

    Returns:
        list[dict[str, Any]]: The updated list with no duplicates.
    """
    object_map = {}

    for item in dicts_list:
        object_id = item[key]
        object_map[object_id] = item

    return list(object_map.values())


def parse_incident(
    incident: dict,
    incident_type: str,
    mirror_direction: str | None = None,
) -> dict:
    """
    Parse alert/event to XSOAR Incident.

    Args:
        incident (dict): alert/event item.
        incident_type (str): Incident type.

    Returns:
        dict: XSOAR Incident.
    """
    incident_id = incident["object_id"] if incident_type == "dlp_incident" else incident["_id"]
    incident["incident_type"] = incident_type
    incident["mirror_direction"] = mirror_direction
    incident["mirror_instance"] = demisto.integrationInstance()
    convert_large_integers_to_strings(incident)

    return {
        "name": f"{incident_type} ID: {incident_id}",
        "incident_type": incident_type,
        "mirror_direction": mirror_direction,
        "mirror_instance": demisto.integrationInstance(),
        "occurred": convert_datetime_int_to_iso(incident["timestamp"]),
        "rawJSON": json.dumps(incident),
    }


def convert_datetime_int_to_iso(creation_timestamp: int) -> datetime:
    """Convert datetime to iso.

    Args:
        creation_timestamp (int): creation timestamp argument.

    Returns:
        datetime: Updated argument.
    """
    date_string = convert_number_to_str_date(creation_timestamp)
    alert_date = datetime.strptime(date_string, TIME_FORMAT)
    return FormatIso8601(alert_date) + "Z"


def fetch_events_as_incidents(
    list_event_func: Callable,
    params: dict[str, Any],
    max_fetch_per_event_type: int,
    last_run: dict[str, Any],
    mirror_direction: str | None = None,
) -> tuple[list, dict]:
    """Fetch events from Netskope as incidents.

    Args:
        list_event_func (Callable): Netskope API client.
        params (Dict[str, Any]): Integration parameters.
        max_fetch_per_event_type (int): Maximum number per event type.
        last_run (Dict[str, Any]): Last run argument.
        mirror_direction (str | None): Mirror direction.

    Returns:Callable
        Tuple[list, dict]: Events as incidents and updated last_run argument.
    """
    incidents = []

    event_types = argToList(params.get("event_types"))
    event_query = params.get("event_query")

    for event_type in event_types:
        relevant_events = []
        last_run_id, last_run_timestamp = get_last_run(params, event_type)
        end_time_number = date_to_seconds_timestamp(datetime.now())

        response = list_event_func(
            event_type=event_type,
            query=event_query,
            limit=max_fetch_per_event_type,
            page=0,
            start_time=last_run_timestamp,
            end_time=end_time_number,
        )
        events = response["result"]

        for event in events:
            if last_run_id != event["_id"]:
                event["incident_type"] = event_type
                incidents.append(parse_incident(event, f"{event_type}-event", mirror_direction))
                relevant_events.append(event)

        last_run = update_last_run(
            relevant_events=relevant_events,
            incident_type=event_type,
            last_run=last_run,
            last_run_id=last_run_id,
            last_run_time_number=last_run_timestamp,
        )

    return incidents, last_run


def fetch_dlp_incidents_as_incidents(
    list_event_func: Callable,
    params: dict[str, Any],
    last_run: dict[str, Any],
    mirror_direction: str | None = None,
) -> tuple[list, dict]:
    """Fetch DLP incidents from Netskope as incidents.
        The incidents will be saved in the integration context to avoid duplicates.

    Args:
        list_event_func (Callable): Netskope API client.
        params (Dict[str, Any]): Integration parameters.
        last_run (dict[str, Any]): Last run.
        mirror_direction (str | None): Mirror direction.

    Returns:Callable
        Tuple[list, dict]: DLP incidents as incidents and updated last_run argument.
    """
    incidents = []

    new_incidents = []
    new_incident_ids = []
    last_run_id, last_run_timestamp = get_last_run(params, "dlp_incident")

    # when last run is rest - update integration context data
    if not last_run_id:
        set_demisto_integration_context("dlp_incident_ids", [], "override")
        set_demisto_integration_context("dlp_incident_last_run_timestamp", None, "override")
        set_demisto_integration_context("dlp_incidents_to_update", [], "override")

    end_time_number = date_to_seconds_timestamp(datetime.now())
    max_fetch = int(params.get("max_dlp_incidents_fetch") or 50)

    hourly_timestamps = get_hourly_timestamps(
        start_time=last_run_timestamp,
        end_time=end_time_number,
    )
    dlp_incidents = []

    # get incident IDs that exist in XSOAR to avoid duplicates
    exist_dlp_incidents: list = get_demisto_integration_context("dlp_incident_ids", [])

    for timestamp in hourly_timestamps:
        response = list_event_func(timestamp)

        if response.get("result"):
            dlp_incidents += response["result"]
            dlp_incidents = remove_duplicates(dlp_incidents, "object_id")

        last_run_timestamp = timestamp

        if len(dlp_incidents) >= max_fetch:
            dlp_incidents = dlp_incidents[:max_fetch]
            break

    for incident in dlp_incidents:
        incident_id = incident["object_id"]

        if incident_id not in exist_dlp_incidents:
            incidents.append(parse_incident(incident, "dlp_incident", mirror_direction))
            new_incidents.append(incident)
            new_incident_ids.append(incident_id)

    if new_incident_ids:
        # set the new incident IDs to avoid duplicates on next fetch
        set_demisto_integration_context("dlp_incident_ids", new_incident_ids, "append")
        last_run_timestamp = max(new_incidents, key=lambda k: k["timestamp"])["timestamp"] + 1
    else:
        last_run_timestamp = end_time_number

    # set the updated fetch last run time for get_modified_remote_data command
    # to update existing incidents and ensure full sync
    set_demisto_integration_context("dlp_incident_last_run_timestamp", last_run_timestamp, "override")

    last_run = update_last_run(
        relevant_events=new_incidents,
        incident_type="dlp_incident",
        last_run=last_run,
        last_run_id=last_run_id,
        last_run_time_number=last_run_timestamp,
    )

    return incidents, last_run


def fetch_alerts_as_incidents(
    list_event_func: Callable,
    params: dict[str, Any],
    max_fetch: int,
    last_run: dict[str, Any],
    alert_query: str | None = None,
    mirror_direction: str | None = None,
) -> tuple[list, dict]:
    """Fetch DLP incidents from Netskope as incidents.

    Args:
        list_event_func (Callable): Netskope API client.
        params (Dict[str, Any]): Integration parameters.
        max_fetch (int): Maximum number per event type.
        last_run (dict[str, Any]): Last run data.
        alert_query (str, optional): Alert query. Defaults to None.
        mirror_direction (str, optional): Mirror direction. Defaults to None.

    Returns:Callable
        Tuple[list, dict]: DLP incidents as incidents and updated last_run argument.
    """
    incidents = []

    new_incidents = []
    last_run_id, last_run_timestamp = get_last_run(params, "alert")
    end_time_number = date_to_seconds_timestamp(datetime.now())

    response = list_event_func(
        query=alert_query,
        limit=max_fetch,
        page=0,
        start_time=last_run_timestamp,
        end_time=end_time_number,
    )
    alerts = response["result"]

    for alert in alerts:
        if last_run_id != alert["_id"]:
            alert["incident_type"] = alert["alert_type"]
            incidents.append(parse_incident(alert, "alert", mirror_direction))
            new_incidents.append(alert)

    last_run = update_last_run(
        relevant_events=new_incidents,
        incident_type="alert",
        last_run=last_run,
        last_run_id=last_run_id,
        last_run_time_number=last_run_timestamp,
    )

    return incidents, last_run


def update_last_run(
    relevant_events: list,
    incident_type: str,
    last_run: dict,
    last_run_id: Any | None,
    last_run_time_number: int,
) -> dict[str, Any]:
    """Update the last run argument by alert type for next run.

    Args:
        relevant_events (list): The new incidents.
        incident_type (str): Incident type.
        last_run (dict): Last run argument.
        last_run_id (int): Last run ID.
        last_run_time_number (int): The last run time number.

    Returns:
        Dict[str, Any]: Updated last run argument.
    """
    if relevant_events:
        last_run_alert = max(relevant_events, key=lambda k: k["timestamp"])

        last_run[incident_type] = {
            "id": (last_run_alert["object_id"] if incident_type == "dlp_incident" else last_run_alert["_id"]),
            "time": last_run_time_number if incident_type == "dlp_incident" else last_run_alert["timestamp"] + 1,
            "date": convert_number_to_str_date(last_run_time_number),
        }

    else:
        last_run[incident_type] = {
            "id": last_run_id,
            "time": last_run_time_number,
            "date": convert_number_to_str_date(last_run_time_number),
        }

    demisto.setLastRun(last_run)
    return last_run


def get_last_run(
    args: dict[str, Any],
    incident_type: str,
) -> tuple[Any | None, int]:
    """Get last run arguments.

    Args:
        args (Dict[str, Any]): XSOAR arguments.
        incident_type (str): Incident type.

    Returns:
        Tuple: Updated last run arguments.
    """
    last_run = demisto.getLastRun()
    ticket_last_run = last_run.get(incident_type)
    last_run_id = None

    if last_run and ticket_last_run:
        last_run_time = ticket_last_run.get("time")
        last_run_id = ticket_last_run.get("id")
    else:
        last_run_time = args.get("first_fetch", "3 Days")

    first_fetch = arg_to_datetime(arg=last_run_time, arg_name="First fetch time", required=True)
    if not first_fetch:
        raise ValueError("First fetch time not specified")

    last_run_timestamp = date_to_seconds_timestamp(first_fetch)

    return last_run_id, last_run_timestamp


def deploy_url_list_if_required(
    args: dict[str, Any],
    deploy_url_list_func: Callable,
):
    """Deploys URL list changes if required.

    Args:
        client (Client): Netskope API client.
        args (Dict[str, Any]): command arguments.
    """

    if optional_arg_to_boolean(args.get("deploy")):
        deploy_url_list_func()


def convert_client_filter(client_filter: str) -> str:
    """Convert client filter to the appropriate format.

    Args:
        client_filter (str): client filter.

    Returns:
        str: Updated filter.
    """
    filter_parts = [part.strip() for part in client_filter.split("eq")]

    if len(filter_parts) != 2:
        raise DemistoException("Filter must contain 'key' eq 'value'")

    attribute_name, attribute_value = filter_parts

    attribute_value = attribute_value.strip().strip('"')

    modified_filter = f'{attribute_name} eq "{attribute_value}"'

    return modified_filter


def get_updated_list_client(response: list | dict) -> list:
    """Updates URL list response.

    Args:
        response (Union[List, Dict]): The API response.

    Returns:
        List: Updated URL list.
    """
    outputs = []
    for list_client in response:
        output = copy.deepcopy(list_client)
        output["client_id"] = output.pop("id")
        output |= output.pop("name", {})
        output["emails"] = [email.get("value") for email in output.get("emails", [])]
        output = snakify(output)
        outputs.append(output)
    return outputs


def get_updated_url_list(response: list | dict) -> list:
    """Updates URL list response.

    Args:
        response (Union[List, Dict]): The API response.

    Returns:
        List: Updated URL list.
    """
    response = [response] if isinstance(response, dict) else response
    outputs = []
    number_to_pending_status = {
        0: "applied",
        1: "pending",
    }
    for url_list in response:
        output = copy.deepcopy(url_list)
        output |= output.pop("data", {})
        output["pending"] = number_to_pending_status.get(output.get("pending"))
        outputs.append(output)
    return outputs


def parse_start_end_times(start_str: str, end_str: str | None) -> tuple[int | None, int | None]:
    """Convert string date to timestamp.

    Args:
        start_str (str): String start date.
        end_str (Optional[str]): String end date.

    Returns:
        Tuple: Timestamp start & end dates.
    """
    start_number = arg_to_seconds_timestamp(start_str) if start_str else None
    end_number = arg_to_seconds_timestamp(end_str) if end_str else date_to_seconds_timestamp(datetime.now())
    return start_number, end_number


def convert_time_args_to_num(args: dict[str, Any]) -> TimeArgs:
    """Convert relevant time arguments to numbers timestamp.
    Args:
        args (Dict[str, Any]): command arguments.
    Raises:
        ValueError: When specified all time arguments or not specified none of them.
    Returns:
        Tuple: The updated time arguments.
    """
    start_time_str = args.get("start_time")
    end_time_str = args.get("end_time")
    insertion_start_time_str = args.get("insertion_start_time")
    insertion_end_time_str = args.get("insertion_end_time")

    start_time_number = None
    end_time_number = None
    insertion_start_time_number = None
    insertion_end_time_number = None

    if start_time_str:
        start_time_number, end_time_number = parse_start_end_times(start_time_str, end_time_str)
    elif insertion_start_time_str:
        insertion_start_time_number, insertion_end_time_number = parse_start_end_times(
            insertion_start_time_str, insertion_end_time_str
        )
    else:
        raise ValueError("Provide either the `start_time` argument or `insertion_start_time`.")

    return TimeArgs(
        start_time_number,
        end_time_number,
        insertion_start_time_number,
        insertion_end_time_number,
    )


def convert_number_to_str_date(number: int) -> str:
    """Convert number to date string.

    Args:
        number (int): The date number to convert.

    Returns:
        str: The string date.
    """

    return datetime.fromtimestamp(float(number)).strftime(TIME_FORMAT)


def arg_to_seconds_timestamp(arg: str | datetime) -> int | None:
    """
    Converts an XSOAR date string argument to a timestamp in seconds.

    Args:
        arg (Optional[str]): The argument to convert.

    Returns:
        Optional[int]: A timestamp if arg can be converted,
        or None if arg is None.
    """
    date_arg = arg_to_datetime(arg)

    if date_arg is None:
        return None

    return date_to_seconds_timestamp(date_arg)


def date_to_seconds_timestamp(date_str_or_dt: str | datetime) -> int:
    """
    Converts date string or datetime object to a timestamp in seconds.

    Args:
        date_str_or_dt (Union[str, datetime]): The datestring or datetime.

    Returns:
        int: The timestamp in seconds.
    """

    return date_to_timestamp(date_str_or_dt) // 1000


def validate_pagination_arguments(
    page: int | None = None,
    limit: int | None = None,
):
    """Validate pagination arguments according to their default.

    Args:
        page (int, optional): Page number of paginated results.
        page_size (int, optional): Number of items per page.
        limit (int, optional): The maximum number of records to retrieve.

    Raises:
        ValueError: Incase page is lower than 1 or Incase limit is lower than 1.
    """
    if page is not None and page < MIN_PAGE_NUM:
        raise ValueError(f"page argument must be greater than {MIN_PAGE_NUM}.")
    if limit is not None and limit < MIN_LIMIT:
        raise ValueError(f"limit argument must be greater than {MIN_LIMIT}.")


def pagination(args: dict[str, Any], start_count_from_zero: bool = True) -> Pagination:
    """Return the correct limit and offset for the API
        based on the user arguments page and limit.

    Args:
        args (Dict[str, Any]): Command arguments from XSOAR.
        start_count_from_zero (bool, optional): Whether to start the offset count from or 1.
            Defaults to True.
    Returns:
        Tuple: page, limit, pagination_message.
    """
    page = arg_to_number(args.get("page")) or 1
    limit = arg_to_number(args.get("limit")) or 50

    validate_pagination_arguments(page, limit)

    pagination_message = f"Showing page {page}. \n Current page size: {limit}."
    updated_page = (page - 1) * limit + (0 if start_count_from_zero else 1)
    return Pagination(updated_page, limit, pagination_message)


def optional_arg_to_boolean(arg: str | bool | None) -> bool | None:
    """Retrieve arg boolean value if it's not none.
    Args:
        arg (Union[str, bool, None]): Boolean argument.
    Returns:
        Optional[bool]: The argument boolean value.
    """
    return argToBoolean(arg) if arg is not None else None


def get_demisto_integration_context(
    key: str,
    default_value: Any,
) -> Any:
    """Get the integration context for a given key.

    Args:
        key (str): The key to retrieve.
        default_value (Any): The default value to return in case the key dont exist.

    Returns:
        Any: The value of the given key.
    """
    return get_integration_context().get(key, default_value)


def set_demisto_integration_context(
    key: str,
    value_to_update: Any,
    action: str,
):
    """Set the integration context for the given key and value.

    Args:
        key (str): The integration context key to update his value.
        value_to_update (Any): The value to update.
        action (str): The action to take when update the value.
    """
    integration_context = get_integration_context()

    if action == "append":
        if not integration_context.get(key):
            integration_context[key] = []

        integration_context[key] += value_to_update

        # check if integration context size is reached to 40mb
        if len(integration_context[key]) >= MAX_IDS_NUMBER:
            # remove the first items to avoid overflow
            number_of_items_to_remove = len(integration_context[key]) - MAX_IDS_NUMBER
            integration_context[key] = integration_context[key][number_of_items_to_remove:]
    else:
        integration_context[key] = value_to_update

    set_integration_context(integration_context)


def main() -> None:  # pragma: no cover
    params: dict[str, Any] = demisto.params()
    args: dict[str, Any] = demisto.args()

    url = params["url"]
    api_token = params["credentials"]["password"]
    proxy = params.get("proxy", False)
    verify_certificate: bool = not params.get("insecure", False)
    user_email = params.get("user_email")
    command = demisto.command()
    demisto.debug(f"Command being called is {command}")

    try:
        client: Client = Client(
            url,
            api_token,
            verify_certificate,
            proxy,
            user_email,
        )

        commands = {
            "netskope-alert-list": list_alert_command,
            "netskope-event-list": list_event_command,
            "netskope-url-list-update": update_url_list_command,
            "netskope-url-list-create": create_url_list_command,
            "netskope-url-lists-list": lists_url_list_command,
            "netskope-url-list-delete": delete_url_list_command,
            "netskope-client-list": list_client_command,
            "netskope-url-list-add": add_url_list_command,
            "netskope-incident-dlp-list": list_dlp_incident_command,
        }

        if command == "test-module":
            return_results(test_module(client))
        elif command == "get-remote-data":
            return_results(get_remote_data_command(args, params))
        elif command == "get-modified-remote-data":
            return_results(get_modified_remote_data(client))
        elif command == "update-remote-system":
            return_results(update_remote_system(client, args, params))
        elif command == "get-mapping-fields":
            return_results(get_mapping_fields_command())
        elif command == "fetch-incidents":
            last_run, incidents = fetch_incidents(client, params)
            demisto.setLastRun(last_run)
            demisto.incidents(incidents)
        elif command in commands:
            return_results(commands[command](client, args))
        else:
            raise NotImplementedError(f"{command} command is not implemented.")

    except Exception as e:
        return_error(str(e))


if __name__ in ["__main__", "builtin", "builtins"]:
    main()