Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom close reason support #35038

Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
38b5f85
custom close reason support
barryyosi-panw Jun 23, 2024
5afc951
fis custom mapping validation
barryyosi-panw Jun 24, 2024
8c8f84a
tests update
barryyosi-panw Jun 25, 2024
ca4e291
unittests
barryyosi-panw Jun 25, 2024
de2df21
server config retrieval fix
barryyosi-panw Jun 25, 2024
a01a74b
unittest fix
barryyosi-panw Jun 25, 2024
6365810
debug logs
barryyosi-panw Jun 29, 2024
b9228d4
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 3, 2024
8597afe
get server config test
barryyosi-panw Jul 3, 2024
186ea05
get server config test
barryyosi-panw Jul 3, 2024
c813ffb
XDRIR unittest
barryyosi-panw Jul 3, 2024
44482a0
XDRIR unittest
barryyosi-panw Jul 3, 2024
491e716
lint
barryyosi-panw Jul 3, 2024
b207def
RN
barryyosi-panw Jul 3, 2024
69a90f5
RN
barryyosi-panw Jul 3, 2024
4cf51b4
RN
barryyosi-panw Jul 3, 2024
ab4869e
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 3, 2024
10c7672
RN
barryyosi-panw Jul 3, 2024
dcb1c83
RN
barryyosi-panw Jul 3, 2024
7d5f1be
pre-commit fix
barryyosi-panw Jul 3, 2024
7f314d9
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 3, 2024
0a7dd00
resolve conflicts
barryyosi-panw Jul 3, 2024
1636191
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 3, 2024
08f8fdf
Merged master into current branch.
Jul 3, 2024
945774c
Bump pack from version Base to 1.34.24.
Jul 3, 2024
2718920
docstring
barryyosi-panw Jul 3, 2024
3d99679
docstring
barryyosi-panw Jul 3, 2024
0f26fda
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 3, 2024
92f97e3
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 4, 2024
9e53647
Switch netutils (#34874)
JudahSchwartz Jul 4, 2024
a379f2c
updated the classifier and layout of the qradar integration (#35222)
omerKarkKatz Jul 4, 2024
4c7f170
improved implementation of IsIncidentPartOfCampaign (#33954)
dantavori Jul 4, 2024
9e9a250
Zerofox/add cac data (#35227)
content-bot Jul 4, 2024
28b8930
Fixed CortexCoreIR http_request (#35206)
maimorag Jul 4, 2024
cf7270c
Azure SQL Management - Client Credentials (#35175)
noydavidi Jul 4, 2024
80616ff
Azure waf client credentials (#35182)
noydavidi Jul 4, 2024
eb7451b
Revert "docstring"
barryyosi-panw Jul 4, 2024
eadefc5
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 4, 2024
4ed0453
update RN
barryyosi-panw Jul 4, 2024
3b87c04
revert docstring
barryyosi-panw Jul 4, 2024
366b9af
merge master
barryyosi-panw Jul 7, 2024
85098c9
updated RN
barryyosi-panw Jul 7, 2024
6c88d3b
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 7, 2024
26629d3
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 7, 2024
58f1d9b
update rn
barryyosi-panw Jul 7, 2024
9dbf0e9
Merged master into current branch.
Jul 7, 2024
b11a23e
Bump pack from version Base to 1.34.27.
Jul 7, 2024
739532c
Merged master into current branch.
Jul 8, 2024
c6211cc
Bump pack from version CortexXDR to 6.1.54.
Jul 8, 2024
ccad7ce
Update docker image
barryyosi-panw Jul 8, 2024
ef8d977
Merge branch 'master' into enhancement/xsup-35810/xdr-ir-custom-close…
barryyosi-panw Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Packs/ApiModules/Scripts/CoreIRApiModule/CoreIRApiModule.py
Original file line number Diff line number Diff line change
Expand Up @@ -2971,23 +2971,23 @@ def resolve_xdr_close_reason(xsoar_close_reason: str) -> str:
"""
# Initially setting the close reason according to the default mapping.
xdr_close_reason = XSOAR_RESOLVED_STATUS_TO_XDR.get(xsoar_close_reason, 'resolved_other')

# Reading custom XSOAR->XDR close-reason mapping.
custom_xsoar_to_xdr_close_reason_mapping = comma_separated_mapping_to_dict(
demisto.params().get("custom_xsoar_to_xdr_close_reason_mapping")
)

# Overriding default close-reason mapping if there exists a custom one.
if xsoar_close_reason in custom_xsoar_to_xdr_close_reason_mapping:
xdr_close_reason_candidate = custom_xsoar_to_xdr_close_reason_mapping[xsoar_close_reason]
xdr_close_reason_candidate = custom_xsoar_to_xdr_close_reason_mapping.get(xsoar_close_reason)
# Transforming resolved close-reason into snake_case format with known prefix to match XDR status format.
demisto.debug(
f"resolve_xdr_close_reason XSOAR->XDR custom close-reason exists, using {xsoar_close_reason}={xdr_close_reason}")
xdr_close_reason_candidate = "resolved_" + "_".join(xdr_close_reason_candidate.lower().split(" "))

if xdr_close_reason_candidate not in XDR_RESOLVED_STATUS_TO_XSOAR:
demisto.debug("Warning: Provided XDR close-reason does not exist. Using default XDR close-reason mapping. ")
else:
xdr_close_reason = xdr_close_reason_candidate
demisto.debug(
f"resolve_xdr_close_reason XSOAR->XDR custom close-reason exists, using {xsoar_close_reason}={xdr_close_reason}")
else:
demisto.debug(f"resolve_xdr_close_reason using default mapping {xsoar_close_reason}={xdr_close_reason}")

Expand Down
6 changes: 6 additions & 0 deletions Packs/Base/ReleaseNotes/1_34_23.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

#### Scripts

##### CommonServerPython

Added a functionality to read server configuration.
16 changes: 16 additions & 0 deletions Packs/Base/Scripts/CommonServerPython/CommonServerPython.py
Original file line number Diff line number Diff line change
Expand Up @@ -12011,6 +12011,22 @@ def is_time_sensitive():
return hasattr(demisto, 'isTimeSensitive') and demisto.isTimeSensitive()


def parse_json_string(json_string):
try:
data = json.loads(json_string)
return data
except json.JSONDecodeError as error: # type: ignore[attr-defined]
demisto.error("Error decoding JSON: {error}".format(error=error))
return {}


def get_server_config():
response = demisto.internalHttpRequest(method='GET', uri='/system/config')
body = parse_json_string(response.get('body'))
server_config = body.get('sysConf', {})
return server_config


from DemistoClassApiModule import * # type:ignore [no-redef] # noqa:E402


Expand Down
74 changes: 62 additions & 12 deletions Packs/Base/Scripts/CommonServerPython/CommonServerPython_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@

import CommonServerPython
import demistomock as demisto
from CommonServerPython import xml2json, json2xml, entryTypes, formats, tableToMarkdown, underscoreToCamelCase, \
flattenCell, date_to_timestamp, datetime, timedelta, camelize, pascalToSpace, argToList, \
remove_nulls_from_dictionary, is_error, get_error, hash_djb2, fileResult, is_ip_valid, get_demisto_version, \
IntegrationLogger, parse_date_string, IS_PY3, PY_VER_MINOR, DebugLogger, b64_encode, parse_date_range, \
return_outputs, is_filename_valid, convert_dict_values_bytes_to_str, \
argToBoolean, ipv4Regex, ipv4cidrRegex, ipv6cidrRegex, urlRegex, ipv6Regex, domainRegex, batch, FeedIndicatorType, \
encode_string_results, safe_load_json, remove_empty_elements, aws_table_to_markdown, is_demisto_version_ge, \
appendContext, auto_detect_indicator_type, handle_proxy, get_demisto_version_as_str, get_x_content_info_headers, \
url_to_clickable_markdown, WarningsHandler, DemistoException, SmartGetDict, JsonTransformer, \
remove_duplicates_from_list_arg, DBotScoreType, DBotScoreReliability, Common, send_events_to_xsiam, ExecutionMetrics, \
response_to_context, is_integration_command_execution, is_xsiam_or_xsoar_saas, is_xsoar, is_xsoar_on_prem, \
is_xsoar_hosted, is_xsoar_saas, is_xsiam, send_data_to_xsiam, censor_request_logs, censor_request_logs, safe_sleep
from CommonServerPython import (xml2json, json2xml, entryTypes, formats, tableToMarkdown, underscoreToCamelCase,
flattenCell, date_to_timestamp, datetime, timedelta, camelize, pascalToSpace, argToList,
remove_nulls_from_dictionary, is_error, get_error, hash_djb2, fileResult, is_ip_valid,
get_demisto_version, IntegrationLogger, parse_date_string, IS_PY3, PY_VER_MINOR, DebugLogger,
b64_encode, parse_date_range, return_outputs, is_filename_valid, convert_dict_values_bytes_to_str,
argToBoolean, ipv4Regex, ipv4cidrRegex, ipv6cidrRegex, urlRegex, ipv6Regex, domainRegex, batch,
FeedIndicatorType, encode_string_results, safe_load_json, remove_empty_elements,
aws_table_to_markdown, is_demisto_version_ge, appendContext, auto_detect_indicator_type,
handle_proxy, get_demisto_version_as_str, get_x_content_info_headers, url_to_clickable_markdown,
WarningsHandler, DemistoException, SmartGetDict, JsonTransformer, remove_duplicates_from_list_arg,
DBotScoreType, DBotScoreReliability, Common, send_events_to_xsiam, ExecutionMetrics,
response_to_context, is_integration_command_execution, is_xsiam_or_xsoar_saas, is_xsoar,
is_xsoar_on_prem, is_xsoar_hosted, is_xsoar_saas, is_xsiam, send_data_to_xsiam,
censor_request_logs, censor_request_logs, safe_sleep, get_server_config
)

EVENTS_LOG_ERROR = \
"""Error sending new events into XSIAM.
Expand Down Expand Up @@ -9769,3 +9772,50 @@ def test_sleep_mocked_time(mocker):

# Verify sleep duration based on mocked time difference
assert sleep_mocker.call_count == 2


def test_get_server_config(mocker):
mock_response = {
'body': '{"sysConf":{"incident.closereasons":"CustomReason1, CustomReason 2, Foo","versn":40},"defaultMap":{}}\n',
'headers': {
'Content-Length': ['104'],
'X-Xss-Protection': ['1; mode=block'],
'X-Content-Type-Options': ['nosniff'],
'Strict-Transport-Security': ['max-age=10886400000000000; includeSubDomains'],
'Vary': ['Accept-Encoding'],
'Server-Timing': ['7'],
'Date': ['Wed, 03 Jul 2010 09:11:35 GMT'],
'X-Frame-Options': ['DENY'],
'Content-Type': ['application/json']
},
'status': '200 OK',
'statusCode': 200
}

mocker.patch.object(demisto, 'internalHttpRequest', return_value=mock_response)
server_config = get_server_config()
assert server_config == {'incident.closereasons': 'CustomReason1, CustomReason 2, Foo', 'versn': 40}


def test_get_server_config_fail(mocker):
mock_response = {
'body': 'NOT A VALID JSON',
'headers': {
'Content-Length': ['104'],
'X-Xss-Protection': ['1; mode=block'],
'X-Content-Type-Options': ['nosniff'],
'Strict-Transport-Security': ['max-age=10886400000000000; includeSubDomains'],
'Vary': ['Accept-Encoding'],
'Server-Timing': ['7'],
'Date': ['Wed, 03 Jul 2010 09:11:35 GMT'],
'X-Frame-Options': ['DENY'],
'Content-Type': ['application/json']
},
'status': '200 OK',
'statusCode': 200
}

mocker.patch.object(demisto, 'internalHttpRequest', return_value=mock_response)
mocked_error = mocker.patch.object(demisto, 'error')
assert get_server_config() == {}
assert mocked_error.call_args[0][0] == 'Error decoding JSON: Expecting value: line 1 column 1 (char 0)'
2 changes: 1 addition & 1 deletion Packs/Base/pack_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Base",
"description": "The base pack for Cortex XSOAR.",
"support": "xsoar",
"currentVersion": "1.34.22",
"currentVersion": "1.34.23",
"author": "Cortex XSOAR",
"serverMinVersion": "6.0.0",
"url": "https://www.paloaltonetworks.com/cortex",
Expand Down
11 changes: 11 additions & 0 deletions Packs/Core/ReleaseNotes/3_0_49.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

#### Integrations

##### Indicators detection

Updated the CoreIRApiModule with support for custom XSOAR close-reasons in XSOAR-XDR close-reason mapping.

##### Investigation & Response

Updated the CoreIRApiModule with support for custom XSOAR close-reasons in XSOAR-XDR close-reason mapping.

2 changes: 1 addition & 1 deletion Packs/Core/pack_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Core - Investigation and Response",
"description": "Automates incident response",
"support": "xsoar",
"currentVersion": "3.0.48",
"currentVersion": "3.0.49",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
Expand Down
111 changes: 64 additions & 47 deletions Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,64 @@ def filter_and_save_unseen_incident(incidents: List, limit: int, number_of_alrea
return filtered_incidents


def get_xsoar_close_reasons():
"""
Get the default XSOAR close-reasons in addition to custom close-reasons from server configuration.
"""
default_xsoar_close_reasons = list(XSOAR_RESOLVED_STATUS_TO_XDR.keys())
custom_close_reasons = []
try:
server_config = get_server_config()
demisto.debug(f'get_xsoar_close_reasons server-config: {str(server_config)}')
if server_config:
custom_close_reasons: list = argToList(server_config.get('incident.closereasons', ''))
except Exception as e:
demisto.error(f"Could not get server configuration: {e}")
return default_xsoar_close_reasons + custom_close_reasons


def validate_custom_close_reasons_mapping(mapping: str, direction: str):
""" Check validity of provided custom close-reason mappings. """

xdr_statuses = [status.replace("resolved_", "").replace("_", " ").title() for status in XDR_RESOLVED_STATUS_TO_XSOAR]
xsoar_statuses = get_xsoar_close_reasons()

exception_message = ('Improper custom mapping ({direction}) provided: "{key_or_value}" is not a valid Cortex '
'{xsoar_or_xdr} close-reason. Valid Cortex {xsoar_or_xdr} close-reasons are: {statuses}')

def to_xdr_status(status):
return "resolved_" + "_".join(status.lower().split(" "))

custom_mapping = comma_separated_mapping_to_dict(mapping)

valid_key = valid_value = True # If no mapping was provided.

for key, value in custom_mapping.items():
if direction == XSOAR_TO_XDR:
xdr_close_reason = to_xdr_status(value)
valid_key = key in xsoar_statuses
valid_value = xdr_close_reason in XDR_RESOLVED_STATUS_TO_XSOAR
elif direction == XDR_TO_XSOAR:
xdr_close_reason = to_xdr_status(key)
valid_key = xdr_close_reason in XDR_RESOLVED_STATUS_TO_XSOAR
valid_value = value in xsoar_statuses

if not valid_key:
raise DemistoException(
exception_message.format(direction=direction,
key_or_value=key,
xsoar_or_xdr="XSOAR" if direction == XSOAR_TO_XDR else "XDR",
statuses=xsoar_statuses
if direction == XSOAR_TO_XDR else xdr_statuses))
elif not valid_value:
raise DemistoException(
exception_message.format(direction=direction,
key_or_value=value,
xsoar_or_xdr="XDR" if direction == XSOAR_TO_XDR else "XSOAR",
statuses=xdr_statuses
if direction == XSOAR_TO_XDR else xsoar_statuses))


class Client(CoreClient):
def __init__(self, base_url, proxy, verify, timeout, params=None):
if not params:
Expand Down Expand Up @@ -157,54 +215,12 @@ def test_module(self, first_fetch_time):
raise

# XSOAR -> XDR
self.validate_custom_mapping(mapping=self._params.get("custom_xsoar_to_xdr_close_reason_mapping"),
direction=XSOAR_TO_XDR)
validate_custom_close_reasons_mapping(mapping=self._params.get("custom_xsoar_to_xdr_close_reason_mapping"),
direction=XSOAR_TO_XDR)

# XDR -> XSOAR
self.validate_custom_mapping(mapping=self._params.get("custom_xdr_to_xsoar_close_reason_mapping"),
direction=XDR_TO_XSOAR)

def validate_custom_mapping(self, mapping: str, direction: str):
""" Check validity of provided custom close-reason mappings. """

xdr_statuses_to_xsoar = [status.replace("resolved_", "").replace("_", " ").title()
for status in XDR_RESOLVED_STATUS_TO_XSOAR]
xsoar_statuses_to_xdr = list(XSOAR_RESOLVED_STATUS_TO_XDR.keys())

exception_message = ('Improper custom mapping ({direction}) provided: "{key_or_value}" is not a valid Cortex '
'{xsoar_or_xdr} close-reason. Valid Cortex {xsoar_or_xdr} close-reasons are: {statuses}')

def to_xdr_status(status):
return "resolved_" + "_".join(status.lower().split(" "))

custom_mapping = comma_separated_mapping_to_dict(mapping)

valid_key = valid_value = True # If no mapping was provided.

for key, value in custom_mapping.items():
if direction == XSOAR_TO_XDR:
xdr_close_reason = to_xdr_status(value)
valid_key = key in XSOAR_RESOLVED_STATUS_TO_XDR
valid_value = xdr_close_reason in XDR_RESOLVED_STATUS_TO_XSOAR
elif direction == XDR_TO_XSOAR:
xdr_close_reason = to_xdr_status(key)
valid_key = xdr_close_reason in XDR_RESOLVED_STATUS_TO_XSOAR
valid_value = value in XSOAR_RESOLVED_STATUS_TO_XDR

if not valid_key:
raise DemistoException(
exception_message.format(direction=direction,
key_or_value=key,
xsoar_or_xdr="XSOAR" if direction == XSOAR_TO_XDR else "XDR",
statuses=xsoar_statuses_to_xdr
if direction == XSOAR_TO_XDR else xdr_statuses_to_xsoar))
elif not valid_value:
raise DemistoException(
exception_message.format(direction=direction,
key_or_value=value,
xsoar_or_xdr="XDR" if direction == XSOAR_TO_XDR else "XSOAR",
statuses=xdr_statuses_to_xsoar
if direction == XSOAR_TO_XDR else xsoar_statuses_to_xdr))
validate_custom_close_reasons_mapping(mapping=self._params.get("custom_xdr_to_xsoar_close_reason_mapping"),
direction=XDR_TO_XSOAR)

def handle_fetch_starred_incidents(self, limit: int, page_number: int, request_data: dict) -> List:
"""
Expand Down Expand Up @@ -789,6 +805,7 @@ def resolve_xsoar_close_reason(xdr_close_reason: str):
:param xdr_close_reason: XDR raw status/close reason e.g. 'resolved_false_positive'.
:return: XSOAR close reason.
"""
possible_xsoar_close_reasons = get_xsoar_close_reasons()

# Check if incoming XDR close-reason has a non-default mapping to XSOAR close-reason.
if demisto.params().get("custom_xdr_to_xsoar_close_reason_mapping"):
Expand All @@ -802,7 +819,7 @@ def resolve_xsoar_close_reason(xdr_close_reason: str):
xdr_close_reason.replace("resolved_", "").replace("_", " ").title()
)
xsoar_close_reason = custom_xdr_to_xsoar_close_reason_mapping.get(title_cased_xdr_close_reason)
if xsoar_close_reason in XSOAR_RESOLVED_STATUS_TO_XDR:
if xsoar_close_reason in possible_xsoar_close_reasons:
demisto.debug(
f"XDR->XSOAR custom close-reason exists, using {xdr_close_reason}={xsoar_close_reason}"
)
Expand Down
Loading
Loading