diff --git a/Packs/Code42/Integrations/Code42/Code42.py b/Packs/Code42/Integrations/Code42/Code42.py index 10255655f0a7..a8b7c2d1300b 100644 --- a/Packs/Code42/Integrations/Code42/Code42.py +++ b/Packs/Code42/Integrations/Code42/Code42.py @@ -165,7 +165,11 @@ def __init__(self, sdk, base_url, auth, verify=True, proxy=False): # Allow sdk parameter for unit testing. # Otherwise, lazily load the SDK so that the TEST Command can effectively check auth. self._sdk = sdk - self._sdk_factory = lambda: py42.sdk.from_local_account(base_url, auth[0], auth[1]) if not self._sdk else None + self._sdk_factory = ( + lambda: py42.sdk.from_local_account(base_url, auth[0], auth[1]) + if not self._sdk + else None + ) py42.settings.set_user_agent_suffix("Cortex XSOAR") def _get_sdk(self): @@ -504,52 +508,61 @@ def create_command_error_message(cmd, ex): @logger def alert_get_command(client, args): code42_securityalert_context = [] - try: - alert = client.get_alert_details(args["id"]) - if not alert: - return "No results found", {}, {} - - code42_context = map_to_code42_alert_context(alert) - code42_securityalert_context.append(code42_context) - readable_outputs = tableToMarkdown( - "Code42 Security Alert Results", - code42_securityalert_context, - headers=SECURITY_ALERT_HEADERS, + alert = client.get_alert_details(args["id"]) + if not alert: + return CommandResults( + readable_output="No results found", + outputs={"Results": []}, + outputs_key_field="ID", + outputs_prefix="Code42.SecurityAlert", + raw_response={}, ) - return readable_outputs, {"Code42.SecurityAlert": code42_securityalert_context}, alert - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + + code42_context = map_to_code42_alert_context(alert) + code42_securityalert_context.append(code42_context) + readable_outputs = tableToMarkdown( + "Code42 Security Alert Results", + code42_securityalert_context, + headers=SECURITY_ALERT_HEADERS, + ) + return CommandResults( + outputs_prefix="Code42.SecurityAlert", + outputs_key_field="ID", + outputs=code42_securityalert_context, + readable_output=readable_outputs, + raw_response=alert, + ) @logger def alert_resolve_command(client, args): - code42_security_alert_context = [] - - try: - alert_id = client.resolve_alert(args["id"]) - - if not alert_id: - return "No results found", {}, {} - - # Retrieve new alert details - alert_details = client.get_alert_details(alert_id) - if not alert_details: - return "Error retrieving updated alert", {}, {} - - code42_context = map_to_code42_alert_context(alert_details) - code42_security_alert_context.append(code42_context) - readable_outputs = tableToMarkdown( - "Code42 Security Alert Resolved", - code42_security_alert_context, - headers=SECURITY_ALERT_HEADERS, - ) - return ( - readable_outputs, - {"Code42.SecurityAlert": code42_security_alert_context}, - alert_details, + code42_securityalert_context = [] + alert_id = client.resolve_alert(args["id"]) + if not alert_id: + return CommandResults( + readable_output="No results found", + outputs={"Results": []}, + outputs_key_field="ID", + outputs_prefix="Code42.SecurityAlert", + raw_response={}, ) - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + + # Retrieve new alert details + alert_details = client.get_alert_details(alert_id) + code42_context = map_to_code42_alert_context(alert_details) + code42_securityalert_context.append(code42_context) + readable_outputs = tableToMarkdown( + "Code42 Security Alert Resolved", + code42_securityalert_context, + headers=SECURITY_ALERT_HEADERS, + ) + return CommandResults( + outputs_prefix="Code42.SecurityAlert", + outputs_key_field="ID", + outputs=code42_securityalert_context, + readable_output=readable_outputs, + raw_response=alert_details, + ) @logger @@ -557,129 +570,161 @@ def departingemployee_add_command(client, args): departing_date = args.get("departuredate") username = args["username"] note = args.get("note") - try: - user_id = client.add_user_to_departing_employee(username, departing_date, note) - de_context = { - "UserID": user_id, - "Username": username, - "DepartureDate": departing_date, - "Note": note, - } - readable_outputs = tableToMarkdown("Code42 Departing Employee List User Added", de_context) - return readable_outputs, {"Code42.DepartingEmployee": de_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.add_user_to_departing_employee(username, departing_date, note) + # CaseID included but is deprecated. + de_context = { + "CaseID": user_id, + "UserID": user_id, + "Username": username, + "DepartureDate": departing_date, + "Note": note, + } + readable_outputs = tableToMarkdown("Code42 Departing Employee List User Added", de_context) + return CommandResults( + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs=de_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def departingemployee_remove_command(client, args): username = args["username"] - try: - user_id = client.remove_user_from_departing_employee(username) - de_context = {"UserID": user_id, "Username": username} - readable_outputs = tableToMarkdown( - "Code42 Departing Employee List User Removed", de_context - ) - return readable_outputs, {"Code42.DepartingEmployee": de_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.remove_user_from_departing_employee(username) + # CaseID included but is deprecated. + de_context = {"CaseID": user_id, "UserID": user_id, "Username": username} + readable_outputs = tableToMarkdown("Code42 Departing Employee List User Removed", de_context) + return CommandResults( + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs=de_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def departingemployee_get_all_command(client, args): results = args.get("results") or 50 - try: - employees = client.get_all_departing_employees(results) - employees_context = [ - { - "UserID": e["userId"], - "Username": e["userName"], - "DepartureDate": e.get("departureDate"), - "Note": e["notes"], - } - for e in employees - ] - readable_outputs = tableToMarkdown("All Departing Employees", employees_context) - return ( - readable_outputs, - {"Code42.DepartingEmployee(val.UserID && val.UserID == obj.UserID)": employees_context}, - employees, + employees = client.get_all_departing_employees(results) + if not employees: + return CommandResults( + readable_output="No results found", + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs={"Results": []}, + raw_response={} ) - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + + employees_context = [ + { + "UserID": e.get("userId"), + "Username": e.get("userName"), + "DepartureDate": e.get("departureDate"), + "Note": e.get("notes"), + } + for e in employees + ] + readable_outputs = tableToMarkdown("All Departing Employees", employees_context) + return CommandResults( + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs=employees_context, + readable_output=readable_outputs, + raw_response=employees, + ) @logger def highriskemployee_add_command(client, args): username = args["username"] note = args.get("note") - try: - user_id = client.add_user_to_high_risk_employee(username, note) - hr_context = {"UserID": user_id, "Username": username} - readable_outputs = tableToMarkdown("Code42 High Risk Employee List User Added", hr_context) - return readable_outputs, {"Code42.HighRiskEmployee": hr_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.add_user_to_high_risk_employee(username, note) + hr_context = {"UserID": user_id, "Username": username} + readable_outputs = tableToMarkdown("Code42 High Risk Employee List User Added", hr_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=hr_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def highriskemployee_remove_command(client, args): username = args["username"] - try: - user_id = client.remove_user_from_high_risk_employee(username) - hr_context = {"UserID": user_id, "Username": username} - readable_outputs = tableToMarkdown( - "Code42 High Risk Employee List User Removed", hr_context - ) - return readable_outputs, {"Code42.HighRiskEmployee": hr_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.remove_user_from_high_risk_employee(username) + hr_context = {"UserID": user_id, "Username": username} + readable_outputs = tableToMarkdown("Code42 High Risk Employee List User Removed", hr_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=hr_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def highriskemployee_get_all_command(client, args): tags = args.get("risktags") results = args.get("results") or 50 - try: - employees = client.get_all_high_risk_employees(tags, results) - employees_context = [ - {"UserID": e.get("userId"), "Username": e.get("userName"), "Note": e.get("notes")} - for e in employees - ] - readable_outputs = tableToMarkdown("Retrieved All High Risk Employees", employees_context) - return ( - readable_outputs, - {"Code42.HighRiskEmployee(val.UserID && val.UserID == obj.UserID)": employees_context}, - employees, + employees = client.get_all_high_risk_employees(tags, results) + if not employees: + return CommandResults( + readable_output="No results found", + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs={"Results": []}, + raw_response={} ) - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + employees_context = [ + {"UserID": e.get("userId"), "Username": e.get("userName"), "Note": e.get("notes")} + for e in employees + ] + readable_outputs = tableToMarkdown("Retrieved All High Risk Employees", employees_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=employees_context, + readable_output=readable_outputs, + raw_response=employees, + ) @logger def highriskemployee_add_risk_tags_command(client, args): username = args.get("username") tags = args.get("risktags") - try: - user_id = client.add_user_risk_tags(username, tags) - rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} - readable_outputs = tableToMarkdown("Code42 Risk Tags Added", rt_context) - return readable_outputs, {"Code42.HighRiskEmployee": rt_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.add_user_risk_tags(username, tags) + rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} + readable_outputs = tableToMarkdown("Code42 Risk Tags Added", rt_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=rt_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def highriskemployee_remove_risk_tags_command(client, args): username = args.get("username") tags = args.get("risktags") - try: - user_id = client.remove_user_risk_tags(username, tags) - rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} - readable_outputs = tableToMarkdown("Code42 Risk Tags Removed", rt_context) - return readable_outputs, {"Code42.HighRiskEmployee": rt_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.remove_user_risk_tags(username, tags) + rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} + readable_outputs = tableToMarkdown("Code42 Risk Tags Removed", rt_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=rt_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger @@ -687,6 +732,7 @@ def securitydata_search_command(client, args): code42_security_data_context = [] _json = args.get("json") file_context = [] + # If JSON payload is passed as an argument, ignore all other args and search by JSON payload if _json is not None: file_events = client.search_file_events(_json) @@ -705,11 +751,29 @@ def securitydata_search_command(client, args): code42_security_data_context, headers=SECURITY_EVENT_HEADERS, ) - security_data_context_key = "Code42.SecurityData(val.EventID && val.EventID == obj.EventID)" - context = {security_data_context_key: code42_security_data_context, "File": file_context} - return readable_outputs, context, file_events + code42_results = CommandResults( + outputs_prefix="Code42.SecurityData", + outputs_key_field="EventID", + outputs=code42_security_data_context, + readable_output=readable_outputs, + raw_response=file_events, + ) + file_results = CommandResults( + outputs_prefix="File", outputs_key_field=None, outputs=file_context + ) + return code42_results, file_results + else: - return "No results found", {}, {} + return CommandResults( + readable_output="No results found", + outputs={"Results": []}, + outputs_key_field="EventID", + outputs_prefix="Code42.SecurityData", + raw_response={}, + ) + + +"""Fetching""" def _create_incident_from_alert_details(details): @@ -770,7 +834,7 @@ def _fetch_remaining_incidents_from_last_run(self): if remaining_incidents: return ( self._last_run, - remaining_incidents[:self._fetch_limit], + remaining_incidents[: self._fetch_limit], remaining_incidents[self._fetch_limit:], ) @@ -835,6 +899,9 @@ def fetch_incidents( return fetcher.fetch() +"""Main and test""" + + def test_module(client): try: # Will fail if unauthorized @@ -848,65 +915,84 @@ def test_module(client): ) -def main(): - """ - PARSE AND VALIDATE INTEGRATION PARAMS - """ +def get_command_map(): + return { + "code42-alert-get": alert_get_command, + "code42-alert-resolve": alert_resolve_command, + "code42-securitydata-search": securitydata_search_command, + "code42-departingemployee-add": departingemployee_add_command, + "code42-departingemployee-remove": departingemployee_remove_command, + "code42-departingemployee-get-all": departingemployee_get_all_command, + "code42-highriskemployee-add": highriskemployee_add_command, + "code42-highriskemployee-remove": highriskemployee_remove_command, + "code42-highriskemployee-get-all": highriskemployee_get_all_command, + "code42-highriskemployee-add-risk-tags": highriskemployee_add_risk_tags_command, + "code42-highriskemployee-remove-risk-tags": highriskemployee_remove_risk_tags_command, + } + + +def handle_test_command(client): + # This is the call made when pressing the integration Test button. + result = test_module(client) + demisto.results(result) + + +def handle_fetch_command(client): + integration_context = demisto.getIntegrationContext() + # Set and define the fetch incidents command to run after activated via integration settings. + next_run, incidents, remaining_incidents = fetch_incidents( + client=client, + last_run=demisto.getLastRun(), + first_fetch_time=demisto.params().get("fetch_time"), + event_severity_filter=demisto.params().get("alert_severity"), + fetch_limit=int(demisto.params().get("fetch_limit")), + include_files=demisto.params().get("include_files"), + integration_context=integration_context, + ) + demisto.setLastRun(next_run) + demisto.incidents(incidents) + # Store remaining incidents in integration context + integration_context["remaining_incidents"] = remaining_incidents + demisto.setIntegrationContext(integration_context) + + +def try_run_command(command): + try: + results = command() + if not isinstance(results, tuple) and not isinstance(results, list): + results = [results] + for result in results: + return_results(result) + except Exception as e: + return_error(create_command_error_message(demisto.command(), e)) + + +def run_code42_integration(): username = demisto.params().get("credentials").get("identifier") password = demisto.params().get("credentials").get("password") base_url = demisto.params().get("console_url") - # Remove trailing slash to prevent wrong URL path to service verify_certificate = not demisto.params().get("insecure", False) proxy = demisto.params().get("proxy", False) LOG("Command being called is {0}.".format(demisto.command())) - try: - client = Code42Client( - base_url=base_url, - sdk=None, - auth=(username, password), - verify=verify_certificate, - proxy=proxy, - ) - commands = { - "code42-alert-get": alert_get_command, - "code42-alert-resolve": alert_resolve_command, - "code42-securitydata-search": securitydata_search_command, - "code42-departingemployee-add": departingemployee_add_command, - "code42-departingemployee-remove": departingemployee_remove_command, - "code42-departingemployee-get-all": departingemployee_get_all_command, - "code42-highriskemployee-add": highriskemployee_add_command, - "code42-highriskemployee-remove": highriskemployee_remove_command, - "code42-highriskemployee-get-all": highriskemployee_get_all_command, - "code42-highriskemployee-add-risk-tags": highriskemployee_add_risk_tags_command, - "code42-highriskemployee-remove-risk-tags": highriskemployee_remove_risk_tags_command, - } - command = demisto.command() - if command == "test-module": - # This is the call made when pressing the integration Test button. - result = test_module(client) - demisto.results(result) - elif command == "fetch-incidents": - integration_context = demisto.getIntegrationContext() - # Set and define the fetch incidents command to run after activated via integration settings. - next_run, incidents, remaining_incidents = fetch_incidents( - client=client, - last_run=demisto.getLastRun(), - first_fetch_time=demisto.params().get("fetch_time"), - event_severity_filter=demisto.params().get("alert_severity"), - fetch_limit=int(demisto.params().get("fetch_limit")), - include_files=demisto.params().get("include_files"), - integration_context=integration_context, - ) - demisto.setLastRun(next_run) - demisto.incidents(incidents) - # Store remaining incidents in integration context - integration_context["remaining_incidents"] = remaining_incidents - demisto.setIntegrationContext(integration_context) - elif command in commands: - return_outputs(*commands[command](client, demisto.args())) - # Log exceptions - except Exception as e: - return_error("Failed to execute {0} command. Error: {1}".format(demisto.command(), str(e))) + client = Code42Client( + base_url=base_url, + sdk=None, + auth=(username, password), + verify=verify_certificate, + proxy=proxy, + ) + commands = get_command_map() + command = demisto.command() + if command == "test-module": + handle_test_command(client) + elif command == "fetch-incidents": + handle_fetch_command(client) + elif command in commands: + try_run_command(lambda: commands[command](client, demisto.args())) + + +def main(): + run_code42_integration() if __name__ in ("__main__", "__builtin__", "builtins"): diff --git a/Packs/Code42/Integrations/Code42/Code42.yml b/Packs/Code42/Integrations/Code42/Code42.yml index 2dcf45befea1..78356ecedd32 100644 --- a/Packs/Code42/Integrations/Code42/Code42.yml +++ b/Packs/Code42/Integrations/Code42/Code42.yml @@ -262,6 +262,9 @@ script: - name: note description: Note to attach to the Departing Employee. outputs: + - contextPath: Code42.DepartingEmployee.CaseID + description: Internal Code42 Case ID for the Departing Employee. Deprecated. Use Code42.DepartingEmployee.UserID. + type: string - contextPath: Code42.DepartingEmployee.UserID description: Internal Code42 User ID for the Departing Employee. type: string @@ -280,6 +283,9 @@ script: required: true description: The username to remove from the Departing Employee List. outputs: + - contextPath: Code42.DepartingEmployee.CaseID + description: Internal Code42 Case ID for the Departing Employee. Deprecated. Use Code42.DepartingEmployee.UserID. + type: string - contextPath: Code42.DepartingEmployee.UserID description: Internal Code42 User ID for the Departing Employee. type: string diff --git a/Packs/Code42/Integrations/Code42/Code42_test.py b/Packs/Code42/Integrations/Code42/Code42_test.py index 046d3422844d..2170f3e21647 100644 --- a/Packs/Code42/Integrations/Code42/Code42_test.py +++ b/Packs/Code42/Integrations/Code42/Code42_test.py @@ -1167,52 +1167,63 @@ def test_map_to_file_context(): def test_alert_get_command(code42_alerts_mock): client = create_client(code42_alerts_mock) - _, outputs, res = alert_get_command(client, {"id": "36fb8ca5-0533-4d25-9763-e09d35d60610"}) - assert res["ruleId"] == "4576576e-13cb-4f88-be3a-ee77739de649" - assert outputs == {"Code42.SecurityAlert": [MOCK_CODE42_ALERT_CONTEXT[0]]} + cmd_res = alert_get_command(client, {"id": "4576576e-13cb-4f88-be3a-ee77739de649"}) + assert cmd_res.raw_response["ruleId"] == "4576576e-13cb-4f88-be3a-ee77739de649" + assert cmd_res.outputs == [MOCK_CODE42_ALERT_CONTEXT[0]] + assert cmd_res.outputs_prefix == "Code42.SecurityAlert" + assert cmd_res.outputs_key_field == "ID" def test_alert_resolve_command(code42_alerts_mock): client = create_client(code42_alerts_mock) - _, outputs, res = alert_resolve_command(client, {"id": "36fb8ca5-0533-4d25-9763-e09d35d60610"}) - assert res["id"] == "36fb8ca5-0533-4d25-9763-e09d35d60610" - assert outputs == {"Code42.SecurityAlert": [MOCK_CODE42_ALERT_CONTEXT[0]]} + cmd_res = alert_resolve_command(client, {"id": "4576576e-13cb-4f88-be3a-ee77739de649"}) + assert cmd_res.raw_response["ruleId"] == "4576576e-13cb-4f88-be3a-ee77739de649" + assert cmd_res.outputs == [MOCK_CODE42_ALERT_CONTEXT[0]] + assert cmd_res.outputs_prefix == "Code42.SecurityAlert" + assert cmd_res.outputs_key_field == "ID" def test_departingemployee_add_command(code42_sdk_mock): client = create_client(code42_sdk_mock) date = "2020-01-01" note = "Dummy note" - _, outputs, res = departingemployee_add_command( - client, - {"username": _TEST_USERNAME, "departuredate": date, "note": note}, + cmd_res = departingemployee_add_command( + client, {"username": _TEST_USERNAME, "departuredate": date, "note": note} ) add_func = code42_sdk_mock.detectionlists.departing_employee.add - assert res == _TEST_USER_ID - assert outputs["Code42.DepartingEmployee"]["DepartureDate"] == date - assert outputs["Code42.DepartingEmployee"]["Note"] == note - assert outputs["Code42.DepartingEmployee"]["Username"] == _TEST_USERNAME - assert outputs["Code42.DepartingEmployee"]["UserID"] == _TEST_USER_ID + assert cmd_res.raw_response == _TEST_USER_ID + assert cmd_res.outputs_prefix == "Code42.DepartingEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs["DepartureDate"] == date + assert cmd_res.outputs["Note"] == note + assert cmd_res.outputs["Username"] == _TEST_USERNAME + assert cmd_res.outputs["UserID"] == _TEST_USER_ID + assert cmd_res.outputs["CaseID"] == _TEST_USER_ID add_func.assert_called_once_with(_TEST_USER_ID, departure_date=date) code42_sdk_mock.detectionlists.update_user_notes.assert_called_once_with(_TEST_USER_ID, note) def test_departingemployee_remove_command(code42_sdk_mock): client = create_client(code42_sdk_mock) - _, outputs, res = departingemployee_remove_command(client, {"username": _TEST_USERNAME}) - assert res == _TEST_USER_ID + cmd_res = departingemployee_remove_command(client, {"username": _TEST_USERNAME}) + assert cmd_res.raw_response == _TEST_USER_ID + assert cmd_res.outputs_prefix == "Code42.DepartingEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs["Username"] == _TEST_USERNAME + assert cmd_res.outputs["UserID"] == _TEST_USER_ID + assert cmd_res.outputs["CaseID"] == _TEST_USER_ID code42_sdk_mock.detectionlists.departing_employee.remove.assert_called_once_with(_TEST_USER_ID) - assert outputs["Code42.DepartingEmployee"]["Username"] == _TEST_USERNAME - assert outputs["Code42.DepartingEmployee"]["UserID"] == _TEST_USER_ID def test_departingemployee_get_all_command(code42_departing_employee_mock): client = create_client(code42_departing_employee_mock) - _, outputs, res = departingemployee_get_all_command(client, {}) - outputs_list = outputs["Code42.DepartingEmployee(val.UserID && val.UserID == obj.UserID)"] - expected = json.loads(MOCK_GET_ALL_DEPARTING_EMPLOYEES_RESPONSE)["items"] - assert res == expected - assert_departingemployee_outputs_match_response(outputs_list, expected) + cmd_res = departingemployee_get_all_command(client, {}) + expected_raw_response = json.loads(MOCK_GET_ALL_DEPARTING_EMPLOYEES_RESPONSE)["items"] + assert cmd_res.outputs_prefix == "Code42.DepartingEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.raw_response == json.loads(MOCK_GET_ALL_DEPARTING_EMPLOYEES_RESPONSE)["items"] + # Tests outputs + assert_departingemployee_outputs_match_response(cmd_res.outputs, expected_raw_response) def test_departingemployee_get_all_command_gets_employees_from_multiple_pages( @@ -1228,14 +1239,15 @@ def test_departingemployee_get_all_command_gets_employees_from_multiple_pages( employee_page_generator ) client = create_client(code42_departing_employee_mock) - _, outputs, res = departingemployee_get_all_command(client, {}) - outputs_list = outputs["Code42.DepartingEmployee(val.UserID && val.UserID == obj.UserID)"] + cmd_res = departingemployee_get_all_command(client, {}) + assert cmd_res.outputs_prefix == "Code42.DepartingEmployee" + assert cmd_res.outputs_key_field == "UserID" # Expect to have employees from 3 pages in the result expected_page = json.loads(MOCK_GET_ALL_DEPARTING_EMPLOYEES_RESPONSE)["items"] expected = expected_page + expected_page + expected_page - assert res == expected - assert_departingemployee_outputs_match_response(outputs_list, res) + assert cmd_res.raw_response == expected + assert_departingemployee_outputs_match_response(cmd_res.outputs, cmd_res.raw_response) def test_departingemployee_get_all_command_gets_number_of_employees_equal_to_results_param( @@ -1253,8 +1265,9 @@ def test_departingemployee_get_all_command_gets_number_of_employees_equal_to_res ) client = create_client(code42_departing_employee_mock) - _, _, res = departingemployee_get_all_command(client, {"results": 1}) - assert len(res) == 1 + cmd_res = departingemployee_get_all_command(client, {"results": 1}) + assert len(cmd_res.raw_response) == 1 + assert len(cmd_res.outputs) == 1 def test_departingemployee_get_all_command_when_no_employees( @@ -1267,7 +1280,7 @@ def test_departingemployee_get_all_command_when_no_employees( no_employees_response ) client = create_client(code42_departing_employee_mock) - _, outputs, res = departingemployee_get_all_command( + cmd_res = departingemployee_get_all_command( client, { "risktags": [ @@ -1277,23 +1290,23 @@ def test_departingemployee_get_all_command_when_no_employees( ] }, ) - outputs_list = outputs["Code42.DepartingEmployee(val.UserID && val.UserID == obj.UserID)"] - - # Only first employee has the given risk tags - expected = [] - assert res == expected + assert cmd_res.outputs_prefix == "Code42.DepartingEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.raw_response == [] + assert cmd_res.outputs == [] assert code42_departing_employee_mock.detectionlists.departing_employee.get_all.call_count == 1 - assert_departingemployee_outputs_match_response(outputs_list, res) def test_highriskemployee_add_command(code42_high_risk_employee_mock): client = create_client(code42_high_risk_employee_mock) - _, outputs, res = highriskemployee_add_command( + cmd_res = highriskemployee_add_command( client, {"username": _TEST_USERNAME, "note": "Dummy note"} ) - assert res == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["UserID"] == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["Username"] == _TEST_USERNAME + assert cmd_res.raw_response == _TEST_USER_ID + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs["UserID"] == _TEST_USER_ID + assert cmd_res.outputs["Username"] == _TEST_USERNAME code42_high_risk_employee_mock.detectionlists.high_risk_employee.add.assert_called_once_with( _TEST_USER_ID ) @@ -1304,21 +1317,24 @@ def test_highriskemployee_add_command(code42_high_risk_employee_mock): def test_highriskemployee_remove_command(code42_sdk_mock): client = create_client(code42_sdk_mock) - _, outputs, res = highriskemployee_remove_command(client, {"username": _TEST_USERNAME}) - assert res == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["UserID"] == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["Username"] == _TEST_USERNAME + cmd_res = highriskemployee_remove_command(client, {"username": _TEST_USERNAME}) + assert cmd_res.raw_response == _TEST_USER_ID + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs["UserID"] == _TEST_USER_ID + assert cmd_res.outputs["Username"] == _TEST_USERNAME code42_sdk_mock.detectionlists.high_risk_employee.remove.assert_called_once_with(_TEST_USER_ID) def test_highriskemployee_get_all_command(code42_high_risk_employee_mock): client = create_client(code42_high_risk_employee_mock) - _, outputs, res = highriskemployee_get_all_command(client, {}) - outputs_list = outputs["Code42.HighRiskEmployee(val.UserID && val.UserID == obj.UserID)"] - expected = json.loads(MOCK_GET_ALL_HIGH_RISK_EMPLOYEES_RESPONSE)["items"] - assert res == expected + cmd_res = highriskemployee_get_all_command(client, {}) + expected_response = json.loads(MOCK_GET_ALL_HIGH_RISK_EMPLOYEES_RESPONSE)["items"] + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.raw_response == expected_response assert code42_high_risk_employee_mock.detectionlists.high_risk_employee.get_all.call_count == 1 - assert_detection_list_outputs_match_response_items(outputs_list, expected) + assert_detection_list_outputs_match_response_items(cmd_res.outputs, expected_response) def test_highriskemployee_get_all_command_gets_employees_from_multiple_pages( @@ -1335,30 +1351,28 @@ def test_highriskemployee_get_all_command_gets_employees_from_multiple_pages( ) client = create_client(code42_high_risk_employee_mock) - _, outputs, res = highriskemployee_get_all_command(client, {"username": _TEST_USERNAME}) - outputs_list = outputs["Code42.HighRiskEmployee(val.UserID && val.UserID == obj.UserID)"] - - # Expect to have employees from 3 pages in the result - expected_page = json.loads(MOCK_GET_ALL_HIGH_RISK_EMPLOYEES_RESPONSE)["items"] - expected = expected_page + expected_page + expected_page - assert res == expected - assert_detection_list_outputs_match_response_items(outputs_list, expected) + cmd_res = highriskemployee_get_all_command(client, {"username": _TEST_USERNAME}) + expected_response = json.loads(MOCK_GET_ALL_HIGH_RISK_EMPLOYEES_RESPONSE)["items"] * 3 + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.raw_response == expected_response + assert_detection_list_outputs_match_response_items(cmd_res.outputs, expected_response) def test_highriskemployee_get_all_command_when_given_risk_tags_only_gets_employees_with_tags( code42_high_risk_employee_mock ): client = create_client(code42_high_risk_employee_mock) - _, outputs, res = highriskemployee_get_all_command( + cmd_res = highriskemployee_get_all_command( client, {"risktags": "PERFORMANCE_CONCERNS SUSPICIOUS_SYSTEM_ACTIVITY POOR_SECURITY_PRACTICES"}, ) - outputs_list = outputs["Code42.HighRiskEmployee(val.UserID && val.UserID == obj.UserID)"] - # Only first employee has the given risk tags - expected = [json.loads(MOCK_GET_ALL_HIGH_RISK_EMPLOYEES_RESPONSE)["items"][0]] - assert res == expected + expected_response = [json.loads(MOCK_GET_ALL_HIGH_RISK_EMPLOYEES_RESPONSE)["items"][0]] + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.raw_response == expected_response assert code42_high_risk_employee_mock.detectionlists.high_risk_employee.get_all.call_count == 1 - assert_detection_list_outputs_match_response_items(outputs_list, expected) + assert_detection_list_outputs_match_response_items(cmd_res.outputs, expected_response) def test_highriskemployee_get_all_command_gets_number_of_employees_equal_to_results_param( @@ -1374,8 +1388,9 @@ def test_highriskemployee_get_all_command_gets_number_of_employees_equal_to_resu employee_page_generator ) client = create_client(code42_high_risk_employee_mock) - _, _, res = highriskemployee_get_all_command(client, {"results": 1}) - assert len(res) == 1 + cmd_res = highriskemployee_get_all_command(client, {"results": 1}) + assert len(cmd_res.raw_response) == 1 + assert len(cmd_res.outputs) == 1 def test_highriskemployee_get_all_command_when_no_employees(code42_high_risk_employee_mock, mocker): @@ -1386,30 +1401,29 @@ def test_highriskemployee_get_all_command_when_no_employees(code42_high_risk_emp no_employees_response ) client = create_client(code42_high_risk_employee_mock) - _, outputs, res = highriskemployee_get_all_command( + cmd_res = highriskemployee_get_all_command( client, - { - "risktags": "PERFORMANCE_CONCERNS SUSPICIOUS_SYSTEM_ACTIVITY POOR_SECURITY_PRACTICES" - }, + {"risktags": "PERFORMANCE_CONCERNS SUSPICIOUS_SYSTEM_ACTIVITY POOR_SECURITY_PRACTICES"}, ) - outputs_list = outputs["Code42.HighRiskEmployee(val.UserID && val.UserID == obj.UserID)"] - # Only first employee has the given risk tags - expected = [] - assert res == expected + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs == [] + assert cmd_res.raw_response == [] assert code42_high_risk_employee_mock.detectionlists.high_risk_employee.get_all.call_count == 1 - assert_detection_list_outputs_match_response_items(outputs_list, expected) def test_highriskemployee_add_risk_tags_command(code42_sdk_mock): tags = "FLIGHT_RISK" client = create_client(code42_sdk_mock) - _, outputs, res = highriskemployee_add_risk_tags_command( + cmd_res = highriskemployee_add_risk_tags_command( client, {"username": _TEST_USERNAME, "risktags": "FLIGHT_RISK"} ) - assert res == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["UserID"] == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["Username"] == _TEST_USERNAME - assert outputs["Code42.HighRiskEmployee"]["RiskTags"] == tags + assert cmd_res.raw_response == _TEST_USER_ID + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs["UserID"] == _TEST_USER_ID + assert cmd_res.outputs["Username"] == _TEST_USERNAME + assert cmd_res.outputs["RiskTags"] == tags code42_sdk_mock.detectionlists.add_user_risk_tags.assert_called_once_with( _TEST_USER_ID, ["FLIGHT_RISK"] ) @@ -1417,13 +1431,15 @@ def test_highriskemployee_add_risk_tags_command(code42_sdk_mock): def test_highriskemployee_remove_risk_tags_command(code42_sdk_mock): client = create_client(code42_sdk_mock) - _, outputs, res = highriskemployee_remove_risk_tags_command( + cmd_res = highriskemployee_remove_risk_tags_command( client, {"username": _TEST_USERNAME, "risktags": "FLIGHT_RISK CONTRACT_EMPLOYEE"} ) - assert res == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["UserID"] == _TEST_USER_ID - assert outputs["Code42.HighRiskEmployee"]["Username"] == _TEST_USERNAME - assert outputs["Code42.HighRiskEmployee"]["RiskTags"] == "FLIGHT_RISK CONTRACT_EMPLOYEE" + assert cmd_res.raw_response == _TEST_USER_ID + assert cmd_res.outputs_prefix == "Code42.HighRiskEmployee" + assert cmd_res.outputs_key_field == "UserID" + assert cmd_res.outputs["UserID"] == _TEST_USER_ID + assert cmd_res.outputs["Username"] == _TEST_USERNAME + assert cmd_res.outputs["RiskTags"] == "FLIGHT_RISK CONTRACT_EMPLOYEE" code42_sdk_mock.detectionlists.remove_user_risk_tags.assert_called_once_with( _TEST_USER_ID, ["FLIGHT_RISK", "CONTRACT_EMPLOYEE"] ) @@ -1431,15 +1447,21 @@ def test_highriskemployee_remove_risk_tags_command(code42_sdk_mock): def test_security_data_search_command(code42_file_events_mock): client = create_client(code42_file_events_mock) - _, outputs, res = securitydata_search_command(client, MOCK_SECURITY_DATA_SEARCH_QUERY) - outputs_list = outputs["Code42.SecurityData(val.EventID && val.EventID == obj.EventID)"] + cmd_res = securitydata_search_command(client, MOCK_SECURITY_DATA_SEARCH_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] filter_groups = json.loads(str(actual_query))["groups"] expected_query_items = [ ("md5Checksum", "d41d8cd98f00b204e9800998ecf8427e"), ("osHostName", "DESKTOP-0001"), ("deviceUserName", "user3@example.com"), - ("exposure", "ApplicationRead") + ("exposure", "ApplicationRead"), ] expected_file_events = json.loads(MOCK_SECURITY_EVENT_RESPONSE)["fileEvents"] @@ -1450,13 +1472,13 @@ def test_security_data_search_command(code42_file_events_mock): assert _filter["term"] == expected_query_items[i][0] assert _filter["value"] == expected_query_items[i][1] - assert len(res) == len(outputs_list) == 3 - assert res == expected_file_events + assert len(code42_res.raw_response) == len(code42_res.outputs) == 3 + assert code42_res.raw_response == expected_file_events # Assert that the Outputs are mapped from the file events. for i in range(0, len(expected_file_events)): mapped_event = map_to_code42_event_context(expected_file_events[i]) - output_item = outputs_list[i] + output_item = code42_res.outputs[i] assert output_item == mapped_event diff --git a/Packs/Code42/Integrations/Code42/integration-Code42.yml b/Packs/Code42/Integrations/Code42/integration-Code42.yml index ac10a56643f6..702ad3f5db7b 100644 --- a/Packs/Code42/Integrations/Code42/integration-Code42.yml +++ b/Packs/Code42/Integrations/Code42/integration-Code42.yml @@ -2,7 +2,7 @@ commonfields: id: Code42 version: -1 name: Code42 -display: Code42 +display: Code42 (Partner Contribution) category: Endpoint description: Use the Code42 integration to identify potential data exfiltration from insider threats while speeding investigation and response by providing fast access to file events and metadata across physical and cloud environments. configuration: @@ -236,7 +236,11 @@ script: # Allow sdk parameter for unit testing. # Otherwise, lazily load the SDK so that the TEST Command can effectively check auth. self._sdk = sdk - self._sdk_factory = lambda: py42.sdk.from_local_account(base_url, auth[0], auth[1]) if not self._sdk else None + self._sdk_factory = ( + lambda: py42.sdk.from_local_account(base_url, auth[0], auth[1]) + if not self._sdk + else None + ) py42.settings.set_user_agent_suffix("Cortex XSOAR") def _get_sdk(self): @@ -582,53 +586,62 @@ script: def alert_get_command(client, args): code42_securityalert_context = [] - try: - alert = client.get_alert_details(args["id"]) - if not alert: - return "No results found", {}, {} - - code42_context = map_to_code42_alert_context(alert) - code42_securityalert_context.append(code42_context) - readable_outputs = tableToMarkdown( - "Code42 Security Alert Results", - code42_securityalert_context, - headers=SECURITY_ALERT_HEADERS, + alert = client.get_alert_details(args["id"]) + if not alert: + return CommandResults( + readable_output="No results found", + outputs={"Results": []}, + outputs_key_field="ID", + outputs_prefix="Code42.SecurityAlert", + raw_response={}, ) - return readable_outputs, {"Code42.SecurityAlert": code42_securityalert_context}, alert - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + + code42_context = map_to_code42_alert_context(alert) + code42_securityalert_context.append(code42_context) + readable_outputs = tableToMarkdown( + "Code42 Security Alert Results", + code42_securityalert_context, + headers=SECURITY_ALERT_HEADERS, + ) + return CommandResults( + outputs_prefix="Code42.SecurityAlert", + outputs_key_field="ID", + outputs=code42_securityalert_context, + readable_output=readable_outputs, + raw_response=alert, + ) @logger def alert_resolve_command(client, args): - code42_security_alert_context = [] - - try: - alert_id = client.resolve_alert(args["id"]) - - if not alert_id: - return "No results found", {}, {} - - # Retrieve new alert details - alert_details = client.get_alert_details(alert_id) - if not alert_details: - return "Error retrieving updated alert", {}, {} - - code42_context = map_to_code42_alert_context(alert_details) - code42_security_alert_context.append(code42_context) - readable_outputs = tableToMarkdown( - "Code42 Security Alert Resolved", - code42_security_alert_context, - headers=SECURITY_ALERT_HEADERS, - ) - return ( - readable_outputs, - {"Code42.SecurityAlert": code42_security_alert_context}, - alert_details, + code42_securityalert_context = [] + alert_id = client.resolve_alert(args["id"]) + if not alert_id: + return CommandResults( + readable_output="No results found", + outputs={"Results": []}, + outputs_key_field="ID", + outputs_prefix="Code42.SecurityAlert", + raw_response={}, ) - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + + # Retrieve new alert details + alert_details = client.get_alert_details(alert_id) + code42_context = map_to_code42_alert_context(alert_details) + code42_securityalert_context.append(code42_context) + readable_outputs = tableToMarkdown( + "Code42 Security Alert Resolved", + code42_securityalert_context, + headers=SECURITY_ALERT_HEADERS, + ) + return CommandResults( + outputs_prefix="Code42.SecurityAlert", + outputs_key_field="ID", + outputs=code42_securityalert_context, + readable_output=readable_outputs, + raw_response=alert_details, + ) @logger @@ -637,58 +650,73 @@ script: departing_date = args.get("departuredate") username = args["username"] note = args.get("note") - try: - user_id = client.add_user_to_departing_employee(username, departing_date, note) - de_context = { - "UserID": user_id, - "Username": username, - "DepartureDate": departing_date, - "Note": note, - } - readable_outputs = tableToMarkdown("Code42 Departing Employee List User Added", de_context) - return readable_outputs, {"Code42.DepartingEmployee": de_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.add_user_to_departing_employee(username, departing_date, note) + # CaseID included but is deprecated. + de_context = { + "CaseID": user_id, + "UserID": user_id, + "Username": username, + "DepartureDate": departing_date, + "Note": note, + } + readable_outputs = tableToMarkdown("Code42 Departing Employee List User Added", de_context) + return CommandResults( + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs=de_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def departingemployee_remove_command(client, args): username = args["username"] - try: - user_id = client.remove_user_from_departing_employee(username) - de_context = {"UserID": user_id, "Username": username} - readable_outputs = tableToMarkdown( - "Code42 Departing Employee List User Removed", de_context - ) - return readable_outputs, {"Code42.DepartingEmployee": de_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.remove_user_from_departing_employee(username) + # CaseID included but is deprecated. + de_context = {"CaseID": user_id, "UserID": user_id, "Username": username} + readable_outputs = tableToMarkdown("Code42 Departing Employee List User Removed", de_context) + return CommandResults( + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs=de_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def departingemployee_get_all_command(client, args): results = args.get("results") or 50 - try: - employees = client.get_all_departing_employees(results) - employees_context = [ - { - "UserID": e["userId"], - "Username": e["userName"], - "DepartureDate": e.get("departureDate"), - "Note": e["notes"], - } - for e in employees - ] - readable_outputs = tableToMarkdown("All Departing Employees", employees_context) - return ( - readable_outputs, - {"Code42.DepartingEmployee(val.UserID && val.UserID == obj.UserID)": employees_context}, - employees, + employees = client.get_all_departing_employees(results) + if not employees: + return CommandResults( + readable_output="No results found", + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs={"Results": []}, + raw_response={} ) - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + + employees_context = [ + { + "UserID": e.get("userId"), + "Username": e.get("userName"), + "DepartureDate": e.get("departureDate"), + "Note": e.get("notes"), + } + for e in employees + ] + readable_outputs = tableToMarkdown("All Departing Employees", employees_context) + return CommandResults( + outputs_prefix="Code42.DepartingEmployee", + outputs_key_field="UserID", + outputs=employees_context, + readable_output=readable_outputs, + raw_response=employees, + ) @logger @@ -696,28 +724,32 @@ script: def highriskemployee_add_command(client, args): username = args["username"] note = args.get("note") - try: - user_id = client.add_user_to_high_risk_employee(username, note) - hr_context = {"UserID": user_id, "Username": username} - readable_outputs = tableToMarkdown("Code42 High Risk Employee List User Added", hr_context) - return readable_outputs, {"Code42.HighRiskEmployee": hr_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.add_user_to_high_risk_employee(username, note) + hr_context = {"UserID": user_id, "Username": username} + readable_outputs = tableToMarkdown("Code42 High Risk Employee List User Added", hr_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=hr_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger def highriskemployee_remove_command(client, args): username = args["username"] - try: - user_id = client.remove_user_from_high_risk_employee(username) - hr_context = {"UserID": user_id, "Username": username} - readable_outputs = tableToMarkdown( - "Code42 High Risk Employee List User Removed", hr_context - ) - return readable_outputs, {"Code42.HighRiskEmployee": hr_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.remove_user_from_high_risk_employee(username) + hr_context = {"UserID": user_id, "Username": username} + readable_outputs = tableToMarkdown("Code42 High Risk Employee List User Removed", hr_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=hr_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger @@ -725,20 +757,27 @@ script: def highriskemployee_get_all_command(client, args): tags = args.get("risktags") results = args.get("results") or 50 - try: - employees = client.get_all_high_risk_employees(tags, results) - employees_context = [ - {"UserID": e.get("userId"), "Username": e.get("userName"), "Note": e.get("notes")} - for e in employees - ] - readable_outputs = tableToMarkdown("Retrieved All High Risk Employees", employees_context) - return ( - readable_outputs, - {"Code42.HighRiskEmployee(val.UserID && val.UserID == obj.UserID)": employees_context}, - employees, + employees = client.get_all_high_risk_employees(tags, results) + if not employees: + return CommandResults( + readable_output="No results found", + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs={"Results": []}, + raw_response={} ) - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + employees_context = [ + {"UserID": e.get("userId"), "Username": e.get("userName"), "Note": e.get("notes")} + for e in employees + ] + readable_outputs = tableToMarkdown("Retrieved All High Risk Employees", employees_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=employees_context, + readable_output=readable_outputs, + raw_response=employees, + ) @logger @@ -746,13 +785,16 @@ script: def highriskemployee_add_risk_tags_command(client, args): username = args.get("username") tags = args.get("risktags") - try: - user_id = client.add_user_risk_tags(username, tags) - rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} - readable_outputs = tableToMarkdown("Code42 Risk Tags Added", rt_context) - return readable_outputs, {"Code42.HighRiskEmployee": rt_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.add_user_risk_tags(username, tags) + rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} + readable_outputs = tableToMarkdown("Code42 Risk Tags Added", rt_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=rt_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger @@ -760,13 +802,16 @@ script: def highriskemployee_remove_risk_tags_command(client, args): username = args.get("username") tags = args.get("risktags") - try: - user_id = client.remove_user_risk_tags(username, tags) - rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} - readable_outputs = tableToMarkdown("Code42 Risk Tags Removed", rt_context) - return readable_outputs, {"Code42.HighRiskEmployee": rt_context}, user_id - except Exception as e: - return_error(create_command_error_message(demisto.command(), e)) + user_id = client.remove_user_risk_tags(username, tags) + rt_context = {"UserID": user_id, "Username": username, "RiskTags": tags} + readable_outputs = tableToMarkdown("Code42 Risk Tags Removed", rt_context) + return CommandResults( + outputs_prefix="Code42.HighRiskEmployee", + outputs_key_field="UserID", + outputs=rt_context, + readable_output=readable_outputs, + raw_response=user_id, + ) @logger @@ -775,6 +820,7 @@ script: code42_security_data_context = [] _json = args.get("json") file_context = [] + # If JSON payload is passed as an argument, ignore all other args and search by JSON payload if _json is not None: file_events = client.search_file_events(_json) @@ -793,11 +839,30 @@ script: code42_security_data_context, headers=SECURITY_EVENT_HEADERS, ) - security_data_context_key = "Code42.SecurityData(val.EventID && val.EventID == obj.EventID)" - context = {security_data_context_key: code42_security_data_context, "File": file_context} - return readable_outputs, context, file_events + code42_results = CommandResults( + outputs_prefix="Code42.SecurityData", + outputs_key_field="EventID", + outputs=code42_security_data_context, + readable_output=readable_outputs, + raw_response=file_events, + ) + file_results = CommandResults( + outputs_prefix="File", outputs_key_field=None, outputs=file_context + ) + return code42_results, file_results + else: - return "No results found", {}, {} + return CommandResults( + readable_output="No results found", + outputs={"Results": []}, + outputs_key_field="EventID", + outputs_prefix="Code42.SecurityData", + raw_response={}, + ) + + + """Fetching""" + def _create_incident_from_alert_details(details): @@ -858,7 +923,7 @@ script: if remaining_incidents: return ( self._last_run, - remaining_incidents[:self._fetch_limit], + remaining_incidents[: self._fetch_limit], remaining_incidents[self._fetch_limit:], ) @@ -923,6 +988,10 @@ script: return fetcher.fetch() + """Main and test""" + + + def test_module(client): try: # Will fail if unauthorized @@ -936,65 +1005,84 @@ script: ) - def main(): - """ - PARSE AND VALIDATE INTEGRATION PARAMS - """ + def get_command_map(): + return { + "code42-alert-get": alert_get_command, + "code42-alert-resolve": alert_resolve_command, + "code42-securitydata-search": securitydata_search_command, + "code42-departingemployee-add": departingemployee_add_command, + "code42-departingemployee-remove": departingemployee_remove_command, + "code42-departingemployee-get-all": departingemployee_get_all_command, + "code42-highriskemployee-add": highriskemployee_add_command, + "code42-highriskemployee-remove": highriskemployee_remove_command, + "code42-highriskemployee-get-all": highriskemployee_get_all_command, + "code42-highriskemployee-add-risk-tags": highriskemployee_add_risk_tags_command, + "code42-highriskemployee-remove-risk-tags": highriskemployee_remove_risk_tags_command, + } + + + def handle_test_command(client): + # This is the call made when pressing the integration Test button. + result = test_module(client) + demisto.results(result) + + + def handle_fetch_command(client): + integration_context = demisto.getIntegrationContext() + # Set and define the fetch incidents command to run after activated via integration settings. + next_run, incidents, remaining_incidents = fetch_incidents( + client=client, + last_run=demisto.getLastRun(), + first_fetch_time=demisto.params().get("fetch_time"), + event_severity_filter=demisto.params().get("alert_severity"), + fetch_limit=int(demisto.params().get("fetch_limit")), + include_files=demisto.params().get("include_files"), + integration_context=integration_context, + ) + demisto.setLastRun(next_run) + demisto.incidents(incidents) + # Store remaining incidents in integration context + integration_context["remaining_incidents"] = remaining_incidents + demisto.setIntegrationContext(integration_context) + + + def try_run_command(command): + try: + results = command() + if not isinstance(results, tuple) and not isinstance(results, list): + results = [results] + for result in results: + return_results(result) + except Exception as e: + return_error(create_command_error_message(demisto.command(), e)) + + + def run_code42_integration(): username = demisto.params().get("credentials").get("identifier") password = demisto.params().get("credentials").get("password") base_url = demisto.params().get("console_url") - # Remove trailing slash to prevent wrong URL path to service verify_certificate = not demisto.params().get("insecure", False) proxy = demisto.params().get("proxy", False) - LOG(f"Command being called is {demisto.command()}") - try: - client = Code42Client( - base_url=base_url, - sdk=None, - auth=(username, password), - verify=verify_certificate, - proxy=proxy, - ) - commands = { - "code42-alert-get": alert_get_command, - "code42-alert-resolve": alert_resolve_command, - "code42-securitydata-search": securitydata_search_command, - "code42-departingemployee-add": departingemployee_add_command, - "code42-departingemployee-remove": departingemployee_remove_command, - "code42-departingemployee-get-all": departingemployee_get_all_command, - "code42-highriskemployee-add": highriskemployee_add_command, - "code42-highriskemployee-remove": highriskemployee_remove_command, - "code42-highriskemployee-get-all": highriskemployee_get_all_command, - "code42-highriskemployee-add-risk-tags": highriskemployee_add_risk_tags_command, - "code42-highriskemployee-remove-risk-tags": highriskemployee_remove_risk_tags_command, - } - command = demisto.command() - if command == "test-module": - # This is the call made when pressing the integration Test button. - result = test_module(client) - demisto.results(result) - elif command == "fetch-incidents": - integration_context = demisto.getIntegrationContext() - # Set and define the fetch incidents command to run after activated via integration settings. - next_run, incidents, remaining_incidents = fetch_incidents( - client=client, - last_run=demisto.getLastRun(), - first_fetch_time=demisto.params().get("fetch_time"), - event_severity_filter=demisto.params().get("alert_severity"), - fetch_limit=int(demisto.params().get("fetch_limit")), - include_files=demisto.params().get("include_files"), - integration_context=integration_context, - ) - demisto.setLastRun(next_run) - demisto.incidents(incidents) - # Store remaining incidents in integration context - integration_context["remaining_incidents"] = remaining_incidents - demisto.setIntegrationContext(integration_context) - elif command in commands: - return_outputs(*commands[command](client, demisto.args())) - # Log exceptions - except Exception as e: - return_error(f"Failed to execute {demisto.command()} command. Error: {str(e)}") + LOG("Command being called is {0}.".format(demisto.command())) + client = Code42Client( + base_url=base_url, + sdk=None, + auth=(username, password), + verify=verify_certificate, + proxy=proxy, + ) + commands = get_command_map() + command = demisto.command() + if command == "test-module": + handle_test_command(client) + elif command == "fetch-incidents": + handle_fetch_command(client) + elif command in commands: + try_run_command(lambda: commands[command](client, demisto.args())) + + + def main(): + run_code42_integration() if __name__ in ("__main__", "__builtin__", "builtins"): @@ -1204,6 +1292,9 @@ script: - name: note description: Note to attach to the Departing Employee. outputs: + - contextPath: Code42.DepartingEmployee.CaseID + description: Internal Code42 Case ID for the Departing Employee. Deprecated. Use Code42.DepartingEmployee.UserID. + type: string - contextPath: Code42.DepartingEmployee.UserID description: Internal Code42 User ID for the Departing Employee. type: string @@ -1222,6 +1313,9 @@ script: required: true description: The username to remove from the Departing Employee List. outputs: + - contextPath: Code42.DepartingEmployee.CaseID + description: Internal Code42 Case ID for the Departing Employee. Deprecated. Use Code42.DepartingEmployee.UserID. + type: string - contextPath: Code42.DepartingEmployee.UserID description: Internal Code42 User ID for the Departing Employee. type: string @@ -1336,6 +1430,16 @@ script: subtype: python3 image: data:image/png;base64,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 detaileddescription: > + ### Partner Contributed Integration + + #### Integration Author: Code42 + + Support and maintenance for this integration are provided by the author. Please use the following contact details: + + - **URL**: [https://support.code42.com/Administrator/Cloud/Monitoring_and_managing](https://support.code42.com/Administrator/Cloud/Monitoring_and_managing) + + *** + ## Code42