Introduced a whitelist (as python list).
[toast/findwwwritable.git] / findwwwritable.py
index bcbb917f7c5a034d85e7a81f70a94356d3087981..b525c3c00d032fd247f01ae351d040df7940edcd 100755 (executable)
@@ -6,17 +6,18 @@ import stat
 from os.path import join
 
 
-def collect_writable_dirs(rootdir, uid, gids):
-    """Returns a list of directories below rootdir (including rootdir) that are writeable 
-    by the user with the given uid or gids or that are world writeable.
-    Normally, uid is the user id of the apache user (e.g. www-data) and gids is a list
+def collect_writable_dirs(rootdir, uids, gids):
+    """Returns a list of directories below rootdir (including rootdir) that are writable 
+    by the users with the given uids or gids or that are world writable.
+    Normally, uid(s) is the user id of the apache user (e.g. www-data) and gids is a list
     of group ids this user is member of.
     
     :param rootdir: string. directory where the search should start at
-    :param uid: integer user id
+    :param uids: list of integer user ids
     :param gids: list of integer group ids"""
     assert isinstance(rootdir, str)
-    assert isinstance(uid, int)
+    assert isinstance(uids, list)
+    for uid in uids: assert isinstance(uid, int)
     assert isinstance(gids, list)
     for gid in gids: assert isinstance(gid, int)
 
@@ -27,7 +28,7 @@ def collect_writable_dirs(rootdir, uid, gids):
             dp = join(root, d) # dp is the dir with path
             s = os.lstat(dp)
             if (s.st_mode & stat.S_IFLNK) == stat.S_IFLNK: continue # skip symlinks
-            if s.st_uid == uid and (s.st_mode & stat.S_IWUSR) > 0:
+            if s.st_uid in uids and (s.st_mode & stat.S_IWUSR) > 0:
                 writable_dirs.append(dp)
             elif s.st_gid in gids and (s.st_mode & stat.S_IWGRP) > 0:
                 writable_dirs.append(dp)
@@ -40,9 +41,9 @@ def collect_writable_dirs(rootdir, uid, gids):
 
 def summarize_dirs(writable_dirs):
     """Takes a list of directories and omits each subdirectory if its parent directory
-    is also included in the list. This list is modified "in place" (nothing is returned).
+    is also included in the list. The modified list is returned.
 
-    :param writeable_dirs: List of directories (strings)."""
+    :param writable_dirs: List of directories (strings)."""
     writable_dirs = sorted(writable_dirs)
 
     i = 0
@@ -52,20 +53,34 @@ def summarize_dirs(writable_dirs):
         else:
             i += 1
 
+    return writable_dirs
+
+
+def apply_whitelist(writable_dirs, whitelist):
+    """Removes all directories that are contained in the list whitelist from the list writable_dirs.
+    It returns the modified writable_dirs.
+
+    :param writable_dirs: List of directories
+    :param whitelist: List of directories that should be removed from writable_dirs.
+    :return: list of writable directories."""
+    return sorted(list(set(writable_dirs).difference(whitelist)))
+
 
 
 if __name__ == '__main__':
 
     # variables
-    uid = 33                   # user id of the user whos write permissions should be found
+    uids = [33]                # user ids of the user whos write permissions should be found
     gids = [33, 42, 121, 127]  # group ids of the user whos write permissions should be found
     rootdir = '/home'          # directory where the seach is started
+    whitelist = []             # list of directories that are known to be writable and that should not be reported.
 
-    # collect and summarize writeable directories
-    writable_dirs = collect_writable_dirs(rootdir, uid, gids)
-    summarize_dirs(writable_dirs)
+    # collect and summarize writable directories
+    writable_dirs = collect_writable_dirs(rootdir, uids, gids)
+    writable_dirs = summarize_dirs(writable_dirs)
+    writable_dirs = apply_whitelist(writable_dirs, whitelist)
 
-    # print writeable directories
+    # print writable directories
     for d in writable_dirs:
         print d