diff options
Diffstat (limited to 'applications/github')
-rw-r--r-- | applications/github/README.org | 112 | ||||
-rw-r--r-- | applications/github/github_admins.py | 76 | ||||
-rw-r--r-- | applications/github/github_audit_log.py | 59 | ||||
-rw-r--r-- | applications/github/github_branch_protections.py | 78 | ||||
-rw-r--r-- | applications/github/github_commits.py | 75 |
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}") |