diff --git a/Packs/Code42/Integrations/Code42/CHANGELOG.md b/Packs/Code42/Integrations/Code42/CHANGELOG.md index 1fdf276feb35..44e1df80a27c 100644 --- a/Packs/Code42/Integrations/Code42/CHANGELOG.md +++ b/Packs/Code42/Integrations/Code42/CHANGELOG.md @@ -21,6 +21,7 @@ - Fixed bug in Fetch where errors occurred when `FileCategory` was set to include only one category. - Fixed bug in Fetch to handle new Code42 exposure type **Outside trusted domains**. - Improved Fetch to handle unsupported exposure types better. +- Added option to specify `All` in `exposure` argument in `search-securitydata` command to fetch all results with exfiltration. ## [20.3.3] - 2020-03-18 #### New Integration diff --git a/Packs/Code42/Integrations/Code42/Code42.py b/Packs/Code42/Integrations/Code42/Code42.py index 162897355b90..2cad184ea20f 100644 --- a/Packs/Code42/Integrations/Code42/Code42.py +++ b/Packs/Code42/Integrations/Code42/Code42.py @@ -529,8 +529,9 @@ def _create_hash_filter(hash_arg): def _create_exposure_filter(exposure_arg): # Because the CLI can't accept lists, convert the args to a list if the type is string. - if isinstance(exposure_arg, str): - exposure_arg = exposure_arg.split(",") + exposure_arg = [arg.strip() for arg in exposure_arg.split(",")] + if "All" in exposure_arg: + return ExposureType.exists() return ExposureType.is_in(exposure_arg) diff --git a/Packs/Code42/Integrations/Code42/Code42.yml b/Packs/Code42/Integrations/Code42/Code42.yml index 866eec6eabfc..62b8d3c3178f 100644 --- a/Packs/Code42/Integrations/Code42/Code42.yml +++ b/Packs/Code42/Integrations/Code42/Code42.yml @@ -78,11 +78,14 @@ script: secret: false - auto: PREDEFINED default: false - description: Exposure types to search for. Can be "RemovableMedia", "ApplicationRead", - "CloudStorage", "IsPublic", "SharedViaLink", "SharedViaDomain", or "OutsideTrustedDomains". + description: Exposure types to search for. Values can be "All", "RemovableMedia", + "ApplicationRead", "CloudStorage", "IsPublic", "SharedViaLink", "SharedViaDomain", + or "OutsideTrustedDomains". When "All" is specified with other types, other + types would be ignored and filter rule for all types would be applied. isArray: true name: exposure predefined: + - All - RemovableMedia - ApplicationRead - CloudStorage @@ -657,7 +660,8 @@ script: required: true secret: false - default: false - description: The name of the legal hold matter to which to which the user will be added. + description: The name of the legal hold matter to which to which the user will + be added. isArray: false name: mattername required: true @@ -680,35 +684,36 @@ script: description: A name for a Code42 legal hold matter. type: String - arguments: - - default: false - description: The username of the user to remove from the given legal hold matter. - isArray: false - name: username - required: true - secret: false - - default: false - description: The name of the legal hold matter from which to which the user will be removed. - isArray: false - name: mattername - required: true - secret: false + - default: false + description: The username of the user to remove from the given legal hold matter. + isArray: false + name: username + required: true + secret: false + - default: false + description: The name of the legal hold matter from which to which the user + will be removed. + isArray: false + name: mattername + required: true + secret: false deprecated: false description: Removes a Code42 user from a legal hold matter. execution: false name: code42-legalhold-remove-user outputs: - - contextPath: Code42.LegalHold.UserID - description: The ID of a Code42 user. - type: Unknown - - contextPath: Code42.LegalHold.MatterID - description: The ID of a Code42 legal hold matter. - type: String - - contextPath: Code42.LegalHold.Username - description: A username for a Code42 user. - type: String - - contextPath: Code42.LegalHold.MatterName - description: A name for a Code42 legal hold matter. - type: String + - contextPath: Code42.LegalHold.UserID + description: The ID of a Code42 user. + type: Unknown + - contextPath: Code42.LegalHold.MatterID + description: The ID of a Code42 legal hold matter. + type: String + - contextPath: Code42.LegalHold.Username + description: A username for a Code42 user. + type: String + - contextPath: Code42.LegalHold.MatterName + description: A name for a Code42 legal hold matter. + type: String - arguments: - default: false description: Either the SHA256 or MD5 hash of the file. @@ -771,7 +776,7 @@ script: - contextPath: Code42.DepartingEmployee.DepartureDate description: The departure date for the Departing Employee. type: Unknown - dockerimage: demisto/py42:1.0.0.9653 + dockerimage: demisto/py42:1.0.0.9801 feed: false isfetch: true longRunning: false diff --git a/Packs/Code42/Integrations/Code42/Code42_test.py b/Packs/Code42/Integrations/Code42/Code42_test.py index 2c0414297634..e979df70eed2 100644 --- a/Packs/Code42/Integrations/Code42/Code42_test.py +++ b/Packs/Code42/Integrations/Code42/Code42_test.py @@ -56,6 +56,28 @@ "results": 50, } +MOCK_SECURITY_DATA_SEARCH_QUERY_EXPOSURE_TYPE_ALL = { + "hash": "d41d8cd98f00b204e9800998ecf8427e", + "hostname": "DESKTOP-0001", + "username": "user3@example.com", + "exposure": "All", + "results": 50, +} + +MOCK_SECURITY_DATA_SEARCH_QUERY_EXPOSURE_TYPE_ALL_WITH_OTHERS = { + "hash": "d41d8cd98f00b204e9800998ecf8427e", + "hostname": "DESKTOP-0001", + "username": "user3@example.com", + "exposure": "ApplicationRead, All", + "results": 50, +} + +MOCK_SECURITY_DATA_SEARCH_QUERY_WITHOUT_EXPOSURE_TYPE = { + "hash": "d41d8cd98f00b204e9800998ecf8427e", + "hostname": "DESKTOP-0001", + "username": "user3@example.com", + "results": 50, +} MOCK_SECURITY_EVENT_RESPONSE = """ { @@ -2067,3 +2089,74 @@ def test_fetch_incidents_fetch_limit(code42_fetch_incidents_mock): assert len(incidents) == 1 assert next_run["last_fetch"] assert not remaining_incidents + + +@pytest.mark.parametrize( + "query", + [MOCK_SECURITY_DATA_SEARCH_QUERY_EXPOSURE_TYPE_ALL, + MOCK_SECURITY_DATA_SEARCH_QUERY_EXPOSURE_TYPE_ALL_WITH_OTHERS + ] +) +def test_security_data_search_command_searches_exposure_exists_when_all_is_specified( + code42_file_events_mock, query +): + client = create_client(code42_file_events_mock) + cmd_res = securitydata_search_command(client, query) + code42_res = cmd_res[0] + file_res = cmd_res[1] + + assert code42_res.outputs_prefix == "Code42.SecurityData" + assert code42_res.outputs_key_field == "EventID" + assert file_res.outputs_prefix == "File" + + actual_query = code42_file_events_mock.securitydata.search_file_events.call_args[0][0] + + # Assert that the correct query gets made + filter_groups = json.loads(str(actual_query))["groups"] + expected_query_items = [ + ("md5Checksum", "d41d8cd98f00b204e9800998ecf8427e"), + ("osHostName", "DESKTOP-0001"), + ("deviceUserName", "user3@example.com"), + ("exposure", None), + ] + + # Assert that the correct query gets made + assert len(filter_groups) == len(expected_query_items) + for i in range(0, len(filter_groups)): + _filter = filter_groups[i]["filters"][0] + assert _filter["term"] == expected_query_items[i][0] + assert _filter["value"] == expected_query_items[i][1] + + assert len(filter_groups) == 4 + + +def test_security_data_search_command_searches_exposure_exists_when_no_exposure_type_is_specified( + code42_file_events_mock, +): + client = create_client(code42_file_events_mock) + cmd_res = securitydata_search_command(client, MOCK_SECURITY_DATA_SEARCH_QUERY_WITHOUT_EXPOSURE_TYPE) + code42_res = cmd_res[0] + file_res = cmd_res[1] + + assert code42_res.outputs_prefix == "Code42.SecurityData" + assert code42_res.outputs_key_field == "EventID" + assert file_res.outputs_prefix == "File" + + actual_query = code42_file_events_mock.securitydata.search_file_events.call_args[0][0] + + # Assert that the correct query gets made + filter_groups = json.loads(str(actual_query))["groups"] + expected_query_items = [ + ("md5Checksum", "d41d8cd98f00b204e9800998ecf8427e"), + ("osHostName", "DESKTOP-0001"), + ("deviceUserName", "user3@example.com"), + ] + + # Assert that the correct query gets made + assert len(filter_groups) == len(expected_query_items) + for i in range(0, len(filter_groups)): + _filter = filter_groups[i]["filters"][0] + assert _filter["term"] == expected_query_items[i][0] + assert _filter["value"] == expected_query_items[i][1] + + assert len(filter_groups) == 3