From 2ba8392d4d5ab4da433b28f49d61b753bad76ef9 Mon Sep 17 00:00:00 2001 From: content-bot <55035720+content-bot@users.noreply.github.com> Date: Wed, 4 Jan 2023 15:15:32 +0200 Subject: [PATCH] [Marketplace Contribution] Web File Repository (#23469) * [Marketplace Contribution] Web File Repository (#23108) * "pack contribution initial commit" * Added test scripts Co-authored-by: spearmin10 * update docker * add description Co-authored-by: xsoar-bot <67315154+xsoar-bot@users.noreply.github.com> Co-authored-by: spearmin10 Co-authored-by: ostolero --- Packs/WebFileRepository/.pack-ignore | 0 Packs/WebFileRepository/.secrets-ignore | 0 .../Integrations/WebFileRepository/README.md | 210 + .../WebFileRepository/WebFileRepository.py | 4278 +++++++++++++++++ .../WebFileRepository/WebFileRepository.yml | 194 + .../WebFileRepository_description.md | 16 + .../WebFileRepository_image.png | Bin 0 -> 3328 bytes .../WebFileRepository_test.py | 2172 +++++++++ .../test_data/delete_out_01.json | 66 + .../test_data/delete_out_02.json | 58 + .../test_data/delete_out_03.json | 34 + .../test_data/download_file.dat | 1 + .../test_data/download_out_01.dat | 1 + .../test_data/download_out_02.dat | 1 + .../test_data/integration_ctx_common.json | 74 + .../test_data/integration_ctx_empty.json | 1 + .../test_data/list_files_results_01.json | 66 + .../test_data/list_files_results_02.json | 66 + .../test_data/list_files_svrresp_01.json | 58 + .../test_data/list_files_svrresp_02.json | 58 + .../test_data/ls_out_01.json | 3 + .../test_data/ls_out_02.json | 46 + .../test_data/ls_out_03.json | 81 + .../test_data/ls_out_04.json | 32 + .../test_data/ls_out_05.json | 25 + .../test_data/ls_out_06.json | 11 + .../test_data/mime_types_out_merge.json | 1066 ++++ .../test_data/mime_types_out_overwrite.json | 9 + .../test_data/mime_types_style_01.json | 6 + .../test_data/mime_types_style_02.txt | 8 + .../test_data/mime_types_style_03.txt | 7 + .../test_data/upload_file.dat | 1 + .../test_data/upload_file.tar.gz | Bin 0 -> 154 bytes .../test_data/upload_file.txt | 1 + .../test_data/upload_file.zip | Bin 0 -> 313 bytes .../test_data/upload_out_01.json | 10 + .../test_data/upload_out_02.json | 18 + .../test_data/upload_out_03.json | 18 + .../test_data/upload_out_04.json | 18 + .../test_data/upload_out_05.json | 10 + Packs/WebFileRepository/README.md | 0 Packs/WebFileRepository/pack_metadata.json | 21 + 42 files changed, 8745 insertions(+) create mode 100644 Packs/WebFileRepository/.pack-ignore create mode 100644 Packs/WebFileRepository/.secrets-ignore create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/README.md create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.py create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.yml create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_description.md create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_image.png create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_test.py create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_01.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_02.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_03.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_file.dat create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_01.dat create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_02.dat create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/integration_ctx_common.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/integration_ctx_empty.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/list_files_results_01.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/list_files_results_02.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/list_files_svrresp_01.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/list_files_svrresp_02.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/ls_out_01.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/ls_out_02.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/ls_out_03.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/ls_out_04.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/ls_out_05.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/ls_out_06.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/mime_types_out_merge.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/mime_types_out_overwrite.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/mime_types_style_01.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/mime_types_style_02.txt create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/mime_types_style_03.txt create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_file.dat create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_file.tar.gz create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_file.txt create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_file.zip create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_out_01.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_out_02.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_out_03.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_out_04.json create mode 100644 Packs/WebFileRepository/Integrations/WebFileRepository/test_data/upload_out_05.json create mode 100644 Packs/WebFileRepository/README.md create mode 100644 Packs/WebFileRepository/pack_metadata.json diff --git a/Packs/WebFileRepository/.pack-ignore b/Packs/WebFileRepository/.pack-ignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Packs/WebFileRepository/.secrets-ignore b/Packs/WebFileRepository/.secrets-ignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/README.md b/Packs/WebFileRepository/Integrations/WebFileRepository/README.md new file mode 100644 index 000000000000..8a1180d9b791 --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/README.md @@ -0,0 +1,210 @@ +Simple web server with a file uploading console to store small files. +This is helpful to make your environment ready for testing purpose for your playbooks or automations to download files from a web server. +## Configure Web File Repository on Cortex XSOAR + +1. Navigate to **Settings** > **Integrations** > **Servers & Services**. +2. Search for Web File Repository. +3. Click **Add instance** to create and configure a new integration instance. + + | **Parameter** | **Description** | **Required** | + | --- | --- | --- | + | Incident type | | False | + | Long running instance | | False | + | Port mapping (<port> or <host port>:<docker port>) | | True | + | User ID for Read/Write | | False | + | Password | | False | + | User ID for Read-Only | | False | + | Password | | False | + | Authentication Method | Some of the browsers such as Chrome may not support Digest-sha256. | False | + | Public Read Access | Authentication is not requiured for read access | False | + | MIME Types for file extensions | "mime-type extension \[extension\]\*" for each line, wild-card file extensions are supported. | False | + | Merge with Default MIME Types | Set true to merge the specified types with the default settings, false to replace them | False | + | Attachment extensions | List of extensions to set "attachment" to Content-Disposition | False | + | Storage Protection | | True | + | The maximum repository size | | False | + | The maximum sandbox repository size | | False | + +4. Click **Test** to validate the URLs, token, and connection. + + +## How to Access the File Management UI + +### Access the File Management UI by URL and Port (HTTP) +In a web browser, go to **`http://:`**. + +### Access the File Management UI by Instance Name (HTTPS) + +To access the File Management UI by instance name, make sure ***Instance execute external*** is enabled. + +1. In Cortex XSOAR, go to **Settings > About > Troubleshooting**. +2. In the **Server Configuration** section, verify that the `instance.execute.external.` key is set to `true`. If this key does not exist, click **+ Add Server Configuration** and add the `instance.execute.external.` and set the value to `true`. See [this documentation](https://xsoar.pan.dev/docs/reference/articles/long-running-invoke) for further information. +3. In a web browser, go to `https:///instance/execute//`. + + In Multi Tenant environments, go to `https:///acc_/instance/execute//` + + +## Commands +You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. +After you successfully execute a command, a DBot message appears in the War Room with the command details. +### wfr-status +*** +Get the service status + + +#### Base Command + +`wfr-status` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | + + +#### Context Output + +| **Path** | **Type** | **Description** | +| --- | --- | --- | +| WebFileRepository.Status.StorageUsage | number | The current storage usage in bytes | +| WebFileRepository.Status.SandboxUsage | number | The current sandbox usage in bytes | +| WebFileRepository.Status.StorageProtection | string | The storage protection mode | +| WebFileRepository.Status.IntercommunicationIP | string | The IP address of the service to which the internal client connects | +| WebFileRepository.Status.IntercommunicationPort | number | The port number of the service to which the internal client connects | +| WebFileRepository.Status.ExternaIP | unknown | The external IP address of the service | +| WebFileRepository.Status.ExternalPort | unknown | The external port number of the service | +| WebFileRepository.Status.ServerIP | string | The IP address of the service | +| WebFileRepository.Status.ServerPort | number | The port number of the service | + +### wfr-cleanup +*** +Remove all the files from the repository + + +#### Base Command + +`wfr-cleanup` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | + + +#### Context Output + +There is no context output for this command. +### wfr-upload-files +*** +Upload files to the repository + + +#### Base Command + +`wfr-upload-files` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| entry_ids | The entry ID list of files. | Required | +| extract_archive | Set to true to extract files to archive files, otherwise false. Possible values are: true, false. Default is false. | Optional | +| upload_directory | The directory path where to upload. Default is /. | Optional | + + +#### Context Output + +There is no context output for this command. +### wfr-list-files +*** +List files in the repository + + +#### Base Command + +`wfr-list-files` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| directory | The directory path where to list files. Default is /. | Optional | +| recursive | Set to true to list subdirectories recursively, otherwise false. Possible values are: true, false. Default is false. | Optional | + + +#### Context Output + +| **Path** | **Type** | **Description** | +| --- | --- | --- | +| WebFileRepository.Files.Name | string | The file name | +| WebFileRepository.Files.Path | string | The file path | +| WebFileRepository.Files.Size | number | The file size in bytes | +| WebFileRepository.Files.LastModified | date | The last modified time | + +### wfr-remove-files +*** +Remove files from the repository + + +#### Base Command + +`wfr-remove-files` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| paths | The list of the file paths. | Required | + + +#### Context Output + +There is no context output for this command. +### wfr-download-file +*** +Download a file from the repository + + +#### Base Command + +`wfr-download-file` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| path | The file path. | Required | +| save_as | The name to give the file to save. | Optional | + + +#### Context Output + +There is no context output for this command. +### wfr-archive-zip +*** +Download a file to which all the files are archived + + +#### Base Command + +`wfr-archive-zip` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| save_as | The name to give the archive-file to save. | Optional | + + +#### Context Output + +There is no context output for this command. +### wfr-reset +*** +Reset the repository data + + +#### Base Command + +`wfr-reset` +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | + + +#### Context Output + +There is no context output for this command. \ No newline at end of file diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.py b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.py new file mode 100644 index 000000000000..8fe528d24a60 --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.py @@ -0,0 +1,4278 @@ +import base64 +import fnmatch +import gzip +import hashlib +import io +import math +import os +import re +import socket +import struct +import tarfile +import urllib.parse +import urllib.request +import uuid +import zipfile +from datetime import timezone +from email import parser as email_parser +from enum import Enum +from tempfile import NamedTemporaryFile +from typing import (IO, Any, Callable, Dict, Generator, List, Optional, Set, + Tuple, Union) + +import bottle +import demistomock as demisto # noqa: F401 +from bottle import BaseRequest, HTTPResponse +from CommonServerPython import * # noqa: F401 +from requests.auth import HTTPBasicAuth, HTTPDigestAuth + + +""" +The resource archive includes the following files provided by DataTables (https://datatables.net/). + - datatables.min.css + - datatables.min.js + +Those files have been downloaded with the options below. + ## styling framework + - DataTables + + ## packages + - jQuery 3 + + ## extensions + - Buttons + - RowGroup + - Select +""" +RESOURCES_ZIP_B64 = ''' +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 +''' +RESOURCES_ZIP: Optional[zipfile.ZipFile] = None + +DEFAULT_MIME_TYPES = ''' +{ + "application/andrew-inset": ["ez"], + "application/applixware": ["aw"], + "application/atom+xml": ["atom"], + "application/atomcat+xml": ["atomcat"], + "application/atomsvc+xml": ["atomsvc"], + "application/bdoc": ["bdoc"], + "application/ccxml+xml": ["ccxml"], + "application/cdmi-capability": ["cdmia"], + "application/cdmi-container": ["cdmic"], + "application/cdmi-domain": ["cdmid"], + "application/cdmi-object": ["cdmio"], + "application/cdmi-queue": ["cdmiq"], + "application/cu-seeme": ["cu"], + "application/dash+xml": ["mpd"], + "application/davmount+xml": ["davmount"], + "application/docbook+xml": ["dbk"], + "application/dssc+der": ["dssc"], + "application/dssc+xml": ["xdssc"], + "application/ecmascript": ["ecma"], + "application/emma+xml": ["emma"], + "application/epub+zip": ["epub"], + "application/exi": ["exi"], + "application/font-tdpfr": ["pfr"], + "application/font-woff": ["woff"], + "application/font-woff2": ["woff2"], + "application/geo+json": ["geojson"], + "application/gml+xml": ["gml"], + "application/gpx+xml": ["gpx"], + "application/gxf": ["gxf"], + "application/gzip": ["gz"], + "application/hyperstudio": ["stk"], + "application/inkml+xml": ["inkml", "ink"], + "application/ipfix": ["ipfix"], + "application/java-archive": ["war", "jar", "ear"], + "application/java-serialized-object": ["ser"], + "application/java-vm": ["class"], + "application/javascript": ["mjs", "js"], + "application/json": ["json", "map"], + "application/json5": ["json5"], + "application/jsonml+json": ["jsonml"], + "application/ld+json": ["jsonld"], + "application/lost+xml": ["lostxml"], + "application/mac-binhex40": ["hqx"], + "application/mac-compactpro": ["cpt"], + "application/mads+xml": ["mads"], + "application/manifest+json": ["webmanifest"], + "application/marc": ["mrc"], + "application/marcxml+xml": ["mrcx"], + "application/mathematica": ["nb", "ma", "mb"], + "application/mathml+xml": ["mathml"], + "application/mbox": ["mbox"], + "application/mediaservercontrol+xml": ["mscml"], + "application/metalink+xml": ["metalink"], + "application/metalink4+xml": ["meta4"], + "application/mets+xml": ["mets"], + "application/mods+xml": ["mods"], + "application/mp21": ["mp21", "m21"], + "application/mp4": ["mp4s", "m4p"], + "application/msword": ["doc", "dot"], + "application/mxf": ["mxf"], + "application/octet-stream": ["msm", "bpk", "deploy", "mar", "dms", "pkg", "elc", "lrf", + "buffer", "deb", "dll", "msi", "dmg", "distz", "iso", "dump", + "img", "dist", "exe", "msp", "so", "bin"], + "application/oda": ["oda"], + "application/oebps-package+xml": ["opf"], + "application/ogg": ["ogx"], + "application/omdoc+xml": ["omdoc"], + "application/onenote": ["onepkg", "onetoc2", "onetoc", "onetmp"], + "application/oxps": ["oxps"], + "application/patch-ops-error+xml": ["xer"], + "application/pdf": ["pdf"], + "application/pgp-encrypted": ["pgp"], + "application/pgp-signature": ["sig", "asc"], + "application/pics-rules": ["prf"], + "application/pkcs10": ["p10"], + "application/pkcs7-mime": ["p7c", "p7m"], + "application/pkcs7-signature": ["p7s"], + "application/pkcs8": ["p8"], + "application/pkix-attr-cert": ["ac"], + "application/pkix-cert": ["cer"], + "application/pkix-crl": ["crl"], + "application/pkix-pkipath": ["pkipath"], + "application/pkixcmp": ["pki"], + "application/pls+xml": ["pls"], + "application/postscript": ["ps", "ai", "eps"], + "application/prs.cww": ["cww"], + "application/pskc+xml": ["pskcxml"], + "application/rdf+xml": ["rdf"], + "application/reginfo+xml": ["rif"], + "application/relax-ng-compact-syntax": ["rnc"], + "application/resource-lists+xml": ["rl"], + "application/resource-lists-diff+xml": ["rld"], + "application/rls-services+xml": ["rs"], + "application/rpki-ghostbusters": ["gbr"], + "application/rpki-manifest": ["mft"], + "application/rpki-roa": ["roa"], + "application/rsd+xml": ["rsd"], + "application/rss+xml": ["rss"], + "application/rtf": ["rtf"], + "application/sbml+xml": ["sbml"], + "application/scvp-cv-request": ["scq"], + "application/scvp-cv-response": ["scs"], + "application/scvp-vp-request": ["spq"], + "application/scvp-vp-response": ["spp"], + "application/sdp": ["sdp"], + "application/set-payment-initiation": ["setpay"], + "application/set-registration-initiation": ["setreg"], + "application/shf+xml": ["shf"], + "application/smil+xml": ["smil", "smi"], + "application/sparql-query": ["rq"], + "application/sparql-results+xml": ["srx"], + "application/srgs": ["gram"], + "application/srgs+xml": ["grxml"], + "application/sru+xml": ["sru"], + "application/ssdl+xml": ["ssdl"], + "application/ssml+xml": ["ssml"], + "application/tei+xml": ["teicorpus", "tei"], + "application/thraud+xml": ["tfi"], + "application/timestamped-data": ["tsd"], + "application/vnd.3gpp.pic-bw-large": ["plb"], + "application/vnd.3gpp.pic-bw-small": ["psb"], + "application/vnd.3gpp.pic-bw-var": ["pvb"], + "application/vnd.3gpp2.tcap": ["tcap"], + "application/vnd.3m.post-it-notes": ["pwn"], + "application/vnd.accpac.simply.aso": ["aso"], + "application/vnd.accpac.simply.imp": ["imp"], + "application/vnd.acucobol": ["acu"], + "application/vnd.acucorp": ["atc", "acutc"], + "application/vnd.adobe.air-application-installer-package+zip": ["air"], + "application/vnd.adobe.formscentral.fcdt": ["fcdt"], + "application/vnd.adobe.fxp": ["fxp", "fxpl"], + "application/vnd.adobe.xdp+xml": ["xdp"], + "application/vnd.adobe.xfdf": ["xfdf"], + "application/vnd.ahead.space": ["ahead"], + "application/vnd.airzip.filesecure.azf": ["azf"], + "application/vnd.airzip.filesecure.azs": ["azs"], + "application/vnd.amazon.ebook": ["azw"], + "application/vnd.americandynamics.acc": ["acc"], + "application/vnd.amiga.ami": ["ami"], + "application/vnd.android.package-archive": ["apk"], + "application/vnd.anser-web-certificate-issue-initiation": ["cii"], + "application/vnd.anser-web-funds-transfer-initiation": ["fti"], + "application/vnd.antix.game-component": ["atx"], + "application/vnd.apple.installer+xml": ["mpkg"], + "application/vnd.apple.mpegurl": ["m3u8"], + "application/vnd.apple.pkpass": ["pkpass"], + "application/vnd.aristanetworks.swi": ["swi"], + "application/vnd.astraea-software.iota": ["iota"], + "application/vnd.audiograph": ["aep"], + "application/vnd.blueice.multipass": ["mpm"], + "application/vnd.bmi": ["bmi"], + "application/vnd.businessobjects": ["rep"], + "application/vnd.chemdraw+xml": ["cdxml"], + "application/vnd.chipnuts.karaoke-mmd": ["mmd"], + "application/vnd.cinderella": ["cdy"], + "application/vnd.claymore": ["cla"], + "application/vnd.cloanto.rp9": ["rp9"], + "application/vnd.clonk.c4group": ["c4d", "c4u", "c4g", "c4p", "c4f"], + "application/vnd.cluetrust.cartomobile-config": ["c11amc"], + "application/vnd.cluetrust.cartomobile-config-pkg": ["c11amz"], + "application/vnd.commonspace": ["csp"], + "application/vnd.contact.cmsg": ["cdbcmsg"], + "application/vnd.cosmocaller": ["cmc"], + "application/vnd.crick.clicker": ["clkx"], + "application/vnd.crick.clicker.keyboard": ["clkk"], + "application/vnd.crick.clicker.palette": ["clkp"], + "application/vnd.crick.clicker.template": ["clkt"], + "application/vnd.crick.clicker.wordbank": ["clkw"], + "application/vnd.criticaltools.wbs+xml": ["wbs"], + "application/vnd.ctc-posml": ["pml"], + "application/vnd.cups-ppd": ["ppd"], + "application/vnd.curl.car": ["car"], + "application/vnd.curl.pcurl": ["pcurl"], + "application/vnd.dart": ["dart"], + "application/vnd.data-vision.rdz": ["rdz"], + "application/vnd.dece.data": ["uvf", "uvvd", "uvd", "uvvf"], + "application/vnd.dece.ttml+xml": ["uvvt", "uvt"], + "application/vnd.dece.unspecified": ["uvvx", "uvx"], + "application/vnd.dece.zip": ["uvvz", "uvz"], + "application/vnd.denovo.fcselayout-link": ["fe_launch"], + "application/vnd.dna": ["dna"], + "application/vnd.dolby.mlp": ["mlp"], + "application/vnd.dpgraph": ["dpg"], + "application/vnd.dreamfactory": ["dfac"], + "application/vnd.ds-keypoint": ["kpxx"], + "application/vnd.dvb.ait": ["ait"], + "application/vnd.dvb.service": ["svc"], + "application/vnd.dynageo": ["geo"], + "application/vnd.ecowin.chart": ["mag"], + "application/vnd.enliven": ["nml"], + "application/vnd.epson.esf": ["esf"], + "application/vnd.epson.msf": ["msf"], + "application/vnd.epson.quickanime": ["qam"], + "application/vnd.epson.salt": ["slt"], + "application/vnd.epson.ssf": ["ssf"], + "application/vnd.eszigno3+xml": ["et3", "es3"], + "application/vnd.ezpix-album": ["ez2"], + "application/vnd.ezpix-package": ["ez3"], + "application/vnd.fdf": ["fdf"], + "application/vnd.fdsn.mseed": ["mseed"], + "application/vnd.fdsn.seed": ["dataless", "seed"], + "application/vnd.flographit": ["gph"], + "application/vnd.fluxtime.clip": ["ftc"], + "application/vnd.framemaker": ["book", "maker", "fm", "frame"], + "application/vnd.frogans.fnc": ["fnc"], + "application/vnd.frogans.ltf": ["ltf"], + "application/vnd.fsc.weblaunch": ["fsc"], + "application/vnd.fujitsu.oasys": ["oas"], + "application/vnd.fujitsu.oasys2": ["oa2"], + "application/vnd.fujitsu.oasys3": ["oa3"], + "application/vnd.fujitsu.oasysgp": ["fg5"], + "application/vnd.fujitsu.oasysprs": ["bh2"], + "application/vnd.fujixerox.ddd": ["ddd"], + "application/vnd.fujixerox.docuworks": ["xdw"], + "application/vnd.fujixerox.docuworks.binder": ["xbd"], + "application/vnd.fuzzysheet": ["fzs"], + "application/vnd.genomatix.tuxedo": ["txd"], + "application/vnd.geogebra.file": ["ggb"], + "application/vnd.geogebra.tool": ["ggt"], + "application/vnd.geometry-explorer": ["gex", "gre"], + "application/vnd.geonext": ["gxt"], + "application/vnd.geoplan": ["g2w"], + "application/vnd.geospace": ["g3w"], + "application/vnd.gmx": ["gmx"], + "application/vnd.google-apps.document": ["gdoc"], + "application/vnd.google-apps.presentation": ["gslides"], + "application/vnd.google-apps.spreadsheet": ["gsheet"], + "application/vnd.google-earth.kml+xml": ["kml"], + "application/vnd.google-earth.kmz": ["kmz"], + "application/vnd.grafeq": ["gqs", "gqf"], + "application/vnd.groove-account": ["gac"], + "application/vnd.groove-help": ["ghf"], + "application/vnd.groove-identity-message": ["gim"], + "application/vnd.groove-injector": ["grv"], + "application/vnd.groove-tool-message": ["gtm"], + "application/vnd.groove-tool-template": ["tpl"], + "application/vnd.groove-vcard": ["vcg"], + "application/vnd.hal+xml": ["hal"], + "application/vnd.handheld-entertainment+xml": ["zmm"], + "application/vnd.hbci": ["hbci"], + "application/vnd.hhe.lesson-player": ["les"], + "application/vnd.hp-hpgl": ["hpgl"], + "application/vnd.hp-hpid": ["hpid"], + "application/vnd.hp-hps": ["hps"], + "application/vnd.hp-jlyt": ["jlt"], + "application/vnd.hp-pcl": ["pcl"], + "application/vnd.hp-pclxl": ["pclxl"], + "application/vnd.hydrostatix.sof-data": ["sfd-hdstx"], + "application/vnd.ibm.minipay": ["mpy"], + "application/vnd.ibm.modcap": ["listafp", "afp", "list3820"], + "application/vnd.ibm.rights-management": ["irm"], + "application/vnd.ibm.secure-container": ["sc"], + "application/vnd.iccprofile": ["icm", "icc"], + "application/vnd.igloader": ["igl"], + "application/vnd.immervision-ivp": ["ivp"], + "application/vnd.immervision-ivu": ["ivu"], + "application/vnd.insors.igm": ["igm"], + "application/vnd.intercon.formnet": ["xpx", "xpw"], + "application/vnd.intergeo": ["i2g"], + "application/vnd.intu.qbo": ["qbo"], + "application/vnd.intu.qfx": ["qfx"], + "application/vnd.ipunplugged.rcprofile": ["rcprofile"], + "application/vnd.irepository.package+xml": ["irp"], + "application/vnd.is-xpr": ["xpr"], + "application/vnd.isac.fcs": ["fcs"], + "application/vnd.jam": ["jam"], + "application/vnd.jcp.javame.midlet-rms": ["rms"], + "application/vnd.jisp": ["jisp"], + "application/vnd.joost.joda-archive": ["joda"], + "application/vnd.kahootz": ["ktz", "ktr"], + "application/vnd.kde.karbon": ["karbon"], + "application/vnd.kde.kchart": ["chrt"], + "application/vnd.kde.kformula": ["kfo"], + "application/vnd.kde.kivio": ["flw"], + "application/vnd.kde.kontour": ["kon"], + "application/vnd.kde.kpresenter": ["kpr", "kpt"], + "application/vnd.kde.kspread": ["ksp"], + "application/vnd.kde.kword": ["kwt", "kwd"], + "application/vnd.kenameaapp": ["htke"], + "application/vnd.kidspiration": ["kia"], + "application/vnd.kinar": ["knp", "kne"], + "application/vnd.koan": ["skt", "skp", "skd", "skm"], + "application/vnd.kodak-descriptor": ["sse"], + "application/vnd.las.las+xml": ["lasxml"], + "application/vnd.llamagraphics.life-balance.desktop": ["lbd"], + "application/vnd.llamagraphics.life-balance.exchange+xml": ["lbe"], + "application/vnd.lotus-1-2-3": ["123"], + "application/vnd.lotus-approach": ["apr"], + "application/vnd.lotus-freelance": ["pre"], + "application/vnd.lotus-notes": ["nsf"], + "application/vnd.lotus-organizer": ["org"], + "application/vnd.lotus-screencam": ["scm"], + "application/vnd.lotus-wordpro": ["lwp"], + "application/vnd.macports.portpkg": ["portpkg"], + "application/vnd.mcd": ["mcd"], + "application/vnd.medcalcdata": ["mc1"], + "application/vnd.mediastation.cdkey": ["cdkey"], + "application/vnd.mfer": ["mwf"], + "application/vnd.mfmp": ["mfm"], + "application/vnd.micrografx.flo": ["flo"], + "application/vnd.micrografx.igx": ["igx"], + "application/vnd.mif": ["mif"], + "application/vnd.mobius.daf": ["daf"], + "application/vnd.mobius.dis": ["dis"], + "application/vnd.mobius.mbk": ["mbk"], + "application/vnd.mobius.mqy": ["mqy"], + "application/vnd.mobius.msl": ["msl"], + "application/vnd.mobius.plc": ["plc"], + "application/vnd.mobius.txf": ["txf"], + "application/vnd.mophun.application": ["mpn"], + "application/vnd.mophun.certificate": ["mpc"], + "application/vnd.mozilla.xul+xml": ["xul"], + "application/vnd.ms-artgalry": ["cil"], + "application/vnd.ms-cab-compressed": ["cab"], + "application/vnd.ms-excel": ["xlm", "xlc", "xla", "xlw", "xlt", "xls"], + "application/vnd.ms-excel.addin.macroenabled.12": ["xlam"], + "application/vnd.ms-excel.sheet.binary.macroenabled.12": ["xlsb"], + "application/vnd.ms-excel.sheet.macroenabled.12": ["xlsm"], + "application/vnd.ms-excel.template.macroenabled.12": ["xltm"], + "application/vnd.ms-fontobject": ["eot"], + "application/vnd.ms-htmlhelp": ["chm"], + "application/vnd.ms-ims": ["ims"], + "application/vnd.ms-lrm": ["lrm"], + "application/vnd.ms-officetheme": ["thmx"], + "application/vnd.ms-outlook": ["msg"], + "application/vnd.ms-pki.seccat": ["cat"], + "application/vnd.ms-pki.stl": ["stl"], + "application/vnd.ms-powerpoint": ["pps", "ppt", "pot"], + "application/vnd.ms-powerpoint.addin.macroenabled.12": ["ppam"], + "application/vnd.ms-powerpoint.presentation.macroenabled.12": ["pptm"], + "application/vnd.ms-powerpoint.slide.macroenabled.12": ["sldm"], + "application/vnd.ms-powerpoint.slideshow.macroenabled.12": ["ppsm"], + "application/vnd.ms-powerpoint.template.macroenabled.12": ["potm"], + "application/vnd.ms-project": ["mpp", "mpt"], + "application/vnd.ms-word.document.macroenabled.12": ["docm"], + "application/vnd.ms-word.template.macroenabled.12": ["dotm"], + "application/vnd.ms-works": ["wps", "wdb", "wcm", "wks"], + "application/vnd.ms-wpl": ["wpl"], + "application/vnd.ms-xpsdocument": ["xps"], + "application/vnd.mseq": ["mseq"], + "application/vnd.musician": ["mus"], + "application/vnd.muvee.style": ["msty"], + "application/vnd.mynfc": ["taglet"], + "application/vnd.neurolanguage.nlu": ["nlu"], + "application/vnd.nitf": ["ntf", "nitf"], + "application/vnd.noblenet-directory": ["nnd"], + "application/vnd.noblenet-sealer": ["nns"], + "application/vnd.noblenet-web": ["nnw"], + "application/vnd.nokia.n-gage.data": ["ngdat"], + "application/vnd.nokia.n-gage.symbian.install": ["n-gage"], + "application/vnd.nokia.radio-preset": ["rpst"], + "application/vnd.nokia.radio-presets": ["rpss"], + "application/vnd.novadigm.edm": ["edm"], + "application/vnd.novadigm.edx": ["edx"], + "application/vnd.novadigm.ext": ["ext"], + "application/vnd.oasis.opendocument.chart": ["odc"], + "application/vnd.oasis.opendocument.chart-template": ["otc"], + "application/vnd.oasis.opendocument.database": ["odb"], + "application/vnd.oasis.opendocument.formula": ["odf"], + "application/vnd.oasis.opendocument.formula-template": ["odft"], + "application/vnd.oasis.opendocument.graphics": ["odg"], + "application/vnd.oasis.opendocument.graphics-template": ["otg"], + "application/vnd.oasis.opendocument.image": ["odi"], + "application/vnd.oasis.opendocument.image-template": ["oti"], + "application/vnd.oasis.opendocument.presentation": ["odp"], + "application/vnd.oasis.opendocument.presentation-template": ["otp"], + "application/vnd.oasis.opendocument.spreadsheet": ["ods"], + "application/vnd.oasis.opendocument.spreadsheet-template": ["ots"], + "application/vnd.oasis.opendocument.text": ["odt"], + "application/vnd.oasis.opendocument.text-master": ["odm"], + "application/vnd.oasis.opendocument.text-template": ["ott"], + "application/vnd.oasis.opendocument.text-web": ["oth"], + "application/vnd.olpc-sugar": ["xo"], + "application/vnd.oma.dd2+xml": ["dd2"], + "application/vnd.openofficeorg.extension": ["oxt"], + "application/vnd.openxmlformats-officedocument.presentationml.presentation": ["pptx"], + "application/vnd.openxmlformats-officedocument.presentationml.slide": ["sldx"], + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": ["ppsx"], + "application/vnd.openxmlformats-officedocument.presentationml.template": ["potx"], + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ["xlsx"], + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": ["xltx"], + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ["docx"], + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": ["dotx"], + "application/vnd.osgeo.mapguide.package": ["mgp"], + "application/vnd.osgi.dp": ["dp"], + "application/vnd.osgi.subsystem": ["esa"], + "application/vnd.palm": ["pqa", "oprc", "pdb"], + "application/vnd.pawaafile": ["paw"], + "application/vnd.pg.format": ["str"], + "application/vnd.pg.osasli": ["ei6"], + "application/vnd.picsel": ["efif"], + "application/vnd.pmi.widget": ["wg"], + "application/vnd.pocketlearn": ["plf"], + "application/vnd.powerbuilder6": ["pbd"], + "application/vnd.previewsystems.box": ["box"], + "application/vnd.proteus.magazine": ["mgz"], + "application/vnd.publishare-delta-tree": ["qps"], + "application/vnd.pvi.ptid1": ["ptid"], + "application/vnd.quark.quarkxpress": ["qwd", "qxb", "qxd", "qxt", "qxl", "qwt"], + "application/vnd.realvnc.bed": ["bed"], + "application/vnd.recordare.musicxml": ["mxl"], + "application/vnd.recordare.musicxml+xml": ["musicxml"], + "application/vnd.rig.cryptonote": ["cryptonote"], + "application/vnd.rim.cod": ["cod"], + "application/vnd.rn-realmedia": ["rm"], + "application/vnd.rn-realmedia-vbr": ["rmvb"], + "application/vnd.route66.link66+xml": ["link66"], + "application/vnd.sailingtracker.track": ["st"], + "application/vnd.seemail": ["see"], + "application/vnd.sema": ["sema"], + "application/vnd.semd": ["semd"], + "application/vnd.semf": ["semf"], + "application/vnd.shana.informed.formdata": ["ifm"], + "application/vnd.shana.informed.formtemplate": ["itp"], + "application/vnd.shana.informed.interchange": ["iif"], + "application/vnd.shana.informed.package": ["ipk"], + "application/vnd.simtech-mindmapper": ["twd", "twds"], + "application/vnd.smaf": ["mmf"], + "application/vnd.smart.teacher": ["teacher"], + "application/vnd.solent.sdkm+xml": ["sdkm", "sdkd"], + "application/vnd.spotfire.dxp": ["dxp"], + "application/vnd.spotfire.sfs": ["sfs"], + "application/vnd.stardivision.calc": ["sdc"], + "application/vnd.stardivision.draw": ["sda"], + "application/vnd.stardivision.impress": ["sdd"], + "application/vnd.stardivision.math": ["smf"], + "application/vnd.stardivision.writer": ["vor", "sdw"], + "application/vnd.stardivision.writer-global": ["sgl"], + "application/vnd.stepmania.package": ["smzip"], + "application/vnd.stepmania.stepchart": ["sm"], + "application/vnd.sun.wadl+xml": ["wadl"], + "application/vnd.sun.xml.calc": ["sxc"], + "application/vnd.sun.xml.calc.template": ["stc"], + "application/vnd.sun.xml.draw": ["sxd"], + "application/vnd.sun.xml.draw.template": ["std"], + "application/vnd.sun.xml.impress": ["sxi"], + "application/vnd.sun.xml.impress.template": ["sti"], + "application/vnd.sun.xml.math": ["sxm"], + "application/vnd.sun.xml.writer": ["sxw"], + "application/vnd.sun.xml.writer.global": ["sxg"], + "application/vnd.sun.xml.writer.template": ["stw"], + "application/vnd.sus-calendar": ["sus", "susp"], + "application/vnd.svd": ["svd"], + "application/vnd.symbian.install": ["sis", "sisx"], + "application/vnd.syncml+xml": ["xsm"], + "application/vnd.syncml.dm+wbxml": ["bdm"], + "application/vnd.syncml.dm+xml": ["xdm"], + "application/vnd.tao.intent-module-archive": ["tao"], + "application/vnd.tcpdump.pcap": ["cap", "dmp", "pcap"], + "application/vnd.tmobile-livetv": ["tmo"], + "application/vnd.trid.tpt": ["tpt"], + "application/vnd.triscape.mxs": ["mxs"], + "application/vnd.trueapp": ["tra"], + "application/vnd.ufdl": ["ufd", "ufdl"], + "application/vnd.uiq.theme": ["utz"], + "application/vnd.umajin": ["umj"], + "application/vnd.unity": ["unityweb"], + "application/vnd.uoml+xml": ["uoml"], + "application/vnd.vcx": ["vcx"], + "application/vnd.visio": ["vst", "vsd", "vsw", "vss"], + "application/vnd.visionary": ["vis"], + "application/vnd.vsf": ["vsf"], + "application/vnd.wap.wbxml": ["wbxml"], + "application/vnd.wap.wmlc": ["wmlc"], + "application/vnd.wap.wmlscriptc": ["wmlsc"], + "application/vnd.webturbo": ["wtb"], + "application/vnd.wolfram.player": ["nbp"], + "application/vnd.wordperfect": ["wpd"], + "application/vnd.wqd": ["wqd"], + "application/vnd.wt.stf": ["stf"], + "application/vnd.xara": ["xar"], + "application/vnd.xfdl": ["xfdl"], + "application/vnd.yamaha.hv-dic": ["hvd"], + "application/vnd.yamaha.hv-script": ["hvs"], + "application/vnd.yamaha.hv-voice": ["hvp"], + "application/vnd.yamaha.openscoreformat": ["osf"], + "application/vnd.yamaha.openscoreformat.osfpvg+xml": ["osfpvg"], + "application/vnd.yamaha.smaf-audio": ["saf"], + "application/vnd.yamaha.smaf-phrase": ["spf"], + "application/vnd.yellowriver-custom-menu": ["cmp"], + "application/vnd.zul": ["zir", "zirz"], + "application/vnd.zzazz.deck+xml": ["zaz"], + "application/voicexml+xml": ["vxml"], + "application/widget": ["wgt"], + "application/winhlp": ["hlp"], + "application/wsdl+xml": ["wsdl"], + "application/wspolicy+xml": ["wspolicy"], + "application/x-7z-compressed": ["7z"], + "application/x-abiword": ["abw"], + "application/x-ace-compressed": ["ace"], + "application/x-apple-diskimage": ["dmg"], + "application/x-arj": ["arj"], + "application/x-authorware-bin": ["vox", "u32", "aab", "x32"], + "application/x-authorware-map": ["aam"], + "application/x-authorware-seg": ["aas"], + "application/x-bcpio": ["bcpio"], + "application/x-bdoc": ["bdoc"], + "application/x-bittorrent": ["torrent"], + "application/x-blorb": ["blb", "blorb"], + "application/x-bzip": ["bz"], + "application/x-bzip2": ["boz", "bz2"], + "application/x-cbr": ["cb7", "cbr", "cbz", "cba", "cbt"], + "application/x-cdlink": ["vcd"], + "application/x-cfs-compressed": ["cfs"], + "application/x-chat": ["chat"], + "application/x-chess-pgn": ["pgn"], + "application/x-chrome-extension": ["crx"], + "application/x-cocoa": ["cco"], + "application/x-conference": ["nsc"], + "application/x-cpio": ["cpio"], + "application/x-csh": ["csh"], + "application/x-debian-package": ["deb", "udeb"], + "application/x-dgc-compressed": ["dgc"], + "application/x-director": ["dir", "swa", "w3d", "fgd", "cst", "dxr", "cct", "cxt", "dcr"], + "application/x-doom": ["wad"], + "application/x-dtbncx+xml": ["ncx"], + "application/x-dtbook+xml": ["dtb"], + "application/x-dtbresource+xml": ["res"], + "application/x-dvi": ["dvi"], + "application/x-envoy": ["evy"], + "application/x-eva": ["eva"], + "application/x-font-bdf": ["bdf"], + "application/x-font-ghostscript": ["gsf"], + "application/x-font-linux-psf": ["psf"], + "application/x-font-otf": ["otf"], + "application/x-font-pcf": ["pcf"], + "application/x-font-snf": ["snf"], + "application/x-font-ttf": ["ttc", "ttf"], + "application/x-font-type1": ["pfm", "pfa", "pfb", "afm"], + "application/x-freearc": ["arc"], + "application/x-futuresplash": ["spl"], + "application/x-gca-compressed": ["gca"], + "application/x-glulx": ["ulx"], + "application/x-gnumeric": ["gnumeric"], + "application/x-gramps-xml": ["gramps"], + "application/x-gtar": ["gtar"], + "application/x-hdf": ["hdf"], + "application/x-httpd-php": ["php"], + "application/x-install-instructions": ["install"], + "application/x-iso9660-image": ["iso"], + "application/x-java-archive-diff": ["jardiff"], + "application/x-java-jnlp-file": ["jnlp"], + "application/x-latex": ["latex"], + "application/x-lua-bytecode": ["luac"], + "application/x-lzh-compressed": ["lzh", "lha"], + "application/x-makeself": ["run"], + "application/x-mie": ["mie"], + "application/x-mobipocket-ebook": ["mobi", "prc"], + "application/x-ms-application": ["application"], + "application/x-ms-shortcut": ["lnk"], + "application/x-ms-wmd": ["wmd"], + "application/x-ms-wmz": ["wmz"], + "application/x-ms-xbap": ["xbap"], + "application/x-msaccess": ["mdb"], + "application/x-msbinder": ["obd"], + "application/x-mscardfile": ["crd"], + "application/x-msclip": ["clp"], + "application/x-msdos-program": ["exe"], + "application/x-msdownload": ["exe", "com", "dll", "msi", "bat"], + "application/x-msmediaview": ["m13", "mvb", "m14"], + "application/x-msmetafile": ["wmf", "emf", "emz", "wmz"], + "application/x-msmoney": ["mny"], + "application/x-mspublisher": ["pub"], + "application/x-msschedule": ["scd"], + "application/x-msterminal": ["trm"], + "application/x-mswrite": ["wri"], + "application/x-netcdf": ["cdf", "nc"], + "application/x-ns-proxy-autoconfig": ["pac"], + "application/x-nzb": ["nzb"], + "application/x-perl": ["pl", "pm"], + "application/x-pilot": ["pdb", "prc"], + "application/x-pkcs12": ["pfx", "p12"], + "application/x-pkcs7-certificates": ["p7b", "spc"], + "application/x-pkcs7-certreqresp": ["p7r"], + "application/x-rar-compressed": ["rar"], + "application/x-redhat-package-manager": ["rpm"], + "application/x-research-info-systems": ["ris"], + "application/x-sea": ["sea"], + "application/x-sh": ["sh"], + "application/x-shar": ["shar"], + "application/x-shockwave-flash": ["swf"], + "application/x-silverlight-app": ["xap"], + "application/x-sql": ["sql"], + "application/x-stuffit": ["sit"], + "application/x-stuffitx": ["sitx"], + "application/x-subrip": ["srt"], + "application/x-sv4cpio": ["sv4cpio"], + "application/x-sv4crc": ["sv4crc"], + "application/x-t3vm-image": ["t3"], + "application/x-tads": ["gam"], + "application/x-tar": ["tar"], + "application/x-tcl": ["tcl", "tk"], + "application/x-tex": ["tex"], + "application/x-tex-tfm": ["tfm"], + "application/x-texinfo": ["texinfo", "texi"], + "application/x-tgif": ["obj"], + "application/x-ustar": ["ustar"], + "application/x-virtualbox-hdd": ["hdd"], + "application/x-virtualbox-ova": ["ova"], + "application/x-virtualbox-ovf": ["ovf"], + "application/x-virtualbox-vbox": ["vbox"], + "application/x-virtualbox-vbox-extpack": ["vbox-extpack"], + "application/x-virtualbox-vdi": ["vdi"], + "application/x-virtualbox-vhd": ["vhd"], + "application/x-virtualbox-vmdk": ["vmdk"], + "application/x-wais-source": ["src"], + "application/x-web-app-manifest+json": ["webapp"], + "application/x-x509-ca-cert": ["pem", "der", "crt"], + "application/x-xfig": ["fig"], + "application/x-xliff+xml": ["xlf"], + "application/x-xpinstall": ["xpi"], + "application/x-xz": ["xz"], + "application/x-zmachine": ["z3", "z4", "z6", "z1", "z8", "z7", "z2", "z5"], + "application/xaml+xml": ["xaml"], + "application/xcap-diff+xml": ["xdf"], + "application/xenc+xml": ["xenc"], + "application/xhtml+xml": ["xhtml", "xht"], + "application/xml": ["xsl", "rng", "xml", "xsd"], + "application/xml-dtd": ["dtd"], + "application/xop+xml": ["xop"], + "application/xproc+xml": ["xpl"], + "application/xslt+xml": ["xslt"], + "application/xspf+xml": ["xspf"], + "application/xv+xml": ["mxml", "xvml", "xhvml", "xvm"], + "application/yang": ["yang"], + "application/yin+xml": ["yin"], + "application/zip": ["zip"], + "audio/3gpp": ["3gpp"], + "audio/adpcm": ["adp"], + "audio/basic": ["snd", "au"], + "audio/midi": ["kar", "midi", "mid", "rmi"], + "audio/mp3": ["mp3"], + "audio/mp4": ["mp4a", "m4a"], + "audio/mpeg": ["mp3", "mp2", "m3a", "mp2a", "mpga", "m2a"], + "audio/ogg": ["oga", "spx", "ogg", "opus"], + "audio/s3m": ["s3m"], + "audio/silk": ["sil"], + "audio/vnd.dece.audio": ["uva", "uvva"], + "audio/vnd.digital-winds": ["eol"], + "audio/vnd.dra": ["dra"], + "audio/vnd.dts": ["dts"], + "audio/vnd.dts.hd": ["dtshd"], + "audio/vnd.lucent.voice": ["lvp"], + "audio/vnd.ms-playready.media.pya": ["pya"], + "audio/vnd.nuera.ecelp4800": ["ecelp4800"], + "audio/vnd.nuera.ecelp7470": ["ecelp7470"], + "audio/vnd.nuera.ecelp9600": ["ecelp9600"], + "audio/vnd.rip": ["rip"], + "audio/wav": ["wav"], + "audio/wave": ["wav"], + "audio/webm": ["weba"], + "audio/x-aac": ["aac"], + "audio/x-aiff": ["aifc", "aiff", "aif"], + "audio/x-caf": ["caf"], + "audio/x-flac": ["flac"], + "audio/x-m4a": ["m4a"], + "audio/x-matroska": ["mka"], + "audio/x-mpegurl": ["m3u"], + "audio/x-ms-wax": ["wax"], + "audio/x-ms-wma": ["wma"], + "audio/x-pn-realaudio": ["ra", "ram"], + "audio/x-pn-realaudio-plugin": ["rmp"], + "audio/x-realaudio": ["ra"], + "audio/x-wav": ["wav"], + "audio/xm": ["xm"], + "chemical/x-cdx": ["cdx"], + "chemical/x-cif": ["cif"], + "chemical/x-cmdf": ["cmdf"], + "chemical/x-cml": ["cml"], + "chemical/x-csml": ["csml"], + "chemical/x-xyz": ["xyz"], + "font/otf": ["otf"], + "image/apng": ["apng"], + "image/bmp": ["bmp"], + "image/cgm": ["cgm"], + "image/g3fax": ["g3"], + "image/gif": ["gif"], + "image/ief": ["ief"], + "image/jpeg": ["jpeg", "jpe", "jpg"], + "image/ktx": ["ktx"], + "image/png": ["png"], + "image/prs.btif": ["btif"], + "image/sgi": ["sgi"], + "image/svg+xml": ["svgz", "svg"], + "image/tiff": ["tif", "tiff"], + "image/vnd.adobe.photoshop": ["psd"], + "image/vnd.dece.graphic": ["uvvi", "uvi", "uvg", "uvvg"], + "image/vnd.djvu": ["djv", "djvu"], + "image/vnd.dvb.subtitle": ["sub"], + "image/vnd.dwg": ["dwg"], + "image/vnd.dxf": ["dxf"], + "image/vnd.fastbidsheet": ["fbs"], + "image/vnd.fpx": ["fpx"], + "image/vnd.fst": ["fst"], + "image/vnd.fujixerox.edmics-mmr": ["mmr"], + "image/vnd.fujixerox.edmics-rlc": ["rlc"], + "image/vnd.ms-modi": ["mdi"], + "image/vnd.ms-photo": ["wdp"], + "image/vnd.net-fpx": ["npx"], + "image/vnd.wap.wbmp": ["wbmp"], + "image/vnd.xiff": ["xif"], + "image/webp": ["webp"], + "image/x-3ds": ["3ds"], + "image/x-cmu-raster": ["ras"], + "image/x-cmx": ["cmx"], + "image/x-freehand": ["fhc", "fh7", "fh5", "fh", "fh4"], + "image/x-icon": ["ico"], + "image/x-jng": ["jng"], + "image/x-mrsid-image": ["sid"], + "image/x-ms-bmp": ["bmp"], + "image/x-pcx": ["pcx"], + "image/x-pict": ["pic", "pct"], + "image/x-portable-anymap": ["pnm"], + "image/x-portable-bitmap": ["pbm"], + "image/x-portable-graymap": ["pgm"], + "image/x-portable-pixmap": ["ppm"], + "image/x-rgb": ["rgb"], + "image/x-tga": ["tga"], + "image/x-xbitmap": ["xbm"], + "image/x-xpixmap": ["xpm"], + "image/x-xwindowdump": ["xwd"], + "message/rfc822": ["eml", "mime"], + "model/gltf+json": ["gltf"], + "model/gltf-binary": ["glb"], + "model/iges": ["iges", "igs"], + "model/mesh": ["silo", "mesh", "msh"], + "model/vnd.collada+xml": ["dae"], + "model/vnd.dwf": ["dwf"], + "model/vnd.gdl": ["gdl"], + "model/vnd.gtw": ["gtw"], + "model/vnd.mts": ["mts"], + "model/vnd.vtu": ["vtu"], + "model/vrml": ["vrml", "wrl"], + "model/x3d+binary": ["x3db", "x3dbz"], + "model/x3d+vrml": ["x3dvz", "x3dv"], + "model/x3d+xml": ["x3d", "x3dz"], + "text/cache-manifest": ["appcache", "manifest"], + "text/calendar": ["ics", "ifb"], + "text/coffeescript": ["litcoffee", "coffee"], + "text/css": ["css"], + "text/csv": ["csv"], + "text/hjson": ["hjson"], + "text/html": ["htm", "html", "shtml"], + "text/jade": ["jade"], + "text/jsx": ["jsx"], + "text/less": ["less"], + "text/markdown": ["md", "markdown"], + "text/mathml": ["mml"], + "text/n3": ["n3"], + "text/plain": ["text", "conf", "list", "txt", "in", "ini", "log", "def"], + "text/prs.lines.tag": ["dsc"], + "text/richtext": ["rtx"], + "text/rtf": ["rtf"], + "text/sgml": ["sgml", "sgm"], + "text/slim": ["slm", "slim"], + "text/stylus": ["stylus", "styl"], + "text/tab-separated-values": ["tsv"], + "text/troff": ["me", "man", "tr", "t", "ms", "roff"], + "text/turtle": ["ttl"], + "text/uri-list": ["uris", "uri", "urls"], + "text/vcard": ["vcard"], + "text/vnd.curl": ["curl"], + "text/vnd.curl.dcurl": ["dcurl"], + "text/vnd.curl.mcurl": ["mcurl"], + "text/vnd.curl.scurl": ["scurl"], + "text/vnd.dvb.subtitle": ["sub"], + "text/vnd.fly": ["fly"], + "text/vnd.fmi.flexstor": ["flx"], + "text/vnd.graphviz": ["gv"], + "text/vnd.in3d.3dml": ["3dml"], + "text/vnd.in3d.spot": ["spot"], + "text/vnd.sun.j2me.app-descriptor": ["jad"], + "text/vnd.wap.wml": ["wml"], + "text/vnd.wap.wmlscript": ["wmls"], + "text/vtt": ["vtt"], + "text/x-asm": ["s", "asm"], + "text/x-c": ["cc", "dic", "cpp", "h", "c", "cxx", "hh"], + "text/x-component": ["htc"], + "text/x-fortran": ["f77", "f90", "f", "for"], + "text/x-handlebars-template": ["hbs"], + "text/x-java-source": ["java"], + "text/x-lua": ["lua"], + "text/x-markdown": ["mkd"], + "text/x-nfo": ["nfo"], + "text/x-opml": ["opml"], + "text/x-org": ["org"], + "text/x-pascal": ["p", "pas"], + "text/x-processing": ["pde"], + "text/x-sass": ["sass"], + "text/x-scss": ["scss"], + "text/x-setext": ["etx"], + "text/x-sfv": ["sfv"], + "text/x-suse-ymp": ["ymp"], + "text/x-uuencode": ["uu"], + "text/x-vcalendar": ["vcs"], + "text/x-vcard": ["vcf"], + "text/xml": ["xml"], + "text/yaml": ["yaml", "yml"], + "video/3gpp": ["3gp", "3gpp"], + "video/3gpp2": ["3g2"], + "video/h261": ["h261"], + "video/h263": ["h263"], + "video/h264": ["h264"], + "video/jpeg": ["jpgv"], + "video/jpm": ["jpgm", "jpm"], + "video/mj2": ["mj2", "mjp2"], + "video/mp2t": ["ts"], + "video/mp4": ["mp4v", "mp4", "mpg4"], + "video/mpeg": ["m1v", "mpg", "mpeg", "m2v", "mpe"], + "video/ogg": ["ogv"], + "video/quicktime": ["qt", "mov"], + "video/vnd.dece.hd": ["uvh", "uvvh"], + "video/vnd.dece.mobile": ["uvvm", "uvm"], + "video/vnd.dece.pd": ["uvvp", "uvp"], + "video/vnd.dece.sd": ["uvs", "uvvs"], + "video/vnd.dece.video": ["uvvv", "uvv"], + "video/vnd.dvb.file": ["dvb"], + "video/vnd.fvt": ["fvt"], + "video/vnd.mpegurl": ["mxu", "m4u"], + "video/vnd.ms-playready.media.pyv": ["pyv"], + "video/vnd.uvvu.mp4": ["uvvu", "uvu"], + "video/vnd.vivo": ["viv"], + "video/webm": ["webm"], + "video/x-f4v": ["f4v"], + "video/x-fli": ["fli"], + "video/x-flv": ["flv"], + "video/x-m4v": ["m4v"], + "video/x-matroska": ["mks", "mkv", "mk3d"], + "video/x-mng": ["mng"], + "video/x-ms-asf": ["asf", "asx"], + "video/x-ms-vob": ["vob"], + "video/x-ms-wm": ["wm"], + "video/x-ms-wmv": ["wmv"], + "video/x-ms-wmx": ["wmx"], + "video/x-ms-wvx": ["wvx"], + "video/x-msvideo": ["avi"], + "video/x-sgi-movie": ["movie"], + "video/x-smv": ["smv"], + "x-conference/x-cooltalk": ["ice"], + "font/collection": ["ttc"], + "font/ttf": ["ttf"], + "font/woff": ["woff"], + "font/woff2": ["woff2"], + "text/javascript": ["mjs"] +} +''' + +HTML_MAIN = r''' + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeBaseNameLast ModifiedSize
Storage Usage:
+ +
+
+
+ +
+ +
+ +
+ + +
+
+
+ + + + ''' + + +STORAGE_PROTECTION = Enum('STORAGE_PROTECTION', ['READ_WRITE', 'READ_ONLY', 'SANDBOX']) +PERMISSION = Enum('PERMISSION', ['READ', 'WRITE']) +VALIDATION = Enum('VALIDATION', ['SUCCESS', 'FAILURE', 'NONCE_EXPIRED']) + + +class Settings: + @staticmethod + def parse_attachment_exts(text: str) -> Set[str]: + """ Parse a text to build a attachment extentions. + + :param text: A attachment extentions configuration + :return: A set of extentions. + """ + return set(ext if ext == '*' or ext.startswith('.') else f'.{ext}' + for ext in text.replace(',', ' ').split()) + + @staticmethod + def parse_mime_types(text: str) -> Dict[str, str]: + """ Parse a text to build a mime type mapping to extensions + + :param text: A mapping configuration + :return: A mapping table (extension (key) and mime type (value)). + """ + mapping = {} + try: + for mime_type, exts in json.loads(text).items(): + for ext in argToList(exts): + mapping[ext if ext.startswith('.') else f'.{ext}'] = mime_type + except json.decoder.JSONDecodeError: + for line in text.splitlines(): + if not (line := line.strip()).startswith('#'): + mime_type, _, exts = line.replace('\t', ' ').partition(' ') + if mime_type: + for ext in exts.rstrip(';').split(): + mapping[ext if ext.startswith('.') else f'.{ext}'] = mime_type + return mapping + + @staticmethod + def parse_human_size(size: str) -> Optional[int]: + """ Parse a human readable size string + + :return: Size in bytes + """ + if not (m := re.match(r'^(\d+(?:\.\d+)?)\s*([KMGT]?B)?$', size.upper())): + return None + + num, unit = m.groups() + UNITS = {None: 1, 'B': 1, 'KB': 2**10, 'MB': 2**20, 'GB': 2**30, 'TB': 2**40} + return int(float(num) * UNITS[unit]) + + def __init__(self, params: Dict[str, Any]): + max_storage_size_str = params.get('maxStorageSize') or '100 MB' + if (max_storage_size := Settings.parse_human_size(max_storage_size_str)) is None: + raise DemistoException('Invalid max storage size') + + max_sandbox_size_str = params.get('maxSandboxSize') or '1 GB' + if (max_sandbox_size := Settings.parse_human_size(max_sandbox_size_str)) is None: + raise DemistoException('Invalid max sandbox repository size') + + self.__max_storage_size = max_storage_size + self.__max_sandbox_size = max_sandbox_size + self.__attachment_exts = Settings.parse_attachment_exts(params.get('attachmentExtensions') or '') + if argToBoolean(params.get('mergeMimeTypes', 'true')): + self.__ext_to_mimetype = Settings.parse_mime_types(DEFAULT_MIME_TYPES) + self.__ext_to_mimetype.update(Settings.parse_mime_types(params.get('mimeTypes') or '{}')) + else: + self.__ext_to_mimetype = Settings.parse_mime_types(params.get('mimeTypes') or DEFAULT_MIME_TYPES) + + self.__public_read_access = argToBoolean(params.get('publicReadAccess', 'true')) + + storage_protection = params.get('storageProtection') or 'read/write' + self.__storage_protection = {'read/write': STORAGE_PROTECTION.READ_WRITE, + 'read-only': STORAGE_PROTECTION.READ_ONLY, + 'sandbox': STORAGE_PROTECTION.SANDBOX, + }.get(storage_protection) + if self.__storage_protection is None: + raise DemistoException(f'Invalid storage protection mode: {storage_protection}') + + host_port, _, docker_port = (params.get('longRunningPort') or '').partition(':') + self.__host_port = int(host_port or docker_port or 0) + self.__docker_port = int(docker_port or host_port or 0) + self.__auth_method = params.get('authenticationMethod') + + creds = params.get('rwCredentials') or {} + self.__rw_username = creds.get('identifier') or '' + self.__rw_password = creds.get('password') or '' + + creds = params.get('roCredentials') or {} + self.__ro_username = creds.get('identifier') or '' + self.__ro_password = creds.get('password') or '' + + def get_user_password(self, username: Optional[str]) -> Optional[str]: + if username == self.__rw_username: + return self.__rw_password + elif username == self.__ro_username: + return self.__ro_password + else: + return None + + def get_user_permissions(self, username: Optional[str]) -> Set[PERMISSION]: + if username == self.__rw_username: + return set({PERMISSION.READ, PERMISSION.WRITE}) + elif username == self.__ro_username: + return set({PERMISSION.READ}) + else: + return set() + + def get_content_type_from_file_extension(self, ext: str) -> str: + if content_type := self.__ext_to_mimetype.get(ext): + return content_type + else: + for pattern, content_type in self.__ext_to_mimetype.items(): + if fnmatch.fnmatch(ext, pattern): + return content_type + return 'application/octet-stream' + + def is_attachment_file_extension(self, ext: str) -> bool: + return ext in self.__attachment_exts or \ + any(fnmatch.fnmatch(ext, pattern) for pattern in self.__attachment_exts) + + @property + def host_port(self) -> int: + return self.__host_port + + @property + def docker_port(self) -> int: + return self.__docker_port + + @property + def attachment_exts(self) -> Set[str]: + return self.__attachment_exts + + @property + def ext_to_mimetype(self) -> Dict[str, str]: + return self.__ext_to_mimetype + + @property + def max_storage_size(self) -> int: + return self.__max_storage_size + + @property + def max_sandbox_size(self) -> int: + return self.__max_sandbox_size + + @property + def public_read_access(self) -> bool: + return self.__public_read_access + + @property + def storage_protection(self) -> STORAGE_PROTECTION: + return self.__storage_protection # type: ignore + + @property + def auth_method(self) -> Optional[str]: + return self.__auth_method + + @property + def rw_user_credentials(self) -> Tuple[str, str]: + return self.__rw_username, self.__rw_password + + @property + def ro_user_credentials(self) -> Tuple[str, str]: + return self.__ro_username, self.__ro_password + + +SETTINGS = Settings(demisto.params()) + + +def get_default_gateway() -> Optional[str]: + """ Get a default gateway address. + + :return: A default gateway address found. + """ + with open('/proc/net/route') as f: + for line in f: + fields = line.strip().split() + if fields[1] != '00000000' or not int(fields[3], 16) & 2: + continue + return socket.inet_ntoa(struct.pack(' str: + """ Get an external IP address. + NOTE: https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib + + :return: An external IP address found. + """ + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: + s.settimeout(0) + try: + # doesn't even have to be reachable + s.connect(('10.254.254.254', 1)) + ip = s.getsockname()[0] + except Exception: + ip = '127.0.0.1' + return ip + + +def detect_service_ip_port(settings: Settings) -> Tuple[str, int]: + """ Detect the IP:port of the local server + + :param settings: The instance settings. + :return: The IP and port number. + """ + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(1) + server_addr = '127.0.0.1' + server_port = settings.docker_port + s.connect((server_addr, server_port)) + except Exception: + server_addr = get_default_gateway() # type: ignore + server_port = settings.host_port + if not server_addr: + raise DemistoException('Unable to get any server addresses.') + return server_addr, server_port + + +def new_client(host_port: Tuple[str, int], settings: Settings) -> BaseClient: + """ Create a new BasicClient + + :param host_port: The IP and port number + :param settings: The instance settings. + :return: A new BasicClient created. + """ + server_addr, server_port = host_port + auth: Optional[Union[HTTPBasicAuth, HTTPDigestAuth]] = None + + username, password = settings.rw_user_credentials + if settings.auth_method in ('', None): + pass + elif settings.auth_method == 'Basic': + auth = HTTPBasicAuth(username, password) + elif settings.auth_method in ('Digest-md5', 'Digest-sha256'): + auth = HTTPDigestAuth(username, password) + else: + raise DemistoException(f'Unknown authentication method - {settings.auth_method}') + + return BaseClient(f'http://{server_addr}:{server_port}', auth=auth) + + +def to_abs_path(path: str) -> str: + return path if path.startswith(os.sep) else os.sep + path + + +def pretty_size(size: int) -> str: + units = ['bytes', 'KB', 'MB', 'GB', 'TB'] + i = min(int(math.log(size or 1, 1024)), len(units) - 1) + return f'{size / 1024 ** i:.{max(min(1, i), 0)}f} {units[i]}' + + +class NonceManager: + def __init__(self): + self.__cache: Dict[str, Dict[str, Any]] = {} + self.__expires = 10 + self.__max_replays = 20 + self.__max_nonce = 4096 + + def __remove_expired_oldest(self, now: Optional[int] = None) -> bool: + """ Remove the expired oldest nonce from the cache + + :param now: The current timestamp + :return: Return True if the expired oldest nonce has been removed. Otherwise False. + """ + if now is None: + now = int(datetime.now().timestamp()) + + oldest_time = 0 + oldest_nonce = None + for nonce, ent in self.__cache.items(): + last_time = ent.get('last_time') or 0 + if last_time + self.__expires < now and last_time < oldest_time: + oldest_time = last_time + oldest_nonce = nonce + + if oldest_nonce is not None: + self.__cache.pop(oldest_nonce, None) + return True + else: + return False + + def __new_nonce(self) -> Tuple[int, str]: + """ Create a new nonce + + :return: The current timestamp and a new nonce. + """ + now = int(datetime.now().timestamp()) + nonce = str(now) + ':' + os.urandom(16).hex() + return now, nonce + + def validate_nonce(self, nonce: str) -> VALIDATION: + """ Check if the nonce is valid. + + :param nonce: A nonce to validate. + :return: Return the result of validation. + """ + if (ent := self.__cache.get(nonce)) is None: + return VALIDATION.FAILURE + + now = int(datetime.now().timestamp()) + if (ent.get('last_time') or 0) + self.__expires < now: + return VALIDATION.NONCE_EXPIRED + + if (count := (ent.get('count') or 0) + 1) >= self.__max_replays: + return VALIDATION.NONCE_EXPIRED + + self.__cache[nonce] = { + 'last_time': now, + 'count': count + } + return VALIDATION.SUCCESS + + def gen_nonce(self) -> str: + """ Generate a new nonce + + :return: A new nonce generated. + """ + now, nonce = self.__new_nonce() + if len(self.__cache) >= self.__max_nonce and \ + not self.__remove_expired_oldest(now): + raise DemistoException('Unable to generate a new nonce (too many active nonces).') + + self.__cache[nonce] = { + 'last_time': now, + 'count': 0 + } + return nonce + + +NONCE_MANAGER = NonceManager() + + +class Master: + def __init__(self, storage_protection: STORAGE_PROTECTION): + """ Initialize the master DB manager + + :param storage_protection: The storage protection mode + """ + self.__repo: Optional[Dict[str, str]] = None + self.__storage_protection = storage_protection + self.__total_data_usage = None + + @property + def storage_protection(self) -> STORAGE_PROTECTION: + return self.__storage_protection + + def reset(self) -> None: + """ Wipe the repository on the normal mode, and Restore the repository on the sandbox mode. + """ + self.__repo = None + if self.storage_protection == STORAGE_PROTECTION.READ_WRITE: + set_integration_context({}) + + def get_full_repository(self) -> Dict[str, str]: + """ Get the full context data from the integration context + + :return: The integration context. + """ + if self.storage_protection == STORAGE_PROTECTION.SANDBOX and self.__repo is not None: + return self.__repo + + ctx = get_integration_context() + if self.storage_protection == STORAGE_PROTECTION.SANDBOX: + self.__repo = ctx + elif self.__repo is None: + self.__repo = {k: v for k, v in ctx.items() if k.startswith(os.sep)} + + return ctx + + def get_attrs_repository(self) -> Dict[str, str]: + """ Get the file entries without payloads from the integration context. + + :return: The integration context without file payloads. + (File payload can be included in the copy-on-write mode) + """ + if self.__repo is not None: + return self.__repo + else: + return self.get_full_repository() + + def set_full_repository(self, repo: Dict[str, str]) -> None: + """ Set the full context data to the integration context. + + :param repo: The integration context. + """ + if self.storage_protection == STORAGE_PROTECTION.SANDBOX: + self.__repo = repo + elif self.storage_protection == STORAGE_PROTECTION.READ_WRITE: + self.__repo = {k: v for k, v in repo.items() if k.startswith(os.sep)} + set_integration_context(repo) + + def total_data_usage(self) -> Tuple[int, int]: + """ Get the data usage + + :return: The sum of all the saved sizes in the DB / on the file system. + """ + if self.__repo is not None and self.__total_data_usage is None: + repo = get_integration_context() + else: + repo = self.get_attrs_repository() + + data_usage = 0 + file_usage = 0 + for k, v in repo.items(): + if k.startswith(os.sep): + attrs = json.loads(v) + if attrs.get('data-type') == 'gzip-file': + file_usage += int(attrs.get('saved-size') or 0) + else: + data_usage += int(attrs.get('saved-size') or 0) + + if self.__total_data_usage is None or \ + self.storage_protection != STORAGE_PROTECTION.SANDBOX: + self.__total_data_usage = data_usage # type: ignore + return data_usage, file_usage + else: + return self.__total_data_usage, file_usage + + +MASTER_REPOSITORY = Master(storage_protection=SETTINGS.storage_protection) + + +class AttrsRepository: + def __init__(self, repo: Dict[str, str]): + self.repo = repo + + @staticmethod + def __split_path_components(abs_path: str) -> List[str]: + comps = [] + path = os.path.normpath(to_abs_path(abs_path)) + while path: + parent, name = os.path.split(path) + comps.append(name or parent) + if parent == path: + break + path = parent + return list(reversed(comps[:-1])) + + def is_file_type(self, data_type: Optional[str]) -> bool: + return data_type == 'gzip-file' + + def list_file_entries(self, + abs_dir: str, + recursive: bool = False) -> Dict[str, Dict[str, Any]]: + """ List the file entries on a directory + + :param abs_dir: The directory path in absolute path on which to list file entries + :param recursive: Set to True to list entries recursively, otherwise False. + :return: The file path and attributes. + """ + abs_path = os.path.normpath(abs_dir) + sub_path = abs_path if abs_path == os.sep else abs_path + os.sep + + ents = {} + for path, eattrs in self.repo.items(): + if path.startswith(sub_path): + comps = self.__split_path_components(path[len(abs_path):]) + if len(comps) >= 1 and comps[0] not in ents: + if recursive or len(comps) == 1: + attrs = json.loads(eattrs) + ents[path] = { + 'type': 'F', + 'name': comps[-1], + 'path': path, + 'base': abs_path, + 'size': attrs.get('size') or 0, + 'last-modified': int(attrs.get('last-modified') or 0) + } + + if len(comps) > 1: + path = os.path.join(abs_path, comps[0]) + ents[path] = { + 'type': 'D', + 'name': comps[0], + 'path': path, + 'base': abs_path, + 'size': None, + 'last-modified': None + } + if abs_path != os.sep: + path = os.path.split(abs_path)[0] + ents[path] = { + 'type': 'P', + 'name': '..', + 'path': path, + 'base': abs_path, + 'size': None, + 'last-modified': None + } + return ents + + +class FullRepository(AttrsRepository): + @staticmethod + def new_decoder(data_type: Optional[str], data: str) -> Generator[bytes, None, None]: + """ Decode a file content in chunks + + :param data_type: The encoding mode of the payload. + :param data: The data to decode + """ + if data_type is None: + yield data.encode() + elif data_type == 'base85': + yield base64.b85decode(data) + elif data_type == 'gzip+base85': + with gzip.GzipFile(mode='rb', fileobj=io.BytesIO(base64.b85decode(data))) as g: + while chunk := g.read(4096): + yield chunk + else: + raise DemistoException(f'Unknown data type: {data_type}') + + @staticmethod + def new_reader(data_type: Optional[str], path: str) -> Generator[bytes, None, None]: + """ Read a file content in chunks + + :param data_type: The file type. + :param path: The file path. + """ + if data_type == 'gzip-file': + with gzip.GzipFile(filename=path, mode='rb') as g: + while chunk := g.read(4096): + yield chunk + else: + raise DemistoException(f'Unknown data type: {data_type}') + + def __init__(self, repo: Master, data_usage_limit: int = 0, file_usage_limit: int = 0): + """ Initialize the instance. + + :param repo: The repository + :param data_usage_limit: The maximum size of the total data usage of the repository. + :param file_usage_limit: The maximum size of the total file usage of the filesystem. + """ + super().__init__(repo.get_full_repository()) + self.__master = repo + self.__data_usage_limit = data_usage_limit + self.__file_usage_limit = file_usage_limit + self.__total_data_usage, self.__total_file_usage = repo.total_data_usage() + + def remove_orphan_entries(self) -> None: + """ Remove unlinked data entries + """ + repo = self.repo + keep_uuids = set() + data_uuids = set() + for key, eattrs in repo.items(): + if key.startswith(os.sep): + attrs = json.loads(eattrs) + if not self.is_file_type(attrs.get('data-type')): + keep_uuids.add(attrs.get('data-id')) + else: + data_uuids.add(key) + + for data_uuid in (data_uuids - keep_uuids): + repo.pop(data_uuid, None) + + def remove_entry(self, abs_path: str) -> None: + """ Remove the file/directory entry + + :param abs_path: The path in absolute path + """ + abs_path = os.path.normpath(abs_path) + sub_path = abs_path if abs_path == os.sep else abs_path + os.sep + + # Remove file entries under the directory + repo = self.repo + for path in [path for path in repo.keys() if path.startswith(sub_path)]: + attrs = json.loads(repo.pop(path, '{}')) + if not self.is_file_type(attrs.get('data-type')): + data = repo.pop((attrs.get('data-id') or ''), None) + self.__total_data_usage -= len(data or '') + else: + if path := attrs.get('data-id'): + if os.path.isfile(path): + os.unlink(path) + self.__total_file_usage -= attrs.get('saved-size') or 0 + + # Remove the file entry + attrs = json.loads(repo.pop(abs_path, '{}')) + if not self.is_file_type(attrs.get('data-type')): + data = repo.pop((attrs.get('data-id') or ''), None) + self.__total_data_usage -= len(data or '') + else: + if path := attrs.get('data-id'): + if os.path.isfile(path): + os.unlink(path) + self.__total_file_usage -= attrs.get('saved-size') or 0 + + def save_file(self, abs_dir: str, name: str, data: IO[bytes]) -> Dict[str, Any]: + """ Save a file + + :param abs_dir: The directory path in absolute path + :param name: The name of the file + :param data: The payload of the file + :return: The file attributes. + """ + repo = self.repo + abs_path = os.path.normpath(os.path.join(abs_dir, os.path.relpath(to_abs_path(name), os.sep))) + + # Remove conflict files in the directory hierarchy + path, name = os.path.split(abs_path) + while path and path != os.sep: + if eattrs := repo.pop(path, None): + attrs = json.loads(eattrs) + if not self.is_file_type(attrs.get('data-type')): + repo.pop((attrs.get('data-id') or ''), None) + path, name = os.path.split(path) + + # Compress and encode the data + sandbox = self.__master.storage_protection == STORAGE_PROTECTION.SANDBOX + try: + data_size = 0 + with NamedTemporaryFile(delete=not sandbox) as gtmp: + with gzip.GzipFile(mode='wb', fileobj=gtmp) as g: + while chunk := data.read(4096): + g.write(chunk) + data_size += len(chunk) + + gtmp.flush() + if not sandbox: + if data_size > gtmp.tell(): + gtmp.seek(0) + out_data = base64.b85encode(gtmp.read()).decode() + data_type = 'gzip+base85' + else: + data.seek(0) + out_data = base64.b85encode(data.read()).decode() + data_type = 'base85' + + saved_size = len(out_data) + + # Check the limit + if self.__data_usage_limit > 0 and\ + self.__data_usage_limit < self.__total_data_usage + saved_size: + raise DemistoException('Repository data size limit exceeded') + + data_id = str(uuid.uuid4()) + else: + data_type = 'gzip-file' + data_id = gtmp.name + + # Check the limit + saved_size = gtmp.tell() + if self.__file_usage_limit > 0 and\ + self.__file_usage_limit < self.__total_file_usage + saved_size: + raise DemistoException('Sandbox data size limit exceeded') + + # Save the file + attr = { + 'last-modified': int(datetime.now(timezone.utc).timestamp()), + 'data-id': data_id, + 'data-type': data_type, + 'size': data_size, + 'saved-size': saved_size, + } + repo[abs_path] = json.dumps(attr) + if not sandbox: + repo[data_id] = out_data + self.__total_data_usage += saved_size + else: + self.__total_file_usage += saved_size + + return attr + except Exception: + if sandbox: + os.unlink(gtmp.name) + raise + + def save_files(self, abs_dir: str, files: Dict[str, IO[bytes]], extract: bool) -> None: + """ Save files + + :param abs_dir: The directory path in absolute path + :param files: The name and payload of files + :param extract: Set to True to extract archives if needed, otherwise False. + """ + for name, file in files.items(): + if extract: + lowername = name.lower() + if lowername.endswith('.zip') and zipfile.is_zipfile(file): + with zipfile.ZipFile(file, 'r') as z: + for filename in [i.filename for i in z.infolist()]: + with z.open(filename) as zd: + self.save_file(abs_dir, filename, zd) + + elif lowername.endswith('.tar') or \ + lowername.endswith('.tar.gz') or\ + lowername.endswith('.tar.bz2') or \ + lowername.endswith('.tar.xz'): + with tarfile.open(mode='r:*', fileobj=file) as t: + for tinfo in t: + if tinfo.isfile() and ((td := t.extractfile(tinfo)) is not None): + self.save_file(abs_dir, tinfo.name, td) + else: + self.save_file(abs_dir, name, file) + else: + self.save_file(abs_dir, name, file) + + def read_file(self, abs_path: str) -> Tuple[Dict[str, Any], + Optional[Generator[bytes, None, None]]]: + """ Read a file content with its attributes + + :param abs_path: The file path + :return: Attributes of the file and the payload reader. + """ + repo = self.repo + if eattrs := repo.get(os.path.normpath(abs_path)): + attrs: Dict[str, Any] = json.loads(eattrs) + if data_id := attrs.get('data-id'): + attrs['name'] = os.path.basename(abs_path) + attrs['path'] = abs_path + data_type = attrs.get('data-type') + if self.is_file_type(data_type): + return attrs, FullRepository.new_reader(data_type, data_id) + elif data := repo.get(data_id): + return attrs, FullRepository.new_decoder(data_type, data) + return {}, None + + def archive_zip(self) -> Generator[bytes, None, None]: + """ Build a zip stream in chunks by archiving all the files + """ + repo = self.repo + with NamedTemporaryFile() as ztmp: + with zipfile.ZipFile(ztmp, 'w', compression=zipfile.ZIP_DEFLATED) as z: + for path, eattrs in repo.items(): + if path.startswith(os.sep): + attrs = json.loads(eattrs) + if data_id := attrs.get('data-id'): + data_type = attrs.get('data-type') + if self.is_file_type(data_type): + # Add a file to the zip + with NamedTemporaryFile() as tf: + for chunk in FullRepository.new_reader(data_type, data_id): + tf.write(chunk) + tf.flush() + z.write(tf.name, path[len(os.sep):]) + elif data := repo.get(data_id): + # Add a file to the zip + with NamedTemporaryFile() as tf: + for chunk in FullRepository.new_decoder(data_type, data): + tf.write(chunk) + tf.flush() + z.write(tf.name, path[len(os.sep):]) + + ztmp.flush() + ztmp.seek(0) + while chunk := ztmp.read(4096): + yield chunk + + def commit(self): + """ Write the cache modified by transactions to the master + + Note: In the copy-on-write mode, the master is not modified. + """ + self.__master.set_full_repository(self.repo) + + +@bottle.error(404) +def error404(error): + return ''' + + + + +Not Found + + +

Not Found

+

The requested URL was not found on this server.

+ + + ''' + + +class ServiceHandler: + def __init__(self, settings: Settings, master: Master): + self.__settings = settings + self.__master = master + + def __validate_basic_auth(self, auth_value) -> Set[PERMISSION]: + """ Checks whether the authentication is valid + + :param auth_value: Credentials given to the Authentication header + :return: Aquired permissions. + """ + username, sep, password = base64.b64decode(auth_value.encode()).decode().partition(':') + if sep == ':' and self.__settings.get_user_password(username) == password: + return self.__settings.get_user_permissions(username) + return set() + + def __validate_digest_auth(self, + auth_value: str, + request_method: str, + realm: str, + hash_name: Tuple[str, str]) -> Tuple[VALIDATION, Set[PERMISSION]]: + """ Checks whether the authentication is valid + + :param auth_value: Credentials given to the Authentication header + :param request_method: The request method + :param real: The realm + :param hash_name: The digest algorithm name for the hashlib and the prorocol. + :return: The result of the validation and the aquired permissions. + """ + params = urllib.request.parse_keqv_list(urllib.request.parse_http_list(auth_value)) + username = params.get('username') + if (password := self.__settings.get_user_password(username)) is None: + return VALIDATION.FAILURE, set() + + username = username or '' + hhash_name, phash_name = hash_name + if params.get('algorithm', 'MD5').upper() != phash_name or\ + not (qnonce := params.get('nonce')) or\ + not (quri := params.get('uri')) or\ + not (qresponse := params.get('response')) or\ + not (qcnonce := params.get('cnonce')) or\ + not (qnc := params.get('nc')): + return VALIDATION.FAILURE, set() + + if (result := NONCE_MANAGER.validate_nonce(qnonce)) != VALIDATION.SUCCESS: + return result, set() + + a1 = hashlib.new(hhash_name, username.encode() + b':' + realm.encode() + b':' + password.encode()).hexdigest() + a2 = hashlib.new(hhash_name, f'{request_method}:{quri}'.encode()).hexdigest() + oresponse = hashlib.new(hhash_name, f'{a1}:{qnonce}:{qnc}:{qcnonce}:auth:{a2}'.encode()).hexdigest() + if qresponse == oresponse: + return VALIDATION.SUCCESS, self.__settings.get_user_permissions(username) + else: + return VALIDATION.FAILURE, set() + + def authenticate(self, request: BaseRequest, permission: PERMISSION) -> Optional[HTTPResponse]: + """ Authenticate user to the required permission + + :param request: The request data + :param permission: The required permission + :return: Return None if the authentication has been successfully done, + otherwise a HTTP response to request an authentication. + """ + required_auth_method = self.__settings.auth_method + + if permission == PERMISSION.READ and self.__settings.public_read_access: + # Authentication is not required + return None + + if not required_auth_method: + # Authentication is not required + return None + + realm = 'protected area' + auth_header = request.get_header('Authorization') or '' + qauth_method, _, qauth_value = auth_header.partition(' ') + + response = HTTPResponse() + response.status = 401 + + if required_auth_method == 'Basic': + """ + Basic Auth + """ + if qauth_method == 'Basic' and \ + permission in self.__validate_basic_auth(qauth_value): + return None + + response.set_header('WWW-Authenticate', f'Basic realm="{realm}"') + elif required_auth_method in ('Digest-md5', 'Digest-sha256'): + """ + Digest Auth + """ + _, _, hhash_name = required_auth_method.partition('-') + rhash_name = { + 'md5': 'MD5', + 'sha256': 'SHA-256', + }[hhash_name] + + result = VALIDATION.FAILURE + if qauth_method == 'Digest': + result, permissions = self.__validate_digest_auth(qauth_value, + request.method, + realm, + (hhash_name, rhash_name)) + if result == VALIDATION.SUCCESS and permission in permissions: + return None + + nonce = NONCE_MANAGER.gen_nonce() + auth_value = f'Digest realm="{realm}", nonce="{nonce}", algorithm={rhash_name}, qop=auth' + if result == VALIDATION.NONCE_EXPIRED: + auth_value += ', stale=true' + response.set_header('WWW-Authenticate', auth_value) + + return response + + def __handle_get_resource(self, request: BaseRequest) -> HTTPResponse: + global RESOURCES_ZIP + if RESOURCES_ZIP is None: + RESOURCES_ZIP = zipfile.ZipFile(io.BytesIO(base64.b64decode(RESOURCES_ZIP_B64)), 'r') + + if request.query.name not in RESOURCES_ZIP.namelist(): + bottle.abort(404, 'go to 404') + + _, ext = os.path.splitext(request.query.name) + + response = HTTPResponse() + response.content_type = { + '.css': 'text/css', + '.js': 'text/javascript', + '.png': 'image/png' + }.get(ext, 'application/octet-stream') + + response.body = RESOURCES_ZIP.read(request.query.name) + return response + + def __handle_get_status(self) -> HTTPResponse: + data_usage, file_usage = self.__master.total_data_usage() + + storage_protection = { + STORAGE_PROTECTION.READ_WRITE: 'read/write', + STORAGE_PROTECTION.READ_ONLY: 'read-only', + STORAGE_PROTECTION.SANDBOX: 'sandbox', + }.get(self.__settings.storage_protection) + + resp = { + 'storage_protection': storage_protection, + 'max_storage_size': self.__settings.max_storage_size, + 'max_sandbox_size': self.__settings.max_sandbox_size, + 'storage_usage': data_usage, + 'sandbox_usage': file_usage, + 'server_ip': get_local_ip(), + 'server_port': self.__settings.docker_port + } + response = HTTPResponse() + response.content_type = 'application/json' + response.body = resp + return response + + def __handle_get_ls(self, request: BaseRequest) -> HTTPResponse: + dirpath = to_abs_path(request.query.dir) + recursive = argToBoolean(request.query.recursive or 'false') + repo = AttrsRepository(self.__master.get_attrs_repository()) + + response = HTTPResponse() + response.content_type = 'application/json' + response.body = { + 'data': list(repo.list_file_entries(dirpath, recursive).values()) + } + return response + + def __handle_get_download(self, request: BaseRequest) -> HTTPResponse: + path = to_abs_path(request.query.path) + attrs, chunks = FullRepository(self.__master).read_file(to_abs_path(path)) + if chunks is None: + bottle.abort(404, 'go to 404') + + encoded_name = urllib.parse.quote(attrs.get('name') or 'file.dat', encoding='utf-8') + + response = HTTPResponse() + response.content_type = 'application/octet-stream' + response.set_header('Content-Disposition', f'attachment; filename*=utf-8\'\'{encoded_name}') + response.body = chunks + return response + + def __handle_get_archive_all(self) -> HTTPResponse: + filename = datetime.now(timezone.utc).strftime('archive-%Y%m%d%H%M%S.zip') + + response = HTTPResponse() + response.content_type = 'application/zip' + response.set_header('Content-Disposition', f'attachment; filename="{filename}"') + response.body = FullRepository(self.__master).archive_zip() + return response + + def __handle_post_health(self, request: BaseRequest) -> Optional[HTTPResponse]: + if permission := request.json.get('permission'): + return self.authenticate( + request, + PERMISSION.WRITE if permission == 'write' else PERMISSION.READ) + return None + + def __handle_post_cleanup(self) -> None: + if self.__master.storage_protection == STORAGE_PROTECTION.READ_ONLY: + raise DemistoException('The storage is read-only mode.') + + self.__master.set_full_repository({}) + + def __handle_post_reset(self) -> None: + if self.__master.storage_protection == STORAGE_PROTECTION.READ_ONLY: + raise DemistoException('The storage is read-only mode.') + + self.__master.reset() + + def __handle_post_delete(self, request: BaseRequest) -> None: + if self.__master.storage_protection == STORAGE_PROTECTION.READ_ONLY: + raise DemistoException('The storage is read-only mode.') + + if paths := request.json.get('path'): + repo = FullRepository(self.__master) + for path in paths: + repo.remove_entry(to_abs_path(path)) + repo.remove_orphan_entries() + repo.commit() + + def __handle_post_upload(self, request: BaseRequest) -> None: + if self.__master.storage_protection == STORAGE_PROTECTION.READ_ONLY: + raise DemistoException('The storage is read-only mode.') + + if (files := list(request.files.getall('file'))): + repo = FullRepository(self.__master, + self.__settings.max_storage_size, + self.__settings.max_sandbox_size) + # request.forms.dir doesn't give a correct unicode string + dir_path = to_abs_path((request.forms.getall('dir')[0:1] or [''])[0]) + entries = {file.raw_filename: file.file for file in files} + extract = argToBoolean(request.forms.extract or 'false') + repo.save_files(dir_path, entries, extract) + repo.remove_orphan_entries() + repo.commit() + + def get(self, request: BaseRequest) -> HTTPResponse: + if response := self.authenticate(request, PERMISSION.READ): + return response + + if request.query.q == 'resource': + return self.__handle_get_resource(request) + + elif request.query.q == 'status': + return self.__handle_get_status() + + elif request.query.q == 'ls': + return self.__handle_get_ls(request) + + elif request.query.q == 'download': + return self.__handle_get_download(request) + + elif request.query.q == 'archive-all': + return self.__handle_get_archive_all() + else: + response = HTTPResponse() + response.content_type = 'text/html' + response.body = HTML_MAIN + return response + + def get_file(self, request: BaseRequest, path: str) -> HTTPResponse: + if response := self.authenticate(request, PERMISSION.READ): + return response + + attrs, chunks = FullRepository(self.__master).read_file(to_abs_path(path)) + if chunks is None: + bottle.abort(404, 'go to 404') + + filename = attrs.get('name') or '' + _, ext = os.path.splitext(filename) + + response = HTTPResponse() + response.content_type = self.__settings.get_content_type_from_file_extension(ext) + if self.__settings.is_attachment_file_extension(ext): + encoded_name = urllib.parse.quote(filename, encoding='utf-8') + response.set_header('Content-Disposition', f'attachment; filename*=utf-8\'\'{encoded_name}') + + response.body = chunks + return response + + def post(self, request: BaseRequest) -> HTTPResponse: + response = HTTPResponse() + response.content_type = 'application/json' + try: + if request.content_type == 'application/json': + q = isinstance(request.json, dict) and request.json.get('q') + if q == 'health': + if resp := self.__handle_post_health(request): + return resp + elif q == 'cleanup': + if resp := self.authenticate(request, PERMISSION.WRITE): + return resp + self.__handle_post_cleanup() + elif q == 'reset': + if resp := self.authenticate(request, PERMISSION.WRITE): + return resp + self.__handle_post_reset() + elif q == 'delete': + if resp := self.authenticate(request, PERMISSION.WRITE): + return resp + self.__handle_post_delete(request) + else: + raise DemistoException('Unknown request') + else: + if request.forms.q == 'upload': + if resp := self.authenticate(request, PERMISSION.WRITE): + return resp + self.__handle_post_upload(request) + else: + raise DemistoException('Unknown request') + + response.body = {'success': True} + except Exception as e: + response.body = {'success': False, 'message': str(e)} + return response + + +@bottle.route('/', method='GET') +def process_download_file(path): + handler = ServiceHandler(settings=SETTINGS, master=MASTER_REPOSITORY) + return handler.get_file(bottle.request, path) + + +@bottle.route('/', method='POST') +def process_root_post(): + handler = ServiceHandler(settings=SETTINGS, master=MASTER_REPOSITORY) + return handler.post(bottle.request) + + +@bottle.route('/', method='GET') +def process_root_get(): + handler = ServiceHandler(settings=SETTINGS, master=MASTER_REPOSITORY) + return handler.get(bottle.request) + + +def run_long_running(settings: Settings, is_test: bool = False): + if not is_test: + bottle.run(host='0.0.0.0', port=settings.docker_port, debug=True) + + +def test_module(args: Dict[str, str], settings: Settings) -> str: + """ + Validates: + """ + run_long_running(settings, is_test=True) + return 'ok' + + +def command_status(args: Dict[str, str], settings: Settings) -> CommandResults: + """ Get the service status + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + server_addr, server_port = detect_service_ip_port(settings) + external_ip = get_default_gateway() or server_addr + client = new_client((server_addr, server_port), settings) + resp = client._http_request('GET', params={'q': 'status'}, raise_on_status=True) + + outputs = assign_params( + StorageUsage=resp.get('storage_usage'), + SandboxUsage=resp.get('sandbox_usage'), + StorageProtection=resp.get('storage_protection'), + IntercommunicationIP=server_addr, + IntercommunicationPort=server_port, + ExternalIP=external_ip, + ExternalPort=settings.host_port, + ServerIP=resp.get('server_ip'), + ServerPort=resp.get('server_port'), + ) + + readable_outputs = { + 'Storage Usage': pretty_size(resp.get('storage_usage') or 0), + 'Sandbox Usage': pretty_size(resp.get('sandbox_usage') or 0), + 'Storage Protection': resp.get('storage_protection'), + 'Intercommunication IP': server_addr, + 'Intercommunication Port': server_port, + 'External IP': external_ip, + 'External Port': settings.host_port, + 'Server IP': resp.get('server_ip'), + 'Server Port': resp.get('server_port'), + } + + return CommandResults( + outputs_prefix='WebFileRepository.Status', + outputs=outputs, + readable_output=tblToMd('Service Status', readable_outputs, headers=readable_outputs.keys()), + raw_response=outputs) + + +def command_cleanup(args: Dict[str, str], settings: Settings) -> str: + """ Remove all the files from the repository + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + client = new_client(detect_service_ip_port(settings), settings) + resp = client._http_request('POST', + json_data={'q': 'cleanup'}, + raise_on_status=True) + if not resp.get('success'): + raise ValueError(f'Failed to clean up entries: {resp.get("message")}') + return 'Done.' + + +def command_reset(args: Dict[str, str], settings: Settings) -> str: + """ Reset the repostiory data + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + client = new_client(detect_service_ip_port(settings), settings) + resp = client._http_request('POST', + json_data={'q': 'reset'}, + raise_on_status=True) + if not resp.get('success'): + raise ValueError(f'Failed to reset the repository: {resp.get("message")}') + return 'Done.' + + +def command_upload_files(args: Dict[str, str], settings: Settings) -> str: + """ Upload files + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + client = new_client(detect_service_ip_port(settings), settings) + + files = [] + for ent_id in argToList(args.get('entry_ids', [])): + res = demisto.getFilePath(ent_id) + path = res['path'] + name = res['name'] + with open(path, 'rb') as f: + files.append(('file', [name, f.read()])) + + data = { + 'q': 'upload', + 'dir': args.get('upload_directory', '/'), + 'extract': 'true' if argToBoolean(args.get('extract_archive', 'false')) else 'false', + } + resp = client._http_request('POST', data=data, files=files, raise_on_status=True) + if not resp.get('success'): + raise ValueError(f'Failed to upload files: {resp.get("message")}') + return 'Done.' + + +def command_list_files(args: Dict[str, str], settings: Settings) -> CommandResults: + """ List file entries in the repository + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + class __MappingValue: + def __init__(self, + readable_value: Callable[[Any], Any], + context_value: Callable[[Any], Any]): + self.readable_value = readable_value + self.context_value = context_value + + query_params = { + 'q': 'ls', + 'dir': args.get('directory', '/'), + 'recursive': 'true' if argToBoolean(args.get('recursive', 'false')) else 'false' + } + client = new_client(detect_service_ip_port(settings), settings) + resp = client._http_request('GET', params=query_params, raise_on_status=True) + ents = resp.get('data') + if not isinstance(ents, list): + raise ValueError('Failed to list file entries') + + file_ents = sorted([ent for ent in ents if ent.get('type') == 'F'], key=lambda x: x['path']) + + mapping = { + 'name': __MappingValue(lambda x: x or '', lambda x: x or ''), + 'path': __MappingValue(lambda x: x or '', lambda x: x or ''), + 'size': __MappingValue(lambda x: pretty_size(x or 0), lambda x: x or 0), + 'last-modified': __MappingValue( + lambda x: datetime.fromtimestamp(int(x or 0), timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC'), + lambda x: datetime.fromtimestamp(int(x or 0), timezone.utc).isoformat()), + } + + outputs = [{camelize_string(k, '-'): v.context_value(ent[k]) for k, v in mapping.items()} for ent in file_ents] + + return CommandResults( + outputs_prefix='WebFileRepository.Files', + outputs=outputs, + readable_output=tblToMd( + 'File List', + [{k: v.readable_value(ent[k]) for k, v in mapping.items()} for ent in file_ents], + headers=mapping.keys(), + headerTransform=lambda x: x.replace('-', ' ').title() + ), + raw_response=file_ents) + + +def command_remove_files(args: Dict[str, str], settings: Settings) -> str: + """ Remove files from the repository + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + client = new_client(detect_service_ip_port(settings), settings) + resp = client._http_request('POST', + json_data={ + 'q': 'delete', + 'path': argToList(args.get('paths', [])) + }, + raise_on_status=True) + if not resp.get('success'): + raise ValueError(f'Failed to remove files: {resp.get("message")}') + return 'Done.' + + +def command_download_file(args: Dict[str, str], settings: Settings) -> Dict[str, Any]: + """ Download a file from the repository + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + if not (path := args.get('path')): + raise DemistoException('A file path is required.') + + client = new_client(detect_service_ip_port(settings), settings) + resp = client._http_request('GET', + params={ + 'q': 'download', + 'path': path + }, + raise_on_status=True, + resp_type='response') + + if not (filename := args.get('save_as')): + if content_disposition := resp.headers.get('Content-Disposition'): + cdp = email_parser.Parser().parsestr(f'Content-Disposition: {content_disposition}', headersonly=True) + filename = cdp.get_filename() + + return fileResult(filename or str(uuid.uuid4()), resp.content) + + +def command_archive_zip(args: Dict[str, str], settings: Settings) -> Dict[str, Any]: + """ Archive all the files into a zip file + + :param args: The parameters which were given to the command. + :param settings: The instance settings. + """ + client = new_client(detect_service_ip_port(settings), settings) + resp = client._http_request('GET', + params={'q': 'archive-all'}, + raise_on_status=True, + resp_type='response') + + if not (filename := args.get('save_as')): + if content_disposition := resp.headers.get('Content-Disposition'): + cdp = email_parser.Parser().parsestr(f'Content-Disposition: {content_disposition}', headersonly=True) + filename = cdp.get_filename() + + return fileResult(filename or str(uuid.uuid4()), resp.content) + + +def main() -> None: + """ + Main + """ + command = demisto.command() + + demisto.debug(f'Command being called is {command}') + commands = { + 'test-module': test_module, + 'wfr-status': command_status, + 'wfr-cleanup': command_cleanup, + 'wfr-reset': command_reset, + 'wfr-upload-files': command_upload_files, + 'wfr-list-files': command_list_files, + 'wfr-remove-files': command_remove_files, + 'wfr-download-file': command_download_file, + 'wfr-archive-zip': command_archive_zip, + } + try: + if command == 'long-running-execution': + run_long_running(SETTINGS) + else: + return_results(commands[command](assign_params(**demisto.args()), SETTINGS)) + except Exception as e: + return_error(f'Failed to execute {command} command.\nError:\n{str(e)}') + + +if __name__ in ('__main__', '__builtin__', 'builtins'): + main() diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.yml b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.yml new file mode 100644 index 000000000000..88dbed4d6d7e --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository.yml @@ -0,0 +1,194 @@ +category: Utilities +commonfields: + id: Web File Repository + version: -1 +configuration: +- display: Incident type + name: incidentType + required: false + type: 13 +- defaultvalue: "true" + display: Long running instance + name: longRunning + required: false + type: 8 +- display: Port mapping ( or :) + name: longRunningPort + required: true + type: 0 +- display: User ID for Read/Write + displaypassword: Password + name: rwCredentials + required: false + type: 9 +- display: User ID for Read-Only + displaypassword: Password + name: roCredentials + required: false + type: 9 +- additionalinfo: Some of the browsers such as Chrome may not support Digest-sha256. + display: Authentication Method + name: authenticationMethod + options: + - Basic + - Digest-md5 + - Digest-sha256 + required: false + type: 15 +- additionalinfo: Authentication is not requiured for read access + defaultvalue: "true" + display: Public Read Access + name: publicReadAccess + required: false + type: 8 +- additionalinfo: '"mime-type extension [extension]*" for each line, wild-card file extensions are supported.' + display: MIME Types for file extensions + name: mimeTypes + required: false + type: 12 +- additionalinfo: Set true to merge the specified types with the default settings, false to replace them + defaultvalue: "true" + display: Merge with Default MIME Types + name: mergeMimeTypes + required: false + type: 8 +- additionalinfo: List of extensions to set "attachment" to Content-Disposition + display: Attachment extensions + name: attachmentExtensions + required: false + type: 0 +- defaultvalue: read/write + display: Storage Protection + name: storageProtection + options: + - read/write + - read-only + - sandbox + required: true + type: 15 +- defaultvalue: 100MB + display: The maximum repository size + name: maxStorageSize + required: false + type: 0 +- defaultvalue: 1GB + display: The maximum sandbox repository size + name: maxSandboxSize + required: false + type: 0 +description: |- + Simple web server with a file uploading console to store small files. + This is helpful to make your environment ready for testing purpose for your playbooks or automations to download files from a web server. +display: Web File Repository +name: Web File Repository +script: + commands: + - arguments: [] + description: Get the service status + name: wfr-status + outputs: + - contextPath: WebFileRepository.Status.StorageUsage + description: The current storage usage in bytes + type: number + - contextPath: WebFileRepository.Status.SandboxUsage + description: The current sandbox usage in bytes + type: number + - contextPath: WebFileRepository.Status.StorageProtection + description: The storage protection mode + type: string + - contextPath: WebFileRepository.Status.IntercommunicationIP + description: The IP address of the service to which the internal client connects + type: string + - contextPath: WebFileRepository.Status.IntercommunicationPort + description: The port number of the service to which the internal client connects + type: number + - contextPath: WebFileRepository.Status.ExternaIP + description: The external IP address of the service + - contextPath: WebFileRepository.Status.ExternalPort + description: The external port number of the service + - contextPath: WebFileRepository.Status.ServerIP + description: The IP address of the service + type: string + - contextPath: WebFileRepository.Status.ServerPort + description: The port number of the service + type: number + - arguments: [] + description: Remove all the files from the repository + name: wfr-cleanup + - arguments: + - description: The entry ID list of files + isArray: true + name: entry_ids + required: true + - auto: PREDEFINED + defaultValue: "false" + description: Set to true to extract files to archive files, otherwise false. + name: extract_archive + predefined: + - "true" + - "false" + - defaultValue: / + description: The directory path where to upload + name: upload_directory + description: Upload files to the repository + name: wfr-upload-files + - arguments: + - defaultValue: / + description: The directory path where to list files + name: directory + - auto: PREDEFINED + defaultValue: "false" + description: Set to true to list subdirectories recursively, otherwise false. + name: recursive + predefined: + - "true" + - "false" + description: List files in the repository + name: wfr-list-files + outputs: + - contextPath: WebFileRepository.Files.Name + description: The file name + type: string + - contextPath: WebFileRepository.Files.Path + description: The file path + type: string + - contextPath: WebFileRepository.Files.Size + description: The file size in bytes + type: number + - contextPath: WebFileRepository.Files.LastModified + description: The last modified time + type: date + - arguments: + - description: The list of the file paths + isArray: true + name: paths + required: true + description: Remove files from the repository + name: wfr-remove-files + - arguments: + - description: The file path + name: path + required: true + - description: The name to give the file to save + name: save_as + description: Download a file from the repository + name: wfr-download-file + - arguments: + - description: The name to give the archive-file to save + name: save_as + description: Download a file to which all the files are archived + name: wfr-archive-zip + - arguments: [] + description: Reset the repository data + name: wfr-reset + dockerimage: demisto/bottle:1.0.0.42800 + longRunning: true + longRunningPort: true + resetContext: true + runonce: false + script: '' + subtype: python3 + type: python +fromversion: 6.0.0 +tests: +- No tests (auto formatted) diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_description.md b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_description.md new file mode 100644 index 000000000000..0975d1cf5be9 --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_description.md @@ -0,0 +1,16 @@ +Note: This integration is meant for testing purposes when developing playbooks or automations to download files from a web server. Do not use for public or production access. + +### How to Access the File Management UI + +#### Access the File Management UI by URL and Port (HTTP) +In a web browser, go to **http://:**. + +#### Access the File Management UI by Instance Name (HTTPS) + +To access the File Management UI by instance name, make sure ***Instance.execute.external*** is enabled. + +1. In Cortex XSOAR, go to **Settings > About > Troubleshooting**. +2. In the **Server Configuration** section, verify that the `instance.execute.external.` key is set to `true`. If this key does not exist, click **+ Add Server Configuration** and add the `instance.execute.external.` and set the value to `true`. See [this documentation](https://xsoar.pan.dev/docs/reference/articles/long-running-invoke) for further information. +3. In a web browser, go to `https:///instance/execute//`. + + In Multi Tenant environments, go to `https:///acc_/instance/execute//` \ No newline at end of file diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_image.png b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_image.png new file mode 100644 index 0000000000000000000000000000000000000000..a8c27c414109b715a64022f2c2bef0679e79ead4 GIT binary patch literal 3328 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 KMNUMnLSTXfR*-xE literal 0 HcmV?d00001 diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_test.py b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_test.py new file mode 100644 index 000000000000..34646e165f5e --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/WebFileRepository_test.py @@ -0,0 +1,2172 @@ +import demistomock as demisto +from CommonServerPython import * # noqa: F401 +import pytest +import pytest_mock +import os +import io +import bottle +import json +import base64 +import zipfile +import importlib +import uuid +import copy +import random +import urllib.parse +import WebFileRepository +import freezegun +from typing import Dict, Any, Tuple + + +def equals_object(obj1, obj2) -> bool: + if type(obj1) != type(obj2): + return False + elif isinstance(obj1, dict): + for k1, v1 in obj1.items(): + if k1 not in obj2: + return False + if not equals_object(v1, obj2[k1]): + return False + return not (set(obj1.keys()) ^ set(obj2.keys())) + elif isinstance(obj1, list): + # Compare lists (ignore order) + list2 = list(obj2) + for i1, v1 in enumerate(obj1): + for i2, v2 in enumerate(list2): + if equals_object(v1, v2): + list2.pop(i2) + break + else: + return False + return not list2 + else: + return obj1 == obj2 + + +class MockIntegrationContext: + @staticmethod + def encode_values(ctx: Dict[str, Any]) -> Dict[str, str]: + return { + k: json.dumps(v) if k.startswith(os.sep) and not isinstance(v, str) else v + for k, v in ctx.items() + } + + def decode_values(ctx: Dict[str, Any]) -> Dict[str, Any]: + return { + k: json.loads(v) if k.startswith(os.sep) and isinstance(v, str) else v + for k, v in ctx.items() + } + + def __init__(self, + ctx: Dict[str, Any], + mocker: Optional[pytest_mock.plugin.MockerFixture] = None): + self.__ctx = MockIntegrationContext.encode_values(ctx) + if mocker: + mocker.patch('WebFileRepository.get_integration_context', + side_effect=self.get_integration_context) + mocker.patch('WebFileRepository.set_integration_context', + side_effect=self.set_integration_context) + + def get_integration_context(self) -> Dict[str, str]: + return copy.deepcopy(self.__ctx) + + def set_integration_context(self, ctx: Dict[str, str]): + self.__ctx = copy.deepcopy(ctx) + + def equals(self, ctx: Dict[str, Any]) -> bool: + return equals_object(MockIntegrationContext.decode_values(self.__ctx), + MockIntegrationContext.decode_values(ctx)) + + def print(self) -> None: + print(json.dumps(MockIntegrationContext.decode_values(self.__ctx), indent=2)) + + +class MockUUID: + def __init__(self, + mocker: pytest_mock.plugin.MockerFixture): + self.__count = 0 + mocker.patch('uuid.uuid4', side_effect=self.uuid4) + + def uuid4(self) -> uuid.UUID: + u = uuid.UUID(int=self.__count) + self.__count += 1 + return u + + +class MockBaseClient: + def __init__(self, + mocker: pytest_mock.plugin.MockerFixture, + headers: Dict[str, str], + content: bytes = None, + json_data: Any = None): + self.__headers = headers + self.__content = json.dumps(json_data).encode() if content is None else content + mocker.patch('CommonServerPython.BaseClient._http_request', side_effect=self._http_request) + + def _http_request(self, method, url_suffix='', full_url=None, headers=None, auth=None, json_data=None, + params=None, data=None, files=None, timeout=None, resp_type='json', ok_codes=None, + return_empty_response=False, retries=0, status_list_to_retry=None, + backoff_factor=5, raise_on_redirect=False, raise_on_status=False, + error_handler=None, empty_valid_codes=None, **kwargs): + + class MockRequestsResponse: + def __init__(self, headers: Dict[str, str], content: bytes): + self.headers = headers + self.content = content + + def json(self): + return json.loads(self.content.decode()) + + if resp_type == 'json': + return json.loads(self.__content.decode()) + elif resp_type == 'json': + return self.__content + else: + return MockRequestsResponse(headers=self.__headers, + content=self.__content) + + +def MockFileResult(filename, data, file_type=None): + if file_type is None: + file_type = entryTypes['file'] + return {'Contents': '', + 'ContentsFormat': formats['text'], + 'Type': file_type, + 'File': filename, + 'FileID': 'fileid'} + + +@pytest.mark.parametrize(argnames='filename, content_type', + argvalues=[ + ('datatables.min.css', 'text/css'), + ('datatables.min.js', 'text/javascript'), + ('fi_file.png', 'image/png'), + ('fi_folder.png', 'image/png'), + ('fi_parent.png', 'image/png'), + ('fi_unknown.png', 'image/png'), + ]) +def test_process_root_get_resource(mocker, filename, content_type): + """ + Given: + A resource file to get + + When: + Running script to get a resource file. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'roCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + bottle.request = bottle.LocalRequest() + bottle.request.query.q = 'resource' + bottle.request.query.name = filename + response = WebFileRepository.process_root_get() + assert response.status_code == 200 + assert response.content_type == content_type + + +@pytest.mark.parametrize(argnames='integration_context_filename, ' + 'storage_protection, ' + 'max_storage_size, ' + 'max_sandbox_size, ' + 'sandbox_usage, ' + 'storage_usage', + argvalues=[ + ('./test_data/integration_ctx_empty.json', + 'read/write', + '10000', + '100000', + 0, + 0 + ) + ]) +def test_process_root_get_status(mocker, + integration_context_filename, + storage_protection, + max_storage_size, + max_sandbox_size, + sandbox_usage, + storage_usage): + """ + Given: + The repository and status parameters + + When: + Running script to list files in the repository. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': storage_protection, + 'maxStorageSize': max_storage_size, + 'maxSandboxSize': max_sandbox_size, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename, 'r') as f: + MockIntegrationContext(json.load(f), mocker) + + bottle.request = bottle.LocalRequest() + bottle.request.query.q = 'status' + response = WebFileRepository.process_root_get() + assert response.status_code == 200 + status = response.body + assert status['storage_protection'] == storage_protection + assert status['max_storage_size'] == int(max_storage_size) + assert status['max_sandbox_size'] == int(max_sandbox_size) + assert status['sandbox_usage'] == sandbox_usage + assert status['storage_usage'] == storage_usage + + +@pytest.mark.parametrize(argnames='integration_context_filename, dir_name, recursive, output_filename', + argvalues=[ + ('./test_data/integration_ctx_empty.json', '/', False, './test_data/ls_out_01.json'), + ('./test_data/integration_ctx_common.json', '/', False, './test_data/ls_out_02.json'), + ('./test_data/integration_ctx_common.json', '/', True, './test_data/ls_out_03.json'), + ('./test_data/integration_ctx_common.json', '/x', False, './test_data/ls_out_04.json'), + ('./test_data/integration_ctx_common.json', '/x/あいうえお', False, './test_data/ls_out_05.json'), + ('./test_data/integration_ctx_common.json', '/x/あいうえお', False, './test_data/ls_out_05.json'), + ('./test_data/integration_ctx_common.json', '/not-found', False, './test_data/ls_out_06.json'), + ]) +def test_process_root_get_ls(mocker, integration_context_filename, dir_name, recursive, output_filename): + """ + Given: + The repository and name of a directory + + When: + Running script to list files in the repository. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename, 'r') as f: + MockIntegrationContext(json.load(f), mocker) + + with open(output_filename, 'r') as f: + expected = json.load(f) + + bottle.request = bottle.LocalRequest() + bottle.request.query.q = 'ls' + bottle.request.query.dir = dir_name + bottle.request.query.recursive = 'true' if recursive else 'false' + response = WebFileRepository.process_root_get() + assert response.status_code == 200 + assert equals_object(response.body, expected) + + +@pytest.mark.parametrize(argnames='integration_context_filename, path, output_filename', + argvalues=[ + ('./test_data/integration_ctx_common.json', '/a.dat', './test_data/download_out_01.dat'), + ('./test_data/integration_ctx_common.json', '/x/XYZ/アイウエオ.txt', './test_data/download_out_02.dat'), + ]) +def test_process_root_get_download(mocker, integration_context_filename, path, output_filename): + """ + Given: + The repository and a file path to download + + When: + Running script to download the file. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename, 'r') as f: + MockIntegrationContext(json.load(f), mocker) + + bottle.request = bottle.LocalRequest() + bottle.request.query.q = 'download' + bottle.request.query.path = path + response = WebFileRepository.process_root_get() + + assert response.status_code == 200 + with open(output_filename, 'rb') as f: + assert f.read() == b''.join(response.body) + + +@pytest.mark.parametrize(argnames='integration_context_filename, path', + argvalues=[ + ('./test_data/integration_ctx_common.json', '/zzz.dat'), + ]) +def test_process_root_get_download_not_found(mocker, integration_context_filename, path): + """ + Given: + The repository and a file path doesn't exist + + When: + Running script to download the file. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename, 'r') as f: + MockIntegrationContext(json.load(f), mocker) + + bottle.request = bottle.LocalRequest() + bottle.request.query.q = 'download' + bottle.request.query.path = path + + with pytest.raises(bottle.HTTPError, match='.*404.*'): + WebFileRepository.process_root_get() + + +@pytest.mark.parametrize(argnames='integration_context_filename, filenames', + argvalues=[ + ('./test_data/integration_ctx_common.json', + [ + 'a.dat', + 'b.dat', + 'c.dat', + 'x/XYZ/アイウエオ.txt', + 'x/d.dat', + 'x/あいうえお/e.dat', + 'x/あいうえお/f.dat', + 'y/g.dat', + 'z.dat', + ] + ), + ]) +def test_process_root_get_archive_zip(mocker, integration_context_filename, filenames): + """ + Given: + The repository + + When: + Running script to archive files. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename, 'r') as f: + integration_context = MockIntegrationContext(json.load(f), mocker) + ctx = integration_context.get_integration_context() + + bottle.request = bottle.LocalRequest() + bottle.request.query.q = 'archive-all' + response = WebFileRepository.process_root_get() + + assert response.status_code == 200 + with zipfile.ZipFile(io.BytesIO(b''.join(response.body)), 'r') as z: + for filename in filenames: + attrs = json.loads(ctx.get(os.sep + filename)) + assert attrs['size'] == len(z.read(filename)) + + +def test_process_root_get_html_main(mocker): + """ + Given: + No query parameters to get + + When: + Running script to get the main html. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + bottle.request = bottle.LocalRequest() + response = WebFileRepository.process_root_get() + + assert response.status_code == 200 + assert response.body == WebFileRepository.HTML_MAIN + + +@pytest.mark.parametrize(argnames='integration_context_filename, ' + 'storage_protection, ' + 'user_permission, ' + 'request_permission, ' + 'ok', + argvalues=[ + ('./test_data/integration_ctx_common.json', + 'read/write', + 'read/write', + 'write', + True, + ), + ('./test_data/integration_ctx_common.json', + 'read/write', + 'read', + 'write', + False, + ), + ('./test_data/integration_ctx_common.json', + 'read-only', + 'read', + 'read', + True, + ), + ('./test_data/integration_ctx_common.json', + 'read-only', + None, + 'read', + False, + ), + ('./test_data/integration_ctx_common.json', + 'sandbox', + 'read/write', + 'write', + True, + ), + ('./test_data/integration_ctx_common.json', + 'sandbox', + None, + 'read', + False, + ), + ]) +def test_process_root_post_health(mocker, + integration_context_filename, + storage_protection, + user_permission, + request_permission, + ok): + """ + Given: + The repository and request parameters with 'health' + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': { + 'identifier': 'RWuser', + 'password': 'password', + }, + 'roCredentials': { + 'identifier': 'ROuser', + 'password': 'password', + }, + 'authenticationMethod': 'Basic', + 'publicReadAccess': False, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': storage_protection, + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + rw_auth_header = f"Basic {base64.b64encode(b'RWuser:password').decode()}" + ro_auth_header = f"Basic {base64.b64encode(b'ROuser:password').decode()}" + + with open(integration_context_filename, 'r') as f: + MockIntegrationContext(json.load(f), mocker) + + post_data = json.dumps({ + 'q': 'health', + 'permission': request_permission + }).encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': 'application/json', + 'wsgi.input': io.BytesIO(post_data), + } + + if user_permission == 'read/write': + environ['HTTP_AUTHORIZATION'] = rw_auth_header + elif user_permission == 'read': + environ['HTTP_AUTHORIZATION'] = ro_auth_header + + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + if ok: + assert response.status_code == 200 + assert response.body.get('success') + else: + assert response.status_code == 401 + + +@pytest.mark.parametrize(argnames='integration_context_filename, ' + 'storage_protection', + argvalues=[ + ('./test_data/integration_ctx_common.json', + 'read/write' + ), + ('./test_data/integration_ctx_common.json', + 'sandbox' + ), + ]) +def test_process_root_post_cleanup(mocker, + integration_context_filename, + storage_protection): + """ + Given: + The repository and request parameters with 'cleanup' + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': storage_protection, + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename, 'r') as f: + integration_context = MockIntegrationContext(json.load(f), mocker) + + post_data = json.dumps({ + 'q': 'cleanup' + }).encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': 'application/json', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + + if storage_protection == 'read-only': + assert response.body.get('success') is False + assert 'read-only' in response.body.get('message') + elif storage_protection == 'read/write': + assert response.body.get('success') is True + assert integration_context.get_integration_context() == {} + else: + bottle.request = bottle.LocalRequest() + bottle.request.query.q = 'ls' + bottle.request.query.dir = '/' + bottle.request.query.recursive = 'true' + response = WebFileRepository.process_root_get() + assert response.status_code == 200 + assert equals_object(response.body, {'data': []}) + + +@pytest.mark.parametrize(argnames='integration_context_filename_before, ' + 'integration_context_filename_after, ' + 'storage_protection', + argvalues=[ + ('./test_data/integration_ctx_common.json', + './test_data/integration_ctx_empty.json', + 'read/write', + ), + ('./test_data/integration_ctx_common.json', + './test_data/integration_ctx_common.json', + 'sandbox', + ), + ]) +def test_process_root_post_reset(mocker, + integration_context_filename_before, + integration_context_filename_after, + storage_protection): + """ + Given: + The repository and request parameters with 'reset'. + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': storage_protection, + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename_before, 'r') as f: + integration_context = MockIntegrationContext(json.load(f), mocker) + + # Modify the repository + post_data = json.dumps({ + 'q': 'delete', + 'path': ['/a.dat'] + }).encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': 'application/json', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is True + + # Reset the repository + post_data = json.dumps({ + 'q': 'reset' + }).encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': 'application/json', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is True + with open(integration_context_filename_after, 'r') as f: + assert integration_context.equals(json.load(f)) + + +def test_process_root_post_reset_in_read_only(mocker): + """ + Given: + The repository and request parameters with 'reset' in read-only mode. + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read-only', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open('./test_data/integration_ctx_common.json', 'r') as f: + MockIntegrationContext(json.load(f), mocker) + + # Reset the repository + post_data = json.dumps({ + 'q': 'reset' + }).encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': 'application/json', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is False + assert 'read-only' in response.body.get('message') + + +@pytest.mark.parametrize(argnames='integration_context_filename_before, ' + 'path_list, ' + 'integration_context_filename_after', + argvalues=[ + ('./test_data/integration_ctx_common.json', + ['/a.dat'], + './test_data/delete_out_01.json', + ), + ('./test_data/integration_ctx_common.json', + ['/a.dat', '/x/XYZ/アイウエオ.txt'], + './test_data/delete_out_02.json', + ), + ('./test_data/integration_ctx_common.json', + ['/a.dat', '/x'], + './test_data/delete_out_03.json', + ), + ]) +def test_process_root_post_delete(mocker, + integration_context_filename_before, + path_list, + integration_context_filename_after): + """ + Given: + The repository and request parameters with 'delete' + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(integration_context_filename_before, 'r') as f: + integration_context = MockIntegrationContext(json.load(f), mocker) + + post_data = json.dumps({ + 'q': 'delete', + 'path': path_list + }).encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': 'application/json', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is True + with open(integration_context_filename_after, 'r') as f: + assert integration_context.equals(json.load(f)) + + +def test_process_root_post_delete_in_read_only(mocker): + """ + Given: + The repository and request parameters with 'delete' in read-only mode + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read-only', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open('./test_data/integration_ctx_common.json', 'r') as f: + MockIntegrationContext(json.load(f), mocker) + + post_data = json.dumps({ + 'q': 'delete', + 'path': ['/a.dat'] + }).encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': 'application/json', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is False + assert 'read-only' in response.body.get('message') + + +@freezegun.freeze_time('2022-01-23 12:34:56') +@pytest.mark.parametrize(argnames='integration_context_filename_before, ' + 'file_list, ' + 'upload_dir, ' + 'extract_archive, ' + 'integration_context_filename_after', + argvalues=[ + ('./test_data/integration_ctx_empty.json', + ['./test_data/upload_file.txt'], + '/', + False, + './test_data/upload_out_01.json', + ), + ('./test_data/integration_ctx_empty.json', + ['./test_data/upload_file.txt', './test_data/upload_file.dat'], + '/', + False, + './test_data/upload_out_02.json', + ), + ('./test_data/integration_ctx_empty.json', + ['./test_data/upload_file.zip'], + '/', + True, + './test_data/upload_out_03.json', + ), + ('./test_data/integration_ctx_empty.json', + ['./test_data/upload_file.tar.gz'], + '/', + True, + './test_data/upload_out_04.json', + ), + ('./test_data/integration_ctx_empty.json', + ['./test_data/upload_file.txt'], + '/あいうえお', + False, + './test_data/upload_out_05.json', + ), + ]) +def test_process_root_post_upload(mocker, + integration_context_filename_before, + file_list, + upload_dir, + extract_archive, + integration_context_filename_after): + """ + Given: + The repository and request parameters with 'upload' + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + MockUUID(mocker) + + importlib.reload(WebFileRepository) + + with open(integration_context_filename_before, 'r') as f: + integration_context = MockIntegrationContext(json.load(f), mocker) + + boundary = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + post_data = b'' + for file_path in file_list: + file_name = os.path.basename(file_path) + post_data += '\r\n'.join([ + '', + f'--{boundary}', + f'Content-Disposition: form-data; name="file"; filename="{file_name}"', + 'Content-Type: application/octet-stream', + '\r\n', + ]).encode() + + with open(file_path, 'rb') as f: + post_data += f.read() + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="dir"', + '', + upload_dir + ]).encode() + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="extract"', + '', + 'true' if extract_archive else 'false' + ]).encode() + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="q"', + '', + 'upload' + ]).encode() + + post_data += f'\r\n--{boundary}--\r\n'.encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': f'multipart/form-data; boundary={boundary}', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is True + with open(integration_context_filename_after, 'r') as f: + assert integration_context.equals(json.load(f)) + + +@freezegun.freeze_time('2022-01-23 12:34:56') +@pytest.mark.parametrize(argnames='storage_limit, ' + 'sandbox_limit, ' + 'storage_protection', + argvalues=[ + ('10', '20', 'read/write'), + ('10', '20', 'sandbox'), + ]) +def test_process_root_post_upload_limit(mocker, + storage_limit, + sandbox_limit, + storage_protection): + """ + Given: + The storage/sandbox limit, and upload a file over the limit. + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': storage_protection, + 'maxStorageSize': storage_limit, + 'maxSandboxSize': sandbox_limit, + }) + importlib.reload(WebFileRepository) + + boundary = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + post_data = '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="file"; filename="test.dat"', + 'Content-Type: application/octet-stream', + '\r\n', + ]).encode() + + post_data += random.randbytes(int(storage_limit) * 100) + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="dir"', + '', + '/' + ]).encode() + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="extract"', + '', + 'false' + ]).encode() + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="q"', + '', + 'upload' + ]).encode() + + post_data += f'\r\n--{boundary}--\r\n'.encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': f'multipart/form-data; boundary={boundary}', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is False + assert 'limit exceeded' in response.body.get('message') + + +def test_process_root_post_upload_in_read_only(mocker): + """ + Given: + The storage/sandbox limit, and upload a file in read-only mode. + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read-only', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + boundary = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' + post_data = '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="file"; filename="test.dat"', + 'Content-Type: application/octet-stream', + '\r\n', + ]).encode() + + post_data += random.randbytes(100) + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="dir"', + '', + '/' + ]).encode() + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="extract"', + '', + 'false' + ]).encode() + + post_data += '\r\n'.join([ + '', + f'--{boundary}', + 'Content-Disposition: form-data; name="q"', + '', + 'upload' + ]).encode() + + post_data += f'\r\n--{boundary}--\r\n'.encode() + + environ = { + 'REQUEST_METHOD': 'POST', + 'CONTENT_LENGTH': len(post_data), + 'PATH_INFO': '/', + 'CONTENT_TYPE': f'multipart/form-data; boundary={boundary}', + 'wsgi.input': io.BytesIO(post_data), + } + bottle.request = bottle.LocalRequest(environ) + + response = WebFileRepository.process_root_post() + assert response.status_code == 200 + assert response.body.get('success') is False + assert 'read-only' in response.body.get('message') + + +def test_command_status(mocker): + """ + Given: + Patterns of parameters for command_list_files + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + + server_resp = { + 'storage_protection': 'read/write', + 'max_storage_size': 1000, + 'max_sandbox_size': 2000, + 'storage_usage': 200, + 'sandbox_usage': 100, + } + client = MockBaseClient(mocker, headers={}, json_data=server_resp) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_status({}, settings).to_context() + + ec = demisto.get(res, 'EntryContext').get('WebFileRepository.Status') + assert ec.get('StorageUsage') == server_resp.get('storage_usage') + assert ec.get('SandboxUsage') == server_resp.get('sandbox_usage') + assert ec.get('StorageProtection') == server_resp.get('storage_protection') + + +def test_command_cleanup(mocker): + """ + Given: + No parameters for command_cleanup + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + + client = MockBaseClient(mocker, headers={}, json_data={ + 'success': True, + 'message': '' + }) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_cleanup({}, settings) + assert 'Done' in res + + +def test_command_reset(mocker): + """ + Given: + No parameters for command_reset + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + + client = MockBaseClient(mocker, headers={}, json_data={ + 'success': True, + 'message': '' + }) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_reset({}, settings) + assert 'Done' in res + + +@pytest.mark.parametrize(argnames='entry_ids', + argvalues=[ + ('0000'), + ('0000,1111,2222'), + (['0000', '1111', '2222']), + ]) +def test_command_upload_files(mocker, entry_ids): + """ + Given: + Some patterns of entry_ids for command_upload_files + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + mocker.patch.object(demisto, 'getFilePath', return_value={ + 'name': 'upload_file.dat', + 'path': 'test_data/upload_file.dat' + }) + + client = MockBaseClient(mocker, headers={}, json_data={ + 'success': True, + 'message': '' + }) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + args = { + 'entry_ids': entry_ids + } + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_upload_files(args, settings) + assert 'Done' in res + + +@pytest.mark.parametrize(argnames='directory, ' + 'recursive, ' + 'response_filename, ' + 'results_filename', + argvalues=[ + ('/', + False, + 'test_data/list_files_svrresp_01.json', + 'test_data/list_files_results_01.json'), + ('/', + True, + 'test_data/list_files_svrresp_02.json', + 'test_data/list_files_results_02.json'), + ]) +def test_command_list_files(mocker, + directory, + recursive, + response_filename, + results_filename): + """ + Given: + Patterns of parameters for command_list_files + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + + with open(response_filename, 'r') as f: + server_resp = json.load(f) + + client = MockBaseClient(mocker, headers={}, json_data=server_resp) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + args = { + 'directory': directory, + 'recursive': recursive + } + keys = ('Type', 'ContentFormat', 'Contents', 'EntryContext') + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_list_files(args, settings).to_context() + res = {k: v for k, v in res.items() if k in keys} + + with open(results_filename, 'r') as f: + expected = {k: v for k, v in json.load(f).items() if k in keys} + + assert equals_object(res, expected) + + +@pytest.mark.parametrize(argnames='paths', + argvalues=[ + ('/0000.dat'), + ('/0000.dat,/1111.dat,/2222.dat'), + (['/0000.dat', '/1111.dat', '/2222.dat']), + ]) +def test_command_remove_files(mocker, paths): + """ + Given: + Some patterns of paths for command_reset + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + + client = MockBaseClient(mocker, headers={}, json_data={ + 'success': True, + 'message': '' + }) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + args = { + 'paths': paths + } + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_remove_files(args, settings) + assert 'Done' in res + + +@pytest.mark.parametrize(argnames='path, ' + 'save_as, ' + 'content_filename', + argvalues=[ + ('/test.dat', + 'aaa.dat', + 'test_data/download_file.dat' + ), + ('/あいうえお.dat', + None, + 'test_data/download_file.dat' + ), + ('/あいうえお.dat', + 'アイウエオ.txt', + 'test_data/download_file.dat' + ), + ]) +def test_command_download_file(mocker, path, save_as, content_filename): + """ + Given: + Some patterns of parameters for command_download_file + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + mocker.patch.object(demisto, 'results') + mocker.patch('CommonServerPython.fileResult', side_effect=MockFileResult) + + filename = os.path.basename(path) + encoded_name = urllib.parse.quote(filename, encoding='utf-8') + headers = { + 'Content-Disposition': f'attachment; filename*=utf-8\'\'{encoded_name}' + } + with open(content_filename, 'rb') as f: + content = f.read() + + client = MockBaseClient(mocker, headers=headers, content=content) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + args = { + 'path': path, + 'save_as': save_as + } + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_download_file(args, settings) + filename = save_as if save_as else filename + assert res['Type'] == entryTypes['file'] + assert res['File'] == filename + + +@pytest.mark.parametrize(argnames='save_as, ' + 'content_filename', + argvalues=[ + ('aaa.dat', + 'test_data/download_file.dat' + ), + (None, + 'test_data/download_file.dat' + ), + ('アイウエオ.txt', + 'test_data/download_file.dat' + ), + ]) +def test_command_archive_zip(mocker, save_as, content_filename): + """ + Given: + Some patterns of parameters for command_archive_zip + + When: + Running script to send a request. + + Then: + Validate the right response returns. + """ + params = { + 'longRunningPort': '8000', + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + } + mocker.patch.object(demisto, 'params', return_value=params) + mocker.patch.object(demisto, 'results') + mocker.patch('CommonServerPython.fileResult', side_effect=MockFileResult) + + filename = 'archive.zip' + headers = { + 'Content-Disposition': f'attachment; filename="{filename}"' + } + with open(content_filename, 'rb') as f: + content = f.read() + + client = MockBaseClient(mocker, headers=headers, content=content) + mocker.patch.object(WebFileRepository, 'new_client', return_value=client) + + importlib.reload(WebFileRepository) + + args = { + 'save_as': save_as + } + settings = WebFileRepository.Settings(params) + res = WebFileRepository.command_archive_zip(args, settings) + + filename = save_as if save_as else filename + assert res['Type'] == entryTypes['file'] + assert res['File'] == filename + + +@pytest.mark.parametrize(argnames='mimetypes_input_filename, ' + 'mimetypes_output_filename, ' + 'merge_mime_types', + argvalues=[ + ('./test_data/mime_types_style_01.json', + './test_data/mime_types_out_overwrite.json', + False, + ), + ('./test_data/mime_types_style_02.txt', + './test_data/mime_types_out_overwrite.json', + False, + ), + ('./test_data/mime_types_style_03.txt', + './test_data/mime_types_out_overwrite.json', + False, + ), + ('./test_data/mime_types_style_01.json', + './test_data/mime_types_out_merge.json', + True, + ), + ('./test_data/mime_types_style_02.txt', + './test_data/mime_types_out_merge.json', + True, + ), + ('./test_data/mime_types_style_03.txt', + './test_data/mime_types_out_merge.json', + True, + ), + ]) +def test_parse_mime_types(mocker, + mimetypes_input_filename, + mimetypes_output_filename, + merge_mime_types): + """ + Given: + The MIME types file + + When: + Running script to initialize with the MIME types. + + Then: + Validate the right response returns. + """ + with open(mimetypes_input_filename, 'r') as f: + input_mime_types = f.read() + + mocker.patch.object(demisto, 'params', return_value={ + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': input_mime_types, + 'mergeMimeTypes': merge_mime_types, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + with open(mimetypes_output_filename, 'r') as f: + assert equals_object(WebFileRepository.SETTINGS.ext_to_mimetype, json.loads(f.read())) + + +@pytest.mark.parametrize(argnames='attachment_exts_input, ' + 'attachment_exts_output', + argvalues=[ + ('exe, bat, dat, zip, 7z', + [".exe", ".bat", ".dat", ".zip", ".7z"], + ), + ('.exe, .bat, .dat, .zip, .7z', + [".exe", ".bat", ".dat", ".zip", ".7z"], + ), + ('exe bat dat zip 7z', + [".exe", ".bat", ".dat", ".zip", ".7z"], + ), + ('.exe .bat .dat .zip .7z', + [".exe", ".bat", ".dat", ".zip", ".7z"], + ), + ('.exe .bat .dat .zip .7z *', + [".exe", ".bat", ".dat", ".zip", ".7z", "*"], + ), + ]) +def test_parse_attachment_exts(mocker, + attachment_exts_input, + attachment_exts_output): + """ + Given: + The attachment extensions text + + When: + Running script to initialize with the attachment extensions. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'rwCredentials': {}, + 'authenticationMethod': None, + 'publicReadAccess': True, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': attachment_exts_input, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + attachment_exts = list(WebFileRepository.SETTINGS.attachment_exts) + assert equals_object(attachment_exts, attachment_exts_output) + + +@pytest.mark.parametrize(argnames='rw_identifier, ' + 'rw_password, ' + 'ro_identifier, ' + 'ro_password, ' + 'require_write, ' + 'public_read_access, ' + 'request_method, ' + 'auth_method, ' + 'auth_header, ' + 'auth_ok', + argvalues=[ + # Given: + # - read/write credentials + # - right credentials + # + # When: + # - basic auth + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth OK + ('test', + 'password', + None, + None, + True, + False, + 'POST', + 'Basic', + 'Basic dGVzdDpwYXNzd29yZA==', + True + ), + # Given: + # - read/write credentials (empty string) + # - right credentials + # + # When: + # - basic auth + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth OK + ('', + '', + None, + None, + True, + False, + 'POST', + 'Basic', + 'Basic Og==', + True + ), + # Given: + # - read-only credentials + # - right credentials + # + # When: + # - basic auth + # - having permissions + # (request read to read-only permissions) + # + # Then: + # - Auth OK + (None, + None, + 'test', + 'password', + False, + False, + 'POST', + 'Basic', + 'Basic dGVzdDpwYXNzd29yZA==', + True + ), + # Given: + # - read-only credentials + # - right credentials + # + # When: + # - basic auth + # - no permissions + # (request write to read-only permissions) + # + # Then: + # - Auth NG + (None, + None, + 'test', + 'password', + True, + False, + 'POST', + 'Basic', + 'Basic dGVzdDpwYXNzd29yZA==', + False + ), + # Given: + # - read/write credentials + # - wrong credentials + # + # When: + # - basic auth + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth NG + ('test', + 'password1', + None, + None, + True, + False, + 'POST', + 'Basic', + 'Basic dGVzdDpwYXNzd29yZA==', + False + ), + # Given: + # - read/write credentials + # - read-only credentials + # - right credentials + # + # When: + # - basic auth + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth OK + ('test', + 'password', + 'test', + 'password', + True, + False, + 'POST', + 'Basic', + 'Basic dGVzdDpwYXNzd29yZA==', + True + ), + # Given: + # - read/write credentials + # - right credentials + # + # When: + # - digest auth (md5) + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth OK + ('test', + 'password', + None, + None, + True, + False, + 'POST', + 'Digest-md5', + 'Digest username="test",' + ' realm="protected area",' + ' nonce="1669998243:1d385c2a503eb73bdfff5824eefc3007",' + ' uri="/",' + ' algorithm=MD5,' + ' response="f888d3020d56ac3c81952c96f7115b47",' + ' qop=auth,' + ' nc=00000001,' + ' cnonce="082c875dcb2ca740"', + True + ), + # Given: + # - read/write credentials (empty string) + # - right credentials + # + # When: + # - digest auth (md5) + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth OK + ('', + '', + None, + None, + True, + False, + 'GET', + 'Digest-md5', + 'Digest username="",' + ' realm="protected area",' + ' nonce="1670654640:61b81bfd7435f5844f77e33c69aa001d",' + ' uri="/",' + ' algorithm=MD5,' + ' response="4e38538b9c6244ff7a5cbe3b5a1eacc7",' + ' qop=auth,' + ' nc=00000001,' + ' cnonce="55257458a653e256"', + True + ), + # Given: + # - read-only credentials + # - right credentials + # + # When: + # - digest auth (md5) + # - having permissions + # (request read to read-only permissions) + # + # Then: + # - Auth OK + (None, + None, + 'test', + 'password', + False, + False, + 'POST', + 'Digest-md5', + 'Digest username="test",' + ' realm="protected area",' + ' nonce="1669998243:1d385c2a503eb73bdfff5824eefc3007",' + ' uri="/",' + ' algorithm=MD5,' + ' response="f888d3020d56ac3c81952c96f7115b47",' + ' qop=auth,' + ' nc=00000001,' + ' cnonce="082c875dcb2ca740"', + True + ), + # Given: + # - read-only credentials + # - right credentials + # + # When: + # - digest auth (md5) + # - no permissions + # (request write to read-only permissions) + # + # Then: + # - Auth NG + (None, + None, + 'test', + 'password', + True, + False, + 'POST', + 'Digest-md5', + 'Digest username="test",' + ' realm="protected area",' + ' nonce="1669998243:1d385c2a503eb73bdfff5824eefc3007",' + ' uri="/",' + ' algorithm=MD5,' + ' response="f888d3020d56ac3c81952c96f7115b47",' + ' qop=auth,' + ' nc=00000001,' + ' cnonce="082c875dcb2ca740"', + False + ), + # Given: + # - read/write credentials + # - wrong credentials + # + # When: + # - digest auth (md5) + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth NG + ('test', + 'password1', + None, + None, + True, + False, + 'POST', + 'Digest-md5', + 'Digest username="test",' + ' realm="protected area",' + ' nonce="1669998243:1d385c2a503eb73bdfff5824eefc3007",' + ' uri="/",' + ' algorithm=MD5,' + ' response="f888d3020d56ac3c81952c96f7115b47",' + ' qop=auth,' + ' nc=00000001,' + ' cnonce="082c875dcb2ca740"', + False + ), + # Given: + # - read/write credentials + # - right credentials + # + # When: + # - digest auth (sha256) + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth OK + ('test', + 'password', + None, + None, + True, + False, + 'POST', + 'Digest-sha256', + 'Digest username="test",' + ' realm="protected area",' + ' nonce="1669998369:e3b6f13700a2c135f7c6cf8f129fc4f0",' + ' uri="/",' + ' algorithm=SHA-256,' + ' response="2e168e4fbbe57265a8bc03bb7e0c6357c27e760f089c81489c3039e3b2a31e9d",' + ' qop=auth,' + ' nc=00000002,' + ' cnonce="2b8d329a8571b99a"', + True + ), + # Given: + # - read/write credentials + # - wrong credentials + # + # When: + # - digest auth (sha256) + # - having permissions + # (request write to read/write permissions) + # + # Then: + # - Auth NG + ('test', + 'password1', + None, + None, + True, + False, + 'POST', + 'Digest-sha256', + 'Digest username="test",' + ' realm="protected area",' + ' nonce="1669998369:e3b6f13700a2c135f7c6cf8f129fc4f0",' + ' uri="/",' + ' algorithm=SHA-256,' + ' response="2e168e4fbbe57265a8bc03bb7e0c6357c27e760f089c81489c3039e3b2a31e9d",' + ' qop=auth,' + ' nc=00000002,' + ' cnonce="2b8d329a8571b99a"', + False + ), + # Given: + # - read/write credentials + # + # When: + # - basic auth + # - public read access + # - having permissions + # (request read to read-only permissions) + # - doesn't send credentials + # + # Then: + # - Auth NG + ('test', + 'password', + None, + None, + False, + False, + 'POST', + 'Basic', + '', + False + ), + ]) +def test_handle_auth(mocker, + rw_identifier, + rw_password, + ro_identifier, + ro_password, + require_write, + public_read_access, + request_method, + auth_method, + auth_header, + auth_ok): + """ + Given: + The authentication method and parameters. + + When: + Running script to request the authentication. + + Then: + Validate the right response returns. + """ + mocker.patch.object(demisto, 'params', return_value={ + 'rwCredentials': { + 'identifier': rw_identifier, + 'password': rw_password + }, + 'roCredentials': { + 'identifier': ro_identifier, + 'password': ro_password + }, + 'authenticationMethod': auth_method, + 'publicReadAccess': public_read_access, + 'mimeTypes': None, + 'mergeMimeTypes': True, + 'attachmentExtensions': None, + 'storageProtection': 'read/write', + 'maxStorageSize': None, + 'maxSandboxSize': None, + }) + importlib.reload(WebFileRepository) + + auth_method, _, auth_value = auth_header.partition(' ') + if auth_method == 'Digest': + def __new_nonce(nonce) -> Tuple[int, str]: + gen_time, _, _ = nonce.partition(':') + return int(gen_time), nonce + + auth_params = urllib.request.parse_keqv_list(urllib.request.parse_http_list(auth_value)) + mocker.patch.object(WebFileRepository.NONCE_MANAGER, '_NonceManager__new_nonce', + side_effect=lambda: __new_nonce(auth_params['nonce'])) + + WebFileRepository.NONCE_MANAGER._NonceManager__expires = 60 * 60 * 24 * 3650 + WebFileRepository.NONCE_MANAGER.gen_nonce() + + environ = { + 'REQUEST_METHOD': request_method, + 'PATH_INFO': '/', + 'HTTP_AUTHORIZATION': auth_header + } + request = bottle.LocalRequest(environ) + handler = WebFileRepository.ServiceHandler(WebFileRepository.SETTINGS, + WebFileRepository.MASTER_REPOSITORY) + if require_write: + permission = WebFileRepository.PERMISSION.WRITE + else: + permission = WebFileRepository.PERMISSION.READ + + response = handler.authenticate(request, permission) + if auth_ok: + assert response is None + else: + assert response.status == 401 or '401' in response.status diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_01.json b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_01.json new file mode 100644 index 000000000000..46f5bdb19d6a --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_01.json @@ -0,0 +1,66 @@ +{ + "/b.dat": { + "last-modified": 1669044167, + "data-id": "fec96279-e127-4d17-bbce-b82bd2ecd23b", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/c.dat": { + "last-modified": 1669044167, + "data-id": "39b07866-d77c-4dc5-9aba-8833816e2fdc", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/x/XYZ/\u30a2\u30a4\u30a6\u30a8\u30aa.txt": { + "last-modified": 1669044167, + "data-id": "fc5fa84d-4986-4f0f-aa0b-454fe316733e", + "size": 15, + "data-type": "base85", + "saved-size": 19 + }, + "/x/d.dat": { + "last-modified": 1669044167, + "data-id": "5e334374-8a07-4e95-9b5c-da37b01bb8f0", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/x/\u3042\u3044\u3046\u3048\u304a/e.dat": { + "last-modified": 1669044167, + "data-id": "eb5ffc00-1a80-4326-9485-57c2e36223fb", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/x/\u3042\u3044\u3046\u3048\u304a/f.dat": { + "last-modified": 1669044167, + "data-id": "2704f4a8-cc82-48fa-b4f9-f328b6a4b3dd", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/y/g.dat": { + "last-modified": 1669044167, + "data-id": "08597087-62d7-42e0-955a-8aaada2e8f46", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/z.dat": { + "last-modified": 1669044167, + "data-id": "1d62a08a-ffa9-426e-8b35-966ccca6acbf", + "size": 7198, + "data-type": "gzip+base85", + "saved-size": 99 + }, + "08597087-62d7-42e0-955a-8aaada2e8f46": "XJ=;", + "1d62a08a-ffa9-426e-8b35-966ccca6acbf": "ABzYG$CrC!0{?VvaB^aCb8~lSVgT*TF%19!2m`R61jAE56C50^fF>7q;_-Mq9*@W4@pwEQkH_Qjcsw4D$K&yMo<9Y|90s!<8~^|", + "2704f4a8-cc82-48fa-b4f9-f328b6a4b3dd": "W@cs", + "39b07866-d77c-4dc5-9aba-8833816e2fdc": "V`F0", + "5e334374-8a07-4e95-9b5c-da37b01bb8f0": "WMpI", + "eb5ffc00-1a80-4326-9485-57c2e36223fb": "Wo2a", + "fc5fa84d-4986-4f0f-aa0b-454fe316733e": "7q;_-Mq9*@W4@pwEQkH_Qjcsw4D$K&yMo<9Y|90s!<8~^|", + "2704f4a8-cc82-48fa-b4f9-f328b6a4b3dd": "W@cs", + "39b07866-d77c-4dc5-9aba-8833816e2fdc": "V`F0", + "5e334374-8a07-4e95-9b5c-da37b01bb8f0": "WMpI", + "eb5ffc00-1a80-4326-9485-57c2e36223fb": "Wo2a", + "fec96279-e127-4d17-bbce-b82bd2ecd23b": "Vq#(" +} diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_03.json b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_03.json new file mode 100644 index 000000000000..be0a547b8fde --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/delete_out_03.json @@ -0,0 +1,34 @@ +{ + "/b.dat": { + "last-modified": 1669044167, + "data-id": "fec96279-e127-4d17-bbce-b82bd2ecd23b", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/c.dat": { + "last-modified": 1669044167, + "data-id": "39b07866-d77c-4dc5-9aba-8833816e2fdc", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/y/g.dat": { + "last-modified": 1669044167, + "data-id": "08597087-62d7-42e0-955a-8aaada2e8f46", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/z.dat": { + "last-modified": 1669044167, + "data-id": "1d62a08a-ffa9-426e-8b35-966ccca6acbf", + "size": 7198, + "data-type": "gzip+base85", + "saved-size": 99 + }, + "08597087-62d7-42e0-955a-8aaada2e8f46": "XJ=;", + "1d62a08a-ffa9-426e-8b35-966ccca6acbf": "ABzYG$CrC!0{?VvaB^aCb8~lSVgT*TF%19!2m`R61jAE56C50^fF>7q;_-Mq9*@W4@pwEQkH_Qjcsw4D$K&yMo<9Y|90s!<8~^|", + "39b07866-d77c-4dc5-9aba-8833816e2fdc": "V`F0", + "fec96279-e127-4d17-bbce-b82bd2ecd23b": "Vq#(" +} \ No newline at end of file diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_file.dat b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_file.dat new file mode 100644 index 000000000000..7c4a013e52c7 --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_file.dat @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_01.dat b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_01.dat new file mode 100644 index 000000000000..7c4a013e52c7 --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_01.dat @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_02.dat b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_02.dat new file mode 100644 index 000000000000..d36fdd8d2f27 --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/download_out_02.dat @@ -0,0 +1 @@ +アイウエオ \ No newline at end of file diff --git a/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/integration_ctx_common.json b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/integration_ctx_common.json new file mode 100644 index 000000000000..ee4821359c42 --- /dev/null +++ b/Packs/WebFileRepository/Integrations/WebFileRepository/test_data/integration_ctx_common.json @@ -0,0 +1,74 @@ +{ + "/a.dat": { + "last-modified": 1669044167, + "data-id": "54727b49-b90d-43f5-a2ba-9473fc8714cf", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/b.dat": { + "last-modified": 1669044167, + "data-id": "fec96279-e127-4d17-bbce-b82bd2ecd23b", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/c.dat": { + "last-modified": 1669044167, + "data-id": "39b07866-d77c-4dc5-9aba-8833816e2fdc", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/x/XYZ/\u30a2\u30a4\u30a6\u30a8\u30aa.txt": { + "last-modified": 1669044167, + "data-id": "fc5fa84d-4986-4f0f-aa0b-454fe316733e", + "size": 15, + "data-type": "base85", + "saved-size": 19 + }, + "/x/d.dat": { + "last-modified": 1669044167, + "data-id": "5e334374-8a07-4e95-9b5c-da37b01bb8f0", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/x/\u3042\u3044\u3046\u3048\u304a/e.dat": { + "last-modified": 1669044167, + "data-id": "eb5ffc00-1a80-4326-9485-57c2e36223fb", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/x/\u3042\u3044\u3046\u3048\u304a/f.dat": { + "last-modified": 1669044167, + "data-id": "2704f4a8-cc82-48fa-b4f9-f328b6a4b3dd", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/y/g.dat": { + "last-modified": 1669044167, + "data-id": "08597087-62d7-42e0-955a-8aaada2e8f46", + "size": 3, + "data-type": "base85", + "saved-size": 4 + }, + "/z.dat": { + "last-modified": 1669044167, + "data-id": "1d62a08a-ffa9-426e-8b35-966ccca6acbf", + "size": 7198, + "data-type": "gzip+base85", + "saved-size": 99 + }, + "08597087-62d7-42e0-955a-8aaada2e8f46": "XJ=;", + "1d62a08a-ffa9-426e-8b35-966ccca6acbf": "ABzYG$CrC!0{?VvaB^aCb8~lSVgT*TF%19!2m`R61jAE56C50^fF>7q;_-Mq9*@W4@pwEQkH_Qjcsw4D$K&yMo<9Y|90s!<8~^|", + "2704f4a8-cc82-48fa-b4f9-f328b6a4b3dd": "W@cs", + "39b07866-d77c-4dc5-9aba-8833816e2fdc": "V`F0", + "54727b49-b90d-43f5-a2ba-9473fc8714cf": "VPRn", + "5e334374-8a07-4e95-9b5c-da37b01bb8f0": "WMpI", + "eb5ffc00-1a80-4326-9485-57c2e36223fb": "Wo2a", + "fc5fa84d-4986-4f0f-aa0b-454fe316733e": "k>FZ~xDL^{TOJ zOdn^i`}w2x{q^5oQ}4Wd5z%_1hXU0fU*2ua z0#paW=;})=|%bp#szh0t#Rf!$K~o1S5k41Cs^I3_s?ZjIR?kW`pQA zLJ&T(1(r39^rXE=0CquYJT6kil*)R`Wsh5m^X7z?+o~