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

import pytest
from freezegun import freeze_time
from ProofpointTAP_v2 import ALL_EVENTS, ISSUES_EVENTS, Client, fetch_incidents, get_events_command

DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
MOCK_URL = "http://123-fake-api.com"
MOCK_DELIVERED_MESSAGE = {
    "GUID": "1111",
    "QID": "r2FNwRHF004109",
    "ccAddresses": ["bruce.wayne@university-of-education.zz"],
    "clusterId": "pharmtech_hosted",
    "fromAddress": "badguy@evil.zz",
    "headerCC": '"Bruce Wayne" <bruce.wayne@university-of-education.zz>',
    "headerFrom": '"A. Badguy" <badguy@evil.zz>',
    "headerReplyTo": None,
    "headerTo": '"Clark Kent" <clark.kent@pharmtech.zz>; "Diana Prince" <diana.prince@pharmtech.zz>',
    "impostorScore": 0,
    "malwareScore": 100,
    "messageID": "1111@evil.zz",
    "threatsInfoMap": [
        {
            "campaignId": "46e01b8a-c899-404d-bcd9-189bb393d1a7",
            "classification": "MALWARE",
            "threat": "threat_num",
            "threatId": "threat_num",
            "threatStatus": "active",
            "threatTime": "2010-01-30T00:00:40.000Z",
            "threatType": "ATTACHMENT",
            "threatUrl": "https://threatinsight.proofpoint.com/43fc1aa4c1cd0146d334c5593b1428f6d062b2c406e5efe8abe95ca",
        },
        {
            "campaignId": "46e01b8a-c899-404d-bcd9-189bb393d1a7",
            "classification": "MALWARE",
            "threat": "badsite.zz",
            "threatId": "3ba97fc852c66a7ba761450edfdfb9f4ffab74715b591294f78b5e37a76481aa",
            "threatTime": "2010-01-30T00:00:30.000Z",
            "threatType": "URL",
            "threatUrl": "https://threatinsight.proofpoint.com/a7ba761450edfdfb9f4ffab74715b591294f78b5e37a76481aa",
        },
    ],
    "messageTime": "2010-01-30T00:00:59.000Z",
    "modulesRun": ["pdr", "sandbox", "spam", "urldefense"],
    "phishScore": 46,
    "policyRoutes": ["default_inbound", "executives"],
    "quarantineFolder": "Attachment Defense",
    "quarantineRule": "module.sandbox.threat",
    "recipient": ["clark.kent@pharmtech.zz", "diana.prince@pharmtech.zz"],
    "replyToAddress": None,
    "sender": "e99d7ed5580193f36a51f597bc2c0210@evil.zz",
    "senderIP": "192.0.2.255",
    "spamScore": 4,
    "subject": "Please find a totally safe invoice attached.",
    "toAddresses": "xx@xxx.com",
    "xmailer": None,
}

MOCK_BLOCKED_MESSAGE = {
    "GUID": "2222",
    "QID": "r2FNwRHF004109",
    "ccAddresses": ["bruce.wayne@university-of-education.zz"],
    "clusterId": "pharmtech_hosted",
    "fromAddress": "badguy@evil.zz",
    "headerCC": '"Bruce Wayne" <bruce.wayne@university-of-education.zz>',
    "headerFrom": '"A. Badguy" <badguy@evil.zz>',
    "headerReplyTo": None,
    "headerTo": '"Clark Kent" <clark.kent@pharmtech.zz>; "Diana Prince" <diana.prince@pharmtech.zz>',
    "impostorScore": 0,
    "malwareScore": 100,
    "messageID": "2222@evil.zz",
    "threatsInfoMap": [
        {
            "campaignId": "46e01b8a-c899-404d-bcd9-189bb393d1a7",
            "classification": "MALWARE",
            "threat": "threat_num",
            "threatId": "threat_num",
            "threatStatus": "active",
            "threatTime": "2010-01-25T00:00:40.000Z",
            "threatType": "ATTACHMENT",
            "threatUrl": "https://threatinsight.proofpoint.com/43fc1aa4c1cd0146d334c5593b1428f6d062b2c406e5efe8abe95ca",
        },
        {
            "campaignId": "46e01b8a-c899-404d-bcd9-189bb393d1a7",
            "classification": "MALWARE",
            "threat": "badsite.zz",
            "threatId": "3ba97fc852c66a7ba761450edfdfb9f4ffab74715b591294f78b5e37a76481aa",
            "threatTime": "2010-01-25T00:00:30.000Z",
            "threatType": "URL",
            "threatUrl": "https://threatinsight.proofpoint.com/a7ba761450edfdfb9f4ffab74715b591294f78b5e37a76481aa",
        },
    ],
    "messageTime": "2010-01-25T00:00:10.000Z",
    "modulesRun": ["pdr", "sandbox", "spam", "urldefense"],
    "phishScore": 46,
    "policyRoutes": ["default_inbound", "executives"],
    "quarantineFolder": "Attachment Defense",
    "quarantineRule": "module.sandbox.threat",
    "recipient": ["clark.kent@pharmtech.zz", "diana.prince@pharmtech.zz"],
    "replyToAddress": None,
    "sender": "e99d7ed5580193f36a51f597bc2c0210@evil.zz",
    "senderIP": "192.0.2.255",
    "spamScore": 4,
    "subject": "Please find a totally safe invoice attached.",
    "toAddresses": "xx@xxx.com",
    "xmailer": None,
}

MOCK_PERMITTED_CLICK = {
    "id": "click-permitted-1",
    "campaignId": "46e01b8a-c899-404d-bcd9-189bb393d1a7",
    "classification": "MALWARE",
    "clickIP": "192.0.2.1",
    "clickTime": "2010-01-11T00:00:20.000Z",
    "messageID": "3333",
    "recipient": "bruce.wayne@pharmtech.zz",
    "sender": "9facbf452def2d7efc5b5c48cdb837fa@badguy.zz",
    "senderIP": "192.0.2.255",
    "threatID": "threat_num2",
    "threatTime": "2010-01-11T00:00:10.000Z",
    "threatURL": "https://threatinsight.proofpoint.com/#/f7622167144dba5e3ae4480eeee78b23d66f7dfed970cfc3d086cc0dabdf50",
    "url": "http://badguy.zz/",
    "userAgent": "Mozilla/5.0(WindowsNT6.1;WOW64;rv:27.0)Gecko/20100101Firefox/27.0",
}

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

MOCK_ISSUES = {"messagesDelivered": [MOCK_DELIVERED_MESSAGE], "clicksPermitted": [MOCK_PERMITTED_CLICK]}

MOCK_ALL_EVENTS = {
    "messagesDelivered": [MOCK_DELIVERED_MESSAGE],
    "clicksPermitted": [MOCK_PERMITTED_CLICK],
    "clicksBlocked": [MOCK_BLOCKED_CLICK],
    "messagesBlocked": [MOCK_BLOCKED_MESSAGE],
}

# Additional "new event" mocks for the look-back + dedup tests.
# These are derived from the existing mocks above by copying them and overriding
# only the dedup-key field (GUID for messages, id for clicks). They represent
# genuinely new events that should NOT collide with the previously-seen IDs.
MOCK_NEW_DELIVERED_MESSAGE = {**MOCK_DELIVERED_MESSAGE, "GUID": "9999", "messageID": "9999@evil.zz"}
MOCK_NEW_BLOCKED_CLICK = {**MOCK_BLOCKED_CLICK, "id": "click-blocked-NEW"}


def get_mocked_time():
    return datetime.strptime("2010-01-01T00:00:00Z", "%Y-%m-%dT%H:%M:%SZ")


def test_command(requests_mock):
    requests_mock.get(
        MOCK_URL + "/v2/siem/issues?format=json&sinceSeconds=100&threatType=url&threatType=attachment", json=MOCK_ISSUES
    )

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    args = {"threatType": "url,attachment", "sinceSeconds": "100", "eventTypes": ISSUES_EVENTS}
    _, outputs, _ = get_events_command(client, args)

    assert len(outputs["Proofpoint.MessagesDelivered(val.GUID == obj.GUID)"]) == 1
    assert len(outputs["Proofpoint.ClicksPermitted(val.GUID == obj.GUID)"]) == 1


def return_self(return_date):
    return return_date


@freeze_time("2010-01-01T00:00:00Z", tz_offset=0)
def test_first_fetch_incidents(requests_mock, mocker):
    requests_mock.get(
        MOCK_URL + "/v2/siem/all?format=json&interval=2009-12-31T23%3A30%3A00Z%2F2010-01-01T00%3A00%3A00Z", json=MOCK_ALL_EVENTS
    )

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    next_run, incidents, _ = fetch_incidents(
        client=client, last_run={}, first_fetch_time="30 minutes", event_type_filter=ALL_EVENTS, threat_status="", threat_type=""
    )

    assert len(incidents) == 4
    assert json.loads(incidents[3]["rawJSON"])["messageID"] == "4444"


def test_next_fetch(requests_mock, mocker):
    # Use 31 minutes to ensure interval is >= 30 seconds
    current_date = "2010-01-01T00:31:00Z"
    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_date, "%Y-%m-%dT%H:%M:%SZ"))

    requests_mock.get(
        MOCK_URL + "/v2/siem/all?format=json&interval=2010-01-01T00%3A00%3A00Z%"
        "2F2010-01-01T00%3A31%3A00Z&threatStatus=active&threatStatus=cleared",
        json=MOCK_ALL_EVENTS,
    )

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    next_run, incidents, _ = fetch_incidents(
        client=client,
        last_run={"last_fetch": "2010-01-01T00:00:00Z"},
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status=["active", "cleared"],
        threat_type="",
        limit=50,
        look_back_minutes=0,
    )

    assert len(incidents) == 4
    assert json.loads(incidents[3]["rawJSON"])["messageID"] == "4444"


def test_fetch_limit(requests_mock, mocker):
    # Use 31 minutes to ensure interval is >= 30 seconds
    current_date = "2010-01-01T00:31:00Z"
    this_run = {"last_fetch": "2010-01-01T00:00:00Z"}
    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_date, "%Y-%m-%dT%H:%M:%SZ"))
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    next_run, incidents, remained = fetch_incidents(
        client=client,
        last_run=this_run,
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status=["active", "cleared"],
        threat_type="",
        limit=3,
        look_back_minutes=0,
    )

    assert next_run["last_fetch"] == "2010-01-01T00:31:00Z"
    assert len(incidents) == 3
    assert len(remained) == 1
    # test another run
    next_run, incidents, remained = fetch_incidents(
        client=client,
        last_run=this_run,
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status=["active", "cleared"],
        threat_type="",
        limit=3,
        integration_context={"incidents": remained},
        look_back_minutes=0,
    )
    assert next_run["last_fetch"] == "2010-01-01T00:00:00Z"
    assert len(incidents) == 1
    assert not remained


@freeze_time("2010-01-01T00:01:00Z", tz_offset=0)
def test_fetch_incidents_with_encoding(requests_mock, mocker):
    """
    Given:
        - Message with latin chars in its subject
        - Raw JSON encoding param set to latin-1

    When:
        - Running fetch incidents

    Then:
        - Ensure subject is returned properly in the raw JSON
    """
    # Mock time to be 1 minute after the parsed time to ensure >= 30 second interval
    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime("2010-01-01T00:01:00Z", "%Y-%m-%dT%H:%M:%SZ"))
    mocker.patch("ProofpointTAP_v2.parse_date_range", return_value=("2010-01-01T00:00:00Z", "never mind"))
    requests_mock.get(
        MOCK_URL + "/v2/siem/all?format=json&interval=2010-01-01T00%3A00%3A00Z%2F2010-01-01T00%3A01%3A00Z",
        json={
            "messagesDelivered": [
                {
                    "subject": "p\u00c3\u00a9rdida",
                    "messageTime": "2010-01-30T00:00:59.000Z",
                },
            ],
        },
    )

    client = Client(
        proofpoint_url=MOCK_URL,
        api_version="v2",
        service_principal="user1",
        secret="123",
        verify=False,
        proxies=None,
    )

    _, incidents, _ = fetch_incidents(
        client=client,
        last_run={},
        first_fetch_time="now",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        raw_json_encoding="latin-1",
        look_back_minutes=0,
    )

    assert json.loads(incidents[0]["rawJSON"])["subject"] == "pérdida"


# Test data: (last_fetch, current_time, expected_interval_count)
# Old format returned list of timestamps, new format returns list of (start, end) tuples
FETCH_TIMES_MOCK = [
    ("2010-01-01T00:00:00Z", "2010-01-01T03:00:00Z", 4),  # 3 hours = 3 intervals of 59min + 1 final interval
    ("2010-01-01T00:00:00Z", "2010-01-01T00:03:00Z", 1),  # 3 minutes = 1 interval
]


@pytest.mark.parametrize("mock_past, mock_now, expected", FETCH_TIMES_MOCK)
def test_get_fetch_times(mocker, mock_past, mock_now, expected):
    from ProofpointTAP_v2 import get_fetch_times

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(mock_now, "%Y-%m-%dT%H:%M:%SZ"))
    intervals = get_fetch_times(mock_past)
    assert len(intervals) == expected
    # Verify all intervals are tuples of (start, end)
    for interval in intervals:
        assert isinstance(interval, tuple)
        assert len(interval) == 2
        start, end = interval
        assert isinstance(start, str)
        assert isinstance(end, str)


# Test data for the conditional look-back logic of get_fetch_times.
# Each row: (last_fetch, now, look_back_minutes, expected_first_interval_start, expected_last_interval_end)
# Logic under test:
#   effective_start = now - max(now - last_fetch, look_back_minutes)
# i.e. the window is expanded ONLY when the existing gap is smaller than look_back_minutes.
GET_FETCH_TIMES_LOOKBACK_MOCK = [
    # 1. look_back_minutes=0 -> behavior unchanged, start == last_fetch.
    (
        "2010-01-01T00:00:00Z",
        "2010-01-01T00:05:00Z",
        0,
        "2010-01-01T00:00:00Z",
        "2010-01-01T00:05:00Z",
    ),
    # 2. now - last_fetch (1 min) < look_back_minutes (10) -> window expanded to look_back.
    #    effective_start = now - 10min = 2009-12-31T23:51:00Z.
    (
        "2010-01-01T00:00:00Z",
        "2010-01-01T00:01:00Z",
        10,
        "2009-12-31T23:51:00Z",
        "2010-01-01T00:01:00Z",
    ),
    # 3. now - last_fetch (5 min) >= look_back_minutes (2) -> window unchanged,
    #    look-back is NOT added on top. effective_start stays at last_fetch.
    (
        "2010-01-01T00:00:00Z",
        "2010-01-01T00:05:00Z",
        2,
        "2010-01-01T00:00:00Z",
        "2010-01-01T00:05:00Z",
    ),
]


@pytest.mark.parametrize(
    "last_fetch, mock_now, look_back_minutes, expected_start, expected_end",
    GET_FETCH_TIMES_LOOKBACK_MOCK,
)
def test_get_fetch_times_look_back_conditional(mocker, last_fetch, mock_now, look_back_minutes, expected_start, expected_end):
    """
    Given:
     - A `last_fetch` timestamp, the current time `now`, and a `look_back_minutes` value.
    When:
     - `get_fetch_times` is called.
    Then:
     - The effective start is `now - max(now - last_fetch, look_back_minutes)`.
       * look_back_minutes=0   -> start == last_fetch (no change).
       * gap <  look_back      -> start == now - look_back_minutes (window expanded).
       * gap >= look_back      -> start == last_fetch (look-back NOT added on top).
     - The window ends at `now`.
    """
    from ProofpointTAP_v2 import get_fetch_times

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(mock_now, "%Y-%m-%dT%H:%M:%SZ"))

    intervals = get_fetch_times(last_fetch, look_back_minutes=look_back_minutes)

    assert intervals, "expected at least one interval"
    assert intervals[0][0] == expected_start
    assert intervals[-1][1] == expected_end


def test_fetch_with_look_back_buffer(requests_mock, mocker):
    """
    Scenario: Fetch incidents with look-back buffer to account for Proofpoint API indexing delay.
    Given:
     - User has configured look_back_minutes=10 to prevent missing events.
     - Last fetch was at 2010-01-01T00:00:00Z.
     - Current time is 2010-01-01T00:01:00Z (gap of 1 min < look_back of 10 min).
    When:
     - fetch_incidents is called with look_back_minutes=10.
    Then:
     - Ensure the fetch window is expanded so its length equals look_back_minutes:
       effective_start = now - 10min = 2009-12-31T23:51:00Z.
     - Ensure the fetch end is current time 2010-01-01T00:01:00Z.
     - Ensure incidents are fetched correctly.
     - Ensure next_run checkpoint is set to the end of the last interval (now).
    """
    from ProofpointTAP_v2 import fetch_incidents

    last_fetch_time = "2010-01-01T00:00:00Z"
    current_time = "2010-01-01T00:01:00Z"
    # gap (1 min) < look_back (10 min) -> effective_start = now - 10min = 23:51:00 previous day
    expected_start_time = "2009-12-31T23:51:00Z"
    # End time is now
    expected_end_time = current_time

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))

    # Mock API call with the expected look-back interval (single interval since < 59 minutes)
    start_encoded = expected_start_time.replace(":", "%3A")
    end_encoded = expected_end_time.replace(":", "%3A")
    requests_mock.get(
        MOCK_URL + f"/v2/siem/all?format=json&interval={start_encoded}%2F{end_encoded}",
        json=MOCK_ALL_EVENTS,
    )

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    next_run, incidents, _ = fetch_incidents(
        client=client,
        # Include an empty `seen_ids` to mark this instance as already initialized
        # for look-back/dedup tracking (NOT a legacy upgrade), so the look-back is
        # applied immediately at its configured value.
        last_run={"last_fetch": last_fetch_time, "seen_ids": {}},
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=10,
    )

    # Verify incidents were fetched
    assert len(incidents) == 4
    # Verify checkpoint is set to the end of the last interval (current time)
    assert next_run["last_fetch"] == expected_end_time


# ---------------------------------------------------------------------------
# Look-back + Deduplication tests
# ---------------------------------------------------------------------------
# The integration tracks already-seen events in `last_run["seen_ids"]` as a
# dict {dedup_key: interval_end_str}. Dedup keys per event type:
#   - messagesDelivered -> "GUID"
#   - messagesBlocked   -> "GUID"
#   - clicksPermitted   -> "id"
#   - clicksBlocked     -> "id"
# ---------------------------------------------------------------------------


def test_dedup_filters_already_seen_incidents_in_lookback_window(requests_mock, mocker):
    """
    Given:
     - last_run.seen_ids contains all four event IDs from a previous fetch,
       and look_back_minutes=2 so the previous window is rescanned.
     - The API returns the exact same events again because of the overlap.
    When:
     - fetch_incidents is called.
    Then:
     - All already-seen events are filtered; zero incidents are produced.
     - The seen_ids dedup state is preserved (not lost) in next_run.
     - last_fetch advances to the end of the last fetched interval.
    """
    last_fetch_time = "2010-01-01T00:00:00Z"
    current_time = "2010-01-01T00:01:00Z"
    interval_end_seen = "2010-01-01T00:00:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))

    # API returns the same events that were already fetched previously.
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    # Previously-seen IDs (mapped to the interval end in which they were first seen,
    # which must be within the lookback window so they are not pruned).
    seen_ids = {
        MOCK_DELIVERED_MESSAGE["GUID"]: interval_end_seen,
        MOCK_BLOCKED_MESSAGE["GUID"]: interval_end_seen,
        MOCK_PERMITTED_CLICK["id"]: interval_end_seen,
        MOCK_BLOCKED_CLICK["id"]: interval_end_seen,
    }

    next_run, incidents, remained = fetch_incidents(
        client=client,
        last_run={"last_fetch": last_fetch_time, "seen_ids": dict(seen_ids)},
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=2,
    )

    # All four events were already seen -> filtered out
    assert incidents == []
    assert remained == []
    # Dedup state is retained in next_run (not lost) — all 4 previously-seen IDs still present
    assert set(next_run["seen_ids"].keys()) == set(seen_ids.keys())
    # last_fetch advances to the end of the last fetched interval (= current time)
    assert next_run["last_fetch"] == current_time


def test_dedup_includes_new_events_within_lookback_window(requests_mock, mocker):
    """
    Given:
     - last_run.seen_ids contains IDs of a delivered message and a blocked click
       from a previous fetch.
     - The API returns a mix of those previously-seen events AND new events
       (MOCK_NEW_DELIVERED_MESSAGE, MOCK_NEW_BLOCKED_CLICK) within the
       overlapped lookback window.
     - look_back_minutes=2.
    When:
     - fetch_incidents is called.
    Then:
     - Only the new (unseen) events become incidents.
     - next_run.seen_ids contains both the previously-seen IDs (still in window)
       AND the new IDs from this fetch.
    """
    last_fetch_time = "2010-01-01T00:00:00Z"
    current_time = "2010-01-01T00:05:00Z"
    interval_end_seen = "2010-01-01T00:00:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))

    api_response = {
        "messagesDelivered": [MOCK_DELIVERED_MESSAGE, MOCK_NEW_DELIVERED_MESSAGE],
        "messagesBlocked": [],
        "clicksPermitted": [],
        "clicksBlocked": [MOCK_BLOCKED_CLICK, MOCK_NEW_BLOCKED_CLICK],
    }
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=api_response)

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    previously_seen = {
        MOCK_DELIVERED_MESSAGE["GUID"]: interval_end_seen,
        MOCK_BLOCKED_CLICK["id"]: interval_end_seen,
    }

    next_run, incidents, _ = fetch_incidents(
        client=client,
        last_run={"last_fetch": last_fetch_time, "seen_ids": dict(previously_seen)},
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=2,
    )

    # Only the two new events become incidents
    assert len(incidents) == 2
    raw_jsons = [json.loads(inc["rawJSON"]) for inc in incidents]
    fetched_dedup_keys = {raw.get("GUID") or raw.get("id") for raw in raw_jsons}
    assert fetched_dedup_keys == {MOCK_NEW_DELIVERED_MESSAGE["GUID"], MOCK_NEW_BLOCKED_CLICK["id"]}

    # next_run.seen_ids contains BOTH old (still within window) and new IDs
    final_ids = set(next_run["seen_ids"].keys())
    assert MOCK_DELIVERED_MESSAGE["GUID"] in final_ids  # old, retained
    assert MOCK_BLOCKED_CLICK["id"] in final_ids  # old, retained
    assert MOCK_NEW_DELIVERED_MESSAGE["GUID"] in final_ids  # new
    assert MOCK_NEW_BLOCKED_CLICK["id"] in final_ids  # new


def test_lookback_zero_prunes_stale_ids(requests_mock, mocker):
    """
    Given:
     - look_back_minutes=0 (default behavior, no lookback).
     - last_run carries a stale seen_ids entry that does not collide with the
       new events from the API.
     - The API returns brand-new events in a non-overlapping window.
    When:
     - fetch_incidents is called.
    Then:
     - All events from the API window become incidents (no dedup-driven drops).
     - The seen_ids in next_run is pruned (pruning runs unconditionally to
       prevent unbounded last_run growth) — the ancient stale ID is removed
       while the freshly-tracked new IDs remain.
    """
    last_fetch_time = "2010-01-01T00:00:00Z"
    current_time = "2010-01-01T00:31:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))

    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    # A stale dedup ID that is far older than any look-back window.
    pre_existing_seen = {"ancient-id": "2000-01-01T00:00:00Z"}

    next_run, incidents, _ = fetch_incidents(
        client=client,
        last_run={"last_fetch": last_fetch_time, "seen_ids": dict(pre_existing_seen)},
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=0,
    )

    # All 4 events appear: no dedup overlap because the pre-existing seen ID
    # doesn't collide with the new event dedup keys.
    assert len(incidents) == 4

    # With look_back_minutes=0 pruning still runs (cutoff = now - 30min), so the
    # ancient stale ID is dropped, but the freshly-tracked new IDs (interval_end
    # = current_time) are preserved.
    final_ids = set(next_run["seen_ids"].keys())
    assert "ancient-id" not in final_ids
    assert MOCK_DELIVERED_MESSAGE["GUID"] in final_ids
    assert MOCK_BLOCKED_MESSAGE["GUID"] in final_ids
    assert MOCK_PERMITTED_CLICK["id"] in final_ids
    assert MOCK_BLOCKED_CLICK["id"] in final_ids


def test_lookback_dedup_state_pruning(requests_mock, mocker):
    """
    Given:
     - last_run.seen_ids contains a mix of IDs:
         * one mapped to an interval_end well outside the lookback window
           (must be pruned),
         * one mapped to an interval_end inside the lookback window
           (must be kept).
     - look_back_minutes=5  ->  prune cutoff = now - (5 + 30) min.
    When:
     - fetch_incidents is called.
    Then:
     - The stale ID is removed from next_run.seen_ids.
     - The in-window ID is preserved.
    """
    last_fetch_time = "2010-01-01T00:55:00Z"
    current_time = "2010-01-01T01:00:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))

    # Empty API response so we focus purely on pruning behavior.
    requests_mock.get(
        MOCK_URL + "/v2/siem/all",
        json={"messagesDelivered": [], "messagesBlocked": [], "clicksPermitted": [], "clicksBlocked": []},
    )

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    # Cutoff for look_back_minutes=5 is now - 35min = 2010-01-01T00:25:00Z.
    seen_ids = {
        "stale-id": "2009-12-31T00:00:00Z",  # way before cutoff -> pruned
        "fresh-id": "2010-01-01T00:55:00Z",  # after cutoff -> kept
    }

    next_run, incidents, _ = fetch_incidents(
        client=client,
        last_run={"last_fetch": last_fetch_time, "seen_ids": dict(seen_ids)},
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=5,
    )

    assert incidents == []
    assert "stale-id" not in next_run["seen_ids"]
    assert "fresh-id" in next_run["seen_ids"]


def test_lookback_first_fetch_initializes_dedup_state(requests_mock, mocker):
    """
    Given:
     - First fetch (last_run = {}) with look_back_minutes=2.
     - The API returns the standard set of all four events.
    When:
     - fetch_incidents is called.
    Then:
     - The seen_ids dedup state is initialized in next_run with the dedup
       keys of every fetched incident (GUIDs for messages, ids for clicks).
     - All events become incidents and last_fetch is populated.
    """
    current_time = "2010-01-01T00:05:00Z"
    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))
    # parse_date_range returns the first_fetch start time for a clean first-fetch flow.
    mocker.patch("ProofpointTAP_v2.parse_date_range", return_value=("2010-01-01T00:00:00Z", "never mind"))

    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    next_run, incidents, _ = fetch_incidents(
        client=client,
        last_run={},  # first fetch
        first_fetch_time="30 minutes",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=2,
    )

    # All 4 events become incidents on first fetch
    assert len(incidents) == 4

    # seen_ids is initialized and contains all 4 dedup keys
    assert "seen_ids" in next_run
    expected_ids = {
        MOCK_DELIVERED_MESSAGE["GUID"],
        MOCK_BLOCKED_MESSAGE["GUID"],
        MOCK_PERMITTED_CLICK["id"],
        MOCK_BLOCKED_CLICK["id"],
    }
    assert set(next_run["seen_ids"].keys()) == expected_ids
    # last_fetch checkpoint is populated (end of last interval = current time)
    assert next_run["last_fetch"] == current_time


class TestGetForensics:
    PLATFORMS_OBJECT = [
        {
            "name": "windows 7 sp1",
            "os": "windows 7",
            "version": "4.5.661",
        }
    ]
    EVIDENCE_OBJECT_URL = {
        "type": "url",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {
            "url": "string",
            "blacklisted": "boolean",
            "ip": "string",
            "httpStatus": "string",
            "md5": "string",
            "offset": "integer",
            "rule": "string",
            "sha256": "string",
            "size": "integer",
        },
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_REGISTRY = {
        "type": "registry",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {
            "action": "string",
            "key": "string",
            "name": "string",
            "rule": "string",
            "value": "string",
        },
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_PROCESS = {
        "type": "process",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {
            "action": "string",
            "path": "string",
        },
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_NETWORK = {
        "type": "network",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {
            "action": "string",
            "ip": "string",
            "port": "string",
            "type": "string",
        },
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_MUTEX = {
        "type": "mutex",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {
            "name": "string",
            "path": "string",
        },
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_IDS = {
        "type": "ids",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {
            "name": "string",
            "signatureId": "integer",
        },
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_FILE = {
        "type": "file",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {"action": "string", "md5": "string", "path": "string", "rule": "string", "sha256": "string", "size": "integer"},
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_DROPPER = {
        "type": "dropper",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {"path": "string", "rule": "string", "url": "string"},
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_DNS = {
        "type": "dns",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {
            "host": "string",
            "cnames": ["string1", "string2"],
            "ips": ["string1", "string2"],
            "nameservers": ["string1", "string2"],
            "nameserversList": ["string1", "string2"],
        },
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_COOKIE = {
        "type": "cookie",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {"action": "string", "domain": "string", "key": "string", "value": "string"},
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_OBJECT_ATTACHMENT = {
        "type": "attachment",
        "display": "string",
        "time": "string",
        "malicious": "string",
        "what": {"sha256": "string", "md5": "string", "offset": "integer", "rule": "string", "size": "integer"},
        "platforms": PLATFORMS_OBJECT,
    }
    EVIDENCE_LIST = [
        EVIDENCE_OBJECT_ATTACHMENT,
        EVIDENCE_OBJECT_COOKIE,
        EVIDENCE_OBJECT_DNS,
        EVIDENCE_OBJECT_DROPPER,
        EVIDENCE_OBJECT_FILE,
        EVIDENCE_OBJECT_IDS,
        EVIDENCE_OBJECT_MUTEX,
        EVIDENCE_OBJECT_NETWORK,
        EVIDENCE_OBJECT_PROCESS,
        EVIDENCE_OBJECT_REGISTRY,
        EVIDENCE_OBJECT_URL,
    ]
    REPORT_OBJECT = [
        {
            "name": "string",
            "scope": "string",
            "type": "string",
            "id": "string",
            "forensics": EVIDENCE_LIST,
        }
    ]

    REPORT = {
        "generated": "string",
        "reports": REPORT_OBJECT * 2,
    }

    FORENSICS_REPORT = {
        "Scope": "string",
        "Type": "string",
        "ID": "string",
        "Attachment": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "SHA256": "string",
                "MD5": "string",
                "Offset": "integer",
                "Size": "integer",
            }
        ],
        "Cookie": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Action": "string",
                "Domain": "string",
                "Key": "string",
                "Value": "string",
            }
        ],
        "DNS": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Host": "string",
                "CNames": ["string1", "string2"],
                "IP": ["string1", "string2"],
                "NameServers": ["string1", "string2"],
                "NameServersList": ["string1", "string2"],
            }
        ],
        "Dropper": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Path": "string",
                "URL": "string",
                "Rule": "string",
            }
        ],
        "File": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Path": "string",
                "Action": "string",
                "SHA256": "string",
                "MD5": "string",
                "Size": "integer",
            }
        ],
        "IDS": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Name": "string",
                "SignatureID": "integer",
            }
        ],
        "Mutex": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Name": "string",
                "Path": "string",
            }
        ],
        "Network": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Action": "string",
                "IP": "string",
                "Port": "string",
                "Protocol": "string",
            }
        ],
        "Process": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Action": "string",
                "Path": "string",
            }
        ],
        "Registry": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "Name": "string",
                "Action": "string",
                "Key": "string",
                "Value": "string",
            }
        ],
        "URL": [
            {
                "Time": "string",
                "Display": "string",
                "Malicious": "string",
                "Platform": [{"Name": "windows 7 sp1", "OS": "windows 7", "Version": "4.5.661"}],
                "URL": "string",
                "Blacklisted": "boolean",
                "SHA256": "string",
                "MD5": "string",
                "Size": "integer",
                "HTTPStatus": "string",
                "IP": "string",
            }
        ],
    }

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )

    def test_get_forensics(self, requests_mock):
        from ProofpointTAP_v2 import get_forensic_command

        requests_mock.get("http://123-fake-api.com/v2/forensics?threatId=1256", json=self.REPORT)
        _, output, _ = get_forensic_command(self.client, {"threatId": "1256"})
        reports = output["Proofpoint.Report(var.ID === obj.ID)"]
        assert len(reports) == 2
        report = reports[0]
        assert all(report)
        assert report == self.FORENSICS_REPORT


def load_mock_response(file_name: str) -> str:
    """
    Load mock file that simulates an API response.

    Args:
        file_name (str): Name of the mock response JSON file to return.

    Returns:
        str: Mock file content.

    """
    with open(f"test_data/{file_name}", encoding="utf-8") as mock_file:
        return mock_file.read()


def test_get_clicks_command(requests_mock):
    """
    Scenario: Retrieves clicks to malicious URLs blocked and permitted in the specified time period.
    Given:
     - User has provided valid credentials and arguments.
    When:
     - A get-clicks command is called and there is clicks in the response.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, get_clicks_command

    requests_mock.get(
        f"{MOCK_URL}/v2/siem/clicks/blocked", json={"queryEndTime": "2021-03-23T14:00:00Z", "clicksBlocked": [MOCK_BLOCKED_CLICK]}
    )
    requests_mock.get(
        f"{MOCK_URL}/v2/siem/clicks/permitted",
        json={"queryEndTime": "2021-03-23T14:00:00Z", "clicksPermitted": [MOCK_PERMITTED_CLICK]},
    )

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    blocked_result = get_clicks_command(client, True, "3 days")
    permitted_result = get_clicks_command(client, False, "3 days")
    assert len(blocked_result.outputs) == 1
    assert blocked_result.outputs_prefix == "Proofpoint.ClicksBlocked"
    assert blocked_result.outputs[0].get("messageID") == "4444"
    assert len(permitted_result.outputs) == 1
    assert permitted_result.outputs_prefix == "Proofpoint.ClicksPermitted"
    assert permitted_result.outputs[0].get("messageID") == "3333"


def test_get_messages_command(requests_mock):
    """
    Scenario: Retrieves messages to malicious URLs blocked and delivered in the specified time period.
    Given:
     - User has provided valid credentials and arguments.
    When:
     - A get-messages command is called and there is messages in the response.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, get_messages_command

    requests_mock.get(
        f"{MOCK_URL}/v2/siem/messages/blocked",
        json={"queryEndTime": "2021-03-23T14:00:00Z", "messagesBlocked": [MOCK_BLOCKED_MESSAGE]},
    )
    requests_mock.get(
        f"{MOCK_URL}/v2/siem/messages/delivered",
        json={"queryEndTime": "2021-03-23T14:00:00Z", "messagesDelivered": [MOCK_DELIVERED_MESSAGE]},
    )

    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    blocked_result = get_messages_command(client, True, "3 days")
    delivered_result = get_messages_command(client, False, "3 days")
    assert len(blocked_result.outputs) == 1
    assert blocked_result.outputs_prefix == "Proofpoint.MessagesBlocked"
    assert blocked_result.outputs[0].get("messageID") == "2222@evil.zz"
    assert len(delivered_result.outputs) == 1
    assert delivered_result.outputs_prefix == "Proofpoint.MessagesDelivered"
    assert delivered_result.outputs[0].get("messageID") == "1111@evil.zz"


def test_list_campaigns_command(requests_mock):
    """
    Scenario: Retrieves a list of IDs of campaigns active in a time window.
    Given:
     - User has provided valid credentials.
    When:
     - A list-campaign-ids command is called and there is campaigns in the response.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, list_campaigns_command

    mock_response = json.loads(load_mock_response("campaigns.json"))
    requests_mock.get(f"{MOCK_URL}/v2/campaign/ids", json=mock_response)
    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    result = list_campaigns_command(client, "3 days")
    assert len(result.outputs) == 2
    assert result.outputs_prefix == "Proofpoint.Campaign"
    assert result.outputs[0].get("id") == "f3ff0874-85ef-475e-b3fe-d05f97b2ed3f"
    assert result.outputs[0].get("lastUpdatedAt") == "2021-03-25T10:37:46.000Z"


def test_get_campaign(requests_mock):
    """
    Scenario: Retrieves information for a given campaign.
    Given:
     - User has provided valid credentials and argument.
    When:
     - A get-campaign command is called and there is a campaign in the response.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, get_campaign_command

    mock_response = json.loads(load_mock_response("campaign_information.json"))
    requests_mock.get(f"{MOCK_URL}/v2/campaign/1", json=mock_response)
    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    result = get_campaign_command(client, "1")
    assert len(result.outputs) == 7
    assert result.outputs_prefix == "Proofpoint.Campaign"
    assert result.outputs.get("info").get("id") == "aa9b3d62-4d72-4ebc-8f39-3da3833e7038"


def test_list_most_attacked_users_command(requests_mock):
    """
    Scenario: Retrieves a list of the most attacked users in the organization for a given period.
    Given:
     - User has provided valid credentials and argument.
    When:
     - A get-vap command is called and there is a attacked people in the response.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, list_most_attacked_users_command

    mock_response = json.loads(load_mock_response("most_attacked_users.json"))
    requests_mock.get(f"{MOCK_URL}/v2/people/vap", json=mock_response)
    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    result = list_most_attacked_users_command(client, "")
    assert len(result.outputs) == 5
    assert result.outputs_prefix == "Proofpoint.Vap"
    assert result.outputs.get("users")[0].get("identity").get("guid") == "88e36bf359-99e8-7e53-f58a-6df8b430be6d"
    assert result.outputs.get("totalVapUsers") == 2


def test_get_top_clickers_command(requests_mock):
    """
    Scenario: Retrieves a list of the top clickers in the organization for a given period.
    Given:
     - User has provided valid credentials and argument.
    When:
     - A get_top_clickers command is called and there is clickers in the response.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, get_top_clickers_command

    mock_response = json.loads(load_mock_response("top_clickers.json"))
    requests_mock.get(f"{MOCK_URL}/v2/people/top-clickers", json=mock_response)
    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    result = get_top_clickers_command(client, "")
    assert len(result.outputs) == 3
    assert result.outputs_prefix == "Proofpoint.Topclickers"
    assert result.outputs.get("users")[1].get("identity").get("guid") == "b4077fsv0e-3a2e-767f-7315-c049f831cc95"
    assert result.outputs.get("totalTopClickers") == 2


def test_url_decode(requests_mock):
    """
    Scenario: Decode URLs that have been rewritten by TAP to their original, target URL.
    Given:
     - User has provided valid credentials and arguments.
    When:
     - A url-decode command is called.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, url_decode_command

    mock_response = json.loads(load_mock_response("url_decode.json"))
    requests_mock.post(f"{MOCK_URL}/v2/url/decode", json=mock_response)
    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    result = url_decode_command(client, "")
    assert len(result.outputs) == 2
    assert result.outputs_prefix == "Proofpoint.URL"
    assert result.outputs[1].get("decodedUrl") == "http://www.bouncycastle.org/"


def test_list_issues_command(requests_mock):
    """
    Scenario: Retrieves events for clicks to malicious URLs permitted and messages delivered in the specified time period.
    Given:
     - User has provided valid credentials and arguments.
    When:
     - A list_issues command is called and there is clicks and messages in the response.
    Then:
     - Ensure number of items is correct.
     - Ensure outputs prefix is correct.
     - Ensure a sample value from the API matches what is generated in the context.

    """
    from ProofpointTAP_v2 import Client, list_issues_command

    requests_mock.get(
        f"{MOCK_URL}/v2/siem/issues",
        json={
            "queryEndTime": "2021-04-16T14:00:00Z",
            "messagesDelivered": [MOCK_DELIVERED_MESSAGE],
            "clicksPermitted": [MOCK_PERMITTED_CLICK],
        },
    )
    client = Client(
        proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None
    )
    result = list_issues_command(client, "3 days")
    messages_result = result[0]
    clicks_result = result[1]

    assert len(clicks_result.outputs) == 1
    assert clicks_result.outputs_prefix == "Proofpoint.ClicksPermitted"
    assert clicks_result.outputs[0].get("messageID") == "3333"

    assert len(messages_result.outputs) == 1
    assert messages_result.outputs_prefix == "Proofpoint.MessagesDelivered"
    assert messages_result.outputs[0].get("messageID") == "1111@evil.zz"


@freeze_time("2024-05-03T11:00:00")
def test_validate_first_fetch_time_valid_str():
    """
    Given:
     - A valid str first_fetch_time
    When:
     - running test_module.
    Then:
     - No exception is thrown.
    """
    from ProofpointTAP_v2 import validate_first_fetch_time

    first_fetch_time = "1 day ago"
    try:
        validate_first_fetch_time(first_fetch_time)
    except Exception as e:
        raise AssertionError(f"validate_first_fetch_time raised an exception {e}")  # noqa: PT015


@freeze_time("2024-05-03T11:00:00")
def test_validate_first_fetch_time_not_valid():
    """
    Given:
     - A first_fetch_time bigger than 7 days ago
    When:
     - running test_module.
    Then:
     - Exception is thrown.
    """
    from ProofpointTAP_v2 import validate_first_fetch_time

    first_fetch_time = "8 days ago"
    try:
        validate_first_fetch_time(first_fetch_time)
    except Exception as e:
        assert (
            "The First fetch time range is more than 7 days ago. Please update this parameter since "
            "Proofpoint supports a maximum 1 week fetch back."
        ) in str(e)


# ---------------------------------------------------------------------------
# Look-back ramp-up tests (legacy upgrade safety)
# ---------------------------------------------------------------------------
# When `look_back_minutes` is enabled but the dedup state (`seen_ids`) is
# empty or only freshly initialized, applying the full look-back would cause
# events to be re-fetched as duplicates. The integration solves this with a
# `look_back_enabled_from` timestamp stored in `last_run`: the effective
# look-back grows linearly with the elapsed wall-clock time since that stamp
# and is capped at the configured `look_back_minutes`.
# These tests assert the effective value passed to `get_fetch_times`.
# ---------------------------------------------------------------------------


def _spy_get_fetch_times(mocker):
    """Patch ProofpointTAP_v2.get_fetch_times to capture its arguments while
    delegating to the real implementation. Returns the spy mock object.
    """
    from ProofpointTAP_v2 import get_fetch_times as _real_get_fetch_times

    return mocker.patch("ProofpointTAP_v2.get_fetch_times", side_effect=_real_get_fetch_times)


def _make_client():
    return Client(proofpoint_url=MOCK_URL, api_version="v2", service_principal="user1", secret="123", verify=False, proxies=None)


def test_lookback_legacy_instance_disables_lookback_and_stamps(requests_mock, mocker):
    """
    Given:
     - A legacy instance whose last_run has `last_fetch` but no `seen_ids` key
       (state from a version of the integration that predates the look-back
       / seen_ids feature).
     - look_back_minutes=30 (the new default).
    When:
     - fetch_incidents is called.
    Then:
     - get_fetch_times is called with look_back_minutes=0 for this cycle
       (avoiding re-fetching the last 30 minutes as duplicates).
     - next_run["look_back_enabled_from"] is stamped with the current time so
       the ramp-up can grow on subsequent fetches.
    """
    last_fetch_time = "2010-01-01T00:00:00Z"
    current_time = "2010-01-01T00:01:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))
    spy = _spy_get_fetch_times(mocker)
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    next_run, _, _ = fetch_incidents(
        client=_make_client(),
        last_run={"last_fetch": last_fetch_time},  # no seen_ids -> legacy
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=30,
    )

    # Effective look_back for this fetch must be 0.
    assert spy.call_args.args[1] == 0 or spy.call_args.kwargs.get("look_back_minutes") == 0
    # Stamp persisted for the next cycle.
    assert next_run.get("look_back_enabled_from") == current_time
    # seen_ids is initialized in next_run regardless (so on the next fetch
    # we are no longer "legacy", just "ramping up").
    assert "seen_ids" in next_run


def test_lookback_rampup_caps_effective_to_elapsed(requests_mock, mocker):
    """
    Given:
     - An instance previously stamped with look_back_enabled_from=00:00:00.
     - Current time is 00:10:00 -> elapsed = 10 minutes.
     - Configured look_back_minutes=30.
    When:
     - fetch_incidents is called.
    Then:
     - get_fetch_times is called with look_back_minutes=10 (capped to the
       elapsed window in which seen_ids has actually been tracking events).
     - The stamp is carried forward in next_run because ramp-up is not done.
    """
    last_fetch_time = "2010-01-01T00:09:00Z"
    current_time = "2010-01-01T00:10:00Z"
    enabled_from = "2010-01-01T00:00:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))
    spy = _spy_get_fetch_times(mocker)
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    next_run, _, _ = fetch_incidents(
        client=_make_client(),
        last_run={
            "last_fetch": last_fetch_time,
            "seen_ids": {},
            "look_back_enabled_from": enabled_from,
        },
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=30,
    )

    # Effective look-back equals elapsed minutes (10), not the configured 30.
    passed_look_back = spy.call_args.args[1] if len(spy.call_args.args) >= 2 else spy.call_args.kwargs.get("look_back_minutes")
    assert passed_look_back == 10
    # Stamp is carried forward while ramp-up continues.
    assert next_run.get("look_back_enabled_from") == enabled_from


def test_lookback_rampup_completes_and_drops_stamp(requests_mock, mocker):
    """
    Given:
     - An instance previously stamped with look_back_enabled_from=00:00:00.
     - Current time is 01:00:00 -> elapsed = 60 minutes >= look_back_minutes.
     - Configured look_back_minutes=30.
    When:
     - fetch_incidents is called.
    Then:
     - get_fetch_times is called with the full configured look_back_minutes=30.
     - The stamp is dropped from next_run (ramp-up complete, steady state).
    """
    last_fetch_time = "2010-01-01T00:55:00Z"
    current_time = "2010-01-01T01:00:00Z"
    enabled_from = "2010-01-01T00:00:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))
    spy = _spy_get_fetch_times(mocker)
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    next_run, _, _ = fetch_incidents(
        client=_make_client(),
        last_run={
            "last_fetch": last_fetch_time,
            "seen_ids": {},
            "look_back_enabled_from": enabled_from,
        },
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=30,
    )

    passed_look_back = spy.call_args.args[1] if len(spy.call_args.args) >= 2 else spy.call_args.kwargs.get("look_back_minutes")
    assert passed_look_back == 30
    # Stamp dropped now that ramp-up is complete.
    assert "look_back_enabled_from" not in next_run


def test_lookback_steady_state_uses_full_lookback(requests_mock, mocker):
    """
    Given:
     - An upgraded instance with last_fetch AND a populated seen_ids,
       and NO look_back_enabled_from stamp (i.e. steady state).
     - Configured look_back_minutes=30.
    When:
     - fetch_incidents is called.
    Then:
     - get_fetch_times is called with the full configured look_back_minutes=30.
     - No look_back_enabled_from is introduced in next_run.
    """
    last_fetch_time = "2010-01-01T00:00:00Z"
    current_time = "2010-01-01T00:01:00Z"

    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))
    spy = _spy_get_fetch_times(mocker)
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    next_run, _, _ = fetch_incidents(
        client=_make_client(),
        last_run={
            "last_fetch": last_fetch_time,
            "seen_ids": {MOCK_DELIVERED_MESSAGE["GUID"]: "2010-01-01T00:00:00Z"},
        },
        first_fetch_time="3 days",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=30,
    )

    passed_look_back = spy.call_args.args[1] if len(spy.call_args.args) >= 2 else spy.call_args.kwargs.get("look_back_minutes")
    assert passed_look_back == 30
    assert "look_back_enabled_from" not in next_run


def test_lookback_first_fetch_uses_full_lookback(requests_mock, mocker):
    """
    Given:
     - A first-time fetch (empty last_run, no last_fetch, no seen_ids).
     - Configured look_back_minutes=30.
    When:
     - fetch_incidents is called.
    Then:
     - get_fetch_times is called with the full configured look_back_minutes=30
       (first fetches have nothing prior to duplicate, so ramp-up is unneeded).
     - No look_back_enabled_from is stamped in next_run.
    """
    current_time = "2010-01-01T00:05:00Z"
    mocker.patch("ProofpointTAP_v2.get_now", return_value=datetime.strptime(current_time, "%Y-%m-%dT%H:%M:%SZ"))
    mocker.patch("ProofpointTAP_v2.parse_date_range", return_value=("2010-01-01T00:00:00Z", "never mind"))
    spy = _spy_get_fetch_times(mocker)
    requests_mock.get(MOCK_URL + "/v2/siem/all", json=MOCK_ALL_EVENTS)

    next_run, _, _ = fetch_incidents(
        client=_make_client(),
        last_run={},  # first fetch
        first_fetch_time="30 minutes",
        event_type_filter=ALL_EVENTS,
        threat_status="",
        threat_type="",
        limit=50,
        look_back_minutes=30,
    )

    passed_look_back = spy.call_args.args[1] if len(spy.call_args.args) >= 2 else spy.call_args.kwargs.get("look_back_minutes")
    assert passed_look_back == 30
    assert "look_back_enabled_from" not in next_run