Cofense Triage v2

Use the Cofense Triage integration to ingest reported phishing indicators.

Data Enrichment & Threat Intelligence · Cofense Triage

Details

IDCofense Triage v2
ProviderCofense
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/chromium:149.0.7827.10133006
Supported ModulesAgentix XSIAM

README

Use the Cofense Triage integration to ingest reported phishing indicators.
This integration was integrated and tested with version 1.20 of Cofense Triage v2

Configure Cofense Triage v2 in Cortex

Parameter Description Required
host Server URL (e.g., https://192.168.0.1) True
user User True
token API Token True
isFetch Fetch incidents False
incidentType Incident type False
date_range First fetch time (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year) False
category_id Category ID to fetch False
match_priority Match Priority - the highest match priority based on rule hits for the report False
tags Tags - CSV list of tags of processed reports by which to filter False
max_fetch Maximum number of incidents to fetch each time False
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False

Commands

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

cofense-search-reports


Runs a query for reports.

Base Command

cofense-search-reports

Input

Argument Name Description Required
file_hash File hash, MD5 or SHA256. Optional
url The reported URLs. Optional
subject Report’s subject Optional
reported_at Retrieve reports that were reported after this time, for example: “2 hours, 4 minutes, 6 month, 1 day”. Optional
created_at Retrieve reports that were created after this time, for example: “2 hours, 4 minutes, 6 month, 1 day”. Optional
reporter Address or ID of the reporter. Optional
max_matches Maximum number of matches to fetch. Default is 30. Optional
verbose Returns all fields of a report. Optional

Context Output

Path Type Description
Cofense.Report.ID unknown ID number of the report.
Cofense.Report.EmailAttachments unknown Email attachments.
Cofense.Report.EmailAttachments.id unknown Email attachment ID.
Cofense.Report.Tags string Report tags.
Cofense.Report.ClusterId number Cluster ID number.
Cofense.Report.CategoryId number Report category.
Cofense.Report.CreatedAt date Report creation date.
Cofense.Report.ReportedAt string Reporting time.
Cofense.Report.MatchPriority number The highest match priority based on rule hits for the report.
Cofense.Report.ReporterId number Reporter ID.
Cofense.Report.Location string Location of the report.
Cofense.Report.Reporter string Reporter email address.
Cofense.Report.SuspectFromAddress string Suspect from address.
Cofense.Report.ReportSubject string Report subject.
Cofense.Report.ReportBody string Report body.
Cofense.Report.Md5 number MD5 hash of the file.
Cofense.Report.Sha256 unknown SHA256 hash of the file.

Command Example

!cofense-search-reports reported_at="7 days" created_at="7 days" max_matches="1"

Context Example

{
    "Cofense": {
        "Report": {
            "CategoryId": 4,
            "ClusterId": null,
            "CreatedAt": "2020-06-04T13:42:26.173Z",
            "EmailAttachments": [
                {
                    "content_type": "image/png; name=image001.png",
                    "decoded_filename": "image001.png",
                    "email_attachment_payload": {
                        "id": 7095,
                        "md5": "5008fb6e6652f56cac5bdc5bf1cbe9c2",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "554aeaaace31c7038a09dd408945583e1035ec124a46b04e5c6c5b148dc96f68"
                    },
                    "id": 18087,
                    "report_id": 13429,
                    "size_in_bytes": 1397
                },
                {
                    "content_type": "image/png; name=image003.png",
                    "decoded_filename": "image003.png",
                    "email_attachment_payload": {
                        "id": 7097,
                        "md5": "731ffb7846c22e41e9de8de307c93ece",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "c911d07d1f7be624e00e44821148629d98cf6d0f2bfac112362c7c564522ea51"
                    },
                    "id": 18089,
                    "report_id": 13429,
                    "size_in_bytes": 1701
                },
                {
                    "content_type": "image/png; name=image006.png",
                    "decoded_filename": "image006.png",
                    "email_attachment_payload": {
                        "id": 7100,
                        "md5": "124bd437f87181fdfe3154b31fd2cf6b",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "3d804c705545bf2a1e5ac6b0ea9b93a41ceb16d7453adebc58fba5df75335b20"
                    },
                    "id": 18092,
                    "report_id": 13429,
                    "size_in_bytes": 1994
                },
                {
                    "content_type": "image/png; name=image002.png",
                    "decoded_filename": "image002.png",
                    "email_attachment_payload": {
                        "id": 7096,
                        "md5": "cc07463ceeaaed79783a7f2a607797f9",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "c6c2c95238f52648faaef4520fa9bba49c10ca0f1df9bfd1912be544f319b80b"
                    },
                    "id": 18088,
                    "report_id": 13429,
                    "size_in_bytes": 1430
                },
                {
                    "content_type": "image/png; name=image004.png",
                    "decoded_filename": "image004.png",
                    "email_attachment_payload": {
                        "id": 7098,
                        "md5": "95878e37974ed3cad67154d36dd58a9a",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "e0d478f6ce56721867a0584ddea0016d713b9b2ab758fd0c9be3f1409d6e2634"
                    },
                    "id": 18090,
                    "report_id": 13429,
                    "size_in_bytes": 1557
                },
                {
                    "content_type": "image/png; name=image005.png",
                    "decoded_filename": "image005.png",
                    "email_attachment_payload": {
                        "id": 7099,
                        "md5": "0e911498bf4dc5eddb544ab5ece4b06a",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "5f2046b3c55a874aadde052f9da4af3c17e2b5bf5baf704f58b1dd1eadf08544"
                    },
                    "id": 18091,
                    "report_id": 13429,
                    "size_in_bytes": 1609
                },
                {
                    "content_type": "application/pdf; name=\"XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf\"",
                    "decoded_filename": "XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf",
                    "email_attachment_payload": {
                        "id": 7110,
                        "md5": "fb7f083f4fb93a88ab8110d857312978",
                        "mime_type": "application/pdf; charset=binary",
                        "sha256": "15ab1b20ada04dfc6285caff5e4da4eab09a9157c2cbe32cd96113da6304a5ee"
                    },
                    "id": 18093,
                    "report_id": 13429,
                    "size_in_bytes": 49597
                }
            ],
            "ID": 13429,
            "Location": "Processed",
            "MatchPriority": 1,
            "Md5": "d312e79695d5de744436006aab6b4ec1",
            "ReportBody": "Testing PDF attachment\r\n\r\n\r\nTest User  |  Director\r\nTEST\r\nm. 123-456-7890\r\ne. test@test.com<mailto:test@test.com>\r\n\r\nConnect with Cofense:\r\n\r\n[signature_527626984]<https://cofense.com/>[signature_379086648]<https://facebook.com/cofense>[signature_426568440]<https://twitter.com/cofense>[signature_1467413640]<https://linkedin.com/company/cofense>[signature_749445379]<https://www.instagram.com/cofense/>[signature_1384270593]<https://www.themuse.com/profiles/cofense>\r\n\r\nUniting Humanity Against Phishing. Watch Our Video<https://cofense.com/project/uhap-video/>\r\n\r\n",
            "ReportSubject": "2020-06-04 XSOAR attachment test",
            "ReportedAt": "2020-06-04T13:40:29.000Z",
            "ReporterId": 5331,
            "Sha256": "ba77b5d984f7da97b6f96daa442535c79f47e4b6ea0055e3472b855ee8c244e4",
            "Tags": []
        }
    }
}

Human Readable Output

Reports

Category Id Created At Email Attachments Id Location Match Priority Md5 Report Body Report Subject Reported At Reporter Id Sha256
4 2020-06-04T13:42:26.173Z {‘id’: 18087, ‘report_id’: 13429, ‘decoded_filename’: ‘image001.png’, ‘content_type’: ‘image/png; name=image001.png’, ‘size_in_bytes’: 1397, ‘email_attachment_payload’: {‘id’: 7095, ‘md5’: ‘5008fb6e6652f56cac5bdc5bf1cbe9c2’, ‘sha256’: ‘554aeaaace31c7038a09dd408945583e1035ec124a46b04e5c6c5b148dc96f68’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18089, ‘report_id’: 13429, ‘decoded_filename’: ‘image003.png’, ‘content_type’: ‘image/png; name=image003.png’, ‘size_in_bytes’: 1701, ‘email_attachment_payload’: {‘id’: 7097, ‘md5’: ‘731ffb7846c22e41e9de8de307c93ece’, ‘sha256’: ‘c911d07d1f7be624e00e44821148629d98cf6d0f2bfac112362c7c564522ea51’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18092, ‘report_id’: 13429, ‘decoded_filename’: ‘image006.png’, ‘content_type’: ‘image/png; name=image006.png’, ‘size_in_bytes’: 1994, ‘email_attachment_payload’: {‘id’: 7100, ‘md5’: ‘124bd437f87181fdfe3154b31fd2cf6b’, ‘sha256’: ‘3d804c705545bf2a1e5ac6b0ea9b93a41ceb16d7453adebc58fba5df75335b20’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18088, ‘report_id’: 13429, ‘decoded_filename’: ‘image002.png’, ‘content_type’: ‘image/png; name=image002.png’, ‘size_in_bytes’: 1430, ‘email_attachment_payload’: {‘id’: 7096, ‘md5’: ‘cc07463ceeaaed79783a7f2a607797f9’, ‘sha256’: ‘c6c2c95238f52648faaef4520fa9bba49c10ca0f1df9bfd1912be544f319b80b’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18090, ‘report_id’: 13429, ‘decoded_filename’: ‘image004.png’, ‘content_type’: ‘image/png; name=image004.png’, ‘size_in_bytes’: 1557, ‘email_attachment_payload’: {‘id’: 7098, ‘md5’: ‘95878e37974ed3cad67154d36dd58a9a’, ‘sha256’: ‘e0d478f6ce56721867a0584ddea0016d713b9b2ab758fd0c9be3f1409d6e2634’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18091, ‘report_id’: 13429, ‘decoded_filename’: ‘image005.png’, ‘content_type’: ‘image/png; name=image005.png’, ‘size_in_bytes’: 1609, ‘email_attachment_payload’: {‘id’: 7099, ‘md5’: ‘0e911498bf4dc5eddb544ab5ece4b06a’, ‘sha256’: ‘5f2046b3c55a874aadde052f9da4af3c17e2b5bf5baf704f58b1dd1eadf08544’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18093, ‘report_id’: 13429, ‘decoded_filename’: ‘XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf’, ‘content_type’: ‘application/pdf; name=”XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf”’, ‘size_in_bytes’: 49597, ‘email_attachment_payload’: {‘id’: 7110, ‘md5’: ‘fb7f083f4fb93a88ab8110d857312978’, ‘sha256’: ‘15ab1b20ada04dfc6285caff5e4da4eab09a9157c2cbe32cd96113da6304a5ee’, ‘mime_type’: ‘application/pdf; charset=binary’}}
13429 Processed 1 d312e79695d5de744436006aab6b4ec1 Testing PDF attachment


Test User | Director
COFENSE
m. 123-456-7890
e. test@test.comtest@test.com

Connect with Cofense:

[signature_527626984]https://cofense.com/[signature_379086648]https://facebook.com/cofense[signature_426568440]https://twitter.com/cofense[signature_1467413640]https://linkedin.com/company/cofense[signature_749445379]https://www.instagram.com/cofense/[signature_1384270593]https://www.themuse.com/profiles/cofense

Uniting Humanity Against Phishing. Watch Our Videohttps://cofense.com/project/uhap-video/

2020-06-04 XSOAR attachment test 2020-06-04T13:40:29.000Z 5331 ba77b5d984f7da97b6f96daa442535c79f47e4b6ea0055e3472b855ee8c244e4

cofense-search-inbox-reports


Runs a query for reports from the inbox mailbox.

Base Command

cofense-search-reports

Input

Argument Name Description Required
file_hash File hash, MD5 or SHA256. Optional
url The reported URLs. Optional
subject Report’s subject Optional
reported_at Retrieve reports that were reported after this time, for example: “2 hours, 4 minutes, 6 month, 1 day”. Optional
created_at Retrieve reports that were created after this time, for example: “2 hours, 4 minutes, 6 month, 1 day”. Optional
reporter Address or ID of the reporter. Optional
max_matches Maximum number of matches to fetch. Default is 30. Optional
verbose Returns all fields of a report. Optional

Context Output

Path Type Description
Cofense.Report.ID unknown ID number of the report.
Cofense.Report.EmailAttachments unknown Email attachments.
Cofense.Report.EmailAttachments.id unknown Email attachment ID.
Cofense.Report.Tags string Report tags.
Cofense.Report.ClusterId number Cluster ID number.
Cofense.Report.CategoryId number Report category.
Cofense.Report.CreatedAt date Report creation date.
Cofense.Report.ReportedAt string Reporting time.
Cofense.Report.MatchPriority number The highest match priority based on rule hits for the report.
Cofense.Report.ReporterId number Reporter ID.
Cofense.Report.Location string Location of the report.
Cofense.Report.Reporter string Reporter email address.
Cofense.Report.SuspectFromAddress string Suspect from address.
Cofense.Report.ReportSubject string Report subject.
Cofense.Report.ReportBody string Report body.
Cofense.Report.Md5 number MD5 hash of the file.
Cofense.Report.Sha256 unknown SHA256 hash of the file.

Command Example

!cofense-search-inbox-reports reported_at="7 days" created_at="7 days" max_matches="1"

Context Example

{
    "Cofense": {
        "Report": {
            "CategoryId": 4,
            "ClusterId": null,
            "CreatedAt": "2020-06-04T13:42:26.173Z",
            "EmailAttachments": [
                {
                    "content_type": "image/png; name=image001.png",
                    "decoded_filename": "image001.png",
                    "email_attachment_payload": {
                        "id": 7095,
                        "md5": "5008fb6e6652f56cac5bdc5bf1cbe9c2",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "554aeaaace31c7038a09dd408945583e1035ec124a46b04e5c6c5b148dc96f68"
                    },
                    "id": 18087,
                    "report_id": 13429,
                    "size_in_bytes": 1397
                },
                {
                    "content_type": "image/png; name=image003.png",
                    "decoded_filename": "image003.png",
                    "email_attachment_payload": {
                        "id": 7097,
                        "md5": "731ffb7846c22e41e9de8de307c93ece",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "c911d07d1f7be624e00e44821148629d98cf6d0f2bfac112362c7c564522ea51"
                    },
                    "id": 18089,
                    "report_id": 13429,
                    "size_in_bytes": 1701
                },
                {
                    "content_type": "image/png; name=image006.png",
                    "decoded_filename": "image006.png",
                    "email_attachment_payload": {
                        "id": 7100,
                        "md5": "124bd437f87181fdfe3154b31fd2cf6b",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "3d804c705545bf2a1e5ac6b0ea9b93a41ceb16d7453adebc58fba5df75335b20"
                    },
                    "id": 18092,
                    "report_id": 13429,
                    "size_in_bytes": 1994
                },
                {
                    "content_type": "image/png; name=image002.png",
                    "decoded_filename": "image002.png",
                    "email_attachment_payload": {
                        "id": 7096,
                        "md5": "cc07463ceeaaed79783a7f2a607797f9",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "c6c2c95238f52648faaef4520fa9bba49c10ca0f1df9bfd1912be544f319b80b"
                    },
                    "id": 18088,
                    "report_id": 13429,
                    "size_in_bytes": 1430
                },
                {
                    "content_type": "image/png; name=image004.png",
                    "decoded_filename": "image004.png",
                    "email_attachment_payload": {
                        "id": 7098,
                        "md5": "95878e37974ed3cad67154d36dd58a9a",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "e0d478f6ce56721867a0584ddea0016d713b9b2ab758fd0c9be3f1409d6e2634"
                    },
                    "id": 18090,
                    "report_id": 13429,
                    "size_in_bytes": 1557
                },
                {
                    "content_type": "image/png; name=image005.png",
                    "decoded_filename": "image005.png",
                    "email_attachment_payload": {
                        "id": 7099,
                        "md5": "0e911498bf4dc5eddb544ab5ece4b06a",
                        "mime_type": "image/png; charset=binary",
                        "sha256": "5f2046b3c55a874aadde052f9da4af3c17e2b5bf5baf704f58b1dd1eadf08544"
                    },
                    "id": 18091,
                    "report_id": 13429,
                    "size_in_bytes": 1609
                },
                {
                    "content_type": "application/pdf; name=\"XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf\"",
                    "decoded_filename": "XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf",
                    "email_attachment_payload": {
                        "id": 7110,
                        "md5": "fb7f083f4fb93a88ab8110d857312978",
                        "mime_type": "application/pdf; charset=binary",
                        "sha256": "15ab1b20ada04dfc6285caff5e4da4eab09a9157c2cbe32cd96113da6304a5ee"
                    },
                    "id": 18093,
                    "report_id": 13429,
                    "size_in_bytes": 49597
                }
            ],
            "ID": 13429,
            "Location": "Inbox",
            "MatchPriority": 1,
            "Md5": "d312e79695d5de744436006aab6b4ec1",
            "ReportBody": "Testing PDF attachment\r\n\r\n\r\nTest User  |  Director\r\nTEST\r\nm. 123-456-7890\r\ne. test@test.com<mailto:test@test.com>\r\n\r\nConnect with Cofense:\r\n\r\n[signature_527626984]<https://cofense.com/>[signature_379086648]<https://facebook.com/cofense>[signature_426568440]<https://twitter.com/cofense>[signature_1467413640]<https://linkedin.com/company/cofense>[signature_749445379]<https://www.instagram.com/cofense/>[signature_1384270593]<https://www.themuse.com/profiles/cofense>\r\n\r\nUniting Humanity Against Phishing. Watch Our Video<https://cofense.com/project/uhap-video/>\r\n\r\n",
            "ReportSubject": "2020-06-04 XSOAR attachment test",
            "ReportedAt": "2020-06-04T13:40:29.000Z",
            "ReporterId": 5331,
            "Sha256": "ba77b5d984f7da97b6f96daa442535c79f47e4b6ea0055e3472b855ee8c244e4",
            "Tags": []
        }
    }
}

Human Readable Output

Reports

Category Id Created At Email Attachments Id Location Match Priority Md5 Report Body Report Subject Reported At Reporter Id Sha256
4 2020-06-04T13:42:26.173Z {‘id’: 18087, ‘report_id’: 13429, ‘decoded_filename’: ‘image001.png’, ‘content_type’: ‘image/png; name=image001.png’, ‘size_in_bytes’: 1397, ‘email_attachment_payload’: {‘id’: 7095, ‘md5’: ‘5008fb6e6652f56cac5bdc5bf1cbe9c2’, ‘sha256’: ‘554aeaaace31c7038a09dd408945583e1035ec124a46b04e5c6c5b148dc96f68’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18089, ‘report_id’: 13429, ‘decoded_filename’: ‘image003.png’, ‘content_type’: ‘image/png; name=image003.png’, ‘size_in_bytes’: 1701, ‘email_attachment_payload’: {‘id’: 7097, ‘md5’: ‘731ffb7846c22e41e9de8de307c93ece’, ‘sha256’: ‘c911d07d1f7be624e00e44821148629d98cf6d0f2bfac112362c7c564522ea51’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18092, ‘report_id’: 13429, ‘decoded_filename’: ‘image006.png’, ‘content_type’: ‘image/png; name=image006.png’, ‘size_in_bytes’: 1994, ‘email_attachment_payload’: {‘id’: 7100, ‘md5’: ‘124bd437f87181fdfe3154b31fd2cf6b’, ‘sha256’: ‘3d804c705545bf2a1e5ac6b0ea9b93a41ceb16d7453adebc58fba5df75335b20’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18088, ‘report_id’: 13429, ‘decoded_filename’: ‘image002.png’, ‘content_type’: ‘image/png; name=image002.png’, ‘size_in_bytes’: 1430, ‘email_attachment_payload’: {‘id’: 7096, ‘md5’: ‘cc07463ceeaaed79783a7f2a607797f9’, ‘sha256’: ‘c6c2c95238f52648faaef4520fa9bba49c10ca0f1df9bfd1912be544f319b80b’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18090, ‘report_id’: 13429, ‘decoded_filename’: ‘image004.png’, ‘content_type’: ‘image/png; name=image004.png’, ‘size_in_bytes’: 1557, ‘email_attachment_payload’: {‘id’: 7098, ‘md5’: ‘95878e37974ed3cad67154d36dd58a9a’, ‘sha256’: ‘e0d478f6ce56721867a0584ddea0016d713b9b2ab758fd0c9be3f1409d6e2634’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18091, ‘report_id’: 13429, ‘decoded_filename’: ‘image005.png’, ‘content_type’: ‘image/png; name=image005.png’, ‘size_in_bytes’: 1609, ‘email_attachment_payload’: {‘id’: 7099, ‘md5’: ‘0e911498bf4dc5eddb544ab5ece4b06a’, ‘sha256’: ‘5f2046b3c55a874aadde052f9da4af3c17e2b5bf5baf704f58b1dd1eadf08544’, ‘mime_type’: ‘image/png; charset=binary’}},
{‘id’: 18093, ‘report_id’: 13429, ‘decoded_filename’: ‘XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf’, ‘content_type’: ‘application/pdf; name=”XSOAR Attachment Test -Inquiry - Agent Tesla Keylogger.pdf”’, ‘size_in_bytes’: 49597, ‘email_attachment_payload’: {‘id’: 7110, ‘md5’: ‘fb7f083f4fb93a88ab8110d857312978’, ‘sha256’: ‘15ab1b20ada04dfc6285caff5e4da4eab09a9157c2cbe32cd96113da6304a5ee’, ‘mime_type’: ‘application/pdf; charset=binary’}}
13429 Processed 1 d312e79695d5de744436006aab6b4ec1 Testing PDF attachment


Test User | Director
TEST
m. 123-456-7890
e. test@test.comtest@test.com

Connect with Cofense:

[signature_527626984]https://cofense.com/[signature_379086648]https://facebook.com/cofense[signature_426568440]https://twitter.com/cofense[signature_1467413640]https://linkedin.com/company/cofense[signature_749445379]https://www.instagram.com/cofense/[signature_1384270593]https://www.themuse.com/profiles/cofense

Uniting Humanity Against Phishing. Watch Our Videohttps://cofense.com/project/uhap-video/

2020-06-04 XSOAR attachment test 2020-06-04T13:40:29.000Z 5331 ba77b5d984f7da97b6f96daa442535c79f47e4b6ea0055e3472b855ee8c244e4

cofense-get-attachment


Retrieves an attachment by the attachment ID number.

Base Command

cofense-get-attachment

Input

Argument Name Description Required
attachment_id ID of the attachment. Required

Context Output

Path Type Description
File.Size number File size.
File.Type string File type, for example: “PE”, “txt”
File.EntryID string The file entry ID.
File.Name string File name.
File.SHA1 string File SHA1 hash.
File.SHA256 string File SHA256 hash.
File.MD5 string File MD5 hash.

Command Example

!cofense-get-attachment attachment_id="13311"

Context Example

{
    "File": {
        "EntryID": "603@cc18bdc4-7c64-494c-879c-23c3aee60818",
        "Info": "text/plain",
        "MD5": "97ee1d575640245abadbba15c0672eec",
        "Name": "13311",
        "SHA1": "13395876300d0a575812878446e15b9bbddda0b2",
        "SHA256": "19d9c63bf4067a897950cfb72c14e8d05d8dcab0655979c6b60b925fb91e329f",
        "SHA512": "31df48f235cc82247c6edc05850f910d6a057717d5d5f6ce84a4bc6c6fc3cc1f6ebae706ac592ace106b0559753928a838a1aee7018bec5b4316b90d95f55bcf",
        "SSDeep": "24:nDBTBpJG4hbUWBFcXekJPkJ1WkJM8PWkJKckJvV/WskJvV28BesR1zvX0:nDNrHb1BWXekJPkJ1WkJfPWkJDkJvV/n",
        "Size": 988,
        "Type": "ASCII text, with CRLF line terminators"
    }
}

Human Readable Output

cofense-get-reporter


Retrieves Email address of the reporter by ID

Base Command

cofense-get-reporter

Input

Argument Name Description Required
reporter_id ID of the reporter. Required

Context Output

Path Type Description
Cofense.Reporter.ID number ID of the reporter.
Cofense.Reporter.Email string Reporter email address.
Cofense.Reporter.CreatedAt string Reporter creation date.
Cofense.Reporter.UpdatedAt string Reporter last-updated date.
Cofense.Reporter.CredibilityScore number Reporter credibility score.
Cofense.Reporter.ReportsCount number Number of reports.
Cofense.Reporter.LastReportedAt string Date of most recent report.
Cofense.Reporter.VIP bool Whether Reporter is a VIP.

Command Example

!cofense-get-reporter reporter_id="1"

Context Example

{
    "Cofense": {
        "Reporter": {
            "CreatedAt": "2019-04-12T02:58:17.401Z",
            "CredibilityScore": 0,
            "Email": "ha.oullette@example.com",
            "ID": 1,
            "LastReportedAt": "2016-02-18T00:24:45.000Z",
            "ReportsCount": 3,
            "UpdatedAt": "2019-04-12T02:59:22.287Z",
            "Vip": false
        }
    }
}

Human Readable Output

Integration log: cmel case attrs: {‘ID’: 1, ‘Email’: ‘ha.oullette@example.com’, ‘CreatedAt’: ‘2019-04-12T02:58:17.401Z’, ‘UpdatedAt’: ‘2019-04-12T02:59:22.287Z’, ‘CredibilityScore’: 0, ‘ReportsCount’: 3, ‘LastReportedAt’: ‘2016-02-18T00:24:45.000Z’, ‘Vip’: False}### Reporter Results:

Created At Credibility Score Email Id Last Reported At Reports Count Updated At Vip
2019-04-12T02:58:17.401Z 0 ha.oullette@example.com 1 2016-02-18T00:24:45.000Z 3 2019-04-12T02:59:22.287Z false

cofense-get-report-by-id


Retrieves a report by the report ID number.

Base Command

cofense-get-report-by-id

Input

Argument Name Description Required
report_id ID of the report Required

Context Output

Path Type Description
Cofense.Report.ID number ID number of the report.
Cofense.Report.EmailAttachments string Email attachments.
Cofense.Report.EmailAttachments.id string Email attachment ID.
Cofense.Report.Tags string Report tags.
Cofense.Report.ClusterId number Cluster ID number.
Cofense.Report.CategoryId number Report category.
Cofense.Report.CreatedAt string Report creation date.
Cofense.Report.ReportedAt string Reporting time.
Cofense.Report.MatchPriority number The highest match priority based on rule hits for the report.
Cofense.Report.ReporterId number Reporter ID.
Cofense.Report.Location string Location of the report.
Cofense.Report.Reporter string Reporter email address.
Cofense.Report.SuspectFromAddress string Suspect from address.
Cofense.Report.ReportSubject string Report subject.
Cofense.Report.ReportBody string Report body.
Cofense.Report.Md5 number MD5 hash of the file.
Cofense.Report.Sha256 unknown SHA256 hash of the file.

Command Example

!cofense-get-report-by-id report_id="5760"

Context Example

{
    "Cofense": {
        "Report": {
            "CategoryId": 4,
            "ClusterId": null,
            "CreatedAt": "2019-04-17T20:53:02.090Z",
            "EmailAttachments": [],
            "ID": 5760,
            "Location": "Processed",
            "MatchPriority": 0,
            "Md5": "f13bbc172fe7d394828ccabb25c3c99e",
            "ReportSubject": "test@test.net Reset password instruction",
            "ReportedAt": "2019-04-17T16:54:57.000Z",
            "ReporterId": 3280,
            "Sha256": "4f6bc0d9c1217a2a6f327423e16b7a6e9294c68cfb33864541bd805fe4ab2d72",
            "Tags": []
        }
    }
}

Human Readable Output

{“HumanReadable”:”### Cofense HTML Report:\nHTML report download request has been completed”,”name”:”5760-report.html”,”path”:”aaf1160b-9176-45d9-aab9-90efd278e05d”}### Report Summary:

Category Id Created At Id Location Match Priority Md5 Report Subject Reported At Reporter Id Sha256
4 2019-04-17T20:53:02.090Z 5760 Processed 0 f13bbc172fe7d394828ccabb25c3c99e test@test.nul Reset password instruction 2019-04-17T16:54:57.000Z 3280 4f6bc0d9c1217a2a6f327423e16b7a6e9294c68cfb33864541bd805fe4ab2d72

cofense-get-report-png-by-id


Retrieves a report by the report ID number and displays as PNG

Base Command

cofense-get-report-png-by-id

Input

Argument Name Description Required
report_id Report ID PNG output Required
set_white_bg Change background to white Optional

Context Output

There is no context output for this command.

Command Example

!cofense-get-report-png-by-id report_id="5760" set_white_bg="True"

Context Example

{
    "InfoFile": {
        "EntryID": "616@cc18bdc4-7c64-494c-879c-23c3aee60818",
        "Extension": "png",
        "Info": "image/png",
        "Name": "cofense_report_5760.png",
        "Size": 40692,
        "Type": "PNG image data, 400 x 369, 8-bit/color RGBA, non-interlaced"
    }
}

Human Readable Output

Cofense: PNG of Report 5760

cofense-get-threat-indicators


Threat Indicators that are designated by analysts as malicious, suspicious or benign

Base Command

cofense-get-threat-indicators

Input

Argument Name Description Required
type indicator type Optional
level indicator severity Optional
start_date designated start date tagged by analyst (format example: YYYY-MM-DD+HH:MM:SS). Default: 6 days ago. Optional
end_date designated end date from assignment (format example: YYYY-MM-DD+HH:MM:SS). Default: current date. Optional

Context Output

Path Type Description
Cofense.ThreatIndicators unknown Threat indicator output
Cofense.ThreatIndicators.ID number Threat indicator ID in Cofense Triage.
Cofense.ThreatIndicators.OperatorId number Cofense Triage operator who designated the threat indicator.
Cofense.ThreatIndicators.ReportId number Associated Report in Cofense Triage.
Cofense.ThreatIndicators.ThreatKey string Threat indicator type.
Cofense.ThreatIndicators.ThreatLevel string Threat indicator level.
Cofense.ThreatIndicators.ThreatValue string Value of the threat indicator.

Command Example

!cofense-get-threat-indicators type="URL" level="Malicious" start_date="2020-05-28"

Context Example

{
    "Cofense": {
        "ThreatIndicators": {
            "CreatedAt": "2020-05-28T22:14:52.690Z",
            "ID": 75,
            "OperatorId": 2,
            "ReportId": 5760,
            "ThreatKey": "URL",
            "ThreatLevel": "Malicious",
            "ThreatValue": "http://bold-air0example.com/notification.php?email=test@test.net"
        }
    }
}

Human Readable Output

Threat Indicators

Created At Id Operator Id Report Id Threat Key Threat Level Threat Value
2020-05-28T22:14:52.690Z 75 2 5760 URL Malicious http://bold-air0example.com/notification.php?email=test@test.net

Configuration parameters

  • host — Server URL (e.g., https://192.168.0.1) (required)
  • user — User (required)
  • token — API Token (required)
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • mailbox_location — Mailbox Location (required)
  • date_range — First fetch time (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)
  • category_id — Category ID to fetch
  • match_priority — Match Priority - the highest match priority based on rule hits for the report
  • tags — Tags - CSV list of tags of processed reports by which to filter
  • max_fetch — Maximum number of incidents to fetch each time
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (7)

  • cofense-get-attachment

    Retrieves an attachment by the attachment ID number.

  • cofense-get-report-by-id

    Retrieves a report by the report ID number.

  • cofense-get-report-png-by-id

    Retrieves a report by the report ID number and displays as PNG.

  • cofense-get-reporter

    Retrieves Email address of the reporter by ID.

  • cofense-get-threat-indicators

    Threat Indicators that are designated by analysts as malicious, suspicious or benign.

  • cofense-search-inbox-reports

    Runs a query for reports from the `inbox` mailbox.

  • cofense-search-reports

    Runs a query for reports.

import functools
import itertools
import json
import math
from datetime import datetime, timezone
from io import BytesIO

import demistomock as demisto
import urllib3
from CommonServerPython import *
from PIL import Image
from urllib3.exceptions import InsecureRequestWarning

urllib3.disable_warnings(category=InsecureRequestWarning)

TERSE_FIELDS = [
    "id",
    "cluster_id",
    "reporter_id",
    "location",
    "created_at",
    "reported_at",
    "report_subject",
    "report_body",
    "md5",
    "sha256",
    "category_id",
    "match_priority",
    "tags",
    "email_attachments",
]


class TriageRequestFailedError(Exception):
    """Triage responded with something other than a normal 200 response"""

    def __init__(self, status_code, message):
        super().__init__(self, status_code, message)
        self.status_code = status_code
        self.message = message

    def __str__(self):
        return f"Call to Cofense Triage failed ({self.status_code}): {self.message}"


class TriageRequestEmptyResponse(Exception):
    """Triage responded without error, but the result set was unexpectedly empty"""

    def __init__(self, record_id, record_type):
        super().__init__(self, record_id, record_type)
        self.record_id = record_id
        self.record_type = record_type

    def __str__(self):
        return f"Could not find a {self.record_type} with id {self.record_id}"


class TriageNoReportersFoundError(Exception):
    """Triage returned empty results, but the integration expected at least one"""


class TriageInstance:
    def __init__(self, *, host, token, user, disable_tls_verification=False, demisto_params):
        self.host = host
        self.token = token
        self.user = user
        self.disable_tls_verification = disable_tls_verification
        self.demisto_params = demisto_params

    def request(self, endpoint, params=None, body=None, raw_response=False):
        """
        Make a request to the configured Triage instance and return the result.
        """
        response = requests.get(
            self.api_url(endpoint),
            headers={
                "Authorization": f"Token token={self.user}:{self.token}",
                "Accept": "application/json",
            },
            params=params,
            data=body,
            verify=not self.disable_tls_verification,
        )

        if not response.ok:
            raise TriageRequestFailedError(response.status_code, response.text)

        if response.status_code == 206:
            # 206 indicates Partial Content. The reason will be in the warning header.
            demisto.debug(str(response.headers))

        if raw_response:
            return response

        if not response.text or response.text == "[]":
            return {}

        try:
            return response.json()
        # when installing simplejson the type of exception is requests.exceptions.JSONDecodeError when it is not
        # possible to load json.
        except (json.decoder.JSONDecodeError, requests.exceptions.JSONDecodeError) as ex:
            demisto.debug(str(ex))
            raise TriageRequestFailedError(response.status_code, "Could not parse result from Cofense Triage")

    def api_url(self, endpoint):
        """Return a full URL for the configured Triage host and the specified endpoint"""

        endpoint = endpoint.lstrip("/")
        return f"{self.host}/api/public/v1/{endpoint}"

    def get_demisto_param(self, name):
        return self.demisto_params[name]


class TriageReport:
    """
    Class representing a Triage report by an end-user of a suspicious message

    Model associations:
    TriageReporter - The user who reported the message. A TriageReport has exactly one TriageReporter.
    """

    def __init__(self, triage_instance, attrs):
        self.triage_instance = triage_instance
        self.attrs = attrs

    @property
    def id(self):
        return self.attrs["id"]

    @property
    def date(self):
        return self.attrs.get("created_at")

    @property
    def category_name(self):
        return {
            1: "Non-Malicious",
            2: "Spam",
            3: "Crimeware",
            4: "Advanced Threats",
            5: "Phishing Simulation",
        }.get(self.attrs["category_id"], "Unknown")

    @property
    def severity(self):
        # Demisto's severity levels are 4 - Critical, 3 - High, 2 - Medium, 1 - Low, 0 - Unknown
        return {
            1: 1,  # non malicious -> low
            2: 0,  # spam -> unknown
            3: 2,  # crimeware -> medium
            4: 2,  # advanced threats -> medium
            5: 1,  # phishing simulation -> low
        }.get(self.attrs["category_id"], 0)

    @property
    def report_subject(self):
        return self.attrs.get("report_subject")

    @property
    def report_body(self):
        return self.attrs.get("report_body")

    @property
    def email_urls(self):
        return [email_url["url"] for email_url in self.attrs["email_urls"]]

    @property
    def email_attachment_hashes(self):
        return [
            [
                att["email_attachment_payload"]["md5"],
                att["email_attachment_payload"]["sha256"],
            ]
            for att in self.attrs["email_attachments"]
        ]

    @property  # type: ignore
    @functools.lru_cache
    def reporter(self):
        return TriageReporter(self.triage_instance, self.attrs["reporter_id"])

    @property
    def terse_attrs(self):
        return {key: self.attrs[key] for key in self.attrs.keys() & TERSE_FIELDS}

    def to_dict(self):
        return {
            **self.attrs,
            # Flatten the Reporter object to a set of `reporter_` prefixed attributes
            **{f"reporter_{k}": v for k, v in self.reporter.attrs.items()},  # type: ignore
        }

    def to_json(self):
        return json.dumps(self.to_dict())

    @property  # type: ignore
    @functools.lru_cache
    def attachment(self):
        if "HTML" in self.report_body:
            html_attachment = fileResult(filename=f"{self.id}-report.html", data=self.report_body.encode())
            attachment = {
                "path": html_attachment.get("FileID"),
                "name": html_attachment.get("File"),
            }
            return attachment

        return None

    @classmethod
    def fetch(cls, triage_instance, report_id):
        return cls(triage_instance, triage_instance.request(f"reports/{report_id}")[0])


class TriageInboxReports:
    """Represents a set of Triage reports from the `inbox` mailbox"""

    def __init__(
        self,
        triage_instance,
        *,
        start_date=None,
        end_date=None,
        filter_params={},
        max_pages=1,
    ):
        self.triage_instance = triage_instance
        self.start_date = start_date
        self.end_date = end_date
        self.filter = TriageInboxReportFilter(filter_params)
        self.max_pages = max_pages

    def inbox_reports(self):
        inbox_reports = [TriageReport(self.triage_instance, response) for response in self.fetch_reports()]

        return [report for report in inbox_reports if self.filter.is_match(report)]

    def fetch_reports(self):
        return itertools.chain.from_iterable(self.fetch_report_pages())

    def fetch_report_pages(self):
        return [
            self.triage_instance.request(
                "inbox_reports",
                params={
                    "start_date": self.start_date,
                    "end_date": self.end_date,
                    "page": page_num,
                },
            )
            for page_num in range(math.ceil(self.max_pages))
        ]


class TriageInboxReportFilter:
    """Performs filtering for Triage Reports"""

    def __init__(self, filter_params):
        self.subject = filter_params.get("subject")
        self.url = filter_params.get("url")
        self.file_hash = filter_params.get("file_hash")
        self.reporter_ids = filter_params.get("reporter_ids")

    def is_match(self, report):
        return (
            (not self.subject or self.subject in report.report_subject)
            and (not self.url or any(self.url in email_url for email_url in report.email_urls))
            and (not self.file_hash or any(self.file_hash in h for h in report.email_attachment_hashes))
            and (not self.reporter_ids or report.reporter.id in self.reporter_ids)
        )


class TriageReporters:
    """
    A set of Triage reporters
    """

    def __init__(self, triage_instance, *, email=None, max_pages=1):
        self.triage_instance = triage_instance
        self.email = email
        self.max_pages = max_pages

    def reporters(self):
        return [
            TriageReporter(
                self.triage_instance, response["id"]
            )  # TODO causes unnecessary extra request---we can just pass in `response`, which has all the fields already # noqa: E501
            for response in self.fetch_reporters()
        ]

    def fetch_reporters(self):
        return itertools.chain.from_iterable(self.fetch_reporter_pages())

    def fetch_reporter_pages(self):
        for page_num in range(math.ceil(self.max_pages)):
            yield self.triage_instance.request("reporters", params={"email": self.email, "page": page_num})


class TriageReporter:
    """
    Class representing an end user who has reported a suspicious message

    Model associations:
    TriageReport - A reporter submitted by this user. A TriageReporter may have many TriageReports.
    """

    def __init__(self, triage_instance, reporter_id):
        """Fetch data for the first matching reporter from Triage"""
        matching_reporters = triage_instance.request(f"reporters/{reporter_id}")

        if matching_reporters:
            self.attrs = matching_reporters[0]
        else:
            self.attrs = {}

    @property
    def id(self):
        return self.attrs["id"]

    @property
    def email(self):
        return self.attrs["email"]

    def exists(self):
        return bool(self.attrs)


def snake_to_camel_keys(snake_list: list[dict]) -> list[dict]:
    def snake_to_camel(snake_str) -> str:
        if snake_str == "id":
            return "ID"
        components = snake_str.split("_")
        return "".join(x.title() for x in components)

    return [{snake_to_camel(k): v for k, v in snake_d.items()} for snake_d in snake_list]


def split_snake(string: str) -> str:
    return string.replace("_", " ").title()


def parse_triage_date(date: str):
    # datetime from isoformat only supports a subset of ISO-8601.
    # See https://discuss.python.org/t/parse-z-timezone-suffix-in-datetime/2220
    if date.endswith("Z"):
        date = date[:-1] + "+00:00"
    return datetime.fromisoformat(date)


def test_function(triage_instance) -> None:
    try:
        response = triage_instance.request("processed_reports")

        if response:
            demisto.results("ok")
        else:
            raise TriageRequestFailedError(
                response.status_code,
                "API call to Cofense Triage failed. Please check integration configuration.\nReason: {response.reason}",
            )
    except Exception as err:
        demisto.debug(str(err))
        raise err


def fetch_reports(triage_instance) -> None:
    """Fetch up to `max_reports` reports since the last time the command was run."""
    start_date = triage_instance.get_demisto_param("start_date")
    max_fetch = triage_instance.get_demisto_param("max_fetch")

    if triage_instance.get_demisto_param("mailbox_location") == "Inbox_Reports":
        endpoint = "inbox_reports"
    else:
        endpoint = "processed_reports"

    triage_response = triage_instance.request(
        endpoint,
        params={
            "category_id": triage_instance.get_demisto_param("category_id"),
            "match_priority": triage_instance.get_demisto_param("match_priority"),
            "tags": triage_instance.get_demisto_param("tags"),
            "start_date": start_date,
        },
    )

    already_fetched = set(json.loads(demisto.getLastRun().get("reports_fetched", "[]")))

    triage_reports = [TriageReport(triage_instance, report) for report in triage_response if report["id"] not in already_fetched]

    incidents = []
    for report in triage_reports:
        incident = {
            "name": f"cofense triage report {report.id}: {report.category_name}",
            "occurred": report.date,
            "rawJSON": report.to_json(),
            "severity": report.severity,
        }

        if report.attachment:
            incident["attachment"] = [report.attachment]

        incidents.append(incident)
        already_fetched.add(report.id)
        if len(incidents) >= max_fetch:
            break

    demisto.incidents(incidents)
    demisto.setLastRun({"reports_fetched": json.dumps(list(already_fetched))})


def search_reports_command(triage_instance) -> None:
    subject = demisto.getArg("subject")  # type: str
    url = demisto.getArg("url")  # type: str
    file_hash = demisto.getArg("file_hash")  # type: str
    reported_at = parse_date_range(demisto.args().get("reported_at", "7 days"))[0].replace(tzinfo=timezone.utc)  # noqa: UP017
    created_at = parse_date_range(demisto.args().get("created_at", "7 days"))[0].replace(tzinfo=timezone.utc)  # noqa: UP017
    try:
        max_matches = int(demisto.getArg("max_matches"))  # type: int
    except ValueError:
        return_error("max_matches must be an integer if specified")
        return
    verbose = demisto.getArg("verbose") == "true"

    try:
        reporters_clause = build_reporters_clause(triage_instance)
    except TriageNoReportersFoundError:
        return_outputs("Reporter not found.", {}, {})
        return

    results = search_reports(
        triage_instance,
        subject,
        url,
        file_hash,
        reported_at,
        created_at,
        reporters_clause,
        verbose,
        max_matches,
    )

    if results:
        ec = {"Cofense.Report(val.ID && val.ID == obj.ID)": snake_to_camel_keys(results)}
        hr = tableToMarkdown("Reports:", results, headerTransform=split_snake, removeNull=True)

        return_outputs(hr, ec, results)
    else:
        return_outputs("no results were found.", {}, {})


def search_reports(
    triage_instance,
    subject=None,
    url=None,
    file_hash=None,
    reported_at=None,
    created_at=None,
    reporters_clause={},
    verbose=False,
    max_matches=30,
) -> list:
    params = {"start_date": reported_at, **reporters_clause}
    reports = triage_instance.request("processed_reports", params=params)

    matches = []

    for report in reports:
        if subject and subject != report.get("report_subject"):
            continue
        if url and url not in [email_url["url"] for email_url in report["email_urls"]]:
            continue
        if created_at and "created_at" in report and created_at >= parse_triage_date(report["created_at"]):
            continue
        if (
            file_hash
            and file_hash not in [attachment["email_attachment_payload"]["md5"] for attachment in report["email_attachments"]]
            and file_hash not in [attachment["email_attachment_payload"]["sha256"] for attachment in report["email_attachments"]]
        ):
            continue

        if not verbose:
            # extract only relevant fields
            report = {key: report[key] for key in report.keys() & TERSE_FIELDS}

        matches.append(report)
        if len(matches) >= max_matches:
            break

    return matches


def search_inbox_reports_command(triage_instance) -> None:
    reported_at = parse_date_range(demisto.args().get("reported_at", "7 days"))[0].replace(tzinfo=timezone.utc)  # noqa: UP017

    try:
        reporters_clause = build_reporters_clause(triage_instance)
    except TriageNoReportersFoundError:
        return_outputs("Reporter not found.", {}, {})
        return

    try:
        max_matches = int(demisto.getArg("max_matches")) or 10
    except ValueError:
        return_error("max_matches must be an integer if specified")
        return

    max_pages = math.ceil(max_matches / 10)  # Triage's number of items per page, rounded up

    reports = TriageInboxReports(
        triage_instance,
        start_date=reported_at,
        filter_params={
            "subject": demisto.getArg("subject"),
            "url": demisto.getArg("url"),
            "file_hash": demisto.getArg("file_hash"),
            **reporters_clause,
        },
        max_pages=max_pages,
    ).inbox_reports()

    if not reports:
        return_outputs("No results found.", {}, {})
        return

    reports_dict = [report.to_dict() for report in reports][:max_matches]
    ec = {"Cofense.Report(val.ID && val.ID == obj.ID)": snake_to_camel_keys(reports_dict)}
    hr = tableToMarkdown("Reports:", reports_dict, headerTransform=split_snake, removeNull=True)

    return_outputs(hr, ec, reports_dict)


def build_reporters_clause(triage_instance):
    reporter_address_or_id = demisto.getArg("reporter")

    if not reporter_address_or_id:
        return {}

    if reporter_address_or_id.isdigit():
        return {"reporter_ids": [int(reporter_address_or_id)]}

    reporters = TriageReporters(triage_instance, email=reporter_address_or_id).reporters()

    if len(reporters) == 0 or not any(reporter.exists() for reporter in reporters):
        raise TriageNoReportersFoundError

    return {"reporter_ids": [reporter.id for reporter in reporters]}


def get_all_reporters(triage_instance, time_frame) -> list:
    res = triage_instance.request("reporters", params={"start_date": time_frame})
    if not isinstance(res, list):
        res = [res]
    reporters = [reporter.get("email") for reporter in res]

    return reporters


def get_reporter_command(triage_instance) -> None:
    reporter_id = demisto.getArg("reporter_id")

    reporter = TriageReporter(triage_instance, reporter_id)

    if not reporter.exists():
        return return_outputs(
            readable_output="Could not find reporter with matching ID",
            outputs=reporter_id,
            raw_response=json.dumps(reporter.attrs),
        )

    camel_case_attrs = snake_to_camel_keys([reporter.attrs])[0]
    return_outputs(
        outputs={"Cofense.Reporter(val.Id && val.Id == obj.Id)": camel_case_attrs},
        readable_output=tableToMarkdown(
            "Reporter Results:",
            reporter.attrs,
            headerTransform=split_snake,
            removeNull=True,
        ),
        raw_response=json.dumps(reporter.attrs),
    )
    return None


def get_attachment_command(triage_instance) -> None:
    attachment_id = str(demisto.getArg("attachment_id"))  # type: str
    file_name = demisto.getArg("file_name") or attachment_id  # type: str

    res = triage_instance.request(f"attachment/{attachment_id}", raw_response=True)

    result = fileResult(file_name, res.content)
    demisto.results(result)


def get_report_by_id_command(triage_instance) -> None:
    report_id = int(demisto.getArg("report_id"))  # type: int
    verbose = demisto.getArg("verbose") == "true"

    report = TriageReport.fetch(triage_instance, report_id)

    if not report:
        raise TriageRequestEmptyResponse(report_id, "Report")

    if verbose:
        report_attrs = report.attrs
    else:
        report_attrs = report.terse_attrs

    if report.attachment:
        demisto.results(
            {
                **report.attachment,
                "HumanReadable": "### Cofense HTML Report:\nHTML report download request has been completed",
            }
        )
        del report_attrs["report_body"]

    hr = tableToMarkdown("Report Summary:", report_attrs, headerTransform=split_snake, removeNull=True)
    ec = {"Cofense.Report(val.ID && val.ID == obj.ID)": snake_to_camel_keys([report_attrs])}
    return_outputs(readable_output=hr, outputs=ec, raw_response=report.to_json())


def get_threat_indicators_command(triage_instance) -> None:
    results = triage_instance.request(
        "triage_threat_indicators",
        params={
            "type": demisto.getArg("type"),
            "level": demisto.getArg("level"),
            "start_date": demisto.getArg("start_date"),
            "end_date": demisto.getArg("end_date"),
            "page": demisto.getArg("page"),
            "per_page": demisto.getArg("per_page"),
        },
    )

    if not results:
        return return_outputs("no results were found.", {})

    demisto.results(
        {
            "Type": entryTypes["note"],
            "ContentsFormat": formats["markdown"],
            "Contents": results if results else "no results were found",
            "HumanReadable": tableToMarkdown(
                "Threat Indicators:",
                results,
                headerTransform=split_snake,
                removeNull=True,
            ),
            "EntryContext": {"Cofense.ThreatIndicators(val.ID && val.ID == obj.ID)": snake_to_camel_keys(results)},
        }
    )
    return None


def get_report_png_by_id_command(triage_instance) -> None:
    report_id = int(demisto.getArg("report_id"))  # type: int
    set_white_bg = demisto.args().get("set_white_bg", "False") == "True"  # type: bool

    orig_png = get_report_png_by_id(triage_instance, report_id)

    if set_white_bg:
        in_buffer = BytesIO()
        in_buffer.write(orig_png)
        in_buffer.seek(0)

        image = Image.open(in_buffer)
        canvas = Image.new("RGBA", image.size, (255, 255, 255, 255))  # Empty canvas colour (r,g,b,a)
        canvas.paste(image, mask=image)  # Paste the image onto the canvas, using it's alpha channel as mask

        out_buffer = BytesIO()
        canvas.save(out_buffer, format="PNG")
        out_buffer.seek(0)

        image_data = out_buffer.getvalue()
    else:
        image_data = orig_png

    cf_file = fileResult(f"cofense_report_{report_id}.png", image_data, entryTypes["image"])
    demisto.results(
        {
            "Type": entryTypes["image"],
            "ContentsFormat": formats["text"],
            "Contents": f"Cofense: PNG of Report {report_id}",
            "File": cf_file.get("File"),
            "FileID": cf_file.get("FileID"),
        }
    )


def get_report_png_by_id(triage_instance, report_id):
    """Fetch and return the PNG file associated with the specified report_id"""
    return triage_instance.request(f"reports/{report_id}.png", raw_response=True).content


def build_triage_instance():
    demisto_params = {
        "start_date": parse_date_range(demisto.getParam("date_range"))[0].isoformat(),
        "max_fetch": int(demisto.getParam("max_fetch")),
        "category_id": demisto.getParam("category_id"),
        "match_priority": demisto.getParam("match_priority"),
        "tags": demisto.getParam("tags"),
        "mailbox_location": demisto.getParam("mailbox_location"),
    }

    return TriageInstance(
        host=demisto.getParam("host").rstrip("/") if demisto.getParam("host") else "",
        token=demisto.getParam("token"),
        user=demisto.getParam("user"),
        disable_tls_verification=demisto.params().get("insecure", False),
        demisto_params=demisto_params,
    )


def main():
    try:
        handle_proxy()

        triage_instance = build_triage_instance()

        if demisto.command() == "test-module":
            test_function(triage_instance)

        if demisto.command() == "fetch-incidents":
            fetch_reports(triage_instance)

        elif demisto.command() == "cofense-search-reports":
            search_reports_command(triage_instance)

        elif demisto.command() == "cofense-search-inbox-reports":
            search_inbox_reports_command(triage_instance)

        elif demisto.command() == "cofense-get-attachment":
            get_attachment_command(triage_instance)

        elif demisto.command() == "cofense-get-reporter":
            get_reporter_command(triage_instance)

        elif demisto.command() == "cofense-get-report-by-id":
            get_report_by_id_command(triage_instance)

        elif demisto.command() == "cofense-get-report-png-by-id":
            get_report_png_by_id_command(triage_instance)

        elif demisto.command() == "cofense-get-threat-indicators":
            get_threat_indicators_command(triage_instance)

    except Exception as e:
        return_error(str(e))
        raise


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