From 192f1cc429ef0deb257fae51e069e71a607bae00 Mon Sep 17 00:00:00 2001 From: RotemAmit Date: Thu, 15 Feb 2024 10:39:38 +0200 Subject: [PATCH] XSUP-31342 - XDR mirroring changes incident resolution (#32359) * added more debug logs to the mirroring process * more debug logs * updated the schema * added ckose_reason * updated the schema name * updated the outgoing mapper and some debug logs * added RN * Bump pack from version CortexXDR to 6.1.14. * Bump pack from version CortexXDR to 6.1.15. * updated test_get_mapping_fields_command * updated handle_outgoing_issue_closure to use close_reason or closeReason * updated RN and docker image * added a unit test test_handle_outgoing_issue_closure * added RN to core pack and ctf01 pack * Bump pack from version CortexXDR to 6.1.16. * updated the RN * added an incident type to the outgoing mapper and updated the RN --------- Co-authored-by: Content Bot --- .../CoreIRApiModule/CoreIRApiModule.py | 11 ++- .../CoreIRApiModule/CoreIRApiModule_test.py | 36 +++++++++- .../handle_outgoing_issue_closure_input.json | 69 +++++++++++++++++++ Packs/Core/ReleaseNotes/3_0_19.md | 5 ++ Packs/Core/pack_metadata.json | 2 +- ...r-outgoing-PaloAltoNetworks_CortexXDR.json | 20 ++++++ .../Integrations/CortexXDRIR/CortexXDRIR.py | 16 ++++- .../CortexXDRIR/CortexXDRIR_test.py | 5 +- Packs/CortexXDR/ReleaseNotes/6_1_16.md | 11 ++- Packs/ctf01/ReleaseNotes/1_0_7.md | 6 ++ Packs/ctf01/pack_metadata.json | 2 +- 11 files changed, 171 insertions(+), 12 deletions(-) create mode 100644 Packs/ApiModules/Scripts/CoreIRApiModule/test_data/handle_outgoing_issue_closure_input.json create mode 100644 Packs/Core/ReleaseNotes/3_0_19.md create mode 100644 Packs/ctf01/ReleaseNotes/1_0_7.md diff --git a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py index b77e1c25db64..dbc1799eb79d 100644 --- a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py +++ b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py @@ -2856,18 +2856,23 @@ def handle_user_unassignment(update_args): def handle_outgoing_issue_closure(remote_args): + incident_id = remote_args.remote_incident_id + demisto.debug(f"handle_outgoing_issue_closure {incident_id=}") update_args = remote_args.delta current_remote_status = remote_args.data.get('status') if remote_args.data else None + close_reason = update_args.get('close_reason') or update_args.get('closeReason') + demisto.debug(f'{current_remote_status=} {remote_args.data=} {remote_args.inc_status=} {close_reason=}') # force closing remote incident only if: # The XSOAR incident is closed # and the remote incident isn't already closed if remote_args.inc_status == 2 and \ - current_remote_status not in XDR_RESOLVED_STATUS_TO_XSOAR: + current_remote_status not in XDR_RESOLVED_STATUS_TO_XSOAR and close_reason: if close_notes := update_args.get('closeNotes'): + demisto.debug(f"handle_outgoing_issue_closure {incident_id=} {close_notes=}") update_args['resolve_comment'] = close_notes - update_args['status'] = XSOAR_RESOLVED_STATUS_TO_XDR.get(update_args.get('closeReason', 'Other')) - demisto.debug(f"Closing Remote incident with status {update_args['status']}") + update_args['status'] = XSOAR_RESOLVED_STATUS_TO_XDR.get(close_reason, 'Other') + demisto.debug(f"handle_outgoing_issue_closure Closing Remote incident {incident_id=} with status {update_args['status']}") def get_update_args(remote_args): diff --git a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py index 0998019aa670..b6e50f811bb0 100644 --- a/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py +++ b/Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule_test.py @@ -9,7 +9,7 @@ import demistomock as demisto from CommonServerPython import Common, tableToMarkdown, pascalToSpace, DemistoException -from CoreIRApiModule import CoreClient +from CoreIRApiModule import CoreClient, handle_outgoing_issue_closure from CoreIRApiModule import add_tag_to_endpoints_command, remove_tag_from_endpoints_command, quarantine_files_command, \ isolate_endpoint_command, list_user_groups_command, parse_user_groups, list_users_command, list_roles_command, \ change_user_role_command, list_risky_users_or_host_command, enrich_error_message_id_group_role, get_incidents_command @@ -3789,3 +3789,37 @@ def test_get_starred_incident_list(self, requests_mock): _, outputs, _ = get_incidents_command(client, args) assert outputs['CoreApiModule.Incident(val.incident_id==obj.incident_id)'][0]['starred'] is True + + +INPUT_test_handle_outgoing_issue_closure = load_test_data('./test_data/handle_outgoing_issue_closure_input.json') + + +@pytest.mark.parametrize("args, expected_delta", + [ + # close an incident from xsoar ui, and the incident type isn't cortex xdr incident + (INPUT_test_handle_outgoing_issue_closure["xsoar_ui_common_mapping"]["args"], + INPUT_test_handle_outgoing_issue_closure["xsoar_ui_common_mapping"]["expected_delta"]), + # close an incident from xsoar ui, and the incident type is cortex xdr incident + (INPUT_test_handle_outgoing_issue_closure["xsoar_ui_cortex_xdr_incident"]["args"], + INPUT_test_handle_outgoing_issue_closure["xsoar_ui_cortex_xdr_incident"]["expected_delta"]), + # close an incident from XDR + (INPUT_test_handle_outgoing_issue_closure["xdr"]["args"], + INPUT_test_handle_outgoing_issue_closure["xdr"]["expected_delta"]) + ]) +def test_handle_outgoing_issue_closure(args, expected_delta): + """ + Given: An UpdateRemoteSystemArgs object. + - case A: data & delta that match a case of closing an incident from xsoar ui, and the incident type isn't cortex xdr incident + - case B: data & delta that match a case of closing an incident from xsoar ui, and the incident type is cortex xdr incident + - case C: data & delta that match a case of closing an incident from XDR. + When: Closing an incident. + Then: Ensure the update_args has the expected value. + - case A: a status is added with the correct value. + - case B: a status is added with the correct value. + - case C: a status isn't added. (If the closing status came from XDR, there is no need to update it again) + """ + from CommonServerPython import UpdateRemoteSystemArgs + + remote_args = UpdateRemoteSystemArgs(args) + handle_outgoing_issue_closure(remote_args) + assert remote_args.delta == expected_delta diff --git a/Packs/ApiModules/Scripts/CoreIRApiModule/test_data/handle_outgoing_issue_closure_input.json b/Packs/ApiModules/Scripts/CoreIRApiModule/test_data/handle_outgoing_issue_closure_input.json new file mode 100644 index 000000000000..e16c5cb98cc4 --- /dev/null +++ b/Packs/ApiModules/Scripts/CoreIRApiModule/test_data/handle_outgoing_issue_closure_input.json @@ -0,0 +1,69 @@ +{ + "xsoar_ui_common_mapping": { + "args": { + "remoteId": "47", + "delta": { + "close_reason": "False Positive", + "resolve_comment": "closing 47 false positive" + }, + "data": { + "assigned_user_mail": "assigned_user_mail", + "assigned_user_pretty_name": "User", + "close_reason": "False Positive", + "manual_severity": "high", + "resolve_comment": "closing 47 false positive", + "status": "new" + }, + "status": 2 + }, + "expected_delta": { + "close_reason": "False Positive", + "resolve_comment": "closing 47 false positive", + "status": "resolved_false_positive" + } + }, + "xsoar_ui_cortex_xdr_incident": { + "args": { + "remoteId": "47", + "delta": { + "closeReason": "False Positive", + "closeNotes": "closing 47 false positive" + }, + "data": { + "assigned_user_mail": "assigned_user_mail", + "assigned_user_pretty_name": "User", + "manual_severity": "high", + "status": "new" + }, + "status": 2 + }, + "expected_delta": { + "closeReason": "False Positive", + "closeNotes": "closing 47 false positive", + "resolve_comment": "closing 47 false positive", + "status": "resolved_false_positive" + } + }, + "xdr": { + "args": { + "remoteId": "47", + "delta": { + "close_reason": "False Positive", + "resolve_comment": "resolving from xdr" + }, + "data": { + "assigned_user_mail": "assigned_user_mail", + "assigned_user_pretty_name": "User", + "close_reason": "False Positive", + "manual_severity": "high", + "resolve_comment": "resolving from xdr", + "status": "resolved_false_positive" + }, + "status": 2 + }, + "expected_delta": { + "close_reason": "False Positive", + "resolve_comment": "resolving from xdr" + } + } +} \ No newline at end of file diff --git a/Packs/Core/ReleaseNotes/3_0_19.md b/Packs/Core/ReleaseNotes/3_0_19.md new file mode 100644 index 000000000000..210cbd9701ca --- /dev/null +++ b/Packs/Core/ReleaseNotes/3_0_19.md @@ -0,0 +1,5 @@ + +#### Integrations + +##### Investigation & Response +- Updated the outgoing mirroring process to also check the **close reason** of the incident, if needed. diff --git a/Packs/Core/pack_metadata.json b/Packs/Core/pack_metadata.json index 82fc53a3b2f2..ddcb7d51396c 100644 --- a/Packs/Core/pack_metadata.json +++ b/Packs/Core/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Core - Investigation and Response", "description": "Automates incident response", "support": "xsoar", - "currentVersion": "3.0.18", + "currentVersion": "3.0.19", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "", diff --git a/Packs/CortexXDR/Classifiers/classifier-mapper-outgoing-PaloAltoNetworks_CortexXDR.json b/Packs/CortexXDR/Classifiers/classifier-mapper-outgoing-PaloAltoNetworks_CortexXDR.json index bca231ca831e..917563e0a07e 100644 --- a/Packs/CortexXDR/Classifiers/classifier-mapper-outgoing-PaloAltoNetworks_CortexXDR.json +++ b/Packs/CortexXDR/Classifiers/classifier-mapper-outgoing-PaloAltoNetworks_CortexXDR.json @@ -127,12 +127,29 @@ } } }, + "Malware Investigation and Response": { + "dontMapEventToLabels": false, + "internalMapping": { + "close_reason": { + "simple": "closeReason" + }, + "resolve_comment": { + "simple": "closeNotes" + }, + "status": { + "simple": "externalstatus.[0]" + } + } + }, "dbot_classification_incident_type_all": { "dontMapEventToLabels": false, "internalMapping": { "assigned_user_mail": { "simple": "xdrassigneduseremail" }, + "close_reason": { + "simple": "closeReason" + }, "manual_severity": { "complex": { "filters": [], @@ -207,6 +224,9 @@ }, "resolve_comment": { "simple": "closeNotes" + }, + "status": { + "simple": "xdrstatusv2" } } } diff --git a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py index 775ffdc305db..6ef9a8e01a96 100644 --- a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py +++ b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py @@ -15,7 +15,7 @@ API_KEY_LENGTH = 128 INTEGRATION_CONTEXT_BRAND = 'PaloAltoNetworksXDR' -XDR_INCIDENT_TYPE_NAME = 'Cortex XDR Incident' +XDR_INCIDENT_TYPE_NAME = 'Cortex XDR Incident Schema' INTEGRATION_NAME = 'Cortex XDR - IR' ALERTS_LIMIT_PER_INCIDENTS = -1 @@ -35,6 +35,8 @@ "manual_severity": {"description": "Incident severity assigned by the user. " "This does not affect the calculated severity low medium high", "xsoar_field_name": "severity"}, + "close_reason": {"description": "The close reason of the XSOAR incident", + "xsoar_field_name": "closeReason"} } MIRROR_DIRECTION = { @@ -688,9 +690,12 @@ def handle_incoming_user_unassignment(incident_data): def handle_incoming_closing_incident(incident_data): + incident_id = incident_data.get('incident_id') + demisto.debug(f'handle_incoming_closing_incident {incident_data=} {incident_id=}') closing_entry = {} # type: Dict if incident_data.get('status') in XDR_RESOLVED_STATUS_TO_XSOAR: - demisto.debug(f"Closing XDR issue {incident_data.get('incident_id')}") + demisto.debug(f"handle_incoming_closing_incident {incident_data.get('status')=} {incident_id=}") + demisto.debug(f"Closing XDR issue {incident_id=}") closing_entry = { 'Type': EntryType.NOTE, 'Contents': { @@ -702,11 +707,14 @@ def handle_incoming_closing_incident(incident_data): } incident_data['closeReason'] = closing_entry['Contents']['closeReason'] incident_data['closeNotes'] = closing_entry['Contents']['closeNotes'] + demisto.debug(f"handle_incoming_closing_incident {incident_id=} {incident_data['closeReason']=} " + f"{incident_data['closeNotes']=}") if incident_data.get('status') == 'resolved_known_issue': close_notes = f'Known Issue.\n{incident_data.get("closeNotes", "")}' closing_entry['Contents']['closeNotes'] = close_notes incident_data['closeNotes'] = close_notes + demisto.debug(f"handle_incoming_closing_incident {incident_id=} {close_notes=}") return closing_entry @@ -829,12 +837,16 @@ def get_remote_data_command(client, args): def update_remote_system_command(client, args): remote_args = UpdateRemoteSystemArgs(args) + incident_id = remote_args.remote_incident_id + demisto.debug(f"update_remote_system_command {incident_id=} {remote_args=}") if remote_args.delta: demisto.debug(f'Got the following delta keys {str(list(remote_args.delta.keys()))} to update' f'incident {remote_args.remote_incident_id}') + demisto.debug(f'{remote_args.delta=}') try: if remote_args.incident_changed: + demisto.debug(f"update_remote_system_command {incident_id=} {remote_args.incident_changed=}") update_args = get_update_args(remote_args) update_args['incident_id'] = remote_args.remote_incident_id diff --git a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py index 38bba65b3557..8df2e6579ddf 100644 --- a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py +++ b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py @@ -444,7 +444,7 @@ def test_get_mapping_fields_command(): - the result fits the expected mapping. """ from CortexXDRIR import get_mapping_fields_command - expected_mapping = {"Cortex XDR Incident": { + expected_mapping = {"Cortex XDR Incident Schema": { "status": "Current status of the incident: \"new\",\"under_" "investigation\",\"resolved_known_issue\"," "\"resolved_duplicate\",\"resolved_false_positive\"," @@ -453,7 +453,8 @@ def test_get_mapping_fields_command(): "assigned_user_pretty_name": "Full name of the user assigned to the incident.", "resolve_comment": "Comments entered by the user when the incident was resolved.", "manual_severity": "Incident severity assigned by the user. This does not " - "affect the calculated severity low medium high" + "affect the calculated severity low medium high", + "close_reason": "The close reason of the XSOAR incident" }} res = get_mapping_fields_command() assert expected_mapping == res.extract_mapping() diff --git a/Packs/CortexXDR/ReleaseNotes/6_1_16.md b/Packs/CortexXDR/ReleaseNotes/6_1_16.md index dfe963d61374..ec97e4007dcc 100644 --- a/Packs/CortexXDR/ReleaseNotes/6_1_16.md +++ b/Packs/CortexXDR/ReleaseNotes/6_1_16.md @@ -2,6 +2,13 @@ #### Integrations ##### Palo Alto Networks Cortex XDR - Investigation and Response - -- Improved implementation of ***fetch-incidents*** and ***xdr-get-incident-extra-data*** by limit the alerts of the incident according to the config map. +- Updated the outgoing mirroring process to also check the **close reason** of the incident, if needed. +- Updated the name of the schema to Cortex XDR Incident Schema. - Updated the Docker image to: *demisto/python3:3.10.13.87159*. + +#### Mappers + +##### Cortex XDR - Outgoing Mapper +- Added the field close_reason to the mapper. +- Added the field status to the Common Mapping. +- Added the incident type **Malware Investigation and Response** to the outgoing mapper. diff --git a/Packs/ctf01/ReleaseNotes/1_0_7.md b/Packs/ctf01/ReleaseNotes/1_0_7.md new file mode 100644 index 000000000000..ae0578a7f03c --- /dev/null +++ b/Packs/ctf01/ReleaseNotes/1_0_7.md @@ -0,0 +1,6 @@ + +#### Integrations + +##### Cortex XDR - IR CTF + +- Updated the outgoing mirroring process to also check the **close reason** of the incident, if needed. diff --git a/Packs/ctf01/pack_metadata.json b/Packs/ctf01/pack_metadata.json index 36f3c6035c70..3b8e54bb0dea 100644 --- a/Packs/ctf01/pack_metadata.json +++ b/Packs/ctf01/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Capture The Flag - 01", "description": "XSOAR's Capture the flag (CTF)", "support": "xsoar", - "currentVersion": "1.0.6", + "currentVersion": "1.0.7", "serverMinVersion": "8.2.0", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex",