ContentDependencies
Searches a set of playbooks, or all playbooks if no playbook query is provided, and identifies all dependencies between playbooks and automations and outputs either CSV or markdown. WARNING: Should be run on DEV since it may consume 100% CPU for 10 or more minutes if all playbooks are searched. This automation should be run in dev environments. It will consume 100% CPU for 10 minutes or more - depending on the amount of playbook and automation content. It is set to timeout at 15 minutes in Advanced settings. This may need to be increased if content amount is large. It only parses python scripts to see if additional automations/commands are invoked via demisto.executeCommand or execute_command. Javascript automations are not parsed. Command names passed in a variable to demisto.executeCommand or execute_command are not reported. If a python automation fails to parse, an error is reported in the war room: any automations it calls are not reported. Integration commands and builtins are not parsed.
python · Content Testing
Source
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 """ This automation should be run in dev environments. It will consume 100% CPU for 10 - 20 minutes or more - depending on the amount of playbook and automation content. It is set to timeout at 15 minutes in Advanced settings. This may need to be increased if content amount is large. It only parses python scripts to see if additional automations/commands are invoked via demisto.executeCommand or execute_command. Javascript automations are not parsed Command names passed in a variable to demisto.executeCommand or execute_command are not reported. If a python automation fails to parse, an error is reported in the war room: any automations it calls are not reported. Integration commands and builtins are not parsed. """ import ast def GetAutomation(scriptid: str): results = demisto.executeCommand("core-api-post", {"uri": f"/automation/load/{scriptid}"}) if is_error(results): return f"MISSING_SCRIPT_FOR_ID_{scriptid}", "", "" response = results[0]["Contents"]["response"] return response["name"], response["script"], response["type"] def GetAutomationName(scriptid: str) -> str: octets = scriptid.split("-") if len(octets) == 5 and ( len(octets[0]) == 8 and len(octets[1]) == 4 and len(octets[2]) == 4 and len(octets[3]) == 4 and len(octets[4]) == 12 ): results = demisto.executeCommand("core-api-post", {"uri": f"/automation/load/{scriptid}"}) if is_error(results): return f"MISSING_SCRIPT_FOR_ID_{scriptid}" return results[0]["Contents"]["response"]["name"] octets = scriptid.split("|") if len(octets) == 4: return "" return scriptid def CalledAutomation(scrname: str, script: str) -> list: if script == "": return [] try: lines = ast.dump(ast.parse(script), indent="").splitlines() # type: ignore except Exception as ex: return_results(f"CalledAutomation: Error parsing script {scrname} - Error: {ex!s}") return [] watchname = False watchatt = False watcharg = False watchval = False names = [] for lin in lines: if not watchname and "Call" in lin: watchname = True continue if watchname: if "func=Name" in lin: name = lin.split("'")[1] if name == "execute_command": watcharg = True continue if "func=Attribute" in lin: watchatt = True continue if watchatt and "attr=" in lin: name = lin.split("'")[1] watchatt = False if name == "executeCommand": watcharg = True continue if watcharg and "args=[" in lin: watcharg = False watchval = True continue if watchval and "Constant(value=" in lin: parts = lin.split("'") if len(parts) > 1: name = parts[1] watchval = False watchname = False names.append(name) final = [] names = list(set(names)) for n in names: if n != "": final.append(n) return final def GetPlaybooks(query: str) -> list: if query != "": query = " AND " + query playbooks = demisto.executeCommand( "core-api-post", {"uri": "/playbook/search", "body": {"query": f"hidden:F AND deprecated:F {query}"}} )[0]["Contents"]["response"]["playbooks"] return playbooks def GetEntities(playbooks: list) -> dict: entities = {} for p in playbooks: pbname = p["name"] entities[pbname] = {"etype": "playbook", "pcalled": [], "pcalls": [], "scalled": [], "scalls": []} for _key, t in p["tasks"].items(): if t["type"] == "playbook": spbname = t["task"].get("name", "notaskname") if spbname not in entities: entities[spbname] = {"etype": "playbook", "pcalled": [], "pcalls": [], "scalled": [], "scalls": []} entities[pbname]["pcalls"].append(spbname) # type: ignore entities[spbname]["pcalled"].append(pbname) # type: ignore elif "scriptId" in t["task"]: scrname = GetAutomationName(t["task"]["scriptId"]) if scrname != "": scrname, script, stype = GetAutomation(scrname) else: scrname = t["task"]["scriptId"] script = "" stype = "" if scrname not in entities: entities[scrname] = {"etype": "script", "pcalled": [], "pcalls": [], "scalled": [], "scalls": []} entities[pbname]["scalls"].append(scrname) # type: ignore entities[scrname]["pcalled"].append(pbname) # type: ignore if stype == "python": calls = CalledAutomation(scrname, script) else: calls = [] if len(calls) > 0: entities[scrname]["scalls"].extend(calls) # type: ignore for s in calls: if s not in entities: entities[s] = {"etype": "script", "pcalled": [], "pcalls": [], "scalled": [], "scalls": []} entities[s]["scalled"].append(scrname) # type: ignore return entities def PlaybookCsv(key: str, ent: dict) -> str: output = "" calls = list(set(ent["pcalls"])) if len(calls) > 0: for val in calls: output += f"Playbook, {key}, Calls, Playbook, {val}\n" calls = list(set(ent["scalls"])) if len(calls) > 0: for val in calls: output += f"Playbook, {key}, Calls, Automation, {val}\n" calls = list(set(ent["pcalled"])) if len(calls) > 0: for val in calls: output += f"Playbook, {key}, Calledby, Playbook, {val}\n" return output def ScriptCsv(key: str, ent: dict) -> str: output = "" calls = list(set(ent["scalls"])) if len(calls) > 0: for val in calls: output += f"Automation, {key}, Calls, Automation, {val}\n" calls = list(set(ent["scalled"])) if len(calls) > 0: for val in calls: output += f"Automation, {key}, Calledby, Automation, {val}\n" calls = list(set(ent["pcalled"])) if len(calls) > 0: for val in calls: output += f"Automation, {key}, Calledby, Playbook, {val}\n" return output def PlaybookMarkdown(key: str, ent: dict) -> str: output = f"{key}\n\n" calls = list(set(ent["pcalls"])) if len(calls) > 0: for val in calls: output += f" Calls: Playbook: {val}\n" calls = list(set(ent["scalls"])) if len(calls) > 0: for val in calls: output += f" Calls: Automation: {val}\n" calls = list(set(ent["pcalled"])) if len(calls) > 0: for val in calls: output += f" Called by: Playbook: {val}\n" return output def ScriptMarkdown(key: str, ent: dict) -> str: output = f"{key}\n\n" calls = list(set(ent["scalls"])) if len(calls) > 0: for val in calls: output += f" Calls: Automation: {val}\n" calls = list(set(ent["scalled"])) if len(calls) > 0: for val in calls: output += f" Called by: Automation: {val}\n" calls = list(set(ent["pcalled"])) if len(calls) > 0: for val in calls: output += f" Called by: Playbook: {val}\n" return output def main(): try: args = demisto.args() outfmt = args["format"] filename = args.get("filename", "dependencies.txt") pbquery = args.get("query", "") playbooks = GetPlaybooks(pbquery) entities = GetEntities(playbooks) output = "" if outfmt == "markdown" or outfmt == "": output += "### Playbooks\n" for key, ent in entities.items(): if ent["etype"] == "playbook": if outfmt == "csv": output += PlaybookCsv(key, ent) else: output += PlaybookMarkdown(key, ent) if outfmt == "markdown" or outfmt == "": output += "### Automations\n" for key, ent in entities.items(): if ent["etype"] == "script": if outfmt == "csv": output += ScriptCsv(key, ent) else: output += ScriptMarkdown(key, ent) if outfmt == "csv": demisto.results(fileResult(filename, output)) else: field = args.get("fieldname", "").strip() if field != "": demisto.executeCommand("setIncident", {"customFields": json.dumps({field: output})}) else: demisto.results({"ContentsFormat": formats["markdown"], "Contents": output}) except Exception as ex: demisto.error(traceback.format_exc()) return_error(f"ContentDependencies: Exception failed to execute. Error: {ex!s}") if __name__ in ("__main__", "__builtin__", "builtins"): main()