From d65661b8535317e0ffd9d7df9700003219362082 Mon Sep 17 00:00:00 2001 From: Josh Feingold Date: Tue, 19 Nov 2024 14:36:11 -0600 Subject: [PATCH] CHANGE @W-17100565@ FlowTest no longer requires PipX (#131) --- .../FlowTest/flow_parser/parse.py | 153 ++++-- .../FlowTest/flowtest/__main__.py | 70 ++- .../FlowTest/flowtest/branch_state.py | 22 +- .../FlowTest/flowtest/control_flow.py | 7 +- .../FlowTest/flowtest/executor.py | 29 +- .../FlowTest/flowtest/flow_metrics.py | 26 +- .../FlowTest/flowtest/flow_result.py | 12 +- .../FlowTest/flowtest/util.py | 12 +- .../FlowTest/flowtest/wire.py | 16 +- .../FlowTest/public/contracts.py | 10 +- .../FlowTest/public/enums.py | 96 ++++ .../FlowTest/public/parse_utils.py | 94 ++-- .../FlowTest/queries/default_query.py | 10 +- .../FlowTest/setup.py | 3 +- .../package.json | 1 - .../src/python/FlowTestCommandWrapper.ts | 14 +- .../src/python/PythonCommandExecutor.ts | 10 +- .../test/engine.test.ts | 4 +- .../test/plugin.test.ts | 2 +- .../python/FlowTestCommandWrapper.test.ts | 4 +- .../test/python/PythonCommandExecutor.test.ts | 2 +- .../python/PythonVersionIdentifier.test.ts | 2 +- .../results-non-windows.goldfile.json | 506 ------------------ .../results-windows.goldfile.json | 506 ------------------ .../results.goldfile.json | 354 ++++++++++++ 25 files changed, 767 insertions(+), 1198 deletions(-) delete mode 100644 packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-non-windows.goldfile.json delete mode 100644 packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-windows.goldfile.json create mode 100644 packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results.goldfile.json diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flow_parser/parse.py b/packages/code-analyzer-flowtest-engine/FlowTest/flow_parser/parse.py index beecc81d..96690ef1 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flow_parser/parse.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flow_parser/parse.py @@ -3,7 +3,11 @@ """ from __future__ import annotations -from lxml import etree as ET + +import sys + +sys.modules['_elementtree'] = None +import xml.etree.ElementTree as ET from typing import Optional import logging @@ -27,7 +31,7 @@ logger: logging.Logger = logging.getLogger(__name__) -def get_root(path: str) -> ET._Element: +def get_root(path: str) -> ET.Element: """Get flow root Args: @@ -37,7 +41,7 @@ def get_root(path: str) -> ET._Element: the root of the xml file """ - return ET.parse(path).getroot() + return ET.parse(path, parser=parse_utils.LineNumberingParser()).getroot() class Parser(FlowParser): @@ -59,7 +63,7 @@ class Parser(FlowParser): def __init__(self, root): #: XMl root of a single flow - self.root: ET._Element = root + self.root: ET.Element = root #: current filepath of flow self.flow_path: str | None = None @@ -74,7 +78,7 @@ def __init__(self, root): self.flow_type: FlowType | None = None #: frozen set of all elements that have a child of and are thus flow globals - self.all_named_elems: frozenset[ET._Element] | None = None + self.all_named_elems: frozenset[ET.Element] | None = None #: variables marked 'available for input', as a pair (flow_path, name) self.input_variables: frozenset[(str, str)] | None = None @@ -100,7 +104,7 @@ def get_declared_run_mode(self) -> RunMode: def get_filename(self) -> str: return self.flow_path - def get_root(self) -> ET._Element: + def get_root(self) -> ET.Element: return self.root def get_literal_var(self) -> VariableType: @@ -124,41 +128,47 @@ def get_flow_type(self) -> FlowType: # Process Builder # no but res = get_by_tag(self.root, 'startElementReference') - if len(res) > 0: - flow_type = FlowType.ProcessBuilder - - else: + if len(res) == 0: res = get_by_tag(self.root, 'start') - assert len(res) != 0 - start = res[0] + if len(res) == 0: + # this is an old format record trigger flow + self.flow_type = FlowType.RecordTrigger + return FlowType.RecordTrigger + start = res[0] - # Trigger, record - # has a child - child = get_by_tag(start, 'triggerType') - if len(child) > 0: - flow_type = FlowType.RecordTrigger + # Trigger, record + # has a child + child = get_by_tag(start, 'triggerType') + if len(child) > 0: + flow_type = FlowType.RecordTrigger - elif len(get_by_tag(start, 'schedule')) > 0: - flow_type = FlowType.Scheduled + elif len(get_by_tag(start, 'schedule')) > 0: + flow_type = FlowType.Scheduled - else: - # We couldn't determine flow type by looking at - # elem, so now look at processType elem - pt = get_by_tag(self.root, 'processType') - if len(pt) > 0: - pt = pt[0].text - - # Screen - # Flow and start does not have trigger or schedule - if pt == 'Flow' or len(get_by_tag(self.root, 'screens')) > 0: - flow_type = FlowType.Screen - - # AutoLaunched - # Some teams have their own names, e.g. FooAutolaunchedFlow - # Notice this messes up capitalization from normal 'AutoLaunchedFlow' - # there are also recommendation strategies, etc. - else: - flow_type = FlowType.AutoLaunched + else: + # We couldn't determine flow type by looking at + # elem, so now look at processType elem + pt = get_by_tag(self.root, 'processType') + if len(pt) > 0: + pt = pt[0].text + + # Screen + # Flow and start does not have trigger or schedule + if pt == 'Flow' or len(get_by_tag(self.root, 'screens')) > 0: + flow_type = FlowType.Screen + + elif pt.lower() == 'workflow': + flow_type = FlowType.Workflow + + elif pt.lower() == 'invocableprocess': + flow_type = FlowType.InvocableProcess + + # AutoLaunched + # Some teams have their own names, e.g. FooAutolaunchedFlow + # Notice this messes up capitalization from normal 'AutoLaunchedFlow' + # there are also recommendation strategies, etc. + else: + flow_type = FlowType.AutoLaunched if flow_type is not None: self.flow_type = flow_type @@ -174,7 +184,8 @@ def resolve_by_name(self, name: str, path: str | None = None, "Account_var.Name" --> ("Account_var", "Name", VariableType) "account_var" --> (account_var, None, VariableType). - (my_subflow.account.Name) --> (my_subflow.account, Name, VarType) + (my_subflow.account.Name) --> (my_subflow.account, Name, VariableType) + (my_action_call.account) --> (my_action_call.account, None, VariableType) Args: name: raw name as it is used in the flow xml file (e.g. foo.bar.baz) @@ -206,6 +217,9 @@ def resolve_by_name(self, name: str, path: str | None = None, if spl_len == 1: logger.warning(f"RESOLUTION ERROR {name}") if strict is False: + # 'strict' = False means that any unknown variable name + # is assumed to be a string literal that is hardcoded into + # the flows runtime and so not declared in flow xml file return name, None, self.literal_var else: return None @@ -231,7 +245,7 @@ def resolve_by_name(self, name: str, path: str | None = None, @classmethod def from_file(cls, filepath: str, old_parser: Parser = None) -> Parser: - root = ET.parse(filepath).getroot() + root = ET.parse(filepath, parser=parse_utils.LineNumberingParser()).getroot() parser = Parser(root) parser.flow_path = filepath parser.update(old_parser=old_parser) @@ -300,10 +314,10 @@ def get_input_variables(self, path: str | None = None) -> {(str, str)}: path = self.flow_path return {(x, y) for (x, y) in self.input_variables if x == path} - def get_input_field_elems(self) -> set[ET._Element] | None: + def get_input_field_elems(self) -> set[ET.Element] | None: return parse_utils.get_input_fields(self.root) - def get_input_output_elems(self) -> {str: set[ET._Element]}: + def get_input_output_elems(self) -> {str: set[ET.Element]}: """ Returns:: {"input": input variable elements, @@ -325,7 +339,7 @@ def get_input_output_elems(self) -> {str: set[ET._Element]}: "output": output_accum } - def get_by_name(self, name_to_match: str, scope: ET._Element | None = None) -> ET._Element | None: + def get_by_name(self, name_to_match: str, scope: ET.Element | None = None) -> ET.Element | None: """returns the first elem with the given name that is a child of the scope element""" if name_to_match == '*': return self.get_start_elem() @@ -356,9 +370,17 @@ def get_run_mode(self) -> RunMode: """ flow_type = self.get_flow_type() - if flow_type is not FlowType.Screen and flow_type is not FlowType.AutoLaunched: + + if flow_type is FlowType.InvocableProcess: + # always runs in user mode + return RunMode.DefaultMode + + if flow_type in [FlowType.Workflow, FlowType.RecordTrigger, FlowType.Scheduled, FlowType.ProcessBuilder]: + # always runs in system mode return RunMode.SystemModeWithoutSharing + # for screen and other autolaunched, check if there is a declaration + # otherwise go with default elems = get_by_tag(self.root, 'runInMode') if len(elems) == 0: return RunMode.DefaultMode @@ -368,7 +390,7 @@ def get_run_mode(self) -> RunMode: def get_api_version(self) -> str: return get_by_tag(self.root, 'apiVersion')[0].text - def get_all_traversable_flow_elements(self) -> [ET._Element]: + def get_all_traversable_flow_elements(self) -> [ET.Element]: """ ignore start""" return [child for child in self.root if get_tag(child) in ['actionCalls', 'assignments', 'decisions', 'loops', @@ -376,40 +398,40 @@ def get_all_traversable_flow_elements(self) -> [ET._Element]: 'collectionProcessors', 'recordDeletes', 'recordCreates', 'screens', 'subflows', 'waits', 'recordRollbacks']] - def get_all_variable_elems(self) -> [ET._Element] or None: + def get_all_variable_elems(self) -> [ET.Element] or None: elems = get_by_tag(self.root, 'variables') if len(elems) == 0: return None else: return elems - def get_templates(self) -> [ET._Element]: + def get_templates(self) -> [ET.Element]: """Grabs all template elements. Returns empty list if none found """ templates = get_by_tag(self.root, 'textTemplates') return templates - def get_formulas(self) -> [ET._Element]: + def get_formulas(self) -> [ET.Element]: """Grabs all formula elements. Returns empty list if none found """ formulas = get_by_tag(self.root, 'formulas') return formulas - def get_choices(self) -> [ET._Element]: + def get_choices(self) -> [ET.Element]: choices = get_by_tag(self.root, 'choices') return choices - def get_dynamic_choice_sets(self) -> [ET._Element]: + def get_dynamic_choice_sets(self) -> [ET.Element]: dcc = get_by_tag(self.root, 'dynamicChoiceSets') return dcc - def get_constants(self) -> [ET._Element]: + def get_constants(self) -> [ET.Element]: constants = get_by_tag(self.root, 'constants') return constants - def get_start_elem(self) -> ET._Element: + def get_start_elem(self) -> ET.Element: """Get first element of flow Returns: @@ -424,10 +446,16 @@ def get_start_elem(self) -> ET._Element: elif len(res2) == 1: return self.get_by_name(res2[0].text) + # Put in provision for older flows that are missing start elements but have only + # a single crud element + candidates = get_by_tag(self.root, 'recordUpdates') + if len(candidates) == 1: + return candidates[0] + else: raise RuntimeError("Currently only flows with a single 'start' or 'startElementReference' can be scanned") - def get_all_indirect_tuples(self) -> list[tuple[str, ET._Element]]: + def get_all_indirect_tuples(self) -> list[tuple[str, ET.Element]]: """returns a list of tuples of all indirect references, e.g. str, elem, where str influences elem. The elem is a formula or template element and @@ -483,13 +511,13 @@ def __get_type(self, name: str, path: str | None = None, strict: bool = False) - else: logger.info(f"Auto-resolving {name} in file {self.flow_path}") - if strict is False: + if strict is True: return self.literal_var else: return None -def build_vartype_from_elem(elem: ET._Element) -> VariableType | None: +def build_vartype_from_elem(elem: ET.Element) -> VariableType | None: """Build VariableType from XML Element The purpose of this function is to assign types to named @@ -538,6 +566,15 @@ def build_vartype_from_elem(elem: ET._Element) -> VariableType | None: object_name=parse_utils.get_obj_name(elem), is_optional=nulls_provided is not None and nulls_provided is False) + if tag == 'actionCalls': + is_ = parse_utils.is_auto_store(elem) + if is_ is True: + reference = ReferenceType.ActionCallReference + # TODO: see if we can get datatype info from return value + + return VariableType(tag=tag, datatype=DataType.StringValue, + reference=reference, is_optional=False) + if tag == 'recordCreates': # Todo: get collection parsing correct, look if record being created is itself # a collection element - do examples of bulkified versions of commands. @@ -640,7 +677,7 @@ def build_vartype_from_elem(elem: ET._Element) -> VariableType | None: return None -def _get_global_flow_data(flow_path, root: ET._Element) -> ([ET._Element], {str: VariableType}): +def _get_global_flow_data(flow_path, root: ET.Element) -> ([ET.Element], {str: VariableType}): all_named = get_named_elems(root) # all named cannot be None, each flow must have at least one named element. @@ -655,15 +692,15 @@ def _get_global_flow_data(flow_path, root: ET._Element) -> ([ET._Element], {str: try: var = build_vartype_from_elem(x) except Exception: - logger.error(f"ERROR parsing element {ET.tounicode(x)}") + logger.error(f"ERROR parsing element {ET.tostring(x, encoding='unicode')}") continue if var is not None: vars_[(flow_path, name_dict[x])] = var - if var.is_input and var.is_input is True: + if var.is_input is True: inputs.append((flow_path, name_dict[x])) - if var.is_output and var.is_output is True: + if var.is_output is True: outputs.append((flow_path, name_dict[x])) return all_named, vars_, frozenset(inputs), frozenset(outputs) diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/__main__.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/__main__.py index 301d10a7..312a38e4 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/__main__.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/__main__.py @@ -60,6 +60,19 @@ def check_dir_exists(x: str) -> str: return x +def check_dir_exists_or_create(x: str) -> str: + """Checks if the argument is a valid directory. Creates directory if it doesn't exist. + + Args: + x: string to check + + Returns: + None + """ + os.makedirs(x, exist_ok=True) + return x + + def check_not_exist(x: str) -> str: """lambda that checks if this path exists or not. Raises an error if it does exist. @@ -87,23 +100,31 @@ def get_flow_paths(args: argparse.Namespace) -> (list[str], {str: str}): """ arg_flow = args.flow arg_dir = args.dir + count = 0 - if arg_flow is None and arg_dir is None: + if arg_dir is None: arg_dir = CURR_DIR - if arg_flow is not None: - print(f"scanning the single flow: {arg_flow}") - return [arg_flow], util.get_flows_in_dir(os.path.dirname(os.path.abspath(arg_flow))) + flow_map = util.get_flows_in_dir(arg_dir) - elif arg_dir is not None: - flow_paths = util.get_flows_in_dir(arg_dir) - count = len(flow_paths) - if count > 0: - print(f"found {count} flows to scan in {arg_dir}") - return list(flow_paths.values()), flow_paths - else: - print("No flow files found to scan. Exiting...") - sys.exit(1) + if arg_flow is None: + flows = list(flow_map.values()) + flow_paths = [os.path.abspath(x) for x in flows] + + else: + flow_paths = [os.path.abspath(x) for x in arg_flow.split(",")] + for a_flow in flow_paths: + label = util.get_label(os.path.dirname(a_flow), a_flow) + if label is not None and label not in flow_map: + flow_map[label] = a_flow + + count = len(flow_paths) + if count > 0: + print(f"found {count} flows to scan") + return flow_paths, flow_map + else: + print("No flow files found to scan. Exiting...") + sys.exit(1) def parse_args(my_args: list[str], default: str = None) -> argparse.Namespace: @@ -126,7 +147,7 @@ def parse_args(my_args: list[str], default: str = None) -> argparse.Namespace: epilog="Please reach out to your security contact with feedback and bugreports", ) """ - most important option + options that display status and tool info """ parser.add_argument("-v", "--version", action='version', version='%(prog)s ' + version.__version__) @@ -138,11 +159,13 @@ def parse_args(my_args: list[str], default: str = None) -> argparse.Namespace: """ paths = parser.add_mutually_exclusive_group() - paths.add_argument("-f", "--flow", help=("path of flow-meta.xml file if only a single " - "flow is to be scanned"), - type=check_file_exists, required=False) + paths.add_argument("-f", "--flow", help=("path of flow files scan, csv separated. " + "If provided, only these will be scanned."), + required=False) paths.add_argument("-d", "--dir", help=("directory containing flow-meta.xml files " - "subdirectories are also searched. Defaults to working directory."), + "subdirectories are also searched. Defaults to working directory. If no" + "flows specified all flows in directory will be scanned, otherwise only" + "subflows in this directory will be scanned"), type=check_dir_exists) """ @@ -154,6 +177,12 @@ def parse_args(my_args: list[str], default: str = None) -> argparse.Namespace: parser.add_argument("--debug", action='store_true', help="whether to set logging level to debug") + """ + Options for crawl-spec generation + """ + parser.add_argument("--crawl_dir", default=None, + help="where to store crawl specification", + type=check_dir_exists) """ Options for storing reports """ @@ -269,12 +298,17 @@ def main(argv: list[str] = None) -> str | None: query_module_path=args.query_path, query_class_name=args.query_class, query_preset=args.preset, + crawl_dir=args.crawl_dir, all_flows=all_flows) except: print(f"error processing flow {flow_path}") print(traceback.format_exc()) print("...continuing to next flow..") + if query_manager is None: + print("No flow could be scanned. Exiting.") + sys.exit(-1) + print("scanning complete.") print(f"{STATUS_LABEL} {STATUS_REPORT_GEN}") if args.xml is not None: diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/branch_state.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/branch_state.py index 2afbf890..914cd4bb 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/branch_state.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/branch_state.py @@ -11,6 +11,7 @@ import copy import logging +import traceback from flow_parser import parse from flow_parser.parse import ET @@ -69,7 +70,7 @@ def __init__(self, parser: parse.Parser): self.current_elem_name: str = '*' #: current Flow element (xml) - self.current_elem: ET._Element = None + self.current_elem: ET.Element = None #: resolves formulas and templates (late binding indirect references) self.formula_map: {(str, str): {DataInfluencePath}} = {} @@ -112,7 +113,7 @@ def get_parser(self) -> parse.Parser: """ return self.parser - def get_current_elem(self) -> ET._Element: + def get_current_elem(self) -> ET.Element: """Get current element being processed Returns: @@ -407,7 +408,7 @@ def is_in_map(self, var_name: str) -> bool: return (self.flow_path, var_name) in influence_map def add_vectors_from_other_flow(self, src_flow_path: str, output_vector_map: {(str, str): FlowVector}, - src2tgt_variable_map: {str: str}, transition_elem: ET._Element, + src2tgt_variable_map: {str: str}, transition_elem: ET.Element, ) -> {(str, str): FlowVector} or None: """Pushes vectors in the source to vectors in the target, by wiring a flow across flow boundaries:: @@ -581,7 +582,7 @@ def _get_influence_map(self, crawl_step: CrawlStep = None) -> {(str, str): FlowV else: return dict.get(self.__influence_map, cs, None) - def _initialize_variables_from_elems(self, elems: set[ET._Element] | None) -> None: + def _initialize_variables_from_elems(self, elems: set[ET.Element] | None) -> None: """Adds variables to the influence map (if not already present) .. WARNING:: Expert use. Only to initialize from named elements that are actually @@ -671,7 +672,7 @@ def _get_vector(self, flow_path: str, name: str, step: CrawlStep = None) -> Flow return dict.get(self._get_influence_map(crawl_step=step), (flow_path, name)) - def _init_vec_from_elem(self, elem: ET._Element, store=True) -> FlowVector | None: + def _init_vec_from_elem(self, elem: ET.Element, store=True) -> FlowVector | None: """Initializes a FlowVector from the provided (named) xml element Args: @@ -832,8 +833,13 @@ def _build_path_from_history(parser: parse.Parser, history: tuple[DataInfluenceS last = history[-1] first = history[0] - (first_parent, first_member, first_type) = parser.resolve_by_name(first.influencer_var, strict=strict) - (last_parent, last_member, last_type) = parser.resolve_by_name(last.influenced_var, strict=strict) + try: + (first_parent, first_member, first_type) = parser.resolve_by_name(first.influencer_var, strict=strict) + (last_parent, last_member, last_type) = parser.resolve_by_name(last.influenced_var, strict=strict) + except TypeError as e: + print(f"Resolver error for {first.influencer_var} or {last.influenced_var} ") + print(traceback.format_exc()) + raise e my_type = propagate(src_type=first_type, dest_type=last_type, **type_replacements) @@ -946,7 +952,7 @@ def _extend_formula_map_by_flows(start_flows: set[DataInfluencePath], return accum -def _resolve_influencers(elem_ref_name: ET._Element, +def _resolve_influencers(elem_ref_name: ET.Element, raw_formula_map: {str: [DataInfluenceStatement]}, parser: parse.Parser) -> {DataInfluencePath}: """Resolves indirect references diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/control_flow.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/control_flow.py index bf455658..1b3d7207 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/control_flow.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/control_flow.py @@ -118,7 +118,7 @@ def _send_outbound(self, visitor): @classmethod def build_from_parser(cls, parser: parse.Parser, - elem: ET._Element, + elem: ET.Element, seen_names: [str] = None): label = get_name(elem) @@ -366,6 +366,11 @@ def crawl_iter(cfg: ControlFlowGraph) -> Generator[(BranchVisitor, [Segment]), N # nowhere to jump, so pull from worklist visitor = worklist.pop(0) + # skip orphaned references + if visitor.current_label not in cfg.segment_map: + visitor = None + continue + segment = cfg.segment_map[visitor.current_label] next_visitors = segment.accept(visitor) diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/executor.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/executor.py index e46c9771..a4427feb 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/executor.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/executor.py @@ -8,12 +8,15 @@ from __future__ import annotations +import json import logging +import os from typing import TYPE_CHECKING +import flowtest.control_flow as crawl_spec import flow_parser.parse as parse import public.parse_utils -from flowtest.control_flow import Crawler +from flowtest.control_flow import Crawler, ControlFlowGraph from flowtest.branch_state import BranchState from flowtest.query_manager import QueryManager, QueryAction from public import parse_utils @@ -288,7 +291,7 @@ def __init__(self, current_flow_path: str | None = None, all_flow_paths: {str: s self.crawler: Crawler | None = None #: Subflow XML element that launched this flow (can be None) - self.parent_subflow: ET._Element | None = None + self.parent_subflow: ET.Element | None = None #: binary semaphore so when we return, we don't execute spawn the subflow again self.child_spawned: bool = False @@ -321,7 +324,7 @@ def __init__(self, current_flow_path: str | None = None, all_flow_paths: {str: s def build(cls, current_flow_path: str | None = None, all_flow_paths: {str: str} = None, resolved_subflows: {} = None, - parent_subflow: ET._Element = None, + parent_subflow: ET.Element = None, query_manager: QueryManager = None) -> Frame: """Call this whenever program analysis starts or a subflow is reached @@ -463,7 +466,7 @@ def get_consolidated_output_vars(self) -> {(str, str): flows.FlowVector}: return new_map - def spawn_child_frame(self, subflow: ET._Element, + def spawn_child_frame(self, subflow: ET.Element, sub_path: str, input_map: {str: str}, vector_map: {(str, str): flows.FlowVector} @@ -523,7 +526,7 @@ def spawn_child_frame(self, subflow: ET._Element, self.child_spawned = True return new_frame - def handle_subflows(self, current_elem: ET._Element) -> Frame | None: + def handle_subflows(self, current_elem: ET.Element) -> Frame | None: """Checks whether we have encountered a subflow elem. Different behavior required if we are returning from the element or entering into it. @@ -653,6 +656,7 @@ def parse_flow(flow_path: str, query_class_name: str = None, query_preset: str = None, query_manager: QueryManager | None = None, + crawl_dir: str = None, all_flows: {str: str} = None) -> QueryManager: """Main loop that performs control and dataflow analysis @@ -668,6 +672,7 @@ def parse_flow(flow_path: str, query_preset: name of preset to run query_manager: existing instance that invokes queries across entire run. Start with None and one will be created. + crawl_dir: directory of where to store crawl specifications all_flows: map flow name -> path of flow (used for looking up flow paths of subflows) Returns: @@ -678,6 +683,18 @@ def parse_flow(flow_path: str, # build parser parser = parse.Parser.from_file(filepath=flow_path) + if crawl_dir is not None: + cfg = ControlFlowGraph.from_parser(parser) + schedule = crawl_spec.get_crawl_schedule(cfg) + cleaned_path = flow_path.replace(os.sep, "_") + + with open(os.path.join(crawl_dir, f"{cleaned_path}__crawl_schedule.json"), + 'w') as fp: + json.dump(schedule, fp, cls=crawl_spec.CrawlEncoder, indent=4) + + with open(os.path.join(crawl_dir, f"{cleaned_path}_cfg.json"), 'w') as fp: + crawl_spec.dump_cfg(cfg, fp) + if query_manager is None: # 1. build result processor results = Results(requestor=requestor, report_label=report_label, @@ -723,7 +740,7 @@ def report(state: BranchState, current_step: int, total_steps: int) -> None: logger.debug(msg) -def get_output_variable_map(subflow_elem: ET._Element, subflow_output_vars: [(str, str)]) -> {str: str}: +def get_output_variable_map(subflow_elem: ET.Element, subflow_output_vars: [(str, str)]) -> {str: str}: # output_variable_map: child name --> parent name the child influences auto, output_variable_map = public.parse_utils.get_subflow_output_map(subflow_elem) if auto is True: diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_metrics.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_metrics.py index a3d39544..af130c90 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_metrics.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_metrics.py @@ -16,7 +16,7 @@ from typing import TYPE_CHECKING # noinspection PyUnresolvedReferences -import lxml.etree as ET +import xml.etree.ElementTree as ET import logging import traceback from . import ESAPI @@ -171,7 +171,10 @@ def serialize(portion, elem): return '' + line_end elif portion is None: - return ET.tounicode(elem, pretty_print=True) + raw_str = ET.tostring(elem, encoding='unicode', + default_namespace='http://soap.sforce.com/2006/04/metadata') + return raw_str #todo: pretty print + else: raise RuntimeError('Called with invalid portion' + portion) @@ -782,8 +785,8 @@ def _make_footer(report_fp): def _clean_up(element): if element is not None: element.clear() - while element.getprevious() is not None: - del element.getparent()[0] + # while element.getprevious() is not None: + # del element.getparent()[0] def _get_signature(element): @@ -889,9 +892,7 @@ def parse_results(xml_file=None, elif xml_report_str is None and xml_file is None: raise ValueError("no xml file passed into function") - context = ET.iterparse(xml_file, events=('end', 'start'), - remove_blank_text=True, - encoding='utf-8') + context = ET.iterparse(xml_file, events=('end', 'start')) query_printed = False # track whether we have rendered this query @@ -899,6 +900,7 @@ def parse_results(xml_file=None, jobinfo.update(root) logger.debug('preset is: ' + jobinfo.preset) + parent = None for event, element in context: @@ -917,7 +919,7 @@ def parse_results(xml_file=None, query_printed is False): # render parent (result) info query_printed = True - _report_append(element.getparent(), report_fp, source_dir) + _report_append(parent, report_fp, source_dir) if report_fp is not None: _report_append(element, report_fp, @@ -925,6 +927,7 @@ def parse_results(xml_file=None, query_data.tallies) if event == 'end': + parent = element if element.tag == 'Query': scan_results.add(query_data) @@ -982,7 +985,7 @@ def _pre_parse(xml_file, Returns: src_data - + """ context = None @@ -1018,6 +1021,7 @@ def _pre_parse(xml_file, # render root out_fp.write(serialize('start', root)) + parent_map = {} for event, element in context: @@ -1053,7 +1057,7 @@ def _pre_parse(xml_file, skip_path = True if event == 'end': - + parent_map if element.tag == 'Query': out_fp.write(serialize('end', element)) @@ -1081,7 +1085,7 @@ def _pre_parse(xml_file, known_path_sig.add(curr_path_sig) # print entire result (inc all paths) to file - out_fp.write(serialize(None, element.getparent())) + out_fp.write(serialize(None, parent)) total += 1 elif element.tag == 'PathNode': diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_result.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_result.py index 8b119008..6b1808fb 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_result.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/flow_result.py @@ -7,10 +7,12 @@ import json import logging +import sys from datetime import datetime from typing import TextIO -import lxml.etree as ET +sys.modules['_elementtree'] = None +import xml.etree.ElementTree as ET from flowtest import ESAPI from flowtest import flow_metrics @@ -156,7 +158,7 @@ def get_cx_xml_str(self): result_str += (f'' - f'') + f'') for index, node in enumerate(statements): filename = node.flow_path line = node.line_no @@ -211,7 +213,7 @@ def gen_result_dict(self) -> {str: {str: str}}: Used internally to generate popcrab compatible xml and html report formats. - + Also useful for testing Returns: @@ -309,7 +311,7 @@ def _validate_and_prettify_xml(xml_str: str) -> str: """ my_root = ET.fromstring(bytes(xml_str, encoding='utf-8')) ET.indent(my_root) - return ET.tounicode(my_root) + return ET.tostring(my_root, encoding='unicode', default_namespace='http://soap.sforce.com/2006/04/metadata') def _merge_results(results: list[QueryResult]) -> [QueryResult]: @@ -359,7 +361,7 @@ def _merge_results(results: list[QueryResult]) -> [QueryResult]: query_id=qr.query_id, influence_statement=qr.influence_statement, paths=frozenset(new_paths) - ) + ) ) return new_list diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/util.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/util.py index 723fee37..c8f8cebb 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/util.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/util.py @@ -43,10 +43,7 @@ def get_flows_in_dir(root_dir: str) -> {str: str}: flow_paths = dict() for root, dir_names, filenames in os.walk(root_dir): for filename in filenames: - if filename.endswith(FLOW_EXTENSION): - flow_paths[get_label(root, filename[:-14])] = os.path.join(root, filename) - elif filename.endswith(PACKAGE_FLOW_EXTENSION): - flow_paths[get_label(root, filename[:-5])] = os.path.join(root, filename) + flow_paths[get_label(root, filename)] = os.path.join(root, filename) return flow_paths @@ -67,6 +64,13 @@ def get_label(root: str, filename: str) -> (str, str): tuple (namespaced_label, local_label) """ + if filename.endswith(PACKAGE_FLOW_EXTENSION): + short_fname = filename[:-5] + elif filename.endswith(FLOW_EXTENSION): + short_fname = filename[:-14] + else: + short_fname = filename + local_label = filename.split('-')[0] parent_dirname = pathlib.PurePath(root).name namespaced_label = f"{parent_dirname}__{local_label}" diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/wire.py b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/wire.py index bd8e98a9..c5b5053e 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/wire.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/flowtest/wire.py @@ -48,7 +48,7 @@ logger = logging.getLogger(__name__) -def handle_auto_store(state: BranchState, elem: ET._Element, elem_name: str) -> None: +def handle_auto_store(state: BranchState, elem: ET.Element, elem_name: str) -> None: """Add this element name to influence map if it represents its own output data (Element name is passed in so we don't need to keep looking it up) @@ -70,7 +70,7 @@ def handle_auto_store(state: BranchState, elem: ET._Element, elem_name: str) -> state.get_or_make_vector(name=ref, store=True) -def wire(state: BranchState, elem: ET._Element): +def wire(state: BranchState, elem: ET.Element): """Wires influence statements and variable initialization. When the value of one variable changes based on another. @@ -128,7 +128,7 @@ def wire(state: BranchState, elem: ET._Element): state.get_or_make_vector(name=parse_utils.get_name(el), store=True) -def wire_assignment(state: BranchState, elem: ET._Element, elem_name: str): +def wire_assignment(state: BranchState, elem: ET.Element, elem_name: str): """Wires assignment statements to influence map in `state` Args: @@ -172,7 +172,7 @@ def wire_assignment(state: BranchState, elem: ET._Element, elem_name: str): store=True) -def wire_loop(state: BranchState, elem: ET._Element, elem_name: str): +def wire_loop(state: BranchState, elem: ET.Element, elem_name: str): """Wires collection loop is over to loop variable. Args: @@ -192,7 +192,8 @@ def wire_loop(state: BranchState, elem: ET._Element, elem_name: str): influenced_var=loop_var, influencer_var=collection_ref_var, element_name=elem_name, - source_text=parse.ET.tounicode(collection_ref_el), + source_text=parse.ET.tostring(collection_ref_el, encoding='unicode', + default_namespace='http://soap.sforce.com/2006/04/metadata'), line_no=collection_ref_el.sourceline, comment='assign to loop variable', flow_path=state.flow_path @@ -200,7 +201,7 @@ def wire_loop(state: BranchState, elem: ET._Element, elem_name: str): state.propagate_flows(statement=stmt, assign=True, store=True) -def wire_collection_processor(state: BranchState, elem: ET._Element, elem_name: str): +def wire_collection_processor(state: BranchState, elem: ET.Element, elem_name: str): """Wires collection reference in collection processor to collection elem. Args: @@ -224,7 +225,8 @@ def wire_collection_processor(state: BranchState, elem: ET._Element, elem_name: influenced_var=collection_var, influencer_var=collection_ref_var, element_name=elem_name, - source_text=parse.ET.tounicode(collection_el), + source_text=parse.ET.tostring(collection_el, encoding='unicode', + default_namespace='http://soap.sforce.com/2006/04/metadata'), line_no=collection_el.sourceline, comment='collection filter', flow_path=state.flow_path diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/public/contracts.py b/packages/code-analyzer-flowtest-engine/FlowTest/public/contracts.py index 81df4b42..9aeb1238 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/public/contracts.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/public/contracts.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: from public.data_obj import DataInfluencePath, VariableType - from lxml import etree as ET + import xml.etree.ElementTree as ET from public.enums import RunMode @@ -161,7 +161,7 @@ def get_parser(self) -> FlowParser: pass @abstractmethod - def get_current_elem(self) -> ET._Element: + def get_current_elem(self) -> ET.Element: pass @abstractmethod @@ -206,7 +206,7 @@ def get_flow_type(self)-> public.enums.FlowType: pass @abstractmethod - def get_root(self) -> ET._Element: + def get_root(self) -> ET.Element: pass @abstractmethod @@ -232,7 +232,7 @@ def get_input_variables(self, path: str | None = None) -> {(str, str)}: pass @abstractmethod - def get_input_field_elems(self) -> set[ET._Element] | None: + def get_input_field_elems(self) -> set[ET.Element] | None: """Named XML elements that are children of Screen Flow Input Text Elements .. Note:: Only returns variables from current flow @@ -243,5 +243,5 @@ def get_input_field_elems(self) -> set[ET._Element] | None: pass @abstractmethod - def get_by_name(self, name_to_match: str, scope: ET._Element | None = None) -> ET._Element | None: + def get_by_name(self, name_to_match: str, scope: ET.Element | None = None) -> ET.Element | None: pass diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/public/enums.py b/packages/code-analyzer-flowtest-engine/FlowTest/public/enums.py index 316acad6..ca74d292 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/public/enums.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/public/enums.py @@ -21,6 +21,102 @@ class RunMode(Enum): SystemModeWithoutSharing = 0 SystemModeWithSharing = 1 DefaultMode = 2 +"""Public Enum types + +""" +from enum import Enum + + +class FlowType(Enum): + Screen = 0 + AutoLaunched = 1 + RecordTrigger = 2 + Scheduled = 3 + ProcessBuilder = 4 + Workflow = 5 + InvocableProcess = 6 + + +class FlowValue(Enum): + ElementReference = 0 + Literal = 1 + + +class RunMode(Enum): + SystemModeWithoutSharing = 0 + SystemModeWithSharing = 1 + DefaultMode = 2 + + +class DataType(Enum): + StringValue = 1 + Object = 2 + Literal = 3 + + +class ConnType(Enum): + Loop = 1 # only for nextValue connectors (for loop unrolling) + Goto = 2 # all connectors labelled goto + Other = 3 # everything else (including noMoreValue connectors) + + +class ReferenceType(Enum): + # this is a variable holding the value + Direct = 0 + + # This is a formula or template field pointing to other fields + Formula = 1 + + # A loop element or collection representative element pointing to a collection + CollectionReference = 2 + + # A reference to a variable passed in from a subflow, so foo.var refers to var in subflow. + SubflowReference = 3 + + # A reference to a variable passed in from an action call, so foo.var refers to var in apex. + ActionCallReference = 4 + + # A reference to a named Flow Element that is not a subflow or loop or collection ref. + ElementReference = 5 + + # A constant + Constant = 6 + + # Global + Global = 7 + + +class Severity(Enum): + """ + All queries must be labelled with a severity field. In the report, they will + be sorted by severity and the severity will be displayed. The severity is per + Query, not per result, so if your code contains branching logic in which one + result is considered more severe, consider defining multiple queries and explaining + the reason for the difference in severity. An example of explaining + the reason for severity can be found in the class definition below: + + """ + + # Something that might be useful for a developer to know but which is not + # considered to be a vulnerability, or is highly unlikely to be a vulnerability + # due to high false positive rates. + Flow_Informational = 0 + + # Issues that are not usually serious but are violations of policy. + # for example, a system mode with sharing read of data without + # the data being returned to the user. + Flow_Low_Severity = 10 + + # For example, a system mode without sharing read of data that is returned to the + # user. Or a system mode with sharing modification of a field without checking FLS + # permissions. + Flow_Moderate_Severity = 20 + + # For example, a system mode without sharing modification of data + Flow_High_Severity = 30 + + def __str__(self): + return str(self.name) class DataType(Enum): diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/public/parse_utils.py b/packages/code-analyzer-flowtest-engine/FlowTest/public/parse_utils.py index 7bf29017..c14cd585 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/public/parse_utils.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/public/parse_utils.py @@ -17,8 +17,11 @@ import logging import re +import sys + +sys.modules['_elementtree'] = None +import xml.etree.ElementTree as ET -from lxml import etree as ET from public.enums import DataType, ConnType @@ -71,22 +74,22 @@ def parse_expression(txt: str) -> list[str]: return accum -def get_tag(elem: ET._Element) -> str: - if isinstance(elem, ET._Comment): - return '' - elif isinstance(elem, ET._Element): +def get_tag(elem: ET.Element) -> str: + if isinstance(elem, ET.Element): return elem.tag[NS_LEN:] + # elif isinstance(elem, ET._Comment): + # return '' else: return '' -def is_subflow(elem: ET._Element) -> bool: +def is_subflow(elem: ET.Element) -> bool: if elem is None: return False return get_tag(elem) == 'subflows' -def is_loop(elem: ET._Element) -> bool: +def is_loop(elem: ET.Element) -> bool: """Is this a Loop Flow Element? Args: @@ -100,7 +103,7 @@ def is_loop(elem: ET._Element) -> bool: return elem.tag.endswith("loops") -def is_goto_connector(elem: ET._Element) -> bool: +def is_goto_connector(elem: ET.Element) -> bool: """Is this element a goto? Args: @@ -116,7 +119,7 @@ def is_goto_connector(elem: ET._Element) -> bool: return False -def is_decision(elem: ET._Element) -> bool: +def is_decision(elem: ET.Element) -> bool: """True if this is a decision Flow Element Args: @@ -128,7 +131,7 @@ def is_decision(elem: ET._Element) -> bool: return get_tag(elem) == 'decisions' -def get_by_tag(elem: ET._Element, tagname: str) -> list[ET.etree._Element]: +def get_by_tag(elem: ET.Element, tagname: str) -> list[ET.etree._Element]: """Get list of all elem with the tag (ignoring ns). Convenience method as manually dealing with namespaces is clumsy. @@ -144,7 +147,7 @@ def get_by_tag(elem: ET._Element, tagname: str) -> list[ET.etree._Element]: return elem.findall(f'{ns}{tagname}') -def get_named_elems(elem: ET._Element) -> list[ET._Element]: +def get_named_elems(elem: ET.Element) -> list[ET.Element]: """Get all descendents (recursive) of elem that have a ``name`` tag Args: @@ -158,7 +161,7 @@ def get_named_elems(elem: ET._Element) -> list[ET._Element]: return [x for x in named if get_tag(x) != f'{ns}processMetadataValues'] -def get_name(elem: ET._Element | None) -> str | None: +def get_name(elem: ET.Element | None) -> str | None: """returns the string name of elem or None if no name or '*'""" if elem is None: return None @@ -171,14 +174,15 @@ def get_name(elem: ET._Element | None) -> str | None: return name.text -def get_elem_string(elem: ET._Element) -> str | None: +def get_elem_string(elem: ET.Element) -> str | None: if elem is None: return '' else: - return ET.tounicode(elem).strip() + return ET.tostring(elem, encoding='unicode', + default_namespace='http://soap.sforce.com/2006/04/metadata').strip() -def get_line_no(elem: ET._Element) -> int: +def get_line_no(elem: ET.Element) -> int: return elem.sourceline @@ -186,7 +190,7 @@ def get_subflow_name(subflow): return get_by_tag(subflow, "flowName")[0].text -def get_assignment_statement_dicts(elem: ET._Element) -> list[(str, {str: str})] | None: +def get_assignment_statement_dicts(elem: ET.Element) -> list[(str, {str: str})] | None: """Returns assignment statement keywords in 'assignments' elems Args: elem: elem to parse, should have a tag of "assignments" @@ -210,7 +214,7 @@ def get_assignment_statement_dicts(elem: ET._Element) -> list[(str, {str: str})] return None -def get_filters(elem: ET._Element) -> [ET._Element]: +def get_filters(elem: ET.Element) -> [ET.Element]: """Find all filter elements Searches recursively to find all elements that are children @@ -226,7 +230,7 @@ def get_filters(elem: ET._Element) -> [ET._Element]: return elem.findall(f'.//{ns}filters') -def get_input_assignments(elem: ET._Element) -> [ET._Element]: +def get_input_assignments(elem: ET.Element) -> [ET.Element]: """Find all input assignments Searches recursively to find all elements that are children @@ -242,7 +246,7 @@ def get_input_assignments(elem: ET._Element) -> [ET._Element]: return elem.findall(f'.//{ns}inputAssignments') -def get_sinks_from_field_values(elems: ET._Element) -> list[(str, str)]: +def get_sinks_from_field_values(elems: ET.Element) -> list[(str, str)]: """Find variables that flow into field/value pairs E.g.if a recordLookup field has a filter:: @@ -297,7 +301,7 @@ def get_sinks_from_field_values(elems: ET._Element) -> list[(str, str)]: return accum -def get_conn_target_map(elem: ET._Element) -> {ET._Element: (str, ConnType, bool)}: +def get_conn_target_map(elem: ET.Element) -> {ET.Element: (str, ConnType, bool)}: """returns map from connectors at elem to where they point Args: @@ -332,7 +336,8 @@ def get_conn_target_map(elem: ET._Element) -> {ET._Element: (str, ConnType, bool res = get_by_tag(elem=x, tagname='targetReference') if res is None or len(res) == 0: - logger.error(f"ERROR: found a connector without a target reference! {ET.tounicode(elem)}") + logger.error(f"ERROR: found a connector without a target reference! " + f"{ET.tostring(elem, encoding='unicode')}") continue else: # don't overwrite existing value -- each connector should have a single target reference @@ -360,14 +365,14 @@ def get_conn_target_map(elem: ET._Element) -> {ET._Element: (str, ConnType, bool # -def is_assign_null(elem: ET._Element) -> bool | None: +def is_assign_null(elem: ET.Element) -> bool | None: res = elem.find(f'{ns}assignNullValuesIfNoRecordsFound') if res is None: return None return res.text == 'true' -def is_auto_store(elem: ET._Element) -> bool | None: +def is_auto_store(elem: ET.Element) -> bool | None: # None if the field is missing or can't be parsed # otherwise true or false res = elem.find(f'{ns}storeOutputAutomatically') @@ -376,7 +381,7 @@ def is_auto_store(elem: ET._Element) -> bool | None: return res.text == 'true' -def is_collection(elem: ET._Element) -> bool | None: +def is_collection(elem: ET.Element) -> bool | None: # None if the field is missing or can't be parsed # otherwise true or false res = elem.find(f'{ns}isCollection') @@ -385,7 +390,7 @@ def is_collection(elem: ET._Element) -> bool | None: return res.text == 'true' -def get_input_fields(elem: ET._Element) -> set[ET._Element] | None: +def get_input_fields(elem: ET.Element) -> set[ET.Element] | None: accum = set() elems = elem.findall(f'.//{ns}fields') for el in elems: @@ -399,21 +404,21 @@ def get_input_fields(elem: ET._Element) -> set[ET._Element] | None: return accum -def get_obj_name(elem: ET._Element) -> str | None: +def get_obj_name(elem: ET.Element) -> str | None: object_name = elem.find(f'{ns}object') if object_name is None: return None return object_name.text -def get_output_reference(elem: ET._Element) -> str | None: +def get_output_reference(elem: ET.Element) -> str | None: object_name = elem.find(f'{ns}outputReference') if object_name is None: return None return object_name.text -def get_datatype(elem: ET._Element) -> DataType | None: +def get_datatype(elem: ET.Element) -> DataType | None: obj_ = elem.find(f'{ns}dataType') if obj_ is None: return None @@ -431,19 +436,19 @@ def get_datatype(elem: ET._Element) -> DataType | None: return DataType.Literal -def is_get_first_record_only(elem: ET._Element) -> bool | None: +def is_get_first_record_only(elem: ET.Element) -> bool | None: res = elem.find(f'{ns}getFirstRecordOnly') if res is None: return None return res.text == 'true' -def is_input(elem: ET._Element) -> bool: +def is_input(elem: ET.Element) -> bool: res = get_by_tag(elem, 'isInput') return len(res) > 0 and res[0].text == 'true' -def is_output(elem: ET._Element) -> bool: +def is_output(elem: ET.Element) -> bool: res = get_by_tag(elem, 'isOutput') return len(res) > 0 and res[0].text == 'true' @@ -455,7 +460,7 @@ def is_output(elem: ET._Element) -> bool: """ -def _process_assignment_item(elem: ET._Element) -> (str, {str: str}): +def _process_assignment_item(elem: ET.Element) -> (str, {str: str}): """Returns assignment item dict from assignment element Args: @@ -500,7 +505,7 @@ def _process_assignment_item(elem: ET._Element) -> (str, {str: str}): return None -def _get_value(el: ET._Eleement) -> str: +def _get_value(el: ET.Element) -> str: for child in el: if get_tag(child) == 'elementReference': return child.text @@ -508,7 +513,7 @@ def _get_value(el: ET._Eleement) -> str: return STRING_LITERAL_TOKEN -def get_subflow_output_map(subflow: ET._Element): +def get_subflow_output_map(subflow: ET.Element): """returns a tuple (bool:, map: child name --> parent name) where the first return value is true if outputs are automatically assigned in which case they are flow_name.flow_var @@ -531,7 +536,7 @@ def get_subflow_output_map(subflow: ET._Element): return auto, mappings -def get_subflow_input_map(subflow: ET._Element) -> {str: str}: +def get_subflow_input_map(subflow: ET.Element) -> {str: str}: """Returns a map from caller variable to variable in called flow E.g. in this example:: @@ -564,3 +569,22 @@ def get_subflow_input_map(subflow: ET._Element) -> {str: str}: key = key_refs[0].text accum[key] = val return accum + + +class LineNumberingParser(ET.XMLParser): + def _start(self, *args, **kwargs): + # Here we assume the default XML parser which is expat + # and copy its element position attributes into output Elements + element = super(self.__class__, self)._start(*args, **kwargs) + element.sourceline = self.parser.CurrentLineNumber + element._start_column_number = self.parser.CurrentColumnNumber + element._start_byte_index = self.parser.CurrentByteIndex + return element + + def _end(self, *args, **kwargs): + element = super(self.__class__, self)._end(*args, **kwargs) + element._end_line_number = self.parser.CurrentLineNumber + element._end_column_number = self.parser.CurrentColumnNumber + element._end_byte_index = self.parser.CurrentByteIndex + return element + diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/queries/default_query.py b/packages/code-analyzer-flowtest-engine/FlowTest/queries/default_query.py index 953df0ef..e6931fa6 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/queries/default_query.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/queries/default_query.py @@ -9,7 +9,7 @@ import logging if TYPE_CHECKING: - from lxml import etree as ET + import xml.etree.ElementTree as ET from public import parse_utils from public.data_obj import DataInfluenceStatement, QueryResult @@ -95,7 +95,7 @@ def __init__(self) -> None: self.parser: FlowParser | None = None #: flow (xml) root - self.root: ET._Element + self.root: ET.Element #: path of flow self.flow_paths: [str] = None @@ -139,7 +139,7 @@ def handle_final(self, all_states: (State,)) -> list[QueryResult] | None: # dataflow graph of the entire fully executed program return None - def process_element(self, elem: ET._Element, state: State) -> list[QueryResult] | None: + def process_element(self, elem: ET.Element, state: State) -> list[QueryResult] | None: """Looks for CRUD influencers from sources (input fields or input variables) Searches the xml element looking for tainted variables that are selector or data influencers. @@ -196,7 +196,7 @@ def process_element(self, elem: ET._Element, state: State) -> list[QueryResult] assert x.paths is not None return res - def process_influencers(self, state: State, current_elem: ET._Element, + def process_influencers(self, state: State, current_elem: ET.Element, filter_influencers: [str], input_influencers: [str], elem_type: str, parser: FlowParser) -> [QueryResult]: @@ -254,7 +254,7 @@ def process_influencers(self, state: State, current_elem: ET._Element, comment=f"flow into {elem_type} via influence over {a_field}" f" in run mode {run_mode.name}", line_no=current_elem.sourceline, - source_text=parse_utils.ET.tounicode(current_elem), + source_text=parse_utils.ET.tostring(current_elem, encoding='unicode'), flow_path=flow_path ) to_return.append(QueryResult(query_id=query_id, diff --git a/packages/code-analyzer-flowtest-engine/FlowTest/setup.py b/packages/code-analyzer-flowtest-engine/FlowTest/setup.py index 39f5ff9d..b96f632e 100644 --- a/packages/code-analyzer-flowtest-engine/FlowTest/setup.py +++ b/packages/code-analyzer-flowtest-engine/FlowTest/setup.py @@ -15,8 +15,7 @@ 'flowtest = flowtest.__main__:main' ]}, python_requires='>=3.10.12', - install_requires=['lxml>=4.9.3', 'lxml-stubs>=0.4.0', 'immutables>=0.20'], package_data={'flowtest': ['data/FlowSecurity_preset.txt', 'data/flowtest_query_data.txt', 'data/footer.out', 'data/header.out']} - ) +) diff --git a/packages/code-analyzer-flowtest-engine/package.json b/packages/code-analyzer-flowtest-engine/package.json index 7aea0829..ff4fd1d3 100644 --- a/packages/code-analyzer-flowtest-engine/package.json +++ b/packages/code-analyzer-flowtest-engine/package.json @@ -36,7 +36,6 @@ "files": [ "dist", "LICENSE", - "pipx.pyz", "FlowTest", "package.json" ], diff --git a/packages/code-analyzer-flowtest-engine/src/python/FlowTestCommandWrapper.ts b/packages/code-analyzer-flowtest-engine/src/python/FlowTestCommandWrapper.ts index a4e83ee2..5be1c4ed 100644 --- a/packages/code-analyzer-flowtest-engine/src/python/FlowTestCommandWrapper.ts +++ b/packages/code-analyzer-flowtest-engine/src/python/FlowTestCommandWrapper.ts @@ -69,8 +69,6 @@ export type FlowNodeDescriptor = { source_text: string; } -const PATH_TO_PIPX_PYZ = path.join(__dirname, '..', '..', 'pipx.pyz'); -const PATH_TO_FLOWTEST_ROOT = path.join(__dirname, '..', '..', 'FlowTest'); const STATUS_DELIMITER = '**STATUS:'; export class RunTimeFlowTestCommandWrapper implements FlowTestCommandWrapper { @@ -82,11 +80,7 @@ export class RunTimeFlowTestCommandWrapper implements FlowTestCommandWrapper { public async getFlowTestRuleDescriptions(): Promise { const pythonArgs: string[] = [ - PATH_TO_PIPX_PYZ, - 'run', - '--spec', - PATH_TO_FLOWTEST_ROOT, - '--', + '-m', 'flowtest', '-p' ]; @@ -108,11 +102,7 @@ export class RunTimeFlowTestCommandWrapper implements FlowTestCommandWrapper { public async runFlowTestRules(dir: string, completionPercentageHandler: (percentage: number) => void): Promise { const tmpFile: string = this.createTmpFileName(); const pythonArgs: string[] = [ - PATH_TO_PIPX_PYZ, - 'run', - '--spec', - PATH_TO_FLOWTEST_ROOT, - '--', + '-m', 'flowtest', '-j', tmpFile, diff --git a/packages/code-analyzer-flowtest-engine/src/python/PythonCommandExecutor.ts b/packages/code-analyzer-flowtest-engine/src/python/PythonCommandExecutor.ts index ccf6bff1..f40be7f1 100644 --- a/packages/code-analyzer-flowtest-engine/src/python/PythonCommandExecutor.ts +++ b/packages/code-analyzer-flowtest-engine/src/python/PythonCommandExecutor.ts @@ -1,9 +1,12 @@ import {ChildProcessWithoutNullStreams, spawn} from 'node:child_process'; +import path from 'node:path'; import {getMessage} from "../messages"; type ProcessStdOutFn = (stdoutMsg: string) => void; const NO_OP = () => {}; +const PATH_TO_FLOWTEST_ROOT = path.join(__dirname, '..', '..', 'FlowTest'); + export class PythonCommandExecutor { private readonly pythonCommand: string; @@ -15,7 +18,12 @@ export class PythonCommandExecutor { return new Promise((res, rej) => { const stderrMessages: string[] = []; - const pythonProcess: ChildProcessWithoutNullStreams = spawn(this.pythonCommand, pythonCmdArgs); + const pythonProcess: ChildProcessWithoutNullStreams = spawn(this.pythonCommand, pythonCmdArgs, { + env: { + ...process.env, + PYTHONPATH: PATH_TO_FLOWTEST_ROOT + } + }); pythonProcess.stdout.on('data', (data: Buffer) => { const msg: string = data.toString().trim(); diff --git a/packages/code-analyzer-flowtest-engine/test/engine.test.ts b/packages/code-analyzer-flowtest-engine/test/engine.test.ts index e9b4365d..0f68b78f 100644 --- a/packages/code-analyzer-flowtest-engine/test/engine.test.ts +++ b/packages/code-analyzer-flowtest-engine/test/engine.test.ts @@ -55,7 +55,7 @@ const PATH_TO_WORKSPACE = path.resolve(__dirname, 'test-data', 'example-workspac const PATH_TO_SAMPLE_RESULTS = path.resolve(__dirname, 'test-data', 'sample-flowtest-results', 'engine.test.ts'); const PATH_TO_GOLDFILES = path.resolve(__dirname, 'test-data', 'goldfiles', 'engine.test.ts'); -xdescribe('Tests for the FlowTestEngine', () => { +describe('Tests for the FlowTestEngine', () => { it('getName() returns correct name', () => { const engine: FlowTestEngine = new FlowTestEngine(new StubCommandWrapper([])); expect(engine.getName()).toEqual('flowtest'); @@ -77,7 +77,7 @@ xdescribe('Tests for the FlowTestEngine', () => { const results: EngineRunResults = await engine.runRules(ruleDescriptors.map(r => r.name), {workspace}); // No need to do in-depth examination of the results, since other tests already do that. Just make sure we // got the right number of violations. - expect(results.violations).toHaveLength(14); + expect(results.violations).toHaveLength(11); }, 60000); }); diff --git a/packages/code-analyzer-flowtest-engine/test/plugin.test.ts b/packages/code-analyzer-flowtest-engine/test/plugin.test.ts index 4e846151..becc9ed0 100644 --- a/packages/code-analyzer-flowtest-engine/test/plugin.test.ts +++ b/packages/code-analyzer-flowtest-engine/test/plugin.test.ts @@ -9,7 +9,7 @@ import {FLOWTEST_ENGINE_CONFIG_DESCRIPTION} from "../src/config"; changeWorkingDirectoryToPackageRoot(); -xdescribe('Tests for the FlowTestEnginePlugin', () => { +describe('Tests for the FlowTestEnginePlugin', () => { it('When getAvailableNames is called, then it returns the FlowTestEngine name', () => { const plugin: EnginePluginV1 = new FlowTestEnginePlugin(); expect(plugin.getAvailableEngineNames()).toEqual([FlowTestEngine.NAME]); diff --git a/packages/code-analyzer-flowtest-engine/test/python/FlowTestCommandWrapper.test.ts b/packages/code-analyzer-flowtest-engine/test/python/FlowTestCommandWrapper.test.ts index 3a95a4f1..bf8af5b5 100644 --- a/packages/code-analyzer-flowtest-engine/test/python/FlowTestCommandWrapper.test.ts +++ b/packages/code-analyzer-flowtest-engine/test/python/FlowTestCommandWrapper.test.ts @@ -8,7 +8,7 @@ const PYTHON_COMMAND = 'python3'; const PATH_TO_GOLDFILES = path.join(__dirname, '..', 'test-data', 'goldfiles', 'FlowTestCommandWrapper.test.ts'); const PATH_TO_WORKSPACES = path.join(__dirname, '..', 'test-data', 'example-workspaces'); -xdescribe('FlowTestCommandWrapper implementations', () => { +describe('FlowTestCommandWrapper implementations', () => { describe('RunTimeFlowTestCommandWrapper', () => { describe('#getFlowTestRuleDescriptions()', () => { afterEach(() => { @@ -57,7 +57,7 @@ xdescribe('FlowTestCommandWrapper implementations', () => { }, 30000); it('Correctly reads and parses results', async () => { - const goldfileName: string = os.platform() === 'win32' ? 'results-windows.goldfile.json' : 'results-non-windows.goldfile.json'; + const goldfileName: string = 'results.goldfile.json'; const goldFileContents: string = (await fs.readFile(path.join(PATH_TO_GOLDFILES, goldfileName), {encoding: 'utf-8'})) .replaceAll('"__PATH_TO_SUBFLOW_TEST1__"', JSON.stringify(path.join(PATH_TO_WORKSPACES, 'contains-multiple-flows', 'subflow_test1.flow-meta.xml'))) .replaceAll('"__PATH_TO_INNER_SUBFLOW_EXAMPLE__"', JSON.stringify(path.join(PATH_TO_WORKSPACES, 'contains-multiple-flows', 'inner_subflow_example.flow-meta.xml'))) diff --git a/packages/code-analyzer-flowtest-engine/test/python/PythonCommandExecutor.test.ts b/packages/code-analyzer-flowtest-engine/test/python/PythonCommandExecutor.test.ts index ce0f5d86..381d47e4 100644 --- a/packages/code-analyzer-flowtest-engine/test/python/PythonCommandExecutor.test.ts +++ b/packages/code-analyzer-flowtest-engine/test/python/PythonCommandExecutor.test.ts @@ -6,7 +6,7 @@ import {PythonCommandExecutor} from '../../src/python/PythonCommandExecutor'; const PATH_TO_ERROR_THROWER = path.resolve(__dirname, '..', 'test-data', 'executable-scripts', 'error-thrower.py'); -xdescribe('PythonCommandExecutor', () => { +describe('PythonCommandExecutor', () => { const executor: PythonCommandExecutor = new PythonCommandExecutor('python3'); describe('#exec()', () => { it('When invoked script fails, rejects with informative message', async () => { diff --git a/packages/code-analyzer-flowtest-engine/test/python/PythonVersionIdentifier.test.ts b/packages/code-analyzer-flowtest-engine/test/python/PythonVersionIdentifier.test.ts index 45f88672..9aefe0c1 100644 --- a/packages/code-analyzer-flowtest-engine/test/python/PythonVersionIdentifier.test.ts +++ b/packages/code-analyzer-flowtest-engine/test/python/PythonVersionIdentifier.test.ts @@ -1,7 +1,7 @@ import {RuntimePythonVersionIdentifier} from "../../src/python/PythonVersionIdentifier"; import {SemVer} from "semver"; -xdescribe('PythonVersionIdentifier implementations', () => { +describe('PythonVersionIdentifier implementations', () => { describe('PythonVersionIdentifierImpl', () => { it('When command outputs parseable version, resolves to that version', async () => { diff --git a/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-non-windows.goldfile.json b/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-non-windows.goldfile.json deleted file mode 100644 index 696a1910..00000000 --- a/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-non-windows.goldfile.json +++ /dev/null @@ -1,506 +0,0 @@ -{ - "preset": "Penetration Testing", - "help_url": null, - "result_id": "a6e63ba2", - "service_version": "0.7.0-ALPHA", - "flowtest_version": "0.7.0-ALPHA", - "report_label": "flowscan of contains-multiple-flows", - "email": null, - "scan_start": "2024-10-07 12:53:12", - "scan_end": "2024-10-07 12:53:12", - "results": { - "FlowSecurity.SystemModeWithoutSharing.recordCreates.data": [ - { - "flow": [ - { - "influenced_var": "input_text", - "influencer_var": "input_text", - "element_name": "input_text", - "comment": "Initialization", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 91, - "source_text": "\n input_text\n String\n input_text\n InputField\n false\n " - }, - { - "influenced_var": "parent_input_var", - "influencer_var": "input_text", - "element_name": "assign_input_to_var", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 10, - "source_text": "\n parent_input_var\n Assign\n \n input_text\n \n " - }, - { - "influenced_var": "input_var1", - "influencer_var": "parent_input_var", - "element_name": "call_subflow", - "comment": "output via subflow assignment", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 109, - "source_text": "\n call_subflow\n \n 520\n 488\n \n display_subflow_result_in_parent\n \n inner_subflow_example\n \n input_var1\n \n parent_input_var\n \n \n true\n " - }, - { - "influenced_var": "AccountId", - "influencer_var": "input_var1", - "element_name": "create_case", - "comment": "flow into recordCreates via influence over AccountId in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 46, - "source_text": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 0, - "elem": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n ", - "elem_name": "create_case", - "field": "AccountId" - }, - { - "flow": [ - { - "influenced_var": "input_text", - "influencer_var": "input_text", - "element_name": "input_text", - "comment": "Initialization", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 91, - "source_text": "\n input_text\n String\n input_text\n InputField\n false\n " - }, - { - "influenced_var": "parent_input_var", - "influencer_var": "input_text", - "element_name": "assign_input_to_var", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 10, - "source_text": "\n parent_input_var\n Assign\n \n input_text\n \n " - }, - { - "influenced_var": "input_var1", - "influencer_var": "parent_input_var", - "element_name": "call_subflow", - "comment": "output via subflow assignment", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 109, - "source_text": "\n call_subflow\n \n 520\n 488\n \n display_subflow_result_in_parent\n \n inner_subflow_example\n \n input_var1\n \n parent_input_var\n \n \n true\n " - }, - { - "influenced_var": "SuppliedName", - "influencer_var": "input_var1", - "element_name": "create_case", - "comment": "flow into recordCreates via influence over SuppliedName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 46, - "source_text": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 1, - "elem": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n ", - "elem_name": "create_case", - "field": "SuppliedName" - }, - { - "flow": [ - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Company", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 369, - "source_text": "\n Company\n String\n Company\n InputField\n true\n " - }, - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over Company in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 3, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "Company" - }, - { - "flow": [ - { - "influenced_var": "First_Name", - "influencer_var": "First_Name", - "element_name": "First_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 355, - "source_text": "\n First_Name\n String\n First Name\n InputField\n true\n " - }, - { - "influenced_var": "FirstName", - "influencer_var": "First_Name", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over FirstName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 4, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "FirstName" - }, - { - "flow": [ - { - "influenced_var": "Last_Name", - "influencer_var": "Last_Name", - "element_name": "Last_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 362, - "source_text": "\n Last_Name\n String\n Last Name\n InputField\n true\n " - }, - { - "influenced_var": "LastName", - "influencer_var": "Last_Name", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over LastName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 5, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "LastName" - }, - { - "flow": [ - { - "influenced_var": "Phone", - "influencer_var": "Phone", - "element_name": "Phone", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 376, - "source_text": "\n Phone\n String\n Phone\n InputField\n true\n " - }, - { - "influenced_var": "Phone", - "influencer_var": "Phone", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over Phone in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 6, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "Phone" - }, - { - "flow": [ - { - "influenced_var": "title", - "influencer_var": "title", - "element_name": "title", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 509, - "source_text": "\n user supplied title for lead\n title\n String\n false\n true\n false\n \n No Title Provided\n \n " - }, - { - "influenced_var": "Title", - "influencer_var": "title", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over Title in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 7, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "Title" - }, - { - "flow": [ - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Company", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 369, - "source_text": "\n Company\n String\n Company\n InputField\n true\n " - }, - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over Company in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 9, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "Company" - }, - { - "flow": [ - { - "influenced_var": "First_Name", - "influencer_var": "First_Name", - "element_name": "First_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 355, - "source_text": "\n First_Name\n String\n First Name\n InputField\n true\n " - }, - { - "influenced_var": "FirstName", - "influencer_var": "First_Name", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over FirstName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 10, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "FirstName" - }, - { - "flow": [ - { - "influenced_var": "Last_Name", - "influencer_var": "Last_Name", - "element_name": "Last_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 362, - "source_text": "\n Last_Name\n String\n Last Name\n InputField\n true\n " - }, - { - "influenced_var": "random_last_name", - "influencer_var": "Last_Name", - "element_name": "random_last_name", - "comment": "Parsed from formulas", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 89, - "source_text": "\n randomizes last name via concatenation\n random_last_name\n String\n {!Last_Name}&SUBSTITUTE(SUBSTITUTE(TEXT(NOW()),\"-\", \"\"), \":\",\"\")\n " - }, - { - "influenced_var": "fixed_last_name", - "influencer_var": "random_last_name", - "element_name": "randomize_last_name_and_phone", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 44, - "source_text": "\n fixed_last_name\n Assign\n \n random_last_name\n \n " - }, - { - "influenced_var": "LastName", - "influencer_var": "fixed_last_name", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over LastName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 11, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "LastName" - }, - { - "flow": [ - { - "influenced_var": "street", - "influencer_var": "street", - "element_name": "street", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 502, - "source_text": "\n street\n String\n false\n true\n false\n " - }, - { - "influenced_var": "Street", - "influencer_var": "street", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over Street in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 12, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "Street" - }, - { - "flow": [ - { - "influenced_var": "title", - "influencer_var": "title", - "element_name": "title", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 509, - "source_text": "\n user supplied title for lead\n title\n String\n false\n true\n false\n \n No Title Provided\n \n " - }, - { - "influenced_var": "Title", - "influencer_var": "title", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over Title in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 13, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "Title" - } - ], - "FlowSecurity.SystemModeWithoutSharing.recordLookups.selector": [ - { - "flow": [ - { - "influenced_var": "enter_text_subflow", - "influencer_var": "enter_text_subflow", - "element_name": "enter_text_subflow", - "comment": "Initialization", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 82, - "source_text": "\n enter_text_subflow\n String\n enter_text_subflow\n InputField\n false\n " - }, - { - "influenced_var": "combine_vars", - "influencer_var": "enter_text_subflow", - "element_name": "combine_vars", - "comment": "Parsed from formulas", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 19, - "source_text": "\n combines id from created case together with user input\n combine_vars\n String\n {!create_case} + {!enter_text_subflow}\n " - }, - { - "influenced_var": "output_var1", - "influencer_var": "combine_vars", - "element_name": "assign_enter_to_output", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 10, - "source_text": "\n output_var1\n Assign\n \n combine_vars\n \n " - }, - { - "influenced_var": "call_subflow.output_var1", - "influencer_var": "output_var1", - "element_name": "call_subflow", - "comment": "output via subflow assignment", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 109, - "source_text": "\n call_subflow\n \n 520\n 488\n \n display_subflow_result_in_parent\n \n inner_subflow_example\n \n input_var1\n \n parent_input_var\n \n \n true\n " - }, - { - "influenced_var": "SuppliedName", - "influencer_var": "call_subflow.output_var1", - "element_name": "get_records", - "comment": "flow into recordLookups via influence over SuppliedName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 42, - "source_text": "\n get_records\n \n 171\n 254\n false\n and\n \n SuppliedName\n Contains\n \n call_subflow.output_var1\n \n \n true\n Case\n true\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordLookups selector", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordLookups element selector in run mode: SystemModeWithoutSharing", - "counter": 2, - "elem": "\n get_records\n \n 171\n 254\n false\n and\n \n SuppliedName\n Contains\n \n call_subflow.output_var1\n \n \n true\n Case\n true\n \n ", - "elem_name": "get_records", - "field": "SuppliedName" - } - ], - "FlowSecurity.SystemModeWithoutSharing.recordUpdates.data": [ - { - "flow": [ - { - "influenced_var": "description", - "influencer_var": "description", - "element_name": "description", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 442, - "source_text": "\n description variable\n description\n String\n false\n true\n false\n \n no description provided\n \n " - }, - { - "influenced_var": "Description", - "influencer_var": "description", - "element_name": "update_lead", - "comment": "flow into recordUpdates via influence over Description in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 266, - "source_text": "\n updates a lead\n update_lead\n \n 270\n 1214\n \n loop_over_recent_leads\n \n and\n \n Id\n EqualTo\n \n loop_over_recent_leads.Id\n \n \n \n Description\n \n description\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordUpdates data", - "severity": "Flow_High_Severity", - "description": "User controlled data flows into recordUpdates element data in run mode: SystemModeWithoutSharing", - "counter": 8, - "elem": "\n updates a lead\n update_lead\n \n 270\n 1214\n \n loop_over_recent_leads\n \n and\n \n Id\n EqualTo\n \n loop_over_recent_leads.Id\n \n \n \n Description\n \n description\n \n \n Lead\n \n ", - "elem_name": "update_lead", - "field": "Description" - } - ] - } -} \ No newline at end of file diff --git a/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-windows.goldfile.json b/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-windows.goldfile.json deleted file mode 100644 index f4069e5b..00000000 --- a/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results-windows.goldfile.json +++ /dev/null @@ -1,506 +0,0 @@ -{ - "preset": "Penetration Testing", - "help_url": null, - "result_id": "ba9fb34b", - "service_version": "0.7.0-ALPHA", - "flowtest_version": "0.7.0-ALPHA", - "report_label": "flowscan of contains-multiple-flows", - "email": null, - "scan_start": "2024-10-10 14:31:06", - "scan_end": "2024-10-10 14:31:06", - "results": { - "FlowSecurity.SystemModeWithoutSharing.recordCreates.data": [ - { - "flow": [ - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Company", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 369, - "source_text": "\n Company\n String\n Company\n InputField\n true\n " - }, - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over Company in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 0, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "Company" - }, - { - "flow": [ - { - "influenced_var": "First_Name", - "influencer_var": "First_Name", - "element_name": "First_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 355, - "source_text": "\n First_Name\n String\n First Name\n InputField\n true\n " - }, - { - "influenced_var": "FirstName", - "influencer_var": "First_Name", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over FirstName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 1, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "FirstName" - }, - { - "flow": [ - { - "influenced_var": "Last_Name", - "influencer_var": "Last_Name", - "element_name": "Last_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 362, - "source_text": "\n Last_Name\n String\n Last Name\n InputField\n true\n " - }, - { - "influenced_var": "LastName", - "influencer_var": "Last_Name", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over LastName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 2, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "LastName" - }, - { - "flow": [ - { - "influenced_var": "Phone", - "influencer_var": "Phone", - "element_name": "Phone", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 376, - "source_text": "\n Phone\n String\n Phone\n InputField\n true\n " - }, - { - "influenced_var": "Phone", - "influencer_var": "Phone", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over Phone in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 3, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "Phone" - }, - { - "flow": [ - { - "influenced_var": "title", - "influencer_var": "title", - "element_name": "title", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 509, - "source_text": "\n user supplied title for lead\n title\n String\n false\n true\n false\n \n No Title Provided\n \n " - }, - { - "influenced_var": "Title", - "influencer_var": "title", - "element_name": "Create_Lead", - "comment": "flow into recordCreates via influence over Title in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 192, - "source_text": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 4, - "elem": "\n Creates a lead with user entered data\n Create_Lead\n \n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Create_Lead", - "field": "Title" - }, - { - "flow": [ - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Company", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 369, - "source_text": "\n Company\n String\n Company\n InputField\n true\n " - }, - { - "influenced_var": "Company", - "influencer_var": "Company", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over Company in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 6, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "Company" - }, - { - "flow": [ - { - "influenced_var": "First_Name", - "influencer_var": "First_Name", - "element_name": "First_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 355, - "source_text": "\n First_Name\n String\n First Name\n InputField\n true\n " - }, - { - "influenced_var": "FirstName", - "influencer_var": "First_Name", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over FirstName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 7, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "FirstName" - }, - { - "flow": [ - { - "influenced_var": "Last_Name", - "influencer_var": "Last_Name", - "element_name": "Last_Name", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 362, - "source_text": "\n Last_Name\n String\n Last Name\n InputField\n true\n " - }, - { - "influenced_var": "random_last_name", - "influencer_var": "Last_Name", - "element_name": "random_last_name", - "comment": "Parsed from formulas", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 89, - "source_text": "\n randomizes last name via concatenation\n random_last_name\n String\n {!Last_Name}&SUBSTITUTE(SUBSTITUTE(TEXT(NOW()),\"-\", \"\"), \":\",\"\")\n " - }, - { - "influenced_var": "fixed_last_name", - "influencer_var": "random_last_name", - "element_name": "randomize_last_name_and_phone", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 44, - "source_text": "\n fixed_last_name\n Assign\n \n random_last_name\n \n " - }, - { - "influenced_var": "LastName", - "influencer_var": "fixed_last_name", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over LastName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 8, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "LastName" - }, - { - "flow": [ - { - "influenced_var": "street", - "influencer_var": "street", - "element_name": "street", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 502, - "source_text": "\n street\n String\n false\n true\n false\n " - }, - { - "influenced_var": "Street", - "influencer_var": "street", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over Street in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 9, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "Street" - }, - { - "flow": [ - { - "influenced_var": "title", - "influencer_var": "title", - "element_name": "title", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 509, - "source_text": "\n user supplied title for lead\n title\n String\n false\n true\n false\n \n No Title Provided\n \n " - }, - { - "influenced_var": "Title", - "influencer_var": "title", - "element_name": "Copy_1_of_Create_Lead", - "comment": "flow into recordCreates via influence over Title in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 138, - "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 10, - "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n \n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", - "elem_name": "Copy_1_of_Create_Lead", - "field": "Title" - }, - { - "flow": [ - { - "influenced_var": "input_text", - "influencer_var": "input_text", - "element_name": "input_text", - "comment": "Initialization", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 91, - "source_text": "\n input_text\n String\n input_text\n InputField\n false\n " - }, - { - "influenced_var": "parent_input_var", - "influencer_var": "input_text", - "element_name": "assign_input_to_var", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 10, - "source_text": "\n parent_input_var\n Assign\n \n input_text\n \n " - }, - { - "influenced_var": "input_var1", - "influencer_var": "parent_input_var", - "element_name": "call_subflow", - "comment": "output via subflow assignment", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 109, - "source_text": "\n call_subflow\n \n 520\n 488\n \n display_subflow_result_in_parent\n \n inner_subflow_example\n \n input_var1\n \n parent_input_var\n \n \n true\n " - }, - { - "influenced_var": "AccountId", - "influencer_var": "input_var1", - "element_name": "create_case", - "comment": "flow into recordCreates via influence over AccountId in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 46, - "source_text": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 11, - "elem": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n ", - "elem_name": "create_case", - "field": "AccountId" - }, - { - "flow": [ - { - "influenced_var": "input_text", - "influencer_var": "input_text", - "element_name": "input_text", - "comment": "Initialization", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 91, - "source_text": "\n input_text\n String\n input_text\n InputField\n false\n " - }, - { - "influenced_var": "parent_input_var", - "influencer_var": "input_text", - "element_name": "assign_input_to_var", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 10, - "source_text": "\n parent_input_var\n Assign\n \n input_text\n \n " - }, - { - "influenced_var": "input_var1", - "influencer_var": "parent_input_var", - "element_name": "call_subflow", - "comment": "output via subflow assignment", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 109, - "source_text": "\n call_subflow\n \n 520\n 488\n \n display_subflow_result_in_parent\n \n inner_subflow_example\n \n input_var1\n \n parent_input_var\n \n \n true\n " - }, - { - "influenced_var": "SuppliedName", - "influencer_var": "input_var1", - "element_name": "create_case", - "comment": "flow into recordCreates via influence over SuppliedName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 46, - "source_text": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordCreates data", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", - "counter": 12, - "elem": "\n create_case\n \n 618\n 564\n \n assign_enter_to_output\n \n \n AccountId\n \n input_var1\n \n \n \n SuppliedName\n \n input_var1\n \n \n Case\n true\n \n ", - "elem_name": "create_case", - "field": "SuppliedName" - } - ], - "FlowSecurity.SystemModeWithoutSharing.recordUpdates.data": [ - { - "flow": [ - { - "influenced_var": "description", - "influencer_var": "description", - "element_name": "description", - "comment": "Initialization", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 442, - "source_text": "\n description variable\n description\n String\n false\n true\n false\n \n no description provided\n \n " - }, - { - "influenced_var": "Description", - "influencer_var": "description", - "element_name": "update_lead", - "comment": "flow into recordUpdates via influence over Description in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_EXAMPLE__", - "line_no": 266, - "source_text": "\n updates a lead\n update_lead\n \n 270\n 1214\n \n loop_over_recent_leads\n \n and\n \n Id\n EqualTo\n \n loop_over_recent_leads.Id\n \n \n \n Description\n \n description\n \n \n Lead\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordUpdates data", - "severity": "Flow_High_Severity", - "description": "User controlled data flows into recordUpdates element data in run mode: SystemModeWithoutSharing", - "counter": 5, - "elem": "\n updates a lead\n update_lead\n \n 270\n 1214\n \n loop_over_recent_leads\n \n and\n \n Id\n EqualTo\n \n loop_over_recent_leads.Id\n \n \n \n Description\n \n description\n \n \n Lead\n \n ", - "elem_name": "update_lead", - "field": "Description" - } - ], - "FlowSecurity.SystemModeWithoutSharing.recordLookups.selector": [ - { - "flow": [ - { - "influenced_var": "enter_text_subflow", - "influencer_var": "enter_text_subflow", - "element_name": "enter_text_subflow", - "comment": "Initialization", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 82, - "source_text": "\n enter_text_subflow\n String\n enter_text_subflow\n InputField\n false\n " - }, - { - "influenced_var": "combine_vars", - "influencer_var": "enter_text_subflow", - "element_name": "combine_vars", - "comment": "Parsed from formulas", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 19, - "source_text": "\n combines id from created case together with user input\n combine_vars\n String\n {!create_case} + {!enter_text_subflow}\n " - }, - { - "influenced_var": "output_var1", - "influencer_var": "combine_vars", - "element_name": "assign_enter_to_output", - "comment": "Variable Assignment", - "flow_path": "__PATH_TO_INNER_SUBFLOW_EXAMPLE__", - "line_no": 10, - "source_text": "\n output_var1\n Assign\n \n combine_vars\n \n " - }, - { - "influenced_var": "call_subflow.output_var1", - "influencer_var": "output_var1", - "element_name": "call_subflow", - "comment": "output via subflow assignment", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 109, - "source_text": "\n call_subflow\n \n 520\n 488\n \n display_subflow_result_in_parent\n \n inner_subflow_example\n \n input_var1\n \n parent_input_var\n \n \n true\n " - }, - { - "influenced_var": "SuppliedName", - "influencer_var": "call_subflow.output_var1", - "element_name": "get_records", - "comment": "flow into recordLookups via influence over SuppliedName in run mode SystemModeWithoutSharing", - "flow_path": "__PATH_TO_SUBFLOW_TEST1__", - "line_no": 42, - "source_text": "\n get_records\n \n 171\n 254\n false\n and\n \n SuppliedName\n Contains\n \n call_subflow.output_var1\n \n \n true\n Case\n true\n \n " - } - ], - "query_name": "Flow: SystemModeWithoutSharing recordLookups selector", - "severity": "Flow_Moderate_Severity", - "description": "User controlled data flows into recordLookups element selector in run mode: SystemModeWithoutSharing", - "counter": 13, - "elem": "\n get_records\n \n 171\n 254\n false\n and\n \n SuppliedName\n Contains\n \n call_subflow.output_var1\n \n \n true\n Case\n true\n \n ", - "elem_name": "get_records", - "field": "SuppliedName" - } - ] - } -} \ No newline at end of file diff --git a/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results.goldfile.json b/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results.goldfile.json new file mode 100644 index 00000000..e05201ec --- /dev/null +++ b/packages/code-analyzer-flowtest-engine/test/test-data/goldfiles/FlowTestCommandWrapper.test.ts/results.goldfile.json @@ -0,0 +1,354 @@ +{ + "preset": "Penetration Testing", + "help_url": null, + "result_id": "a6e63ba2", + "service_version": "0.7.0-ALPHA", + "flowtest_version": "0.7.0-ALPHA", + "report_label": "flowscan of contains-multiple-flows", + "email": null, + "scan_start": "2024-10-07 12:53:12", + "scan_end": "2024-10-07 12:53:12", + "results": { + "FlowSecurity.SystemModeWithoutSharing.recordCreates.data": [ + { + "flow": [ + { + "influenced_var": "Company", + "influencer_var": "Company", + "element_name": "Company", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 369, + "source_text": "\n Company\n String\n Company\n InputField\n true\n " + }, + { + "influenced_var": "Company", + "influencer_var": "Company", + "element_name": "Create_Lead", + "comment": "flow into recordCreates via influence over Company in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 192, + "source_text": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 0, + "elem": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Create_Lead", + "field": "Company" + }, + { + "flow": [ + { + "influenced_var": "First_Name", + "influencer_var": "First_Name", + "element_name": "First_Name", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 355, + "source_text": "\n First_Name\n String\n First Name\n InputField\n true\n " + }, + { + "influenced_var": "FirstName", + "influencer_var": "First_Name", + "element_name": "Create_Lead", + "comment": "flow into recordCreates via influence over FirstName in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 192, + "source_text": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 1, + "elem": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Create_Lead", + "field": "FirstName" + }, + { + "flow": [ + { + "influenced_var": "Last_Name", + "influencer_var": "Last_Name", + "element_name": "Last_Name", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 362, + "source_text": "\n Last_Name\n String\n Last Name\n InputField\n true\n " + }, + { + "influenced_var": "LastName", + "influencer_var": "Last_Name", + "element_name": "Create_Lead", + "comment": "flow into recordCreates via influence over LastName in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 192, + "source_text": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 2, + "elem": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Create_Lead", + "field": "LastName" + }, + { + "flow": [ + { + "influenced_var": "Phone", + "influencer_var": "Phone", + "element_name": "Phone", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 376, + "source_text": "\n Phone\n String\n Phone\n InputField\n true\n " + }, + { + "influenced_var": "Phone", + "influencer_var": "Phone", + "element_name": "Create_Lead", + "comment": "flow into recordCreates via influence over Phone in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 192, + "source_text": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 3, + "elem": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Create_Lead", + "field": "Phone" + }, + { + "flow": [ + { + "influenced_var": "title", + "influencer_var": "title", + "element_name": "title", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 509, + "source_text": "\n user supplied title for lead\n title\n String\n false\n true\n false\n \n No Title Provided\n \n " + }, + { + "influenced_var": "Title", + "influencer_var": "title", + "element_name": "Create_Lead", + "comment": "flow into recordCreates via influence over Title in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 192, + "source_text": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 4, + "elem": "\n Creates a lead with user entered data\n Create_Lead\n Create Lead\n 182\n 734\n created_id\n \n id_return\n \n \n fault_handle\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n Last_Name\n \n \n \n Phone\n \n Phone\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Create_Lead", + "field": "Title" + }, + { + "flow": [ + { + "influenced_var": "Company", + "influencer_var": "Company", + "element_name": "Company", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 369, + "source_text": "\n Company\n String\n Company\n InputField\n true\n " + }, + { + "influenced_var": "Company", + "influencer_var": "Company", + "element_name": "Copy_1_of_Create_Lead", + "comment": "flow into recordCreates via influence over Company in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 138, + "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 6, + "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Copy_1_of_Create_Lead", + "field": "Company" + }, + { + "flow": [ + { + "influenced_var": "First_Name", + "influencer_var": "First_Name", + "element_name": "First_Name", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 355, + "source_text": "\n First_Name\n String\n First Name\n InputField\n true\n " + }, + { + "influenced_var": "FirstName", + "influencer_var": "First_Name", + "element_name": "Copy_1_of_Create_Lead", + "comment": "flow into recordCreates via influence over FirstName in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 138, + "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 7, + "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Copy_1_of_Create_Lead", + "field": "FirstName" + }, + { + "flow": [ + { + "influenced_var": "Last_Name", + "influencer_var": "Last_Name", + "element_name": "Last_Name", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 362, + "source_text": "\n Last_Name\n String\n Last Name\n InputField\n true\n " + }, + { + "influenced_var": "random_last_name", + "influencer_var": "Last_Name", + "element_name": "random_last_name", + "comment": "Parsed from formulas", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 89, + "source_text": "\n randomizes last name via concatenation\n random_last_name\n String\n {!Last_Name}&SUBSTITUTE(SUBSTITUTE(TEXT(NOW()),\"-\", \"\"), \":\",\"\")\n " + }, + { + "influenced_var": "fixed_last_name", + "influencer_var": "random_last_name", + "element_name": "randomize_last_name_and_phone", + "comment": "Variable Assignment", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 44, + "source_text": "\n fixed_last_name\n Assign\n \n random_last_name\n \n " + }, + { + "influenced_var": "LastName", + "influencer_var": "fixed_last_name", + "element_name": "Copy_1_of_Create_Lead", + "comment": "flow into recordCreates via influence over LastName in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 138, + "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 8, + "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Copy_1_of_Create_Lead", + "field": "LastName" + }, + { + "flow": [ + { + "influenced_var": "street", + "influencer_var": "street", + "element_name": "street", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 502, + "source_text": "\n street\n String\n false\n true\n false\n " + }, + { + "influenced_var": "Street", + "influencer_var": "street", + "element_name": "Copy_1_of_Create_Lead", + "comment": "flow into recordCreates via influence over Street in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 138, + "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 9, + "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Copy_1_of_Create_Lead", + "field": "Street" + }, + { + "flow": [ + { + "influenced_var": "title", + "influencer_var": "title", + "element_name": "title", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 509, + "source_text": "\n user supplied title for lead\n title\n String\n false\n true\n false\n \n No Title Provided\n \n " + }, + { + "influenced_var": "Title", + "influencer_var": "title", + "element_name": "Copy_1_of_Create_Lead", + "comment": "flow into recordCreates via influence over Title in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 138, + "source_text": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordCreates data", + "severity": "Flow_Moderate_Severity", + "description": "User controlled data flows into recordCreates element data in run mode: SystemModeWithoutSharing", + "counter": 10, + "elem": "\n Creates a lead with user entered data\n Copy_1_of_Create_Lead\n Copy 1 of Create Lead\n 578\n 1094\n created_id\n \n Copy_1_of_id_return\n \n \n Company\n \n Company\n \n \n \n Description\n \n description2\n \n \n \n FirstName\n \n First_Name\n \n \n \n LastName\n \n fixed_last_name\n \n \n \n Phone\n \n fixed_phone\n \n \n \n Street\n \n street\n \n \n \n Title\n \n title\n \n \n Lead\n \n ", + "elem_name": "Copy_1_of_Create_Lead", + "field": "Title" + } + ], + "FlowSecurity.SystemModeWithoutSharing.recordUpdates.data": [ + { + "flow": [ + { + "influenced_var": "description", + "influencer_var": "description", + "element_name": "description", + "comment": "Initialization", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 442, + "source_text": "\n description variable\n description\n String\n false\n true\n false\n \n no description provided\n \n " + }, + { + "influenced_var": "Description", + "influencer_var": "description", + "element_name": "update_lead", + "comment": "flow into recordUpdates via influence over Description in run mode SystemModeWithoutSharing", + "flow_path": "__PATH_TO_EXAMPLE__", + "line_no": 266, + "source_text": "\n updates a lead\n update_lead\n update lead\n 270\n 1214\n \n loop_over_recent_leads\n \n and\n \n Id\n EqualTo\n \n loop_over_recent_leads.Id\n \n \n \n Description\n \n description\n \n \n Lead\n \n " + } + ], + "query_name": "Flow: SystemModeWithoutSharing recordUpdates data", + "severity": "Flow_High_Severity", + "description": "User controlled data flows into recordUpdates element data in run mode: SystemModeWithoutSharing", + "counter": 5, + "elem": "\n updates a lead\n update_lead\n update lead\n 270\n 1214\n \n loop_over_recent_leads\n \n and\n \n Id\n EqualTo\n \n loop_over_recent_leads.Id\n \n \n \n Description\n \n description\n \n \n Lead\n \n ", + "elem_name": "update_lead", + "field": "Description" + } + ] + } +} \ No newline at end of file