diff --git a/Packs/Code42/Integrations/Code42/Code42.py b/Packs/Code42/Integrations/Code42/Code42.py index 3a6d3e61e369..f069d0675ac5 100644 --- a/Packs/Code42/Integrations/Code42/Code42.py +++ b/Packs/Code42/Integrations/Code42/Code42.py @@ -88,21 +88,6 @@ "osHostName": "Hostname", } -CODE42_FILE_CATEGORY_MAPPER = { - "SourceCode": FileCategory.SOURCE_CODE, - "Audio": FileCategory.AUDIO, - "Executable": FileCategory.EXECUTABLE, - "Document": FileCategory.DOCUMENT, - "Image": FileCategory.IMAGE, - "PDF": FileCategory.PDF, - "Presentation": FileCategory.PRESENTATION, - "Script": FileCategory.SCRIPT, - "Spreadsheet": FileCategory.SPREADSHEET, - "Video": FileCategory.VIDEO, - "VirtualDiskImage": FileCategory.VIRTUAL_DISK_IMAGE, - "Archive": FileCategory.ZIP, -} - SECURITY_EVENT_HEADERS = [ "EventType", "FileName", @@ -540,8 +525,24 @@ def _create_exposure_filter(exposure_arg): return ExposureType.is_in(exposure_arg) -def _get_file_category_value(key): - return CODE42_FILE_CATEGORY_MAPPER.get(key, "UNCATEGORIZED") +def get_file_category_value(key): + # Meant to handle all possible cases + key = key.lower().replace("-", "").replace("_", "") + category_map = { + "sourcecode": FileCategory.SOURCE_CODE, + "audio": FileCategory.AUDIO, + "executable": FileCategory.EXECUTABLE, + "document": FileCategory.DOCUMENT, + "image": FileCategory.IMAGE, + "pdf": FileCategory.PDF, + "presentation": FileCategory.PRESENTATION, + "script": FileCategory.SCRIPT, + "spreadsheet": FileCategory.SPREADSHEET, + "video": FileCategory.VIDEO, + "virtualdiskimage": FileCategory.VIRTUAL_DISK_IMAGE, + "archive": FileCategory.ZIP, + } + return category_map.get(key, "UNCATEGORIZED") class ObservationToSecurityQueryMapper(object): @@ -642,7 +643,7 @@ def _create_file_category_filters(self): observed_file_categories = self._observation_data.get("fileCategories") if observed_file_categories: categories = [ - _get_file_category_value(c.get("category")) + get_file_category_value(c.get("category")) for c in observed_file_categories if c.get("isSignificant") and c.get("category") ] diff --git a/Packs/Code42/Integrations/Code42/Code42_test.py b/Packs/Code42/Integrations/Code42/Code42_test.py index 2b820b5c6aa1..e6fa3333addc 100644 --- a/Packs/Code42/Integrations/Code42/Code42_test.py +++ b/Packs/Code42/Integrations/Code42/Code42_test.py @@ -1,12 +1,15 @@ import json import pytest +from py42.sdk.queries.fileevents.filters import FileCategory from requests import Response from py42.sdk import SDKClient from py42.response import Py42Response +from py42.sdk.queries.alerts.filters import Severity from Code42 import ( Code42Client, Code42LegalHoldMatterNotFoundError, Code42InvalidLegalHoldMembershipError, + get_file_category_value, build_query_payload, map_observation_to_security_query, map_to_code42_event_context, @@ -476,6 +479,13 @@ "fileCount": 3, "totalFileSize": 533846, "isSignificant": true + }, + { + "type$": "OBSERVED_FILE_CATEGORY", + "category": "Pdf", + "fileCount": 3, + "totalFileSize": 533846, + "isSignificant": true } ], "files": [ @@ -723,9 +733,12 @@ "filters": [{"operator": "IS", "term": "exposure", "value": "ApplicationRead"}], }, { - "filterClause": "AND", - "filters": [{"operator": "IS", "term": "fileCategory", "value": "SOURCE_CODE"}], - }, + "filterClause": "OR", + "filters": [ + {"operator": "IS", "term": "fileCategory", "value": "PDF"}, + {"operator": "IS", "term": "fileCategory", "value": "SOURCE_CODE"} + ] + } ], "pgNum": 1, "pgSize": 10000, @@ -1347,6 +1360,48 @@ def assert_detection_list_outputs_match_response_items(outputs_list, response_it """TESTS""" +def test_get_file_category_value_handles_screaming_snake_case(): + actual = get_file_category_value("SOURCE_CODE") + expected = FileCategory.SOURCE_CODE + assert actual == expected + + +def test_get_file_category_value_handles_capitalized_case(): + actual = get_file_category_value("Pdf") + expected = FileCategory.PDF + assert actual == expected + + +def test_get_file_category_value_handles_lower_case(): + actual = get_file_category_value("pdf") + expected = FileCategory.PDF + assert actual == expected + + +def test_get_file_category_value_handles_upper_case(): + actual = get_file_category_value("PDF") + expected = FileCategory.PDF + assert actual == expected + + +def test_get_file_category_value_handles_pascal_case(): + actual = get_file_category_value("SourceCode") + expected = FileCategory.SOURCE_CODE + assert actual == expected + + +def test_get_file_category_value_handles_hungarian_case(): + actual = get_file_category_value("sourceCode") + expected = FileCategory.SOURCE_CODE + assert actual == expected + + +def test_get_file_category_value_handles_hyphenated_case(): + actual = get_file_category_value("source-code") + expected = FileCategory.SOURCE_CODE + assert actual == expected + + def test_client_lazily_inits_sdk(mocker, code42_sdk_mock): sdk_factory_mock = mocker.patch("py42.sdk.from_local_account") response_json_mock = """{"total": 1, "users": [{"username": "Test"}]}""" @@ -1992,8 +2047,8 @@ def test_fetch_incidents_handles_multi_severity(code42_fetch_incidents_mock): integration_context=None, ) call_args = str(code42_fetch_incidents_mock.alerts.search.call_args[0][0]) - assert "HIGH" in call_args - assert "LOW" in call_args + assert Severity.HIGH in call_args + assert Severity.LOW in call_args def test_fetch_when_include_files_includes_files(code42_fetch_incidents_mock): diff --git a/Packs/Code42/ReleaseNotes/2_0_4.md b/Packs/Code42/ReleaseNotes/2_0_4.md index ff0726b13117..162127ac56d9 100644 --- a/Packs/Code42/ReleaseNotes/2_0_4.md +++ b/Packs/Code42/ReleaseNotes/2_0_4.md @@ -1,6 +1,7 @@ #### Integrations ##### Code42 +- Fix bug where capitalized Alert file-observation file categories would not map to file event query values. - Upgrade py42 dependency and internal code improvements. #### Playbooks