aboutsummaryrefslogtreecommitdiff
path: root/applications
diff options
context:
space:
mode:
Diffstat (limited to 'applications')
-rw-r--r--applications/github/README.org112
-rw-r--r--applications/github/github_admins.py76
-rw-r--r--applications/github/github_audit_log.py59
-rw-r--r--applications/github/github_branch_protections.py78
-rw-r--r--applications/github/github_commits.py75
5 files changed, 400 insertions, 0 deletions
diff --git a/applications/github/README.org b/applications/github/README.org
new file mode 100644
index 0000000..0ac08ae
--- /dev/null
+++ b/applications/github/README.org
@@ -0,0 +1,112 @@
+#+title: GitHub Scripts
+
+*NOTE*: I used the same [[https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens][PAT]] for all scripts within this folder. Note that you can likely reduce permissions for certain scripts - it's best practice to define a PAT for a specific purpose and avoid using a single PAT with broad permissions.
+
+- Personal Access Token:
+ - [X] Repository Permissions
+ - [X] Actions: read-only
+ - [X] Contents: read-only
+ - [X] Metadata: read-only
+ - [X] Workflows: read-only
+ - [X] Organization Permissions
+ - [X] Administration: read-only
+
+* =github_admins.py=
+
+#+begin_src sh
+python ./github_admins.py
+#+end_src
+
+#+begin_src text
+Members of the organization 'your_organization':
+
+Repositories in the organization 'your_organization':
+- demo-repository
+
+Collaborators for the repository 'demo-repository':
+- user1: admin
+#+end_src
+
+* =github_audit_log.py=
+
+*NOTE*: Requires an active GitHub Enterprise subscription.
+
+#+begin_src sh
+python ./github_audit_log.py
+#+end_src
+
+#+begin_src text
+TODO: Need to get an Enterprise subscription to test this script.
+#+end_src
+
+* =github_branch_protections.py=
+
+
+#+begin_src sh
+python ./github_branch_protections.py
+#+end_src
+
+#+begin_src text
+Total branches in the repository 'demo-repository': 1
+
+Branch: main
+No protection settings
+
+Repository rulesets for 'demo-repository':
+[{'id': 2311373, 'name': 'default', 'target': 'branch', 'source_type': 'Repository', 'source': 'phryq/demo-repository', 'enforcement': 'active', 'node_id': 'RRS_lACqUmVwb3NpdG9yec40LV1PzgAjRM0', '_links': {'self': {'href': 'https://api.github.com/repos/phryq/demo-repository/rulesets/2311373'}, 'html': {'href': 'https://github.com/phryq/demo-repository/rules/2311373'}}, 'created_at': '2024-10-19T15:59:35.200-05:00', 'updated_at': '2024-10-19T15:59:35.200-05:00'}]
+#+end_src
+
+* =github_commits.py=
+
+#+begin_src sh
+python ./github_commits.py
+#+end_src
+
+#+begin_src text
+Total commits in the repository 'demo-repository' on branch 'main': 3
+
+Commit SHA: 13c488a2cdda08e4043f8ef36ced5fdd429e9718
+Author: Christian Cleberg <156287552+ccleberg@users.noreply.github.com>
+Date: 2024-10-19T20:57:55Z
+Message: Merge pull request #2 from phryq/1-test-issue
+
+fixes
+URL: https://github.com/phryq/demo-repository/commit/13c488a2cdda08e4043f8ef36ced5fdd429e9718
+Files changed:
+ - .gitignore (added)
+ Additions: 0, Deletions: 0, Changes: 0
+ - README.md (removed)
+ Additions: 0, Deletions: 4, Changes: 4
+ - README.org (added)
+ Additions: 7, Deletions: 0, Changes: 7
+
+Commit SHA: 6bfde238a2a34a93ce8ee02082eaf4ab3c189368
+Author: Christian Cleberg <hello@cleberg.net>
+Date: 2024-10-19T20:56:50Z
+Message: fixes
+URL: https://github.com/phryq/demo-repository/commit/6bfde238a2a34a93ce8ee02082eaf4ab3c189368
+Files changed:
+ - .gitignore (added)
+ Additions: 0, Deletions: 0, Changes: 0
+ - README.md (removed)
+ Additions: 0, Deletions: 4, Changes: 4
+ - README.org (added)
+ Additions: 7, Deletions: 0, Changes: 7
+
+Commit SHA: be1ddf31e08fc790f54d68f8067b7b2f3805f999
+Author: Christian Cleberg <156287552+ccleberg@users.noreply.github.com>
+Date: 2024-10-19T20:54:08Z
+Message: Initial commit
+URL: https://github.com/phryq/demo-repository/commit/be1ddf31e08fc790f54d68f8067b7b2f3805f999
+Files changed:
+ - .github/workflows/auto-assign.yml (added)
+ Additions: 19, Deletions: 0, Changes: 19
+ - .github/workflows/proof-html.yml (added)
+ Additions: 11, Deletions: 0, Changes: 11
+ - README.md (added)
+ Additions: 4, Deletions: 0, Changes: 4
+ - index.html (added)
+ Additions: 1, Deletions: 0, Changes: 1
+ - package.json (added)
+ Additions: 9, Deletions: 0, Changes: 9
+#+end_src \ No newline at end of file
diff --git a/applications/github/github_admins.py b/applications/github/github_admins.py
new file mode 100644
index 0000000..d737887
--- /dev/null
+++ b/applications/github/github_admins.py
@@ -0,0 +1,76 @@
+"""
+Gather all members of a GitHub organization, all repos within that organization,
+and list each user's permission per repo.
+"""
+
+import requests
+
+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'
+}
+
+def get_org_members(org):
+ """
+ Get members of an organization
+ """
+ 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'
+ 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'
+ 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'
+ response = requests.get(url, headers=headers, timeout=TIMEOUT)
+ response.raise_for_status()
+ return response.json()
+
+# Main script
+if __name__ == '__main__':
+ # Get organization members
+ members = get_org_members(ORGANIZATION)
+ print(f"Members of the organization '{ORGANIZATION}':")
+ for member in members:
+ print(f"- {member['login']}")
+
+ # Get organization repositories
+ repositories = get_org_repos(ORGANIZATION)
+ print(f"\nRepositories in the organization '{ORGANIZATION}':")
+ for repository in repositories:
+ print(f"- {repository['name']}")
+
+ # Get collaborators for each repository and their permissions
+ for repository in repositories:
+ 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)
+ print(f"- {user_login}: {permissions['permission']}")
diff --git a/applications/github/github_audit_log.py b/applications/github/github_audit_log.py
new file mode 100644
index 0000000..0937764
--- /dev/null
+++ b/applications/github/github_audit_log.py
@@ -0,0 +1,59 @@
+"""
+Extract a specific list of events from the GitHub Audit Log API.
+
+NOTE: REQUIRES A GITHUB ENTERPRISE SUBSCRIPTION TO ACCESS THE API.
+"""
+
+import requests
+
+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'
+}
+
+def get_audit_log_events(org, actions):
+ """
+ Get audit log events for specific 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)}')
+ response = requests.get(url, headers=headers, timeout=TIMEOUT)
+ response.raise_for_status()
+ page_events = response.json()
+ if not page_events:
+ break
+ events.extend(page_events)
+ page += 1
+ return events
+
+if __name__ == '__main__':
+ try:
+ # Define the actions to filter
+ 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)
+ print(f"Total audit log events for specified actions: {len(audit_log_events)}")
+
+ # Print detailed information for each event
+ for event in audit_log_events:
+ print(f"\nEvent ID: {event['@id']}")
+ print(f"Action: {event['action']}")
+ print(f"Actor: {event['actor']}")
+ print(f"Repository: {event.get('repo', 'N/A')}")
+ print(f"Created At: {event['created_at']}")
+ print(f"Details: {event}")
+ except requests.exceptions.Timeout:
+ print("The request timed out")
+ except requests.exceptions.RequestException as e:
+ print(f"An error occurred: {e}")
diff --git a/applications/github/github_branch_protections.py b/applications/github/github_branch_protections.py
new file mode 100644
index 0000000..73c94b6
--- /dev/null
+++ b/applications/github/github_branch_protections.py
@@ -0,0 +1,78 @@
+"""
+Gathers branch protection rules for a repository.
+"""
+
+import requests
+
+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'
+}
+
+def get_all_branches(org, repo):
+ """
+ Get all branches in a repository
+ """
+ all_branches = []
+ page = 1
+ while True:
+ 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()
+ if not page_branches:
+ break
+ all_branches.extend(page_branches)
+ 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'
+ 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'
+ response = requests.get(url, headers=headers, timeout=TIMEOUT)
+ response.raise_for_status()
+ return response.json()
+
+if __name__ == '__main__':
+ try:
+ # Get all branches in the repository
+ branches = get_all_branches(ORGANIZATION, REPOSITORY)
+ print(f"Total branches in the repository '{REPOSITORY}': {len(branches)}")
+
+ # Get protection settings for each branch
+ for branch in branches:
+ 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}")
+ else:
+ print("No protection settings")
+
+ # Get repository rulesets
+ rulesets = get_repository_rulesets(ORGANIZATION, REPOSITORY)
+ print(f"\nRepository rulesets for '{REPOSITORY}':")
+ print(rulesets)
+
+ except requests.exceptions.Timeout:
+ print("The request timed out")
+ except requests.exceptions.RequestException as e:
+ print(f"An error occurred: {e}")
diff --git a/applications/github/github_commits.py b/applications/github/github_commits.py
new file mode 100644
index 0000000..f35d5d6
--- /dev/null
+++ b/applications/github/github_commits.py
@@ -0,0 +1,75 @@
+"""
+Gather all commits from a specific branch of a repository in a GitHub organization.
+"""
+
+import requests
+
+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'
+}
+
+# Define a timeout value (in seconds)
+TIMEOUT = 10
+
+def get_commit_log(org, repo, branch):
+ """
+ Get the full commit log for a repository branch
+ """
+ commits = []
+ page = 1
+ while True:
+ 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()
+ if not page_commits:
+ break
+ commits.extend(page_commits)
+ 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}'
+ response = requests.get(url, headers=headers, timeout=TIMEOUT)
+ response.raise_for_status()
+ return response.json()
+
+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)}")
+
+ # Get detailed information for each commit
+ for commit in commit_log:
+ 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"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']:
+ print(f" - {file['filename']} ({file['status']})")
+ 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:
+ print(f"An error occurred: {e}")