From 8ba55546fd9af9dec4d217788682349544fd5418 Mon Sep 17 00:00:00 2001 From: content-bot <55035720+content-bot@users.noreply.github.com> Date: Fri, 3 Nov 2023 00:35:06 +0200 Subject: [PATCH] add a way to upload binary file (#30638) * add a way to upload binary file (#30532) * add a way to upload binary file * patch ci/cd error * patch ci/cd error * Apply suggestions from code review Co-authored-by: Moshe Eichler <78307768+MosheEichler@users.noreply.github.com> --------- Co-authored-by: Pierre SOLER <9917674+Winultimatum@users.noreply.github.com> Co-authored-by: Moshe Eichler <78307768+MosheEichler@users.noreply.github.com> * Update Docker Image To demisto/crypto (#30624) * Updated Metadata Of Pack AzureSecurityCenter * Added release notes to pack AzureSecurityCenter * Packs/AzureSecurityCenter/Integrations/AzureSecurityCenter_v2/AzureSecurityCenter_v2.yml Docker image update * fix jira convert issues print (#30608) fix jira convert issues print #30608 * fix merge dev secrets and sync buckets (#30609) fix merge dev secrets and sync buckets #30609 * Fixed Gitlab event collector fetch (#30379) * Fix GitLab collector * release notes * pre-commit fix * pre-commit fix * increase coverage * CR fixes * fix * Update 2_2_19.md * CR fix * default limit to 500 * update docker * Remove checking 6.2 server version (#30351) * removed usage of is_ge_version function with server version less than 6.2.0. * added rn * updated docker image * added rn * removed unit tests * fixed unittests * edit unittest * fixed unittest * edited unittests * edited unittests * mypy fixes * fixes * fixes * updated docker image * added rn * mypy fixes * edited unittests * edit * edit unit tests * edited the tpb to work with ng server * edited tpb * removed paging unit tests, removed arguments * reverted removing the args * Fix for dhcp (#30610) * Updated the modeling rule of microsoft dhcp * Updated the modeling rule of microsoft dhcp * Update 1_0_3.md --------- Co-authored-by: eepstain <116078117+eepstain@users.noreply.github.com> * Xdr lite playbbok (#30497) * new incidents fields * New playbook,incident type,script,layout for xdr lite changing integration default incident type and classifier * resolve conflicts - RN * update RN, update unsearchable to true in incident fields and remove unnecessary incident fields from the layout * updated layout - InvestigationVerdict dynamic section * Apply suggestions from code review Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com> * fixes after review * Update Packs/CortexXDR/Scripts/InvestigationVerdict/README.md Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com> * fix for test playbooks Test_XDR_Playbook_execute_script_commands.yml * fix after review. change script name * RN after fixes * RN after fixes * change name of the script yml file in pack ignore * lint fix * time range fix --------- Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com> * comment out us its looks unused and adding unchanged_files to the changed_files (#30632) * OpsGenie v3 - Fix polling commands (#30579) * fix get_request_command method * update RN * validate results.content is not empty * add "." to missing descriptions * added test_get_request_command_valid_raw_response * fix mypy * fix flake8 * [Marketplace Contribution] Atlassian Jira - Content Pack Update (#30512) * [Marketplace Contribution] Atlassian Jira - Content Pack Update (#29838) * "contribution update to pack "Atlassian Jira"" * Update YML * Update Readme * Add missing docstrings * Apply suggestions from code review Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> Co-authored-by: omerKarkKatz <95565843+omerKarkKatz@users.noreply.github.com> * Adjust response type to empty body response * Added periods on various strings, per pre-commit test failure * Unit tests for JIRA assignee --------- Co-authored-by: 68zuhKQfKPk <98105006+68zuhKQfKPk@users.noreply.github.com> Co-authored-by: kPKfQKhuz86 <147484800+kPKfQKhuz86@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> Co-authored-by: omerKarkKatz <95565843+omerKarkKatz@users.noreply.github.com> Co-authored-by: Jakob Weinzettl * updated docker and fixed RN * added self to the class function * restored some yml marketplace download changeS * fixed the unit test * typing --------- Co-authored-by: xsoar-bot <67315154+xsoar-bot@users.noreply.github.com> Co-authored-by: 68zuhKQfKPk <98105006+68zuhKQfKPk@users.noreply.github.com> Co-authored-by: kPKfQKhuz86 <147484800+kPKfQKhuz86@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> Co-authored-by: omerKarkKatz <95565843+omerKarkKatz@users.noreply.github.com> Co-authored-by: Jakob Weinzettl Co-authored-by: okarkkatz --------- Co-authored-by: Pierre Co-authored-by: Pierre SOLER <9917674+Winultimatum@users.noreply.github.com> Co-authored-by: Moshe Eichler <78307768+MosheEichler@users.noreply.github.com> Co-authored-by: Koby Meir Co-authored-by: Yaakov Praisler <59408745+yaakovpraisler@users.noreply.github.com> Co-authored-by: merit-maita <49760643+merit-maita@users.noreply.github.com> Co-authored-by: yasta5 <112320333+yasta5@users.noreply.github.com> Co-authored-by: eepstain <116078117+eepstain@users.noreply.github.com> Co-authored-by: OmriItzhak <115150792+OmriItzhak@users.noreply.github.com> Co-authored-by: ShirleyDenkberg <62508050+ShirleyDenkberg@users.noreply.github.com> Co-authored-by: Moshe Galitzky <112559840+moishce@users.noreply.github.com> Co-authored-by: Adi Daud <46249224+adi88d@users.noreply.github.com> Co-authored-by: xsoar-bot <67315154+xsoar-bot@users.noreply.github.com> Co-authored-by: 68zuhKQfKPk <98105006+68zuhKQfKPk@users.noreply.github.com> Co-authored-by: kPKfQKhuz86 <147484800+kPKfQKhuz86@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> Co-authored-by: omerKarkKatz <95565843+omerKarkKatz@users.noreply.github.com> Co-authored-by: Jakob Weinzettl Co-authored-by: okarkkatz Co-authored-by: MosheEichler --- .../XSOARFileManagement.py | 93 ++++++++++++++----- .../XSOARFileManagement.yml | 42 +++++---- .../XSOARFileManagement/ReleaseNotes/1_0_2.md | 4 + Packs/XSOARFileManagement/pack_metadata.json | 2 +- 4 files changed, 97 insertions(+), 44 deletions(-) create mode 100644 Packs/XSOARFileManagement/ReleaseNotes/1_0_2.md diff --git a/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.py b/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.py index c5be5c5d826e..010fd0c2eeb4 100644 --- a/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.py +++ b/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.py @@ -4,17 +4,20 @@ import time from typing import Tuple import urllib3 +import base64 # Disable insecure warnings urllib3.disable_warnings() -''' CONSTANTS ''' ''' CLIENT CLASS ''' class Client(BaseClient): - def upload_file(self, incident_id: str, file_content, file_name: str, as_incident_attachment: bool = True): + def upload_file(self, incident_id: str, + file_content, + file_name: str, + as_incident_attachment: bool = True): """Upload file Arguments: client: (Client) The client class. @@ -37,6 +40,14 @@ def upload_file(self, incident_id: str, file_content, file_name: str, as_inciden return response def delete_context(self, incident_id: str, key_to_delete: str): + """Send the command "DeleteContext" to a specific incident + Arguments: + client: (Client) The client class. + incident_id {str} -- incident id to upload the file to + key_to_delete {str} -- context data incident key to delete + Returns: + json -- return of the API + """ body_content = { "id": "", "version": 0, @@ -153,18 +164,30 @@ def get_current_user(self): ) -def test_module(client: Client, params: Dict[str, Any]) -> str: +def test_module(client: Client) -> str: + """Test module command + Arguments: + client: (Client) The client class. + Returns: + str -- ok or the error + """ try: client.get_current_user() return 'ok' - except DemistoException as e: - if 'Forbidden' in str(e): + except DemistoException as error: + if 'Forbidden' in str(error): return 'Authorization Error: make sure API Key is correctly set' else: - raise e + raise error def get_incident_id(entry_id: str) -> str: + """Parse the entryID to get the incident id + Arguments: + entryID {str} -- entry ID of the file + Returns: + str -- incident id + """ res = re.findall("(\d+)@(\d+)", entry_id) if len(res) <= 0: return_error("EntryID unknow or malformatted !") @@ -224,7 +247,7 @@ def check_file_command(client: Client, args: dict) -> CommandResults: Returns: CommandResults -- Readable output """ - entry_id = demisto.args()['entryID'] + entry_id = args.get('entryID') output_content = demisto.context().get('IsFileExists', {}) if len(output_content) > 0: client.delete_context(demisto.incident()["id"], "IsFileExists") @@ -259,12 +282,15 @@ def delete_attachment_command(client: Client, args: dict) -> CommandResults: This command delete file on the disk """ incident_id = args.get('incidentID', demisto.incident()["id"]) - file_path = demisto.args()['filePath'] + file_path = args.get('filePath', "") field_name = args.get('fieldName', "attachment") + + if not file_path: + return_error("Argument file_path is empty.") try: client.delete_attachment(incident_id, file_path, field_name) - except DemistoException as e: - return_error(f"File already deleted or not found !\n{str(e)}") + except DemistoException as error: + return_error(f"File already deleted or not found !\n{str(error)}") return CommandResults(readable_output=f"Attachment {file_path} deleted !") @@ -274,8 +300,8 @@ def delete_file(client: Client, entry_id: str): # delete old file try: client.delete_file(entry_id) - except DemistoException as e: - return_error(f"File already deleted or not found !\n{str(e)}") + except DemistoException as error: + return_error(f"File already deleted or not found !\n{str(error)}") # output incident_id = get_incident_id(entry_id) client.delete_context(incident_id, "File") @@ -293,7 +319,10 @@ def delete_file_command(client: Client, args: dict) -> CommandResults: Note: This command delete file on the disk """ - entry_id = demisto.args()['entryID'] + entry_id = args.get('entryID', "") + + if not entry_id: + return_error("Argument entry_id is empty.") new_files = delete_file(client, entry_id) return CommandResults(readable_output=f"File {entry_id} deleted !", outputs=new_files, outputs_prefix="File") @@ -339,32 +368,49 @@ def upload_file_command(client: Client, args: dict) -> CommandResults: """ incident_id = args.get('incidentID', demisto.incident()["id"]) file_content = args.get('fileContent', '') + file_content_b64 = args.get('fileContentB64', '') entry_id = args.get('entryID', '') file_path = args.get('filePath', '') file_name = args.get('fileName', '') target = args.get('target', 'war room entry') + # id can be empty if this command is triggered by a field changed + if not incident_id: + return_error("Unable to get the incident id of the incident !") # check if some content is given and not too many - if len(list(filter(None, [file_content, entry_id, file_path]))) != 1: - return_error("You have to give either the content of the file using the arg 'fileContent' or an entryID or a file path !") + if len(list(filter(None, [file_content, file_content_b64, entry_id, file_path]))) != 1: + return_error("You have to give either the content of the file using the arg 'fileContent'" + "or 'fileContentB64' or an entryID or a file path !") # if file_name is not set when using content of the file - if file_content and not file_name: + if (file_content or file_content_b64) and not file_name: return_error("You have to choose a name for your file !") response = {} if file_content: - response = client.upload_file(incident_id, file_content, file_name, target == 'incident attachment') + response = client.upload_file(incident_id, + file_content, + file_name, + target == 'incident attachment') + elif file_content_b64: + file_content_tmp = base64.b64decode(file_content_b64) + response = client.upload_file(incident_id, + file_content_tmp, + file_name, + target == 'incident attachment') else: arg_path: str = list(filter(None, [entry_id, file_path]))[0] res_path, res_name = get_file_path_name(arg_path) # file name override by user file_name = file_name if file_name else res_name if not file_name: - return_error("Impossible to detect a filename in the path, use the argument 'fileName' to set one !") + return_error("Impossible to detect a filename in the path, " + "use the argument 'fileName' to set one !") file_binary = open(res_path, 'rb') - response = client.upload_file(incident_id, file_binary, file_name, target == 'incident attachment') + response = client.upload_file(incident_id, + file_binary, + file_name, + target == 'incident attachment') file_binary.close() - demisto.results(response) readable = f'File {file_name} uploaded successfully to incident {incident_id}.' # in case the file uploaded as war room entry if target == 'war room entry': @@ -402,7 +448,7 @@ def main() -> None: proxy=proxy) if command == 'test-module': - result = test_module(client, params) + result = test_module(client) return_results(result) elif command == 'file-management-upload-file-to-incident': return_results(upload_file_command(client, args)) @@ -418,11 +464,12 @@ def main() -> None: raise NotImplementedError(f'Command {command} is not implemented') # Log exceptions and return errors - except Exception as e: - return_error(f'Failed to execute {command} command.\nError:\n{str(e)}') + except Exception as error: + return_error(f'Failed to execute {command} command.\nError:\n{str(error)}') ''' ENTRY POINT ''' + if __name__ in ('__main__', '__builtin__', 'builtins'): main() diff --git a/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.yml b/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.yml index 36d06a597e2d..6141652c57d9 100644 --- a/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.yml +++ b/Packs/XSOARFileManagement/Integrations/XSOARFileManagement/XSOARFileManagement.yml @@ -28,62 +28,64 @@ name: XSOAR File Management script: commands: - arguments: - - description: Incident ID to upload the file. If empty, the current incident ID is taken + - description: Incident ID to upload the file. If empty, the current incident ID is taken. name: incidentID - - description: Non binary content of the file (if set let filePath and filePath empty) + - description: Content of the file encoded in Base64 (if set let filePath, entrID and fileContent empty). + name: fileContentB64 + - description: Non binary content of the file (if set let filePath, entryID and fileContentB64 empty). name: fileContent - - description: Entry ID of the file to read (if set let filePath and fileContent empty) + - description: Entry ID of the file to read (if set let filePath, fileContent and fileContentB64 empty). name: entryID - - description: 'Path of the file to read ex: incident.attachment.path (if set let entryID and fileContent empty)' + - description: 'Path of the file to read ex: incident.attachment.path (if set let entryID, fileContent and fileContentB64 empty).' name: filePath - - description: Name of the file. Mandatory if used with filePath and fileContent otherwise the name of the file will not change + - description: Name of the file. Mandatory if used with filePath and fileContent otherwise the name of the file will not change. name: fileName - auto: PREDEFINED defaultValue: war room entry - description: 'Where to upload the file - Available options are: - ''war room entry'': the file will be uploaded as war room entry. - ''incident attachment'': the file will be uploaded as incident attachment. - default are ''war room entry''' + description: 'Where to upload the file - Available options are: - ''war room entry'': the file will be uploaded as war room entry. - ''incident attachment'': the file will be uploaded as incident attachment. - default are ''war room entry''.' name: target predefined: - war room entry - incident attachment - description: Copies a file from this incident to the specified incident. Usefull if you want to manipule file in the preprocessing + description: Copies a file from this incident to the specified incident. Usefull if you want to manipule file in the preprocessing. name: file-management-upload-file-to-incident - arguments: - - description: Entry ID of the file + - description: Entry ID of the file. name: entryID required: true - description: Delete the file from the incident and from the XSOAR server + description: Delete the file from the incident and from the XSOAR server. execution: true name: file-management-delete-file - arguments: - - description: Entry ID of the file + - description: Entry ID of the file. name: entryID required: true - description: Check if entry ID exist + description: Check if entry ID exist. name: file-management-check-file outputs: - - description: Dictionary with EntryID as key and boolean if the file exists as value + - description: Dictionary with EntryID as key and boolean if the file exists as value. contextPath: IsFileExists - arguments: - - description: File path of the file + - description: File path of the file. name: filePath required: true - - description: ID of the incident to delete attachment + - description: ID of the incident to delete attachment. name: incidentID - - description: 'Name of the field (type attachment) you want to remove the attachment by default it''s the incident attachment (incident.attachment) field' + - description: 'Name of the field (type attachment) you want to remove the attachment by default it''s the incident attachment (incident.attachment) field.' name: fieldName - description: Delete the attachment from the incident and from the XSOAR server + description: Delete the attachment from the incident and from the XSOAR server. execution: true name: file-management-delete-attachment - arguments: - - description: Entry ID of the file to rename + - description: Entry ID of the file to rename. name: entryID required: true - - description: New name for the file + - description: New name for the file. name: newFileName required: true - description: 'Rename a file. Warning: use this only if necessary, it''s HEAVY to run, this will delete and recreate the file with another name !' + description: 'Rename a file. Warning: use this only if necessary, it''s HEAVY to run, this will delete and recreate the file with another name.' name: file-management-rename-file - dockerimage: demisto/python3:3.10.12.63474 + dockerimage: demisto/python3:3.10.13.78960 runonce: false script: '' subtype: python3 diff --git a/Packs/XSOARFileManagement/ReleaseNotes/1_0_2.md b/Packs/XSOARFileManagement/ReleaseNotes/1_0_2.md new file mode 100644 index 000000000000..bd59b84a94fd --- /dev/null +++ b/Packs/XSOARFileManagement/ReleaseNotes/1_0_2.md @@ -0,0 +1,4 @@ +#### Integrations +##### XSOAR File Management +- Updated the Docker image to: *demisto/python3:3.10.13.78960*. +- Added support for the *fileContentB64* argument in the ***file-management-upload-file-to-incident*** command. \ No newline at end of file diff --git a/Packs/XSOARFileManagement/pack_metadata.json b/Packs/XSOARFileManagement/pack_metadata.json index ca1ae3e92c7e..bae4301d70c5 100644 --- a/Packs/XSOARFileManagement/pack_metadata.json +++ b/Packs/XSOARFileManagement/pack_metadata.json @@ -2,7 +2,7 @@ "name": "XSOAR File Management", "description": "This pack let user manipulate file inside XSOAR more easily than with the builtin functions.", "support": "community", - "currentVersion": "1.0.1", + "currentVersion": "1.0.2", "author": "Pierre", "url": "", "email": "",