diff --git a/ciscosma.json b/ciscosma.json index ea43012..af8b252 100644 --- a/ciscosma.json +++ b/ciscosma.json @@ -783,6 +783,128 @@ } ], "versions": "EQ(*)" + }, + { + "action": "search list", + "identifier": "search_list", + "description": "Search safelist or blocklist entries", + "type": "investigate", + "read_only": true, + "parameters": { + "list_type": { + "description": "Type of list to search", + "data_type": "string", + "required": false, + "order": 0, + "value_list": ["safelist", "blocklist"], + "default": "safelist" + }, + "view_by": { + "description": "View entries by sender or recipient", + "data_type": "string", + "required": false, + "order": 1, + "value_list": ["sender", "recipient"], + "default": "recipient" + }, + "order_by": { + "description": "Field to sort results by", + "data_type": "string", + "required": false, + "order": 2, + "value_list": ["sender", "recipient"], + "default": "recipient" + }, + "order_direction": { + "description": "Sort direction", + "data_type": "string", + "required": false, + "order": 3, + "value_list": ["asc", "desc"], + "default": "desc" + }, + "offset": { + "description": "Number of records to skip/offset", + "data_type": "numeric", + "required": false, + "order": 4, + "default": 0 + }, + "limit": { + "description": "Max number of records to return", + "data_type": "numeric", + "required": false, + "order": 5, + "default": 25 + }, + "search": { + "description": "Search term (only supported when order_by=recipient)", + "data_type": "string", + "required": false, + "order": 6 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.list_type", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.view_by", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.order_by", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.order_direction", + "data_type": "string" + }, + { + "data_path": "action_result.parameter.offset", + "data_type": "numeric" + }, + { + "data_path": "action_result.parameter.limit", + "data_type": "numeric" + }, + { + "data_path": "action_result.parameter.search", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.senderList", + "data_type": "string", + "contains": ["email"] + }, + { + "data_path": "action_result.data.*.recipientAddress", + "data_type": "string", + "contains": ["email"] + }, + { + "data_path": "action_result.summary.total_entries", + "data_type": "numeric" + }, + { + "data_path": "action_result.summary.entries_returned", + "data_type": "numeric" + }, + { + "data_path": "action_result.summary.list_type", + "data_type": "string" + }, + { + "data_path": "action_result.message", + "data_type": "string" + } + ], + "versions": "EQ(*)" } ] } diff --git a/ciscosma_connector.py b/ciscosma_connector.py index 5d07585..1e50cf8 100644 --- a/ciscosma_connector.py +++ b/ciscosma_connector.py @@ -23,14 +23,21 @@ from phantom.base_connector import BaseConnector from ciscosma_consts import ( + CISCOSMA_DEFAULT_LIST_LIMIT, + CISCOSMA_DEFAULT_LIST_OFFSET, + CISCOSMA_DELETE_MESSAGES_ENDPOINT, CISCOSMA_GET_MESSAGE_DETAILS_ENDPOINT, CISCOSMA_GET_MESSAGE_TRACKING_DETAILS_ENDPOINT, CISCOSMA_GET_TOKEN_ENDPOINT, CISCOSMA_RELEASE_MESSAGES_ENDPOINT, - CISCOSMA_DELETE_MESSAGES_ENDPOINT, + CISCOSMA_SEARCH_BLOCKLIST_ENDPOINT, CISCOSMA_SEARCH_MESSAGES_ENDPOINT, + CISCOSMA_SEARCH_SAFELIST_ENDPOINT, CISCOSMA_SEARCH_TRACKING_MESSAGES_ENDPOINT, CISCOSMA_VALID_FILTER_OPERATORS, + CISCOSMA_VALID_LIST_ORDER_BY, + CISCOSMA_VALID_LIST_TYPES, + CISCOSMA_VALID_LIST_VIEW_BY, CISCOSMA_VALID_ORDER_BY, CISCOSMA_VALID_ORDER_DIRECTIONS, ) @@ -299,16 +306,10 @@ def _handle_delete_email(self, param): except ValueError: return action_result.set_status(phantom.APP_ERROR, "Parameter 'message_id' must be a valid integer") - payload = { - "quarantineType": "spam", - "mids": [message_id] - } + payload = {"quarantineType": "spam", "mids": [message_id]} ret_val, response = self._make_authenticated_request( - action_result, - CISCOSMA_DELETE_MESSAGES_ENDPOINT, - json_data=payload, - method="delete" + action_result, CISCOSMA_DELETE_MESSAGES_ENDPOINT, json_data=payload, method="delete" ) if phantom.is_fail(ret_val): @@ -318,10 +319,7 @@ def _handle_delete_email(self, param): delete_data = response.get("data", {}) action_result.add_data(delete_data) - summary = { - "total_deleted": delete_data.get("totalCount", 0), - "action": delete_data.get("action") - } + summary = {"total_deleted": delete_data.get("totalCount", 0), "action": delete_data.get("action")} action_result.update_summary(summary) except Exception as e: @@ -418,6 +416,64 @@ def _handle_get_message_tracking_details(self, param): return action_result.set_status(phantom.APP_SUCCESS, "Successfully retrieved message tracking details") + def _handle_search_list(self, param): + action_result = self.add_action_result(ActionResult(dict(param))) + + list_type = param.get("list_type", "safelist").lower() + if list_type not in CISCOSMA_VALID_LIST_TYPES: + return action_result.set_status(phantom.APP_ERROR, "Invalid parameter 'list_type'") + + endpoint = CISCOSMA_SEARCH_SAFELIST_ENDPOINT if list_type == "safelist" else CISCOSMA_SEARCH_BLOCKLIST_ENDPOINT + + params = {"action": "view", "quarantineType": "spam"} + + view_by = param.get("view_by", "recipient") + if view_by not in CISCOSMA_VALID_LIST_VIEW_BY: + return action_result.set_status(phantom.APP_ERROR, "Invalid parameter 'view_by'") + params["viewBy"] = view_by + + order_by = param.get("order_by", "recipient") + if order_by not in CISCOSMA_VALID_LIST_ORDER_BY: + return action_result.set_status(phantom.APP_ERROR, "Invalid parameter 'order_by'") + params["orderBy"] = order_by + + order_dir = param.get("order_direction", "desc") + if order_dir not in ["asc", "desc"]: + return action_result.set_status(phantom.APP_ERROR, "Invalid parameter 'order_direction'") + params["orderDir"] = order_dir + + # TODO: Check these constants may want to change + offset = param.get("offset", CISCOSMA_DEFAULT_LIST_OFFSET) + limit = param.get("limit", CISCOSMA_DEFAULT_LIST_LIMIT) + params["offset"] = offset + params["limit"] = limit + + # Handle search (only when orderBy=recipient) + if search := param.get("search"): + if order_by != "recipient": + return action_result.set_status(phantom.APP_ERROR, "Search parameter is only supported when order_by is set to 'recipient'") + params["search"] = search + + ret_val, response = self._make_authenticated_request(action_result, endpoint, params=params) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + try: + entries = response.get("data", []) + total_count = response.get("meta", {}).get("totalCount", 0) + + for entry in entries: + action_result.add_data(entry) + + summary = {"total_entries": total_count, "entries_returned": len(entries), "list_type": list_type} + action_result.update_summary(summary) + + except Exception as e: + return action_result.set_status(phantom.APP_ERROR, f"Error parsing response: {str(e)}") + + return action_result.set_status(phantom.APP_SUCCESS, f"Successfully retrieved {list_type} entries") + def initialize(self): config = self.get_config() self._base_url = config["host"].rstrip("/") @@ -436,6 +492,8 @@ def handle_action(self, param): "search_quarantine_messages": self._handle_search_quarantine_messages, "search_tracking_messages": self._handle_search_tracking_messages, "release_email": self._handle_release_email, + "delete_email": self._handle_delete_email, + "search_list": self._handle_search_list, } action = self.get_action_identifier() diff --git a/ciscosma_consts.py b/ciscosma_consts.py index 84bd6c5..ad6f7d2 100644 --- a/ciscosma_consts.py +++ b/ciscosma_consts.py @@ -18,14 +18,21 @@ CISCOSMA_VALID_ORDER_BY = ["from_address", "to_address", "subject"] CISCOSMA_VALID_ORDER_DIRECTIONS = ["asc", "desc"] CISCOSMA_VALID_FILTER_OPERATORS = ["contains", "is", "begins_with", "ends_with", "does_not_contain"] +CISCOSMA_VALID_LIST_TYPES = ["safelist", "blocklist"] +CISCOSMA_VALID_LIST_VIEW_BY = ["sender", "recipient"] +CISCOSMA_VALID_LIST_ORDER_BY = ["sender", "recipient"] +CISCOSMA_DEFAULT_LIST_LIMIT = 25 +CISCOSMA_DEFAULT_LIST_OFFSET = 0 CISCOSMA_GET_TOKEN_ENDPOINT = "/sma/api/v2.0/login" CISCOSMA_GET_MESSAGE_DETAILS_ENDPOINT = "/sma/api/v2.0/quarantine/messages/details" CISCOSMA_GET_MESSAGE_TRACKING_DETAILS_ENDPOINT = "/sma/api/v2.0/message-tracking/details" CISCOSMA_SEARCH_MESSAGES_ENDPOINT = "/sma/api/v2.0/quarantine/messages" CISCOSMA_SEARCH_TRACKING_MESSAGES_ENDPOINT = "/sma/api/v2.0/message-tracking/messages" -CISCOSMA_RELEASE_MESSAGES_ENDPOINT = "sma/api/v2.0/quarantine/messages" +CISCOSMA_RELEASE_MESSAGES_ENDPOINT = "/sma/api/v2.0/quarantine/messages" CISCOSMA_DELETE_MESSAGES_ENDPOINT = "/sma/api/v2.0/quarantine/messages" +CISCOSMA_SEARCH_SAFELIST_ENDPOINT = "/sma/api/v2.0/quarantine/safelist" +CISCOSMA_SEARCH_BLOCKLIST_ENDPOINT = "/sma/api/v2.0/quarantine/blocklist" # Future endpoints # GET /api/v2.0/reporting/report?resource_attribute