CiscoESA

The Cisco Email Security Appliance is an email security gateway product. It is designed to detect and block a wide variety of email-born threats, such as malware, spam and phishing attempts.

Network Security · Cisco Email Security Appliance (IronPort)

Details

IDCiscoESA
ProviderCisco Systems
CategoryNetwork Security
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

The Cisco Email Security Appliance is an email security gateway product. It is designed to detect and block a wide variety of email-born threats, such as malware, spam and phishing attempts.
This integration was integrated and tested with version 16.0 of Cisco Email Security Appliance.

Configure Cisco ESA in Cortex

Parameter Description Required
Server URL Base URL, e.g., https://XXX.eu.iphmx.com True
Username   True
Password   True
Maximum incidents per fetch Default is 50. Maximum is 100. False
First fetch timestamp Timestamp in ISO format or number time unit,
e.g., 2022-01-01T00:00:00000Z, 12 hours, 7 days, 3 months, now.
False
Filter by The message field by which to fetch results. False
Filter operator The message field operator by which to fetch results. False
Filter value The message filter value by which to fetch results. False
Recipient filter operator The message recipient filter operator by which to fetch results. False
Recipient filter value The message recipient filter value by which to fetch results. False
Time to live for the JWT connection token (in minutes).   False
Use system proxy settings   False
Trust any certificate (not secure)   False
Incident type   False
Fetch incidents   False

Troubleshooting

If you encounter multiple recurring errors similar to the following message:

Authorization Error: make sure username and password are set correctly.

By default, the integration assumes your JWT session tokens have a time to live of 30 minutes.
If the time to live is shorter, it can lead to the authorization error above. To resolve this error, reduce the value for the Time to live for JWT session token parameter.
By default, this value is 30 minutes and should only be reduced if these errors occur.

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.

cisco-esa-spam-quarantine-message-search


Search messages in the spam quarantine.

Base Command

cisco-esa-spam-quarantine-message-search

Input

Argument Name Description Required
start_date Start date in ISO format or <number> <time unit>,
e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now.
Required
end_date End date in ISO format or <number> <time unit>,
e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now.
Required
filter_by The message field by which to filter the results. Possible values are: from_address, to_address, subject. Optional
filter_operator Filter operator by which to filter the results. Possible values are: contains, is, begins_with, ends_with, does_not_contain. Optional
filter_value The value to search for. This is a user defined value. D.g., filterValue=abc.com. Optional
recipient_filter_operator Recipient operator filter by which to filter the results. Possible values are: contains, is, begins_with, ends_with, does_not_contain. Optional
recipient_filter_value Recipient filter by which to filter the results. Optional
order_by The attribute by which to order the data in the response. Possible values are: from_address, date, subject, size. Optional
order_dir Results order direction. Possible values are: asc, desc. Optional
page Page number of paginated results.
Minimum value: 1.
Optional
page_size Number of results per page. Maximum value 100. Optional
limit The maximum number of records to retrieve. Default is 50. Optional

Context Output

Path Type Description
CiscoESA.SpamQuarantineMessage.envelopeRecipient String Recipient email address.
CiscoESA.SpamQuarantineMessage.toAddress String Recipient email address.
CiscoESA.SpamQuarantineMessage.subject String Email subject.
CiscoESA.SpamQuarantineMessage.date String Email due date.
CiscoESA.SpamQuarantineMessage.fromAddress String Sender email address.
CiscoESA.SpamQuarantineMessage.size String email size.
CiscoESA.SpamQuarantineMessage.mid Number Message ID.

Command example

!cisco-esa-spam-quarantine-message-search start_date=2weeks end_date=now page=3 page_size=2

Context Example

{
    "CiscoESA": {
        "SpamQuarantineMessage": [
            {
                "date": "13 Oct 2022 11:56 (GMT +00:00)",
                "envelopeRecipient": [
                    "test@test.com"
                ],
                "fromAddress": [
                    "Test Test <test@test.com>"
                ],
                "mid": 1573,
                "size": "10.20K",
                "subject": "hello 1",
                "toAddress": [
                    "test@test.com <test@test.com>"
                ]
            },
            {
                "date": "13 Oct 2022 11:54 (GMT +00:00)",
                "envelopeRecipient": [
                    "test@test.com"
                ],
                "fromAddress": [
                    "Test Test <test@test.com>"
                ],
                "mid": 1571,
                "size": "10.20K",
                "subject": "test 2",
                "toAddress": [
                    "test@test.com <test@test.com>"
                ]
            }
        ]
    }
}

Human Readable Output

Spam Quarantine Messages List

Showing page 3.
Current page size: 2.

Mid Date From Address To Address Subject Size
1573 13 Oct 2022 11:56 (GMT +00:00) Test Test test@test.com “test@test.com” test@test.com hello 1 10.20K
1571 13 Oct 2022 11:54 (GMT +00:00) Test Test test@test.com “test@test.com” test@test.com test 2 10.20K

cisco-esa-spam-quarantine-message-get


Get spam quarantine message details.

Base Command

cisco-esa-spam-quarantine-message-get

Input

Argument Name Description Required
message_id Message ID. Required

Context Output

Path Type Description
CiscoESA.SpamQuarantineMessage.envelopeRecipient String Message recipient.
CiscoESA.SpamQuarantineMessage.toAddress String Message recipient.
CiscoESA.SpamQuarantineMessage.messageBody String Message body.
CiscoESA.SpamQuarantineMessage.date String Message date.
CiscoESA.SpamQuarantineMessage.fromAddress String Message sender.
CiscoESA.SpamQuarantineMessage.subject String Message subject.
CiscoESA.SpamQuarantineMessage.mid Number Message ID.

Command example

!cisco-esa-spam-quarantine-message-get message_id=1572

Context Example

{
    "CiscoESA": {
        "SpamQuarantineMessage": {
            "attachments": [],
            "date": "13 Oct 2022 11:56 (GMT +00:00)",
            "envelopeRecipient": [
                "test@test.com"
            ],
            "fromAddress": [
                "Test Test <test@test.com>"
            ],
            "messageBody": "X-MGA-submission: MDFwkBZw0xxrJATK37WrEGRJETolGcH/Ec0fhopBiCRuw7z3sB/lgKvnfUMzauVhijIde5pya7OR9Xn3ykXf7DGOX2PG4OSu//hcfzlboDzNMfYKbQ2c3Zs+883VYMeiUtz+xN/UCnIv9OHLDgJQ93IexI75JnATjKoedFoZpy80/g==<br />\nIronPort-HdrOrdr: A9a23:3J6AuqN81clqn8BcTyb155DYdb4zR+YMi2TDiHoddfUFSKalfp\r\n 6V98jzjSWE7gr5K0tQ4OxoWZPwNk80kKQY3WB/B8bHYOCLggqVxeJZnP3fKl/bakrDH4dmvM\r\n 8OHZSWY+eAbmSS+PyKhTVQZOxQouVvnprJuc7ui1NWCS16YaBp6Al0TiyBFFdteQVADZ0lUL\r\n KB+8tuvVObCDwqR/X+IkNAc/nIptXNmp6jSwUBHQQb5A6Hii7twKLmEiKfwgwVX1p0sPwfGC\r\n n+4kbED5eYwr2GIyznpiDuBlNt6ZXcI+54dYGxYw4uW3TRY0iTFcRcsva5zUgISamUmS0XeZ\r\n /30l4d1o1ImgnsV3Dwrh331wb61jEyr3fk1F+DmHPm5df0XTQgFqN69PBkmzbimjodVetHod\r\n F29nPcs4ASAQLLnSz76dSNXxZ2llCsqX5nleIIlXRQXYYXdbcU9OUkjTdoOYZFGDi/5JEsEe\r\n FoAs2Z7PFKcUmCZ3ScumV02tSjUnk6Ax/72<br />\nX-SLBL-Result: BLOCK-LISTED<br />\nX-IronPort-MailFlowPolicy: $ACCEPTED<br />\nX-IronPort-SenderGroup: ACCEPTLIST<br />\nX-IronPort-Listener: MailFlow<br />\nX-IronPort-Reputation: 3.5<br />\nX-IronPort-MID: 1572<br />\nX-IronPort-RemoteIP: 1.1.1.1<br />\nIronPort-SDR:\r\n\tboundary=_000_AS4P192MB1694AF23A0D358D3FE1B6B71AB259AS4P192MB1694EURP_<br />\nMIME-Version: 1.0<br />\nX-OriginatorOrg: test.com<br />\nX-MS-Exchange-CrossTenant-AuthAs: Internal<br />\nX-MS-Exchange-CrossTenant-AuthSource: test.test.COM<br />\nX-MS-Exchange-CrossTenant-Network-Message-Id: 26a48316-d039-47af-e556-08daad11e929<br />\nX-MS-Exchange-CrossTenant-originalarrivaltime: 13 Oct 2022 11:56:06.4076\r\n (UTC)<br />\nX-MS-Exchange-CrossTenant-fromentityheader: Hosted<br />\nX-MS-Exchange-CrossTenant-id: ed363dfd-16fd-4038-8e58-9237411a84e5<br />\nX-MS-Exchange-CrossTenant-mailboxtype: HOSTED<br />\nX-MS-Exchange-CrossTenant-userprincipalname: CkxCbZ1GZcqcuiVMCbo/AlVFa3/u8MxVWLuGIDg099YXDpyeHTh+tTrYpdMa/AWXF41GXNn/phrOWU4SsBEH6A==<br />\nX-MS-Exchange-Transport-CrossTenantHeadersStamped: DB8P192MB0598<br /><br />\n\r<br>\n",
            "mid": 1572,
            "subject": "hello",
            "toAddress": [
                "test@test.com <test@test.com>"
            ]
        }
    }
}

Human Readable Output

Spam Quarantine Message

Found spam quarantine message with ID: 1572

Mid From Address To Address Date Subject
1572 Test Test test@test.com “test@test.com” test@test.com 13 Oct 2022 11:56 (GMT +00:00) hello

cisco-esa-spam-quarantine-message-release


Release quarantine emails.

Base Command

cisco-esa-spam-quarantine-message-release

Input

Argument Name Description Required
message_ids A comma-separated list of message IDs. Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-spam-quarantine-message-release message_ids=1573

Human Readable Output

Quarantined message 1573 successfully released.

cisco-esa-spam-quarantine-message-delete


Delete quarantine emails.

Base Command

cisco-esa-spam-quarantine-message-delete

Input

Argument Name Description Required
message_ids A comma-separated list of message IDs to delete. Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-spam-quarantine-message-delete message_ids=1574

Human Readable Output

Quarantined message 1574 successfully deleted.

cisco-esa-list-entry-get


Get spam quarantine blocklist/safelist entry.

Base Command

cisco-esa-list-entry-get

Input

Argument Name Description Required
entry_type List entry type. Possible values are: blocklist, safelist. Required
page Page number of paginated results.
Minimum value: 1.
Optional
page_size Number of results per page. Maximum value 100. Optional
limit The maximum number of records to retrieve. Default is 50. Optional
order_by The attribute by which to order the data in the response. Possible values are: recipient, sender. Optional
order_dir Results order direction. Possible values are: asc, desc. Optional
view_by View results by. Possible values are: recipient, sender. Default is recipient. Optional
search Search for recipients or senders in blocklist/safelist with ‘contains’ operator.
e.g., test@test.com, test.com
This is only supported for the argument view_by=recipient.
Optional

Context Output

Path Type Description
CiscoESA.ListEntry.Blocklist.senderList String Sender list.
CiscoESA.ListEntry.Blocklist.recipientAddress String Recipient address.
CiscoESA.ListEntry.Blocklist.recipientList String Recipient list.
CiscoESA.ListEntry.Blocklist.senderAddress String Sender address.
CiscoESA.ListEntry.Safelist.senderList String Sender list.
CiscoESA.ListEntry.Safelist.recipientAddress String Recipient address.
CiscoESA.ListEntry.Safelist.recipientList String Recipient list.
CiscoESA.ListEntry.Safelist.senderAddress String Sender address.

Command example

!cisco-esa-list-entry-get entry_type=safelist page=2 page_size=3 view_by=recipient order_by=recipient order_dir=desc

Context Example

{
    "CiscoESA": {
        "ListEntry": {
            "Safelist": [
                {
                    "recipientAddress": "test4@test.com",
                    "senderList": [
                        "test@test.com"
                    ]
                },
                {
                    "recipientAddress": "test3@test.com",
                    "senderList": [
                        "test@test.com"
                    ]
                },
                {
                    "recipientAddress": "test2@test.com",
                    "senderList": [
                        "test@test.com"
                    ]
                }
            ]
        }
    }
}

Human Readable Output

Safelist Entries

Showing page 2.
Current page size: 3.

Recipient Address Sender List
test4@test.com test@test.com
test3@test.com test@test.com
test2@test.com test@test.com

cisco-esa-list-entry-add


Add spam quarantine blocklist/safelist entry.

Base Command

cisco-esa-list-entry-add

Input

Argument Name Description Required
entry_type List entry type. Possible values are: blocklist, safelist. Required
view_by Add list entry by recipient/sender.
When view_by = recipient, recipient_addresses and sender_list are mandatory.
When view_by = sender, sender_addresses and recipient_list are mandatory. Possible values are: recipient, sender. Default is recipient.
Optional
recipient_addresses A comma-separated list of recipient addresses to add. Optional
sender_list A comma-separated list of senders to add. Optional
sender_addresses A comma-separated list of sender addresses to add. Optional
recipient_list A comma-separated list of recipients to add. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-list-entry-add entry_type=blocklist view_by=recipient recipient_addresses=test@test.com sender_list=t1@test.com,t2@test.com

Human Readable Output

Successfully added t1@test.com, t2@test.com senders to test@test.com recipients in blocklist.

cisco-esa-list-entry-append


Append spam quarantine blocklist/safelist entry.

Base Command

cisco-esa-list-entry-append

Input

Argument Name Description Required
entry_type List entry type. Possible values are: blocklist, safelist. Required
view_by Append list entry by recipient/sender.
When view_by = recipient, recipient_addresses and sender_list are mandatory.
When view_by = sender, sender_addresses and recipient_list are mandatory. Possible values are: recipient, sender. Default is recipient.
Optional
recipient_list A comma-separated list of recipients to append. Optional
sender_list A comma-separated list of senders to append. Optional
recipient_addresses A comma-separated list of recipient addresses to append. Optional
sender_addresses A comma-separated list of sender addresses to append. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-list-entry-append entry_type=blocklist recipient_addresses=test@test.com sender_list=t4@test.com

Human Readable Output

Successfully appended t4@test.com senders to test@test.com recipients in blocklist.

cisco-esa-list-entry-edit


Edit spam quarantine blocklist/safelist entry. Using this command will override the existing value.

Base Command

cisco-esa-list-entry-edit

Input

Argument Name Description Required
entry_type List entry type. Possible values are: blocklist, safelist. Required
view_by Edit list entry by recipient/sender.
When view_by = recipient, recipient_addresses and sender_list are mandatory.
When view_by = sender, sender_addresses and recipient_list are mandatory. Possible values are: recipient, sender. Default is recipient.
Optional
recipient_list A comma-separated list of recipients to edit. Optional
sender_list A comma-separated list of senders to edit. Optional
recipient_addresses A comma-separated list of recipient addresses to edit. Optional
sender_addresses A comma-separated list of sender addresses to edit. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-list-entry-edit entry_type=blocklist view_by=recipient recipient_addresses=test@test.com sender_list=t5@test.com,t6@test.com

Human Readable Output

Successfully edited test@test.com recipients’ senders to t5@test.com, t6@test.com in blocklist.

cisco-esa-list-entry-delete


Delete spam quarantine blocklist/safelist entry.

Base Command

cisco-esa-list-entry-delete

Input

Argument Name Description Required
entry_type List entry type. Possible values are: blocklist, safelist. Required
view_by Delete list entry by recipient/sender.
When view_by = recipient, recipient_list is mandatory.
When view_by = sender, sender_list is mandatory. Possible values are: recipient, sender. Default is recipient.
Optional
recipient_list List of recipient/sender addresses to delete. Optional
sender_list List of recipient/sender addresses to delete. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-list-entry-delete entry_type=blocklist view_by=recipient recipient_list=test@test.com

Human Readable Output

Successfully deleted test@test.com recipients from blocklist.

cisco-esa-message-search


Search tracking messages.

Base Command

cisco-esa-message-search

Input

Argument Name Description Required
start_date Start date in ISO format or <number> <time unit>,
e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now.
Required
end_date End date in ISO format or <number> <time unit>,
e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now.
Required
page Page number of paginated results.
Minimum value: 1.
Optional
page_size Number of results per page. Maximum value 100. Optional
limit The maximum number of records to retrieve. Default is 50. Optional
sender_filter_operator Sender filter operator. Possible values are: contains, is, begins_with. Optional
sender_filter_value Sender filter value. Optional
recipient_filter_operator Recipient filter operator. Possible values are: contains, is, begins_with. Optional
recipient_filter_value Recipient filter value. Optional
subject_filter_operator Subject filter operator. Possible values are: contains, is, begins_with. Optional
subject_filter_value Subject filter value. Optional
attachment_name_operator Attachment name operator. Possible values are: contains, is, begins_with. Optional
attachment_name_value Attachment name value. Optional
file_sha_256 SHA256 must be 64 characters long and can contain only “0-9” and “a-f” characters.
e.g. e0d123e5f316bef78bfdf5a008837577e0d123e5f316bef78bfdf5a008837577.
Optional
custom_query Custom query for cisco ESA’s advanced filters.
Syntax: <key>=<value>;<key>=<value>;<key>=<value>
e.g., graymail=True;message_delivered=True.
Optional

Context Output

Path Type Description
CiscoESA.Message.hostName String Email gateway hostname.
CiscoESA.Message.friendly_from String Friendly formatted sender email address.
CiscoESA.Message.isCompleteData String Whether the entire data was pulled.
CiscoESA.Message.messageStatus String Message delivery status.
CiscoESA.Message.recipientMap String Recipients list.
CiscoESA.Message.senderIp String Sender IP address.
CiscoESA.Message.mailPolicy String Matched mail policy.
CiscoESA.Message.senderGroup String Matched sender group.
CiscoESA.Message.subject String Subject of email message.
CiscoESA.Message.mid Number Message ID.
CiscoESA.Message.senderDomain String Domain of email message sender.
CiscoESA.Message.finalSubject String Extended email subject.
CiscoESA.Message.direction String Message direction, incoming or outgoing.
CiscoESA.Message.icid Number An Injection Connection ID (ICID). A numerical identifier for an individual SMTP connection to the system.
CiscoESA.Message.replyTo String Email message reply to.
CiscoESA.Message.timestamp String Time of the email message.
CiscoESA.Message.messageID String Extended message ID.
CiscoESA.Message.verdictChart String Verdict visual chart ID.
CiscoESA.Message.recipient String Recipients email addresses list.
CiscoESA.Message.sender String Sender email address.
CiscoESA.Message.serialNumber String Cisco ESA email gateway serial number.
CiscoESA.Message.allIcid Number ICIDs list.
CiscoESA.Message.sbrs String Sender Base Reputation Scores.

Command example

!cisco-esa-message-search start_date=1month end_date=now page=3 page_size=2 subject_filter_operator=contains subject_filter_value=test

Context Example

{
    "CiscoESA": {
        "Message": [
            {
                "allIcid": [
                    29969
                ],
                "direction": "incoming",
                "finalSubject": {
                    "1438": "test"
                },
                "friendly_from": [
                    "test@test.com"
                ],
                "hostName": "",
                "icid": 29969,
                "isCompleteData": "N/A",
                "mailPolicy": [
                    "DEFAULT"
                ],
                "messageID": {
                    "1438": "<test@test.test.COM>"
                },
                "messageStatus": {
                    "1438": "Quarantined by Anti-Spam/Graymail"
                },
                "mid": [
                    1438
                ],
                "morDetails": {},
                "recipient": [
                    "test@test.com"
                ],
                "recipientMap": {
                    "1438": [
                        "test@test.com"
                    ]
                },
                "replyTo": "N/A",
                "sbrs": "3.5",
                "sender": "test@test.com",
                "senderDomain": "test.com",
                "senderGroup": "ACCEPTLIST",
                "senderIp": "1.1.1.1",
                "serialNumber": "test-test",
                "subject": "test",
                "timestamp": "2022-10-03T11:54:28Z",
                "unique_message_id": "1438",
                "verdictChart": {
                    "1438": "16140210"
                }
            },
            {
                "allIcid": [
                    19653
                ],
                "direction": "incoming",
                "finalSubject": {
                    "758": "test123"
                },
                "friendly_from": [
                    "test@test.com"
                ],
                "hostName": "",
                "icid": 19653,
                "isCompleteData": "N/A",
                "mailPolicy": [
                    "DEFAULT"
                ],
                "messageID": {
                    "758": "<test@test.test.COM>"
                },
                "messageStatus": {
                    "758": "Quarantined by Anti-Spam/Graymail"
                },
                "mid": [
                    758
                ],
                "morDetails": {},
                "recipient": [
                    "test@test.com"
                ],
                "recipientMap": {
                    "758": [
                        "test@test.com"
                    ]
                },
                "replyTo": "N/A",
                "sbrs": "3.5",
                "sender": "test@test.com",
                "senderDomain": "test.com",
                "senderGroup": "ACCEPTLIST",
                "senderIp": "1.1.1.1",
                "serialNumber": "test-test",
                "subject": "test123",
                "timestamp": "2022-09-20T15:03:40Z",
                "unique_message_id": "758",
                "verdictChart": {
                    "758": "16130210"
                }
            }
        ]
    }
}

Human Readable Output

Messages List

Showing page 3.
Current page size: 2.

Mid All Icid Serial Number Sender Recipient Subject Message Status Timestamp Sender Ip Sbrs
1438 29969 test-test test@test.com test@test.com test 1438: Quarantined by Anti-Spam/Graymail 2022-10-03T11:54:28Z 1.1.1.1 3.5
758 19653 test-test test@test.com test@test.com test123 758: Quarantined by Anti-Spam/Graymail 2022-09-20T15:03:40Z 1.1.1.1 3.5

cisco-esa-message-details-get


Get message details.

Base Command

cisco-esa-message-details-get

Input

Argument Name Description Required
serial_number Email gateway serial number. Required
message_ids Message ID list. Required
injection_connection_id Injection connection ID. Optional

Context Output

Path Type Description
CiscoESA.Message.sdrAge String Sender domain reputation age.
CiscoESA.Message.attachments String Message attachments.
CiscoESA.Message.hostName String Email gateway hostname.
CiscoESA.Message.isCompleteData Boolean Whether the entire data was pulled.
CiscoESA.Message.messageStatus String Message delivery status.
CiscoESA.Message.mailPolicy String Matched mail policy.
CiscoESA.Message.senderGroup String Matched sender group.
CiscoESA.Message.subject String Subject of email message.
CiscoESA.Message.showSummaryTimeBox Boolean Whether to display the summary timebox.
CiscoESA.Message.sdrCategory String Sender domain reputation category.
CiscoESA.Message.mid Number Message ID.
CiscoESA.Message.sendingHostSummary.reverseDnsHostname String Sending host reverse DNS hostname.
CiscoESA.Message.sendingHostSummary.ipAddress String Sending host IP address.
CiscoESA.Message.sendingHostSummary.sbrsScore String Sending host sender base reputation scores.
CiscoESA.Message.direction String Message direction, incoming or outgoing.
CiscoESA.Message.smtpAuthId String SMTP authorization ID.
CiscoESA.Message.midHeader String Message ID header.
CiscoESA.Message.timestamp String Email message time.
CiscoESA.Message.showDLP Boolean Whether the DLP report is available.
CiscoESA.Message.messageSize String Email message size.
CiscoESA.Message.sdrReputation String Sender domain reputation.
CiscoESA.Message.showURL Boolean Whether the URL report is available.
CiscoESA.Message.recipient String Message recipient email address.
CiscoESA.Message.sender String Message sender email address.
CiscoESA.Message.showAMP Boolean Whether the AMP report is available.
CiscoESA.Message.summary.timestamp String Event summary time.
CiscoESA.Message.summary.description String Event summary description
CiscoESA.Message.summary.lastEvent Boolean Whether this is the last summary event.
CiscoESA.Message.allIcid Number ICIDs list.
CiscoESA.Message.headerFrom String Email message header from.

Command example

!cisco-esa-message-details-get serial_number=test-test message_ids=1576 injection_connection_id=36859

Context Example

{
    "CiscoESA": {
        "Message": {
            "allIcid": [
                36859
            ],
            "ampTgCategories": [],
            "attachments": [],
            "direction": "incoming",
            "headerFrom": "test@test.com",
            "hostName": "(Name unresolved,  SN:test-test)",
            "isCompleteData": false,
            "mailPolicy": [
                "DEFAULT"
            ],
            "messageSize": "9.17 (KB)",
            "messageStatus": "Quarantined by Anti-Spam/Graymail",
            "mid": [
                1576
            ],
            "midHeader": "<test@test.test.com>",
            "recipient": [
                "test@test.com"
            ],
            "sdrAge": "30 days (or greater)",
            "sdrCategory": "N/A",
            "sdrReputation": "Neutral",
            "sdrThreatLevels": "3",
            "sender": "test@test.com",
            "senderGroup": "ACCEPTLIST",
            "sendingHostSummary": {
                "ipAddress": "1.1.1.1",
                "reverseDnsHostname": "mail-test.test.test.test.com (verified)",
                "sbrsScore": "3.5"
            },
            "showAMP": false,
            "showDLP": false,
            "showSummaryTimeBox": true,
            "showURL": false,
            "smtpAuthId": "",
            "subject": "hello 4",
            "summary": [
                {
                    "description": "Incoming connection (ICID 36859) has sender_group: ACCEPTLIST, sender_ip: 1.1.1.1 and sbrs: 3.5",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:22Z"
                },
                {
                    "description": "Protocol SMTP interface Data 1  (IP 1.1.1.1) on incoming connection (ICID 36859) from sender IP 1.1.1.1. Reverse DNS  host mail-test.test.test.test.com verified yes.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:22Z"
                },
                {
                    "description": "(ICID 36859) ACCEPT sender group ACCEPTLIST match sbrs[0.0:10.0] SBRS 3.5 country Ireland",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:22Z"
                },
                {
                    "description": "Incoming connection (ICID 36859) successfully accepted TLS protocol TLSv1.2 cipher test-test-test.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 Sender Domain: test.com",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Start message 1576 on incoming connection (ICID 36859).",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 enqueued on incoming connection (ICID 36859) from test@test.com.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 direction: incoming",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 Domains for which SDR is requested: reverse DNS host: mail-test.test.test.test.com, helo: test.test.test.com, env-from: test.com, header_from: Not Present, reply_to: Not Present",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 Consolidated Sender Threat Level: Neutral, Threat Category: N/A, Suspected Domain(s) : N/A (other reasons for verdict). Sender Maturity: 30 days (or greater) for domain: test.com",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 on incoming connection (ICID 36859) added recipient (test@test.com).",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 SPF: mailfrom identity test@test.com Pass",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 DKIM: pass signature verified (d=test.test.com s=selector2-test-test-com i=@test.test.com)",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576: DMARC Verification skipped (No record found for the sending domain).",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 contains message ID header '<test@test.test.com>'.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 original subject on injection: hello 4",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 Domains for which SDR is requested: reverse DNS host: mail-test.test.test.test.com, helo: test.test.test.com, env-from: test.com, header_from: test.com, reply_to: Not Present",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 Consolidated Sender Threat Level: Neutral, Threat Category: N/A, Suspected Domain(s) : N/A (other reasons for verdict). Sender Maturity: 30 days (or greater) for domain: test.com",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 (9389 bytes) from test@test.com ready.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 has sender_group: ACCEPTLIST, sender_ip: 1.1.1.1 and sbrs: 3.5",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 matched per-recipient policy DEFAULT for inbound mail policies.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 scanned by Anti-Spam engine: SLBL. Interim verdict: Positive",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 scanned by Anti-Spam engine: SLBL. Final verdict: Positive",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Incoming connection (ICID 36859) lost.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 scanned by Anti-Virus engine McAfee. Interim verdict: CLEAN",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 scanned by Anti-Virus engine Sophos. Interim verdict: CLEAN",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 scanned by Anti-Virus engine. Final verdict: Negative ",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 scanned by Advanced Malware Protection engine. Final verdict: SKIPPED(no attachment in message)",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:23Z"
                },
                {
                    "description": "Message 1576 scanned by Outbreak Filters. Verdict: Negative",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:24Z"
                },
                {
                    "description": "Message 1576 queued for delivery.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:24Z"
                },
                {
                    "description": "Remote procedure call connection (RCID 1) started for message 1576 to local Spam Quarantine.",
                    "lastEvent": false,
                    "timestamp": "2022-10-13T11:56:27Z"
                },
                {
                    "description": "Message 1576 quarantined in Spam Quarantine.",
                    "lastEvent": true,
                    "timestamp": "2022-10-13T11:56:27Z"
                }
            ],
            "timestamp": "2022-10-13T11:56:23Z",
            "unique_message_id": "1576"
        }
    }
}

Human Readable Output

Message Details

Found message with ID 1576.

Mid All Icid Subject Sender Recipient Timestamp Message Size Sending Host Summary Message Status Direction Mail Policy Sender Group Show AMP Show DLP Show URL
1576 36859 hello 4 test@test.com test@test.com 2022-10-13T11:56:23Z 9.17 (KB) reverseDnsHostname: mail-test.test.test.test.com (verified)
ipAddress: 1.1.1.1
sbrsScore: 3.5
Quarantined by Anti-Spam/Graymail incoming DEFAULT ACCEPTLIST false false false

Message Summary

Description Timestamp Last Event
Incoming connection (ICID 36859) has sender_group: ACCEPTLIST, sender_ip: 1.1.1.1 and sbrs: 3.5 2022-10-13T11:56:22Z false
Protocol SMTP interface Data 1 (IP 1.1.1.1) on incoming connection (ICID 36859) from sender IP 1.1.1.1. Reverse DNS host mail-test.test.test.test.com verified yes. 2022-10-13T11:56:22Z false
(ICID 36859) ACCEPT sender group ACCEPTLIST match sbrs[0.0:10.0] SBRS 3.5 country Ireland 2022-10-13T11:56:22Z false
Incoming connection (ICID 36859) successfully accepted TLS protocol TLSv1.2 cipher test-test-test. 2022-10-13T11:56:23Z false
Message 1576 Sender Domain: test.com 2022-10-13T11:56:23Z false
Start message 1576 on incoming connection (ICID 36859). 2022-10-13T11:56:23Z false
Message 1576 enqueued on incoming connection (ICID 36859) from test@test.com. 2022-10-13T11:56:23Z false
Message 1576 direction: incoming 2022-10-13T11:56:23Z false
Message 1576 Domains for which SDR is requested: reverse DNS host: mail-test.test.test.test.com, helo: test.test.test.com, env-from: test.com, header_from: Not Present, reply_to: Not Present 2022-10-13T11:56:23Z false
Message 1576 Consolidated Sender Threat Level: Neutral, Threat Category: N/A, Suspected Domain(s) : N/A (other reasons for verdict). Sender Maturity: 30 days (or greater) for domain: test.com 2022-10-13T11:56:23Z false
Message 1576 on incoming connection (ICID 36859) added recipient (test@test.com). 2022-10-13T11:56:23Z false
Message 1576 SPF: mailfrom identity test@test.com Pass 2022-10-13T11:56:23Z false
Message 1576 DKIM: pass signature verified (d=test.test.com s=selector2-test-test-com i=@test.test.com) 2022-10-13T11:56:23Z false
Message 1576: DMARC Verification skipped (No record found for the sending domain). 2022-10-13T11:56:23Z false
Message 1576 contains message ID header ‘test@test.test.com’. 2022-10-13T11:56:23Z false
Message 1576 original subject on injection: hello 4 2022-10-13T11:56:23Z false
Message 1576 Domains for which SDR is requested: reverse DNS host: mail-test.test.test.test.com, helo: test.test.test.com, env-from: test.com, header_from: test.com, reply_to: Not Present 2022-10-13T11:56:23Z false
Message 1576 Consolidated Sender Threat Level: Neutral, Threat Category: N/A, Suspected Domain(s) : N/A (other reasons for verdict). Sender Maturity: 30 days (or greater) for domain: test.com 2022-10-13T11:56:23Z false
Message 1576 (9389 bytes) from test@test.com ready. 2022-10-13T11:56:23Z false
Message 1576 has sender_group: ACCEPTLIST, sender_ip: 1.1.1.1 and sbrs: 3.5 2022-10-13T11:56:23Z false
Message 1576 matched per-recipient policy DEFAULT for inbound mail policies. 2022-10-13T11:56:23Z false
Message 1576 scanned by Anti-Spam engine: SLBL. Interim verdict: Positive 2022-10-13T11:56:23Z false
Message 1576 scanned by Anti-Spam engine: SLBL. Final verdict: Positive 2022-10-13T11:56:23Z false
Incoming connection (ICID 36859) lost. 2022-10-13T11:56:23Z false
Message 1576 scanned by Anti-Virus engine McAfee. Interim verdict: CLEAN 2022-10-13T11:56:23Z false
Message 1576 scanned by Anti-Virus engine Sophos. Interim verdict: CLEAN 2022-10-13T11:56:23Z false
Message 1576 scanned by Anti-Virus engine. Final verdict: Negative 2022-10-13T11:56:23Z false
Message 1576 scanned by Advanced Malware Protection engine. Final verdict: SKIPPED(no attachment in message) 2022-10-13T11:56:23Z false
Message 1576 scanned by Outbreak Filters. Verdict: Negative 2022-10-13T11:56:24Z false
Message 1576 queued for delivery. 2022-10-13T11:56:24Z false
Remote procedure call connection (RCID 1) started for message 1576 to local Spam Quarantine. 2022-10-13T11:56:27Z false
Message 1576 quarantined in Spam Quarantine. 2022-10-13T11:56:27Z true

cisco-esa-message-amp-details-get


Get message AMP summary details.

Base Command

cisco-esa-message-amp-details-get

Input

Argument Name Description Required
serial_number Email gateway serial number. Required
message_ids Message ID list. Required

Context Output

Path Type Description
CiscoESA.AMPDetail.sdrAge String Sender domain reputation age.
CiscoESA.AMPDetail.attachments String Message attachments.
CiscoESA.AMPDetail.hostName String Email gateway hostname.
CiscoESA.AMPDetail.direction String Message direction, incoming or outgoing.
CiscoESA.AMPDetail.messageStatus String Message delivery status.
CiscoESA.AMPDetail.senderGroup String Matched sender group.
CiscoESA.AMPDetail.subject String Email message subject.
CiscoESA.AMPDetail.sdrCategory String Sender domain reputation category.
CiscoESA.AMPDetail.mid Number Message ID.
CiscoESA.AMPDetail.ampDetails.timestamp String AMP event summary details timestamp.
CiscoESA.AMPDetail.ampDetails.description String AMP event summary details description.
CiscoESA.AMPDetail.ampDetails.lastEvent Boolean AMP event summary details last event.
CiscoESA.AMPDetail.smtpAuthId String SMTP authorization ID.
CiscoESA.AMPDetail.midHeader String Message ID header.
CiscoESA.AMPDetail.timestamp String Email message time.
CiscoESA.AMPDetail.messageSize String Email message size.
CiscoESA.AMPDetail.sdrThreatLevels String Sender domain reputation threat levels.
CiscoESA.AMPDetail.sdrReputation String Sender domain reputation.
CiscoESA.AMPDetail.recipient String Message recipient email address.
CiscoESA.AMPDetail.sender String Message sender email address.
CiscoESA.AMPDetail.showAMPDetails Boolean Whether to show AMP details.
CiscoESA.AMPDetail.allIcid Number ICIDs list.
CiscoESA.AMPDetail.headerFrom String Email header from.

Command example

!cisco-esa-message-amp-details-get message_ids=741,742,743 serial_number=test-test

Context Example

{
    "CiscoESA": {
        "AMPDetail": {
            "allIcid": [
                19599
            ],
            "ampDetails": [
                {
                    "description": "File reputation query initiating. File Name = bear.jpg, MID = 741, File Size = 325663 bytes, File Type = image/jpeg",
                    "timestamp": "2022-09-20T13:31:18Z"
                },
                {
                    "description": "Response received for file reputation query from Cache. File Name = bear.jpg, MID = 741, Disposition = FILE UNKNOWN, Malware = None, Analysis Score = 0, sha256 = 23a9113530549916cd5b410edee79cb5a0fc01233eb9051f9c882a2e7c3fbfbe, upload_action = Recommended to send the file for analysis, verdict_source = AMP, Suspected Malware Categories = None",
                    "timestamp": "2022-09-20T13:31:18Z"
                },
                {
                    "description": "File not uploaded for analysis. MID = 741 File SHA256[23a9113530549916cd5b410edee79cb5a0fc01233eb9051f9c882a2e7c3fbfbe] file mime[image/jpeg] Reason: The file type is not configured for analysis",
                    "lastEvent": true,
                    "timestamp": "2022-09-20T13:31:18Z"
                }
            ],
            "ampTgCategories": [],
            "attachments": [
                "bear.jpg"
            ],
            "direction": "incoming",
            "headerFrom": "test@test.com",
            "hostName": "(Name unresolved,  SN:test-test)",
            "messageSize": "439.26 (KB)",
            "messageStatus": "Quarantined by Multiple Engines",
            "mid": [
                741,
                742,
                743
            ],
            "midHeader": "<test@test.test.com>",
            "recipient": [
                "test@test.com"
            ],
            "sdrAge": "30 days (or greater)",
            "sdrCategory": "N/A",
            "sdrReputation": "Neutral",
            "sdrThreatLevels": "3",
            "sender": "test@test.com",
            "senderGroup": "ACCEPTLIST",
            "sendingHostSummary": {},
            "showAMPDetails": true,
            "smtpAuthId": "",
            "subject": "Fwd: test",
            "timestamp": "2022-09-20T13:31:15Z"
        }
    }
}

Human Readable Output

Message AMP Report Details

Found AMP details for message ID 741, 742, 743.

Mid All Icid Subject Sender Recipient Attachments Timestamp Message Size Message Status Direction Sender Group
741,
742,
743
19599 Fwd: test test@test.com test@test.com bear.jpg 2022-09-20T13:31:15Z 439.26 (KB) Quarantined by Multiple Engines incoming ACCEPTLIST

Message AMP Report Details Summary

Description Timestamp
File reputation query initiating. File Name = bear.jpg, MID = 741, File Size = 325663 bytes, File Type = image/jpeg 2022-09-20T13:31:18Z
Response received for file reputation query from Cache. File Name = bear.jpg, MID = 741, Disposition = FILE UNKNOWN, Malware = None, Analysis Score = 0, sha256 = 23a9113530549916cd5b410edee79cb5a0fc01233eb9051f9c882a2e7c3fbfbe, upload_action = Recommended to send the file for analysis, verdict_source = AMP, Suspected Malware Categories = None 2022-09-20T13:31:18Z
File not uploaded for analysis. MID = 741 File SHA256[23a9113530549916cd5b410edee79cb5a0fc01233eb9051f9c882a2e7c3fbfbe] file mime[image/jpeg] Reason: The file type is not configured for analysis 2022-09-20T13:31:18Z

cisco-esa-message-dlp-details-get


Get message DLP summary details.

Base Command

cisco-esa-message-dlp-details-get

Input

Argument Name Description Required
serial_number Email gateway serial number. Required
message_ids Message ID list. Required

Context Output

Path Type Description
CiscoESA.DLPDetail.direction String Message direction, incoming or outgoing.
CiscoESA.DLPDetail.smtpAuthId String SMTP authorization ID.
CiscoESA.DLPDetail.sdrAge String Sender domain reputation age.
CiscoESA.DLPDetail.sender String Message sender email address.
CiscoESA.DLPDetail.midHeader String Message ID header.
CiscoESA.DLPDetail.timestamp String Email message time.
CiscoESA.DLPDetail.sdrCategory String Sender domain reputation category.
CiscoESA.DLPDetail.hostName String Email gateway hostname.
CiscoESA.DLPDetail.mid Number Message ID.
CiscoESA.DLPDetail.attachments String Message attachments.
CiscoESA.DLPDetail.messageSize String Email message size.
CiscoESA.DLPDetail.dlpDetails.violationSeverity String DLP details violation severity.
CiscoESA.DLPDetail.dlpDetails.dlpMatchedContent.messagePartMatch.classifier String DLP matched content classifier.
CiscoESA.DLPDetail.dlpDetails.dlpMatchedContent.messagePartMatch.classifierMatch String DLP matched content classifier match.
CiscoESA.DLPDetail.dlpDetails.dlpMatchedContent.messagePart String DLP matched content message part.
CiscoESA.DLPDetail.dlpDetails.mid String DLP message ID.
CiscoESA.DLPDetail.dlpDetails.riskFactor Number DLP risk factor.
CiscoESA.DLPDetail.dlpDetails.dlpPolicy String DLP policy.
CiscoESA.DLPDetail.sdrThreatLevels String Sender domain reputation threat levels.
CiscoESA.DLPDetail.sdrReputation String Sender domain reputation.
CiscoESA.DLPDetail.messageStatus String Message delivery status.
CiscoESA.DLPDetail.allIcid Number ICIDs list.
CiscoESA.DLPDetail.senderGroup String Matched sender group.
CiscoESA.DLPDetail.recipient String Message recipient email address.
CiscoESA.DLPDetail.subject String Email message subject.
CiscoESA.DLPDetail.headerFrom String Email header from.

Command example

!cisco-esa-message-dlp-details-get message_ids=1131 serial_number=test-test

Context Example

{
    "CiscoESA": {
        "DLPDetail": {
            "allIcid": [
                20460
            ],
            "ampTgCategories": [],
            "attachments": [],
            "direction": "outgoing",
            "dlpDetails": {
                "dlpMatchedContent": [
                    {
                        "messagePart": "Message",
                        "messagePartMatch": [
                            {
                                "classifier": "Proper Names (US)",
                                "classifierMatch": [
                                    "Tim Testmane",
                                    "Albert Iorio",
                                    "Adriane Morrison",
                                    "Lisa Garrison",
                                    "Charles Testman",
                                    "Dan Example",
                                    "Christopher Diaz",
                                    "Marjorie Green",
                                    "Mark Testman",
                                    "Stacey Peacock",
                                    "Robert Aragon",
                                    "Thomas Conley"
                                ]
                            },
                            {
                                "classifier": "Personal Information (US)",
                                "classifierMatch": [
                                    "Test Test <test@test.com>\nSubject: DLP\n\nFirst and Last Name     SSN     Credit Card Number\nVisa MC AMEX\nRobert Aragon   489-36-8350     4929-3813-3266-4295\nAshley Borden   514-14-8905     5370-4638-8881-3020\nThomas Conley   690-05-5315     4916-4811-5814-8111\nSusan Davis     421-37-1396     4916-4034-9269-8783\nChristopher Diaz        458-02-6124     5299-1561-5689-1938\nRick Edwards    612-20-6832     5293-8502-0071-3058\nVictor Faulkner 300-62-3266     5548-0246-6336-5664\nLisa Garrison   660-03-8360     4539-5385-7425-5825\nMarjorie Green  213-46-8915     4916-9766-5240-6147\nMark Testman       449-48-3135     4556-0072-1294-7415\nJames Heard     559-81-1301     4532-4220-6922-9909\nAlbert Iorio    322-84-2281     4916-6734-7572-5015\nCharles Testman 646-44-9061     5218-0144-2703-9266\nTeresa Kaminski 465-73-5022     5399-0706-4128-0178\nTim Testmane        044-34-6954     5144-8691-2776-1108\nMonte Mceachern 477-36-0282     5527-1247-5046-7780\nAdriane Morrison        421-90-3440     4539-0031-3703-0728\nJerome Munsch   524-02-7657     5180-3807-3679-8221\nAgnes Nelson    205-52-0027     5413-4428-0145-0036\nLynette Oyola   587-03-2682     4532-9929-3036-9308\nStacey Peacock  687-05-8365     5495-8602-4508-6804\nJulie Renfro    751-01-2327"
                                ]
                            }
                        ]
                    }
                ],
                "dlpPolicy": "US HIPAA and HITECH (Low Threshold)",
                "mid": "1131",
                "riskFactor": 72,
                "violationSeverity": "HIGH"
            },
            "headerFrom": "test@test.com",
            "hostName": "(Name unresolved,  SN:test-test)",
            "messageSize": "29.67 (KB)",
            "messageStatus": "Delivered",
            "mid": [
                1131
            ],
            "midHeader": "<test@test.test.com>",
            "recipient": [
                "test@test.com"
            ],
            "sender": "test@test.com",
            "senderGroup": "RELAY_O365",
            "sendingHostSummary": {},
            "showDLPDetails": true,
            "smtpAuthId": "",
            "subject": "Fw: DLP",
            "timestamp": "2022-09-21T08:42:32Z"
        }
    }
}

Human Readable Output

Message DLP Report Details

Found DLP details for message ID 1131.

Mid All Icid Subject Sender Recipient Timestamp Message Size Message Status Direction Sender Group
1131 20460 Fw: DLP test@test.com test@test.com 2022-09-21T08:42:32Z 29.67 (KB) Delivered outgoing RELAY_O365

Message DLP Report Details Summary

Mid Violation Severity Risk Factor Dlp Policy
1131 HIGH 72 US HIPAA and HITECH (Low Threshold)

cisco-esa-message-url-details-get


Get message URL summary details.

Base Command

cisco-esa-message-url-details-get

Input

Argument Name Description Required
serial_number Email gateway serial number. Required
message_ids Message ID list. Required

Context Output

Path Type Description
CiscoESA.URLDetail.sdrAge String Sender domain reputation age.
CiscoESA.URLDetail.attachments String Message attachments.
CiscoESA.URLDetail.showURLDetails Boolean Whether to show URL event details.
CiscoESA.URLDetail.urlDetails.timestamp String URL event details timestamp.
CiscoESA.URLDetail.urlDetails.description String URL event details description.
CiscoESA.URLDetail.hostName String Email gateway hostname.
CiscoESA.URLDetail.direction String Message direction, incoming or outgoing.
CiscoESA.URLDetail.messageStatus String Message delivery status.
CiscoESA.URLDetail.senderGroup String Matched sender group.
CiscoESA.URLDetail.subject String Email message subject.
CiscoESA.URLDetail.sdrCategory String Sender domain reputation category.
CiscoESA.URLDetail.mid Number Message ID.
CiscoESA.URLDetail.smtpAuthId String SMTP authorization ID.
CiscoESA.URLDetail.midHeader String Message ID header.
CiscoESA.URLDetail.timestamp String Email message time.
CiscoESA.URLDetail.messageSize String Email message size.
CiscoESA.URLDetail.sdrThreatLevels String Sender domain reputation threat levels.
CiscoESA.URLDetail.sdrReputation String Sender domain reputation.
CiscoESA.URLDetail.recipient String Message recipient email address.
CiscoESA.URLDetail.sender String Message sender email address.
CiscoESA.URLDetail.allIcid Number ICIDs list.
CiscoESA.URLDetail.headerFrom String Email header from.

Command example

!cisco-esa-message-url-details-get message_ids=737,738,739 serial_number=test-test

Context Example

{
    "CiscoESA": {
        "URLDetail": {
            "allIcid": [
                19598
            ],
            "ampTgCategories": [],
            "attachments": [
                "bear.jpg"
            ],
            "direction": "incoming",
            "headerFrom": "test@test.com",
            "hostName": "(Name unresolved,  SN:test-test)",
            "messageSize": "439.25 (KB)",
            "messageStatus": "Quarantined by Multiple Engines",
            "mid": [
                737,
                738,
                739
            ],
            "midHeader": "<test@test.test.com>",
            "recipient": [
                "test@test.com"
            ],
            "sdrAge": "30 days (or greater)",
            "sdrCategory": "N/A",
            "sdrReputation": "Neutral",
            "sdrThreatLevels": "3",
            "sender": "test@test.com",
            "senderGroup": "ACCEPTLIST",
            "sendingHostSummary": {},
            "showURLDetails": true,
            "smtpAuthId": "",
            "subject": "Fwd: test",
            "timestamp": "2022-09-20T13:31:08Z",
            "urlDetails": [
                {
                    "description": "Message 737 URL: http://1.1.1.1:8080/, URL reputation: -6.8, Condition: URL Reputation Rule.",
                    "timestamp": "2022-09-20T13:31:12Z"
                },
                {
                    "description": "Message 737 URL: https://test.com/test/, URL reputation: -6.6, Condition: URL Reputation Rule.",
                    "timestamp": "2022-09-20T13:31:12Z"
                },
                {
                    "description": "Message 737 URL: http://1.1.1.1:8080, URL reputation: -6.8, Condition: URL Reputation Rule.",
                    "timestamp": "2022-09-20T13:31:12Z"
                },
                {
                    "description": "Message 737 rewritten URL u'http://1.1.1.1:8080'.",
                    "timestamp": "2022-09-20T13:31:12Z"
                },
                {
                    "description": "Message 737 rewritten URL u'https://test.com/test/'.",
                    "timestamp": "2022-09-20T13:31:12Z"
                },
                {
                    "description": "Message 737 rewritten URL u'http://1.1.1.1:8080/'.",
                    "timestamp": "2022-09-20T13:31:12Z"
                },
                {
                    "description": "Message 737 rewritten URL u'https://test.com/test/'.",
                    "timestamp": "2022-09-20T13:31:12Z"
                }
            ]
        }
    }
}

Human Readable Output

Message URL Report Details

Found URL details for message ID 737, 738, 739.

Mid All Icid Subject Sender Recipient Attachments Timestamp Message Size Message Status Direction Sender Group
737,
738,
739
19598 Fwd: test test@test.com test@test.com bear.jpg 2022-09-20T13:31:08Z 439.25 (KB) Quarantined by Multiple Engines incoming ACCEPTLIST

Message URL Report Details Summary

Description Timestamp
Message 737 URL: http://1.1.1.1:8080/, URL reputation: -6.8, Condition: URL Reputation Rule. 2022-09-20T13:31:12Z
Message 737 URL: https://test.com/test/, URL reputation: -6.6, Condition: URL Reputation Rule. 2022-09-20T13:31:12Z
Message 737 URL: http://1.1.1.1:8080, URL reputation: -6.8, Condition: URL Reputation Rule. 2022-09-20T13:31:12Z
Message 737 rewritten URL u’http://1.1.1.1:8080’. 2022-09-20T13:31:12Z
Message 737 rewritten URL u’https://test.com/test/’. 2022-09-20T13:31:12Z
Message 737 rewritten URL u’http://1.1.1.1:8080/’. 2022-09-20T13:31:12Z
Message 737 rewritten URL u’https://test.com/test/’. 2022-09-20T13:31:12Z

cisco-esa-report-get


Get statistics reports.
Note that each report type is compatible with different arguments.
Refer to Addendum for Cisco Secure Email Gateway (“Secure Email Reporting” sheet in the file), to view the dedicated arguments for each report type.
https://www.cisco.com/c/dam/en/us/td/docs/security/esa/esa14-0/api/AsyncOS-14-0-API-Addendum.xlsx

Base Command

cisco-esa-report-get

Input

Argument Name Description Required
report_type Report Type. Possible values are: mail_incoming_traffic_summary, reporting_system, mail_vof_threat_summary, mail_vof_specific_threat_summary, mail_amp_threat_summary. Default is mail_incoming_traffic_summary. Optional
custom_report_type Custom report type.
Specify this argument to get a report that does not exist in the report_type argument.
Optional
start_date Start date in ISO format or <number> <time unit>,
e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now.
Required
end_date End date in ISO format or <number> <time unit>,
e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now.
Required
order_by The attribute by which to order the data in the response. For example, orderBy=total_clean_recipients. Optional
order_dir The report sort order direction. Possible values are: asc, desc. Optional
top The number of records with the highest values to return. Optional
filter_value The filter value to search for. Optional
filter_by The filter field to use. Filter the data to be retrieved according to the filter property and value. Optional
filter_operator The filter operator. Filter the response data based on the value specified. Possible values are: begins_with, is. Optional

Context Output

Path Type Description
CiscoESA.Report.type String Report type.
CiscoESA.Report.resultSet Number Report results summary.

Command example

!cisco-esa-report-get start_date=2weeks end_date=now report_type=mail_incoming_traffic_summary

Context Example

{
    "CiscoESA": {
        "Report": {
            "resultSet": [
                {
                    "failed_dkim": 0
                },
                {
                    "total_graymail_recipients": 5
                },
                {
                    "detected_spam": 19
                },
                {
                    "malicious_url": 3
                },
                {
                    "total_spoofed_emails": 1
                },
                {
                    "verif_decrypt_success": 0
                },
                {
                    "detected_virus": 0
                },
                {
                    "threat_content_filter": 4
                },
                {
                    "blocked_invalid_recipient": 12
                },
                {
                    "blocked_sdr": 0
                },
                {
                    "marketing_mail": 4
                },
                {
                    "ims_spam_increment_over_case": 0
                },
                {
                    "total_mailbox_auto_remediated_recipients": 0
                },
                {
                    "detected_spam_certain": 15
                },
                {
                    "detected_spam_suspect": 4
                },
                {
                    "blocked_dmarc": 1
                },
                {
                    "total_threat_recipients": 1383
                },
                {
                    "total_recipients": 1567
                },
                {
                    "verif_decrypt_fail": 0
                },
                {
                    "detected_amp": 0
                },
                {
                    "bulk_mail": 1
                },
                {
                    "social_mail": 0
                },
                {
                    "total_clean_recipients": 179
                },
                {
                    "detected_virus_per_msg": 0
                },
                {
                    "failed_spf": 0
                },
                {
                    "blocked_reputation": 1345
                }
            ],
            "type": "mail_incoming_traffic_summary",
            "uuid": "6535f7b3-0d35-411b-ab76-42e27ea661ce"
        }
    }
}

Human Readable Output

Report type: mail_incoming_traffic_summary

Report UUID: 6535f7b3-0d35-411b-ab76-42e27ea661ce

Blocked Dmarc Blocked Invalid Recipient Blocked Reputation Blocked Sdr Bulk Mail Detected Amp Detected Spam Detected Spam Certain Detected Spam Suspect Detected Virus Detected Virus Per Msg Failed Dkim Failed Spf Ims Spam Increment Over Case Malicious Url Marketing Mail Social Mail Threat Content Filter Total Clean Recipients Total Graymail Recipients Total Mailbox Auto Remediated Recipients Total Recipients Total Spoofed Emails Total Threat Recipients Verif Decrypt Fail Verif Decrypt Success
1 12 1345 0 1 0 19 15 4 0 0 0 0 0 3 4 0 4 179 5 0 1567 1 1383 0 0

cisco-esa-dictionary-add


Add a new dictionary.

Base Command

cisco-esa-dictionary-add

Input

Argument Name Description Required
mode The cluster mode of the email gateway determines its configuration. If the cluster mode is set to ‘group’, specify a group_name. If the cluster mode is set to ‘machine’, specify a host_name. Possible values are: cluster, group, machine. Default is cluster. Optional
host_name Required when cluster mode is ‘machine’. Optional
group_name Required when cluster mode is ‘group’. Optional
dictionary_name The name of the dictionary for which to get information. This argument is optional. Optional
whole_words Indicates if the words need to be matched completely. Possible values are: True, False. Default value is True. Required
words A list of terms to add to a dictionary. The term can have a weight of (0-10) associated with it. If no weight is given, the default weight is taken as “1”.
A smart identifier can have an additional parameter - “prefix” associated with it. If no value is mentioned, no prefix is taken as default.
Example: [‘credit’,2,’prefix’],[‘aba’],[‘À term 1’].
Required
ignore_case_sensitive Indicates if the term that needs to be matched is case-sensitive. Possible values are: True, False. Default value is False. Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-dictionary-add dictionary_name=testing1 ignore_case_sensitive=False whole_words=False mode=cluster words=['*credit',2,'prefix'],['test2']

!cisco-esa-dictionary-add dictionary_name=testing2 words=['*credit',2,'prefix'],['test2']

Human Readable Output

test_dictionary was added successfully.

cisco-esa-dictionary-edit


Edit a dictionary.

Base Command

cisco-esa-dictionary-edit

Input

Argument Name Description Required
mode The cluster mode of the email gateway determines its configuration. If the cluster mode is set to ‘group’, specify a group_name. If the cluster mode is set to ‘machine’, specify a host_name. Possible values are: cluster, group, machine. Default is cluster. Optional
host_name Required when cluster mode is ‘machine’. Optional
group_name Required when cluster mode is ‘group’. Optional
dictionary_name The name of the dictionary for which to get information. This argument is optional. Optional
updated_name Specifies a new name for the dictionary to modify. Optional
whole_words Indicates if the words need to be matched completely. Possible values are: True, False. Default value is True. Optional
words A list of terms to add to a dictionary. The term can have a weight of (0-10) associated with it. If no weight is given, the default weight is taken as “1”.
A smart identifier can have an additional parameter - “prefix” associated with it. If no value is mentioned, no prefix is taken as default.
Example: [‘credit’,2,’prefix’],[‘aba’],[‘À term 1’].
Required
ignore_case_sensitive Indicates if the term that needs to be matched is case-sensitive. Possible values are: True, False. Default value is False. Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-dictionary-edit dictionary_name=testing1 words=['*credit',6,'prefix'],['test2']

Human Readable Output

test_dictionary has been successfully updated.

cisco-esa-dictionary-list


Retrieve information of all dictionaries or a specific configured dictionary and their list of words.

Base Command

cisco-esa-dictionary-list

Input

Argument Name Description Required
mode The cluster mode of the email gateway determines its configuration. If the cluster mode is set to ‘group’, please a group_name. If the cluster mode is set to ‘machine’, specify a host_name. Possible values are: cluster, group, machine. Default is cluster. Optional
host_name Required when cluster mode is ‘machine’. Optional
group_name Required when cluster mode is ‘group’. Optional
dictionary_name The name of the dictionary for which to get information. This argument is optional. Optional

Context Output

Path Type Description
CiscoESA.Dictionary.name String The name of the dictionary.
CiscoESA.Dictionary.encoding String The encoding format used for the dictionary.
CiscoESA.Dictionary.ignorecase Number Indicates whether the dictionary ignores case sensitivity (1 for true, 0 for false).
CiscoESA.Dictionary.words List The words in the dictionary.
CiscoESA.Dictionary.words_count.term_count Number The count of individual terms in the dictionary.
CiscoESA.Dictionary.words_count.smart_identifier_count Number The count of smart identifiers in the dictionary (special terms with additional metadata).
CiscoESA.Dictionary.wholewords Number Indicates whether the dictionary considers whole words only (1 for true, 0 for false).

Command example

!cisco-esa-dictionary-list mode=cluster dictionary_name=test

Human Readable Output

Information for Dictionary: testing

Name Words Ignore Case Whole Words Words Count Smart Identifier Count Encoding
testing [‘test6’, 1], [‘testing7’, 1], [‘noy’, 1] 1 1 term_count: 4 0 utf-8

cisco-esa-dictionary-words-delete


Delete existing words from a specific dictionary.

Base Command

cisco-esa-dictionary-words-delete

Input

Argument Name Description Required
mode The cluster mode of the email gateway determines its configuration. If the cluster mode is set to ‘group’, specify a group_name. If the cluster mode is set to ‘machine’, specify a host_name. Possible values are: cluster, group, machine. Default is cluster. Optional
host_name Required when cluster mode is ‘machine’. Optional
group_name Required when cluster mode is ‘group’. Optional
dictionary_name The name of the dictionary for which to get information. This argument is optional. Optional
words A list of terms that need to be deleted.
Example: *credit,aba,term.
Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-dictionary-words-delete dictionary_name=testing mode=cluster words=*credit

!cisco-esa-dictionary-words-delete dictionary_name=testing words=test1,test2

Human Readable Output

Words deleted successfully from test_dictionary.

cisco-esa-dictionary-words-add


Add words to a specific dictionary.

Base Command

cisco-esa-dictionary-words-add

Input

Argument Name Description Required
mode The cluster mode of the email gateway determines its configuration. If the cluster mode is set to ‘group’, specify a group_name. If the cluster mode is set to ‘machine’, specify a host_name. Possible values are: cluster, group, machine. Default is cluster. Optional
host_name Required when cluster mode is ‘machine’. Optional
group_name Required when cluster mode is ‘group’. Optional
dictionary_name The name of the dictionary for which to get information. This argument is optional. Optional
words A list of terms to add to a dictionary. The term can have a weight of (0-10) associated with it. If no weight is given, the default weight is taken as “1”.
A smart identifier can have an additional parameter - “prefix” associated with it. If no value is mentioned, no prefix is taken as default.
Example: [‘credit’,2,’prefix’],[‘aba’],[‘À term 1’].
Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-dictionary-words-add dictionary_name=testing words=['*ssn',2,'prefix'],['test3']

!cisco-esa-dictionary-words-add dictionary_name=testing1 mode=cluster words=['test1'],['testing2']

Human Readable Output

Added successfully to test_dictionary.

cisco-esa-dictionary-delete


Delete a dictionary.

Base Command

cisco-esa-dictionary-delete

Input

Argument Name Description Required
mode The cluster mode of the email gateway determines its configuration. If the cluster mode is set to ‘group’, specify a group_name. If the cluster mode is set to ‘machine’, specify a host_name. Possible values are: cluster, group, machine. Default is cluster. Optional
host_name Required when cluster mode is ‘machine’. Optional
group_name Required when cluster mode is ‘group’. Optional
dictionary_name The name of the dictionary for which to get information. This argument is optional. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-dictionary-delete dictionary_name=test mode=cluster

Human Readable Output

test_dictionary deleted successfully.

cisco-esa-dictionary-words-update


Modify words in a specified content dictionary. Unlike cisco-esa-dictionary-edit, which overrides the entire dictionary, this command adds new terms or updates existing ones without removing existing terms.

Base Command

cisco-esa-dictionary-words-update

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
dictionary_name The name of the dictionary to update with the specified words. Required
words A list of terms to update in the dictionary. The term can have a weight of (0-10) associated with it. If no weight is given, the default weight is taken as “1”.
A smart identifier can have an additional parameter - “prefix” associated with it. If no value is mentioned, no prefix is taken as default.
Example: [‘credit’,2,’prefix’],[‘aba’],[‘À term 1’].
Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-dictionary-words-update dictionary_name=test words=['a1',1]

Human Readable Output

Added successfully to test.

cisco-esa-url-list-delete


Delete one or more URL lists. You cannot delete lists that are in use.

Base Command

cisco-esa-url-list-delete

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
url_list_names A comma-separated list of URL List names to delete. Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-url-list-delete url_list_names=testurllist

Human Readable Output

URL List(s) testurllist were successfully deleted.

cisco-esa-url-list-create


Create a new URL list.

Base Command

cisco-esa-url-list-create

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
url_list_name The URL List name to create. Required
urls A comma-separated list of URLs to add to the list. For example, malicious.com,phishing.com. Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-url-list-create url_list_name=testurllist urls=test.com

Human Readable Output

testurllist was successfully created.

cisco-esa-pvo-quarantine-message-release


Release messages associated with a specific PVO quarantine rule. This command releases all messages that match the specified rule from quarantine.

Base Command

cisco-esa-pvo-quarantine-message-release

Input

Argument Name Description Required
rule_ids Comma-separated list of rule IDs to release messages from. Required
quarantine_type The quarantine rule type. Default is pvo. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-pvo-quarantine-message-release rule_ids=12

Human Readable Output

Messages were successfully released from rule(s): 12

cisco-esa-pvo-quarantine-list


List all PVO quarantine rules or retrieve a specific rule by ID. PVO rules determine how messages are quarantined based on policy violations, detected viruses, or outbreak conditions.

Base Command

cisco-esa-pvo-quarantine-list

Input

Argument Name Description Required
type The rule type to return. Default is pvo. Optional
limit The maximum number of records to return. Default is 50. Optional
all_results Whether to retrieve all the results by overriding the default limit. Default is false. Optional
rule_id The rule ID to retrieve. Optional
offset Specify an offset value to retrieve a subset of records starting with the offset value. Offset works with limit, which determines how many records to retrieve starting from the offset. Relevant with using rule_id and required when using limit. Optional
order_by The order of the results. Optional
order_direction The direction of the results. Optional

Context Output

Path Type Description
CiscoESA.QuarantineRule.number_of_messages Number The number of messages matching the rule.
CiscoESA.QuarantineRule.capacity String The storage capacity used by messages matching the rule, represented as a percentage.
CiscoESA.QuarantineRule.rule_id String The unique identifier or label for the quarantine rule.
CiscoESA.QuarantineRule.total_size String The total size of all messages matching the rule.
CiscoESA.QuarantineRule.rule_description String Description of the quarantine rule.
CiscoESA.QuarantineRule.average_message_size String The average size of the quarantined messages.
CiscoESA.QuarantineRule.rid Number The rule identifier associated with the quarantine entry.

Command example

!cisco-esa-pvo-quarantine-list limit=1

Context Example

{
    "CiscoESA": {
        "QuarantineRule": {
            "average_message_size": "320KB",
            "capacity": "0.0%",
            "number_of_messages": 6,
            "rid": 1,
            "rule_description": "N/A",
            "rule_id": "Malware: Malware",
            "total_size": "1.9MB"
        }
    }
}

Human Readable Output

PVO Quarantine Rule List

Rule Id Rule Description Number Of Messages Capacity Average Message Size
Malware: Malware N/A 6 0.0% 320KB

cisco-esa-incoming-policy-user-list


Retrieve user-defined sender/recipient entries from an incoming mail policy.

Base Command

cisco-esa-incoming-policy-user-list

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
policy_name The name of the policy to retrieve user entries. No helper command exists to retrieve policy names; you must provide this manually. Required
limit The maximum number of records to return. Default is 50. Optional
all_results Whether to retrieve all the results by overriding the default limit. Default is false. Optional

Context Output

Path Type Description
CiscoESA.IncomingMailPolicyUser.sender_config.sender.domain_entries String List of sender domain entries. Messages from these senders will match the policy.
CiscoESA.IncomingMailPolicyUser.receiver_config.operation String Logical operator used to evaluate receiver and receiver_not entries. Valid values are ‘and’ or ‘or’.
CiscoESA.IncomingMailPolicyUser.receiver_config.receiver.domain_entries String List of recipient domain entries. Messages sent to these recipients will match the policy.
CiscoESA.IncomingMailPolicyUser.receiver_config.receiver_not.domain_entries String List of recipient domain entries to exclude. Messages NOT sent to these recipients will match the policy. Only valid with the ‘and’ operator.

cisco-esa-url-list


Retrieve all URL lists or a specific list by name. URL lists define allowed or blocked domains for email content scanning and can be used in URL filtering configurations within mail policies.

Base Command

cisco-esa-url-list

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
url_list_name The name of the URL list to retrieve. Optional
limit The maximum number of records to return. Default is 50. Optional
all_results Whether to retrieve all the results by overriding the default limit. Default is false. Optional

Context Output

Path Type Description
CiscoESA.URLList.used_by String The policy names that using the list.
CiscoESA.URLList.urls_count Number The number of urls in the list.
CiscoESA.URLList.name String The name of the list.
CiscoESA.URLList.urls String The URLs in the list.

Command example

!cisco-esa-url-list limit=1

Context Example

{
    "CiscoESA": {
        "URLList": {
            "name": "test",
            "urls": [
                "blabla.com"
            ],
            "urls_count": 1,
            "used_by": "Incoming Content Filters"
        }
    }
}

Human Readable Output

URL List

Name Urls Urls Count Used By
test blabla.com 1 Incoming Content Filters

cisco-esa-message-remediation-details-get


Retrieve historical remediation actions taken on messages, such as message deletions or recalls. Includes batch initiator details, message status, delivery timestamps, and whether the message was read, supporting audit and compliance tracking for retroactive threat actions.

Base Command

cisco-esa-message-remediation-details-get

Input

Argument Name Description Required
injection_connection_id An Injection Connection ID (ICID). A numerical identifier for an individual SMTP connection to the system. Use cisco-esa-message-search to get message Injection Connection IDs. Consider to insert this parameter in case you don’t achive any results. Optional
message_ids Comma-separated list of message IDs. Use cisco-esa-message-search to get message IDs. Required
serial_number The unique serial number assigned to an email message. Use cisco-esa-message-search to get the message serial numer. Required
start_date Start date for the messages. Timestamp in ISO format or <number> <time unit>, e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now. Default is 1 month. Optional
end_date The end date for the messages. Timestamp in ISO format or , for example, 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now.. Timestamp in ISO format or <number> <time unit>, e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now. Default is now. Optional
limit The maximum number of records to return. Default is 50. Optional
all_results Whether to retrieve all the results by overriding the default limit. Default is false. Optional

Context Output

Path Type Description
CiscoESA.MessageRemediation.batch_details.b_init_username String Username that initiated the remediation batch.
CiscoESA.MessageRemediation.batch_details.mor_action String The remediation action type (e.g., Delete).
CiscoESA.MessageRemediation.batch_details.b_init_time Number Timestamp when the remediation batch was initiated (UNIX time).
CiscoESA.MessageRemediation.batch_details.batch_name String The name assigned to the remediation batch.
CiscoESA.MessageRemediation.batch_details.batch_desc String The description of the remediation batch.
CiscoESA.MessageRemediation.batch_details.b_init_source String The source that initiated the batch (e.g., ESA device name).
CiscoESA.MessageRemediation.message_details.delivered_at Number Timestamp when the message was delivered (UNIX time).
CiscoESA.MessageRemediation.message_details.mid String The message ID of the remediated email.
CiscoESA.MessageRemediation.message_details.from_email String Sender email address of the remediated message.
CiscoESA.MessageRemediation.message_details.recipient_email String Recipient email address of the remediated message.
CiscoESA.MessageRemediation.message_details.mor_status String The status of the remediation operation (for example, Success, Failed).
CiscoESA.MessageRemediation.message_details.msg_read String Indicates whether the message was read (0 = unread, 1 = read, or N/A).

Command example

!cisco-esa-message-remediation-details-get message_ids=123 injection_connection_id=123 serial_number=123 limit=1

Context Example

{
    "CiscoESA": {
        "MessageRemediation": {
            "batch_details": {
                "b_init_source": "ESA 117",
                "b_init_time": 1590646987,
                "b_init_username": "admin",
                "batch_desc": "N/A",
                "batch_name": "Re7",
                "mor_action": "Delete"
            },
            "message_details": [
                {
                    "delivered_at": 1584574165,
                    "from_email": "test@test.com",
                    "mid": "3",
                    "mor_status": "Success",
                    "msg_read": "0",
                    "recipient_email": "test@test.com"
                },
                {
                    "delivered_at": 1584574165,
                    "from_email": "test@test.com",
                    "mid": "3",
                    "mor_status": "Success",
                    "msg_read": "0",
                    "recipient_email": "test@test.com"
                }
            ]
        }
    }
}

Human Readable Output

Message Remediation Report Details Summary

Delivered _ At Mid From _ Email Recipient _ Email Mor _ Status Msg _ Read
1584574165 3 test@test.com test@test.com Success 0
1584574165 3 test@test.com test@test.com Success 0

cisco-esa-url-list-update


Update URLs in an existing URL list. This command overrides all the URL entries in the list.

Base Command

cisco-esa-url-list-update

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
url_list_name The URL List name to edit. Required
urls A comma-separated list of URLs to add to the list. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-url-list-update url_list_name=testurllist urls=test2.com

Human Readable Output

testurllist was successfully updated.

cisco-esa-file-hash-list


Retrieve all file hash lists or the contents of a specific list. File hash lists define allowed or blocked file signatures and can be used in Incoming Content Filter configurations to control file-based threats.

Base Command

cisco-esa-file-hash-list

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
file_hash_list_name The file hash list name to retrieve. Optional
limit The maximum number of records to return. Default is 50. Optional
all_results Whether to retrieve all the results by overriding the default limit. Default is false. Optional

Context Output

Path Type Description
CiscoESA.FileHashList.filehashes String The hashes in the file hash List
CiscoESA.FileHashList.description String The description of the file hash List
CiscoESA.FileHashList.list_type String The file hash list type.
CiscoESA.FileHashList.name String The file hash list name.
CiscoESA.FileHashList.filehashes_count String The number of hashes in the list.

Command example

!cisco-esa-file-hash-list limit=1

Context Example

{
    "CiscoESA": {
        "FileHashList": {
            "description": "",
            "filehashes": [
                "44444444444444444444444444444443",
                "44444444444444444444444444444444",
                "44444444444444444444444444444445"
            ],
            "filehashes_count": 3,
            "list_type": "any",
            "name": "test2"
        }
    }
}

Human Readable Output

File Hash List

Name Filehashes Filehashes Count List Type
test2 44444444444444444444444444444443,
44444444444444444444444444444444,
44444444444444444444444444444445
3 any

cisco-esa-incoming-policy-user-add


Add sender and recipient entries to an incoming mail policy.

Base Command

cisco-esa-incoming-policy-user-add

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
policy_name The name of the policy to add sender and recipient entry. Required
sender_domain_entries Comma-separated sender domain entries (for example, user@example.com, @example.com) used to match messages from these senders. Required only if sender_non_domain_entries is not provided. Cannot be used together with sender_non_domain_entries. Optional
sender_non_domain_entries Comma-separated sender domain entries to exclude. Used to match messages not from these senders. Required only if sender_domain_entries is not provided. Cannot be used together with sender_domain_entries. Optional
receiver_operation Specifies how to combine receiver matching conditions. Use and if receiver_not_domain_entries is provided. Use or if using ANY as a domain. Possible values are: and, or. Required
receiver_domain_entries Comma-separated recipient domain entries (for example, user@example.com, @example.com) used to match messages sent to these recipients. Required
receiver_not_domain_entries Comma-separated recipient domain entries to exclude. Used to match messages not sent to these recipients. Only allowed when receiver_operation is set to and. Optional

Context Output

There is no context output for this command.

cisco-esa-file-hash-update


Updates the provided file hash list. This command overrides all existing hash entries in the list.

Base Command

cisco-esa-file-hash-update

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
file_hash_list_name The name of the file hash list to update. . Required
filehashes A comma-separated string of file hash values to be added to the specified fle hash list. Each hash must have a valid format (for example, SHA256). Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-file-hash-update file_hash_list_name=testfilehash filehashes=2286f6ffea7d0e58dcb3ecfd874041b2

Human Readable Output

testfilehash was successfully updated.

cisco-esa-pvo-quarantine-message-delete


Delete messages associated with a specific PVO quarantine rule. This command deletes all messages that match the specified rule from quarantine.

Base Command

cisco-esa-pvo-quarantine-message-delete

Input

Argument Name Description Required
rule_ids A comma-separated list of rule IDs to release message from. Required
quarantine_type The quarantine rule type. Default is pvo. Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-pvo-quarantine-message-delete rule_ids=12

Human Readable Output

Messages were successfully deleted from rule(s): 12

cisco-esa-file-hash-create


Create a new file hash list with specified hash entries.

Base Command

cisco-esa-file-hash-create

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
file_hash_list_name The file hash list name to create. Required
filehashes A comma-separated list of hashes to add to the list. Required
description Short description for the hash list. Required
list_type The type of the hashes in the list. Possible values are: md5, sha256, any. Default is any. Required

Context Output

There is no context output for this command.

cisco-esa-incoming-policy-user-update


Update sender and recipient entries in an existing incoming mail policy. This command overrides all existing sender and recipient entries with the provided values.

Base Command

cisco-esa-incoming-policy-user-update

Input

Argument Name Description Required
mode Defines the configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, group to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
policy_name The name of the policy to edit all the sender and recipient entries. Required
sender_domain_entries Comma-separated sender domain entries (for example, user@example.com, @example.com) used to match messages from these senders. Required only if sender_non_domain_entries is not provided. Cannot be used together with sender_non_domain_entries. Optional
sender_non_domain_entries Comma-separated sender domain entries to exclude. Used to match messages not from these senders. Required only if sender_domain_entries is not provided. Cannot be used together with sender_domain_entries. Optional
receiver_operation Specifies how to combine receiver matching conditions. Use and if receiver_not_domain_entries is provided. Use or if using ANY as a domain. Possible values are: and, or. Required
receiver_domain_entries Comma-separated recipient domain entries (for example, user@example.com, @example.com) used to match messages sent to these recipients. Required
receiver_not_domain_entries Comma-separated recipient domain entries to exclude. Used to match messages not sent to these recipients. Only allowed when receiver_operation is set to and. Optional

Context Output

There is no context output for this command.

cisco-esa-message-connection-details-get


Retrieve metadata about the SMTP connection and transmission behavior of specified messages. Returns details such as SBRS (Sender Base Reputation Score), connection summary logs, and sender group classification to help understand how messages were handled at the connection layer.

Base Command

cisco-esa-message-connection-details-get

Input

Argument Name Description Required
injection_connection_id An Injection Connection ID (ICID). A numerical identifier for an individual SMTP connection to the system. Use cisco-esa-message-search to get message Injection Connection IDs. Consider to insert this parameter in case you don’t achive any results. Optional
message_ids Comma-separated list of message IDs. Use cisco-esa-message-search to get message IDs. Required
serial_number The unique serial number assigned to an email message. Use cisco-esa-message-search to get the message serial numer. Required
start_date Start date for the messages. Timestamp in ISO format or <number> <time unit>, e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now. Default is 1 month. Optional
end_date The end date for the messages. Timestamp in ISO format or <number> <time unit>, e.g., 2022-01-01T00:00:00.000Z, 12 hours, 7 days, 3 months, now. Default is now. Optional
limit The maximum number of records to return. Default is 50. Optional
all_results Whether to retrieve all the results by overriding the default limit. Default is false. Optional

Context Output

Path Type Description
CiscoESA.MessageConnection.sender_group String The sender group assigned to the connection (for example, RELAYLIST).
CiscoESA.MessageConnection.Message.timestamp String Timestamp of the connection summary event.
CiscoESA.MessageConnection.Message.description String Description of the connection summary event.
CiscoESA.MessageConnection.Message.last_event Boolean Indicates whether this is the final summary event.
CiscoESA.MessageConnection.sbrs String Sender Base Reputation Score (SBRS) of the sender IP.

Command example

!cisco-esa-message-connection-details-get message_ids=123 injection_connection_id=123 serial_number=123 limit=1

Context Example

{
    "CiscoESA": {
        "MessageConnection": {
            "Message": [
                {
                    "description": "ICID 19213 sender_group: RELAYLIST sender_ip: 1.11.11.1, \n                 sbrs: not enabled",
                    "last_event": false,
                    "timestamp": "16 Nov 2018 11:01:08 (GMT)"
                },
                {
                    "description": "Protocol SMTP interface Management  (IP 10.76.71.196) on \n                 incoming connection (ICID 19213) from sender IP 1.11.11.1. Reverse DNS\n                 host vm30bsd0199.com verified yes.",
                    "last_event": false,
                    "timestamp": "16 Nov 2018 11:01:08 (GMT)"
                }
            ],
            "sbrs": "not enabled",
            "sender_group": "RELAYLIST"
        }
    }
}

Human Readable Output

Message Connection Report Details Summary: Sender group: RELAYLIST

Timestamp Description
16 Nov 2018 11:01:08 (GMT) ICID 19213 sender_group: RELAYLIST sender_ip: 1.11.11.1,
sbrs: not enabled
16 Nov 2018 11:01:08 (GMT) Protocol SMTP interface Management (IP 10.76.71.196) on
incoming connection (ICID 19213) from sender IP 1.11.11.1. Reverse DNS
host vm30bsd0199.com verified yes.

cisco-esa-message-filter-list


Retrieve all message filters or a specific message filter by name. Message filters are evaluated early in the Work Queue, before security engines. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

Base Command

cisco-esa-message-filter-list

Input

Argument Name Description Required
mode The configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, ‘group’ to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
filter_name The name of a specific message filter to retrieve. When omitted, all filters in scope are returned. Optional
active Whether to return only active or only inactive filters. Ignored when filter_name is supplied. Possible values are: true, false. Optional
limit The maximum number of filters to return. Ignored when filter_name is supplied. Default is 50. Optional

Context Output

Path Type Description
CiscoESA.MessageFilter.name String The filter name.
CiscoESA.MessageFilter.active String Whether the filter is active (`“true”` / `“false”`).
CiscoESA.MessageFilter.valid String Whether Cisco AsyncOS parsed the filter as valid (`“true”` / `“false”`).
CiscoESA.MessageFilter.order Number The 1-based position in the execution order.
CiscoESA.MessageFilter.rules_and_actions String The full Cisco filter DSL body.
CiscoESA.MessageFilter.invalid_reason String The reason the filter is marked as invalid. Present only when `valid` is `“false”`.

Command example


#### Context Example

```json
{
    "CiscoESA": {
        "MessageFilter": [
            {
                "active": "true",
                "name": "URL_QUARANTINE_MALICIOUS",
                "order": 1,
                "rules_and_actions": "if (recv-listener == \"MailFlow\") AND (url-reputation(-10.00, -6.00 , \"bypass_urls\", 1, 1)) { quarantine(\"URL_MALICIOUS\"); }",
                "valid": "true"
            },
            {
                "active": "true",
                "name": "URL_REWRITE_SUSPICIOUS",
                "order": 2,
                "rules_and_actions": "if (recv-listener == \"MailFlow\") AND (url-reputation(-6.00, -5.60 , \"bypass_urls\", 0, 1)) { url-reputation-proxy-redirect(-6.00, -5.60,\"\",0); }",
                "valid": "true"
            },
            {
                "active": "false",
                "name": "BLOCKED_LIST_QUARANTINE",
                "order": 3,
                "rules_and_actions": "if recv-listener == \"MailFlow\" { quarantine(\"BLOCKED_LIST\"); }",
                "valid": "true"
            }
        ]
    }
}

Human Readable Output

Cisco ESA — Message Filters (mode: cluster)

Name Active Order Rules And Actions Validation Warning
URL_QUARANTINE_MALICIOUS true 1 if (recv-listener == “MailFlow”) AND (url-reputation(-10.00, -6.00 , “bypass_urls”, 1, 1)) { quarantine(“URL_MALICIOUS”); }  
URL_REWRITE_SUSPICIOUS true 2 if (recv-listener == “MailFlow”) AND (url-reputation(-6.00, -5.60 , “bypass_urls”, 0, 1)) { url-reputation-proxy-redirect(-6.00, -5.60,”“,0); }  
BLOCKED_LIST_QUARANTINE false 3 if recv-listener == “MailFlow” { quarantine(“BLOCKED_LIST”); }  

Command example

!cisco-esa-message-filter-list active=true limit=5

Context Example

{
    "CiscoESA": {
        "MessageFilter": [
            {
                "active": "true",
                "name": "URL_QUARANTINE_MALICIOUS",
                "order": 1,
                "rules_and_actions": "if (recv-listener == \"MailFlow\") AND (url-reputation(-10.00, -6.00 , \"bypass_urls\", 1, 1)) { quarantine(\"URL_MALICIOUS\"); }",
                "valid": "true"
            },
            {
                "active": "true",
                "name": "URL_REWRITE_SUSPICIOUS",
                "order": 2,
                "rules_and_actions": "if (recv-listener == \"MailFlow\") AND (url-reputation(-6.00, -5.60 , \"bypass_urls\", 0, 1)) { url-reputation-proxy-redirect(-6.00, -5.60,\"\",0); }",
                "valid": "true"
            }
        ]
    }
}

Human Readable Output

Cisco ESA — Message Filters (mode: cluster)

Name Active Order Rules And Actions  
URL_QUARANTINE_MALICIOUS true 1 if (recv-listener == “MailFlow”) AND (url-reputation(-10.00, -6.00 , “bypass_urls”, 1, 1)) { quarantine(“URL_MALICIOUS”); }  
URL_REWRITE_SUSPICIOUS true 2 if (recv-listener == “MailFlow”) AND (url-reputation(-6.00, -5.60 , “bypass_urls”, 0, 1)) { url-reputation-proxy-redirect(-6.00, -5.60,”“,0); }  

cisco-esa-message-filter-create


Create a new message filter. If the appliance accepts the filter but flags it as invalid (e.g. unknown listener/interface), the command returns success and surfaces the appliance warning in the human-readable output. For details on Cisco’s filter rule language, see “Using Message Filters to Enforce Email Policies” in the Cisco Secure Email Gateway 16.0 Admin Guide: https://www.cisco.com/c/en/us/td/docs/security/esa/esa16-0/user_guide/b_ESA_Admin_Guide_16-0/b_ESA_Admin_Guide_12_1_chapter_01000.html. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

Base Command

cisco-esa-message-filter-create

Input

Argument Name Description Required
mode The configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, ‘group’ to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
filter_name The name for the new filter. Required
rules_and_actions The full Cisco filter DSL: if (&lt;conditions&gt;) { &lt;actions&gt;; }. See the Cisco AsyncOS for Email Security Admin Guide for the DSL reference. Required
active Whether the filter should be active on creation. Possible values are: true, false. Default is true. Optional
order The 1-based position in the filter list. If omitted, or if the value exceeds the current number of filters, the filter is appended to the end of the list. Optional

Context Output

There is no context output for this command.

cisco-esa-message-filter-update


Update an existing message filter. Only the supplied fields (active, order) are sent — at least one must be provided. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

Base Command

cisco-esa-message-filter-update

Input

Argument Name Description Required
mode The configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, ‘group’ to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
filter_name The name of the filter to update. Required
active Whether to enable or disable the filter. Possible values are: true, false. Optional
order The new 1-based position in the filter list. Must be within the range of existing filter positions (1 to the current filter count). Optional

Context Output

There is no context output for this command.

Command example

!cisco-esa-message-filter-update filter_name=example_filter active=false

Human Readable Output

Filter example_filter was successfully updated.

Command example

!cisco-esa-message-filter-update filter_name=example_filter order=2

Human Readable Output

Filter example_filter was successfully updated.

cisco-esa-message-filter-delete


Delete a message filter. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

Base Command

cisco-esa-message-filter-delete

Input

Argument Name Description Required
mode The configuration level for applying changes on the email gateway. Set to ‘cluster’ to update the entire cluster, ‘group’ to target a specific group (requires group_name), or ‘machine’ to update a single machine (requires host_name). Possible values are: cluster, group, machine. Default is cluster. Optional
group_name The name of the group to target when ‘mode’ is set to group. This value is required only if mode is group. Optional
host_name The hostname of the machine to target when ‘mode’ is set to ‘machine’. This value is required only if mode is ‘machine’. Optional
filter_name The name of the filter to delete. Required

Context Output

There is no context output for this command.

Command example

!cisco-esa-message-filter-delete filter_name=example_filter

Human Readable Output

Filter example_filter was deleted successfully.

Troubleshooting

Filter created but marked invalid (unknown Listener)

Symptom

A cisco-esa-message-filter-create (or cisco-esa-message-filter-update) call completes without raising an error, but the human-readable output includes a warning from the appliance similar to:

Filter <FILTER_NAME> has been marked invalid for these reasons: Listener ‘<LISTENER_NAME>’ unknown;.

On the Cisco Email Security Appliance the filter exists, but it is flagged as invalid and will not run against mail flow.

Cause

The rules_and_actions value passed to the command references a Listener name (for example, recv-listener == "InboundMail") that does not exist on the target appliance. The appliance accepts the filter definition but refuses to activate it because it cannot resolve the Listener.

How to find the correct Listener name

The Listener is configured on the Cisco Email Security Appliance itself — not in Cortex XSOAR. Use either of the following on the appliance:

  • Appliance web UI: Navigate to Network → IP Interfaces and look at the Listener column to see the Listener names bound to each interface. Common values are IncomingMail, Default, Inbound, or Public.
  • Appliance CLI: Run listenerconfig, then use SETUP or DISPLAY to list the configured Listeners.

How to fix

  1. Delete the invalid filter:

    !cisco-esa-message-filter-delete filter_name=<FILTER_NAME>
    
  2. Re-create the filter using a Listener name that exists on the appliance. For example:

    !cisco-esa-message-filter-create filter_name=URL_QUARANTINE_MALICIOUS \
      rules_and_actions="if (recv-listener == \"MailFlow\") and (url-reputation(-10.00, -6.00, \"bypass_urls\", 1, 1)) { quarantine(\"URL_MALICIOUS\"); }" \
      active=true
    

For details on Cisco’s filter rule language, see Using Message Filters to Enforce Email Policies in the Cisco Secure Email Gateway 16.0 Admin Guide.

Configuration parameters

  • base_url — Server URL (required)
  • credentials — Username (required)
  • max_fetch — Maximum incidents per fetch
  • first_fetch — First fetch timestamp
  • filter_by — Filter by
  • filter_operator — Filter operator
  • filter_value — Filter value
  • recipient_filter_operator — Recipient filter operator
  • recipient_filter_value — Recipient filter value
  • jwt_token_expiration_period — Time to live for JWT session token (in minutes).
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • isFetch — Fetch incidents

Commands (41)

  • cisco-esa-dictionary-add

    Add a new dictionary.

  • cisco-esa-dictionary-delete

    Delete a dictionary.

  • cisco-esa-dictionary-edit

    Edit a dictionary.

  • cisco-esa-dictionary-list

    Retrieve information of all dictionaries or a specific configured dictionary and their list of words.

  • cisco-esa-dictionary-words-add

    Add words to a specific dictionary.

  • cisco-esa-dictionary-words-delete

    Delete existing words from specific dictionary.

  • cisco-esa-dictionary-words-update

    Modify words in a specified content dictionary. Unlike `cisco-esa-dictionary-edit`, which overrides the entire dictionary, this command adds new terms or updates existing ones without removing existing terms.

  • cisco-esa-file-hash-create

    Create a new file hash list with specified hash entries.

  • cisco-esa-file-hash-list

    Retrieve all file hash lists or the contents of a specific list. File hash lists define allowed or blocked file signatures and can be used in Incoming Content Filter configurations to control file-based threats.

  • cisco-esa-file-hash-update

    Updates the provided file hash list. This command overrides all existing hash entries in the list.

  • cisco-esa-incoming-policy-user-add

    Add sender and recipient entries to an incoming mail policy.

  • cisco-esa-incoming-policy-user-list

    Retrieve user-defined sender/recipient entries from an incoming mail policy.

  • cisco-esa-incoming-policy-user-update

    Update sender and recipient entries in an existing incoming mail policy. This command overrides all existing sender and recipient entries with the provided values.

  • cisco-esa-list-entry-add

    Add spam quarantine blocklist/safelist entry.

  • cisco-esa-list-entry-append

    Append spam quarantine blocklist/safelist entry.

  • cisco-esa-list-entry-delete

    Delete spam quarantine blocklist/safelist entry.

  • cisco-esa-list-entry-edit

    Edit spam quarantine blocklist/safelist entry. Using this command will override the existing value.

  • cisco-esa-list-entry-get

    Get spam quarantine blocklist/safelist entry.

  • cisco-esa-message-amp-details-get

    Retrieve AMP (Advanced Malware Protection) summary for specified email messages. Provides insight into file analysis, disposition changes, malware detection, and retrospective verdicts triggered by Cisco's Threat Grid or AMP engine for attachments in the email.

  • cisco-esa-message-connection-details-get

    Retrieve metadata about the SMTP connection and transmission behavior of specified messages. Returns details such as SBRS (Sender Base Reputation Score), connection summary logs, and sender group classification to help understand how messages were handled at the connection layer.

  • cisco-esa-message-details-get

    Retrieves metadata and tracking information for email messages processed by the ESA. Includes sender/recipient details, timestamps, message status for example, delivered, dropped), message size, AMP and DLP visibility flags, and applied mail policies. Useful for auditing and threat tracking.

  • cisco-esa-message-dlp-details-get

    Get message DLP summary details.

  • cisco-esa-message-filter-create

    Create a new message filter. If the appliance accepts the filter but flags it as invalid (e.g. unknown listener/interface), the command returns success and surfaces the appliance warning in the human-readable output. For details on Cisco's filter rule language, see "Using Message Filters to Enforce Email Policies" in the Cisco Secure Email Gateway 16.0 Admin Guide: https://www.cisco.com/c/en/us/td/docs/security/esa/esa16-0/user_guide/b_ESA_Admin_Guide_16-0/b_ESA_Admin_Guide_12_1_chapter_01000.html. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

  • cisco-esa-message-filter-delete

    Delete a message filter. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

  • cisco-esa-message-filter-list

    Retrieve all message filters or a specific message filter by name. Message filters are evaluated early in the Work Queue, before security engines. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

  • cisco-esa-message-filter-update

    Update an existing message filter. Only the supplied fields (`active`, `order`) are sent — at least one must be provided. This command manages Message Filters only. For more granular, per-policy control over message handling, use Content Filters in the Cisco AsyncOS web UI.

  • cisco-esa-message-remediation-details-get

    Retrieve historical remediation actions taken on messages, such as message deletions or recalls. Includes batch initiator details, message status, delivery timestamps, and whether the message was read, supporting audit and compliance tracking for retroactive threat actions.

  • cisco-esa-message-search

    Search tracking messages.

  • cisco-esa-message-url-details-get

    Retrieve URL summary details for specific email messages. This includes information about all URLs found within each message, such as rewritten URLs, timestamped access logs, and verdicts assigned by the email security engine.

  • cisco-esa-pvo-quarantine-list

    List all PVO quarantine rules or retrieve a specific rule by ID. PVO rules determine how messages are quarantined based on policy violations, detected viruses, or outbreak conditions.

  • cisco-esa-pvo-quarantine-message-delete

    Delete messages associated with a specific PVO quarantine rule. This command deletes all messages that match the specified rule from quarantine.

  • cisco-esa-pvo-quarantine-message-release

    Release messages associated with a specific PVO quarantine rule. This command releases all messages that match the specified rule from quarantine.

  • cisco-esa-report-get

    Get statistics reports. Note that each report type is compatible with different arguments. Refer to Addendum for Cisco Secure Email Gateway ("Secure Email Reporting" sheet in the file), to view the dedicated arguments for each report type. https://www.cisco.com/c/dam/en/us/td/docs/security/esa/esa14-0/api/AsyncOS-14-0-API-Addendum.xlsx

  • cisco-esa-spam-quarantine-message-delete

    Delete quarantine emails.

  • cisco-esa-spam-quarantine-message-get

    Get spam quarantine message details.

  • cisco-esa-spam-quarantine-message-release

    Release quarantine emails.

  • cisco-esa-spam-quarantine-message-search

    Search messages in the spam quarantine.

  • cisco-esa-url-list

    Retrieve all URL lists or a specific list by name. URL lists define allowed or blocked domains for email content scanning and can be used in URL filtering configurations within mail policies.

  • cisco-esa-url-list-create

    Create a new URL list.

  • cisco-esa-url-list-delete

    Delete one or more URL lists. You cannot delete lists that are in use.

  • cisco-esa-url-list-update

    Update URLs in an existing URL list. This command overrides all the URL entries in the list.

import json
import os
from typing import Any
from unittest.mock import patch

import pytest
from freezegun import freeze_time

"""MOCK PARAMETERS"""
CREDENTIALS = "credentials"


"""CONSTANTS"""
BASE_URL = "https://example.com/esa/api/v2.0"
USERNAME = "MOCK_USER"
PASSWORD = "XXX"
TOKEN = "XXX-XXXX"


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(os.path.join("test_data/outputs", file_name), encoding="utf-8") as mock_file:
        return json.loads(mock_file.read())


def mock_access_token(client):
    return TOKEN


@pytest.fixture(autouse=True)
@patch(
    "CiscoEmailSecurityApplianceIronPortV2.Client.handle_request_headers",
    mock_access_token,
)
def mock_client():
    """
    Mock client
    """
    from CiscoEmailSecurityApplianceIronPortV2 import Client

    return Client(BASE_URL, USERNAME, PASSWORD, verify=False, proxy=False)


""" TESTING INTEGRATION COMMANDS"""


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_outputs_len,expected_message_id",
    [
        (
            "spam_quarantine_message_search.json",
            {
                "start_date": "1 day",
                "end_date": "now",
                "limit": "3",
            },
            3,
            783,
        ),
        (
            "spam_quarantine_message_search.json",
            {
                "start_date": "2 weeks",
                "end_date": "1 day",
                "page": "2",
                "page_size": "3",
                "filter_by": "subject",
                "filter_operator": "contains",
                "filter_value": "test",
            },
            3,
            783,
        ),
    ],
)
def test_spam_quarantine_message_search_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_outputs_len: int,
    expected_message_id: int,
    requests_mock,
    mock_client,
):
    """
    Scenario: Spam quarantine message search.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-spam-quarantine-message-search command called.
    Then:
     - Ensure outputs prefix is correct.
     - Ensure number of items is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        spam_quarantine_message_search_command,
    )

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/quarantine/messages"
    requests_mock.get(url=url, json=mock_response)

    result = spam_quarantine_message_search_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.SpamQuarantineMessage"
    assert len(outputs) == expected_outputs_len
    assert outputs[0]["mid"] == expected_message_id


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_message_id",
    [
        (
            "spam_quarantine_message_get.json",
            {"message_id": 620},
            620,
        )
    ],
)
def test_spam_quarantine_message_get_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_message_id: int,
    requests_mock,
    mock_client,
):
    """
    Scenario: Spam quarantine message get.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-spam-quarantine-message-get command called.
    Then:
     - Ensure outputs prefix is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        spam_quarantine_message_get_command,
    )

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/quarantine/messages/details"
    requests_mock.get(url=url, json=mock_response)

    result = spam_quarantine_message_get_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.SpamQuarantineMessage"
    assert outputs["mid"] == expected_message_id


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_message",
    [
        (
            "spam_quarantine_message_release.json",
            {"message_ids": [50]},
            "Quarantined message 50 successfully released.",
        )
    ],
)
def test_spam_quarantine_message_release_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_message: str,
    requests_mock,
    mock_client,
):
    """
    Scenario: Spam quarantine message release.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-spam-quarantine-message-release command called.
    Then:
     - Ensure the human readable message is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        spam_quarantine_message_release_command,
    )

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/quarantine/messages"
    requests_mock.post(url=url, json=mock_response)

    result = spam_quarantine_message_release_command(mock_client, command_arguments)

    assert result[0].readable_output == expected_message


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_message_first_id,expected_message_second_id",
    [
        (
            "spam_quarantine_message_delete.json",
            {"message_ids": [100, 101]},
            "Quarantined message 100 successfully deleted.",
            "Quarantined message 101 successfully deleted.",
        ),
        (
            "spam_quarantine_message_delete_failed.json",
            {"message_ids": [120, 121]},
            "Quarantined message 120 not found.",
            "Quarantined message 121 not found.",
        ),
    ],
)
def test_spam_quarantine_message_delete_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_message_first_id: str,
    expected_message_second_id: str,
    requests_mock,
    mock_client,
):
    """
    Scenario: Spam quarantine message delete.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-spam-quarantine-message-delete command called.
    Then:
     - Ensure the human readable message is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        spam_quarantine_message_delete_command,
    )

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/quarantine/messages"
    requests_mock.delete(url=url, json=mock_response)

    result = spam_quarantine_message_delete_command(mock_client, command_arguments)

    assert result[0].readable_output == expected_message_first_id
    assert result[1].readable_output == expected_message_second_id


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_outputs_len,expected_recipient_address",
    [
        (
            "list_entry_get_blocklist.json",
            {
                "entry_type": "blocklist",
                "limit": "2",
            },
            2,
            "test@test.com",
        ),
        (
            "list_entry_get_safelist.json",
            {
                "entry_type": "safelist",
                "page": "2",
                "page_size": "1",
            },
            1,
            "test@test.com",
        ),
    ],
)
def test_list_entry_get_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_outputs_len: int,
    expected_recipient_address: str,
    requests_mock,
    mock_client,
):
    """
    Scenario: List entry get.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-list-entry-get command called.
    Then:
     - Ensure outputs prefix is correct.
     - Ensure number of items is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import list_entry_get_command

    mock_response = load_mock_response(response_file_name)
    entry_type = command_arguments.get("entry_type")
    url = f"{BASE_URL}/quarantine/{entry_type}"
    requests_mock.get(url=url, json=mock_response)

    result = list_entry_get_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == f"CiscoESA.ListEntry.{entry_type.title()}"
    assert len(outputs) == expected_outputs_len
    assert outputs[0]["recipientAddress"] == expected_recipient_address


@pytest.mark.parametrize(
    "command_arguments,expected_message",
    [
        (
            {
                "entry_type": "blocklist",
                "view_by": "recipient",
                "recipient_addresses": ["test@test.com"],
                "sender_list": ["t1@test.com", "t2@test.com"],
            },
            "Successfully added t1@test.com, t2@test.com senders to test@test.com recipients in blocklist.",
        )
    ],
)
def test_list_entry_add_command(command_arguments: dict[str, Any], expected_message: str, requests_mock, mock_client):
    """
    Scenario: List entry add.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-list-entry-add command called.
    Then:
     - Ensure the human readable message is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import list_entry_add_command

    entry_type = command_arguments.get("entry_type")
    url = f"{BASE_URL}/quarantine/{entry_type}"
    requests_mock.post(url=url, json={})

    result = list_entry_add_command(mock_client, command_arguments)

    assert result.readable_output == expected_message


@pytest.mark.parametrize(
    "command_arguments,expected_message",
    [
        (
            {
                "entry_type": "safelist",
                "view_by": "sender",
                "sender_addresses": ["test@test.com", "test2@test.com"],
                "recipient_list": ["t3@test.com", "t4@test.com"],
            },
            "Successfully appended t3@test.com, t4@test.com recipients to test@test.com, test2@test.com senders in safelist.",
        )
    ],
)
def test_list_entry_append_command(command_arguments: dict[str, Any], expected_message: str, requests_mock, mock_client):
    """
    Scenario: List entry append.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-list-entry-append command called.
    Then:
     - Ensure the human readable message is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import list_entry_append_command

    entry_type = command_arguments.get("entry_type")
    url = f"{BASE_URL}/quarantine/{entry_type}"
    requests_mock.post(url=url, json={})

    result = list_entry_append_command(mock_client, command_arguments)

    assert result.readable_output == expected_message


@pytest.mark.parametrize(
    "command_arguments,expected_message",
    [
        (
            {
                "entry_type": "safelist",
                "view_by": "sender",
                "sender_addresses": ["test@test.com", "test2@test.com"],
                "recipient_list": ["t3@test.com", "t4@test.com"],
            },
            "Successfully edited test@test.com, test2@test.com senders' recipients to t3@test.com, t4@test.com in safelist.",
        )
    ],
)
def test_list_entry_edit_command(command_arguments: dict[str, Any], expected_message: str, requests_mock, mock_client):
    """
    Scenario: List entry edit.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-list-entry-edit command called.
    Then:
     - Ensure the human readable message is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import list_entry_edit_command

    entry_type = command_arguments.get("entry_type")
    url = f"{BASE_URL}/quarantine/{entry_type}"
    requests_mock.post(url=url, json={})

    result = list_entry_edit_command(mock_client, command_arguments)

    assert result.readable_output == expected_message


@pytest.mark.parametrize(
    "command_arguments,expected_message",
    [
        (
            {
                "entry_type": "blocklist",
                "view_by": "recipient",
                "recipient_list": ["test@test.com"],
            },
            "Successfully deleted test@test.com recipients from blocklist.",
        )
    ],
)
def test_list_entry_delete_command(command_arguments: dict[str, Any], expected_message: str, requests_mock, mock_client):
    """
    Scenario: List entry delete.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-list-entry-delete command called.
    Then:
     - Ensure the human readable message is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import list_entry_delete_command

    entry_type = command_arguments.get("entry_type")
    url = f"{BASE_URL}/quarantine/{entry_type}"
    requests_mock.delete(url=url, json={})

    result = list_entry_delete_command(mock_client, command_arguments)

    assert result.readable_output == expected_message


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_outputs_len,expected_message_id,expected_recipients",
    [
        (
            "message_search.json",
            {
                "start_date": "1 week",
                "end_date": "now",
                "limit": "4",
            },
            4,
            [780],
            ["test@test.com"],
        ),
        (
            "message_search.json",
            {
                "start_date": "2 weeks",
                "end_date": "1 day",
                "page": "2",
                "page_size": "4",
                "recipient_filter_operator": "is",
                "recipient_filter_value": "test@test.com",
            },
            4,
            [780],
            ["test@test.com"],
        ),
    ],
)
def test_message_search_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_outputs_len: int,
    expected_message_id: list[int],
    expected_recipients: list[str],
    requests_mock,
    mock_client,
):
    """
    Scenario: Tracking message search.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-message-search command called.
    Then:
     - Ensure outputs prefix is correct.
     - Ensure number of items is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_search_command

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/message-tracking/messages"
    requests_mock.get(url=url, json=mock_response)

    result = message_search_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.Message"
    assert len(outputs) == expected_outputs_len
    assert outputs[0]["mid"] == expected_message_id
    assert outputs[1]["recipient"] == expected_recipients


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_message_id,expected_recipients,expected_summary_len",
    [
        (
            "message_details_get.json",
            {
                "serial_number": "TESTAAA",
                "message_ids": [765, 766, 767],
                "injection_connection_id": 23092,
            },
            [765, 766, 767],
            ["test@test.com"],
            5,
        )
    ],
)
def test_message_details_get_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_message_id: list[int],
    expected_recipients: list[str],
    expected_summary_len: int,
    requests_mock,
    mock_client,
):
    """
    Scenario: Message detail get.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-message-details-get command called.
    Then:
     - Ensure outputs prefix is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_details_get_command

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/message-tracking/details"
    requests_mock.get(url=url, json=mock_response)

    result = message_details_get_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.Message"
    assert outputs["mid"] == expected_message_id
    assert outputs["recipient"] == expected_recipients
    assert len(outputs["summary"]) == expected_summary_len


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_message_id,expected_recipients,expected_amp_summary_len",
    [
        (
            "message_amp_details_get.json",
            {
                "serial_number": "TESTAAA",
                "message_ids": [765, 766, 767],
            },
            [765, 766, 767],
            ["test@test.com"],
            3,
        )
    ],
)
def test_message_amp_details_get_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_message_id: list[int],
    expected_recipients: list[str],
    expected_amp_summary_len: int,
    requests_mock,
    mock_client,
):
    """
    Scenario: Message AMP details summary get.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-message-amp-details-get command called.
    Then:
     - Ensure outputs prefix is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_amp_details_get_command

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/message-tracking/amp-details"
    requests_mock.get(url=url, json=mock_response)

    result = message_amp_details_get_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.AMPDetail"
    assert outputs["mid"] == expected_message_id
    assert outputs["recipient"] == expected_recipients
    assert len(outputs["ampDetails"]) == expected_amp_summary_len


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_message_id,expected_recipients,expected_dlp_policy",
    [
        (
            "message_dlp_details_get.json",
            {
                "serial_number": "TESTAAA",
                "message_ids": [1131],
            },
            [1131],
            ["test@test.com"],
            "US HIPAA and HITECH (Low Threshold)",
        )
    ],
)
def test_message_dlp_details_get_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_message_id: list[int],
    expected_recipients: list[str],
    expected_dlp_policy: str,
    requests_mock,
    mock_client,
):
    """
    Scenario: Message dlp details summary get.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-message-dlp-details-get command called.
    Then:
     - Ensure outputs prefix is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_dlp_details_get_command

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/message-tracking/dlp-details"
    requests_mock.get(url=url, json=mock_response)

    result = message_dlp_details_get_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.DLPDetail"
    assert outputs["mid"] == expected_message_id
    assert outputs["recipient"] == expected_recipients
    assert outputs["dlpDetails"]["dlpPolicy"] == expected_dlp_policy


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_message_id,expected_recipients,expected_url_summary_len",
    [
        (
            "message_url_details_get.json",
            {
                "serial_number": "TESTAAA",
                "message_ids": [222, 223, 224],
            },
            [222, 223, 224],
            ["test@test.com"],
            7,
        )
    ],
)
def test_message_url_details_get_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_message_id: list[int],
    expected_recipients: list[str],
    expected_url_summary_len: int,
    requests_mock,
    mock_client,
):
    """
    Scenario: Message url details summary get.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-message-url-details-get command called.
    Then:
     - Ensure outputs prefix is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_url_details_get_command

    mock_response = load_mock_response(response_file_name)
    url = f"{BASE_URL}/message-tracking/url-details"
    requests_mock.get(url=url, json=mock_response)

    result = message_url_details_get_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.URLDetail"
    assert outputs["mid"] == expected_message_id
    assert outputs["recipient"] == expected_recipients
    assert len(outputs["urlDetails"]) == expected_url_summary_len


@pytest.mark.parametrize(
    "response_file_name,command_arguments,expected_type,expected_results_len",
    [
        (
            "report_get.json",
            {
                "report_type": "mail_incoming_traffic_summary",
                "start_date": "1 week",
                "end_date": "now",
            },
            "mail_incoming_traffic_summary",
            26,
        )
    ],
)
def test_message_report_get_command(
    response_file_name: str,
    command_arguments: dict[str, Any],
    expected_type: str,
    expected_results_len: int,
    requests_mock,
    mock_client,
):
    """
    Scenario: Report get.
    Given:
     - User has provided valid credentials.
     - User may provided pagination args.
     - User may Provided filtering arguments.
    When:
     - cisco-esa-report-get command called.
    Then:
     - Ensure outputs prefix is correct.
     - Validate outputs' fields.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import report_get_command

    mock_response = load_mock_response(response_file_name)
    url = f'{BASE_URL}/reporting/{command_arguments.get("report_type")}'
    requests_mock.get(url=url, json=mock_response)

    result = report_get_command(mock_client, command_arguments)
    outputs = result.outputs

    assert result.outputs_prefix == "CiscoESA.Report"
    assert outputs["type"] == expected_type
    assert len(outputs["resultSet"]) == expected_results_len


""" TESTING HELPER FUNCTIONS"""


@pytest.mark.parametrize(
    "number_list_argument,expected_result",
    [
        (
            "10,20,30",
            [10, 20, 30],
        )
    ],
)
def test_format_number_list_argument(number_list_argument: str, expected_result: list[int]):
    """
    Scenario: Format number list argument.
    Given:
     - User has provided number list argument.
    When:
     - format_number_list_argument function called.
    Then:
     - Ensure result is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import format_number_list_argument

    result = format_number_list_argument(number_list_argument)

    assert result == expected_result


@pytest.mark.parametrize(
    "custom_query_argument,expected_result",
    [
        (
            "test_key1=test_value1;test_key2=test_value2",
            {"test_key1": "test_value1", "test_key2": "test_value2"},
        )
    ],
)
def test_format_custom_query_args(custom_query_argument: str, expected_result: dict[str, Any]):
    """
    Scenario: Format custom query arguments for tracking message advanced filters.
    Given:
     - User has provided custom query argument.
    When:
     - format_custom_query_args function called.
    Then:
     - Ensure result is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import format_custom_query_args

    result = format_custom_query_args(custom_query_argument)

    assert result == expected_result


@pytest.mark.parametrize(
    "timestamp,output_format,expected_result",
    [
        ("07 Sep 2022 09:08:03 (GMT)", "%Y-%m-%dT%H:%M:%SZ", "2022-09-07T09:08:03Z"),
        (
            "24 Apr 2023 10:14:50 (GMT -05:00)",
            "%Y-%m-%dT%H:%M:00.000Z",
            "2023-04-24T15:14:00.000Z",
        ),
        (
            "24 Apr 2023 10:14:50 (GMT-06:00)",
            "%Y-%m-%dT%H:%M:%SZ",
            "2023-04-24T16:14:50Z",
        ),
        (
            "24 Apr 2023 10:14:50 (GMT +01:00)",
            "%Y-%m-%dT%H:%M:%SZ",
            "2023-04-24T09:14:50Z",
        ),
        (None, "%Y-%m-%dT%H:%M:%SZ", None),
    ],
)
def test_format_timestamp(timestamp, output_format, expected_result):
    """
    Given:
     - timestamps strings.
    When:
     - format_timestamp function called.
    Then:
     - Ensure result is correct.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import format_timestamp

    result = format_timestamp(timestamp, output_format)

    assert result == expected_result


data_test_fetch_incidents = [
    ({}, 0, {}),
    ({}, 1, {"last_minute_incident_ids": [1], "start_time": "2023-06-29T00:00:00Z"}),
    ({}, 2, {"last_minute_incident_ids": [1, 2], "start_time": "2023-06-29T00:00:00Z"}),
    (
        {"last_minute_incident_ids": [1, 2]},
        2,
        {"offset": 2, "last_minute_incident_ids": [1, 2]},
    ),
    (
        {"last_minute_incident_ids": [1, 2], "offset": 2},
        2,
        {"last_minute_incident_ids": [1, 2], "offset": 4},
    ),
    (
        {"last_minute_incident_ids": [3, 2], "offset": 2},
        1,
        {"last_minute_incident_ids": [3, 2, 1], "start_time": "2023-06-29T00:00:00Z"},
    ),
]


@pytest.mark.parametrize("previous_run, fetch_size, expected_last_run", data_test_fetch_incidents)
@freeze_time("2023-06-29T00:00:00Z")
def test_fetch_incidents(mock_client, mocker, previous_run, fetch_size, expected_last_run):
    from CiscoEmailSecurityApplianceIronPortV2 import fetch_incidents

    mocker.patch.object(
        mock_client,
        "spam_quarantine_message_search_request",
        return_value={"data": [{"attributes": {"date": "now"}, "mid": i + 1} for i in range(fetch_size)]},
    )
    incidents = [{"mid": i + 1} for i in range(fetch_size)]
    mocker.patch.object(
        mock_client,
        "spam_quarantine_message_get_request",
        new=lambda *_a, **_b: {"data": incidents.pop(0)},
    )

    _, last_run = fetch_incidents(
        mock_client,
        max_fetch=2,
        first_fetch="1 day",
        last_run=previous_run,
    )
    assert last_run == expected_last_run


def test_check_dictionary_mode_args():
    """
    Given:
     - A mode type ("group" or "machine") and respective arguments (`host_name` and `group_name`).
    When:
     - The check_dictionary_mode_args function is called with these arguments.
    Then:
     - Ensure the function correctly returns a tuple with the appropriate host name and group name based on the mode.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        check_dictionary_mode_args,
    )

    assert check_dictionary_mode_args("group", host_name="", group_name="group1") == (None, "group1")
    assert check_dictionary_mode_args("machine", host_name="host1", group_name="") == ("host1", None)


def test_convert_words_to_list():
    """
    Given:
     - A string representing a list of lists containing words, numbers, or both.
    When:
     - The convert_words_to_list function is called with the input string.
    Then:
     - Ensure the function correctly converts the input string to a list of lists, preserving the order and data types.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        convert_words_to_list,
    )

    assert convert_words_to_list("['test']") == [["test"]]
    assert convert_words_to_list("['test1'],['test2']") == [["test1"], ["test2"]]
    assert convert_words_to_list("['test1',5],['test2',7]") == [["test1", 5], ["test2", 7]]
    assert convert_words_to_list("['test1',5,'prefix'],['test2',7]") == [["test1", 5, "prefix"], ["test2", 7]]


def test_dictionary_list_command(mocker, mock_client):
    """
    Given:
     - A dictionary name as input.
    When:
     - The dictionary_list_command is called.
    Then:
     - Ensure the command returns the correct information about the dictionary.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        dictionary_list_command,
    )

    mock_response = {
        "data": [
            {
                "name": "example_dictionary",
                "encoding": "UTF-8",
                "ignorecase": 1,
                "words": ["example", "test", "demo"],
                "words_count": {
                    "term_count": 3,
                    "smart_identifier_count": 1,
                },
                "wholewords": 0,
            }
        ]
    }
    mocker.patch("CiscoEmailSecurityApplianceIronPortV2.Client.dictionary_list_request", return_value=mock_response)

    args = {"dictionary_name": "example_dictionary"}
    result = dictionary_list_command(mock_client, args)

    assert "Information for Dictionary: example_dictionary" in result.readable_output

    outputs = result.outputs
    assert outputs == mock_response["data"]

    dictionary_output = outputs[0]
    assert dictionary_output["name"] == "example_dictionary"
    assert dictionary_output["encoding"] == "UTF-8"
    assert dictionary_output["ignorecase"] == 1
    assert dictionary_output["words"] == ["example", "test", "demo"]
    assert dictionary_output["words_count"]["term_count"] == 3
    assert dictionary_output["words_count"]["smart_identifier_count"] == 1
    assert dictionary_output["wholewords"] == 0


def test_dictionary_add_command(mocker, mock_client):
    """
    Given:
     - A dictionary name and words to add.
    When:
     - The dictionary_add_command is called.
    Then:
     - Ensure the dictionary is added successfully with the correct output message.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        dictionary_add_command,
    )

    mocker.patch("CiscoEmailSecurityApplianceIronPortV2.Client.dictionary_add_request", return_value={"status": "success"})

    args = {"dictionary_name": "TestDict", "words": "[['word1'],['word2']]"}
    result = dictionary_add_command(mock_client, args)

    assert "TestDict was added successfully." in result.readable_output


def test_dictionary_edit_command(mocker, mock_client):
    """
    Given:
     - A dictionary name, updated name, and new words.
    When:
     - The dictionary_edit_command is called.
    Then:
     - Ensure the dictionary is updated successfully with the correct output message.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        dictionary_edit_command,
    )

    mocker.patch("CiscoEmailSecurityApplianceIronPortV2.Client.dictionary_edit_request", return_value={"status": "success"})

    args = {"dictionary_name": "TestDict", "updated_name": "NewTestDict", "words": "[['word1'],['word2']]"}
    result = dictionary_edit_command(mock_client, args)

    assert "TestDict has been successfully updated." in result.readable_output


def test_dictionary_delete_command(mocker, mock_client):
    """
    Given:
     - A dictionary name to delete.
    When:
     - The dictionary_delete_command is called.
    Then:
     - Ensure the dictionary is deleted successfully with the correct output message.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        dictionary_delete_command,
    )

    mocker.patch("CiscoEmailSecurityApplianceIronPortV2.Client.dictionary_delete_request", return_value={"status": "success"})

    args = {"dictionary_name": "TestDict"}
    result = dictionary_delete_command(mock_client, args)
    assert "TestDict deleted successfully." in result.readable_output


def test_dictionary_words_add_command(mocker, mock_client):
    """
    Given:
     - A dictionary name and words to add to it.
    When:
     - The dictionary_words_add_command is called.
    Then:
     - Ensure the words are added successfully with the correct output message.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        dictionary_words_add_command,
    )

    mocker.patch("CiscoEmailSecurityApplianceIronPortV2.Client.dictionary_words_add_request", return_value={"status": "success"})

    args = {"dictionary_name": "TestDict", "words": "['word1', 'word2']"}
    result = dictionary_words_add_command(mock_client, args)

    assert "Added successfully to TestDict." in result.readable_output


def test_dictionary_words_delete_command(mocker, mock_client):
    """
    Given:
     - A dictionary name and words to delete from it.
    When:
     - The dictionary_words_delete_command is called.
    Then:
     - Ensure the words are deleted successfully with the correct output message and result type.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import (
        dictionary_words_delete_command,
    )

    mocker.patch(
        "CiscoEmailSecurityApplianceIronPortV2.Client.dictionary_words_delete_request", return_value={"status": "success"}
    )

    args = {"dictionary_name": "TestDict", "words": "['word1', 'word2']"}
    result = dictionary_words_delete_command(mock_client, args)

    assert "Words deleted successfully from TestDict." in result.readable_output


def test_dictionary_words_update_command(mock_client, requests_mock):
    """
    Scenario: Retrieve all URL lists.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import dictionary_words_update_command

    args = {"dictionary_name": "TestDict", "words": "['*credit',6,'prefix'],['test2']"}
    url = f"{BASE_URL}/config/dictionaries/TestDict/words?device_type=esa&mode=cluster"

    mock_response = load_mock_response("updated_successfully.json")

    requests_mock.put(url=url, json=mock_response)

    result = dictionary_words_update_command(mock_client, args)
    assert "Added successfully to TestDict." in result.readable_output


def test_url_list_all_command(mock_client, requests_mock):
    """
    Scenario: Retrieve all URL lists.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import url_list_command

    args = {"mode": "cluster", "limit": "50", "all_results": False}
    url = f"{BASE_URL}/config/url_lists"

    mock_response = load_mock_response("url_list.json")
    requests_mock.get(url=url, json=mock_response)

    result = url_list_command(mock_client, args)

    assert result.outputs_prefix == "CiscoESA.URLList"
    assert isinstance(result.outputs, list)
    assert len(result.outputs) == 3
    assert result.outputs[0]["name"] == "urllisttest"


def test_url_list_single_command(mock_client, requests_mock):
    """
    Scenario: Retrieve a specific URL list by name.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import url_list_command

    args = {"mode": "cluster", "url_list_name": "BlockUrls"}
    url = f"{BASE_URL}/config/url_lists/BlockUrls"

    mock_response = load_mock_response("url_list_single.json")
    requests_mock.get(url=url, json=mock_response)

    result = url_list_command(mock_client, args)

    assert result.outputs_prefix == "CiscoESA.URLList"
    assert isinstance(result.outputs, dict)
    assert result.outputs["name"] == "urllisttest_single"


def test_url_list_create_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import url_list_create_command

    args = {"url_list_name": "TestList", "urls": "http://malicious.com,https://evil.com"}
    url = f"{BASE_URL}/config/url_lists/TestList"
    requests_mock.post(
        url=url,
        json=load_mock_response("added_successfully.json"),
    )

    result = url_list_create_command(mock_client, args)
    assert "TestList" in result.readable_output


def test_url_list_update_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import url_list_update_command

    args = {"url_list_name": "TestList", "urls": "https://update.com"}
    url = f"{BASE_URL}/config/url_lists/TestList"
    requests_mock.put(url=url, json=load_mock_response("updated_successfully.json"))

    result = url_list_update_command(mock_client, args)
    assert "TestList" in result.readable_output


def test_url_list_delete_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import url_list_delete_command

    args = {"url_list_names": "TestList1,TestList2"}
    url = f"{BASE_URL}/config/url_lists"
    requests_mock.delete(url=url, json={"data": {"message": "Deleted Successfully"}})

    result = url_list_delete_command(mock_client, args)
    assert "TestList1" in result.readable_output
    assert "TestList2" in result.readable_output


def test_file_hash_list_all_command(mock_client, requests_mock):
    """
    Scenario: Retrieve all File Hash lists.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import file_hash_list_command

    args = {"mode": "cluster"}
    url = f"{BASE_URL}/config/file_hash_lists"
    mock_response = load_mock_response("file_hash_list.json")
    requests_mock.get(url=url, json=mock_response)

    result = file_hash_list_command(mock_client, args)

    assert result.outputs_prefix == "CiscoESA.FileHashList"
    assert isinstance(result.outputs, list)
    assert len(result.outputs) == 2
    assert result.outputs[0]["name"] == "test"


def test_file_hash_list_single_command(mock_client, requests_mock):
    """
    Scenario: Retrieve a specific File Hash list by name.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import file_hash_list_command

    args = {"mode": "cluster", "file_hash_list_name": "MyList"}
    url = f"{BASE_URL}/config/file_hash_lists/MyList"
    mock_response = load_mock_response("file_hash_list_single.json")
    requests_mock.get(url=url, json=mock_response)

    result = file_hash_list_command(mock_client, args)

    assert result.outputs_prefix == "CiscoESA.FileHashList"
    assert isinstance(result.outputs, dict)
    assert result.outputs["name"] == "test"


def test_file_hash_create_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import file_hash_create_command

    args = {
        "file_hash_list_name": "MalwareHashes",
        "filehashes": "abcd1234,abcd5678",
        "description": "Test hash list",
        "list_type": "sha256",
    }

    url = f"{BASE_URL}/config/file_hash_lists/MalwareHashes"
    requests_mock.post(url=url, json=load_mock_response("added_successfully.json"))

    result = file_hash_create_command(mock_client, args)
    assert "MalwareHashes" in result.readable_output


def test_file_hash_update_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import file_hash_update_command

    args = {"file_hash_list_name": "MalwareHashes", "filehashes": "updated1234"}

    url = f"{BASE_URL}/config/file_hash_lists/MalwareHashes"
    requests_mock.put(url=url, json=load_mock_response("updated_successfully.json"))

    result = file_hash_update_command(mock_client, args)
    assert "MalwareHashes" in result.readable_output


@pytest.mark.parametrize(
    "response_file_name, args, expected_rule_id",
    [
        ("pvo_quarantine_list.json", {"type": "pvo"}, "Malware: Malware"),
    ],
)
def test_pvo_quarantine_list_command(response_file_name, args, expected_rule_id, mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import pvo_quarantine_list_command

    url = f"{BASE_URL}/quarantine/rules"
    mock_response = load_mock_response(response_file_name)
    requests_mock.get(url=url, json=mock_response)

    result = pvo_quarantine_list_command(mock_client, args)
    assert result.outputs_prefix == "CiscoESA.QuarantineRule"
    assert any(entry["rule_id"] == expected_rule_id for entry in result.outputs)


def test_pvo_quarantine_release_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import pvo_quarantine_release_command

    args = {"rule_ids": "pvo_123", "quarantine_type": "pvo"}
    url = f"{BASE_URL}/quarantine/rules"
    requests_mock.post(url=url, json={})

    result = pvo_quarantine_release_command(mock_client, args)
    assert "pvo_123" in result.readable_output


def test_pvo_quarantine_delete_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import pvo_quarantine_delete_command

    args = {"rule_ids": "pvo_123", "quarantine_type": "pvo"}
    url = f"{BASE_URL}/quarantine/rules"
    requests_mock.delete(url=url, json={})

    result = pvo_quarantine_delete_command(mock_client, args)
    assert "pvo_123" in result.readable_output


@pytest.mark.parametrize(
    "response_file_name, args, expected_sender",
    [
        (
            "user_entry_list.json",
            {"mode": "cluster", "policy_name": "Inbound Policy"},
            "yyo@[1.11.11.1]",
        )
    ],
)
def test_user_entry_list_command(response_file_name, args, expected_sender, mock_client, requests_mock):
    """
    Scenario: Retrieve user entry list for a given mail policy.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import user_entry_list_command

    url = f"{BASE_URL}/config/incoming_mail_policies/{args['policy_name']}/senders_and_recipients"
    mock_response = load_mock_response(response_file_name)
    requests_mock.get(url=url, json=mock_response)

    result = user_entry_list_command(mock_client, args)
    assert result.outputs_prefix == "CiscoESA.IncomingMailPolicyUser"

    # Look through sender domain entries explicitly
    found = any(
        expected_sender in entry.get("sender_config", {}).get("sender", {}).get("domain_entries", []) for entry in result.outputs
    )

    assert found, f"Expected sender {expected_sender} not found in outputs"


def test_user_entry_add_command(mock_client, requests_mock):
    """
    Scenario: Add sender/receiver entries to a mail policy.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import user_entry_add_command

    args = {
        "mode": "cluster",
        "policy_name": "Inbound Policy",
        "sender_domain_entries": "user@example.com",
        "receiver_operation": "and",
        "receiver_domain_entries": "target@example.com",
    }

    url = f"{BASE_URL}/config/incoming_mail_policies/{args['policy_name']}/senders_and_recipients"
    requests_mock.post(url=url, json=load_mock_response("added_successfully.json"))

    result = user_entry_add_command(mock_client, args)
    assert "Inbound Policy" in result.readable_output


def test_user_entry_update_command(mock_client, requests_mock):
    """
    Scenario: Update sender/receiver entries in a mail policy.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import user_entry_update_command

    args = {
        "mode": "cluster",
        "policy_name": "Inbound Policy",
        "sender_domain_entries": "updated@example.com",
        "receiver_operation": "or",
        "receiver_domain_entries": "any@example.com",
    }

    url = f"{BASE_URL}/config/incoming_mail_policies/{args['policy_name']}/senders_and_recipients"

    requests_mock.get(url=url, json=load_mock_response("user_entry_list.json"))
    requests_mock.put(url=url, json=load_mock_response("updated_successfully.json"))

    result = user_entry_update_command(mock_client, args)
    assert "Inbound Policy" in result.readable_output


def test_message_connection_details_get_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import message_connection_details_get_command

    args = {
        "serial_number": "TEST_SERIAL",
        "message_ids": "123,456",
        "injection_connection_id": "789",
    }

    url = f"{BASE_URL}/message-tracking/connection-details"
    mock_response = load_mock_response("message_connection_details_get.json")
    requests_mock.get(url, json=mock_response)

    result = message_connection_details_get_command(mock_client, args)

    assert result.outputs_prefix == "CiscoESA.MessageConnection"
    assert isinstance(result.outputs, dict)
    assert "Message" in result.outputs


def test_message_remediation_details_get_command(mock_client, requests_mock):
    from CiscoEmailSecurityApplianceIronPortV2 import message_remediation_details_get_command

    args = {"serial_number": "TEST_SERIAL", "message_ids": "999", "injection_connection_id": "555"}

    url = f"{BASE_URL}/message-tracking/remediation-details"
    mock_response = load_mock_response("message_remediation_details_get.json")
    requests_mock.get(url, json=mock_response)

    result = message_remediation_details_get_command(mock_client, args)

    assert result.outputs_prefix == "CiscoESA.MessageRemediation"
    assert isinstance(result.outputs, dict)
    assert "batch_details" in result.outputs
    assert "message_details" in result.outputs


def test_message_filter_list_command_all(requests_mock, mock_client):
    """
    Scenario: List all message filters (no filter_name).
    Given:
     - A valid client.
     - No filter_name argument.
    When:
     - cisco-esa-message-filter-list is called.
    Then:
     - Outputs prefix is CiscoESA.MessageFilter with replace_existing=True
       (full-array overwrite semantics; deleted filters drop out).
     - All rows from the fixture's data array are returned.
     - device_type=esa is passed in the underlying request.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_all.json")
    url = f"{BASE_URL}/config/message_filters"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {})

    assert result.outputs_prefix == "CiscoESA.MessageFilter"
    assert result.replace_existing is True
    assert result.outputs == mock_response["data"]
    assert len(result.outputs) == 3
    assert {row["name"] for row in result.outputs} == {"F1", "F2", "F3"}
    assert requests_mock.last_request.qs.get("device_type") == ["esa"]


def test_message_filter_list_command_specific(requests_mock, mock_client):
    """
    Scenario: List a specific filter by name.
    Given:
     - filter_name argument.
    When:
     - cisco-esa-message-filter-list is called.
    Then:
     - Endpoint includes the filter name.
     - Outputs prefix is CiscoESA.MessageFilter; replace_existing is False
       (single-row lookup must not wipe the collection).
     - Outputs contain the single returned row (no extra wrapping).
     - Title in HR mentions the filter name.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_specific.json")
    url = f"{BASE_URL}/config/message_filters/F1"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {"filter_name": "F1"})

    assert result.outputs_prefix == "CiscoESA.MessageFilter"
    assert result.replace_existing is False
    assert result.outputs == mock_response["data"]
    assert len(result.outputs) == 1
    assert result.outputs[0]["name"] == "F1"
    assert "F1" in result.readable_output


def test_message_filter_list_command_emits_overwrite_dt_form(requests_mock, mock_client):
    """
    Scenario: List-all emits the parent-level DT-predicate context shape so the
    server overwrites only the MessageFilter sub-key, preserving CiscoESA siblings.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_all.json")
    url = f"{BASE_URL}/config/message_filters"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {})
    entry_context = result.to_context()["EntryContext"]

    assert "CiscoESA(true)" in entry_context
    assert list(entry_context["CiscoESA(true)"].keys()) == ["MessageFilter"]
    assert entry_context["CiscoESA(true)"]["MessageFilter"] == mock_response["data"]
    assert "CiscoESA.MessageFilter(true)" not in entry_context


def test_message_filter_list_command_with_active_true_filter(requests_mock, mock_client):
    """
    Scenario: List filters with active=true filter applied client-side.
    Given:
     - Mixed-active fixture (2 active, 2 inactive rows).
     - active='true' argument.
    When:
     - cisco-esa-message-filter-list is called.
    Then:
     - Only rows where active == 'true' are returned in outputs.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_mixed_active.json")
    url = f"{BASE_URL}/config/message_filters"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {"active": "true"})

    assert len(result.outputs) == 2
    assert all(row["active"] == "true" for row in result.outputs)
    assert {row["name"] for row in result.outputs} == {"F1", "F3"}


def test_message_filter_list_command_with_active_false_filter(requests_mock, mock_client):
    """
    Scenario: List filters with active=false filter applied client-side.
    Given:
     - Mixed-active fixture (2 active, 2 inactive rows).
     - active='false' argument.
    When:
     - cisco-esa-message-filter-list is called.
    Then:
     - Only rows where active == 'false' are returned in outputs.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_mixed_active.json")
    url = f"{BASE_URL}/config/message_filters"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {"active": "false"})

    assert len(result.outputs) == 2
    assert all(row["active"] == "false" for row in result.outputs)
    assert {row["name"] for row in result.outputs} == {"F2", "F4"}


def test_message_filter_list_command_with_limit(requests_mock, mock_client):
    """
    Scenario: Truncate the list to the requested limit.
    Given:
     - A fixture with 3 rows.
     - limit=1 argument.
    When:
     - cisco-esa-message-filter-list is called.
    Then:
     - Only the first row is returned.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_all.json")
    url = f"{BASE_URL}/config/message_filters"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {"limit": "1"})

    assert len(result.outputs) == 1
    assert result.outputs[0]["name"] == "F1"


def test_message_filter_list_command_hr_includes_invalid_reason_column(requests_mock, mock_client):
    """
    Scenario: HR table includes the 'Validation Warning' column when at least one row has invalid_reason.
    Given:
     - A fixture where some rows include the invalid_reason key.
    When:
     - cisco-esa-message-filter-list is called.
    Then:
     - The 'Validation Warning' column appears in the readable output.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_all.json")
    url = f"{BASE_URL}/config/message_filters"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {})

    assert "Validation Warning" in result.readable_output


def test_message_filter_list_command_hr_omits_invalid_reason_column(requests_mock, mock_client):
    """
    Scenario: HR table omits the 'Validation Warning' column when no row has invalid_reason.
    Given:
     - A fixture where no rows include the invalid_reason key.
    When:
     - cisco-esa-message-filter-list is called.
    Then:
     - The 'Validation Warning' column does NOT appear in the readable output.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_list_command

    mock_response = load_mock_response("message_filter_list_no_invalid.json")
    url = f"{BASE_URL}/config/message_filters"
    requests_mock.get(url=url, json=mock_response)

    result = message_filter_list_command(mock_client, {})

    assert "Validation Warning" not in result.readable_output


def test_message_filter_create_command_happy_path(requests_mock, mock_client):
    """
    Scenario: Create a new message filter — success without warning.
    Given:
     - filter_name, rules_and_actions, active, order arguments.
    When:
     - cisco-esa-message-filter-create is called.
    Then:
     - HR confirms the filter name was added successfully.
     - No 'Warning:' segment appears in HR.
     - The request body includes rules_and_actions, active, and order (no None values).
     - device_type=esa is passed in the underlying request.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_create_command

    mock_response = load_mock_response("message_filter_create_response.json")
    url = f"{BASE_URL}/config/message_filters/my_filter"
    requests_mock.post(url=url, json=mock_response)

    args = {
        "filter_name": "my_filter",
        "rules_and_actions": "if (true) { skip-filters(); }",
        "active": "true",
        "order": "2",
    }
    result = message_filter_create_command(mock_client, args)

    assert "my_filter" in result.readable_output
    assert "added successfully" in result.readable_output.lower()
    assert "Warning:" not in result.readable_output

    sent_body = requests_mock.last_request.json()["data"]
    assert sent_body == {
        "rules_and_actions": "if (true) { skip-filters(); }",
        "active": "true",
        "order": 2,
    }
    assert requests_mock.last_request.qs.get("device_type") == ["esa"]


def test_message_filter_create_command_with_warning(requests_mock, mock_client):
    """
    Scenario: Create succeeds but the appliance returns meta.warning.
    Given:
     - filter_name and rules_and_actions arguments.
     - The API returns meta.warning.
    When:
     - cisco-esa-message-filter-create is called.
    Then:
     - The command does not raise.
     - HR contains both the success line and the warning text.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_create_command

    mock_response = load_mock_response("message_filter_create_warning.json")
    url = f"{BASE_URL}/config/message_filters/f3"
    requests_mock.post(url=url, json=mock_response)

    args = {
        "filter_name": "f3",
        "rules_and_actions": "if (true) { skip-filters(); }",
    }
    result = message_filter_create_command(mock_client, args)

    assert "f3 was added successfully" in result.readable_output
    assert "Warning:" in result.readable_output
    assert mock_response["meta"]["warning"] in result.readable_output


def test_message_filter_create_command_body_drops_none_order(requests_mock, mock_client):
    """
    Scenario: Optional `order` is omitted by the user.
    Given:
     - filter_name + rules_and_actions only (no order).
    When:
     - cisco-esa-message-filter-create is called.
    Then:
     - The request body contains rules_and_actions and active but NOT 'order' (None dropped by assign_params).
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_create_command

    mock_response = load_mock_response("message_filter_create_response.json")
    url = f"{BASE_URL}/config/message_filters/my_filter"
    requests_mock.post(url=url, json=mock_response)

    args = {
        "filter_name": "my_filter",
        "rules_and_actions": "if (true) { skip-filters(); }",
    }
    message_filter_create_command(mock_client, args)

    sent_body = requests_mock.last_request.json()["data"]
    assert "order" not in sent_body
    assert sent_body.get("rules_and_actions") == "if (true) { skip-filters(); }"
    assert sent_body.get("active") == "true"


def test_message_filter_update_command_happy_path_only_active(requests_mock, mock_client):
    """
    Scenario: Update a filter providing only `active` (no rules_and_actions, no order).
    Given:
     - filter_name and active arguments only.
    When:
     - cisco-esa-message-filter-update is called.
    Then:
     - HR confirms the filter was successfully updated.
     - The request body contains only the 'active' key.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_update_command

    mock_response = load_mock_response("message_filter_update_response.json")
    url = f"{BASE_URL}/config/message_filters/my_filter"
    requests_mock.put(url=url, json=mock_response)

    args = {"filter_name": "my_filter", "active": "false"}
    result = message_filter_update_command(mock_client, args)

    assert "my_filter was successfully updated" in result.readable_output
    sent_body = requests_mock.last_request.json()["data"]
    assert sent_body == {"active": "false"}


def test_message_filter_update_command_no_updatable_fields_raises(mock_client):
    """
    Scenario: Update is called without any of active/order/rules_and_actions.
    Given:
     - filter_name only.
    When:
     - cisco-esa-message-filter-update is called.
    Then:
     - DemistoException is raised.
    """
    from CommonServerPython import DemistoException

    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_update_command

    with pytest.raises(DemistoException):
        message_filter_update_command(mock_client, {"filter_name": "my_filter"})


def test_message_filter_delete_command_happy_path(requests_mock, mock_client):
    """
    Scenario: Delete a message filter.
    Given:
     - filter_name argument.
    When:
     - cisco-esa-message-filter-delete is called.
    Then:
     - HR confirms deletion of the named filter.
     - device_type=esa is passed in the underlying request.
    """
    from CiscoEmailSecurityApplianceIronPortV2 import message_filter_delete_command

    mock_response = load_mock_response("message_filter_delete_response.json")
    url = f"{BASE_URL}/config/message_filters/F1"
    requests_mock.delete(url=url, json=mock_response)

    result = message_filter_delete_command(mock_client, {"filter_name": "F1"})

    assert "F1 was deleted successfully" in result.readable_output
    assert requests_mock.last_request.qs.get("device_type") == ["esa"]