From b3b8785a3418cf05e696825ce14433f7e8ececcd Mon Sep 17 00:00:00 2001 From: SchoobyDrew Date: Sat, 7 Jan 2023 22:16:03 -0500 Subject: [PATCH 1/8] adding baseline graph components --- inql/generators/poi.py | 66 ++++++++++++++++++++++++++++++++++++++++++ inql/introspection.py | 9 +++++- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 inql/generators/poi.py diff --git a/inql/generators/poi.py b/inql/generators/poi.py new file mode 100644 index 0000000..c316ef4 --- /dev/null +++ b/inql/generators/poi.py @@ -0,0 +1,66 @@ +from inql.utils import simplify_introspection +GRAPHQL_BUILTINS = ["Int", "String", "ID", "Boolean", "Float"] +class Graph: + def __init__(self,graph=None,data=None): + self.graph = graph or {} + self.data = data or {} + def get(self,node_name): + node = self.graph.get(node_name,False) + if node: + return node + else: + breakpoint() + self.data[node_name] + + def __str__(self): + return "" + def __repr__(self): + return "" +class Node: + def __init__(self,name,ntype='',inputs=None,children=None,parents=None,raw=None): + self.name = name + self.ntype = ntype or "Object" + self.inputs = inputs or ... + self.children = children or {} + self.parents = parents or {} + self.raw = raw or {} + def add_child(self,field_name,child): + self.children[field_name] = child + def add_parent(self,parent): + self.parents[parent.name] = parent + def __str__(self): + return f"{self.ntype}(name={self.name})" + def __repr__(self): + return f"{self.ntype}(name={self.name})" + def __hash__(self): + return hash((self.name,self.ntype)) + def __eq__(self,other): + return isinstance(other,Node) and (self.name,self.ntype) == (other.name,self.ntype) + def __ne__(self,other): + return not (self == other) + + +def generate(argument, fpath="cycles.txt", green_print=lambda s: print(s)): + """ + Generate Report on Sensitive Field Names + + :param argument: introspection query result + :param fpath: output result + :return: None + """ + green_print("Generating POI's") + # simplify schema + si = simplify_introspection(argument) + schema = [Node(name=v["type"],ntype=k) for k,v in si["schema"].items()] + # all nodes will have a name and their corresponding object + graph = Graph(data=si["type"]) + # get high level function names and return types + for query in schema: + for func,data in si["type"].pop(query.name).items(): + function_gqlo = Node(name=func,type="function",raw=data) + + + function_gqlo.add_child(nodes[data["type"]]) + query.add_child(function_gqlo) + + breakpoint() \ No newline at end of file diff --git a/inql/introspection.py b/inql/introspection.py index a05cf64..885f2fb 100755 --- a/inql/introspection.py +++ b/inql/introspection.py @@ -19,7 +19,7 @@ from .utils import string_join, mkdir_p, raw_request, urlopen -from .generators import html, schema, cycles, query, tsv +from .generators import html, schema, cycles, query, tsv, poi try: # Use UTF8 On Python2 and Jython @@ -172,6 +172,8 @@ def main(): help="Some graph are too complex to generate cycles in reasonable time, stream to stdout") parser.add_argument("--generate-tsv", dest="generate_tsv", action='store_true', default=False, help="Generate TSV representation of query templates. It may be useful to quickly search for vulnerable I/O.") + parser.add_argument("--generate-poi", dest="generate_poi", action='store_true', default=False, + help="Generate report on points of interest with detected sensitive fields") parser.add_argument("--insecure", dest="insecure_certificate", action="store_true", help="Accept any SSL/TLS certificate") parser.add_argument("-o", dest="output_directory", default=os.getcwd(), @@ -316,6 +318,11 @@ def init(args, print_help=None): tsv.generate(argument, fpath=os.path.join(host, "endpoint_%s.tsv"), green_print=lambda s: print(string_join(green, s, reset))) + + if args.generate_poi: + poi.generate(argument, + fpath=os.path.join(host, "poi-%s-%s.txt" % (today, timestamp)), + green_print=lambda s: print(string_join(green, s, reset))) else: # Likely missing a required arguments From 604b9fc39a53e266b9f9847d9c7332a24e90254d Mon Sep 17 00:00:00 2001 From: Schooby Date: Sun, 8 Jan 2023 14:40:02 -0600 Subject: [PATCH 2/8] added poi functionality,to matrix || need to add regexes list --- inql/generators/poi.py | 146 +++++++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 28 deletions(-) diff --git a/inql/generators/poi.py b/inql/generators/poi.py index c316ef4..2aa03a9 100644 --- a/inql/generators/poi.py +++ b/inql/generators/poi.py @@ -1,42 +1,133 @@ from inql.utils import simplify_introspection +import re + GRAPHQL_BUILTINS = ["Int", "String", "ID", "Boolean", "Float"] + + class Graph: - def __init__(self,graph=None,data=None): - self.graph = graph or {} + def __init__(self, nodes=None, data=None, schema=None, functions=None): + self.nodes = nodes or {} + self.schema = schema or {} + self.functions = functions or {} self.data = data or {} - def get(self,node_name): - node = self.graph.get(node_name,False) - if node: + + def node(self, node_name=None): + if node_name: + node = self.nodes.get(node_name, Node(node_name)) + self.nodes[node_name] = node return node else: - breakpoint() - self.data[node_name] - + return {k: v for k, v in self.nodes.items()} + + def function(self, function_name=None): + if function_name: + node = self.functions.get(function_name, Node(function_name)) + self.functions[function_name] = node + return node + else: + return {k: v for k, v in self.functions.items()} + + def generate(self): + for obj_name, obj_data in self.data.items(): + if obj_name in self.schema: + node = self.schema[obj_name] + for field_name, field_data in obj_data.items(): + field = self.function(field_data["type"]) + node.add_child(field_name, field) + field.add_parent(node) + else: + node = self.node(obj_name) + for field_name, field_data in obj_data.items(): + if field_name in ["__implements"]: + continue + elif field_data["type"].startswith(tuple(GRAPHQL_BUILTINS)): + field = field_data["type"] + else: + field = self.node(field_data["type"]) + field.add_parent(node) + node.add_child(field_name, field) + def __str__(self): - return "" + return f"Graph()" + def __repr__(self): - return "" + return f"Graph()" + + def gen_poi(self): + def isInteresting(s): + return "pass" in s + poi = {"functions":{},"nodes":[],"fields":{}} + # interesting function names + for node in self.schema.values(): + print(node.name) + for name in node.children: + if isInteresting(name): + arr = poi["functions"].get(node.name,[]) + arr.append(name) + poi["functions"][node.name] = arr + # interesting object names + for name in self.nodes: + if isInteresting(name): + poi["nodes"].append(name) + # interesting field names + for node in self.nodes.values(): + for field in node.children: + if isInteresting(field): + arr = poi["fields"].get(node.name,[]) + arr.append(field) + poi["fields"][node.name] = arr + return poi + + def gen_matrix(self): + keys = {name:idx for idx,name in enumerate(self.nodes)} + length = len(keys) + matrix = [[0]*length]*length + for node in self.nodes.values(): + row = keys[node.name] + for field in node.children.values(): + if isinstance(field,str): + # must be a scalar + continue + matrix[row][keys[field.name]] = 1 + return matrix + + class Node: - def __init__(self,name,ntype='',inputs=None,children=None,parents=None,raw=None): + def __init__( + self, name, ntype="", inputs=None, children=None, parents=None, raw=None + ): self.name = name self.ntype = ntype or "Object" self.inputs = inputs or ... self.children = children or {} self.parents = parents or {} self.raw = raw or {} - def add_child(self,field_name,child): + + def add_child(self, field_name, child): self.children[field_name] = child - def add_parent(self,parent): + + def add_parent(self, parent): self.parents[parent.name] = parent + def __str__(self): return f"{self.ntype}(name={self.name})" + def __repr__(self): return f"{self.ntype}(name={self.name})" + def __hash__(self): - return hash((self.name,self.ntype)) - def __eq__(self,other): - return isinstance(other,Node) and (self.name,self.ntype) == (other.name,self.ntype) - def __ne__(self,other): + return hash((self.name, self.ntype)) + + def __eq__(self, other): + return isinstance(other, Node) and ( + (self.name, self.ntype) + == ( + other.name, + self.ntype, + ) + ) + + def __ne__(self, other): return not (self == other) @@ -51,16 +142,15 @@ def generate(argument, fpath="cycles.txt", green_print=lambda s: print(s)): green_print("Generating POI's") # simplify schema si = simplify_introspection(argument) - schema = [Node(name=v["type"],ntype=k) for k,v in si["schema"].items()] # all nodes will have a name and their corresponding object - graph = Graph(data=si["type"]) - # get high level function names and return types - for query in schema: - for func,data in si["type"].pop(query.name).items(): - function_gqlo = Node(name=func,type="function",raw=data) + graph = Graph( + schema={ + v["type"]: Node(name=v["type"], ntype=k) for k, v in si["schema"].items() + }, + data=si["type"], + ) + graph.generate() - - function_gqlo.add_child(nodes[data["type"]]) - query.add_child(function_gqlo) - - breakpoint() \ No newline at end of file + report = graph.gen_poi() + + breakpoint() From 9c81911be5151101bf901ea1712b955f627a77a9 Mon Sep 17 00:00:00 2001 From: SchoobyDrew Date: Mon, 16 Jan 2023 09:45:40 -0500 Subject: [PATCH 3/8] added cmdline arguments and regex matcher --- inql/generators/poi.py | 45 +++++++++++++++++++++++++++++++----------- inql/introspection.py | 6 ++++++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/inql/generators/poi.py b/inql/generators/poi.py index 2aa03a9..7143508 100644 --- a/inql/generators/poi.py +++ b/inql/generators/poi.py @@ -1,7 +1,24 @@ from inql.utils import simplify_introspection import re +import json GRAPHQL_BUILTINS = ["Int", "String", "ID", "Boolean", "Float"] +DEFAULT_REGEX = "|".join([ + r"pass", + r"pwd", + r"user", + r"email", + r"key", + r"config", + r"secret", + r"cred", + r"env", + r"api", + r"hook", + r"token", + r"hash", + r"salt" +]) class Graph: @@ -53,29 +70,30 @@ def __str__(self): def __repr__(self): return f"Graph()" - def gen_poi(self): + def gen_poi(self,pattern=DEFAULT_REGEX): def isInteresting(s): - return "pass" in s - poi = {"functions":{},"nodes":[],"fields":{}} + matches = re.findall(pattern,s,re.IGNORECASE) + return bool(matches) + poi = {"Interesting Functions Names":{},"Interesting Node Names":[],"Interesting Field Names":{}} # interesting function names for node in self.schema.values(): print(node.name) for name in node.children: if isInteresting(name): - arr = poi["functions"].get(node.name,[]) + arr = poi["Interesting Functions Names"].get(node.name,[]) arr.append(name) - poi["functions"][node.name] = arr + poi["Interesting Functions Names"][node.name] = arr # interesting object names for name in self.nodes: if isInteresting(name): - poi["nodes"].append(name) + poi["Interesting Node Names"].append(name) # interesting field names for node in self.nodes.values(): for field in node.children: if isInteresting(field): - arr = poi["fields"].get(node.name,[]) + arr = poi["Interesting Field Names"].get(node.name,[]) arr.append(field) - poi["fields"][node.name] = arr + poi["Interesting Field Names"][node.name] = arr return poi def gen_matrix(self): @@ -131,7 +149,7 @@ def __ne__(self, other): return not (self == other) -def generate(argument, fpath="cycles.txt", green_print=lambda s: print(s)): +def generate(argument, fpath="poi.txt", regex=None, streaming=False, green_print=lambda s: print(s)): """ Generate Report on Sensitive Field Names @@ -151,6 +169,11 @@ def generate(argument, fpath="cycles.txt", green_print=lambda s: print(s)): ) graph.generate() - report = graph.gen_poi() - + report = graph.gen_poi(pattern=regex or DEFAULT_REGEX) + if streaming: + + print(json.dumps(report, indent=4, sort_keys=True)) + else: + with open(fpath, "w") as schema_file: + schema_file.write(json.dumps(report, indent=4, sort_keys=True)) breakpoint() diff --git a/inql/introspection.py b/inql/introspection.py index 885f2fb..29a23a1 100755 --- a/inql/introspection.py +++ b/inql/introspection.py @@ -174,6 +174,10 @@ def main(): help="Generate TSV representation of query templates. It may be useful to quickly search for vulnerable I/O.") parser.add_argument("--generate-poi", dest="generate_poi", action='store_true', default=False, help="Generate report on points of interest with detected sensitive fields") + parser.add_argument("--poi-regex", dest="poi_regex", default=None, type=str, + help="User defined Point of Interest regex") + parser.add_argument("--poi-streaming", dest="poi_stdout", action='store_true', default=False, + help="Stream points of interest to stdout") parser.add_argument("--insecure", dest="insecure_certificate", action="store_true", help="Accept any SSL/TLS certificate") parser.add_argument("-o", dest="output_directory", default=os.getcwd(), @@ -322,6 +326,8 @@ def init(args, print_help=None): if args.generate_poi: poi.generate(argument, fpath=os.path.join(host, "poi-%s-%s.txt" % (today, timestamp)), + regex=args.poi_regex, + streaming=args.poi_stdout, green_print=lambda s: print(string_join(green, s, reset))) else: From e42badf80d6056be50192ca11157d6431ea13781 Mon Sep 17 00:00:00 2001 From: SchoobyDrew Date: Mon, 16 Jan 2023 10:13:12 -0500 Subject: [PATCH 4/8] black formatting --- inql/generators/poi.py | 72 +++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/inql/generators/poi.py b/inql/generators/poi.py index 7143508..c878023 100644 --- a/inql/generators/poi.py +++ b/inql/generators/poi.py @@ -1,24 +1,27 @@ from inql.utils import simplify_introspection +from collections import defaultdict import re import json GRAPHQL_BUILTINS = ["Int", "String", "ID", "Boolean", "Float"] -DEFAULT_REGEX = "|".join([ - r"pass", - r"pwd", - r"user", - r"email", - r"key", - r"config", - r"secret", - r"cred", - r"env", - r"api", - r"hook", - r"token", - r"hash", - r"salt" -]) +DEFAULT_REGEX = "|".join( + [ + r"pass", + r"pwd", + r"user", + r"email", + r"key", + r"config", + r"secret", + r"cred", + r"env", + r"api", + r"hook", + r"token", + r"hash", + r"salt", + ] +) class Graph: @@ -70,17 +73,22 @@ def __str__(self): def __repr__(self): return f"Graph()" - def gen_poi(self,pattern=DEFAULT_REGEX): + def gen_poi(self, pattern=DEFAULT_REGEX): def isInteresting(s): - matches = re.findall(pattern,s,re.IGNORECASE) + matches = re.findall(pattern, s, re.IGNORECASE) return bool(matches) - poi = {"Interesting Functions Names":{},"Interesting Node Names":[],"Interesting Field Names":{}} + + poi = { + "Interesting Functions Names": {}, + "Interesting Node Names": [], + "Interesting Field Names": {}, + } # interesting function names for node in self.schema.values(): print(node.name) for name in node.children: if isInteresting(name): - arr = poi["Interesting Functions Names"].get(node.name,[]) + arr = poi["Interesting Functions Names"].get(node.name, []) arr.append(name) poi["Interesting Functions Names"][node.name] = arr # interesting object names @@ -91,24 +99,24 @@ def isInteresting(s): for node in self.nodes.values(): for field in node.children: if isInteresting(field): - arr = poi["Interesting Field Names"].get(node.name,[]) + arr = poi["Interesting Field Names"].get(node.name, []) arr.append(field) poi["Interesting Field Names"][node.name] = arr return poi - + def gen_matrix(self): - keys = {name:idx for idx,name in enumerate(self.nodes)} + keys = {name: idx for idx, name in enumerate(self.nodes)} length = len(keys) - matrix = [[0]*length]*length + matrix = [[0] * length] * length for node in self.nodes.values(): row = keys[node.name] for field in node.children.values(): - if isinstance(field,str): + if isinstance(field, str): # must be a scalar continue matrix[row][keys[field.name]] = 1 - return matrix - + return matrix, keys + class Node: def __init__( @@ -149,7 +157,13 @@ def __ne__(self, other): return not (self == other) -def generate(argument, fpath="poi.txt", regex=None, streaming=False, green_print=lambda s: print(s)): +def generate( + argument, + fpath="poi.txt", + regex=None, + streaming=False, + green_print=lambda s: print(s), +): """ Generate Report on Sensitive Field Names @@ -171,7 +185,7 @@ def generate(argument, fpath="poi.txt", regex=None, streaming=False, green_print report = graph.gen_poi(pattern=regex or DEFAULT_REGEX) if streaming: - + print(json.dumps(report, indent=4, sort_keys=True)) else: with open(fpath, "w") as schema_file: From 923011bbfbf9d8fbf5c73887dca9187546878d14 Mon Sep 17 00:00:00 2001 From: SchoobyDrew Date: Mon, 16 Jan 2023 11:58:54 -0500 Subject: [PATCH 5/8] added docs and matrix for fastcycles --- inql/generators/poi.py | 65 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/inql/generators/poi.py b/inql/generators/poi.py index c878023..58f9846 100644 --- a/inql/generators/poi.py +++ b/inql/generators/poi.py @@ -107,17 +107,73 @@ def isInteresting(s): def gen_matrix(self): keys = {name: idx for idx, name in enumerate(self.nodes)} length = len(keys) - matrix = [[0] * length] * length + matrix = Matrix(length) for node in self.nodes.values(): row = keys[node.name] - for field in node.children.values(): + for fname,field in node.children.items(): if isinstance(field, str): # must be a scalar continue - matrix[row][keys[field.name]] = 1 + # intermediate node edge + intermediate = (node.name,fname) + keys[intermediate] = length + matrix.addEdge(row,length) + matrix.addEdge(length, keys[field.name]) + length += 1 + matrix.V = length return matrix, keys +class Matrix: + # https://www.geeksforgeeks.org/tarjan-algorithm-find-strongly-connected-components/ + def __init__(self, num_vertices): + self.V = num_vertices + self.graph = defaultdict(list) + self.Time = 0 + self.cycles = [] + + def addEdge(self, u, v): + self.graph[u].append(v) + + def SCCUtil(self, u, low, disc, stackMember, st): + disc[u] = self.Time + low[u] = self.Time + self.Time += 1 + stackMember[u] = True + st.append(u) + for v in self.graph[u]: + if disc[v] == -1: + self.SCCUtil(v, low, disc, stackMember, st) + low[u] = min(low[u], low[v]) + elif stackMember[v] == True: + low[u] = min(low[u], disc[v]) + w = -1 + if low[u] == disc[u]: + tmp = [] + while w != u: + w = st.pop() + tmp.append(w) + stackMember[w] = False + self.cycles.append(tmp) + + def SCC(self): + disc = [-1] * (self.V) + low = [-1] * (self.V) + stackMember = [False] * (self.V) + st = [] + for i in range(self.V): + if disc[i] == -1: + self.SCCUtil(i, low, disc, stackMember, st) + + def format_cycles(self,keys): + transformed = [] + for cycle in self.cycles: + if len(cycle) == 1: + continue + transformed.append([keys[idx] for idx in cycle[::-1]]) + return transformed + + class Node: def __init__( self, name, ntype="", inputs=None, children=None, parents=None, raw=None @@ -169,6 +225,8 @@ def generate( :param argument: introspection query result :param fpath: output result + :param regex: custom regex to filter graph against + :param streaming: boolean trigger to output to stdout or write to file with fpath :return: None """ green_print("Generating POI's") @@ -190,4 +248,3 @@ def generate( else: with open(fpath, "w") as schema_file: schema_file.write(json.dumps(report, indent=4, sort_keys=True)) - breakpoint() From 5fa4caace184b5b8af49f88ffa44a70b81ad2fc4 Mon Sep 17 00:00:00 2001 From: SchoobyDrew Date: Mon, 16 Jan 2023 12:05:04 -0500 Subject: [PATCH 6/8] added fast cycle detection --- inql/generators/fastcycles.py | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 inql/generators/fastcycles.py diff --git a/inql/generators/fastcycles.py b/inql/generators/fastcycles.py new file mode 100644 index 0000000..6419db7 --- /dev/null +++ b/inql/generators/fastcycles.py @@ -0,0 +1,41 @@ +from inql.generators.poi import Graph, Node +from inql.utils import simplify_introspection +from collections import defaultdict +import re +import json + +def generate( + argument, + fpath="fastcycles.txt", + streaming=False, + green_print=lambda s: print(s), +): + """ + Generate Report on Sensitive Field Names + + :param argument: introspection query result + :param fpath: output result + :param streaming: boolean trigger to output to stdout or write to file with fpath + :return: None + """ + green_print("Generating Fast Cycles") + # simplify schema + si = simplify_introspection(argument) + # all nodes will have a name and their corresponding object + graph = Graph( + schema={ + v["type"]: Node(name=v["type"], ntype=k) for k, v in si["schema"].items() + }, + data=si["type"], + ) + graph.generate() + + matrix,keys = graph.gen_matrix() + matrix.SCC() + cycles = matrix.format_cycles(list(keys)) + + if streaming: + print(cycles) + else: + with open(fpath, "w") as schema_file: + schema_file.write(cycles) \ No newline at end of file From e2f8d1970501df1f7a79ece43841370616d826da Mon Sep 17 00:00:00 2001 From: SchoobyDrew Date: Sun, 22 Jan 2023 09:54:51 -0500 Subject: [PATCH 7/8] added cmdline arguments and updated graphql types interpretation --- inql/generators/fastcycles.py | 1 + inql/generators/poi.py | 7 ++++--- inql/introspection.py | 11 ++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/inql/generators/fastcycles.py b/inql/generators/fastcycles.py index 6419db7..c58efac 100644 --- a/inql/generators/fastcycles.py +++ b/inql/generators/fastcycles.py @@ -27,6 +27,7 @@ def generate( v["type"]: Node(name=v["type"], ntype=k) for k, v in si["schema"].items() }, data=si["type"], + types=list(si.get("enum",{}).keys()) + list(si.get("scalar",{}).keys()) ) graph.generate() diff --git a/inql/generators/poi.py b/inql/generators/poi.py index 58f9846..338c66e 100644 --- a/inql/generators/poi.py +++ b/inql/generators/poi.py @@ -3,7 +3,6 @@ import re import json -GRAPHQL_BUILTINS = ["Int", "String", "ID", "Boolean", "Float"] DEFAULT_REGEX = "|".join( [ r"pass", @@ -25,11 +24,12 @@ class Graph: - def __init__(self, nodes=None, data=None, schema=None, functions=None): + def __init__(self, nodes=None, data=None, schema=None, functions=None, types=None): self.nodes = nodes or {} self.schema = schema or {} self.functions = functions or {} self.data = data or {} + self.GRAPHQL_BUILTINS = ["Int", "String", "ID", "Boolean", "Float"] + (types or []) def node(self, node_name=None): if node_name: @@ -60,7 +60,7 @@ def generate(self): for field_name, field_data in obj_data.items(): if field_name in ["__implements"]: continue - elif field_data["type"].startswith(tuple(GRAPHQL_BUILTINS)): + elif field_data["type"].startswith(tuple(self.GRAPHQL_BUILTINS)): field = field_data["type"] else: field = self.node(field_data["type"]) @@ -238,6 +238,7 @@ def generate( v["type"]: Node(name=v["type"], ntype=k) for k, v in si["schema"].items() }, data=si["type"], + types=list(si.get("enum",{}).keys()) + list(si.get("scalar",{}).keys()) ) graph.generate() diff --git a/inql/introspection.py b/inql/introspection.py index 29a23a1..e484d73 100755 --- a/inql/introspection.py +++ b/inql/introspection.py @@ -19,7 +19,7 @@ from .utils import string_join, mkdir_p, raw_request, urlopen -from .generators import html, schema, cycles, query, tsv, poi +from .generators import html, schema, cycles, query, tsv, poi, fastcycles try: # Use UTF8 On Python2 and Jython @@ -178,6 +178,10 @@ def main(): help="User defined Point of Interest regex") parser.add_argument("--poi-streaming", dest="poi_stdout", action='store_true', default=False, help="Stream points of interest to stdout") + parser.add_argument("--generate-fast-cycles", dest="generate_fast_cycles", action='store_true', default=False, + help="Generate report on points of interest with detected sensitive fields") + parser.add_argument("--fast-cycles-streaming", dest="fast_cycles_stdout", action='store_true', default=False, + help="Stream points of interest to stdout") parser.add_argument("--insecure", dest="insecure_certificate", action="store_true", help="Accept any SSL/TLS certificate") parser.add_argument("-o", dest="output_directory", default=os.getcwd(), @@ -329,6 +333,11 @@ def init(args, print_help=None): regex=args.poi_regex, streaming=args.poi_stdout, green_print=lambda s: print(string_join(green, s, reset))) + if args.generate_fast_cycles: + fastcycles.generate(argument, + fpath=os.path.join(host, "fastcycles-%s-%s.txt" % (today, timestamp)), + streaming=args.fast_cycles_stdout, + green_print=lambda s: print(string_join(green, s, reset))) else: # Likely missing a required arguments From 72099a9901238d63840b0686b8e02d9ab9c12bee Mon Sep 17 00:00:00 2001 From: SchoobyDrew Date: Sun, 22 Jan 2023 12:43:12 -0500 Subject: [PATCH 8/8] format cycle output --- inql/generators/fastcycles.py | 2 ++ inql/generators/poi.py | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/inql/generators/fastcycles.py b/inql/generators/fastcycles.py index c58efac..5d2b1f7 100644 --- a/inql/generators/fastcycles.py +++ b/inql/generators/fastcycles.py @@ -34,6 +34,8 @@ def generate( matrix,keys = graph.gen_matrix() matrix.SCC() cycles = matrix.format_cycles(list(keys)) + cycles_view = ',\n\t'.join(['->'.join(cycle) for cycle in cycles]) + cycles = f"Cycles(\n\t{cycles_view}\n)" if streaming: print(cycles) diff --git a/inql/generators/poi.py b/inql/generators/poi.py index 338c66e..3d09d14 100644 --- a/inql/generators/poi.py +++ b/inql/generators/poi.py @@ -170,7 +170,12 @@ def format_cycles(self,keys): for cycle in self.cycles: if len(cycle) == 1: continue - transformed.append([keys[idx] for idx in cycle[::-1]]) + tmp = [] + for idx in cycle[::-1]: + n = keys[idx] + rep = n if isinstance(n,str) else f"{n[0]}-[{n[1]}]" + tmp.append(rep) + transformed.append(tmp) return transformed