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
main: applications/github/audit.py · raw
1"""
2GitHub audit CLI.
3
4Runs all collectors against a GitHub organization and writes a timestamped
5audit package to an output directory.
6
7Usage:
8 export GITHUB_TOKEN=your_token
9 export GITHUB_ORG=your_org
10
11 python audit.py
12 python audit.py --org my-org
13 python audit.py --org my-org --out ./output
14 python audit.py --org my-org --branch main
15
16Output:
17 <out>/github_audit_<org>_<date>/
18 member_roster.csv
19 two_factor_disabled.csv
20 outside_collaborators.csv
21 privileged_access.csv
22 pending_invitations.csv
23 team_permissions.csv
24 permission_matrix.csv
25 branch_protections.csv
26 commits.csv
27 audit_log.csv
28 summary.txt
29"""
30
31import argparse
32import os
33import sys
34from datetime import date
35
36import config
37from collectors import (
38 audit_log,
39 branch_protections,
40 commits,
41 deploy_keys,
42 members,
43 org_settings,
44 security_alerts,
45 webhooks,
46)
47from reporters import csv_reporter
48
49
50def parse_args():
51 parser = argparse.ArgumentParser(
52 description="Generate a GitHub audit package for an organization."
53 )
54 parser.add_argument(
55 "--org",
56 help="GitHub organization name. Overrides GITHUB_ORG env var.",
57 )
58 parser.add_argument(
59 "--out",
60 default="./output",
61 help="Directory to write the audit package into. Default: ./output",
62 )
63 parser.add_argument(
64 "--branch",
65 default="main",
66 help="Branch to collect commits from. Default: main",
67 )
68 parser.add_argument(
69 "--include-audit-log",
70 action="store_true",
71 help=argparse.SUPPRESS,
72 )
73 return parser.parse_args()
74
75
76def run():
77 args = parse_args()
78 cfg = config.load(org_override=args.org)
79 org = cfg["org"]
80
81 output_dir = os.path.join(
82 args.out, f"github_audit_{org}_{date.today().isoformat()}"
83 )
84
85 print(f"GitHub Audit — {org}")
86 print(f"Output directory: {output_dir}")
87 print()
88
89 sections = []
90
91 def collect(label, fn, filename, *fn_args):
92 print(f"Collecting: {label}...")
93 try:
94 rows = fn(*fn_args)
95 except Exception as e:
96 print(f" Error: {e}", file=sys.stderr)
97 rows = []
98 csv_reporter.write(output_dir, filename, rows)
99 sections.append((label, len(rows)))
100 return rows
101
102 collect("Member roster", members.member_roster, "member_roster.csv", org, cfg)
103 collect(
104 "2FA disabled", members.two_factor_disabled, "two_factor_disabled.csv", org, cfg
105 )
106
107 print("Fetching repo collaborators (shared cache)...")
108 try:
109 repo_collabs = members.fetch_repo_collaborators(org, cfg)
110 except Exception as e:
111 print(f" Error fetching collaborators: {e}", file=sys.stderr)
112 repo_collabs = []
113
114 collect(
115 "Outside collaborators",
116 members.outside_collaborators,
117 "outside_collaborators.csv",
118 org,
119 cfg,
120 repo_collabs,
121 )
122 collect(
123 "Privileged access",
124 members.privileged_access,
125 "privileged_access.csv",
126 org,
127 cfg,
128 repo_collabs,
129 )
130 collect(
131 "Pending invitations",
132 members.pending_invitations,
133 "pending_invitations.csv",
134 org,
135 cfg,
136 )
137 collect(
138 "Team permissions", members.team_permissions, "team_permissions.csv", org, cfg
139 )
140 collect(
141 "Permission matrix",
142 members.permission_matrix,
143 "permission_matrix.csv",
144 org,
145 cfg,
146 repo_collabs,
147 )
148 collect(
149 "Branch protections",
150 branch_protections.branch_protections,
151 "branch_protections.csv",
152 org,
153 cfg,
154 )
155 collect("Commits", commits.commits, "commits.csv", org, cfg, args.branch)
156 collect(
157 "Org security settings", org_settings.org_security, "org_security.csv", org, cfg
158 )
159 collect("Webhooks", webhooks.webhooks, "webhooks.csv", org, cfg)
160 collect("Deploy keys", deploy_keys.deploy_keys, "deploy_keys.csv", org, cfg)
161 collect(
162 "Secret scanning alerts",
163 security_alerts.secret_scanning,
164 "secret_scanning.csv",
165 org,
166 cfg,
167 )
168 collect(
169 "Dependabot alerts",
170 security_alerts.dependabot_alerts,
171 "dependabot_alerts.csv",
172 org,
173 cfg,
174 )
175 collect(
176 "Audit log branch/ruleset changes",
177 audit_log.audit_log,
178 "audit_log.csv",
179 org,
180 cfg,
181 )
182
183 print()
184 csv_reporter.write_summary(output_dir, org, sections)
185 print()
186 print("Done.")
187
188
189if __name__ == "__main__":
190 run()