Skip to content

Commit

Permalink
Fix CS Falcon outgoing mapper (#38087)
Browse files Browse the repository at this point in the history
* fix

* rn

* improves

* Bump pack from version CrowdStrikeFalcon to 2.1.6.

* works

* rn

* add test

* add test

* pre commit

* Bump pack from version CrowdStrikeFalcon to 2.1.7.

---------

Co-authored-by: Content Bot <[email protected]>
  • Loading branch information
RosenbergYehuda and Content Bot authored Jan 15, 2025
1 parent 5a8d325 commit 3dfa91f
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@
}
}
},
"CrowdStrike Falcon On-Demand Scans Detection": {
"dontMapEventToLabels": true,
"internalMapping": {
"status": {
"simple": "state"
}
}
},
"CrowdStrike Falcon OFP Detection": {
"dontMapEventToLabels": true,
"internalMapping": {
"status": {
"simple": "state"
}
}
},
"CrowdStrike Falcon Incident": {
"dontMapEventToLabels": true,
"internalMapping": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,12 @@

''' MIRRORING DICTIONARIES & PARAMS '''

DETECTION_STATUS = {'new', 'in_progress', 'true_positive', 'false_positive', 'ignored', 'closed', 'reopened'}
IDP_AND_MOBILE_DETECTION_STATUS = {'new', 'in_progress', 'closed', 'reopened'}
LEGACY_DETECTION_STATUS = {'new', 'in_progress', 'true_positive', 'false_positive', 'ignored', 'closed', 'reopened'}
STATUS_LIST_FOR_MULTIPLE_DETECTION_TYPES = {'new', 'in_progress', 'closed', 'reopened'}
LEGACY_CS_FALCON_DETECTION_OUTGOING_ARGS = {'status': f'Updated detection status, one of {"/".join(LEGACY_DETECTION_STATUS)}'}

CS_FALCON_DETECTION_OUTGOING_ARGS = {'status': f'Updated detection status, one of {"/".join(DETECTION_STATUS)}'}
CS_FALCON_DETECTION_OUTGOING_ARGS = {
'status': f'Updated detection status, one of {"/".join(STATUS_LIST_FOR_MULTIPLE_DETECTION_TYPES)}'}

CS_FALCON_INCIDENT_OUTGOING_ARGS = {'tag': 'A tag that have been added or removed from the incident',
'status': f'Updated incident status, one of {"/".join(STATUS_TEXT_TO_NUM.keys())}'}
Expand Down Expand Up @@ -2102,23 +2104,6 @@ def resolve_detection(ids, status, assigned_to_uuid, show_in_ui, comment, tag):
return http_request('PATCH', url, data=data)


def resolve_idp_or_mobile_detection(ids, status):
"""
Send a request to update IDP/Mobile detection status.
:type ids: ``list``
:param ids: The list of ids to update.
:type status: ``str``
:param status: The new status to set.
:return: The response.
:rtype ``dict``
"""
data = {
"action_parameters": [{"name": "update_status", "value": status}],
"ids": ids
}
return http_request('PATCH', '/alerts/entities/alerts/v2', data=json.dumps(data))


def contain_host(ids):
"""
Contains host(s) with matching ids
Expand Down Expand Up @@ -2248,9 +2233,10 @@ def update_incident_request(ids: list[str], action_parameters: dict[str, Any]):


def update_detection_request(ids: list[str], status: str) -> dict:
if status not in DETECTION_STATUS:
list_of_stats = LEGACY_DETECTION_STATUS if LEGACY_VERSION else STATUS_LIST_FOR_MULTIPLE_DETECTION_TYPES
if status not in list_of_stats:
raise DemistoException(f'CrowdStrike Falcon Error: '
f'Status given is {status} and it is not in {DETECTION_STATUS}')
f'Status given is {status} and it is not in {list_of_stats}')
return resolve_detection(ids=ids, status=status, assigned_to_uuid=None, show_in_ui=None, comment=None, tag=None)


Expand All @@ -2264,10 +2250,10 @@ def update_idp_or_mobile_detection_request(ids: list[str], status: str) -> dict:
:return: The response.
:rtype ``dict``
"""
if status not in IDP_AND_MOBILE_DETECTION_STATUS:
if status not in STATUS_LIST_FOR_MULTIPLE_DETECTION_TYPES:
raise DemistoException(f'CrowdStrike Falcon Error: '
f'Status given is {status} and it is not in {IDP_AND_MOBILE_DETECTION_STATUS}')
return resolve_idp_or_mobile_detection(ids=ids, status=status)
f'Status given is {status} and it is not in {STATUS_LIST_FOR_MULTIPLE_DETECTION_TYPES}')
return resolve_detections_request(ids=ids, update_status=status)


def list_host_groups(filter: str | None, limit: str | None, offset: str | None) -> dict:
Expand Down Expand Up @@ -2883,11 +2869,25 @@ def get_mapping_fields_command() -> GetMappingFieldsResponse:
incident_type_scheme.add_field(name=argument, description=description)
mapping_response.add_scheme_type(incident_type_scheme)

detection_type_scheme = SchemeTypeMapping(type_name='CrowdStrike Falcon Detection')
for argument, description in CS_FALCON_DETECTION_OUTGOING_ARGS.items():
detection_type_scheme.add_field(name=argument, description=description)
mapping_response.add_scheme_type(detection_type_scheme)

if LEGACY_VERSION:
legacy_detection_type_scheme = SchemeTypeMapping(type_name='CrowdStrike Falcon Detection - LAGACY')
for argument, description in LEGACY_CS_FALCON_DETECTION_OUTGOING_ARGS.items():
legacy_detection_type_scheme .add_field(name=argument, description=description)
mapping_response.add_scheme_type(legacy_detection_type_scheme)

return mapping_response

# Supported only in the new version (Raptor) and not in the legacy version
detection_types = [
'CrowdStrike Falcon Detection',
'CrowdStrike Falcon OFP Detection',
'CrowdStrike Falcon On-Demand Scans Detection']

for detection_type in detection_types:
detection_type_scheme = SchemeTypeMapping(type_name=detection_type)
for argument, description in CS_FALCON_DETECTION_OUTGOING_ARGS.items():
detection_type_scheme.add_field(name=argument, description=description)
mapping_response.add_scheme_type(detection_type_scheme)
return mapping_response


Expand Down Expand Up @@ -7218,6 +7218,7 @@ def main():

elif command == 'cs-falcon-rtr-list-network-stats':
host_id = args.get('host_id')

offline = argToBoolean(args.get('queue_offline', False))
timeout = arg_to_number(args.get('timeout'))
return_results(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4508,6 +4508,26 @@ def test_get_modified_remote_data_command(mocker):

@pytest.mark.parametrize('status',
['new', 'in_progress', 'true_positive', 'false_positive', 'ignored', 'closed', 'reopened'])
def test_update_detection_request_good__legacy(mocker, status):
"""
Given
- list of detections IDs
- status to change for the given detection in the remote system, which is one of the permitted statuses
When
- running update_remote_system_command
Then
- the resolve_detection command is called successfully with the right arguments
"""
from CrowdStrikeFalcon import update_detection_request
mock_resolve_detection = mocker.patch('CrowdStrikeFalcon.resolve_detection')
mocker.patch('CrowdStrikeFalcon.LEGACY_VERSION', True)
update_detection_request([input_data.remote_detection_id], status)
assert mock_resolve_detection.call_args[1]['ids'] == [input_data.remote_detection_id]
assert mock_resolve_detection.call_args[1]['status'] == status


@pytest.mark.parametrize('status',
['new', 'in_progress', 'closed', 'reopened'])
def test_update_detection_request_good(mocker, status):
"""
Given
Expand All @@ -4520,23 +4540,43 @@ def test_update_detection_request_good(mocker, status):
"""
from CrowdStrikeFalcon import update_detection_request
mock_resolve_detection = mocker.patch('CrowdStrikeFalcon.resolve_detection')
mocker.patch('CrowdStrikeFalcon.LEGACY_VERSION', False)
update_detection_request([input_data.remote_detection_id], status)
assert mock_resolve_detection.call_args[1]['ids'] == [input_data.remote_detection_id]
assert mock_resolve_detection.call_args[1]['status'] == status


@pytest.mark.parametrize('status', ['other', ''])
def test_update_detection_request_bad(status):
def test_update_detection_request_bad__lagacy(status):
"""
Given
- list of detections IDs
- status to change for the given detection in the remote system, which is not one of the permitted statuses
When
- running update_remote_system_command
Then
- an exception is raised
"""
from CrowdStrikeFalcon import update_detection_request
with pytest.raises(DemistoException) as de:
update_detection_request([input_data.remote_detection_id], status)
assert 'CrowdStrike Falcon Error' in str(de.value)


@pytest.mark.parametrize('status', ['true_positive', ''])
def test_update_detection_request_bad(status, mocker):
"""
Given
- list of detections IDs
- status to change for the given detection in the remote system, which is not one of the permitted statuses
'true_positive' is not a valid status for the new version of the API
When
- running update_remote_system_command
Then
- an exception is raised
"""
from CrowdStrikeFalcon import update_detection_request
mocker.patch('CrowdStrikeFalcon.LEGACY_VERSION', False)
with pytest.raises(DemistoException) as de:
update_detection_request([input_data.remote_detection_id], status)
assert 'CrowdStrike Falcon Error' in str(de.value)
Expand Down Expand Up @@ -4706,21 +4746,45 @@ def test_remote_incident_handle_tags(mocker, tags, action_name):
assert mock_update_incident_request.call_args_list[0].kwargs['json']['action_parameters'][0]['name'] == action_name


def test_get_mapping_fields_command():
def test_get_mapping_fields_command(mocker):
"""
Given
- nothing
When
- running get_mapping_fields_command
- running get_mapping_fields_command on the new version of the API
Then
- the result fits the expected mapping scheme
"""
from CrowdStrikeFalcon import get_mapping_fields_command
mocker.patch('CrowdStrikeFalcon.LEGACY_VERSION', False)
result = get_mapping_fields_command()
assert result.scheme_types_mappings[0].type_name == 'CrowdStrike Falcon Incident'
assert result.scheme_types_mappings[0].fields.keys() == {'status', 'tag'}
assert result.scheme_types_mappings[1].type_name == 'CrowdStrike Falcon Detection'
assert result.scheme_types_mappings[1].fields.keys() == {'status'}
assert result.scheme_types_mappings[2].type_name == 'CrowdStrike Falcon OFP Detection'
assert result.scheme_types_mappings[2].fields.keys() == {'status'}
assert result.scheme_types_mappings[3].type_name == 'CrowdStrike Falcon On-Demand Scans Detection'
assert result.scheme_types_mappings[3].fields.keys() == {'status'}


def test_get_mapping_fields_command__legacy(mocker):
"""
Given
- nothing
When
- running get_mapping_fields_command on the legacy version of the API
Then
- the result fits the expected mapping scheme
"""
from CrowdStrikeFalcon import get_mapping_fields_command
mocker.patch('CrowdStrikeFalcon.LEGACY_VERSION', True)
result = get_mapping_fields_command()
assert result.scheme_types_mappings[0].type_name == 'CrowdStrike Falcon Incident'
assert result.scheme_types_mappings[0].fields.keys() == {'status', 'tag'}
assert result.scheme_types_mappings[1].type_name == 'CrowdStrike Falcon Detection - LAGACY'
assert result.scheme_types_mappings[1].fields.keys() == {'status'}
assert len(result.scheme_types_mappings) == 2


def test_error_in_get_detections_by_behaviors(mocker):
Expand Down
14 changes: 14 additions & 0 deletions Packs/CrowdStrikeFalcon/ReleaseNotes/2_1_7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

#### Mappers

##### CrowdStrike Falcon - Outgoing Mapper

Fixed an issue where changes to the status of `ODS Detections` and `OFP Detections` incident types were not reflected in the mirror out.

#### Integrations

##### CrowdStrike Falcon

- Added support for the ***get-mapping-fields*** command to include `ODS Detections` and `OFP Detections`.

- Fixed an issue where the mirroring out of certain incident types was using the legacy endpoint.
2 changes: 1 addition & 1 deletion Packs/CrowdStrikeFalcon/pack_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "CrowdStrike Falcon",
"description": "The CrowdStrike Falcon OAuth 2 API (formerly the Falcon Firehose API), enables fetching and resolving detections, searching devices, getting behaviors by ID, containing hosts, and lifting host containment.",
"support": "xsoar",
"currentVersion": "2.1.6",
"currentVersion": "2.1.7",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
Expand Down

0 comments on commit 3dfa91f

Please sign in to comment.