audit-labs/audit-tools

A collection of scripts, queries, and other goodies you can use in an audit.

clone: git clone https://gitbay.org/audit-labs/audit-tools.git

bee22b97b652cd04fa470d7f31c3b917e44f3ab9

signed_unknown_key

author: Christian Cleberg <hello@cleberg.net> · 2025-04-05T18:43:01Z
committer: <noreply@github.com>

migrate from pylint to ruff (#1)

* migrate from pylint to ruff

* Commit from GitHub Actions (Pylint)

* rename pylint.yml to ruff.yml

* only run ruff-action when python files change

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
 .github/workflows/pylint.yml                     | 23 -------
 .github/workflows/ruff.yml                       | 37 +++++++++++
 applications/github/github_admins.py             | 31 ++++++----
 applications/github/github_audit_log.py          | 26 +++++---
 applications/github/github_branch_protections.py | 28 +++++----
 applications/github/github_commits.py            | 52 +++++++++-------
 applications/gitlab/gitlab_admins.py             |  6 +-
 databases/passwords/sql/test.py                  | 53 ++++++++--------
 project_management/dash/app.py                   | 79 ++++++++++++------------
 9 files changed, 193 insertions(+), 142 deletions(-)

diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
deleted file mode 100644
index 2eaeecd..0000000
--- a/.github/workflows/pylint.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-name: Pylint
-
-on: [push]
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        python-version: ["3.8", "3.9", "3.10"]
-    steps:
-    - uses: actions/checkout@v4
-    - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v5
-      with:
-        python-version: ${{ matrix.python-version }}
-    - name: Install dependencies
-      run: |
-        python -m pip install --upgrade pip
-        pip install pylint pandas dash plotly.express
-    - name: Analysing the code with pylint
-      run: |
-        pylint -d R0801 $(git ls-files '*.py')
diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml
new file mode 100644
index 0000000..e7212d3
--- /dev/null
+++ b/.github/workflows/ruff.yml
@@ -0,0 +1,37 @@
+name: Ruff
+
+on:
+  push:
+    paths:
+      - '**.py'
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ["3.x"]
+    steps:
+    - uses: actions/checkout@v4
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v5
+      with:
+        python-version: ${{ matrix.python-version }}
+        ref: ${{ github.event.pull_request.head.ref }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install pandas dash plotly.express
+    - name: Install Ruff
+      uses: astral-sh/ruff-action@v3.2.2
+    - name: Ruff Actions
+      run: |
+        ruff check --fix
+        ruff format
+    - name: Add and Commit
+      uses: EndBug/add-and-commit@v9
+      env:
+        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      with:
+        default_author: github_actions
+        pathspec_error_handling: ignore
\ No newline at end of file
diff --git a/applications/github/github_admins.py b/applications/github/github_admins.py
index d737887..b6db534 100644
--- a/applications/github/github_admins.py
+++ b/applications/github/github_admins.py
@@ -5,54 +5,59 @@ and list each user's permission per repo.
 
 import requests
 
-GITHUB_TOKEN = 'your_personal_access_token'
-ORGANIZATION = 'your_organization'
+GITHUB_TOKEN = "your_personal_access_token"
+ORGANIZATION = "your_organization"
 TIMEOUT = 30
 
 # Headers for authentication
 headers = {
-    'Authorization': f'token {GITHUB_TOKEN}',
-    'Accept': 'application/vnd.github.v3+json'
+    "Authorization": f"token {GITHUB_TOKEN}",
+    "Accept": "application/vnd.github.v3+json",
 }
 
+
 def get_org_members(org):
     """
     Get members of an organization
     """
-    url = f'https://api.github.com/orgs/{org}/members'
+    url = f"https://api.github.com/orgs/{org}/members"
     response = requests.get(url, headers=headers, timeout=TIMEOUT)
     response.raise_for_status()
     return response.json()
 
+
 def get_org_repos(org):
     """
     Get repositories of an organization
     """
-    url = f'https://api.github.com/orgs/{org}/repos'
+    url = f"https://api.github.com/orgs/{org}/repos"
     response = requests.get(url, headers=headers, timeout=TIMEOUT)
     response.raise_for_status()
     return response.json()
 
+
 def get_repo_collaborators(org, repo):
     """
     Get collaborators of a repository with their permissions
     """
-    url = f'https://api.github.com/repos/{org}/{repo}/collaborators'
+    url = f"https://api.github.com/repos/{org}/{repo}/collaborators"
     response = requests.get(url, headers=headers, timeout=TIMEOUT)
     response.raise_for_status()
     return response.json()
 
+
 def get_user_permissions(org, repo, user):
     """
     Get a user's permissions for a repository
     """
-    url = f'https://api.github.com/repos/{org}/{repo}/collaborators/{user}/permission'
+    url = f"https://api.github.com/repos/{org}/{repo}/collaborators/{user}/permission"
     response = requests.get(url, headers=headers, timeout=TIMEOUT)
     response.raise_for_status()
     return response.json()
 
+
 # Main script
-if __name__ == '__main__':
+if __name__ == "__main__":
     # Get organization members
     members = get_org_members(ORGANIZATION)
     print(f"Members of the organization '{ORGANIZATION}':")
@@ -67,10 +72,12 @@ if __name__ == '__main__':
 
     # Get collaborators for each repository and their permissions
     for repository in repositories:
-        repository_name = repository['name']
+        repository_name = repository["name"]
         collaborators = get_repo_collaborators(ORGANIZATION, repository_name)
         print(f"\nCollaborators for the repository '{repository_name}':")
         for collaborator in collaborators:
-            user_login = collaborator['login']
-            permissions = get_user_permissions(ORGANIZATION, repository_name, user_login)
+            user_login = collaborator["login"]
+            permissions = get_user_permissions(
+                ORGANIZATION, repository_name, user_login
+            )
             print(f"- {user_login}: {permissions['permission']}")
diff --git a/applications/github/github_audit_log.py b/applications/github/github_audit_log.py
index 0937764..dd00535 100644
--- a/applications/github/github_audit_log.py
+++ b/applications/github/github_audit_log.py
@@ -6,16 +6,17 @@ NOTE: REQUIRES A GITHUB ENTERPRISE SUBSCRIPTION TO ACCESS THE API.
 
 import requests
 
-GITHUB_TOKEN = 'your_personal_access_token'
-ORGANIZATION = 'your_organization'
+GITHUB_TOKEN = "your_personal_access_token"
+ORGANIZATION = "your_organization"
 TIMEOUT = 30
 
 # Headers for authentication
 headers = {
-    'Authorization': f'token {GITHUB_TOKEN}',
-    'Accept': 'application/vnd.github.v3+json'
+    "Authorization": f"token {GITHUB_TOKEN}",
+    "Accept": "application/vnd.github.v3+json",
 }
 
+
 def get_audit_log_events(org, actions):
     """
     Get audit log events for specific actions
@@ -23,8 +24,10 @@ def get_audit_log_events(org, actions):
     events = []
     page = 1
     while True:
-        url = (f'https://api.github.com/orgs/{org}/audit-log?page={page}&per_page=100'
-               f'&action={",".join(actions)}')
+        url = (
+            f"https://api.github.com/orgs/{org}/audit-log?page={page}&per_page=100"
+            f"&action={','.join(actions)}"
+        )
         response = requests.get(url, headers=headers, timeout=TIMEOUT)
         response.raise_for_status()
         page_events = response.json()
@@ -34,12 +37,15 @@ def get_audit_log_events(org, actions):
         page += 1
     return events
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     try:
         # Define the actions to filter
-        action_filters = ['protected_branch',
-                   'repository_branch_protection_evaluation',
-                   'repository_ruleset']
+        action_filters = [
+            "protected_branch",
+            "repository_branch_protection_evaluation",
+            "repository_ruleset",
+        ]
 
         # Get audit log events for the specified actions
         audit_log_events = get_audit_log_events(ORGANIZATION, action_filters)
diff --git a/applications/github/github_branch_protections.py b/applications/github/github_branch_protections.py
index 73c94b6..705e498 100644
--- a/applications/github/github_branch_protections.py
+++ b/applications/github/github_branch_protections.py
@@ -4,16 +4,17 @@ Gathers branch protection rules for a repository.
 
 import requests
 
-GITHUB_TOKEN = 'your_personal_access_token'
-ORGANIZATION = 'your_organization'
-REPOSITORY = 'your_repository'
+GITHUB_TOKEN = "your_personal_access_token"
+ORGANIZATION = "your_organization"
+REPOSITORY = "your_repository"
 TIMEOUT = 30
 
 headers = {
-    'Authorization': f'token {GITHUB_TOKEN}',
-    'Accept': 'application/vnd.github.v3+json'
+    "Authorization": f"token {GITHUB_TOKEN}",
+    "Accept": "application/vnd.github.v3+json",
 }
 
+
 def get_all_branches(org, repo):
     """
     Get all branches in a repository
@@ -21,7 +22,7 @@ def get_all_branches(org, repo):
     all_branches = []
     page = 1
     while True:
-        url = f'https://api.github.com/repos/{org}/{repo}/branches?page={page}&per_page=100'
+        url = f"https://api.github.com/repos/{org}/{repo}/branches?page={page}&per_page=100"
         response = requests.get(url, headers=headers, timeout=TIMEOUT)
         response.raise_for_status()
         page_branches = response.json()
@@ -31,27 +32,30 @@ def get_all_branches(org, repo):
         page += 1
     return all_branches
 
+
 def get_branch_protection(org, repo, repo_branch):
     """
     Get branch protection settings
     """
-    url = f'https://api.github.com/repos/{org}/{repo}/branches/{repo_branch}/protection'
+    url = f"https://api.github.com/repos/{org}/{repo}/branches/{repo_branch}/protection"
     response = requests.get(url, headers=headers, timeout=TIMEOUT)
     if response.status_code == 404:
         return None  # No protection settings for this branch
     response.raise_for_status()
     return response.json()
 
+
 def get_repository_rulesets(org, repo):
     """
     Get repository rulesets
     """
-    url = f'https://api.github.com/repos/{org}/{repo}/rulesets'
+    url = f"https://api.github.com/repos/{org}/{repo}/rulesets"
     response = requests.get(url, headers=headers, timeout=TIMEOUT)
     response.raise_for_status()
     return response.json()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     try:
         # Get all branches in the repository
         branches = get_all_branches(ORGANIZATION, REPOSITORY)
@@ -59,8 +63,10 @@ if __name__ == '__main__':
 
         # Get protection settings for each branch
         for branch in branches:
-            branch_name = branch['name']
-            protection_settings = get_branch_protection(ORGANIZATION, REPOSITORY, branch_name)
+            branch_name = branch["name"]
+            protection_settings = get_branch_protection(
+                ORGANIZATION, REPOSITORY, branch_name
+            )
             print(f"\nBranch: {branch_name}")
             if protection_settings:
                 print(f"Protection settings: {protection_settings}")
diff --git a/applications/github/github_commits.py b/applications/github/github_commits.py
index f35d5d6..7581ff1 100644
--- a/applications/github/github_commits.py
+++ b/applications/github/github_commits.py
@@ -4,20 +4,21 @@ Gather all commits from a specific branch of a repository in a GitHub organizati
 
 import requests
 
-GITHUB_TOKEN = 'your_personal_access_token'
-ORGANIZATION = 'your_organization'
-REPOSITORY = 'your_repository'
-BRANCH = 'your_branch'
+GITHUB_TOKEN = "your_personal_access_token"
+ORGANIZATION = "your_organization"
+REPOSITORY = "your_repository"
+BRANCH = "your_branch"
 
 # Headers for authentication
 headers = {
-    'Authorization': f'token {GITHUB_TOKEN}',
-    'Accept': 'application/vnd.github.v3+json'
+    "Authorization": f"token {GITHUB_TOKEN}",
+    "Accept": "application/vnd.github.v3+json",
 }
 
 # Define a timeout value (in seconds)
 TIMEOUT = 10
 
+
 def get_commit_log(org, repo, branch):
     """
     Get the full commit log for a repository branch
@@ -25,8 +26,10 @@ def get_commit_log(org, repo, branch):
     commits = []
     page = 1
     while True:
-        url = (f'https://api.github.com/repos/{org}/{repo}/commits?sha={branch}'
-        f'&page={page}&per_page=100')
+        url = (
+            f"https://api.github.com/repos/{org}/{repo}/commits?sha={branch}"
+            f"&page={page}&per_page=100"
+        )
         response = requests.get(url, headers=headers, timeout=TIMEOUT)
         response.raise_for_status()
         page_commits = response.json()
@@ -36,39 +39,46 @@ def get_commit_log(org, repo, branch):
         page += 1
     return commits
 
+
 def get_commit_details(org, repo, sha):
     """
     Get detailed information for a specific commit
     """
-    url = f'https://api.github.com/repos/{org}/{repo}/commits/{sha}'
+    url = f"https://api.github.com/repos/{org}/{repo}/commits/{sha}"
     response = requests.get(url, headers=headers, timeout=TIMEOUT)
     response.raise_for_status()
     return response.json()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     try:
         # Get the full commit log for the specified branch
         commit_log = get_commit_log(ORGANIZATION, REPOSITORY, BRANCH)
-        print(f"Total commits in the repository '{REPOSITORY}' on branch "
-              f"'{BRANCH}': {len(commit_log)}")
+        print(
+            f"Total commits in the repository '{REPOSITORY}' on branch "
+            f"'{BRANCH}': {len(commit_log)}"
+        )
 
         # Get detailed information for each commit
         for commit in commit_log:
-            sha_hash = commit['sha']
-            commit_details = get_commit_details(ORGANIZATION, REPOSITORY,
-                                                sha_hash)
+            sha_hash = commit["sha"]
+            commit_details = get_commit_details(ORGANIZATION, REPOSITORY, sha_hash)
             print(f"\nCommit SHA: {commit_details['sha']}")
-            print(f"Author: {commit_details['commit']['author']['name']} "
-                  f"<{commit_details['commit']['author']['email']}>")
+            print(
+                f"Author: {commit_details['commit']['author']['name']} "
+                f"<{commit_details['commit']['author']['email']}>"
+            )
             print(f"Date: {commit_details['commit']['author']['date']}")
             print(f"Message: {commit_details['commit']['message']}")
             print(f"URL: {commit_details['html_url']}")
             print("Files changed:")
-            for file in commit_details['files']:
+            for file in commit_details["files"]:
                 print(f"  - {file['filename']} ({file['status']})")
-                print(f"    Additions: {file['additions']}, "
-                      f"Deletions: {file['deletions']}, "
-                      f"Changes: {file['changes']}")
+                print(
+                    f"    Additions: {file['additions']}, "
+                    f"Deletions: {file['deletions']}, "
+                    f"Changes: {file['changes']}"
+                )
     except requests.exceptions.Timeout:
         print("The request timed out")
     except requests.exceptions.RequestException as e:
diff --git a/applications/gitlab/gitlab_admins.py b/applications/gitlab/gitlab_admins.py
index 7fb2c78..091032c 100644
--- a/applications/gitlab/gitlab_admins.py
+++ b/applications/gitlab/gitlab_admins.py
@@ -12,12 +12,14 @@ TIMEOUT = 30
 URL = f"{BASE_URL}/groups/{GROUP_ID}/members"
 HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     # Get group members
     response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
     if response.status_code == 200:
         members = response.json()
         for member in members:
-            print(f"Username: {member['username']}, Access Level: {member['access_level']}")
+            print(
+                f"Username: {member['username']}, Access Level: {member['access_level']}"
+            )
     else:
         print(f"Failed to fetch group members: {response.status_code}, {response.text}")
diff --git a/databases/passwords/sql/test.py b/databases/passwords/sql/test.py
index bfacb20..81c1138 100644
--- a/databases/passwords/sql/test.py
+++ b/databases/passwords/sql/test.py
@@ -6,7 +6,8 @@ Checks SQL Server user data for compliance with Windows policies.
 import pandas as pd
 
 # Load the data into a pandas DataFrame
-df_input = pd.read_csv('./data.csv')
+df_input = pd.read_csv("./data.csv")
+
 
 # Function to apply rules and generate report
 def apply_rules_and_report(df):
@@ -22,45 +23,46 @@ def apply_rules_and_report(df):
     report = []
     for _, row in df.iterrows():
         result = {
-            'Name': row['name'],
-            'Type Check': '',
-            'Policy Check': '',
-            'Expiration Check': '',
-            'Reason': ''
+            "Name": row["name"],
+            "Type Check": "",
+            "Policy Check": "",
+            "Expiration Check": "",
+            "Reason": "",
         }
 
         # Check the type_desc
-        if row['type_desc'] == 'SQL_LOGIN':
-            result['Type Check'] = 'SQL_LOGIN'
-        elif row['type_desc'] == 'WINDOWS_LOGIN':
-            result['Type Check'] = 'N/A'
-            result['Reason'] = 'Refer to Windows password policy.'
+        if row["type_desc"] == "SQL_LOGIN":
+            result["Type Check"] = "SQL_LOGIN"
+        elif row["type_desc"] == "WINDOWS_LOGIN":
+            result["Type Check"] = "N/A"
+            result["Reason"] = "Refer to Windows password policy."
         else:
-            result['Type Check'] = 'Manual Review'
-            result['Reason'] = 'Reviewer to manually review.'
+            result["Type Check"] = "Manual Review"
+            result["Reason"] = "Reviewer to manually review."
 
         # Check if password policy is enforced
-        if row['is_policy_checked'] == 1:
-            result['Policy Check'] = 'PASS'
-            result['Reason'] += '''Password policy is enforced. Reviewer to
-            check the assigned policy.'''
+        if row["is_policy_checked"] == 1:
+            result["Policy Check"] = "PASS"
+            result["Reason"] += """Password policy is enforced. Reviewer to
+            check the assigned policy."""
         else:
-            result['Policy Check'] = 'FAIL'
-            result['Reason'] += 'Password policy is not enforced.'
+            result["Policy Check"] = "FAIL"
+            result["Reason"] += "Password policy is not enforced."
 
         # Check if password expiration is enforced
-        if row['is_expiration_checked'] == 1:
-            result['Expiration Check'] = 'PASS'
-            result['Reason'] += '''Password expiration is enforced. Reviewer to
-            check the expiration policy.'''
+        if row["is_expiration_checked"] == 1:
+            result["Expiration Check"] = "PASS"
+            result["Reason"] += """Password expiration is enforced. Reviewer to
+            check the expiration policy."""
         else:
-            result['Expiration Check'] = 'FAIL'
-            result['Reason'] += 'Password expiration is not enforced.'
+            result["Expiration Check"] = "FAIL"
+            result["Reason"] += "Password expiration is not enforced."
 
         report.append(result)
 
     return report
 
+
 # Main function to run the script
 def main():
     """
@@ -73,5 +75,6 @@ def main():
     # Print the report
     print(report_df)
 
+
 if __name__ == "__main__":
     main()
diff --git a/project_management/dash/app.py b/project_management/dash/app.py
index 2a01911..ea530a6 100644
--- a/project_management/dash/app.py
+++ b/project_management/dash/app.py
@@ -8,51 +8,54 @@ import pandas as pd
 import plotly.express as px
 
 # Incorporate data
-df = pd.read_excel('project_data.xlsx')
+df = pd.read_excel("project_data.xlsx")
 
 # Initialize the app
 app = Dash()
 
 # App layout
 app.layout = [
-	html.H1(children='Project Dashboard', style={'textAlign':'center'}),
-	html.Div(children = [
-		dcc.Graph(
-			figure=px.histogram(
-				df,
-				x='Preparer',
-				color='High Priority?',
-				title='Control Count by Preparer'
-			),
-			style = {'flex-grow':'1'}
-		),
-		dcc.Graph(
-			figure=px.histogram(
-				df,
-				x='Preparer',
-				y='Projected Hours ',
-				color='Status ',
-				title='Project Hours by Preparer'
-			),
-			style = {'flex-grow':'1'}
-		)
-	],
-	style = {
-		'display':'flex',
-		'flex-wrap':'wrap',
-		'justify-content':'space-between',
-'align-items':'center'}),
-	dcc.Graph(
-		figure=px.pie(
-			df,
-			values = df['Preparer'].value_counts().values,
-			names=df['Reviewer'].value_counts().index,
-			title='Reviewer Breakdown',
-			hole=0.5
-		)
-	)
+    html.H1(children="Project Dashboard", style={"textAlign": "center"}),
+    html.Div(
+        children=[
+            dcc.Graph(
+                figure=px.histogram(
+                    df,
+                    x="Preparer",
+                    color="High Priority?",
+                    title="Control Count by Preparer",
+                ),
+                style={"flex-grow": "1"},
+            ),
+            dcc.Graph(
+                figure=px.histogram(
+                    df,
+                    x="Preparer",
+                    y="Projected Hours ",
+                    color="Status ",
+                    title="Project Hours by Preparer",
+                ),
+                style={"flex-grow": "1"},
+            ),
+        ],
+        style={
+            "display": "flex",
+            "flex-wrap": "wrap",
+            "justify-content": "space-between",
+            "align-items": "center",
+        },
+    ),
+    dcc.Graph(
+        figure=px.pie(
+            df,
+            values=df["Preparer"].value_counts().values,
+            names=df["Reviewer"].value_counts().index,
+            title="Reviewer Breakdown",
+            hole=0.5,
+        )
+    ),
 ]
 
 # Run the app
-if __name__ == '__main__':
+if __name__ == "__main__":
     app.run(debug=True)