import demistomock as demisto from CommonServerPython import * """ IMPORTS """ import html import json import logging import mimetypes import traceback from typing import Any import dateparser import urllib3 from argus_api import session as argus_session from argus_api.exceptions.http import AccessDeniedException from argus_api.lib.cases.v2.case import ( add_attachment, add_case_tag, add_comment, advanced_case_search, close_case, create_case, delete_case, delete_comment, download_attachment, edit_comment, get_attachment, get_case_metadata_by_id, list_case_attachments, list_case_comments, list_case_tags, remove_case_tag_by_id, remove_case_tag_by_key_value, update_case, ) from argus_api.lib.currentuser.v1.user import get_current_user from argus_api.lib.events.v1 import get_event_by_path from argus_api.lib.events.v1.aggregated import ( find_aggregated_events, list_aggregated_events, ) from argus_api.lib.events.v1.case.case import get_events_for_case from argus_api.lib.events.v1.nids import find_n_i_d_s_events, list_n_i_d_s_events from argus_api.lib.events.v1.payload import get_payload from argus_api.lib.events.v1.pcap import get_pcap from argus_api.lib.pdns.v3.search import search_records from argus_api.lib.reputation.v1.observation import ( fetch_observations_for_domain, fetch_observations_for_i_p, ) # Disable insecure warnings urllib3.disable_warnings() """ CONSTANTS """ DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" PRETTY_DATE_FORMAT = "%b %d, %Y, %H:%M:%S" FETCH_TAG = demisto.params().get("fetch_tag") ATTACHMENT_SUBSTRING = "_xsoar-upload" MIRROR_DIRECTION = { "None": None, "Incoming": "In", "Outgoing": "Out", "Incoming And Outgoing": "Both", } ARGUS_STATUS_MAPPING = { "pendingCustomer": 0, "pendingSoc": 0, "pendingVendor": 0, "pendingClose": 0, "workingSoc": 1, "workingCustomer": 1, "closed": 2, } ARGUS_PRIORITY_MAPPING = {"low": 1, "medium": 2, "high": 3, "critical": 4} """ HELPER FUNCTIONS """ def set_argus_settings(api_key: str, base_url: str = None, proxies: dict = None, verify: bool = None): argus_session.api_key = api_key argus_session.base_url = base_url argus_session.proxies = proxies argus_session.verify = verify def argus_priority_to_demisto_severity(priority: str) -> int: return ARGUS_PRIORITY_MAPPING.get(priority, 0) def argus_status_to_demisto_status(status: str) -> int: return ARGUS_STATUS_MAPPING.get(status, 0) def build_argus_priority_from_min_severity(min_severity: str) -> list[str]: severities = ["low", "medium", "high", "critical"] min_severity_list = [] for severity in severities: if argus_priority_to_demisto_severity(min_severity.lower()) <= argus_priority_to_demisto_severity(severity): min_severity_list.append(severity) return min_severity_list def parse_first_fetch(first_fetch: Any) -> Any: if isinstance(first_fetch, str) and first_fetch[0] != "-": first_fetch = f"-{first_fetch}" return first_fetch def build_tags_from_list(lst: list) -> list[dict]: if not lst: return [] if len(lst) % 2 != 0: return [] tags = [] for i in range(0, len(lst), 2): tags.append({"key": lst[i], "value": lst[i + 1]}) return tags def str_to_dict(string: str) -> dict: if not string: return {} lst = argToList(string) if len(lst) % 2 != 0: return {} return {lst[i]: lst[i + 1] for i in range(0, len(lst), 2)} def date_time_to_epoch_milliseconds(date_time: datetime | str = None) -> int: # type: ignore[assignment] if isinstance(date_time, datetime): return int(date_time.timestamp() * 1000) if isinstance(date_time, str): return date_time_to_epoch_milliseconds(dateparser.parse(date_time)) # type: ignore[arg-type] return int(datetime.now().timestamp() * 1000) def pretty_print_date(date_time: datetime | str = None) -> str: # type: ignore[assignment] if isinstance(date_time, datetime): return date_time.strftime(PRETTY_DATE_FORMAT) if isinstance(date_time, str): return pretty_print_date(dateparser.parse(date_time)) # type: ignore[arg-type] return datetime.now().strftime(PRETTY_DATE_FORMAT) def pretty_print_case_metadata(result: dict, title: str = None) -> str: data = result["data"] string = title if title else f"# #{data['id']}: {data['subject']}\n" string += "_Priority: {}, status: {}, last updated: {}_\n".format( data["priority"], data["status"], pretty_print_date(data["lastUpdatedTime"]) ) string += "Reported by {} at {}\n\n".format(data["publishedByUser"]["name"], pretty_print_date(data["publishedTime"])) string += data["description"] return string def pretty_print_case_metadata_html(case: dict, title: str = None) -> str: string = title if title else f"

#{case['id']}: {html.escape(str(case['subject']))}

" string += "Priority: {}, status: {}, last updated: {}
".format( html.escape(str(case["priority"])), html.escape(str(case["status"])), pretty_print_date(case["lastUpdatedTime"]) ) string += "Reported by {} at {}

".format( html.escape(str(case["publishedByUser"]["name"])), pretty_print_date(case["publishedTime"]) ) string += html.escape(str(case["description"])) return string def pretty_print_comment(comment: dict, title: str = None) -> str: string = title if title else "" string += f"#### *{comment['addedByUser']['userName']} - {pretty_print_date(comment['addedTime'])}*\n" string += f"_Last updated {pretty_print_date(comment['lastUpdatedTime'])}_\n" if comment["lastUpdatedTime"] else "" string += f"{comment['comment']}\n\n" string += f"_id: {comment['id']}_\n" string += f"_Flags: {comment['flags']!s}_\n" if comment["flags"] else "" string += "* * *\n" return string def pretty_print_comment_html(comment: dict, title: str = None) -> str: escaped_username = html.escape(str(comment["addedByUser"]["userName"])) escaped_comment_text = html.escape(str(comment["comment"])) string = f"

{title}

" if title else "" string += "" string += f"Added by {escaped_username} at " string += f"{pretty_print_date(comment['addedTime'])}
" string += f"Last updated {pretty_print_date(comment['lastUpdatedTime'])}
" if comment["lastUpdatedTime"] else "" if comment["associatedAttachments"]: string += "Associated attachment(s): " for attachment in comment["associatedAttachments"]: string += f"{html.escape(str(attachment.get('name', '')))} " string += "
" string += "
" string += escaped_comment_text return string def pretty_print_comments(comments: list, title: str = None) -> str: string = title if title else "" for comment in comments: string += pretty_print_comment(comment) return string def pretty_print_comments_html(comments: list, title: str = None) -> str: string = title if title else "" for comment in comments: string += pretty_print_comment_html(comment) string += "
" return string def pretty_print_events(result: dict, title: str = None) -> str: string = title if title else "" string += "_Count: {}, showing {} events, from {} to {}_\n".format( result["count"], result["size"], result["offset"], result["limit"] ) string += tableToMarkdown("Events", result["data"]) return string def pretty_print_attachment_metadata(result: dict, title: str = None) -> str: string = title if title else "" string += f"#### *{result['data']['addedByUser']['userName']} - {result['data']['addedTime']}*\n" string += f"{result['data']['name']} ({result['data']['mimeType']}, {result['data']['size']} bytes)\n\n" string += f"_id: {result['data']['id']}_\n" return string def add_attachment_helper(case_id: int, file_id: str) -> dict: path_res = demisto.getFilePath(file_id) full_file_name = path_res.get("name") file_name, file_extension = os.path.splitext(full_file_name) file_name = f"{file_name}{ATTACHMENT_SUBSTRING}{file_extension}" mime_type = mimetypes.guess_type(full_file_name) if not mime_type[0]: error = f"File {full_file_name} mimetype unknown, not sending. Consider zipping file." demisto.error(error) return {"error": error} with open(path_res.get("path"), "rb") as file_to_send: # noinspection PyTypeChecker return add_attachment( caseID=case_id, name=file_name, mimeType=mime_type[0], data=b64_encode(file_to_send.read()), ) """ COMMAND FUNCTIONS """ def test_module_command() -> str: response = get_current_user() if response["responseCode"] == 200: return "ok" return f"Unable to communicate with Argus API {response['responseCode']}, {response}" def fetch_incidents( last_run: dict, first_fetch_period: str, limit: int = 25, min_severity: str = "low", integration_instance: str = "", mirror_direction: str = "None", mirror_tags: str = "argus_mirror", exclude_tag: str = "", ): start_timestamp = last_run.get("start_time") if last_run else None # Exclude closed cases sub_criteria = [{"exclude": True, "status": ["closed"]}] # Exclude cases with {key} or {key: value} tags if exclude_tag: tag_list = exclude_tag.strip().split(",") if len(tag_list) == 1: sub_criteria.append({"exclude": True, "tag": {"key": tag_list[0]}}) elif len(tag_list) == 2: sub_criteria.append({"exclude": True, "tag": {"key": tag_list[0], "values": tag_list[1]}}) # noinspection PyTypeChecker result = advanced_case_search( startTimestamp=start_timestamp if start_timestamp else first_fetch_period, endTimestamp="now", limit=limit, sortBy=["createdTimestamp"], priority=build_argus_priority_from_min_severity(min_severity), subCriteria=sub_criteria, timeFieldStrategy=["createdTimestamp"], ) incidents = [] for case in result.get("data", []): case["xsoar_mirroring"] = { "dbotMirrorId": str(case["id"]), "dbotMirrorInstance": integration_instance, "dbotMirrorDirection": MIRROR_DIRECTION[mirror_direction], "dbotMirrorTags": argToList(mirror_tags), } case["url"] = f"https://portal.mnemonic.no/spa/case/view/{case['id']}" incident = { "name": f"#{case['id']}: {case['subject']}", "occurred": case["createdTime"], "severity": argus_priority_to_demisto_severity(case["priority"]), "status": argus_status_to_demisto_status(case["status"]), "details": json.dumps(case), "rawJSON": json.dumps(case), } incidents.append(incident) if result.get("data", []): last_run["start_time"] = str(result.get("data")[-1]["createdTimestamp"] + 1) return last_run, incidents def get_remote_data_command( args: dict[str, Any], integration_instance: str = "", mirror_direction: str = "None", mirror_tags: str = "argus_mirror", ) -> GetRemoteDataResponse: remote_args = GetRemoteDataArgs(args) case_id = remote_args.remote_incident_id if not case_id: case_id = args.get("id", "") if not case_id: raise ValueError("case id not found") demisto.debug(f"Getting update for remote [{case_id}]") last_mirror_update = dateparser.parse(remote_args.last_update) if not last_mirror_update: last_mirror_update = dateparser.parse(args.get("lastUpdate", "")) if not last_mirror_update: raise ValueError("last update not found") demisto.debug(f"Getting update with last update [{last_mirror_update}]") case = get_case_metadata_by_id(id=int(case_id)).get("data", {}) # There are no updates to case, return empty if last_mirror_update > dateparser.parse(case.get("lastUpdatedTime", "")): # type: ignore return GetRemoteDataResponse({}, []) entries = [] last_update_timestamp = date_time_to_epoch_milliseconds(last_mirror_update) # Update status and severity (updates whether there are changes or not) entries.append({"severity": argus_priority_to_demisto_severity(case.get("priority"))}) entries.append({"arguscasestatus": case.get("status")}) entries.append({"status": argus_status_to_demisto_status(case.get("status"))}) # Add new attachments case_attachments = list_case_attachments(caseID=int(case_id)).get("data", []) for attachment in case_attachments: if ATTACHMENT_SUBSTRING in attachment["name"]: # file already uploaded by xsoar demisto.debug(f"Ignoring file {attachment['name']} since it contains {ATTACHMENT_SUBSTRING}") elif last_update_timestamp < attachment.get("addedTimestamp", 0): entries.append( fileResult( attachment["name"], download_attachment(caseID=int(case_id), attachmentID=attachment["id"]).content, ) ) # Attach comments as notes case_comments = list_case_comments(caseID=int(case_id)).get("data", []) for comment in case_comments: # New comment if last_update_timestamp < comment.get("addedTimestamp", 0): entries.append( { "Note": True, "Type": entryTypes["note"], "ContentsFormat": formats["html"], # type: ignore "Contents": pretty_print_comment_html(comment), # type: ignore } ) # Existing comment has been updated elif comment.get("addedTimestamp", 0) < last_update_timestamp < comment.get("lastUpdatedTimestamp", ""): entries.append( { "Note": True, "Type": entryTypes["note"], "ContentsFormat": formats["html"], # type: ignore "Contents": (pretty_print_comment_html(comment, "Comment updated")), # type: ignore } ) # Re-attach xsoar mirroring tags, otherwise mirroring breaks case["xsoar_mirroring"] = { "dbotMirrorId": str(case["id"]), "dbotMirrorInstance": integration_instance, "dbotMirrorDirection": MIRROR_DIRECTION[mirror_direction], "dbotMirrorTags": argToList(mirror_tags), } # Close case? close_incident = demisto.params().get("close_incident", True) if case.get("status", "") == "closed" and close_incident: entries.append( { "Type": EntryType.NOTE, "ContentsFormat": EntryFormat.JSON, # type: ignore "Contents": { # type: ignore "dbotIncidentClose": True, "closeReason": "Argus Case closed", "closeNotes": "Argus Case was marked as closed remotely, incident closed.", }, } ) return GetRemoteDataResponse(case, entries) def update_remote_system_command(args: dict[str, Any]) -> CommandResults: parsed_args = UpdateRemoteSystemArgs(args) if parsed_args.delta: demisto.debug(f"Got the following delta keys {list(parsed_args.delta.keys())!s}") demisto.debug(f"Sending incident with remote ID [{parsed_args.remote_incident_id}] to remote system\n") if parsed_args.incident_changed and parsed_args.delta: demisto.debug(f"Incident {parsed_args.remote_incident_id} changed, updating") to_update = {} for key, value in parsed_args.delta.items(): # Allow changing status of case from XSOAR layout if key == "arguscasestatus": if value in ARGUS_STATUS_MAPPING: to_update["status"] = value # Allow changing argus priority based upon XSOAR severity elif key == "severity": for priority, severity in ARGUS_PRIORITY_MAPPING.items(): if severity == value: to_update["priority"] = priority break if to_update: updates = "Following keys have been updated by XSOAR
" for key, value in to_update.items(): updates += f"{key}: {value}
" to_update["comment"] = updates to_update["internal_comment"] = True update_case( id=parsed_args.remote_incident_id, status=to_update.get("status"), priority=to_update.get("priority"), comment=to_update.get("comment"), internalComment=to_update.get("internal_comment"), ) else: demisto.debug( f"Skipping updating remote incident fields [{parsed_args.remote_incident_id}] as it is not new nor changed." ) # Send over comments and new files if parsed_args.entries: for entry in parsed_args.entries: demisto.debug(f'Sending entry {entry.get("id")}') append_demisto_entry_to_argus_case(int(parsed_args.remote_incident_id), entry) # Close incident if relevant close_argus_case = demisto.params().get("close_argus_case", True) if parsed_args.inc_status == IncidentStatus.DONE and close_argus_case: demisto.debug(f"Closing remote incident {parsed_args.remote_incident_id}") close_case( caseID=parsed_args.remote_incident_id, comment=( f"

Case closed by XSOAR

" f"Reason: {parsed_args.data.get('closeReason')}
" f"Closing notes:
{parsed_args.data.get('closeNotes')}" ), ) return parsed_args.remote_incident_id def append_demisto_entry_to_argus_case(case_id: int, entry: dict[str, Any]) -> None: demisto.debug(f"Appending entry to case {case_id}: {entry!s}") if entry.get("type") == 1: # type note / chat comment = "

Note mirrored from XSOAR

" comment += ( f"Added by {entry.get('user')} at" # type: ignore[arg-type] f" {pretty_print_date(entry.get('created'))}

" # type: ignore[arg-type] ) # type: ignore[arg-type] comment += str(entry.get("contents")) add_comment(caseID=case_id, comment=comment) elif entry.get("type") == 3: # type file add_attachment_helper(case_id, str(entry.get("id"))) def add_attachment_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") file_id = args.get("file_id") if not case_id: raise ValueError("case_id not specified") if not file_id: raise ValueError("file_id not specified") result = add_attachment_helper(case_id, file_id) if "error" in result: raise Exception(result["error"]) readable_output = pretty_print_attachment_metadata(result, f"# #{case_id}: attachment metadata\n") return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Attachments", outputs=result, raw_response=result, ) def add_case_tag_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") key = args.get("key") value = args.get("value") if not case_id: raise ValueError("case_id not specified") if not key: raise ValueError("key not specified") if not value: raise ValueError("value not specified") tag = {"key": key, "value": value} result = add_case_tag(caseID=case_id, tags=tag) headers = ["key", "value", "addedTime"] readable_output = tableToMarkdown(f"#{case_id}: Tags", result["data"], headers=headers) return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Tags", outputs=result, raw_response=result, ) def add_comment_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") comment = args.get("comment") if not case_id: raise ValueError("case_id not specified") if not comment: raise ValueError("comment not specified") result = add_comment( caseID=case_id, comment=comment, asReplyTo=args.get("as_reply_to"), internal=args.get("internal"), originEmailAddress=args.get("origin_email_address"), associatedAttachmentID=args.get("associated_attachment_id"), ) return CommandResults( readable_output=pretty_print_comment(result["data"], f"# #{case_id}: Added comment\n"), outputs_prefix="Argus.Comment", outputs=result, raw_response=result, ) def advanced_case_search_command(args: dict[str, Any]) -> CommandResults: # noinspection PyTypeChecker result = advanced_case_search( startTimestamp=args.get("start_timestamp"), endTimestamp=args.get("end_timestamp"), limit=args.get("limit"), offset=args.get("offset"), includeDeleted=args.get("include_deleted"), subCriteria=argToList(args.get("sub_criteria")), exclude=args.get("exclude"), required=args.get("required"), customerID=argToList(args.get("customer_id")), caseID=argToList(args.get("case_id")), customer=argToList(args.get("customer")), type=argToList(args.get("case_type")), service=argToList(args.get("service")), category=argToList(args.get("category")), status=argToList(args.get("status")), priority=argToList(args.get("priority")), assetID=argToList(args.get("asset_id")), tag=argToList(args.get("tag")), workflow=argToList(args.get("workflow")), field=argToList(args.get("field")), keywords=argToList(args.get("keywords")), timeFieldStrategy=argToList(args.get("time_field_strategy")), timeMatchStrategy=args.get("time_match_strategy"), keywordFieldStrategy=argToList(args.get("keyword_field_strategy")), keywordMatchStrategy=args.get("keyword_match_strategy"), user=argToList(args.get("user")), userFieldStrategy=argToList(args.get("user_field_strategy")), userAssigned=args.get("user_assigned"), techAssigned=args.get("tech_assigned"), includeWorkflows=args.get("include_workflows"), includeDescription=args.get("include_description"), accessMode=argToList(args.get("access_mode")), explicitAccess=argToList(args.get("explicit_access")), sortBy=argToList(args.get("sort_by")), includeFlags=argToList(args.get("include_flags")), excludeFlags=argToList(args.get("exclude_flags")), ) readable_output = f"Advanced Case Search: {result['count']} result(s)\n" readable_output += tableToMarkdown("Output not suitable for playground", result["data"]) return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Cases", outputs=result, raw_response=result, ) def close_case_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") if not case_id: raise ValueError("case_id not specified") result = close_case( caseID=case_id, comment=args.get("comment"), ) readable_output = f"# #{case_id}: close case\n" readable_output += f"_Status: {result['data']['status']}, at: {result['data']['closedTime']}_" return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Case", outputs=result, raw_response=result, ) def create_case_command(args: dict[str, Any]) -> CommandResults: subject = args.get("subject") description = args.get("description") service = args.get("service") case_type = args.get("type") tags = args.get("tags") if not subject: raise ValueError("subject not specified") if not description: raise ValueError("description not specified") if not service: raise ValueError("service not specified") if not case_type: raise ValueError("case_type not specified") if tags: tags = str(tags).split(",") if len(tags) % 2 != 0: raise ValueError("tags list must be of even number", tags) tags = build_tags_from_list(tags) result = create_case( customer=args.get("customer"), service=service, category=args.get("category"), type=case_type, status=args.get("status"), tags=tags, subject=subject, description=description, customerReference=args.get("customer_reference"), priority=args.get("priority"), accessMode=args.get("access_mode"), originEmailAddress=args.get("origin_email_address"), publish=args.get("publish"), defaultWatchers=args.get("default_watchers"), ) return CommandResults( readable_output=pretty_print_case_metadata(result), outputs_prefix="Argus.Case", outputs=result, raw_response=result, ) def delete_case_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") if not case_id: raise ValueError("case id not specified") result = delete_case(caseID=case_id) return CommandResults( readable_output=pretty_print_case_metadata(result, "Case deleted"), outputs_prefix="Argus.Case", outputs=result, raw_response=result, ) def delete_comment_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") comment_id = args.get("comment_id") if not case_id: raise ValueError("case id not specified") if not comment_id: raise ValueError("comment id not specified") result = delete_comment(caseID=case_id, commentID=comment_id) return CommandResults( readable_output=pretty_print_comment(result["data"], f"# #{case_id}: Deleted comment\n"), outputs_prefix="Argus.Comment", outputs=result, raw_response=result, ) def download_attachment_by_filename_command(args: dict[str, Any]) -> dict: case_id = args.get("case_id") file_name = args.get("file_name") if case_id is None: raise ValueError("case id not specified") if not file_name: raise ValueError("file name not given") attachment_id = "" case_attachments = list_case_attachments(caseID=case_id).get("data", []) for attachment in case_attachments: if file_name in attachment.get("name", ""): attachment_id = attachment.get("id", "") file_name = attachment.get("name", "") break if not attachment_id: raise ValueError("file name not found in case") result = download_attachment(caseID=case_id, attachmentID=attachment_id) return fileResult(file_name, result.content) def download_attachment_command(args: dict[str, Any]) -> dict: case_id = args.get("case_id") attachment_id = args.get("attachment_id") file_name = args.get("file_name", attachment_id) if case_id is None: raise ValueError("case id not specified") if not attachment_id: raise ValueError("attachment id not given") result = download_attachment(caseID=case_id, attachmentID=attachment_id) return fileResult(file_name, result.content) def download_case_attachments_command(args: dict[str, Any]) -> list[dict]: case_id = args.get("case_id") if case_id is None: raise ValueError("case id not specified") case_attachments = list_case_attachments(caseID=int(case_id)).get("data", []) incident_files = [] for attachment in case_attachments: incident_files.append( fileResult( attachment["name"], download_attachment(caseID=int(case_id), attachmentID=attachment["id"]).content, ) ) return incident_files def edit_comment_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") comment_id = args.get("comment_id") comment = args.get("comment") if not case_id: raise ValueError("case id not specified") if not comment_id: raise ValueError("comment id not specified") if not comment: raise ValueError("comment not specified") result = edit_comment(caseID=case_id, commentID=comment_id, comment=comment) return CommandResults( readable_output=pretty_print_comment(result["data"], f"# #{case_id}: Updated comment\n"), outputs_prefix="Argus.Comment", outputs=result, raw_response=result, ) def get_attachment_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") attachment_id = args.get("attachment_id") if not case_id: raise ValueError("case id not specified") if not attachment_id: raise ValueError("attachment id not specified") result = get_attachment(caseID=case_id, attachmentID=attachment_id) return CommandResults( readable_output=pretty_print_attachment_metadata(result, f"# #{case_id}: attachment metadata\n"), outputs_prefix="Argus.Attachments", outputs=result, raw_response=result, ) def get_case_metadata_by_id_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") if not case_id: raise ValueError("case id not specified") result = get_case_metadata_by_id(id=case_id, skipRedirect=args.get("skip_redirect")) return CommandResults( readable_output=pretty_print_case_metadata(result), outputs_prefix="Argus.Case", outputs=result, raw_response=result, ) def print_case_metadata_by_id_command(args: dict[str, Any]) -> dict: case_id = args.get("case_id") if not case_id: raise ValueError("case id not specified") result = get_case_metadata_by_id(id=case_id, skipRedirect=args.get("skip_redirect")) return { "ContentsFormat": formats["html"], "Type": EntryType.NOTE, "Contents": pretty_print_case_metadata_html(result.get("data")), # "Note": True, } def list_case_attachments_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") if not case_id: raise ValueError("case_id not specified") result = list_case_attachments(caseID=case_id, limit=args.get("limit"), offset=args.get("offset")) readable_output = f"# #{case_id}: Case attachments\n" for attachment in result["data"]: readable_output += f"#### *{attachment['addedByUser']['userName']} - {attachment['addedTime']}*\n" readable_output += f"{attachment['name']} ({attachment['mimeType']}, {attachment['size']} kb)\n\n" readable_output += f"_id: {attachment['id']}_\n" readable_output += "* * *\n" return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Attachments", outputs=result, raw_response=result, ) def list_case_tags_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") if not case_id: raise ValueError("case_id not specified") result = list_case_tags(caseID=case_id, limit=args.get("limit"), offset=args.get("offset")) headers = ["key", "value", "addedTime", "id"] readable_output = tableToMarkdown(f"#{case_id}: Tags", result["data"], headers=headers) return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Tags", outputs=result, raw_response=result, ) def print_case_comments_command(args: dict[str, Any]) -> list[dict]: case_id = args.get("case_id") sort_by = args.get("sort_by") if not case_id: raise ValueError("case_id not specified") if sort_by: sort_by = ["addedTimestamp"] if sort_by == "ascending" else ["-addedTimestamp"] result = list_case_comments( caseID=case_id, beforeComment=args.get("before_comment"), afterComment=args.get("after_comment"), offset=args.get("offset"), limit=args.get("limit"), sortBy=sort_by, ) notes = [] for comment in result.get("data", []): notes.append( { "ContentsFormat": formats["html"], "Type": entryTypes["note"], "Contents": pretty_print_comment_html(comment), "Note": True, } ) return notes def list_case_comments_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") sort_by = args.get("sort_by") if not case_id: raise ValueError("case_id not specified") if sort_by: sort_by = ["addedTimestamp"] if sort_by == "ascending" else ["-addedTimestamp"] result = list_case_comments( caseID=case_id, beforeComment=args.get("before_comment"), afterComment=args.get("after_comment"), offset=args.get("offset"), limit=args.get("limit"), sortBy=sort_by, ) return CommandResults( readable_output=pretty_print_comments(result["data"], f"# #{case_id}: Comments\n"), outputs_prefix="Argus.Comments", outputs=result, raw_response=result, ) def remove_case_tag_by_id_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") tag_id = args.get("tag_id") if not case_id: raise ValueError("case id not specified") if not tag_id: raise ValueError("tag id not specified") result = remove_case_tag_by_id(caseID=case_id, tagID=tag_id) headers = ["key", "value", "addedTime", "id", "flags"] readable_output = tableToMarkdown(f"#{case_id}: Delete tags", result["data"], headers=headers) return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Tags", outputs=result, raw_response=result, ) def remove_case_tag_by_key_value_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") key = args.get("key") value = args.get("value") if not case_id: raise ValueError("case id not specified") if not key: raise ValueError("key not specified") if not value: raise ValueError("value not specified") result = remove_case_tag_by_key_value(caseID=case_id, tagKey=key, tagValue=value) headers = ["key", "value", "addedTime", "id", "flags"] readable_output = tableToMarkdown(f"#{case_id}: Delete tags", result["data"], headers=headers) return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Tags", outputs=result, raw_response=result, ) def update_case_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") if not case_id: raise ValueError("case id not specified") result = update_case( id=case_id, subject=args.get("subject"), description=args.get("description"), status=args.get("status"), priority=args.get("priority"), category=args.get("category"), reporter=args.get("reporter"), assignedUser=args.get("assigned_user"), assignedTech=args.get("assigned_tech"), customerReference=args.get("customer_reference"), comment=args.get("comment"), originEmailAddress=args.get("origin_email_address"), hasEvents=args.get("has_events"), internalComment=args.get("internal_comment"), ) return CommandResults( readable_output=pretty_print_case_metadata(result), outputs_prefix="Argus.Case", outputs=result, raw_response=result, ) def get_event_command(args: dict[str, Any]) -> CommandResults: event_type = args.get("type") timestamp = args.get("timestamp") customer_id = args.get("customer_id") event_id = args.get("event_id") if not event_type: raise ValueError("event type not specified") if not timestamp: raise ValueError("timestamp not specified") if not customer_id: raise ValueError("customer id not specified") if not event_id: raise ValueError("event id not specified") result = get_event_by_path(type=event_type, timestamp=timestamp, customerID=customer_id, eventID=event_id) return CommandResults( readable_output=tableToMarkdown(f"Event: {event_id}", result["data"]), outputs_prefix="Argus.Event", outputs=result, raw_response=result, ) def get_events_for_case_command(args: dict[str, Any]) -> CommandResults: case_id = args.get("case_id") if not case_id: raise ValueError("case id not specified") result = get_events_for_case(caseID=case_id, limit=args.get("limit"), offset=args.get("offset")) return CommandResults( readable_output=pretty_print_events(dict(result), f"# #{case_id}: Associated Events\n"), outputs_prefix="Argus.Events", outputs=result, raw_response=result, ) def find_aggregated_events_command(args: dict[str, Any]) -> CommandResults: # noinspection PyTypeChecker result = find_aggregated_events( skipFutureEvents=args.get("skip_future_events"), exclude=args.get("exclude"), locationID=argToList(args.get("location_id")), severity=argToList(args.get("severity")), customer=argToList(args.get("customer")), alarmID=argToList(args.get("alarm_id")), attackCategoryID=argToList(args.get("attack_category_id")), sourceGeoCountry=argToList(args.get("source_geo_country")), destinationGeoCountry=argToList(args.get("destination_geo_country")), geoCountry=argToList(args.get("geo_country")), properties=str_to_dict(args.get("properties", "")), exactMatchProperties=args.get("exact_match_properties"), subCriteria=argToList(args.get("sub_criteria")), signature=argToList(args.get("signature")), lastUpdatedTimestamp=args.get("last_updated_timestamp"), indexStartTime=args.get("index_start_time"), indexEndTime=args.get("index_end_time"), destinationIP=argToList(args.get("destination_ip")), sourceIP=argToList(args.get("source_ip")), ip=argToList(args.get("ip")), destinationPort=argToList(args.get("destination_port")), sourcePort=argToList(args.get("source_port")), port=argToList(args.get("port")), minSeverity=args.get("min_severity"), maxSeverity=args.get("max_severity"), limit=args.get("limit", 25), offset=args.get("offset"), includeDeleted=args.get("include_deleted"), minCount=args.get("min_count"), associatedCaseID=argToList(args.get("associated_case_id")), sourceIPMinBits=args.get("source_ip_min_bits"), destinationIPMinBits=args.get("destination_ip_min_bits"), startTimestamp=args.get("start_timestamp", "-24hours"), endTimestamp=args.get("end_timestamp", "now"), sortBy=argToList(args.get("sort_by")), includeFlags=argToList(args.get("include_flags")), excludeFlags=argToList(args.get("exclude_flags")), ) return CommandResults( readable_output=pretty_print_events(dict(result), "# Find events\n"), outputs_prefix="Argus.Events", outputs=result, raw_response=result, ) def list_aggregated_events_command(args: dict[str, Any]) -> CommandResults: result = list_aggregated_events( customerID=args.get("customer_id"), signature=args.get("signature"), ip=args.get("ip"), startTimestamp=args.get("start_timestamp"), endTimestamp=args.get("end_timestamp"), limit=args.get("limit"), offset=args.get("offset"), ) return CommandResults( readable_output=pretty_print_events(dict(result), "# List Events\n"), outputs_prefix="Argus.Events", outputs=result, raw_response=result, ) def get_payload_command(args: dict[str, Any]) -> CommandResults: event_type = args.get("type") timestamp = args.get("timestamp") customer_id = args.get("customer_id") event_id = args.get("event_id") if not event_type: raise ValueError("event type not specified") if not timestamp: raise ValueError("timestamp not specified") if not customer_id: raise ValueError("customer id not specified") if not event_id: raise ValueError("event id not specified") result = get_payload(type=event_type, timestamp=timestamp, customerID=customer_id, eventID=event_id) readable_output = "# Event payload\n" readable_output += f"Event: {event_id}, type: {result['data']['type']}\n" readable_output += result["data"]["payload"] return CommandResults( readable_output=readable_output, outputs_prefix="Argus.Payload", outputs=result, raw_response=result, ) def get_pcap_command(args: dict[str, Any]) -> Any: event_type = args.get("type") timestamp = args.get("timestamp") customer_id = args.get("customer_id") event_id = args.get("event_id") if not event_type: raise ValueError("event type not specified") if not timestamp: raise ValueError("timestamp not specified") if not customer_id: raise ValueError("customer id not specified") if not event_id: raise ValueError("event id not specified") result = get_pcap(type=event_type, timestamp=timestamp, customerID=customer_id, eventID=event_id) return fileResult(f"{event_id}_pcap", result.content) def find_nids_events_command(args: dict[str, Any]) -> CommandResults: # noinspection PyTypeChecker result = find_n_i_d_s_events( skipFutureEvents=args.get("skip_future_events"), exclude=args.get("exclude"), eventIdentifier=argToList(args.get("event_identifier")), locationID=argToList(args.get("location_id")), severity=argToList(args.get("severity")), customer=argToList(args.get("customer")), alarmID=argToList(args.get("alarm_id")), attackCategoryID=argToList(args.get("attack_category_id")), sourceGeoCountry=argToList(args.get("source_geo_country")), destinationGeoCountry=argToList(args.get("destination_geo_country")), geoCountry=argToList(args.get("geo_country")), properties=str_to_dict(args.get("properties", "")), exactMatchProperties=args.get("exact_match_properties"), sensorID=argToList(args.get("sensor_id")), subCriteria=argToList(args.get("sub_criteria")), signature=argToList(args.get("signature")), lastUpdatedTimestamp=args.get("last_updated_timestamp"), indexStartTime=args.get("index_start_time"), indexEndTime=args.get("index_end_time"), destinationIP=argToList(args.get("destination_ip")), sourceIP=argToList(args.get("source_ip")), ip=argToList(args.get("ip")), destinationPort=argToList(args.get("destination_port")), sourcePort=argToList(args.get("source_port")), port=argToList(args.get("port")), minSeverity=args.get("min_severity"), maxSeverity=args.get("max_severity"), limit=args.get("limit", 25), offset=args.get("offset"), includeDeleted=args.get("include_deleted"), startTimestamp=args.get("start_timestamp", "-24hours"), endTimestamp=args.get("end_timestamp", "now"), sortBy=argToList(args.get("sort_by")), includeFlags=argToList(args.get("include_flags")), excludeFlags=argToList(args.get("exclude_flags")), ) return CommandResults( readable_output=pretty_print_events(dict(result), "# Find NIDS Events\n"), outputs_prefix="Argus.NIDS", outputs=result, raw_response=result, ) def list_nids_events_command(args: dict[str, Any]) -> CommandResults: result = list_n_i_d_s_events( customerID=args.get("customer_id"), signature=args.get("signature"), ip=args.get("ip"), startTimestamp=args.get("start_timestamp"), endTimestamp=args.get("end_timestamp"), limit=args.get("limit"), offset=args.get("offset"), ) return CommandResults( readable_output=pretty_print_events(dict(result), "# List NIDS Events\n"), outputs_prefix="Argus.NIDS", outputs=result, raw_response=result, ) def search_records_command(args: dict[str, Any]) -> CommandResults: query = args.get("query") if not query: raise ValueError("query not specified") # noinspection PyTypeChecker result = search_records( query=query, aggregateResult=args.get("aggregate_result"), includeAnonymousResults=args.get("include_anonymous_results"), rrClass=argToList(args.get("rr_class")), rrType=argToList(args.get("rr_type")), customerID=argToList(args.get("customer_id")), tlp=argToList(args.get("tlp")), limit=args.get("limit", 25), offset=args.get("offset"), ) return CommandResults( readable_output=tableToMarkdown("PDNS records", result["data"]), outputs_prefix="Argus.PDNS", outputs=result, raw_response=result, ) def fetch_observations_for_domain_command(args: dict[str, Any]) -> CommandResults: fqdn = args.get("fqdn") if not fqdn: raise ValueError("fqdn not specified") result = fetch_observations_for_domain(fqdn=fqdn) return CommandResults( readable_output=tableToMarkdown(f'Domain observations for "{fqdn}"', result["data"]), outputs_prefix="Argus.ObservationsDomain", outputs=result, raw_response=result, ) def fetch_observations_for_i_p_command(args: dict[str, Any]) -> CommandResults: ip = args.get("ip") if not ip: raise ValueError("ip not specified") result = fetch_observations_for_i_p(ip=ip) return CommandResults( readable_output=tableToMarkdown(f'IP observations for "{ip}"', result["data"]), outputs_prefix="Argus.ObservationsIP", outputs=result, raw_response=result, ) """ MAIN FUNCTION """ def main() -> None: logging.getLogger("argus_cli").setLevel("WARNING") first_fetch_period = parse_first_fetch(demisto.params().get("first_fetch", "-1 day")) set_argus_settings( demisto.params().get("api_key"), demisto.params().get("api_url"), handle_proxy(), demisto.params().get("insecure"), ) demisto.debug(f"Command being called is {demisto.command()}") try: if demisto.command() == "test-module": # This is the call made when pressing the integration Test button. return_results(test_module_command()) elif demisto.command() == "fetch-incidents": # Set and define the fetch incidents command to run after activated via integration settings. next_run, incidents = fetch_incidents( last_run=demisto.getLastRun(), first_fetch_period=first_fetch_period, limit=demisto.params().get("max_fetch", 25), min_severity=demisto.params().get("min_severity", "low").lower(), integration_instance=demisto.integrationInstance(), mirror_direction=demisto.params().get("mirror_direction", "None"), mirror_tags=demisto.params().get("mirror_tag"), exclude_tag=demisto.params().get("exclude_tag"), ) demisto.setLastRun(next_run) demisto.incidents(incidents) elif demisto.command() == "get-remote-data": return_results( get_remote_data_command( demisto.args(), integration_instance=demisto.integrationInstance(), mirror_direction=demisto.params().get("mirror_direction", "None"), mirror_tags=demisto.params().get("mirror_tag"), ) ) if demisto.command() == "get-modified-remote-data": # Hotfix for mirroring issues. raise NotImplementedError('The "get-modified-remote-data" command is not implemented') elif demisto.command() == "argus-add-attachment": return_results(add_attachment_command(demisto.args())) elif demisto.command() == "update-remote-system": return_results(update_remote_system_command(demisto.args())) elif demisto.command() == "argus-add-case-tag": return_results(add_case_tag_command(demisto.args())) elif demisto.command() == "argus-add-comment": return_results(add_comment_command(demisto.args())) elif demisto.command() == "argus-advanced-case-search": return_results(advanced_case_search_command(demisto.args())) elif demisto.command() == "argus-close-case": return_results(close_case_command(demisto.args())) elif demisto.command() == "argus-create-case": return_results(create_case_command(demisto.args())) elif demisto.command() == "argus-delete-case": return_results(delete_case_command(demisto.args())) elif demisto.command() == "argus-delete-comment": return_results(delete_comment_command(demisto.args())) elif demisto.command() == "argus-download-attachment": return_results(download_attachment_command(demisto.args())) elif demisto.command() == "argus-download-attachment-by-filename": return_results(download_attachment_by_filename_command(demisto.args())) elif demisto.command() == "argus-edit-comment": return_results(edit_comment_command(demisto.args())) elif demisto.command() == "argus-get-attachment": return_results(get_attachment_command(demisto.args())) elif demisto.command() == "argus-get-case-metadata-by-id": return_results(get_case_metadata_by_id_command(demisto.args())) elif demisto.command() == "argus-list-case-attachments": return_results(list_case_attachments_command(demisto.args())) elif demisto.command() == "argus-list-case-tags": return_results(list_case_tags_command(demisto.args())) elif demisto.command() == "argus-list-case-comments": return_results(list_case_comments_command(demisto.args())) elif demisto.command() == "argus-remove-case-tag-by-id": return_results(remove_case_tag_by_id_command(demisto.args())) elif demisto.command() == "argus-remove-case-tag-by-key-value": return_results(remove_case_tag_by_key_value_command(demisto.args())) elif demisto.command() == "argus-update-case": return_results(update_case_command(demisto.args())) elif demisto.command() == "argus-get-event": return_results(get_event_command(demisto.args())) elif demisto.command() == "argus-get-events-for-case": return_results(get_events_for_case_command(demisto.args())) elif demisto.command() == "argus-find-aggregated-events": return_results(find_aggregated_events_command(demisto.args())) elif demisto.command() == "argus-list-aggregated-events": return_results(list_aggregated_events_command(demisto.args())) elif demisto.command() == "argus-get-payload": return_results(get_payload_command(demisto.args())) elif demisto.command() == "argus-get-pcap": return_results(get_pcap_command(demisto.args())) elif demisto.command() == "argus-find-nids-events": return_results(find_nids_events_command(demisto.args())) elif demisto.command() == "argus-list-nids-events": return_results(list_nids_events_command(demisto.args())) elif demisto.command() == "argus-pdns-search-records": return_results(search_records_command(demisto.args())) elif demisto.command() == "argus-fetch-observations-for-domain": return_results(fetch_observations_for_domain_command(demisto.args())) elif demisto.command() == "argus-fetch-observations-for-ip": return_results(fetch_observations_for_i_p_command(demisto.args())) elif demisto.command() == "argus-print-case-comments": return_results(print_case_comments_command(demisto.args())) elif demisto.command() == "argus-print-case-metadata-by-id": return_results(print_case_metadata_by_id_command(demisto.args())) elif demisto.command() == "argus-download-case-attachments": return_results(download_case_attachments_command(demisto.args())) # Log exceptions and return errors except AccessDeniedException as denied: demisto.info(denied.message) return_warning(denied.message) except Exception as e: demisto.error(traceback.format_exc()) # print the traceback return_error(f"Failed to execute {demisto.command()} command.\nError:\n{e!s}") """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): main()