-
Notifications
You must be signed in to change notification settings - Fork 7
/
crimson_waccess.py
101 lines (86 loc) · 4.56 KB
/
crimson_waccess.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import os
import time
import subprocess
import sys
class Waccess:
'''Class to check write permissions on directories and files.'''
def log_issue(self, path, issue_type, output_path):
'''Log an issue when write permissions are unexpectedly granted.'''
with open(output_path, "a") as log_file:
log_data = (f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {issue_type}: {path}\n")
log_file.write(log_data)
print(log_data, end="")
def check_write_permission(self, path, output_path):
'''Check write permission for a directory or file.'''
if os.path.isdir(path):
self.check_directory_write_permission(path, output_path)
elif os.path.isfile(path):
self.check_file_write_permission(path, output_path)
else:
sys.stderr.write(f"Path {path} does not exist.")
def check_directory_write_permission(self, directory_path, output_path):
'''Check if a directory is writable by attempting to create a test file.'''
test_file_path = os.path.join(directory_path, f"{int(time.time())}_crimson_write_test.txt")
try:
with open(test_file_path, "w") as f:
f.write("This is a test.")
os.remove(test_file_path)
self.log_issue(directory_path, "Directory writable", output_path)
except Exception:
pass # Suppress errors for denied write operations
def check_file_write_permission(self, file_path, output_path):
'''Check if a file is writable by attempting to 'touch' it.'''
result = subprocess.run(['touch', file_path], capture_output=True)
if result.returncode == 0:
self.log_issue(file_path, "File writable", output_path)
def check_paths(self, paths_to_check, output_path, recursive):
'''Iterate through paths and check permissions.'''
checked_paths = set()
while paths_to_check:
path = paths_to_check.pop()
if path in checked_paths:
continue
checked_paths.add(path)
base_path = path.rstrip('*') # Remove trailing asterisks for checking
if path.endswith('*'):
self.check_write_permission(base_path, output_path)
# Always check recursively if '*' is present
if os.path.isdir(base_path):
self.add_child_paths(base_path, paths_to_check, checked_paths)
else:
self.check_write_permission(path, output_path)
if recursive and os.path.isdir(path):
self.add_child_paths(path, paths_to_check, checked_paths)
return checked_paths
def add_child_paths(self, base_path, paths_to_check, checked_paths):
'''Add child directories and files to the list to check.'''
for root, dirs, files in os.walk(base_path):
for dir_name in dirs:
child_path = os.path.join(root, dir_name)
if child_path not in checked_paths:
paths_to_check.add(child_path)
for file_name in files:
child_file_path = os.path.join(root, file_name)
if child_file_path not in checked_paths:
paths_to_check.add(child_file_path)
def check_sip_fp(self, file_path, output_path, recursive):
'''Main function to check each file and directory from the provided list.'''
try:
with open(file_path, "r") as paths_file:
paths_to_check = {line.strip() for line in paths_file if line.strip()}
checked_paths = self.check_paths(paths_to_check, output_path, recursive)
# Save all checked paths to a log file
with open("crimson_waccess_checked_paths.log", "w") as checked_paths_file:
for checked_path in checked_paths:
checked_paths_file.write(f"{checked_path}\n")
except FileNotFoundError:
sys.stderr.write(f"The file {file_path} does not exist.")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Check SIP-protected file and directory permissions.")
parser.add_argument("-f", "--file", required=True, help="Path to the file containing paths to check.")
parser.add_argument("-o", "--output", default="crimson_waccess.log", help="Path to the log file to write issues to.")
parser.add_argument("-r", "--recursive", action="store_true", help="Check directories recursively.")
args = parser.parse_args()
waccess = Waccess()
waccess.check_sip_fp(args.file, args.output, args.recursive)