Agari Phishing Defense
Agari Phishing Defense stops phishing, BEC, and other identity deception attacks that trick employees into harming your business.
Data Enrichment & Threat Intelligence · Agari Phishing Defense
Details
| ID | Agari Phishing Defense |
|---|---|
| Provider | Fortra |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM EDR Cortex Cloud Cloud Runtime Security |
README
Agari Phishing Defense stops phishing, BEC, and other identity deception attacks that trick employees into harming your business.
This integration was integrated and tested with a standard version of Agari Phishing Defense.
Configure Agari Phishing Defense in Cortex
| Parameter | Description | Required |
|---|---|---|
| url | URL to connect to Agari | True |
| apikey | API Key | True |
| apisecret | Secret Key | True |
| max_fetch | Maximum number of incidents to fetch every time | True |
| first_fetch | First fetch time interval | False |
| fetch_policy_actions | Policy Action | False |
| exclude_alert_type | Exclude alerts | False |
| policy_filter | Fetches policy events to limit the amount of data. Can be applied to specific fields | False |
| incidentType | Incident type | False |
| isFetch | Fetch incidents | 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.
apd-list-policy-events
Retrieves a list of policy events.
Base Command
apd-list-policy-events
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of items to be returned in the paged response. | Optional |
| start_date | The earliest date time (UTC) a search should target (ISO 8601 format). Formats accepted: YYYY-MM-dd YYYY-MM-ddTHH:mm:ss N days N hours Example: 2020-05-01 2020-05-01T00:00:00 2 days 5 hours. |
Optional |
| end_date | The latest date time (UTC) a search should target (ISO 8601 format). Formats accepted: YYYY-MM-dd YYYY-MM-ddTHH:mm:ss N days N hours Example: 2020-05-01 2020-05-01T00:00:00 2 days 5 hours. |
Optional |
| page_id | To page through a collection of policy events. | Optional |
| sort | A comma-delimited string that specifies the field ordering to be applied to the response. Example: created_at DESC, id ASC. | Optional |
| add_fields | A comma-delimited list of optional fields to add to the default payload. Additional fields would add data in the entry context. | Optional |
| rem_fields | A comma-delimited list of fields to remove from the default payload. Limited fields would return limited data in entry context. | Optional |
| fields | A comma-delimited list of fields to include in the payload. Limited fields would return limited data in entry context. | Optional |
| filter | Search filters that can be applied to the response. | Optional |
| exclude_alert_types | Exclude policy types such as ‘MessageAlert’ or ‘SystemAlert’. | Optional |
| policy_name | Find by policy name. | Optional |
| policy_action | Filter by policy action: ‘deliver’, ‘mark-spam’, ‘move’, ‘inbox’, ‘delete’ and ‘none’. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AgariPhishingDefense.Alert.alert_definition_name | String | Alert definition name. |
| AgariPhishingDefense.Alert.id | String | Unique alert id. |
| AgariPhishingDefense.Alert.summary | String | Summary of the alert. |
| AgariPhishingDefense.Alert.policy_action | String | Alert policy action. |
| AgariPhishingDefense.Alert.policy_enabled | Boolean | Indicates if policy is enabled. |
| AgariPhishingDefense.Alert.updated_at | Date | Updated time of the alert. The format is ISO8601. |
| AgariPhishingDefense.Alert.created_at | Date | Created time of the alert. The format is ISO8601. |
| AgariPhishingDefense.Alert.admin_recipients | Unknown | List of notified admin recipients. |
| AgariPhishingDefense.Alert.notified_original_recipients | Boolean | Indicates whether the original recipient was notified. |
Command Example
!apd-list-policy-events limit=2
Context Example
{
"AgariPhishingDefense": {
"Alert": [
{
"alert_definition_name": "Spoof of Partner Domains",
"created_at": "2020-12-03T04:32:23Z",
"id": 549904303,
"notified_original_recipients": false,
"policy_action": "none",
"policy_enabled": true,
"summary": false,
"updated_at": "2020-12-03T04:32:23Z"
},
{
"alert_definition_name": "Untrusted Messages",
"created_at": "2020-12-03T04:32:23Z",
"id": 549904302,
"notified_original_recipients": false,
"policy_action": "none",
"policy_enabled": true,
"summary": false,
"updated_at": "2020-12-03T04:32:23Z"
}
]
}
}
Human Readable Output
Policy Events
Event ID Alert Definition Name Policy Action Notified Original Recipients Created Updated 549904303 Spoof of Partner Domains none false 2020-12-03T04:32:23Z 2020-12-03T04:32:23Z 549904302 Untrusted Messages none false 2020-12-03T04:32:23Z 2020-12-03T04:32:23Z
apd-list-message-data
Retrieves a list of messages.
Base Command
apd-list-message-data
Input
| Argument Name | Description | Required |
|---|---|---|
| start_date | The earliest date time (UTC) a search should target (ISO 8601 format). Formats accepted: YYYY-MM-dd YYYY-MM-ddTHH:mm:ss N days N hours Example: 2020-05-01 2020-05-01T00:00:00 2 days 5 hours. |
Optional |
| end_date | The latest date time (UTC) a search should target (ISO 8601 format). Formats accepted: YYYY-MM-dd YYYY-MM-ddTHH:mm:ss N days N hours Example: 2020-05-01 2020-05-01T00:00:00 2 days 5 hours. |
Optional |
| add_fields | A comma-delimited list of optional fields to add to the default payload. Additional fields would add data in the entry context. | Optional |
| rem_fields | A comma-delimited list of fields to remove from the default payload. Limited fields would return limited data in entry context. | Optional |
| fields | A comma-delimited list of fields to include in the payload. Limited fields would return limited data in entry context. | Optional |
| limit | The maximum number of items to be returned in the paged response. | Optional |
| page_id | To page through a collection of message data. | Optional |
| sort | A comma-delimited string that specifies the field ordering to be applied to the response. | Optional |
| search | Search using advanced search syntax. Format: field operator operand {and/or field operator operand} Example: has_attachment=true and ip=’10.0.0.0’ sbrs in [3.5, 2.6] domain_reputation is not null sbrs gt 3 sbrs>=3 and domain_tags eq internal |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| AgariPhishingDefense.Message.has_attachment | Boolean | Has attachment. |
| AgariPhishingDefense.Message.ip | String | IP address. |
| AgariPhishingDefense.Message.message_id | String | The Global message ID. |
| AgariPhishingDefense.Message.ptr_name | String | PTR name. |
| AgariPhishingDefense.Message.sbrs | String | SBRS. |
| AgariPhishingDefense.Message.id | String | The internal message ID. |
| AgariPhishingDefense.Message.authenticity | Number | Authenticity score. |
| AgariPhishingDefense.Message.to | String | Message recipient. |
| AgariPhishingDefense.Message.date | String | Date in ISO format. |
| AgariPhishingDefense.Message.timestamp_ms | Number | Timestamp in ms since epoch. |
| AgariPhishingDefense.Message.from | String | Sender’s email. |
| AgariPhishingDefense.Message.from_domain | String | From domain. |
| AgariPhishingDefense.Message.subject | String | Message subject. |
| AgariPhishingDefense.Message.domain_reputation | Number | Reputation of sender domain. |
| AgariPhishingDefense.Message.message_trust_score | Number | Risk score. |
| AgariPhishingDefense.Message.message_details_link | String | Link to message details. |
| AgariPhishingDefense.Message.domain_tags | Unknown | List of domain tags. |
| AgariPhishingDefense.Message.mail_from | String | Mail from domain. |
| AgariPhishingDefense.Message.reply_to | String | Reply-to address. |
| AgariPhishingDefense.Message.uris | Unknown | List of URIs. |
| AgariPhishingDefense.Message.attachment_extensions | Unknown | List of message attachment extensions. |
| AgariPhishingDefense.Message.attachment_filenames | Unknown | List of message attachment filenames. |
| AgariPhishingDefense.Message.attachment_sha256 | Unknown | List of message attachment SHA256 hashes. |
| AgariPhishingDefense.Message.attachment_types | Unknown | List of message attachment types. |
| AgariPhishingDefense.Message.attack_types | Unknown | List of attack type classifications. |
| AgariPhishingDefense.Message.dkim_result | String | DKIM result. |
| AgariPhishingDefense.Message.dmarc_result | String | DMARC result. |
| AgariPhishingDefense.Message.domain_dmarc_policy | String | DMARC policy for domain. |
| AgariPhishingDefense.Message.enforcement_action | String | Enforcement action. |
| AgariPhishingDefense.Message.enforcement_folder | String | Enforcement folder. |
| AgariPhishingDefense.Message.enforcement_result | String | Enforcement result. |
| AgariPhishingDefense.Message.expanded_from | String | Expanded from. |
| AgariPhishingDefense.Message.forwarded_from | String | Forwarded from. |
| AgariPhishingDefense.Message.has_malicious_attachment | Boolean | Has malicious attachment. |
| AgariPhishingDefense.Message.message_read_status | Boolean | Message read status. |
| AgariPhishingDefense.Message.org_domain | String | Organization domain. |
| AgariPhishingDefense.Message.policy_ids | Unknown | List of triggered policy IDs. |
| AgariPhishingDefense.Message.sender_approval_state | String | Sender approval state. |
| AgariPhishingDefense.Message.sender_type | String | Sender type. |
| AgariPhishingDefense.Message.spf_result | String | SPF result. |
Command Example
!apd-list-message-data limit=2
Context Example
{
"AgariPhishingDefense": {
"Message": [
{
"attack_types": [
"spoof (Domain spoof)"
],
"authenticity": "0.085819915",
"date": "2020-12-03T02:07:02+00:00",
"domain_reputation": "9.0",
"domain_tags": [
"partner"
],
"enforcement_action": "move",
"enforcement_result": "pending",
"from": "Accounts@abc.com",
"from_domain": "abc.com",
"has_attachment": "false",
"id": "785d91a8-34fb-11eb-bf90-f6ba445dac4f",
"ip": "1.2.3.4",
"message_details_link": "https://apis.com/messages/785d91a8-34fb-11eb-bf90-f6ba445dac4f",
"message_id": "<facade2c8712345c91c755d17f1134cb@BY2PR12MB0054.abc.com>",
"message_trust_score": "0.6",
"policy_ids": [
9014,
9008,
2843446
],
"subject": "Please approve and forward expense report \"December Expenses\"",
"timestamp_ms": "1606961222000",
"to": "acoyle@xyz.com"
},
{
"attack_types": [
"spoof (Domain spoof)"
],
"authenticity": "0.07902577",
"date": "2020-12-03T02:07:02+00:00",
"domain_reputation": "8.6",
"domain_tags": [
"internal"
],
"enforcement_action": "move",
"enforcement_result": "pending",
"from": "help@xyz.com",
"from_domain": "xyz.com",
"has_attachment": "false",
"id": "7852dc68-34fb-11eb-bf90-f6ba445dac4f",
"ip": "2.2.3.4",
"message_details_link": "https://apis.com/messages/7852dc68-34fb-11eb-bf90-f6ba445dac4f",
"message_id": "<facade2c12345f9c91c755d17f1134cb@BY2PR12MB0054.xyz.com>",
"message_trust_score": "0.8",
"policy_ids": [
9014,
9009,
2843455
],
"subject": "Please approve and forward expense report \"December Expenses\"",
"timestamp_ms": "1606961222000",
"to": "aarmstrong@xyz.com"
}
]
}
}
Human Readable Output
Messages
ID From To Subject Message Trust Score Domain Reputation IP Authenticity Attack Types Date 785d91a8-34fb-11eb-bf90-f6ba445dac4f Accounts@abc.com acoyle@xyz.com Please approve and forward expense report “December Expenses” 0.6 9.0 1.2.3.4 0.085819915 spoof (Domain spoof) 2020-12-03T02:07:02+00:00 7852dc68-34fb-11eb-bf90-f6ba445dac4f help@xyz.com aarmstrong@xyz.com Please approve and forward expense report “December Expenses” 0.8 8.6 2.2.3.4 0.07902577 spoof (Domain spoof) 2020-12-03T02:07:02+00:00
apd-remediate-message
Remediate suspected message.
Base Command
apd-remediate-message
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The primary identifier to remediate a message (UUID). To retrieve the ID execute the apd-list-message-data command. | Required |
| operation | An operation to remediate a message. Remediation operation is either ‘delete’ or ‘move’. | Required |
Context Output
There is no context output for this command.
Command Example
!apd-remediate-message id="0e43a684-2e0e-11eb-815a-0a8f2da72108" operation="move"
Context Example
{}
Human Readable Output
Message ID - 0e43a684-2e0e-11eb-815a-0a8f2da72108 remediated successfully with operation ‘move’.
Configuration parameters
url— URL to connect to Agari (required)apikey— API Key (required)apisecret— Secret Key (required)max_fetch— Maximum number of incidents to fetch every time (required)first_fetch— First fetch time intervalfetch_policy_actions— Policy action takenexclude_alert_type— Exclude alertspolicy_filter— Fetches policy events as per the filter to limit the amount of data. Can be applied to specific fields.incidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalisFetch— Fetch incidentsinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (3)
-
apd-list-message-dataRetrieves a list of messages.
-
apd-list-policy-eventsRetrieves a list of policy events.
-
apd-remediate-messageRemediate suspected message.
from unittest import mock from unittest.mock import patch import pytest from requests.exceptions import ( MissingSchema, InvalidSchema, SSLError, InvalidURL, HTTPError, ) from CommonServerPython import * API_TOKEN = "dummy_token" CONTENT_TYPE_JSON = "application/json" SAMPLE_URL = "https://sample.api.com" AUTHENTICATION_RESP_HEADER = { "access_token": API_TOKEN, "token_type": "Bearer", "expires_in": 14400, "created_at": 1604920579, } PAYLOAD = "client_id=client_dummy_id&client_secret=client_dummy_secret " MOCK_INTEGRATION_CONTEXT = { "api_token": API_TOKEN, "valid_until": time.time() + 14400, } PARAMS = { "url": SAMPLE_URL, "apikey": "client_dummy_api_key", "apisecret": "client_dummy_api_secret", "isFetch": False, "incidentType": "Agari Phishing Defense Policy Event", "max_fetch": 40, "first_fetch": "3 days", "fetch_policy_actions": "none", "policy_filter": "", } SAMPLE_FETCH_DATE = "2020-11-22T12:41:36Z" MOCK_TEST_URL_SUFFIX = "/test/url/suffix" """ HELPER FUNCTION""" @pytest.fixture() @patch("AgariPhishingDefense.Client.get_api_token") @patch("demistomock.setIntegrationContext") @patch("demistomock.getIntegrationContext") def client(mocker_get_context, mocker_set_context, mocker_get_api_token): from AgariPhishingDefense import Client mocker_get_context.return_value = {} mocker_set_context.return_value = {} mocker_get_api_token.return_value = API_TOKEN, 14400 return Client( base_url=SAMPLE_URL, verify=False, proxy=False, payload=PAYLOAD, request_timeout=14400, ) def mock_http_response( status=200, headers=None, json_data=None, raise_for_status=None, text=None, content=None, ): mock_resp = mock.Mock() # mock raise_for_status call w/optional error mock_resp.raise_for_status = mock.Mock() if raise_for_status: mock_resp.raise_for_status.side_effect = raise_for_status # set status code mock_resp.status_code = status # add header if provided mock_resp.text = text mock_resp.content = content if headers: mock_resp.headers = headers mock_resp.ok = status < 400 # add json data if provided if json_data: mock_resp.json = mock.Mock(return_value=json_data) return mock_resp class MockResponse: def __init__(self, content, headers, status_code): self.content = content self.status_code = status_code self.headers = headers def text(self): return self.content def json(self): return json.loads(self.content) def raise_for_status(self): if self.status_code != 200: raise HTTPError("test") """ Unit Test Cases """ @patch("demistomock.debug") @patch("AgariPhishingDefense.Client.http_request") @patch("demistomock.setIntegrationContext") @patch("demistomock.getIntegrationContext") def test_get_api_token_when_not_found_in_integration_context( mocker_get_context, mocker_set_context, mock_request, mocker_demisto_debug, client, ): """ When get_api_token method called and headers is set with access-token also call_count is one, it should match. """ mocker_get_context.return_value = {} mocker_set_context.return_value = {} mocker_demisto_debug.return_value = True mock_request.return_value = AUTHENTICATION_RESP_HEADER api_token, valid = client.get_api_token() assert api_token == "Bearer " + AUTHENTICATION_RESP_HEADER["access_token"] assert mocker_set_context.call_count == 1 @patch("demistomock.debug") @patch("AgariPhishingDefense.Client._http_request") @patch("demistomock.getIntegrationContext") @patch("demistomock.setIntegrationContext") def test_get_api_token_when_found_in_integration_context( mocker_set_context, mocker_get_context, mock_request, mocker_demisto_debug, client, ): """ When get_api_token method called and headers is set with X-FeApi-Token also call_count is zero, it should match. """ mocker_get_context.return_value = MOCK_INTEGRATION_CONTEXT mocker_set_context.return_value = {} mocker_demisto_debug.return_value = True mock_request.return_value = AUTHENTICATION_RESP_HEADER api_token, valid = client.get_api_token() assert api_token == AUTHENTICATION_RESP_HEADER["access_token"] assert mocker_set_context.call_count == 0 @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.BaseClient._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_request_invalid_schema_error(mocker_api_token, mock_base_http_request, demisto_version, client): """ When http request return invalid schema exception then appropriate error message should match. """ # Configure mock_base_http_request.side_effect = InvalidSchema mocker_api_token.return_value = API_TOKEN, 14400 demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(ValueError) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert str(e.value) == "Invalid API URL. Supplied schema is invalid, supports http(s)." @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.BaseClient._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_proxy_error(mocker_get_api_token, mock_base_http_request, demisto_version, client): """ When http request return proxy error with exception then appropriate error message should match. """ # Configure mock_base_http_request.side_effect = DemistoException("Proxy Error") mocker_get_api_token.return_value = API_TOKEN, 14400 demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(ConnectionError) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert ( str(e.value) == "Proxy Error - cannot connect to proxy. Either try clearing the 'Use system proxy'" " check-box or check the host, authentication details and connection details for the proxy." ) @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.Client._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_request_connection_error(mocker_get_api_token, mock_base_http_request, demisto_version, client): """ When http request return connection error with Demisto exception then appropriate error message should match. """ # Configure mocker_get_api_token.return_value = API_TOKEN, 14400 mock_base_http_request.side_effect = DemistoException("ConnectionError") demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(ConnectionError) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert str(e.value) == "Connectivity failed. Check your internet connection or the API URL." @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.BaseClient._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_request_read_timeout_error(mocker_get_api_token, mock_base_http_request, demisto_version, client): """ When http request return connection error with Demisto exception then appropriate error message should match. """ # Configure mock_base_http_request.side_effect = DemistoException("ReadTimeoutError") mocker_get_api_token.return_value = API_TOKEN, 14400 demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(ConnectionError) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert str(e.value) == "Request timed out. Check the configured HTTP(S) Request Timeout (in seconds) value." @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.BaseClient._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_ssl_error(mocker_get_api_token, mock_base_http_request, demisto_version, client): """ When http request return ssl error with Demisto exception then appropriate error message should match. """ # Configure mock_base_http_request.side_effect = DemistoException("SSLError") mocker_get_api_token.return_value = API_TOKEN, 14400 demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(SSLError) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert ( str(e.value) == "SSL Certificate Verification Failed - try selecting 'Trust any certificate' checkbox " "in the integration configuration." ) @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.BaseClient._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_request_missing_schema_error(mocker_get_api_token, mock_base_http_request, demisto_version, client): """ When http request return MissingSchema exception then appropriate error message should display. """ # Configure mock_base_http_request.side_effect = MissingSchema mocker_get_api_token.return_value = API_TOKEN, 14400 demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(ValueError) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert str(e.value) == "Invalid API URL. No schema supplied: http(s)." @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.BaseClient._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_request_invalid_url_error(mocker_get_api_token, mock_base_http_request, demisto_version, client): """ When http request return invalid url exception then appropriate error message should match. """ # Configure mock_base_http_request.side_effect = InvalidURL mocker_get_api_token.return_value = API_TOKEN, 14400 demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(ValueError) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert str(e.value) == "Invalid API URL." @patch("demistomock.demistoVersion", create=True) @patch("AgariPhishingDefense.BaseClient._http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_http_request_other_demisto_exception(mocker_get_api_token, mock_base_http_request, demisto_version, client): """ When http request return other custom Demisto exception then appropriate error message should match. """ # Configure mock_base_http_request.side_effect = DemistoException("custom") mocker_get_api_token.return_value = API_TOKEN, 14400 demisto_version.return_value = {"version": "6.0.2"} # Execute with pytest.raises(Exception) as e: client.http_request("GET", MOCK_TEST_URL_SUFFIX, headers={}) # Assert assert str(e.value) == "custom" @patch("demistomock.results") @patch("demistomock.debug") @patch("demistomock.info") @patch("AgariPhishingDefense.Client.get_api_token") def test_main_success( mocker_get_api_token, mocker_demisto_debug, mocker_demisto_info, mocker_demisto_results, mocker, ): """ When main function called test function should call. """ import AgariPhishingDefense mocker_get_api_token.return_value = API_TOKEN, 14400 mocker_demisto_debug.return_value = True mocker_demisto_info.return_value = True mocker_demisto_results.return_value = True mocker.patch.object(demisto, "params", return_value=PARAMS) mocker.patch.object(demisto, "command", return_value="test-module") mocker.patch.object(AgariPhishingDefense, "test_function", return_value="ok") AgariPhishingDefense.main() assert AgariPhishingDefense.test_function.called def test_strip_blank(): import AgariPhishingDefense args = {"policy_name": " SystemAlert ", "limit": 4, "url": SAMPLE_URL} actual_output = { "policy_name": "SystemAlert", "limit": 4, "url": SAMPLE_URL, } assert AgariPhishingDefense.strip_blank(args) == actual_output @patch("demistomock.results") @patch("demistomock.info") @patch("demistomock.debug") @patch("AgariPhishingDefense.Client.get_api_token") def test_main_called_strip_function( mock_api_token, mocker_demisto_debug, mocker_demisto_info, mocker_demisto_results, mocker, ): import AgariPhishingDefense mocker_demisto_debug.return_value = True mocker_demisto_info.return_value = True mocker_demisto_results.return_value = True mock_api_token.return_value = API_TOKEN, 14400 mocker.patch.object(demisto, "params", return_value=PARAMS) mocker.patch.object(demisto, "command", return_value="agari-list-policy-events") mocker.patch.object(AgariPhishingDefense, "list_policy_events_command", return_value="ok") args = {"policy_name": " SystemAlert ", "limit": 4, "url": SAMPLE_URL} mocker.patch.object(demisto, "args", return_value=args) strip_blank = mocker.patch.object(AgariPhishingDefense, "strip_blank") AgariPhishingDefense.main() assert strip_blank.call_count > 0 @patch("AgariPhishingDefense.Client.http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_get_events_command_no_record_failure(mock_api_token, mock_request, client): """ When agari-list-policy-events command called passed with valid arguments but records are not present then it must return error message. """ mock_api_token.return_value = API_TOKEN from AgariPhishingDefense import list_policy_events_command args = {"policy_name": "SystemAlert", "limit": 4, "url": SAMPLE_URL} mock_request.return_value = {"alert_events": []} cmd_result = list_policy_events_command(client, args=args) assert cmd_result == "No event(s) were found for the given argument(s)." @patch("AgariPhishingDefense.Client.http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_get_events_command_success(mock_api_token, mock_request, client): """ When agari-list-policy-events command executes successfully then context output and response should match. """ from AgariPhishingDefense import list_policy_events_command mock_api_token.return_value = API_TOKEN args = {"policy_name": "SystemAlert", "limit": 4, "url": SAMPLE_URL} with open("test_data/get_events_response.json") as f: expected_res = json.load(f) mock_request.return_value = expected_res cmd_res = list_policy_events_command(client, args) with open("test_data/get_events_context.json", encoding="utf-8") as f: expected_outputs = json.load(f) with open("test_data/get_events.md") as f: expected_hr = f.read() assert cmd_res.raw_response["alert_events"] == expected_res["alert_events"] assert cmd_res.outputs == expected_outputs["alert_events"] assert cmd_res.readable_output == expected_hr def test_handle_error_response_when_status_code_not_in_list_then_raise_for_status(): """ When handle_error_response method called and status is not in list then it must raise DemistoException. """ from AgariPhishingDefense import Client err_res = { "version": 1, "status": "error", "code": 401, "error": "Unauthorized", "error_description": "You are not authorized to make that request.", } resp = MockResponse( content=str(err_res), headers={}, status_code=200, ) with pytest.raises(DemistoException): Client.handle_error_response(resp) @patch("demistomock.debug") def test_handle_error_response_when_content_not_type_json_throw_value_error( mocker_demisto_debug, ): """ When handle_error_response method called and json string have error then through ValueError and it passed and again raise DemistoException. """ from AgariPhishingDefense import Client mocker_demisto_debug.return_value = True resp = MockResponse(content="{[]}", headers={}, status_code=400) with pytest.raises(DemistoException) as e: Client.handle_error_response(resp) assert str(e.value) == "An error occurred while fetching the data. " def test_fetch_limit_when_valid_value_success(mocker): """ When valid fetch_limit is given, test should pass. """ from AgariPhishingDefense import get_fetch_limit mocker.patch.object(demisto, "params", return_value=PARAMS) fetch_limit = get_fetch_limit(fetch_limit="") assert fetch_limit == 50 def test_validate_fetch_policy_action(mocker): """ When valid policy_action is given, test should pass. """ from AgariPhishingDefense import validate_fetch_policy_action with pytest.raises(ValueError) as e: validate_fetch_policy_action("None") assert ( str(e.value) == 'The given value for Policy Actions is invalid. Expected "deliver", "mark-spam", "move", ' '"inbox", "delete" or "none". ' ) def test_validate_exclude_alert_type(mocker): """ When valid policy_action is given, test should pass. """ from AgariPhishingDefense import validate_exclude_alert_type with pytest.raises(ValueError) as e: validate_exclude_alert_type("SystemAlert") assert str(e.value) == 'The given value for Exclude Alerts is invalid. Expected "System Alert" or "Message Alert". ' def test_event_params_invalid_limit(): """ When limit is negative or greater than 200 then through value error. """ from AgariPhishingDefense import get_events_params args = {"limit": "2as"} with pytest.raises(ValueError) as e: get_events_params(args=args, max_record=200) assert str(e.value) == "Argument limit must be a positive integer between 1 to 200." def test_event_params_invalid_page_id(): """ When page_id is negative then through value error. """ from AgariPhishingDefense import get_events_params args = {"page_id": "-1", "limit": "50"} with pytest.raises(ValueError) as e: get_events_params(args=args, max_record=200) assert str(e.value) == "Argument page_id must be a positive integer." def test_event_params_invalid_start_date(): """ When start_date is invalid then through value error. """ from AgariPhishingDefense import get_events_params args = {"start_date": "2"} with pytest.raises(ValueError) as e: get_events_params(args=args) assert str(e.value) == "The given value for start_date argument is invalid." def test_event_params_invalid_end_date(): """ When end_date is invalid then through value error. """ from AgariPhishingDefense import get_events_params args = {"end_date": "end_date"} with pytest.raises(ValueError) as e: get_events_params(args=args) assert str(e.value) == "The given value for end_date argument is invalid." def test_event_params_invalid_rem_fields(): """ When end_date is invalid then through value error. """ from AgariPhishingDefense import get_events_params args = {"rem_fields": "id,created_at"} with pytest.raises(ValueError) as e: get_events_params(args=args) assert str(e.value) == 'Cannot pass "id" in rem_fields argument.' @patch("AgariPhishingDefense.Client.http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_remediate_message_valid_id_passed(mock_api_token, http_request, client): """ When valid ID (message id) is passed and API returns 200 """ from AgariPhishingDefense import remediate_message_command args = {"id": "53daff5a-2ac4-11eb-9375-0a8f2da72108", "operation": "move"} with open("test_data/remediate_success_resp.json") as f: expected_res = json.load(f) mock_api_token.return_value = API_TOKEN http_request.return_value = expected_res expected_hr = "Message ID - 53daff5a-2ac4-11eb-9375-0a8f2da72108 remediated successfully with operation 'move'." result = remediate_message_command(client, args) # ASSERT assert result.readable_output == expected_hr def test_remediate_when_action_is_wrong(client): from AgariPhishingDefense import remediate_message_command args = {"id": "53daff5a-2ac4-11eb-9375-0a8f2da72108", "action": " "} with pytest.raises(ValueError) as e: remediate_message_command(args=args, client=client) assert str(e.value) == 'Invalid argument value. Requires both "id" and "operation" argument.' def test_incident_params_invalid_fetch_limit(): """ When fetch_limit is invalid then through value error. """ from AgariPhishingDefense import fetch_incidents_params args = { "start_date": SAMPLE_FETCH_DATE, "fetch_limit": "300", "id": "", "policy_filter": "", } with pytest.raises(ValueError) as e: fetch_incidents_params(**args) assert str(e.value) == "Value of Fetch Limit must be a positive integer between 1 to 200." def test_incident_params_valid(): """ Check for all valid params. """ from AgariPhishingDefense import fetch_incidents_params args = { "start_date": SAMPLE_FETCH_DATE, "id": "544635182", "fetch_limit": "", "policy_filter": "created_at.gt(2020-11-22T12:59:59Z)", "fetch_policy_actions": "inbox", "exclude_alert_type": "System Alert", } params = fetch_incidents_params(**args) assert params["exclude_alert_types"] == "SystemAlert" assert params["sort"] == "created_at ASC,id ASC" assert params["filter"] == "created_at.gt(2020-11-22T12:59:59Z) and id.gt(+544635182)" @patch("AgariPhishingDefense.Client.http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_fetch_incidents_command_no_record_failure(mock_api_token, mock_request, client): """ When fetch_incidents command called passed with valid arguments but records are not present then it must return error message. """ mock_api_token.return_value = API_TOKEN from AgariPhishingDefense import fetch_incidents args = { "policy_name": "SystemAlert", "limit": 2, "url": SAMPLE_URL, "start_date": SAMPLE_FETCH_DATE, "policy_filter": "inbox", } mock_request.return_value = {"alert_events": []} last_run = {"last_fetch": "2020-11-23T12:41:36Z"} cmd_result, _ = fetch_incidents(client, last_run=last_run, args=args) assert cmd_result == last_run @patch("AgariPhishingDefense.Client.http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_fetch_incident_command_success(mock_api_token, mock_request, client): """ When fetch_incidents command executes successfully then response should match """ from AgariPhishingDefense import fetch_incidents mock_api_token.return_value = API_TOKEN, 14400 args = { "start_date": "", "id": "", "fetch_limit": "1", "policy_filter": "", } with open("test_data/get_fetch_response.json") as f: expected_res = json.load(f) mock_request.side_effect = expected_res ans = [] cmd_res_next_run, incidents = fetch_incidents(client, {}, args) ans.append(cmd_res_next_run) ans.append(incidents) assert ans == expected_res[3] @patch("AgariPhishingDefense.Client.http_request") @patch("AgariPhishingDefense.Client.get_api_token") def test_list_message_data_command_success(mock_api_token, mock_request, client): """ When agari-list-policy-events command executes successfully then context output and response should match. """ from AgariPhishingDefense import list_message_data_command mock_api_token.return_value = API_TOKEN args = {"limit": 4, "url": SAMPLE_URL} with open("test_data/get_messages_response.json") as f: expected_res = json.load(f) mock_request.return_value = expected_res cmd_res = list_message_data_command(client, args) with open("test_data/get_messages_context.json", encoding="utf-8") as f: expected_outputs = json.load(f) with open("test_data/get_messages.md") as f: expected_hr = f.read() assert cmd_res.raw_response["messages"] == expected_res["messages"] assert cmd_res.outputs == expected_outputs["messages"] assert cmd_res.readable_output == expected_hr