Cyren Inbox Security
Cyren Inbox Security is an innovative solution that safeguards Office 365 mailboxes in your organization against evasive phishing, business email compromise (BEC), and fraud. This integration imports incidents from Cyren Inbox Security into XSOAR, and includes a playbook for incident resolution.
Utilities · Cyren Inbox Security
Details
| ID | Cyren Inbox Security |
|---|---|
| Provider | Data443 |
| Category | Utilities |
| From Version | 6.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Cyren Inbox Security is an innovative solution that safeguards Office 365 mailboxes in your organization against evasive phishing, business email compromise (BEC), and fraud. This integration imports incidents from Cyren Inbox Security into XSOAR, and includes a playbook for incident resolution.
This integration was integrated and tested with version 1.0 of Cyren Inbox Security
Configure Cyren Inbox Security in Cortex
| Parameter | Description | Required |
|---|---|---|
| Server URL | The endpoint provided by your Cyren Representative. (use “sample” to test) | True |
| Client ID | The client iD provided by your Cyren Representative. (use “sample” to test) | True |
| Client Secret | The client secret provided by your Cyren Representative. (use “sample” to test) | True |
| First fetch time | 1 day, 2 days, etc… | False |
| Maximum number of incidents per fetch | False | |
| Incident type | False | |
| Fetch incidents | 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.
cyren-resolve-and-remediate
resolve a case and remediate incidents
Base Command
cyren-resolve-and-remediate
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | case ID. | Required |
| resolution | resolution. Possible values are: phishing, malware, clean, other. | Optional |
| resolution_reason | the reason of the resolution. Possible values are: Identified phishing URL, Identified suspicious sender, Other, Scam, Spam. | Optional |
| resolution_reason_text | free text for resolution reason. | Optional |
| actions | remediation actions to perform. Possible values are: MOVE_TO_SPAM, MOVE_TO_DELETED, ADD_BANNER, SOFT_DELETE, MOVE_TO_INBOX, REMOVE_BANNER. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Cyren.data.status | string | status of actions performed |
Command Example
!cyren-resolve-and-remediate resolution=phishing resolution_reason="Identified suspicious sender" case_id="62877980-6ac7-4944-b3fa-62ddf628a0fe" resolution_reason_text="I think it is phishing" actions=ADD_BANNER,MOVE_TO_DELETED
Context Example
{
"Cyren": {
"data": {
"status": "ok"
}
}
}
Human Readable Output
cyren-resolve-and-remediate results
status ok end of results
cyren-reset-sample-fetch
resets integration to fetch a sample incident
Base Command
cyren-reset-sample-fetch
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
There is no context output for this command.
Command Example
!cyren-reset-sample-fetch
Human Readable Output
A sample incident will be created on the next execution of system fetch-incidents command
Configuration parameters
url— Server URL (required)client_id— Client ID (required)client_secret— Client Secret (required)first_fetch— First fetch timemax_fetch— Maximum number of incidents per fetchincidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalisFetch— Fetch incidents
Commands (1)
-
cyren-resolve-and-remediateresolve a case and remediate incidents.
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 # noqa: F401 # noqa: F401 # noqa: F401 # noqa: F401 # noqa: F401 # noqa: F401 """ CyrenInboxSecurity Integration for Cortex XSOAR (formerly Demisto) """ import json import urllib3 # Disable insecure warnings urllib3.disable_warnings() """ CONSTANTS """ MAX_INCIDENTS_TO_FETCH = 50 """ CLIENT CLASS """ class Client(BaseClient): """Client class to interact with the service API This Client implements API calls, and does not contain any XSOAR logic. It handles requests and returns data. It inherits from BaseClient defined in CommonServer Python. Most calls use _http_request() that handles proxy, SSL verification, etc. """ def get_token(self, client_id, client_secret): request_params = {"client_id": client_id, "client_secret": client_secret, "grant_type": "client_credentials"} return self._http_request(method="POST", url_suffix="/v1/token", params=request_params) def get_incidents(self, token, date_from, max_fetch): request_params = {"date_from": date_from, "limit": max_fetch} headers = {"Authorization": f"Bearer {token}"} return self._http_request(method="GET", url_suffix="/v1/incidents", headers=headers, params=request_params) def resolve_and_remediate(self, token, case_id, **kwargs): headers = {"Authorization": f"Bearer {token}"} return self._http_request(method="PATCH", url_suffix=f"/v1/cases/{case_id}/resolution", headers=headers, json_data=kwargs) """ HELPER FUNCTIONS """ def convert_to_demisto_severity(severity): """Maps CyrenInboxSecurity severity to Cortex XSOAR severity Converts the CyrenInboxSecurity confidence levels (1, 2, 3) to Cortex XSOAR incident severity (1 to 4) for mapping. Note, there are only 3 Cyren confidence levels :type severity: ``int`` :param severity: severity as returned from the CyrenInboxSecurity API (int) :return: Cortex XSOAR Severity (2 to 4) :rtype: ``int`` """ return {0: IncidentSeverity.LOW, 1: IncidentSeverity.MEDIUM, 2: IncidentSeverity.HIGH, 3: IncidentSeverity.CRITICAL}[severity] """ COMMAND FUNCTIONS """ def test_module(client, client_id, client_secret): """Tests API connectivity and authentication' Returning 'ok' indicates that the integration works like it is supposed to. Connection to the service is successful. Raises exceptions if something goes wrong. :type client: ``Client`` :param Client: CyrenInboxSecurity client to use :type client_id: ``str`` :param client_id: A unique string that identifies the client :type client_secret: ``str`` :param client_secret: A unique string that represent the client secret :return: 'ok' if test passed, anything else will fail the test. :rtype: ``str`` """ try: client.get_token(client_id, client_secret) except DemistoException as e: raise e return "ok" def simulate_fetch(): """ Ingesting a sample incident to XSOAR """ # Ensure only one sample is created last_run = demisto.getLastRun() if last_run.get("sample_fetched", False): return [] else: demisto.setLastRun({"sample_fetched": True}) now_time = datetime.now() now_time_timestamp_seconds = int(date_to_timestamp(now_time) / 1000) sample_incident = { "incident_id": "incidentID-" + str(now_time_timestamp_seconds), "case_id": "caseID-123", "tx_id": "txID-" + str(now_time_timestamp_seconds), "threat_type": "phishing", "application": "office365", "connection_id": "connectionID", "connection_name": "ConnectionName", "org_id": 1, "tenant_id": "tenantID", "remediation_data": { "remediation_type": "auto_remediation", "remediation": "not_remediated", "policy_action": [ {"performed_date": 1625484377, "messages": None, "action_type": "ADD_BANNER", "action_status": "success"}, {"performed_date": 1625484378, "messages": None, "action_type": "MOVE_TO_SPAM", "action_status": "success"}, {"performed_date": 1625484378, "messages": None, "action_type": "INCIDENT", "action_status": "success"}, ], "policy_condition": [ {"condition_type": "ORIGIN", "condition_value": "ALL"}, {"condition_type": "EMAIL_STATE", "condition_value": "ALL"}, {"condition_type": "REPORTED_BY", "condition_value": "ALL"}, ], "policy_name": "Default Rule", "resolution": "pending", }, "message_details": { "internet_message_id": "internetMessageID-" + str(now_time_timestamp_seconds), "targeted_employee": {"name": "user1", "address": "user1@sample.com"}, "other_recipients": [], "sender": {"name": "user2", "address": "user2@sample.com"}, "from": {"name": "user2", "address": "user2@sample.com"}, "to_recipients": [{"name": "user1", "address": "user1@sample.com"}], "to_cc_recipients": [], "to_bcc_recipients": [], "reply_to": [], "email_subject": "subject - this is a sample", "email_body": "", "received_datetime": "2021-07-05T11:23:27Z", "sent_datetime": "2021-07-05T11:23:22Z", "created_datetime": "2021-07-05T11:23:26Z", "last_modified_datetime": "2021-07-05T11:23:28Z", "is_read": False, "is_external_from": False, "is_external_reply_to": False, "is_external_sender": False, "is_user_subscribed": True, "urls_classification": [ { "url": "http://phishing.com", "classification_type": "malicious", "is_phishing": True, "classification": "malicious", } ], "user_id": "userID", "message_id": "messageID-" + str(now_time_timestamp_seconds), "attachments": [ { "file_size": 158, "file_name": "attachment.html", "file_hash": "hash", "file_category": "PHISHING", "aws_key": "key.zip", "aws_bucket": "bucket", } ], "folder": "inbox", }, "threat_indicators": [ { "type": "url", "subType": "malicious_url", "value": "http://phishing.com", "details": {"categories": [""], "family_names": ["Generic"], "industries": [""], "screenshot": "screenshot.png"}, }, { "type": "attachment", "subType": "attachment_hash", "value": "hash", "details": {"categories": None, "family_names": None, "industries": None, "screenshot": ""}, "attachment": { "file_size": 158, "file_name": "attachment.html", "file_hash": "hash", "file_category": "PHISHING", "aws_key": "key.zip", "aws_bucket": "screenshot", }, }, ], "feedback": [ { "details": "phishing url", "tx_id": "txID", "feedback_type": "phishing", "created_at": 1625653814, "threat_indicators_type": ["url"], }, ], "reported_by": "admin@sample.com", "confidence": 3, "created_at": now_time_timestamp_seconds, "updated_at": now_time_timestamp_seconds, } incidents = [] incident_name = "Cyren Inbox Security Sample - {} ({})".format( sample_incident.get("threat_type", "phishing"), sample_incident.get("reported_by", "System") ) incident_timestamp = now_time_timestamp_seconds incident_str_time = timestamp_to_datestring(incident_timestamp * 1000) incident = { "name": incident_name, "occurred": incident_str_time, "rawJSON": json.dumps(sample_incident), "severity": convert_to_demisto_severity(1), } incidents.append(incident) return incidents def fetch_incidents(client, client_id, client_secret, last_run, first_fetch_time, max_fetch): """ This function will execute each interval (default is 1 minute). Args: client (Client): the client object client_id (String): client id client_secret (String): client secret last_run (dict): a dict with last_fetch as a key and a timestamp in seconds as a value first_fetch_time (datetime): datetime object on when to start fetching incidents max_fetch (int): Maximum numbers of incidents per fetch Returns: incidents: Incidents that will be created in Cortex XSOAR """ # last run is a dict with last_fetch as a key and # a timestamp in seconds as a value last_fetch = last_run.get("last_fetch") # Handle first fetch time if last_fetch is None: # if missing, use what provided via first_fetch_time date_from = int(date_to_timestamp(first_fetch_time) / 1000) else: # otherwise use the stored last fetch date_from = last_fetch last_fetched_time = date_from incidents = [] token = client.get_token(client_id, client_secret).get("data").get("access_token") cyren_incidents = client.get_incidents(token, date_from, max_fetch) for ci in cyren_incidents.get("data", []): incident_name = "Cyren Inbox Security - {} ({})".format( ci.get("threat_type", "phishing"), ci.get("reported_by", "System") ) incident_timestamp = ci.get("created_at") incident_str_time = timestamp_to_datestring(incident_timestamp * 1000) incident = { "name": incident_name, "occurred": incident_str_time, "rawJSON": json.dumps(ci), "severity": convert_to_demisto_severity(ci.get("confidence", 1)), } incidents.append(incident) # Update last_fetch if the incident is newer than last_fetch if incident_timestamp > last_fetched_time: last_fetched_time = incident_timestamp + 1 demisto.setLastRun({"last_fetch": last_fetched_time}) return incidents def resolve_and_remediate_command(client, args, client_id, client_secret): """ Resolve a case and remediate it """ case_id = args.get("case_id") resolution = args.get("resolution", "phishing") resolution_reason = args.get("resolution_reason", "") resolution_reason_text = args.get("resolution_reason_text", "") actions = args.get("actions", []) if actions: actions = actions.split(",") if client_id == client_secret == "sample": resp = {"data": {"status": "ok"}} else: token = client.get_token(client_id, client_secret).get("data").get("access_token") resp = client.resolve_and_remediate( token, case_id, resolution=resolution, resolution_reason=resolution_reason, resolution_reason_text=resolution_reason_text, actions=actions, ) readable_output = tableToMarkdown("cyren-resolve-and-remediate results", resp["data"]) + "\n*** end of results ***" return CommandResults(outputs_prefix="Cyren", outputs=resp, readable_output=readable_output) """ MAIN FUNCTION """ def main() -> None: """ PARSE AND VALIDATE INTEGRATION PARAMS """ url = demisto.params().get("url") client_id = demisto.params().get("client_id") client_secret = demisto.params().get("client_secret") first_fetch_time = arg_to_datetime(demisto.params().get("first_fetch", "3 days").strip()) max_results = arg_to_number(arg=demisto.params().get("max_fetch", 50)) if max_results is None: max_results = 1 if max_results > MAX_INCIDENTS_TO_FETCH: max_results = MAX_INCIDENTS_TO_FETCH try: client = Client(base_url=url, verify=True, proxy=False) if demisto.command() == "test-module": if url == client_id == client_secret == "sample": return_results("ok") else: result = test_module(client, client_id, client_secret) return_results(result) if demisto.command() == "fetch-incidents": if url == client_id == client_secret == "sample": incidents = simulate_fetch() demisto.incidents(incidents) else: incidents = fetch_incidents( client=client, client_id=client_id, client_secret=client_secret, last_run=demisto.getLastRun(), first_fetch_time=first_fetch_time, max_fetch=max_results, ) demisto.incidents(incidents) if demisto.command() == "cyren-resolve-and-remediate": return_results( resolve_and_remediate_command(client, demisto.args(), client_id=client_id, client_secret=client_secret) ) # Log exceptions and return errors except Exception as e: return_error(f"Failed to execute {demisto.command()} command.\nError:\n{e!s}") """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): main()