palo_alto_networks_pan_os_edl_management Deprecated
Deprecated. Use the Generic Export Indicators Service integration instead. This integration is still supported however, for customers with over 1000 Firewalls.
Utilities · Palo Alto Networks PAN-OS EDL Management (Deprecated)
Details
| ID | palo_alto_networks_pan_os_edl_management |
|---|---|
| Provider | Palo Alto Networks |
| Category | Utilities |
| From Version | 5.0.0 |
| Docker Image | demisto/openssh:1.0.0.12410 |
| Supported Modules | Agentix |
README
Deprecated. Use the Generic Export Indicators Service integration instead. This integration is still supported however, for customers with over 1000 Firewalls.
This integration requires root access in order to execute ssh commands.
If you’ve configured the server to run Docker images with a non-root internal user make sure to exclude the demisto/openssh Docker image as documented here.
Configure Palo Alto Networks PAN-OS EDL Management in Cortex
| Parameter | Required |
|---|---|
| Hostname or IP of server | True |
| Server port | False |
| SSH credentials to server (username and certificate, see in the credential manager) | True |
| Password | True |
| SSH extra parameters (e.g., “-c ChaCha20”) | False |
| SCP extra parameters (e.g., “-c ChaCha20 -l 8000”) | False |
| Document root (e.g., var/www/html/files) | False |
Commands
You can execute these commands from the 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.
pan-os-edl-get-external-file
Displays the contents of the specified remote file located in the War Room.
Base Command
pan-os-edl-get-external-file
Input
| Argument Name | Description | Required |
|---|---|---|
| file_path | Unique path to the file on a remote server. | Required |
| retries | Number of retries. Default is 5. | Optional |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-get-external-file file_path=test_playbook_list4.txt
Human Readable Output
File Content
List jojo.com koko.com upload.wikimedia.org
pan-os-edl-search-external-file
Searches for a string in a remote file.
Base Command
pan-os-edl-search-external-file
Input
| Argument Name | Description | Required |
|---|---|---|
| file_path | Unique path to the file on a remote server. | Required |
| search_string | String to search for in the remote file. | Required |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-search-external-file file_path=test_playbook_list4.txt search_string=koko
Human Readable Output
Search Results for koko
Result koko.com
pan-os-edl-update
Updates the instance context with the specified list name and list items, and then overrides the path of the remote file with the internal list.
Base Command
pan-os-edl-update
Input
| Argument Name | Description | Required |
|---|---|---|
| list_name | List from the instance context with which to override the remote file. | Required |
| file_path | Unique path to file. | Required |
| verbose | Prints the updated remote file to the War Room. Default is “false”. Possible values are: true, false. Default is false. | Optional |
| list_items | List items. | Required |
| add_or_remove | Whether to add to, or remove from the list. Default is “add”. Possible values are: add, remove. Default is add. | Required |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-update add_or_remove=remove file_path=test_playbook_list4.txt list_items=toto.com list_name=test_playbook_list4 verbose=true
Human Readable Output
Updated File Data
Data jojo.com
koko.com
upload.wikimedia.org
pan-os-edl-update-from-external-file
Updates internal list data with the contents of a remote file.
Base Command
pan-os-edl-update-from-external-file
Input
| Argument Name | Description | Required |
|---|---|---|
| file_path | Unique path to the file on a remote server. | Required |
| list_name | List name. | Required |
| type | Update type. “Merge” adds non-duplicate values, “Override” deletes existing data in the internal list. Default is “merge”. Possible values are: merge, override. Default is merge. | Required |
| verbose | Prints the updated internal list to the War Room. Default is “false”. Possible values are: true, false. Default is false. | Optional |
| retries | Number of retries. Default is 5. | Optional |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-update-from-external-file file_path=test_playbook_list4.txt list_name=test_playbook_list4 type=override
Human Readable Output
Instance context updated successfully
pan-os-edl-delete-external-file
Deletes a file from a remote server.
Base Command
pan-os-edl-delete-external-file
Input
| Argument Name | Description | Required |
|---|---|---|
| file_path | Unique path to the file on a remote server. | Required |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-delete-external-file file_path=test_playbook_list5.txt
Human Readable Output
File deleted successfully
pan-os-edl-print-internal-list
Displays internal list data in the War Room.
Base Command
pan-os-edl-print-internal-list
Input
| Argument Name | Description | Required |
|---|---|---|
| list_name | List name. | Required |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-print-internal-list list_name=test_playbook_list4
Human Readable Output
List items
test_playbook_list4 jojo.com koko.com upload.wikimedia.org
pan-os-edl-dump-internal-list
Dumps (copies) instance context to either the incident context or a file.
Base Command
pan-os-edl-dump-internal-list
Input
| Argument Name | Description | Required |
|---|---|---|
| destination | List data destination. Default is “file”. Possible values are: file, incident_context. Default is file. | Required |
| list_name | List name. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| PANOSEDL.ListItems | string | Items of the internal list. |
| PANOSEDL.ListName | string | Name of the internal list. |
Command Example
!pan-os-edl-dump-internal-list list_name=test_playbook_list4 destination=incident_context
Context Example
{
"PANOSEDL": {
"ListItems": [
"jojo.com",
"koko.com",
"upload.wikimedia.org"
],
"ListName": "test_playbook_list4"
}
}
Human Readable Output
List items
test_playbook_list4 jojo.com koko.com upload.wikimedia.org
pan-os-edl-list-internal-lists
Displays instance context list names.
Base Command
pan-os-edl-list-internal-lists
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
Command Example
#### Human Readable Output
>### Instance context Lists
>
>|List names|
>|---|
>| test_playbook_list4 |
### pan-os-edl-search-internal-list
***
Search for a string in internal list.
#### Base Command
`pan-os-edl-search-internal-list`
#### Input
| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| list_name | Name of list. | Required |
| search_string | String to search for in the remote file. | Required |
#### Context Output
There is no context output for this command.
#### Command Example
```!pan-os-edl-search-internal-list list_name=test_playbook_list4 search_string=koko.com```
#### Human Readable Output
>Search string koko.com is in the internal list test_playbook_list4.
### pan-os-edl-compare
***
Compares internal list and external file contents.
#### Base Command
`pan-os-edl-compare`
#### Input
| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| list_name | List name. | Required |
| file_path | Unique path to the file on a remote server. | Required |
| retries | Number of retries. Default is 5. | Optional |
#### Context Output
There is no context output for this command.
#### Command Example
```!pan-os-edl-compare file_path=test_playbook_list4.txt list_name=list_name=test_playbook_list4```
#### Human Readable Output
>List was not found in instance context.
### pan-os-edl-get-external-file-metadata
***
Gets metadata for an external file.
#### Base Command
`pan-os-edl-get-external-file-metadata`
#### Input
| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| file_path | Unique path to the file on a remote server. | Required |
#### Context Output
| **Path** | **Type** | **Description** |
| --- | --- | --- |
| PANOSEDL.FileName | String | Name of the external file. |
| PANOSEDL.Size | Number | File size. |
| PANOSEDL.NumberOfLines | Number | Number of lines. |
| PANOSEDL.LastModified | String | Date that the file was last modified. |
#### Command Example
```!pan-os-edl-get-external-file-metadata file_path=test_playbook_list4.txt```
#### Context Example
```json
{
"PANOSEDL": {
"FileName": "test_playbook_list4.txt",
"LastModified": "2021-07-11 06:46:21.290803188",
"NumberOfLines": 3,
"Size": 38
}
}
Human Readable Output
File metadata
FileName Size NumberOfLines LastModified test_playbook_list4.txt 38 3 2021-07-11 06:46:21.290803188
pan-os-edl-update-internal-list
Updates the instance context with the specified list name and list items.
Base Command
pan-os-edl-update-internal-list
Input
| Argument Name | Description | Required |
|---|---|---|
| list_name | The list from the instance context to update. | Required |
| list_items | An array of list items. | Required |
| verbose | Whether to print the updated remote file to the War Room. Can ve “true” or “false”. Default is “false”. Possible values are: true, false. Default is false. | Optional |
| add_or_remove | Whether to add to, or remove from the list. Can be “add” or “remove”. Default is “add”. Possible values are: add, remove. Default is add. | Required |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-update-internal-list add_or_remove=add list_items=toto.com list_name=test_playbook_list4
Human Readable Output
Instance context updated successfully.
pan-os-edl-update-external-file
Updates a remote file with the contents of an internal list.
Base Command
pan-os-edl-update-external-file
Input
| Argument Name | Description | Required |
|---|---|---|
| file_path | Unique path to the file on a remote server. | Required |
| list_name | List name. | Required |
| verbose | Whether to add to, or remove from the list. Can be “add” or “remove”. Default is “add”. Possible values are: true, false. Default is false. | Optional |
Context Output
There is no context output for this command.
Command Example
!pan-os-edl-update-external-file file_path=test_playbook_list4.txt list_name=test_playbook_list4 verbose=true
Human Readable Output
Updated File Data
Data jojo.com
koko.com
toto.com
upload.wikimedia.org
Configuration parameters
hostname— Hostname or IP of server (required)port— Server portAuthentication— SSH credentials to server (username and certificate, see in the credential manager) (required)ssh_extra_params— SSH extra parameters (e.g., "-c ChaCha20")scp_extra_params— SCP extra parameters (e.g., "-c ChaCha20 -l 8000")document_root— Document root (e.g., var/www/html/files)
Commands (13)
-
pan-os-edl-compareCompares internal list and external file contents.
-
pan-os-edl-delete-external-fileDeletes a file from a remote server.
-
pan-os-edl-dump-internal-listDumps (copies) instance context to either the incident context or a file.
-
pan-os-edl-get-external-fileDisplays the contents of the specified remote file located in the War Room.
-
pan-os-edl-get-external-file-metadataGets metadata for an external file.
-
pan-os-edl-list-internal-listsDisplays instance context list names.
-
pan-os-edl-print-internal-listDisplays internal list data in the War Room.
-
pan-os-edl-search-external-fileSearches for a string in a remote file.
-
pan-os-edl-search-internal-listSearch for a string in internal list.
-
pan-os-edl-updateUpdates the instance context with the specified list name and list items, and then overrides the path of the remote file with the internal list.
-
pan-os-edl-update-external-fileUpdates a remote file with the contents of an internal list.
-
pan-os-edl-update-from-external-fileUpdates internal list data with the contents of a remote file.
-
pan-os-edl-update-internal-listUpdates the instance context with the specified list name and list items.
import shutil import subprocess import tempfile from urllib.parse import urlparse, urlunparse from CommonServerPython import * ''' GLOBALS ''' HOSTNAME = '' USERNAME = '' PORT = '' SSH_EXTRA_PARAMS = '' SCP_EXTRA_PARAMS = '' DOCUMENT_ROOT = '' CERTIFICATE_FILE = tempfile.NamedTemporaryFile(delete=False, mode='w') INTEGRATION_COMMAND_NAME = 'pan-os-edl' def initialize_instance(params: Dict[str, str]) -> None: global HOSTNAME, USERNAME, PORT, SSH_EXTRA_PARAMS, SCP_EXTRA_PARAMS, DOCUMENT_ROOT, CERTIFICATE_FILE authentication = params.get('Authentication', {}) # type: ignore HOSTNAME = str(params.get('hostname', '')) # type: ignore USERNAME = str(authentication.get('identifier', '')) # type: ignore PORT = str(params.get('port')) if params.get('port', '') and len(params.get('port')) > 0 else '' # type: ignore SSH_EXTRA_PARAMS = params.get('ssh_extra_params').split() if params.get( # type: ignore 'ssh_extra_params') else None SCP_EXTRA_PARAMS = params.get('scp_extra_params').split() if params.get( # type: ignore 'scp_extra_params') else None DOCUMENT_ROOT = f'/{params.get("document_root")}' if params.get('document_root') else '' create_certificate_file(authentication) def create_certificate_file(authentication) -> None: password = authentication.get('password', None) certificate = None if 'credentials' in authentication and 'sshkey' in authentication['credentials'] and len( authentication['credentials']['sshkey']) > 0: certificate = authentication.get('credentials', None).get('sshkey') if certificate: CERTIFICATE_FILE.write(certificate) CERTIFICATE_FILE.flush() os.chmod(CERTIFICATE_FILE.name, 0o400) elif password: # check that password field holds a certificate and not a password if password.find('-----') == -1: raise DemistoException('Password parameter must contain a certificate.') # split certificate by dashes password_list = password.split('-----') # replace spaces with newline characters password_fixed = '-----'.join(password_list[:2] + [password_list[2].replace(' ', '\n')] + password_list[3:]) CERTIFICATE_FILE.write(password_fixed) CERTIFICATE_FILE.flush() os.chmod(CERTIFICATE_FILE.name, 0o400) else: raise DemistoException('To connect to the remote server, provide a certificate.') ''' UTILS ''' def ssh_execute(command: str): if PORT and SSH_EXTRA_PARAMS: param_list = ['ssh', '-o', 'StrictHostKeyChecking=no', '-i', CERTIFICATE_FILE.name, '-p', PORT] + SSH_EXTRA_PARAMS + [USERNAME + '@' + HOSTNAME, command] # type: ignore result = subprocess.run(param_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) elif PORT: result = subprocess.run( ['ssh', '-o', 'StrictHostKeyChecking=no', '-i', CERTIFICATE_FILE.name, '-p', PORT, USERNAME + '@' + HOSTNAME, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) elif SSH_EXTRA_PARAMS: param_list = ['ssh', '-o', 'StrictHostKeyChecking=no', '-i', CERTIFICATE_FILE.name] + SSH_EXTRA_PARAMS + [USERNAME + '@' + HOSTNAME, command] # type: ignore result = subprocess.run(param_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) else: result = subprocess.run( ['ssh', '-o', 'StrictHostKeyChecking=no', '-i', CERTIFICATE_FILE.name, USERNAME + '@' + HOSTNAME, command], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) if result.returncode != 0: if result.stderr: if result.stderr.find("Warning: Permanently added") != -1: return result.stdout # ignore addition of new hosts warnings elif result.stderr.find("Permission denied") != -1: raise DemistoException( 'Permission denied, check your username and certificate.\n' + 'Got error: ' + result.stderr) else: raise DemistoException(result.stderr) elif command.find('grep') != -1 and result.returncode == 1: # a search command that did not find any value demisto.results({ 'Type': 11, 'Contents': 'Search string was not found in the external file path given.', 'ContentsFormat': formats['text'] }) sys.exit(0) else: raise DemistoException(f'Command failed with exit status:{str(result.returncode)}') return result.stdout def scp_execute(file_name: str, file_path: str): if SCP_EXTRA_PARAMS: param_list = ['scp', '-o', 'StrictHostKeyChecking=no', '-i', CERTIFICATE_FILE.name] + SCP_EXTRA_PARAMS + [file_name, # type: ignore f'{USERNAME}@{HOSTNAME}:\'{file_path}\''] result = subprocess.run(param_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) else: param_list = ['scp', '-o', 'StrictHostKeyChecking=no', '-i', CERTIFICATE_FILE.name, file_name, f'{USERNAME}@{HOSTNAME}:\'{file_path}\''] result = subprocess.run(param_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) if result.returncode != 0: if result.stderr: if result.stderr.find("Warning: Permanently added") != -1: return True # ignore addition of new hosts warnings else: raise DemistoException(result.stderr) else: raise DemistoException(f'Command failed with exit status:{str(result.returncode)}') else: return True def parse_url(item: str) -> str: """ Parse url if in url form to valid EDL form - without http / https Args: item(str): Item to parse. Returns: str: parsed item, if URL returned without http / https Examples: >>> parse_url('http://google.com') 'google.com' >>> parse_url('https://google.com') 'google.com' >>> parse_url('https://google.com/hello_world') 'google.com/hello_world' >>> parse_url('not url') 'not url' """ try: url_obj = urlparse(item)._replace(scheme='') return urlunparse(url_obj).replace('//', '') except ValueError: return item def parse_items(items: str) -> List[str]: """ Parse list of item to update, parsing steps: 1. Remove http and https from Args: items(str): items for update Returns: list: list of parsed items. """ return [parse_url(item) for item in argToList(items)] ''' COMMANDS ''' def edl_get_external_file(file_path: str, retries: int = 1) -> str: command = f'cat \'{file_path}\'' while retries > 0: result = ssh_execute(command) # counting newlines as in some edge cases the external web server returns the file content intermittently # with newline as every other char num_lines = float(result.count('\n')) if num_lines > len(result) / 3: demisto.info(f'The number of newlines chars in the file is too big. Try number {retries} before failure.') retries -= 1 else: return result # if we get here, we failed as the file contains too many newlines to be valid raise DemistoException('The file contains too many newlines to be valid. ' 'Please check the file contents on the external web server manually.') def edl_get_external_file_command(args: dict): """ Get external file from web-server and prints to the war room """ file_path = str(args.get('file_path', '')) retries = int(args.get('retries', '1')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) result = edl_get_external_file(file_path, retries) sorted_list = sorted(result.split('\n')) md = tableToMarkdown('File Content:', sorted_list, headers=['List']) demisto.results({ 'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': md }) def edl_search_external_file(file_path: str, search_string: str): return ssh_execute(f'grep \'{search_string}\' \'{file_path}\'') def edl_search_external_file_command(args: dict): """ Search the external file and return all matching entries to Warroom """ file_path: str = str(args.get('file_path', '')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) search_string: str = str(args.get('search_string', '')) result = edl_search_external_file(file_path, search_string) sorted_list = sorted(result.split('\n')) md = tableToMarkdown(f'Search Results for {search_string}:', sorted_list, headers=['Result']) demisto.results({ 'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': md }) def edl_update_external_file(file_path: str, list_name: str, verbose: bool): dict_of_lists = demisto.getIntegrationContext() list_data = sorted(dict_of_lists.get(list_name)) file_name = file_path.rsplit('/', 1)[-1] if not file_name.endswith('.txt'): file_name += '.txt' try: with open(file_name, 'w') as file: file.write("\n".join(list_data)) success = scp_execute(file_name, file_path) finally: shutil.rmtree(file_name, ignore_errors=True) if not success: raise DemistoException('External file was not updated successfully.') else: if verbose: external_file_items = ssh_execute(f'cat \'{file_path}\'') if external_file_items: md = tableToMarkdown('Updated File Data:', external_file_items, headers=['Data']) else: md = 'External file has no items.' else: md = 'External file updated successfully.' demisto.results({ 'Type': entryTypes['note'], 'Contents': md, 'ContentsFormat': formats['markdown'] }) def edl_update_external_file_command(args: dict): """ Overrides external file path with internal list """ file_path: str = str(args.get('file_path', '')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) list_name: str = str(args.get('list_name', '')) verbose = args.get('verbose') == 'true' edl_update_external_file(file_path, list_name, verbose) def edl_update_internal_list(list_name: str, list_items: list, add: bool, verbose: bool): dict_of_lists = demisto.getIntegrationContext() if not dict_of_lists: demisto.debug('PAN-OS EDL Management integration context is empty.') dict_of_lists = {list_name: list_items} if verbose: md = tableToMarkdown('List items:', list_items, headers=[list_name]) else: md = 'Instance context updated successfully.' else: if not dict_of_lists.get(list_name, None) and not add: raise Exception(f'Cannot remove items from an empty list: {list_name}.') if dict_of_lists.get(list_name, None): if add: chosen_list = dict_of_lists.get(list_name) if not isinstance(chosen_list, list): chosen_list = [chosen_list] list_items = list(set(chosen_list + list_items)) else: # remove list_items = [item for item in dict_of_lists.get(list_name) if item not in list_items] if not add and len(list_items) == 0: # delete list from instance context, can happen only upon remove of objects demisto.debug(f'PAN-OS EDL Management deleting {list_name} from the integration context.') dict_of_lists.pop(list_name, None) md = 'List is empty, deleted from instance context.' else: # update list in instance context, can happen upon removal or addition of objects sorted_list = sorted(list_items) dict_of_lists.update({list_name: sorted_list}) if verbose: md = tableToMarkdown('List items:', sorted_list, headers=[list_name]) else: md = 'Instance context updated successfully.' if not dict_of_lists: # to be removed, debugging purposes only demisto.debug('PAN-OS EDL Management updating an empty object to the integration context.') demisto.debug(f'PAN-OS EDL Management updating {list_name} with {len(list_items)} in the integration context.') demisto.setIntegrationContext(dict_of_lists) demisto.results({ 'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': md }) def edl_update_internal_list_command(args: dict): """ Updates the instance context with the list name and items given """ list_name: str = str(args.get('list_name', '')) list_items: list = argToList(str(args.get('list_items', ''))) if args.get('add_or_remove') not in ['add', 'remove']: raise Exception('add_or_remove argument is not \'add\' neither \'remove\'.') add = args.get('add_or_remove') == 'add' verbose = args.get('verbose') == 'true' edl_update_internal_list(list_name, list_items, add, verbose) def edl_update(args: dict): """ Updates the instance context with the list name and items given Overrides external file path with internal list """ file_path: str = str(args.get('file_path', '')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) # Parse list items list_name: str = str(args.get('list_name', '')) list_items = parse_items(items=str(args.get('list_items', ''))) if args.get('add_or_remove') not in ['add', 'remove']: raise DemistoException('add_or_remove argument is neither \'add\' nor \'remove\'.') add = args.get('add_or_remove') == 'add' verbose = args.get('verbose') == 'true' # update internal list edl_update_internal_list(list_name, list_items, add, verbose) # scp internal list to file_path edl_update_external_file(file_path, list_name, verbose) def edl_update_from_external_file(list_name: str, file_path: str, type_: str, retries: int): dict_of_lists = demisto.getIntegrationContext() list_data = dict_of_lists.get(list_name, None) file_data = edl_get_external_file(file_path, retries) sorted_file_data = sorted(file_data.split('\n')) if list_data: set_internal = set(list_data) set_external = set(sorted_file_data) set_external.discard('') if type_ == 'merge': unified = set_internal.union(set_external) list_data_new = list(unified) else: # type_ == 'override' list_data_new = list(set_external) sorted_list_data_new = sorted(list_data_new) dict_of_lists.update({list_name: sorted_list_data_new}) demisto.setIntegrationContext(dict_of_lists) return sorted_list_data_new else: dict_of_lists.update({list_name: sorted_file_data}) demisto.setIntegrationContext(dict_of_lists) return sorted_file_data def edl_update_from_external_file_command(args: dict): """ Updates internal list data with external file contents """ file_path: str = str(args.get('file_path', '')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) list_name: str = str(args.get('list_name', '')) type_: str = args.get('type', 'false') verbose: bool = args.get('verbose') == 'true' retries: int = int(args.get('retries', '1')) list_data_new = edl_update_from_external_file(list_name, file_path, type_, retries) if verbose: md = tableToMarkdown('List items:', list_data_new, headers=[list_name]) else: md = 'Instance context updated successfully' demisto.results({ 'Type': entryTypes['note'], 'Contents': md, 'ContentsFormat': formats['markdown'] }) def edl_delete_external_file(file_path: str) -> str: ssh_execute(f'rm -f \'{file_path}\'') return 'File deleted successfully' def edl_delete_external_file_command(args: dict): """ Delete external file """ file_path = str(args.get('file_path', '')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) result = edl_delete_external_file(file_path) demisto.results({ 'Type': entryTypes['note'], 'Contents': result, 'ContentsFormat': formats['text'] }) def edl_list_internal_lists_command(): """ List all instance context lists """ dict_of_lists = demisto.getIntegrationContext() list_names = sorted(list(dict_of_lists.keys())) md = tableToMarkdown('Instance context Lists:', list_names, headers=['List names']) demisto.results({ 'ContentsFormat': formats['markdown'], 'Type': entryTypes['note'], 'Contents': md }) def edl_search_internal_list_command(args: dict): """ Search a string on internal list """ list_name = args.get('list_name') search_string = args.get('search_string') dict_of_lists = demisto.getIntegrationContext() list_data = dict_of_lists.get(list_name, None) if not list_data: demisto.results({ 'Type': 11, 'Contents': f'List {list_name} was not found in the instance context.', 'ContentsFormat': formats['text'] }) elif search_string in list_data: demisto.results({ 'Type': entryTypes['note'], 'Contents': f'Search string {search_string} is in the internal list {list_name}.', 'ContentsFormat': formats['text'] }) else: demisto.results({ 'Type': 11, 'Contents': f'Search string {search_string} was not found in the instance context list {list_name}.', 'ContentsFormat': formats['text'] }) def edl_print_internal_list_command(args: dict): """ Print to the war room instance context list """ list_name = str(args.get('list_name', '')) dict_of_lists = demisto.getIntegrationContext() list_data = sorted(dict_of_lists.get(list_name, None)) if not list_data: demisto.results({ 'Type': 11, 'Contents': 'List was not found in instance context.', 'ContentsFormat': formats['text'] }) else: md = tableToMarkdown('List items:', list_data, headers=[list_name]) demisto.results({ 'Type': entryTypes['note'], 'Contents': md, 'ContentsFormat': formats['markdown'] }) def edl_dump_internal_list_command(args: dict): """ Dumps an instance context list to either a file or incident context """ destination = args.get('destination') list_name = str(args.get('list_name', '')) dict_of_lists = demisto.getIntegrationContext() list_data = sorted(dict_of_lists.get(list_name, [])) if not list_data: demisto.results({ 'Type': 11, 'Contents': 'List was not found in instance context or has no data.', 'ContentsFormat': formats['text'] }) sys.exit(0) if destination == 'file': # dump list as file internal_file_path = demisto.uniqueFile() try: with open(internal_file_path, 'w') as f: f.write("\n".join(list_data)) file_type = entryTypes['entryInfoFile'] with open(internal_file_path, 'rb') as file: file_entry = fileResult(internal_file_path, file.read(), file_type) demisto.results(file_entry) finally: shutil.rmtree(internal_file_path, ignore_errors=True) else: # update incident context md = tableToMarkdown('List items:', list_data, headers=[list_name]) ec = { 'ListName': list_name, 'ListItems': list_data } demisto.results({ 'Type': entryTypes['note'], 'Contents': md, 'ContentsFormat': formats['markdown'], 'EntryContext': { "PANOSEDL(val.ListName == obj.ListName)": ec } }) def edl_compare_command(args: dict): list_name = str(args.get('list_name', '')) file_path = str(args.get('file_path', '')) retries = int(args.get('retries', '1')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) dict_of_lists = demisto.getIntegrationContext() list_data = dict_of_lists.get(list_name, None) if not list_data: demisto.results({ 'Type': 11, 'Contents': 'List was not found in instance context.', 'ContentsFormat': formats['text'] }) sys.exit(0) file_data = edl_get_external_file(file_path, retries) if not file_data: demisto.results({ 'Type': 11, 'Contents': 'file was not found in external web-server.', 'ContentsFormat': formats['text'] }) sys.exit(0) set_internal = set(list_data) set_external = set(file_data.split('\n')) set_external.discard('') unique_internal = set_internal - set_external unique_external = set_external - set_internal md = '' if unique_external: md += '### Warning: External file contains values that are not in the internal Demisto list.\n' md += '#### If these changes are unexpected, check who has permission to write to the external file.\n' md += tableToMarkdown('', list(unique_external), headers=[file_path.rsplit('/')[-1]]) if unique_internal: md += '### Warning: Internal list contains values that are not in the external file.\n' md += '#### If these changes are unexpected, check who has permission to write to the external file.\n' md += tableToMarkdown('', list(unique_internal), headers=[list_name]) if len(md) == 0: md = 'Internal list and external file have the same values.' demisto.results({ 'Type': 11 if unique_external or unique_internal else entryTypes['note'], 'Contents': md, 'ContentsFormat': formats['markdown'], }) def edl_get_external_file_metadata_command(args: dict): file_path = str(args.get('file_path', '')) if DOCUMENT_ROOT: file_path = os.path.join(DOCUMENT_ROOT, file_path) result = ssh_execute(f'stat \'{file_path}\'') file_size = int(result.split("Size: ", 1)[1].split(" ", 1)[0]) file_name = file_path.split("/")[-1] if len(file_name) < 0: file_name = file_path last_modified_parts = result.split("Change: ", 1)[1].split(" ", 2)[0:2] last_modified = ' '.join(last_modified_parts) number_of_lines = int(ssh_execute(f'wc -l < \'{file_path}\'')) + 1 metadata_outputs = { 'FileName': file_name, 'Size': file_size, 'LastModified': last_modified, 'NumberOfLines': number_of_lines } demisto.results({ 'Type': entryTypes['note'], 'Contents': result, 'ContentsFormat': formats['text'], 'ReadableContentsFormat': formats['markdown'], 'HumanReadable': tableToMarkdown('File metadata:', metadata_outputs, ['FileName', 'Size', 'NumberOfLines', 'LastModified'], removeNull=True), 'EntryContext': {"PANOSEDL(val.FileName == obj.FileName)": metadata_outputs} }) ''' EXECUTION ''' def main() -> None: command = demisto.command() args = demisto.args() params = demisto.params() LOG(f'command is {command}') commands = { f'{INTEGRATION_COMMAND_NAME}-get-external-file': edl_get_external_file_command, f'{INTEGRATION_COMMAND_NAME}-search-external-file': edl_search_external_file_command, f'{INTEGRATION_COMMAND_NAME}-update-internal-list': edl_update_internal_list_command, f'{INTEGRATION_COMMAND_NAME}-update-external-file': edl_update_external_file_command, f'{INTEGRATION_COMMAND_NAME}-update': edl_update, f'{INTEGRATION_COMMAND_NAME}-update-from-external-file': edl_update_from_external_file_command, f'{INTEGRATION_COMMAND_NAME}-delete-external-file': edl_delete_external_file_command, f'{INTEGRATION_COMMAND_NAME}-search-internal-list': edl_search_internal_list_command, f'{INTEGRATION_COMMAND_NAME}-print-internal-list': edl_print_internal_list_command, f'{INTEGRATION_COMMAND_NAME}-dump-internal-list': edl_dump_internal_list_command, f'{INTEGRATION_COMMAND_NAME}-compare': edl_compare_command, f'{INTEGRATION_COMMAND_NAME}-get-external-file-metadata': edl_get_external_file_metadata_command, } try: initialize_instance(params=params) if command == 'test-module': ssh_execute('echo 1') return_results('ok') elif command == 'pan-os-edl-list-internal-lists': edl_list_internal_lists_command() elif command in commands: commands[command](args) else: raise NotImplementedError(f'Command "{command}" is not implemented in {INTEGRATION_COMMAND_NAME}.') except Exception as err: if str(err).find('warning') != -1: LOG(str(err)) else: demisto.error(traceback.format_exc()) # print the traceback return_error(str(err), err) finally: shutil.rmtree(CERTIFICATE_FILE.name, ignore_errors=True) LOG.print_log() if __name__ in ('__main__', '__builtin__', 'builtins'): main()