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
| ID | CiscoESA |
|---|---|
| Provider | Cisco Systems |
| Category | Network Security |
| From Version | 6.5.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix 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.5Quarantined 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,
74319599 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,
73919598 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)
testurllistwere 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
testurllistwas 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
testurllistwas 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,
444444444444444444444444444444453 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
testfilehashwas 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 enabled16 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
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 (<conditions>) { <actions>; }. 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, orPublic. - Appliance CLI: Run
listenerconfig, then useSETUPorDISPLAYto list the configured Listeners.
How to fix
-
Delete the invalid filter:
!cisco-esa-message-filter-delete filter_name=<FILTER_NAME> -
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 fetchfirst_fetch— First fetch timestampfilter_by— Filter byfilter_operator— Filter operatorfilter_value— Filter valuerecipient_filter_operator— Recipient filter operatorrecipient_filter_value— Recipient filter valuejwt_token_expiration_period— Time to live for JWT session token (in minutes).proxy— Use system proxy settingsinsecure— Trust any certificate (not secure)incidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalisFetch— Fetch incidents
Commands (41)
-
cisco-esa-dictionary-addAdd a new dictionary.
-
cisco-esa-dictionary-deleteDelete a dictionary.
-
cisco-esa-dictionary-editEdit a dictionary.
-
cisco-esa-dictionary-listRetrieve information of all dictionaries or a specific configured dictionary and their list of words.
-
cisco-esa-dictionary-words-addAdd words to a specific dictionary.
-
cisco-esa-dictionary-words-deleteDelete existing words from specific dictionary.
-
cisco-esa-dictionary-words-updateModify 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-createCreate a new file hash list with specified hash entries.
-
cisco-esa-file-hash-listRetrieve 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-updateUpdates the provided file hash list. This command overrides all existing hash entries in the list.
-
cisco-esa-incoming-policy-user-addAdd sender and recipient entries to an incoming mail policy.
-
cisco-esa-incoming-policy-user-listRetrieve user-defined sender/recipient entries from an incoming mail policy.
-
cisco-esa-incoming-policy-user-updateUpdate 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-addAdd spam quarantine blocklist/safelist entry.
-
cisco-esa-list-entry-appendAppend spam quarantine blocklist/safelist entry.
-
cisco-esa-list-entry-deleteDelete spam quarantine blocklist/safelist entry.
-
cisco-esa-list-entry-editEdit spam quarantine blocklist/safelist entry. Using this command will override the existing value.
-
cisco-esa-list-entry-getGet spam quarantine blocklist/safelist entry.
-
cisco-esa-message-amp-details-getRetrieve 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-getRetrieve 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-getRetrieves 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-getGet message DLP summary details.
-
cisco-esa-message-filter-createCreate 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-deleteDelete 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-listRetrieve 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-updateUpdate 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-getRetrieve 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-searchSearch tracking messages.
-
cisco-esa-message-url-details-getRetrieve 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-listList 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-deleteDelete 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-releaseRelease messages associated with a specific PVO quarantine rule. This command releases all messages that match the specified rule from quarantine.
-
cisco-esa-report-getGet 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-deleteDelete quarantine emails.
-
cisco-esa-spam-quarantine-message-getGet spam quarantine message details.
-
cisco-esa-spam-quarantine-message-releaseRelease quarantine emails.
-
cisco-esa-spam-quarantine-message-searchSearch messages in the spam quarantine.
-
cisco-esa-url-listRetrieve 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-createCreate a new URL list.
-
cisco-esa-url-list-deleteDelete one or more URL lists. You cannot delete lists that are in use.
-
cisco-esa-url-list-updateUpdate URLs in an existing URL list. This command overrides all the URL entries in the list.
import ast import uuid from collections.abc import Callable from typing import Any import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 DEFAULT_FETCH = 50 TIMESTAMP_FORMAT = "%d %b %Y %H:%M:%S (%Z +00:00)" QUARANTINE_TIMESTAMP_FORMAT = "%d %b %Y %H:%M (%Z +00:00)" DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" CISCO_TIME_FORMAT = "%Y-%m-%dT%H:%M:00.000Z" CISCO_REPORTING_TIME_FORMAT = "%Y-%m-%dT%H:00:00.000Z" MIN_PAGE_NUMBER = 1 MIN_LIMIT = 1 MIN_PAGE_SIZE = 1 MAX_PAGE_SIZE = 100 REQUEST_MAX_PULL = 100 QUARANTINE_TYPE = "spam" VIEW_ACTION = "view" RELEASE_ACTION = "release" ADD_ACTION = "add" APPEND_ACTION = "append" EDIT_ACTION = "edit" DEFAULT_MODE_DICTIONARIES = "cluster" class Client(BaseClient): """Client class to interact with Cisco ESA API.""" def __init__( self, server_url: str, username: str, password: str, verify: bool, proxy: bool, jwt_token_expiration_period: int = 30, ): super().__init__(base_url=server_url, headers={}, verify=verify, proxy=proxy) self.username = username self.password = password self.jwt_token_expiration_period = jwt_token_expiration_period self.handle_request_headers() def handle_request_headers(self, force_retrieve_jwt: bool = False): """Retrieve and save to integration context JWT token for authorized client class API requests.""" integration_context = get_integration_context() jwt_token = integration_context.get("jwt_token") jwt_token_issued_time = integration_context.get("jwt_token_issued_time", 0.0) current_time = datetime.now().timestamp() next_refresh = ( datetime.fromtimestamp(jwt_token_issued_time) + timedelta(minutes=self.jwt_token_expiration_period - 0.2) ).timestamp() if force_retrieve_jwt or not jwt_token or current_time > next_refresh: jwt_token = self.retrieve_jwt_token() set_integration_context({"jwt_token": jwt_token, "jwt_token_issued_time": current_time}) self._headers["jwtToken"] = jwt_token def retrieve_jwt_token(self) -> str: """ Retrieve JWT token from Cisco ESA. Returns: str: JWT token from Cisco ESA. """ data = { "data": { "userName": b64_encode(self.username), "passphrase": b64_encode(self.password), } } try: response = super()._http_request("POST", "login", json_data=data) return dict_safe_get(response, ["data", "jwtToken"]) except DemistoException as e: if e.res.status_code == 401: raise Exception("Authorization Error: make sure username and password are set correctly.") from e raise e def _http_request(self, *args, **kwargs): try: return super()._http_request(*args, **kwargs) except DemistoException as e: if e.res.status_code == 401: self._session.cookies.clear() self.handle_request_headers(force_retrieve_jwt=True) return super()._http_request(*args, **kwargs) raise e def spam_quarantine_message_search_request( self, quarantine_type: str, start_date: str, end_date: str, offset: int, limit: int, filter_by: str = None, filter_operator: str = None, filter_value: str = None, recipient_filter_operator: str = None, recipient_filter_value: str = None, order_by: str = None, order_dir: str = None, ) -> dict[str, Any]: """ Search spam quarantine messages. Args: quarantine_type (str): Quarantine type. start_date (str): Start date in ISO format. end_date (str): End date in ISO format. offset (int): Offset of results to skip. limit (int): Limit of results to retrieve. filter_by (str, Optional): Filter by field. Required if filter_operator/filter_value are specified. Defaults to None. filter_operator (str, Optional): Filter operator. Required if filter_by/filter_value are specified. Defaults to None. filter_value (str, Optional): Filter value. Required if filter_by/filter_operator are specified. Defaults to None. recipient_filter_operator (str, Optional): Recipient filter operator. Required if recipient_filter_value is specified. Defaults to None. recipient_filter_value (str, Optional): Recipient address filter. Required if recipient_filter_operator is specified. Defaults to None. order_by (str, Optional): Results order by field. Required if order_dir is specified. Defaults to None. order_dir (str, Optional): Results order direction. Required if order_by is specified. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( quarantineType=quarantine_type, startDate=start_date, endDate=end_date, filterBy=filter_by, filterOperator=filter_operator, filterValue=filter_value, envelopeRecipientFilterOperator=recipient_filter_operator, envelopeRecipientFilterValue=recipient_filter_value, offset=offset, limit=limit, orderBy=order_by, orderDir=order_dir, ) return self._http_request("GET", "quarantine/messages", params=params) def spam_quarantine_message_get_request(self, quarantine_type: str, message_id: str) -> dict[str, Any]: """ Get spam quarantine message. Args: quarantine_type (str): Quarantine Type. message_id (str): Message ID. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params(quarantineType=quarantine_type, mid=message_id) return self._http_request("GET", "quarantine/messages/details", params=params) def spam_quarantine_message_release_request( self, action: str, quarantine_type: str, message_ids: list[int] ) -> dict[str, Any]: """ Release spam quarantine message. Args: action (str): Release action. quarantine_type (str): Quarantine type. message_ids (List[int]): Message IDs list. Returns: Dict[str, Any]: API response from Cisco ESA. """ data = assign_params(action=action, mids=message_ids, quarantineType=quarantine_type) return self._http_request("POST", "quarantine/messages", json_data=data) def spam_quarantine_message_delete_request(self, quarantine_type: str, message_ids: list[int]) -> dict[str, Any]: """ Delete spam quarantine message. Args: quarantine_type (str): Quarantine type. message_ids (List[int]): Message IDs list. Returns: Dict[str, Any]: API response from Cisco ESA. """ data = assign_params(mids=message_ids, quarantineType=quarantine_type) return self._http_request("DELETE", "quarantine/messages", json_data=data) def list_entry_get_request( self, entry_type: str, action: str, limit: int, offset: int, quarantine_type: str, view_by: str, order_by: str = None, order_dir: str = None, search: str = None, ) -> dict[str, Any]: """ List spam quarantine blocklist/safelist. Args: entry_type (str): Blocklist/Safelist list type. action (str): List action. limit (int): Limit of results to retrieve. offset (int): Offset of results to skip. quarantine_type (str): Quarantine type. view_by (str): View list entry results by recipient/sender. order_by (str, Optional): Results order by field. Required if order_dir is specified. Defaults to None. order_dir (str, Optional): Results order direction. Required if order_by is specified. Defaults to None. search (str, Optional): Search for results in blocklist/safelist. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( action=action, limit=limit, offset=offset, orderBy=order_by, orderDir=order_dir, quarantineType=quarantine_type, viewBy=view_by, search=search, ) return self._http_request("GET", f"quarantine/{entry_type}", params=params) def list_entry_add_request( self, entry_type: str, quarantine_type: str, action: str, view_by: str, recipient_addresses: list[str] = None, sender_list: list[str] = None, sender_addresses: list[str] = None, recipient_list: list[str] = None, ) -> dict[str, Any]: """ Add spam quarantine blocklist/safelist entries. Args: entry_type (str): Blocklist/Safelist list type. quarantine_type (str): Quarantine type. action (str): Add action. view_by (str): Add list entry results by recipient/sender. recipient_addresses (List[str], Optional): Recipient addresses list to add. Defaults to None. sender_list (List[str], Optional): Sender addresses list to add. Defaults to None. sender_addresses (List[str], Optional): Sender addresses list to add. Defaults to None. recipient_list (List[str], Optional): Recipient addresses list to add. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ data = assign_params( action=action, quarantineType=quarantine_type, viewBy=view_by, recipientAddresses=recipient_addresses, senderAddresses=sender_addresses, recipientList=recipient_list, senderList=sender_list, ) return self._http_request("POST", f"quarantine/{entry_type}", json_data=data) def list_entry_append_request( self, entry_type: str, quarantine_type: str, action: str, view_by: str, recipient_addresses: list[str] = None, sender_list: list[str] = None, sender_addresses: list[str] = None, recipient_list: list[str] = None, ) -> dict[str, Any]: """ Append spam quarantine blocklist/safelist entries. Args: entry_type (str): Blocklist/Safelist list type. quarantine_type (str): Quarantine type. action (str): Append action. view_by (str): Append list entry results by recipient/sender. recipient_addresses (List[str], Optional): Recipient addresses list to append. Defaults to None. sender_list (List[str], Optional): Sender addresses list to append. Defaults to None. sender_addresses (List[str], Optional): Sender addresses list to append. Defaults to None. recipient_list (List[str], Optional): Recipient addresses list to append. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ data = assign_params( action=action, quarantineType=quarantine_type, viewBy=view_by, recipientAddresses=recipient_addresses, senderAddresses=sender_addresses, recipientList=recipient_list, senderList=sender_list, ) return self._http_request("POST", f"quarantine/{entry_type}", json_data=data) def list_entry_edit_request( self, entry_type: str, quarantine_type: str, action: str, view_by: str, recipient_addresses: list[str] = None, sender_list: list[str] = None, sender_addresses: list[str] = None, recipient_list: list[str] = None, ) -> dict[str, Any]: """ Edit spam quarantine blocklist/safelist entries. Args: entry_type (str): Blocklist/Safelist list type. quarantine_type (str): Quarantine type. action (str): Edit action. view_by (str): Edit list entry results by recipient/sender. recipient_addresses (List[str], Optional): Recipient addresses list to edit Defaults to None. sender_list (List[str], Optional): Sender addresses list to edit Defaults to None. sender_addresses (List[str], Optional): Sender addresses list to edit Defaults to None. recipient_list (List[str], Optional): Recipient addresses list to edit Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ data = assign_params( action=action, quarantineType=quarantine_type, viewBy=view_by, recipientAddresses=recipient_addresses, senderAddresses=sender_addresses, recipientList=recipient_list, senderList=sender_list, ) return self._http_request("POST", f"quarantine/{entry_type}", json_data=data) def list_entry_delete_request( self, entry_type: str, quarantine_type: str, view_by: str, recipient_list: list[str] = None, sender_list: list[str] = None, ) -> dict[str, Any]: """ Delete spam quarantine blocklist/safelist entries. Args: entry_type (str): Blocklist/Safelist list type. quarantine_type (str): Quarantine type. view_by (str): Delete list entry results by recipient/sender. recipient_list (List[str], Optional): Recipient list to delete. Defaults to None. sender_list (List[str], Optional): Sender list to delete. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ data = assign_params( quarantineType=quarantine_type, recipientList=recipient_list, senderList=sender_list, viewBy=view_by, ) return self._http_request("DELETE", f"quarantine/{entry_type}", json_data=data) def message_search_request( self, start_date: str, end_date: str, offset: int, limit: int, search_option: str, cisco_host: str, sender_filter_operator: str = None, sender_filter_value: str = None, recipient_filter_operator: str = None, recipient_filter_value: str = None, subject_filter_operator: str = None, subject_filter_value: str = None, attachment_name_operator: str = None, attachment_name_value: str = None, file_sha_256: str = None, custom_query: str = None, ) -> dict[str, Any]: """ Search tracking messages. Args: start_date (str): Start date in ISO format. end_date (str): End date in ISO format. offset (int): Offset of results to skip. limit (int): Limit of results to retrieve. search_option (str): Messages option. cisco_host (str): Cisco host. sender_filter_operator (str, Optional): Sender filter operator. Required if sender_filter_value is specified. Defaults to None. sender_filter_value (str, Optional): Sender address filter. Required if sender_filter_operator is specified. Defaults to None. recipient_filter_operator (str, Optional): Recipient filter operator. Required if recipient_filter_value is specified. Defaults to None. recipient_filter_value (str, Optional): Recipient address filter. Required if recipient_filter_operator is specified. Defaults to None. subject_filter_operator (str, Optional): Subject filter operator. Required if subject_filter_value is specified. Defaults to None. subject_filter_value (str, Optional): Subject address filter. Required if subject_filter_operator is specified. Defaults to None. attachment_name_operator (str, Optional): Attachment name operator. Required if attachment_name_value is specified. Defaults to None. attachment_name_value (str, Optional): Attachment name filter. Required if attachment_name_operator is specified. Defaults to None. file_sha_256 (str, Optional): SHA256 must be 64 characters long and can contain only "0-9" and "a-f" symbols. e.g. e0d123e5f316bef78bfdf5a008837577e0d123e5f316bef78bfdf5a008837577. Defaults to None. custom_query (str, Optional): Custom query for cisco ESA advanced filters. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( envelopeSenderfilterOperator=sender_filter_operator, envelopeSenderfilterValue=sender_filter_value, envelopeRecipientfilterOperator=recipient_filter_operator, envelopeRecipientfilterValue=recipient_filter_value, subjectfilterOperator=subject_filter_operator, subjectfilterValue=subject_filter_value, ciscoHost=cisco_host, searchOption=search_option, offset=offset, limit=limit, fileSha256=file_sha_256, attachmentNameOperator=attachment_name_operator, attachmentNameValue=attachment_name_value, **format_custom_query_args(custom_query), ) return self._http_request( "GET", f"message-tracking/messages?startDate={start_date}&endDate={end_date}", params=params, ) def message_details_get_request( self, serial_number: str, message_ids: list[int], injection_connection_id: int = None, ) -> dict[str, Any]: """ Get message details. Args: serial_number (str): mail Gateway serial number. message_id (List[int]): Message ID List. injection_connection_id (int, Optional): ICID, injection connection ID. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( serialNumber=serial_number, mid=message_ids, icid=injection_connection_id, ) return self._http_request("GET", "message-tracking/details", params=params) def message_amp_details_get_request(self, serial_number: str, message_ids: list[int]) -> dict[str, Any]: """ Get message AMP report details. Args: serial_number (str): mail Gateway serial number. message_id (List[int]): Message ID List. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( serialNumber=serial_number, mid=message_ids, ) return self._http_request("GET", "message-tracking/amp-details", params=params) def message_dlp_details_get_request(self, serial_number: str, message_ids: list[int]) -> dict[str, Any]: """ Get message DLP report details. Args: serial_number (str): mail Gateway serial number. message_id (List[int]): Message ID List. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( serialNumber=serial_number, mid=message_ids, ) return self._http_request("GET", "message-tracking/dlp-details", params=params) def message_url_details_get_request(self, serial_number: str, message_ids: list[int]) -> dict[str, Any]: """ Get message URL report details. Args: serial_number (str): mail Gateway serial number. message_id (List[int]): Message ID List. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( serialNumber=serial_number, mid=message_ids, ) return self._http_request("GET", "message-tracking/url-details", params=params) def report_get_request( self, report_type: str, start_date: str, end_date: str, device_type: str, order_by: str = None, order_dir: str = None, top: str = None, filter_value: str = None, filter_by: str = None, filter_operator: str = None, ) -> dict[str, Any]: """ Get statistics reports. Args: report_type (str): Report type. start_date (str): Start date. end_date (str): End date. device_type (str): Device type. order_by (str, optional): Order results by field. Defaults to None. order_dir (str, optional): Order direction. Defaults to None. top (str, optional): Number of records with the highest values to return. Defaults to None. filter_value (str, optional): Filter value. Defaults to None. filter_by (str, optional): Filter by field. Defaults to None. filter_operator (str, optional): Filter operator. Defaults to None. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( startDate=start_date, endDate=end_date, device_type=device_type, orderBy=order_by, orderDir=order_dir, top=top, filterValue=filter_value, filterBy=filter_by, filterOperator=filter_operator, ) return self._http_request("GET", f"reporting/{report_type}", params=params) def dictionary_list_request( self, dictionary_name: str | None, mode: str, host_name: str | None, group_name: str | None ) -> dict[str, Any]: endpoint = "config/dictionaries" if dictionary_name: endpoint += f"/{dictionary_name}" params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) return self._http_request( "GET", endpoint, params=params, ) def dictionary_add_request( self, dictionary_name: str, mode: str, host_name: str | None, group_name: str | None, whole_words: int, words: list, ignore_case_sensitive: int, ) -> dict[str, Any]: params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) json_data = { "data": { "ignorecase": ignore_case_sensitive, "wholewords": whole_words, "words": words, "encoding": "utf-8", } } return self._http_request("POST", f"config/dictionaries/{dictionary_name}", params=params, json_data=json_data) def dictionary_edit_request( self, dictionary_name: str, mode: str, host_name: str | None, group_name: str | None, whole_words: int, words: list, ignore_case_sensitive: int, updated_name: str | None, ) -> dict[str, Any]: params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) json_data = { "data": { "ignorecase": ignore_case_sensitive, "wholewords": whole_words, "words": words, "encoding": "utf-8", } } if updated_name: json_data["data"]["name"] = updated_name return self._http_request("PUT", f"config/dictionaries/{dictionary_name}", params=params, json_data=json_data) def dictionary_delete_request( self, dictionary_name: str, mode: str, host_name: str | None, group_name: str | None ) -> dict[str, Any]: params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) return self._http_request( "DELETE", f"config/dictionaries/{dictionary_name}", params=params, ) def dictionary_words_add_request( self, dictionary_name: str, mode: str, host_name: str | None, group_name: str | None, words: list ) -> dict[str, Any]: params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) json_data = { "data": { "words": words, } } return self._http_request("POST", f"config/dictionaries/{dictionary_name}/words", params=params, json_data=json_data) def dictionary_words_delete_request( self, dictionary_name: str, mode: str, host_name: str | None, group_name: str | None, words: list ) -> dict[str, Any]: params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) json_data = { "data": { "words": words, } } return self._http_request("DELETE", f"config/dictionaries/{dictionary_name}/words", params=params, json_data=json_data) def dictionary_words_update_request( self, dictionary_name: str, mode: str, host_name: str | None, group_name: str | None, words: list, ) -> dict[str, Any]: """ Update the words in a specified dictionary. This method sends a PUT request to update the list of words in a dictionary on a Cisco ESA device or group/cluster. Args: dictionary_name (str): Name of the dictionary to update. mode (str): Mode of operation ('cluster', 'group', or 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. words (list): List of words to be added or updated in the dictionary. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) json_data = { "data": { "words": words, } } return self._http_request( "PUT", f"config/dictionaries/{dictionary_name}/words", params=params, json_data=json_data, ) def message_filter_list_request( self, filter_name: str | None, mode: str, host_name: str | None, group_name: str | None, ) -> dict[str, Any]: """ Retrieve all message filters or a single named message filter. Args: filter_name (str, optional): Specific filter to read. When omitted, list all filters. mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name when mode is 'machine'. group_name (str, optional): Group name when mode is 'group'. Returns: Dict[str, Any]: API response from Cisco ESA. """ endpoint = "config/message_filters" if filter_name: endpoint += f"/{filter_name}" params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) return self._http_request("GET", endpoint, params=params) def message_filter_create_request( self, filter_name: str, rules_and_actions: str, active: str, order: int | None, mode: str, host_name: str | None, group_name: str | None, ) -> dict[str, Any]: """ Create a new message filter. Args: filter_name (str): Name of the new filter. rules_and_actions (str): Full Cisco filter DSL body. active (str): "true" or "false" — whether the filter should be active on creation. order (int, optional): 1-based position in the filter list. mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name when mode is 'machine'. group_name (str, optional): Group name when mode is 'group'. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) body_data = assign_params(rules_and_actions=rules_and_actions, active=active, order=order) return self._http_request( "POST", f"config/message_filters/{filter_name}", params=params, json_data={"data": body_data}, ) def message_filter_update_request( self, filter_name: str, active: str | None, order: int | None, mode: str, host_name: str | None, group_name: str | None, ) -> dict[str, Any]: """ Partially update an existing message filter. Only the supplied fields (active / order) are sent to the API. Args: filter_name (str): Name of the filter to update. active (str, optional): "true" or "false". order (int, optional): New 1-based position. mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name when mode is 'machine'. group_name (str, optional): Group name when mode is 'group'. Returns: Dict[str, Any]: API response from Cisco ESA. Raises: DemistoException: If neither `active` nor `order` was supplied. """ params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) body_data = assign_params(active=active, order=order) if not body_data: demisto.debug("message_filter_update_request: rejecting call — no updatable fields supplied") raise DemistoException("At least one of 'active' or 'order' must be supplied.") return self._http_request( "PUT", f"config/message_filters/{filter_name}", params=params, json_data={"data": body_data}, ) def message_filter_delete_request( self, filter_name: str, mode: str, host_name: str | None, group_name: str | None, ) -> dict[str, Any]: """ Delete a message filter. Args: filter_name (str): Name of the filter to delete. mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name when mode is 'machine'. group_name (str, optional): Group name when mode is 'group'. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) return self._http_request( "DELETE", f"config/message_filters/{filter_name}", params=params, ) def url_list_request( self, mode: str, host_name: str | None, group_name: str | None, url_list_name: str | None = None, ) -> dict[str, Any]: """ Retrieve URL list or all URL lists. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. url_list_name (str, optional): Specific URL list name to retrieve. Returns: Dict[str, Any]: API response from Cisco ESA. """ endpoint = "config/url_lists" if url_list_name: endpoint += f"/{url_list_name}" params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) return self._http_request("GET", endpoint, params=params) def url_list_create_request( self, mode: str, host_name: str | None, group_name: str | None, url_list_name: str, urls: list[str], ) -> dict[str, Any]: """ Create a new URL list with the specified URLs. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. url_list_name (str): Name of the URL list to create. urls (List[str]): List of URLs to include in the new list. Returns: Dict[str, Any]: API response from Cisco ESA. """ endpoint = f"config/url_lists/{url_list_name}" params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) json_data = {"data": {"urls": urls}} return self._http_request("POST", endpoint, params=params, json_data=json_data) def url_list_update_request( self, mode: str, host_name: str | None, group_name: str | None, url_list_name: str, urls: list[str], ) -> dict[str, Any]: """ Update an existing URL list with a new set of URLs. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. url_list_name (str): Name of the URL list to update. urls (List[str]): New list of URLs to apply. Returns: Dict[str, Any]: API response from Cisco ESA. """ endpoint = f"config/url_lists/{url_list_name}" params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) json_data = {"data": {"urls": urls}} return self._http_request("PUT", endpoint, params=params, json_data=json_data) def url_list_delete_request( self, mode: str, host_name: str | None, group_name: str | None, url_list_names: list[str], ) -> dict[str, Any]: """ Delete one or more URL lists. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. url_list_names (List[str]): List of URL list names to delete. Returns: Dict[str, Any]: API response from Cisco ESA. """ endpoint = "config/url_lists" params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) json_data = {"data": {"url_lists": url_list_names}} return self._http_request("DELETE", endpoint, params=params, json_data=json_data) def file_hash_list_request( self, mode: str, host_name: str | None, group_name: str | None, file_hash_list_name: str | None = None, ) -> dict[str, Any]: """ Retrieve a specific file hash list or all file hash lists. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. file_hash_list_name (str, optional): Specific list name to retrieve. Returns: Dict[str, Any]: API response from Cisco ESA. """ endpoint = "config/file_hash_lists" if file_hash_list_name: endpoint += f"/{file_hash_list_name}" params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) return self._http_request("GET", endpoint, params=params) def file_hash_create_request( self, mode: str, host_name: str | None, group_name: str | None, file_hash_list_name: str, filehashes: list[str], description: str, list_type: str, ) -> dict[str, Any]: """ Create a new file hash list. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. file_hash_list_name (str): Name of the file hash list to create. filehashes (List[str]): List of file hashes to include. description (str): Description of the list. list_type (str): Type of the list (e.g., 'malware', 'trusted'). Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) json_data = {"data": {"filehashes": filehashes, "description": description, "list_type": list_type}} return self._http_request( "POST", f"config/file_hash_lists/{file_hash_list_name}", params=params, json_data=json_data, ) def file_hash_update_request( self, mode: str, host_name: str | None, group_name: str | None, file_hash_list_name: str, filehashes: list[str], ) -> dict[str, Any]: """ Update an existing file hash list with new hashes. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. file_hash_list_name (str): Name of the file hash list to update. filehashes (List[str]): List of file hashes to set. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) json_data = {"data": {"filehashes": filehashes}} return self._http_request( "PUT", f"config/file_hash_lists/{file_hash_list_name}", params=params, json_data=json_data, ) def pvo_quarantine_list_request( self, quarantine_type: str, limit: int | None = 50, all_results: bool = False, rule_id: str | None = None, offset: int | None = None, order_by: str | None = None, order_direction: str | None = None, ) -> dict[str, Any]: """ List all PVO quarantine rules or a specific rule by ID. """ endpoint = "quarantine/rules" params = assign_params( quarantineType=quarantine_type, limit=limit if rule_id else None, ruleId=rule_id, offset=offset if rule_id else None, orderBy=order_by if rule_id else None, orderDir=order_direction if rule_id else None, ) return self._http_request("GET", endpoint, params=params) def pvo_quarantine_release_request(self, quarantine_type: str, rule_ids: list[str]) -> dict[str, Any]: """ Release messages matching the specified PVO rule IDs. """ endpoint = "quarantine/rules" json_data = {"data": {"action": "release", "quarantineType": quarantine_type, "ruleIds": rule_ids}} return self._http_request("POST", endpoint, json_data=json_data) def pvo_quarantine_delete_request(self, quarantine_type: str, rule_ids: list[str]) -> dict[str, Any]: """ Delete messages matching the specified PVO rule IDs. """ endpoint = "quarantine/rules" json_data = {"data": {"action": "delete", "quarantineType": quarantine_type, "ruleIds": rule_ids}} return self._http_request("DELETE", endpoint, json_data=json_data) def user_entry_list_request( self, mode: str, host_name: str | None, group_name: str | None, policy_name: str ) -> dict[str, Any]: """ Retrieve user-defined sender and recipient entries for a given mail policy. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. policy_name (str): Name of the mail policy. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) return self._http_request( "GET", f"config/incoming_mail_policies/{policy_name}/senders_and_recipients", params=params, ) def user_entry_add_request( self, mode: str, host_name: str | None, group_name: str | None, policy_name: str, sender_domain_entries: list[str] | None = None, sender_non_domain_entries: list[str] | None = None, receiver_operation: str | None = None, receiver_domain_entries: list[str] | None = None, receiver_not_domain_entries: list[str] | None = None, ) -> dict[str, Any]: """ Add new sender and/or recipient entries to a mail policy. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. policy_name (str): Name of the mail policy. sender_domain_entries (List[str], optional): Sender domain entries. sender_non_domain_entries (List[str], optional): Sender non-domain entries (e.g., specific emails). receiver_operation (str, optional): Logical operation for receivers ('AND', 'OR', etc.). receiver_domain_entries (List[str], optional): Recipient domain entries. receiver_not_domain_entries (List[str], optional): Negated recipient domain entries. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params(device_type="esa", mode=mode, host_name=host_name, group_name=group_name) json_data = build_user_entry_payload( sender_domain_entries, sender_non_domain_entries, receiver_operation, receiver_domain_entries, receiver_not_domain_entries, ) return self._http_request( "POST", f"config/incoming_mail_policies/{policy_name}/senders_and_recipients", params=params, json_data={"data": json_data}, ) def user_entry_update_request( self, mode: str, host_name: str | None, group_name: str | None, policy_name: str, sender_domain_entries: list[str] | None = None, sender_non_domain_entries: list[str] | None = None, receiver_operation: str | None = None, receiver_domain_entries: list[str] | None = None, receiver_not_domain_entries: list[str] | None = None, ) -> dict[str, Any]: """ Update user-defined sender and recipient entries in a mail policy. Args: mode (str): Cluster mode ('cluster', 'group', 'machine'). host_name (str, optional): Host name if mode is 'machine'. group_name (str, optional): Group name if mode is 'group'. policy_name (str): Name of the mail policy. current_data (List[Dict[str, Any]]): Existing list of user entries to be updated. sender_domain_entries (List[str], optional): Sender domain entries. sender_non_domain_entries (List[str], optional): Sender non-domain entries. receiver_operation (str, optional): Logical operation for receivers. receiver_domain_entries (List[str], optional): Recipient domain entries. receiver_not_domain_entries (List[str], optional): Negated recipient domain entries. Returns: Dict[str, Any]: API response from Cisco ESA. """ params = assign_params( device_type="esa", mode=mode, host_name=host_name, group_name=group_name, ) json_data = build_user_entry_payload( sender_domain_entries, sender_non_domain_entries, receiver_operation, receiver_domain_entries, receiver_not_domain_entries, ) return self._http_request( "PUT", f"config/incoming_mail_policies/{policy_name}/senders_and_recipients", params=params, json_data={"data": [json_data]}, ) def message_connection_details_get_request( self, serial_number: str, message_ids: list[int], injection_connection_id: int | None = None, start_date: str | None = None, end_date: str | None = None, ) -> dict[str, Any]: """ Get SMTP connection and transmission behavior details for specific messages. Returns: Dict[str, Any]: API response from Cisco ESA. """ return self._http_request( "GET", "message-tracking/connection-details", params=remove_empty_elements( { "sma_serial_number": serial_number, "mid": message_ids, "icid": injection_connection_id, "start_date": start_date, "end_date": end_date, } ), ) def message_remediation_details_get_request( self, serial_number: str, message_ids: list[int], injection_connection_id: int | None = None, start_date: str | None = None, end_date: str | None = None, ) -> dict[str, Any]: """ Get historical remediation actions (deletion, recall) taken on specific messages. Returns: Dict[str, Any]: API response from Cisco ESA. """ return self._http_request( "GET", "message-tracking/remediation-details", params=remove_empty_elements( { "sma_serial_number": serial_number, "mid": message_ids, "icid": injection_connection_id, "start_date": start_date, "end_date": end_date, } ), ) def build_user_entry_payload( sender_domain_entries: list[str] | None = None, sender_non_domain_entries: list[str] | None = None, receiver_operation: str | None = None, receiver_domain_entries: list[str] | None = None, receiver_not_domain_entries: list[str] | None = None, ) -> dict[str, Any]: """ Constructs the user entry payload for add/update commands. Accepts already-parsed lists for domain_entries fields. """ payload = { "sender_config": { "sender": {"domain_entries": sender_domain_entries}, "sender_not": {"domain_entries": sender_non_domain_entries}, }, "receiver_config": { "operation": receiver_operation, "receiver": {"domain_entries": receiver_domain_entries}, "receiver_not": {"domain_entries": receiver_not_domain_entries}, }, } return remove_empty_elements(payload) def get_paginated_data(data: list | None, limit: int, all_results: bool) -> list: """ Returns a paginated or full list of data based on input parameters. Args: data (Optional[List]): The list of data items to paginate. Can be None. limit (int): The maximum number of items to return if pagination is applied. all_results (bool): If True, return the entire list. If False, return up to 'limit' items. Returns: List: A list containing either all items or up to 'limit' items, or an empty list if data is None or empty. """ if not data: return [] return data if all_results else data[:limit] def format_custom_query_args(custom_query: str = None) -> dict[str, Any]: """ Format custom query arguments for tracking message advanced filters. Args: custom_query (str, optional): Custom query of advanced filters. Defaults to None. Returns: Dict[str, Any]: Formatted dictionary of custom query arguments. """ try: if custom_query: return dict(field.split("=") for field in custom_query.split(";")) else: return {} except ValueError: raise ValueError('Please validate the format of argument "custom_query". For example: "key1=value1;key2=value2".') def format_datetime(time_expression: str) -> str: """ Format string from time expression to Cisco ESA datetime format. Args: time_expression (str): Time expression or ISO format datetime. Returns: str: Datetime formatted string. """ return arg_to_datetime(time_expression).strftime(CISCO_TIME_FORMAT) # type: ignore def format_reporting_datetime(time_expression: str) -> str: """ Format string from time expression to Cisco ESA reporting datetime format. Args: time_expression (str): Time expression or ISO format datetime. Returns: str: Datetime formatted string. """ return arg_to_datetime(time_expression).strftime(CISCO_REPORTING_TIME_FORMAT) # type: ignore def format_timestamp(timestamp: str, output_format: str = DATETIME_FORMAT) -> str: """ Format Cisco ESA timestamp to datetime string. Args: timestamp (str): Cisco ESA timestamp. output_format (str): The format of the return date. Returns: str: Datetime formatted string. """ try: try: datetime_res = arg_to_datetime(timestamp) except ValueError: datetime_res = arg_to_datetime(timestamp.replace("GMT ", "GMT")) return datetime_res.strftime(output_format) # type: ignore except: # noqa: E722 return timestamp def format_number_list_argument(number_list_string: str) -> list[int]: """ Format number list argument to list of integer type. Args: number_list_string (str): Number list argument. Returns: List[int]: List of integers. """ return [arg_to_number(number) for number in argToList(number_list_string)] # type: ignore def validate_pagination_arguments( page: int | None = None, page_size: int | None = None, limit: int | None = None, ): """ Validate pagination arguments, raise error if argument is not valid. Args: page (int): Page. page_size (int): Page Size. limit (int): Limit. """ if page and page_size: if page_size < MIN_PAGE_SIZE or page_size > MAX_PAGE_SIZE: raise ValueError( f"page size argument must be greater than or equal to {MIN_PAGE_SIZE} " f"and smaller or equal to {MAX_PAGE_SIZE}." ) if page < MIN_PAGE_NUMBER: raise ValueError(f"page argument must be equal or greater than {MIN_PAGE_NUMBER}.") else: if limit and limit < MIN_LIMIT: raise ValueError(f"limit argument must be equal or greater than {MIN_LIMIT}.") def validate_related_arguments(args: dict[str, Any], related_arguments_list: list[list[str]]): """ Validate correct usage of arguments that are related to each other. Args: args (Dict[str, Any]): Command arguments. related_arguments_list (List[List[str]]): Related arguments list. """ args = {key: value for key, value in args.items() if value != ""} for related_arguments in related_arguments_list: exist_list = [argument in args for argument in related_arguments] if not all(exist_list) and any(exist_list): raise ValueError(f"{', '.join(related_arguments)} arguments should be used together but one or more are empty.") def format_list_entry_arguments(view_by: str, args: dict[str, Any]) -> dict[str, Any]: """ Format list entry arguments. Args: view_by (str): View by recipient/sender. args (Dict[str, Any]): Command arguments to format. Returns: Dict[str, Any]: Formatted list entry arguments. """ if view_by == "recipient": if args.get("recipient_addresses") and args.get("sender_list"): args["sender_addresses"] = None args["recipient_list"] = None else: raise DemistoException("Please specify recipient_addresses and sender_list arguments when using view_by recipient.") elif view_by == "sender": if args.get("sender_addresses") and args.get("recipient_list"): args["recipient_addresses"] = None args["sender_list"] = None else: raise DemistoException("Please specify sender_addresses and recipient_list arguments when using view_by sender.") else: raise DemistoException(f'Please check the value of argument "view_by". Valid values are recipient/sender, got {view_by}.') return args def pagination(request_command: Callable, args: dict[str, Any], **kwargs) -> tuple: """ Executing Manual Pagination (using the page and page size arguments) or Automatic Pagination (display a number of total results). Args: request_command (Callable): The command to execute. args (Dict[str, Any]): The command arguments. Returns: Tuple: output, pagination message for Command Results. """ page = arg_to_number(args.get("page")) page_size = arg_to_number(args.get("page_size")) limit = arg_to_number(args.get("limit")) validate_pagination_arguments(page, page_size, limit) if page and page_size: offset = page_size * (page - 1) output = request_command(offset=offset, limit=page_size, **kwargs).get("data") pagination_message = f"Showing page {page}.\n Current page size: {page_size}." elif limit: output = [] offset = 0 while limit > 0: page_size = min(limit, REQUEST_MAX_PULL) output.extend(request_command(offset=offset, limit=page_size, **kwargs).get("data")) limit -= REQUEST_MAX_PULL offset += REQUEST_MAX_PULL pagination_message = f"Showing {len(output)} rows." if len(output) > 0 else None # type: ignore else: pagination_message = "No pagination information" output = [] demisto.debug(f"No pagination parameters {pagination_message=} {output=}") return output, pagination_message def check_dictionary_mode_args(mode: str, host_name: str, group_name: str) -> tuple: """ Check the validity of cluster parameters and return appropriate values based on the mode. Args: mode (str): The cluster mode, which can be either "group" or "machine". host_name (str): The name of the host, required when the mode is "machine". group_name (str): The name of the group, required when the mode is "group". Returns: - Raises a DemistoException if the required parameters are missing based on the mode: - If the mode is "group" and no group_name is provided. - If the mode is "machine" and no host_name is provided. - If both parameters are provided, returns: - (None, group_name) if the mode is "group". - (host_name, None) if the mode is not "group". - Else: returns (host_name, group_name) as they are. """ if mode == "group" and not group_name: raise DemistoException("Please specify a group name for a cluster from type group.") if mode == "machine" and not host_name: raise DemistoException("Please specify a host name for a cluster from type machine.") return (None, group_name) if mode == "group" else (host_name, None) def convert_words_to_list(words: str) -> list[list]: """ Convert a string of words into a list of lists. Args: words (str): A string containing a list of words. Returns: List[str]: A list of lists containing words and their associated values. Raises: DemistoException: If the input string is not formatted correctly, with a message indicating the correct pattern. This pattern is used for adding or editing dictionary entries or adding words, not for deleting words. """ try: converted_list = list(ast.literal_eval(words)) if isinstance(converted_list[0], str): return [converted_list] return converted_list except Exception: raise DemistoException("Words list is not defined correctly. Please use the following pattern: ['word1',3],['word2'].") def spam_quarantine_message_search_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Search spam quarantine messages. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ quarantine_type = QUARANTINE_TYPE start_date = format_datetime(args["start_date"]) end_date = format_datetime(args["end_date"]) filter_by = args.get("filter_by") filter_operator = args.get("filter_operator") filter_value = args.get("filter_value") recipient_filter_operator = args.get("recipient_filter_operator") recipient_filter_value = args.get("recipient_filter_value") order_by = args.get("order_by") order_dir = args.get("order_dir") validate_related_arguments( args=args, related_arguments_list=[ ["filter_by", "filter_operator", "filter_value"], ["recipient_filter_operator", "recipient_filter_value"], ["order_by", "order_dir"], ], ) output, pagination_message = pagination( client.spam_quarantine_message_search_request, args=args, quarantine_type=quarantine_type, start_date=start_date, end_date=end_date, filter_by=filter_by, filter_operator=filter_operator, filter_value=filter_value, recipient_filter_operator=recipient_filter_operator, recipient_filter_value=recipient_filter_value, order_by=order_by, order_dir=order_dir, ) spam_quarantine_message_lists = [dict(message.get("attributes", {}), mid=message.get("mid")) for message in output] readable_output = tableToMarkdown( name="Spam Quarantine Messages List", metadata=pagination_message, t=spam_quarantine_message_lists, headers=["mid", "date", "fromAddress", "toAddress", "subject", "size"], headerTransform=pascalToSpace, ) return CommandResults( readable_output=readable_output, outputs_prefix="CiscoESA.SpamQuarantineMessage", outputs_key_field="mid", outputs=spam_quarantine_message_lists, raw_response=spam_quarantine_message_lists, ) def spam_quarantine_message_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get spam quarantine message details. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ quarantine_type = QUARANTINE_TYPE message_id = args["message_id"] response: dict[str, Any] = client.spam_quarantine_message_get_request(quarantine_type, message_id).get("data", {}) new_message = dict(response.get("attributes", {}), mid=response.get("mid")) readable_message = f'Found spam quarantine message with ID: {new_message.get("mid")}' readable_output = tableToMarkdown( name="Spam Quarantine Message", metadata=readable_message, t=new_message, headers=["mid", "fromAddress", "toAddress", "date", "subject", "attachments"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( readable_output=readable_output, outputs_prefix="CiscoESA.SpamQuarantineMessage", outputs_key_field="mid", outputs=new_message, raw_response=response, ) def spam_quarantine_message_release_command(client: Client, args: dict[str, Any]) -> list[CommandResults]: """ Release spam quarantine message. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: List[CommandResults]: readable outputs for XSOAR. """ action = RELEASE_ACTION quarantine_type = QUARANTINE_TYPE message_ids = format_number_list_argument(args["message_ids"]) command_results_list = [] for message_id in message_ids: response = client.spam_quarantine_message_release_request(action, quarantine_type, [message_id]) if dict_safe_get(response, ["data", "totalCount"]) == 1: readable_output = f"Quarantined message {message_id} successfully released." else: readable_output = f"Quarantined message {message_id} not found." command_results_list.append(CommandResults(readable_output=readable_output)) return command_results_list def spam_quarantine_message_delete_command(client: Client, args: dict[str, Any]) -> list[CommandResults]: """ Delete spam quarantine message details. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: List[CommandResults]: readable outputs for XSOAR. """ quarantine_type = QUARANTINE_TYPE message_ids = format_number_list_argument(args["message_ids"]) command_results_list = [] for message_id in message_ids: response = client.spam_quarantine_message_delete_request(quarantine_type, [message_id]) if dict_safe_get(response, ["data", "totalCount"]) == 1: readable_output = f"Quarantined message {message_id} successfully deleted." else: readable_output = f"Quarantined message {message_id} not found." command_results_list.append(CommandResults(readable_output=readable_output)) return command_results_list def list_entry_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ List spam quarantine blocklist/safelist. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ entry_type: str = args["entry_type"] action = VIEW_ACTION order_by = args.get("order_by") order_dir = args.get("order_dir") quarantine_type = QUARANTINE_TYPE view_by = args.get("view_by") search = args.get("search") validate_related_arguments(args=args, related_arguments_list=[["order_by", "order_dir"]]) output, pagination_message = pagination( client.list_entry_get_request, args=args, entry_type=entry_type, action=action, quarantine_type=quarantine_type, view_by=view_by, order_by=order_by, order_dir=order_dir, search=search, ) readable_output = tableToMarkdown( name=f"{entry_type.title()} Entries", metadata=pagination_message, t=output, headers=["recipientAddress", "senderList"] if view_by == "recipient" else ["senderAddress", "recipientList"], headerTransform=pascalToSpace, ) return CommandResults( readable_output=readable_output, outputs_prefix=f"CiscoESA.ListEntry.{entry_type.title()}", outputs_key_field="recipientAddress" if view_by == "recipient" else "senderAddress", outputs=output, raw_response=output, ) def list_entry_add_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Add spam quarantine blocklist/safelist entries. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ entry_type = args["entry_type"] quarantine_type = QUARANTINE_TYPE action = ADD_ACTION view_by = args["view_by"] formatted_arguments = format_list_entry_arguments(view_by=view_by, args=args) recipient_addresses = argToList(formatted_arguments.get("recipient_addresses")) sender_list = argToList(formatted_arguments.get("sender_list")) sender_addresses = argToList(formatted_arguments.get("sender_addresses")) recipient_list = argToList(formatted_arguments.get("recipient_list")) response = client.list_entry_add_request( entry_type=entry_type, quarantine_type=quarantine_type, action=action, view_by=view_by, recipient_addresses=recipient_addresses, sender_list=sender_list, sender_addresses=sender_addresses, recipient_list=recipient_list, ) if view_by == "recipient": readable_output = ( f'Successfully added {", ".join(sender_list)} senders to ' f'{", ".join(recipient_addresses)} recipients in {entry_type}.' ) else: readable_output = ( f'Successfully added {", ".join(recipient_list)} recipients to ' f'{", ".join(sender_addresses)} senders in {entry_type}.' ) return CommandResults(readable_output=readable_output, raw_response=response) def list_entry_append_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Append spam quarantine blocklist/safelist entries. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ entry_type = args["entry_type"] quarantine_type = QUARANTINE_TYPE action = APPEND_ACTION view_by = args["view_by"] formatted_arguments = format_list_entry_arguments(view_by=view_by, args=args) recipient_addresses = argToList(formatted_arguments.get("recipient_addresses")) sender_list = argToList(formatted_arguments.get("sender_list")) sender_addresses = argToList(formatted_arguments.get("sender_addresses")) recipient_list = argToList(formatted_arguments.get("recipient_list")) response = client.list_entry_append_request( entry_type=entry_type, quarantine_type=quarantine_type, action=action, view_by=view_by, recipient_addresses=recipient_addresses, sender_list=sender_list, sender_addresses=sender_addresses, recipient_list=recipient_list, ) if view_by == "recipient": readable_output = ( f'Successfully appended {", ".join(sender_list)} senders to ' f'{", ".join(recipient_addresses)} recipients in {entry_type}.' ) else: readable_output = ( f'Successfully appended {", ".join(recipient_list)} recipients to ' f'{", ".join(sender_addresses)} senders in {entry_type}.' ) return CommandResults(readable_output=readable_output, raw_response=response) def list_entry_edit_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Edit spam quarantine blocklist/safelist entries. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ entry_type = args["entry_type"] quarantine_type = QUARANTINE_TYPE action = EDIT_ACTION view_by = args["view_by"] formatted_arguments = format_list_entry_arguments(view_by=view_by, args=args) recipient_addresses = argToList(formatted_arguments.get("recipient_addresses")) sender_list = argToList(formatted_arguments.get("sender_list")) sender_addresses = argToList(formatted_arguments.get("sender_addresses")) recipient_list = argToList(formatted_arguments.get("recipient_list")) response = client.list_entry_edit_request( entry_type=entry_type, quarantine_type=quarantine_type, action=action, view_by=view_by, recipient_addresses=recipient_addresses, sender_list=sender_list, sender_addresses=sender_addresses, recipient_list=recipient_list, ) if view_by == "recipient": readable_output = ( f"Successfully edited {', '.join(recipient_addresses)} recipients' senders to " f"{', '.join(sender_list)} in {entry_type}." ) else: readable_output = ( f"Successfully edited {', '.join(sender_addresses)} senders' recipients to " f"{', '.join(recipient_list)} in {entry_type}." ) return CommandResults(readable_output=readable_output, raw_response=response) def list_entry_delete_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Delete spam quarantine blocklist/safelist entries. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ entry_type = args["entry_type"] quarantine_type = QUARANTINE_TYPE view_by = args["view_by"] recipient_list = argToList(args.get("recipient_list")) sender_list = argToList(args.get("sender_list")) if view_by == "recipient": if recipient_list: sender_list = None else: raise DemistoException("Please specify recipient_list argument when using view_by recipient.") else: if sender_list: recipient_list = None else: raise DemistoException("Please specify sender_list argument when using view_by sender.") response = client.list_entry_delete_request( entry_type=entry_type, quarantine_type=quarantine_type, view_by=view_by, recipient_list=recipient_list, sender_list=sender_list, ) deleted_entries = ", ".join(recipient_list if view_by == "recipient" else sender_list) return CommandResults( readable_output=f"Successfully deleted {deleted_entries} {view_by}s from {entry_type}.", raw_response=response, ) def message_search_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Search tracking messages. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ start_date = format_datetime(args["start_date"]) end_date = format_datetime(args["end_date"]) sender_filter_operator = args.get("sender_filter_operator") sender_filter_value = args.get("sender_filter_value") recipient_filter_operator = args.get("recipient_filter_operator") recipient_filter_value = args.get("recipient_filter_value") subject_filter_operator = args.get("subject_filter_operator") subject_filter_value = args.get("subject_filter_value") attachment_name_operator = args.get("attachment_name_operator") attachment_name_value = args.get("attachment_name_value") cisco_host = "All_Hosts" search_option = "messages" file_sha_256 = args.get("file_sha_256") custom_query = args.get("custom_query") validate_related_arguments( args=args, related_arguments_list=[ ["sender_filter_operator", "sender_filter_value"], ["recipient_filter_operator", "recipient_filter_value"], ["subject_filter_operator", "subject_filter_value"], ["attachment_name_operator", "attachment_name_value"], ["order_by", "order_dir"], ], ) output, pagination_message = pagination( client.message_search_request, args=args, start_date=start_date, end_date=end_date, sender_filter_operator=sender_filter_operator, sender_filter_value=sender_filter_value, recipient_filter_operator=recipient_filter_operator, recipient_filter_value=recipient_filter_value, subject_filter_operator=subject_filter_operator, subject_filter_value=subject_filter_value, attachment_name_operator=attachment_name_operator, attachment_name_value=attachment_name_value, cisco_host=cisco_host, search_option=search_option, file_sha_256=file_sha_256, custom_query=custom_query, ) messages_lists = [ dict( message.get("attributes", {}), timestamp=format_timestamp(dict_safe_get(message, ["attributes", "timestamp"])), unique_message_id="".join(map(str, dict_safe_get(message, ["attributes", "mid"]))), ) for message in output ] readable_output = tableToMarkdown( name="Messages List", metadata=pagination_message, t=messages_lists, headers=[ "mid", "allIcid", "serialNumber", "sender", "recipient", "subject", "messageStatus", "timestamp", "senderIp", "sbrs", ], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( readable_output=readable_output, outputs_prefix="CiscoESA.Message", outputs_key_field="unique_message_id", outputs=messages_lists, raw_response=messages_lists, ) def message_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get message details. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ serial_number = args["serial_number"] message_ids = format_number_list_argument(args["message_ids"]) injection_connection_id = arg_to_number(args.get("injection_connection_id")) response = ( client.message_details_get_request( serial_number=serial_number, message_ids=message_ids, injection_connection_id=injection_connection_id, ) .get("data", {}) .get("messages", {}) ) mid = response.get("mid") if not mid or "N/A" in mid: raise DemistoException( f'Message ID {", ".join(map(str, message_ids))} was not found.\nPlease check message IDs or Serial Number.' ) response["timestamp"] = format_timestamp(response.get("timestamp")) response["unique_message_id"] = "".join(map(str, response.get("mid"))) for event in response.get("summary", ()): event["timestamp"] = format_timestamp(event.get("timestamp")) readable_output = tableToMarkdown( name="Message Details", metadata=f'Found message with ID {", ".join(map(str, response.get("mid")))}.', t=response, headers=[ "mid", "allIcid", "subject", "sender", "recipient", "timestamp", "messageSize", "sendingHostSummary", "messageStatus", "direction", "mailPolicy", "senderGroup", "showAMP", "showDLP", "showURL", ], headerTransform=pascalToSpace, ) summary_readable_output = tableToMarkdown( name="Message Summary", t=response.get("summary"), headers=["description", "timestamp", "lastEvent"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( readable_output=readable_output + summary_readable_output, outputs_prefix="CiscoESA.Message", outputs_key_field="unique_message_id", outputs=response, raw_response=response, ) def message_amp_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get message AMP report details. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ serial_number = args["serial_number"] message_ids = format_number_list_argument(args["message_ids"]) response = ( client.message_amp_details_get_request(serial_number=serial_number, message_ids=message_ids) .get("data", {}) .get("messages", {}) ) mid = response.get("mid") if not mid or "N/A" in mid: raise DemistoException( f'Message ID {", ".join(map(str, message_ids))} was not found.\nPlease check message IDs or Serial Number.' ) response["timestamp"] = format_timestamp(response.get("timestamp")) readable_output = tableToMarkdown( name="Message AMP Report Details", metadata=f'Found AMP details for message ID {", ".join(map(str, response.get("mid")))}.', t=response, headers=[ "mid", "allIcid", "subject", "sender", "recipient", "attachments", "timestamp", "messageSize", "messageStatus", "direction", "senderGroup", ], headerTransform=pascalToSpace, ) amp_summary: list[dict[str, Any]] = response.get("ampDetails") if amp_summary: for event in amp_summary: timestamp = event.get("timestamp") if timestamp: event["timestamp"] = format_timestamp(timestamp) summary_readable_output = tableToMarkdown( name="Message AMP Report Details Summary", t=amp_summary, headers=["description", "timestamp"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( readable_output=readable_output + summary_readable_output, outputs_prefix="CiscoESA.AMPDetail", outputs_key_field="mid", outputs=response, raw_response=response, ) def message_dlp_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get message DLP report details. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ serial_number = args["serial_number"] message_ids = format_number_list_argument(args["message_ids"]) response = ( client.message_dlp_details_get_request(serial_number=serial_number, message_ids=message_ids) .get("data", {}) .get("messages", {}) ) mid = response.get("mid") if not mid or "N/A" in mid: raise DemistoException( f'Message ID {", ".join(map(str, message_ids))} was not found.\nPlease check message IDs or Serial Number.' ) response["timestamp"] = format_timestamp(response.get("timestamp")) readable_output = tableToMarkdown( name="Message DLP Report Details", metadata=f'Found DLP details for message ID {", ".join(map(str, response.get("mid")))}.', t=response, headers=[ "mid", "allIcid", "subject", "sender", "recipient", "attachments", "timestamp", "messageSize", "messageStatus", "direction", "senderGroup", ], headerTransform=pascalToSpace, removeNull=True, ) dlp_summary: list[dict[str, Any]] = response.get("dlpDetails") summary_readable_output = tableToMarkdown( name="Message DLP Report Details Summary", t=dlp_summary, headers=["mid", "violationSeverity", "riskFactor", "dlpPolicy"], headerTransform=pascalToSpace, ) return CommandResults( readable_output=readable_output + summary_readable_output, outputs_prefix="CiscoESA.DLPDetail", outputs_key_field="mid", outputs=response, raw_response=response, ) def message_url_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get message URL report details. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ serial_number = args["serial_number"] message_ids = format_number_list_argument(args["message_ids"]) response = ( client.message_url_details_get_request(serial_number=serial_number, message_ids=message_ids) .get("data", {}) .get("messages", {}) ) mid = response.get("mid") if not mid or "N/A" in mid: raise DemistoException( f'Message ID {", ".join(map(str, message_ids))} was not found.\nPlease check message IDs or Serial Number.' ) response["timestamp"] = format_timestamp(response.get("timestamp")) url_summary: list[dict[str, Any]] = response.get("urlDetails") if url_summary: for event in url_summary: timestamp = event.get("timestamp") if timestamp: event["timestamp"] = format_timestamp(timestamp) readable_output = tableToMarkdown( name="Message URL Report Details", metadata=f'Found URL details for message ID {", ".join(map(str, response.get("mid")))}.', t=response, headers=[ "mid", "allIcid", "subject", "sender", "recipient", "attachments", "timestamp", "messageSize", "messageStatus", "direction", "senderGroup", ], headerTransform=pascalToSpace, ) summary_readable_output = tableToMarkdown( name="Message URL Report Details Summary", t=url_summary, headers=["description", "timestamp"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( readable_output=readable_output + summary_readable_output, outputs_prefix="CiscoESA.URLDetail", outputs_key_field="mid", outputs=response, raw_response=response, ) def report_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get statistics reports. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: readable outputs for XSOAR. """ report_type = args.get("custom_report_type", args.get("report_type")) start_date = format_reporting_datetime(args["start_date"]) end_date = format_reporting_datetime(args["end_date"]) device_type = "esa" order_by = args.get("order_by") order_dir = args.get("order_dir") top = args.get("top") filter_value = args.get("filter_value") filter_by = args.get("filter_by") filter_operator = args.get("filter_operator") response: dict[str, Any] = client.report_get_request( report_type=report_type, start_date=start_date, end_date=end_date, device_type=device_type, order_by=order_by, order_dir=order_dir, top=top, filter_value=filter_value, filter_by=filter_by, filter_operator=filter_operator, ).get("data", {}) response["uuid"] = str(uuid.uuid4()) try: table = {k: v for results in response.get("resultSet", [{}]) for k, v in results.items()} except Exception: table = response.get("resultSet", response) readable_output = tableToMarkdown( name=f'Report type: {response.get("type")}', metadata=f'Report UUID: {response["uuid"]}', t=table, headerTransform=string_to_table_header, removeNull=True, ) return CommandResults( readable_output=readable_output, outputs_prefix="CiscoESA.Report", outputs_key_field="uuid", outputs=response, raw_response=response, ) def dictionary_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Retrieve dictionary configuration details. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs for XSOAR, including dictionary configuration details. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") dictionary_name = args.get("dictionary_name", "") host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) response = client.dictionary_list_request( dictionary_name=dictionary_name, mode=mode, host_name=host_name, group_name=group_name ) if dictionary_name: name = f"Information for Dictionary: {dictionary_name}" else: name = f"Information for All Configured Dictionaries in mode: {mode}" readable_output = tableToMarkdown( name=name, t=response.get("data"), removeNull=True, headers=["name", "words", "ignorecase", "wholewords", "words_count", "encoding"], ) return CommandResults( outputs_prefix="CiscoESA.Dictionary", outputs=response.get("data"), raw_response=response, readable_output=readable_output ) def dictionary_add_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Add a new dictionary configuration. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs for XSOAR, indicating the successful addition of the dictionary. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") dictionary_name = args.get("dictionary_name", "") whole_words = args.get("whole_words", True) words = args.get("words", "") ignore_case_sensitive = args.get("ignore_case_sensitive", False) host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) ignore_case_sensitive = int(argToBoolean(ignore_case_sensitive)) # will be sent to the api as 0 or 1 whole_words = int(argToBoolean(whole_words)) # will be sent to the api as 0 or 1 words = convert_words_to_list(words) response = client.dictionary_add_request( dictionary_name=dictionary_name, mode=mode, host_name=host_name, group_name=group_name, whole_words=whole_words, words=words, ignore_case_sensitive=ignore_case_sensitive, ) return CommandResults( readable_output=f"{dictionary_name} was added successfully.", raw_response=response, ) def dictionary_edit_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Edit an existing dictionary configuration. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs for XSOAR, indicating the successful update of the dictionary. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") dictionary_name = args.get("dictionary_name", "") updated_name = args.get("updated_name", "") whole_words = args.get("whole_words", True) words = args.get("words", "") ignore_case_sensitive = args.get("ignore_case_sensitive", False) host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) ignore_case_sensitive = int(argToBoolean(ignore_case_sensitive)) # will be sent to the api as 0 or 1 whole_words = int(argToBoolean(whole_words)) # will be sent to the api as 0 or 1 words = convert_words_to_list(words) response = client.dictionary_edit_request( dictionary_name=dictionary_name, mode=mode, host_name=host_name, group_name=group_name, whole_words=whole_words, words=words, ignore_case_sensitive=ignore_case_sensitive, updated_name=updated_name, ) return CommandResults( readable_output=f"{dictionary_name} has been successfully updated.", raw_response=response, ) def dictionary_delete_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Delete an existing dictionary configuration. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs for XSOAR, indicating the successful deletion of the dictionary. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") dictionary_name = args.get("dictionary_name", "") host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) response = client.dictionary_delete_request( dictionary_name=dictionary_name, mode=mode, host_name=host_name, group_name=group_name ) return CommandResults( readable_output=f"{dictionary_name} deleted successfully.", raw_response=response, ) def dictionary_words_add_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Add words to an existing dictionary configuration. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs for XSOAR, indicating the successful addition of words to the dictionary. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") dictionary_name = args.get("dictionary_name", "") words = args.get("words", "") host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) words = convert_words_to_list(words) response = client.dictionary_words_add_request( dictionary_name=dictionary_name, mode=mode, host_name=host_name, group_name=group_name, words=words ) return CommandResults( readable_output=f"Added successfully to {dictionary_name}.", raw_response=response, ) def dictionary_words_delete_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Delete words from an existing dictionary configuration. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs for XSOAR, indicating the successful deletion of words from the dictionary. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") dictionary_name = args.get("dictionary_name", "") words = argToList(args.get("words", "")) host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) response = client.dictionary_words_delete_request( dictionary_name=dictionary_name, mode=mode, host_name=host_name, group_name=group_name, words=words ) return CommandResults( readable_output=f"Words deleted successfully from {dictionary_name}.", raw_response=response, ) def dictionary_words_update_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Add words to an existing dictionary configuration. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs for XSOAR, indicating the successful addition of words to the dictionary. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") dictionary_name = args.get("dictionary_name", "") words = args.get("words", "") host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) words = convert_words_to_list(words) response = client.dictionary_words_update_request( dictionary_name=dictionary_name, mode=mode, host_name=host_name, group_name=group_name, words=words, ) return CommandResults( readable_output=f"Added successfully to {dictionary_name}.", raw_response=response, ) def message_filter_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Retrieve all message filters in scope or read a single named filter. Applies client-side filtering on `active` and a client-side cap (`limit`) when listing all filters. When `filter_name` is supplied both filters are ignored. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs, structured outputs under CiscoESA.MessageFilter, and the raw API response. """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") filter_name = args.get("filter_name") or "" active = args.get("active") limit = arg_to_number(args.get("limit")) or 50 host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) demisto.debug(f"calling Cisco with {filter_name=}, {mode=}, {host_name=}, {group_name=}") response = client.message_filter_list_request( filter_name=filter_name or None, mode=mode, host_name=host_name, group_name=group_name, ) rows: list[dict[str, Any]] = list(response.get("data") or []) # Client-side filtering is only meaningful when listing (not when reading one by name). if not filter_name: if active: rows = [row for row in rows if row.get("active").lower() == active.lower()] # type: ignore rows = rows[:limit] # Conditionally include the 'Validation Warning' column only when at least one row has it. headers = ["Name", "Active", "Order", "Rules And Actions"] if any(row.get("invalid_reason") for row in rows): headers.append("Validation Warning") hr_rows = [ { "Name": row.get("name"), "Active": row.get("active"), "Order": row.get("order"), "Rules And Actions": row.get("rules_and_actions"), "Validation Warning": row.get("invalid_reason"), } for row in rows ] if filter_name: title = f"Cisco ESA — Message Filter: {filter_name}" else: title = f"Cisco ESA — Message Filters (mode: {mode})" readable_output = tableToMarkdown( name=title, t=hr_rows, headers=headers, removeNull=False, ) # Only enable overwrite when listing the full collection. When the user # requested a single filter by name, fall back to the keyed merge behavior # so that a one-row lookup does not wipe the rest of the array. if not filter_name: return CommandResults( outputs_prefix="CiscoESA.MessageFilter", outputs_key_field="name", outputs=rows, raw_response=response, readable_output=readable_output, replace_existing=True, ) return CommandResults( outputs_prefix="CiscoESA.MessageFilter", outputs_key_field="name", outputs=rows, raw_response=response, readable_output=readable_output, ) def message_filter_create_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Create a new message filter. On success the API may still return a `meta.warning` (e.g. when a referenced listener or interface is unknown). The filter is created either way; the warning is surfaced in the human-readable output. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs and the raw API response (no context outputs). """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") filter_name = args["filter_name"] rules_and_actions = args["rules_and_actions"] active = args.get("active", "true") order = arg_to_number(args.get("order")) host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) demisto.debug(f"calling Cisco with {filter_name=}, {mode=}, {host_name=}, {group_name=}") response = client.message_filter_create_request( filter_name=filter_name, rules_and_actions=rules_and_actions, active=active, order=order, mode=mode, host_name=host_name, group_name=group_name, ) readable_output = f"Filter {filter_name} was added successfully." warning = (response.get("meta") or {}).get("warning") if warning: demisto.debug(f"appliance returned meta.warning={warning!r} for filter={filter_name!r}") readable_output += f"\n\n**Warning:** {warning}" return CommandResults( readable_output=readable_output, raw_response=response, ) def message_filter_update_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Partially update an existing message filter. At least one of `active` or `order` must be supplied. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs and the raw API response (no context outputs). """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") filter_name = args["filter_name"] active = args.get("active") order = arg_to_number(args.get("order")) host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) demisto.debug(f"calling Cisco with {filter_name=}, {mode=}, {host_name=}, {group_name=}") response = client.message_filter_update_request( filter_name=filter_name, active=active, order=order, mode=mode, host_name=host_name, group_name=group_name, ) readable_output = f"Filter {filter_name} was successfully updated." return CommandResults( readable_output=readable_output, raw_response=response, ) def message_filter_delete_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Delete a message filter. Args: client (Client): Cisco ESA API client. args (Dict[str, Any]): Command arguments from XSOAR. Returns: CommandResults: Readable outputs and the raw API response (no context outputs). """ mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name = args.get("host_name", "") group_name = args.get("group_name", "") filter_name = args["filter_name"] host_name, group_name = check_dictionary_mode_args(mode, host_name, group_name) demisto.debug( "cisco-esa-message-filter-delete: calling Cisco with " f"filter_name={filter_name!r}, mode={mode!r}, host_name={host_name!r}, group_name={group_name!r}" ) response = client.message_filter_delete_request( filter_name=filter_name, mode=mode, host_name=host_name, group_name=group_name, ) return CommandResults( readable_output=f"Filter {filter_name} was deleted successfully.", raw_response=response, ) def url_list_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) url_list_name = args.get("url_list_name") all_results = argToBoolean(args.get("all_results", False)) limit = arg_to_number(args.get("limit")) or 50 response = client.url_list_request(mode, host_name, group_name, url_list_name) outputs = response.get("data") if not url_list_name: outputs = get_paginated_data(outputs, limit, all_results) readable_output = tableToMarkdown( name="URL List" if not url_list_name else f"URL List: {url_list_name}", t=outputs, headers=["name", "urls", "urls_count", "used_by"], removeNull=True, headerTransform=string_to_table_header, ) return CommandResults( outputs_prefix="CiscoESA.URLList", outputs=outputs, outputs_key_field="name", raw_response=response, readable_output=readable_output, ) def url_list_create_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) url_list_name = args["url_list_name"] urls = argToList(args["urls"]) response = client.url_list_create_request(mode, host_name, group_name, url_list_name, urls) return CommandResults(readable_output=f"`{url_list_name}` was successfully created.", raw_response=response) def url_list_update_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) url_list_name = args["url_list_name"] urls = argToList(args["urls"]) response = client.url_list_update_request(mode, host_name, group_name, url_list_name, urls) return CommandResults(readable_output=f"`{url_list_name}` was successfully updated.", raw_response=response) def url_list_delete_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) url_list_names = argToList(args["url_list_names"]) response = client.url_list_delete_request(mode, host_name, group_name, url_list_names) return CommandResults( readable_output=f"URL List(s) `{', '.join(url_list_names)}` were successfully deleted.", raw_response=response, ) def file_hash_list_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) file_hash_list_name = args.get("file_hash_list_name") all_results = argToBoolean(args.get("all_results", False)) limit = arg_to_number(args.get("limit")) or 50 response = client.file_hash_list_request(mode, host_name, group_name, file_hash_list_name) outputs = response.get("data") if not file_hash_list_name: outputs = get_paginated_data(outputs, limit, all_results) readable_output = tableToMarkdown( name="File Hash List", t=outputs, headers=["name", "description", "filehashes", "filehashes_count", "list_type"], removeNull=True, headerTransform=string_to_table_header, ) return CommandResults( outputs_prefix="CiscoESA.FileHashList", outputs_key_field="name", outputs=outputs, raw_response=response, readable_output=readable_output, ) def file_hash_create_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) file_hash_list_name = args["file_hash_list_name"] filehashes = argToList(args["filehashes"]) description = args["description"] list_type = args["list_type"] response = client.file_hash_create_request( mode, host_name, group_name, file_hash_list_name, filehashes, description, list_type ) return CommandResults(readable_output=f"`{file_hash_list_name}` was successfully created.", raw_response=response) def file_hash_update_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) file_hash_list_name = args["file_hash_list_name"] filehashes = argToList(args["filehashes"]) response = client.file_hash_update_request(mode, host_name, group_name, file_hash_list_name, filehashes) return CommandResults(readable_output=f"`{file_hash_list_name}` was successfully updated.", raw_response=response) def pvo_quarantine_list_command(client: Client, args: dict[str, Any]) -> CommandResults: quarantine_type = args.get("type", "pvo") rule_id = args.get("rule_id") offset = arg_to_number(args.get("offset")) limit = arg_to_number(args.get("limit", 50)) order_by = args.get("order_by") order_direction = args.get("order_direction") all_results = argToBoolean(args.get("all_results", False)) limit = arg_to_number(args.get("limit")) or 50 response = client.pvo_quarantine_list_request( quarantine_type=quarantine_type, rule_id=rule_id, offset=offset, limit=limit, order_by=order_by, order_direction=order_direction, ) paginated_outputs = get_paginated_data(response.get("data"), limit, all_results) outputs = [snakify(entry.get("attributes", {})) | {"rid": entry.get("rid")} for entry in paginated_outputs] readable_output = tableToMarkdown( name="PVO Quarantine Rule List", t=outputs, headers=[ "rule_id", "rule_description", "number_of_messages", "totalsize", "capacity", "average_message_size", ], removeNull=True, headerTransform=string_to_table_header, ) return CommandResults( outputs_prefix="CiscoESA.QuarantineRule", outputs_key_field="rule_id", outputs=outputs, raw_response=response, readable_output=readable_output, ) def pvo_quarantine_release_command(client: Client, args: dict[str, Any]) -> CommandResults: quarantine_type = args.get("quarantine_type", "pvo") rule_ids = argToList(args["rule_ids"]) response = client.pvo_quarantine_release_request(quarantine_type, rule_ids) return CommandResults( readable_output=f"Messages were successfully released from rule(s): {', '.join(rule_ids)}", raw_response=response, ) def pvo_quarantine_delete_command(client: Client, args: dict[str, Any]) -> CommandResults: quarantine_type = args.get("quarantine_type", "pvo") rule_ids = argToList(args["rule_ids"]) response = client.pvo_quarantine_delete_request(quarantine_type, rule_ids) return CommandResults( readable_output=f"Messages were successfully deleted from rule(s): {', '.join(rule_ids)}", raw_response=response, ) def user_entry_list_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) policy_name = args["policy_name"] all_results = argToBoolean(args.get("all_results", False)) limit = arg_to_number(args.get("limit")) or 50 response = client.user_entry_list_request(mode, host_name, group_name, policy_name) paginated_outputs = get_paginated_data(response.get("data"), limit, all_results) hr = [ { "sender_domain_entries": dict_safe_get(user_entry, ["sender_config", "sender", "domain_entries"]), "sender_not_domain_entries": dict_safe_get(user_entry, ["sender_config", "sender_not", "domain_entries"]), "reciver_operation": dict_safe_get(user_entry, ["receiver_config", "operation"]), "receiver_domain_entries": dict_safe_get(user_entry, ["receiver_config", "receiver", "domain_entries"]), "receiver_not_domain_entries": dict_safe_get(user_entry, ["receiver_config", "receiver_not", "domain_entries"]), } for user_entry in paginated_outputs ] readable_output = tableToMarkdown( name=f"User Entry List for Policy: {policy_name}", t=hr, headers=[ "sender_domain_entries", "sender_not_domain_entries", "reciver_operation", "receiver_domain_entries", "receiver_not_domain_entries", ], removeNull=True, headerTransform=string_to_table_header, ) return CommandResults( outputs_prefix="CiscoESA.IncomingMailPolicyUser", outputs=paginated_outputs, raw_response=response, readable_output=readable_output, ) def user_entry_add_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) policy_name = args["policy_name"] response = client.user_entry_add_request( mode=mode, host_name=host_name, group_name=group_name, policy_name=policy_name, sender_domain_entries=argToList(args.get("sender_domain_entries")), sender_non_domain_entries=argToList(args.get("sender_non_domain_entries")), receiver_operation=args.get("receiver_operation"), receiver_domain_entries=argToList(args.get("receiver_domain_entries")), receiver_not_domain_entries=argToList(args.get("receiver_not_domain_entries")), ) return CommandResults( readable_output=f"`{policy_name}` user entry was successfully added.", raw_response=response, ) def user_entry_update_command(client: Client, args: dict[str, Any]) -> CommandResults: mode = args.get("mode", DEFAULT_MODE_DICTIONARIES) host_name, group_name = check_dictionary_mode_args(mode, args.get("host_name", ""), args.get("group_name", "")) policy_name = args["policy_name"] response = client.user_entry_update_request( mode=mode, host_name=host_name, group_name=group_name, policy_name=policy_name, sender_domain_entries=argToList(args.get("sender_domain_entries")), sender_non_domain_entries=argToList(args.get("sender_non_domain_entries")), receiver_operation=args.get("receiver_operation"), receiver_domain_entries=argToList(args.get("receiver_domain_entries")), receiver_not_domain_entries=argToList(args.get("receiver_not_domain_entries")), ) return CommandResults( readable_output=f"`{policy_name}` user entry was successfully updated.", raw_response=response, ) def message_connection_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults: serial_number = args["serial_number"] message_ids = format_number_list_argument(args["message_ids"]) injection_connection_id = arg_to_number(args.get("injection_connection_id")) start_date = args.get("start_date") end_date = args.get("end_date") limit = args.get("limit") or 50 all_results = argToBoolean(args.get("all_results", "false")) response = client.message_connection_details_get_request( serial_number=serial_number, message_ids=message_ids, injection_connection_id=injection_connection_id, start_date=start_date, end_date=end_date, ) sender_group = response.get("senderGroup") messages = get_paginated_data(response.get("messages", {}).get("summary", []), limit, all_results) outputs = { "Message": [snakify(msg) for msg in messages], "sbrs": response.get("sbrs"), "sender_group": sender_group, } readable_output = tableToMarkdown( name=f"Message Connection Report Details Summary: Sender group: {sender_group}", t=outputs["Message"], headers=[ "timestamp", "description", ], removeNull=True, headerTransform=string_to_table_header, ) return CommandResults( readable_output=readable_output, outputs_prefix="CiscoESA.MessageConnection", outputs=outputs, raw_response=response, ) def message_remediation_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults: serial_number = args["serial_number"] message_ids = format_number_list_argument(args["message_ids"]) injection_connection_id = arg_to_number(args.get("injection_connection_id")) start_date = args.get("start_date") end_date = args.get("end_date") limit = args.get("limit") or 50 all_results = argToBoolean(args.get("all_results", "true")) response = client.message_remediation_details_get_request( serial_number=serial_number, message_ids=message_ids, injection_connection_id=injection_connection_id, start_date=start_date, end_date=end_date, ) outputs = { **response, "message_details": get_paginated_data(response.get("message_details", []), limit, all_results), } readable_output = tableToMarkdown( name="Message Remediation Report Details Summary:", t=outputs.get("message_details", []), headers=["delivered_at", "mid", "from_email", "recipient_email", "mor_status", "msg_read"], headerTransform=pascalToSpace, ) return CommandResults( readable_output=readable_output, outputs_prefix="CiscoESA.MessageRemediation", outputs=outputs, raw_response=response, ) def fetch_incidents( client: Client, max_fetch: int, first_fetch: str, last_run: dict[str, Any], filter_by: str = None, filter_operator: str = None, filter_value: str = None, recipient_filter_operator: str = None, recipient_filter_value: str = None, ) -> tuple: """ Fetch Cisco ESA quarantined messages as incidents. Args: client (Client): Cisco ESA API client. max_fetch (int): Max number of messages to fetch. first_fetch (str): From which time to fetch. last_run (Dict[str, Any]): Last run info. filter_by (str, optional): Filter results by message field. Defaults to None. filter_operator (str, optional): Filter operator. Defaults to None. filter_value (str, optional): Filter value. Defaults to None. recipient_filter_operator (str, optional): Recipient filter operator. Defaults to None. recipient_filter_value (str, optional): Recipient filter value. Defaults to None. Returns: tuple: Incidents and last run info. """ start_time = last_run.get("start_time") start_date = format_timestamp(start_time, output_format=CISCO_TIME_FORMAT) if start_time else format_datetime(first_fetch) end_date = format_datetime("now") quarantine_type = QUARANTINE_TYPE offset = last_run.pop("offset", 0) or 0 order_by = "date" order_dir = "asc" quarantine_messages: list[dict[str, Any]] = client.spam_quarantine_message_search_request( quarantine_type=quarantine_type, start_date=start_date, end_date=end_date, offset=offset, limit=max_fetch, filter_by=filter_by, filter_operator=filter_operator, filter_value=filter_value, recipient_filter_operator=recipient_filter_operator, recipient_filter_value=recipient_filter_value, order_by=order_by, order_dir=order_dir, ).get("data", []) data_length = len(quarantine_messages) incidents: list[dict[str, Any]] = [] last_minute_incident_ids = last_run.get("last_minute_incident_ids", []) for incident in quarantine_messages: incident_datetime = format_timestamp(dict_safe_get(incident, ["attributes", "date"])) message_id = incident.get("mid") if message_id and message_id not in last_minute_incident_ids and start_date < incident_datetime: quarantine_message: dict[str, Any] = client.spam_quarantine_message_get_request( quarantine_type=quarantine_type, message_id=message_id ).get("data", {}) incident_details = dict( quarantine_message.get("attributes", {}), mid=quarantine_message.get("mid"), ) incidents.append( { "name": incident_details.get("subject"), "occurred": incident_datetime, "rawJSON": json.dumps(incident_details, ensure_ascii=False), } ) if incidents: start_time = incidents[-1].get("occurred") last_run["start_time"] = start_time new_fetched_tickets = [ json.loads(incident.get("rawJSON", {})).get("mid") for incident in incidents if incident.get("occurred") == start_time ] if offset == 0: last_run["last_minute_incident_ids"] = new_fetched_tickets else: last_run["last_minute_incident_ids"].extend(new_fetched_tickets) # In case that all the incidents where dropped if data_length != 0 and not incidents: last_run["offset"] = offset + max_fetch return incidents, last_run def test_module(client: Client, **kwargs) -> str: """ Validates the correctness of the instance parameters and connectivity to Cisco ESA API service. Args: client (Client): Cisco ESA API client. """ arg_to_datetime(kwargs.get("first_fetch")) validate_related_arguments( kwargs, [ ["filter_by", "filter_operator", "filter_value"], ["recipient_filter_operator", "recipient_filter_value"], ], ) start_date = format_datetime("1 month") end_date = format_datetime("now") offset = 0 limit = 1 search_option = "messages" cisco_host = "All_Hosts" client.message_search_request( start_date=start_date, end_date=end_date, offset=offset, limit=limit, search_option=search_option, cisco_host=cisco_host, ) return "ok" def main() -> None: params: dict[str, Any] = demisto.params() args: dict[str, Any] = demisto.args() base_url = params.get("base_url") username = params.get("credentials", {}).get("identifier") password = params.get("credentials", {}).get("password") max_fetch = arg_to_number(params.get("max_fetch", DEFAULT_FETCH)) first_fetch = params.get("first_fetch") or "7 days" filter_by = params.get("filter_by") filter_operator = params.get("filter_operator") filter_value = params.get("filter_value") recipient_filter_operator = params.get("recipient_filter_operator") recipient_filter_value = params.get("recipient_filter_value") verify_certificate: bool = not params.get("insecure", False) proxy = params.get("proxy", False) jwt_token_expiration_period = int(params.get("jwt_token_expiration_period") or 30) command = demisto.command() commands = { "cisco-esa-spam-quarantine-message-search": spam_quarantine_message_search_command, "cisco-esa-spam-quarantine-message-get": spam_quarantine_message_get_command, "cisco-esa-spam-quarantine-message-release": spam_quarantine_message_release_command, "cisco-esa-spam-quarantine-message-delete": spam_quarantine_message_delete_command, "cisco-esa-list-entry-get": list_entry_get_command, "cisco-esa-list-entry-add": list_entry_add_command, "cisco-esa-list-entry-append": list_entry_append_command, "cisco-esa-list-entry-edit": list_entry_edit_command, "cisco-esa-list-entry-delete": list_entry_delete_command, "cisco-esa-message-search": message_search_command, "cisco-esa-message-details-get": message_details_get_command, "cisco-esa-message-dlp-details-get": message_dlp_details_get_command, "cisco-esa-report-get": report_get_command, "cisco-esa-dictionary-list": dictionary_list_command, "cisco-esa-dictionary-add": dictionary_add_command, "cisco-esa-dictionary-edit": dictionary_edit_command, "cisco-esa-dictionary-delete": dictionary_delete_command, "cisco-esa-dictionary-words-add": dictionary_words_add_command, "cisco-esa-dictionary-words-delete": dictionary_words_delete_command, "cisco-esa-url-list": url_list_command, "cisco-esa-url-list-create": url_list_create_command, "cisco-esa-url-list-update": url_list_update_command, "cisco-esa-url-list-delete": url_list_delete_command, "cisco-esa-file-hash-list": file_hash_list_command, "cisco-esa-file-hash-create": file_hash_create_command, "cisco-esa-file-hash-update": file_hash_update_command, "cisco-esa-pvo-quarantine-list": pvo_quarantine_list_command, "cisco-esa-pvo-quarantine-message-release": pvo_quarantine_release_command, "cisco-esa-pvo-quarantine-message-delete": pvo_quarantine_delete_command, "cisco-esa-incoming-policy-user-list": user_entry_list_command, "cisco-esa-incoming-policy-user-add": user_entry_add_command, "cisco-esa-incoming-policy-user-update": user_entry_update_command, "cisco-esa-message-connection-details-get": message_connection_details_get_command, "cisco-esa-message-remediation-details-get": message_remediation_details_get_command, "cisco-esa-message-amp-details-get": message_amp_details_get_command, "cisco-esa-message-url-details-get": message_url_details_get_command, "cisco-esa-dictionary-words-update": dictionary_words_update_command, "cisco-esa-message-filter-list": message_filter_list_command, "cisco-esa-message-filter-create": message_filter_create_command, "cisco-esa-message-filter-update": message_filter_update_command, "cisco-esa-message-filter-delete": message_filter_delete_command, } try: client: Client = Client( urljoin(base_url, "/esa/api/v2.0"), username, password, verify_certificate, proxy, jwt_token_expiration_period, ) if command == "test-module": return_results( test_module( client, max_fetch=max_fetch, first_fetch=first_fetch, filter_by=filter_by, filter_operator=filter_operator, filter_value=filter_value, recipient_filter_operator=recipient_filter_operator, recipient_filter_value=recipient_filter_value, ) ) elif command == "fetch-incidents": incidents, last_run = fetch_incidents( client, max_fetch, # type: ignore first_fetch, # type: ignore demisto.getLastRun(), filter_by, filter_operator, filter_value, recipient_filter_operator, recipient_filter_value, ) demisto.setLastRun(last_run) demisto.incidents(incidents) elif command in commands: return_results(commands[command](client, args)) else: raise NotImplementedError(f"{command} command is not implemented.") except Exception as e: return_error(str(e)) if __name__ in ["__main__", "builtin", "builtins"]: main()