Proofpoint Threat Response

Use the Proofpoint Threat Response integration to orchestrate and automate incident response.

Network Security · Proofpoint Threat Response

Details

IDProofpoint Threat Response
ProviderThoma Bravo
CategoryNetwork Security
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM EDR Cortex Cloud Cloud Runtime Security

README

Use the Proofpoint Threat Response integration to orchestrate and automate incident response.

Configure Proofpoint Threat Response in Cortex

Parameter Description Required
Server URL (e.g. https://192.168.0.1)   True
API Key   True
Trust any certificate (not secure)   False
Use system proxy settings   False
Fetch incidents   False
Incident type   False
First fetch timestamp (“number” “time unit”, e.g., 12 hours, 7 days) The time range for the initial data fetch. If timeout errors occur, consider changing this value. False
Fetch limit - maximum number of incidents per fetch   False
Fetch delta - The delta time in each batch. e.g. 1 hour, 3 minutes. The time range between create_after and created_before that is sent to the API when fetching older incidents. If timeout errors occur, consider changing this value. False
Fetch incidents with specific event sources. Can be a list of comma separated values.   False
Fetch incidents with specific ‘Abuse Disposition’ values. Can be a list of comma separated values.   False
Fetch incident with specific states.   False
POST URL of the JSON alert source. You can find this value by navigating to Sources -> JSON event source -> POST URL. False

Commands

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

proofpoint-tr-get-list


Gets items for the specified list.

Base Command

proofpoint-tr-get-list

Input

Argument Name Description Required
list-id The ID of the list. Required

Context Output

There is no context output for this command.

proofpoint-tr-add-to-list


Adds a member to the specified list.

Base Command

proofpoint-tr-add-to-list

Input

Argument Name Description Required
list-id The list to add a member to. Required
indicator A comma-separated list of indicator values. Can be IP addresses, URLs, domains, or file hashes.
For example: “192.168.1.1,192.168.1.2”.
Required
comment A comment about the member. Optional
expiration The expiration of the member. Optional

Context Output

There is no context output for this command.

proofpoint-tr-block-ip


Adds the supplied IP addresses to the specified IP block list.

Base Command

proofpoint-tr-block-ip

Input

Argument Name Description Required
ip A comma-separated list of IP addresses to block list. Required
expiration The date and time the supplied IP addresses should be removed from the block list, in the format YYYY-MM-DDTHH:MM:SSZ.
For example: 2020-02-02T19:00:00Z.
Optional
blacklist_ip The ID of the IP block list. Required

Context Output

There is no context output for this command.

proofpoint-tr-block-domain


Adds the supplied domains to the specified block list.

Base Command

proofpoint-tr-block-domain

Input

Argument Name Description Required
domain A comma-separated list of domains to add to the block list. Required
expiration The date and time the supplied IP addresses should be removed from the block list, in the format YYYY-MM-DDTHH:MM:SSZ.
For example: 2020-02-02T19:00:00Z.
Optional
blacklist_domain The ID of the domain block list. Required

Context Output

There is no context output for this command.

proofpoint-tr-search-indicator


Returns indicators from the specified list, according to the defined filter.

Base Command

proofpoint-tr-search-indicator

Input

Argument Name Description Required
list-id The ID of the list in which to search. Required
filter The filter for the indicator search.
For example, “1.1” will return [1.1.1.1, 22.22.1.1, 1.1.22.22].
Required

Context Output

There is no context output for this command.

proofpoint-tr-delete-indicator


Deletes an indicator from the specified list.

Base Command

proofpoint-tr-delete-indicator

Input

Argument Name Description Required
list-id ID of the list from which to delete indicators. Required
indicator The indicator value to delete from the list. Can be an IP address, URL, domain, or file hash.
For example: “demisto.com”.
Required

Context Output

There is no context output for this command.

proofpoint-tr-block-url


Adds the supplied URLs to the specified URL block list.

Base Command

proofpoint-tr-block-url

Input

Argument Name Description Required
url A comma-separated list of URLs to add to the URL block list. Required
expiration The date and time the supplied URLs should be removed from the block list, in the format YYYY-MM-DDTHH:MM:SSZ.
For example: 2020-02-02T19:00:00Z.
Optional
blacklist_url The ID of the URL block list. Required

Context Output

There is no context output for this command.

proofpoint-tr-block-hash


Adds the supplied file hashes to the specified file hash block list.

Base Command

proofpoint-tr-block-hash

Input

Argument Name Description Required
hash A comma-separated list of file hashes to add to the file hash block list. Required
expiration The date and time the supplied file hashes should be removed from the block list, in the format YYYY-MM-DDTHH:MM:SSZ.
For example: 2020-02-02T19:00:00Z.
Optional
blacklist_hash The ID of the hash block list. Required

Context Output

There is no context output for this command.

proofpoint-tr-list-incidents


Retrieves all incident metadata from Threat Response by specifying filter criteria such as the state of the incident or time of closure.

Base Command

proofpoint-tr-list-incidents

Input

Argument Name Description Required
state State of the incidents to retrieve. Possible values are: new, open, assigned, closed, ignored. Optional
created_after Retrieve incidents that were created after this date, in ISO 8601 format (UTC).
Example: 2020-02-22 or 2020-02-22T00:00:00Z. Possible values are: .
Optional
created_before Retrieve incidents that were created before this date, in ISO 8601 format (UTC).
Example: 2020-02-22 or 2020-02-22T00:00:00Z.
Optional
closed_after Retrieve incidents that were closed after this date, in ISO 8601 format (UTC).
Example: 2020-02-22 or 2020-02-22T00:00:00Z.
Optional
closed_before Retrieve incidents that were closed before this date, in ISO 8601 format (UTC).
Example: 2020-02-22 or 2020-02-22T00:00:00Z.
Optional
expand_events If false, will return an array of event IDs instead of full event objects. This will significantly speed up the response time of the API for incidents with large numbers of alerts. Optional
limit The maximum number of incidents to return. The default value is 50. Required

Context Output

Path Type Description
ProofPointTRAP.Incident.id Number The incident ID.
ProofPointTRAP.Incident.summary String The summary of the incident.
ProofPointTRAP.Incident.score Number The score of the incident from Proofpoint.
ProofPointTRAP.Incident.state String The state of the incident. Can be - Open, Closed, New, Assigned, Ignored.
ProofPointTRAP.Incident.created_at Date The date the incident was created.
ProofPointTRAP.Incident.updated_at Date The date the incident was last updated.
ProofPointTRAP.Incident.event_count Number The number of events attached to the incident.
ProofPointTRAP.Incident.false_positive_count Number The number of false positive events in the incident.
ProofPointTRAP.Incident.event_sources String The sources of the events.
ProofPointTRAP.Incident.assignee String The user assigned to the incident.
ProofPointTRAP.Incident.team String The team assigned to the incident.
ProofPointTRAP.Incident.hosts.attacker String The host attacker.
ProofPointTRAP.Incident.hosts.forensics String The host forensics.
ProofPointTRAP.Incident.incident_field_values.Severity String The severity of the incident.
ProofPointTRAP.Incident.incident_field_values.Abuse_disposition String The abuse disposition of the incident.
ProofPointTRAP.Incident.incident_field_values.Attack_vector String The attack vector of the incident.
ProofPointTRAP.Incident.incident_field_values.Classification String The classification of the incident.
ProofPointTRAP.Incident.events.id Number The event ID.
ProofPointTRAP.Incident.events.category String The event category.
ProofPointTRAP.Incident.events.alertType String The alert type of the event.
ProofPointTRAP.Incident.events.severity String The severity of the event.
ProofPointTRAP.Incident.events.source String The source of the event.
ProofPointTRAP.Incident.events.state String The state of the event.
ProofPointTRAP.Incident.events.attackDirection String The attack direction of the event.
ProofPointTRAP.Incident.events.received Date The time the incident was received.
ProofPointTRAP.Incident.events.emails.sender String The sender of the email.
ProofPointTRAP.Incident.events.emails.recipient String The recipient of the email.
ProofPointTRAP.Incident.events.emails.message_Id String The message ID of the email.
ProofPointTRAP.Incident.events.emails.message_delivery_time Number The delivery time of the message.
ProofPointTRAP.Incident.events.attackers.location String The location of the attacker.
ProofPointTRAP.Incident.events.falsePositive Boolean Whether this incident is a false positive.
ProofPointTRAP.Incident.events.threatname String The threat name.
ProofPointTRAP.Incident.events.description String The description of the event.
ProofPointTRAP.Incident.events.malwareName String The malware name.
ProofPointTRAP.Incident.quarantine_results.alertSource String The alert source.
ProofPointTRAP.Incident.quarantine_results.startTime Date The start time of the result.
ProofPointTRAP.Incident.quarantine_results.endTime Date The end time of the result.
ProofPointTRAP.Incident.quarantine_results.status String The status of the result.
ProofPointTRAP.Incident.quarantine_results.recipientType String The recipient type.
ProofPointTRAP.Incident.quarantine_results.recipient String The recipient email address.
ProofPointTRAP.Incident.quarantine_results.messageId String The message ID.
ProofPointTRAP.Incident.quarantine_results.isRead Boolean Whether the message has been read.
ProofPointTRAP.Incident.quarantine_results.wasUndone String Whether the message was undone.
ProofPointTRAP.Incident.quarantine_results.details String The details about the result.
ProofPointTRAP.Incident.successful_quarantines Number The number of successful quarantines.
ProofPointTRAP.Incident.failed_quarantines Number The number of failed quarantines.
ProofPointTRAP.Incident.pending_quarantines Number The number of pending quarantines.
ProofPointTRAP.Incident.events.emails.body String The body of the email.
ProofPointTRAP.Incident.events.emails.body_type String The format of the body.
ProofPointTRAP.Incident.events.emails.headers Unknown The email headers.
ProofPointTRAP.Incident.events.emails.urls Unknown The list of URLs from the email.
ProofPoint.Incident.event_ids Unknown The list of IDs attached to the incident.

proofpoint-tr-get-incident


Retrieves incident metadata from Threat Response.

Base Command

proofpoint-tr-get-incident

Input

Argument Name Description Required
incident_id The ID value of the incident to retrieve (e.g. for incident INC-4000, the input for this argument should be 4000). Required
expand_events If false, will return an array of event IDs instead of full event objects. This will significantly speed up the response time of the API for incidents with large numbers of alerts. Optional

Context Output

Path Type Description
ProofPointTRAP.Incident.id Number The incident ID.
ProofPointTRAP.Incident.summary String The summary of the incident.
ProofPointTRAP.Incident.score Number The score of the incident from Proofpoint.
ProofPointTRAP.Incident.state String The state of the incident. Can be - Open, Closed, New, Assigned, Ignored.
ProofPointTRAP.Incident.created_at Date The date the incident was created.
ProofPointTRAP.Incident.updated_at Date The date the incident was last updated.
ProofPointTRAP.Incident.event_count Number The number of events attached to the incident.
ProofPointTRAP.Incident.false_positive_count Number The number of false positive events in the incident.
ProofPointTRAP.Incident.event_sources String The sources of the events.
ProofPointTRAP.Incident.assignee String The user assigned to the incident.
ProofPointTRAP.Incident.team String The team assigned to the incident.
ProofPointTRAP.Incident.hosts.attacker String The host attacker.
ProofPointTRAP.Incident.hosts.forensics String The host forensics.
ProofPointTRAP.Incident.incident_field_values.Severity String The severity of the incident.
ProofPointTRAP.Incident.incident_field_values.Abuse_disposition String The abuse disposition of the incident.
ProofPointTRAP.Incident.incident_field_values.Attack_vector String The attack vector of the incident.
ProofPointTRAP.Incident.incident_field_values.Classification String The classification of the incident.
ProofPointTRAP.Incident.events.id Number The event ID.
ProofPointTRAP.Incident.events.category String The event category.
ProofPointTRAP.Incident.events.alertType String The alert type of the event.
ProofPointTRAP.Incident.events.severity String The severity of the event.
ProofPointTRAP.Incident.events.source String The source of the event.
ProofPointTRAP.Incident.events.state String The state of the event.
ProofPointTRAP.Incident.events.attackDirection String The attack direction of the event
ProofPointTRAP.Incident.events.received Date The date the incident was received.
ProofPointTRAP.Incident.events.emails.sender String The sender of the email.
ProofPointTRAP.Incident.events.emails.recipient String The recipient of the email.
ProofPointTRAP.Incident.events.emails.message_Id String The message ID of the email.
ProofPointTRAP.Incident.events.emails.message_delivery_time Number The time the message was delivered.
ProofPointTRAP.Incident.events.attackers.location String The location of the attacker.
ProofPointTRAP.Incident.events.falsePositive Boolean Whether this incident is a false positive.
ProofPointTRAP.Incident.events.threatname String The threat name.
ProofPointTRAP.Incident.events.description String The description of the event.
ProofPointTRAP.Incident.events.malwareName String The malware name.
ProofPointTRAP.Incident.quarantine_results.alertSource String The alert source.
ProofPointTRAP.Incident.quarantine_results.startTime Date The start time of the result.
ProofPointTRAP.Incident.quarantine_results.endTime Date The end time of the result.
ProofPointTRAP.Incident.quarantine_results.status String The status of the result.
ProofPointTRAP.Incident.quarantine_results.recipientType String The recipient type.
ProofPointTRAP.Incident.quarantine_results.recipient String The recipient email address.
ProofPointTRAP.Incident.quarantine_results.messageId String The message ID.
ProofPointTRAP.Incident.quarantine_results.isRead Boolean Whether the message has been read.
ProofPointTRAP.Incident.quarantine_results.wasUndone String Whether the message was undone.
ProofPointTRAP.Incident.quarantine_results.details String The details about the result.
ProofPointTRAP.Incident.successful_quarantines Number The number of successful quarantines.
ProofPointTRAP.Incident.failed_quarantines Number The number of failed quarantines.
ProofPointTRAP.Incident.pending_quarantines Number The number of pending quarantines.
ProofPointTRAP.Incident.events.emails.body String The body of the email.
ProofPointTRAP.Incident.events.emails.body_type String The format of the body.
ProofPointTRAP.Incident.events.emails.headers Unknown The email headers.
ProofPointTRAP.Incident.events.emails.urls Unknown The list of URLs from the email.
ProofPoint.Incident.event_ids Unknown The list of IDs attached to the incident.

proofpoint-tr-update-incident-comment


Adds comments to an existing Threat Response incident, by incident ID.

Base Command

proofpoint-tr-update-incident-comment

Input

Argument Name Description Required
incident_id The ID value of the incident to add the comment to (e.g. for incident INC-4000, the input for this argument should be 4000). Required
details The details of the comments. Required
comments The summary of the comments. Required

Context Output

Path Type Description
ProofPointTRAP.IncidentComment.id Number The ID of the comment.
ProofPointTRAP.IncidentComment.incident_id Number The ID of the incident.
ProofPointTRAP.IncidentComment.response_id Number The ID of the response.
ProofPointTRAP.IncidentComment.user_id String The ID of the user.
ProofPointTRAP.IncidentComment.history_type String The history type.
ProofPointTRAP.IncidentComment.state_from String The state from of the incident.
ProofPointTRAP.IncidentComment.state_to String The state to of the incident.
ProofPointTRAP.IncidentComment.summary String The summary of the comments.
ProofPointTRAP.IncidentComment.detail String The details of the comment.
ProofPointTRAP.IncidentComment.created_at Date The date the incident was created.
ProofPointTRAP.IncidentComment.updated_at Date The date the incident was last updated.

proofpoint-tr-add-user-to-incident


Assigns a user to an incident as a target or attacker.

Base Command

proofpoint-tr-add-user-to-incident

Input

Argument Name Description Required
incident_id The ID value of the incident to add the user to (e.g. for incident INC-4000, the input for this argument should be 4000). Required
targets The list of targets to add to the incident. Required
attackers The list of attackers to add to the incident. Required

Context Output

There is no context output for this command.

proofpoint-tr-ingest-alert


Ingest an alert into Threat Response.

Base Command

proofpoint-tr-ingest-alert

Input

Argument Name Description Required
post_url_id The POST URL of the JSON alert source. You can find it by navigating to Sources -> JSON event source -> POST URL. Optional
json_version The Threat Response JSON version.
Possible values are: 2.0, 1.0. Default is 2.0.
Required
attacker An attacker object in JSON format : “{“attacker” : {…}}”. The attacker object must contain one of [“ip_address”, mac_address”, “host_name”, “url”, “user”] keys. You can also add the “port” key to the object. For more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”. Optional
classification The alert classification shown as “Alert Type” in the TRAP UI.
Possible values are: malware, policy-violation, vulnerability, network, spam, phish, command-and-control, data-match, authentication, system-behavior, impostor, reported-abuse, unknown.
Optional
cnc_hosts The Command and Control host information in JSON format : “{“cnc_hosts”: [{“host” : “-“, “port”: “-“}, …]}”.
Note: Every item of the “cnc_hosts” list is in JSON format. For more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”.
Optional
detector The threat detection tool such as Firewall and IPS/IDS systems (in the format: “{“detector” : {…}}”), which generated the original alert. To see all relevant JSON fields and for more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”. Optional
email The email metadata related to the alert, in JSON format: “{“email”: {…}}”. To see all relevant JSON fields and for more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”. Optional
forensics_hosts The forensics host information in JSON format : “{“forensics_hosts”: [{“host” : “-“, “port”: “-“}…]}”.
Note: Every item of the “forensics_hosts” list is in JSON format. For more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”.
Optional
link_attribute The attribute to link alerts to.
Possible values are: target_ip_address, target_hostname, target_machine_name, target_user, target_mac_address, attacker_ip_address, attacker_hostname, attacker_machine_name, attacker_user, attacker_mac_address, email_recipient, email_sender, email_subject, message_id, threat_filename, threat_filehash.
Optional
severity The severity of the alert.
Possible values are: info, minor, moderate, major, critical, Informational, Low, Medium, High, Critical.
Optional
summary The alert summary. This argument will populate the Alert Details field. Optional
target The target host information in JSON format : “{“target”: {…}}”. To see all relevant JSON fields and for more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”. Optional
threat_info The threat information in JSON format: “{“threat_info”: {…}}”. To see all relevant JSON fields and for more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”. Optional
custom_fields A JSON object for collecting custom name-value pairs as part of the JSON alert sent to Threat Response, in the format: “{“custom_fields”: {..}}”. Although there is no limit to the number of custom fields, Proofpoint recommends keeping it to 10 or fewer fields. To see all relevant JSON fields and for more information, see Proofpoint TRAP documentation under “JSON Alert Source 2.0”. Optional

Context Output

There is no context output for this command.

proofpoint-tr-close-incident


Close an incident

Base Command

proofpoint-tr-close-incident

Input

Argument Name Description Required
incident_id The ID value of the incident to close. Required
details The details for the closure notes. Required
summary The summary for the closure notes. Required

Context Output

There is no context output for this command.

proofpoint-tr-verify-quarantine


Verify if an email has been quarantined.

Base Command

proofpoint-tr-verify-quarantine

Input

Argument Name Description Required
message_id The ID value of an email. True
time The creation time of the incident in the format YYYY-MM-DDTHH:MM:SSZ. For example: 2020-02-02T19:00:00Z. True
recipient The email recipient. True
limit_quarantine_occurred_time Whether or not to limit the results to include only quarantines that occurred within a limited time after alert creation. Automated quarantine actions for example typically occur within 120 seconds of alert firing. Manual actions executed by human may take longer. Default is True. False
quarantine_limit Only used if ‘limit_quarantine_occurred_time’ argument is set to true. Sets a limit on the quarantines retrieved to include only those where the time difference between alert time and quarantined time is less than the number of seconds specified. The time is in seconds. Default is 120. False
fetch_delta The time frame (in hours) used to identify the size of batches when retrieving the messages. e.g., 12. Default is 6. False
check_exact_reception_time If true, the command checks for quarantined emails with the exact reception time of the email. If false, the verification of the quarantined email only uses the message ID and the recipient email. Default is true. False

Context Output

Path Type Description
ProofPointTRAP.Quarantine String The result of the quarantine.

Command Example

!proofpoint-tr-verify-quarantine messageid=<message_id_example> time="2022-06-02T17:22:45Z" recipient=example@example.com

Context Example

{
    "ProofPointTRAP": {
        "Quarantine": [
            {
                "alert": {
                    "id": 1030,
                    "time": "2022-06-02T17:33:18Z"
                },
                "incident": {
                    "id": 265,
                    "time": "2022-06-02T17:33:18Z"
                },
                "quarantine": {
                    "alertSource": "Admin Portal",
                    "details": "Success",
                    "endTime": "2022-06-02T17:33:37.926Z",
                    "isRead": "false",
                    "messageId": "message_id_example",
                    "recipient": "example@example.com",
                    "recipientType": "Original Recipient",
                    "startTime": "2022-06-02T17:33:20.352Z",
                    "status": "successful",
                    "wasUndone": "false"
                }
            },
            {
                "alert": {
                    "id": 1030,
                    "time": "2022-06-02T17:33:18Z"
                },
                "incident": {
                    "id": 265,
                    "time": "2022-06-02T17:33:18Z"
                },
                "quarantine": {
                    "alertSource": "Admin Portal",
                    "details": "Success",
                    "endTime": "2022-06-02T17:33:37.321Z",
                    "isRead": "false",
                    "messageId": "message_id_example",
                    "recipient": "example@example.com",
                    "recipientType": "Original Recipient",
                    "startTime": "2022-06-02T17:33:20.283Z",
                    "status": "successful",
                    "wasUndone": "false"
                }
            }
        ]
    }
}

Configuration parameters

  • url — Server URL (e.g. https://192.168.0.1) (required)
  • credentials
  • apikey — API Key
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • first_fetch — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)
  • fetch_limit — Fetch limit - maximum number of incidents per fetch
  • fetch_delta — Fetch delta - The delta time in each batch. e.g. 1 hour, 3 minutes.
  • states — Fetch incident with specific states.
  • event_sources — Fetch incidents with specific event sources. Can be a list of comma separated values.
  • abuse_disposition — Fetch incidents with specific 'Abuse Disposition' values. Can be a list of comma separated values.
  • post_url_id — POST URL of the JSON alert source.

Commands (15)

  • proofpoint-tr-add-to-list

    Adds a member to the specified list.

  • proofpoint-tr-add-user-to-incident

    Assigns a user to an incident as a target or attacker.

  • proofpoint-tr-block-domain

    Adds the supplied domains to the specified block list.

  • proofpoint-tr-block-hash

    Adds the supplied file hashes to the specified file hash block list.

  • proofpoint-tr-block-ip

    Adds the supplied IP addresses to the specified IP block list.

  • proofpoint-tr-block-url

    Adds the supplied URLs to the specified URL block list.

  • proofpoint-tr-close-incident

    Close a specified incident.

  • proofpoint-tr-delete-indicator

    Deletes an indicator from the specified list.

  • proofpoint-tr-get-incident

    Retrieves incident metadata from Threat Response.

  • proofpoint-tr-get-list

    Gets items for the specified list.

  • proofpoint-tr-ingest-alert

    Ingest an alert into Threat Response.

  • proofpoint-tr-list-incidents

    Retrieves all incident metadata from Threat Response by specifying filter criteria such as the state of the incident or time of closure.

  • proofpoint-tr-search-indicator

    Returns indicators from the specified list, according to the defined filter.

  • proofpoint-tr-update-incident-comment

    Adds comments to an existing Threat Response incident, by incident ID.

  • proofpoint-tr-verify-quarantine

    Verify if an email has been quarantined.

import json
from datetime import date, timedelta

import dateparser
import demistomock as demisto  # noqa: F401
import requests
import urllib3
from CommonServerPython import *  # noqa: F401

urllib3.disable_warnings()

""" GLOBAL VARS """
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
BASE_URL = demisto.params().get("url")
if BASE_URL and BASE_URL[-1] != "/":
    BASE_URL += "/"
API_KEY = demisto.params().get("credentials", {}).get("password") or demisto.params().get("apikey")
VERIFY_CERTIFICATE = not demisto.params().get("insecure")
# How many time before the first fetch to retrieve incidents
FIRST_FETCH, _ = parse_date_range(demisto.params().get("first_fetch", "12 hours") or "12 hours", date_format=TIME_FORMAT)

""" COMMAND FUNCTIONS """


def get_list(list_id):
    fullurl = BASE_URL + f"api/lists/{list_id}/members.json"
    res = requests.get(fullurl, headers={"Content-Type": "application/json", "Authorization": API_KEY}, verify=VERIFY_CERTIFICATE)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(f"Get list failed. URL: {fullurl}, StatusCode: {res.status_code}")

    return res.json()


def get_list_command():
    """Retrieves all indicators of a the given list ID in Threat Response"""
    list_id = demisto.args().get("list-id")
    list_items = get_list(list_id)

    demisto.results({"list": list_items})


def add_to_list(list_id, indicator, comment, expiration):
    fullurl = BASE_URL + f"api/lists/{list_id}/members.json"

    indicator = {"member": indicator}
    if comment:
        indicator["description"] = comment

    if expiration:
        indicator["expiration"] = expiration

    res = requests.post(fullurl, headers={"Authorization": API_KEY}, verify=VERIFY_CERTIFICATE, json=indicator)

    if res.status_code < 200 or res.status_code >= 300:
        return_error(f"Add to list failed. URL: {fullurl}, Request Body: {json.dumps(indicator)}")

    return res.json()


def add_to_list_command():
    """Adds given indicators to the given list ID in Threat Response"""
    list_id = demisto.args().get("list-id")
    indicators = argToList(demisto.args().get("indicator"))
    comment = demisto.args().get("comment")
    expiration = demisto.args().get("expiration")

    message = ""
    for indicator in indicators:
        add_to_list(list_id, indicator, comment, expiration)
        message += f"{indicator} added successfully to {list_id}\n"

    demisto.results(message)


def block_ip_command():
    """Adds given IPs to the relevant blacklist in Threat Response"""
    list_id = demisto.params().get("blacklist_ip", demisto.args().get("blacklist_ip"))
    ips = argToList(demisto.args().get("ip"))
    expiration = demisto.args().get("expiration")

    message = ""
    for ip in ips:
        add_to_list(list_id, ip, None, expiration)
        message += f"{ip} added successfully to block_ip list\n"

    demisto.results(message)


def block_domain_command():
    """Adds given domains to the relevant blacklist in Threat Response"""
    list_id = demisto.params().get("blacklist_domain", demisto.args().get("blacklist_domain"))
    domains = argToList(demisto.args().get("domain"))
    expiration = demisto.args().get("expiration")

    message = ""
    for domain in domains:
        add_to_list(list_id, domain, None, expiration)
        message += f"{domain} added successfully to block_domain list\n"

    demisto.results(message)


def block_url_command():
    """Adds given URLs to the relevant blacklist in Threat Response"""
    list_id = demisto.params().get("blacklist_url", demisto.args().get("blacklist_url"))
    urls = argToList(demisto.args().get("url"))
    expiration = demisto.args().get("expiration")

    message = ""
    for url in urls:
        add_to_list(list_id, url, None, expiration)
        message += f"{url} added successfully to block_url list\n"

    demisto.results(message)


def block_hash_command():
    """Adds given hashes to the relevant blacklist in Threat Response"""
    list_id = demisto.params().get("blacklist_hash", demisto.args().get("blacklist_hash"))
    hashes = argToList(demisto.args().get("hash"))
    expiration = demisto.args().get("expiration")

    message = ""
    for h in hashes:
        add_to_list(list_id, h, None, expiration)
        message += f"{h} added successfully to block_hash list\n"

    demisto.results(message)


def search_indicators(list_id, indicator_filter):
    list_indicators = get_list(list_id)
    found_items = []
    for item in list_indicators:
        item_indicator = demisto.get(item, "host.host")
        if item_indicator and indicator_filter in item_indicator:
            found_items.append(item)

    return found_items


def search_indicator_command():
    """Retrieves indicators of a list, using a filter"""
    list_id = demisto.args().get("list-id")
    indicator_filter = demisto.args().get("filter")
    found = search_indicators(list_id, indicator_filter)

    demisto.results({"indicators": found})


def delete_indicator(list_id, indicator_filter):
    indicator = search_indicators(list_id, indicator_filter)
    if len(indicator) == 0:
        return_error(f"{indicator_filter} not exists in {list_id}")

    indicator_id = indicator.get("id")  # pylint: disable=E1101
    fullurl = BASE_URL + f"api/lists/{list_id}/members/{indicator_id}.json"
    res = requests.delete(fullurl, headers={"Authorization": API_KEY}, verify=VERIFY_CERTIFICATE)
    if res.status_code < 200 or res.status_code >= 300:
        return_error(f"Delete indicator failed. URL: {fullurl}, StatusCode: {res.status_code}")


def delete_indicator_command():
    """Deletes an indicator from a list"""
    list_id = demisto.args().get("list-id")
    indicator = demisto.args().get("indicator")
    delete_indicator(list_id, indicator)

    demisto.results(f"{list_id} deleted successfully from list {indicator}")


def test():
    """Perform API call to check that the API is accessible.

    Returns:
        'ok' if test passed, anything else will fail the test.
    """
    integration_params = demisto.params()
    if integration_params.get("isFetch") and not integration_params.get("states"):
        raise DemistoException("Missing argument - You must provide at least one incident state.")
    get_incidents_request({"created_after": date.today(), "state": "open"})
    demisto.results("ok")


# TRAP API
def create_incident_field_context(incident):
    """Parses the 'incident_fields' entry of the incident and returns it

    Args:
        incident (dict): The incident to parse

    Returns:
        list. The parsed incident fields list
    """
    incident_field_values = {}
    for incident_field in incident.get("incident_field_values", []):
        incident_field_values[incident_field["name"].replace(" ", "_")] = incident_field["value"]

    return incident_field_values


def get_emails_context(event):
    """Returns the context of the emails in the event

    Args:
        event (dict): The event to parse the emails from

    Returns:
        list. The parsed emails list from the event
    """
    emails_context = []
    for email in event.get("emails", []):
        email_obj = {
            "sender": email.get("sender", {}).get("email"),
            "recipient": email.get("recipient", {}).get("email"),
            "subject": email.get("subject"),
            "message_id": email.get("messageId"),
            "body": email.get("body"),
            "body_type": email.get("bodyType"),
            "headers": email.get("headers"),
            "urls": email.get("urls"),
            "sender_vap": email.get("sender", {}).get("vap"),
            "recipient_vap": email.get("recipient", {}).get("vap"),
            "attachments": email.get("attachments"),
        }
        message_delivery_time = email.get("messageDeliveryTime", {})
        if message_delivery_time and isinstance(message_delivery_time, dict):
            email_obj["message_delivery_time"] = message_delivery_time.get("millis")
        elif message_delivery_time and isinstance(message_delivery_time, str):
            email_obj["message_delivery_time"] = message_delivery_time
        emails_context.append(assign_params(**email_obj))

    return emails_context


def create_incidents_context(incidents_list):
    """Parses the incidents list and returns the incidents context

    Args:
        incidents_list (list): The incidents list to parse

    Returns:
        list. The context created from the incidents list
    """
    context = list(incidents_list)
    for incident in context:
        incident["incident_field_values"] = create_incident_field_context(incident)

        if incident.get("events"):
            for event in incident["events"]:
                event["emails"] = get_emails_context(event)

    return context


def create_incidents_human_readable(human_readable_message, incidents_list):
    """Creates the human readable entry for incidents

    Args:
        human_readable_message (str): The title of the human readable table
        incidents_list (list): The incidents list to insert to the table

    Returns:
        str. The incidents human readable in markdown format
    """
    human_readable = []
    human_readable_headers = [
        "ID",
        "Created At",
        "Type",
        "Summary",
        "Score",
        "Event Count",
        "Assignee",
        "Successful Quarantines",
        "Failed Quarantines",
        "Pending Quarantines",
    ]
    for incident in incidents_list:
        human_readable.append(
            {
                "Created At": incident.get("created_at"),
                "ID": incident.get("id"),
                "Type": incident.get("type"),
                "Summary": incident.get("summary"),
                "Score": incident.get("score"),
                "Event Count": incident.get("event_count"),
                "Assignee": incident.get("assignee"),
                "Successful Quarantines": incident.get("successful_quarantine"),
                "Failed Quarantines": incident.get("failed_quarantines"),
                "Pending Quarantines": incident.get("pending_quarantines"),
            }
        )

    return tableToMarkdown(human_readable_message, human_readable, human_readable_headers, removeNull=True)


def list_incidents_command():
    """Retrieves incidents from ProofPoint API"""
    args = demisto.args()
    limit = int(args.pop("limit"))

    incidents_list = get_incidents_request(args)

    incidents_list = incidents_list[:limit]
    human_readable = create_incidents_human_readable("List Incidents Results:", incidents_list)
    context = create_incidents_context(incidents_list)

    return_outputs(human_readable, {"ProofPointTRAP.Incident(val.id === obj.id)": context}, incidents_list)


def get_incident_command():
    """
    Retrieves a single incident from ProofPoint API
    """
    args = demisto.args()
    incident_id = args.pop("incident_id")
    expand_events = args.get("expand_events")
    fullurl = BASE_URL + f"api/incidents/{incident_id}.json"
    incident_data = requests.get(
        fullurl,
        headers={"Content-Type": "application/json", "Authorization": API_KEY},
        params={
            "expand_events": expand_events,
        },
        verify=VERIFY_CERTIFICATE,
    )

    if incident_data.status_code < 200 or incident_data.status_code >= 300:
        return_error(f"Get incident failed. URL: {fullurl}, StatusCode: {incident_data.status_code}")

    incident_data = incident_data.json()
    human_readable = create_incidents_human_readable("Incident Results:", [incident_data])
    context = create_incidents_context([incident_data])

    return_outputs(human_readable, {"ProofPointTRAP.Incident(val.id === obj.id)": context}, incident_data)


def pass_sources_list_filter(incident, sources_list):
    """Checks whether the event sources of the incident contains at least one of the sources in the sources list.

    Args:
        incident (dict): The incident to check
        sources_list (list): The list of sources from the customer

    Returns:
        bool. Whether the incident has passed the filter or not
    """
    if len(sources_list) == 0:
        return True

    return any(source in incident.get("event_sources") for source in sources_list)


def pass_abuse_disposition_filter(incident, abuse_disposition_values):
    """Checks whether the incident's 'Abuse Disposition' value is in the abuse_disposition_values list.

    Args:
        incident (dict): The incident to check
        abuse_disposition_values (list): The list of relevant values from the customer

    Returns:
        bool. Whether the incident has passed the filter or not
    """
    if len(abuse_disposition_values) == 0:
        return True

    for incident_field in incident.get("incident_field_values", []):
        if incident_field["name"] == "Abuse Disposition" and incident_field["value"] in abuse_disposition_values:
            return True

    return False


def filter_incidents(incidents_list):
    """Filters the incidents list by 'abuse disposition' and 'source list' values

    Args:
        incidents_list (list): The incidents list to filter

    Returns:
        list. The filtered incidents list
    """
    filtered_incidents_list = []
    params = demisto.params()
    sources_list = argToList(params.get("event_sources"))
    abuse_disposition_values = argToList(params.get("abuse_disposition"))

    if not sources_list and not abuse_disposition_values:
        return incidents_list

    for incident in incidents_list:
        if pass_sources_list_filter(incident, sources_list) and pass_abuse_disposition_filter(incident, abuse_disposition_values):
            filtered_incidents_list.append(incident)

    return filtered_incidents_list


def get_incidents_request(params):
    """Perform an API request to get incidents from ProofPoint.

    Args:
        params(dict): The params of the request

    Returns:
        list. The incidents returned from the API call
    """
    fullurl = BASE_URL + "api/incidents"
    incidents_list = requests.get(
        fullurl, headers={"Content-Type": "application/json", "Authorization": API_KEY}, params=params, verify=VERIFY_CERTIFICATE
    )
    if incidents_list.status_code < 200 or incidents_list.status_code >= 300:
        if incidents_list.status_code == 502 or incidents_list.status_code == 504:
            return_error(
                "The operation failed. There is a possibility you are trying to get too many incidents.\n"
                "You may consider adding a filter argument to the command.\n"
                f"URL: {fullurl}, StatusCode: {incidents_list.status_code}"
            )
        else:
            return_error(f"The operation failed. URL: {fullurl}, StatusCode: {incidents_list.status_code}")

    return incidents_list.json()


def get_time_delta(fetch_delta):
    """Gets the time delta from a string that is combined with a number and a string of (minute/hour)
    Args:
        fetch_delta(str): The fetch delta param.
    Returns:
        The time delta.
    """
    fetch_delta_split = fetch_delta.strip().split(" ")
    if len(fetch_delta_split) != 2:
        raise Exception("The fetch_delta is invalid. Please make sure to insert both the number and the unit of the fetch delta.")

    unit = fetch_delta_split[1].lower()
    number = int(fetch_delta_split[0])

    if unit not in [
        "minute",
        "minutes",
        "hour",
        "hours",
    ]:
        raise Exception('The unit of fetch_delta is invalid. Possible values are "minutes" or "hours".')

    if "hour" in unit:
        time_delta = timedelta(hours=number)  # batch by hours
    else:
        time_delta = timedelta(minutes=number)  # batch by minutes
    return time_delta


def get_new_incidents(request_params, last_fetched_id):
    """Perform an API request to get incidents from ProofPoint , filters then according to params, order them and
    return only the new incidnts.

    As the api does not return the results in an specific order, we query the api on specific time frames using
    created_before and created_after using the fetch delta parameter.
    Args:
        request_params(dict): The params of the request
        last_fetched_id(int): The ID of the last incident that was fetched in the previous fetch.
    Returns:
        list. The incidents returned from after the necessary actions.
    """
    incidents = get_incidents_request(request_params)
    filtered_incidents_list = filter_incidents(incidents)
    ordered_incidents = sorted(filtered_incidents_list, key=lambda k: (k["created_at"], k["id"]))
    return list(filter(lambda incident: int(incident.get("id")) > last_fetched_id, ordered_incidents))


def get_incidents_batch_by_time_request(params):
    """Perform an API request to get incidents from ProofPoint in batches to prevent a timeout.

    As the api does not return the results in an specific order, we query the api on specific time frames using
    created_before and created_after using the fetch delta parameter.
    Args:
        params(dict): The params of the request

    Returns:
        list. The incidents returned from the API call
    """
    incidents_list = []  # type:list

    fetch_delta = params.get("fetch_delta", "6 hours")
    fetch_limit = int(params.get("fetch_limit", "50"))
    last_fetched_id = int(params.get("last_fetched_id", "0"))

    current_time = datetime.now()

    time_delta = get_time_delta(fetch_delta)

    created_after = datetime.strptime(params.get("created_after"), TIME_FORMAT)
    created_before = created_after + time_delta

    request_params = {
        "state": params.get("state"),
        "created_after": created_after.isoformat().split(".")[0] + "Z",
        "created_before": created_before.isoformat().split(".")[0] + "Z",
    }

    if message_id := params.get("message_id"):  # used in search quarantine
        request_params["message_id"] = message_id

    # while loop relevant for fetching old incidents
    while created_before < current_time and len(incidents_list) < fetch_limit:
        demisto.debug(
            "PTR: Entered the batch loop , with fetch_limit {} and incidents list {} and incident length {} "
            "with created_after {} and created_before {}.".format(
                str(fetch_limit),
                str([incident.get("id") for incident in incidents_list]),
                str(len(incidents_list)),
                str(request_params["created_after"]),
                str(request_params["created_before"]),
            )
        )

        new_incidents = get_new_incidents(request_params, last_fetched_id)
        incidents_list.extend(new_incidents)

        # advancing fetch time by given fetch delta time
        created_after = created_before
        created_before = created_before + time_delta

        # updating params according to the new times
        request_params["created_after"] = created_after.isoformat().split(".")[0] + "Z"
        request_params["created_before"] = created_before.isoformat().split(".")[0] + "Z"
        demisto.debug(f"PTR: End of the current batch loop with {len(incidents_list)!s} incidents")

    # fetching the last batch when created_before is bigger then current time = fetching new incidents
    if len(incidents_list) < fetch_limit:
        # fetching the last batch
        request_params["created_before"] = current_time.isoformat().split(".")[0] + "Z"
        new_incidents = get_new_incidents(request_params, last_fetched_id)
        incidents_list.extend(new_incidents)

        demisto.debug(
            "PTR: Finished the last batch, with fetch_limit {} and incidents list {} and incident length {}".format(
                str(fetch_limit), str([incident.get("id") for incident in incidents_list]), str(len(incidents_list))
            )
        )

    incidents_list_limit = incidents_list[:fetch_limit]
    return incidents_list_limit


def fetch_incidents_command():
    """
    Fetches incidents from the ProofPoint API.
    """
    integration_params = demisto.params()
    last_fetch = demisto.getLastRun().get("last_fetch", {})
    last_fetched_id = demisto.getLastRun().get("last_fetched_incident_id", {})

    fetch_delta = integration_params.get("fetch_delta", "6 hours")
    fetch_limit = integration_params.get("fetch_limit", "50")

    incidents_states = integration_params.get("states")
    for state in incidents_states:
        if not last_fetch.get(state):
            last_fetch[state] = FIRST_FETCH

    for state in incidents_states:
        if not last_fetched_id.get(state):
            last_fetched_id[state] = "0"

    incidents = []
    for state in incidents_states:
        request_params = {
            "created_after": last_fetch[state],
            "last_fetched_id": last_fetched_id[state],
            "fetch_delta": fetch_delta,
            "state": state,
            "fetch_limit": fetch_limit,
        }
        id = last_fetched_id[state]
        incidents_list = get_incidents_batch_by_time_request(request_params)
        for incident in incidents_list:
            id = incident.get("id")
            inc = {"name": f"ProofPoint_TRAP - ID {id}", "rawJSON": json.dumps(incident), "occurred": incident["created_at"]}
            incidents.append(inc)

        if incidents:
            last_fetch_time = incidents[-1]["occurred"]
            last_fetch[state] = (datetime.strptime(last_fetch_time, TIME_FORMAT) - timedelta(minutes=1)).isoformat().split(".")[
                0
            ] + "Z"
            last_fetched_id[state] = id

    demisto.debug(
        f"PTR: End of current fetch function with last_fetch {str(last_fetch)} and last_fetched_id {str(last_fetched_id)}"
    )

    demisto.setLastRun({"last_fetch": last_fetch})
    demisto.setLastRun({"last_fetched_incident_id": last_fetched_id})

    demisto.info(f"extracted {len(incidents)} incidents")

    demisto.incidents(incidents)


def create_add_comment_human_readable(incident):
    """Creates the human readable entry for the 'add_comment_to_incident' command

    Args:
        incident (dict): The incident to parse

    Returns:
        str. The command human readable in markdown format
    """
    human_readable = []
    human_readable_headers = ["Incident ID", "Created At", "Details", "Comments Summary", "Action ID"]
    incident_id = incident.get("incident_id")
    human_readable.append(
        {
            "Created At": incident.get("created_at"),
            "Incident ID": incident_id,
            "Details": incident.get("detail"),
            "Comments Summary": incident.get("summary"),
            "Action ID": incident.get("id"),
        }
    )

    return tableToMarkdown(
        f"Comments added successfully to incident:{incident_id}", human_readable, human_readable_headers, removeNull=True
    )


def add_comment_to_incident_command():
    """
    Adds comments to an incident by incident ID
    """
    args = demisto.args()
    incident_id = args.get("incident_id")
    comments_to_add = args.get("comments")
    details = args.get("details")
    request_body = {"summary": comments_to_add, "detail": details}

    fullurl = BASE_URL + f"api/incidents/{incident_id}/comments.json"
    incident_data = requests.post(
        fullurl,
        headers={"Content-Type": "application/json", "Authorization": API_KEY},
        json=request_body,
        verify=VERIFY_CERTIFICATE,
    )

    if incident_data.status_code < 200 or incident_data.status_code >= 300:
        return_error(f"Add comment to incident command failed. URL: {fullurl}, StatusCode: {incident_data.status_code}")

    incident_data = incident_data.json()
    human_readable = create_add_comment_human_readable(incident_data)

    return_outputs(
        human_readable, {"ProofPointTRAP.IncidentComment(val.incident_id === obj.incident_id)": incident_data}, incident_data
    )


def add_user_to_incident_command():
    """
    Adds user to an incident by incident ID
    """
    args = demisto.args()
    incident_id = args.get("incident_id")
    attackers = argToList(args.get("attackers"))
    targets = argToList(args.get("targets"))
    request_body = {"targets": targets, "attackers": attackers}

    fullurl = BASE_URL + "api/incidents/{incident_id}/users.json"
    incident_data = requests.post(
        fullurl,
        headers={"Content-Type": "application/json", "Authorization": API_KEY},
        json=request_body,
        verify=VERIFY_CERTIFICATE,
    )

    if incident_data.status_code < 200 or incident_data.status_code >= 300:
        return_error(f"Add comment to incident command failed. URL: {fullurl}, StatusCode: {incident_data.status_code}")

    return_outputs(f"The user was added successfully to incident {incident_id}", {}, {})


def parse_json_argument(argument_string_value, argument_name):
    parsed_arg = {}
    try:
        parsed_arg = json.loads(argument_string_value)
    except ValueError as error:
        return_error(f"The '{argument_name}' argument is not a valid json. Error: {error}")
    if not parsed_arg.get(argument_name):
        return_error(f"The '{argument_name}' json argument should start with a key named '{argument_name}'")

    return parsed_arg


def prepare_ingest_alert_request_body(args):
    json_arguments = ["attacker", "cnc_host", "detector", "email", "forensics_hosts", "target", "threat_info", "custom_fields"]
    request_body = {}  # type: dict
    for argument_name, argument_value in args.items():
        if argument_name in json_arguments:
            parsed_argument = parse_json_argument(argument_value, argument_name)
            request_body.update(parsed_argument)

        else:
            request_body[argument_name] = argument_value
    return request_body


def ingest_alert_command():
    """
    Ingest an alert into Threat Response.
    """
    args = demisto.args()
    json_source_id = args.pop("post_url_id", demisto.params().get("post_url_id"))

    if not json_source_id:
        return_error(
            "To ingest alert into TRAP, you mast specify a post_url_id,either as an argument or as an integration parameter."
        )

    request_body = prepare_ingest_alert_request_body(assign_params(**args))
    fullurl = BASE_URL + f"threat/json_event/events/{json_source_id}"
    alert_data = requests.post(
        fullurl, headers={"Content-Type": "application/json"}, json=request_body, verify=VERIFY_CERTIFICATE
    )

    if alert_data.status_code < 200 or alert_data.status_code >= 300:
        return_error(f"Failed to ingest the alert into TRAP. URL: {fullurl}, StatusCode: {alert_data.status_code}")

    return_outputs("The alert was successfully ingested to TRAP", {}, {})


def close_incident_command():
    args = demisto.args()
    incident_id = args.get("incident_id")
    details = args.get("details")
    summary = args.get("summary")
    request_body = {"summary": summary, "detail": details}

    fullurl = BASE_URL + f"api/incidents/{incident_id}/close.json"
    incident_data = requests.post(
        fullurl,
        headers={"Content-Type": "application/json", "Authorization": API_KEY},
        json=request_body,
        verify=VERIFY_CERTIFICATE,
    )

    if incident_data.status_code < 200 or incident_data.status_code >= 300:
        return_error(f"Incident closure failed. URL: {fullurl}, StatusCode: {incident_data.status_code}")

    return_outputs(f"The incident {incident_id} was successfully closed", {}, {})


def search_quarantine():
    args = demisto.args()
    arg_time = dateparser.parse(args.get("time"))
    emailTAPtime = 0
    if isinstance(arg_time, datetime):
        emailTAPtime = int(arg_time.timestamp())
    else:
        return_error("Timestamp was bad")

    lstAlert = []
    mid = args.get("message_id")
    recipient = args.get("recipient")
    limit_quarantine_occurred_time = argToBoolean(args.get("limit_quarantine_occurred_time", "True"))
    quarantine_limit = arg_to_number(args.get("quarantine_limit", 120))
    fetch_delta = arg_to_number(args.get("fetch_delta", 6))
    check_exact_reception_time = argToBoolean(args.get("check_exact_reception_time", True))

    request_params = {
        "created_after": datetime.strftime(arg_time - get_time_delta("1 hour"), TIME_FORMAT),  # for safety
        "fetch_delta": f"{fetch_delta} hours",
        "fetch_limit": "50",
        "message_id": mid,
    }

    incidents_list = get_incidents_batch_by_time_request(request_params)

    found = {"email": False, "mid": False, "quarantine": False}
    resQ = []

    # Collecting emails inside alert to find those with same recipient and messageId
    for incident in incidents_list:
        for alert in incident.get("events"):
            demisto.debug(f'New alert being processed with Alertid = {alert.get("id")}')
            for email in alert.get("emails"):
                demisto.debug(f'New email being processed with messageid {email.get("messageId")}')
                message_delivery_time = email.get("messageDeliveryTime", {})
                demisto.debug(f"PTR: Got {message_delivery_time=} with type {type(message_delivery_time)}.")
                if message_delivery_time and isinstance(message_delivery_time, dict):
                    message_delivery_time = message_delivery_time.get("millis")
                elif message_delivery_time and isinstance(message_delivery_time, str):
                    message_delivery_time = dateparser.parse(message_delivery_time)
                    if message_delivery_time:
                        message_delivery_time = int(message_delivery_time.timestamp() * 1000)
                    else:
                        demisto.info(f'PTR: Could not parse time of incident {incident.get("id")}, got {message_delivery_time=}')
                        continue

                if email.get("messageId") == mid and email.get("recipient").get("email") == recipient and message_delivery_time:
                    found["mid"] = True
                    demisto.debug("PTR: Found the email, adding the alert")
                    emailTRAPtimestamp = int(message_delivery_time / 1000)
                    if emailTAPtime == emailTRAPtimestamp or not check_exact_reception_time:
                        demisto.debug(f'PTR: Adding the alert with id {alert.get("id")}')
                        found["email"] = True
                        lstAlert.append(
                            {
                                "incidentid": incident.get("id"),
                                "alertid": alert.get("id"),
                                "alerttime": alert.get("received"),
                                "incidenttime": incident.get("created_at"),
                                "messageId": mid,
                                "quarantine_results": incident.get("quarantine_results"),
                            }
                        )
                    else:
                        demisto.debug(
                            f'PTR: Alert id {alert.get("id")} found but not added to lstAlert list as '
                            f'{emailTAPtime=} did not match {emailTRAPtimestamp=}'
                        )
                else:
                    demisto.debug(
                        f'PTR: skipped message with ID {email.get("messageId")} and recipient'
                        f' {email.get("recipient").get("email")}. As it did not match {mid=} and'
                        f' recipient={email.get("recipient").get("email")}'
                    )

    quarantineFoundcpt = 0

    # Go though the alert list, and check the quarantine results:
    for alert in lstAlert:
        for quarantine in alert.get("quarantine_results"):
            if quarantine.get("messageId") == mid and quarantine.get("recipient") == recipient:
                found["quarantine"] = True
                tsquarantine = dateparser.parse(quarantine.get("startTime"))
                tsalert = dateparser.parse(alert.get("alerttime"))
                if isinstance(tsquarantine, datetime) and isinstance(tsalert, datetime):
                    diff = (tsquarantine - tsalert).total_seconds()
                    # Append alerts if limit_quarantine_occurred_time=False
                    # else checks diff is less then quarantine_limit
                    if (not limit_quarantine_occurred_time) or (quarantine_limit and 0 < diff < quarantine_limit):
                        resQ.append(
                            {
                                "quarantine": quarantine,
                                "alert": {"id": alert.get("alertid"), "time": alert.get("alerttime")},
                                "incident": {"id": alert.get("incidentid"), "time": alert.get("incidenttime")},
                            }
                        )
                    else:
                        quarantineFoundcpt += 1
                        demisto.debug(
                            f'PTR: Quarantine found for {quarantine.get("messageId")} but not returned as it did not meet filter'
                            f' requirements.  limit_quarantine_occurred_time = {limit_quarantine_occurred_time} with type '
                            f'{type(limit_quarantine_occurred_time)}. diff = {diff}, quarantine_limit = {quarantine_limit}'
                        )
                else:
                    demisto.debug(f"PTR: Failed to parse timestamp of incident: {alert=} {quarantine=}.")

    if quarantineFoundcpt > 0:
        return CommandResults(
            readable_output=(
                f"{mid} Message ID matches to {quarantineFoundcpt} emails quarantined, but time between alert "
                f"received and the quarantine starting exceeded the quarantine_limit provided"
            )
        )
    if not found["mid"]:
        return CommandResults(readable_output=f"Message ID {mid} not found in TRAP incidents")

    midtxt = f"{mid} Message ID found in TRAP alerts,"
    if not found["email"]:
        return CommandResults(
            readable_output=f"{midtxt} but timestamp between email delivery time and time given as argument doesn't match"
        )
    elif not found["quarantine"]:
        demisto.debug("PTR: " + "\n".join([json.dumps(alt, indent=4) for alt in lstAlert]))
        return CommandResults(f"{midtxt} but not in the quarantine list meaning that email has not be quarantined.")

    return CommandResults(
        outputs_prefix="ProofPointTRAP.Quarantine",
        outputs=resQ,
        readable_output=tableToMarkdown("Quarantine Result", resQ),
        raw_response=resQ,
    )


""" EXECUTION CODE """


def main():
    handle_proxy(demisto.params().get("proxy"))
    command = demisto.command()
    demisto.info(f"Command being called is {command}")

    if command == "test-module":
        test()

    elif command == "fetch-incidents":
        fetch_incidents_command()

    elif command == "proofpoint-tr-get-list":
        get_list_command()

    elif command == "proofpoint-tr-add-to-list":
        add_to_list_command()

    elif command == "proofpoint-tr-block-ip":
        block_ip_command()

    elif command == "proofpoint-tr-block-domain":
        block_domain_command()

    elif command == "proofpoint-tr-block-url":
        block_url_command()

    elif command == "proofpoint-tr-block-hash":
        block_hash_command()

    elif command == "proofpoint-tr-delete-indicator":
        delete_indicator_command()

    elif command == "proofpoint-tr-search-indicator":
        search_indicator_command()

    elif command == "proofpoint-tr-list-incidents":
        list_incidents_command()

    elif command == "proofpoint-tr-get-incident":
        get_incident_command()

    elif command == "proofpoint-tr-update-incident-comment":
        add_comment_to_incident_command()

    elif command == "proofpoint-tr-add-user-to-incident":
        add_user_to_incident_command()

    elif command == "proofpoint-tr-ingest-alert":
        ingest_alert_command()

    elif command == "proofpoint-tr-close-incident":
        close_incident_command()

    elif command == "proofpoint-tr-verify-quarantine":
        return_results(search_quarantine())


if __name__ == "__builtin__" or __name__ == "builtins":
    main()