From 417933a1e9fc1de832c6541792ce8068356b93fd Mon Sep 17 00:00:00 2001 From: micahjmartin Date: Sat, 10 Feb 2024 22:11:51 -0500 Subject: [PATCH 1/6] Add headers and recursive depth to file_list --- tavern/tomes/file_list/main.eldritch | 69 ++++++++++++++++++++++------ tavern/tomes/file_list/metadata.yml | 4 ++ 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/tavern/tomes/file_list/main.eldritch b/tavern/tomes/file_list/main.eldritch index 84863be9f..1168dc74b 100644 --- a/tavern/tomes/file_list/main.eldritch +++ b/tavern/tomes/file_list/main.eldritch @@ -1,18 +1,61 @@ -def file_list(path): +usernfo = sys.get_user() +PERM_READ = 4 + +name = { + "Directory": "Dir", + "File": "File", + "Link": "Link" +} + +def can_read(f): + """Return true if the user can read this dir/file + """ + f_user = int(f["permissions"][-3]) # User byte + f_group = int(f["permissions"][-2]) # Group byte + f_world = int(f["permissions"][-1]) # World byte applies to everyone + + # Are we root? + root = usernfo["euid"]["uid"] == 0 + + # If the user isnt root and the user doesnt own the file, clear the user byte + if not root and f["owner"] not in (usernfo["euid"]["name"], usernfo["uid"]["name"]): + f_user = 0 + + # TODO: https://github.com/spellshift/realm/issues/570 + # Will NOT match any group other than primary until #570 is fixed + + # If the user isnt root and the group doesnt own the file, clear the group byte + if not root and f["group"] not in (str(usernfo["egid"]), str(usernfo["gid"])): + f_group = 0 + + if PERM_READ in (f_world & PERM_READ, f_group & PERM_READ, f_user & PERM_READ): + return True + + return False + +def file_list(path, recurse=1, ignore_dirs=[]): + """List all files in the given path up to """ + # TODO: https://github.com/spellshift/realm/issues/569 + # No way to get perms of a path, so this will crash if the initial path is unreadable + # if not can_read(path): return + if file.is_dir(path): - files = file.list(path) - for f in files: - type_str = "" - if f['type'] == "Directory": - type_str = "Dir" - if f['type'] == "Link": - type_str = "Link" - if f['type'] == "File": - type_str = "File" - print(f['permissions']+"\t"+f['owner']+"\t"+f['group']+"\t"+str(f['size'])+"\t"+f['modified']+"\t"+type_str+"\t"+f['file_name']+"\n") + for f in file.list(path): + full = path.rstrip("/") + "/" + f["file_name"] + if f["type"] == "Directory": + full += "/" + print(f['permissions']+"\t"+f['owner']+"\t"+f['group']+"\t"+str(f['size'])+"\t"+f['modified']+"\t"+name.get(f['type'], f['type'])+"\t"+full+"\n") + if f["type"] == "Directory" and recurse-1 > 0: + # Skip files we cant read + if not can_read(f): continue + file_list(full, recurse=recurse-1, ignore_dirs=ignore_dirs) else: - print("Error: Invalid Path ("+path+")\n") + print("Error: Invalid Path ("+path+"), is a regular file\n") + +#input_params = {"path": "/tmp","depth": "0"} # Uncomment to test +# TODO: Is there any way to validate 'depth' is an int? -file_list(input_params['path']) +print("PERM\tUSER\tGROUP\tSIZE\tTIME\tTYPE\tPATH\n") +file_list(input_params['path'], recurse=int(input_params.get('depth', "1"))) print("\n") print("\n") diff --git a/tavern/tomes/file_list/metadata.yml b/tavern/tomes/file_list/metadata.yml index 65640f03d..bab59a049 100644 --- a/tavern/tomes/file_list/metadata.yml +++ b/tavern/tomes/file_list/metadata.yml @@ -8,3 +8,7 @@ paramdefs: type: string label: File path placeholder: "/etc/" +- name: depth + type: int + label: Recurse Depth + placeholder: "1" \ No newline at end of file From e0fc8f8861e0e9ec51939b448918b2a009f06b4d Mon Sep 17 00:00:00 2001 From: micahjmartin Date: Tue, 13 Feb 2024 15:45:15 -0500 Subject: [PATCH 2/6] remove recursion, add glob support --- tavern/tomes/file_list/main.eldritch | 109 ++++++++++++++++++++------- tavern/tomes/file_list/metadata.yml | 4 - 2 files changed, 80 insertions(+), 33 deletions(-) diff --git a/tavern/tomes/file_list/main.eldritch b/tavern/tomes/file_list/main.eldritch index 1168dc74b..6436e0b95 100644 --- a/tavern/tomes/file_list/main.eldritch +++ b/tavern/tomes/file_list/main.eldritch @@ -1,5 +1,4 @@ usernfo = sys.get_user() -PERM_READ = 4 name = { "Directory": "Dir", @@ -7,13 +6,21 @@ name = { "Link": "Link" } +SEP = "/" +if sys.get_os().get("platform", "") == "PLATFORM_WINDOWS": + SEP = "\\" + def can_read(f): """Return true if the user can read this dir/file """ + PERM_READ = 4 f_user = int(f["permissions"][-3]) # User byte f_group = int(f["permissions"][-2]) # Group byte - f_world = int(f["permissions"][-1]) # World byte applies to everyone - + + # Check world byte first so it hopefully is fast + if int(f["permissions"][-1]) & PERM_READ: + return True + # Are we root? root = usernfo["euid"]["uid"] == 0 @@ -28,34 +35,78 @@ def can_read(f): if not root and f["group"] not in (str(usernfo["egid"]), str(usernfo["gid"])): f_group = 0 - if PERM_READ in (f_world & PERM_READ, f_group & PERM_READ, f_user & PERM_READ): + if (f_group & PERM_READ) | (f_user & PERM_READ): return True - return False -def file_list(path, recurse=1, ignore_dirs=[]): - """List all files in the given path up to """ - # TODO: https://github.com/spellshift/realm/issues/569 - # No way to get perms of a path, so this will crash if the initial path is unreadable - # if not can_read(path): return - - if file.is_dir(path): - for f in file.list(path): - full = path.rstrip("/") + "/" + f["file_name"] - if f["type"] == "Directory": - full += "/" - print(f['permissions']+"\t"+f['owner']+"\t"+f['group']+"\t"+str(f['size'])+"\t"+f['modified']+"\t"+name.get(f['type'], f['type'])+"\t"+full+"\n") - if f["type"] == "Directory" and recurse-1 > 0: - # Skip files we cant read - if not can_read(f): continue - file_list(full, recurse=recurse-1, ignore_dirs=ignore_dirs) - else: - print("Error: Invalid Path ("+path+"), is a regular file\n") - -#input_params = {"path": "/tmp","depth": "0"} # Uncomment to test -# TODO: Is there any way to validate 'depth' is an int? - -print("PERM\tUSER\tGROUP\tSIZE\tTIME\tTYPE\tPATH\n") -file_list(input_params['path'], recurse=int(input_params.get('depth', "1"))) +def glob(s, pattern): + #if pattern == s or pattern == "*": + # return True + p = pattern.split("*") + # Check the first chunk + chunk = p.pop(0) + if not s.startswith(chunk): + return False + s = s[len(chunk):] + # Check the last chunk + if p: + chunk = p.pop() + if chunk: + if not s.endswith(chunk): + return False + s = s[:-len(chunk)] + + # Check all the middle chunks + for part in p: + if part not in s: + return False + s = s[s.index(part)+1:] + return True + +def print_file(path, f): + full = path.rstrip("/") + "/" + f["file_name"] + if f["type"] == "Directory": + full += "/" + print(f['permissions']+"\t"+f['owner']+"\t"+f['group']+"\t"+str(f['size'])+"\t"+f['modified']+"\t"+name.get(f['type'], f['type'])+"\t"+full+"\n") + +def file_list(path): + """List all files in the given path""" + parts = path.strip(SEP).split(SEP) + base = [] # The base of the path that doesnt have a glob + pattern = "" + for p in parts: + if '*' in p: + pattern = p + break + base.append(p) + + base = SEP.join(base) + if SEP == "/": + base = "/" + base + + # Safety checking + if not file.exists(base): + print("Error Path '"+path+"' does not exist\n") + return + elif file.is_file(base): + print("Error Path '"+path+"' is a file\n") + return + + # TODO: No way to check if we can read base + for f in file.list(base): + if pattern == "*": + # List each file/dir in this folder + if f["type"] == "Directory" and can_read(f): + d = base+SEP+f["file_name"] + for f in file.list(d): + print_file(d, f) + elif not pattern: + # Just list each file/folder + print_file(base, f) + elif glob(f["file_name"], pattern): + # Only print files/folders that match the glob + print_file(base, f) + +file_list(input_params['path']) print("\n") print("\n") diff --git a/tavern/tomes/file_list/metadata.yml b/tavern/tomes/file_list/metadata.yml index bab59a049..65640f03d 100644 --- a/tavern/tomes/file_list/metadata.yml +++ b/tavern/tomes/file_list/metadata.yml @@ -8,7 +8,3 @@ paramdefs: type: string label: File path placeholder: "/etc/" -- name: depth - type: int - label: Recurse Depth - placeholder: "1" \ No newline at end of file From abcbd77635ba3e88b00eeec799bf182e5c1559eb Mon Sep 17 00:00:00 2001 From: micahjmartin Date: Tue, 13 Feb 2024 15:53:32 -0500 Subject: [PATCH 3/6] update metadata --- tavern/tomes/file_list/metadata.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tavern/tomes/file_list/metadata.yml b/tavern/tomes/file_list/metadata.yml index 65640f03d..b18e689b9 100644 --- a/tavern/tomes/file_list/metadata.yml +++ b/tavern/tomes/file_list/metadata.yml @@ -1,5 +1,5 @@ name: List files -description: List the files and directories found at the path +description: List the files and directories found at the path. Supports basic glob functionality. Does not glob more than one level author: hulto support_model: FIRST_PARTY tactic: RECON From b7cee15dec78bb54bb357b40a4aa36d26d033bf5 Mon Sep 17 00:00:00 2001 From: micahjmartin Date: Tue, 13 Feb 2024 21:52:47 -0500 Subject: [PATCH 4/6] remove dead code --- tavern/tomes/file_list/main.eldritch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tavern/tomes/file_list/main.eldritch b/tavern/tomes/file_list/main.eldritch index 6436e0b95..a0cd9ffec 100644 --- a/tavern/tomes/file_list/main.eldritch +++ b/tavern/tomes/file_list/main.eldritch @@ -40,8 +40,7 @@ def can_read(f): return False def glob(s, pattern): - #if pattern == s or pattern == "*": - # return True + """Basic glob functionality""" p = pattern.split("*") # Check the first chunk chunk = p.pop(0) @@ -64,6 +63,7 @@ def glob(s, pattern): return True def print_file(path, f): + """Pretty Print a file""" full = path.rstrip("/") + "/" + f["file_name"] if f["type"] == "Directory": full += "/" From ebc0217325bc8486a8b28deee0c3276e71c639e8 Mon Sep 17 00:00:00 2001 From: micahjmartin Date: Tue, 13 Feb 2024 21:54:34 -0500 Subject: [PATCH 5/6] Bypass permissions check on windows --- tavern/tomes/file_list/main.eldritch | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tavern/tomes/file_list/main.eldritch b/tavern/tomes/file_list/main.eldritch index a0cd9ffec..07ebac0b4 100644 --- a/tavern/tomes/file_list/main.eldritch +++ b/tavern/tomes/file_list/main.eldritch @@ -13,6 +13,10 @@ if sys.get_os().get("platform", "") == "PLATFORM_WINDOWS": def can_read(f): """Return true if the user can read this dir/file """ + + # Until we get permissions on windows, just go ahead and try to read + if SEP == "\\": + return True PERM_READ = 4 f_user = int(f["permissions"][-3]) # User byte f_group = int(f["permissions"][-2]) # Group byte From f7a5bae12bad48f2256d7f0f18c0307bbad8a459 Mon Sep 17 00:00:00 2001 From: micahjmartin Date: Wed, 14 Feb 2024 19:50:32 -0500 Subject: [PATCH 6/6] Update documentation --- tavern/tomes/file_list/metadata.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tavern/tomes/file_list/metadata.yml b/tavern/tomes/file_list/metadata.yml index b18e689b9..eb56d44de 100644 --- a/tavern/tomes/file_list/metadata.yml +++ b/tavern/tomes/file_list/metadata.yml @@ -7,4 +7,4 @@ paramdefs: - name: path type: string label: File path - placeholder: "/etc/" + placeholder: "/etc/open*"