audit-labs/audit-tools

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

Commit 6817ff6335

6817ff633541af6b049946ddaa13bad10d732376

parent: cb6fe10542

Unsigned

cmc <hello@cleberg.net> · 2025-04-05 18:43 UTC
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>

Layout: unified · split

.github/workflows/pylint.yml deleted −23
@@ -1,23 +0,0 @@
1name: Pylint
2
3on: [push]
4
5jobs:
6 build:
7 runs-on: ubuntu-latest
8 strategy:
9 matrix:
10 python-version: ["3.8", "3.9", "3.10"]
11 steps:
12 - uses: actions/checkout@v4
13 - name: Set up Python ${{ matrix.python-version }}
14 uses: actions/setup-python@v5
15 with:
16 python-version: ${{ matrix.python-version }}
17 - name: Install dependencies
18 run: |
19 python -m pip install --upgrade pip
20 pip install pylint pandas dash plotly.express
21 - name: Analysing the code with pylint
22 run: |
23 pylint -d R0801 $(git ls-files '*.py')
.github/workflows/ruff.yml added +37
@@ -0,0 +1,37 @@
1name: Ruff
2
3on:
4 push:
5 paths:
6 - '**.py'
7
8jobs:
9 build:
10 runs-on: ubuntu-latest
11 strategy:
12 matrix:
13 python-version: ["3.x"]
14 steps:
15 - uses: actions/checkout@v4
16 - name: Set up Python ${{ matrix.python-version }}
17 uses: actions/setup-python@v5
18 with:
19 python-version: ${{ matrix.python-version }}
20 ref: ${{ github.event.pull_request.head.ref }}
21 - name: Install dependencies
22 run: |
23 python -m pip install --upgrade pip
24 pip install pandas dash plotly.express
25 - name: Install Ruff
26 uses: astral-sh/ruff-action@v3.2.2
27 - name: Ruff Actions
28 run: |
29 ruff check --fix
30 ruff format
31 - name: Add and Commit
32 uses: EndBug/add-and-commit@v9
33 env:
34 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 with:
36 default_author: github_actions
37 pathspec_error_handling: ignore
\ No newline at end of file
applications/github/github_admins.py +19 −12
@@ -5,54 +5,59 @@ and list each user's permission per repo.
55
66import requests
77
8GITHUB_TOKEN = 'your_personal_access_token'
9ORGANIZATION = 'your_organization'
8GITHUB_TOKEN = "your_personal_access_token"
9ORGANIZATION = "your_organization"
1010TIMEOUT = 30
1111
1212# Headers for authentication
1313headers = {
14 'Authorization': f'token {GITHUB_TOKEN}',
15 'Accept': 'application/vnd.github.v3+json'
14 "Authorization": f"token {GITHUB_TOKEN}",
15 "Accept": "application/vnd.github.v3+json",
1616}
1717
18
1819def get_org_members(org):
1920 """
2021 Get members of an organization
2122 """
22 url = f'https://api.github.com/orgs/{org}/members'
23 url = f"https://api.github.com/orgs/{org}/members"
2324 response = requests.get(url, headers=headers, timeout=TIMEOUT)
2425 response.raise_for_status()
2526 return response.json()
2627
28
2729def get_org_repos(org):
2830 """
2931 Get repositories of an organization
3032 """
31 url = f'https://api.github.com/orgs/{org}/repos'
33 url = f"https://api.github.com/orgs/{org}/repos"
3234 response = requests.get(url, headers=headers, timeout=TIMEOUT)
3335 response.raise_for_status()
3436 return response.json()
3537
38
3639def get_repo_collaborators(org, repo):
3740 """
3841 Get collaborators of a repository with their permissions
3942 """
40 url = f'https://api.github.com/repos/{org}/{repo}/collaborators'
43 url = f"https://api.github.com/repos/{org}/{repo}/collaborators"
4144 response = requests.get(url, headers=headers, timeout=TIMEOUT)
4245 response.raise_for_status()
4346 return response.json()
4447
48
4549def get_user_permissions(org, repo, user):
4650 """
4751 Get a user's permissions for a repository
4852 """
49 url = f'https://api.github.com/repos/{org}/{repo}/collaborators/{user}/permission'
53 url = f"https://api.github.com/repos/{org}/{repo}/collaborators/{user}/permission"
5054 response = requests.get(url, headers=headers, timeout=TIMEOUT)
5155 response.raise_for_status()
5256 return response.json()
5357
58
5459# Main script
55if __name__ == '__main__':
60if __name__ == "__main__":
5661 # Get organization members
5762 members = get_org_members(ORGANIZATION)
5863 print(f"Members of the organization '{ORGANIZATION}':")
@@ -67,10 +72,12 @@ if __name__ == '__main__':
6772
6873 # Get collaborators for each repository and their permissions
6974 for repository in repositories:
70 repository_name = repository['name']
75 repository_name = repository["name"]
7176 collaborators = get_repo_collaborators(ORGANIZATION, repository_name)
7277 print(f"\nCollaborators for the repository '{repository_name}':")
7378 for collaborator in collaborators:
74 user_login = collaborator['login']
75 permissions = get_user_permissions(ORGANIZATION, repository_name, user_login)
79 user_login = collaborator["login"]
80 permissions = get_user_permissions(
81 ORGANIZATION, repository_name, user_login
82 )
7683 print(f"- {user_login}: {permissions['permission']}")
applications/github/github_audit_log.py +16 −10
@@ -6,16 +6,17 @@ NOTE: REQUIRES A GITHUB ENTERPRISE SUBSCRIPTION TO ACCESS THE API.
66
77import requests
88
9GITHUB_TOKEN = 'your_personal_access_token'
10ORGANIZATION = 'your_organization'
9GITHUB_TOKEN = "your_personal_access_token"
10ORGANIZATION = "your_organization"
1111TIMEOUT = 30
1212
1313# Headers for authentication
1414headers = {
15 'Authorization': f'token {GITHUB_TOKEN}',
16 'Accept': 'application/vnd.github.v3+json'
15 "Authorization": f"token {GITHUB_TOKEN}",
16 "Accept": "application/vnd.github.v3+json",
1717}
1818
19
1920def get_audit_log_events(org, actions):
2021 """
2122 Get audit log events for specific actions
@@ -23,8 +24,10 @@ def get_audit_log_events(org, actions):
2324 events = []
2425 page = 1
2526 while True:
26 url = (f'https://api.github.com/orgs/{org}/audit-log?page={page}&per_page=100'
27 f'&action={",".join(actions)}')
27 url = (
28 f"https://api.github.com/orgs/{org}/audit-log?page={page}&per_page=100"
29 f"&action={','.join(actions)}"
30 )
2831 response = requests.get(url, headers=headers, timeout=TIMEOUT)
2932 response.raise_for_status()
3033 page_events = response.json()
@@ -34,12 +37,15 @@ def get_audit_log_events(org, actions):
3437 page += 1
3538 return events
3639
37if __name__ == '__main__':
40
41if __name__ == "__main__":
3842 try:
3943 # Define the actions to filter
40 action_filters = ['protected_branch',
41 'repository_branch_protection_evaluation',
42 'repository_ruleset']
44 action_filters = [
45 "protected_branch",
46 "repository_branch_protection_evaluation",
47 "repository_ruleset",
48 ]
4349
4450 # Get audit log events for the specified actions
4551 audit_log_events = get_audit_log_events(ORGANIZATION, action_filters)
applications/github/github_branch_protections.py +17 −11
@@ -4,16 +4,17 @@ Gathers branch protection rules for a repository.
44
55import requests
66
7GITHUB_TOKEN = 'your_personal_access_token'
8ORGANIZATION = 'your_organization'
9REPOSITORY = 'your_repository'
7GITHUB_TOKEN = "your_personal_access_token"
8ORGANIZATION = "your_organization"
9REPOSITORY = "your_repository"
1010TIMEOUT = 30
1111
1212headers = {
13 'Authorization': f'token {GITHUB_TOKEN}',
14 'Accept': 'application/vnd.github.v3+json'
13 "Authorization": f"token {GITHUB_TOKEN}",
14 "Accept": "application/vnd.github.v3+json",
1515}
1616
17
1718def get_all_branches(org, repo):
1819 """
1920 Get all branches in a repository
@@ -21,7 +22,7 @@ def get_all_branches(org, repo):
2122 all_branches = []
2223 page = 1
2324 while True:
24 url = f'https://api.github.com/repos/{org}/{repo}/branches?page={page}&per_page=100'
25 url = f"https://api.github.com/repos/{org}/{repo}/branches?page={page}&per_page=100"
2526 response = requests.get(url, headers=headers, timeout=TIMEOUT)
2627 response.raise_for_status()
2728 page_branches = response.json()
@@ -31,27 +32,30 @@ def get_all_branches(org, repo):
3132 page += 1
3233 return all_branches
3334
35
3436def get_branch_protection(org, repo, repo_branch):
3537 """
3638 Get branch protection settings
3739 """
38 url = f'https://api.github.com/repos/{org}/{repo}/branches/{repo_branch}/protection'
40 url = f"https://api.github.com/repos/{org}/{repo}/branches/{repo_branch}/protection"
3941 response = requests.get(url, headers=headers, timeout=TIMEOUT)
4042 if response.status_code == 404:
4143 return None # No protection settings for this branch
4244 response.raise_for_status()
4345 return response.json()
4446
47
4548def get_repository_rulesets(org, repo):
4649 """
4750 Get repository rulesets
4851 """
49 url = f'https://api.github.com/repos/{org}/{repo}/rulesets'
52 url = f"https://api.github.com/repos/{org}/{repo}/rulesets"
5053 response = requests.get(url, headers=headers, timeout=TIMEOUT)
5154 response.raise_for_status()
5255 return response.json()
5356
54if __name__ == '__main__':
57
58if __name__ == "__main__":
5559 try:
5660 # Get all branches in the repository
5761 branches = get_all_branches(ORGANIZATION, REPOSITORY)
@@ -59,8 +63,10 @@ if __name__ == '__main__':
5963
6064 # Get protection settings for each branch
6165 for branch in branches:
62 branch_name = branch['name']
63 protection_settings = get_branch_protection(ORGANIZATION, REPOSITORY, branch_name)
66 branch_name = branch["name"]
67 protection_settings = get_branch_protection(
68 ORGANIZATION, REPOSITORY, branch_name
69 )
6470 print(f"\nBranch: {branch_name}")
6571 if protection_settings:
6672 print(f"Protection settings: {protection_settings}")
applications/github/github_commits.py +31 −21
@@ -4,20 +4,21 @@ Gather all commits from a specific branch of a repository in a GitHub organizati
44
55import requests
66
7GITHUB_TOKEN = 'your_personal_access_token'
8ORGANIZATION = 'your_organization'
9REPOSITORY = 'your_repository'
10BRANCH = 'your_branch'
7GITHUB_TOKEN = "your_personal_access_token"
8ORGANIZATION = "your_organization"
9REPOSITORY = "your_repository"
10BRANCH = "your_branch"
1111
1212# Headers for authentication
1313headers = {
14 'Authorization': f'token {GITHUB_TOKEN}',
15 'Accept': 'application/vnd.github.v3+json'
14 "Authorization": f"token {GITHUB_TOKEN}",
15 "Accept": "application/vnd.github.v3+json",
1616}
1717
1818# Define a timeout value (in seconds)
1919TIMEOUT = 10
2020
21
2122def get_commit_log(org, repo, branch):
2223 """
2324 Get the full commit log for a repository branch
@@ -25,8 +26,10 @@ def get_commit_log(org, repo, branch):
2526 commits = []
2627 page = 1
2728 while True:
28 url = (f'https://api.github.com/repos/{org}/{repo}/commits?sha={branch}'
29 f'&page={page}&per_page=100')
29 url = (
30 f"https://api.github.com/repos/{org}/{repo}/commits?sha={branch}"
31 f"&page={page}&per_page=100"
32 )
3033 response = requests.get(url, headers=headers, timeout=TIMEOUT)
3134 response.raise_for_status()
3235 page_commits = response.json()
@@ -36,39 +39,46 @@ def get_commit_log(org, repo, branch):
3639 page += 1
3740 return commits
3841
42
3943def get_commit_details(org, repo, sha):
4044 """
4145 Get detailed information for a specific commit
4246 """
43 url = f'https://api.github.com/repos/{org}/{repo}/commits/{sha}'
47 url = f"https://api.github.com/repos/{org}/{repo}/commits/{sha}"
4448 response = requests.get(url, headers=headers, timeout=TIMEOUT)
4549 response.raise_for_status()
4650 return response.json()
4751
48if __name__ == '__main__':
52
53if __name__ == "__main__":
4954 try:
5055 # Get the full commit log for the specified branch
5156 commit_log = get_commit_log(ORGANIZATION, REPOSITORY, BRANCH)
52 print(f"Total commits in the repository '{REPOSITORY}' on branch "
53 f"'{BRANCH}': {len(commit_log)}")
57 print(
58 f"Total commits in the repository '{REPOSITORY}' on branch "
59 f"'{BRANCH}': {len(commit_log)}"
60 )
5461
5562 # Get detailed information for each commit
5663 for commit in commit_log:
57 sha_hash = commit['sha']
58 commit_details = get_commit_details(ORGANIZATION, REPOSITORY,
59 sha_hash)
64 sha_hash = commit["sha"]
65 commit_details = get_commit_details(ORGANIZATION, REPOSITORY, sha_hash)
6066 print(f"\nCommit SHA: {commit_details['sha']}")
61 print(f"Author: {commit_details['commit']['author']['name']} "
62 f"<{commit_details['commit']['author']['email']}>")
67 print(
68 f"Author: {commit_details['commit']['author']['name']} "
69 f"<{commit_details['commit']['author']['email']}>"
70 )
6371 print(f"Date: {commit_details['commit']['author']['date']}")
6472 print(f"Message: {commit_details['commit']['message']}")
6573 print(f"URL: {commit_details['html_url']}")
6674 print("Files changed:")
67 for file in commit_details['files']:
75 for file in commit_details["files"]:
6876 print(f" - {file['filename']} ({file['status']})")
69 print(f" Additions: {file['additions']}, "
70 f"Deletions: {file['deletions']}, "
71 f"Changes: {file['changes']}")
77 print(
78 f" Additions: {file['additions']}, "
79 f"Deletions: {file['deletions']}, "
80 f"Changes: {file['changes']}"
81 )
7282 except requests.exceptions.Timeout:
7383 print("The request timed out")
7484 except requests.exceptions.RequestException as e:
applications/gitlab/gitlab_admins.py +4 −2
@@ -12,12 +12,14 @@ TIMEOUT = 30
1212URL = f"{BASE_URL}/groups/{GROUP_ID}/members"
1313HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
1414
15if __name__ == '__main__':
15if __name__ == "__main__":
1616 # Get group members
1717 response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
1818 if response.status_code == 200:
1919 members = response.json()
2020 for member in members:
21 print(f"Username: {member['username']}, Access Level: {member['access_level']}")
21 print(
22 f"Username: {member['username']}, Access Level: {member['access_level']}"
23 )
2224 else:
2325 print(f"Failed to fetch group members: {response.status_code}, {response.text}")
databases/passwords/sql/test.py +28 −25
@@ -6,7 +6,8 @@ Checks SQL Server user data for compliance with Windows policies.
66import pandas as pd
77
88# Load the data into a pandas DataFrame
9df_input = pd.read_csv('./data.csv')
9df_input = pd.read_csv("./data.csv")
10
1011
1112# Function to apply rules and generate report
1213def apply_rules_and_report(df):
@@ -22,45 +23,46 @@ def apply_rules_and_report(df):
2223 report = []
2324 for _, row in df.iterrows():
2425 result = {
25 'Name': row['name'],
26 'Type Check': '',
27 'Policy Check': '',
28 'Expiration Check': '',
29 'Reason': ''
26 "Name": row["name"],
27 "Type Check": "",
28 "Policy Check": "",
29 "Expiration Check": "",
30 "Reason": "",
3031 }
3132
3233 # Check the type_desc
33 if row['type_desc'] == 'SQL_LOGIN':
34 result['Type Check'] = 'SQL_LOGIN'
35 elif row['type_desc'] == 'WINDOWS_LOGIN':
36 result['Type Check'] = 'N/A'
37 result['Reason'] = 'Refer to Windows password policy.'
34 if row["type_desc"] == "SQL_LOGIN":
35 result["Type Check"] = "SQL_LOGIN"
36 elif row["type_desc"] == "WINDOWS_LOGIN":
37 result["Type Check"] = "N/A"
38 result["Reason"] = "Refer to Windows password policy."
3839 else:
39 result['Type Check'] = 'Manual Review'
40 result['Reason'] = 'Reviewer to manually review.'
40 result["Type Check"] = "Manual Review"
41 result["Reason"] = "Reviewer to manually review."
4142
4243 # Check if password policy is enforced
43 if row['is_policy_checked'] == 1:
44 result['Policy Check'] = 'PASS'
45 result['Reason'] += '''Password policy is enforced. Reviewer to
46 check the assigned policy.'''
44 if row["is_policy_checked"] == 1:
45 result["Policy Check"] = "PASS"
46 result["Reason"] += """Password policy is enforced. Reviewer to
47 check the assigned policy."""
4748 else:
48 result['Policy Check'] = 'FAIL'
49 result['Reason'] += 'Password policy is not enforced.'
49 result["Policy Check"] = "FAIL"
50 result["Reason"] += "Password policy is not enforced."
5051
5152 # Check if password expiration is enforced
52 if row['is_expiration_checked'] == 1:
53 result['Expiration Check'] = 'PASS'
54 result['Reason'] += '''Password expiration is enforced. Reviewer to
55 check the expiration policy.'''
53 if row["is_expiration_checked"] == 1:
54 result["Expiration Check"] = "PASS"
55 result["Reason"] += """Password expiration is enforced. Reviewer to
56 check the expiration policy."""
5657 else:
57 result['Expiration Check'] = 'FAIL'
58 result['Reason'] += 'Password expiration is not enforced.'
58 result["Expiration Check"] = "FAIL"
59 result["Reason"] += "Password expiration is not enforced."
5960
6061 report.append(result)
6162
6263 return report
6364
65
6466# Main function to run the script
6567def main():
6668 """
@@ -73,5 +75,6 @@ def main():
7375 # Print the report
7476 print(report_df)
7577
78
7679if __name__ == "__main__":
7780 main()
project_management/dash/app.py +41 −38
@@ -8,51 +8,54 @@ import pandas as pd
88import plotly.express as px
99
1010# Incorporate data
11df = pd.read_excel('project_data.xlsx')
11df = pd.read_excel("project_data.xlsx")
1212
1313# Initialize the app
1414app = Dash()
1515
1616# App layout
1717app.layout = [
18 html.H1(children='Project Dashboard', style={'textAlign':'center'}),
19 html.Div(children = [
20 dcc.Graph(
21 figure=px.histogram(
22 df,
23 x='Preparer',
24 color='High Priority?',
25 title='Control Count by Preparer'
26 ),
27 style = {'flex-grow':'1'}
28 ),
29 dcc.Graph(
30 figure=px.histogram(
31 df,
32 x='Preparer',
33 y='Projected Hours ',
34 color='Status ',
35 title='Project Hours by Preparer'
36 ),
37 style = {'flex-grow':'1'}
38 )
39 ],
40 style = {
41 'display':'flex',
42 'flex-wrap':'wrap',
43 'justify-content':'space-between',
44'align-items':'center'}),
45 dcc.Graph(
46 figure=px.pie(
47 df,
48 values = df['Preparer'].value_counts().values,
49 names=df['Reviewer'].value_counts().index,
50 title='Reviewer Breakdown',
51 hole=0.5
52 )
53 )
18 html.H1(children="Project Dashboard", style={"textAlign": "center"}),
19 html.Div(
20 children=[
21 dcc.Graph(
22 figure=px.histogram(
23 df,
24 x="Preparer",
25 color="High Priority?",
26 title="Control Count by Preparer",
27 ),
28 style={"flex-grow": "1"},
29 ),
30 dcc.Graph(
31 figure=px.histogram(
32 df,
33 x="Preparer",
34 y="Projected Hours ",
35 color="Status ",
36 title="Project Hours by Preparer",
37 ),
38 style={"flex-grow": "1"},
39 ),
40 ],
41 style={
42 "display": "flex",
43 "flex-wrap": "wrap",
44 "justify-content": "space-between",
45 "align-items": "center",
46 },
47 ),
48 dcc.Graph(
49 figure=px.pie(
50 df,
51 values=df["Preparer"].value_counts().values,
52 names=df["Reviewer"].value_counts().index,
53 title="Reviewer Breakdown",
54 hole=0.5,
55 )
56 ),
5457]
5558
5659# Run the app
57if __name__ == '__main__':
60if __name__ == "__main__":
5861 app.run(debug=True)