From 445c25e205faa4a1ed14708429639976377fe98e Mon Sep 17 00:00:00 2001 From: Peter Clemenko Date: Wed, 21 Oct 2020 21:39:59 -0400 Subject: [PATCH 1/5] appsdk kali --- backend/Dockerfile | 3 + backend/app_sdk_kali/Dockerfile | 19 + backend/app_sdk_kali/LICENSE | 21 + backend/app_sdk_kali/README.md | 16 + backend/app_sdk_kali/__init__.py | 0 backend/app_sdk_kali/app_base.py | 1169 +++++++++++++++++++++++ backend/app_sdk_kali/build.sh | 13 + backend/app_sdk_kali/requirements.txt | 2 + backend/app_sdk_kali/static_baseline.py | 76 ++ docker-compose.yml | 1 + functions/onprem/README.md | 8 + 11 files changed, 1328 insertions(+) create mode 100644 backend/app_sdk_kali/Dockerfile create mode 100644 backend/app_sdk_kali/LICENSE create mode 100644 backend/app_sdk_kali/README.md create mode 100644 backend/app_sdk_kali/__init__.py create mode 100644 backend/app_sdk_kali/app_base.py create mode 100644 backend/app_sdk_kali/build.sh create mode 100644 backend/app_sdk_kali/requirements.txt create mode 100644 backend/app_sdk_kali/static_baseline.py diff --git a/backend/Dockerfile b/backend/Dockerfile index d203ed78..a1887c93 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -14,6 +14,8 @@ ADD ./go-app/go.mod /app # Required files for code generation ADD ./app_sdk/app_base.py /app_sdk ADD ./app_sdk/static_baseline.py /app_sdk +ADD ./app_sdk_kali/app_base.py /app_sdk_kali +ADD ./app_sdk_kali/static_baseline.py /app_sdk_kali ADD ./app_gen /app_gen RUN go get -v @@ -28,6 +30,7 @@ FROM alpine:3.12 COPY --from=builder /app/ /app COPY --from=builder /app_sdk/ /app_sdk +COPY --from=builder /app_sdk_kali/ /app_sdk_kali COPY --from=builder /app_gen/ /app_gen COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt diff --git a/backend/app_sdk_kali/Dockerfile b/backend/app_sdk_kali/Dockerfile new file mode 100644 index 00000000..af7fd24f --- /dev/null +++ b/backend/app_sdk_kali/Dockerfile @@ -0,0 +1,19 @@ +FROM kalilinux/kali-rolling as base + +FROM base as builder + +RUN apt-get update +RUN apt-get dist-upgrade -y +RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y + +RUN mkdir /install +WORKDIR /install + +COPY requirements.txt /requirements.txt +RUN pip install --prefix="/install" -r /requirements.txt + +FROM base + +COPY --from=builder /install /usr/local +COPY __init__.py /app/walkoff_app_sdk/__init__.py +COPY app_base.py /app/walkoff_app_sdk/app_base.py diff --git a/backend/app_sdk_kali/LICENSE b/backend/app_sdk_kali/LICENSE new file mode 100644 index 00000000..ce11f6f3 --- /dev/null +++ b/backend/app_sdk_kali/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Frikkylikeme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/backend/app_sdk_kali/README.md b/backend/app_sdk_kali/README.md new file mode 100644 index 00000000..754392ff --- /dev/null +++ b/backend/app_sdk_kali/README.md @@ -0,0 +1,16 @@ +# app_sdk.py +This is the SDK used for apps to behave like they should. +To change it in the backend, upload it to Buckets/shuffler.appspot.com/generated_apps/baseline. + +# static_baseline.py +It's used for python code generation and should be under MIT. Has to be located here because it's used by the backend. + +## If you want to update apps.. PS: downloads from docker hub do overrides.. :) +1. Write your code & check if runtime works +2. Build app_base image +3. docker rm $(docker ps -aq) # Remove all stopped containers +4. Delete the specific app's Docker image (docker rmi frikky/shuffle:...) +5. Rebuild the Docker image (click load in GUI?) + +# LICENSE +Everything in here is MIT, not AGPLv3 as indicated by the license. diff --git a/backend/app_sdk_kali/__init__.py b/backend/app_sdk_kali/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/app_sdk_kali/app_base.py b/backend/app_sdk_kali/app_base.py new file mode 100644 index 00000000..e896f67c --- /dev/null +++ b/backend/app_sdk_kali/app_base.py @@ -0,0 +1,1169 @@ +import os +import sys +import re +import time +import json +import logging +import requests +import urllib.parse + +class AppBase: + """ The base class for Python-based apps in Shuffle, handles logging and callbacks configurations""" + __version__ = None + app_name = None + + def __init__(self, redis=None, logger=None, console_logger=None):#, docker_client=None): + self.logger = logger if logger is not None else logging.getLogger("AppBaseLogger") + self.redis=redis + self.console_logger = logger if logger is not None else logging.getLogger("AppBaseLogger") + + # apikey is for the user / org + # authorization is for the specific workflow + self.url = os.getenv("CALLBACK_URL", "https://shuffler.io") + self.action = os.getenv("ACTION", "") + self.authorization = os.getenv("AUTHORIZATION", "") + self.current_execution_id = os.getenv("EXECUTIONID", "") + self.full_execution = os.getenv("FULL_EXECUTION", "") + + if isinstance(self.action, str): + self.action = json.loads(self.action) + + def send_result(self, action_result, headers, stream_path): + if action_result["status"] == "EXECUTING": + action_result["status"] = "FAILURE" + + # I wonder if this actually works + self.logger.info("Before last stream result") + try: + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Result: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + except requests.exceptions.ConnectionError as e: + self.logger.exception(e) + return + except TypeError as e: + self.logger.exception(e) + action_result["status"] = "FAILURE" + action_result["result"] = "POST error: %s" % e + self.logger.info("Before typeerror stream result") + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Result: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + + async def execute_action(self, action): + # FIXME - add request for the function STARTING here. Use "results stream" or something + # PAUSED, AWAITING_DATA, PENDING, COMPLETED, ABORTED, EXECUTING, SUCCESS, FAILURE + + # !!! Let this line stay - its used for some horrible codegeneration / stitching !!! # + #STARTCOPY + stream_path = "/api/v1/streams" + action_result = { + "action": action, + "authorization": self.authorization, + "execution_id": self.current_execution_id, + "result": "", + "started_at": int(time.time()), + "status": "EXECUTING" + } + self.logger.info("ACTION RESULT (start): %s", action_result) + + if len(self.action) == 0: + print("ACTION env not defined") + action_result["result"] = "Error in setup ENV: ACTION not defined" + self.send_result(action_result, headers, stream_path) + return + if len(self.authorization) == 0: + print("AUTHORIZATION env not defined") + action_result["result"] = "Error in setup ENV: AUTHORIZATION not defined" + self.send_result(action_result, headers, stream_path) + return + if len(self.current_execution_id) == 0: + print("EXECUTIONID env not defined") + action_result["result"] = "Error in setup ENV: EXECUTIONID not defined" + self.send_result(action_result, headers, stream_path) + return + + headers = { + "Content-Type": "application/json", + "Authorization": "Bearer %s" % self.authorization + } + + # Add async logger + # self.console_logger.handlers[0].stream.set_execution_id() + #self.logger.info("Before initial stream result") + try: + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Workflow: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + except requests.exceptions.ConnectionError as e: + print("Connectionerror: %s" % e) + + action_result["result"] = "Bad setup during startup: %s" % e + self.send_result(action_result, headers, stream_path) + return + + # Verify whether there are any parameters with ACTION_RESULT required + # If found, we get the full results list from backend + fullexecution = {} + if len(self.full_execution) == 0: + print("NO EXECUTION - LOADING!") + try: + tmpdata = { + "authorization": self.authorization, + "execution_id": self.current_execution_id + } + + self.logger.info("Before FULLEXEC stream result") + ret = requests.post( + "%s/api/v1/streams/results" % (self.url), + headers=headers, + json=tmpdata + ) + + if ret.status_code == 200: + fullexecution = ret.json() + else: + self.logger.info("Error: Data: ", ret.json()) + self.logger.info("Error with status code for results. Crashing because ACTION_RESULTS or WORKFLOW_VARIABLE can't be handled. Status: %d" % ret.status_code) + action_result["result"] = "Bad result from backend: %d" % ret.status_code + self.send_result(action_result, headers, stream_path) + return + except requests.exceptions.ConnectionError as e: + self.logger.info("Connectionerror: %s" % e) + action_result["result"] = "Connection error during startup: %s" % e + self.send_result(action_result, headers, stream_path) + return + else: + try: + fullexecution = json.loads(self.full_execution) + except json.decoder.JSONDecodeError as e: + print("Json decode execution error: %s" % e) + action_result["result"] = "Json error during startup: %s" % e + self.send_result(action_result, headers, stream_path) + return + + print("") + + + self.logger.info("AFTER FULLEXEC stream result") + + # Gets the value at the parenthesis level you want + def parse_nested_param(string, level): + """ + Generate strings contained in nested (), indexing i = level + """ + if len(re.findall("\(", string)) == len(re.findall("\)", string)): + LeftRightIndex = [x for x in zip( + [Left.start()+1 for Left in re.finditer('\(', string)], + reversed([Right.start() for Right in re.finditer('\)', string)]))] + + elif len(re.findall("\(", string)) > len(re.findall("\)", string)): + return parse_nested_param(string + ')', level) + elif len(re.findall("\(", string)) < len(re.findall("\)", string)): + return parse_nested_param('(' + string, level) + + else: + return 'Failed to parse params' + + try: + return [string[LeftRightIndex[level][0]:LeftRightIndex[level][1]]] + except IndexError: + return [string[LeftRightIndex[level+1][0]:LeftRightIndex[level+1][1]]] + + # Finds the deepest level parenthesis in a string + def maxDepth(S): + current_max = 0 + max = 0 + n = len(S) + + # Traverse the input string + for i in range(n): + if S[i] == '(': + current_max += 1 + + if current_max > max: + max = current_max + elif S[i] == ')': + if current_max > 0: + current_max -= 1 + else: + return -1 + + # finally check for unbalanced string + if current_max != 0: + return -1 + + return max-1 + + # Specific type parsing + def parse_type(data, thistype): + if data == None: + return "Empty" + + if "int" in thistype or "number" in thistype: + try: + return int(data) + except ValueError: + print("ValueError while casting %s" % data) + return data + if "lower" in thistype: + return data.lower() + if "upper" in thistype: + return data.upper() + if "trim" in thistype: + return data.strip() + if "strip" in thistype: + return data.strip() + if "split" in thistype: + return data.split() + if "len" in thistype or "length" in thistype: + tmp = "" + try: + tmp = json.loads(data) + except: + pass + + if isinstance(tmp, list): + return str(len(tmp)) + + return str(len(data)) + if "parse" in thistype: + splitvalues = [] + default_error = """Error. Expected syntax: parse(["hello","test1"],0:1)""" + if "," in data: + splitvalues = data.split(",") + + for item in range(len(splitvalues)): + splitvalues[item] = splitvalues[item].strip() + else: + return default_error + + lastsplit = [] + if ":" in splitvalues[-1]: + lastsplit = splitvalues[-1].split(":") + else: + try: + lastsplit = [int(splitvalues[-1])] + except ValueError: + return default_error + + try: + parsedlist = ",".join(splitvalues[0:-1]) + if len(lastsplit) > 1: + tmp = json.loads(parsedlist)[int(lastsplit[0]):int(lastsplit[1])] + else: + tmp = json.loads(parsedlist)[lastsplit[0]] + + print(tmp) + return tmp + except IndexError as e: + return default_error + + # Parses the INNER value and recurses until everything is done + def parse_wrapper(data): + try: + if "(" not in data or ")" not in data: + return data + except TypeError: + return data + + #print("Running %s" % data) + + # Look for the INNER wrapper first, then move out + wrappers = ["int", "number", "lower", "upper", "trim", "strip", "split", "parse", "len", "length"] + found = False + for wrapper in wrappers: + if wrapper not in data.lower(): + continue + + found = True + break + + if not found: + return data + + # Do stuff here. + innervalue = parse_nested_param(data, maxDepth(data)-0) + outervalue = parse_nested_param(data, maxDepth(data)-1) + print("INNER: ", innervalue) + print("OUTER: ", outervalue) + + if outervalue != innervalue: + #print("Outer: ", outervalue, " inner: ", innervalue) + for key in range(len(innervalue)): + # Replace OUTERVALUE[key] with INNERVALUE[key] in data. + print("Replace %s with %s in %s" % (outervalue[key], innervalue[key], data)) + data = data.replace(outervalue[key], innervalue[key]) + else: + for thistype in wrappers: + if thistype.lower() not in data.lower(): + continue + + parsed_value = parse_type(innervalue[0], thistype.lower()) + return parsed_value + + print("DATA: %s\n" % data) + return parse_wrapper(data) + + def parse_wrapper_start(data): + newdata = [] + newstring = "" + record = True + paranCnt = 0 + for char in data: + if char == "(": + paranCnt += 1 + + if not record: + record = True + + if record: + newstring += char + + if paranCnt == 0 and char == " ": + newdata.append(newstring) + newstring = "" + record = True + + if char == ")": + paranCnt -= 1 + + if paranCnt == 0: + record = False + + if len(newstring) > 0: + newdata.append(newstring) + + #print(newdata) + parsedlist = [] + non_string = False + for item in newdata: + ret = parse_wrapper(item) + if not isinstance(ret, str): + non_string = True + + parsedlist.append(ret) + + if len(parsedlist) > 0 and not non_string: + return " ".join(parsedlist) + elif len(parsedlist) == 1 and non_string: + return parsedlist[0] + else: + #print("Casting back to string because multi: ", parsedlist) + newlist = [] + for item in parsedlist: + try: + newlist.append(str(item)) + except ValueError: + newlist.append("parsing_error") + return " ".join(newlist) + + # Parses JSON loops and such down to the item you're looking for + def recurse_json(basejson, parsersplit): + match = "#(\d+):?-?([0-9a-z]+)?#?" + print("Split: %s\n%s" % (parsersplit, basejson)) + try: + outercnt = 0 + + # Loops over split values + for value in parsersplit: + print("VALUE: %s\n" % value) + actualitem = re.findall(match, value, re.MULTILINE) + if value == "#": + newvalue = [] + for innervalue in basejson: + # 1. Check the next item (message) + # 2. Call this function again + + try: + ret, is_loop = recurse_json(innervalue, parsersplit[outercnt+1:]) + except IndexError: + # Only in here if it's the last loop without anything in it? + ret, is_loop = recurse_json(innervalue, parsersplit[outercnt:]) + + newvalue.append(ret) + + # Magical way of returning which makes app sdk identify + # it as multi execution + return newvalue, True + elif len(actualitem) > 0: + # FIXME: This is absolutely not perfect. + print("In recursion v2: ", actualitem) + + is_loop = True + newvalue = [] + firstitem = actualitem[0][0] + seconditem = actualitem[0][1] + + # Means it's a single item -> continue + if seconditem == "": + print("In first - handling %s", seconditem) + tmpitem = basejson[int(firstitem)] + try: + newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) + except IndexError: + newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) + else: + if seconditem == "max": + seconditem = len(basejson) + if seconditem == "min": + seconditem = 0 + + newvalue = [] + for i in range(int(firstitem), int(seconditem)): + # 1. Check the next item (message) + # 2. Call this function again + print("Base: %s" % basejson[i]) + + try: + ret, is_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) + except IndexError: + print("INDEXERROR: ", parsersplit[outercnt]) + #ret = innervalue + ret, is_loop = recurse_json(innervalue, parsersplit[outercnt:]) + + print(ret) + #exit() + newvalue.append(ret) + + return newvalue, is_loop + + # FIXME: Add specific loop for other indexes + else: + #print("BEFORE NORMAL VALUE: ", basejson, value) + if len(value) == 0: + return basejson, False + + if isinstance(basejson[value], str): + print(f"LOADING STRING '%s' AS JSON" % basejson[value]) + try: + basejson = json.loads(basejson[value]) + except json.decoder.JSONDecodeError as e: + print("RETURNING BECAUSE '%s' IS A NORMAL STRING" % basejson[value]) + return basejson[value], False + else: + basejson = basejson[value] + + outercnt += 1 + + except KeyError as e: + print("Lower keyerror: %s" % e) + #return basejson + #return "KeyError: Couldn't find key: %s" % e + + return basejson, False + + # Takes a workflow execution as argument + # Returns a string if the result is single, or a list if it's a list + def get_json_value(execution_data, input_data): + parsersplit = input_data.split(".") + actionname = parsersplit[0][1:].replace(" ", "_", -1) + #Actionname: Start_node + + print(f"Actionname: {actionname}") + + # 1. Find the action + baseresult = "" + actionname_lower = actionname.lower() + try: + if actionname_lower == "exec" or actionname_lower == "webhook" or actionname_lower == "schedule" or actionname_lower == "userinput" or actionname_lower == "email_trigger" or actionname_lower == "trigger": + baseresult = execution_data["execution_argument"] + else: + for result in execution_data["results"]: + resultlabel = result["action"]["label"].replace(" ", "_", -1).lower() + if resultlabel.lower() == actionname_lower: + baseresult = result["result"] + break + + print("BEFORE VARIABLES!") + if len(baseresult) == 0: + try: + #print("WF Variables: %s" % execution_data["workflow"]["workflow_variables"]) + for variable in execution_data["workflow"]["workflow_variables"]: + variablename = variable["name"].replace(" ", "_", -1).lower() + + if variablename.lower() == actionname_lower: + baseresult = variable["value"] + break + except KeyError as e: + print("KeyError wf variables: %s" % e) + pass + except TypeError as e: + print("TypeError wf variables: %s" % e) + pass + + print("BEFORE EXECUTION VAR") + if len(baseresult) == 0: + try: + #print("Execution Variables: %s" % execution_data["execution_variables"]) + for variable in execution_data["execution_variables"]: + variablename = variable["name"].replace(" ", "_", -1).lower() + if variablename.lower() == actionname_lower: + baseresult = variable["value"] + break + except KeyError as e: + print("KeyError exec variables: %s" % e) + pass + except TypeError as e: + print("TypeError exec variables: %s" % e) + pass + + except KeyError as error: + print(f"KeyError in JSON: {error}") + + print(f"After first trycatch") + + # 2. Find the JSON data + if len(baseresult) == 0: + return "", False + + if len(parsersplit) == 1: + return baseresult, False + + baseresult = baseresult.replace("\'", "\"") + basejson = {} + try: + basejson = json.loads(baseresult) + except json.decoder.JSONDecodeError as e: + return baseresult, False + + data, is_loop = recurse_json(basejson, parsersplit[1:]) + parseditem = data + if is_loop: + print("DATA IS A LOOP - SHOULD WRAP") + if parsersplit[-1] == "#": + print("SET DATA WRAPPER TO NORMAL!") + parseditem = "${SHUFFLE_NO_SPLITTER%s}$" % json.dumps(data) + else: + # Return value: ${id[12345, 45678]}$ + print("SET DATA WRAPPER TO %s!" % parsersplit[-1]) + parseditem = "${%s%s}$" % (parsersplit[-1], json.dumps(data)) + + return parseditem, is_loop + + # Parses parameters sent to it and returns whether it did it successfully with the values found + def parse_params(action, fullexecution, parameter): + # Skip if it starts with $? + jsonparsevalue = "$." + is_loop = False + + # Matches with space in the first part, but not in subsequent parts. + # JSON / yaml etc shouldn't have spaces in their fields anyway. + match = ".*?([$]{1}([a-zA-Z0-9 _-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,})" + + # Regex to find all the things + if parameter["variant"] == "STATIC_VALUE": + data = parameter["value"] + actualitem = re.findall(match, data, re.MULTILINE) + #self.logger.debug(f"\n\nHandle static data with JSON: {data}\n\n") + #self.logger.info("STATIC PARSED: %s" % actualitem) + if len(actualitem) > 0: + print("ACTUAL: ", actualitem) + for replace in actualitem: + try: + to_be_replaced = replace[0] + except IndexError: + continue + + # Handles for loops etc. + value, is_loop = get_json_value(fullexecution, to_be_replaced) + + if isinstance(value, str): + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + elif isinstance(value, dict): + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + else: + print("Unknown type %s" % type(value)) + try: + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + except json.decoder.JSONDecodeError as e: + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + + + if parameter["variant"] == "WORKFLOW_VARIABLE": + print("Handling workflow variable") + found = False + try: + for item in fullexecution["workflow"]["workflow_variables"]: + if parameter["action_field"] == item["name"]: + found = True + parameter["value"] = item["value"] + break + except KeyError as e: + print("KeyError WF variable 1: %s" % e) + pass + except TypeError as e: + print("TypeError WF variables 1: %s" % e) + pass + + if not found: + try: + for item in fullexecution["execution_variables"]: + if parameter["action_field"] == item["name"]: + parameter["value"] = item["value"] + break + except KeyError as e: + print("KeyError WF variable 2: %s" % e) + pass + except TypeError as e: + print("TypeError WF variables 2: %s" % e) + pass + + elif parameter["variant"] == "ACTION_RESULT": + # FIXME - calculate value based on action_field and $if prominent + # FIND THE RIGHT LABEL + # GET THE LABEL'S RESULT + + tmpvalue = "" + self.logger.info("ACTION FIELD: %s" % parameter["action_field"]) + + fullname = "$" + if parameter["action_field"] == "Execution Argument": + tmpvalue = fullexecution["execution_argument"] + fullname += "exec" + else: + fullname += parameter["action_field"] + + self.logger.info("PRE Fullname: %s" % fullname) + + if parameter["value"].startswith(jsonparsevalue): + fullname += parameter["value"][1:] + #else: + # fullname = "$%s" % parameter["action_field"] + + self.logger.info("Fullname: %s" % fullname) + actualitem = re.findall(match, fullname, re.MULTILINE) + self.logger.info("ACTION PARSED: %s" % actualitem) + if len(actualitem) > 0: + for replace in actualitem: + try: + to_be_replaced = replace[0] + except IndexError: + print("Nothing to replace?: " % e) + continue + + # This will never be a loop aka multi argument + parameter["value"] = to_be_replaced + + value, is_loop = get_json_value(fullexecution, to_be_replaced) + print("Loop: %s" % is_loop) + if isinstance(value, str): + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + elif isinstance(value, dict): + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + else: + print("Unknown type %s" % type(value)) + try: + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + except json.decoder.JSONDecodeError as e: + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + + return "", parameter["value"], is_loop + + def run_validation(sourcevalue, check, destinationvalue): + self.logger.info("Checking %s %s %s" % (sourcevalue, check, destinationvalue)) + + if check == "=" or check.lower() == "equals": + if sourcevalue.lower() == destinationvalue.lower(): + return True + elif check == "!=" or check.lower() == "does not equal": + if sourcevalue.lower() != destinationvalue.lower(): + return True + elif check.lower() == "startswith": + if sourcevalue.lower().startswith(destinationvalue.lower()): + return True + elif check.lower() == "endswith": + if sourcevalue.lower().endswith(destinationvalue.lower()): + return True + elif check.lower() == "contains": + if destinationvalue.lower() in sourcevalue.lower(): + return True + elif check.lower() == "larger than": + try: + if sourcevalue.isdigit() and destinationvalue.isdigit(): + if int(sourcevalue) > int(destinationvalue): + return True + except AttributeError as e: + self.logger.error("Condition larger than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + return False + elif check.lower() == "smaller than": + try: + if sourcevalue.isdigit() and destinationvalue.isdigit(): + if int(sourcevalue) < int(destinationvalue): + return True + except AttributeError as e: + self.logger.error("Condition smaller than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + return False + else: + self.logger.info("Condition: can't handle %s yet. Setting to true" % check) + + return False + + def check_branch_conditions(action, fullexecution): + # relevantbranches = workflow.branches where destination = action + try: + if fullexecution["workflow"]["branches"] == None or len(fullexecution["workflow"]["branches"]) == 0: + return True, "" + except KeyError: + return True, "" + + relevantbranches = [] + for branch in fullexecution["workflow"]["branches"]: + if branch["destination_id"] != action["id"]: + continue + + # Remove anything without a condition + try: + if (branch["conditions"]) == 0 or branch["conditions"] == None: + continue + except KeyError: + continue + + self.logger.info("Relevant conditions: %s" % branch["conditions"]) + successful_conditions = [] + failed_conditions = [] + for condition in branch["conditions"]: + self.logger.info("Getting condition value of %s" % condition) + + # Parse all values first here + sourcevalue = condition["source"]["value"] + check, sourcevalue, is_loop = parse_params(action, fullexecution, condition["source"]) + if check: + return False, "Failed condition: %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check) + + + #sourcevalue = sourcevalue.encode("utf-8") + sourcevalue = parse_wrapper_start(sourcevalue) + destinationvalue = condition["destination"]["value"] + + check, destinationvalue, is_loop = parse_params(action, fullexecution, condition["destination"]) + if check: + return False, "Failed condition: %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check) + + #destinationvalue = destinationvalue.encode("utf-8") + destinationvalue = parse_wrapper_start(destinationvalue) + available_checks = [ + "=", + "equals", + "!=", + "does not equal", + ">", + "larger than", + "<", + "less than", + ">=", + "<=", + "startswith", + "endswith", + "contains", + "re", + "matches regex", + ] + + # FIXME - what should I do here? + if not condition["condition"]["value"] in available_checks: + self.logger.info("Skipping %s %s %s because %s is invalid." % (sourcevalue, condition["condition"]["value"], destinationvalue, condition["condition"]["value"])) + continue + + #print(destinationvalue) + # NEGATE + validation = run_validation(sourcevalue, condition["condition"]["value"], destinationvalue) + + # Configuration = negated because of WorkflowAppActionParam.. + try: + if condition["condition"]["configuration"]: + validation = not validation + except KeyError: + pass + + if not validation: + self.logger.info("Failed condition check for %s %s %s." % (sourcevalue, condition["condition"]["value"], destinationvalue)) + return False, "Failed condition: %s %s %s" % (sourcevalue, condition["condition"]["value"], destinationvalue) + + + # Make a general parser here, at least to get param["name"] = param["value"] in maparameter[string]string + #for condition in branch.conditons: + + return True, "" + + # Checks whether conditions are met, otherwise set + branchcheck, tmpresult = check_branch_conditions(action, fullexecution) + if not branchcheck: + self.logger.info("Failed one or more branch conditions.") + action_result["result"] = tmpresult + action_result["status"] = "FAILURE" + try: + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Result: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + except requests.exceptions.ConnectionError as e: + self.logger.exception(e) + + print("\n\nRETURNING BECAUSE A BRANCH FAILED\n\n") + return + + # Replace name cus there might be issues + # Not doing lower() as there might be user-made functions + actionname = action["name"] + if " " in actionname: + actionname.replace(" ", "_", -1) + #if action.generated: + # actionname = actionname.lower() + + # Runs the actual functions + try: + func = getattr(self, actionname, None) + if func == None: + self.logger.debug("Failed executing %s because func is None." % actionname) + action_result["status"] = "FAILURE" + action_result["result"] = "Function %s doesn't exist." % actionname + elif callable(func): + try: + if len(action["parameters"]) < 1: + result = await func() + else: + # Potentially parse JSON here + # FIXME - add potential authentication as first parameter(s) here + # params[parameter["name"]] = parameter["value"] + #print(fullexecution["authentication"] + # What variables are necessary here tho hmm + + params = {} + try: + for item in action["authentication"]: + print("AUTH: ", key, value) + params[item["key"]] = item["value"] + except KeyError: + print("No authentication specified!") + pass + #action["authentication"] + + # calltimes is used to handle forloops in the app itself. + # 2 kinds of loop - one in gui with one app each, and one like this, + # which is super fast, but has a bad overview (potentially good tho) + calltimes = 1 + result = "" + + all_executions = [] + + # Multi_parameter has the data for each. variable + minlength = 0 + multi_parameters = json.loads(json.dumps(params)) + multiexecution = False + multi_execution_lists = [] + for parameter in action["parameters"]: + check, value, is_loop = parse_params(action, fullexecution, parameter) + + if check: + raise "Value check error: %s" % Exception(check) + + # Custom format for ${name[0,1,2,...]}$ + #submatch = "([${]{2}([0-9a-zA-Z_-]+)(\[.*\])[}$]{2})" + submatch = "([${]{2}([0-9a-zA-Z_-]+)(\[.*\])[}$]{2})" + actualitem = re.findall(submatch, value, re.MULTILINE) + try: + if action["skip_multicheck"]: + print("Skipping multicheck") + actualitem = [] + except KeyError: + pass + + print("Return value: %s" % value) + actionname = action["name"] + #print("Multicheck ", actualitem) + print("Actual item: %s" % actualitem) + if len(actualitem) > 0: + multiexecution = True + + # Loop WITHOUT JSON variables go here. + # Loop WITH variables go in else. + if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER": + print("Pre replacement: %s" % actualitem[0][2]) + tmpitem = value + + replacement = actualitem[0][2] + if replacement.startswith("\"") and replacement.endswith("\""): + replacement = replacement[1:len(replacement)-1] + + replacement = replacement.replace("\'", "\"", -1) + print("POST replacement: %s" % replacement) + + json_replacement = replacement + try: + json_replacement = json.loads(replacement) + except json.decoder.JSONDecodeError as e: + print("JSON error singular: %s" % e) + + if len(json_replacement) > minlength: + minlength = len(json_replacement) + + tmpitem = tmpitem.replace(actualitem[0][0], replacement, 1) + params[parameter["name"]] = tmpitem + multi_execution_lists.append(json_replacement) + multi_parameters[parameter["name"]] = json_replacement + + #print("LENGTH OF ARR: %d" % len(resultarray)) + #print("RESULTARRAY: %s" % resultarray) + print("MULTI finished: %s" % replacement) + else: + + # This is here to handle for loops within variables.. kindof + # 1. Find the length of the longest array + # 2. Build an array with the base values based on parameter["value"] + # 3. Get the n'th value of the generated list from values + # 4. Execute all n answers + replacements = {} + for replace in actualitem: + try: + to_be_replaced = replace[0] + actualitem = replace[2] + except IndexError: + continue + + try: + itemlist = json.loads(actualitem) + if len(itemlist) > minlength: + minlength = len(itemlist) + except json.decoder.JSONDecodeError as e: + print("JSON Error: %s in %s" % (e, actualitem)) + + replacements[to_be_replaced] = actualitem + + # This is a result array for JUST this value.. + # What if there are more? + resultarray = [] + for i in range(0, minlength): + tmpitem = json.loads(json.dumps(parameter["value"])) + for key, value in replacements.items(): + replacement = json.dumps(json.loads(value)[i]) + if replacement.startswith("\"") and replacement.endswith("\""): + replacement = replacement[1:len(replacement)-1] + #except json.decoder.JSONDecodeError as e: + + #print("REPLACING %s with %s" % (key, replacement)) + #replacement = parse_wrapper_start(replacement) + tmpitem = tmpitem.replace(key, replacement, -1) + + resultarray.append(tmpitem) + + # With this parameter ready, add it to... a greater list of parameters. Rofl + print("LENGTH OF ARR: %d" % len(resultarray)) + print("RESULTARRAY: %s" % resultarray) + if resultarray not in multi_execution_lists: + multi_execution_lists.append(resultarray) + + multi_parameters[parameter["name"]] = resultarray + else: + # Parses things like int(value) + self.logger.info("Parsing wrapper data for %s" % value) + value = parse_wrapper_start(value) + + params[parameter["name"]] = value + multi_parameters[parameter["name"]] = value + + # Fix lists here + print("CHECKING multi execution list!") + if len(multi_execution_lists) > 0: + print("\n Multi execution list has more data: %d" % len(multi_execution_lists)) + filteredlist = [] + for listitem in multi_execution_lists: + if listitem in filteredlist: + continue + + filteredlist.append(listitem) + + #print("New list length: %d" % len(filteredlist)) + if len(filteredlist) > 1: + print("Calculating new multi-loop length with %d lists" % len(filteredlist)) + tmplength = 1 + for innerlist in filteredlist: + print("List length: %d. %d*%d" % (len(innerlist), len(innerlist), tmplength)) + tmplength = len(innerlist)*tmplength + + minlength = tmplength + + print("New multi execution length: %d\n" % tmplength) + + # FIXME - this is horrible, but works for now + #for i in range(calltimes): + if not multiexecution: + print("APP_SDK DONE: Starting NORMAL execution of function") + newres = await func(**params) + #print("NEWRES: ", newres) + if isinstance(newres, str): + result += newres + else: + try: + result += str(newres) + except ValueError: + result += "Failed autocasting. Can't handle %s type from function. Must be string" % type(newres) + print("Can't handle type %s value from function" % (type(newres))) + print("POST NEWRES RESULT: ", result) + else: + print("APP_SDK DONE: Starting MULTI execution with values %s of length %d" % (multi_parameters, minlength)) + # 1. Use number of executions based on the arrays being similar + # 2. Find the right value from the parsed multi_params + results = [] + json_object = False + for i in range(0, minlength): + # To be able to use the results as a list: + baseparams = json.loads(json.dumps(multi_parameters)) + # {'call': ['GoogleSafebrowsing_2_0', 'VirusTotal_GetReport_3_0']} + # 1. Check if list length is same as minlength + # 2. If NOT same length, duplicate based on length of array + # arraylength = 3 ["1", "2", "3"] + # arraylength = 4 ["1", "2", "3", "4"] + # minlength = 12 - 12/3 = 4 per item = ["1", "1", "1", "1", "2", "2", ...] + + try: + firstlist = True + for key, value in baseparams.items(): + + if isinstance(value, list): + try: + newvalue = value[i] + except IndexError: + pass + + if len(value) != minlength and len(value) > 0: + newarray = [] + print("VALUE: ", value) + additiontime = minlength/len(value) + print("Bad length for value: %d - should be %d. Additiontime: %d" % (len(value), minlength, additiontime)) + if firstlist: + print("Running normal list (FIRST)") + for subvalue in value: + for number in range(int(additiontime)): + newarray.append(subvalue) + else: + #print("Running secondary lists") + ## 1. Set up length of array + ## 2. Put values spread out + # FIXME: This works well, except if lists are same length + newarray = [""] * minlength + + cnt = 0 + for number in range(int(additiontime)): + for subvaluerange in range(len(value)): + # newlocation = number+(additiontime*subvaluerange) + # print("%d+(%d*%d) = %d. VAL: %s" % (number, additiontime, subvaluerange, newlocation, value[subvaluerange])) + # Reverse if same length? + if int(minlength/len(value)) == len(value): + tmp = int(len(value)-subvaluerange-1) + print("NEW: %d" % tmp) + newarray[cnt] = value[tmp] + else: + newarray[cnt] = value[subvaluerange] + cnt += 1 + + #print("Newarray =", newarray) + newvalue = newarray[i] + firstlist = False + + baseparams[key] = newvalue + except IndexError as e: + print("IndexError: %s" % e) + baseparams[key] = "IndexError: %s" % e + except KeyError as e: + print("KeyError: %s" % e) + baseparams[key] = "KeyError: %s" % e + + print("Running with params %s" % baseparams) + ret = await func(**baseparams) + if isinstance(ret, dict) or isinstance(ret, list): + results.append(ret) + json_object = True + else: + ret = ret.replace("\"", "\\\"", -1) + + try: + results.append(json.loads(ret)) + json_object = True + except json.decoder.JSONDecodeError as e: + #print("Json: %s" % e) + results.append(ret) + + #print("Inner ret parsed: %s" % ret) + + # Dump the result as a string of a list + #print("RESULTS: %s" % results) + if isinstance(results, list): + print("JSON OBJECT? ", json_object) + if json_object: + result = json.dumps(results) + else: + result = "[" + for item in results: + try: + json.loads(item) + result += item + except json.decoder.JSONDecodeError as e: + # Common nested issue which puts " around everything + try: + tmpitem = item.replace("\\\"", "\"", -1) + json.loads(tmpitem) + result += tmpitem + + except: + result += "\"%s\"" % item + + result += ", " + + result = result[:-2] + result += "]" + else: + print("Normal result?") + result = results + + print("RESULT: %s" % result) + action_result["status"] = "SUCCESS" + action_result["result"] = str(result) + if action_result["result"] == "": + action_result["result"] = result + + self.logger.debug(f"Executed {action['label']}-{action['id']} with result: {result}") + #self.logger.debug(f"Data: %s" % action_result) + except TypeError as e: + print("TypeError issue: %s" % e) + action_result["status"] = "FAILURE" + action_result["result"] = "TypeError: %s" % str(e) + else: + print("Function %s doesn't exist?" % action["name"]) + self.logger.error(f"App {self.__class__.__name__}.{action['name']} is not callable") + action_result["status"] = "FAILURE" + action_result["result"] = "Function %s is not callable." % actionname + + except Exception as e: + print(f"Failed to execute: {e}") + self.logger.exception(f"Failed to execute {e}-{action['id']}") + action_result["status"] = "FAILURE" + action_result["result"] = f"General exception: {e}" + + action_result["completed_at"] = int(time.time()) + + # Send the result :) + self.send_result(action_result, headers, stream_path) + return + + + #STOPCOPY + # !!! Let the above line stay - its used for some horrible codegeneration / stitching !!! # + + @classmethod + async def run(cls): + """ Connect to Redis and HTTP session, await actions """ + logging.basicConfig(format="{asctime} - {name} - {levelname}:{message}", style='{') + logger = logging.getLogger(f"{cls.__name__}") + logger.setLevel(logging.DEBUG) + print("Started execution!!") + + app = cls(redis=None, logger=logger, console_logger=logger) + + # Authorization for the app/function to control the workflow + # Function will crash if its wrong, which it probably should. + + await app.execute_action(app.action) diff --git a/backend/app_sdk_kali/build.sh b/backend/app_sdk_kali/build.sh new file mode 100644 index 00000000..78c76ced --- /dev/null +++ b/backend/app_sdk_kali/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash +NAME=app_sdk_kali +VERSION=0.7.3 + +docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force +docker build . -t frikky/shuffle:$NAME -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION + +#docker push frikky/$NAME:$VERSION +#docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION +#docker push ghcr.io/frikky/$NAME:$VERSION + +docker push frikky/shuffle:$NAME +docker push ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/app_sdk_kali/requirements.txt b/backend/app_sdk_kali/requirements.txt new file mode 100644 index 00000000..804abb1b --- /dev/null +++ b/backend/app_sdk_kali/requirements.txt @@ -0,0 +1,2 @@ +requests +urllib3 diff --git a/backend/app_sdk_kali/static_baseline.py b/backend/app_sdk_kali/static_baseline.py new file mode 100644 index 00000000..152131cd --- /dev/null +++ b/backend/app_sdk_kali/static_baseline.py @@ -0,0 +1,76 @@ +import os +import sys +import time +import logging +import requests + +# Goal here: +# * Make an app from WALKOFF able to run without app_base.py from WALKOFF +# # How: +# * Make it rely 100% on INPUT throug HTTP invocations instead of redis READS +# # But really, how? +# * Make a WORKER that reads the queue, and reuses a function + +# Here to get it global +apikey = "" +try: + apikey = os.environ["FUNCTION_APIKEY"] +except KeyError: + pass + +# Authorize the execution +def authorization(request): + # This is basically my issue, but it enforces the use of an internal API key for execution + try: + apikey = os.environ["FUNCTION_APIKEY"] + except KeyError: + return f"Internal server error", 500 + + + # Check API key from ENV authentication + authentication = request.headers.get("Authorization") + if authentication == None: return f"Unauthorized", 401 + + apikey_split = authentication.split(" ") + if apikey_split[0] != "Bearer" or len(apikey_split) != 2: + return f"Apikey error", 401 + + if apikey != apikey_split[1]: + return f"Unauthorized", 401 + + return run(request) + +class AppBase: + """ The base class for Python-based Walkoff applications, handles Redis and logging configurations. """ + __version__ = None + app_name = None + + def __init__(self, redis=None, logger=None, console_logger=None):#, docker_client=None): + self.logger = logger if logger is not None else logging.getLogger("AppBaseLogger") + self.redis=redis + self.console_logger=console_logger + self.current_execution_id = None + self.url = "https://shuffler.io" + self.apikey = apikey + + @classmethod + async def run(cls, action): + """ Connect to Redis and HTTP session, await actions """ + logging.basicConfig(format="{asctime} - {name} - {levelname}:{message}", style='{') + logger = logging.getLogger(f"{cls.__name__}") + logger.setLevel(logging.DEBUG) + + app = cls(redis=None, logger=logger, console_logger=logger) + + # Authorization for the app/function to control the workflow + # Function will crash if its wrong, which it probably should. + + await app.execute_action(action) + + async def execute_action(self, action): + # FIXME - add request for the function STARTING here. Use "results stream" or something + # PAUSED, AWAITING_DATA, PENDING, COMPLETED, ABORTED, EXECUTING, SUCCESS, FAILURE + + self.authorization = action["authorization"] + self.execution_id = action["execution_id"] + self.current_execution_id = action["execution_id"] diff --git a/docker-compose.yml b/docker-compose.yml index 6d92e85a..8457834a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,7 @@ services: - /var/run/docker.sock:/var/run/docker.sock environment: - SHUFFLE_APP_SDK_VERSION=0.6.0 + - SHUFFLE_APP_SDK_KALI_VERSION=0.6.0 - SHUFFLE_WORKER_VERSION=0.6.0 - ORG_ID=${ORG_ID} - ENVIRONMENT_NAME=${ENVIRONMENT_NAME} diff --git a/functions/onprem/README.md b/functions/onprem/README.md index 51e5782d..74358762 100644 --- a/functions/onprem/README.md +++ b/functions/onprem/README.md @@ -38,3 +38,11 @@ cd app_sdk docker build . -t frikky/shuffle:app_sdk docker push frikky/shuffle:app_sdk ``` + + +Update app_sdk_kali: +``` +cd app_sdk_kali +docker build . -t frikky/shuffle:app_sdk_kali +docker push frikky/shuffle:app_sdk_kali +``` From 564b2dae4759ddad3e72e30c832af417311ea619 Mon Sep 17 00:00:00 2001 From: Peter Clemenko Date: Wed, 21 Oct 2020 22:46:41 -0400 Subject: [PATCH 2/5] blackarch appsdk --- backend/Dockerfile | 3 + backend/app_sdk_blackarch/Dockerfile | 19 + backend/app_sdk_blackarch/LICENSE | 21 + backend/app_sdk_blackarch/README.md | 16 + backend/app_sdk_blackarch/__init__.py | 0 backend/app_sdk_blackarch/app_base.py | 1169 ++++++++++++++++++ backend/app_sdk_blackarch/build.sh | 13 + backend/app_sdk_blackarch/requirements.txt | 2 + backend/app_sdk_blackarch/static_baseline.py | 76 ++ docker-compose.yml | 1 + functions/onprem/README.md | 8 + 11 files changed, 1328 insertions(+) create mode 100644 backend/app_sdk_blackarch/Dockerfile create mode 100644 backend/app_sdk_blackarch/LICENSE create mode 100644 backend/app_sdk_blackarch/README.md create mode 100644 backend/app_sdk_blackarch/__init__.py create mode 100644 backend/app_sdk_blackarch/app_base.py create mode 100644 backend/app_sdk_blackarch/build.sh create mode 100644 backend/app_sdk_blackarch/requirements.txt create mode 100644 backend/app_sdk_blackarch/static_baseline.py diff --git a/backend/Dockerfile b/backend/Dockerfile index a1887c93..21422785 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -16,6 +16,8 @@ ADD ./app_sdk/app_base.py /app_sdk ADD ./app_sdk/static_baseline.py /app_sdk ADD ./app_sdk_kali/app_base.py /app_sdk_kali ADD ./app_sdk_kali/static_baseline.py /app_sdk_kali +ADD ./app_sdk_blackarch/app_base.py /app_sdk_blackarch +ADD ./app_sdk_blackarch/static_baseline.py /app_sdk_blackarch ADD ./app_gen /app_gen RUN go get -v @@ -31,6 +33,7 @@ FROM alpine:3.12 COPY --from=builder /app/ /app COPY --from=builder /app_sdk/ /app_sdk COPY --from=builder /app_sdk_kali/ /app_sdk_kali +COPY --from=builder /app_sdk_blackarch/ /app_sdk_blackarch COPY --from=builder /app_gen/ /app_gen COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt diff --git a/backend/app_sdk_blackarch/Dockerfile b/backend/app_sdk_blackarch/Dockerfile new file mode 100644 index 00000000..e48f3095 --- /dev/null +++ b/backend/app_sdk_blackarch/Dockerfile @@ -0,0 +1,19 @@ +FROM peterclemenko/blackarch as base + +FROM base as builder + +RUN apt-get update +RUN apt-get dist-upgrade -y +RUN apt install base-devel libffi musl openssl python python-pip -y + +RUN mkdir /install +WORKDIR /install + +COPY requirements.txt /requirements.txt +RUN pip install --prefix="/install" -r /requirements.txt + +FROM base + +COPY --from=builder /install /usr/local +COPY __init__.py /app/walkoff_app_sdk/__init__.py +COPY app_base.py /app/walkoff_app_sdk/app_base.py diff --git a/backend/app_sdk_blackarch/LICENSE b/backend/app_sdk_blackarch/LICENSE new file mode 100644 index 00000000..ce11f6f3 --- /dev/null +++ b/backend/app_sdk_blackarch/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Frikkylikeme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/backend/app_sdk_blackarch/README.md b/backend/app_sdk_blackarch/README.md new file mode 100644 index 00000000..754392ff --- /dev/null +++ b/backend/app_sdk_blackarch/README.md @@ -0,0 +1,16 @@ +# app_sdk.py +This is the SDK used for apps to behave like they should. +To change it in the backend, upload it to Buckets/shuffler.appspot.com/generated_apps/baseline. + +# static_baseline.py +It's used for python code generation and should be under MIT. Has to be located here because it's used by the backend. + +## If you want to update apps.. PS: downloads from docker hub do overrides.. :) +1. Write your code & check if runtime works +2. Build app_base image +3. docker rm $(docker ps -aq) # Remove all stopped containers +4. Delete the specific app's Docker image (docker rmi frikky/shuffle:...) +5. Rebuild the Docker image (click load in GUI?) + +# LICENSE +Everything in here is MIT, not AGPLv3 as indicated by the license. diff --git a/backend/app_sdk_blackarch/__init__.py b/backend/app_sdk_blackarch/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/app_sdk_blackarch/app_base.py b/backend/app_sdk_blackarch/app_base.py new file mode 100644 index 00000000..e896f67c --- /dev/null +++ b/backend/app_sdk_blackarch/app_base.py @@ -0,0 +1,1169 @@ +import os +import sys +import re +import time +import json +import logging +import requests +import urllib.parse + +class AppBase: + """ The base class for Python-based apps in Shuffle, handles logging and callbacks configurations""" + __version__ = None + app_name = None + + def __init__(self, redis=None, logger=None, console_logger=None):#, docker_client=None): + self.logger = logger if logger is not None else logging.getLogger("AppBaseLogger") + self.redis=redis + self.console_logger = logger if logger is not None else logging.getLogger("AppBaseLogger") + + # apikey is for the user / org + # authorization is for the specific workflow + self.url = os.getenv("CALLBACK_URL", "https://shuffler.io") + self.action = os.getenv("ACTION", "") + self.authorization = os.getenv("AUTHORIZATION", "") + self.current_execution_id = os.getenv("EXECUTIONID", "") + self.full_execution = os.getenv("FULL_EXECUTION", "") + + if isinstance(self.action, str): + self.action = json.loads(self.action) + + def send_result(self, action_result, headers, stream_path): + if action_result["status"] == "EXECUTING": + action_result["status"] = "FAILURE" + + # I wonder if this actually works + self.logger.info("Before last stream result") + try: + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Result: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + except requests.exceptions.ConnectionError as e: + self.logger.exception(e) + return + except TypeError as e: + self.logger.exception(e) + action_result["status"] = "FAILURE" + action_result["result"] = "POST error: %s" % e + self.logger.info("Before typeerror stream result") + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Result: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + + async def execute_action(self, action): + # FIXME - add request for the function STARTING here. Use "results stream" or something + # PAUSED, AWAITING_DATA, PENDING, COMPLETED, ABORTED, EXECUTING, SUCCESS, FAILURE + + # !!! Let this line stay - its used for some horrible codegeneration / stitching !!! # + #STARTCOPY + stream_path = "/api/v1/streams" + action_result = { + "action": action, + "authorization": self.authorization, + "execution_id": self.current_execution_id, + "result": "", + "started_at": int(time.time()), + "status": "EXECUTING" + } + self.logger.info("ACTION RESULT (start): %s", action_result) + + if len(self.action) == 0: + print("ACTION env not defined") + action_result["result"] = "Error in setup ENV: ACTION not defined" + self.send_result(action_result, headers, stream_path) + return + if len(self.authorization) == 0: + print("AUTHORIZATION env not defined") + action_result["result"] = "Error in setup ENV: AUTHORIZATION not defined" + self.send_result(action_result, headers, stream_path) + return + if len(self.current_execution_id) == 0: + print("EXECUTIONID env not defined") + action_result["result"] = "Error in setup ENV: EXECUTIONID not defined" + self.send_result(action_result, headers, stream_path) + return + + headers = { + "Content-Type": "application/json", + "Authorization": "Bearer %s" % self.authorization + } + + # Add async logger + # self.console_logger.handlers[0].stream.set_execution_id() + #self.logger.info("Before initial stream result") + try: + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Workflow: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + except requests.exceptions.ConnectionError as e: + print("Connectionerror: %s" % e) + + action_result["result"] = "Bad setup during startup: %s" % e + self.send_result(action_result, headers, stream_path) + return + + # Verify whether there are any parameters with ACTION_RESULT required + # If found, we get the full results list from backend + fullexecution = {} + if len(self.full_execution) == 0: + print("NO EXECUTION - LOADING!") + try: + tmpdata = { + "authorization": self.authorization, + "execution_id": self.current_execution_id + } + + self.logger.info("Before FULLEXEC stream result") + ret = requests.post( + "%s/api/v1/streams/results" % (self.url), + headers=headers, + json=tmpdata + ) + + if ret.status_code == 200: + fullexecution = ret.json() + else: + self.logger.info("Error: Data: ", ret.json()) + self.logger.info("Error with status code for results. Crashing because ACTION_RESULTS or WORKFLOW_VARIABLE can't be handled. Status: %d" % ret.status_code) + action_result["result"] = "Bad result from backend: %d" % ret.status_code + self.send_result(action_result, headers, stream_path) + return + except requests.exceptions.ConnectionError as e: + self.logger.info("Connectionerror: %s" % e) + action_result["result"] = "Connection error during startup: %s" % e + self.send_result(action_result, headers, stream_path) + return + else: + try: + fullexecution = json.loads(self.full_execution) + except json.decoder.JSONDecodeError as e: + print("Json decode execution error: %s" % e) + action_result["result"] = "Json error during startup: %s" % e + self.send_result(action_result, headers, stream_path) + return + + print("") + + + self.logger.info("AFTER FULLEXEC stream result") + + # Gets the value at the parenthesis level you want + def parse_nested_param(string, level): + """ + Generate strings contained in nested (), indexing i = level + """ + if len(re.findall("\(", string)) == len(re.findall("\)", string)): + LeftRightIndex = [x for x in zip( + [Left.start()+1 for Left in re.finditer('\(', string)], + reversed([Right.start() for Right in re.finditer('\)', string)]))] + + elif len(re.findall("\(", string)) > len(re.findall("\)", string)): + return parse_nested_param(string + ')', level) + elif len(re.findall("\(", string)) < len(re.findall("\)", string)): + return parse_nested_param('(' + string, level) + + else: + return 'Failed to parse params' + + try: + return [string[LeftRightIndex[level][0]:LeftRightIndex[level][1]]] + except IndexError: + return [string[LeftRightIndex[level+1][0]:LeftRightIndex[level+1][1]]] + + # Finds the deepest level parenthesis in a string + def maxDepth(S): + current_max = 0 + max = 0 + n = len(S) + + # Traverse the input string + for i in range(n): + if S[i] == '(': + current_max += 1 + + if current_max > max: + max = current_max + elif S[i] == ')': + if current_max > 0: + current_max -= 1 + else: + return -1 + + # finally check for unbalanced string + if current_max != 0: + return -1 + + return max-1 + + # Specific type parsing + def parse_type(data, thistype): + if data == None: + return "Empty" + + if "int" in thistype or "number" in thistype: + try: + return int(data) + except ValueError: + print("ValueError while casting %s" % data) + return data + if "lower" in thistype: + return data.lower() + if "upper" in thistype: + return data.upper() + if "trim" in thistype: + return data.strip() + if "strip" in thistype: + return data.strip() + if "split" in thistype: + return data.split() + if "len" in thistype or "length" in thistype: + tmp = "" + try: + tmp = json.loads(data) + except: + pass + + if isinstance(tmp, list): + return str(len(tmp)) + + return str(len(data)) + if "parse" in thistype: + splitvalues = [] + default_error = """Error. Expected syntax: parse(["hello","test1"],0:1)""" + if "," in data: + splitvalues = data.split(",") + + for item in range(len(splitvalues)): + splitvalues[item] = splitvalues[item].strip() + else: + return default_error + + lastsplit = [] + if ":" in splitvalues[-1]: + lastsplit = splitvalues[-1].split(":") + else: + try: + lastsplit = [int(splitvalues[-1])] + except ValueError: + return default_error + + try: + parsedlist = ",".join(splitvalues[0:-1]) + if len(lastsplit) > 1: + tmp = json.loads(parsedlist)[int(lastsplit[0]):int(lastsplit[1])] + else: + tmp = json.loads(parsedlist)[lastsplit[0]] + + print(tmp) + return tmp + except IndexError as e: + return default_error + + # Parses the INNER value and recurses until everything is done + def parse_wrapper(data): + try: + if "(" not in data or ")" not in data: + return data + except TypeError: + return data + + #print("Running %s" % data) + + # Look for the INNER wrapper first, then move out + wrappers = ["int", "number", "lower", "upper", "trim", "strip", "split", "parse", "len", "length"] + found = False + for wrapper in wrappers: + if wrapper not in data.lower(): + continue + + found = True + break + + if not found: + return data + + # Do stuff here. + innervalue = parse_nested_param(data, maxDepth(data)-0) + outervalue = parse_nested_param(data, maxDepth(data)-1) + print("INNER: ", innervalue) + print("OUTER: ", outervalue) + + if outervalue != innervalue: + #print("Outer: ", outervalue, " inner: ", innervalue) + for key in range(len(innervalue)): + # Replace OUTERVALUE[key] with INNERVALUE[key] in data. + print("Replace %s with %s in %s" % (outervalue[key], innervalue[key], data)) + data = data.replace(outervalue[key], innervalue[key]) + else: + for thistype in wrappers: + if thistype.lower() not in data.lower(): + continue + + parsed_value = parse_type(innervalue[0], thistype.lower()) + return parsed_value + + print("DATA: %s\n" % data) + return parse_wrapper(data) + + def parse_wrapper_start(data): + newdata = [] + newstring = "" + record = True + paranCnt = 0 + for char in data: + if char == "(": + paranCnt += 1 + + if not record: + record = True + + if record: + newstring += char + + if paranCnt == 0 and char == " ": + newdata.append(newstring) + newstring = "" + record = True + + if char == ")": + paranCnt -= 1 + + if paranCnt == 0: + record = False + + if len(newstring) > 0: + newdata.append(newstring) + + #print(newdata) + parsedlist = [] + non_string = False + for item in newdata: + ret = parse_wrapper(item) + if not isinstance(ret, str): + non_string = True + + parsedlist.append(ret) + + if len(parsedlist) > 0 and not non_string: + return " ".join(parsedlist) + elif len(parsedlist) == 1 and non_string: + return parsedlist[0] + else: + #print("Casting back to string because multi: ", parsedlist) + newlist = [] + for item in parsedlist: + try: + newlist.append(str(item)) + except ValueError: + newlist.append("parsing_error") + return " ".join(newlist) + + # Parses JSON loops and such down to the item you're looking for + def recurse_json(basejson, parsersplit): + match = "#(\d+):?-?([0-9a-z]+)?#?" + print("Split: %s\n%s" % (parsersplit, basejson)) + try: + outercnt = 0 + + # Loops over split values + for value in parsersplit: + print("VALUE: %s\n" % value) + actualitem = re.findall(match, value, re.MULTILINE) + if value == "#": + newvalue = [] + for innervalue in basejson: + # 1. Check the next item (message) + # 2. Call this function again + + try: + ret, is_loop = recurse_json(innervalue, parsersplit[outercnt+1:]) + except IndexError: + # Only in here if it's the last loop without anything in it? + ret, is_loop = recurse_json(innervalue, parsersplit[outercnt:]) + + newvalue.append(ret) + + # Magical way of returning which makes app sdk identify + # it as multi execution + return newvalue, True + elif len(actualitem) > 0: + # FIXME: This is absolutely not perfect. + print("In recursion v2: ", actualitem) + + is_loop = True + newvalue = [] + firstitem = actualitem[0][0] + seconditem = actualitem[0][1] + + # Means it's a single item -> continue + if seconditem == "": + print("In first - handling %s", seconditem) + tmpitem = basejson[int(firstitem)] + try: + newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) + except IndexError: + newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) + else: + if seconditem == "max": + seconditem = len(basejson) + if seconditem == "min": + seconditem = 0 + + newvalue = [] + for i in range(int(firstitem), int(seconditem)): + # 1. Check the next item (message) + # 2. Call this function again + print("Base: %s" % basejson[i]) + + try: + ret, is_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) + except IndexError: + print("INDEXERROR: ", parsersplit[outercnt]) + #ret = innervalue + ret, is_loop = recurse_json(innervalue, parsersplit[outercnt:]) + + print(ret) + #exit() + newvalue.append(ret) + + return newvalue, is_loop + + # FIXME: Add specific loop for other indexes + else: + #print("BEFORE NORMAL VALUE: ", basejson, value) + if len(value) == 0: + return basejson, False + + if isinstance(basejson[value], str): + print(f"LOADING STRING '%s' AS JSON" % basejson[value]) + try: + basejson = json.loads(basejson[value]) + except json.decoder.JSONDecodeError as e: + print("RETURNING BECAUSE '%s' IS A NORMAL STRING" % basejson[value]) + return basejson[value], False + else: + basejson = basejson[value] + + outercnt += 1 + + except KeyError as e: + print("Lower keyerror: %s" % e) + #return basejson + #return "KeyError: Couldn't find key: %s" % e + + return basejson, False + + # Takes a workflow execution as argument + # Returns a string if the result is single, or a list if it's a list + def get_json_value(execution_data, input_data): + parsersplit = input_data.split(".") + actionname = parsersplit[0][1:].replace(" ", "_", -1) + #Actionname: Start_node + + print(f"Actionname: {actionname}") + + # 1. Find the action + baseresult = "" + actionname_lower = actionname.lower() + try: + if actionname_lower == "exec" or actionname_lower == "webhook" or actionname_lower == "schedule" or actionname_lower == "userinput" or actionname_lower == "email_trigger" or actionname_lower == "trigger": + baseresult = execution_data["execution_argument"] + else: + for result in execution_data["results"]: + resultlabel = result["action"]["label"].replace(" ", "_", -1).lower() + if resultlabel.lower() == actionname_lower: + baseresult = result["result"] + break + + print("BEFORE VARIABLES!") + if len(baseresult) == 0: + try: + #print("WF Variables: %s" % execution_data["workflow"]["workflow_variables"]) + for variable in execution_data["workflow"]["workflow_variables"]: + variablename = variable["name"].replace(" ", "_", -1).lower() + + if variablename.lower() == actionname_lower: + baseresult = variable["value"] + break + except KeyError as e: + print("KeyError wf variables: %s" % e) + pass + except TypeError as e: + print("TypeError wf variables: %s" % e) + pass + + print("BEFORE EXECUTION VAR") + if len(baseresult) == 0: + try: + #print("Execution Variables: %s" % execution_data["execution_variables"]) + for variable in execution_data["execution_variables"]: + variablename = variable["name"].replace(" ", "_", -1).lower() + if variablename.lower() == actionname_lower: + baseresult = variable["value"] + break + except KeyError as e: + print("KeyError exec variables: %s" % e) + pass + except TypeError as e: + print("TypeError exec variables: %s" % e) + pass + + except KeyError as error: + print(f"KeyError in JSON: {error}") + + print(f"After first trycatch") + + # 2. Find the JSON data + if len(baseresult) == 0: + return "", False + + if len(parsersplit) == 1: + return baseresult, False + + baseresult = baseresult.replace("\'", "\"") + basejson = {} + try: + basejson = json.loads(baseresult) + except json.decoder.JSONDecodeError as e: + return baseresult, False + + data, is_loop = recurse_json(basejson, parsersplit[1:]) + parseditem = data + if is_loop: + print("DATA IS A LOOP - SHOULD WRAP") + if parsersplit[-1] == "#": + print("SET DATA WRAPPER TO NORMAL!") + parseditem = "${SHUFFLE_NO_SPLITTER%s}$" % json.dumps(data) + else: + # Return value: ${id[12345, 45678]}$ + print("SET DATA WRAPPER TO %s!" % parsersplit[-1]) + parseditem = "${%s%s}$" % (parsersplit[-1], json.dumps(data)) + + return parseditem, is_loop + + # Parses parameters sent to it and returns whether it did it successfully with the values found + def parse_params(action, fullexecution, parameter): + # Skip if it starts with $? + jsonparsevalue = "$." + is_loop = False + + # Matches with space in the first part, but not in subsequent parts. + # JSON / yaml etc shouldn't have spaces in their fields anyway. + match = ".*?([$]{1}([a-zA-Z0-9 _-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,})" + + # Regex to find all the things + if parameter["variant"] == "STATIC_VALUE": + data = parameter["value"] + actualitem = re.findall(match, data, re.MULTILINE) + #self.logger.debug(f"\n\nHandle static data with JSON: {data}\n\n") + #self.logger.info("STATIC PARSED: %s" % actualitem) + if len(actualitem) > 0: + print("ACTUAL: ", actualitem) + for replace in actualitem: + try: + to_be_replaced = replace[0] + except IndexError: + continue + + # Handles for loops etc. + value, is_loop = get_json_value(fullexecution, to_be_replaced) + + if isinstance(value, str): + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + elif isinstance(value, dict): + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + else: + print("Unknown type %s" % type(value)) + try: + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + except json.decoder.JSONDecodeError as e: + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + + + if parameter["variant"] == "WORKFLOW_VARIABLE": + print("Handling workflow variable") + found = False + try: + for item in fullexecution["workflow"]["workflow_variables"]: + if parameter["action_field"] == item["name"]: + found = True + parameter["value"] = item["value"] + break + except KeyError as e: + print("KeyError WF variable 1: %s" % e) + pass + except TypeError as e: + print("TypeError WF variables 1: %s" % e) + pass + + if not found: + try: + for item in fullexecution["execution_variables"]: + if parameter["action_field"] == item["name"]: + parameter["value"] = item["value"] + break + except KeyError as e: + print("KeyError WF variable 2: %s" % e) + pass + except TypeError as e: + print("TypeError WF variables 2: %s" % e) + pass + + elif parameter["variant"] == "ACTION_RESULT": + # FIXME - calculate value based on action_field and $if prominent + # FIND THE RIGHT LABEL + # GET THE LABEL'S RESULT + + tmpvalue = "" + self.logger.info("ACTION FIELD: %s" % parameter["action_field"]) + + fullname = "$" + if parameter["action_field"] == "Execution Argument": + tmpvalue = fullexecution["execution_argument"] + fullname += "exec" + else: + fullname += parameter["action_field"] + + self.logger.info("PRE Fullname: %s" % fullname) + + if parameter["value"].startswith(jsonparsevalue): + fullname += parameter["value"][1:] + #else: + # fullname = "$%s" % parameter["action_field"] + + self.logger.info("Fullname: %s" % fullname) + actualitem = re.findall(match, fullname, re.MULTILINE) + self.logger.info("ACTION PARSED: %s" % actualitem) + if len(actualitem) > 0: + for replace in actualitem: + try: + to_be_replaced = replace[0] + except IndexError: + print("Nothing to replace?: " % e) + continue + + # This will never be a loop aka multi argument + parameter["value"] = to_be_replaced + + value, is_loop = get_json_value(fullexecution, to_be_replaced) + print("Loop: %s" % is_loop) + if isinstance(value, str): + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + elif isinstance(value, dict): + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + else: + print("Unknown type %s" % type(value)) + try: + parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) + except json.decoder.JSONDecodeError as e: + parameter["value"] = parameter["value"].replace(to_be_replaced, value) + + return "", parameter["value"], is_loop + + def run_validation(sourcevalue, check, destinationvalue): + self.logger.info("Checking %s %s %s" % (sourcevalue, check, destinationvalue)) + + if check == "=" or check.lower() == "equals": + if sourcevalue.lower() == destinationvalue.lower(): + return True + elif check == "!=" or check.lower() == "does not equal": + if sourcevalue.lower() != destinationvalue.lower(): + return True + elif check.lower() == "startswith": + if sourcevalue.lower().startswith(destinationvalue.lower()): + return True + elif check.lower() == "endswith": + if sourcevalue.lower().endswith(destinationvalue.lower()): + return True + elif check.lower() == "contains": + if destinationvalue.lower() in sourcevalue.lower(): + return True + elif check.lower() == "larger than": + try: + if sourcevalue.isdigit() and destinationvalue.isdigit(): + if int(sourcevalue) > int(destinationvalue): + return True + except AttributeError as e: + self.logger.error("Condition larger than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + return False + elif check.lower() == "smaller than": + try: + if sourcevalue.isdigit() and destinationvalue.isdigit(): + if int(sourcevalue) < int(destinationvalue): + return True + except AttributeError as e: + self.logger.error("Condition smaller than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + return False + else: + self.logger.info("Condition: can't handle %s yet. Setting to true" % check) + + return False + + def check_branch_conditions(action, fullexecution): + # relevantbranches = workflow.branches where destination = action + try: + if fullexecution["workflow"]["branches"] == None or len(fullexecution["workflow"]["branches"]) == 0: + return True, "" + except KeyError: + return True, "" + + relevantbranches = [] + for branch in fullexecution["workflow"]["branches"]: + if branch["destination_id"] != action["id"]: + continue + + # Remove anything without a condition + try: + if (branch["conditions"]) == 0 or branch["conditions"] == None: + continue + except KeyError: + continue + + self.logger.info("Relevant conditions: %s" % branch["conditions"]) + successful_conditions = [] + failed_conditions = [] + for condition in branch["conditions"]: + self.logger.info("Getting condition value of %s" % condition) + + # Parse all values first here + sourcevalue = condition["source"]["value"] + check, sourcevalue, is_loop = parse_params(action, fullexecution, condition["source"]) + if check: + return False, "Failed condition: %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check) + + + #sourcevalue = sourcevalue.encode("utf-8") + sourcevalue = parse_wrapper_start(sourcevalue) + destinationvalue = condition["destination"]["value"] + + check, destinationvalue, is_loop = parse_params(action, fullexecution, condition["destination"]) + if check: + return False, "Failed condition: %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check) + + #destinationvalue = destinationvalue.encode("utf-8") + destinationvalue = parse_wrapper_start(destinationvalue) + available_checks = [ + "=", + "equals", + "!=", + "does not equal", + ">", + "larger than", + "<", + "less than", + ">=", + "<=", + "startswith", + "endswith", + "contains", + "re", + "matches regex", + ] + + # FIXME - what should I do here? + if not condition["condition"]["value"] in available_checks: + self.logger.info("Skipping %s %s %s because %s is invalid." % (sourcevalue, condition["condition"]["value"], destinationvalue, condition["condition"]["value"])) + continue + + #print(destinationvalue) + # NEGATE + validation = run_validation(sourcevalue, condition["condition"]["value"], destinationvalue) + + # Configuration = negated because of WorkflowAppActionParam.. + try: + if condition["condition"]["configuration"]: + validation = not validation + except KeyError: + pass + + if not validation: + self.logger.info("Failed condition check for %s %s %s." % (sourcevalue, condition["condition"]["value"], destinationvalue)) + return False, "Failed condition: %s %s %s" % (sourcevalue, condition["condition"]["value"], destinationvalue) + + + # Make a general parser here, at least to get param["name"] = param["value"] in maparameter[string]string + #for condition in branch.conditons: + + return True, "" + + # Checks whether conditions are met, otherwise set + branchcheck, tmpresult = check_branch_conditions(action, fullexecution) + if not branchcheck: + self.logger.info("Failed one or more branch conditions.") + action_result["result"] = tmpresult + action_result["status"] = "FAILURE" + try: + ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result) + self.logger.info("Result: %d" % ret.status_code) + if ret.status_code != 200: + self.logger.info(ret.text) + except requests.exceptions.ConnectionError as e: + self.logger.exception(e) + + print("\n\nRETURNING BECAUSE A BRANCH FAILED\n\n") + return + + # Replace name cus there might be issues + # Not doing lower() as there might be user-made functions + actionname = action["name"] + if " " in actionname: + actionname.replace(" ", "_", -1) + #if action.generated: + # actionname = actionname.lower() + + # Runs the actual functions + try: + func = getattr(self, actionname, None) + if func == None: + self.logger.debug("Failed executing %s because func is None." % actionname) + action_result["status"] = "FAILURE" + action_result["result"] = "Function %s doesn't exist." % actionname + elif callable(func): + try: + if len(action["parameters"]) < 1: + result = await func() + else: + # Potentially parse JSON here + # FIXME - add potential authentication as first parameter(s) here + # params[parameter["name"]] = parameter["value"] + #print(fullexecution["authentication"] + # What variables are necessary here tho hmm + + params = {} + try: + for item in action["authentication"]: + print("AUTH: ", key, value) + params[item["key"]] = item["value"] + except KeyError: + print("No authentication specified!") + pass + #action["authentication"] + + # calltimes is used to handle forloops in the app itself. + # 2 kinds of loop - one in gui with one app each, and one like this, + # which is super fast, but has a bad overview (potentially good tho) + calltimes = 1 + result = "" + + all_executions = [] + + # Multi_parameter has the data for each. variable + minlength = 0 + multi_parameters = json.loads(json.dumps(params)) + multiexecution = False + multi_execution_lists = [] + for parameter in action["parameters"]: + check, value, is_loop = parse_params(action, fullexecution, parameter) + + if check: + raise "Value check error: %s" % Exception(check) + + # Custom format for ${name[0,1,2,...]}$ + #submatch = "([${]{2}([0-9a-zA-Z_-]+)(\[.*\])[}$]{2})" + submatch = "([${]{2}([0-9a-zA-Z_-]+)(\[.*\])[}$]{2})" + actualitem = re.findall(submatch, value, re.MULTILINE) + try: + if action["skip_multicheck"]: + print("Skipping multicheck") + actualitem = [] + except KeyError: + pass + + print("Return value: %s" % value) + actionname = action["name"] + #print("Multicheck ", actualitem) + print("Actual item: %s" % actualitem) + if len(actualitem) > 0: + multiexecution = True + + # Loop WITHOUT JSON variables go here. + # Loop WITH variables go in else. + if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER": + print("Pre replacement: %s" % actualitem[0][2]) + tmpitem = value + + replacement = actualitem[0][2] + if replacement.startswith("\"") and replacement.endswith("\""): + replacement = replacement[1:len(replacement)-1] + + replacement = replacement.replace("\'", "\"", -1) + print("POST replacement: %s" % replacement) + + json_replacement = replacement + try: + json_replacement = json.loads(replacement) + except json.decoder.JSONDecodeError as e: + print("JSON error singular: %s" % e) + + if len(json_replacement) > minlength: + minlength = len(json_replacement) + + tmpitem = tmpitem.replace(actualitem[0][0], replacement, 1) + params[parameter["name"]] = tmpitem + multi_execution_lists.append(json_replacement) + multi_parameters[parameter["name"]] = json_replacement + + #print("LENGTH OF ARR: %d" % len(resultarray)) + #print("RESULTARRAY: %s" % resultarray) + print("MULTI finished: %s" % replacement) + else: + + # This is here to handle for loops within variables.. kindof + # 1. Find the length of the longest array + # 2. Build an array with the base values based on parameter["value"] + # 3. Get the n'th value of the generated list from values + # 4. Execute all n answers + replacements = {} + for replace in actualitem: + try: + to_be_replaced = replace[0] + actualitem = replace[2] + except IndexError: + continue + + try: + itemlist = json.loads(actualitem) + if len(itemlist) > minlength: + minlength = len(itemlist) + except json.decoder.JSONDecodeError as e: + print("JSON Error: %s in %s" % (e, actualitem)) + + replacements[to_be_replaced] = actualitem + + # This is a result array for JUST this value.. + # What if there are more? + resultarray = [] + for i in range(0, minlength): + tmpitem = json.loads(json.dumps(parameter["value"])) + for key, value in replacements.items(): + replacement = json.dumps(json.loads(value)[i]) + if replacement.startswith("\"") and replacement.endswith("\""): + replacement = replacement[1:len(replacement)-1] + #except json.decoder.JSONDecodeError as e: + + #print("REPLACING %s with %s" % (key, replacement)) + #replacement = parse_wrapper_start(replacement) + tmpitem = tmpitem.replace(key, replacement, -1) + + resultarray.append(tmpitem) + + # With this parameter ready, add it to... a greater list of parameters. Rofl + print("LENGTH OF ARR: %d" % len(resultarray)) + print("RESULTARRAY: %s" % resultarray) + if resultarray not in multi_execution_lists: + multi_execution_lists.append(resultarray) + + multi_parameters[parameter["name"]] = resultarray + else: + # Parses things like int(value) + self.logger.info("Parsing wrapper data for %s" % value) + value = parse_wrapper_start(value) + + params[parameter["name"]] = value + multi_parameters[parameter["name"]] = value + + # Fix lists here + print("CHECKING multi execution list!") + if len(multi_execution_lists) > 0: + print("\n Multi execution list has more data: %d" % len(multi_execution_lists)) + filteredlist = [] + for listitem in multi_execution_lists: + if listitem in filteredlist: + continue + + filteredlist.append(listitem) + + #print("New list length: %d" % len(filteredlist)) + if len(filteredlist) > 1: + print("Calculating new multi-loop length with %d lists" % len(filteredlist)) + tmplength = 1 + for innerlist in filteredlist: + print("List length: %d. %d*%d" % (len(innerlist), len(innerlist), tmplength)) + tmplength = len(innerlist)*tmplength + + minlength = tmplength + + print("New multi execution length: %d\n" % tmplength) + + # FIXME - this is horrible, but works for now + #for i in range(calltimes): + if not multiexecution: + print("APP_SDK DONE: Starting NORMAL execution of function") + newres = await func(**params) + #print("NEWRES: ", newres) + if isinstance(newres, str): + result += newres + else: + try: + result += str(newres) + except ValueError: + result += "Failed autocasting. Can't handle %s type from function. Must be string" % type(newres) + print("Can't handle type %s value from function" % (type(newres))) + print("POST NEWRES RESULT: ", result) + else: + print("APP_SDK DONE: Starting MULTI execution with values %s of length %d" % (multi_parameters, minlength)) + # 1. Use number of executions based on the arrays being similar + # 2. Find the right value from the parsed multi_params + results = [] + json_object = False + for i in range(0, minlength): + # To be able to use the results as a list: + baseparams = json.loads(json.dumps(multi_parameters)) + # {'call': ['GoogleSafebrowsing_2_0', 'VirusTotal_GetReport_3_0']} + # 1. Check if list length is same as minlength + # 2. If NOT same length, duplicate based on length of array + # arraylength = 3 ["1", "2", "3"] + # arraylength = 4 ["1", "2", "3", "4"] + # minlength = 12 - 12/3 = 4 per item = ["1", "1", "1", "1", "2", "2", ...] + + try: + firstlist = True + for key, value in baseparams.items(): + + if isinstance(value, list): + try: + newvalue = value[i] + except IndexError: + pass + + if len(value) != minlength and len(value) > 0: + newarray = [] + print("VALUE: ", value) + additiontime = minlength/len(value) + print("Bad length for value: %d - should be %d. Additiontime: %d" % (len(value), minlength, additiontime)) + if firstlist: + print("Running normal list (FIRST)") + for subvalue in value: + for number in range(int(additiontime)): + newarray.append(subvalue) + else: + #print("Running secondary lists") + ## 1. Set up length of array + ## 2. Put values spread out + # FIXME: This works well, except if lists are same length + newarray = [""] * minlength + + cnt = 0 + for number in range(int(additiontime)): + for subvaluerange in range(len(value)): + # newlocation = number+(additiontime*subvaluerange) + # print("%d+(%d*%d) = %d. VAL: %s" % (number, additiontime, subvaluerange, newlocation, value[subvaluerange])) + # Reverse if same length? + if int(minlength/len(value)) == len(value): + tmp = int(len(value)-subvaluerange-1) + print("NEW: %d" % tmp) + newarray[cnt] = value[tmp] + else: + newarray[cnt] = value[subvaluerange] + cnt += 1 + + #print("Newarray =", newarray) + newvalue = newarray[i] + firstlist = False + + baseparams[key] = newvalue + except IndexError as e: + print("IndexError: %s" % e) + baseparams[key] = "IndexError: %s" % e + except KeyError as e: + print("KeyError: %s" % e) + baseparams[key] = "KeyError: %s" % e + + print("Running with params %s" % baseparams) + ret = await func(**baseparams) + if isinstance(ret, dict) or isinstance(ret, list): + results.append(ret) + json_object = True + else: + ret = ret.replace("\"", "\\\"", -1) + + try: + results.append(json.loads(ret)) + json_object = True + except json.decoder.JSONDecodeError as e: + #print("Json: %s" % e) + results.append(ret) + + #print("Inner ret parsed: %s" % ret) + + # Dump the result as a string of a list + #print("RESULTS: %s" % results) + if isinstance(results, list): + print("JSON OBJECT? ", json_object) + if json_object: + result = json.dumps(results) + else: + result = "[" + for item in results: + try: + json.loads(item) + result += item + except json.decoder.JSONDecodeError as e: + # Common nested issue which puts " around everything + try: + tmpitem = item.replace("\\\"", "\"", -1) + json.loads(tmpitem) + result += tmpitem + + except: + result += "\"%s\"" % item + + result += ", " + + result = result[:-2] + result += "]" + else: + print("Normal result?") + result = results + + print("RESULT: %s" % result) + action_result["status"] = "SUCCESS" + action_result["result"] = str(result) + if action_result["result"] == "": + action_result["result"] = result + + self.logger.debug(f"Executed {action['label']}-{action['id']} with result: {result}") + #self.logger.debug(f"Data: %s" % action_result) + except TypeError as e: + print("TypeError issue: %s" % e) + action_result["status"] = "FAILURE" + action_result["result"] = "TypeError: %s" % str(e) + else: + print("Function %s doesn't exist?" % action["name"]) + self.logger.error(f"App {self.__class__.__name__}.{action['name']} is not callable") + action_result["status"] = "FAILURE" + action_result["result"] = "Function %s is not callable." % actionname + + except Exception as e: + print(f"Failed to execute: {e}") + self.logger.exception(f"Failed to execute {e}-{action['id']}") + action_result["status"] = "FAILURE" + action_result["result"] = f"General exception: {e}" + + action_result["completed_at"] = int(time.time()) + + # Send the result :) + self.send_result(action_result, headers, stream_path) + return + + + #STOPCOPY + # !!! Let the above line stay - its used for some horrible codegeneration / stitching !!! # + + @classmethod + async def run(cls): + """ Connect to Redis and HTTP session, await actions """ + logging.basicConfig(format="{asctime} - {name} - {levelname}:{message}", style='{') + logger = logging.getLogger(f"{cls.__name__}") + logger.setLevel(logging.DEBUG) + print("Started execution!!") + + app = cls(redis=None, logger=logger, console_logger=logger) + + # Authorization for the app/function to control the workflow + # Function will crash if its wrong, which it probably should. + + await app.execute_action(app.action) diff --git a/backend/app_sdk_blackarch/build.sh b/backend/app_sdk_blackarch/build.sh new file mode 100644 index 00000000..029756ce --- /dev/null +++ b/backend/app_sdk_blackarch/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash +NAME=app_sdk_blackarch +VERSION=0.7.3 + +docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force +docker build . -t frikky/shuffle:$NAME -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION + +#docker push frikky/$NAME:$VERSION +#docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION +#docker push ghcr.io/frikky/$NAME:$VERSION + +docker push frikky/shuffle:$NAME +docker push ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/app_sdk_blackarch/requirements.txt b/backend/app_sdk_blackarch/requirements.txt new file mode 100644 index 00000000..804abb1b --- /dev/null +++ b/backend/app_sdk_blackarch/requirements.txt @@ -0,0 +1,2 @@ +requests +urllib3 diff --git a/backend/app_sdk_blackarch/static_baseline.py b/backend/app_sdk_blackarch/static_baseline.py new file mode 100644 index 00000000..152131cd --- /dev/null +++ b/backend/app_sdk_blackarch/static_baseline.py @@ -0,0 +1,76 @@ +import os +import sys +import time +import logging +import requests + +# Goal here: +# * Make an app from WALKOFF able to run without app_base.py from WALKOFF +# # How: +# * Make it rely 100% on INPUT throug HTTP invocations instead of redis READS +# # But really, how? +# * Make a WORKER that reads the queue, and reuses a function + +# Here to get it global +apikey = "" +try: + apikey = os.environ["FUNCTION_APIKEY"] +except KeyError: + pass + +# Authorize the execution +def authorization(request): + # This is basically my issue, but it enforces the use of an internal API key for execution + try: + apikey = os.environ["FUNCTION_APIKEY"] + except KeyError: + return f"Internal server error", 500 + + + # Check API key from ENV authentication + authentication = request.headers.get("Authorization") + if authentication == None: return f"Unauthorized", 401 + + apikey_split = authentication.split(" ") + if apikey_split[0] != "Bearer" or len(apikey_split) != 2: + return f"Apikey error", 401 + + if apikey != apikey_split[1]: + return f"Unauthorized", 401 + + return run(request) + +class AppBase: + """ The base class for Python-based Walkoff applications, handles Redis and logging configurations. """ + __version__ = None + app_name = None + + def __init__(self, redis=None, logger=None, console_logger=None):#, docker_client=None): + self.logger = logger if logger is not None else logging.getLogger("AppBaseLogger") + self.redis=redis + self.console_logger=console_logger + self.current_execution_id = None + self.url = "https://shuffler.io" + self.apikey = apikey + + @classmethod + async def run(cls, action): + """ Connect to Redis and HTTP session, await actions """ + logging.basicConfig(format="{asctime} - {name} - {levelname}:{message}", style='{') + logger = logging.getLogger(f"{cls.__name__}") + logger.setLevel(logging.DEBUG) + + app = cls(redis=None, logger=logger, console_logger=logger) + + # Authorization for the app/function to control the workflow + # Function will crash if its wrong, which it probably should. + + await app.execute_action(action) + + async def execute_action(self, action): + # FIXME - add request for the function STARTING here. Use "results stream" or something + # PAUSED, AWAITING_DATA, PENDING, COMPLETED, ABORTED, EXECUTING, SUCCESS, FAILURE + + self.authorization = action["authorization"] + self.execution_id = action["execution_id"] + self.current_execution_id = action["execution_id"] diff --git a/docker-compose.yml b/docker-compose.yml index 8457834a..b854d998 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,6 +53,7 @@ services: environment: - SHUFFLE_APP_SDK_VERSION=0.6.0 - SHUFFLE_APP_SDK_KALI_VERSION=0.6.0 + - SHUFFLE_APP_SDK_BLACKARCH_VERSION=0.6.0 - SHUFFLE_WORKER_VERSION=0.6.0 - ORG_ID=${ORG_ID} - ENVIRONMENT_NAME=${ENVIRONMENT_NAME} diff --git a/functions/onprem/README.md b/functions/onprem/README.md index 74358762..8539c949 100644 --- a/functions/onprem/README.md +++ b/functions/onprem/README.md @@ -46,3 +46,11 @@ cd app_sdk_kali docker build . -t frikky/shuffle:app_sdk_kali docker push frikky/shuffle:app_sdk_kali ``` + + +Update app_sdk_blackarch: +``` +cd app_sdk_blackarch +docker build . -t frikky/shuffle:app_sdk_blackarch +docker push frikky/shuffle:app_sdk_blackarch +``` \ No newline at end of file From 7c186700f72888b97bf74e10c34f4011f55af458 Mon Sep 17 00:00:00 2001 From: Peter Clemenko Date: Thu, 22 Oct 2020 01:10:56 -0400 Subject: [PATCH 3/5] It would help if I used pacman instead of apt on arch. --- backend/app_sdk_blackarch/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/app_sdk_blackarch/Dockerfile b/backend/app_sdk_blackarch/Dockerfile index e48f3095..02480aad 100644 --- a/backend/app_sdk_blackarch/Dockerfile +++ b/backend/app_sdk_blackarch/Dockerfile @@ -2,9 +2,9 @@ FROM peterclemenko/blackarch as base FROM base as builder -RUN apt-get update -RUN apt-get dist-upgrade -y -RUN apt install base-devel libffi musl openssl python python-pip -y +RUN /bin/pacman -Syu --noconfirm + +RUN /bin/pacman -Sy --noconfirm base-devel libffi musl openssl python python-pip -y RUN mkdir /install WORKDIR /install From 1ca662787a1842e918e07935e4b3509bbe830819 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 27 Oct 2020 12:41:59 +0100 Subject: [PATCH 4/5] Added architecture image --- README.md | 5 ++++- .../src/assets/img/shuffle_architecture.png | Bin 0 -> 146059 bytes 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 frontend/src/assets/img/shuffle_architecture.png diff --git a/README.md b/README.md index 56bee494..2466375e 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,9 @@ Please consider [sponsoring](https://github.com/sponsors/frikky) the project if * App creator for [OpenAPI](https://github.com/frikky/OpenAPI-security-definitions) * Easy to learn Python library for custom apps +## Architecture +![Shuffle Architecture](https://github.com/frikky/Shuffle/blob/master/frontend/src/assets/img/shuffle_architecture.png) + ## Website https://shuffler.io @@ -46,7 +49,7 @@ All modular information related to Shuffle will be under MIT (anyone can use it Apps & App SDK: MIT Shuffle backend: AGPLv3 -### Project overview +### Repository overview Below is the folder structure with a short explanation ```bash ├── README.md # What you're reading right now diff --git a/frontend/src/assets/img/shuffle_architecture.png b/frontend/src/assets/img/shuffle_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..585529a8919caaf9eb84228c9d83f2e211a74ea5 GIT binary patch literal 146059 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⪚f8sXZNx+MLr3!~Sr{<9Eo&WO3(9~m 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%0AXRY+3?}!P~j%aOZax7bL(}i2MlX_ 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 P0s-wP5@k<8U@!j-R_;!> literal 0 HcmV?d00001 From 0d5873367c09c66e64df283562304c734d30beb2 Mon Sep 17 00:00:00 2001 From: fp Date: Fri, 30 Oct 2020 11:37:23 -0700 Subject: [PATCH 5/5] Update README.md Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2466375e..00aed0be 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Shuffle -[Shuffle](https://shuffler.io) is an automation platform to unify your security services (SOAR). It has thousands of premade integrations and is based on open frameworks like OpenAPI and Mitre Att&ck. The workflow editor is based on a no-code thought process to empower non-developers, and the app creator makes you able to inegrate any platform in minutes. +[Shuffle](https://shuffler.io) is an automation platform to unify your security services (SOAR). It has thousands of premade integrations and is based on open frameworks like OpenAPI and Mitre Att&ck. The workflow editor is based on a no-code thought process to empower non-developers, and the app creator makes you able to integrate any platform in minutes. [![Discord](https://img.shields.io/discord/463752820026376202.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/B2CBzUm)