Proofpoint TAP v2

Use the Proofpoint Targeted Attack Protection (TAP) integration to protect against and provide additional visibility into phishing and other malicious email attacks.

Email · Proofpoint TAP

Details

IDProofpoint TAP v2
ProviderThoma Bravo
CategoryEmail
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Proofpoint TAP

Use the Proofpoint Targeted Attack Protection (TAP) integration to protect against and provide additional visibility into phishing and other malicious email attacks.
This integration was integrated and tested with version v2 of Proofpoint TAP v2

Events Fetch Look-Back

Proofpoint’s SIEM API does not always index events the moment they occur. Indexing can lag by up to ~30 minutes, so an event that already happened may not yet be returned when the integration queries for it in real time. Without a safety mechanism, these late-indexed events can be permanently missed.

The Events Fetch Look-Back (minutes) parameter solves this by overlapping fetch cycles:

  • Why it’s needed: Compensates for the SIEM API indexing delay so events that are indexed after their occurrence time are still collected.
  • How it works: On every fetch cycle the integration requeries the previous N minutes. Built-in deduplication ensures events already fetched are not ingested again, so no duplicates are created.
  • Recommended usage: A value of 30 minutes generally covers the typical indexing delay. Set it to 0 to disable look-back entirely.
  • Existing instances: For instances that were already configured before version 1.3.0, the look-back window is rolled out gradually rather than all at once. This prevents duplicate events from being created when look-back is first applied against historical data that was already fetched. No action is required on your part — the behavior is automatic and safe.

Configure Proofpoint TAP v2 in Cortex

Parameter Description Required
Server URL e.g., https://tap-api-v2.proofpoint.com True
Service Principal The password refers to secret True
API Version v1 is deprecated for new instances. The current API version is v2. False
Trust any certificate (not secure)   False
Use system proxy settings   False
Threat type A string specifying which threat type to return. If empty, all threat types are returned. Can be “url”, “attachment”, or “messageText”. False
Threat status A string specifying which threat statuses to return. If empty, will return “active” and “cleared” threats. Can be “active”, “cleared”, or “falsePositive”. False
Events to fetch   False
Maximum number of incident per fetch   False
First fetch time range First fetch time range (<number> <time unit>, e.g., 1 hour, 30 minutes). Proofpoint supports a maximum 1 week fetch back. False
Advanced: Raw message encoding The character encoding to apply on the message fetched (e.g. latin-1). Advanced configuration to be used only if instructed by XSOAR Support False
Events Fetch Look-Back (minutes) The number of minutes to look back during each fetch to handle the Proofpoint SIEM API indexing delay (up to 30 minutes). When set, each fetch cycle requeries the previous N minutes to catch late-indexing events that would otherwise be missed. Duplicates are filtered automatically. Set to 0 to disable. False
Fetch incidents   False
Incident type   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-get-events


Fetches events for all clicks and messages relating to known threats within the specified time period. Details as per clicks/blocked.

Base Command

proofpoint-get-events

Input

Argument Name Description Required
interval A string containing an ISO8601-formatted interval. If this interval overlaps with previous requests for data, records from the previous request might be duplicated. The minimum interval is thirty seconds. The maximum interval is one hour. Examples: 2016-05-01T12:00:00Z/2016-05-01T13:00:00Z - an hour interval, beginning at noon UTC on 05-01-2016 PT30M/2016-05-01T12:30:00Z - the thirty minutes beginning at noon UTC on 05-01-2016 and ending at 12:30pm * UTC 2016-05-01T05:00:00-0700/PT30M - the same interval as above, but using -0700 as the time zone. Optional
threatType A comma-separated list of the threat types to return. If empty, all threat types are returned. The following values are accepted: “url”, “attachment”, and “messageText”. Possible values are: url, attachment, messageText. Optional
threatStatus A string specifying which threat statuses to return. If empty, active and cleared threats are returned. Can be “active”, “cleared”, “falsePositive”. Possible values are: active, cleared, falsePositive. Optional
sinceTime A string containing an ISO8601 date. It represents the start of the data retrieval period. The end of the period is determined by the current API server time rounded to the nearest minute. If JSON output is selected, the end time is included in the returned result. Example: 2016-05-01T12:00:00Z. Optional
sinceSeconds An integer representing a time window (in seconds) from the current API server time. The start of the window is the current API server time, rounded to the nearest minute, less the number of seconds provided. The end of the window is the current API server time rounded to the nearest minute. If JSON output is selected, the end time is included in the returned result. Optional
eventTypes Event types to return. Possible values: “All”, “Issues”, “Delivered Messages”, “Blocked Messages”, “Permitted Clicks”, and “Blocked Clicks”. Possible values are: All, Issues, Delivered Messages, Blocked Messages, Permitted Clicks, Blocked Clicks. Default is All. Optional

Context Output

Path Type Description
Proofpoint.MessagesDelivered.GUID String The ID of the message within PPS. It can be used to identify the message in PPS, which is unique.
Proofpoint.MessagesDelivered.QID String The queue ID of the message within PPS. It can be used to identify the message in PPS, which is not unique.
Proofpoint.MessagesDelivered.ccAddresses String A list of email addresses contained within the CC: header, excluding any friendly names.
Proofpoint.MessagesDelivered.clusterId String The name of the PPS cluster which processed the message.
Proofpoint.MessagesDelivered.fromAddress String The email address contained in the From: header, excluding any friendly name.
Proofpoint.MessagesDelivered.headerCC String The CC header.
Proofpoint.MessagesDelivered.headerFrom String The full content of the From: header, including any friendly name.
Proofpoint.MessagesDelivered.headerReplyTo String If present, the full content of the Reply-To: header, including any friendly names.
Proofpoint.MessagesDelivered.impostorScore Number The impostor score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.malwareScore Number The malware score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.messageId String Message-ID extracted from the headers of the email message. It can be used to look up the associated message in PPS, which is not unique.
Proofpoint.MessagesDelivered.threatsInfoMap.threat String The artifact which was condemned by Proofpoint. The malicious URL, hash of the attachment threat, or email address of the impostor sender.
Proofpoint.MessagesDelivered.threatsInfoMap.threatId String The unique identifier associated with this threat. It can be used to query the forensics and campaign endpoints.
Proofpoint.MessagesDelivered.threatsInfoMap.threatStatus String The current state of the threat (active, expired, false-positive, cleared).
Proofpoint.MessagesDelivered.threatsInfoMap.threatTime Date The time Proofpoint assigned the threatStatus (ISO8601 format).
Proofpoint.MessagesDelivered.threatsInfoMap.threatType String Whether the threat was an attachment, URL, or message type.
Proofpoint.MessagesDelivered.threatsInfoMap.threatUrl String A link to the entry about the threat on the TAP Dashboard.
Proofpoint.MessagesDelivered.messageTime Date The time the message was delivered to the user or quarantined by PPS.
Proofpoint.MessagesDelivered.modulesRun String The list of PPS modules that processed the message.
Proofpoint.MessagesDelivered.phishScore Number The phishing score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.policyRoutes String The policy routes that the message matched during processing by PPS.
Proofpoint.MessagesDelivered.quarantineFolder String The name of the folder that contains the quarantined message. This appears only for messagesBlocked.
Proofpoint.MessagesDelivered.quarantineRule String The name of the rule that quarantined the message. This appears only for messagesBlocked events.
Proofpoint.MessagesDelivered.recipient String A list containing the email addresses of the recipients.
Proofpoint.MessagesDelivered.replyToAddress String The email address contained in the Reply-To: header, excluding any friendly name.
Proofpoint.MessagesDelivered.sender String The email address of the SMTP (envelope) sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.MessagesDelivered.senderIP String The IP address of the sender.
Proofpoint.MessagesDelivered.spamScore Number The spam score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.subject String The subject line of the message, if available.
Proofpoint.MessagesBlocked.GUID String The ID of the message within PPS. It can be used to identify the message in PPS, which is unique.
Proofpoint.MessagesBlocked.QID String The queue ID of the message within PPS. It can be used to identify the message in PPS, which is not unique.
Proofpoint.MessagesBlocked.ccAddresses String A list of email addresses contained within the CC: header, excluding any friendly names.
Proofpoint.MessagesBlocked.clusterId String The name of the PPS cluster that processed the message.
Proofpoint.MessagesBlocked.fromAddress String The email address contained in the From: header, excluding any friendly name.
Proofpoint.MessagesBlocked.headerCC String The CC header.
Proofpoint.MessagesBlocked.headerFrom String The full content of the From: header, including any friendly name.
Proofpoint.MessagesBlocked.headerReplyTo String If present, the full content of the Reply-To: header, including any friendly names.
Proofpoint.MessagesBlocked.impostorScore Number The impostor score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.malwareScore Number The malware score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.messageId String Message-ID extracted from the headers of the email message. It can be used to look up the associated message in PPS, which is not unique.
Proofpoint.MessagesBlocked.threatsInfoMap.threat String The artifact which was condemned by Proofpoint. The malicious URL, hash of the attachment threat, or email address of the impostor sender.
Proofpoint.MessagesBlocked.threatsInfoMap.threatId String The unique identifier associated with this threat. It can be used to query the forensics and campaign endpoints.
Proofpoint.MessagesBlocked.threatsInfoMap.threatStatus String The current state of the threat (active, expired, false-positive, cleared).
Proofpoint.MessagesBlocked.threatsInfoMap.threatTime Date The time Proofpoint assigned the threatStatus (ISO8601 format).
Proofpoint.MessagesBlocked.threatsInfoMap.threatType String Whether the threat was an attachment, URL, or message type.
Proofpoint.MessagesBlocked.threatsInfoMap.threatUrl String A link to the entry about the threat on the TAP dashboard.
Proofpoint.MessagesBlocked.messageTime Date The time the message was blocked to the user or quarantined by PPS.
Proofpoint.MessagesBlocked.messageTime String The list of PPS modules that processed the message.
Proofpoint.MessagesBlocked.modulesRun String The list of PPS modules that processed the message.
Proofpoint.MessagesBlocked.phishScore Number The phishing score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.policyRoutes String The policy routes that the message matched during processing by PPS.
Proofpoint.MessagesBlocked.quarantineFolder String The name of the folder that contains the quarantined message. This appears only for messagesBlocked.
Proofpoint.MessagesBlocked.quarantineRule String The name of the rule that quarantined the message. This appears only for messagesBlocked events.
Proofpoint.MessagesBlocked.recipient String A list containing the email addresses of the recipients.
Proofpoint.MessagesBlocked.replyToAddress String The email address contained in the Reply-To: header, excluding any friendly name.
Proofpoint.MessagesBlocked.sender String The email address of the SMTP (envelope) sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.MessagesBlocked.senderIP String The IP address of the sender.
Proofpoint.MessagesBlocked.spamScore Number The spam score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.subject String The subject line of the message, if available.
Proofpoint.ClicksPermitted.GUID String The ID of the message within PPS. It can be used to identify the message in PPS, which is unique.
Proofpoint.ClicksPermitted.campaignId String An identifier for the campaign of which the threat is a member, if available at the time of the query. Threats can be linked to campaigns even after these events are retrieved.
Proofpoint.ClicksPermitted.classification String The threat category of the malicious URL.
Proofpoint.ClicksPermitted.clickIP String The external IP address of the user who clicked the link. If the user is behind a firewall performing network address translation, the IP address of the firewall will be shown.
Proofpoint.ClicksPermitted.clickTime Date The time the user clicked the URL.
Proofpoint.ClicksPermitted.messageID String The Message-ID extracted from the headers of the email message. It can be used to look up the associated message in PPS and is not unique.
Proofpoint.ClicksPermitted.recipient String The email address of the recipient.
Proofpoint.ClicksPermitted.sender String The email address of the sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.ClicksPermitted.senderIP String The IP address of the sender.
Proofpoint.ClicksPermitted.threatID String The unique identifier associated with this threat. It can be used to query the forensics and campaign endpoints.
Proofpoint.ClicksPermitted.threatTime Date The time Proofpoint identified the URL as a threat.
Proofpoint.ClicksPermitted.threatURL String A link to the entry on the TAP Dashboard for the particular threat.
Proofpoint.ClicksPermitted.url String The malicious URL which was clicked.
Proofpoint.ClicksPermitted.userAgent String The User-Agent header from the clicker’s HTTP request.
Proofpoint.ClicksBlocked.GUID String The ID of the message within PPS. It can be used to identify the message in PPS and is guaranteed to be unique.
Proofpoint.ClicksBlocked.campaignId String An identifier for the campaign of which the threat is a member, if available at the time of the query. Threats can be linked to campaigns even after these events are retrieved.
Proofpoint.ClicksBlocked.classification String The threat category of the malicious URL.
Proofpoint.ClicksBlocked.clickIP String The external IP address of the user who clicked the link. If the user is behind a firewall performing network address translation, the IP address of the firewall will be shown.
Proofpoint.ClicksBlocked.clickTime Date The time the user clicked the URL.
Proofpoint.ClicksBlocked.messageID String Message-ID extracted from the headers of the email message. It can be used to look up the associated message in PPS and is not unique.
Proofpoint.ClicksBlocked.recipient String The email address of the recipient.
Proofpoint.ClicksBlocked.sender String The email address of the sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.ClicksBlocked.senderIP String The IP address of the sender.
Proofpoint.ClicksBlocked.threatID String The unique identifier associated with this threat. It can be used to query the forensics and campaign endpoints.
Proofpoint.ClicksBlocked.threatTime Date The time Proofpoint identified the URL as a threat.
Proofpoint.ClicksBlocked.threatURL String A link to the entry on the TAP dashboard for the particular threat.
Proofpoint.ClicksBlocked.url String The malicious URL that was clicked.
Proofpoint.ClicksBlocked.userAgent String The User-Agent header from the clicker’s HTTP request.

Command Example

!proofpoint-get-events interval="2021-06-07T02:00:00Z/2021-06-07T03:00:00Z"

Context Example

{
    "Proofpoint": {
        "ClicksBlocked": null,
        "ClicksPermitted": null,
        "MessagesBlocked": [
            {
                "GUID": "9JRzwqiZEzBesdfnEM48ItsowO9ZJ1jmBbo",
                "QID": "3901vhsdfsdfg0q5d-1",
                "ccAddresses": [],
                "cluster": "hosted",
                "completelyRewritten": false,
                "fromAddress": [
                    "xxxx@xxx.com"
                ],
                "headerFrom": "\"xxxx@xxx.com\" <xxxx@xxx.com>",
                "headerReplyTo": null,
                "id": "867899c4-bbbvnvde-9948-fxv0a2-740c13aafb98",
                "impostorScore": 0,
                "malwareScore": 0,
                "messageID": "<98fd30b9-b15b-b883-ca4xcvxvc8-3a0dsfsf766719bc4@xxxx@xxx.com>",
                "messageParts": [
                    {
                        "contentType": "text/html",
                        "disposition": "inline",
                        "filename": "text.html",
                        "md5": "af671999d59182d8e66e100d4140b577",
                        "oContentType": "text/html",
                        "sandboxStatus": null,
                        "sha256": "99e2546be00c1c2a763a51861dfgf6b2981871051843dc18542ba1417b0b464c00f"
                    }
                ],
                "messageSize": 3684,
                "messageTime": "2021-06-07T01:50:00.000Z",
                "modulesRun": [
                    "av",
                    "spf",
                    "dkimv",
                    "spam",
                    "dmarc",
                    "pdr",
                    "urldefense"
                ],
                "phishScore": 100,
                "policyRoutes": [
                    "default_inbound"
                ],
                "quarantineFolder": "Phish",
                "quarantineRule": "inbound_spam_phish",
                "recipient": [
                    "xxxx@xxx.com"
                ],
                "replyToAddress": [],
                "sender": "xxxx@xxx.com",
                "senderIP": "000.000.000.000",
                "spamScore": 100,
                "subject": "Your mailbox is full......",
                "threatsInfoMap": [
                    {
                        "campaignID": null,
                        "classification": "phish",
                        "threat": "io/login/verify",
                        "threatID": "9a53601a616eb78609e525sdfsdfc0f73356c3d9ff80f00e782105ff08c53ee5a3cfca",
                        "threatStatus": "active",
                        "threatTime": "2021-06-07T00:47:12.000Z",
                        "threatType": "url",
                        "threatUrl": "https://threatinsight.proofpoint.com"
                    }
                ],
                "toAddresses": [
                    "xxxx@xxx.com"
                ],
                "xmailer": null
            }
        ],
        "MessagesDelivered": null
    }
}

Human Readable Output

Proofpoint Events

clicksBlocked clicksPermitted messagesBlocked messagesDelivered queryEndTime
    {‘spamScore’: 100, ‘phishScore’: 100, ‘threatsInfoMap’: [{‘threatID’: ‘9a53601a616eb78609e525c0f73356c3d9ff80f00e782105ff08c53ee5a3cfca’, ‘threatStatus’: ‘active’, ‘classification’: ‘phish’, ‘threatUrl’: ‘https://threatinsight.proofpoint.com’, ‘threatTime’: ‘2021-06-07T00:47:12.000Z’, ‘threat’: ‘storage.libertychurch9848737878.io/login/verify’, ‘campaignID’: None, ‘threatType’: ‘url’}, {‘threatID’: ‘b72f9ac2cec86c5f2fb795ea47f2aea23d402fe46c5c64e2565363464b1b0eb2’, ‘threatStatus’: ‘active’, ‘classification’: ‘phish’, ‘threatUrl’: ‘https://threatinsight.proofpoint.com/1c863185-589c-ad2d-49cb-0020fe555aae/threat/email/b72f9ac2cec86c5f2fb795ea47f2aea23d402fe46c5c64e2565363464b1b0eb2’, ‘threatTime’: ‘2021-06-07T00:47:23.000Z’, ‘threat’: ‘libertychurch9848737878.io’, ‘campaignID’: None, ‘threatType’: ‘url’}, {‘threatID’: ‘da0ba8d6a9d5111900f5927eb4554e49fd30e6c5c4ad5b0c975feeb19c3bfc5b’, ‘threatStatus’: ‘active’, ‘classification’: ‘phish’, ‘threatUrl’: ‘https://threatinsight.proofpoint.com/1c863185-589c-ad2d-49cb-0020fe555aae/threat/email/da0ba8d6a9d5111900f5927eb4554e49fd30e6c5c4ad5b0c975feeb19c3bfc5b’, ‘threatTime’: ‘2021-06-07T00:47:22.000Z’, ‘threat’: ‘storage.libertychurch9848737878.io/login/’, ‘campaignID’: None, ‘threatType’: ‘url’}], ‘messageTime’: ‘2021-06-07T01:50:00.000Z’, ‘impostorScore’: 0.0, ‘malwareScore’: 0, ‘cluster’: ‘hosted’, ‘subject’: ‘Your mailbox is full……’, ‘quarantineFolder’: ‘Phish’, ‘quarantineRule’: ‘inbound_spam_phish’, ‘policyRoutes’: [‘default_inbound’], ‘modulesRun’: [‘av’, ‘spf’, ‘dkimv’, ‘spam’, ‘dmarc’, ‘pdr’, ‘urldefense’], ‘messageSize’: 3684, ‘headerFrom’: ‘“xxxx@xxx.com” xxxx@xxx.com’, ‘headerReplyTo’: None, ‘fromAddress’: [‘xxxx@xxx.com’], ‘ccAddresses’: [], ‘replyToAddress’: [], ‘toAddresses’: [‘xxxx@xxx.com’], ‘xmailer’: None, ‘messageParts’: [{‘disposition’: ‘inline’, ‘sha256’: ‘99e2546be00c1c2a763a51861f6b29818710dsfsdf51843dc18542ba1417b0b464c00f’, ‘md5’: ‘af671999d59182d8e66e100d4140b577’, ‘filename’: ‘text.html’, ‘sandboxStatus’: None, ‘oContentType’: ‘text/html’, ‘contentType’: ‘text/html’}], ‘completelyRewritten’: False, ‘id’: ‘867899c4-bbde-9948-f0a2-740c13aafb98’, ‘QID’: ‘3901vhsdvf0q5d-1’, ‘GUID’: ‘9JRzwqisvsdvZEzBenEM48ItsowO9ZJ1jmBbo’, ‘sender’: ‘xxxx@xxx.com’, ‘recipient’: [‘xxxx@xxx.com’], ‘senderIP’: ‘000.000.000.000’, ‘messageID’: ‘xxxx@xxx.com’}    

proofpoint-get-forensics


Returns forensics evidence.

Base Command

proofpoint-get-forensics

Input

Argument Name Description Required
threatId The ID of the threat (use with either threatId or campaignId). Optional
campaignId ID of the campaign (use with either threatId or campaignId). Optional
includeCampaignForensics Whether to include forensic evidence for the whole campaign. Can be used with threatId only. Default is false. Optional

Context Output

Path Type Description
Proofpoint.Report.ID String The ID of the report.
Proofpoint.Report.Type String The threat type. Can be: “attachment”, “url”, or “hybrid”.
Proofpoint.Report.Scope String Whether the report scope covers a campaign or an individual threat.
Proofpoint.Report.Attachment.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Attachment.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Attachment.Display String A friendly display string.
Proofpoint.Report.Attachment.SHA256 String The SHA256 hash of the attachment’s contents.
Proofpoint.Report.Attachment.MD5 String The MD5 hash of the attachment’s contents.
Proofpoint.Report.Attachment.Blacklisted Number Optional. Whether the file was block listed.
Proofpoint.Report.Attachment.Offset Number Optional. The offset in bytes where the malicious content was found.
Proofpoint.Report.Attachment.Size Number Optional. The size in bytes of the attachment’s contents.
Proofpoint.Report.Attachment.Platform.Name String The name of the platform.
Proofpoint.Report.Attachment.Platform.OS String The operating system of the platform.
Proofpoint.Report.Attachment.Platform.Version String The version of the platform.
Proofpoint.Report.Cookie.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Cookie.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Cookie.Display String A friendly display string.
Proofpoint.Report.Cookie.Action String Whether the cookie was set or deleted.
Proofpoint.Report.Cookie.Domain String The domain that set the cookie.
Proofpoint.Report.Cookie.Key String The name of the cookie being set or deleted.
Proofpoint.Report.Cookie.Value String Optional. The content of the cookie being set.
Proofpoint.Report.Cookie.Platform.Name String Name of the platform.
Proofpoint.Report.Cookie.Platform.OS String The operating system of the platform.
Proofpoint.Report.Cookie.Platform.Version String The version of the platform.
Proofpoint.Report.DNS.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.DNS.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.DNS.Display String A friendly display string.
Proofpoint.Report.DNS.Host String The hostname being resolved.
Proofpoint.Report.DNS.CNames String Optional. An array of CNames, which were associated with the hostname.
Proofpoint.Report.DNS.IP String Optional. An array of IP addresses that were resolved to the hostname.
Proofpoint.Report.DNS.NameServers String Optional. The nameservers responsible for the hostname’s domain.
Proofpoint.Report.DNS.NameServersList String Optional. The nameservers responsible for the hostnames.
Proofpoint.Report.DNS.Platform.Name String The name of the platform.
Proofpoint.Report.DNS.Platform.OS String The operating system of the platform.
Proofpoint.Report.DNS.Platform.Version String The version of the platform.
Proofpoint.Report.Dropper.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Dropper.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Dropper.Display String A friendly display string.
Proofpoint.Report.Dropper.Path String The location of the dropper file.
Proofpoint.Report.Dropper.URL String Optional. The name of the static rule inside the sandbox that identified the dropper.
Proofpoint.Report.Dropper.Rule String Optional. The URL the dropper contacted.
Proofpoint.Report.Dropper.Platform.Name String The name of the platform.
Proofpoint.Report.Dropper.Platform.OS String The operating system of the platform.
Proofpoint.Report.Dropper.Platform.Version String The version of the platform.
Proofpoint.Report.File.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.File.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.File.Display String A friendly display string.
Proofpoint.Report.File.Path String Optional. The location of the file operated on.
Proofpoint.Report.File.Action String Optional. The filesystem call made (create, modify, or delete).
Proofpoint.Report.File.Rule String Optional. The name of the static rule inside the sandbox that identified the suspicious file.
Proofpoint.Report.File.SHA256 Unknown Optional. The SH256 hash of the file’s contents.
Proofpoint.Report.File.MD5 String Optional. The MD5 hash of the file’s contents.
Proofpoint.Report.File.Size Number Optional. The size in bytes of the file’s contents.
Proofpoint.Report.File.Platform.Name String The name of the platform.
Proofpoint.Report.File.Platform.OS String The operating system of the platform.
Proofpoint.Report.File.Platform.Version String The version of the platform.
Proofpoint.Report.IDS.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.IDS.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.IDS.Display String A friendly display string.
Proofpoint.Report.IDS.Name String The friendly name of the IDS rule that observed the malicious traffic.
Proofpoint.Report.IDS.SignatureID String The identifier of the IDS rule that observed the malicious traffic.
Proofpoint.Report.IDS.Platform.Name String The name of the platform.
Proofpoint.Report.IDS.Platform.OS String The operating system of the platform.
Proofpoint.Report.IDS.Platform.Version String The version of the platform.
Proofpoint.Report.Mutex.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Mutex.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Mutex.Display String A friendly display string.
Proofpoint.Report.Mutex.Name String The name of the mutex.
Proofpoint.Report.Mutex.Path String Optional. The path to the process which spawned the mutex.
Proofpoint.Report.Mutex.Platform.Name String The name of the platform.
Proofpoint.Report.Mutex.Platform.OS String The operating system of the platform.
Proofpoint.Report.Mutex.Platform.Version String The version of the platform.
Proofpoint.Report.Network.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Network.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Network.Display String A friendly display string.
Proofpoint.Report.Network.Action String The type of network activity being initiated (connect or listen).
Proofpoint.Report.Network.IP String The remote IP address being contacted.
Proofpoint.Report.Network.Port String The remote IP port being contacted.
Proofpoint.Report.Network.Type String The protocol being used (tcp or udp).
Proofpoint.Report.Network.Platform.Name String The name of the platform.
Proofpoint.Report.Network.Platform.OS String The operating system of the platform.
Proofpoint.Report.Network.Platform.Version String The version of the platform.
Proofpoint.Report.Process.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Process.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Process.Display String A friendly display string.
Proofpoint.Report.Process.Action String The action performed on the process. Relevant when create is produced.
Proofpoint.Report.Process.Path String The location of the executable that spawned the process.
Proofpoint.Report.Process.Platform.Name String The name of the platform.
Proofpoint.Report.Process.Platform.OS String The operating system of the platform.
Proofpoint.Report.Process.Platform.Version String The version of the platform.
Proofpoint.Report.Registry.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Registry.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Registry.Display String A friendly display string.
Proofpoint.Report.Registry.Name String Optional. The name of the registry entry being created or set.
Proofpoint.Report.Registry.Action String The registry change made (create or set).
Proofpoint.Report.Registry.Key String The location of the registry key being modified.
Proofpoint.Report.Registry.Value String Optional. The contents of the key being created or set.
Proofpoint.Report.Registry.Platform.Name String The name of the platform.
Proofpoint.Report.Registry.Platform.OS String The operating system of the platform.
Proofpoint.Report.Registry.Platform.Version String The version of the platform.
Proofpoint.Report.URL.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.URL.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.URL.Display String A friendly display string.
Proofpoint.Report.URL.URL String The URL which was observed.
Proofpoint.Report.URL.Blacklisted Boolean Optional. Whether the URL appeared on a block list.
Proofpoint.Report.URL.SHA256 String Optional. The SHA256 hash of the file downloaded from the URL.
Proofpoint.Report.URL.MD5 String Optional. The MD5 hash of the file downloaded from the URL.
Proofpoint.Report.URL.Size Number Optional. The size in bytes of the file retrieved from the URL.
Proofpoint.Report.URL.HTTPStatus Number Optional. The HTTP status code that was produced when our sandbox visited the URL.
Proofpoint.Report.URL.IP String Optional. The IP address that was resolved to the hostname by the sandbox.
Proofpoint.Report.URL.Platform.Name String The name of the platform.
Proofpoint.Report.URL.Platform.OS String The operating system of the platform.
Proofpoint.Report.URL.Platform.Version String The version of the platform.
Proofpoint.Report.Behavior.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Behavior.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Behavior.Display String A friendly display string.
Proofpoint.Report.Behavior.URL String The URL that was observed.
Proofpoint.Report.Behavior.Path String The location of the executable which spawned the behavior.
Proofpoint.Report.Behavior.Platform.Name String The name of the platform.
Proofpoint.Report.Behavior.Platform.OS String The operating system of the platform.
Proofpoint.Report.Behavior.Platform.Version String The version of the platform.
Proofpoint.Report.Behavior.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Behavior.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Behavior.Display String A friendly display string.
Proofpoint.Report.Behavior.URL String The URL that was observed.
Proofpoint.Report.Behavior.Path String The location of the executable that spawned the behavior.
Proofpoint.Report.Behavior.Platform.Name String The name of the platform.
Proofpoint.Report.Behavior.Platform.OS String The operating system of the platform.
Proofpoint.Report.Behavior.Platform.Version String The version of the platform.
Proofpoint.Report.Screenshot.Time Date The relative time at which the evidence was observed during sandboxing.
Proofpoint.Report.Screenshot.Malicious String Whether the evidence was used to reach a malicious verdict.
Proofpoint.Report.Screenshot.Display String A friendly display string.
Proofpoint.Report.Screenshot.URL String The URL hosting the screenshot image.

Command Example

!proofpoint-get-forensics campaignId="35e291e1-c9da-4ebd-b229-538bf759b546"

Context Example

{
    "Proofpoint": {
        "Report": {
            "Attachment": [
                {
                    "Display": "Malicious attachment with SHA-256: 1c207a1ea4b89cc63c2d8391afcf25",
                    "Malicious": true,
                    "Platform": [
                        {
                            "Name": "Win10",
                            "OS": "win",
                            "Version": "win10"
                        }
                    ],
                    "SHA256": "1c207da4b89cc63c2d8391afcf25",
                    "Time": 0
                }
            ]
        }
    }
}

Human Readable Output

Forensic results from ProofPoint for ID: 35e291e1-c9da-4ebd-b229-538bf759b546

ID Scope Type
35e291e1-c9da-4ebd-b229-538bf759b546 CAMPAIGN  

proofpoint-get-events-clicks-blocked


Gets events for clicks to malicious URLs blocked in the specified time period. Must provide either the interval or time_range arguments.

Base Command

proofpoint-get-events-clicks-blocked

Input

Argument Name Description Required
threat_status Click’s threat status to be retrieved. If no value is specified, active and cleared threats will be retrieved. Possible values: ‘active’, ‘cleared’, and ‘falsePositive’. Possible values are: active, cleared, falsePositive. Optional
interval ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour. For example: 2021-04-27T09:00:00Z/2021-04-27T10:00:00Z. Optional
time_range Represents the start of the data retrieval period. For example: 1 week, 2 days, 3 hours, etc. The maximum is 1 week. Optional

Context Output

Path Type Description
Proofpoint.ClicksBlocked.url String The malicious URL was clicked.
Proofpoint.ClicksBlocked.classification String The threat category of the malicious URL (Malware, Phish, or Spam)
Proofpoint.ClicksBlocked.clickTime Date The time the user clicked the URL.
Proofpoint.ClicksBlocked.threatTime Date The time that Proofpoint identified the URL as a threat.
Proofpoint.ClicksBlocked.userAgent String The User-Agent header from the clicker’s HTTP request.
Proofpoint.ClicksBlocked.campaignId String An identifier for the campaign of which the threat is a member.
Proofpoint.ClicksBlocked.id String The unique ID of the click.
Proofpoint.ClicksBlocked.clickIP String The external IP address of the user who clicked the link.
Proofpoint.ClicksBlocked.sender String The email address of the sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.ClicksBlocked.recipient String The email address of the recipient.
Proofpoint.ClicksBlocked.senderIP String The IP address of the sender.
Proofpoint.ClicksBlocked.threatID String The unique identifier associated with this threat.
Proofpoint.ClicksBlocked.threatURL String A link to the entry on the TAP dashboard for the particular threat.
Proofpoint.ClicksBlocked.threatStatus String The current state of the threat.
Proofpoint.ClicksBlocked.messageID String The ID of the message that the URL belongs to.
Proofpoint.ClicksBlocked.GUID String The ID of the message within PPS. It can be used to identify the message in PPS.

Command Example

!proofpoint-get-events-clicks-blocked time_range="1 hour"

Context Example

{
    "Proofpoint": {
        "ClicksBlocked": {
            "campaignId": "46e01b8a-c899-404d-bcd9-189bb393d1a7",
            "classification": "MALWARE",
            "clickIP": "192.0.2.2",
            "clickTime": "2010-01-22T00:00:10.000Z",
            "messageID": "4444",
            "recipient": "xxxx@xxx.com",
            "sender": "xxxx@xxx.com",
            "senderIP": "000.000.000.000",
            "threatID": "threat_num2",
            "threatTime": "2010-01-22T00:00:20.000Z",
            "threatURL": "https://threatinsight.proofpoint.com",
            "url": "http://badguy.zz/",
            "userAgent": "Mozilla/5.0(WindowsNT6.1;WOW64;rv:27.0)Gecko/20100101Firefox/27.0"
        }
    }
}

Human Readable Output

Blocked Clicks

Id Sender IP Recipient Classification Threat ID Threat URL Threat Status Threat Time Click Time Campaign Id User Agent
  000.000.000.000 xxxx@xxx.com MALWARE threat_num2 https://threatinsight.proofpoint.com   2010-01-22T00:00:20.000Z 2010-01-22T00:00:10.000Z 46e01b8a-c899-404d-bcd9-189bb393d1a7 Mozilla/5.0(WindowsNT6.1;WOW64;rv:27.0)Gecko/20100101Firefox/27.0

proofpoint-get-events-clicks-permitted


Get events for clicks to malicious URLs permitted in the specified time period. Must provide either the interval or time_range arguments.

Base Command

proofpoint-get-events-clicks-permitted

Input

Argument Name Description Required
threat_status Click’s threat status to be retrieved. If no value is specified, active and cleared threats will be retrieved. Possible values: ‘active’, ‘cleared’, and ‘falsePositive’. Possible values are: active, cleared, falsePositive. Optional
interval ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour. For example: 2021-04-27T09:00:00Z/2021-04-27T10:00:00Z. Optional
time_range Represents the start of the data retrieval period. For example: 1 week, 2 days, 3 hours, etc. The maximum is 1 week. Optional

Context Output

Path Type Description
Proofpoint.ClicksPermitted.url String The malicious URL that was clicked.
Proofpoint.ClicksPermitted.classification String The threat category of the malicious URL (Malware, Phish, or Spam).
Proofpoint.ClicksPermitted.clickTime Date The time the user clicked the URL.
Proofpoint.ClicksPermitted.threatTime Date The time that Proofpoint identified the URL as a threat.
Proofpoint.ClicksPermitted.userAgent String The User-Agent header from the clicker’s HTTP request.
Proofpoint.ClicksPermitted.campaignId String An identifier for the campaign of which the threat is a member.
Proofpoint.ClicksPermitted.id String The unique ID of the click.
Proofpoint.ClicksPermitted.clickIP String The external IP address of the user who clicked the link.
Proofpoint.ClicksPermitted.sender String The email address of the sender. The user-part is hashed. The domain-part is in cleartext.
Proofpoint.ClicksPermitted.recipient String The email address of the recipient.
Proofpoint.ClicksPermitted.senderIP String The IP address of the sender.
Proofpoint.ClicksPermitted.threatID String The unique identifier associated with this threat.
Proofpoint.ClicksPermitted.threatURL String A link to the entry on the TAP dashboard for the particular threat.
Proofpoint.ClicksPermitted.threatStatus String The current state of the threat.
Proofpoint.ClicksPermitted.messageID String The ID of the message that the URL belongs to.
Proofpoint.ClicksPermitted.GUID String The ID of the message within PPS. It can be used to identify the message in PPS.

Command Example

!proofpoint-get-events-clicks-permitted time_range="1 hour"

Context Example

{
    "Proofpoint": {
        "ClicksPermitted": {
            "campaignId": "46e01b8a-c899-404d-bcd9-189bb393d1a7",
            "classification": "MALWARE",
            "clickIP": "192.0.2.2",
            "clickTime": "2010-01-22T00:00:10.000Z",
            "messageID": "4444",
            "recipient": "xxxx@xxx.com",
            "sender": "9facbf452def2d7efc5b5c48cdb837fa@badguy.zz",
            "senderIP": "000.000.000.000",
            "threatID": "threat_num2",
            "threatTime": "2010-01-22T00:00:20.000Z",
            "threatURL": "https://threatinsight.proofpoint.com",
            "url": "http://badguy.zz/",
            "userAgent": "Mozilla/5.0(WindowsNT6.1;WOW64;rv:27.0)Gecko/20100101Firefox/27.0"
        }
    }
}

Human Readable Output

Permitted Clicks

Id Sender IP Recipient Classification Threat ID Threat URL Threat Status Threat Time Click Time Campaign Id User Agent
  192.0.2.255 xxxx@xxx.com MALWARE threat_num2 https://threatinsight.proofpoint.com   2010-01-22T00:00:20.000Z 2010-01-22T00:00:10.000Z 46e01b8a-c899-404d-bcd9-189bb393d1a7 Mozilla/5.0(WindowsNT6.1;WOW64;rv:27.0)Gecko/20100101Firefox/27.0

proofpoint-get-events-messages-blocked


Get events for blocked messages in the specified time period. Must provide either the interval or time_range arguments.

Base Command

proofpoint-get-events-messages-blocked

Input

Argument Name Description Required
threat_type Message’s threat type to be retrieved. If no value is specified, all threat types will be retrieved. Possible values: ‘url’, ‘attachment’, and ‘message’. Possible values are: url, attachment, message. Optional
threat_status Message’s threat status to be retrieved. If no value is specified, active and cleared threats will be retrieved. Possible values: ‘active’, ‘cleared’, and ‘falsePositive’. Possible values are: active, cleared, falsePositive. Optional
interval ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour. For example: 2021-04-27T09:00:00Z/2021-04-27T10:00:00Z. Optional
time_range Represents the start of the data retrieval period. For example: 1 week, 2 days, 3 hours, etc. The maximum is 1 week. Optional

Context Output

Path Type Description
Proofpoint.MessagesBlocked.spamScore Number The spam score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.phishScore Number The phish score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.threatsInfoMap List List that contains details about detected threats within the message. Contains: campaignID, classification, threat, threatID, threatStatus,threatTime, threatType, threatUrl.
Proofpoint.MessagesBlocked.messageTime Date The time the message was delivered to the user or quarantined by PPS.
Proofpoint.MessagesBlocked.impostorScore Number The impostor score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.malwareScore Number The malware score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesBlocked.cluster String The name of the PPS cluster that processed the message.
Proofpoint.MessagesBlocked.subject String The subject line of the message, if available.
Proofpoint.MessagesBlocked.quarantineFolder String The name of the folder that contains the quarantined message. This appears only for blocked messages. For delivered messages will be ‘None’.
Proofpoint.MessagesBlocked.quarantineRule String The name of the rule that quarantined the message. This appears only for messagesBlocked events.
Proofpoint.MessagesBlocked.policyRoutes List The policy routes that the message matched during processing by PPS.
Proofpoint.MessagesBlocked.modulesRun String The list of PPS modules that processed the message.
Proofpoint.MessagesBlocked.messageSize Number The size in bytes of the message, including headers and attachments.
Proofpoint.MessagesBlocked.Header.headerFrom String The full content of the From header, including any friendly name.
Proofpoint.MessagesBlocked.Header.headerReplyTo String If present, the full content of the Reply-To: header, including any friendly names.
Proofpoint.MessagesBlocked.Header.fromAddress List The email address contained in the From header, excluding the friendly name.
Proofpoint.MessagesBlocked.Header.ccAddresses List A list of email addresses contained within the CC: header, excluding friendly names.
Proofpoint.MessagesBlocked.Header.replyToAddress List The email address contained in the Reply-To: header, excluding friendly name.
Proofpoint.MessagesBlocked.Header.toAddresses List A list of email addresses contained within the To: header, excluding friendly names.
Proofpoint.MessagesBlocked.Header.xmailer String The content of the X-Mailer: header, if present.
Proofpoint.MessagesBlocked.messageParts List An array of structures that contain details about parts of the message, including both message bodies and attachments.
Proofpoint.MessagesBlocked.completelyRewritten String The rewrite status of the message. If value is true, all instances of URL threats within the message were successfully rewritten. If the value is false, at least one instance of the threat URL was not rewritten. If the value is ‘na’, the message did not contain any URL-based threats.
Proofpoint.MessagesBlocked.id String The unique ID of the message.
Proofpoint.MessagesBlocked.sender String The email address of the SMTP (envelope) sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.MessagesBlocked.recipient List A list containing the email addresses of the recipients.
Proofpoint.MessagesBlocked.senderIP String The IP address of the sender.
Proofpoint.MessagesBlocked.messageID String Message-ID extracted from the headers of the email message.
Proofpoint.MessagesBlocked.GUID String The ID of the message within PPS. It can be used to identify the message in PPS.

Command Example

!proofpoint-get-events-messages-blocked interval="2021-06-07T02:00:00Z/2021-06-07T03:00:00Z"

Context Example

{
    "Proofpoint": {
        "MessagesBlocked": [
            {
                "GUID": "9JRzwqiZEzBenEMsdgsdfg48ItsowO9ZJ1jmBbo",
                "Header": {
                    "ccAddresses": [],
                    "fromAddress": [
                        "xxxx@xxx.com"
                    ],
                    "headerFrom": "\"xxxx@xxx.com\" <xxxx@xxx.com>",
                    "headerReplyTo": null,
                    "replyToAddress": [],
                    "toAddresses": [
                        "xxxx@xxx.com"
                    ],
                    "xmailer": null
                },
                "cluster": "hosted",
                "completelyRewritten": false,
                "id": "867899c4-bbde-9948-f0a2-740c13aafb98",
                "impostorScore": 0,
                "malwareScore": 0,
                "messageID": "<xxxx@xxx.com>",
                "messageParts": [
                    {
                        "contentType": "text/html",
                        "disposition": "inline",
                        "filename": "text.html",
                        "md5": "a",
                        "oContentType": "text/html",
                        "sandboxStatus": null,
                        "sha256": "99843dc18542ba1417b0b464c00f"
                    }
                ],
                "messageSize": 3684,
                "messageTime": "2021-06-07T01:50:00.000Z",
                "modulesRun": [
                    "av",
                    "spf",
                    "dkimv",
                    "spam",
                    "dmarc",
                    "pdr",
                    "urldefense"
                ],
                "phishScore": 100,
                "policyRoutes": [
                    "default_inbound"
                ],
                "quarantineFolder": "Phish",
                "quarantineRule": "inbound_spam_phish",
                "recipient": [
                    "xxxx@xxx.com"
                ],
                "sender": "xxxx@xxx.com",
                "senderIP": "000.000.000.000",
                "spamScore": 100,
                "subject": "Your mailbox is full......",
                "threatsInfoMap": [
                    {
                        "campaignID": null,
                        "classification": "phish",
                        "threat": "login/verify",
                        "threatID": "9a",
                        "threatStatus": "active",
                        "threatTime": "2021-06-07T00:47:12.000Z",
                        "threatType": "url",
                        "threatUrl": "https://threatinsight.proofpoint.com"
                    },
                ]
            }
        ]
    }
}

Human Readable Output

Blocked Messages

Sender IP Sender Recipient Subject Message Size Message Time Malware Score Phish Score Spam Score
000.000.000.000 xxxx@xxx.com xxxx@xxx.com Your mailbox is full…… 3684 2021-06-07T01:50:00.000Z 0 100 100

Blocked Messages Threats Information

Sender Recipient Subject Classification Threat Threat Status Threat Url Threat ID Threat Time Campaign ID
xxxx@xxx.com xxxx@xxx.com Your mailbox is full…… phish login/verify active https://threatinsight.proofpoint.com 9a53601a616eb78609e525c0f3ee5a3cfca 2021-06-07T00:47:12.000Z  

proofpoint-get-events-messages-delivered


Get events for delivered messages in the specified time period. Must provide either the interval or time_range arguments.

Base Command

proofpoint-get-events-messages-delivered

Input

Argument Name Description Required
threat_type Message’s threat type to be retrieved. If no value is specified, all threat types will be retrieved. Possible values: ‘url’, ‘attachment’, and ‘message’. Possible values are: url, attachment, message. Optional
threat_status Message’s threat status to be retrieved. If no value is specified, active and cleared threats will be retrieved. Possible values: ‘active’, ‘cleared’, and ‘falsePositive’. Possible values are: active, cleared, falsePositive. Optional
interval ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour. For example: 2021-04-27T09:00:00Z/2021-04-27T10:00:00Z. Optional
time_range Represents the start of the data retrieval period. For example: 1 week, 2 days, 3 hours, etc. The maximum is 1 week. Optional

Context Output

Path Type Description
Proofpoint.MessagesDelivered.spamScore Number The spam score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.phishScore Number The phish score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.threatsInfoMap List List that contains details about detected threats within the message. Contains: campaignID, classification, threat, threatID, threatStatus,threatTime, threatType, threatUrl.
Proofpoint.MessagesDelivered.messageTime Date The time the message was delivered to the user or quarantined by PPS.
Proofpoint.MessagesDelivered.impostorScore Number The impostor score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.malwareScore Number The malware score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.cluster String The name of the PPS cluster that processed the message.
Proofpoint.MessagesDelivered.subject String The subject line of the message, if available.
Proofpoint.MessagesDelivered.quarantineFolder String The name of the folder that contains the quarantined message. This appears only for blocked messages. For delivered messages will be ‘None’.
Proofpoint.MessagesDelivered.quarantineRule String The name of the rule that quarantined the message. This appears only for messagesBlocked events.
Proofpoint.MessagesDelivered.policyRoutes List The policy routes that the message matched during processing by PPS.
Proofpoint.MessagesDelivered.modulesRun String The list of PPS modules that processed the message.
Proofpoint.MessagesDelivered.messageSize Number The size in bytes of the message, including headers and attachments.
Proofpoint.MessagesDelivered.Header.headerFrom String The full content of the From header, including any friendly name.
Proofpoint.MessagesDelivered.Header.headerReplyTo String If present, the full content of the Reply-To: header, including any friendly names.
Proofpoint.MessagesDelivered.Header.fromAddress List The email address contained in the From header, excluding the friendly name.
Proofpoint.MessagesDelivered.Header.ccAddresses List A list of email addresses contained within the CC: header, excluding friendly names.
Proofpoint.MessagesDelivered.Header.replyToAddress List The email address contained in the Reply-To: header, excluding friendly name.
Proofpoint.MessagesDelivered.Header.toAddresses List A list of email addresses contained within the To: header, excluding friendly names.
Proofpoint.MessagesDelivered.Header.xmailer String The content of the X-Mailer: header, if present.
Proofpoint.MessagesDelivered.messageParts List An array of structures that contains details about parts of the message, including both message bodies and attachments.
Proofpoint.MessagesDelivered.completelyRewritten String The rewrite status of the message. If value is true, all instances of URL threats within the message were successfully rewritten. If the value is false, at least one instance of the threat URL was not rewritten. If the value is ‘na’, the message did not contain any URL-based threats.
Proofpoint.MessagesDelivered.id String The unique ID of the message.
Proofpoint.MessagesDelivered.sender String The email address of the SMTP (envelope) sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.MessagesDelivered.recipient List A list containing the email addresses of the recipients.
Proofpoint.MessagesDelivered.senderIP String The IP address of the sender.
Proofpoint.MessagesDelivered.messageID String Message-ID extracted from the headers of the email message.
Proofpoint.MessagesDelivered.GUID String The ID of the message within PPS. It can be used to identify the message in PPS and is guaranteed to be unique.

Command Example

!proofpoint-get-events-messages-delivered interval="2021-06-03T17:00:00Z/2021-06-03T18:00:00Z"

Context Example

{
    "Proofpoint": {
        "MessagesDelivered": {
            "GUID": "Ggfsdfsdf",
            "Header": {
                "ccAddresses": [],
                "fromAddress": [
                    "xxxx@xxx.com"
                ],
                "headerFrom": "\"j.\" <xxxx@xxx.com>",
                "headerReplyTo": null,
                "replyToAddress": [],
                "toAddresses": [],
                "xmailer": null
            },
            "cluster": "hosted",
            "completelyRewritten": true,
            "id": "1828003vsdv05566e842",
            "impostorScore": 0,
            "malwareScore": 0,
            "messageID": "<SI2PR0dfbvd.xxxx@xxx.com.com>",
            "messageParts": [
                {
                    "contentType": "text/html",
                    "disposition": "inline",
                    "filename": "text.html",
                    "md5": "fcfa9b21f43fbdf02965263c63e",
                    "oContentType": "text/html",
                    "sandboxStatus": null,
                    "sha256": "72d3dc7a01dfbdbe8e871536864f56bf235ba08ff259105ac"
                },
            ],
            "messageSize": 10171,
            "messageTime": "2021-06-02T13:41:32.000Z",
            "modulesRun": [
                "av",
                "spf",
                "dkimv",
                "spam",
                "dmarc",
                "urldefense"
            ],
            "phishScore": 0,
            "policyRoutes": [
                "default_inbound",
                "allow_relay"
            ],
            "quarantineFolder": null,
            "quarantineRule": null,
            "recipient": [
                "xxxx@xxx.com"
            ],
            "sender": "xxxx@xxx.com",
            "senderIP": "400.000.000",
            "spamScore": 43,
            "subject": "=",
            "threatsInfoMap": [
                {
                    "campaignID": null,
                    "classification": "phish",
                    "threat": "https://bit.ly",
                    "threatID": "45fe3b35ghkk2b8916934b6c0a536cc9b2603d03",
                    "threatStatus": "active",
                    "threatTime": "2021-06-03T07:17:11.000Z",
                    "threatType": "url",
                    "threatUrl": "https://threatinsight.proofpoint.com"
                }
            ]
        }
    }
}

Human Readable Output

Delivered Messages

Sender IP Sender Recipient Subject Message Size Message Time Malware Score Phish Score Spam Score
00.000.000.0000 xxxx@xxx.com xxxx@xxx.com = 10171 2021-06-02T13:41:32.000Z 0 0 43

Delivered Messages Threats Information

Sender Recipient Subject Classification Threat Threat Status Threat Url Threat ID Threat Time Campaign ID
xxxx@xxx.com xxxx@xxx.com = phish https://bit.ly active https://threatinsight.proofpoint.com 45fe3b35b7bd2adfad6dea4d305bea3e7c1a2b8gfhh03d03 2021-06-03T07:17:11.000Z  

proofpoint-list-issues


Get events for clicks to malicious URLs permitted and messages delivered containing a known attachment threat within the specified time period. Must provide either the interval or time_range arguments.

Base Command

proofpoint-list-issues

Input

Argument Name Description Required
threat_type Event’s threat type to be retrieved. If no value is specified, all threat types will be retrieved. Possible values: ‘url’, ‘attachment’, and ‘message’. Possible values are: url, attachment, message. Optional
threat_status Event’s threat status to be retrieved.If no value is specified, active and cleared threats will be retrieved. Possible values: ‘url’, ‘attachment’, and ‘message’. Possible values are: active, cleared, falsePositive. Optional
interval ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour. For example: 2021-04-27T09:00:00Z/2021-04-27T10:00:00Z. Optional
time_range Represents the start of the data retrieval period. For example: 1 week, 2 days, 3 hours, etc. The maximum is 1 week. Optional

Context Output

Path Type Description
Proofpoint.ClicksPermitted.url String The malicious URL was clicked.
Proofpoint.ClicksPermitted.classification String The threat category of the malicious URL (Malware, Phish, or Spam).
Proofpoint.ClicksPermitted.clickTime Date The time the user clicked the URL.
Proofpoint.ClicksPermitted.threatTime Date The time that Proofpoint identified the URL as a threat.
Proofpoint.ClicksPermitted.userAgent String The User-Agent header from the clicker’s HTTP request.
Proofpoint.ClicksPermitted.campaignId String An identifier for the campaign of which the threat is a member.
Proofpoint.ClicksPermitted.id String The unique ID of the click.
Proofpoint.ClicksPermitted.clickIP String The external IP address of the user who clicked the link.
Proofpoint.ClicksPermitted.sender String The email address of the sender. The user-part is hashed. The domain-part is in cleartext.
Proofpoint.ClicksPermitted.recipient String The email address of the recipient.
Proofpoint.ClicksPermitted.senderIP String The IP address of the sender.
Proofpoint.ClicksPermitted.threatID String The unique identifier associated with this threat.
Proofpoint.ClicksPermitted.threatURL String A link to the entry on the TAP dashboard for the particular threat.
Proofpoint.ClicksPermitted.threatStatus String The current state of the threat.
Proofpoint.ClicksPermitted.messageID String The ID of the message that the URL belongs to.
Proofpoint.ClicksPermitted.GUID String The ID of the message within PPS. It can be used to identify the message in PPS and is guaranteed to be unique.
Proofpoint.MessagesDelivered.spamScore Number The spam score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.phishScore Number The phish score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.threatsInfoMap List List which contain details about detected threats within the message. Contains: campaignID, classification, threat, threatID, threatStatus,threatTime, threatType, threatUrl.
Proofpoint.MessagesDelivered.messageTime Date THe time the message was delivered to the user or quarantined by PPS.
Proofpoint.MessagesDelivered.impostorScore Number The impostor score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.malwareScore Number The malware score of the message. Higher scores indicate higher certainty.
Proofpoint.MessagesDelivered.cluster String The name of the PPS cluster that processed the message.
Proofpoint.MessagesDelivered.subject String The subject line of the message, if available.
Proofpoint.MessagesDelivered.quarantineFolder String The name of the folder that contains the quarantined message. This appears only for blocked messages. For delivered messages will be ‘None’.
Proofpoint.MessagesDelivered.quarantineRule String The name of the rule that quarantined the message. This appears only for messagesBlocked events.
Proofpoint.MessagesDelivered.policyRoutes List The policy routes that the message matched during processing by PPS.
Proofpoint.MessagesDelivered.modulesRun String The list of PPS modules that processed the message.
Proofpoint.MessagesDelivered.messageSize Number The size in bytes of the message, including headers and attachments.
Proofpoint.MessagesDelivered.Header.headerFrom String The full content of the From header, including any friendly name.
Proofpoint.MessagesDelivered.Header.headerReplyTo String If present, the full content of the Reply-To: header, including any friendly names.
Proofpoint.MessagesDelivered.Header.fromAddress List The email address contained in the From header, excluding any friendly name.
Proofpoint.MessagesDelivered.Header.ccAddresses List A list of email addresses contained within the CC: header, excluding any friendly names.
Proofpoint.MessagesDelivered.Header.replyToAddress List The email address contained in the Reply-To: header, excluding any friendly name.
Proofpoint.MessagesDelivered.Header.toAddresses List A list of email addresses contained within the To: header, excluding any friendly names.
Proofpoint.MessagesDelivered.Header.xmailer String The content of the X-Mailer: header, if present.
Proofpoint.MessagesDelivered.messageParts List An array of structures that contain details about parts of the message, including both message bodies and attachments.
Proofpoint.MessagesDelivered.completelyRewritten String The rewrite status of the message. If value is true, all instances of URL threats within the message were successfully rewritten. If the value is false, at least one instance of the threat URL was not rewritten. If the value is ‘na’, the message did not contain any URL-based threats.
Proofpoint.MessagesDelivered.id String The unique ID of the message.
Proofpoint.MessagesDelivered.sender String The email address of the SMTP (envelope) sender. The user-part is hashed. The domain-part is cleartext.
Proofpoint.MessagesDelivered.recipient List A list containing the email addresses of the recipients
Proofpoint.MessagesDelivered.senderIP String The IP address of the sender.
Proofpoint.MessagesDelivered.messageID String Message-ID extracted from the headers of the email message.
Proofpoint.MessagesDelivered.GUID String The ID of the message within PPS. It can be used to identify the message in PPS and is guaranteed to be unique.

Command Example

!proofpoint-list-issues interval="2021-06-03T17:00:00Z/2021-06-03T18:00:00Z"

Context Example

{
    "Proofpoint": {
        "MessagesDelivered": {
            "GUID": "Ggfsdfsdf",
            "Header": {
                "ccAddresses": [],
                "fromAddress": [
                    "xxxx@xxx.com"
                ],
                "headerFrom": "\"j.\" <xxxx@xxx.com>",
                "headerReplyTo": null,
                "replyToAddress": [],
                "toAddresses": [],
                "xmailer": null
            },
            "cluster": "hosted",
            "completelyRewritten": true,
            "id": "1828003vsdv05566e842",
            "impostorScore": 0,
            "malwareScore": 0,
            "messageID": "<SI2PR0dfbvd.xxxx@xxx.com.com>",
            "messageParts": [
                {
                    "contentType": "text/html",
                    "disposition": "inline",
                    "filename": "text.html",
                    "md5": "fcfa9b21f43fbdf02965263c63e",
                    "oContentType": "text/html",
                    "sandboxStatus": null,
                    "sha256": "72d3dc7a01dfbdbe8e871536864f56bf235ba08ff259105ac"
                },
            ],
            "messageSize": 10171,
            "messageTime": "2021-06-02T13:41:32.000Z",
            "modulesRun": [
                "av",
                "spf",
                "dkimv",
                "spam",
                "dmarc",
                "urldefense"
            ],
            "phishScore": 0,
            "policyRoutes": [
                "default_inbound",
                "allow_relay"
            ],
            "quarantineFolder": null,
            "quarantineRule": null,
            "recipient": [
                "xxxx@xxx.com"
            ],
            "sender": "xxxx@xxx.com",
            "senderIP": "400.000.000",
            "spamScore": 43,
            "subject": "=",
            "threatsInfoMap": [
                {
                    "campaignID": null,
                    "classification": "phish",
                    "threat": "https://bit.ly",
                    "threatID": "45fe3b35ghkk2b8916934b6c0a536cc9b2603d03",
                    "threatStatus": "active",
                    "threatTime": "2021-06-03T07:17:11.000Z",
                    "threatType": "url",
                    "threatUrl": "https://threatinsight.proofpoint.com"
                }
            ]
        }
    }
}

Human Readable Output

Delivered Messages

Sender IP Sender Recipient Subject Message Size Message Time Malware Score Phish Score Spam Score
00.000.000.0000 xxxx@xxx.com xxxx@xxx.com = 10171 2021-06-02T13:41:32.000Z 0 0 43

Delivered Messages Threats Information

Sender Recipient Subject Classification Threat Threat Status Threat Url Threat ID Threat Time Campaign ID
xxxx@xxx.com xxxx@xxx.com = phish https://bit.ly active https://threatinsight.proofpoint.com 45fe3b35b7bd2adfad6dea4d305bea3e7c1a2b8gfhh03d03 2021-06-03T07:17:11.000Z  

Permitted click from list-issues command result

No entries.

proofpoint-list-campaigns


Gets a list of IDs of campaigns active in a specified time period. Must provide either the interval or time_range arguments.

Base Command

proofpoint-list-campaigns

Input

Argument Name Description Required
interval ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one day. For example: 2021-04-27T09:00:00Z/2021-04-27T10:00:00Z. Optional
limit The maximum number of campaign IDs to produce in the response. Defaults to 100 and the maximum supported value is 200. Default is 100. Optional
page The page of results to return, in multiples of the specified size. Default is 1. Optional
time_range Represents the start of the data retrieval period. For example: 1 week, 2 days, 3 hours, etc. The maximum is 1 week. Optional

Context Output

Path Type Description
Proofpoint.Campaigns.id String The campaign ID.
Proofpoint.Campaigns.lastUpdatedAt String Last updated timestamp of the campaign.

Command Example

!proofpoint-list-campaigns interval="2021-06-01T11:00:00Z/2021-06-02T11:00:00Z"

Context Example

{
    "Proofpoint": {
        "Campaign": {
            "id": "7c91b71fdgdfgdfg591a1ad38",
            "lastUpdatedAt": "2021-06-03T13:01:57.000Z"
        }
    }
}

Human Readable Output

Campaigns List

Id Last Updated At
7c91b71fdgdfgdfg591a1ad38 2021-06-03T13:01:57.000Z

proofpoint-get-campaign


Gets details for a given campaign.

Base Command

proofpoint-get-campaign

Input

Argument Name Description Required
campaign_id ID of the required campaign. Required

Context Output

Path Type Description
Proofpoint.Campaign.info List The campaign information - ID,name, description, startDate, and notable.
Proofpoint.Campaign.actors List A list of actor objects.
Proofpoint.Campaign.families List A list of family objects.
Proofpoint.Campaign.malware List A list of malware objects.
Proofpoint.Campaign.techniques List A list of technique objects.
Proofpoint.Campaign.brands List A list of brand objects.
Proofpoint.Campaign.campaignMembers List A list of campaign member objects.

Command Example

!proofpoint-get-campaign campaign_id="f3ff0874-85ef-475e-b3fe-d05f97b2ed3f"

Context Example

{
    "Proofpoint": {
        "Campaign": {
            "actors": [],
            "brands": [],
            "campaignMembers": [],
            "families": [
                {
                    "id": "69a63403-f478-40f6-a4cb-3d2ffb85b98e",
                    "name": "Keylogger"
                }
            ],
            "info": {
                "description": "Messages purporting to be e.g.\r\n\r\n* from &lt;xxxx@xxx.com;' and subject \"Re: New Order From customer\".\r\n\r\nThese messages contain compressed executables that lead to the installation of AgentTesla with the following example configuration:\r\n\r\n<pre>C2_Email_Address: xxxx@xxx.com\r\nC2_Email_Password: \r\nC2_Email_Server: xxxx@xxx.com</pre>",
                "id": "f3ff087dfgdfge-d05f97b2ed3f",
                "name": "AgentTesla | Compressed Executables | \"techie\" | 25 March 2021",
                "notable": false,
                "startDate": "2021-03-25T00:00:00.000Z"
            },
            "malware": [
                {
                    "id": "4b50dfbdfb-901a-1cb4cf8a21fb",
                    "name": "AgentTesla"
                }
            ],
            "techniques": [
                {
                    "id": "e488ddfbdfb20-a1aa-d1a85494067c",
                    "name": "Compressed Executable"
                }
            ]
        }
    }
}

Human Readable Output

Campaign Information

Id Name Description Start Date Notable
f3ff08dfbdb5e-b3fe-d05f97b2ed3f AgentTesla | Compressed Executables | “techie” | 25 March 2021 Messages purporting to be e.g.

* from <xxxx@xxx.com>’ and subject “Re: New Order From customer”.

These messages contain compressed executables that lead to the installation of AgentTesla with the following example configuration:

<pre>C2_Email_Address: xxxx@xxx.com
C2_Email_Password:
C2_Email_Server: xxxx@xxx.com</pre>
2021-03-25T00:00:00.000Z false

Campaign Members

No entries.

Families

Id Name
69a63403-dbfdfb4cb-3d2ffb85b98e Keylogger

Techniques

Id Name
e48835be-xcvxcvaa-d1a85494067c Compressed Executable

Actors

No entries.

Brands

No entries.

Malware

Id Name
4b500558-23d0-sfdsdf1cb4cf8a21fb AgentTesla

proofpoint-list-most-attacked-users


Gets a list of the most attacked users in the organization.

Base Command

proofpoint-list-most-attacked-users

Input

Argument Name Description Required
window An integer indicating how many days the data should be retrieved for. Possible values: “14”, “30”, “90”. Possible values are: 14, 30, 90. Default is false. Required
limit The maximum number of users to produce in the response. Default is 1000. Optional
page The page of results to return. Default is 1. Optional

Context Output

Path Type Description
Proofpoint.Vap.users List List of users in the organization.
Proofpoint.Vap.totalVapUsers Number The total number of VAP users for the interval.
Proofpoint.Vap.interval String An ISO8601-formatted interval showing the time the response was calculated for.
Proofpoint.Vap.averageAttackIndex Number The average attack index value for users during the interval.
Proofpoint.Vap.vapAttackIndexThreshold Number This interval’s attack index threshold, past which a user is considered a VAP.

Command Example

!proofpoint-list-most-attacked-users window="14"

Context Example

{
    "Proofpoint": {
        "Vap": {
            "averageAttackIndex": 307.05145,
            "interval": "2021-05-23T21:44:53Z/2021-06-06T21:44:53Z",
            "totalVapUsers": 1,
            "users": [
                {
                    "identity": {
                        "customerUserId": null,
                        "department": null,
                        "emails": [
                            "xxxx@xxx.com"
                        ],
                        "guid": "3b5132sdvsd76-c442-919e69175bdd",
                        "location": null,
                        "name": null,
                        "title": null,
                        "vip": false
                    },
                    "threatStatistics": {
                        "attackIndex": 4576,
                        "families": [
                            {
                                "name": "credential phishing",
                                "score": 7008
                            }
                        ]
                    }
                }
            ],
            "vapAttackIndexThreshold": 965.9637
        }
    }
}

Human Readable Output

Most Attacked Users Information

Total Vap Users Interval Average Attack Index Vap Attack Index Threshold
7 2021-05-23T21:44:53Z/2021-06-06T21:44:53Z 307.05145 965.9637

Threat Families

Mailbox Threat Family Name Threat Score
xxxx@xxx.com credential phishing 7008

proofpoint-get-top-clickers


Gets a list of the top clickers in the organization for a specified time period.

Base Command

proofpoint-get-top-clickers

Input

Argument Name Description Required
window An integer indicating how many days the data should be retrieved for. Possible values: “14”, “30”, “90”. Possible values are: 14, 30, 90. Default is false. Required
limit The maximum number of top clickers to produce in the response.The max supported value is 200. Default is 100. Optional
page The page of results to return. Default is 1. Optional

Context Output

Path Type Description
Proofpoint.Topclickers.users List List of users in the organization.
Proofpoint.Topclickers.totalTopClickers int The total number of top clickers in the time interval.
Proofpoint.Topclickers.interval Date An ISO8601-formatted interval showing the time the response was calculated for.

Command Example

!proofpoint-get-top-clickers window="90"

Context Example

{
    "Proofpoint": {
        "Topclickers": {
            "interval": "2021-03-09T07:17:00Z/2021-06-07T07:17:00Z",
            "totalTopClickers": 1,
            "users": [
                {
                    "clickStatistics": {
                        "clickCount": 2,
                        "families": [
                            {
                                "clicks": 2,
                                "name": "Malware"
                            }
                        ]
                    },
                    "identity": {
                        "customerUserId": null,
                        "department": null,
                        "emails": [
                            "xxxx@xxx.come"
                        ],
                        "guid": "44fa5svfdgae-f22f-b49b49b1e4e3",
                        "location": null,
                        "name": null,
                        "title": null,
                        "vip": false
                    }
                }
            ]
        }
    }
}

Human Readable Output

Top Clickers Users Information

Total Top Clickers Interval
1 2021-03-09T07:17:00Z/2021-06-07T07:17:00Z

Threat Families

Mailbox Threat Family Name Threat Score
xxxx@xxx.com Malware  

proofpoint-url-decode


Decodes URLs that have been rewritten by TAP to their original, target URL.

Base Command

proofpoint-url-decode

Input

Argument Name Description Required
urls A comma-separated list of encoded URLs. Required

Context Output

Path Type Description
Proofpoint.URL.encodedUrl String The original, rewritten URL supplied to the endpoint.
Proofpoint.URL.decodedUrl String The target URL embedded inside the rewritten link.
Proofpoint.URL.success Boolean Indicates whether the URL could successfully be decoded.

Command Example

!proofpoint-url-decode urls="https://urldefense.proofpoint.com/v2/url?u=http-3A__links.mkt3337.com_ctt-3Fkn-3D3-26ms-3DMzQ3OTg3MDQS1-26r-3DMzkxNzk3NDkwMDA0S0-26b-3D0-26j-3DMTMwMjA1ODYzNQS2-26mt-3D1-26rt-3D0&d=DwMFaQ&c=Vxt5e0Osvvt2gflwSlsJ5DmPGcPvTRKLJyp031rXjhg&r=MujLDFBJstxoxZI_GKbsW7wxGM7nnIK__qZvVy6j9Wc&m=QJGhloAyfD0UZ6n8r6y9dF-khNKqvRAIWDRU_K65xPI&s=ew-rOtBFjiX1Hgv71XQJ5BEgl9TPaoWRm_Xp9Nuo8bk&e="

Context Example

{
    "Proofpoint": {
        "URL": {
            "decodedUrl": "http://links.mkt3337.com/ctt?kn=3&ms=MzQ3OTg3MDQS1&r=MzkxNzk3NDkwMDA0S0&b=0&j=MTMwMjA1ODYzNQS2&mt=1&rt=0",
            "encodedUrl": "https://urldefense.proofpoint.com/v2/url?u=http-3A__links.mkt3337.com_ctt-3Fkn-3D3-26ms-3DMzQ3OTg3MDQS1-26r-3DMzkxNzk3NDkwMDA0S0-26b-3D0-26j-3DMTMwMjA1ODYzNQS2-26mt-3D1-26rt-3D0&d=DwMFaQ&c=Vxt5e0Osvvt2gflwSlsJ5DmPGcPvTRKLJyp031rXjhg&r=MujLDFBJstxoxZI_GKbsW7wxGM7nnIK__qZvVy6j9Wc&m=QJGhloAyfD0UZ6n8r6y9dF-khNKqvRAIWDRU_K65xPI&s=ew-rOtBFjiX1Hgv71XQJ5BEgl9TPaoWRm_Xp9Nuo8bk&e=",
            "success": true
        }
    }
}

Human Readable Output

URLs decoded information

Encoded Url Decoded Url
https://urldefense.proofpoint.com/v2/url?u=http-3A__links.mkt3337.com_ctt-3Fkn-3D3-26ms-3DMzQ3OTg3MDQS1-26r-3DMzkxNzk3NDkwMDA0S0-26b-3D0-26j-3DMTMwMjA1ODYzNQS2-26mt-3D1-26rt-3D0&d=DwMFaQ&c=Vxt5e0Osvvt2gflwSlsJ5DmPGcPvTRKLJyp031rXjhg&r=MujLDFBJstxoxZI_GKbsW7wxGM7nnIK__qZvVy6j9Wc&m=QJGhloAyfD0UZ6n8r6y9dF-khNKqvRAIWDRU_K65xPI&s=ew-rOtBFjiX1Hgv71XQJ5BEgl9TPaoWRm_Xp9Nuo8bk&e= http://links.mkt3337.com/ctt?kn=3&ms=MzQ3OTg3MDQS1&r=MzkxNzk3NDkwMDA0S0&b=0&j=MTMwMjA1ODYzNQS2&mt=1&rt=0

Configuration parameters

  • url — Server URL (required)
  • credentials — Service Principal (required)
  • api_version — API Version
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • threat_type — Threat type
  • threat_status — Threat status
  • events_type — Events to fetch
  • limit — Maximum number of incident per fetch
  • fetch_time — First fetch time range
  • raw_json_encoding — Advanced: Raw message encoding
  • look_back_minutes — Events Fetch Look-Back (minutes)
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval

Commands (12)

  • proofpoint-get-campaign

    Gets details for a given campaign.

  • proofpoint-get-events

    Fetches events for all clicks and messages relating to known threats within the specified time period. Details as per clicks/blocked.

  • proofpoint-get-events-clicks-blocked

    Gets events for clicks to malicious URLs blocked in the specified time period. Must provide either the interval or time_range arguments.

  • proofpoint-get-events-clicks-permitted

    Get events for clicks to malicious URLs permitted in the specified time period. Must provide either the interval or time_range arguments.

  • proofpoint-get-events-messages-blocked

    Get events for blocked messages in the specified time period. Must provide either the interval or time_range arguments.

  • proofpoint-get-events-messages-delivered

    Get events for delivered messages in the specified time period. Must provide either the interval or time_range arguments.

  • proofpoint-get-forensics

    Returns forensics evidence.

  • proofpoint-get-top-clickers

    Gets a list of the top clickers in the organization for a specified time period.

  • proofpoint-list-campaigns

    Gets a list of IDs of campaigns active in a specified time period. Must provide either the interval or time_range arguments.

  • proofpoint-list-issues

    Get events for clicks to malicious URLs permitted and messages delivered containing a known attachment threat within the specified time period. Must provide either the interval or time_range arguments.

  • proofpoint-list-most-attacked-users

    Gets a list of the most attacked users in the organization.

  • proofpoint-url-decode

    Decodes URLs that have been rewritten by TAP to their original, target URL.

import json
from datetime import datetime, timedelta, UTC

import dateparser
import demistomock as demisto
import requests
import urllib3
from CommonServerPython import *

# Disable insecure warnings
urllib3.disable_warnings()

ALL_EVENTS = "All"
ISSUES_EVENTS = "Issues"
BLOCKED_CLICKS = "Blocked Clicks"
PERMITTED_CLICKS = "Permitted Clicks"
BLOCKED_MESSAGES = "Blocked Messages"
DELIVERED_MESSAGES = "Delivered Messages"

DEFAULT_LIMIT = 50
DEFAULT_LOOK_BACK_MINUTES = 30
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
MAX_SEEN_IDS = 10_000


def get_now():
    """A wrapper function for datetime.now(timezone.utc)
    helps handle tests
    Returns:
        datetime: time right now in the UTC timezone (naive, for backward compatibility).
    """
    return datetime.now(UTC).replace(tzinfo=None)


def prune_seen_ids(seen_ids: dict[str, str], look_back_minutes: int) -> dict[str, str]:
    """Remove event IDs outside the lookback window. Apply hard cap if needed.

    Args:
        seen_ids: Mapping of event_id → interval_end_str (the end of the fetch interval
                  in which the event was first seen) for deduplication.
        look_back_minutes: The lookback window in minutes. A 30-minute safety margin is
                           added on top to tolerate clock skew and API indexing delay.

    Returns:
        Pruned dict with only IDs whose interval_end is within the lookback window + 30min margin.
    """
    if not seen_ids:
        return seen_ids

    cutoff = (get_now() - timedelta(minutes=look_back_minutes + 30)).strftime(DATE_FORMAT)
    pruned = {event_id: interval_end for event_id, interval_end in seen_ids.items() if interval_end >= cutoff}

    if len(pruned) > MAX_SEEN_IDS:
        demisto.info(f"seen_ids exceeded {MAX_SEEN_IDS}, pruning oldest 25%")
        sorted_ids = sorted(pruned.items(), key=lambda x: x[1])
        pruned = dict(sorted_ids[len(sorted_ids) // 4 :])

    demisto.debug(f"Pruned seen_ids: {len(seen_ids)=}, {len(pruned)=}")
    return pruned


def get_fetch_times(last_fetch, look_back_minutes: int = 0):
    """Generate time intervals for fetching events from Proofpoint TAP API.

    When `look_back_minutes` > 0, the effective fetch window between the start time
    (derived from `last_fetch`) and `now` is guaranteed to be at least
    `look_back_minutes` long. Equivalently:
    ``effective_start = now - max(now - last_fetch, look_back_minutes)``.

    - If ``now - last_fetch >= look_back_minutes``: leave the start unchanged
      (do NOT add `look_back_minutes` on top of an already-large gap).
    - If ``now - last_fetch <  look_back_minutes``: shift the start back so that
      ``now - effective_start == look_back_minutes``, ensuring late-indexed events
      from the last `look_back_minutes` are re-scanned.

    Deduplication (handled by the caller) prevents duplicates produced by the
    overlapping region.

    Args:
        last_fetch (datetime or str): Starting time for fetch. May be shifted back so
                                      that the window length is at least `look_back_minutes`.
        look_back_minutes (int): Minimum length (in minutes) of the fetch window, used to
                                 re-scan for late-indexed events. Default is 0 (no look-back).

    Returns:
        List[tuple[str, str]]: List of (start_time, end_time) tuples in DATE_FORMAT.
                               Each interval is ≤59 minutes and ≥30 seconds.
                               Returns empty list if no valid intervals can be created.
    """
    now = get_now()
    time_format = DATE_FORMAT

    if isinstance(last_fetch, str):
        last_fetch = datetime.strptime(last_fetch, time_format)

    # Ensure the window [effective_start, now] is at least `look_back_minutes` long.
    # Equivalent to: effective_start = now - max(now - last_fetch, look_back_minutes).
    # Only shift back when the existing gap is smaller than `look_back_minutes`; do not
    # subtract `look_back_minutes` on top of an already-sufficient gap.
    effective_start = last_fetch
    if look_back_minutes > 0:
        look_back_delta = timedelta(minutes=look_back_minutes)
        if (now - last_fetch) < look_back_delta:
            effective_start = now - look_back_delta

    # Guard against invalid intervals
    if effective_start >= now:
        demisto.debug(f"Skipping fetch. {effective_start=} >= {now=}")
        return []

    intervals = []
    current_start = effective_start

    # Create 59-minute intervals
    while now - current_start > timedelta(minutes=59):
        current_end = current_start + timedelta(minutes=59)
        intervals.append((current_start.strftime(time_format), current_end.strftime(time_format)))
        current_start = current_end

    # Add final interval (minimum 30 seconds)
    seconds_remaining = (now - current_start).total_seconds()
    if seconds_remaining >= 30:
        intervals.append((current_start.strftime(time_format), now.strftime(time_format)))
        demisto.debug(f"Final interval: {seconds_remaining:.0f} seconds")
    else:
        demisto.debug(f"Skipping final interval: only {seconds_remaining:.0f} seconds remaining (< 30s minimum)")

    demisto.debug(
        f"Generated {len(intervals)} intervals from {effective_start.strftime(time_format)} to {now.strftime(time_format)}"
    )
    return intervals


class Client:
    def __init__(self, proofpoint_url, api_version, verify, service_principal, secret, proxies):
        self.base_url = proofpoint_url
        self.api_version = api_version
        self.verify = verify
        self.service_principal = service_principal
        self.secret = secret
        self.proxies = proxies

    def http_request(self, method, url_suffix, params=None, data=None, forensics_api=False):
        if forensics_api:
            full_url = urljoin(self.base_url, "/v2/forensics")
        else:
            full_url = urljoin(urljoin(self.base_url, self.api_version), url_suffix)

        res = requests.request(
            method,
            full_url,
            verify=self.verify,
            params=params,
            json=data,
            auth=(self.service_principal, self.secret),
            proxies=self.proxies,
        )

        if res.status_code not in [200, 204]:
            raise ValueError(f"Error in API call to Proofpoint TAP {res.status_code}. Reason: {res.text}")

        try:
            return res.json()
        except Exception:
            raise ValueError(f"Failed to parse http response to JSON format. Original response body: \n{res.text}")

    def get_events(
        self, interval=None, since_time=None, since_seconds=None, threat_type=None, threat_status=None, event_type_filter="All"
    ):
        if not interval and not since_time and not since_seconds:
            raise ValueError("Required to pass interval or sinceTime or sinceSeconds.")

        query_params = {"format": "json"}
        query_params.update(
            assign_params(
                interval=interval,
                sinceTime=since_time,
                sinceSeconds=since_seconds,
                threatStatus=threat_status,
                threatType=threat_type,
            )
        )

        url_route = {
            "All": "/all",
            "Issues": "/issues",
            "Blocked Clicks": "/clicks/blocked",
            "Permitted Clicks": "/clicks/permitted",
            "Blocked Messages": "/messages/blocked",
            "Delivered Messages": "/messages/delivered",
        }[event_type_filter]

        events = self.http_request("GET", urljoin("siem", url_route), params=query_params)

        return events

    def get_forensics(self, threat_id=None, campaign_id=None, include_campaign_forensics=None):
        if threat_id and campaign_id:
            raise DemistoException("threadId and campaignID supplied, supply only one of them")
        if include_campaign_forensics and campaign_id:
            raise DemistoException("includeCampaignForensics can be true only with threadId")
        if campaign_id:
            params = assign_params(campaignId=campaign_id)
        else:
            params = assign_params(threatId=threat_id, includeCampaignForensics=include_campaign_forensics)
        return self.http_request("GET", None, params=params, forensics_api=True)

    def get_clicks(self, clicks_type: str, interval: str, threat_status: str = None) -> dict:
        """
        Retrieves clicks on malicious URLs in the specified time period. Clicks can either be blocked or permitted.

        Args:
            interval (str): ISO8601-formatted interval date. The minimum interval is 30 seconds. The maximum interval is one hour.
            threat_status (str): The status of the threat. Can be: active, cleared or falsePositive.
            clicks_type (str): The type of the click. Can be either "blocked" or "permitted".

        Returns:
            dict: API response from ProofpointTAP.

        """
        params = remove_empty_elements({"interval": interval, "threatStatus": threat_status, "format": "json"})
        return self.http_request("GET", f"/siem/clicks/{clicks_type}", params=params)

    def get_messages(self, messages_type: str, interval: str, threat_status: str = None, threat_type: str = None) -> dict:
        """
        Retrieves events for messages in the specified time period. Messages can either be blocked or delivered.

        Args:
            interval (str): ISO8601-formatted interval date. The minimum interval is 30 seconds. The maximum interval is one hour.
            threat_status (str): The status of the threat. Can be: active, cleared or falsePositive.
            threat_type (str): The type of the threat. Can be: url, attachment or message.
            messages_type (str): The type of the messages. Can be either "blocked" or "delivered"

        Returns:
            dict: API response from ProofpointTAP.

        """
        params = remove_empty_elements(
            {"interval": interval, "threatStatus": threat_status, "threatType": threat_type, "format": "json"}
        )
        return self.http_request("GET", f"/siem/messages/{messages_type}", params=params)

    def list_campaigns(self, interval: str, page: str = None, limit: str = None) -> dict:
        """
        Retrieves a list of IDs of campaigns active in a time window.
        Args:
            interval (str): ISO8601-formatted interval date. The minimum interval is 30 seconds. The maximum interval is one day.
            limit (str): The maximum number of campaign IDs to produce in the response.
            page (str): The page of results to return, in multiples of the specified size.

        Returns:
            dict: API response from ProofpointTAP.

        """
        params = remove_empty_elements({"interval": interval, "page": page, "size": limit, "format": "json"})
        return self.http_request("GET", "/campaign/ids", params=params)

    def get_campaign(self, campaign_id: str) -> dict:
        """
        Retrieves information for a given campaign.
        Args:
            campaign_id (str): The ID of the required campaign.
        Returns:
            dict: API response from ProofpointTAP.
        """
        return self.http_request("GET", f"/campaign/{campaign_id}")

    def list_most_attacked_users(self, window: str, limit: str = None, page: str = None) -> dict:
        """
        Retrieves a list of the most attacked users in the organization for a given period.
        Args:
            window (str): The number of days for which the information will be retrieved.
            limit (str): The maximum number of VAPs to produce.
            page (str): The page of results to return, in multiples of the specified size.
        Returns:
            dict: API response from ProofpointTAP.
        """
        params = remove_empty_elements({"window": window, "size": limit, "page": page})
        return self.http_request("GET", "/people/vap", params=params)

    def get_top_clickers(self, window: str, limit: str = None, page: str = None) -> dict:
        """
        Retrieves a list of the top clickers in the organization for a given period.
        Args:
            window (str): The number of days for which the information will be retrieved.
            limit (str): The maximum number of top clickers to produce.
            page (str): The page of results to return, in multiples of the specified size.
        Returns:
            dict: API response from ProofpointTAP.
        """
        params = remove_empty_elements({"window": window, "size": limit, "page": page})
        return self.http_request("GET", "/people/top-clickers", params=params)

    def url_decode(self, url_list: list) -> dict:
        """
        Decode URLs that have been rewritten by TAP to their original, target URL.
        Args:
            url_list (list): List of encoded URLs.
        Returns:
            dict: API response from ProofpointTAP.
        """
        data = {"urls": url_list}
        return self.http_request("POST", "/url/decode", data=data)

    def list_issues(self, interval: str, threat_status: str = None, threat_type: str = None) -> dict:
        """
        Retrieves events for permitted clicks on malicious URLs and delivered messages in the specified time period.
        Args:
            interval (str): ISO8601-formatted interval date. The minimum interval is 30 seconds. The maximum interval is one hour.
            threat_status (str): The status of the threat. Can be: active, cleared or falsePositive.
            threat_type (str): The type of the threat. Can be: url, attachment or messageText.
        Returns:
            dict: API response from ProofpointTAP.
        """
        params = remove_empty_elements(
            {"interval": interval, "threatStatus": threat_status, "threatType": threat_type, "format": "json"}
        )
        return self.http_request("GET", "/siem/issues", params=params)

    def get_all_siem(self, format: str, since: int):
        return self.http_request("GET", "/siem/all", params={"format": format, "sinceSeconds": since})


def test_module(client: Client) -> str:
    """
    Tests API connectivity and authentication.
    Args:
        client (Client): ProofpointTAP API client.
    Returns:
        str : 'ok' if test passed, anything else will fail the test.
    """

    try:
        client.get_all_siem(format="JSON", since=60)
    except Exception as exception:
        if "Unauthorized" in str(exception) or "authentication" in str(exception):
            return "Authorization Error: make sure API Credentials are correctly set"

        if "connection" in str(exception):
            return "Connection Error: make sure Server URL is correctly set"
        raise exception

    return "ok"


def build_context_attachment(what: dict) -> dict:
    return assign_params(
        SHA256=what.get("sha256"),
        MD5=what.get("md5"),
        Blacklisted=what.get("blacklisted"),
        Offset=what.get("offset"),
        Size=what.get("size"),
    )


def build_context_cookie(what: dict) -> dict:
    return assign_params(
        Action=what.get("action"),
        Domain=what.get("domain"),
        Key=what.get("key"),
        Value=what.get("value"),
    )


def build_context_dns(what: dict) -> dict:
    return assign_params(
        Host=what.get("host"),
        CNames=what.get("cnames"),
        IP=what.get("ips"),
        NameServers=what.get("nameservers"),
        NameServersList=what.get("nameserversList"),
    )


def build_context_mutex(what: dict) -> dict:
    return assign_params(Name=what.get("name"), Path=what.get("path"))


def build_context_ids(what: dict) -> dict:
    return assign_params(Name=what.get("name"), SignatureID=what.get("signatureId"))


def build_context_network(what: dict) -> dict:
    return assign_params(Action=what.get("action"), IP=what.get("ip"), Port=what.get("port"), Protocol=what.get("type"))


def build_context_process(what: dict) -> dict:
    return assign_params(
        Action=what.get("action"),
        Path=what.get("path"),
    )


def build_context_dropper(what: dict) -> dict:
    return assign_params(
        Path=what.get("path"),
        URL=what.get("url"),
        Rule=what.get("rule"),
    )


def build_context_registry(what: dict) -> dict:
    return assign_params(
        Name=what.get("name"),
        Action=what.get("action"),
        Key=what.get("key"),
        Value=what.get("value"),
    )


def build_context_file(what: dict) -> dict:
    return assign_params(
        Path=what.get("path"),
        Action=what.get("action"),
        SHA256=what.get("sha256"),
        MD5=what.get("md5"),
        Size=what.get("size"),
    )


def build_context_url(what: dict) -> dict:
    return assign_params(
        URL=what.get("url"),
        Blacklisted=what.get("blacklisted"),
        SHA256=what.get("sha256"),
        MD5=what.get("md5"),
        Size=what.get("size"),
        HTTPStatus=what.get("httpStatus"),
        IP=what.get("ip"),
    )


def build_context_behavior(forensics_data: dict) -> dict:
    """
    Build forensics behavior evidence type objects in order to update the command report context.
    Args:
        forensics_data (dict): Forensics data. A map of values associated with the specific evidence type.

    Returns:
        dict: Dictionary from given kwargs without empty values.

    """
    return assign_params(
        Path=forensics_data.get("path"),
        URL=forensics_data.get("url"),
    )


def build_context_screenshot(forensics_data: dict) -> dict:
    """
    Build forensics screenshot evidence type objects in order to update the command report context.
    Args:
        forensics_data (dict): Forensics data. A map of values associated with the specific evidence type.

    Returns:
        dict: Dictionary from given kwargs without empty values.

    """
    return assign_params(
        URL=forensics_data.get("url"),
    )


def get_forensic_command(client: Client, args: dict) -> tuple[str, dict, dict]:
    """
    Args:
        client:
        args: demisto.args()
    Returns:
        Outputs
    """
    forensic_types = {
        "attachment": "Attachment",
        "cookie": "Cookie",
        "dns": "DNS",
        "dropper": "Dropper",
        "file": "File",
        "ids": "IDS",
        "mutex": "Mutex",
        "network": "Network",
        "process": "Process",
        "registry": "Registry",
        "url": "URL",
        "behavior": "Behavior",
        "screenshot": "Screenshot",
    }
    threat_id = args.get("threatId")
    campaign_id = args.get("campaignId")
    include_campaign_forensics = args.get("includeCampaignForensics") == "true"
    limit = args.get("limit", DEFAULT_LIMIT)
    raw_response = client.get_forensics(
        threat_id=threat_id, campaign_id=campaign_id, include_campaign_forensics=include_campaign_forensics
    )
    reports = raw_response.get("reports", [])
    if len(reports) > limit:
        reports = reports[:limit]
    reports_context = []
    for report in reports:
        report_context = assign_params(Scope=report.get("scope"), Type=report.get("type"), ID=report.get("id"))
        for evidence in report.get("forensics", []):
            evidence_type = evidence.get("type")
            evidence_type = forensic_types.get(evidence_type)
            if evidence_type:
                # Create list in report
                if evidence_type not in report_context:
                    report_context[evidence_type] = []
                what = evidence.get("what", {})
                basic_report = assign_params(
                    Time=evidence.get("time"),
                    Display=evidence.get("display"),
                    Malicious=evidence.get("malicious"),
                )
                basic_report["Platform"] = [
                    {"Name": platform.get("name"), "OS": platform.get("os"), "Version": platform.get("version")}
                    for platform in evidence.get("platforms", [])
                ]

                if evidence_type == "Attachment":
                    basic_report.update(build_context_attachment(what))
                    report_context[evidence_type].append(basic_report)
                elif evidence_type == "Cookie":
                    basic_report.update(build_context_cookie(what))
                    report_context[evidence_type].append(basic_report)
                elif evidence_type == "DNS":
                    basic_report.update(build_context_dns(what))
                    report_context["DNS"].append(basic_report)
                elif evidence_type == "Dropper":
                    basic_report.update(build_context_dropper(what))
                    report_context["Dropper"].append(basic_report)
                elif evidence_type == "File":
                    basic_report.update(build_context_file(what))
                    report_context["File"].append(basic_report)
                elif evidence_type == "IDS":
                    basic_report.update(build_context_ids(what))
                    report_context["IDS"].append(basic_report)
                elif evidence_type == "Mutex":
                    basic_report.update(build_context_mutex(what))
                    report_context["Mutex"].append(basic_report)
                elif evidence_type == "Network":
                    basic_report.update(build_context_network(what))
                    report_context["Network"].append(basic_report)
                elif evidence_type == "Process":
                    basic_report.update(build_context_process(what))
                    report_context["Process"].append(basic_report)
                elif evidence_type == "Registry":
                    basic_report.update(build_context_registry(what))
                    report_context["Registry"].append(basic_report)
                elif evidence_type == "URL":
                    basic_report.update(build_context_url(what))
                    report_context["URL"].append(basic_report)
                elif evidence_type == "Behavior":
                    basic_report.update(build_context_behavior(what))
                    report_context["Behavior"].append(basic_report)
                elif evidence_type == "Screenshot":
                    basic_report.update(build_context_screenshot(what))
                    report_context["Screenshot"].append(basic_report)
        reports_context.append(report_context)
    outputs = {"Proofpoint.Report(var.ID === obj.ID)": reports_context}
    readable_outputs = tableToMarkdown(
        f"Forensic results from ProofPoint for ID: {threat_id or campaign_id}", reports_context, headers=["ID", "Scope", "Type"]
    )
    return readable_outputs, outputs, raw_response


@logger
def get_events_command(client, args):
    interval = args.get("interval")
    threat_type = argToList(args.get("threatType"))
    threat_status = args.get("threatStatus")
    since_time = args.get("sinceTime")
    since_seconds = int(args.get("sinceSeconds")) if args.get("sinceSeconds") else None
    event_type_filter = args.get("eventTypes")

    raw_events = client.get_events(interval, since_time, since_seconds, threat_type, threat_status, event_type_filter)

    return (
        tableToMarkdown("Proofpoint Events", raw_events),
        {
            "Proofpoint.MessagesDelivered(val.GUID == obj.GUID)": raw_events.get("messagesDelivered"),
            "Proofpoint.MessagesBlocked(val.GUID == obj.GUID)": raw_events.get("messagesBlocked"),
            "Proofpoint.ClicksBlocked(val.GUID == obj.GUID)": raw_events.get("clicksBlocked"),
            "Proofpoint.ClicksPermitted(val.GUID == obj.GUID)": raw_events.get("clicksPermitted"),
        },
        raw_events,
    )


def _build_incident(raw_event: dict, event_type: str, occurred: str, raw_json_encoding: str | None = None) -> dict:
    """Build an incident dict from a raw event.

    Args:
        raw_event: The raw event data from Proofpoint API.
        event_type: Human-readable event type (e.g., "messages delivered").
        occurred: The event timestamp string.
        raw_json_encoding: Optional encoding for the raw JSON.

    Returns:
        dict: Incident dict with name, rawJSON, and occurred fields.
    """
    raw_event["type"] = event_type
    event_id = raw_event.get("id") or raw_event.get("GUID", "")

    if raw_json_encoding:
        raw_json = json.dumps(raw_event, ensure_ascii=False).encode(raw_json_encoding).decode()
    else:
        raw_json = json.dumps(raw_event)

    type_label = event_type.replace("_", " ").title()
    return {
        "name": f"Proofpoint - {type_label} - {event_id}",
        "rawJSON": raw_json,
        "occurred": occurred,
    }


# Event type configuration: (response_key, event_type_label, dedup_field, time_field_or_None)
# When time_field is None, use max(threatTime, clickTime)
EVENT_TYPE_CONFIGS = [
    ("messagesDelivered", "messages delivered", "GUID", "messageTime"),
    ("messagesBlocked", "messages blocked", "GUID", "messageTime"),
    ("clicksPermitted", "clicks permitted", "id", None),
    ("clicksBlocked", "clicks blocked", "id", None),
]


def validate_first_fetch_time(first_fetch_time: str):
    """
    validate that the start time is less than 7 days ago
    Args:
        first_fetch_time(str) - the start date time that needs to be validated.
    Returns:
        A valid datetime for the start_query_time
    """
    dt_start_query_time = arg_to_datetime(first_fetch_time) or get_now() - timedelta(hours=1)
    seven_days_ago = get_now() - timedelta(days=7)
    if dt_start_query_time <= seven_days_ago:
        raise DemistoException(
            "The First fetch time range is more than 7 days ago. Please update this parameter since "
            "Proofpoint supports a maximum 1 week fetch back."
        )
    else:
        demisto.debug(f"The {first_fetch_time=} is less than 7 days ago.")


def fetch_incidents(
    client: Client,
    last_run,
    first_fetch_time,
    event_type_filter,
    threat_type,
    threat_status,
    limit=DEFAULT_LIMIT,
    integration_context=None,
    raw_json_encoding: str | None = None,
    look_back_minutes: int = 0,
) -> tuple[dict, list, list]:
    incidents = []
    end_query_time = ""

    # Check if there are incidents saved in context (overflow from previous fetch)
    if integration_context:
        remained_incidents = integration_context.get("incidents")
        demisto.debug(f"remained_incidents: {len(remained_incidents) if remained_incidents else 0}")
        if remained_incidents:
            return last_run, remained_incidents[:limit], remained_incidents[limit:]

    # Load dedup state
    seen_ids: dict[str, str] = last_run.get("seen_ids", {})
    demisto.debug(f"Loaded {len(seen_ids)} seen_ids from last_run")

    # Get the last fetch time, if exists
    start_query_time = last_run.get("last_fetch")
    is_first_fetch = not start_query_time
    # Handle first time fetch, fetch incidents retroactively
    if is_first_fetch:
        start_query_time, _ = parse_date_range(first_fetch_time, date_format=DATE_FORMAT, utc=True)

    # Look-back ramp-up for legacy / freshly-upgraded instances.
    # Relevant for v1.3.0 and above (introduced with the look-back feature in v1.3.0).
    # Purpose: gradually ramp up the look-back window for legacy / freshly-upgraded
    # instances so events are not re-fetched as duplicates.
    # NOTE: This block can (and should) be removed in the future once all existing
    # instances have completed the gradual ramp-up and it is no longer relevant.
    # ------------------------------------------------------------------
    # Applying the full `look_back_minutes` immediately on an instance that has
    # no (or recently-initialized) `seen_ids` state would cause events from the
    # look-back window to be re-fetched as duplicates, because there is nothing
    # in `seen_ids` to dedupe against.
    #
    # First-time fetches do not need ramp-up (no prior state to duplicate).
    now = get_now()
    look_back_enabled_from_str = last_run.get("look_back_enabled_from")
    effective_look_back_minutes = look_back_minutes
    carry_enabled_from: str | None = None

    if look_back_minutes > 0 and not is_first_fetch:
        if "seen_ids" not in last_run and look_back_enabled_from_str is None:
            # Legacy instance: first fetch under the look-back feature.
            carry_enabled_from = now.strftime(DATE_FORMAT)
            effective_look_back_minutes = 0
            demisto.debug(
                f"Legacy instance detected (last_fetch present but no seen_ids in last_run); "
                f"initializing look_back ramp-up from {carry_enabled_from} and skipping look_back "
                f"of {look_back_minutes} minutes for this fetch to avoid duplicates."
            )
        elif look_back_enabled_from_str is not None:
            # Ramp-up in progress: grow effective look-back with elapsed wall-clock time.
            enabled_from_dt = datetime.strptime(look_back_enabled_from_str, DATE_FORMAT)
            elapsed_minutes = int((now - enabled_from_dt).total_seconds() // 60)
            ramp_minutes = max(0, min(look_back_minutes, elapsed_minutes))
            effective_look_back_minutes = ramp_minutes
            if ramp_minutes < look_back_minutes:
                # Still ramping up — carry the stamp forward.
                carry_enabled_from = look_back_enabled_from_str
                demisto.debug(
                    f"Look-back ramp-up active: enabled_from={look_back_enabled_from_str}, "
                    f"elapsed={elapsed_minutes} min, effective look_back={ramp_minutes} "
                    f"(target {look_back_minutes})."
                )
            else:
                # Ramp-up complete — drop the stamp; steady-state full look-back from now on.
                demisto.debug(
                    f"Look-back ramp-up complete (elapsed={elapsed_minutes} min "
                    f">= {look_back_minutes} min target); using full look_back and clearing stamp."
                )

    fetch_intervals = get_fetch_times(start_query_time, effective_look_back_minutes)

    # If no valid intervals, skip this fetch cycle
    if not fetch_intervals:
        demisto.debug("No fetch intervals generated - skipping this fetch cycle")
        return last_run, [], []

    dedup_count = 0

    for interval_start, interval_end in fetch_intervals:
        demisto.debug(f"Fetching interval: {interval_start} / {interval_end}")
        raw_events = client.get_events(
            interval=interval_start + "/" + interval_end,
            event_type_filter=event_type_filter,
            threat_status=threat_status,
            threat_type=threat_type,
        )

        for response_key, event_type_label, dedup_field, time_field in EVENT_TYPE_CONFIGS:
            events = raw_events.get(response_key, [])
            if events:
                demisto.debug(f"Fetched {len(events)} {response_key} events")

            for raw_event in events:
                # Get the dedup key
                dedup_key = raw_event.get(dedup_field, "")

                # Skip if already seen (dedup)
                if dedup_key and dedup_key in seen_ids:
                    demisto.debug(
                        f"Dedup: skipping event {dedup_key} ({response_key}) — "
                        f"already seen in interval ended by {seen_ids[dedup_key]}"
                    )
                    dedup_count += 1
                    continue

                # Determine occurred time
                if time_field:
                    occurred = raw_event.get(time_field, "")
                else:
                    # Safely pick the latest of threatTime / clickTime, ignoring missing values
                    candidate_times = [t for t in (raw_event.get("threatTime"), raw_event.get("clickTime")) if t]
                    occurred = max(candidate_times) if candidate_times else ""
                if not occurred:
                    demisto.debug(f"Event {dedup_key} ({response_key}) is missing an occurred time; using empty value")

                # Build incident
                incident = _build_incident(raw_event, event_type_label, occurred, raw_json_encoding)
                incidents.append(incident)

                # Track this event for dedup — store the end of the fetch interval
                if dedup_key:
                    seen_ids[dedup_key] = interval_end

    if dedup_count > 0:
        demisto.debug(f"Deduplicated {dedup_count} events")

    # Prune old IDs outside the lookback window (always, to avoid unbounded growth of last_run)
    seen_ids = prune_seen_ids(seen_ids, look_back_minutes)

    # Advance last_fetch to end of last interval (real now, not shifted)
    end_query_time = fetch_intervals[-1][1]

    next_run: dict = {
        "last_fetch": end_query_time,
        "seen_ids": seen_ids,
    }
    # Carry the look-back ramp-up stamp forward while ramp-up is still in progress.
    if carry_enabled_from is not None:
        next_run["look_back_enabled_from"] = carry_enabled_from

    demisto.debug(
        f"Fetch summary: {len(fetch_intervals)} intervals, "
        f"{len(incidents)} total incidents, "
        f"returning {min(len(incidents), limit)}, "
        f"remaining {max(0, len(incidents) - limit)}, "
        f"seen_ids={len(seen_ids)}, "
        f"next last_fetch={end_query_time}"
    )

    return next_run, incidents[:limit], incidents[limit:]


def handle_interval(time_range: datetime, is_hours_interval: bool = True, is_days_interval: bool = False):
    """
    Create a list of interval objects from the current time over time range.
    Most of ProofpointTAP requests required interval string in order to retrieve information from the API requests.
    interval objects will be in the following format: '2021-04-27T09:00:00Z/2021-04-27T10:00:00Z'
    Args:
        time_range (datetime): Last interval time.
        is_days_interval (bool): If True, create hours interval objects.
        is_hours_interval (bool): If True, create days interval objects.
    Returns:
        list: List of hour interval items.
    """

    current_time = datetime.utcnow()
    intervals = []
    if current_time - time_range > timedelta(
        days=7
    ):  # The maximum time range of Proofpoint TAP API requests is 7 days minus one minute.
        time_range += timedelta(minutes=1)

    if is_days_interval:
        while current_time - time_range > timedelta(days=1):
            start = time_range.strftime(DATE_FORMAT)
            time_range += timedelta(days=1)
            intervals.append(f"{start}/{time_range.strftime(DATE_FORMAT)}")

    if is_hours_interval:
        while current_time - time_range > timedelta(hours=1):
            start = time_range.strftime(DATE_FORMAT)
            time_range += timedelta(hours=1)
            intervals.append(f"{start}/{time_range.strftime(DATE_FORMAT)}")

    return intervals


def get_clicks_command(
    client: Client, is_blocked: bool, interval: str = None, threat_status: str = None, time_range: str = None
) -> CommandResults:
    """
    Retrieves clicks on malicious URLs in the specified time period. Clicks can either be blocked or permitted.
    Args:
        client (Client): ProofpointTAP API client.
        is_blocked (bool): Indicates the clicks type.
        interval (str): ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour.
        threat_status (str): The status of the threat. Can be: active, cleared or falsePositive.
        time_range (str): Time range, for example: 1 week, 2 days, 3 hours etc.
    Returns:
        CommandResults: raw response, outputs, and readable outputs.
    """
    clicks_type = "blocked" if is_blocked else "permitted"
    if not (interval or time_range):
        raise Exception("Must provide interval or time_range.")
    if interval and time_range:
        raise Exception("Must provide only one of the arguments interval or time_range.")
    if time_range and dateparser.parse("7 days") > dateparser.parse(time_range):  # type: ignore
        raise Exception("The maximum time range is 7 days")
    if time_range and dateparser.parse("30 seconds") < dateparser.parse(time_range):  # type: ignore
        raise Exception("The minimum time range is thirty seconds.")

    if time_range and dateparser.parse("1 hour") < dateparser.parse(time_range):  # type: ignore
        end = datetime.utcnow().strftime(DATE_FORMAT)
        start = dateparser.parse(time_range).strftime(DATE_FORMAT)  # type: ignore
        intervals = [f"{start}/{end}"]
    else:
        intervals = handle_interval(dateparser.parse(time_range)) if time_range else [interval]  # type: ignore

    outputs = []
    raw_responses = []
    for interval_string in intervals:
        raw_response = client.get_clicks(clicks_type, interval_string, threat_status)

        clicks_path = ["clicksBlocked"] if clicks_type == "blocked" else ["clicksPermitted"]
        if dict_safe_get(raw_response, clicks_path):
            outputs.extend(dict_safe_get(raw_response, clicks_path))
            raw_responses.append(raw_response)

    readable_output = tableToMarkdown(
        f"{clicks_type.title()} Clicks",
        outputs,
        headers=[
            "id",
            "senderIP",
            "recipient",
            "classification",
            "threatID",
            "threatURL",
            "threatStatus",
            "threatTime",
            "clickTime",
            "campaignId",
            "userAgent",
        ],
        headerTransform=pascalToSpace,
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix=f"Proofpoint.Clicks{clicks_type.capitalize()}",
        outputs=outputs,
        outputs_key_field=["GUID", "id"],
        raw_response=raw_responses,
    )


def create_messages_output(messages_list: list) -> list:
    """
    Creates and filters the required fields of messages output.
    Args:
        messages_list (list): List of retrieved messages.
    Returns:
        list:  List of messages with the required fields.
    """
    outputs = []
    message_keys = [
        "spamScore",
        "phishScore",
        "threatsInfoMap",
        "messageTime",
        "impostorScore",
        "malwareScore",
        "cluster",
        "subject",
        "quarantineFolder",
        "quarantineRule",
        "policyRoutes",
        "modulesRun",
        "messageSize",
        "messageParts",
        "completelyRewritten",
        "id",
        "sender",
        "recipient",
        "senderIP",
        "messageID",
        "GUID",
    ]

    header_fields = [
        "headerFrom",
        "headerReplyTo",
        "fromAddress",
        "fromAddress",
        "ccAddresses",
        "replyToAddress",
        "toAddresses",
        "xmailer",
    ]

    for message in messages_list:
        message_header = {}
        for field in header_fields:
            message_header[field] = message[field]

        message_output = {key: value for key, value in message.items() if key in message_keys}
        message_output["Header"] = message_header
        outputs.append(message_output)

    return outputs


def create_threats_objects(messages: list) -> list:
    """
    Creates list of threats items of messages.

    Args:
        messages (list): List of messages items.

    Returns:
        list: List of threats items.

    """
    threats_info_map = []
    message_keys = ["sender", "recipient", "subject"]
    for message in messages:
        for threat in message.get("threatsInfoMap"):
            threat_object = {key: value for key, value in message.items() if key in message_keys}
            threat_object.update(threat)
            threats_info_map.append(threat_object)

    return threats_info_map


def get_messages_command(
    client: Client,
    is_blocked: bool,
    interval: str = None,
    threat_status: str = None,
    threat_type: str = None,
    time_range: str = None,
) -> CommandResults:
    """
    Retrieves events for messages in the specified time period. Messages can either be blocked or delivered.
    Args:
        client (Client): ProofpointTAP API client.
        is_blocked (bool): Indicates the messages type.
        interval (str): ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour.
        threat_status (str): The status of the threat. Can be: active, cleared or falsePositive.
        threat_type (str): The type of the threat. Can be: url, attachment or message.
        time_range (str): Time range, for example: 1 week, 2 days, 3 hours etc.
    Returns:
        CommandResults: raw response, outputs, and readable outputs.
    """
    messages_type = "blocked" if is_blocked else "delivered"

    if not (interval or time_range):
        raise Exception("Must provide interval or time_range.")
    if interval and time_range:
        raise Exception("Must provide only one of the arguments interval or time_range.")
    if time_range and dateparser.parse("7 days") > dateparser.parse(time_range):  # type: ignore
        raise Exception("The maximum time range is 7 days")
    if time_range and dateparser.parse("30 seconds") < dateparser.parse(time_range):  # type: ignore
        raise Exception("The minimum time range is thirty seconds.")

    if time_range and dateparser.parse("1 hour") < dateparser.parse(time_range):  # type: ignore
        end = datetime.utcnow().strftime(DATE_FORMAT)
        start = dateparser.parse(time_range).strftime(DATE_FORMAT)  # type: ignore
        intervals = [f"{start}/{end}"]
    else:
        intervals = handle_interval(dateparser.parse(time_range)) if time_range else [interval]  # type: ignore
    outputs = []
    raw_responses = []
    for interval_string in intervals:
        raw_response = client.get_messages(messages_type, interval_string, threat_status, threat_type)

        messages_path = ["messagesBlocked"] if messages_type == "blocked" else ["messagesDelivered"]
        if dict_safe_get(raw_response, messages_path):
            outputs.extend(create_messages_output(dict_safe_get(raw_response, messages_path)))
            raw_responses.append(raw_response)

    threats_info_map = create_threats_objects(outputs)

    messages_readable_output = tableToMarkdown(
        f"{messages_type.title()} Messages",
        outputs,
        headers=[
            "senderIP",
            "sender",
            "recipient",
            "subject",
            "messageSize",
            "messageTime",
            "malwareScore",
            "phishScore",
            "spamScore",
        ],
        headerTransform=pascalToSpace,
    )

    threats_info_readable_output = tableToMarkdown(
        f"{messages_type.title()} Messages Threats Information",
        threats_info_map,
        headers=[
            "sender",
            "recipient",
            "subject",
            "classification",
            "threat",
            "threatStatus",
            "threatUrl",
            "threatID",
            "threatTime",
            "campaignID",
        ],
        headerTransform=pascalToSpace,
    )

    readable_output = messages_readable_output + "\n" + threats_info_readable_output

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix=f"Proofpoint.Messages{messages_type.capitalize()}",
        outputs=outputs,
        outputs_key_field=["GUID", "id"],
        raw_response=raw_responses,
    )


def list_campaigns_command(
    client: Client, interval: str = None, limit: str = None, page: str = None, time_range: str = None
) -> CommandResults:
    """
    Retrieves a list of IDs of campaigns active in a time window.
    Args:
        client (Client): ProofpointTAP API client.
        interval (str): ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one day.
        limit (str): The maximum number of campaign IDs to produce in the response.
        page (str): The page of results to return, in multiples of the specified size.
        time_range (str): Time range, for example: 1 week, 2 days, 3 hours etc.
    Returns:
        CommandResults: raw response, outputs, and readable outputs.
    """

    if not (interval or time_range):
        raise Exception("Must provide interval or time_range.")
    if interval and time_range:
        raise Exception("Must provide only one of the arguments interval or time_range.")
    if time_range and dateparser.parse("7 days") > dateparser.parse(time_range):  # type: ignore
        raise Exception("The maximum time range is 7 days")
    if time_range and dateparser.parse("30 seconds") < dateparser.parse(time_range):  # type: ignore
        raise Exception("The minimum time range is thirty seconds.")

    if time_range and dateparser.parse("1 hour") < dateparser.parse(time_range):  # type: ignore
        end = datetime.utcnow().strftime(DATE_FORMAT)
        start = dateparser.parse(time_range).strftime(DATE_FORMAT)  # type: ignore
        intervals = [f"{start}/{end}"]
    else:
        intervals = (
            handle_interval(
                dateparser.parse(time_range),  # type: ignore
                is_days_interval=True,
            )
            if time_range
            else [  # type: ignore
                interval  # type: ignore
            ]
        )

    outputs = []
    raw_responses = []
    request_error = []
    for interval_string in intervals:
        try:
            raw_response = client.list_campaigns(interval_string, page, limit)
        except ValueError:  # In case there are no campaigns for the interval,  the request returns status code 404
            # which causes an error in http_request function
            request_error.append({"interval": interval_string, "message": f"Not found campaigns data from {interval_string}"})
            continue

        if dict_safe_get(raw_response, ["campaigns"]):
            outputs.extend(dict_safe_get(raw_response, ["campaigns"]))
            raw_responses.append(raw_response)

    readable_output = tableToMarkdown("Campaigns List", outputs, headers=["id", "lastUpdatedAt"], headerTransform=pascalToSpace)

    if request_error:
        readable_output += "\n" + tableToMarkdown(
            "Errors", request_error, headers=["interval", "message"], headerTransform=pascalToSpace
        )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Proofpoint.Campaign",
        outputs=outputs,
        outputs_key_field="id",
        raw_response=raw_responses,
    )


def get_campaign_command(client: Client, campaign_id: str) -> CommandResults | str:
    """
    Retrieves information for a given campaign.
    Args:
        client (Client): ProofpointTAP API client.
        campaign_id (str): The ID of the required campaign.
    Returns:
        CommandResults: raw response, outputs, and readable outputs.
    """
    try:
        raw_response = client.get_campaign(campaign_id)
    except ValueError:
        return "Campaign Id not found"

    campaign_general_fields = ["id", "name", "description", "startDate", "notable"]
    campaign_fields = ["families", "techniques", "actors", "brands", "malware"]

    outputs = {}
    outputs["campaignMembers"] = dict_safe_get(raw_response, ["campaignMembers"])
    outputs["info"] = {key: value for key, value in raw_response.items() if key in campaign_general_fields}
    outputs.update({key: value for key, value in raw_response.items() if key in campaign_fields})
    fields_readable_output = ""
    for field in campaign_fields:
        fields_readable_output += "\n" + tableToMarkdown(
            field.capitalize(), dict_safe_get(outputs, [field]), headers=["id", "name"], headerTransform=pascalToSpace
        )

    campaign_info_output = tableToMarkdown(
        "Campaign Information",
        outputs["info"],
        headers=["id", "name", "description", "startDate", "notable"],
        headerTransform=pascalToSpace,
    )
    campaign_members_output = tableToMarkdown(
        "Campaign Members", outputs["campaignMembers"], headers=["id", "threat", "type"], headerTransform=pascalToSpace
    )

    readable_output = campaign_info_output + "\n" + campaign_members_output + fields_readable_output

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Proofpoint.Campaign",
        outputs=outputs,
        outputs_key_field="id",
        raw_response=raw_response,
    )


def create_families_objects(users: list, statistics_key: str) -> list:
    """
    Creates list of threat families items of users.

    Args:
        statistics_key (str): Dictionary key of users statistics.
        users (list): List of users items.

    Returns:
        list: List of threats items

    """
    threat_families = []
    for user in users:
        emails = dict_safe_get(user, ["identity", "emails"])
        for family in dict_safe_get(user, [statistics_key, "families"]):
            families_object = {"Mailbox": emails, "Threat Family Name": family.get("name"), "Threat Score": family.get("score")}
            threat_families.append(families_object)

    return sorted(threat_families, key=lambda x: (x.get("Threat Score", 0), x.get("Mailbox")), reverse=True)


def list_most_attacked_users_command(client: Client, window: str, limit: str = None, page: str = None) -> CommandResults:
    """
    Retrieves a list of the most attacked users in the organization for a given period.
    Args:
        client (Client): ProofpointTAP API client.
        window (str): The number of days for which the information will be retrieved.
        limit (str): The maximum number of VAPs to produce.
        page (str): The page of results to return, in multiples of the specified size.
    Returns:
        CommandResults: raw response, outputs, and readable outputs.
    """

    raw_response = client.list_most_attacked_users(window, limit, page)
    outputs = raw_response
    threat_families = create_families_objects(dict_safe_get(outputs, ["users"]), "threatStatistics")

    most_attacked_users_output = tableToMarkdown(
        "Most Attacked Users Information",
        outputs,
        headers=["totalVapUsers", "interval", "averageAttackIndex", "vapAttackIndexThreshold"],
        headerTransform=pascalToSpace,
    )

    threat_families_output = tableToMarkdown(
        "Threat Families",
        threat_families,
        headers=["Mailbox", "Threat Family Name", "Threat Score"],
        headerTransform=pascalToSpace,
    )

    readable_output = most_attacked_users_output + "\n" + threat_families_output

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Proofpoint.Vap",
        outputs=outputs,
        raw_response=raw_response,
        outputs_key_field="interval",
    )


def get_top_clickers_command(client: Client, window: str, limit: str = None, page: str = None) -> CommandResults:
    """
    Retrieves a list of the top clickers in the organization for a given period.
    Args:
        client (Client): ProofpointTAP API client.
        window (str): The number of days for which the information will be retrieved.
        limit (str): The maximum number of top clickers to produce.
        page (str): The page of results to return, in multiples of the specified size.
    Returns:
        CommandResults: raw response, outputs, and readable outputs.
    """
    raw_response = client.get_top_clickers(window, limit, page)

    outputs = raw_response
    threat_families = create_families_objects(dict_safe_get(outputs, ["users"]), "clickStatistics")

    top_clickers_output = tableToMarkdown(
        "Top Clickers Users Information", outputs, headers=["totalTopClickers", "interval"], headerTransform=pascalToSpace
    )

    threat_families_output = tableToMarkdown(
        "Threat Families",
        threat_families,
        headers=["Mailbox", "Threat Family Name", "Threat Score"],
        headerTransform=pascalToSpace,
    )

    readable_output = top_clickers_output + threat_families_output

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Proofpoint.Topclickers",
        outputs=outputs,
        raw_response=raw_response,
        outputs_key_field="interval",
    )


def url_decode_command(client: Client, urls: str) -> CommandResults:
    """
    Decode URLs that have been rewritten by TAP to their original, target URL.
    Args:
        client (Client): ProofpointTAP API client.
        urls (str): Encoded URLs.
    Returns:
        CommandResults: raw response, outputs, and readable outputs.
    """
    raw_response = client.url_decode(argToList(urls))
    outputs = dict_safe_get(raw_response, ["urls"])

    readable_output = tableToMarkdown(
        "URLs decoded information", outputs, headers=["encodedUrl", "decodedUrl"], headerTransform=pascalToSpace
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="Proofpoint.URL",
        outputs_key_field="encodedUrl",
        outputs=outputs,
        raw_response=raw_response,
    )


def list_issues_command(
    client: Client, interval: str = None, threat_status: str = None, threat_type: str = None, time_range: str = None
) -> list:
    """
    Retrieves events for permitted clicks on malicious URLs and delivered messages in the specified time period.
    Args:
        client (Client): ProofpointTAP API client.
        interval (str): ISO8601-formatted interval date. The minimum interval is thirty seconds. The maximum interval is one hour.
        threat_status (str): The status of the threat. Can be: active, cleared or falsePositive.
        threat_type (str): The type of the threat. Can be: url, attachment or messageText.
        time_range (str): Time range, for example: 1 week, 2 days, 3 hours etc.
    Returns:
        list: List of CommandResults objects.
    """

    if not (interval or time_range):
        raise Exception("Must provide interval or time_range.")
    if interval and time_range:
        raise Exception("Must provide only one of the arguments interval or time_range.")
    if time_range and dateparser.parse("7 days") > dateparser.parse(time_range):  # type: ignore
        raise Exception("The maximum time range is 7 days")
    if time_range and dateparser.parse("30 seconds") < dateparser.parse(time_range):  # type: ignore
        raise Exception("The minimum time range is thirty seconds.")

    if time_range and dateparser.parse("1 hour") < dateparser.parse(time_range):  # type: ignore
        end = datetime.utcnow().strftime(DATE_FORMAT)
        start = dateparser.parse(time_range).strftime(DATE_FORMAT)  # type: ignore
        intervals = [f"{start}/{end}"]
    else:
        intervals = handle_interval(dateparser.parse(time_range)) if time_range else [interval]  # type: ignore

    messages_outputs = []
    messages_raw_responses = []
    clicks_outputs = []
    clicks_raw_responses = []
    command_results_list = []

    for interval_string in intervals:
        raw_response = client.list_issues(interval_string, threat_status, threat_type)

        messages = dict_safe_get(raw_response, ["messagesDelivered"])

        if messages:
            messages_outputs.extend(create_messages_output(messages))
            messages_raw_responses.append(raw_response)

        clicks = dict_safe_get(raw_response, ["clicksPermitted"])

        if clicks:
            clicks_outputs.extend(clicks)
            clicks_raw_responses.append(raw_response)

    threats_info_map = create_threats_objects(messages_outputs)

    delivered_messages_output = tableToMarkdown(
        "Delivered Messages",
        messages_outputs,
        headers=[
            "senderIP",
            "sender",
            "recipient",
            "subject",
            "messageSize",
            "messageTime",
            "malwareScore",
            "phishScore",
            "spamScore",
        ],
        headerTransform=pascalToSpace,
    )

    threats_info_output = tableToMarkdown(
        "Delivered Messages Threats Info Map:",
        threats_info_map,
        headers=[
            "sender",
            "recipient",
            "subject",
            "classification",
            "threat",
            "threatStatus",
            "threatUrl",
            "threatID",
            "threatTime",
            "campaignID",
        ],
        headerTransform=pascalToSpace,
    )

    messages_readable_output = delivered_messages_output + "\n" + threats_info_output

    command_results_list.append(
        CommandResults(
            readable_output=messages_readable_output,
            outputs_prefix="Proofpoint.MessagesDelivered",
            outputs=messages_outputs,
            outputs_key_field=["GUID", "id"],
            raw_response=messages_raw_responses,
        )
    )

    clicks_readable_output = tableToMarkdown(
        "Permitted click from list-issues command result:",
        clicks_outputs,
        headers=[
            "id",
            "senderIP",
            "recipient",
            "classification",
            "threatID",
            "threatURL",
            "threatStatus",
            "threatTime",
            "clickTime",
            "campaignId",
            "userAgent",
        ],
        headerTransform=pascalToSpace,
    )

    command_results_list.append(
        CommandResults(
            readable_output=clicks_readable_output,
            outputs_prefix="Proofpoint.ClicksPermitted",
            outputs=clicks_outputs,
            outputs_key_field=["GUID", "id"],
            raw_response=clicks_raw_responses,
        )
    )

    return command_results_list


def main():
    """
    PARSE AND VALIDATE INTEGRATION PARAMS
    """
    params = demisto.params()
    service_principal = params.get("credentials", {}).get("identifier")
    secret = params.get("credentials", {}).get("password")

    # Remove trailing slash to prevent wrong URL path to service
    server_url = params["url"][:-1] if (params["url"] and params["url"].endswith("/")) else params["url"]
    api_version = params.get("api_version")

    verify_certificate = not params.get("insecure", False)
    # How many time before the first fetch to retrieve incidents
    fetch_time = params.get("fetch_time", "60 minutes")

    threat_status = argToList(params.get("threat_status"))

    threat_type = argToList(params.get("threat_type"))

    event_type_filter = params.get("events_type")

    raw_json_encoding = params.get("raw_json_encoding")

    fetch_limit = min(int(params.get("limit", DEFAULT_LIMIT)), DEFAULT_LIMIT)

    # Get look-back buffer parameter (default 0 for backward compatibility)
    look_back_minutes = arg_to_number(params.get("look_back_minutes", DEFAULT_LOOK_BACK_MINUTES)) or DEFAULT_LOOK_BACK_MINUTES

    # Remove proxy if not set to true in params
    proxies = handle_proxy()

    command = demisto.command()
    args = demisto.args()
    demisto.info(f"Command being called is {command}")
    demisto.debug(f"{fetch_time=}, {look_back_minutes=}")
    try:
        client = Client(server_url, api_version, verify_certificate, service_principal, secret, proxies)
        commands = {"proofpoint-get-events": get_events_command, "proofpoint-get-forensics": get_forensic_command}
        if command == "test-module":
            validate_first_fetch_time(fetch_time)
            return_outputs(test_module(client))

        elif demisto.command() == "fetch-incidents":
            integration_context = demisto.getIntegrationContext()
            next_run, incidents, remained_incidents = fetch_incidents(
                client=client,
                last_run=demisto.getLastRun(),
                first_fetch_time=fetch_time,
                event_type_filter=event_type_filter,
                threat_status=threat_status,
                threat_type=threat_type,
                limit=fetch_limit,
                integration_context=integration_context,
                raw_json_encoding=raw_json_encoding,
                look_back_minutes=look_back_minutes,
            )
            # Save last_run, incidents, remained incidents into integration
            demisto.setLastRun(next_run)
            demisto.incidents(incidents)
            # preserve context dict
            demisto.setIntegrationContext({"incidents": remained_incidents})

        elif command in commands:
            return_outputs(*commands[command](client, args))

        elif command == "proofpoint-get-events-clicks-blocked":
            return_results(get_clicks_command(client, is_blocked=True, **args))

        elif command == "proofpoint-get-events-clicks-permitted":
            return_results(get_clicks_command(client, is_blocked=False, **args))

        elif command == "proofpoint-get-events-messages-blocked":
            return_results(get_messages_command(client, is_blocked=True, **args))

        elif command == "proofpoint-get-events-messages-delivered":
            return_results(get_messages_command(client, is_blocked=False, **args))

        elif command == "proofpoint-list-campaigns":
            return_results(list_campaigns_command(client, **args))

        elif command == "proofpoint-get-campaign":
            return_results(get_campaign_command(client, **args))

        elif command == "proofpoint-list-most-attacked-users":
            return_results(list_most_attacked_users_command(client, **args))

        elif command == "proofpoint-get-top-clickers":
            return_results(get_top_clickers_command(client, **args))

        elif command == "proofpoint-url-decode":
            return_results(url_decode_command(client, **args))

        elif command == "proofpoint-list-issues":
            return_results(list_issues_command(client, **args))

    except Exception as exception:
        if command == "test-module":
            return_error(str(exception))
        return_error(f"Failed to execute {command} command. Error: {exception!s}")


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