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

IDAgari Phishing Defense
ProviderFortra
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix 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 interval
  • fetch_policy_actions — Policy action taken
  • exclude_alert_type — Exclude alerts
  • policy_filter — Fetches policy events as per the filter to limit the amount of data. Can be applied to specific fields.
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • isFetch — Fetch incidents
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (3)

  • apd-list-message-data

    Retrieves a list of messages.

  • apd-list-policy-events

    Retrieves a list of policy events.

  • apd-remediate-message

    Remediate 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