ExtractIndicatorsFromWordFile

Used to extract indicators from Word files (DOC, DOCX). The script does not extract data from macros (e.g., embedded code). This automation runs using the default Limited User role, unless you explicitly change the permissions. For more information, see the section about permissions here: - For Cortex XSOAR 6 see https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.x/Cortex-XSOAR-Playbook-Design-Guide/Automations - For Cortex XSOAR 8 Cloud see https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8/Cortex-XSOAR-Cloud-Documentation/Create-a-script - For Cortex XSOAR 8.7 On-prem see https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8.7/Cortex-XSOAR-On-prem-Documentation/Create-a-script

python · Common Scripts

Source

import subprocess

import demistomock as demisto
from CommonServerPython import *
from docx import Document
from docx.opc.constants import RELATIONSHIP_TYPE as RT
from docx.opc.exceptions import PackageNotFoundError

from CommonServerUserPython import *


class WordParser:
    def __init__(self):
        self.res = []  # type: List[Dict[str, str]]
        self.errEntry = {"Type": entryTypes["error"], "ContentsFormat": formats["text"], "Contents": ""}
        self.file_path = ""
        self.file_name = ""
        self.file_type = ""
        self.paragraphs = ""
        self.tables = ""
        self.hyperlinks = ""
        self.core_properties = {}

    def get_file_details(self):
        file_path_data = demisto.getFilePath(demisto.args().get("entryID"))
        self.file_path = file_path_data.get("path")
        self.file_name = file_path_data.get("name")
        file_entry = demisto.dt(self.get_context(), "File(val.EntryID === '{}')".format(demisto.args().get("entryID")))
        if isinstance(file_entry, list):
            file_entry = file_entry[0]

        self.file_type = file_entry.get("Type")

    def convert_doc_to_docx(self):
        output = subprocess.check_output(
            [
                "soffice",
                "--headless",
                "-env:UserInstallation=file:///tmp/.config/extractindicators",
                "--convert-to",
                "docx",
                self.file_path,
            ],
            stderr=subprocess.STDOUT,
        )
        demisto.debug(f"soffice output: [{output!s}]")
        # Requires office-utils docker image
        output_file_name = self.file_name[0 : self.file_name.rfind(".")] + ".docx"
        self.file_path = self.file_path + ".docx"
        try:
            with open(self.file_path, "rb") as f:
                f_data = f.read()
                self.res = fileResult(output_file_name, f_data)
        except OSError:
            return_error("Error: was not able to convert the input file to docx format.")

    def extract_indicators(self):
        try:
            document = Document(self.file_path)
            self.paragraphs = self.get_paragraphs(document)
            self.tables = self.get_tables(document)
            self.hyperlinks = self.get_hyperlinks(document)
            self.get_core_properties(document)
        except PackageNotFoundError:
            self.errEntry["Contents"] = "Input file is not a valid docx/doc file."
            self.res = self.errEntry  # type: ignore
        except BaseException as e:
            self.errEntry["Contents"] = "Error occurred while parsing input file.\nException info: " + str(e)
            self.res = self.errEntry  # type: ignore

    def get_paragraphs(self, document):
        return "\n".join([para.text for para in document.paragraphs])

    def get_tables(self, document):
        all_cells_txt = ""
        if document.tables:
            for table in document.tables:
                for row in table.rows:
                    for cell in row.cells:
                        for para in cell.paragraphs:
                            all_cells_txt += " " + para.text
        return " ".join(all_cells_txt.split())  # Removes extra whitespaces

    def get_core_properties(self, document):
        self.core_properties["author"] = document.core_properties.author
        self.core_properties["category"] = document.core_properties.category
        self.core_properties["comments"] = document.core_properties.comments
        self.core_properties["identifier"] = document.core_properties.identifier
        self.core_properties["keywords"] = document.core_properties.keywords
        self.core_properties["subject"] = document.core_properties.subject
        self.core_properties["title"] = document.core_properties.title

    def get_hyperlinks(self, document):
        all_hyperlinks = ""
        rels = document.part.rels
        for rel in rels:
            if rels[rel].reltype == RT.HYPERLINK:
                all_hyperlinks += rels[rel]._target + " "
        return all_hyperlinks

    def parse_word(self):
        self.get_file_details()
        if self.file_name.lower().endswith(".doc") or "Composite Document File V2 Document" in self.file_type:
            self.convert_doc_to_docx()
            self.extract_indicators()
        elif self.file_name.lower().endswith(".docx") or "Microsoft Word 2007+" in self.file_type:
            self.extract_indicators()
        else:
            return_error("Input file is not a doc file.")

    def get_context(self):
        incident_id = demisto.incident()["id"]
        res = execute_command("getContext", {"id": incident_id})
        return demisto.get(res, "context")


def main():
    # Parsing:
    parser = WordParser()
    try:
        parser.parse_word()
    except subprocess.CalledProcessError as perr:
        return_error(f"ProcessError: exit code: {perr.returncode}. Output: {perr.output}")
    except Exception as e:
        return_error(str(e))

    core_properties_str = " ".join(prop for prop in parser.core_properties.values())
    all_data = " ".join([parser.paragraphs, parser.tables, parser.hyperlinks, core_properties_str])

    # Returning Indicators:
    indicators_hr = demisto.executeCommand("extractIndicators", {"text": all_data})
    return_results(indicators_hr)

    hr_output = tableToMarkdown("Properties", parser.core_properties)
    hr_output += f"### Paragraphs\n{parser.paragraphs}\n"
    hr_output += f"### Tables\n{parser.tables}\n"
    hr_output += f"### Hyperlinks\n{parser.hyperlinks}"

    # Returning all parsed data:
    return_results(CommandResults(readable_output=hr_output))

    # Returning error:
    if parser.res:  # If there was an error:
        contents = parser.res["Contents"]  # type: ignore
        if "Error occurred while parsing input" in contents or "Input file is not a valid" in contents:
            return_results(parser.res)  # Return error too


# python2 uses __builtin__ python3 uses builtins
if __name__ == "__builtin__" or __name__ == "builtins":
    main()

README

Used to extract indicators from Word files (DOC, DOCX).
The script does not extract data from macros (e.g., embedded code).

This automation runs using the default Limited User role, unless you explicitly change the permissions.
For more information, see the section about permissions here: For Cortex XSOAR 6, see the https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.x/Cortex-XSOAR-Playbook-Design-Guide/Automations for Cortex XSOAR 8 Cloud, see the https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8/Cortex-XSOAR-Cloud-Documentation/Create-a-script for Cortex XSOAR 8 On-prem, see the https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/8.7/Cortex-XSOAR-On-prem-Documentation/Create-a-script.

Script Data


Name Description
Script Type python3
Tags parser, autoextract, doc, docx, word
Cortex XSOAR Version 5.0.0

Used In


This script is used in the following playbooks and scripts.

  • Extract Indicators From File - Generic
  • Extract Indicators From File - Generic v2

Inputs


Argument Name Description
entryID The entry ID of the word document to be parsed for indicators. The document can be either in DOC or DOCX format.

Outputs


There are no outputs for this script.